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 static int eeprom_bus_read(unsigned dev_addr, unsigned offset, 25 uchar *buffer, unsigned cnt) 26 { 27 int rcode; 28 #if defined(CONFIG_I2C_ENV_EEPROM_BUS) 29 int old_bus = i2c_get_bus_num(); 30 31 if (old_bus != CONFIG_I2C_ENV_EEPROM_BUS) 32 i2c_set_bus_num(CONFIG_I2C_ENV_EEPROM_BUS); 33 #endif 34 35 rcode = eeprom_read(dev_addr, offset, buffer, cnt); 36 37 #if defined(CONFIG_I2C_ENV_EEPROM_BUS) 38 i2c_set_bus_num(old_bus); 39 #endif 40 41 return rcode; 42 } 43 44 static int eeprom_bus_write(unsigned dev_addr, unsigned offset, 45 uchar *buffer, unsigned cnt) 46 { 47 int rcode; 48 #if defined(CONFIG_I2C_ENV_EEPROM_BUS) 49 int old_bus = i2c_get_bus_num(); 50 51 if (old_bus != CONFIG_I2C_ENV_EEPROM_BUS) 52 i2c_set_bus_num(CONFIG_I2C_ENV_EEPROM_BUS); 53 #endif 54 55 rcode = eeprom_write(dev_addr, offset, buffer, cnt); 56 57 #if defined(CONFIG_I2C_ENV_EEPROM_BUS) 58 i2c_set_bus_num(old_bus); 59 #endif 60 61 return rcode; 62 } 63 64 /** Call this function from overridden env_get_char_spec() if you need 65 * this functionality. 66 */ 67 int env_eeprom_get_char(int index) 68 { 69 uchar c; 70 unsigned int off = CONFIG_ENV_OFFSET; 71 72 #ifdef CONFIG_ENV_OFFSET_REDUND 73 if (gd->env_valid == ENV_REDUND) 74 off = CONFIG_ENV_OFFSET_REDUND; 75 #endif 76 eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, 77 off + index + offsetof(env_t, data), &c, 1); 78 79 return c; 80 } 81 82 static int env_eeprom_load(void) 83 { 84 char buf_env[CONFIG_ENV_SIZE]; 85 unsigned int off = CONFIG_ENV_OFFSET; 86 87 #ifdef CONFIG_ENV_OFFSET_REDUND 88 ulong len, crc[2], crc_tmp; 89 unsigned int off_env[2]; 90 uchar rdbuf[64], flags[2]; 91 int i, crc_ok[2] = {0, 0}; 92 93 eeprom_init(-1); /* prepare for EEPROM read/write */ 94 95 off_env[0] = CONFIG_ENV_OFFSET; 96 off_env[1] = CONFIG_ENV_OFFSET_REDUND; 97 98 for (i = 0; i < 2; i++) { 99 /* read CRC */ 100 eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, 101 off_env[i] + offsetof(env_t, crc), 102 (uchar *)&crc[i], sizeof(ulong)); 103 /* read FLAGS */ 104 eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, 105 off_env[i] + offsetof(env_t, flags), 106 (uchar *)&flags[i], sizeof(uchar)); 107 108 crc_tmp = 0; 109 len = ENV_SIZE; 110 off = off_env[i] + offsetof(env_t, data); 111 while (len > 0) { 112 int n = (len > sizeof(rdbuf)) ? sizeof(rdbuf) : len; 113 114 eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, off, 115 rdbuf, n); 116 117 crc_tmp = crc32(crc_tmp, rdbuf, n); 118 len -= n; 119 off += n; 120 } 121 122 if (crc_tmp == crc[i]) 123 crc_ok[i] = 1; 124 } 125 126 if (!crc_ok[0] && !crc_ok[1]) { 127 gd->env_addr = 0; 128 gd->env_valid = ENV_INVALID; 129 } else if (crc_ok[0] && !crc_ok[1]) { 130 gd->env_valid = ENV_VALID; 131 } else if (!crc_ok[0] && crc_ok[1]) { 132 gd->env_valid = ENV_REDUND; 133 } else { 134 /* both ok - check serial */ 135 if (flags[0] == ACTIVE_FLAG && flags[1] == OBSOLETE_FLAG) 136 gd->env_valid = ENV_VALID; 137 else if (flags[0] == OBSOLETE_FLAG && flags[1] == ACTIVE_FLAG) 138 gd->env_valid = ENV_REDUND; 139 else if (flags[0] == 0xFF && flags[1] == 0) 140 gd->env_valid = ENV_REDUND; 141 else if (flags[1] == 0xFF && flags[0] == 0) 142 gd->env_valid = ENV_VALID; 143 else /* flags are equal - almost impossible */ 144 gd->env_valid = ENV_VALID; 145 } 146 147 #else /* CONFIG_ENV_OFFSET_REDUND */ 148 ulong crc, len, new; 149 uchar rdbuf[64]; 150 151 eeprom_init(-1); /* prepare for EEPROM read/write */ 152 153 /* read old CRC */ 154 eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, 155 CONFIG_ENV_OFFSET + offsetof(env_t, crc), 156 (uchar *)&crc, sizeof(ulong)); 157 158 new = 0; 159 len = ENV_SIZE; 160 off = offsetof(env_t, data); 161 while (len > 0) { 162 int n = (len > sizeof(rdbuf)) ? sizeof(rdbuf) : len; 163 164 eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, 165 CONFIG_ENV_OFFSET + off, rdbuf, n); 166 new = crc32(new, rdbuf, n); 167 len -= n; 168 off += n; 169 } 170 171 if (crc == new) { 172 gd->env_valid = ENV_VALID; 173 } else { 174 gd->env_valid = ENV_INVALID; 175 } 176 #endif /* CONFIG_ENV_OFFSET_REDUND */ 177 178 off = CONFIG_ENV_OFFSET; 179 #ifdef CONFIG_ENV_OFFSET_REDUND 180 if (gd->env_valid == ENV_REDUND) 181 off = CONFIG_ENV_OFFSET_REDUND; 182 #endif 183 184 eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, 185 off, (uchar *)buf_env, CONFIG_ENV_SIZE); 186 187 return env_import(buf_env, 1); 188 } 189 190 static int env_eeprom_save(void) 191 { 192 env_t env_new; 193 int rc; 194 unsigned int off = CONFIG_ENV_OFFSET; 195 #ifdef CONFIG_ENV_OFFSET_REDUND 196 unsigned int off_red = CONFIG_ENV_OFFSET_REDUND; 197 char flag_obsolete = OBSOLETE_FLAG; 198 #endif 199 200 rc = env_export(&env_new); 201 if (rc) 202 return rc; 203 204 #ifdef CONFIG_ENV_OFFSET_REDUND 205 if (gd->env_valid == ENV_VALID) { 206 off = CONFIG_ENV_OFFSET_REDUND; 207 off_red = CONFIG_ENV_OFFSET; 208 } 209 210 env_new.flags = ACTIVE_FLAG; 211 #endif 212 213 rc = eeprom_bus_write(CONFIG_SYS_DEF_EEPROM_ADDR, 214 off, (uchar *)&env_new, CONFIG_ENV_SIZE); 215 216 #ifdef CONFIG_ENV_OFFSET_REDUND 217 if (rc == 0) { 218 eeprom_bus_write(CONFIG_SYS_DEF_EEPROM_ADDR, 219 off_red + offsetof(env_t, flags), 220 (uchar *)&flag_obsolete, 1); 221 222 if (gd->env_valid == ENV_VALID) 223 gd->env_valid = ENV_REDUND; 224 else 225 gd->env_valid = ENV_VALID; 226 } 227 #endif 228 return rc; 229 } 230 231 U_BOOT_ENV_LOCATION(eeprom) = { 232 .location = ENVL_EEPROM, 233 ENV_NAME("EEPROM") 234 .load = env_eeprom_load, 235 .save = env_save_ptr(env_eeprom_save), 236 }; 237