Some of Samsung eMMC does not show the argument for ffu in ext_csd. In case of this, eMMC shows 0x0 from ext_csd, Host has to modify the argument. Add "#define CID_MANFID_SAMSUNG 0x15" > +int mmc_ffu_download(struct mmc_card *card, struct mmc_command *cmd, > + u8 *data, int buf_bytes) > +{ > + u8 ext_csd[CARD_BLOCK_SIZE]; > + int err; > + int ret; > + > + /* 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; > + } > + > + /* Check if FFU is supported by card */ > + if (!FFU_SUPPORTED_MODE(ext_csd[EXT_CSD_SUPPORTED_MODE])) { > + err = -EINVAL; > + pr_err("FFU: %s: error %d FFU is not supported\n", > + mmc_hostname(card->host), err); > + goto exit; > + } > + > + err = mmc_host_set_ffu(card, ext_csd[EXT_CSD_FW_CONFIG]); > + if (err) { > + pr_err("FFU: %s: error %d FFU is not supported\n", > + mmc_hostname(card->host), err); > + err = -EINVAL; > + goto exit; > + } > + > + /* set device to FFU mode */ > + 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) { > + pr_err("FFU: %s: error %d FFU is not supported\n", > + mmc_hostname(card->host), err); > + goto exit_normal; > + } > + > + /* set CMD ARG */ > + cmd->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; > + Add followings " /* If arg is zero, should be set to a special value for samsung eMMC */ if ( card->cid.manfid == CID_MANFID_SAMSUNG && cmd->arg == 0x0 ) { cmd->arg = 0xc7810000; } " > + err = mmc_ffu_write(card, data, cmd->arg, buf_bytes); > + > +exit_normal: > + /* host switch back to work in normal MMC Read/Write commands */ > + ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, > + EXT_CSD_MODE_CONFIG, MMC_FFU_MODE_NORMAL, > + card->ext_csd.generic_cmd6_time); > + if (ret) > + err = ret; > + > +exit: > + return err; > +} > +EXPORT_SYMBOL(mmc_ffu_download); > + eMMC 5.0 Spec. says if device does not support MODE_OPERATION_CODES, device doesn't need to use NUMBER_OF_FW_SECTORS_CORRECTLY_PROGRAMMED. So, it's better to move the code for checking this value to FFU_FEATURES(ext_csd[EXT_CSD_FFU_FEATURES] > +int mmc_ffu_install(struct mmc_card *card) { > + u8 ext_csd[CARD_BLOCK_SIZE]; > + int err; > + u32 ffu_data_len; > + u32 timeout; > + > + 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; > + } > + > + /* 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; > + pr_err("FFU: %s: error %d FFU is not supported\n", > + mmc_hostname(card->host), err); > + goto exit; > + } Remove followings " 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) { err = -EPERM; return err; } " > + > + /* check mode operation */ > + if (!FFU_FEATURES(ext_csd[EXT_CSD_FFU_FEATURES])) { > + /* restart the eMMC */ > + err = mmc_ffu_restart(card); > + if (err) { > + pr_err("FFU: %s: error %d FFU install:\n", > + mmc_hostname(card->host), err); > + } > + } else { Add followings " 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) { err = -EPERM; return err; } " > + /* set device to FFU mode */ > + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, > + EXT_CSD_MODE_CONFIG, 0x1, > + card->ext_csd.generic_cmd6_time); > + if (err) { > + pr_err("FFU: %s: error %d FFU is not supported\n", > + mmc_hostname(card->host), err); > + goto exit; > + } Checking ffu status in ext_csd should be done even if device does not support MODE_OPERATION_CODES So, it's better to move the code for checking this value to out of brace for FFU_FEATURES(ext_csd[EXT_CSD_FFU_FEATURES] > + /* set ext_csd to install mode */ > + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, > + EXT_CSD_MODE_OPERATION_CODES, > + MMC_FFU_INSTALL_SET, timeout); > + > + if (err) { > + pr_err("FFU: %s: error %d setting install mode\n", > + mmc_hostname(card->host), err); > + goto exit; > + } > + Remove followings " /* read 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; } /* return status */ err = ext_csd[EXT_CSD_FFU_STATUS]; if (err) { pr_err("FFU: %s: error %d FFU install:\n", mmc_hostname(card->host), err); err = -EINVAL; goto exit; } " > + } > + Add followings " /* read 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; } /* return status */ err = ext_csd[EXT_CSD_FFU_STATUS]; if (err) { pr_err("FFU: %s: error %d FFU install:\n", mmc_hostname(card->host), err); err = -EINVAL; goto exit; } " > +exit: > + return err; > +} > +EXPORT_SYMBOL(mmc_ffu_install); > + And some device's fw should be transferred with one command. They does not support multiple commands for fw transfer. For these devices, MMC_IOC_MAX_BYTES should be greater. diff --git a/include/uapi/linux/mmc/ioctl.h b/include/uapi/linux/mmc/ioctl.h index 1f5e689..af9ea62 100644 --- a/include/uapi/linux/mmc/ioctl.h +++ b/include/uapi/linux/mmc/ioctl.h @@ -53,5 +53,5 @@ struct mmc_ioc_cmd { * is enforced per ioctl call. For larger data transfers, use the normal * block device operations. */ -#define MMC_IOC_MAX_BYTES (512L * 256) +#define MMC_IOC_MAX_BYTES (512L * 1024) #endif /* LINUX_MMC_IOCTL_H */ -- 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