[mmc] Add Field Firmware Upgrade command
Add ffu command for updating eMMC firmware.
The kernel must have support for 2 new ioctls:
MMC_FFU_DOWNLOAD_OP and MMC_FFU_INSTALL_OP.
TEST=Upgrading Hynix eMMC on gnawty
BUG=None
Change-Id: I10b846a56f80b72775da94365c3269571dfa632f
Reviewed-on: https://chromium-review.googlesource.com/206888
Reviewed-by: Puthikorn Voravootivat <puthik@chromium.org>
Commit-Queue: Gwendal Grignou <gwendal@chromium.org>
Tested-by: Gwendal Grignou <gwendal@chromium.org>
diff --git a/mmc.c b/mmc.c
index a30426d..ffddb77 100644
--- a/mmc.c
+++ b/mmc.c
@@ -115,6 +115,11 @@
"Send Sanitize command to the <device>.\nThis will delete the unmapped memory region of the device.",
NULL
},
+ { do_emmc50_ffu, -2,
+ "ffu", "<image name> <device>\n"
+ "run eMMC 5.0 Field firmware update.\n.",
+ NULL
+ },
{ 0, 0, 0, 0 }
};
diff --git a/mmc.h b/mmc.h
index 9871d62..b338e1d 100644
--- a/mmc.h
+++ b/mmc.h
@@ -64,6 +64,7 @@
#define EXT_CSD_ENH_START_ADDR_2 138
#define EXT_CSD_ENH_START_ADDR_1 137
#define EXT_CSD_ENH_START_ADDR_0 136
+#define EXT_CSD_REV 192
#define EXT_CSD_NATIVE_SECTOR_SIZE 63 /* R */
#define EXT_CSD_USE_NATIVE_SECTOR 62 /* R/W */
#define EXT_CSD_DATA_SECTOR_SIZE 61 /* R */
@@ -79,6 +80,9 @@
*/
#define BKOPS_ENABLE (1<<0)
+#define MMC_FFU_DOWNLOAD_OP 302
+#define MMC_FFU_INSTALL_OP 303
+
/*
* EXT_CSD field definitions
*/
diff --git a/mmc_cmds.c b/mmc_cmds.c
index 3fda502..1793da6 100644
--- a/mmc_cmds.c
+++ b/mmc_cmds.c
@@ -14,10 +14,12 @@
* Boston, MA 021110-1307, USA.
*/
+#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
+#include <sys/param.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
@@ -31,6 +33,7 @@
#include "mmc_cmds.h"
#define EXT_CSD_SIZE 512
+#define FFU_PATH_SIZE (512 - 1)
#define CID_SIZE 16
@@ -1315,3 +1318,69 @@
}
+static int ffu_download_image(char *fname, int mmc_fd)
+{
+ struct mmc_ioc_cmd mmc_ioc_cmd;
+
+ /* prepare and send ioctl */
+ memset(&mmc_ioc_cmd, 0, sizeof(mmc_ioc_cmd));
+ mmc_ioc_cmd.opcode = MMC_FFU_DOWNLOAD_OP;
+ mmc_ioc_cmd.blksz = MIN(strlen(fname), FFU_PATH_SIZE);
+ mmc_ioc_cmd.blocks = 1;
+ mmc_ioc_cmd.arg = 0;
+ mmc_ioc_cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
+ mmc_ioc_cmd.write_flag = 1;
+ mmc_ioc_cmd_set_data(mmc_ioc_cmd, fname);
+ return ioctl(mmc_fd, MMC_IOC_CMD, &mmc_ioc_cmd);
+}
+
+static int ffu_install(int mmc_fd)
+{
+ struct mmc_ioc_cmd mmc_ioc_cmd;
+
+ memset(&mmc_ioc_cmd, 0, sizeof(mmc_ioc_cmd));
+ mmc_ioc_cmd.opcode = MMC_FFU_INSTALL_OP;
+ mmc_ioc_cmd.blocks = 0;
+ mmc_ioc_cmd.arg = 0;
+ mmc_ioc_cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
+ mmc_ioc_cmd.write_flag = 0;
+ return ioctl(mmc_fd, MMC_IOC_CMD, &mmc_ioc_cmd);
+}
+
+int do_emmc50_ffu (int nargs, char **argv)
+{
+ int fd, ret;
+ char *device;
+ char *path;
+
+ CHECK(nargs != 3, "Usage: ffu <image name> </path/to/mmcblkX> \n",
+ exit(1));
+
+ path = argv[1];
+ if (strlen(path) > FFU_PATH_SIZE) {
+ fprintf(stderr, "Filename \"%.20s\" too long\n", path);
+ exit(1);
+ }
+ device = argv[2];
+ fd = open(device, O_RDWR);
+ if (fd < 0) {
+ perror("open");
+ exit(1);
+ }
+
+ ret = ffu_download_image(path, fd);
+ if (ret) {
+ fprintf(stderr, "FFU download failed : %s\n", strerror(errno));
+ exit(1);
+ }
+
+ ret = ffu_install(fd);
+ if (ret) {
+ fprintf(stderr, "FFU install failed : %s\n", strerror(errno));
+ exit(1);
+ }
+
+ close(fd);
+ return 0;
+}
+
diff --git a/mmc_cmds.h b/mmc_cmds.h
index f023fb0..4c7b6ed 100644
--- a/mmc_cmds.h
+++ b/mmc_cmds.h
@@ -29,3 +29,4 @@
int do_status_get(int nargs, char **argv);
int do_enh_area_set(int nargs, char **argv);
int do_write_reliability_set(int nargs, char **argv);
+int do_emmc50_ffu(int nargs, char **argv);