blob: 18da6dc58c88e90695f685e9ccd280f4218bb626 [file] [log] [blame]
Terry Lva8060352010-05-17 10:57:01 +08001/*
Mingkai Hu97039ab2011-01-24 17:09:55 +00002 * (C) Copyright 2008-2011 Freescale Semiconductor, Inc.
Terry Lva8060352010-05-17 10:57:01 +08003 *
Wolfgang Denk3765b3e2013-10-07 13:07:26 +02004 * SPDX-License-Identifier: GPL-2.0+
Terry Lva8060352010-05-17 10:57:01 +08005 */
6
7/* #define DEBUG */
8
9#include <common.h>
10
11#include <command.h>
12#include <environment.h>
Philipp Tomsichf8b8a552017-05-16 00:16:31 +020013#include <fdtdec.h>
Terry Lva8060352010-05-17 10:57:01 +080014#include <linux/stddef.h>
15#include <malloc.h>
Simon Glasscf92e052015-09-02 17:24:58 -060016#include <memalign.h>
Terry Lva8060352010-05-17 10:57:01 +080017#include <mmc.h>
Lei Wen6d1d51b2010-11-10 07:39:23 +080018#include <search.h>
Lei Wene79f4832010-10-13 11:07:21 +080019#include <errno.h>
Terry Lva8060352010-05-17 10:57:01 +080020
Michael Heimpoldd196bd82013-04-10 10:36:19 +000021#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
Terry Lva8060352010-05-17 10:57:01 +080026#ifdef ENV_IS_EMBEDDED
Igor Grinberg994bc672011-11-17 06:07:23 +000027env_t *env_ptr = &environment;
Terry Lva8060352010-05-17 10:57:01 +080028#else /* ! ENV_IS_EMBEDDED */
Igor Grinberge8db8f72011-11-07 01:14:05 +000029env_t *env_ptr;
Terry Lva8060352010-05-17 10:57:01 +080030#endif /* ENV_IS_EMBEDDED */
31
Terry Lva8060352010-05-17 10:57:01 +080032DECLARE_GLOBAL_DATA_PTR;
33
Mingkai Hu97039ab2011-01-24 17:09:55 +000034#if !defined(CONFIG_ENV_OFFSET)
35#define CONFIG_ENV_OFFSET 0
36#endif
37
Philipp Tomsichf8b8a552017-05-16 00:16:31 +020038#if CONFIG_IS_ENABLED(OF_CONTROL)
39static inline s64 mmc_offset(int copy)
Mingkai Hu97039ab2011-01-24 17:09:55 +000040{
Philipp Tomsichf8b8a552017-05-16 00:16:31 +020041 const char *propname = "u-boot,mmc-env-offset";
42 s64 defvalue = CONFIG_ENV_OFFSET;
Stephen Warren5c088ee2013-06-11 15:14:02 -060043
Philipp Tomsichf8b8a552017-05-16 00:16:31 +020044#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
54static inline s64 mmc_offset(int copy)
55{
56 s64 offset = CONFIG_ENV_OFFSET;
57
58#if defined(CONFIG_ENV_OFFSET_REDUND)
Michael Heimpoldd196bd82013-04-10 10:36:19 +000059 if (copy)
Stephen Warren5c088ee2013-06-11 15:14:02 -060060 offset = CONFIG_ENV_OFFSET_REDUND;
Michael Heimpoldd196bd82013-04-10 10:36:19 +000061#endif
Philipp Tomsichf8b8a552017-05-16 00:16:31 +020062 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);
Stephen Warren5c088ee2013-06-11 15:14:02 -060069
70 if (offset < 0)
71 offset += mmc->capacity;
72
73 *env_addr = offset;
74
Mingkai Hu97039ab2011-01-24 17:09:55 +000075 return 0;
76}
Mingkai Hu97039ab2011-01-24 17:09:55 +000077
Clemens Grubere92029c2016-01-20 15:43:37 +010078__weak int mmc_get_env_dev(void)
79{
80 return CONFIG_SYS_MMC_ENV_DEV;
81}
82
Tom Rinib9c8cca2014-03-28 12:03:34 -040083#ifdef CONFIG_SYS_MMC_ENV_PART
Dmitry Lifshitz6e7b7df2014-07-30 13:19:06 +030084__weak uint mmc_get_env_part(struct mmc *mmc)
85{
86 return CONFIG_SYS_MMC_ENV_PART;
87}
88
Stephen Warren873cc1d2015-12-07 11:38:49 -070089static unsigned char env_mmc_orig_hwpart;
90
Dmitry Lifshitz6e7b7df2014-07-30 13:19:06 +030091static int mmc_set_env_part(struct mmc *mmc)
92{
93 uint part = mmc_get_env_part(mmc);
Clemens Grubere92029c2016-01-20 15:43:37 +010094 int dev = mmc_get_env_dev();
Dmitry Lifshitz6e7b7df2014-07-30 13:19:06 +030095 int ret = 0;
Tom Rinib9c8cca2014-03-28 12:03:34 -040096
Simon Glass69f45cd2016-05-01 13:52:29 -060097 env_mmc_orig_hwpart = mmc_get_blk_desc(mmc)->hwpart;
98 ret = blk_select_hwpart_devnum(IF_TYPE_MMC, dev, part);
Stephen Warren873cc1d2015-12-07 11:38:49 -070099 if (ret)
100 puts("MMC partition switch failed\n");
Dmitry Lifshitz6e7b7df2014-07-30 13:19:06 +0300101
102 return ret;
103}
104#else
105static inline int mmc_set_env_part(struct mmc *mmc) {return 0; };
Tom Rinib9c8cca2014-03-28 12:03:34 -0400106#endif
107
Tim Harveyc75648d2015-05-08 14:52:09 -0700108static const char *init_mmc_for_env(struct mmc *mmc)
Dmitry Lifshitz6e7b7df2014-07-30 13:19:06 +0300109{
Tim Harveyc75648d2015-05-08 14:52:09 -0700110 if (!mmc)
Hans de Goedea85da212015-08-15 20:05:01 +0200111 return "!No MMC card found";
Terry Lva8060352010-05-17 10:57:01 +0800112
Simon Glass01b73fe2017-05-27 11:37:18 -0600113#ifdef CONFIG_BLK
114 struct udevice *dev;
115
116 if (blk_get_from_parent(mmc->dev, &dev))
117 return "!No block device";
118#else
Tim Harveyc75648d2015-05-08 14:52:09 -0700119 if (mmc_init(mmc))
Hans de Goedea85da212015-08-15 20:05:01 +0200120 return "!MMC init failed";
Simon Glasse7017a32017-04-23 20:02:04 -0600121#endif
Tim Harveyc75648d2015-05-08 14:52:09 -0700122 if (mmc_set_env_part(mmc))
Hans de Goedea85da212015-08-15 20:05:01 +0200123 return "!MMC partition switch failed";
Tim Harveyc75648d2015-05-08 14:52:09 -0700124
125 return NULL;
Terry Lva8060352010-05-17 10:57:01 +0800126}
127
Stephen Warren9404a5f2012-07-30 10:55:44 +0000128static void fini_mmc_for_env(struct mmc *mmc)
129{
130#ifdef CONFIG_SYS_MMC_ENV_PART
Clemens Grubere92029c2016-01-20 15:43:37 +0100131 int dev = mmc_get_env_dev();
Tom Rinib9c8cca2014-03-28 12:03:34 -0400132
Simon Glass69f45cd2016-05-01 13:52:29 -0600133 blk_select_hwpart_devnum(IF_TYPE_MMC, dev, env_mmc_orig_hwpart);
Stephen Warren9404a5f2012-07-30 10:55:44 +0000134#endif
135}
136
Simon Glasse5bce242017-08-03 12:22:01 -0600137#if defined(CONFIG_CMD_SAVEENV) && !defined(CONFIG_SPL_BUILD)
Igor Grinberge8db8f72011-11-07 01:14:05 +0000138static inline int write_env(struct mmc *mmc, unsigned long size,
139 unsigned long offset, const void *buffer)
Terry Lva8060352010-05-17 10:57:01 +0800140{
141 uint blk_start, blk_cnt, n;
Simon Glass5461acb2016-05-14 14:03:03 -0600142 struct blk_desc *desc = mmc_get_blk_desc(mmc);
Terry Lva8060352010-05-17 10:57:01 +0800143
Igor Grinberge8db8f72011-11-07 01:14:05 +0000144 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;
Terry Lva8060352010-05-17 10:57:01 +0800146
Simon Glass5461acb2016-05-14 14:03:03 -0600147 n = blk_dwrite(desc, blk_start, blk_cnt, (u_char *)buffer);
Terry Lva8060352010-05-17 10:57:01 +0800148
149 return (n == blk_cnt) ? 0 : -1;
150}
151
Simon Glasse5bce242017-08-03 12:22:01 -0600152static int env_mmc_save(void)
Terry Lva8060352010-05-17 10:57:01 +0800153{
Tom Rinicd0f4fa2013-04-05 14:55:21 -0400154 ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1);
Clemens Grubere92029c2016-01-20 15:43:37 +0100155 int dev = mmc_get_env_dev();
156 struct mmc *mmc = find_mmc_device(dev);
Igor Grinberge8db8f72011-11-07 01:14:05 +0000157 u32 offset;
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000158 int ret, copy = 0;
Tim Harveyc75648d2015-05-08 14:52:09 -0700159 const char *errmsg;
Terry Lva8060352010-05-17 10:57:01 +0800160
Tim Harveyc75648d2015-05-08 14:52:09 -0700161 errmsg = init_mmc_for_env(mmc);
162 if (errmsg) {
163 printf("%s\n", errmsg);
Mingkai Hu97039ab2011-01-24 17:09:55 +0000164 return 1;
Tim Harveyc75648d2015-05-08 14:52:09 -0700165 }
Mingkai Hu97039ab2011-01-24 17:09:55 +0000166
Marek Vasut7ce15262014-03-05 19:59:50 +0100167 ret = env_export(env_new);
168 if (ret)
Stephen Warren9404a5f2012-07-30 10:55:44 +0000169 goto fini;
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000170
171#ifdef CONFIG_ENV_OFFSET_REDUND
Simon Glass203e94f2017-08-03 12:21:56 -0600172 if (gd->env_valid == ENV_VALID)
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000173 copy = 1;
174#endif
175
176 if (mmc_get_env_addr(mmc, copy, &offset)) {
177 ret = 1;
178 goto fini;
179 }
180
Clemens Grubere92029c2016-01-20 15:43:37 +0100181 printf("Writing to %sMMC(%d)... ", copy ? "redundant " : "", dev);
Stephen Warren4036b632012-05-24 11:38:33 +0000182 if (write_env(mmc, CONFIG_ENV_SIZE, offset, (u_char *)env_new)) {
Terry Lva8060352010-05-17 10:57:01 +0800183 puts("failed\n");
Stephen Warren9404a5f2012-07-30 10:55:44 +0000184 ret = 1;
185 goto fini;
Terry Lva8060352010-05-17 10:57:01 +0800186 }
187
188 puts("done\n");
Stephen Warren9404a5f2012-07-30 10:55:44 +0000189 ret = 0;
190
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000191#ifdef CONFIG_ENV_OFFSET_REDUND
Simon Glass203e94f2017-08-03 12:21:56 -0600192 gd->env_valid = gd->env_valid == ENV_REDUND ? ENV_VALID : ENV_REDUND;
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000193#endif
194
Stephen Warren9404a5f2012-07-30 10:55:44 +0000195fini:
196 fini_mmc_for_env(mmc);
197 return ret;
Terry Lva8060352010-05-17 10:57:01 +0800198}
Simon Glasse5bce242017-08-03 12:22:01 -0600199#endif /* CONFIG_CMD_SAVEENV && !CONFIG_SPL_BUILD */
Terry Lva8060352010-05-17 10:57:01 +0800200
Igor Grinberge8db8f72011-11-07 01:14:05 +0000201static inline int read_env(struct mmc *mmc, unsigned long size,
202 unsigned long offset, const void *buffer)
Terry Lva8060352010-05-17 10:57:01 +0800203{
204 uint blk_start, blk_cnt, n;
Simon Glass5461acb2016-05-14 14:03:03 -0600205 struct blk_desc *desc = mmc_get_blk_desc(mmc);
Terry Lva8060352010-05-17 10:57:01 +0800206
Igor Grinberge8db8f72011-11-07 01:14:05 +0000207 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;
Terry Lva8060352010-05-17 10:57:01 +0800209
Simon Glass5461acb2016-05-14 14:03:03 -0600210 n = blk_dread(desc, blk_start, blk_cnt, (uchar *)buffer);
Terry Lva8060352010-05-17 10:57:01 +0800211
212 return (n == blk_cnt) ? 0 : -1;
213}
214
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000215#ifdef CONFIG_ENV_OFFSET_REDUND
Simon Glasse5bce242017-08-03 12:22:01 -0600216static void env_mmc_load(void)
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000217{
218#if !defined(ENV_IS_EMBEDDED)
Tom Rinib9c8cca2014-03-28 12:03:34 -0400219 struct mmc *mmc;
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000220 u32 offset1, offset2;
221 int read1_fail = 0, read2_fail = 0;
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000222 int ret;
Clemens Grubere92029c2016-01-20 15:43:37 +0100223 int dev = mmc_get_env_dev();
Tim Harveyc75648d2015-05-08 14:52:09 -0700224 const char *errmsg = NULL;
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000225
Markus Niebel452a2722013-10-04 15:48:03 +0200226 ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env1, 1);
227 ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env2, 1);
228
Tom Rinib9c8cca2014-03-28 12:03:34 -0400229 mmc = find_mmc_device(dev);
230
Tim Harveyc75648d2015-05-08 14:52:09 -0700231 errmsg = init_mmc_for_env(mmc);
232 if (errmsg) {
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000233 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
Fiach Antaw9d364af2017-01-25 18:53:12 +1000252 if (read1_fail && read2_fail) {
Tim Harveyc75648d2015-05-08 14:52:09 -0700253 errmsg = "!bad CRC";
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000254 ret = 1;
255 goto fini;
Fiach Antaw9d364af2017-01-25 18:53:12 +1000256 } else if (!read1_fail && read2_fail) {
Simon Glass203e94f2017-08-03 12:21:56 -0600257 gd->env_valid = ENV_VALID;
Fiach Antaw9d364af2017-01-25 18:53:12 +1000258 env_import((char *)tmp_env1, 1);
259 } else if (read1_fail && !read2_fail) {
Simon Glass203e94f2017-08-03 12:21:56 -0600260 gd->env_valid = ENV_REDUND;
Fiach Antaw9d364af2017-01-25 18:53:12 +1000261 env_import((char *)tmp_env2, 1);
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000262 } else {
Fiach Antaw9d364af2017-01-25 18:53:12 +1000263 env_import_redund((char *)tmp_env1, (char *)tmp_env2);
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000264 }
265
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000266 ret = 0;
267
268fini:
269 fini_mmc_for_env(mmc);
270err:
271 if (ret)
Tim Harveyc75648d2015-05-08 14:52:09 -0700272 set_default_env(errmsg);
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000273#endif
274}
275#else /* ! CONFIG_ENV_OFFSET_REDUND */
Simon Glasse5bce242017-08-03 12:22:01 -0600276static void env_mmc_load(void)
Terry Lva8060352010-05-17 10:57:01 +0800277{
278#if !defined(ENV_IS_EMBEDDED)
Tom Rinicd0f4fa2013-04-05 14:55:21 -0400279 ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE);
Tom Rinib9c8cca2014-03-28 12:03:34 -0400280 struct mmc *mmc;
Mingkai Hu97039ab2011-01-24 17:09:55 +0000281 u32 offset;
Stephen Warren9404a5f2012-07-30 10:55:44 +0000282 int ret;
Clemens Grubere92029c2016-01-20 15:43:37 +0100283 int dev = mmc_get_env_dev();
Tim Harveyc75648d2015-05-08 14:52:09 -0700284 const char *errmsg;
Tom Rinib9c8cca2014-03-28 12:03:34 -0400285
Tom Rinib9c8cca2014-03-28 12:03:34 -0400286 mmc = find_mmc_device(dev);
Terry Lva8060352010-05-17 10:57:01 +0800287
Tim Harveyc75648d2015-05-08 14:52:09 -0700288 errmsg = init_mmc_for_env(mmc);
289 if (errmsg) {
Stephen Warren9404a5f2012-07-30 10:55:44 +0000290 ret = 1;
291 goto err;
292 }
Terry Lva8060352010-05-17 10:57:01 +0800293
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000294 if (mmc_get_env_addr(mmc, 0, &offset)) {
Stephen Warren9404a5f2012-07-30 10:55:44 +0000295 ret = 1;
296 goto fini;
297 }
298
Tom Rinicd0f4fa2013-04-05 14:55:21 -0400299 if (read_env(mmc, CONFIG_ENV_SIZE, offset, buf)) {
Tim Harveyc75648d2015-05-08 14:52:09 -0700300 errmsg = "!read failed";
Stephen Warren9404a5f2012-07-30 10:55:44 +0000301 ret = 1;
302 goto fini;
303 }
Terry Lva8060352010-05-17 10:57:01 +0800304
Tom Rinicd0f4fa2013-04-05 14:55:21 -0400305 env_import(buf, 1);
Stephen Warren9404a5f2012-07-30 10:55:44 +0000306 ret = 0;
307
308fini:
309 fini_mmc_for_env(mmc);
310err:
311 if (ret)
Tim Harveyc75648d2015-05-08 14:52:09 -0700312 set_default_env(errmsg);
Terry Lva8060352010-05-17 10:57:01 +0800313#endif
314}
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000315#endif /* CONFIG_ENV_OFFSET_REDUND */
Simon Glass4415f1d2017-08-03 12:21:58 -0600316
317U_BOOT_ENV_LOCATION(mmc) = {
318 .location = ENVL_MMC,
Simon Glassac358be2017-08-03 12:22:03 -0600319 ENV_NAME("MMC")
Simon Glasse5bce242017-08-03 12:22:01 -0600320 .load = env_mmc_load,
Simon Glass4415f1d2017-08-03 12:21:58 -0600321#ifndef CONFIG_SPL_BUILD
Simon Glasse5bce242017-08-03 12:22:01 -0600322 .save = env_save_ptr(env_mmc_save),
Simon Glass4415f1d2017-08-03 12:21:58 -0600323#endif
Simon Glass4415f1d2017-08-03 12:21:58 -0600324};