520 lines
14 KiB
C
Executable File
520 lines
14 KiB
C
Executable File
#include <linux/fs.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/ccic/s2mm005.h>
|
|
#include <linux/ccic/s2mm005_ext.h>
|
|
#include <linux/ccic/s2mm005_fw.h>
|
|
#include <linux/ccic/ccic_sysfs.h>
|
|
#include <linux/ccic/BOOT_FLASH_FW_0x10_BOOT8.h>
|
|
|
|
#define S2MM005_FIRMWARE_PATH "usbpd/s2mm005.bin"
|
|
|
|
#define FW_CHECK_RETRY 5
|
|
#define VALID_FW_BOOT_VERSION(fw_boot) (fw_boot == 0x8)
|
|
#define VALID_FW_MAIN_VERSION(fw_main) \
|
|
(!((fw_main[0] == 0xff) && (fw_main[1] == 0xff)) \
|
|
&& !((fw_main[0] == 0x00) && (fw_main[1] == 0x00)))
|
|
|
|
const char *flashmode_to_string(u32 mode)
|
|
{
|
|
switch (mode) {
|
|
#define FLASH_MODE_STR(mode) case mode: return(#mode)
|
|
FLASH_MODE_STR(FLASH_MODE_NORMAL);
|
|
FLASH_MODE_STR(FLASH_MODE_FLASH);
|
|
FLASH_MODE_STR(FLASH_MODE_ERASE);
|
|
}
|
|
return "?";
|
|
}
|
|
|
|
void s2mm005_write_flash(const struct i2c_client *i2c,
|
|
unsigned int fAddr, unsigned int fData) {
|
|
u8 data[8];
|
|
|
|
data[0] = 0x42;
|
|
data[1] = 0x04; /* long write */
|
|
|
|
data[2] = (u8)(fAddr & 0xFF);
|
|
data[3] = (u8)((fAddr >> 8) & 0xFF);
|
|
|
|
data[4] = (uint8_t)(fData & 0xFF);
|
|
data[5] = (uint8_t)((fData>>8) & 0xFF);
|
|
|
|
data[6] = (uint8_t)((fData>>16) & 0xFF);
|
|
data[7] = (uint8_t)((fData>>24) & 0xFF);
|
|
/* pr_info("Flash Write Address :0x%08X Data:0x%08X\n", fAddr, fData);*/
|
|
s2mm005_write_byte(i2c, 0x10, &data[0], 8);
|
|
}
|
|
|
|
void s2mm005_verify_flash(const struct i2c_client *i2c,
|
|
uint32_t fAddr, uint32_t *fData) {
|
|
uint16_t REG_ADD;
|
|
uint8_t W_DATA[8];
|
|
uint8_t R_DATA[4];
|
|
|
|
uint32_t fRead[3];
|
|
|
|
uint32_t fCnt;
|
|
|
|
for (fCnt = 0; fCnt < 2; fCnt++) {
|
|
W_DATA[0] = 0x42;
|
|
W_DATA[1] = 0x40; /* Long Read */
|
|
|
|
W_DATA[2] = (uint8_t)(fAddr & 0xFF);
|
|
W_DATA[3] = (uint8_t)((fAddr>>8) & 0xFF);
|
|
|
|
|
|
REG_ADD = 0x10;
|
|
udelay(10);
|
|
s2mm005_write_byte(i2c, REG_ADD, &W_DATA[0], 4);
|
|
REG_ADD = 0x14;
|
|
udelay(10);
|
|
s2mm005_read_byte_flash(i2c, REG_ADD, &R_DATA[0], 4);
|
|
|
|
fRead[fCnt] = 0;
|
|
|
|
fRead[fCnt] |= R_DATA[0];
|
|
fRead[fCnt] |= (R_DATA[1]<<8);
|
|
fRead[fCnt] |= (R_DATA[2]<<16);
|
|
fRead[fCnt] |= (R_DATA[3]<<24);
|
|
|
|
}
|
|
|
|
if (fRead[0] == fRead[1]) {
|
|
*fData = fRead[0];
|
|
/* pr_err("> Flash Read Address : 0x%08X Data : 0x%08X\n",fAddr, *fData); */
|
|
} else {
|
|
W_DATA[0] = 0x42;
|
|
W_DATA[1] = 0x40; /* Long Read */
|
|
|
|
W_DATA[2] = (uint8_t)(fAddr & 0xFF);
|
|
W_DATA[3] = (uint8_t)((fAddr>>8) & 0xFF);
|
|
|
|
|
|
REG_ADD = 0x10;
|
|
udelay(10);
|
|
s2mm005_write_byte(i2c, REG_ADD, &W_DATA[0], 4);
|
|
REG_ADD = 0x14;
|
|
udelay(10);
|
|
s2mm005_read_byte_flash(i2c, REG_ADD, &R_DATA[0], 4);
|
|
|
|
fRead[fCnt] = 0;
|
|
|
|
fRead[2] |= R_DATA[0];
|
|
fRead[2] |= (R_DATA[1]<<8);
|
|
fRead[2] |= (R_DATA[2]<<16);
|
|
fRead[2] |= (R_DATA[3]<<24);
|
|
|
|
if (fRead[2] == fRead[0]) {
|
|
*fData = fRead[0];
|
|
pr_err("> Flash Read[0] Address : 0x%08X Data : 0x%08X\n", fAddr, *fData);
|
|
} else if (fRead[2] == fRead[1]) {
|
|
*fData = fRead[1];
|
|
pr_err("> Flash Read[1] Address : 0x%08X Data : 0x%08X\n", fAddr, *fData);
|
|
} else {
|
|
*fData = 0;
|
|
pr_err("> Flash Read[FAIL] Address : 0x%08X Data : 0x%08X\n", fAddr, *fData);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int s2mm005_flash_write(struct s2mm005_data *usbpd_data, unsigned char *fw_data)
|
|
{
|
|
struct i2c_client *i2c = usbpd_data->i2c;
|
|
u8 val, reg, fError;
|
|
uint32_t *pFlash_FW, *pFlash_FWCS;
|
|
uint32_t LopCnt, fAddr, fData, fRData, sLopCnt;
|
|
uint32_t recheck_count = 0;
|
|
struct s2mm005_fw *fw_hd;
|
|
unsigned int size;
|
|
|
|
reg = FLASH_WRITE_0x42;
|
|
s2mm005_write_byte(i2c, CMD_MODE_0x10, ®, 1);
|
|
reg = FLASH_WRITING_BYTE_SIZE_0x4;
|
|
s2mm005_write_byte(i2c, CMD_HOST_0x11, ®, 1);
|
|
s2mm005_read_byte_flash(i2c, FLASH_STATUS_0x24, &val, 1);
|
|
|
|
pFlash_FW = (uint32_t *)(fw_data + 12);
|
|
pFlash_FWCS = (uint32_t *)fw_data;
|
|
fw_hd = (struct s2mm005_fw*)fw_data;
|
|
size = fw_hd -> size;
|
|
|
|
if(fw_hd -> boot < 6)
|
|
sLopCnt = 0x1000/4;
|
|
else if (fw_hd -> boot == 6)
|
|
sLopCnt = 0x8000/4;
|
|
else if (fw_hd -> boot == 7)
|
|
sLopCnt = 0x7000/4;
|
|
else if (fw_hd -> boot >= 8)
|
|
sLopCnt = 0x5000/4;
|
|
|
|
/* Flash write */
|
|
for (LopCnt = sLopCnt; LopCnt < (size/4); LopCnt++) {
|
|
fAddr = LopCnt*4;
|
|
fData = pFlash_FW[LopCnt];
|
|
udelay(10);
|
|
s2mm005_write_flash(i2c, fAddr, fData);
|
|
}
|
|
usleep_range(10 * 1000, 10 * 1000);
|
|
/* Partial verify */
|
|
while(1) {
|
|
for (LopCnt = sLopCnt; LopCnt < (size/4); LopCnt++) {
|
|
fError = 1;
|
|
fAddr = LopCnt*4;
|
|
fData = pFlash_FW[LopCnt];
|
|
s2mm005_verify_flash(i2c, fAddr, &fRData);
|
|
if (fData != fRData) {
|
|
recheck_count++;
|
|
LopCnt = (fAddr & 0xffffff00) / 4;
|
|
sLopCnt = LopCnt;
|
|
fError = 0;
|
|
pr_err("%s partial verify fail!! recheck count : %d\n", __func__, recheck_count);
|
|
pr_err("Verify Error Address = 0x%08X WData = 0x%08X VData = 0x%08X\n", fAddr, fData, fRData);
|
|
s2mm005_write_flash(i2c, fAddr, fData);
|
|
msleep(20);
|
|
if(recheck_count == 1000)
|
|
return -EFLASH_VERIFY;
|
|
break;
|
|
}
|
|
}
|
|
if(fError)
|
|
break;
|
|
}
|
|
pr_err("%s verify success!! recheck count : %d\n", __func__, recheck_count);
|
|
|
|
if (LopCnt >= (size / 4)) {
|
|
if (fw_hd -> boot >= 6) {
|
|
/* SW version from header */
|
|
recheck_count = 0;
|
|
while(1) {
|
|
recheck_count++;
|
|
fAddr = 0xEFF0;
|
|
pr_err("SW version = 0x%08X\n", pFlash_FWCS[0]);
|
|
fData = pFlash_FWCS[0];
|
|
s2mm005_write_flash(i2c, fAddr, fData);
|
|
fRData = 0;
|
|
s2mm005_verify_flash(i2c, fAddr, &fRData);
|
|
if(fData == fRData)
|
|
break;
|
|
else {
|
|
if (recheck_count == 30)
|
|
return -EFLASH_WRITE_SWVERSION;
|
|
}
|
|
}
|
|
/* Size from header */
|
|
recheck_count = 0;
|
|
while(1) {
|
|
recheck_count++;
|
|
fAddr = 0xEFF4;
|
|
pr_err("SW Size = 0x%08X\n", pFlash_FWCS[2]);
|
|
fData = pFlash_FWCS[2];
|
|
s2mm005_write_flash(i2c, fAddr, fData);
|
|
fRData = 0;
|
|
s2mm005_verify_flash(i2c, fAddr, &fRData);
|
|
if(fData == fRData)
|
|
break;
|
|
else {
|
|
if (recheck_count == 30)
|
|
return -EFLASH_WRITE_SIZE;
|
|
}
|
|
}
|
|
/* CRC Check sum */
|
|
recheck_count = 0;
|
|
while(1) {
|
|
recheck_count++;
|
|
fAddr = 0xEFF8;
|
|
pr_err("SW CheckSum = 0x%08X\n", pFlash_FWCS[((size + 16) / 4) - 1]);
|
|
fData = pFlash_FWCS[((size + 16) / 4) - 1];
|
|
s2mm005_write_flash(i2c, fAddr, fData);
|
|
fRData = 0;
|
|
s2mm005_verify_flash(i2c, fAddr, &fRData);
|
|
if(fData == fRData)
|
|
break;
|
|
else {
|
|
if (recheck_count == 30)
|
|
return -EFLASH_WRITE_CRC;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* flash done */
|
|
recheck_count = 0;
|
|
while(1)
|
|
{
|
|
recheck_count++;
|
|
fAddr = 0xEFFC;
|
|
fData = 0x1;
|
|
s2mm005_write_flash(i2c, fAddr, fData);
|
|
fRData = 0;
|
|
s2mm005_verify_flash(i2c, fAddr, &fRData);
|
|
pr_err("0xeffc = %d\n", fRData);
|
|
if(fData == fRData)
|
|
break;
|
|
else {
|
|
if (recheck_count == 30)
|
|
return -EFLASH_WRITE_DONE;
|
|
}
|
|
}
|
|
pr_err("%s flash write succesfully done!!\n", __func__);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void s2mm005_flash_ready(struct s2mm005_data *usbpd_data)
|
|
{
|
|
struct i2c_client *i2c = usbpd_data->i2c;
|
|
u8 W_DATA[5];
|
|
|
|
/* FLASH_READY */
|
|
W_DATA[0] = 0x02;
|
|
W_DATA[1] = 0x01;
|
|
W_DATA[2] = 0x30;
|
|
W_DATA[3] = 0x50;
|
|
W_DATA[4] = 0x01;
|
|
s2mm005_write_byte(i2c, CMD_MODE_0x10, &W_DATA[0], 5);
|
|
}
|
|
|
|
int s2mm005_flash(struct s2mm005_data *usbpd_data, unsigned int input)
|
|
{
|
|
struct i2c_client *i2c = usbpd_data->i2c;
|
|
u8 val, reg;
|
|
int ret = 0;
|
|
int retry = 0;
|
|
struct s2mm005_fw *fw_hd;
|
|
struct file *fp;
|
|
mm_segment_t old_fs;
|
|
long fw_size, nread;
|
|
int irq_gpio_status;
|
|
FLASH_STATE_Type Flash_DATA;
|
|
|
|
switch (input) {
|
|
case FLASH_MODE_ENTER: { /* enter flash mode */
|
|
/* FLASH_READY */
|
|
s2mm005_flash_ready(usbpd_data);
|
|
do {
|
|
/* FLASH_MODE */
|
|
reg = FLASH_MODE_ENTER_0x10;
|
|
s2mm005_write_byte(i2c, CMD_MODE_0x10, ®, 1);
|
|
usleep_range(50 * 1000, 50 * 1000);
|
|
/* If irq status is not clear, CCIC can not enter flash mode. */
|
|
irq_gpio_status = gpio_get_value(usbpd_data->irq_gpio);
|
|
dev_info(&i2c->dev, "%s IRQ0:%02d\n", __func__, irq_gpio_status);
|
|
if(!irq_gpio_status) {
|
|
s2mm005_int_clear(usbpd_data); // interrupt clear
|
|
usleep_range(10 * 1000, 10 * 1000);
|
|
}
|
|
s2mm005_read_byte_flash(i2c, FLASH_STATUS_0x24, &val, 1);
|
|
pr_err("%s %s retry %d\n", __func__, flashmode_to_string(val), retry);
|
|
usleep_range(50*1000, 50*1000);
|
|
|
|
s2mm005_read_byte(i2c, 0x24, Flash_DATA.BYTE, 4);
|
|
dev_info(&i2c->dev, "Flash_State:0x%02X Reserved:0x%06X\n",
|
|
Flash_DATA.BITS.Flash_State, Flash_DATA.BITS.Reserved);
|
|
|
|
if(val != FLASH_MODE_FLASH) {
|
|
retry++;
|
|
if(retry == 10) {
|
|
/* RESET */
|
|
s2mm005_reset(usbpd_data);
|
|
msleep(3000);
|
|
/* FLASH_READY */
|
|
s2mm005_flash_ready(usbpd_data);
|
|
} else if (retry == 20) {
|
|
panic("Flash mode change fail!\n");
|
|
}
|
|
}
|
|
} while (val != FLASH_MODE_FLASH);
|
|
break;
|
|
}
|
|
case FLASH_ERASE: { /* erase flash */
|
|
reg = FLASH_ERASE_0x44;
|
|
usleep_range(10 * 1000, 10 * 1000);
|
|
s2mm005_write_byte(i2c, CMD_MODE_0x10, ®, 1);
|
|
msleep(200);
|
|
s2mm005_read_byte_flash(i2c, FLASH_STATUS_0x24, &val, 1);
|
|
pr_err("flash mode : %s\n", flashmode_to_string(val));
|
|
break;
|
|
}
|
|
case FLASH_WRITE8: { /* write flash & verify */
|
|
switch (usbpd_data->s2mm005_fw_product_id) {
|
|
case PRODUCT_NUM_VIEW2:
|
|
ret = s2mm005_flash_write(usbpd_data, (unsigned char*)&BOOT_FLASH_FW_0x10_BOOT8[0]);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case FLASH_WRITE_UMS: {
|
|
old_fs = get_fs();
|
|
set_fs(KERNEL_DS);
|
|
fp = filp_open(CCIC_DEFAULT_UMS_FW, O_RDONLY, S_IRUSR);
|
|
if (IS_ERR(fp)) {
|
|
pr_err("%s: failed to open %s.\n", __func__,
|
|
CCIC_DEFAULT_UMS_FW);
|
|
ret = -ENOENT;
|
|
set_fs(old_fs);
|
|
return ret;
|
|
}
|
|
|
|
fw_size = fp->f_path.dentry->d_inode->i_size;
|
|
if (0 < fw_size) {
|
|
unsigned char *fw_data;
|
|
fw_data = kzalloc(fw_size, GFP_KERNEL);
|
|
nread = vfs_read(fp, (char __user *)fw_data,
|
|
fw_size, &fp->f_pos);
|
|
|
|
pr_err("%s: start, file path %s, size %ld Bytes\n",
|
|
__func__, CCIC_DEFAULT_UMS_FW, fw_size);
|
|
|
|
if (nread != fw_size) {
|
|
pr_err("%s: failed to read firmware file, nread %ld Bytes\n",
|
|
__func__, nread);
|
|
ret = -EIO;
|
|
} else {
|
|
fw_hd = (struct s2mm005_fw*)fw_data;
|
|
pr_err("%02X %02X %02X %02X size:%05d\n", fw_hd->boot, fw_hd->main[0], fw_hd->main[1], fw_hd->main[2], fw_hd->size);
|
|
/* TODO : DISABLE IRQ */
|
|
/* TODO : FW UPDATE */
|
|
ret = s2mm005_flash_write(usbpd_data, (unsigned char*)fw_data);
|
|
/* TODO : ENABLE IRQ */
|
|
}
|
|
kfree(fw_data);
|
|
}
|
|
|
|
filp_close(fp, NULL);
|
|
set_fs(old_fs);
|
|
break;
|
|
}
|
|
case FLASH_MODE_EXIT: { /* exit flash mode */
|
|
reg = FLASH_MODE_EXIT_0x20;
|
|
s2mm005_write_byte(i2c, CMD_MODE_0x10, ®, 1);
|
|
usleep_range(15 * 1000, 15 * 1000);
|
|
s2mm005_read_byte_flash(i2c, FLASH_STATUS_0x24, &val, 1);
|
|
pr_err("flash mode : %s\n", flashmode_to_string(val));
|
|
break;
|
|
}
|
|
default: {
|
|
pr_err("Flash value does not matched menu\n");
|
|
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void s2mm005_get_fw_version(int product_id,
|
|
struct s2mm005_version *version, u8 boot_version, u32 hw_rev)
|
|
{
|
|
struct s2mm005_fw *fw_hd;
|
|
|
|
switch (product_id) {
|
|
case PRODUCT_NUM_VIEW2:
|
|
default:
|
|
fw_hd = (struct s2mm005_fw*) BOOT_FLASH_FW_0x10_BOOT8;
|
|
break;
|
|
}
|
|
version->boot = fw_hd->boot;
|
|
version->main[0] = fw_hd->main[0];
|
|
version->main[1] = fw_hd->main[1];
|
|
version->main[2] = fw_hd->main[2];
|
|
}
|
|
|
|
void s2mm005_get_chip_hwversion(struct s2mm005_data *usbpd_data,
|
|
struct s2mm005_version *version)
|
|
{
|
|
struct i2c_client *i2c = usbpd_data->i2c;
|
|
|
|
s2mm005_read_byte_flash(i2c, 0x0, (u8 *)&version->boot, 1);
|
|
s2mm005_read_byte_flash(i2c, 0x1, (u8 *)&version->main, 3);
|
|
s2mm005_read_byte_flash(i2c, 0x4, (u8 *)&version->ver2, 4);
|
|
}
|
|
|
|
void s2mm005_get_chip_swversion(struct s2mm005_data *usbpd_data,
|
|
struct s2mm005_version *version)
|
|
{
|
|
struct i2c_client *i2c = usbpd_data->i2c;
|
|
int i;
|
|
|
|
for(i=0; i < FW_CHECK_RETRY; i++) {
|
|
s2mm005_read_byte_flash(i2c, 0x8, (u8 *)&version->boot, 1);
|
|
if(VALID_FW_BOOT_VERSION(version->boot))
|
|
break;
|
|
}
|
|
for(i=0; i < FW_CHECK_RETRY; i++) {
|
|
s2mm005_read_byte_flash(i2c, 0x9, (u8 *)&version->main, 3);
|
|
if(VALID_FW_MAIN_VERSION(version->main))
|
|
break;
|
|
}
|
|
for (i = 0; i < FW_CHECK_RETRY; i++)
|
|
s2mm005_read_byte_flash(i2c, 0xc, (u8 *)&version->ver2, 4);
|
|
}
|
|
|
|
int s2mm005_check_version(struct s2mm005_version *version1,
|
|
struct s2mm005_version *version2)
|
|
{
|
|
if (version1->boot != version2->boot) {
|
|
return FLASH_FW_VER_BOOT;
|
|
}
|
|
if (memcmp(version1->main, version2->main, 3)) {
|
|
return FLASH_FW_VER_MAIN;
|
|
}
|
|
|
|
return FLASH_FW_VER_MATCH;
|
|
}
|
|
|
|
int s2mm005_flash_fw(struct s2mm005_data *usbpd_data, unsigned int input)
|
|
{
|
|
int ret = 0;
|
|
u8 val = 0;
|
|
|
|
if( usbpd_data->fw_product_id != usbpd_data->s2mm005_fw_product_id)
|
|
{
|
|
pr_err("FW_UPDATE fail, product number is different (%d)(%d) \n", usbpd_data->fw_product_id,usbpd_data->s2mm005_fw_product_id);
|
|
return 0;
|
|
}
|
|
|
|
pr_err("FW_UPDATE %d\n", input);
|
|
switch (input) {
|
|
case FLASH_WRITE8:
|
|
s2mm005_flash(usbpd_data, FLASH_MODE_ENTER);
|
|
usleep_range(10 * 1000, 10 * 1000);
|
|
s2mm005_flash(usbpd_data, FLASH_ERASE);
|
|
msleep(200);
|
|
ret = s2mm005_flash(usbpd_data, input);
|
|
if (ret < 0)
|
|
return ret;
|
|
usleep_range(10 * 1000, 10 * 1000);
|
|
s2mm005_flash(usbpd_data, FLASH_MODE_EXIT);
|
|
usleep_range(10 * 1000, 10 * 1000);
|
|
s2mm005_reset(usbpd_data);
|
|
usleep_range(10 * 1000, 10 * 1000);
|
|
break;
|
|
case FLASH_WRITE_UMS:
|
|
s2mm005_read_byte_flash(usbpd_data->i2c, FLASH_STATUS_0x24, &val, 1);
|
|
if(val != FLASH_MODE_NORMAL) {
|
|
pr_err("Can't CCIC FW update: cause by %s\n", flashmode_to_string(val));
|
|
}
|
|
disable_irq(usbpd_data->irq);
|
|
s2mm005_manual_LPM(usbpd_data, 0x7); // LP Off
|
|
msleep(3000);
|
|
s2mm005_flash(usbpd_data, FLASH_MODE_ENTER);
|
|
usleep_range(10 * 1000, 10 * 1000);
|
|
s2mm005_flash(usbpd_data, FLASH_ERASE);
|
|
msleep(200);
|
|
ret = s2mm005_flash(usbpd_data, input);
|
|
if (ret < 0)
|
|
panic("infinite write fail!\n");
|
|
usleep_range(10 * 1000, 10 * 1000);
|
|
s2mm005_flash(usbpd_data, FLASH_MODE_EXIT);
|
|
usleep_range(10 * 1000, 10 * 1000);
|
|
s2mm005_reset(usbpd_data);
|
|
usleep_range(10 * 1000, 10 * 1000);
|
|
s2mm005_manual_LPM(usbpd_data, 0x6); // LP On
|
|
enable_irq(usbpd_data->irq);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|