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