以MELFAS的MS6000芯片固件升级实例,说明一般的数组格式的IMAGE文件烧录的过程。该芯片需要PIN脚组合完成一定的时序并实现一组命令码进入烧录模式,并且在烧录模式下的I2C地址是0XFA(跟芯片正常工作时的地址0X40不同),在烧录完毕后进行复位并开始相应正常的TP操作。芯片大致的烧录了流程图如下:
实现代码如下:
(1)基础宏定义
#define FIAMWARE_NAME "MELFAS_W105.h" //数组格式IMAGE的名字
static unsigned char MS6000CTPM_FW[] = //以数组划分空间存储烧录映象
{
#include FIAMWARE_NAME
};
#define MS6000_ADDR_MODULE_REVISION 0x98
#define MS6000_ADDR_FIRMWARE_VERSION 0x9C //烧录IMAGE中对应的特定字节地址
#define MS6000_TRANSFER_LENGTH 64 //一次烧录的packet字节数
/*ISP command*/
#define MS6000_ISP_CMD_ERASE 0x02
#define MS6000_ISP_CMD_ERASE_TIMING 0x0F
#define MS6000_ISP_CMD_PROGRAM_FLASH 0x03
#define MS6000_ISP_CMD_READ_FLASH 0x04
#define MS6000_ISP_CMD_PROGRAM_TIMING 0x0F
#define MS6000_ISP_CMD_READ_INFORMATION 0x06
#define MS6000_ISP_CMD_RESET 0x07
#define MS6000_7BIT_DOWNLOAD_ADDR 0x7D
#define MS6000_8BIT_DOWNLOAD_ADDR (MS6000_7BIT_DOWNLOAD_ADDR<<1) //linux的i2Cclient需8位地址
#define MS6000_I2C_SLAVE_READY_STATUS 0x55
// MCS6000's responses
#define MS6000_ISP_ACK_ERASE_DONE 0x82
#define MS6000_ISP_ACK_PREPARE_ERASE_DONE 0x8F
#define MS6000_I2C_ACK_PREPARE_PROGRAM 0x8F
#define MS6000_MDS_ACK_PROGRAM_FLASH 0x83
#define MS6000_MDS_ACK_READ_FLASH 0x84
#define MS6000_MDS_ACK_PROGRAM_INFORMATION 0x88
#define MS6000_MDS_ACK_PROGRAM_LOCKED 0xFE
#define MS6000_MDS_ACK_READ_LOCKED 0xFE
#define MS6000_MDS_ACK_FAIL 0xFE
#define MS6000_ISP_ERASE_TIMING_VALUE_0 0x01
#define MS6000_ISP_ERASE_TIMING_VALUE_1 0xD4
#define MS6000_ISP_ERASE_TIMING_VALUE_2 0xC0
#define MS6000_ISP_PROGRAM_TIMING_VALUE_0 0x00
#define MS6000_ISP_PROGRAM_TIMING_VALUE_1 0x00
#define MS6000_ISP_PROGRAM_TIMING_VALUE_2 0x78
(2)I2C烧写和读TP FLASH的函数
注意:该芯片在烧录模式下的单字节读操作和写操作都不需要寄存器地址,只send芯片地址就行。里面用到的i2c_client->address已变换地址。
static bool mfs_i2c_write_single_byte(unsigned char bufVal)
{
int ret;
unsigned char buf;
buf = bufVal;
ret = i2c_master_send(i2c_client, &buf, 1);
if(ret <= 0){
printk("mfs_i2c_write_single_byte error line = %d, ret = %d\n", __LINE__, ret);
return false;
}
return true;
}
static bool mfs_i2c_read_single_byte(unsigned char *buf)
{
int ret;
ret = i2c_master_recv(i2c_client, buf, 1);
if(ret <= 0){
printk("mfs_i2c_read_single_byte error line = %d, ret = %d\n", __LINE__, ret);
return false;
}
return true;
}
static int mfs_i2c_read_flash(unsigned char *pBuffer,UINT16 nAddr_start,unsigned char cLength)
{ //将nAddr_start开始的cLength个字节读到pBuffer所指的空间中
int nRet = MS6000_RET_READ_FLASH_FAILED,i;
BOOL bRet;
unsigned char cmd[4],ucTemp;
// Send Read Flash command [ Read code - address high - address low - size ]
cmd[0] = MS6000_ISP_CMD_READ_FLASH;
cmd[1] = (UINT8)((nAddr_start >> 8 ) & 0xFF);
cmd[2] = (UINT8)((nAddr_start ) & 0xFF);
cmd[3] = cLength;
for(i=0;i<4;i++){
bRet = mfs_i2c_write_single_byte(cmd[i]);
udelay(15);
if(bRet == FALSE)
goto MS6000_I2C_READ_FLASH_FINISH;
}
// Read 'Result of command'
bRet = mfs_i2c_read_single_byte(&ucTemp);
if( !bRet || ucTemp != MS6000_MDS_ACK_READ_FLASH){
goto MS6000_I2C_READ_FLASH_FINISH;
}
// Read Data [ pCmd[3] == Size ]
for(i=0; i<(int)cmd[3]; i++){
udelay(100);
bRet = mfs_i2c_read_single_byte(pBuffer++);
if( bRet == FALSE && i!=(int)(cmd[3]-1) )
goto MS6000_I2C_READ_FLASH_FINISH;
}
nRet = MS6000_RET_SUCCESS;
MS6000_I2C_READ_FLASH_FINISH:
return nRet;
}
(3)进入download功能
static void ms6000_write_download_mode_signal(void) //通过RESET脚和EINT脚发出一组组合电平
{
int i;
unsigned char enter_code[14] = { 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1 };
for(i=0; i<14; i++){
if(enter_code[i]){
mt_set_gpio_out(GPIO_CTP_RST_PIN, GPIO_OUT_ONE);
mt_set_gpio_out(GPIO_CTP_EINT_PIN, GPIO_OUT_ONE);
}
else{
mt_set_gpio_out(GPIO_CTP_RST_PIN, GPIO_OUT_ZERO);
mt_set_gpio_out(GPIO_CTP_EINT_PIN, GPIO_OUT_ZERO);
}
mt_set_gpio_out(GPIO_I2C0_SCA_PIN, GPIO_OUT_ONE);
udelay(15);
mt_set_gpio_out(GPIO_I2C0_SCA_PIN, GPIO_OUT_ZERO);
mt_set_gpio_out(GPIO_CTP_RST_PIN, GPIO_OUT_ZERO);
mt_set_gpio_out(GPIO_CTP_EINT_PIN, GPIO_OUT_ZERO);
udelay(100);
}
mt_set_gpio_out(GPIO_I2C0_SCA_PIN, GPIO_OUT_ONE);
udelay(100);
mt_set_gpio_out(GPIO_CTP_EINT_PIN, GPIO_OUT_ONE);
mt_set_gpio_out(GPIO_CTP_RST_PIN, GPIO_OUT_ONE);
}
static int ms6000_enter_download_mode(void)
{
bool bRet;
int nRet = MS6000_RET_ENTER_DOWNLOAD_MODE_FAILED;
unsigned char cData=0;
hwPowerDown(MT65XX_POWER_LDO_VGP2, "TP"); //TKEY_VDD_SET_LOW();
mt_set_gpio_mode(GPIO_I2C0_SCA_PIN, GPIO_I2C0_SCA_PIN_M_GPIO);
mt_set_gpio_dir(GPIO_I2C0_SCA_PIN, GPIO_DIR_OUT);
mt_set_gpio_out(GPIO_I2C0_SCA_PIN, GPIO_OUT_ZERO);
mt_set_gpio_mode(GPIO_I2C0_SDA_PIN, GPIO_I2C0_SDA_PIN_M_GPIO);
mt_set_gpio_dir(GPIO_I2C0_SDA_PIN, GPIO_DIR_OUT);
mt_set_gpio_out(GPIO_I2C0_SDA_PIN, GPIO_OUT_ZERO); //I2C变GPIO功能
mt_set_gpio_mode(GPIO_CTP_EINT_PIN, GPIO_CTP_EINT_PIN_M_GPIO);
mt_set_gpio_dir(GPIO_CTP_EINT_PIN, GPIO_DIR_OUT); //TKEY_INTR_SET_OUTPUT();
mt_set_gpio_out(GPIO_CTP_EINT_PIN, GPIO_OUT_ZERO); //TKEY_INTR_SET_LOW();
mt_set_gpio_mode(GPIO_CTP_RST_PIN, GPIO_CTP_RST_PIN_M_GPIO);
mt_set_gpio_dir(GPIO_CTP_RST_PIN, GPIO_DIR_OUT); //TKEY_RESETB_SET_OUTPUT();
mt_set_gpio_out(GPIO_CTP_RST_PIN, GPIO_OUT_ZERO); //若干功能脚均需完成电平拉低的作用
mdelay(90); //Delay for Stable VDD
hwPowerOn(MT65XX_POWER_LDO_VGP2, VOL_2800, "TP"); //TKEY_VDD_SET_HIGH();
mt_set_gpio_out(GPIO_CTP_RST_PIN, GPIO_OUT_ONE); //TKEY_CE_SET_HIGH();
mt_set_gpio_out(GPIO_I2C0_SDA_PIN, GPIO_OUT_ONE); //TKEY_I2C_SDA_SET_HIGH();
mdelay(25);
ms6000_write_download_mode_signal(); //写命令码
mt_set_gpio_mode(GPIO_I2C0_SCA_PIN, GPIO_I2C0_SCA_PIN_M_SCL);
mt_set_gpio_mode(GPIO_I2C0_SDA_PIN, GPIO_I2C0_SDA_PIN_M_SDA); //使能I2C的PIN脚恢复I2C功能
mdelay(2);
bRet = mfs_i2c_read_single_byte(&cData);
if( bRet != TRUE || cData != MS6000_I2C_SLAVE_READY_STATUS ){
goto MS6000_ENTER_DOWNLOAD_MODE_FINISH;
}
else
printk("respond from download mode commande is 0x55 \r\n"); //只有芯片状态返回0x55,才说明进入DOWNLOAD模式
nRet = MS6000_RET_SUCCESS; //Entering MDS ISP mode finished.
MS6000_ENTER_DOWNLOAD_MODE_FINISH:
return nRet;
}
(4)芯片复位和TP FLASH擦除
static void ms6000_reset_command(void) //不管是升级过程失败还是成功,最后都需要复位并使能芯片
{
unsigned char buf;
mdelay(1);
buf = MS6000_ISP_CMD_RESET;
if(mfs_i2c_write_single_byte(buf) == true)
printk("mfs6000_reset_command reset success \r\n");
mt_set_gpio_mode(GPIO_CTP_EINT_PIN, GPIO_CTP_EINT_PIN_M_EINT);
mt_set_gpio_dir(GPIO_CTP_EINT_PIN, GPIO_DIR_IN);
mt_set_gpio_pull_enable(GPIO_CTP_EINT_PIN, GPIO_PULL_ENABLE);
mt_set_gpio_pull_select(GPIO_CTP_EINT_PIN, GPIO_PULL_UP); //恢复EINT的pin功能
mdelay(180);
}
static unsigned char ms6000_GetLibVer(void) //取得新固件中的版本号,以便比较
{
unsigned int sz;
sz = sizeof(MS6000CTPM_FW);
if(sz > 2){
return (MS6000CTPM_FW[157]-0x30); //版本号字节数据
}
else{
return 0xff;
}
}
static int ms6000_i2c_prepare_erase_flash(void)
{
int nRet = MS6000_RET_PREPARE_ERASE_FLASH_FAILED,i;
BOOL bRet;
UINT8 i2c_buffer[4] = { MS6000_ISP_CMD_ERASE_TIMING,
MS6000_ISP_ERASE_TIMING_VALUE_0,
MS6000_ISP_ERASE_TIMING_VALUE_1,
MS6000_ISP_ERASE_TIMING_VALUE_2 };
UINT8 ucTemp;
// Send Erase Setting code
for(i=0; i<4; i++){
bRet = mfs_i2c_write_single_byte(i2c_buffer[i]);
if( !bRet ){
goto MS6000_I2C_PREPARE_ERASE_FLASH_FINISH;
}
udelay(15);
}
// Read Result
udelay(500);
bRet = mfs_i2c_read_single_byte(&ucTemp);
if( bRet && ucTemp == MS6000_ISP_ACK_PREPARE_ERASE_DONE ){
nRet = MS6000_RET_SUCCESS;
}
MS6000_I2C_PREPARE_ERASE_FLASH_FINISH:
return nRet;
}
static int ms6000_i2c_erase_flash(void)
{
int nRet = MS6000_RET_ERASE_FLASH_FAILED,i;
BOOL bRet;
UINT8 i2c_buffer[1] = { MS6000_ISP_CMD_ERASE};
UINT8 ucTemp;
// Send Erase code
for(i=0; i<1; i++){
bRet = mfs_i2c_write_single_byte(i2c_buffer[i]);
if( !bRet )
goto MS6000_I2C_ERASE_FLASH_FINISH;
udelay(15);
}
// Read Result
mdelay(45);
bRet = mfs_i2c_read_single_byte(&ucTemp);
if( bRet && ucTemp == MS6000_ISP_ACK_ERASE_DONE ){
nRet = MS6000_RET_SUCCESS;
}
MS6000_I2C_ERASE_FLASH_FINISH:
return nRet;
}
(5)预编程和编程函数,以及download映象主函数
static int ms6000_i2c_prepare_program(void)
{
int nRet = MS6000_RET_PREPARE_PROGRAM_FAILED,i;
BOOL bRet;
UINT8 i2c_buffer[4] = { MS6000_ISP_CMD_PROGRAM_TIMING,
MS6000_ISP_PROGRAM_TIMING_VALUE_0,
MS6000_ISP_PROGRAM_TIMING_VALUE_1,
MS6000_ISP_PROGRAM_TIMING_VALUE_2};
// Write Program timing information
for(i=0; i<4; i++){
bRet = mfs_i2c_write_single_byte(i2c_buffer[i]);
if( bRet == FALSE )
goto MS6000_I2C_PREPARE_PROGRAM_FINISH;
udelay(15);
}
udelay(500);
// Read command's result
bRet = mfs_i2c_read_single_byte(&i2c_buffer[4]);
if( bRet == FALSE || i2c_buffer[4] != MS6000_I2C_ACK_PREPARE_PROGRAM)
goto MS6000_I2C_PREPARE_PROGRAM_FINISH;
mdelay(100);
nRet = MS6000_RET_SUCCESS;
MS6000_I2C_PREPARE_PROGRAM_FINISH:
return nRet;
}
static int ms6000_i2c_program_flash( UINT8 *pData, UINT16 nAddr_start, UINT8 cLength ) //FLASH one packet编程主函数
{ //参数是待编程数据,写入地址,待编程数据长度
int nRet = MS6000_RET_PROGRAM_FLASH_FAILED;
int i,j;
BOOL bRet;
UINT8 cData;
UINT8 tmp;
UINT8 cmd[4];
// Send program code
cmd[0] = MS6000_ISP_CMD_PROGRAM_FLASH;
cmd[1] = (UINT8)((nAddr_start >> 8 ) & 0xFF);
cmd[2] = (UINT8)((nAddr_start ) & 0xFF);
cmd[3] = cLength;
for(i=0; i<4; i++){
bRet = mfs_i2c_write_single_byte(cmd[i]);
udelay(15);
if( bRet == FALSE )
goto MS6000_I2C_PROGRAM_FLASH_FINISH;
}
// Check command result
bRet = mfs_i2c_read_single_byte(&cData);
if( bRet == FALSE || cData != MS6000_MDS_ACK_PROGRAM_FLASH ){
goto MS6000_I2C_PROGRAM_FLASH_FINISH;
}
// Program Data
udelay(150);
for(i=0; i<(int)cmd[3]; i+=2){ //一次写入两个字节,先写高位,再写低位
bRet = mfs_i2c_write_single_byte(pData[i+1]);
if( bRet == FALSE )
goto MS6000_I2C_PROGRAM_FLASH_FINISH;
udelay(100); // Delay about 150us
bRet = mfs_i2c_write_single_byte(pData[i]);
udelay(150); // Delay about 150us
if( bRet == FALSE )
goto MS6000_I2C_PROGRAM_FLASH_FINISH;
}
nRet = MS6000_RET_SUCCESS;
MS6000_I2C_PROGRAM_FLASH_FINISH:
return nRet;
}
static int ms6000_download(const UINT8 *pData, const UINT16 nLength ) //download主函数,参数是映象内存首地址及映象长度
{
int i,nRet;
unsigned char cLength,buffer[MS6000_TRANSFER_LENGTH];
uint16_t nStart_address=0;
unsigned char *pOriginal_data;
//enter in download mode
nRet = ms6000_enter_download_mode();
if(nRet != MS6000_RET_SUCCESS)
goto MS6000_DOWNLOAD_FINISH;
mdelay(1);
// Erase Flash
nRet = ms6000_i2c_prepare_erase_flash();
if(nRet !=MS6000_RET_SUCCESS){
goto MS6000_DOWNLOAD_FINISH;
}
mdelay(1);
nRet = ms6000_i2c_erase_flash();
if(nRet !=MS6000_RET_SUCCESS)
goto MS6000_DOWNLOAD_FINISH;
mdelay(1);
// Verify erase
nRet = mfs_i2c_read_flash( buffer, 0x00, 16 ); // Must be '0xFF' after erase
if( nRet != MS6000_RET_SUCCESS )
goto MS6000_DOWNLOAD_FINISH;
for(i=0; i<16; i++){
if( buffer[i] != 0xFF ){
nRet = MS6000_RET_ERASE_VERIFY_FAILED;
goto MS6000_DOWNLOAD_FINISH;
}
}
mdelay(1);
// Prepare for Program flash.
nRet = ms6000_i2c_prepare_program();
if( nRet != MS6000_RET_SUCCESS )
goto MS6000_DOWNLOAD_FINISH;
mdelay(1);
// Program flash
#if 1
pOriginal_data = (UINT8 *)pData; //保留原始首地址
nStart_address = 0; //烧录起始地址
cLength = MS6000_TRANSFER_LENGTH; //一次烧录长度,64B
for( nStart_address = 0; nStart_address < nLength; nStart_address+=cLength ){
if( ( nLength - nStart_address ) < MS6000_TRANSFER_LENGTH ){
cLength = (UINT8)(nLength - nStart_address);
cLength += (cLength%2); // For odd length.最后不足64B的,补上1字节当偶数处理,因为以WORD烧录
}
nRet = ms6000_i2c_program_flash( pOriginal_data, nStart_address, cLength );
if( nRet != MS6000_RET_SUCCESS ){
goto MS6000_DOWNLOAD_FINISH;
}
pOriginal_data += cLength;
udelay(500);
printk("#");
}
printk("mfs6000 program finished \r\n");
#endif
// Verify flash
#if 1
pOriginal_data = (UINT8 *) pData; //保留原始首地址
nStart_address = 0;
cLength = MS6000_TRANSFER_LENGTH;
for( nStart_address = 0; nStart_address < nLength; nStart_address+=cLength ){
if( ( nLength - nStart_address ) < MS6000_TRANSFER_LENGTH ){
cLength = (UINT8)(nLength - nStart_address);
cLength += (cLength%2); // For odd length.
}
// Read flash
nRet = mfs_i2c_read_flash( buffer, nStart_address, cLength );
// Comparing
for(i=0; i<(int)cLength; i++){
if( buffer[i] != pOriginal_data[i] ){ //如果读出的对应地址字节与原始对应地址数据不同
nRet = MS6000_RET_PROGRAM_VERIFY_FAILED;
goto MS6000_DOWNLOAD_FINISH;
}
}
pOriginal_data += cLength;
udelay(500);
printk("*");
}
printk("mfs6000 Verify finished \r\n");
#endif
nRet = MS6000_RET_SUCCESS;
MS6000_DOWNLOAD_FINISH:
ms6000_reset_command();
return nRet;
}
(6)升级主函数,以及TP probe函数中的处理
static int ms6000_firmware_upgrade()
{
unsigned char NewFwVersion,OldFwVersion;
uint16_t nBinary_length = 0;
int nRead = 0;
unsigned char *ptrBuff = NULL;
int ret = MS6000_RET_FILE_ACCESS_FAILED;
if(mfs_i2c_read_single_reg(0x21,&OldFwVersion) == true){ //在此之前,TP的供电及初始化一定要有,否则读不出来
NewFwVersion = ms6000_GetLibVer();
printk("mfs6000 OldFwVersion is %d,and NewFwVersion is %d \r\n",OldFwVersion,NewFwVersion);
}
i2c_client->addr = MS6000_8BIT_DOWNLOAD_ADDR; //变换成TP的升级I2C地址
if(NewFwVersion != OldFwVersion){ //如果版本号不同就升级
ptrBuff = MS6000CTPM_FW;
nBinary_length = sizeof(MS6000CTPM_FW);
//download process
printk("start download \r\n");
mtk_wdt_disable();
ret = ms6000_download(ptrBuff,nBinary_length);
mtk_wdt_get_en_setting(); //升级前后必须有禁止WDT和使能WDT的动作,否则易重启
//check process
}
else{ //如果版本号相同则不动作
printk("because of the same lib, update abort!\r\n");
}
return ret;
}
//如下是TPD_RROBE中的改动
{
mt65xx_eint_mask(CUST_EINT_TOUCH_PANEL_NUM); //mask TP中断
int UpResult;
UpResult = ms6000_firmware_upgrade();
if(UpResult== MS6000_RET_SUCCESS)
printk("MFS6000 DOWNLOAD SUCCESS \r\n");
else
mfs6000_print_fail_result(UpResult);
i2c_client->addr = MS6000_8BIT_I2CADDR; //恢复TP的正常操作时I2C地址
mt_set_gpio_mode(GPIO_I2C0_SCA_PIN, GPIO_I2C0_SCA_PIN_M_SCL);
mt_set_gpio_mode(GPIO_I2C0_SDA_PIN, GPIO_I2C0_SDA_PIN_M_SDA); //恢复I2C功能脚
mt65xx_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM); //unmask TP中断
}