Until now the FW was written in a Single Write Command (CMD24) mode. In order to write the firmware, the host sends hundreds of CMD24. By using new mode (closed ended multiple write: CMD23,CMD25), the number of write commands is reduced to few commands. In case the host doesn't support closed ended writing, flash the ffu by single sector writing. By set [sector] to 1 or omit this parameter. Signed-off-by: Arthur Simchaev <Arthur.Simchaev@xxxxxxxxxxx> --- mmc.c | 10 +++- mmc.h | 1 + mmc_cmds.c | 184 ++++++++++++++++++++++++++++++++++++++++++++---------------- 3 files changed, 146 insertions(+), 49 deletions(-) diff --git a/mmc.c b/mmc.c index 50c9c9e..069ce9f 100644 --- a/mmc.c +++ b/mmc.c @@ -211,8 +211,14 @@ static struct Command commands[] = { NULL }, { do_ffu, -2, - "ffu", "<image name> <device>\n" - "Run Field Firmware Update with <image name> on <device>.\n", + "ffu", "<image name> <device> [sectors]\n" + "Run Field Firmware Update with <image name> on <device>\n" + "Write FFU file into device with number of blocks = <sectors>\n" + "[sectors] is optional ,default value of the <sector> is 1 sector.\n" + "In case the platform has [sectors] limitation\n" + "(e.g. limited by hw, unsupported close ended writing),\n" + "use default (one sector) command.\n" + "[sectors] parameter cannot be more then MMC_IOC_MAX_BYTES/device +sector size\n", NULL }, { 0, 0, 0, 0 } diff --git a/mmc.h b/mmc.h index a3d732c..89ebb9f 100644 --- a/mmc.h +++ b/mmc.h @@ -37,6 +37,7 @@ #define R1_SWITCH_ERROR (1 << 7) /* sx, c */ #define MMC_SWITCH_MODE_WRITE_BYTE 0x03 /* Set target to value */ #define MMC_READ_MULTIPLE_BLOCK 18 /* adtc [31:0] data addr R1 */ +#define MMC_SET_BLOCK_COUNT 23 /* adtc [31:0] data addr R1 */ #define MMC_WRITE_BLOCK 24 /* adtc [31:0] data addr R1 */ #define MMC_WRITE_MULTIPLE_BLOCK 25 /* adtc R1 */ #define MMC_SET_WRITE_PROT 28 /* ac [31:0] data addr R1b */ diff --git a/mmc_cmds.c b/mmc_cmds.c index 6b86115..c0df757 100644 --- a/mmc_cmds.c +++ b/mmc_cmds.c @@ -2357,18 +2357,21 @@ int do_ffu(int nargs, char **argv) exit(1); #else int dev_fd, img_fd; - int sect_done = 0, retry = 3, ret = -EINVAL; + int sect_done = 0, retry = 5, ret = -EINVAL; + int num_ioctl_blks = 0; unsigned int sect_size; __u8 ext_csd[512]; - __u8 *buf; - __u32 arg; + __u8 *buf = NULL; + __u32 arg = 0; off_t fw_size; - ssize_t chunk_size; + int remain_fw_size = 0; + ssize_t chunk_size = 0; char *device; - struct mmc_ioc_multi_cmd *multi_cmd; - - CHECK(nargs != 3, "Usage: ffu <image name> </path/to/mmcblkX> \n", - exit(1)); + struct mmc_ioc_multi_cmd *multi_cmd = NULL; + int bl_counter = 1; + CHECK(nargs != 3 && nargs != 4, + "Usage: ffu <image name> </path/to/mmcblkX> <sectors>\n", + exit(1)); device = argv[2]; dev_fd = open(device, O_RDWR); @@ -2383,10 +2386,23 @@ int do_ffu(int nargs, char **argv) exit(1); } - buf = malloc(512); - multi_cmd = calloc(1, sizeof(struct mmc_ioc_multi_cmd) + - 3 * sizeof(struct mmc_ioc_cmd)); - if (!buf || !multi_cmd) { + if (nargs == 3) + bl_counter = 1; + else + bl_counter = atoi(argv[3]); + if (bl_counter <= 0) { + fprintf(stderr, "Wrong <sector> value %d\n", bl_counter); + goto out; + } + + if (bl_counter == 1) + multi_cmd = calloc(1, sizeof(struct mmc_ioc_multi_cmd) + + 3 * sizeof(struct mmc_ioc_cmd)); + else/* using multiple write cmd */ + multi_cmd = calloc(1, sizeof(struct mmc_ioc_multi_cmd) + + 4 * sizeof(struct mmc_ioc_cmd)); + + if (!multi_cmd) { perror("failed to allocate memory"); goto out; } @@ -2427,55 +2443,119 @@ int do_ffu(int nargs, char **argv) goto out; } + if (sect_size * bl_counter > MMC_IOC_MAX_BYTES) { + fprintf(stderr, "<sectors> parameter cannot be more when %d\n", + (int) MMC_IOC_MAX_BYTES/sect_size); + goto out; + } + + buf = calloc(1, bl_counter * sect_size); + if (!buf) { + perror("failed to allocate memory"); + goto out; + } /* set CMD ARG */ arg = ext_csd[EXT_CSD_FFU_ARG_0] | ext_csd[EXT_CSD_FFU_ARG_1] << 8 | ext_csd[EXT_CSD_FFU_ARG_2] << 16 | ext_csd[EXT_CSD_FFU_ARG_3] << 24; + /* In this case single write cmd will be use + * Needed for the platform which doesn't support + * close ended writing */ + if (bl_counter == 1) { + multi_cmd->num_of_cmds = 3; + /* put device into ffu mode */ + multi_cmd->cmds[0].opcode = MMC_SWITCH; + multi_cmd->cmds[0].arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | + (EXT_CSD_MODE_CONFIG << 16) | + (EXT_CSD_FFU_MODE << 8) | + EXT_CSD_CMD_SET_NORMAL; + multi_cmd->cmds[0].flags = MMC_RSP_SPI_R1B | + MMC_RSP_R1B | + MMC_CMD_AC; + multi_cmd->cmds[0].write_flag = 1; + + /* send image chunk */ + multi_cmd->cmds[1].opcode = MMC_WRITE_BLOCK; + multi_cmd->cmds[1].blksz = sect_size; + multi_cmd->cmds[1].blocks = 1; + multi_cmd->cmds[1].arg = arg; + multi_cmd->cmds[1].flags = MMC_RSP_SPI_R1 | + MMC_RSP_R1 | + MMC_CMD_ADTC; + multi_cmd->cmds[1].write_flag = 1; + mmc_ioc_cmd_set_data(multi_cmd->cmds[1], buf); - /* prepare multi_cmd to be sent */ - multi_cmd->num_of_cmds = 3; + /* return device into normal mode */ + multi_cmd->cmds[2].opcode = MMC_SWITCH; + multi_cmd->cmds[2].arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | + (EXT_CSD_MODE_CONFIG << 16) | + (EXT_CSD_NORMAL_MODE << 8) | + EXT_CSD_CMD_SET_NORMAL; + multi_cmd->cmds[2].flags = MMC_RSP_SPI_R1B | + MMC_RSP_R1B | + MMC_CMD_AC; + multi_cmd->cmds[2].write_flag = 1; + } else { + /* prepare multi_cmd to be sent */ + multi_cmd->num_of_cmds = 4; + /* put device into ffu mode */ + multi_cmd->cmds[0].opcode = MMC_SWITCH; + multi_cmd->cmds[0].arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | + (EXT_CSD_MODE_CONFIG << 16) | + (EXT_CSD_FFU_MODE << 8) | + EXT_CSD_CMD_SET_NORMAL; + multi_cmd->cmds[0].flags = MMC_RSP_SPI_R1B | + MMC_RSP_R1B | + MMC_CMD_AC; + multi_cmd->cmds[0].write_flag = 1; + + multi_cmd->cmds[1].opcode = MMC_SET_BLOCK_COUNT; + multi_cmd->cmds[1].arg = bl_counter & 0x0000FFFF; + multi_cmd->cmds[1].flags = MMC_RSP_SPI_R1 + |MMC_RSP_R1 | + MMC_CMD_AC; + multi_cmd->cmds[1].write_flag = 1; - /* put device into ffu mode */ - multi_cmd->cmds[0].opcode = MMC_SWITCH; - multi_cmd->cmds[0].arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | - (EXT_CSD_MODE_CONFIG << 16) | - (EXT_CSD_FFU_MODE << 8) | - EXT_CSD_CMD_SET_NORMAL; - multi_cmd->cmds[0].flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; - multi_cmd->cmds[0].write_flag = 1; - - /* send image chunk */ - multi_cmd->cmds[1].opcode = MMC_WRITE_BLOCK; - multi_cmd->cmds[1].blksz = sect_size; - multi_cmd->cmds[1].blocks = 1; - multi_cmd->cmds[1].arg = arg; - multi_cmd->cmds[1].flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC; - multi_cmd->cmds[1].write_flag = 1; - mmc_ioc_cmd_set_data(multi_cmd->cmds[1], buf); - - /* return device into normal mode */ - multi_cmd->cmds[2].opcode = MMC_SWITCH; - multi_cmd->cmds[2].arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | + multi_cmd->cmds[2].opcode = MMC_WRITE_MULTIPLE_BLOCK; + multi_cmd->cmds[2].blksz = sect_size; + multi_cmd->cmds[2].blocks = bl_counter; + multi_cmd->cmds[2].arg = arg; + multi_cmd->cmds[2].flags = MMC_RSP_SPI_R1 | + MMC_RSP_R1 | + MMC_CMD_ADTC; + multi_cmd->cmds[2].write_flag = 1; + mmc_ioc_cmd_set_data(multi_cmd->cmds[2], buf); + + multi_cmd->cmds[3].opcode = MMC_SWITCH; + multi_cmd->cmds[3].arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | (EXT_CSD_MODE_CONFIG << 16) | (EXT_CSD_NORMAL_MODE << 8) | EXT_CSD_CMD_SET_NORMAL; - multi_cmd->cmds[2].flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; - multi_cmd->cmds[2].write_flag = 1; + multi_cmd->cmds[3].flags = MMC_RSP_SPI_R1B | + MMC_RSP_R1B | + MMC_CMD_AC; + multi_cmd->cmds[3].write_flag = 1; + } do_retry: /* read firmware chunk */ lseek(img_fd, 0, SEEK_SET); - chunk_size = read(img_fd, buf, 512); + num_ioctl_blks = bl_counter; + chunk_size = read(img_fd, buf, num_ioctl_blks * sect_size); + remain_fw_size = fw_size - chunk_size; while (chunk_size > 0) { /* send ioctl with multi-cmd */ ret = ioctl(dev_fd, MMC_IOC_MULTI_CMD, multi_cmd); if (ret) { - perror("Multi-cmd ioctl"); + perror("Multi-cmd ioctl failed."); /* In case multi-cmd ioctl failed before exiting from ffu mode */ - ioctl(dev_fd, MMC_IOC_CMD, &multi_cmd->cmds[2]); + if (bl_counter == 1) + ioctl(dev_fd, MMC_IOC_CMD, &multi_cmd->cmds[2]); + else + ioctl(dev_fd, MMC_IOC_CMD, &multi_cmd->cmds[3]); goto out; } @@ -2494,7 +2574,6 @@ do_retry: if (sect_done == 0) { if (retry > 0) { retry--; - fprintf(stderr, "Programming failed. Retrying... (%d)\n", retry); goto do_retry; } fprintf(stderr, "Programming failed! Aborting...\n"); @@ -2502,14 +2581,25 @@ do_retry: } else { fprintf(stderr, "Programmed %d/%jd bytes\r", sect_done * sect_size, (intmax_t)fw_size); } + memset(buf, 0, num_ioctl_blks); + if (remain_fw_size >= bl_counter * sect_size) + num_ioctl_blks = bl_counter; + else + num_ioctl_blks = remain_fw_size / sect_size; /* read the next firmware chunk (if any) */ - chunk_size = read(img_fd, buf, 512); + chunk_size = read(img_fd, buf, num_ioctl_blks * sect_size); + if (chunk_size > 0) + remain_fw_size = remain_fw_size - chunk_size; + /* Update the values for Multiple Write CMD */ + if (bl_counter > 1) { + multi_cmd->cmds[1].arg = num_ioctl_blks & 0x0000FFFF; + multi_cmd->cmds[2].blocks = num_ioctl_blks; + } } - if ((sect_done * sect_size) == fw_size) { fprintf(stderr, "Programmed %jd/%jd bytes\n", (intmax_t)fw_size, (intmax_t)fw_size); - fprintf(stderr, "Programming finished with status %d \n", ret); + fprintf(stderr, "Programming finished with status %d\n", ret); } else { fprintf(stderr, "FW size and number of sectors written mismatch. Status return %d\n", ret); @@ -2529,9 +2619,9 @@ do_retry: multi_cmd->cmds[1].blksz = 0; multi_cmd->cmds[1].blocks = 0; multi_cmd->cmds[1].arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | - (EXT_CSD_MODE_OPERATION_CODES << 16) | - (EXT_CSD_FFU_INSTALL << 8) | - EXT_CSD_CMD_SET_NORMAL; + (EXT_CSD_MODE_OPERATION_CODES << 16) | + (EXT_CSD_FFU_INSTALL << 8) | + EXT_CSD_CMD_SET_NORMAL; multi_cmd->cmds[1].flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; multi_cmd->cmds[1].write_flag = 1; -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html