xref: /openbmc/u-boot/env/mmc.c (revision c74dda8b)
1 /*
2  * (C) Copyright 2008-2011 Freescale Semiconductor, Inc.
3  *
4  * SPDX-License-Identifier:	GPL-2.0+
5  */
6 
7 /* #define DEBUG */
8 
9 #include <common.h>
10 
11 #include <command.h>
12 #include <environment.h>
13 #include <fdtdec.h>
14 #include <linux/stddef.h>
15 #include <malloc.h>
16 #include <memalign.h>
17 #include <mmc.h>
18 #include <search.h>
19 #include <errno.h>
20 
21 #if defined(CONFIG_ENV_SIZE_REDUND) &&  \
22 	(CONFIG_ENV_SIZE_REDUND != CONFIG_ENV_SIZE)
23 #error CONFIG_ENV_SIZE_REDUND should be the same as CONFIG_ENV_SIZE
24 #endif
25 
26 DECLARE_GLOBAL_DATA_PTR;
27 
28 #if !defined(CONFIG_ENV_OFFSET)
29 #define CONFIG_ENV_OFFSET 0
30 #endif
31 
32 #if CONFIG_IS_ENABLED(OF_CONTROL)
33 static inline s64 mmc_offset(int copy)
34 {
35 	const char *propname = "u-boot,mmc-env-offset";
36 	s64 defvalue = CONFIG_ENV_OFFSET;
37 
38 #if defined(CONFIG_ENV_OFFSET_REDUND)
39 	if (copy) {
40 		propname = "u-boot,mmc-env-offset-redundant";
41 		defvalue = CONFIG_ENV_OFFSET_REDUND;
42 	}
43 #endif
44 
45 	return fdtdec_get_config_int(gd->fdt_blob, propname, defvalue);
46 }
47 #else
48 static inline s64 mmc_offset(int copy)
49 {
50 	s64 offset = CONFIG_ENV_OFFSET;
51 
52 #if defined(CONFIG_ENV_OFFSET_REDUND)
53 	if (copy)
54 		offset = CONFIG_ENV_OFFSET_REDUND;
55 #endif
56 	return offset;
57 }
58 #endif
59 
60 __weak int mmc_get_env_addr(struct mmc *mmc, int copy, u32 *env_addr)
61 {
62 	s64 offset = mmc_offset(copy);
63 
64 	if (offset < 0)
65 		offset += mmc->capacity;
66 
67 	*env_addr = offset;
68 
69 	return 0;
70 }
71 
72 __weak int mmc_get_env_dev(void)
73 {
74 	return CONFIG_SYS_MMC_ENV_DEV;
75 }
76 
77 #ifdef CONFIG_SYS_MMC_ENV_PART
78 __weak uint mmc_get_env_part(struct mmc *mmc)
79 {
80 	return CONFIG_SYS_MMC_ENV_PART;
81 }
82 
83 static unsigned char env_mmc_orig_hwpart;
84 
85 static int mmc_set_env_part(struct mmc *mmc)
86 {
87 	uint part = mmc_get_env_part(mmc);
88 	int dev = mmc_get_env_dev();
89 	int ret = 0;
90 
91 	env_mmc_orig_hwpart = mmc_get_blk_desc(mmc)->hwpart;
92 	ret = blk_select_hwpart_devnum(IF_TYPE_MMC, dev, part);
93 	if (ret)
94 		puts("MMC partition switch failed\n");
95 
96 	return ret;
97 }
98 #else
99 static inline int mmc_set_env_part(struct mmc *mmc) {return 0; };
100 #endif
101 
102 static const char *init_mmc_for_env(struct mmc *mmc)
103 {
104 	if (!mmc)
105 		return "!No MMC card found";
106 
107 #ifdef CONFIG_BLK
108 	struct udevice *dev;
109 
110 	if (blk_get_from_parent(mmc->dev, &dev))
111 		return "!No block device";
112 #else
113 	if (mmc_init(mmc))
114 		return "!MMC init failed";
115 #endif
116 	if (mmc_set_env_part(mmc))
117 		return "!MMC partition switch failed";
118 
119 	return NULL;
120 }
121 
122 static void fini_mmc_for_env(struct mmc *mmc)
123 {
124 #ifdef CONFIG_SYS_MMC_ENV_PART
125 	int dev = mmc_get_env_dev();
126 
127 	blk_select_hwpart_devnum(IF_TYPE_MMC, dev, env_mmc_orig_hwpart);
128 #endif
129 }
130 
131 #if defined(CONFIG_CMD_SAVEENV) && !defined(CONFIG_SPL_BUILD)
132 static inline int write_env(struct mmc *mmc, unsigned long size,
133 			    unsigned long offset, const void *buffer)
134 {
135 	uint blk_start, blk_cnt, n;
136 	struct blk_desc *desc = mmc_get_blk_desc(mmc);
137 
138 	blk_start	= ALIGN(offset, mmc->write_bl_len) / mmc->write_bl_len;
139 	blk_cnt		= ALIGN(size, mmc->write_bl_len) / mmc->write_bl_len;
140 
141 	n = blk_dwrite(desc, blk_start, blk_cnt, (u_char *)buffer);
142 
143 	return (n == blk_cnt) ? 0 : -1;
144 }
145 
146 static int env_mmc_save(void)
147 {
148 	ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1);
149 	int dev = mmc_get_env_dev();
150 	struct mmc *mmc = find_mmc_device(dev);
151 	u32	offset;
152 	int	ret, copy = 0;
153 	const char *errmsg;
154 
155 	errmsg = init_mmc_for_env(mmc);
156 	if (errmsg) {
157 		printf("%s\n", errmsg);
158 		return 1;
159 	}
160 
161 	ret = env_export(env_new);
162 	if (ret)
163 		goto fini;
164 
165 #ifdef CONFIG_ENV_OFFSET_REDUND
166 	if (gd->env_valid == ENV_VALID)
167 		copy = 1;
168 #endif
169 
170 	if (mmc_get_env_addr(mmc, copy, &offset)) {
171 		ret = 1;
172 		goto fini;
173 	}
174 
175 	printf("Writing to %sMMC(%d)... ", copy ? "redundant " : "", dev);
176 	if (write_env(mmc, CONFIG_ENV_SIZE, offset, (u_char *)env_new)) {
177 		puts("failed\n");
178 		ret = 1;
179 		goto fini;
180 	}
181 
182 	puts("done\n");
183 	ret = 0;
184 
185 #ifdef CONFIG_ENV_OFFSET_REDUND
186 	gd->env_valid = gd->env_valid == ENV_REDUND ? ENV_VALID : ENV_REDUND;
187 #endif
188 
189 fini:
190 	fini_mmc_for_env(mmc);
191 	return ret;
192 }
193 #endif /* CONFIG_CMD_SAVEENV && !CONFIG_SPL_BUILD */
194 
195 static inline int read_env(struct mmc *mmc, unsigned long size,
196 			   unsigned long offset, const void *buffer)
197 {
198 	uint blk_start, blk_cnt, n;
199 	struct blk_desc *desc = mmc_get_blk_desc(mmc);
200 
201 	blk_start	= ALIGN(offset, mmc->read_bl_len) / mmc->read_bl_len;
202 	blk_cnt		= ALIGN(size, mmc->read_bl_len) / mmc->read_bl_len;
203 
204 	n = blk_dread(desc, blk_start, blk_cnt, (uchar *)buffer);
205 
206 	return (n == blk_cnt) ? 0 : -1;
207 }
208 
209 #ifdef CONFIG_ENV_OFFSET_REDUND
210 static int env_mmc_load(void)
211 {
212 #if !defined(ENV_IS_EMBEDDED)
213 	struct mmc *mmc;
214 	u32 offset1, offset2;
215 	int read1_fail = 0, read2_fail = 0;
216 	int ret;
217 	int dev = mmc_get_env_dev();
218 	const char *errmsg = NULL;
219 
220 	ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env1, 1);
221 	ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env2, 1);
222 
223 	mmc = find_mmc_device(dev);
224 
225 	errmsg = init_mmc_for_env(mmc);
226 	if (errmsg) {
227 		ret = -EIO;
228 		goto err;
229 	}
230 
231 	if (mmc_get_env_addr(mmc, 0, &offset1) ||
232 	    mmc_get_env_addr(mmc, 1, &offset2)) {
233 		ret = -EIO;
234 		goto fini;
235 	}
236 
237 	read1_fail = read_env(mmc, CONFIG_ENV_SIZE, offset1, tmp_env1);
238 	read2_fail = read_env(mmc, CONFIG_ENV_SIZE, offset2, tmp_env2);
239 
240 	if (read1_fail && read2_fail)
241 		puts("*** Error - No Valid Environment Area found\n");
242 	else if (read1_fail || read2_fail)
243 		puts("*** Warning - some problems detected "
244 		     "reading environment; recovered successfully\n");
245 
246 	if (read1_fail && read2_fail) {
247 		errmsg = "!bad CRC";
248 		ret = -EIO;
249 		goto fini;
250 	} else if (!read1_fail && read2_fail) {
251 		gd->env_valid = ENV_VALID;
252 		env_import((char *)tmp_env1, 1);
253 	} else if (read1_fail && !read2_fail) {
254 		gd->env_valid = ENV_REDUND;
255 		env_import((char *)tmp_env2, 1);
256 	} else {
257 		env_import_redund((char *)tmp_env1, (char *)tmp_env2);
258 	}
259 
260 	ret = 0;
261 
262 fini:
263 	fini_mmc_for_env(mmc);
264 err:
265 	if (ret)
266 		set_default_env(errmsg);
267 
268 #endif
269 	return ret;
270 }
271 #else /* ! CONFIG_ENV_OFFSET_REDUND */
272 static int env_mmc_load(void)
273 {
274 #if !defined(ENV_IS_EMBEDDED)
275 	ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE);
276 	struct mmc *mmc;
277 	u32 offset;
278 	int ret;
279 	int dev = mmc_get_env_dev();
280 	const char *errmsg;
281 
282 	mmc = find_mmc_device(dev);
283 
284 	errmsg = init_mmc_for_env(mmc);
285 	if (errmsg) {
286 		ret = -EIO;
287 		goto err;
288 	}
289 
290 	if (mmc_get_env_addr(mmc, 0, &offset)) {
291 		ret = -EIO;
292 		goto fini;
293 	}
294 
295 	if (read_env(mmc, CONFIG_ENV_SIZE, offset, buf)) {
296 		errmsg = "!read failed";
297 		ret = -EIO;
298 		goto fini;
299 	}
300 
301 	env_import(buf, 1);
302 	ret = 0;
303 
304 fini:
305 	fini_mmc_for_env(mmc);
306 err:
307 	if (ret)
308 		set_default_env(errmsg);
309 #endif
310 	return ret;
311 }
312 #endif /* CONFIG_ENV_OFFSET_REDUND */
313 
314 U_BOOT_ENV_LOCATION(mmc) = {
315 	.location	= ENVL_MMC,
316 	ENV_NAME("MMC")
317 	.load		= env_mmc_load,
318 #ifndef CONFIG_SPL_BUILD
319 	.save		= env_save_ptr(env_mmc_save),
320 #endif
321 };
322