1 /* 2 * (C) Copyright 2000-2010 3 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. 4 * 5 * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com> 6 * Andreas Heppel <aheppel@sysgo.de> 7 * 8 * SPDX-License-Identifier: GPL-2.0+ 9 */ 10 11 #include <common.h> 12 #include <command.h> 13 #include <environment.h> 14 #include <linux/stddef.h> 15 #if defined(CONFIG_I2C_ENV_EEPROM_BUS) 16 #include <i2c.h> 17 #endif 18 #include <search.h> 19 #include <errno.h> 20 #include <linux/compiler.h> /* for BUG_ON */ 21 22 DECLARE_GLOBAL_DATA_PTR; 23 24 env_t *env_ptr; 25 26 char *env_name_spec = "EEPROM"; 27 28 static int eeprom_bus_read(unsigned dev_addr, unsigned offset, 29 uchar *buffer, unsigned cnt) 30 { 31 int rcode; 32 #if defined(CONFIG_I2C_ENV_EEPROM_BUS) 33 int old_bus = i2c_get_bus_num(); 34 35 if (old_bus != CONFIG_I2C_ENV_EEPROM_BUS) 36 i2c_set_bus_num(CONFIG_I2C_ENV_EEPROM_BUS); 37 #endif 38 39 rcode = eeprom_read(dev_addr, offset, buffer, cnt); 40 41 #if defined(CONFIG_I2C_ENV_EEPROM_BUS) 42 i2c_set_bus_num(old_bus); 43 #endif 44 45 return rcode; 46 } 47 48 static int eeprom_bus_write(unsigned dev_addr, unsigned offset, 49 uchar *buffer, unsigned cnt) 50 { 51 int rcode; 52 #if defined(CONFIG_I2C_ENV_EEPROM_BUS) 53 int old_bus = i2c_get_bus_num(); 54 55 if (old_bus != CONFIG_I2C_ENV_EEPROM_BUS) 56 i2c_set_bus_num(CONFIG_I2C_ENV_EEPROM_BUS); 57 #endif 58 59 rcode = eeprom_write(dev_addr, offset, buffer, cnt); 60 61 #if defined(CONFIG_I2C_ENV_EEPROM_BUS) 62 i2c_set_bus_num(old_bus); 63 #endif 64 65 return rcode; 66 } 67 68 uchar env_get_char_spec(int index) 69 { 70 uchar c; 71 unsigned int off = CONFIG_ENV_OFFSET; 72 73 #ifdef CONFIG_ENV_OFFSET_REDUND 74 if (gd->env_valid == 2) 75 off = CONFIG_ENV_OFFSET_REDUND; 76 #endif 77 eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, 78 off + index + offsetof(env_t, data), &c, 1); 79 80 return c; 81 } 82 83 void env_relocate_spec(void) 84 { 85 char buf_env[CONFIG_ENV_SIZE]; 86 unsigned int off = CONFIG_ENV_OFFSET; 87 88 #ifdef CONFIG_ENV_OFFSET_REDUND 89 ulong len, crc[2], crc_tmp; 90 unsigned int off_env[2]; 91 uchar rdbuf[64], flags[2]; 92 int i, crc_ok[2] = {0, 0}; 93 94 eeprom_init(-1); /* prepare for EEPROM read/write */ 95 96 off_env[0] = CONFIG_ENV_OFFSET; 97 off_env[1] = CONFIG_ENV_OFFSET_REDUND; 98 99 for (i = 0; i < 2; i++) { 100 /* read CRC */ 101 eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, 102 off_env[i] + offsetof(env_t, crc), 103 (uchar *)&crc[i], sizeof(ulong)); 104 /* read FLAGS */ 105 eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, 106 off_env[i] + offsetof(env_t, flags), 107 (uchar *)&flags[i], sizeof(uchar)); 108 109 crc_tmp = 0; 110 len = ENV_SIZE; 111 off = off_env[i] + offsetof(env_t, data); 112 while (len > 0) { 113 int n = (len > sizeof(rdbuf)) ? sizeof(rdbuf) : len; 114 115 eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, off, 116 rdbuf, n); 117 118 crc_tmp = crc32(crc_tmp, rdbuf, n); 119 len -= n; 120 off += n; 121 } 122 123 if (crc_tmp == crc[i]) 124 crc_ok[i] = 1; 125 } 126 127 if (!crc_ok[0] && !crc_ok[1]) { 128 gd->env_addr = 0; 129 gd->env_valid = 0; 130 } else if (crc_ok[0] && !crc_ok[1]) { 131 gd->env_valid = 1; 132 } else if (!crc_ok[0] && crc_ok[1]) { 133 gd->env_valid = 2; 134 } else { 135 /* both ok - check serial */ 136 if (flags[0] == ACTIVE_FLAG && flags[1] == OBSOLETE_FLAG) 137 gd->env_valid = 1; 138 else if (flags[0] == OBSOLETE_FLAG && flags[1] == ACTIVE_FLAG) 139 gd->env_valid = 2; 140 else if (flags[0] == 0xFF && flags[1] == 0) 141 gd->env_valid = 2; 142 else if (flags[1] == 0xFF && flags[0] == 0) 143 gd->env_valid = 1; 144 else /* flags are equal - almost impossible */ 145 gd->env_valid = 1; 146 } 147 148 #else /* CONFIG_ENV_OFFSET_REDUND */ 149 ulong crc, len, new; 150 uchar rdbuf[64]; 151 152 eeprom_init(-1); /* prepare for EEPROM read/write */ 153 154 /* read old CRC */ 155 eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, 156 CONFIG_ENV_OFFSET + offsetof(env_t, crc), 157 (uchar *)&crc, sizeof(ulong)); 158 159 new = 0; 160 len = ENV_SIZE; 161 off = offsetof(env_t, data); 162 while (len > 0) { 163 int n = (len > sizeof(rdbuf)) ? sizeof(rdbuf) : len; 164 165 eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, 166 CONFIG_ENV_OFFSET + off, rdbuf, n); 167 new = crc32(new, rdbuf, n); 168 len -= n; 169 off += n; 170 } 171 172 if (crc == new) { 173 gd->env_valid = 1; 174 } else { 175 gd->env_valid = 0; 176 } 177 #endif /* CONFIG_ENV_OFFSET_REDUND */ 178 179 off = CONFIG_ENV_OFFSET; 180 #ifdef CONFIG_ENV_OFFSET_REDUND 181 if (gd->env_valid == 2) 182 off = CONFIG_ENV_OFFSET_REDUND; 183 #endif 184 185 eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, 186 off, (uchar *)buf_env, CONFIG_ENV_SIZE); 187 188 env_import(buf_env, 1); 189 } 190 191 int saveenv(void) 192 { 193 env_t env_new; 194 int rc; 195 unsigned int off = CONFIG_ENV_OFFSET; 196 #ifdef CONFIG_ENV_OFFSET_REDUND 197 unsigned int off_red = CONFIG_ENV_OFFSET_REDUND; 198 char flag_obsolete = OBSOLETE_FLAG; 199 #endif 200 201 BUG_ON(env_ptr != NULL); 202 203 rc = env_export(&env_new); 204 if (rc) 205 return rc; 206 207 #ifdef CONFIG_ENV_OFFSET_REDUND 208 if (gd->env_valid == 1) { 209 off = CONFIG_ENV_OFFSET_REDUND; 210 off_red = CONFIG_ENV_OFFSET; 211 } 212 213 env_new.flags = ACTIVE_FLAG; 214 #endif 215 216 rc = eeprom_bus_write(CONFIG_SYS_DEF_EEPROM_ADDR, 217 off, (uchar *)&env_new, CONFIG_ENV_SIZE); 218 219 #ifdef CONFIG_ENV_OFFSET_REDUND 220 if (rc == 0) { 221 eeprom_bus_write(CONFIG_SYS_DEF_EEPROM_ADDR, 222 off_red + offsetof(env_t, flags), 223 (uchar *)&flag_obsolete, 1); 224 225 if (gd->env_valid == 1) 226 gd->env_valid = 2; 227 else 228 gd->env_valid = 1; 229 } 230 #endif 231 return rc; 232 } 233 234 /* 235 * Initialize Environment use 236 * 237 * We are still running from ROM, so data use is limited. 238 * Use a (moderately small) buffer on the stack 239 */ 240 int env_init(void) 241 { 242 gd->env_addr = (ulong)&default_environment[0]; 243 gd->env_valid = 1; 244 return 0; 245 } 246