To apply on top of [RFC PATCH 1/1 v7]mmc: Support-FFU-for-eMMC-v5.0 Signed-off-by: Gwendal Grignou <gwendal@xxxxxxxxxxxx> --- Avi, As I mentioned earlier, I found several problems with your patch. Here is a patch that allows to go further. It is still not working reliably. The eMMC I am using has a problem being reseted after upgrade, and I am still hitting what appears to be memory corruptions. I believe the issues I am still having are due to the kernel code, not the eMMC I am testing. Gwendal. drivers/mmc/card/block.c | 3 +- drivers/mmc/card/ffu.c | 121 +++++++++++++++++++++++------------------------ drivers/mmc/core/mmc.c | 8 ++++ include/linux/mmc/card.h | 1 + include/linux/mmc/ffu.h | 13 +---- 5 files changed, 71 insertions(+), 75 deletions(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index fc65a6c..9d866fb 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -504,8 +504,7 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev, mmc_get_card(card); if (cmd.opcode == MMC_FFU_DOWNLOAD_OP) { - err = mmc_ffu_download(card, &cmd , idata->buf, - idata->buf_bytes); + err = mmc_ffu_download(card, idata->buf); goto cmd_rel_host; } diff --git a/drivers/mmc/card/ffu.c b/drivers/mmc/card/ffu.c index 04e07a5..0e85b2b 100644 --- a/drivers/mmc/card/ffu.c +++ b/drivers/mmc/card/ffu.c @@ -63,15 +63,13 @@ struct mmc_ffu_area { static void mmc_ffu_prepare_mrq(struct mmc_card *card, struct mmc_request *mrq, struct scatterlist *sg, unsigned int sg_len, - u32 arg, unsigned int blocks, unsigned int blksz, int write) { + u32 arg, unsigned int blocks, unsigned int blksz) { BUG_ON(!mrq || !mrq->cmd || !mrq->data || !mrq->stop); if (blocks > 1) { - mrq->cmd->opcode = write ? - MMC_WRITE_MULTIPLE_BLOCK : MMC_READ_MULTIPLE_BLOCK; + mrq->cmd->opcode = MMC_WRITE_MULTIPLE_BLOCK; } else { - mrq->cmd->opcode = write ? MMC_WRITE_BLOCK : - MMC_READ_SINGLE_BLOCK; + mrq->cmd->opcode = MMC_WRITE_BLOCK; } mrq->cmd->arg = arg; @@ -89,11 +87,12 @@ static void mmc_ffu_prepare_mrq(struct mmc_card *card, mrq->data->blksz = blksz; mrq->data->blocks = blocks; - mrq->data->flags = write ? MMC_DATA_WRITE : MMC_DATA_READ; + mrq->data->flags = MMC_DATA_WRITE; mrq->data->sg = sg; mrq->data->sg_len = sg_len; - mmc_set_data_timeout(mrq->data, card); } + mmc_set_data_timeout(mrq->data, card); +} /* * Checks that a normal transfer didn't have any errors @@ -153,7 +152,7 @@ static int mmc_ffu_wait_busy(struct mmc_card *card) { */ static int mmc_ffu_simple_transfer(struct mmc_card *card, struct scatterlist *sg, unsigned int sg_len, u32 arg, - unsigned int blocks, unsigned int blksz, int write) { + unsigned int blocks, unsigned int blksz) { struct mmc_request mrq = {0}; struct mmc_command cmd = {0}; struct mmc_command stop = {0}; @@ -162,8 +161,7 @@ static int mmc_ffu_simple_transfer(struct mmc_card *card, mrq.cmd = &cmd; mrq.data = &data; mrq.stop = &stop; - mmc_ffu_prepare_mrq(card, &mrq, sg, sg_len, arg, blocks, blksz, - write); + mmc_ffu_prepare_mrq(card, &mrq, sg, sg_len, arg, blocks, blksz); mmc_wait_for_req(card->host, &mrq); mmc_ffu_wait_busy(card); @@ -175,19 +173,17 @@ static int mmc_ffu_simple_transfer(struct mmc_card *card, * Map memory into a scatterlist. */ static unsigned int mmc_ffu_map_sg(struct mmc_ffu_mem *mem, int size, - struct scatterlist *sglist, unsigned int max_segs, - unsigned int max_seg_sz) + struct scatterlist *sglist) { struct scatterlist *sg = sglist; unsigned int i; unsigned long sz = size; - unsigned int sctr_len = 0; unsigned long len; - sg_init_table(sglist, max_segs); + sg_init_table(sglist, mem->cnt); - for (i = 0; i < mem->cnt && sz; i++, sz -= len) { - len = PAGE_SIZE * (1 << mem->arr[i].order); + for (i = 0; i < mem->cnt && sz; i++, sg++, sz -= len) { + len = PAGE_SIZE << mem->arr[i].order; if (len > sz) { len = sz; @@ -195,12 +191,10 @@ static unsigned int mmc_ffu_map_sg(struct mmc_ffu_mem *mem, int size, } sg_set_page(sg, mem->arr[i].page, len, 0); - sg = sg_next(sg); - sctr_len += 1; } sg_mark_end(sg); - return sctr_len; + return mem->cnt; } static void mmc_ffu_free_mem(struct mmc_ffu_mem *mem) { @@ -277,6 +271,8 @@ static struct mmc_ffu_mem *mmc_ffu_alloc_mem(unsigned long min_sz, mem->arr[mem->cnt].page = page; mem->arr[mem->cnt].order = order; mem->cnt += 1; + pr_debug("FFU: cnt: %d - order %d\n", mem->cnt, order); + if (max_page_cnt <= (1UL << order)) break; max_page_cnt -= 1UL << order; @@ -298,11 +294,11 @@ out_free: * Copy the data to the allocated pages. */ static int mmc_ffu_area_init(struct mmc_ffu_area *area, struct mmc_card *card, - u8 *data, int size) + const u8 *data, int size) { int ret; int i; - int length = 0; + int length = 0, page_length; area->max_tfr = size; @@ -323,20 +319,20 @@ static int mmc_ffu_area_init(struct mmc_ffu_area *area, struct mmc_card *card, goto out_free; } + page_length = PAGE_SIZE << area->mem->arr[i].order; memcpy(page_address(area->mem->arr[i].page), data + length, - min(size - length, (int)area->max_seg_sz)); - length += area->max_seg_sz; + min(size - length, page_length)); + length += page_length; } area->sg = kmalloc(sizeof(struct scatterlist) * area->mem->cnt, - GFP_KERNEL); + GFP_KERNEL); if (!area->sg) { ret = -ENOMEM; goto out_free; } - area->sg_len = mmc_ffu_map_sg(area->mem, size, area->sg, - area->max_segs, area->mem->cnt); + area->sg_len = mmc_ffu_map_sg(area->mem, size, area->sg); return 0; @@ -345,7 +341,7 @@ out_free: return ret; } -static int mmc_ffu_write(struct mmc_card *card, u8 *src, u32 arg, +static int mmc_ffu_write(struct mmc_card *card, const u8 *src, u32 arg, int size) { int rc; @@ -370,7 +366,8 @@ static int mmc_ffu_write(struct mmc_card *card, u8 *src, u32 arg, goto exit; rc = mmc_ffu_simple_transfer(card, area.sg, area.sg_len, arg, - max_tfr / CARD_BLOCK_SIZE, CARD_BLOCK_SIZE, 1); + max_tfr / CARD_BLOCK_SIZE, CARD_BLOCK_SIZE); + mmc_ffu_area_cleanup(&area); if (rc != 0) goto exit; @@ -379,7 +376,6 @@ static int mmc_ffu_write(struct mmc_card *card, u8 *src, u32 arg, } while (size > 0); exit: - mmc_ffu_area_cleanup(&area); return rc; } @@ -406,47 +402,39 @@ exit: return err; } -int mmc_ffu_download(struct mmc_card *card, struct mmc_command *cmd, - u8 *data, int buf_bytes) +int mmc_ffu_download(struct mmc_card *card, const char *name) { u8 ext_csd[CARD_BLOCK_SIZE]; int err; int ret; - u8 *buf = NULL; + u32 arg; const struct firmware *fw; - /* Read the EXT_CSD */ - err = mmc_send_ext_csd(card, ext_csd); - if (err) { - pr_err("FFU: %s: error %d sending ext_csd\n", - mmc_hostname(card->host), err); + pr_debug("FFU: %s uploading firmware %.20s to device\n", + mmc_hostname(card->host), name); + + if (strlen(name) > 512) { + err = -EINVAL; + pr_err("FFU: %s: %.20s is not a valid argument\n", + mmc_hostname(card->host), name); goto exit; } - /* check if card is eMMC 5.0 or higher */ - if (card->ext_csd.rev < 7) - return -EINVAL; - /* Check if FFU is supported */ - if (!FFU_SUPPORTED_MODE(ext_csd[EXT_CSD_SUPPORTED_MODE]) || - FFU_CONFIG(ext_csd[EXT_CSD_FW_CONFIG])) { - err = -EINVAL; + if (!card->ext_csd.ffu_capable) { + err = -EOPNOTSUPP; pr_err("FFU: %s: error %d FFU is not supported\n", mmc_hostname(card->host), err); goto exit; } - /* setup FW data buffer */ - err = request_firmware(&fw, data, &card->dev); + /* setup FW name buffer */ + mmc_put_card(card); + err = request_firmware(&fw, name, &card->dev); + mmc_get_card(card); if (err) { pr_err("Firmware request failed %d\n", err); - goto exit_normal; - } - - buf = kmalloc(fw->size, GFP_KERNEL); - if (buf == NULL) { - pr_err("Allocating memory for firmware failed!\n"); - goto exit_normal; + goto exit; } if ((fw->size % CARD_BLOCK_SIZE)) { @@ -454,9 +442,16 @@ int mmc_ffu_download(struct mmc_card *card, struct mmc_command *cmd, mmc_hostname(card->host), fw->size); } - memcpy(buf, fw->data, fw->size); + /* Read the EXT_CSD */ + err = mmc_send_ext_csd(card, ext_csd); + if (err) { + pr_err("FFU: %s: error %d sending ext_csd\n", + mmc_hostname(card->host), err); + goto exit; + } /* set device to FFU mode */ + pr_debug("FFU: %s switch to FFU mode\n", mmc_hostname(card->host)); err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_MODE_CONFIG, MMC_FFU_MODE_SET, card->ext_csd.generic_cmd6_time); if (err) { @@ -466,18 +461,18 @@ int mmc_ffu_download(struct mmc_card *card, struct mmc_command *cmd, } /* set CMD ARG */ - cmd->arg = ext_csd[EXT_CSD_FFU_ARG] | + arg = ext_csd[EXT_CSD_FFU_ARG] | ext_csd[EXT_CSD_FFU_ARG + 1] << 8 | ext_csd[EXT_CSD_FFU_ARG + 2] << 16 | ext_csd[EXT_CSD_FFU_ARG + 3] << 24; - err = mmc_ffu_write(card, buf, cmd->arg, (int)fw->size); + err = mmc_ffu_write(card, fw->data, arg, (int)fw->size); exit_normal: release_firmware(fw); - kfree(buf); /* host switch back to work in normal MMC Read/Write commands */ + pr_debug("FFU: %s switch to normal mode\n", mmc_hostname(card->host)); ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_MODE_CONFIG, MMC_FFU_MODE_NORMAL, card->ext_csd.generic_cmd6_time); @@ -495,6 +490,8 @@ int mmc_ffu_install(struct mmc_card *card) u32 ffu_data_len; u32 timeout; + pr_debug("FFU: %s installing firmware to device\n", + mmc_hostname(card->host)); err = mmc_send_ext_csd(card, ext_csd); if (err) { pr_err("FFU: %s: error %d sending ext_csd\n", @@ -503,9 +500,8 @@ int mmc_ffu_install(struct mmc_card *card) } /* Check if FFU is supported */ - if (!FFU_SUPPORTED_MODE(ext_csd[EXT_CSD_SUPPORTED_MODE]) || - FFU_CONFIG(ext_csd[EXT_CSD_FW_CONFIG])) { - err = -EINVAL; + if (!card->ext_csd.ffu_capable) { + err = -EOPNOTSUPP; pr_err("FFU: %s: error %d FFU is not supported\n", mmc_hostname(card->host), err); goto exit; @@ -514,19 +510,20 @@ int mmc_ffu_install(struct mmc_card *card) /* check mode operation */ if (!FFU_FEATURES(ext_csd[EXT_CSD_FFU_FEATURES])) { /* restart the eMMC */ + mmc_put_card(card); err = mmc_ffu_restart(card); + mmc_get_card(card); if (err) { pr_err("FFU: %s: error %d FFU install:\n", mmc_hostname(card->host), err); } } else { - ffu_data_len = ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG]| ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG + 1] << 8 | ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG + 2] << 16 | ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG + 3] << 24; - if (!ffu_data_len) { + if (ffu_data_len == 0) { err = -EPERM; return err; } diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 4099424..ace9123 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -552,6 +552,14 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) card->ext_csd.data_sector_size = 512; } + /* eMMC v5 or later */ + if (card->ext_csd.rev >= 7) { + card->ext_csd.ffu_capable = + ((ext_csd[EXT_CSD_SUPPORTED_MODE] & 1) == 1) && + ((ext_csd[EXT_CSD_FW_CONFIG] & 1) == 0); + } else { + card->ext_csd.ffu_capable = false; + } out: return err; } diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 6a5c754..6a7de8d 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -87,6 +87,7 @@ struct mmc_ext_csd { unsigned int data_tag_unit_size; /* DATA TAG UNIT size */ unsigned int boot_ro_lock; /* ro lock support */ bool boot_ro_lockable; + bool ffu_capable; /* support Firmware Field Upgrade */ u8 raw_exception_status; /* 54 */ u8 raw_partition_support; /* 160 */ u8 raw_rpmb_size_mult; /* 168 */ diff --git a/include/linux/mmc/ffu.h b/include/linux/mmc/ffu.h index f5dcedb..a0ade1b 100644 --- a/include/linux/mmc/ffu.h +++ b/include/linux/mmc/ffu.h @@ -34,19 +34,10 @@ #define MMC_FFU_INSTALL_SET 0x1 #ifdef CONFIG_MMC_FFU -#define MMC_FFU_ENABLE 0x0 -#define MMC_FFU_CONFIG 0x1 -#define MMC_FFU_SUPPORTED_MODES 0x1 #define MMC_FFU_FEATURES 0x1 +#define FFU_FEATURES(ffu_features) (ffu_features & MMC_FFU_FEATURES) -#define FFU_ENABLED(ffu_enable) (ffu_enable & MMC_FFU_CONFIG) -#define FFU_SUPPORTED_MODE(ffu_sup_mode) \ - (ffu_sup_mode && MMC_FFU_SUPPORTED_MODES) -#define FFU_CONFIG(ffu_config) (ffu_config & MMC_FFU_CONFIG) -#define FFU_FEATURES(ffu_fetures) (ffu_fetures & MMC_FFU_FEATURES) - -int mmc_ffu_download(struct mmc_card *card, struct mmc_command *cmd, - u8 *data, int buf_bytes); +int mmc_ffu_download(struct mmc_card *card, const char *name); int mmc_ffu_install(struct mmc_card *card); #else static inline int mmc_ffu_download(struct mmc_card *card, -- 2.0.0.526.g5318336 -- 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