1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * (C) Copyright 2000-2010 4 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. 5 * 6 * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com> 7 * Andreas Heppel <aheppel@sysgo.de> 8 */ 9 10 #include <common.h> 11 #include <command.h> 12 #include <environment.h> 13 #include <linux/stddef.h> 14 #if defined(CONFIG_I2C_ENV_EEPROM_BUS) 15 #include <i2c.h> 16 #endif 17 #include <search.h> 18 #include <errno.h> 19 #include <linux/compiler.h> /* for BUG_ON */ 20 21 DECLARE_GLOBAL_DATA_PTR; 22 23 static int eeprom_bus_read(unsigned dev_addr, unsigned offset, 24 uchar *buffer, unsigned cnt) 25 { 26 int rcode; 27 #if defined(CONFIG_I2C_ENV_EEPROM_BUS) 28 int old_bus = i2c_get_bus_num(); 29 30 if (old_bus != CONFIG_I2C_ENV_EEPROM_BUS) 31 i2c_set_bus_num(CONFIG_I2C_ENV_EEPROM_BUS); 32 #endif 33 34 rcode = eeprom_read(dev_addr, offset, buffer, cnt); 35 36 #if defined(CONFIG_I2C_ENV_EEPROM_BUS) 37 i2c_set_bus_num(old_bus); 38 #endif 39 40 return rcode; 41 } 42 43 static int eeprom_bus_write(unsigned dev_addr, unsigned offset, 44 uchar *buffer, unsigned cnt) 45 { 46 int rcode; 47 #if defined(CONFIG_I2C_ENV_EEPROM_BUS) 48 int old_bus = i2c_get_bus_num(); 49 50 if (old_bus != CONFIG_I2C_ENV_EEPROM_BUS) 51 i2c_set_bus_num(CONFIG_I2C_ENV_EEPROM_BUS); 52 #endif 53 54 rcode = eeprom_write(dev_addr, offset, buffer, cnt); 55 56 #if defined(CONFIG_I2C_ENV_EEPROM_BUS) 57 i2c_set_bus_num(old_bus); 58 #endif 59 60 return rcode; 61 } 62 63 /** Call this function from overridden env_get_char_spec() if you need 64 * this functionality. 65 */ 66 int env_eeprom_get_char(int index) 67 { 68 uchar c; 69 unsigned int off = CONFIG_ENV_OFFSET; 70 71 #ifdef CONFIG_ENV_OFFSET_REDUND 72 if (gd->env_valid == ENV_REDUND) 73 off = CONFIG_ENV_OFFSET_REDUND; 74 #endif 75 eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, 76 off + index + offsetof(env_t, data), &c, 1); 77 78 return c; 79 } 80 81 static int env_eeprom_load(void) 82 { 83 char buf_env[CONFIG_ENV_SIZE]; 84 unsigned int off = CONFIG_ENV_OFFSET; 85 86 #ifdef CONFIG_ENV_OFFSET_REDUND 87 ulong len, crc[2], crc_tmp; 88 unsigned int off_env[2]; 89 uchar rdbuf[64], flags[2]; 90 int i, crc_ok[2] = {0, 0}; 91 92 eeprom_init(-1); /* prepare for EEPROM read/write */ 93 94 off_env[0] = CONFIG_ENV_OFFSET; 95 off_env[1] = CONFIG_ENV_OFFSET_REDUND; 96 97 for (i = 0; i < 2; i++) { 98 /* read CRC */ 99 eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, 100 off_env[i] + offsetof(env_t, crc), 101 (uchar *)&crc[i], sizeof(ulong)); 102 /* read FLAGS */ 103 eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, 104 off_env[i] + offsetof(env_t, flags), 105 (uchar *)&flags[i], sizeof(uchar)); 106 107 crc_tmp = 0; 108 len = ENV_SIZE; 109 off = off_env[i] + offsetof(env_t, data); 110 while (len > 0) { 111 int n = (len > sizeof(rdbuf)) ? sizeof(rdbuf) : len; 112 113 eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, off, 114 rdbuf, n); 115 116 crc_tmp = crc32(crc_tmp, rdbuf, n); 117 len -= n; 118 off += n; 119 } 120 121 if (crc_tmp == crc[i]) 122 crc_ok[i] = 1; 123 } 124 125 if (!crc_ok[0] && !crc_ok[1]) { 126 gd->env_addr = 0; 127 gd->env_valid = ENV_INVALID; 128 } else if (crc_ok[0] && !crc_ok[1]) { 129 gd->env_valid = ENV_VALID; 130 } else if (!crc_ok[0] && crc_ok[1]) { 131 gd->env_valid = ENV_REDUND; 132 } else { 133 /* both ok - check serial */ 134 if (flags[0] == ACTIVE_FLAG && flags[1] == OBSOLETE_FLAG) 135 gd->env_valid = ENV_VALID; 136 else if (flags[0] == OBSOLETE_FLAG && flags[1] == ACTIVE_FLAG) 137 gd->env_valid = ENV_REDUND; 138 else if (flags[0] == 0xFF && flags[1] == 0) 139 gd->env_valid = ENV_REDUND; 140 else if (flags[1] == 0xFF && flags[0] == 0) 141 gd->env_valid = ENV_VALID; 142 else /* flags are equal - almost impossible */ 143 gd->env_valid = ENV_VALID; 144 } 145 146 #else /* CONFIG_ENV_OFFSET_REDUND */ 147 ulong crc, len, new; 148 uchar rdbuf[64]; 149 150 eeprom_init(-1); /* prepare for EEPROM read/write */ 151 152 /* read old CRC */ 153 eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, 154 CONFIG_ENV_OFFSET + offsetof(env_t, crc), 155 (uchar *)&crc, sizeof(ulong)); 156 157 new = 0; 158 len = ENV_SIZE; 159 off = offsetof(env_t, data); 160 while (len > 0) { 161 int n = (len > sizeof(rdbuf)) ? sizeof(rdbuf) : len; 162 163 eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, 164 CONFIG_ENV_OFFSET + off, rdbuf, n); 165 new = crc32(new, rdbuf, n); 166 len -= n; 167 off += n; 168 } 169 170 if (crc == new) { 171 gd->env_valid = ENV_VALID; 172 } else { 173 gd->env_valid = ENV_INVALID; 174 } 175 #endif /* CONFIG_ENV_OFFSET_REDUND */ 176 177 off = CONFIG_ENV_OFFSET; 178 #ifdef CONFIG_ENV_OFFSET_REDUND 179 if (gd->env_valid == ENV_REDUND) 180 off = CONFIG_ENV_OFFSET_REDUND; 181 #endif 182 183 eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, 184 off, (uchar *)buf_env, CONFIG_ENV_SIZE); 185 186 return env_import(buf_env, 1); 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 .load = env_eeprom_load, 234 .save = env_save_ptr(env_eeprom_save), 235 }; 236