1509 lines
30 KiB
C
1509 lines
30 KiB
C
/*
|
|
*
|
|
* This software is licensed under the terms of the GNU General Public
|
|
* License version 2, as published by the Free Software Foundation, and
|
|
* may be copied, distributed, and modified under those terms.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* VERSION DATE AUTHOR
|
|
*
|
|
*/
|
|
#include <linux/firmware.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/module.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/i2c.h>
|
|
#include <linux/input.h>
|
|
#include <asm/uaccess.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/regulator/consumer.h>
|
|
#include <linux/input/mt.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/of_gpio.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/kthread.h>
|
|
#include <linux/wakelock.h>
|
|
#include <linux/completion.h>
|
|
#include <linux/err.h>
|
|
#include <linux/suspend.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/firmware.h>
|
|
#include "tlsc6x_main.h"
|
|
#include "tlsc6x_update.h"
|
|
#include "tlsc6x_gesture_binlib.h"
|
|
|
|
#ifdef TLSC_BUILDIN_BOOT
|
|
#include "tlsc6x_boot.h"
|
|
#endif
|
|
|
|
unsigned int g_tlsc6x_cfg_ver;
|
|
unsigned int g_tlsc6x_boot_ver;
|
|
unsigned int g_needKeepRamCode;
|
|
unsigned short g_tlsc6x_chip_code;
|
|
|
|
int tlsc_boot_version = -1;
|
|
int tlsc_cfg_version = -1;
|
|
int tlsc_vendor_id = -1;
|
|
|
|
unsigned int g_mccode; /* 0:3535, 1:3536 */
|
|
|
|
struct i2c_client *g_tlsc6x_client;
|
|
extern struct mutex i2c_rw_access;
|
|
|
|
#ifdef TLSC_BUILDIN_BOOT
|
|
|
|
static int new_idx_active = -1;
|
|
#endif
|
|
|
|
/* Telink CTP */
|
|
unsigned int MTK_TXRX_BUF;
|
|
unsigned int CMD_ADDR;
|
|
unsigned int RSP_ADDR;
|
|
|
|
typedef struct __test_cmd_wr {
|
|
/* offset 0; */
|
|
unsigned char id; /* cmd_id; */
|
|
unsigned char idv; /* inverse of cmd_id */
|
|
unsigned short d0; /* data 0 */
|
|
unsigned short d1; /* data 1 */
|
|
unsigned short d2; /* data 2 */
|
|
/* offset 8; */
|
|
unsigned char resv; /* offset 8 */
|
|
unsigned char tag; /* offset 9 */
|
|
unsigned short chk; /* 16 bit checksum */
|
|
unsigned short s2Pad0; /* */
|
|
unsigned short s2Pad1; /* */
|
|
} ctp_test_wr_t;
|
|
|
|
typedef struct __test_cmd_rd {
|
|
/* offset 0; */
|
|
unsigned char id; /* cmd_id; */
|
|
unsigned char cc; /* complete code */
|
|
unsigned short d0; /* data 0 */
|
|
unsigned short sn; /* session number */
|
|
unsigned short chk; /* 16 bit checksum */
|
|
} ctp_test_rd_t;
|
|
#define DIRECTLY_MODE (0x0)
|
|
#define DEDICATE_MODE (0x1)
|
|
#define MAX_TRX_LEN (64) /* max IIC data length */
|
|
/* #define CMD_ADDR (0xb400) */
|
|
/* #define RSP_ADDR (0xb440) */
|
|
/* #define MTK_TXRX_BUF (0xcc00) // 1k, buffer used for memory r &w */
|
|
|
|
#define LEN_CMD_CHK_TX (10)
|
|
#define LEN_CMD_PKG_TX (16)
|
|
|
|
#define LEN_RSP_CHK_RX (8)
|
|
#define MAX_BULK_SIZE (1024)
|
|
unsigned short tl_target_cfg[102];
|
|
unsigned short tl_buf_tmpcfg[102];
|
|
/* to direct memory access mode */
|
|
unsigned char cmd_2dma_42bd[6] = {
|
|
/*0x42, 0xbd, */0x28, 0x35, 0xc1, 0x00, 0x35, 0xae };
|
|
|
|
/* in directly memory access mode */
|
|
/* RETURN:0->pass else->fail */
|
|
int tlsc6x_read_bytes_u16addr_sub(struct i2c_client *client, u16 addr,
|
|
u8 *rxbuf, u16 len)
|
|
{
|
|
int err = 0;
|
|
int retry = 0;
|
|
u16 offset = 0;
|
|
u8 buffer[2];
|
|
|
|
struct i2c_msg msgs[2] = { {.addr = client->addr, .flags = 0, .len = 2, /* 16bit memory address */
|
|
.buf = buffer,}, {.addr =
|
|
client->addr, .flags =
|
|
I2C_M_RD,},
|
|
};
|
|
|
|
if (rxbuf == NULL) {
|
|
return -EPERM;
|
|
}
|
|
/* mutex_lock(&g_mutex_i2c_access); */
|
|
|
|
while (len > 0) {
|
|
buffer[0] = (u8) ((addr + offset) >> 8);
|
|
buffer[1] = (u8) (addr + offset);
|
|
|
|
msgs[1].buf = &rxbuf[offset];
|
|
if (len > MAX_TRX_LEN) {
|
|
len -= MAX_TRX_LEN;
|
|
msgs[1].len = MAX_TRX_LEN;
|
|
} else {
|
|
msgs[1].len = len;
|
|
len = 0;
|
|
}
|
|
|
|
retry = 0;
|
|
while (tlsc6x_i2c_read_sub(client, buffer, 2, &rxbuf[offset],
|
|
msgs[1].len) < 0) {
|
|
if (retry++ == 3) {
|
|
err = -1;
|
|
break;
|
|
}
|
|
}
|
|
offset += MAX_TRX_LEN;
|
|
if (err < 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* mutex_unlock(&g_mutex_i2c_access); */
|
|
|
|
return err;
|
|
}
|
|
|
|
int tlsc6x_read_bytes_u16addr(struct i2c_client *client, u16 addr, u8 *rxbuf,
|
|
u16 len)
|
|
{
|
|
int ret = 0;
|
|
|
|
mutex_lock(&i2c_rw_access);
|
|
ret = tlsc6x_read_bytes_u16addr_sub(client, addr, rxbuf, len);
|
|
mutex_unlock(&i2c_rw_access);
|
|
return ret;
|
|
}
|
|
|
|
/* in directly memory access mode */
|
|
/* RETURN:0->pass else->fail */
|
|
int tlsc6x_write_bytes_u16addr_sub(struct i2c_client *client, u16 addr,
|
|
u8 *txbuf, u16 len)
|
|
{
|
|
u8 buffer[MAX_TRX_LEN];
|
|
u16 offset = 0;
|
|
u8 retry = 0;
|
|
int err = 0;
|
|
|
|
struct i2c_msg msg = {.addr = client->addr, .flags = 0, .buf = buffer, };
|
|
|
|
if (txbuf == NULL) {
|
|
return -EPERM;
|
|
}
|
|
/* mutex_lock(&g_mutex_i2c_access); */
|
|
|
|
while (len) {
|
|
buffer[0] = (u8) ((addr + offset) >> 8);
|
|
buffer[1] = (u8) (addr + offset);
|
|
|
|
if (len > (MAX_TRX_LEN - 2)) { /* (sizeof(addr)+payload) <= MAX_TRX_LEN */
|
|
memcpy(&buffer[2], &txbuf[offset], (MAX_TRX_LEN - 2));
|
|
len -= (MAX_TRX_LEN - 2);
|
|
offset += (MAX_TRX_LEN - 2);
|
|
msg.len = MAX_TRX_LEN;
|
|
} else {
|
|
memcpy(&buffer[2], &txbuf[offset], len);
|
|
msg.len = len + 2;
|
|
len = 0;
|
|
}
|
|
|
|
retry = 0;
|
|
while (tlsc6x_i2c_write_sub(client, buffer, msg.len) < 0) {
|
|
if (retry++ == 3) {
|
|
err = -1;
|
|
break;
|
|
}
|
|
}
|
|
if (err < 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* mutex_unlock(&g_mutex_i2c_access); */
|
|
|
|
return err;
|
|
}
|
|
|
|
int tlsc6x_write_bytes_u16addr(struct i2c_client *client, u16 addr, u8 *txbuf,
|
|
u16 len)
|
|
{
|
|
int ret = 0;
|
|
|
|
mutex_lock(&i2c_rw_access);
|
|
ret = tlsc6x_write_bytes_u16addr_sub(client, addr, txbuf, len);
|
|
mutex_unlock(&i2c_rw_access);
|
|
return ret;
|
|
}
|
|
|
|
/* <0 : i2c error */
|
|
/* 0: direct address mode */
|
|
/* 1: protect mode */
|
|
int tlsc6x_get_i2cmode(void)
|
|
{
|
|
u8 regData[4];
|
|
|
|
if (tlsc6x_read_bytes_u16addr_sub(g_tlsc6x_client, 0x01, regData, 3)) {
|
|
return -EPERM;
|
|
}
|
|
|
|
if (((u8) (g_tlsc6x_client->addr) == (regData[0] >> 1))
|
|
&& (regData[2] == 0X01)) {
|
|
return DIRECTLY_MODE;
|
|
}
|
|
|
|
return DEDICATE_MODE;
|
|
}
|
|
|
|
/* 0:successful */
|
|
int tlsc6x_set_dd_mode_sub(void)
|
|
{
|
|
int mod = -1;
|
|
int retry = 0;
|
|
|
|
int ret = 0;
|
|
|
|
ret = tlsc6x_get_i2cmode();
|
|
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
if (ret == DIRECTLY_MODE) {
|
|
return 0;
|
|
}
|
|
|
|
while (retry++ < 5) {
|
|
msleep(20);
|
|
tlsc6x_write_bytes_u16addr_sub(g_tlsc6x_client, 0x42bd,
|
|
cmd_2dma_42bd, 6);
|
|
msleep(50);
|
|
mod = tlsc6x_get_i2cmode();
|
|
if (mod == DIRECTLY_MODE) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (mod == DIRECTLY_MODE) {
|
|
return 0;
|
|
} else {
|
|
return -EPERM;
|
|
}
|
|
}
|
|
|
|
int tlsc6x_set_dd_mode(void)
|
|
{
|
|
int ret = 0;
|
|
|
|
mutex_lock(&i2c_rw_access);
|
|
ret = tlsc6x_set_dd_mode_sub();
|
|
mutex_unlock(&i2c_rw_access);
|
|
return ret;
|
|
}
|
|
|
|
/* 0:successful */
|
|
int tlsc6x_set_nor_mode_sub(void)
|
|
{
|
|
int mod = -1;
|
|
int retry = 0;
|
|
u8 reg = 0x05;
|
|
|
|
while (retry++ < 5) {
|
|
tlsc6x_write_bytes_u16addr_sub(g_tlsc6x_client, 0x03, ®, 1);
|
|
usleep_range(5000, 5500);
|
|
mod = tlsc6x_get_i2cmode();
|
|
if (mod == DEDICATE_MODE) {
|
|
break;
|
|
}
|
|
msleep(50);
|
|
}
|
|
if (mod == DEDICATE_MODE) {
|
|
return 0;
|
|
} else {
|
|
return -EPERM;
|
|
}
|
|
}
|
|
|
|
int tlsc6x_set_nor_mode(void)
|
|
{
|
|
int ret = 0;
|
|
|
|
mutex_lock(&i2c_rw_access);
|
|
ret = tlsc6x_set_nor_mode_sub();
|
|
mutex_unlock(&i2c_rw_access);
|
|
return ret;
|
|
}
|
|
|
|
/* ret=0 : successful */
|
|
/* write with read-back check, in dd mode */
|
|
static int tlsc6x_bulk_down_check(u8 *pbuf, u16 addr, u16 len)
|
|
{
|
|
unsigned int j, k, retry;
|
|
u8 rback[128];
|
|
|
|
TLSC_FUNC_ENTER();
|
|
while (len) {
|
|
k = (len < 128) ? len : 128;
|
|
retry = 0;
|
|
do {
|
|
rback[k - 1] = pbuf[k - 1] + 1;
|
|
tlsc6x_write_bytes_u16addr(g_tlsc6x_client, addr, pbuf,
|
|
k);
|
|
tlsc6x_read_bytes_u16addr(g_tlsc6x_client, addr, rback,
|
|
k);
|
|
for (j = 0; j < k; j++) {
|
|
if (pbuf[j] != rback[j]) {
|
|
break;
|
|
}
|
|
}
|
|
if (j >= k) {
|
|
break; /* match */
|
|
}
|
|
} while (++retry < 3);
|
|
|
|
if (j < k) {
|
|
break;
|
|
}
|
|
|
|
addr += k;
|
|
pbuf += k;
|
|
len -= k;
|
|
}
|
|
|
|
return (int)len;
|
|
}
|
|
|
|
static u16 tlsc6x_checksum_u16(u16 *buf, u16 length)
|
|
{
|
|
unsigned short sum, len, i;
|
|
|
|
sum = 0;
|
|
|
|
len = length >> 1;
|
|
|
|
for (i = 0; i < len; i++) {
|
|
sum += buf[i];
|
|
}
|
|
tlsc_info("sum=%d, 0-succ\n", sum);
|
|
return sum;
|
|
}
|
|
|
|
static u32 tlsc6x_checksumEx(u8 *buf, u16 length)
|
|
{
|
|
u32 combChk;
|
|
u16 k, check, checkEx;
|
|
|
|
check = 0;
|
|
checkEx = 0;
|
|
for (k = 0; k < length; k++) {
|
|
check += buf[k];
|
|
checkEx += (u16) (k * buf[k]);
|
|
}
|
|
combChk = (checkEx << 16) | check;
|
|
|
|
return combChk;
|
|
|
|
}
|
|
|
|
/* 0:successful */
|
|
int tlsc6x_download_ramcode(u8 *pcode, u16 len)
|
|
{
|
|
u8 dwr, retry;
|
|
int ret = -2;
|
|
int sig;
|
|
|
|
TLSC_FUNC_ENTER();
|
|
if (tlsc6x_set_dd_mode()) {
|
|
return -EPERM;
|
|
}
|
|
|
|
sig = (int)pcode[3];
|
|
sig = (sig << 8) + (int)pcode[2];
|
|
sig = (sig << 8) + (int)pcode[1];
|
|
sig = (sig << 8) + (int)pcode[0];
|
|
|
|
if (sig == 0x6d6f8008) {
|
|
sig = 0;
|
|
tlsc6x_read_bytes_u16addr(g_tlsc6x_client, 0x8000, (u8 *) &sig,
|
|
4);
|
|
if (sig == 0x6d6f8008) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
dwr = 0x05;
|
|
if (tlsc6x_bulk_down_check(&dwr, 0x0602, 1) == 0) { /* stop mcu */
|
|
dwr = 0x00;
|
|
tlsc6x_bulk_down_check(&dwr, 0x0643, 1); /* disable irq */
|
|
} else {
|
|
return -EPERM;
|
|
}
|
|
if (tlsc6x_bulk_down_check(pcode, 0x8000, len) == 0) {
|
|
dwr = 0x88;
|
|
retry = 0;
|
|
do {
|
|
ret =
|
|
tlsc6x_write_bytes_u16addr(g_tlsc6x_client, 0x0602,
|
|
&dwr, 1);
|
|
} while ((++retry < 3) && (ret != 0));
|
|
}
|
|
msleep(50);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* return 0=successful: send cmd and get rsp. */
|
|
static int tlsc6x_cmd_send(ctp_test_wr_t *ptchcw, ctp_test_rd_t *pcr)
|
|
{
|
|
int ret;
|
|
u32 retry;
|
|
|
|
TLSC_FUNC_ENTER();
|
|
retry = 0;
|
|
tlsc6x_write_bytes_u16addr(g_tlsc6x_client, RSP_ADDR, (u8 *) &retry,
|
|
1);
|
|
|
|
/* send command */
|
|
ptchcw->idv = ~(ptchcw->id);
|
|
ptchcw->tag = 0x35;
|
|
ptchcw->chk =
|
|
1 + ~(tlsc6x_checksum_u16((u16 *) ptchcw, LEN_CMD_CHK_TX));
|
|
ptchcw->tag = 0x30;
|
|
ret =
|
|
tlsc6x_write_bytes_u16addr(g_tlsc6x_client, CMD_ADDR, (u8 *) ptchcw,
|
|
LEN_CMD_PKG_TX);
|
|
if (ret) {
|
|
goto exit;
|
|
}
|
|
ptchcw->tag = 0x35;
|
|
ret = tlsc6x_write_bytes_u16addr(g_tlsc6x_client, CMD_ADDR + 9,
|
|
(u8 *) &(ptchcw->tag), 1);
|
|
if (ret) {
|
|
goto exit;
|
|
}
|
|
/* polling rsp, the caller must init rsp buffer. */
|
|
ret = -1;
|
|
retry = 0;
|
|
while (retry++ < 100) { /* 2s */
|
|
msleep(20);
|
|
if (tlsc6x_read_bytes_u16addr
|
|
(g_tlsc6x_client, RSP_ADDR, (u8 *) pcr, 1)) {
|
|
break;
|
|
}
|
|
|
|
if (ptchcw->id != pcr->id) {
|
|
continue;
|
|
}
|
|
/* msleep(50); */
|
|
tlsc6x_read_bytes_u16addr(g_tlsc6x_client, RSP_ADDR, (u8 *) pcr,
|
|
LEN_RSP_CHK_RX);
|
|
if (!tlsc6x_checksum_u16((u16 *) pcr, LEN_RSP_CHK_RX)) {
|
|
if ((ptchcw->id == pcr->id) && (pcr->cc == 0)) {
|
|
ret = 0;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
exit:
|
|
/* clean rsp buffer */
|
|
/* retry = 0; */
|
|
/* tlsc6x_write_bytes_u16addr(g_tlsc6x_client, RSP_ADDR, (u8*)&retry, 1); */
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
/* return 0=successful */
|
|
int tlsc6x_read_burn_space(u8 *pdes, u16 adr, u16 len)
|
|
{
|
|
int rsp;
|
|
u32 left = len;
|
|
u32 combChk, retry;
|
|
ctp_test_wr_t m_cmd;
|
|
ctp_test_rd_t m_rsp;
|
|
|
|
TLSC_FUNC_ENTER();
|
|
m_cmd.id = 0x31;
|
|
m_cmd.resv = 0x03;
|
|
while (left) {
|
|
len = (left > MAX_BULK_SIZE) ? MAX_BULK_SIZE : left;
|
|
|
|
m_cmd.d0 = adr;
|
|
m_cmd.d1 = len;
|
|
|
|
rsp = -1;
|
|
retry = 0;
|
|
while (retry++ < 3) {
|
|
m_rsp.id = 0;
|
|
if (tlsc6x_cmd_send(&m_cmd, &m_rsp) == 0X0) {
|
|
tlsc6x_read_bytes_u16addr(g_tlsc6x_client,
|
|
MTK_TXRX_BUF, pdes,
|
|
len);
|
|
combChk = tlsc6x_checksumEx(pdes, len);
|
|
if (m_rsp.d0 == (unsigned short)combChk) {
|
|
if (m_rsp.sn ==
|
|
(unsigned short)(combChk >> 16)) {
|
|
rsp = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (rsp < 0) {
|
|
break;
|
|
}
|
|
left -= len;
|
|
adr += len;
|
|
pdes += len;
|
|
}
|
|
|
|
return rsp;
|
|
}
|
|
|
|
int tlsc6x_write_burn_space(u8 *psrc, u16 adr, u16 len)
|
|
{
|
|
int rsp = 0;
|
|
u16 left = len;
|
|
u32 retry, combChk;
|
|
ctp_test_wr_t m_cmd;
|
|
ctp_test_rd_t m_rsp;
|
|
|
|
TLSC_FUNC_ENTER();
|
|
m_cmd.id = 0x30;
|
|
m_cmd.resv = 0x11;
|
|
|
|
while (left) {
|
|
len = (left > MAX_BULK_SIZE) ? MAX_BULK_SIZE : left;
|
|
combChk = tlsc6x_checksumEx(psrc, len);
|
|
|
|
m_cmd.d0 = adr;
|
|
m_cmd.d1 = len;
|
|
m_cmd.d2 = (u16) combChk;
|
|
m_cmd.s2Pad0 = (u16) (combChk >> 16);
|
|
|
|
rsp = -1; /* avoid dead loop */
|
|
retry = 0;
|
|
while (retry < 3) {
|
|
tlsc6x_write_bytes_u16addr(g_tlsc6x_client,
|
|
MTK_TXRX_BUF, psrc, len);
|
|
m_rsp.id = 0;
|
|
rsp = tlsc6x_cmd_send(&m_cmd, &m_rsp);
|
|
if (rsp < 0) {
|
|
if ((m_rsp.d0 == 0X05) && (m_rsp.cc == 0X09)) { /* fotal error */
|
|
break;
|
|
}
|
|
retry++;
|
|
} else {
|
|
left -= len;
|
|
adr += len;
|
|
psrc += len;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (rsp < 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (!left) ? 0 : -1;
|
|
}
|
|
|
|
static int is_valid_cfg_data(u16 *ptcfg)
|
|
{
|
|
if (ptcfg == NULL) {
|
|
return 0;
|
|
}
|
|
tlsc_info("ptcfg[53]=0x%x,addr =0x%x\n", ptcfg[53],
|
|
(g_tlsc6x_client->addr));
|
|
if ((u8) ((ptcfg[53] >> 1) & 0x7f) != (u8) (g_tlsc6x_client->addr)) {
|
|
return 0;
|
|
}
|
|
|
|
if (tlsc6x_checksum_u16(ptcfg, 204)) {
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int tlsc6x_tpcfg_ver_comp(unsigned short *ptcfg)
|
|
{
|
|
unsigned int u32tmp;
|
|
unsigned short vnow, vbuild;
|
|
|
|
TLSC_FUNC_ENTER();
|
|
if (g_tlsc6x_cfg_ver == 0) { /* no available version information */
|
|
return 0;
|
|
}
|
|
|
|
if (is_valid_cfg_data(ptcfg) == 0) {
|
|
return 0;
|
|
}
|
|
|
|
u32tmp = ptcfg[1];
|
|
u32tmp = (u32tmp << 16) | ptcfg[0];
|
|
if ((g_tlsc6x_cfg_ver & 0x3ffffff) != (u32tmp & 0x3ffffff)) {
|
|
return 0;
|
|
}
|
|
|
|
vnow = (g_tlsc6x_cfg_ver >> 26) & 0x3f;
|
|
vbuild = (u32tmp >> 26) & 0x3f;
|
|
tlsc_info("vnow: 0x%x,vbuild: 0x%x", vnow, vbuild);
|
|
if (vbuild <= vnow) {
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int tlsc6x_tpcfg_ver_comp_weak(unsigned short *ptcfg)
|
|
{
|
|
unsigned int u32tmp;
|
|
|
|
TLSC_FUNC_ENTER();
|
|
if (g_tlsc6x_cfg_ver == 0) { /* no available version information */
|
|
return 0;
|
|
}
|
|
|
|
if (is_valid_cfg_data(ptcfg) == 0) {
|
|
tlsc_err("Tlsc6x:cfg_data is invalid!\n");
|
|
return 0;
|
|
}
|
|
|
|
u32tmp = ptcfg[1];
|
|
u32tmp = (u32tmp << 16) | ptcfg[0];
|
|
if ((g_tlsc6x_cfg_ver & 0x3ffffff) != (u32tmp & 0x3ffffff)) { /* */
|
|
tlsc_err
|
|
("tlsc6x:ptcfg version error,g_tlsc6x_cfg_ver is 0x%x:ptcfg version is 0x%x!\n",
|
|
g_tlsc6x_cfg_ver, u32tmp);
|
|
return 0;
|
|
}
|
|
|
|
if (g_tlsc6x_cfg_ver == u32tmp) {
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* 0 error */
|
|
/* 0x7f80 no data */
|
|
static u16 tlsx6x_find_last_cfg(void)
|
|
{
|
|
unsigned short addr, check;
|
|
|
|
addr = 0x7f80 - 256;
|
|
while (addr > 0x6000) { /* 0x6080 */
|
|
check = 0;
|
|
if (tlsc6x_read_burn_space((u8 *) &check, addr, 2)) {
|
|
addr = 0;
|
|
goto exit;
|
|
}
|
|
if (check == 0xffff) {
|
|
break;
|
|
}
|
|
addr -= 256;
|
|
}
|
|
|
|
addr += 256;
|
|
|
|
exit:
|
|
|
|
return addr;
|
|
}
|
|
|
|
static int find_last_valid_burn_cfg(u16 *ptcfg)
|
|
{
|
|
unsigned short addr;
|
|
|
|
if (tlsc6x_download_ramcode(fw_burn_bin, sizeof(fw_burn_bin))) {
|
|
return -EPERM;
|
|
}
|
|
|
|
addr = tlsx6x_find_last_cfg();
|
|
if ((addr == 0) || (addr == 0x7f80)) {
|
|
return -EPERM;
|
|
}
|
|
|
|
while (addr <= 0x7e80) {
|
|
if (tlsc6x_read_burn_space((u8 *) ptcfg, addr, 204)) {
|
|
addr = 0x7f80; /* force error */
|
|
break;
|
|
}
|
|
if (is_valid_cfg_data(ptcfg) == 0) {
|
|
addr += 256;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (addr <= 0x7e80) ? 0 : -1;
|
|
|
|
}
|
|
|
|
int tlsx6x_update_fcomp_cfg(u16 *ptcfg)
|
|
{
|
|
if (tlsc6x_tpcfg_ver_comp_weak(ptcfg) == 0) {
|
|
tlsc_err("Tlsc6x:update error:version error!\n");
|
|
return -EPERM;
|
|
}
|
|
|
|
if (g_tlsc6x_cfg_ver && ((u16) (g_tlsc6x_cfg_ver & 0xffff) != ptcfg[0])) {
|
|
return -EPERM;
|
|
}
|
|
|
|
if (tlsc6x_download_ramcode(fw_fcode_burn, sizeof(fw_fcode_burn))) {
|
|
tlsc_err("Tlsc6x:update error:ram-code error!\n");
|
|
return -EPERM;
|
|
}
|
|
|
|
if (tlsc6x_write_burn_space((unsigned char *)ptcfg, 0x8000, 204)) {
|
|
tlsc_err("Tlsc6x:update fcomp_cfg fail!\n");
|
|
return -EPERM;
|
|
}
|
|
|
|
tlsc_info("Tlsc6x:update fcomp_cfg pass!\n");
|
|
|
|
memcpy(tl_target_cfg, ptcfg, 204);
|
|
g_tlsc6x_cfg_ver = (ptcfg[1] << 16) | ptcfg[0];
|
|
tlsc_cfg_version = g_tlsc6x_cfg_ver >> 26;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int tlsx6x_update_fcomp_boot(unsigned char *pdata, u16 len)
|
|
{
|
|
if (tlsc6x_download_ramcode(fw_fcode_burn, sizeof(fw_fcode_burn))) {
|
|
tlsc_err("Tlsc6x:update error:ram-code error!\n");
|
|
return -EPERM;
|
|
}
|
|
pdata[8] = 0xff;
|
|
if (tlsc6x_write_burn_space((unsigned char *)pdata, 0x00, len)) {
|
|
tlsc_err("Tlsc6x:update fcomp_boot fail!\n");
|
|
return -EPERM;
|
|
}
|
|
pdata[8] = 0x4b;
|
|
if (tlsc6x_write_burn_space((unsigned char *)&pdata[8], 0x08, 1)) {
|
|
tlsc_err("Tlsc6x:update fcomp_boot last sig-byte fail!\n");
|
|
return -EPERM;
|
|
}
|
|
|
|
tlsc_info("Tlsc6x:update fcomp_boot pass!\n");
|
|
g_tlsc6x_boot_ver = pdata[5];
|
|
g_tlsc6x_boot_ver = (g_tlsc6x_boot_ver << 8) + pdata[4];
|
|
tlsc_boot_version = g_tlsc6x_boot_ver;
|
|
return 0;
|
|
}
|
|
|
|
int tlsx6x_update_ocomp_boot(unsigned char *pdata, u16 len)
|
|
{
|
|
unsigned int oo_tail[4] = { 0x60298bf, 0x15cbf, 0x60301bf, 0x3d43f };
|
|
|
|
if (tlsc6x_download_ramcode(fw_burn_bin, sizeof(fw_burn_bin))) {
|
|
tlsc_err("Tlsc6x:update error:ram-code error!\n");
|
|
return -EPERM;
|
|
}
|
|
|
|
if (tlsc6x_write_burn_space((unsigned char *)pdata, 0x00, len)) {
|
|
tlsc_err("Tlsc6x:update ocomp_boot fail!\n");
|
|
return -EPERM;
|
|
}
|
|
|
|
if (tlsc6x_write_burn_space((unsigned char *)oo_tail, 0x7fec, 16)) {
|
|
tlsc_err("Tlsc6x:update ocomp_boot fail!\n");
|
|
return -EPERM;
|
|
}
|
|
tlsc_info("Tlsc6x:update ocomp_boot pass!\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
int tlsc6x_update_f_combboot(u8 *pdata, u16 len)
|
|
{
|
|
int ret = 0;
|
|
unsigned short boot_len;
|
|
|
|
boot_len = pdata[7];
|
|
boot_len = (boot_len << 8) + pdata[6];
|
|
|
|
printk
|
|
("tlsc_fail_debug:Now update bin the len is%d,the boot len is%d.\n",
|
|
len, boot_len);
|
|
|
|
if (g_mccode == 0) {
|
|
if (g_needKeepRamCode == 0) {
|
|
tlsc_err
|
|
("Tlsc6x:update_f_combboot error:mccode error!\n");
|
|
return -EPERM;
|
|
}
|
|
if ((len >= boot_len)
|
|
&& ((pdata[2] == 0x35) && (pdata[3] == 0x35))) {
|
|
ret = tlsx6x_update_ocomp_boot(pdata, boot_len);
|
|
pdata += boot_len;
|
|
len = len - boot_len;
|
|
}
|
|
if ((ret == 0) && (len >= 204)) {
|
|
memcpy(tl_buf_tmpcfg, pdata, 204);
|
|
ret = tlsx6x_update_burn_cfg(tl_buf_tmpcfg);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
if ((len >= boot_len) && ((pdata[2] == 0x36) && (pdata[3] == 0x35))) {
|
|
ret = tlsx6x_update_fcomp_boot(pdata, boot_len);
|
|
pdata += boot_len;
|
|
len = len - boot_len;
|
|
}
|
|
|
|
printk
|
|
("tlsc_fail_debug:Boot bin is finish. Now the len is%d,the boot len is%d.\n",
|
|
len, boot_len);
|
|
|
|
if ((ret == 0) && (len >= 204)) {
|
|
memcpy(tl_buf_tmpcfg, pdata, 204);
|
|
ret = tlsx6x_update_fcomp_cfg(tl_buf_tmpcfg);
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
int tlsx6x_update_burn_cfg(u16 *ptcfg)
|
|
{
|
|
u16 addr, check;
|
|
|
|
TLSC_FUNC_ENTER();
|
|
if (g_mccode == 1) {
|
|
return tlsx6x_update_fcomp_cfg(ptcfg);
|
|
}
|
|
|
|
if (tlsc6x_tpcfg_ver_comp_weak(ptcfg) == 0) {
|
|
tlsc_err("Tlsc6x:update error:version error!\n");
|
|
return -EPERM;
|
|
}
|
|
|
|
if (g_tlsc6x_cfg_ver && ((u16) (g_tlsc6x_cfg_ver & 0xffff) != ptcfg[0])) {
|
|
return -EPERM;
|
|
}
|
|
|
|
if (tlsc6x_download_ramcode(fw_burn_bin, sizeof(fw_burn_bin))) {
|
|
tlsc_err("Tlsc6x:update error:ram-code error!\n");
|
|
return -EPERM;
|
|
}
|
|
|
|
addr = tlsx6x_find_last_cfg();
|
|
if ((addr <= 0x6180) || (addr == 0x7f80)) {
|
|
tlsc_err("Tlsc6x:update error:time limit!\n");
|
|
return -EPERM;
|
|
}
|
|
|
|
addr = addr - 256;
|
|
|
|
/* pre-check */
|
|
check = 0;
|
|
if (tlsc6x_read_burn_space((unsigned char *)&check, addr - 256, 2)) {
|
|
tlsc_err("Tlsc6x:update error:pre-read error!\n");
|
|
return -EPERM;
|
|
}
|
|
if (check != 0xffff) {
|
|
tlsc_err("Tlsc6x:update error:pre-read limit!\n");
|
|
return -EPERM;
|
|
}
|
|
|
|
if (tlsc6x_write_burn_space((unsigned char *)ptcfg, addr, 204)) {
|
|
tlsc_err("Tlsc6x:update fail!\n");
|
|
return -EPERM;
|
|
}
|
|
|
|
tlsc_info("Tlsc6x:update pass!\n");
|
|
|
|
memcpy(tl_target_cfg, ptcfg, 204);
|
|
g_tlsc6x_cfg_ver = (ptcfg[1] << 16) | ptcfg[0];
|
|
g_tlsc6x_chip_code = (unsigned short)ptcfg[53];
|
|
|
|
tlsc_cfg_version = g_tlsc6x_cfg_ver >> 26;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* NOTE:caller guarantee the legitimacy */
|
|
/* download tp-cfg. */
|
|
int tlsx6x_update_running_cfg(u16 *ptcfg)
|
|
{
|
|
unsigned int retry;
|
|
unsigned int tmp[2];
|
|
|
|
TLSC_FUNC_ENTER();
|
|
if (is_valid_cfg_data(ptcfg) == 0) {
|
|
return -EPERM;
|
|
}
|
|
if (tlsc6x_set_dd_mode()) {
|
|
return -EPERM;
|
|
}
|
|
|
|
if (tlsc6x_bulk_down_check((unsigned char *)ptcfg, 0xd7e0, 204)) { /* stop mcu */
|
|
goto exit;
|
|
}
|
|
|
|
tmp[0] = 0x6798;
|
|
tmp[1] = 0xcd3500ff;
|
|
|
|
retry = 0;
|
|
while (++retry < 3) {
|
|
if (tlsc6x_write_bytes_u16addr
|
|
(g_tlsc6x_client, 0xdf10, (u8 *) &tmp[0], 8)) {
|
|
usleep_range(5000, 5500);
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* write error? don't care */
|
|
retry = 0;
|
|
while (++retry < 5) {
|
|
usleep_range(10000, 11000);
|
|
tmp[0] = 0;
|
|
tlsc6x_read_bytes_u16addr(g_tlsc6x_client, 0xdf16,
|
|
(u8 *) &tmp[0], 1);
|
|
if (tmp[0] == 0x30) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
exit: tlsc6x_set_nor_mode();
|
|
memcpy(tl_target_cfg, ptcfg, 204);
|
|
return 0;
|
|
}
|
|
|
|
/* return :0->no hw resetting needed */
|
|
/* else -> caller do hw resettting */
|
|
|
|
/* 0:successful */
|
|
static int tlsc6x_download_gestlib_fast(u8 *pcode, u16 len)
|
|
{
|
|
u8 dwr, retry;
|
|
int ret = -2;
|
|
|
|
TLSC_FUNC_ENTER();
|
|
if (tlsc6x_set_dd_mode()) {
|
|
return -EPERM;
|
|
}
|
|
|
|
dwr = 0x05;
|
|
if (tlsc6x_bulk_down_check(&dwr, 0x0602, 1) == 0) { /* stop mcu */
|
|
dwr = 0x00;
|
|
tlsc6x_bulk_down_check(&dwr, 0x0643, 1); /* disable irq */
|
|
} else {
|
|
return -EPERM;
|
|
}
|
|
|
|
if (tlsc6x_bulk_down_check(pcode, 0x8000, 1024) == 0) {
|
|
if (tlsc6x_write_bytes_u16addr(g_tlsc6x_client, 0x8400, pcode + 1024, len - 1024) == 0) { /* */
|
|
ret = 0;
|
|
}
|
|
}
|
|
|
|
if (ret == 0) {
|
|
dwr = 0x88;
|
|
retry = 0;
|
|
do {
|
|
ret =
|
|
tlsc6x_write_bytes_u16addr(g_tlsc6x_client, 0x0602,
|
|
&dwr, 1);
|
|
} while ((++retry < 3) && (ret != 0));
|
|
}
|
|
|
|
msleep(40); /* 30ms */
|
|
if (tlsc6x_get_i2cmode() == DIRECTLY_MODE) {
|
|
ret = tlsc6x_download_ramcode(pcode, len);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int tlsc6x_load_gesture_binlib(void)
|
|
{
|
|
int ret;
|
|
|
|
TLSC_FUNC_ENTER();
|
|
ret = tlsc6x_download_gestlib_fast(tlsc6x_gesture_binlib,
|
|
sizeof(tlsc6x_gesture_binlib));
|
|
if (ret) {
|
|
tlsc_err("Tlsc6x:load gesture binlib error!\n");
|
|
return -EPERM;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void tlsc6x_3536enter_gest(int event_mask)
|
|
{
|
|
tlsc_info("tlsc6x::3536 setup gesture: 0x%x!\n", event_mask);
|
|
|
|
if (tlsc6x_write_reg(g_tlsc6x_client, 0xd0, 0x01) < 0) {
|
|
tlsc_err("tlsc6x error::3536 setup gesture fail!\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
void tlsc6x_enter_gest_mode(int event_mask)
|
|
{
|
|
if (g_mccode == 1) {
|
|
tlsc6x_3536enter_gest(event_mask);
|
|
} else {
|
|
tlsc6x_load_gesture_binlib();
|
|
}
|
|
}
|
|
|
|
int tlsc6x_load_ext_binlib(u8 *pdata, u16 len)
|
|
{
|
|
int ret = 0;
|
|
unsigned short boot_len;
|
|
|
|
TLSC_FUNC_ENTER();
|
|
|
|
boot_len = pdata[7];
|
|
boot_len = (boot_len << 8) + pdata[6];
|
|
|
|
if (len >= boot_len) {
|
|
ret = tlsc6x_download_ramcode(pdata, boot_len);
|
|
pdata += boot_len;
|
|
len = len - boot_len;
|
|
if ((ret == 0) && (len >= 204)) {
|
|
msleep(30);
|
|
ret = tlsx6x_update_running_cfg((u16 *) pdata);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*get running time tp-cfg.
|
|
*@ptcfg: data buffer
|
|
*@addr: real data address for different chips
|
|
*
|
|
*return: 0 SUCESS else FAIL
|
|
* Note: touch chip MUST work in DD-mode.
|
|
*/
|
|
static int tlsx6x_comb_get_running_cfg(u16 *ptcfg, u16 addr)
|
|
{
|
|
int retry, err_type;
|
|
|
|
TLSC_FUNC_ENTER();
|
|
retry = 0;
|
|
err_type = 0;
|
|
|
|
tlsc6x_set_dd_mode();
|
|
|
|
while (++retry < 5) {
|
|
err_type = 0;
|
|
if (tlsc6x_read_bytes_u16addr
|
|
(g_tlsc6x_client, addr, (u8 *) ptcfg, 204)) {
|
|
msleep(20);
|
|
err_type = 2; /* i2c error */
|
|
continue;
|
|
}
|
|
|
|
if (is_valid_cfg_data(ptcfg) == 0) {
|
|
tlsc6x_set_dd_mode();
|
|
err_type = 1; /* data error or no data */
|
|
msleep(20);
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return err_type;
|
|
|
|
}
|
|
|
|
static int tlsx6x_3535get_running_cfg(unsigned short *ptcfg)
|
|
{
|
|
|
|
TLSC_FUNC_ENTER();
|
|
|
|
return tlsx6x_comb_get_running_cfg(ptcfg, 0xd6e0);
|
|
}
|
|
|
|
static int tlsx6x_3536get_running_cfg(unsigned short *ptcfg)
|
|
{
|
|
|
|
TLSC_FUNC_ENTER();
|
|
|
|
return tlsx6x_comb_get_running_cfg(ptcfg, 0x9e00);
|
|
}
|
|
|
|
static int tlsx6x_3536get_os_data(unsigned short *ptdata, int len)
|
|
{
|
|
int ret = 0;
|
|
int retryTime;
|
|
u8 writebuf[4];
|
|
|
|
TLSC_FUNC_ENTER();
|
|
|
|
if (len < 96) {
|
|
tlsc_err("%s(): error size.", __func__);
|
|
return -EPERM;
|
|
}
|
|
|
|
msleep(100);
|
|
|
|
mutex_lock(&i2c_rw_access);
|
|
//write addr
|
|
writebuf[0] = 0x9F;
|
|
writebuf[1] = 0x20;
|
|
writebuf[2] = 48;
|
|
writebuf[3] = 0xFF;
|
|
ret = tlsc6x_i2c_write_sub(g_tlsc6x_client, writebuf, 4);
|
|
writebuf[0] = 0x9F;
|
|
writebuf[1] = 0x24;
|
|
writebuf[2] = 1;
|
|
|
|
ret = tlsc6x_i2c_write_sub(g_tlsc6x_client, writebuf, 3);
|
|
retryTime = 100;
|
|
do {
|
|
ret =
|
|
tlsc6x_i2c_read_sub(g_tlsc6x_client, writebuf, 2,
|
|
&writebuf[2], 1);
|
|
if (ret < 0) {
|
|
break;
|
|
}
|
|
retryTime--;
|
|
msleep(30);
|
|
} while ((retryTime > 0) && (writebuf[2] == 1));
|
|
|
|
if (ret >= 0) {
|
|
writebuf[0] = 0x9F;
|
|
writebuf[1] = 0x26;
|
|
ret =
|
|
tlsc6x_i2c_read_sub(g_tlsc6x_client, writebuf, 2,
|
|
(char *)ptdata, 96);
|
|
}
|
|
|
|
mutex_unlock(&i2c_rw_access);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int tlsx6x_3535find_lastvaild_ver(void)
|
|
{
|
|
if (find_last_valid_burn_cfg(tl_buf_tmpcfg) == 0) {
|
|
g_tlsc6x_cfg_ver = (unsigned int)tl_buf_tmpcfg[1];
|
|
g_tlsc6x_cfg_ver = (g_tlsc6x_cfg_ver << 16)
|
|
+ (unsigned int)tl_buf_tmpcfg[0];
|
|
|
|
tlsc_cfg_version = g_tlsc6x_cfg_ver >> 26;
|
|
tlsc_vendor_id = (g_tlsc6x_cfg_ver >> 9) & 0x7F;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tlsx6x_3536find_lastvaild_ver(void)
|
|
{
|
|
if (tlsc6x_download_ramcode(fw_fcode_burn, sizeof(fw_fcode_burn))) {
|
|
tlsc_err("Tlsc6x:update error:ram-code error!\n");
|
|
return -EPERM;
|
|
}
|
|
|
|
if (tlsc6x_read_burn_space((u8 *) tl_buf_tmpcfg, 0xf000, 204)) {
|
|
return -EPERM;
|
|
}
|
|
|
|
if (is_valid_cfg_data(tl_buf_tmpcfg)) {
|
|
g_tlsc6x_cfg_ver = (unsigned int)tl_buf_tmpcfg[1];
|
|
g_tlsc6x_cfg_ver = (g_tlsc6x_cfg_ver << 16)
|
|
+ (unsigned int)tl_buf_tmpcfg[0];
|
|
g_tlsc6x_cfg_ver = g_tlsc6x_cfg_ver & 0x3ffffff;
|
|
tlsc_cfg_version = g_tlsc6x_cfg_ver >> 26;
|
|
tlsc_vendor_id = (g_tlsc6x_cfg_ver >> 9) & 0x7F;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#ifdef TLSC_BUILDIN_BOOT
|
|
static int tlsc6x_upgrade_romcfg_array(unsigned short *parray,
|
|
unsigned int cfg_num)
|
|
{
|
|
unsigned int k;
|
|
|
|
TLSC_FUNC_ENTER();
|
|
|
|
tlsc_info("g_tlsc6x_cfg_ver is 0x%x\n", g_tlsc6x_cfg_ver);
|
|
if (g_tlsc6x_cfg_ver == 0) { /* no available version information */
|
|
tlsc_err("Tlsc6x:no current version information!\n");
|
|
return 0;
|
|
}
|
|
|
|
new_idx_active = -1;
|
|
|
|
for (k = 0; k < cfg_num; k++) {
|
|
if (tlsc6x_tpcfg_ver_comp(parray) == 1) {
|
|
new_idx_active = k;
|
|
tlsc_info("%s, new_idx_active is %d.\n", __func__,
|
|
new_idx_active);
|
|
break;
|
|
}
|
|
parray = parray + 102;
|
|
}
|
|
|
|
if (new_idx_active < 0) {
|
|
tlsc_info("Tlsc6x:auto update skip:no updated version!\n");
|
|
return -EPERM;
|
|
}
|
|
|
|
if (tlsc6x_set_dd_mode()) {
|
|
tlsc_err("Tlsc6x:auto update error:can't control hw mode!\n");
|
|
return -EPERM;
|
|
}
|
|
|
|
if (tlsx6x_update_burn_cfg(parray) == 0) {
|
|
tlsc_info("Tlsc6x:update pass!\n");
|
|
} else {
|
|
tlsc_err("Tlsc6x:update fail!\n");
|
|
}
|
|
|
|
return 1; /* need hw reset */
|
|
|
|
}
|
|
|
|
static int tlsc6x_boot_ver_comp(unsigned int ver)
|
|
{
|
|
if (g_tlsc6x_boot_ver == 0) {
|
|
return 1;
|
|
}
|
|
|
|
if (ver > g_tlsc6x_boot_ver) {
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tlsc6x_3536boot_update(u8 *pdata, u16 boot_len)
|
|
{
|
|
unsigned int ver = 0;
|
|
|
|
ver = pdata[5];
|
|
ver = (ver << 8) + pdata[4];
|
|
|
|
if (tlsc6x_boot_ver_comp(ver) == 0) {
|
|
tlsc_info("Tlsc6x:3536 boot not need update!\n");
|
|
return 0;
|
|
}
|
|
|
|
return tlsx6x_update_fcomp_boot(pdata, boot_len);
|
|
}
|
|
|
|
static int tlsc6x_3535boot_update(u8 *pdata, u16 boot_len)
|
|
{
|
|
unsigned int ver = 0;
|
|
|
|
ver = pdata[5];
|
|
ver = (ver << 8) + pdata[4];
|
|
|
|
if (tlsc6x_boot_ver_comp(ver) == 0) {
|
|
tlsc_info("Tlsc6x:3535 boot not need update!\n");
|
|
return 0;
|
|
}
|
|
|
|
g_needKeepRamCode = 1;
|
|
|
|
return tlsc6x_load_ext_binlib(pdata, boot_len);
|
|
}
|
|
|
|
static int tlsc6x_boot_update(u8 *pdata, u16 boot_len)
|
|
{
|
|
|
|
if (g_mccode == 0) {
|
|
return tlsc6x_3535boot_update(pdata, boot_len);
|
|
}
|
|
|
|
return tlsc6x_3536boot_update(pdata, boot_len);
|
|
}
|
|
|
|
int tlsc6x_update_compat_ctl(u8 *pupd, int len)
|
|
{
|
|
u32 k;
|
|
u32 n;
|
|
u32 offset;
|
|
u32 *vlist;
|
|
|
|
struct tlsc6x_updfile_header *upd_header;
|
|
|
|
if (len < sizeof(struct tlsc6x_updfile_header)) {
|
|
return -EPERM;
|
|
}
|
|
|
|
upd_header = (struct tlsc6x_updfile_header *)pupd;
|
|
|
|
if (upd_header->sig != 0x43534843) {
|
|
return -EPERM;
|
|
}
|
|
|
|
n = upd_header->n_cfg;
|
|
offset =
|
|
(upd_header->n_match * 4) + sizeof(struct tlsc6x_updfile_header);
|
|
|
|
if ((offset + upd_header->len_cfg + upd_header->len_boot) != len) {
|
|
return -EPERM;
|
|
}
|
|
|
|
if ((n * 204) != upd_header->len_cfg) {
|
|
return -EPERM;
|
|
}
|
|
|
|
if (n != 0) {
|
|
tlsc6x_upgrade_romcfg_array((u16 *) (pupd + offset), n);
|
|
}
|
|
|
|
n = upd_header->n_match;
|
|
if (n != 0) {
|
|
vlist = (u32 *) (pupd + sizeof(struct tlsc6x_updfile_header));
|
|
offset = offset + upd_header->len_cfg;
|
|
for (k = 0; k < n; k++) {
|
|
if (vlist[k] == (g_tlsc6x_cfg_ver & 0xffffff)) {
|
|
tlsc6x_boot_update((pupd + offset),
|
|
upd_header->len_boot);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void tlsc6x_do_update_ifneed(void)
|
|
{
|
|
const u8 *fupd = tlsc6x_boot_bin;
|
|
|
|
tlsc6x_update_compat_ctl((u8 *) fupd, (int)sizeof(tlsc6x_boot_bin));
|
|
}
|
|
#endif
|
|
|
|
int tlsc6x_get_running_cfg(unsigned short *ptcfg)
|
|
{
|
|
if (g_mccode == 0) {
|
|
return tlsx6x_3535get_running_cfg(ptcfg);
|
|
}
|
|
return tlsx6x_3536get_running_cfg(ptcfg);
|
|
}
|
|
|
|
int tlsc6x_get_os_data(unsigned short *ptdata, int len)
|
|
{
|
|
if (g_mccode == 1) {
|
|
return tlsx6x_3536get_os_data(ptdata, len);
|
|
}
|
|
return -EPERM;
|
|
}
|
|
|
|
int tlsc6x_find_ver(void)
|
|
{
|
|
if (g_mccode == 0) {
|
|
return tlsx6x_3535find_lastvaild_ver();
|
|
}
|
|
return tlsx6x_3536find_lastvaild_ver();
|
|
|
|
}
|
|
|
|
/* NOT a public function, only one caller!!! */
|
|
static void tlsc6x_tp_mccode(void)
|
|
{
|
|
unsigned int tmp[3];
|
|
|
|
g_mccode = 0xff;
|
|
|
|
if (tlsc6x_read_bytes_u16addr(g_tlsc6x_client, 0x8000, (u8 *) tmp, 12)) {
|
|
return;
|
|
}
|
|
if (tmp[2] == 0x544c4e4b) { /* boot code */
|
|
if (tmp[0] == 0x35368008) {
|
|
g_mccode = 1;
|
|
g_tlsc6x_boot_ver = tmp[1] & 0xffff;
|
|
tlsc_boot_version = g_tlsc6x_boot_ver;
|
|
} else if (tmp[0] == 0x35358008) {
|
|
g_mccode = 0;
|
|
g_tlsc6x_boot_ver = tmp[1] & 0xffff;
|
|
tlsc_boot_version = g_tlsc6x_boot_ver;
|
|
}
|
|
tlsc_info("Tlsc6x:get mccode via boot code!\n");
|
|
} else { /* none code */
|
|
tlsc_info("Tlsc6x:get mccode via registers!\n");
|
|
tmp[0] = 0;
|
|
if (tlsc6x_read_bytes_u16addr
|
|
(g_tlsc6x_client, 0x09, (u8 *) tmp, 3)) {
|
|
return;
|
|
}
|
|
if ((tmp[0] == 0x444240) || (tmp[0] == 0x5c5c5c)) {
|
|
g_mccode = 1;
|
|
} else if (tmp[0] == 0x35358008) {
|
|
g_mccode = 0;
|
|
}
|
|
}
|
|
|
|
if (g_mccode == 0) {
|
|
MTK_TXRX_BUF = 0x80cc00;
|
|
CMD_ADDR = 0x80b400;
|
|
RSP_ADDR = 0x80b440;
|
|
//chip_op_if.get_running_cfg = tlsx6x_3535get_running_cfg;
|
|
//chip_op_if.find_ver = tlsx6x_3535find_lastvaild_ver;
|
|
//chip_op_if.update_boot = tlsc6x_3535boot_update;
|
|
} else {
|
|
MTK_TXRX_BUF = 0x809000;
|
|
CMD_ADDR = 0x809f00;
|
|
RSP_ADDR = 0x809f40;
|
|
//chip_op_if.get_running_cfg = tlsx6x_3536get_running_cfg;
|
|
//chip_op_if.find_ver = tlsx6x_3536find_lastvaild_ver;
|
|
//chip_op_if.update_boot = tlsc6x_3536boot_update;
|
|
}
|
|
|
|
}
|
|
|
|
/* is tlsc6x module ? */
|
|
int tlsc6x_tp_dect(struct i2c_client *client)
|
|
{
|
|
TLSC_FUNC_ENTER();
|
|
|
|
g_mccode = 0; /* default */
|
|
g_tlsc6x_client = client;
|
|
|
|
if (tlsc6x_set_dd_mode()) {
|
|
return 0;
|
|
}
|
|
|
|
tlsc6x_tp_mccode(); /* MUST: call this function there!!! */
|
|
tlsc_info("g_mccode is 0x%x\n", g_mccode);
|
|
|
|
if (g_mccode == 0xff) {
|
|
tlsc_err("get mccode fail\n");
|
|
goto exit;
|
|
}
|
|
|
|
/*try to get running time tp-cfg. if fail : wrong boot? wrong rom-cfg? */
|
|
if (tlsc6x_get_running_cfg(tl_buf_tmpcfg) == 0) {
|
|
g_tlsc6x_cfg_ver = (unsigned int)tl_buf_tmpcfg[1];
|
|
g_tlsc6x_cfg_ver = (g_tlsc6x_cfg_ver << 16)
|
|
+ (unsigned int)tl_buf_tmpcfg[0];
|
|
g_tlsc6x_chip_code = (unsigned short)tl_buf_tmpcfg[53];
|
|
|
|
tlsc_cfg_version = g_tlsc6x_cfg_ver >> 26;
|
|
tlsc_vendor_id = (g_tlsc6x_cfg_ver >> 9) & 0x7F;
|
|
} else {
|
|
tlsc6x_find_ver();
|
|
}
|
|
|
|
if (g_tlsc6x_cfg_ver == 0) {
|
|
tlsc_err("get cfg-ver fail\n");
|
|
goto exit;
|
|
}
|
|
#ifdef TLSC_BUILDIN_BOOT
|
|
tlsc6x_do_update_ifneed();
|
|
#endif
|
|
|
|
exit:
|
|
|
|
tlsc6x_set_nor_mode();
|
|
|
|
return 1;
|
|
}
|