Merge branch 'master' of git://git.denx.de/u-boot-fdt
[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  * See file CREDITS for list of people who contributed to this
9  * project.
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License as
13  * published by the Free Software Foundation; either version 2 of
14  * the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
24  * MA 02111-1307 USA
25  */
26
27 #include <common.h>
28 #include <command.h>
29 #include <environment.h>
30 #include <linux/stddef.h>
31 #if defined(CONFIG_I2C_ENV_EEPROM_BUS)
32 #include <i2c.h>
33 #endif
34 #include <search.h>
35 #include <errno.h>
36 #include <linux/compiler.h>     /* for BUG_ON */
37
38 DECLARE_GLOBAL_DATA_PTR;
39
40 env_t *env_ptr = NULL;
41
42 char *env_name_spec = "EEPROM";
43 int env_eeprom_bus = -1;
44
45 static int eeprom_bus_read(unsigned dev_addr, unsigned offset,
46         uchar *buffer, unsigned cnt)
47 {
48         int rcode;
49 #if defined(CONFIG_I2C_ENV_EEPROM_BUS)
50         int old_bus = i2c_get_bus_num();
51
52         if (gd->flags & GD_FLG_RELOC) {
53                 if (env_eeprom_bus == -1) {
54                         I2C_MUX_DEVICE *dev = NULL;
55                         dev = i2c_mux_ident_muxstring(
56                                 (uchar *)CONFIG_I2C_ENV_EEPROM_BUS);
57                         if (dev != NULL)
58                                 env_eeprom_bus = dev->busid;
59                         else
60                                 printf ("error adding env eeprom bus.\n");
61                 }
62                 if (old_bus != env_eeprom_bus) {
63                         i2c_set_bus_num(env_eeprom_bus);
64                         old_bus = env_eeprom_bus;
65                 }
66         } else {
67                 rcode = i2c_mux_ident_muxstring_f(
68                                 (uchar *)CONFIG_I2C_ENV_EEPROM_BUS);
69         }
70 #endif
71
72         rcode = eeprom_read (dev_addr, offset, buffer, cnt);
73
74 #if defined(CONFIG_I2C_ENV_EEPROM_BUS)
75         if (old_bus != env_eeprom_bus)
76                 i2c_set_bus_num(old_bus);
77 #endif
78         return rcode;
79 }
80
81 static int eeprom_bus_write(unsigned dev_addr, unsigned offset,
82         uchar *buffer, unsigned cnt)
83 {
84         int rcode;
85 #if defined(CONFIG_I2C_ENV_EEPROM_BUS)
86         int old_bus = i2c_get_bus_num();
87
88         rcode = i2c_mux_ident_muxstring_f((uchar *)CONFIG_I2C_ENV_EEPROM_BUS);
89 #endif
90         rcode = eeprom_write(dev_addr, offset, buffer, cnt);
91 #if defined(CONFIG_I2C_ENV_EEPROM_BUS)
92         i2c_set_bus_num(old_bus);
93 #endif
94         return rcode;
95 }
96
97 uchar env_get_char_spec (int index)
98 {
99         uchar c;
100         unsigned int off;
101         off = CONFIG_ENV_OFFSET;
102
103 #ifdef CONFIG_ENV_OFFSET_REDUND
104         if (gd->env_valid == 2)
105                 off = CONFIG_ENV_OFFSET_REDUND;
106 #endif
107         eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
108                      off + index + offsetof(env_t,data),
109                      &c, 1);
110
111         return (c);
112 }
113
114 void env_relocate_spec (void)
115 {
116         char buf[CONFIG_ENV_SIZE];
117         unsigned int off = CONFIG_ENV_OFFSET;
118
119 #ifdef CONFIG_ENV_OFFSET_REDUND
120         if (gd->env_valid == 2)
121                 off = CONFIG_ENV_OFFSET_REDUND;
122 #endif
123         eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
124                      off,
125                      (uchar *)buf,
126                      CONFIG_ENV_SIZE);
127
128         env_import(buf, 1);
129 }
130
131 int saveenv(void)
132 {
133         env_t   env_new;
134         ssize_t len;
135         char    *res;
136         int rc;
137         unsigned int off = CONFIG_ENV_OFFSET;
138 #ifdef CONFIG_ENV_OFFSET_REDUND
139         unsigned int off_red = CONFIG_ENV_OFFSET_REDUND;
140         char flag_obsolete = OBSOLETE_FLAG;
141 #endif
142
143         BUG_ON(env_ptr != NULL);
144
145         res = (char *)&env_new.data;
146         len = hexport_r(&env_htab, '\0', &res, ENV_SIZE);
147         if (len < 0) {
148                 error("Cannot export environment: errno = %d\n", errno);
149                 return 1;
150         }
151         env_new.crc = crc32(0, env_new.data, ENV_SIZE);
152
153 #ifdef CONFIG_ENV_OFFSET_REDUND
154         if (gd->env_valid == 1) {
155                 off = CONFIG_ENV_OFFSET_REDUND;
156                 off_red = CONFIG_ENV_OFFSET;
157         }
158
159         env_new.flags = ACTIVE_FLAG;
160 #endif
161
162         rc = eeprom_bus_write(CONFIG_SYS_DEF_EEPROM_ADDR,
163                              off,
164                              (uchar *)&env_new,
165                              CONFIG_ENV_SIZE);
166
167 #ifdef CONFIG_ENV_OFFSET_REDUND
168         if (rc == 0) {
169                 eeprom_bus_write(CONFIG_SYS_DEF_EEPROM_ADDR,
170                                   off_red + offsetof(env_t,flags),
171                                   (uchar *)&flag_obsolete,
172                                   1);
173                 if (gd->env_valid == 1)
174                         gd->env_valid = 2;
175                 else
176                         gd->env_valid = 1;
177
178         }
179 #endif
180
181         return rc;
182 }
183
184 /*
185  * Initialize Environment use
186  *
187  * We are still running from ROM, so data use is limited.
188  * Use a (moderately small) buffer on the stack
189  */
190
191 #ifdef CONFIG_ENV_OFFSET_REDUND
192 int env_init(void)
193 {
194         ulong len;
195         ulong crc[2], crc_tmp;
196         unsigned int off, off_env[2];
197         uchar buf[64];
198         int crc_ok[2] = {0,0};
199         unsigned char flags[2];
200         int i;
201
202         eeprom_init();  /* prepare for EEPROM read/write */
203
204         off_env[0] = CONFIG_ENV_OFFSET;
205         off_env[1] = CONFIG_ENV_OFFSET_REDUND;
206
207         for (i = 0; i < 2; i++) {
208                 /* read CRC */
209                 eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
210                         off_env[i] + offsetof(env_t,crc),
211                         (uchar *)&crc[i], sizeof(ulong));
212                 /* read FLAGS */
213                 eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
214                         off_env[i] + offsetof(env_t,flags),
215                         (uchar *)&flags[i], sizeof(uchar));
216
217                 crc_tmp = 0;
218                 len = ENV_SIZE;
219                 off = off_env[i] + offsetof(env_t,data);
220                 while (len > 0) {
221                         int n = (len > sizeof(buf)) ? sizeof(buf) : len;
222
223                         eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, off,
224                                 buf, n);
225
226                         crc_tmp = crc32(crc_tmp, buf, n);
227                         len -= n;
228                         off += n;
229                 }
230                 if (crc_tmp == crc[i])
231                         crc_ok[i] = 1;
232         }
233
234         if (!crc_ok[0] && !crc_ok[1]) {
235                 gd->env_addr  = 0;
236                 gd->env_valid = 0;
237
238                 return 0;
239         } else if (crc_ok[0] && !crc_ok[1]) {
240                 gd->env_valid = 1;
241         }
242         else if (!crc_ok[0] && crc_ok[1]) {
243                 gd->env_valid = 2;
244         } else {
245                 /* both ok - check serial */
246                 if (flags[0] == ACTIVE_FLAG && flags[1] == OBSOLETE_FLAG)
247                         gd->env_valid = 1;
248                 else if (flags[0] == OBSOLETE_FLAG && flags[1] == ACTIVE_FLAG)
249                         gd->env_valid = 2;
250                 else if (flags[0] == 0xFF && flags[1] == 0)
251                         gd->env_valid = 2;
252                 else if(flags[1] == 0xFF && flags[0] == 0)
253                         gd->env_valid = 1;
254                 else /* flags are equal - almost impossible */
255                         gd->env_valid = 1;
256         }
257
258         if (gd->env_valid == 2)
259                 gd->env_addr = off_env[1] + offsetof(env_t,data);
260         else if (gd->env_valid == 1)
261                 gd->env_addr = off_env[0] + offsetof(env_t,data);
262
263         return (0);
264 }
265 #else
266 int env_init(void)
267 {
268         ulong crc, len, new;
269         unsigned off;
270         uchar buf[64];
271
272         eeprom_init();  /* prepare for EEPROM read/write */
273
274         /* read old CRC */
275         eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
276                      CONFIG_ENV_OFFSET+offsetof(env_t,crc),
277                      (uchar *)&crc, sizeof(ulong));
278
279         new = 0;
280         len = ENV_SIZE;
281         off = offsetof(env_t,data);
282
283         while (len > 0) {
284                 int n = (len > sizeof(buf)) ? sizeof(buf) : len;
285
286                 eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
287                                 CONFIG_ENV_OFFSET + off, buf, n);
288                 new = crc32(new, buf, n);
289                 len -= n;
290                 off += n;
291         }
292
293         if (crc == new) {
294                 gd->env_addr  = offsetof(env_t,data);
295                 gd->env_valid = 1;
296         } else {
297                 gd->env_addr  = 0;
298                 gd->env_valid = 0;
299         }
300
301         return (0);
302 }
303 #endif