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