/*===============================================================================================*/ /** * @file touch.c * * @version v1.0 */ /*================================================================================================= Rootlink Confidential Proprietary Advanced Technology and Software Operations (c) Copyright Rootlink 2015 - , All Rights Reserved Revision History: Modification Tracking Author Date Number Description of Changes -------- -------- ------- ------------------------ Portability: Indicate if this module is portable to other compilers or platforms. If not, indicate specific reasons why is it not portable. =================================================================================================== INCLUDE FILES =================================================================================================*/ #include #include "fr30xx.h" #include "chsc5816_ctp.h" #if 0 #define PRINT_DBG printf #else #define PRINT_DBG(...) #endif #define RETRY 1000 #define _TOUCH_DOWN 0 #define _TOUCH_UP 1 #define _TOUCH_CONTACT 2 #define CHSC5816_ADDRESS (0x2e<<1) #define _XSIZE_PHYS 368 #define _YSIZE_PHYS 448 #define __CHSC5816_RESET_SET() touchpad_reset_set() #define __CHSC5816_RESET_CLEAR() touchpad_reset_clear() #define HAL_Delay(counter) touchpad_delay_ms(counter) extern I2C_HandleTypeDef i2c_touchpad_handle; static struct sm_touch_dev st_dev; static uint8_t counter; void touchpad_reset_set(void); void touchpad_reset_clear(void); void touchpad_delay_ms(uint32_t counter); /************************************************************************************ * @fn i2c_memory_read * * @brief i2c memory read. */ bool i2c_memory_chsc5816_read(I2C_HandleTypeDef *hi2c, uint16_t fu16_DevAddress, uint32_t fu32_MemAddress, uint8_t *fp_Data, uint32_t fu32_Size) { uint32_t lu32_RxCount = fu32_Size; counter = 0; if (fu32_Size == 0) goto _exit_i2c_read; __I2C_DISABLE(hi2c->I2Cx); hi2c->I2Cx->TAR.TAR = fu16_DevAddress >> 1; __I2C_ENABLE(hi2c->I2Cx); hi2c->I2Cx->DATA_CMD = (fu32_MemAddress >> 24) & 0xFF; hi2c->I2Cx->DATA_CMD = (fu32_MemAddress >> 16) & 0xFF; while (!__I2C_IS_TxFIFO_EMPTY(hi2c->I2Cx)); /* DevAddress NACK */ if (i2c_get_int_status(hi2c, INT_TX_ABRT)) { i2c_clear_int_status(hi2c, INT_TX_ABRT); __I2C_DISABLE(hi2c->I2Cx); counter = 1; goto _exit_i2c_read; } hi2c->I2Cx->DATA_CMD = (fu32_MemAddress >> 8) & 0xFF; hi2c->I2Cx->DATA_CMD = fu32_MemAddress & 0xFF; if (fu32_Size > 1) { hi2c->I2Cx->DATA_CMD = CMD_RESTART | CMD_READ; while (fu32_Size - 2) { if (!__I2C_IS_TxFIFO_FULL(hi2c->I2Cx)) { hi2c->I2Cx->DATA_CMD = CMD_READ; fu32_Size--; } while (!__I2C_IS_RxFIFO_EMPTY(hi2c->I2Cx)) { *fp_Data++ = hi2c->I2Cx->DATA_CMD & 0xFF; lu32_RxCount--; } if (i2c_get_int_status(hi2c, INT_TX_ABRT)) { i2c_clear_int_status(hi2c, INT_TX_ABRT); __I2C_DISABLE(hi2c->I2Cx); counter = 2; goto _exit_i2c_read; } } /* Last byte with stop */ while (__I2C_IS_TxFIFO_FULL(hi2c->I2Cx)); hi2c->I2Cx->DATA_CMD = CMD_READ | CMD_STOP; } else { hi2c->I2Cx->DATA_CMD = CMD_RESTART | CMD_READ | CMD_STOP; } while (lu32_RxCount) { if (!__I2C_IS_RxFIFO_EMPTY(hi2c->I2Cx)) { *fp_Data++ = hi2c->I2Cx->DATA_CMD & 0xFF; lu32_RxCount--; } } while(__I2C_IS_BUSY(hi2c->I2Cx)); return true; _exit_i2c_read: PRINT_DBG("I2c r:%d\n",counter); __NOP(); return false; } /************************************************************************************ * @fn i2c_memory_write * * @brief i2c memory write. */ bool i2c_memory_chsc5816_write(I2C_HandleTypeDef *hi2c, uint16_t fu16_DevAddress, uint8_t *fp_Data, uint32_t fu32_Size) { counter = 0; if (fu32_Size < 2) goto _exit_i2c_write; __I2C_DISABLE(hi2c->I2Cx); hi2c->I2Cx->TAR.TAR = fu16_DevAddress >> 1; __I2C_ENABLE(hi2c->I2Cx); hi2c->I2Cx->DATA_CMD = *fp_Data++; hi2c->I2Cx->DATA_CMD = *fp_Data++; while (!__I2C_IS_TxFIFO_EMPTY(hi2c->I2Cx)); /* DevAddress NACK */ if (i2c_get_int_status(hi2c, INT_TX_ABRT)) { i2c_clear_int_status(hi2c, INT_TX_ABRT); __I2C_DISABLE(hi2c->I2Cx); counter = 1; goto _exit_i2c_write; } fu32_Size -= 2; while (fu32_Size - 1 > 0) { if (!__I2C_IS_TxFIFO_FULL(hi2c->I2Cx)) { hi2c->I2Cx->DATA_CMD = *fp_Data++; fu32_Size--; } if (i2c_get_int_status(hi2c, INT_TX_ABRT)) { i2c_clear_int_status(hi2c, INT_TX_ABRT); __I2C_DISABLE(hi2c->I2Cx); counter = 2; goto _exit_i2c_write; } } /* Last byte with stop */ while (__I2C_IS_TxFIFO_FULL(hi2c->I2Cx)); hi2c->I2Cx->DATA_CMD = *fp_Data | CMD_STOP; while(__I2C_IS_BUSY(hi2c->I2Cx)); //while(i2c_memory_is_busy(hi2c, fu16_DevAddress)); return true; _exit_i2c_write: PRINT_DBG("I2c w:%d\n",counter); __NOP(); return false; } void semi_touch_power_int(void) { return; } static void semi_touch_msdelay(uint32_t millisecs) { HAL_Delay(millisecs); } static void semi_touch_reset(void) { __CHSC5816_RESET_SET(); HAL_Delay(10); __CHSC5816_RESET_CLEAR(); HAL_Delay(5); __CHSC5816_RESET_SET(); HAL_Delay(200); } int32_t semi_touch_iic_write(uint32_t reg, uint8_t* pdata, uint16_t len) { if(i2c_memory_chsc5816_write(&i2c_touchpad_handle, (uint16_t)CHSC5816_ADDRESS, pdata, len) == 0) { return -1; } return SEMI_DRV_ERR_OK; } /* reg : must 4B aligned len : must multiple of 4 */ int32_t semi_i2c_read_bytes(uint32_t reg, uint8_t* pdata, uint16_t len) { if(i2c_memory_chsc5816_read(&i2c_touchpad_handle, (uint16_t)CHSC5816_ADDRESS, reg, pdata, len) == 0) { return -1; } return SEMI_DRV_ERR_OK; } /* reg - register address, must 4B aligned buffer - data buffer len - data length, must 4B aligned return: 0 - pass others - fail */ int32_t semi_touch_read_bytes(uint32_t reg, uint8_t* buffer, uint16_t len) { int32_t ret = SEMI_DRV_ERR_OK; uint16_t once; uint32_t retry; while(len > 0){ once = (len>MAX_IO_BUFFER_LEN)?MAX_IO_BUFFER_LEN:len; ret = -1; for(retry=0; retry<3; retry++){ if(semi_i2c_read_bytes(reg, buffer,once) == SEMI_DRV_ERR_OK){ ret = SEMI_DRV_ERR_OK; break; } } if(ret != SEMI_DRV_ERR_OK){ break; } reg += once; buffer += once; len -= once; } return ret; } /* reg - register address, must 4B aligned buffer - data buffer len - data length, must 4B aligned return: 0 - pass others - fail */ int32_t semi_touch_write_bytes(uint32_t reg, uint8_t* buffer, uint16_t len) { int32_t ret = SEMI_DRV_ERR_OK; uint16_t once; uint32_t k, retry; uint8_t writeBuff[MAX_IO_BUFFER_LEN]; while(len > 0){ once = (len<(MAX_IO_BUFFER_LEN-4))?len:(MAX_IO_BUFFER_LEN-4); writeBuff[0] = (uint8_t)(reg>>24); writeBuff[1] = (uint8_t)(reg>>16); writeBuff[2] = (uint8_t)(reg>>8); writeBuff[3] = (uint8_t)(reg); for(k=0; k 0){ retry = 0; do{ ret = SEMI_DRV_ERR_OK; once = (len<(MAX_IO_BUFFER_LEN-4))?len:(MAX_IO_BUFFER_LEN-4); ret = semi_touch_write_bytes(addr, buffer, once); ret = semi_touch_read_bytes(addr, cmp_buffer, once); for(index = 0; index < once; index++){ if(cmp_buffer[index] != buffer[index]){ ret = -SEMI_DRV_ERR_CHECKSUM; break; } } if(SEMI_DRV_ERR_OK == ret){ break; } }while(++retry < 3); if(SEMI_DRV_ERR_OK != ret){ break; } addr += once; buffer += once; len -= once; } return ret; } int32_t semi_touch_run_ram_code(const uint8_t* bin_code, uint16_t len) { int32_t retry; int32_t ret = 0, reg_value = 0; for(retry = 0; retry < 5; retry++){ //reset mcu semi_touch_reset(); //hold mcu reg_value = 0x12044000; ret = semi_touch_write_bytes(0x40007000, (uint8_t*)®_value, 4); if(ret != 0){ continue; } //open auto feed reg_value = 0x0000925a; ret = semi_touch_write_bytes(0x40007010, (uint8_t*)®_value, 4); if(ret != 0){ continue; } //run ramcode ret = semi_touch_write_and_check(0x20000000, (uint8_t* )bin_code, len); if(ret != 0){ continue; } break; } if(ret != 0){ return -1; } //remap reg_value = 0x12044002; ret = semi_touch_write_bytes(0x40007000, (uint8_t*)®_value, 4); if(ret != 0){ return -1; } //release mcu reg_value = 0x12044003; ret = semi_touch_write_bytes(0x40007000, (uint8_t*)®_value, 4); if(ret != 0){ return -1; } semi_touch_msdelay(30); return 0; } static uint16_t caculate_checksum_u16(uint16_t *buf, uint16_t length) { uint16_t sum, len, i; sum = 0; len = length >> 1; for (i = 0; i < len; i++) { sum += buf[i]; } return sum; } static uint32_t caculate_checksum_ex(uint8_t * buf, uint16_t length) { uint32_t combchk = 0; uint16_t k = 0, check = 0, checkex = 0; for (k = 0; k < length; k++) { check += buf[k]; checkex += (uint16_t)(k * buf[k]); } combchk = (checkex<<16) | check; return combchk; } static int32_t cmd_send_to_tp(struct m_ctp_cmd_std_t *ptr_cmd, struct m_ctp_rsp_std_t *ptr_rsp, int32_t once_delay, int32_t poolgap) { int32_t ret = -SEMI_DRV_ERR_HAL_IO; uint32_t retry = 0; uint32_t cmd_rsp_ok = 0; ptr_cmd->tag = 0xE9; ptr_cmd->chk = 1 + ~caculate_checksum_u16((uint16_t*)&ptr_cmd->d0, sizeof(struct m_ctp_cmd_std_t) - 2); ret = semi_touch_write_bytes(0x20000000, (uint8_t*)ptr_cmd, sizeof(struct m_ctp_cmd_std_t)); if(ret != 0){ // TODO: need confirm!!! return -1; } semi_touch_msdelay(once_delay); while(retry++ < 20){ semi_touch_msdelay(poolgap); ret = semi_touch_read_bytes(0x20000000, (uint8_t*)ptr_rsp, sizeof(struct m_ctp_rsp_std_t)); if(ret != 0){ // TODO: need confirm!!! return -1; } if(ptr_cmd->id != ptr_rsp->id){ continue; } if(!caculate_checksum_u16((uint16_t*)ptr_rsp, sizeof(struct m_ctp_rsp_std_t))){ if(0 == ptr_rsp->cc){ //success cmd_rsp_ok = 1; } break; } } ret = -1; if(cmd_rsp_ok == 1){ ret = SEMI_DRV_ERR_OK; } return ret; } /* return: 0(SEMI_DRV_ERR_OK) ->success others ->fail */ static int32_t semi_touch_nvm_read(uint8_t *pdes, uint32_t adr, uint32_t len) { int32_t ret = -1; uint32_t left = len; uint32_t local_check, retry; struct m_ctp_cmd_std_t cmd_send_tp; struct m_ctp_rsp_std_t ack_from_tp; cmd_send_tp.id = CMD_MEM_RD; while (left) { len = (left > 1024) ? 1024 : left; cmd_send_tp.d0 = adr & 0xffff; cmd_send_tp.d1 = len; cmd_send_tp.d2 = 0; cmd_send_tp.d3 = NVM_R; cmd_send_tp.d5 = (adr >> 16) & 0xffff; retry = 0; while (retry++ < 3) { ack_from_tp.id = CMD_NA; ret = cmd_send_to_tp(&cmd_send_tp, &ack_from_tp, 20, 10); if(SEMI_DRV_ERR_OK != ret){ continue; } semi_touch_read_bytes(TP_RD_BUFF_ADDR, pdes, len); local_check = caculate_checksum_ex(pdes, len); if ((ack_from_tp.d0 != (uint16_t)local_check) || (ack_from_tp.d1 != (uint16_t)(local_check >> 16))){ ret = -SEMI_DRV_ERR_CHECKSUM; continue; } break; } adr += len; left -= len; pdes += len; if(ret != SEMI_DRV_ERR_OK){ break; } } return ret; } static int32_t semi_touch_nvm_write(uint8_t *psrc, uint32_t adr, uint32_t len) { int32_t ret = -1; uint32_t left = len; uint32_t retry, combChk; struct m_ctp_cmd_std_t cmd_send_tp; struct m_ctp_rsp_std_t ack_from_tp; cmd_send_tp.id = CMD_MEM_WR; while (left) { len = (left > 1024) ? 1024 : left; combChk = caculate_checksum_ex(psrc, len); cmd_send_tp.d0 = adr & 0xffff; /* addrss space[0,64K) */ cmd_send_tp.d1 = len; cmd_send_tp.d3 = NVM_W; cmd_send_tp.d2 = (uint16_t) combChk; cmd_send_tp.d4 = (uint16_t) (combChk >> 16); cmd_send_tp.d5 = (adr >> 16) & 0xffff; retry = 0; while (++retry <= 3) { ret = semi_touch_write_bytes(TP_WR_BUFF_ADDR, psrc, len); if(SEMI_DRV_ERR_OK != ret) continue; ack_from_tp.id = CMD_NA; ret = cmd_send_to_tp(&cmd_send_tp, &ack_from_tp, 200, 20); if(SEMI_DRV_ERR_OK != ret) continue; break; } left -= len; adr += len; psrc += len; if(ret != SEMI_DRV_ERR_OK){ break; } } return ret; } static int32_t semi_touch_burn_erase(void) { struct m_ctp_cmd_std_t cmd_send_tp; struct m_ctp_rsp_std_t ack_from_tp; cmd_send_tp.id = CMD_FLASH_ERASE; cmd_send_tp.d0 = 0x01; return cmd_send_to_tp(&cmd_send_tp, &ack_from_tp, 1000, 40); } /* This function push IC into NVM mode, call it carefully and must reset IC to enter normal mode. return: 0(SEMI_DRV_ERR_OK) ->success others ->fail */ static int32_t semi_touch_enter_burn_mode(void) { struct m_ctp_cmd_std_t cmd_send_tp; struct m_ctp_rsp_std_t ack_from_tp; ack_from_tp.d0 = 0; cmd_send_tp.id = CMD_IDENTITY; cmd_send_to_tp(&cmd_send_tp, &ack_from_tp, 20, 5); if((ack_from_tp.d0 == 0xE9A2) && (ack_from_tp.d1 == 0x165d)){ return SEMI_DRV_ERR_OK; } if(semi_touch_run_ram_code(fw_5816_burn, sizeof(fw_5816_burn)) != 0){ return -1; } cmd_send_tp.id = CMD_IDENTITY; if(cmd_send_to_tp(&cmd_send_tp, &ack_from_tp, 20, 5) != 0){ return -1; } if((ack_from_tp.d0 == 0xE9A2) && (ack_from_tp.d1 == 0x165d)){ return SEMI_DRV_ERR_OK; } return -SEMI_DRV_ERR_HAL_IO; } static int32_t semi_get_backup_pid(uint32_t *id) { st_dev.ctp_status = CTP_UPGRAD_RUNING; if(semi_touch_enter_burn_mode() != SEMI_DRV_ERR_OK){ return -1; } return semi_touch_nvm_read((uint8_t *)id, VID_PID_BACKUP_ADDR, 4); } static int32_t semi_touch_update_check(void) { uint32_t pvid; uint8_t * pfw; uint32_t * plist; int32_t k, idx_active; uint16_t upd_boot_ver = 0; struct chsc_updfile_header *upd_header; st_dev.needUpd = 0; st_dev.updPdata = 0; st_dev.newBootLen = 0; if(st_dev.setup_ok == 0){ if(semi_get_backup_pid(&pvid) == 0){ st_dev.vid_pid = pvid; } } if((uint32_t)chsc_upd_data & 3){ PRINT_DBG("chsc::illegal memory buffer, must 4B aliged\n"); return -SEMI_DRV_INVALID_PARAM; } upd_header = (struct chsc_updfile_header *)chsc_upd_data; if((upd_header->sig != 0x43534843) || (upd_header->n_match == 0)) { PRINT_DBG("chsc::illegal upd_header\n"); return -SEMI_DRV_ERR_NOT_MATCH; } if((upd_header->len_boot <= 15*1024) || (upd_header->len_boot >= 40*1024)) { PRINT_DBG("chsc::illegal upd_header\n"); return -SEMI_DRV_ERR_NOT_MATCH; } plist = (uint32_t *)((uint8_t *)chsc_upd_data + sizeof(struct chsc_updfile_header)); pfw = (uint8_t *)plist + (upd_header->n_match*4) + upd_header->len_cfg; if((pfw[0x30] != 0x16) || (pfw[0x31] != 0x58)){ PRINT_DBG("chsc:no chsc5816 fw found\n"); return -SEMI_DRV_ERR_NOT_MATCH; } st_dev.updPdata = pfw; st_dev.newBootLen = upd_header->len_boot; idx_active = -1; upd_boot_ver = (pfw[0x3f] << 8) + pfw[0x3e]; for (k=0; kn_match; k++) { pvid = plist[k]; PRINT_DBG("chsc::pid_vid in list=0x%x\n", pvid); if ((pvid & PID_VID_MASK) == (st_dev.vid_pid & PID_VID_MASK)) { PRINT_DBG("chsc::running_ver=%d, upd_ver=%d\n", st_dev.fw_ver, upd_boot_ver); if((st_dev.fw_ver < upd_boot_ver) || (st_dev.setup_ok == 0)){ idx_active = k; } break; } } if((st_dev.setup_ok == 0) && (idx_active < 0)){ idx_active = 0; } if(idx_active >= 0){ st_dev.needUpd = 1; } return SEMI_DRV_ERR_OK; } /* return: 0 ->success others ->fail */ static int32_t semi_touch_update(uint8_t *pdata, uint32_t len) { if((pdata == NULL) || (len<1024) || (len>0x9f00)){ PRINT_DBG("chsc:semi_touch_update, not chsc5816 fw\n"); return -1; } if((pdata[0x30] != 0x16) || (pdata[0x31] != 0x58)){ PRINT_DBG("chsc:semi_touch_update, not chsc5816 fw\n"); return -1; } if(semi_touch_enter_burn_mode() != 0){ //kal_prompt_trace(MOD_WAP,"chsc::semi_touch_enter_burn_mode fail\n"); return -1; } if(semi_touch_burn_erase() != 0){ PRINT_DBG("chsc::semi_touch_burn_erase fail\n"); return -1; } if(semi_touch_nvm_write(pdata, 0x00000000, len) != 0){ PRINT_DBG("chsc::semi_touch_nvm_write fail\n"); return -1; } return SEMI_DRV_ERR_OK; } static void semi_touch_setup_check(void) { int32_t retry = 0; uint32_t naFlag = 0; img_header_t image_header; img_header_t image_confirm; //clean boot status semi_touch_write_bytes(0x20000018, (uint8_t*)&naFlag, 4); semi_touch_reset(); st_dev.fw_ver = 0; st_dev.vid_pid = 0; st_dev.setup_ok = 0;//default error image_header.sig = 0; for(retry=0; retry<10; retry++){ semi_touch_msdelay(10); if(semi_touch_read_bytes(0x20000014, (uint8_t*)&image_header, sizeof(image_header)) != 0){ continue; } if(semi_touch_read_bytes(0x20000014, (uint8_t*)&image_confirm, sizeof(image_confirm)) != 0){ continue; } if((image_header.sig != image_confirm.sig) || (image_header.vid_pid != image_confirm.vid_pid) || (image_header.raw_offet != image_confirm.raw_offet) || (image_header.dif_offet != image_confirm.dif_offet) || (image_header.fw_ver != image_confirm.fw_ver)){ //kal_prompt_trace(MOD_WAP,"chsc::double check, retry\n"); continue; } if(image_header.sig == 0x43534843){ //"CHSC" st_dev.fw_ver = image_header.fw_ver; st_dev.vid_pid = image_header.vid_pid; st_dev.raw_adr = image_confirm.raw_offet + 0x20000000; st_dev.setup_ok = 1;//pass st_dev.ctp_status = CTP_POINTING_WORK; break; }else if(image_header.sig == 0x4F525245){ //boot self check fail break; } } return; } int32_t semi_touch_dect(void) { uint32_t u32Data, retry; for(retry=0; retry<3; retry++){ semi_touch_reset(); if(!semi_touch_read_bytes(0x20000000, (uint8_t *)&u32Data, 4)){ return 0; } } return -1; } void semi_touch_irq_handler_imp(void) { #define _UI_MAX_POINTS 1 #define GEST_CODE_ACT_LS 0x10 //left #define GEST_CODE_ACT_RS 0x20 //right #define GEST_CODE_ACT_US 0x30 //up #define GEST_CODE_ACT_DS 0x40 //down int pointed = 0; union rpt_point_t* ppt; unsigned char gestCode; unsigned char data[8]; int x, y; PRINT_DBG("semi_touch_irq_handler_imp\r\n"); if(semi_touch_read_bytes(0x2000002c, data, 8)){ PRINT_DBG("chsc:read pixel data fail\n" ); return; } PRINT_DBG("imp = %x %x\r\n",data[0], data[1]); pointed = 0; ppt = (union rpt_point_t*)&data[2]; if((data[0] == 0xff) && (data[1] <= 2)){ if(data[1] > 0){ pointed = 1; x = (unsigned int)(ppt->rp.x_h4 << 8) | ppt->rp.x_l8; y = (unsigned int)(ppt->rp.y_h4 << 8) | ppt->rp.y_l8; } }else{ return; } } bool semi_touch_read_coordinate(int16_t *x, int16_t *y) { int pointed = 0; union rpt_point_t* ppt; unsigned char data[8]; if(semi_touch_read_bytes(0x2000002c, data, 8)){ PRINT_DBG("chsc:read pixel data fail\n" ); return 0; } pointed = 0; ppt = (union rpt_point_t*)&data[2]; if((data[0] == 0xff) && (data[1] <= 2)){ if(data[1] > 0){ pointed = 1; *x = (unsigned int)(ppt->rp.x_h4 << 8) | ppt->rp.x_l8; *y = (unsigned int)(ppt->rp.y_h4 << 8) | ppt->rp.y_l8; PRINT_DBG("x = %d y = %d \r\n",*x, *y); } } return pointed; } int semi_touch_init() { semi_touch_power_int(); if(semi_touch_dect() != SEMI_DRV_ERR_OK){ PRINT_DBG("chsc:no chsc5816\r\n" ); return -1; } semi_touch_setup_check(); semi_touch_update_check(); semi_touch_reset(); st_dev.ctp_status = CTP_POINTING_WORK; PRINT_DBG("chsc init ok\r\n" ); return 0; }