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