]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
a8060359 | 2 | /* |
97039ab9 | 3 | * (C) Copyright 2008-2011 Freescale Semiconductor, Inc. |
a8060359 TL |
4 | */ |
5 | ||
6 | /* #define DEBUG */ | |
7 | ||
8 | #include <common.h> | |
401d1c4f | 9 | #include <asm/global_data.h> |
a8060359 TL |
10 | |
11 | #include <command.h> | |
0ac7d722 | 12 | #include <env.h> |
f3998fdc | 13 | #include <env_internal.h> |
f8b8a554 | 14 | #include <fdtdec.h> |
a8060359 TL |
15 | #include <linux/stddef.h> |
16 | #include <malloc.h> | |
cf92e05c | 17 | #include <memalign.h> |
a8060359 | 18 | #include <mmc.h> |
c9e87ba6 | 19 | #include <part.h> |
6d1d51b3 | 20 | #include <search.h> |
e79f4839 | 21 | #include <errno.h> |
7de8bd03 | 22 | #include <dm/ofnode.h> |
a8060359 | 23 | |
c9e87ba6 JRO |
24 | #define __STR(X) #X |
25 | #define STR(X) __STR(X) | |
26 | ||
a8060359 TL |
27 | DECLARE_GLOBAL_DATA_PTR; |
28 | ||
d11d1bec MV |
29 | /* |
30 | * In case the environment is redundant, stored in eMMC hardware boot | |
31 | * partition and the environment and redundant environment offsets are | |
32 | * identical, store the environment and redundant environment in both | |
33 | * eMMC boot partitions, one copy in each. | |
34 | * */ | |
35 | #if (defined(CONFIG_SYS_REDUNDAND_ENVIRONMENT) && \ | |
36 | (CONFIG_SYS_MMC_ENV_PART == 1) && \ | |
37 | (CONFIG_ENV_OFFSET == CONFIG_ENV_OFFSET_REDUND)) | |
38 | #define ENV_MMC_HWPART_REDUND | |
39 | #endif | |
40 | ||
f8b8a554 | 41 | #if CONFIG_IS_ENABLED(OF_CONTROL) |
5d4f7b4e | 42 | static inline int mmc_offset_try_partition(const char *str, int copy, s64 *val) |
c9e87ba6 | 43 | { |
0528979f | 44 | struct disk_partition info; |
c9e87ba6 JRO |
45 | struct blk_desc *desc; |
46 | int len, i, ret; | |
2b2f7275 | 47 | char dev_str[4]; |
c9e87ba6 | 48 | |
2b2f7275 PD |
49 | snprintf(dev_str, sizeof(dev_str), "%d", mmc_get_env_dev()); |
50 | ret = blk_get_device_by_str("mmc", dev_str, &desc); | |
c9e87ba6 JRO |
51 | if (ret < 0) |
52 | return (ret); | |
53 | ||
54 | for (i = 1;;i++) { | |
55 | ret = part_get_info(desc, i, &info); | |
56 | if (ret < 0) | |
57 | return ret; | |
58 | ||
2a0a577a | 59 | if (!strncmp((const char *)info.name, str, sizeof(info.name))) |
c9e87ba6 JRO |
60 | break; |
61 | } | |
62 | ||
63 | /* round up to info.blksz */ | |
76b640c3 | 64 | len = DIV_ROUND_UP(CONFIG_ENV_SIZE, info.blksz); |
c9e87ba6 JRO |
65 | |
66 | /* use the top of the partion for the environment */ | |
5d4f7b4e | 67 | *val = (info.start + info.size - (1 + copy) * len) * info.blksz; |
c9e87ba6 JRO |
68 | |
69 | return 0; | |
70 | } | |
71 | ||
f8b8a554 PT |
72 | static inline s64 mmc_offset(int copy) |
73 | { | |
c9e87ba6 JRO |
74 | const struct { |
75 | const char *offset_redund; | |
76 | const char *partition; | |
77 | const char *offset; | |
78 | } dt_prop = { | |
79 | .offset_redund = "u-boot,mmc-env-offset-redundant", | |
80 | .partition = "u-boot,mmc-env-partition", | |
81 | .offset = "u-boot,mmc-env-offset", | |
82 | }; | |
fd374665 | 83 | s64 val = 0, defvalue; |
c9e87ba6 JRO |
84 | const char *propname; |
85 | const char *str; | |
86 | int err; | |
87 | ||
88 | /* look for the partition in mmc CONFIG_SYS_MMC_ENV_DEV */ | |
7de8bd03 | 89 | str = ofnode_conf_read_str(dt_prop.partition); |
c9e87ba6 JRO |
90 | if (str) { |
91 | /* try to place the environment at end of the partition */ | |
5d4f7b4e | 92 | err = mmc_offset_try_partition(str, copy, &val); |
c9e87ba6 JRO |
93 | if (!err) |
94 | return val; | |
95 | } | |
96 | ||
97 | defvalue = CONFIG_ENV_OFFSET; | |
98 | propname = dt_prop.offset; | |
f8b8a554 PT |
99 | |
100 | #if defined(CONFIG_ENV_OFFSET_REDUND) | |
101 | if (copy) { | |
f8b8a554 | 102 | defvalue = CONFIG_ENV_OFFSET_REDUND; |
c9e87ba6 | 103 | propname = dt_prop.offset_redund; |
f8b8a554 PT |
104 | } |
105 | #endif | |
7de8bd03 | 106 | return ofnode_conf_read_int(propname, defvalue); |
f8b8a554 PT |
107 | } |
108 | #else | |
109 | static inline s64 mmc_offset(int copy) | |
97039ab9 | 110 | { |
f8b8a554 | 111 | s64 offset = CONFIG_ENV_OFFSET; |
5c088ee8 | 112 | |
f8b8a554 | 113 | #if defined(CONFIG_ENV_OFFSET_REDUND) |
d196bd88 | 114 | if (copy) |
5c088ee8 | 115 | offset = CONFIG_ENV_OFFSET_REDUND; |
d196bd88 | 116 | #endif |
f8b8a554 PT |
117 | return offset; |
118 | } | |
119 | #endif | |
120 | ||
121 | __weak int mmc_get_env_addr(struct mmc *mmc, int copy, u32 *env_addr) | |
122 | { | |
123 | s64 offset = mmc_offset(copy); | |
5c088ee8 SW |
124 | |
125 | if (offset < 0) | |
126 | offset += mmc->capacity; | |
127 | ||
128 | *env_addr = offset; | |
129 | ||
97039ab9 MH |
130 | return 0; |
131 | } | |
97039ab9 | 132 | |
b9c8ccab | 133 | #ifdef CONFIG_SYS_MMC_ENV_PART |
6e7b7df4 DL |
134 | __weak uint mmc_get_env_part(struct mmc *mmc) |
135 | { | |
136 | return CONFIG_SYS_MMC_ENV_PART; | |
137 | } | |
138 | ||
873cc1d7 SW |
139 | static unsigned char env_mmc_orig_hwpart; |
140 | ||
d11d1bec | 141 | static int mmc_set_env_part(struct mmc *mmc, uint part) |
6e7b7df4 | 142 | { |
e92029c0 | 143 | int dev = mmc_get_env_dev(); |
6e7b7df4 | 144 | int ret = 0; |
b9c8ccab | 145 | |
69f45cd5 | 146 | ret = blk_select_hwpart_devnum(IF_TYPE_MMC, dev, part); |
873cc1d7 SW |
147 | if (ret) |
148 | puts("MMC partition switch failed\n"); | |
6e7b7df4 DL |
149 | |
150 | return ret; | |
151 | } | |
152 | #else | |
d11d1bec | 153 | static inline int mmc_set_env_part(struct mmc *mmc, uint part) {return 0; }; |
b9c8ccab TR |
154 | #endif |
155 | ||
c75648d7 | 156 | static const char *init_mmc_for_env(struct mmc *mmc) |
6e7b7df4 | 157 | { |
c75648d7 | 158 | if (!mmc) |
c5d548a9 | 159 | return "No MMC card found"; |
a8060359 | 160 | |
d48b8d11 | 161 | #if CONFIG_IS_ENABLED(BLK) |
01b73fe6 SG |
162 | struct udevice *dev; |
163 | ||
164 | if (blk_get_from_parent(mmc->dev, &dev)) | |
c5d548a9 | 165 | return "No block device"; |
01b73fe6 | 166 | #else |
c75648d7 | 167 | if (mmc_init(mmc)) |
c5d548a9 | 168 | return "MMC init failed"; |
e7017a3c | 169 | #endif |
d11d1bec MV |
170 | env_mmc_orig_hwpart = mmc_get_blk_desc(mmc)->hwpart; |
171 | if (mmc_set_env_part(mmc, mmc_get_env_part(mmc))) | |
c5d548a9 | 172 | return "MMC partition switch failed"; |
a8060359 | 173 | |
c75648d7 | 174 | return NULL; |
a8060359 TL |
175 | } |
176 | ||
9404a5fc SW |
177 | static void fini_mmc_for_env(struct mmc *mmc) |
178 | { | |
179 | #ifdef CONFIG_SYS_MMC_ENV_PART | |
e92029c0 | 180 | int dev = mmc_get_env_dev(); |
b9c8ccab | 181 | |
69f45cd5 | 182 | blk_select_hwpart_devnum(IF_TYPE_MMC, dev, env_mmc_orig_hwpart); |
9404a5fc SW |
183 | #endif |
184 | } | |
185 | ||
e5bce247 | 186 | #if defined(CONFIG_CMD_SAVEENV) && !defined(CONFIG_SPL_BUILD) |
e8db8f71 IG |
187 | static inline int write_env(struct mmc *mmc, unsigned long size, |
188 | unsigned long offset, const void *buffer) | |
a8060359 TL |
189 | { |
190 | uint blk_start, blk_cnt, n; | |
5461acba | 191 | struct blk_desc *desc = mmc_get_blk_desc(mmc); |
a8060359 | 192 | |
e8db8f71 IG |
193 | blk_start = ALIGN(offset, mmc->write_bl_len) / mmc->write_bl_len; |
194 | blk_cnt = ALIGN(size, mmc->write_bl_len) / mmc->write_bl_len; | |
a8060359 | 195 | |
5461acba | 196 | n = blk_dwrite(desc, blk_start, blk_cnt, (u_char *)buffer); |
a8060359 TL |
197 | |
198 | return (n == blk_cnt) ? 0 : -1; | |
199 | } | |
200 | ||
e5bce247 | 201 | static int env_mmc_save(void) |
a8060359 | 202 | { |
cd0f4fa1 | 203 | ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1); |
e92029c0 CG |
204 | int dev = mmc_get_env_dev(); |
205 | struct mmc *mmc = find_mmc_device(dev); | |
e8db8f71 | 206 | u32 offset; |
d196bd88 | 207 | int ret, copy = 0; |
c75648d7 | 208 | const char *errmsg; |
a8060359 | 209 | |
c75648d7 TH |
210 | errmsg = init_mmc_for_env(mmc); |
211 | if (errmsg) { | |
212 | printf("%s\n", errmsg); | |
97039ab9 | 213 | return 1; |
c75648d7 | 214 | } |
97039ab9 | 215 | |
7ce1526e MV |
216 | ret = env_export(env_new); |
217 | if (ret) | |
9404a5fc | 218 | goto fini; |
d196bd88 MH |
219 | |
220 | #ifdef CONFIG_ENV_OFFSET_REDUND | |
203e94f6 | 221 | if (gd->env_valid == ENV_VALID) |
d196bd88 | 222 | copy = 1; |
d11d1bec MV |
223 | |
224 | #ifdef ENV_MMC_HWPART_REDUND | |
225 | ret = mmc_set_env_part(mmc, copy + 1); | |
226 | if (ret) | |
227 | goto fini; | |
228 | #endif | |
229 | ||
d196bd88 MH |
230 | #endif |
231 | ||
232 | if (mmc_get_env_addr(mmc, copy, &offset)) { | |
233 | ret = 1; | |
234 | goto fini; | |
235 | } | |
236 | ||
e92029c0 | 237 | printf("Writing to %sMMC(%d)... ", copy ? "redundant " : "", dev); |
4036b630 | 238 | if (write_env(mmc, CONFIG_ENV_SIZE, offset, (u_char *)env_new)) { |
a8060359 | 239 | puts("failed\n"); |
9404a5fc SW |
240 | ret = 1; |
241 | goto fini; | |
a8060359 TL |
242 | } |
243 | ||
9404a5fc SW |
244 | ret = 0; |
245 | ||
d196bd88 | 246 | #ifdef CONFIG_ENV_OFFSET_REDUND |
203e94f6 | 247 | gd->env_valid = gd->env_valid == ENV_REDUND ? ENV_VALID : ENV_REDUND; |
d196bd88 MH |
248 | #endif |
249 | ||
9404a5fc SW |
250 | fini: |
251 | fini_mmc_for_env(mmc); | |
252 | return ret; | |
a8060359 | 253 | } |
34853925 | 254 | |
34853925 FW |
255 | static inline int erase_env(struct mmc *mmc, unsigned long size, |
256 | unsigned long offset) | |
257 | { | |
258 | uint blk_start, blk_cnt, n; | |
259 | struct blk_desc *desc = mmc_get_blk_desc(mmc); | |
260 | ||
261 | blk_start = ALIGN(offset, mmc->write_bl_len) / mmc->write_bl_len; | |
262 | blk_cnt = ALIGN(size, mmc->write_bl_len) / mmc->write_bl_len; | |
263 | ||
264 | n = blk_derase(desc, blk_start, blk_cnt); | |
265 | printf("%d blocks erased: %s\n", n, (n == blk_cnt) ? "OK" : "ERROR"); | |
266 | ||
267 | return (n == blk_cnt) ? 0 : 1; | |
268 | } | |
269 | ||
270 | static int env_mmc_erase(void) | |
271 | { | |
272 | int dev = mmc_get_env_dev(); | |
273 | struct mmc *mmc = find_mmc_device(dev); | |
274 | int ret, copy = 0; | |
275 | u32 offset; | |
276 | const char *errmsg; | |
277 | ||
278 | errmsg = init_mmc_for_env(mmc); | |
279 | if (errmsg) { | |
280 | printf("%s\n", errmsg); | |
281 | return 1; | |
282 | } | |
283 | ||
f47f87f2 MV |
284 | if (mmc_get_env_addr(mmc, copy, &offset)) { |
285 | ret = CMD_RET_FAILURE; | |
286 | goto fini; | |
287 | } | |
34853925 FW |
288 | |
289 | ret = erase_env(mmc, CONFIG_ENV_SIZE, offset); | |
290 | ||
291 | #ifdef CONFIG_ENV_OFFSET_REDUND | |
292 | copy = 1; | |
293 | ||
d11d1bec MV |
294 | #ifdef ENV_MMC_HWPART_REDUND |
295 | ret = mmc_set_env_part(mmc, copy + 1); | |
296 | if (ret) | |
297 | goto fini; | |
298 | #endif | |
299 | ||
f47f87f2 MV |
300 | if (mmc_get_env_addr(mmc, copy, &offset)) { |
301 | ret = CMD_RET_FAILURE; | |
302 | goto fini; | |
303 | } | |
34853925 FW |
304 | |
305 | ret |= erase_env(mmc, CONFIG_ENV_SIZE, offset); | |
306 | #endif | |
307 | ||
f47f87f2 MV |
308 | fini: |
309 | fini_mmc_for_env(mmc); | |
34853925 FW |
310 | return ret; |
311 | } | |
e5bce247 | 312 | #endif /* CONFIG_CMD_SAVEENV && !CONFIG_SPL_BUILD */ |
a8060359 | 313 | |
e8db8f71 IG |
314 | static inline int read_env(struct mmc *mmc, unsigned long size, |
315 | unsigned long offset, const void *buffer) | |
a8060359 TL |
316 | { |
317 | uint blk_start, blk_cnt, n; | |
5461acba | 318 | struct blk_desc *desc = mmc_get_blk_desc(mmc); |
a8060359 | 319 | |
e8db8f71 IG |
320 | blk_start = ALIGN(offset, mmc->read_bl_len) / mmc->read_bl_len; |
321 | blk_cnt = ALIGN(size, mmc->read_bl_len) / mmc->read_bl_len; | |
a8060359 | 322 | |
5461acba | 323 | n = blk_dread(desc, blk_start, blk_cnt, (uchar *)buffer); |
a8060359 TL |
324 | |
325 | return (n == blk_cnt) ? 0 : -1; | |
326 | } | |
327 | ||
d196bd88 | 328 | #ifdef CONFIG_ENV_OFFSET_REDUND |
c5951991 | 329 | static int env_mmc_load(void) |
d196bd88 MH |
330 | { |
331 | #if !defined(ENV_IS_EMBEDDED) | |
b9c8ccab | 332 | struct mmc *mmc; |
d196bd88 MH |
333 | u32 offset1, offset2; |
334 | int read1_fail = 0, read2_fail = 0; | |
d196bd88 | 335 | int ret; |
e92029c0 | 336 | int dev = mmc_get_env_dev(); |
c75648d7 | 337 | const char *errmsg = NULL; |
d196bd88 | 338 | |
452a2722 MN |
339 | ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env1, 1); |
340 | ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env2, 1); | |
341 | ||
26862b4a FA |
342 | mmc_initialize(NULL); |
343 | ||
b9c8ccab TR |
344 | mmc = find_mmc_device(dev); |
345 | ||
c75648d7 TH |
346 | errmsg = init_mmc_for_env(mmc); |
347 | if (errmsg) { | |
c5951991 | 348 | ret = -EIO; |
d196bd88 MH |
349 | goto err; |
350 | } | |
351 | ||
352 | if (mmc_get_env_addr(mmc, 0, &offset1) || | |
353 | mmc_get_env_addr(mmc, 1, &offset2)) { | |
c5951991 | 354 | ret = -EIO; |
d196bd88 MH |
355 | goto fini; |
356 | } | |
357 | ||
d11d1bec MV |
358 | #ifdef ENV_MMC_HWPART_REDUND |
359 | ret = mmc_set_env_part(mmc, 1); | |
360 | if (ret) | |
361 | goto fini; | |
362 | #endif | |
363 | ||
d196bd88 | 364 | read1_fail = read_env(mmc, CONFIG_ENV_SIZE, offset1, tmp_env1); |
d11d1bec MV |
365 | |
366 | #ifdef ENV_MMC_HWPART_REDUND | |
367 | ret = mmc_set_env_part(mmc, 2); | |
368 | if (ret) | |
369 | goto fini; | |
370 | #endif | |
371 | ||
d196bd88 MH |
372 | read2_fail = read_env(mmc, CONFIG_ENV_SIZE, offset2, tmp_env2); |
373 | ||
31f044bd | 374 | ret = env_import_redund((char *)tmp_env1, read1_fail, (char *)tmp_env2, |
890feeca | 375 | read2_fail, H_EXTERNAL); |
d196bd88 MH |
376 | |
377 | fini: | |
378 | fini_mmc_for_env(mmc); | |
379 | err: | |
380 | if (ret) | |
0ac7d722 | 381 | env_set_default(errmsg, 0); |
c5951991 | 382 | |
d196bd88 | 383 | #endif |
c5951991 | 384 | return ret; |
d196bd88 MH |
385 | } |
386 | #else /* ! CONFIG_ENV_OFFSET_REDUND */ | |
c5951991 | 387 | static int env_mmc_load(void) |
a8060359 TL |
388 | { |
389 | #if !defined(ENV_IS_EMBEDDED) | |
cd0f4fa1 | 390 | ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE); |
b9c8ccab | 391 | struct mmc *mmc; |
97039ab9 | 392 | u32 offset; |
9404a5fc | 393 | int ret; |
e92029c0 | 394 | int dev = mmc_get_env_dev(); |
c75648d7 | 395 | const char *errmsg; |
0536b440 | 396 | env_t *ep = NULL; |
b9c8ccab | 397 | |
b9c8ccab | 398 | mmc = find_mmc_device(dev); |
a8060359 | 399 | |
c75648d7 TH |
400 | errmsg = init_mmc_for_env(mmc); |
401 | if (errmsg) { | |
c5951991 | 402 | ret = -EIO; |
9404a5fc SW |
403 | goto err; |
404 | } | |
a8060359 | 405 | |
d196bd88 | 406 | if (mmc_get_env_addr(mmc, 0, &offset)) { |
c5951991 | 407 | ret = -EIO; |
9404a5fc SW |
408 | goto fini; |
409 | } | |
410 | ||
cd0f4fa1 | 411 | if (read_env(mmc, CONFIG_ENV_SIZE, offset, buf)) { |
c75648d7 | 412 | errmsg = "!read failed"; |
c5951991 | 413 | ret = -EIO; |
9404a5fc SW |
414 | goto fini; |
415 | } | |
a8060359 | 416 | |
890feeca | 417 | ret = env_import(buf, 1, H_EXTERNAL); |
0536b440 PG |
418 | if (!ret) { |
419 | ep = (env_t *)buf; | |
420 | gd->env_addr = (ulong)&ep->data; | |
421 | } | |
9404a5fc SW |
422 | |
423 | fini: | |
424 | fini_mmc_for_env(mmc); | |
425 | err: | |
426 | if (ret) | |
0ac7d722 | 427 | env_set_default(errmsg, 0); |
a8060359 | 428 | #endif |
c5951991 | 429 | return ret; |
a8060359 | 430 | } |
d196bd88 | 431 | #endif /* CONFIG_ENV_OFFSET_REDUND */ |
4415f1d1 SG |
432 | |
433 | U_BOOT_ENV_LOCATION(mmc) = { | |
434 | .location = ENVL_MMC, | |
ac358beb | 435 | ENV_NAME("MMC") |
e5bce247 | 436 | .load = env_mmc_load, |
4415f1d1 | 437 | #ifndef CONFIG_SPL_BUILD |
e5bce247 | 438 | .save = env_save_ptr(env_mmc_save), |
1af031ac | 439 | .erase = ENV_ERASE_PTR(env_mmc_erase) |
4415f1d1 | 440 | #endif |
4415f1d1 | 441 | }; |