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