1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * (C) Copyright 2008-2011 Freescale Semiconductor, Inc. 4 */ 5 6 /* #define DEBUG */ 7 8 #include <common.h> 9 10 #include <command.h> 11 #include <environment.h> 12 #include <fdtdec.h> 13 #include <linux/stddef.h> 14 #include <malloc.h> 15 #include <memalign.h> 16 #include <mmc.h> 17 #include <part.h> 18 #include <search.h> 19 #include <errno.h> 20 21 #define __STR(X) #X 22 #define STR(X) __STR(X) 23 24 #if defined(CONFIG_ENV_SIZE_REDUND) && \ 25 (CONFIG_ENV_SIZE_REDUND != CONFIG_ENV_SIZE) 26 #error CONFIG_ENV_SIZE_REDUND should be the same as CONFIG_ENV_SIZE 27 #endif 28 29 DECLARE_GLOBAL_DATA_PTR; 30 31 #if !defined(CONFIG_ENV_OFFSET) 32 #define CONFIG_ENV_OFFSET 0 33 #endif 34 35 #if CONFIG_IS_ENABLED(OF_CONTROL) 36 static inline int mmc_offset_try_partition(const char *str, s64 *val) 37 { 38 disk_partition_t info; 39 struct blk_desc *desc; 40 int len, i, ret; 41 42 ret = blk_get_device_by_str("mmc", STR(CONFIG_SYS_MMC_ENV_DEV), &desc); 43 if (ret < 0) 44 return (ret); 45 46 for (i = 1;;i++) { 47 ret = part_get_info(desc, i, &info); 48 if (ret < 0) 49 return ret; 50 51 if (!strncmp((const char *)info.name, str, sizeof(str))) 52 break; 53 } 54 55 /* round up to info.blksz */ 56 len = (CONFIG_ENV_SIZE + info.blksz - 1) & ~(info.blksz - 1); 57 58 /* use the top of the partion for the environment */ 59 *val = (info.start + info.size - 1) - len / info.blksz; 60 61 return 0; 62 } 63 64 static inline s64 mmc_offset(int copy) 65 { 66 const struct { 67 const char *offset_redund; 68 const char *partition; 69 const char *offset; 70 } dt_prop = { 71 .offset_redund = "u-boot,mmc-env-offset-redundant", 72 .partition = "u-boot,mmc-env-partition", 73 .offset = "u-boot,mmc-env-offset", 74 }; 75 s64 val = 0, defvalue; 76 const char *propname; 77 const char *str; 78 int err; 79 80 /* look for the partition in mmc CONFIG_SYS_MMC_ENV_DEV */ 81 str = fdtdec_get_config_string(gd->fdt_blob, dt_prop.partition); 82 if (str) { 83 /* try to place the environment at end of the partition */ 84 err = mmc_offset_try_partition(str, &val); 85 if (!err) 86 return val; 87 } 88 89 defvalue = CONFIG_ENV_OFFSET; 90 propname = dt_prop.offset; 91 92 #if defined(CONFIG_ENV_OFFSET_REDUND) 93 if (copy) { 94 defvalue = CONFIG_ENV_OFFSET_REDUND; 95 propname = dt_prop.offset_redund; 96 } 97 #endif 98 return fdtdec_get_config_int(gd->fdt_blob, propname, defvalue); 99 } 100 #else 101 static inline s64 mmc_offset(int copy) 102 { 103 s64 offset = CONFIG_ENV_OFFSET; 104 105 #if defined(CONFIG_ENV_OFFSET_REDUND) 106 if (copy) 107 offset = CONFIG_ENV_OFFSET_REDUND; 108 #endif 109 return offset; 110 } 111 #endif 112 113 __weak int mmc_get_env_addr(struct mmc *mmc, int copy, u32 *env_addr) 114 { 115 s64 offset = mmc_offset(copy); 116 117 if (offset < 0) 118 offset += mmc->capacity; 119 120 *env_addr = offset; 121 122 return 0; 123 } 124 125 __weak int mmc_get_env_dev(void) 126 { 127 return CONFIG_SYS_MMC_ENV_DEV; 128 } 129 130 #ifdef CONFIG_SYS_MMC_ENV_PART 131 __weak uint mmc_get_env_part(struct mmc *mmc) 132 { 133 return CONFIG_SYS_MMC_ENV_PART; 134 } 135 136 static unsigned char env_mmc_orig_hwpart; 137 138 static int mmc_set_env_part(struct mmc *mmc) 139 { 140 uint part = mmc_get_env_part(mmc); 141 int dev = mmc_get_env_dev(); 142 int ret = 0; 143 144 env_mmc_orig_hwpart = mmc_get_blk_desc(mmc)->hwpart; 145 ret = blk_select_hwpart_devnum(IF_TYPE_MMC, dev, part); 146 if (ret) 147 puts("MMC partition switch failed\n"); 148 149 return ret; 150 } 151 #else 152 static inline int mmc_set_env_part(struct mmc *mmc) {return 0; }; 153 #endif 154 155 static const char *init_mmc_for_env(struct mmc *mmc) 156 { 157 if (!mmc) 158 return "No MMC card found"; 159 160 #if CONFIG_IS_ENABLED(BLK) 161 struct udevice *dev; 162 163 if (blk_get_from_parent(mmc->dev, &dev)) 164 return "No block device"; 165 #else 166 if (mmc_init(mmc)) 167 return "MMC init failed"; 168 #endif 169 if (mmc_set_env_part(mmc)) 170 return "MMC partition switch failed"; 171 172 return NULL; 173 } 174 175 static void fini_mmc_for_env(struct mmc *mmc) 176 { 177 #ifdef CONFIG_SYS_MMC_ENV_PART 178 int dev = mmc_get_env_dev(); 179 180 blk_select_hwpart_devnum(IF_TYPE_MMC, dev, env_mmc_orig_hwpart); 181 #endif 182 } 183 184 #if defined(CONFIG_CMD_SAVEENV) && !defined(CONFIG_SPL_BUILD) 185 static inline int write_env(struct mmc *mmc, unsigned long size, 186 unsigned long offset, const void *buffer) 187 { 188 uint blk_start, blk_cnt, n; 189 struct blk_desc *desc = mmc_get_blk_desc(mmc); 190 191 blk_start = ALIGN(offset, mmc->write_bl_len) / mmc->write_bl_len; 192 blk_cnt = ALIGN(size, mmc->write_bl_len) / mmc->write_bl_len; 193 194 n = blk_dwrite(desc, blk_start, blk_cnt, (u_char *)buffer); 195 196 return (n == blk_cnt) ? 0 : -1; 197 } 198 199 static int env_mmc_save(void) 200 { 201 ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1); 202 int dev = mmc_get_env_dev(); 203 struct mmc *mmc = find_mmc_device(dev); 204 u32 offset; 205 int ret, copy = 0; 206 const char *errmsg; 207 208 errmsg = init_mmc_for_env(mmc); 209 if (errmsg) { 210 printf("%s\n", errmsg); 211 return 1; 212 } 213 214 ret = env_export(env_new); 215 if (ret) 216 goto fini; 217 218 #ifdef CONFIG_ENV_OFFSET_REDUND 219 if (gd->env_valid == ENV_VALID) 220 copy = 1; 221 #endif 222 223 if (mmc_get_env_addr(mmc, copy, &offset)) { 224 ret = 1; 225 goto fini; 226 } 227 228 printf("Writing to %sMMC(%d)... ", copy ? "redundant " : "", dev); 229 if (write_env(mmc, CONFIG_ENV_SIZE, offset, (u_char *)env_new)) { 230 puts("failed\n"); 231 ret = 1; 232 goto fini; 233 } 234 235 ret = 0; 236 237 #ifdef CONFIG_ENV_OFFSET_REDUND 238 gd->env_valid = gd->env_valid == ENV_REDUND ? ENV_VALID : ENV_REDUND; 239 #endif 240 241 fini: 242 fini_mmc_for_env(mmc); 243 return ret; 244 } 245 #endif /* CONFIG_CMD_SAVEENV && !CONFIG_SPL_BUILD */ 246 247 static inline int read_env(struct mmc *mmc, unsigned long size, 248 unsigned long offset, const void *buffer) 249 { 250 uint blk_start, blk_cnt, n; 251 struct blk_desc *desc = mmc_get_blk_desc(mmc); 252 253 blk_start = ALIGN(offset, mmc->read_bl_len) / mmc->read_bl_len; 254 blk_cnt = ALIGN(size, mmc->read_bl_len) / mmc->read_bl_len; 255 256 n = blk_dread(desc, blk_start, blk_cnt, (uchar *)buffer); 257 258 return (n == blk_cnt) ? 0 : -1; 259 } 260 261 #ifdef CONFIG_ENV_OFFSET_REDUND 262 static int env_mmc_load(void) 263 { 264 #if !defined(ENV_IS_EMBEDDED) 265 struct mmc *mmc; 266 u32 offset1, offset2; 267 int read1_fail = 0, read2_fail = 0; 268 int ret; 269 int dev = mmc_get_env_dev(); 270 const char *errmsg = NULL; 271 272 ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env1, 1); 273 ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env2, 1); 274 275 mmc_initialize(NULL); 276 277 mmc = find_mmc_device(dev); 278 279 errmsg = init_mmc_for_env(mmc); 280 if (errmsg) { 281 ret = -EIO; 282 goto err; 283 } 284 285 if (mmc_get_env_addr(mmc, 0, &offset1) || 286 mmc_get_env_addr(mmc, 1, &offset2)) { 287 ret = -EIO; 288 goto fini; 289 } 290 291 read1_fail = read_env(mmc, CONFIG_ENV_SIZE, offset1, tmp_env1); 292 read2_fail = read_env(mmc, CONFIG_ENV_SIZE, offset2, tmp_env2); 293 294 ret = env_import_redund((char *)tmp_env1, read1_fail, (char *)tmp_env2, 295 read2_fail); 296 297 fini: 298 fini_mmc_for_env(mmc); 299 err: 300 if (ret) 301 set_default_env(errmsg, 0); 302 303 #endif 304 return ret; 305 } 306 #else /* ! CONFIG_ENV_OFFSET_REDUND */ 307 static int env_mmc_load(void) 308 { 309 #if !defined(ENV_IS_EMBEDDED) 310 ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE); 311 struct mmc *mmc; 312 u32 offset; 313 int ret; 314 int dev = mmc_get_env_dev(); 315 const char *errmsg; 316 317 mmc = find_mmc_device(dev); 318 319 errmsg = init_mmc_for_env(mmc); 320 if (errmsg) { 321 ret = -EIO; 322 goto err; 323 } 324 325 if (mmc_get_env_addr(mmc, 0, &offset)) { 326 ret = -EIO; 327 goto fini; 328 } 329 330 if (read_env(mmc, CONFIG_ENV_SIZE, offset, buf)) { 331 errmsg = "!read failed"; 332 ret = -EIO; 333 goto fini; 334 } 335 336 ret = env_import(buf, 1); 337 338 fini: 339 fini_mmc_for_env(mmc); 340 err: 341 if (ret) 342 set_default_env(errmsg, 0); 343 #endif 344 return ret; 345 } 346 #endif /* CONFIG_ENV_OFFSET_REDUND */ 347 348 U_BOOT_ENV_LOCATION(mmc) = { 349 .location = ENVL_MMC, 350 ENV_NAME("MMC") 351 .load = env_mmc_load, 352 #ifndef CONFIG_SPL_BUILD 353 .save = env_save_ptr(env_mmc_save), 354 #endif 355 }; 356