@@ -57,6 +57,22 @@ struct mmc_ffu_area {
};
/*
+ * Get hack value
+ */
+static const struct mmc_ffu_hack *mmc_get_hack(
+ const struct mmc_ffu_args *args,
+ enum mmc_ffu_hack_type type)
+{
+ int i;
+
+ for (i = 0; i < args->ack_nb; i++) {
+ if (args->hack[i].type == type)
+ return &args->hack[i];
+ }
+ return NULL;
+}
+
+/*
* Map memory into a scatterlist.
*/
static unsigned int mmc_ffu_map_sg(struct mmc_ffu_mem *mem, int size,
@@ -350,13 +366,15 @@ static int mmc_ffu_install(struct mmc_card *card, u8 **ext_csd)
return 0;
}
-int mmc_ffu_invoke(struct mmc_card *card, const char *name)
+int mmc_ffu_invoke(struct mmc_card *card, const struct mmc_ffu_args *args)
{
u8 *ext_csd = NULL;
int err;
u32 arg;
u32 fw_prog_bytes;
const struct firmware *fw;
+ const struct mmc_ffu_hack *hack;
+
/* Check if FFU is supported */
if (!card->ext_csd.ffu_capable) {
@@ -366,14 +384,14 @@ int mmc_ffu_invoke(struct mmc_card *card, const char *name)
return -EOPNOTSUPP;
}
- if (strlen(name) > 512) {
+ if (strlen(args->name) > 512) {
pr_err("FFU: %s: name %.20s is too long.\n",
- mmc_hostname(card->host), name);
+ mmc_hostname(card->host), args->name);
return -EINVAL;
}
/* setup FW data buffer */
- err = request_firmware(&fw, name, &card->dev);
+ err = request_firmware(&fw, args->name, &card->dev);
if (err) {
pr_err("FFU: %s: Firmware request failed %d\n",
mmc_hostname(card->host), err);
@@ -404,10 +422,15 @@ int mmc_ffu_invoke(struct mmc_card *card, const char *name)
}
/* set CMD 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;
+ hack = mmc_get_hack(args, MMC_OVERRIDE_FFU_ARG);
+ if (hack == NULL) {
+ 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;
+ } else {
+ arg = cpu_to_le32(hack->value);
+ }
/* set device to FFU mode */
err = mmc_ffu_switch_mode(card, MMC_FFU_MODE_SET);
@@ -29,18 +29,37 @@
#define MMC_FFU_MODE_NORMAL 0x0
#define MMC_FFU_INSTALL_SET 0x2
+#define FFU_NAME_LEN 80 /* Name of the firmware file udev should find */
+
+enum mmc_ffu_hack_type {
+ MMC_OVERRIDE_FFU_ARG = 0,
+ MMC_HACK_LEN,
+};
+
+struct mmc_ffu_hack {
+ enum mmc_ffu_hack_type type;
+ u64 value;
+};
+
+struct mmc_ffu_args {
+ char name[FFU_NAME_LEN];
+ u32 ack_nb;
+ struct mmc_ffu_hack hack[0];
+};
+
#ifdef CONFIG_MMC_FFU
#define MMC_FFU_FEATURES 0x1
#define FFU_FEATURES(ffu_features) (ffu_features & MMC_FFU_FEATURES)
-int mmc_ffu_invoke(struct mmc_card *card, const char *name);
+int mmc_ffu_invoke(struct mmc_card *card, const struct mmc_ffu_args *args);
#else
-static inline int mmc_ffu_invoke(struct mmc_card *card, const char *name)
+static inline int mmc_ffu_invoke(struct mmc_card *card,
+ const struct mmc_ffu_args *args)
{
return -EOPNOTSUPP;
}
#endif
-#endif /* FFU_H_ */
+#endif /* _FFU_H_ */
Samsung eMMC5.0 is not fully Jedec compliant: CMD25 write argument is a fixed value not from FFU_ARG. Signed-off-by: Gwendal Grignou <gwendal@chromium.org> --- drivers/mmc/core/ffu.c | 39 +++++++++++++++++++++++++++++++-------- include/linux/mmc/ffu.h | 25 ++++++++++++++++++++++--- 2 files changed, 53 insertions(+), 11 deletions(-)