env_mmc: allow environment to be in an eMMC partition

eMMC devices may have hardware-level partitions: 2 boot partitions,
up to 4 general partitions, plus the user area. This change introduces
optional config variable CONFIG_SYS_MMC_ENV_PART to indicate which
partition the environment should be stored in: 0=user, 1=boot0, 2=boot1,
4..7=general0..3. This allows the environment to be kept out of the user
area, which simplifies the management of OS-/user-level (MBR/GPT)
partitions within the user area.

Signed-off-by: Stephen Warren <swarren@nvidia.com>
Signed-off-by: Tom Warren <twarren@nvidia.com>
diff --git a/common/env_mmc.c b/common/env_mmc.c
index be2f2be..a2ff90b 100644
--- a/common/env_mmc.c
+++ b/common/env_mmc.c
@@ -75,9 +75,28 @@
 		return -1;
 	}
 
+#ifdef CONFIG_SYS_MMC_ENV_PART
+	if (CONFIG_SYS_MMC_ENV_PART != mmc->part_num) {
+		if (mmc_switch_part(CONFIG_SYS_MMC_ENV_DEV,
+				    CONFIG_SYS_MMC_ENV_PART)) {
+			puts("MMC partition switch failed\n");
+			return -1;
+		}
+	}
+#endif
+
 	return 0;
 }
 
+static void fini_mmc_for_env(struct mmc *mmc)
+{
+#ifdef CONFIG_SYS_MMC_ENV_PART
+	if (CONFIG_SYS_MMC_ENV_PART != mmc->part_num)
+		mmc_switch_part(CONFIG_SYS_MMC_ENV_DEV,
+				mmc->part_num);
+#endif
+}
+
 #ifdef CONFIG_CMD_SAVEENV
 static inline int write_env(struct mmc *mmc, unsigned long size,
 			    unsigned long offset, const void *buffer)
@@ -100,26 +119,38 @@
 	char	*res;
 	struct mmc *mmc = find_mmc_device(CONFIG_SYS_MMC_ENV_DEV);
 	u32	offset;
+	int	ret;
 
-	if (init_mmc_for_env(mmc) || mmc_get_env_addr(mmc, &offset))
+	if (init_mmc_for_env(mmc))
 		return 1;
 
+	if (mmc_get_env_addr(mmc, &offset)) {
+		ret = 1;
+		goto fini;
+	}
+
 	res = (char *)&env_new->data;
 	len = hexport_r(&env_htab, '\0', &res, ENV_SIZE, 0, NULL);
 	if (len < 0) {
 		error("Cannot export environment: errno = %d\n", errno);
-		return 1;
+		ret = 1;
+		goto fini;
 	}
 
 	env_new->crc = crc32(0, &env_new->data[0], ENV_SIZE);
 	printf("Writing to MMC(%d)... ", CONFIG_SYS_MMC_ENV_DEV);
 	if (write_env(mmc, CONFIG_ENV_SIZE, offset, (u_char *)env_new)) {
 		puts("failed\n");
-		return 1;
+		ret = 1;
+		goto fini;
 	}
 
 	puts("done\n");
-	return 0;
+	ret = 0;
+
+fini:
+	fini_mmc_for_env(mmc);
+	return ret;
 }
 #endif /* CONFIG_CMD_SAVEENV */
 
@@ -143,13 +174,30 @@
 	ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE);
 	struct mmc *mmc = find_mmc_device(CONFIG_SYS_MMC_ENV_DEV);
 	u32 offset;
+	int ret;
+
+	if (init_mmc_for_env(mmc)) {
+		ret = 1;
+		goto err;
+	}
 
-	if (init_mmc_for_env(mmc) || mmc_get_env_addr(mmc, &offset))
-		return set_default_env(NULL);
+	if (mmc_get_env_addr(mmc, &offset)) {
+		ret = 1;
+		goto fini;
+	}
 
-	if (read_env(mmc, CONFIG_ENV_SIZE, offset, buf))
-		return set_default_env(NULL);
+	if (read_env(mmc, CONFIG_ENV_SIZE, offset, buf)) {
+		ret = 1;
+		goto fini;
+	}
 
 	env_import(buf, 1);
+	ret = 0;
+
+fini:
+	fini_mmc_for_env(mmc);
+err:
+	if (ret)
+		set_default_env(NULL);
 #endif
 }