blob: 966f6206538510fbf2351157e5d912cee979967e [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 +080026char *env_name_spec = "MMC";
27
28#ifdef ENV_IS_EMBEDDED
Igor Grinberg994bc672011-11-17 06:07:23 +000029env_t *env_ptr = &environment;
Terry Lva8060352010-05-17 10:57:01 +080030#else /* ! ENV_IS_EMBEDDED */
Igor Grinberge8db8f72011-11-07 01:14:05 +000031env_t *env_ptr;
Terry Lva8060352010-05-17 10:57:01 +080032#endif /* ENV_IS_EMBEDDED */
33
Terry Lva8060352010-05-17 10:57:01 +080034DECLARE_GLOBAL_DATA_PTR;
35
Mingkai Hu97039ab2011-01-24 17:09:55 +000036#if !defined(CONFIG_ENV_OFFSET)
37#define CONFIG_ENV_OFFSET 0
38#endif
39
Philipp Tomsichf8b8a552017-05-16 00:16:31 +020040#if CONFIG_IS_ENABLED(OF_CONTROL)
41static inline s64 mmc_offset(int copy)
Mingkai Hu97039ab2011-01-24 17:09:55 +000042{
Philipp Tomsichf8b8a552017-05-16 00:16:31 +020043 const char *propname = "u-boot,mmc-env-offset";
44 s64 defvalue = CONFIG_ENV_OFFSET;
Stephen Warren5c088ee2013-06-11 15:14:02 -060045
Philipp Tomsichf8b8a552017-05-16 00:16:31 +020046#if defined(CONFIG_ENV_OFFSET_REDUND)
47 if (copy) {
48 propname = "u-boot,mmc-env-offset-redundant";
49 defvalue = CONFIG_ENV_OFFSET_REDUND;
50 }
51#endif
52
53 return fdtdec_get_config_int(gd->fdt_blob, propname, defvalue);
54}
55#else
56static inline s64 mmc_offset(int copy)
57{
58 s64 offset = CONFIG_ENV_OFFSET;
59
60#if defined(CONFIG_ENV_OFFSET_REDUND)
Michael Heimpoldd196bd82013-04-10 10:36:19 +000061 if (copy)
Stephen Warren5c088ee2013-06-11 15:14:02 -060062 offset = CONFIG_ENV_OFFSET_REDUND;
Michael Heimpoldd196bd82013-04-10 10:36:19 +000063#endif
Philipp Tomsichf8b8a552017-05-16 00:16:31 +020064 return offset;
65}
66#endif
67
68__weak int mmc_get_env_addr(struct mmc *mmc, int copy, u32 *env_addr)
69{
70 s64 offset = mmc_offset(copy);
Stephen Warren5c088ee2013-06-11 15:14:02 -060071
72 if (offset < 0)
73 offset += mmc->capacity;
74
75 *env_addr = offset;
76
Mingkai Hu97039ab2011-01-24 17:09:55 +000077 return 0;
78}
Mingkai Hu97039ab2011-01-24 17:09:55 +000079
Clemens Grubere92029c2016-01-20 15:43:37 +010080__weak int mmc_get_env_dev(void)
81{
82 return CONFIG_SYS_MMC_ENV_DEV;
83}
84
Tom Rinib9c8cca2014-03-28 12:03:34 -040085#ifdef CONFIG_SYS_MMC_ENV_PART
Dmitry Lifshitz6e7b7df2014-07-30 13:19:06 +030086__weak uint mmc_get_env_part(struct mmc *mmc)
87{
88 return CONFIG_SYS_MMC_ENV_PART;
89}
90
Stephen Warren873cc1d2015-12-07 11:38:49 -070091static unsigned char env_mmc_orig_hwpart;
92
Dmitry Lifshitz6e7b7df2014-07-30 13:19:06 +030093static int mmc_set_env_part(struct mmc *mmc)
94{
95 uint part = mmc_get_env_part(mmc);
Clemens Grubere92029c2016-01-20 15:43:37 +010096 int dev = mmc_get_env_dev();
Dmitry Lifshitz6e7b7df2014-07-30 13:19:06 +030097 int ret = 0;
Tom Rinib9c8cca2014-03-28 12:03:34 -040098
Simon Glass69f45cd2016-05-01 13:52:29 -060099 env_mmc_orig_hwpart = mmc_get_blk_desc(mmc)->hwpart;
100 ret = blk_select_hwpart_devnum(IF_TYPE_MMC, dev, part);
Stephen Warren873cc1d2015-12-07 11:38:49 -0700101 if (ret)
102 puts("MMC partition switch failed\n");
Dmitry Lifshitz6e7b7df2014-07-30 13:19:06 +0300103
104 return ret;
105}
106#else
107static inline int mmc_set_env_part(struct mmc *mmc) {return 0; };
Tom Rinib9c8cca2014-03-28 12:03:34 -0400108#endif
109
Tim Harveyc75648d2015-05-08 14:52:09 -0700110static const char *init_mmc_for_env(struct mmc *mmc)
Dmitry Lifshitz6e7b7df2014-07-30 13:19:06 +0300111{
Tim Harveyc75648d2015-05-08 14:52:09 -0700112 if (!mmc)
Hans de Goedea85da212015-08-15 20:05:01 +0200113 return "!No MMC card found";
Terry Lva8060352010-05-17 10:57:01 +0800114
Simon Glass01b73fe2017-05-27 11:37:18 -0600115#ifdef CONFIG_BLK
116 struct udevice *dev;
117
118 if (blk_get_from_parent(mmc->dev, &dev))
119 return "!No block device";
120#else
Tim Harveyc75648d2015-05-08 14:52:09 -0700121 if (mmc_init(mmc))
Hans de Goedea85da212015-08-15 20:05:01 +0200122 return "!MMC init failed";
Simon Glasse7017a32017-04-23 20:02:04 -0600123#endif
Tim Harveyc75648d2015-05-08 14:52:09 -0700124 if (mmc_set_env_part(mmc))
Hans de Goedea85da212015-08-15 20:05:01 +0200125 return "!MMC partition switch failed";
Tim Harveyc75648d2015-05-08 14:52:09 -0700126
127 return NULL;
Terry Lva8060352010-05-17 10:57:01 +0800128}
129
Stephen Warren9404a5f2012-07-30 10:55:44 +0000130static void fini_mmc_for_env(struct mmc *mmc)
131{
132#ifdef CONFIG_SYS_MMC_ENV_PART
Clemens Grubere92029c2016-01-20 15:43:37 +0100133 int dev = mmc_get_env_dev();
Tom Rinib9c8cca2014-03-28 12:03:34 -0400134
Simon Glass69f45cd2016-05-01 13:52:29 -0600135 blk_select_hwpart_devnum(IF_TYPE_MMC, dev, env_mmc_orig_hwpart);
Stephen Warren9404a5f2012-07-30 10:55:44 +0000136#endif
137}
138
Simon Glasse5bce242017-08-03 12:22:01 -0600139#if defined(CONFIG_CMD_SAVEENV) && !defined(CONFIG_SPL_BUILD)
Igor Grinberge8db8f72011-11-07 01:14:05 +0000140static inline int write_env(struct mmc *mmc, unsigned long size,
141 unsigned long offset, const void *buffer)
Terry Lva8060352010-05-17 10:57:01 +0800142{
143 uint blk_start, blk_cnt, n;
Simon Glass5461acb2016-05-14 14:03:03 -0600144 struct blk_desc *desc = mmc_get_blk_desc(mmc);
Terry Lva8060352010-05-17 10:57:01 +0800145
Igor Grinberge8db8f72011-11-07 01:14:05 +0000146 blk_start = ALIGN(offset, mmc->write_bl_len) / mmc->write_bl_len;
147 blk_cnt = ALIGN(size, mmc->write_bl_len) / mmc->write_bl_len;
Terry Lva8060352010-05-17 10:57:01 +0800148
Simon Glass5461acb2016-05-14 14:03:03 -0600149 n = blk_dwrite(desc, blk_start, blk_cnt, (u_char *)buffer);
Terry Lva8060352010-05-17 10:57:01 +0800150
151 return (n == blk_cnt) ? 0 : -1;
152}
153
Simon Glasse5bce242017-08-03 12:22:01 -0600154static int env_mmc_save(void)
Terry Lva8060352010-05-17 10:57:01 +0800155{
Tom Rinicd0f4fa2013-04-05 14:55:21 -0400156 ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1);
Clemens Grubere92029c2016-01-20 15:43:37 +0100157 int dev = mmc_get_env_dev();
158 struct mmc *mmc = find_mmc_device(dev);
Igor Grinberge8db8f72011-11-07 01:14:05 +0000159 u32 offset;
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000160 int ret, copy = 0;
Tim Harveyc75648d2015-05-08 14:52:09 -0700161 const char *errmsg;
Terry Lva8060352010-05-17 10:57:01 +0800162
Tim Harveyc75648d2015-05-08 14:52:09 -0700163 errmsg = init_mmc_for_env(mmc);
164 if (errmsg) {
165 printf("%s\n", errmsg);
Mingkai Hu97039ab2011-01-24 17:09:55 +0000166 return 1;
Tim Harveyc75648d2015-05-08 14:52:09 -0700167 }
Mingkai Hu97039ab2011-01-24 17:09:55 +0000168
Marek Vasut7ce15262014-03-05 19:59:50 +0100169 ret = env_export(env_new);
170 if (ret)
Stephen Warren9404a5f2012-07-30 10:55:44 +0000171 goto fini;
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000172
173#ifdef CONFIG_ENV_OFFSET_REDUND
Simon Glass203e94f2017-08-03 12:21:56 -0600174 if (gd->env_valid == ENV_VALID)
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000175 copy = 1;
176#endif
177
178 if (mmc_get_env_addr(mmc, copy, &offset)) {
179 ret = 1;
180 goto fini;
181 }
182
Clemens Grubere92029c2016-01-20 15:43:37 +0100183 printf("Writing to %sMMC(%d)... ", copy ? "redundant " : "", dev);
Stephen Warren4036b632012-05-24 11:38:33 +0000184 if (write_env(mmc, CONFIG_ENV_SIZE, offset, (u_char *)env_new)) {
Terry Lva8060352010-05-17 10:57:01 +0800185 puts("failed\n");
Stephen Warren9404a5f2012-07-30 10:55:44 +0000186 ret = 1;
187 goto fini;
Terry Lva8060352010-05-17 10:57:01 +0800188 }
189
190 puts("done\n");
Stephen Warren9404a5f2012-07-30 10:55:44 +0000191 ret = 0;
192
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000193#ifdef CONFIG_ENV_OFFSET_REDUND
Simon Glass203e94f2017-08-03 12:21:56 -0600194 gd->env_valid = gd->env_valid == ENV_REDUND ? ENV_VALID : ENV_REDUND;
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000195#endif
196
Stephen Warren9404a5f2012-07-30 10:55:44 +0000197fini:
198 fini_mmc_for_env(mmc);
199 return ret;
Terry Lva8060352010-05-17 10:57:01 +0800200}
Simon Glasse5bce242017-08-03 12:22:01 -0600201#endif /* CONFIG_CMD_SAVEENV && !CONFIG_SPL_BUILD */
Terry Lva8060352010-05-17 10:57:01 +0800202
Igor Grinberge8db8f72011-11-07 01:14:05 +0000203static inline int read_env(struct mmc *mmc, unsigned long size,
204 unsigned long offset, const void *buffer)
Terry Lva8060352010-05-17 10:57:01 +0800205{
206 uint blk_start, blk_cnt, n;
Simon Glass5461acb2016-05-14 14:03:03 -0600207 struct blk_desc *desc = mmc_get_blk_desc(mmc);
Terry Lva8060352010-05-17 10:57:01 +0800208
Igor Grinberge8db8f72011-11-07 01:14:05 +0000209 blk_start = ALIGN(offset, mmc->read_bl_len) / mmc->read_bl_len;
210 blk_cnt = ALIGN(size, mmc->read_bl_len) / mmc->read_bl_len;
Terry Lva8060352010-05-17 10:57:01 +0800211
Simon Glass5461acb2016-05-14 14:03:03 -0600212 n = blk_dread(desc, blk_start, blk_cnt, (uchar *)buffer);
Terry Lva8060352010-05-17 10:57:01 +0800213
214 return (n == blk_cnt) ? 0 : -1;
215}
216
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000217#ifdef CONFIG_ENV_OFFSET_REDUND
Simon Glasse5bce242017-08-03 12:22:01 -0600218static void env_mmc_load(void)
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000219{
220#if !defined(ENV_IS_EMBEDDED)
Tom Rinib9c8cca2014-03-28 12:03:34 -0400221 struct mmc *mmc;
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000222 u32 offset1, offset2;
223 int read1_fail = 0, read2_fail = 0;
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000224 int ret;
Clemens Grubere92029c2016-01-20 15:43:37 +0100225 int dev = mmc_get_env_dev();
Tim Harveyc75648d2015-05-08 14:52:09 -0700226 const char *errmsg = NULL;
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000227
Markus Niebel452a2722013-10-04 15:48:03 +0200228 ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env1, 1);
229 ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env2, 1);
230
Tom Rinib9c8cca2014-03-28 12:03:34 -0400231 mmc = find_mmc_device(dev);
232
Tim Harveyc75648d2015-05-08 14:52:09 -0700233 errmsg = init_mmc_for_env(mmc);
234 if (errmsg) {
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000235 ret = 1;
236 goto err;
237 }
238
239 if (mmc_get_env_addr(mmc, 0, &offset1) ||
240 mmc_get_env_addr(mmc, 1, &offset2)) {
241 ret = 1;
242 goto fini;
243 }
244
245 read1_fail = read_env(mmc, CONFIG_ENV_SIZE, offset1, tmp_env1);
246 read2_fail = read_env(mmc, CONFIG_ENV_SIZE, offset2, tmp_env2);
247
248 if (read1_fail && read2_fail)
249 puts("*** Error - No Valid Environment Area found\n");
250 else if (read1_fail || read2_fail)
251 puts("*** Warning - some problems detected "
252 "reading environment; recovered successfully\n");
253
Fiach Antaw9d364af2017-01-25 18:53:12 +1000254 if (read1_fail && read2_fail) {
Tim Harveyc75648d2015-05-08 14:52:09 -0700255 errmsg = "!bad CRC";
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000256 ret = 1;
257 goto fini;
Fiach Antaw9d364af2017-01-25 18:53:12 +1000258 } else if (!read1_fail && read2_fail) {
Simon Glass203e94f2017-08-03 12:21:56 -0600259 gd->env_valid = ENV_VALID;
Fiach Antaw9d364af2017-01-25 18:53:12 +1000260 env_import((char *)tmp_env1, 1);
261 } else if (read1_fail && !read2_fail) {
Simon Glass203e94f2017-08-03 12:21:56 -0600262 gd->env_valid = ENV_REDUND;
Fiach Antaw9d364af2017-01-25 18:53:12 +1000263 env_import((char *)tmp_env2, 1);
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000264 } else {
Fiach Antaw9d364af2017-01-25 18:53:12 +1000265 env_import_redund((char *)tmp_env1, (char *)tmp_env2);
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000266 }
267
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000268 ret = 0;
269
270fini:
271 fini_mmc_for_env(mmc);
272err:
273 if (ret)
Tim Harveyc75648d2015-05-08 14:52:09 -0700274 set_default_env(errmsg);
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000275#endif
276}
277#else /* ! CONFIG_ENV_OFFSET_REDUND */
Simon Glasse5bce242017-08-03 12:22:01 -0600278static void env_mmc_load(void)
Terry Lva8060352010-05-17 10:57:01 +0800279{
280#if !defined(ENV_IS_EMBEDDED)
Tom Rinicd0f4fa2013-04-05 14:55:21 -0400281 ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE);
Tom Rinib9c8cca2014-03-28 12:03:34 -0400282 struct mmc *mmc;
Mingkai Hu97039ab2011-01-24 17:09:55 +0000283 u32 offset;
Stephen Warren9404a5f2012-07-30 10:55:44 +0000284 int ret;
Clemens Grubere92029c2016-01-20 15:43:37 +0100285 int dev = mmc_get_env_dev();
Tim Harveyc75648d2015-05-08 14:52:09 -0700286 const char *errmsg;
Tom Rinib9c8cca2014-03-28 12:03:34 -0400287
Tom Rinib9c8cca2014-03-28 12:03:34 -0400288 mmc = find_mmc_device(dev);
Terry Lva8060352010-05-17 10:57:01 +0800289
Tim Harveyc75648d2015-05-08 14:52:09 -0700290 errmsg = init_mmc_for_env(mmc);
291 if (errmsg) {
Stephen Warren9404a5f2012-07-30 10:55:44 +0000292 ret = 1;
293 goto err;
294 }
Terry Lva8060352010-05-17 10:57:01 +0800295
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000296 if (mmc_get_env_addr(mmc, 0, &offset)) {
Stephen Warren9404a5f2012-07-30 10:55:44 +0000297 ret = 1;
298 goto fini;
299 }
300
Tom Rinicd0f4fa2013-04-05 14:55:21 -0400301 if (read_env(mmc, CONFIG_ENV_SIZE, offset, buf)) {
Tim Harveyc75648d2015-05-08 14:52:09 -0700302 errmsg = "!read failed";
Stephen Warren9404a5f2012-07-30 10:55:44 +0000303 ret = 1;
304 goto fini;
305 }
Terry Lva8060352010-05-17 10:57:01 +0800306
Tom Rinicd0f4fa2013-04-05 14:55:21 -0400307 env_import(buf, 1);
Stephen Warren9404a5f2012-07-30 10:55:44 +0000308 ret = 0;
309
310fini:
311 fini_mmc_for_env(mmc);
312err:
313 if (ret)
Tim Harveyc75648d2015-05-08 14:52:09 -0700314 set_default_env(errmsg);
Terry Lva8060352010-05-17 10:57:01 +0800315#endif
316}
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000317#endif /* CONFIG_ENV_OFFSET_REDUND */
Simon Glass4415f1d2017-08-03 12:21:58 -0600318
319U_BOOT_ENV_LOCATION(mmc) = {
320 .location = ENVL_MMC,
Simon Glasse5bce242017-08-03 12:22:01 -0600321 .load = env_mmc_load,
Simon Glass4415f1d2017-08-03 12:21:58 -0600322#ifndef CONFIG_SPL_BUILD
Simon Glasse5bce242017-08-03 12:22:01 -0600323 .save = env_save_ptr(env_mmc_save),
Simon Glass4415f1d2017-08-03 12:21:58 -0600324#endif
Simon Glass4415f1d2017-08-03 12:21:58 -0600325};