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