Merge samsung, imx, tegra into u-boot-arm/master
[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  * 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;
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 = CONFIG_ENV_OFFSET;
101
102 #ifdef CONFIG_ENV_OFFSET_REDUND
103         if (gd->env_valid == 2)
104                 off = CONFIG_ENV_OFFSET_REDUND;
105 #endif
106         eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
107                         off + index + offsetof(env_t, data), &c, 1);
108
109         return c;
110 }
111
112 void env_relocate_spec(void)
113 {
114         char buf[CONFIG_ENV_SIZE];
115         unsigned int off = CONFIG_ENV_OFFSET;
116
117 #ifdef CONFIG_ENV_OFFSET_REDUND
118         if (gd->env_valid == 2)
119                 off = CONFIG_ENV_OFFSET_REDUND;
120 #endif
121         eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
122                         off, (uchar *)buf, CONFIG_ENV_SIZE);
123
124         env_import(buf, 1);
125 }
126
127 int saveenv(void)
128 {
129         env_t   env_new;
130         ssize_t len;
131         char    *res;
132         int     rc;
133         unsigned int off        = CONFIG_ENV_OFFSET;
134 #ifdef CONFIG_ENV_OFFSET_REDUND
135         unsigned int off_red    = CONFIG_ENV_OFFSET_REDUND;
136         char flag_obsolete      = OBSOLETE_FLAG;
137 #endif
138
139         BUG_ON(env_ptr != NULL);
140
141         res = (char *)&env_new.data;
142         len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL);
143         if (len < 0) {
144                 error("Cannot export environment: errno = %d\n", errno);
145                 return 1;
146         }
147         env_new.crc = crc32(0, env_new.data, ENV_SIZE);
148
149 #ifdef CONFIG_ENV_OFFSET_REDUND
150         if (gd->env_valid == 1) {
151                 off     = CONFIG_ENV_OFFSET_REDUND;
152                 off_red = CONFIG_ENV_OFFSET;
153         }
154
155         env_new.flags = ACTIVE_FLAG;
156 #endif
157
158         rc = eeprom_bus_write(CONFIG_SYS_DEF_EEPROM_ADDR,
159                               off, (uchar *)&env_new, CONFIG_ENV_SIZE);
160
161 #ifdef CONFIG_ENV_OFFSET_REDUND
162         if (rc == 0) {
163                 eeprom_bus_write(CONFIG_SYS_DEF_EEPROM_ADDR,
164                                  off_red + offsetof(env_t, flags),
165                                  (uchar *)&flag_obsolete, 1);
166
167                 if (gd->env_valid == 1)
168                         gd->env_valid = 2;
169                 else
170                         gd->env_valid = 1;
171         }
172 #endif
173         return rc;
174 }
175
176 /*
177  * Initialize Environment use
178  *
179  * We are still running from ROM, so data use is limited.
180  * Use a (moderately small) buffer on the stack
181  */
182 #ifdef CONFIG_ENV_OFFSET_REDUND
183 int env_init(void)
184 {
185         ulong len, crc[2], crc_tmp;
186         unsigned int off, off_env[2];
187         uchar buf[64], flags[2];
188         int i, crc_ok[2] = {0, 0};
189
190         eeprom_init();  /* prepare for EEPROM read/write */
191
192         off_env[0] = CONFIG_ENV_OFFSET;
193         off_env[1] = CONFIG_ENV_OFFSET_REDUND;
194
195         for (i = 0; i < 2; i++) {
196                 /* read CRC */
197                 eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
198                                 off_env[i] + offsetof(env_t, crc),
199                                 (uchar *)&crc[i], sizeof(ulong));
200                 /* read FLAGS */
201                 eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
202                                 off_env[i] + offsetof(env_t, flags),
203                                 (uchar *)&flags[i], sizeof(uchar));
204
205                 crc_tmp = 0;
206                 len = ENV_SIZE;
207                 off = off_env[i] + offsetof(env_t, data);
208                 while (len > 0) {
209                         int n = (len > sizeof(buf)) ? sizeof(buf) : len;
210
211                         eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, off,
212                                         buf, n);
213
214                         crc_tmp = crc32(crc_tmp, buf, n);
215                         len -= n;
216                         off += n;
217                 }
218
219                 if (crc_tmp == crc[i])
220                         crc_ok[i] = 1;
221         }
222
223         if (!crc_ok[0] && !crc_ok[1]) {
224                 gd->env_addr    = 0;
225                 gd->env_valid   = 0;
226
227                 return 0;
228         } else if (crc_ok[0] && !crc_ok[1]) {
229                 gd->env_valid = 1;
230         } else if (!crc_ok[0] && crc_ok[1]) {
231                 gd->env_valid = 2;
232         } else {
233                 /* both ok - check serial */
234                 if (flags[0] == ACTIVE_FLAG && flags[1] == OBSOLETE_FLAG)
235                         gd->env_valid = 1;
236                 else if (flags[0] == OBSOLETE_FLAG && flags[1] == ACTIVE_FLAG)
237                         gd->env_valid = 2;
238                 else if (flags[0] == 0xFF && flags[1] == 0)
239                         gd->env_valid = 2;
240                 else if (flags[1] == 0xFF && flags[0] == 0)
241                         gd->env_valid = 1;
242                 else /* flags are equal - almost impossible */
243                         gd->env_valid = 1;
244         }
245
246         if (gd->env_valid == 2)
247                 gd->env_addr = off_env[1] + offsetof(env_t, data);
248         else if (gd->env_valid == 1)
249                 gd->env_addr = off_env[0] + offsetof(env_t, data);
250
251         return 0;
252 }
253 #else
254 int env_init(void)
255 {
256         ulong crc, len, new;
257         unsigned off;
258         uchar buf[64];
259
260         eeprom_init();  /* prepare for EEPROM read/write */
261
262         /* read old CRC */
263         eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
264                         CONFIG_ENV_OFFSET + offsetof(env_t, crc),
265                         (uchar *)&crc, sizeof(ulong));
266
267         new = 0;
268         len = ENV_SIZE;
269         off = offsetof(env_t, data);
270
271         while (len > 0) {
272                 int n = (len > sizeof(buf)) ? sizeof(buf) : len;
273
274                 eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
275                                 CONFIG_ENV_OFFSET + off, buf, n);
276                 new = crc32(new, buf, n);
277                 len -= n;
278                 off += n;
279         }
280
281         if (crc == new) {
282                 gd->env_addr    = offsetof(env_t, data);
283                 gd->env_valid   = 1;
284         } else {
285                 gd->env_addr    = 0;
286                 gd->env_valid   = 0;
287         }
288
289         return 0;
290 }
291 #endif