[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_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;
+}
+