Merge branch 'master' of git://www.denx.de/git/u-boot-cfi-flash
[platform/kernel/u-boot.git] / common / env_eeprom.c
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 int env_eeprom_bus = -1;
28
29 static int eeprom_bus_read(unsigned dev_addr, unsigned offset,
30                            uchar *buffer, unsigned cnt)
31 {
32         int rcode;
33 #if defined(CONFIG_I2C_ENV_EEPROM_BUS)
34         int old_bus = i2c_get_bus_num();
35
36         if (old_bus != CONFIG_I2C_ENV_EEPROM_BUS)
37                 i2c_set_bus_num(CONFIG_I2C_ENV_EEPROM_BUS);
38 #endif
39
40         rcode = eeprom_read(dev_addr, offset, buffer, cnt);
41
42 #if defined(CONFIG_I2C_ENV_EEPROM_BUS)
43         if (old_bus != env_eeprom_bus)
44                 i2c_set_bus_num(old_bus);
45 #endif
46
47         return rcode;
48 }
49
50 static int eeprom_bus_write(unsigned dev_addr, unsigned offset,
51                             uchar *buffer, unsigned cnt)
52 {
53         int rcode;
54 #if defined(CONFIG_I2C_ENV_EEPROM_BUS)
55         int old_bus = i2c_get_bus_num();
56
57         if (old_bus != CONFIG_I2C_ENV_EEPROM_BUS)
58                 i2c_set_bus_num(CONFIG_I2C_ENV_EEPROM_BUS);
59 #endif
60
61         rcode = eeprom_write(dev_addr, offset, buffer, cnt);
62
63 #if defined(CONFIG_I2C_ENV_EEPROM_BUS)
64         i2c_set_bus_num(old_bus);
65 #endif
66         return rcode;
67 }
68
69 uchar env_get_char_spec(int index)
70 {
71         uchar c;
72         unsigned int off = CONFIG_ENV_OFFSET;
73
74 #ifdef CONFIG_ENV_OFFSET_REDUND
75         if (gd->env_valid == 2)
76                 off = CONFIG_ENV_OFFSET_REDUND;
77 #endif
78         eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
79                         off + index + offsetof(env_t, data), &c, 1);
80
81         return c;
82 }
83
84 void env_relocate_spec(void)
85 {
86         char buf[CONFIG_ENV_SIZE];
87         unsigned int off = CONFIG_ENV_OFFSET;
88
89 #ifdef CONFIG_ENV_OFFSET_REDUND
90         if (gd->env_valid == 2)
91                 off = CONFIG_ENV_OFFSET_REDUND;
92 #endif
93         eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
94                         off, (uchar *)buf, CONFIG_ENV_SIZE);
95
96         env_import(buf, 1);
97 }
98
99 int saveenv(void)
100 {
101         env_t   env_new;
102         ssize_t len;
103         char    *res;
104         int     rc;
105         unsigned int off        = CONFIG_ENV_OFFSET;
106 #ifdef CONFIG_ENV_OFFSET_REDUND
107         unsigned int off_red    = CONFIG_ENV_OFFSET_REDUND;
108         char flag_obsolete      = OBSOLETE_FLAG;
109 #endif
110
111         BUG_ON(env_ptr != NULL);
112
113         res = (char *)&env_new.data;
114         len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL);
115         if (len < 0) {
116                 error("Cannot export environment: errno = %d\n", errno);
117                 return 1;
118         }
119         env_new.crc = crc32(0, env_new.data, ENV_SIZE);
120
121 #ifdef CONFIG_ENV_OFFSET_REDUND
122         if (gd->env_valid == 1) {
123                 off     = CONFIG_ENV_OFFSET_REDUND;
124                 off_red = CONFIG_ENV_OFFSET;
125         }
126
127         env_new.flags = ACTIVE_FLAG;
128 #endif
129
130         rc = eeprom_bus_write(CONFIG_SYS_DEF_EEPROM_ADDR,
131                               off, (uchar *)&env_new, CONFIG_ENV_SIZE);
132
133 #ifdef CONFIG_ENV_OFFSET_REDUND
134         if (rc == 0) {
135                 eeprom_bus_write(CONFIG_SYS_DEF_EEPROM_ADDR,
136                                  off_red + offsetof(env_t, flags),
137                                  (uchar *)&flag_obsolete, 1);
138
139                 if (gd->env_valid == 1)
140                         gd->env_valid = 2;
141                 else
142                         gd->env_valid = 1;
143         }
144 #endif
145         return rc;
146 }
147
148 /*
149  * Initialize Environment use
150  *
151  * We are still running from ROM, so data use is limited.
152  * Use a (moderately small) buffer on the stack
153  */
154 #ifdef CONFIG_ENV_OFFSET_REDUND
155 int env_init(void)
156 {
157         ulong len, crc[2], crc_tmp;
158         unsigned int off, off_env[2];
159         uchar buf[64], flags[2];
160         int i, crc_ok[2] = {0, 0};
161
162         eeprom_init();  /* prepare for EEPROM read/write */
163
164         off_env[0] = CONFIG_ENV_OFFSET;
165         off_env[1] = CONFIG_ENV_OFFSET_REDUND;
166
167         for (i = 0; i < 2; i++) {
168                 /* read CRC */
169                 eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
170                                 off_env[i] + offsetof(env_t, crc),
171                                 (uchar *)&crc[i], sizeof(ulong));
172                 /* read FLAGS */
173                 eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
174                                 off_env[i] + offsetof(env_t, flags),
175                                 (uchar *)&flags[i], sizeof(uchar));
176
177                 crc_tmp = 0;
178                 len = ENV_SIZE;
179                 off = off_env[i] + offsetof(env_t, data);
180                 while (len > 0) {
181                         int n = (len > sizeof(buf)) ? sizeof(buf) : len;
182
183                         eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, off,
184                                         buf, n);
185
186                         crc_tmp = crc32(crc_tmp, buf, n);
187                         len -= n;
188                         off += n;
189                 }
190
191                 if (crc_tmp == crc[i])
192                         crc_ok[i] = 1;
193         }
194
195         if (!crc_ok[0] && !crc_ok[1]) {
196                 gd->env_addr    = 0;
197                 gd->env_valid   = 0;
198
199                 return 0;
200         } else if (crc_ok[0] && !crc_ok[1]) {
201                 gd->env_valid = 1;
202         } else if (!crc_ok[0] && crc_ok[1]) {
203                 gd->env_valid = 2;
204         } else {
205                 /* both ok - check serial */
206                 if (flags[0] == ACTIVE_FLAG && flags[1] == OBSOLETE_FLAG)
207                         gd->env_valid = 1;
208                 else if (flags[0] == OBSOLETE_FLAG && flags[1] == ACTIVE_FLAG)
209                         gd->env_valid = 2;
210                 else if (flags[0] == 0xFF && flags[1] == 0)
211                         gd->env_valid = 2;
212                 else if (flags[1] == 0xFF && flags[0] == 0)
213                         gd->env_valid = 1;
214                 else /* flags are equal - almost impossible */
215                         gd->env_valid = 1;
216         }
217
218         if (gd->env_valid == 2)
219                 gd->env_addr = off_env[1] + offsetof(env_t, data);
220         else if (gd->env_valid == 1)
221                 gd->env_addr = off_env[0] + offsetof(env_t, data);
222
223         return 0;
224 }
225 #else
226 int env_init(void)
227 {
228         ulong crc, len, new;
229         unsigned off;
230         uchar buf[64];
231
232         eeprom_init();  /* prepare for EEPROM read/write */
233
234         /* read old CRC */
235         eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
236                         CONFIG_ENV_OFFSET + offsetof(env_t, crc),
237                         (uchar *)&crc, sizeof(ulong));
238
239         new = 0;
240         len = ENV_SIZE;
241         off = offsetof(env_t, data);
242
243         while (len > 0) {
244                 int n = (len > sizeof(buf)) ? sizeof(buf) : len;
245
246                 eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
247                                 CONFIG_ENV_OFFSET + off, buf, n);
248                 new = crc32(new, buf, n);
249                 len -= n;
250                 off += n;
251         }
252
253         if (crc == new) {
254                 gd->env_addr    = offsetof(env_t, data);
255                 gd->env_valid   = 1;
256         } else {
257                 gd->env_addr    = 0;
258                 gd->env_valid   = 0;
259         }
260
261         return 0;
262 }
263 #endif