/* * wacom_i2c_flash.c - Wacom G5 Digitizer Controller (I2C bus) * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "wacom_i2c.h" #include "wacom_i2c_flash.h" static int wacom_enter_flash_mode(struct wacom_i2c *wac_i2c) { int ret, len = 0; char buf[8]; dev_info(&wac_i2c->client->dev, "%s\n", __func__); memset(buf, 0, 8); strncpy(buf, "\rflash\r", 7); len = sizeof(buf) / sizeof(buf[0]); ret = wac_i2c->wacom_i2c_send(wac_i2c, buf, len, WACOM_I2C_MODE_BOOT); if (ret < 0) { dev_err(&wac_i2c->client->dev, "%s: Sending flash command failed, %d\n", __func__, ret); return ret; } msleep(300); return 0; } static int wacom_set_cmd_feature(struct wacom_i2c *wac_i2c) { int ret, len = 0; u8 buf[4]; usleep_range(60, 61); buf[len++] = 4; buf[len++] = 0; buf[len++] = 0x37; buf[len++] = CMD_SET_FEATURE; ret = wac_i2c->wacom_i2c_send(wac_i2c, buf, len, WACOM_I2C_MODE_BOOT); if (ret < 0) dev_err(&wac_i2c->client->dev, "%s: failed send CMD_SET_FEATURE, %d\n", __func__, ret); usleep_range(60, 61); return ret; } static int wacom_get_cmd_feature(struct wacom_i2c *wac_i2c) { int ret, len = 0; u8 buf[6]; usleep_range(60, 61); buf[len++] = 4; buf[len++] = 0; buf[len++] = 0x38; buf[len++] = CMD_GET_FEATURE; buf[len++] = 5; buf[len++] = 0; ret = wac_i2c->wacom_i2c_send(wac_i2c, buf, len, WACOM_I2C_MODE_BOOT); if (ret < 0) dev_err(&wac_i2c->client->dev, "%s: failed send CMD_GET_FEATURE, ret: %d\n", __func__, ret); usleep_range(60, 61); return ret; } /* * mode1. BOOT_QUERY: check enter boot mode for flash. * mode2. BOOT_BLVER : check bootloader version. * mode3. BOOT_MPU : check MPU version */ int wacom_check_flash_mode(struct wacom_i2c *wac_i2c, int mode) { int ret, ECH; unsigned char response_cmd = 0; unsigned char command[CMD_SIZE]; unsigned char response[RSP_SIZE]; switch (mode) { case BOOT_QUERY: response_cmd = QUERY_CMD; break; case BOOT_BLVER: response_cmd = BOOT_CMD; break; case BOOT_MPU: response_cmd = MPU_CMD; break; default: break; } dev_info(&wac_i2c->client->dev, "%s, mode = %s\n", __func__, (mode == BOOT_QUERY) ? "BOOT_QUERY" : (mode == BOOT_BLVER) ? "BOOT_BLVER" : (mode == BOOT_MPU) ? "BOOT_MPU" : "Not Support"); ret = wacom_set_cmd_feature(wac_i2c); if (ret < 0) return ret; command[0] = 5; command[1] = 0; command[2] = 5; command[3] = 0; command[4] = BOOT_CMD_REPORT_ID; command[5] = mode; command[6] = ECH = 7; ret = wac_i2c->wacom_i2c_send(wac_i2c, command, 7, WACOM_I2C_MODE_BOOT); if (ret < 0) { dev_err(&wac_i2c->client->dev, "%s: failed send REPORT_ID, %d\n", __func__, ret); return ret; } ret = wacom_get_cmd_feature(wac_i2c); if (ret < 0) return ret; ret = wac_i2c->wacom_i2c_recv(wac_i2c, response, BOOT_RSP_SIZE, WACOM_I2C_MODE_BOOT); if (ret < 0) { dev_err(&wac_i2c->client->dev, "%s: failed receive response, %d\n", __func__, ret); return ret; } if ((response[3] != response_cmd) || (response[4] != ECH)) { dev_err(&wac_i2c->client->dev, "%s: failed matching response3[%x], 4[%x]\n", __func__, response[3], response[4]); return -EIO; } return response[5]; } int wacom_enter_bootloader(struct wacom_i2c *wac_i2c) { int ret; int retry = 0; dev_info(&wac_i2c->client->dev, "%s\n", __func__); ret = wacom_enter_flash_mode(wac_i2c); if (ret < 0) msleep(500); do { msleep(100); ret = wacom_check_flash_mode(wac_i2c, BOOT_QUERY); if (ret == QUERY_RSP) dev_info(&wac_i2c->client->dev, "%s: enter flash mode\n", __func__); else dev_err(&wac_i2c->client->dev, "%s: retry BOOT_QUERY\n", __func__); retry++; } while (ret < 0 && retry < 10); if (ret < 0) return -EXIT_FAIL_GET_BOOT_LOADER_VERSION; ret = wacom_check_flash_mode(wac_i2c, BOOT_MPU); wac_i2c->boot_ver = ret; if (ret < 0) return -EXIT_FAIL_GET_BOOT_LOADER_VERSION; dev_info(&wac_i2c->client->dev, "%s: MPU:%X\n", __func__, wac_i2c->boot_ver); return ret; } static int wacom_flash_end(struct wacom_i2c *wac_i2c) { int ret, ECH; unsigned char command[CMD_SIZE]; dev_info(&wac_i2c->client->dev, "%s\n", __func__); command[0] = 4; command[1] = 0; command[2] = 0x37; command[3] = CMD_SET_FEATURE; command[4] = 5; command[5] = 0; command[6] = 5; command[7] = 0; command[8] = BOOT_CMD_REPORT_ID; command[9] = BOOT_EXIT; /* command[10] = ECH = 7;*/ command[10] = ECH = 0; ret = wac_i2c->wacom_i2c_send(wac_i2c, command, 11, WACOM_I2C_MODE_BOOT); if (ret < 0) dev_err(&wac_i2c->client->dev, "%s failed, %d\n", __func__, ret); return ret; } static bool erase_datamem(struct wacom_i2c *wac_i2c) { u8 command[CMD_SIZE]; u8 response[BOOT_RSP_SIZE]; unsigned char sum = 0; unsigned char cmd_chksum; int ret, ECH, j; int len = 0; dev_info(&wac_i2c->client->dev, "%s\n", __func__); command[len++] = 4; /* Command Register-LSB */ command[len++] = 0; /* Command Register-MSB */ command[len++] = 0x37; /* Command-LSB, ReportType:Feature(11) ReportID:7 */ command[len++] = CMD_SET_FEATURE; /* Command-MSB, SET_REPORT */ command[len++] = 5; /* Data Register-LSB */ command[len++] = 0; /* Data-Register-MSB */ command[len++] = 0x07; /* Length Field-LSB */ command[len++] = 0; /* Length Field-MSB */ command[len++] = BOOT_CMD_REPORT_ID; /* Report:ReportID */ command[len++] = BOOT_ERASE_DATAMEM; /* Report:erase datamem command */ command[len++] = ECH = BOOT_ERASE_DATAMEM; /* Report:echo */ command[len++] = DATAMEM_SECTOR0; /* Report:erased block No. */ sum = 0; for (j = 4; j < 12; j++) sum += command[j]; cmd_chksum = ~sum + 1; /* Report:check sum */ command[len++] = cmd_chksum; udelay(50); ret = wac_i2c->wacom_i2c_send(wac_i2c, command, len, WACOM_I2C_MODE_BOOT); if (ret < 0) { dev_err(&wac_i2c->client->dev, "%s: failing 1: %d \n", __func__, ret); return false; } udelay(50); do { len = 0; command[len++] = 4; command[len++] = 0; command[len++] = 0x38; command[len++] = CMD_GET_FEATURE; command[len++] = 5; command[len++] = 0; ret = wac_i2c->wacom_i2c_send(wac_i2c, command, len, WACOM_I2C_MODE_BOOT); if (ret < 0) { dev_err(&wac_i2c->client->dev, "%s: failing 2:%d \n", __func__, ret); return false; } udelay(50); ret = wac_i2c->wacom_i2c_recv(wac_i2c, response, BOOT_RSP_SIZE, WACOM_I2C_MODE_BOOT); if (ret < 0) { dev_err(&wac_i2c->client->dev, "%s: failing 3:%d \n", __func__, ret); return false; } if ((response[3] != 0x0e || response[4] != ECH) || (response[5] != 0xff && response[5] != 0x00)) return false; udelay(50); } while (response[3] == 0x0e && response[4] == ECH && response[5] == 0xff); msleep(50); return true; } static bool erase_codemem(struct wacom_i2c *wac_i2c, int *eraseBlock, int num) { u8 command[CMD_SIZE]; u8 response[BOOT_RSP_SIZE]; unsigned char sum = 0; unsigned char cmd_chksum; int ret, ECH; int len = 0; int i, j; dev_info(&wac_i2c->client->dev, "%s: total erase code-block number = %d\n", __func__, num); for (i = 0; i < num; i++) { len = 0; command[len++] = 4; /* Command Register-LSB */ command[len++] = 0; /* Command Register-MSB */ command[len++] = 0x37; /* Command-LSB, ReportType:Feature(11) ReportID:7 */ command[len++] = CMD_SET_FEATURE; /* Command-MSB, SET_REPORT */ command[len++] = 5; /* Data Register-LSB */ command[len++] = 0; /* Data-Register-MSB */ command[len++] = 7; /* Length Field-LSB */ command[len++] = 0; /* Length Field-MSB */ command[len++] = BOOT_CMD_REPORT_ID; /* Report:ReportID */ command[len++] = BOOT_ERASE_FLASH; /* Report:erase command */ command[len++] = ECH = i; /* Report:echo */ command[len++] = *eraseBlock; /* Report:erased block No. */ eraseBlock++; sum = 0; for (j = 4; j < 12; j++) sum += command[j]; cmd_chksum = ~sum + 1; /* Report:check sum */ command[len++] = cmd_chksum; udelay(50); ret = wac_i2c->wacom_i2c_send(wac_i2c, command, len, WACOM_I2C_MODE_BOOT); if (ret < 0) { dev_err(&wac_i2c->client->dev, "%s: failing 1:%d \n", __func__, i); return false; } udelay(50); do { len = 0; command[len++] = 4; command[len++] = 0; command[len++] = 0x38; command[len++] = CMD_GET_FEATURE; command[len++] = 5; command[len++] = 0; ret = wac_i2c->wacom_i2c_send(wac_i2c, command, len, WACOM_I2C_MODE_BOOT); if (ret < 0) { dev_err(&wac_i2c->client->dev, "%s: failing 2:%d \n", __func__, i); return false; } udelay(50); ret = wac_i2c->wacom_i2c_recv(wac_i2c, response, BOOT_RSP_SIZE, WACOM_I2C_MODE_BOOT); if (ret < 0) { printk("%s failing 3:%d \n", __func__, i); return false; } if ((response[3] != 0x00 || response[4] != ECH) || (response[5] != 0xff && response[5] != 0x00)) return false; udelay(50); } while (response[3] == 0x00 && response[4] == ECH && response[5] == 0xff); udelay(50); } msleep(50); return true; } static bool flash_erase_w9012(struct wacom_i2c *wac_i2c, int *eraseBlock, int num) { bool ret; ret = erase_datamem(wac_i2c); if (!ret) { dev_err(&wac_i2c->client->dev, "%s: erasing datamem failed\n", __func__); return false; } ret = erase_codemem(wac_i2c, eraseBlock, num); if (!ret) { dev_err(&wac_i2c->client->dev, "%s: erasing codemem failed\n", __func__); return false; } return true; } static bool flash_write_block_w9012(struct wacom_i2c *wac_i2c, char *flash_data, unsigned long ulAddress, u8 *pcommand_id, int *ECH) { const int MAX_COM_SIZE = (16 + FLASH_BLOCK_SIZE + 2); /* 16: num of command[0] to command[15] ** FLASH_BLOCK_SIZE: unit to erase the block ** Num of Last 2 checksums */ u8 command[300]; unsigned char sum = 0; int ret, i; command[0] = 4; /* Command Register-LSB */ command[1] = 0; /* Command Register-MSB */ command[2] = 0x37; /* Command-LSB, ReportType:Feature(11) ReportID:7 */ command[3] = CMD_SET_FEATURE; /* Command-MSB, SET_REPORT */ command[4] = 5; /* Data Register-LSB */ command[5] = 0; /* Data-Register-MSB */ command[6] = 76; /* Length Field-LSB */ command[7] = 0; /* Length Field-MSB */ command[8] = BOOT_CMD_REPORT_ID; /* Report:ReportID */ command[9] = BOOT_WRITE_FLASH; /* Report:program command */ command[10] = *ECH = ++(*pcommand_id); /* Report:echo */ command[11] = ulAddress & 0x000000ff; command[12] = (ulAddress & 0x0000ff00) >> 8; command[13] = (ulAddress & 0x00ff0000) >> 16; command[14] = (ulAddress & 0xff000000) >> 24; /* Report:address(4bytes) */ command[15] = 8; /* Report:size(8*8=64) */ sum = 0; for (i = 4; i < 16; i++) sum += command[i]; command[MAX_COM_SIZE - 2] = ~sum+1; /* Report:command checksum */ sum = 0; for (i = 16; i < (FLASH_BLOCK_SIZE + 16); i++) { command[i] = flash_data[ulAddress+(i-16)]; sum += flash_data[ulAddress+(i-16)]; } command[MAX_COM_SIZE - 1] = ~sum+1; /* Report:data checksum */ udelay(50); ret = wac_i2c->wacom_i2c_send(wac_i2c, command, (BOOT_CMD_SIZE + 4), WACOM_FLASH_W9012); if (ret < 0) { dev_err(&wac_i2c->client->dev, "%s: 1 ret:%d \n", __func__, ret); return false; } udelay(50); return true; } static bool flash_write_w9012(struct wacom_i2c *wac_i2c, unsigned char *flash_data, unsigned long start_address, unsigned long *max_address) { u8 command_id = 0; u8 command[BOOT_RSP_SIZE]; u8 response[BOOT_RSP_SIZE]; int ret, i, j, len, ECH = 0, ECH_len = 0; int ECH_ARRAY[3]; unsigned long ulAddress; dev_info(&wac_i2c->client->dev, "%s\n", __func__); j = 0; for (ulAddress = start_address; ulAddress < *max_address; ulAddress += FLASH_BLOCK_SIZE) { for (i = 0; i < FLASH_BLOCK_SIZE; i++) { if (flash_data[ulAddress+i] != 0xFF) break; } if (i == (FLASH_BLOCK_SIZE)) continue; if (!flash_write_block_w9012(wac_i2c, flash_data, ulAddress, &command_id, &ECH)) return false; if (ECH_len == 3) ECH_len = 0; ECH_ARRAY[ECH_len++] = ECH; if (ECH_len == 3) { for (j = 0; j < 3; j++) { do { len = 0; command[len++] = 4; command[len++] = 0; command[len++] = 0x38; command[len++] = CMD_GET_FEATURE; command[len++] = 5; command[len++] = 0; udelay(50); ret = wac_i2c->wacom_i2c_send(wac_i2c, command, len, WACOM_FLASH_W9012); if (ret < 0) { dev_err(&wac_i2c->client->dev, "%s: failing 2:%d; system returning: %d\n", __func__, i, ret); return false; } udelay(50); ret = wac_i2c->wacom_i2c_recv(wac_i2c, response, BOOT_RSP_SIZE, WACOM_FLASH_W9012); if (ret < 0) { dev_err(&wac_i2c->client->dev, "%s: failing 3:%d system returning %d \n", __func__, i, ret); return false; } if ((response[3] != 0x01 || response[4] != ECH_ARRAY[j]) || (response[5] != 0xff && response[5] != 0x00)) return false; udelay(50); } while (response[3] == 0x01 && response[4] == ECH_ARRAY[j] && response[5] == 0xff); } } } return true; } int wacom_i2c_flash(struct wacom_i2c *wac_i2c) { unsigned long max_address = 0; unsigned long start_address; int i, ret = 0; int eraseBlock[200], eraseBlockNum; if (wac_i2c->ic_mpu_ver != MPU_W9007 && wac_i2c->ic_mpu_ver != MPU_W9010 && wac_i2c->ic_mpu_ver != MPU_W9012) return -EXIT_FAIL_GET_MPU_TYPE; wac_i2c->compulsory_flash_mode(wac_i2c, true); /*Reset*/ wac_i2c->reset_platform_hw(wac_i2c); msleep(300); dev_err(&wac_i2c->client->dev, "%s: Set FWE\n", __func__); ret = wacom_enter_bootloader(wac_i2c); if (ret < 0) { dev_err(&wac_i2c->client->dev, "%s: failed to enter bootloader mode\n", __func__); goto flashing_fw_err; } dev_err(&wac_i2c->client->dev, "%s: enter bootloader mode, erase & writing firmware\n", __func__); /*Set start and end address and block numbers*/ eraseBlockNum = 0; start_address = START_ADDR_W9012; max_address = MAX_ADDR_W9012; for (i = BLOCK_NUM_W9012; i >= 8; i--) { eraseBlock[eraseBlockNum] = i; eraseBlockNum++; } /*Erase the old program code */ if (!flash_erase_w9012(wac_i2c, eraseBlock, eraseBlockNum)) { dev_err(&wac_i2c->client->dev, "%s: failed erase old firmware, %d\n", __func__, ret); goto flashing_fw_err; } /*Write the new program */ if (!flash_write_w9012(wac_i2c, wac_i2c->fw_data, start_address, &max_address)) { dev_err(&wac_i2c->client->dev, "%s: failed writing new firmware, %d\n", __func__, ret); goto flashing_fw_err; } /*Enable */ ret = wacom_flash_end(wac_i2c); if (ret < 0) { dev_err(&wac_i2c->client->dev, "%s: failed flash mode close, %d\n", __func__, ret); goto flashing_fw_err; } dev_err(&wac_i2c->client->dev, "%s: Successed new Firmware writing\n", __func__); flashing_fw_err: wac_i2c->compulsory_flash_mode(wac_i2c, false); msleep(150); return ret; } int wacom_i2c_firm_update(struct wacom_i2c *wac_i2c) { int ret = 0; int retry = 3; while (retry--) { ret = wacom_i2c_flash(wac_i2c); if (ret < 0) dev_err(&wac_i2c->client->dev, "%s: failed to write firmware(%d)\n", __func__, ret); else dev_err(&wac_i2c->client->dev, "%s: Successed to write firmware(%d)\n", __func__, ret); /* Reset IC */ wac_i2c->reset_platform_hw(wac_i2c); if (ret >= 0) return 0; } return ret; } int wacom_fw_load_from_UMS(struct wacom_i2c *wac_i2c) { struct file *fp; mm_segment_t old_fs; long fsize, nread; int ret = 0; const struct firmware *firm_data = NULL; dev_info(&wac_i2c->client->dev, "%s: Start firmware flashing (UMS).\n", __func__); old_fs = get_fs(); set_fs(KERNEL_DS); fp = filp_open(WACOM_UMS_FW_PATH, O_RDONLY, S_IRUSR); if (IS_ERR(fp)) { dev_err(&wac_i2c->client->dev, "%s: failed to open %s.\n", __func__, WACOM_UMS_FW_PATH); ret = -ENOENT; goto open_err; } fsize = fp->f_path.dentry->d_inode->i_size; dev_info(&wac_i2c->client->dev, "%s: start, file path %s, size %ld Bytes\n", __func__, WACOM_UMS_FW_PATH, fsize); firm_data = kzalloc(fsize, GFP_KERNEL); if (IS_ERR(firm_data)) { dev_err(&wac_i2c->client->dev, "%s, kmalloc failed\n", __func__); ret = -EFAULT; goto malloc_error; } nread = vfs_read(fp, (char __user *)firm_data, fsize, &fp->f_pos); dev_info(&wac_i2c->client->dev, "%s: nread %ld Bytes\n", __func__, nread); if (nread != fsize) { dev_err(&wac_i2c->client->dev, "%s: failed to read firmware file, nread %ld Bytes\n", __func__, nread); ret = -EIO; goto read_err; } filp_close(fp, current->files); set_fs(old_fs); /*start firm update*/ wac_i2c->fw_data=(unsigned char *)firm_data; return 0; read_err: kfree(firm_data); malloc_error: filp_close(fp, current->files); open_err: set_fs(old_fs); return ret; } int wacom_load_fw_from_req_fw(struct wacom_i2c *wac_i2c) { int ret = 0; const struct firmware *firm_data = NULL; char fw_path[WACOM_MAX_FW_PATH]; dev_info(&wac_i2c->client->dev, "%s\n", __func__); /*Obtain MPU type: this can be manually done in user space */ dev_info(&wac_i2c->client->dev, "%s: MPU type: %x , BOOT ver: %x\n", __func__, wac_i2c->ic_mpu_ver, wac_i2c->boot_ver); memset(&fw_path, 0, WACOM_MAX_FW_PATH); if (wac_i2c->ic_mpu_ver == MPU_W9012) { snprintf(fw_path, WACOM_MAX_FW_PATH, "%s", WACOM_FW_NAME_W9012); } else { dev_info(&wac_i2c->client->dev, "%s: firmware name is NULL. return -1\n", __func__); ret = -ENOENT; goto firm_name_null_err; } ret = request_firmware(&firm_data, fw_path, &wac_i2c->client->dev); if (ret < 0) { dev_err(&wac_i2c->client->dev, "%s: Unable to open firmware. ret %d\n", __func__, ret); goto request_firm_err; } dev_info(&wac_i2c->client->dev, "%s: firmware name: %s, size: %d\n", __func__, fw_path, (int)firm_data->size); /* firmware version check */ if (wac_i2c->ic_mpu_ver == MPU_W9010 || wac_i2c->ic_mpu_ver == MPU_W9007 || wac_i2c->ic_mpu_ver == MPU_W9012) wac_i2c->wac_query_data->fw_version_bin = (firm_data->data[FIRM_VER_LB_ADDR_W9012] | (firm_data->data[FIRM_VER_UB_ADDR_W9012] << 8)); dev_info(&wac_i2c->client->dev, "%s: firmware version = %x\n", __func__, wac_i2c->wac_query_data->fw_version_bin); wac_i2c->fw_data=(unsigned char *)firm_data->data; release_firmware(firm_data); firm_name_null_err: request_firm_err: return ret; } int wacom_i2c_usermode(struct wacom_i2c *wac_i2c) { int ret; bool bRet = false; wac_i2c->compulsory_flash_mode(wac_i2c, true); ret = wacom_enter_flash_mode(wac_i2c); if (ret < 0) { dev_err(&wac_i2c->client->dev, "%s cannot send flash command at user-mode \n", __func__); return ret; } /*Return to the user mode */ dev_info(&wac_i2c->client->dev, "%s closing the boot mode \n", __func__); bRet = wacom_flash_end(wac_i2c); if (!bRet) { dev_err(&wac_i2c->client->dev, "%s closing boot mode failed \n", __func__); ret = -EXIT_FAIL_WRITING_MARK_NOT_SET; goto end_usermode; } wac_i2c->compulsory_flash_mode(wac_i2c, false); dev_info(&wac_i2c->client->dev, "%s making user-mode completed \n", __func__); ret = EXIT_OK; end_usermode: return ret; }