#include #include #include #include #include #include #include #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; }