mmc-utils: Add command to set the boot bus conditions

This was added because some eMMC devices had the boot bus conditions
set incorrectly causing them to hang on boot.

Signed-off-by: Al Cooper <acooperx@gmail.com>
Signed-off-by: Chris Ball <chris@printf.net>
diff --git a/mmc.c b/mmc.c
index e0667fc..2adf15c 100644
--- a/mmc.c
+++ b/mmc.c
@@ -95,6 +95,14 @@
 		"Enable the boot partition for the <device>.\nTo receive acknowledgment of boot from the card set <send_ack>\nto 1, else set it to 0.",
 	  NULL
 	},
+	{ do_boot_bus_conditions_set, -4,
+	  "bootbus set", "<boot_mode> " "<reset_boot_bus_conditions> " "<boot_bus_width> " "<device>\n"
+	  "Set Boot Bus Conditions.\n"
+	  "<boot_mode> must be \"single_backward|single_hs|dual\"\n"
+	  "<reset_boot_bus_conditions> must be \"x1|retain\"\n"
+	  "<boot_bus_width> must be \"x1|x4|x8\"",
+	  NULL
+	},
 	{ do_write_bkops_en, -1,
 	  "bkops enable", "<device>\n"
 		"Enable the eMMC BKOPS feature on <device>.\nNOTE!  This is a one-time programmable (unreversible) change.",
diff --git a/mmc.h b/mmc.h
index a30ce1c..d57cad5 100644
--- a/mmc.h
+++ b/mmc.h
@@ -50,9 +50,10 @@
 #define EXT_CSD_PART_SWITCH_TIME	199
 #define EXT_CSD_BOOT_CFG		179
 #define EXT_CSD_PART_CONFIG		179
+#define EXT_CSD_BOOT_BUS_CONDITIONS	177
 #define EXT_CSD_ERASE_GROUP_DEF		175
 #define EXT_CSD_BOOT_WP			173
-#define EXT_CSD_WR_REL_SET			167
+#define EXT_CSD_WR_REL_SET		167
 #define EXT_CSD_WR_REL_PARAM		166
 #define EXT_CSD_SANITIZE_START		165
 #define EXT_CSD_BKOPS_EN		163	/* R/W */
diff --git a/mmc_cmds.c b/mmc_cmds.c
index 2471eb0..bf3ff78 100644
--- a/mmc_cmds.c
+++ b/mmc_cmds.c
@@ -303,6 +303,75 @@
 	return ret;
 }
 
+int do_boot_bus_conditions_set(int nargs, char **argv)
+{
+	__u8 ext_csd[512];
+	__u8 value = 0;
+	int fd, ret;
+	char *device;
+
+	CHECK(nargs != 5, "Usage: mmc: bootbus set <boot_mode> "
+	      "<reset_boot_bus_conditions> <boot_bus_width> <device>\n",
+		exit(1));
+
+	if (strcmp(argv[1], "single_backward") == 0)
+		value |= 0;
+	else if (strcmp(argv[1], "single_hs") == 0)
+		value |= 0x8;
+	else if (strcmp(argv[1], "dual") == 0)
+		value |= 0x10;
+	else {
+		fprintf(stderr, "illegal <boot_mode> specified\n");
+		exit(1);
+	}
+
+	if (strcmp(argv[2], "x1") == 0)
+		value |= 0;
+	else if (strcmp(argv[2], "retain") == 0)
+		value |= 0x4;
+	else {
+		fprintf(stderr,
+			"illegal <reset_boot_bus_conditions> specified\n");
+		exit(1);
+	}
+
+	if (strcmp(argv[3], "x1") == 0)
+		value |= 0;
+	else if (strcmp(argv[3], "x4") == 0)
+		value |= 0x1;
+	else if (strcmp(argv[3], "x8") == 0)
+		value |= 0x2;
+	else {
+		fprintf(stderr,	"illegal <boot_bus_width> specified\n");
+		exit(1);
+	}
+
+	device = argv[4];
+	fd = open(device, O_RDWR);
+	if (fd < 0) {
+		perror("open");
+		exit(1);
+	}
+
+	ret = read_extcsd(fd, ext_csd);
+	if (ret) {
+		fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
+		exit(1);
+	}
+	printf("Changing ext_csd[BOOT_BUS_CONDITIONS] from 0x%02x to 0x%02x\n",
+		ext_csd[EXT_CSD_BOOT_BUS_CONDITIONS], value);
+
+	ret = write_extcsd_value(fd, EXT_CSD_BOOT_BUS_CONDITIONS, value);
+	if (ret) {
+		fprintf(stderr, "Could not write 0x%02x to "
+			"EXT_CSD[%d] in %s\n",
+			value, EXT_CSD_BOOT_BUS_CONDITIONS, device);
+		exit(1);
+	}
+	close(fd);
+	return ret;
+}
+
 int do_hwreset(int value, int nargs, char **argv)
 {
 	__u8 ext_csd[512];
@@ -948,6 +1017,9 @@
 	ext_csd_rev = ext_csd[192];
 
 	switch (ext_csd_rev) {
+	case 7:
+		str = "5.0";
+		break;
 	case 6:
 		str = "4.5";
 		break;
diff --git a/mmc_cmds.h b/mmc_cmds.h
index 6d2a5ed..ab181d4 100644
--- a/mmc_cmds.h
+++ b/mmc_cmds.h
@@ -21,6 +21,7 @@
 int do_writeprotect_set(int nargs, char **argv);
 int do_disable_512B_emulation(int nargs, char **argv);
 int do_write_boot_en(int nargs, char **argv);
+int do_boot_bus_conditions_set(int nargs, char **argv);
 int do_write_bkops_en(int nargs, char **argv);
 int do_hwreset_en(int nargs, char **argv);
 int do_hwreset_dis(int nargs, char **argv);