以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中断 }