]>
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 | |
e33a5c6b | 146 | ret = blk_select_hwpart_devnum(UCLASS_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 | |
e33a5c6b | 182 | blk_select_hwpart_devnum(UCLASS_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); | |
d7226704 | 260 | u32 erase_size; |
34853925 | 261 | |
d7226704 PD |
262 | erase_size = mmc->erase_grp_size * desc->blksz; |
263 | blk_start = ALIGN_DOWN(offset, erase_size) / desc->blksz; | |
264 | blk_cnt = ALIGN(size, erase_size) / desc->blksz; | |
34853925 FW |
265 | |
266 | n = blk_derase(desc, blk_start, blk_cnt); | |
d7226704 PD |
267 | printf("%d blocks erased at 0x%x: %s\n", n, blk_start, |
268 | (n == blk_cnt) ? "OK" : "ERROR"); | |
34853925 FW |
269 | |
270 | return (n == blk_cnt) ? 0 : 1; | |
271 | } | |
272 | ||
273 | static int env_mmc_erase(void) | |
274 | { | |
275 | int dev = mmc_get_env_dev(); | |
276 | struct mmc *mmc = find_mmc_device(dev); | |
277 | int ret, copy = 0; | |
278 | u32 offset; | |
279 | const char *errmsg; | |
280 | ||
281 | errmsg = init_mmc_for_env(mmc); | |
282 | if (errmsg) { | |
283 | printf("%s\n", errmsg); | |
284 | return 1; | |
285 | } | |
286 | ||
f47f87f2 MV |
287 | if (mmc_get_env_addr(mmc, copy, &offset)) { |
288 | ret = CMD_RET_FAILURE; | |
289 | goto fini; | |
290 | } | |
34853925 | 291 | |
d7226704 | 292 | printf("\n"); |
34853925 FW |
293 | ret = erase_env(mmc, CONFIG_ENV_SIZE, offset); |
294 | ||
295 | #ifdef CONFIG_ENV_OFFSET_REDUND | |
296 | copy = 1; | |
297 | ||
d11d1bec MV |
298 | #ifdef ENV_MMC_HWPART_REDUND |
299 | ret = mmc_set_env_part(mmc, copy + 1); | |
300 | if (ret) | |
301 | goto fini; | |
302 | #endif | |
303 | ||
f47f87f2 MV |
304 | if (mmc_get_env_addr(mmc, copy, &offset)) { |
305 | ret = CMD_RET_FAILURE; | |
306 | goto fini; | |
307 | } | |
34853925 FW |
308 | |
309 | ret |= erase_env(mmc, CONFIG_ENV_SIZE, offset); | |
310 | #endif | |
311 | ||
f47f87f2 MV |
312 | fini: |
313 | fini_mmc_for_env(mmc); | |
34853925 FW |
314 | return ret; |
315 | } | |
e5bce247 | 316 | #endif /* CONFIG_CMD_SAVEENV && !CONFIG_SPL_BUILD */ |
a8060359 | 317 | |
e8db8f71 IG |
318 | static inline int read_env(struct mmc *mmc, unsigned long size, |
319 | unsigned long offset, const void *buffer) | |
a8060359 TL |
320 | { |
321 | uint blk_start, blk_cnt, n; | |
5461acba | 322 | struct blk_desc *desc = mmc_get_blk_desc(mmc); |
a8060359 | 323 | |
e8db8f71 IG |
324 | blk_start = ALIGN(offset, mmc->read_bl_len) / mmc->read_bl_len; |
325 | blk_cnt = ALIGN(size, mmc->read_bl_len) / mmc->read_bl_len; | |
a8060359 | 326 | |
5461acba | 327 | n = blk_dread(desc, blk_start, blk_cnt, (uchar *)buffer); |
a8060359 TL |
328 | |
329 | return (n == blk_cnt) ? 0 : -1; | |
330 | } | |
331 | ||
d196bd88 | 332 | #ifdef CONFIG_ENV_OFFSET_REDUND |
c5951991 | 333 | static int env_mmc_load(void) |
d196bd88 MH |
334 | { |
335 | #if !defined(ENV_IS_EMBEDDED) | |
b9c8ccab | 336 | struct mmc *mmc; |
d196bd88 MH |
337 | u32 offset1, offset2; |
338 | int read1_fail = 0, read2_fail = 0; | |
d196bd88 | 339 | int ret; |
e92029c0 | 340 | int dev = mmc_get_env_dev(); |
c75648d7 | 341 | const char *errmsg = NULL; |
d196bd88 | 342 | |
452a2722 MN |
343 | ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env1, 1); |
344 | ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env2, 1); | |
345 | ||
26862b4a FA |
346 | mmc_initialize(NULL); |
347 | ||
b9c8ccab TR |
348 | mmc = find_mmc_device(dev); |
349 | ||
c75648d7 TH |
350 | errmsg = init_mmc_for_env(mmc); |
351 | if (errmsg) { | |
c5951991 | 352 | ret = -EIO; |
d196bd88 MH |
353 | goto err; |
354 | } | |
355 | ||
356 | if (mmc_get_env_addr(mmc, 0, &offset1) || | |
357 | mmc_get_env_addr(mmc, 1, &offset2)) { | |
c5951991 | 358 | ret = -EIO; |
d196bd88 MH |
359 | goto fini; |
360 | } | |
361 | ||
d11d1bec MV |
362 | #ifdef ENV_MMC_HWPART_REDUND |
363 | ret = mmc_set_env_part(mmc, 1); | |
364 | if (ret) | |
365 | goto fini; | |
366 | #endif | |
367 | ||
d196bd88 | 368 | read1_fail = read_env(mmc, CONFIG_ENV_SIZE, offset1, tmp_env1); |
d11d1bec MV |
369 | |
370 | #ifdef ENV_MMC_HWPART_REDUND | |
371 | ret = mmc_set_env_part(mmc, 2); | |
372 | if (ret) | |
373 | goto fini; | |
374 | #endif | |
375 | ||
d196bd88 MH |
376 | read2_fail = read_env(mmc, CONFIG_ENV_SIZE, offset2, tmp_env2); |
377 | ||
31f044bd | 378 | ret = env_import_redund((char *)tmp_env1, read1_fail, (char *)tmp_env2, |
890feeca | 379 | read2_fail, H_EXTERNAL); |
d196bd88 MH |
380 | |
381 | fini: | |
382 | fini_mmc_for_env(mmc); | |
383 | err: | |
384 | if (ret) | |
0ac7d722 | 385 | env_set_default(errmsg, 0); |
c5951991 | 386 | |
d196bd88 | 387 | #endif |
c5951991 | 388 | return ret; |
d196bd88 MH |
389 | } |
390 | #else /* ! CONFIG_ENV_OFFSET_REDUND */ | |
c5951991 | 391 | static int env_mmc_load(void) |
a8060359 TL |
392 | { |
393 | #if !defined(ENV_IS_EMBEDDED) | |
cd0f4fa1 | 394 | ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE); |
b9c8ccab | 395 | struct mmc *mmc; |
97039ab9 | 396 | u32 offset; |
9404a5fc | 397 | int ret; |
e92029c0 | 398 | int dev = mmc_get_env_dev(); |
c75648d7 | 399 | const char *errmsg; |
0536b440 | 400 | env_t *ep = NULL; |
b9c8ccab | 401 | |
b9c8ccab | 402 | mmc = find_mmc_device(dev); |
a8060359 | 403 | |
c75648d7 TH |
404 | errmsg = init_mmc_for_env(mmc); |
405 | if (errmsg) { | |
c5951991 | 406 | ret = -EIO; |
9404a5fc SW |
407 | goto err; |
408 | } | |
a8060359 | 409 | |
d196bd88 | 410 | if (mmc_get_env_addr(mmc, 0, &offset)) { |
c5951991 | 411 | ret = -EIO; |
9404a5fc SW |
412 | goto fini; |
413 | } | |
414 | ||
cd0f4fa1 | 415 | if (read_env(mmc, CONFIG_ENV_SIZE, offset, buf)) { |
c75648d7 | 416 | errmsg = "!read failed"; |
c5951991 | 417 | ret = -EIO; |
9404a5fc SW |
418 | goto fini; |
419 | } | |
a8060359 | 420 | |
890feeca | 421 | ret = env_import(buf, 1, H_EXTERNAL); |
0536b440 PG |
422 | if (!ret) { |
423 | ep = (env_t *)buf; | |
424 | gd->env_addr = (ulong)&ep->data; | |
425 | } | |
9404a5fc SW |
426 | |
427 | fini: | |
428 | fini_mmc_for_env(mmc); | |
429 | err: | |
430 | if (ret) | |
0ac7d722 | 431 | env_set_default(errmsg, 0); |
a8060359 | 432 | #endif |
c5951991 | 433 | return ret; |
a8060359 | 434 | } |
d196bd88 | 435 | #endif /* CONFIG_ENV_OFFSET_REDUND */ |
4415f1d1 SG |
436 | |
437 | U_BOOT_ENV_LOCATION(mmc) = { | |
438 | .location = ENVL_MMC, | |
ac358beb | 439 | ENV_NAME("MMC") |
e5bce247 | 440 | .load = env_mmc_load, |
4415f1d1 | 441 | #ifndef CONFIG_SPL_BUILD |
e5bce247 | 442 | .save = env_save_ptr(env_mmc_save), |
1af031ac | 443 | .erase = ENV_ERASE_PTR(env_mmc_erase) |
4415f1d1 | 444 | #endif |
4415f1d1 | 445 | }; |