Merge remote-tracking branch 'origin/master'
[platform/kernel/u-boot.git] / common / env_sf.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  * (C) Copyright 2008 Atmel Corporation
9  *
10  * See file CREDITS for list of people who contributed to this
11  * project.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License as
15  * published by the Free Software Foundation; either version 2 of
16  * the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
26  * MA 02111-1307 USA
27  */
28 #include <common.h>
29 #include <environment.h>
30 #include <malloc.h>
31 #include <spi_flash.h>
32 #include <search.h>
33 #include <errno.h>
34
35 #ifndef CONFIG_ENV_SPI_BUS
36 # define CONFIG_ENV_SPI_BUS     0
37 #endif
38 #ifndef CONFIG_ENV_SPI_CS
39 # define CONFIG_ENV_SPI_CS      0
40 #endif
41 #ifndef CONFIG_ENV_SPI_MAX_HZ
42 # define CONFIG_ENV_SPI_MAX_HZ  1000000
43 #endif
44 #ifndef CONFIG_ENV_SPI_MODE
45 # define CONFIG_ENV_SPI_MODE    SPI_MODE_3
46 #endif
47
48 #ifdef CONFIG_ENV_OFFSET_REDUND
49 static ulong env_offset         = CONFIG_ENV_OFFSET;
50 static ulong env_new_offset     = CONFIG_ENV_OFFSET_REDUND;
51
52 #define ACTIVE_FLAG     1
53 #define OBSOLETE_FLAG   0
54 #endif /* CONFIG_ENV_OFFSET_REDUND */
55
56 DECLARE_GLOBAL_DATA_PTR;
57
58 char *env_name_spec = "SPI Flash";
59
60 static struct spi_flash *env_flash;
61
62 #if defined(CONFIG_ENV_OFFSET_REDUND)
63 int saveenv(void)
64 {
65         env_t   env_new;
66         ssize_t len;
67         char    *res, *saved_buffer = NULL, flag = OBSOLETE_FLAG;
68         u32     saved_size, saved_offset, sector = 1;
69         int     ret;
70
71         if (!env_flash) {
72                 env_flash = spi_flash_probe(CONFIG_ENV_SPI_BUS,
73                         CONFIG_ENV_SPI_CS,
74                         CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE);
75                 if (!env_flash) {
76                         set_default_env("!spi_flash_probe() failed");
77                         return 1;
78                 }
79         }
80
81         res = (char *)&env_new.data;
82         len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL);
83         if (len < 0) {
84                 error("Cannot export environment: errno = %d\n", errno);
85                 return 1;
86         }
87         env_new.crc     = crc32(0, env_new.data, ENV_SIZE);
88         env_new.flags   = ACTIVE_FLAG;
89
90         if (gd->env_valid == 1) {
91                 env_new_offset = CONFIG_ENV_OFFSET_REDUND;
92                 env_offset = CONFIG_ENV_OFFSET;
93         } else {
94                 env_new_offset = CONFIG_ENV_OFFSET;
95                 env_offset = CONFIG_ENV_OFFSET_REDUND;
96         }
97
98         /* Is the sector larger than the env (i.e. embedded) */
99         if (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) {
100                 saved_size = CONFIG_ENV_SECT_SIZE - CONFIG_ENV_SIZE;
101                 saved_offset = env_new_offset + CONFIG_ENV_SIZE;
102                 saved_buffer = malloc(saved_size);
103                 if (!saved_buffer) {
104                         ret = 1;
105                         goto done;
106                 }
107                 ret = spi_flash_read(env_flash, saved_offset,
108                                         saved_size, saved_buffer);
109                 if (ret)
110                         goto done;
111         }
112
113         if (CONFIG_ENV_SIZE > CONFIG_ENV_SECT_SIZE) {
114                 sector = CONFIG_ENV_SIZE / CONFIG_ENV_SECT_SIZE;
115                 if (CONFIG_ENV_SIZE % CONFIG_ENV_SECT_SIZE)
116                         sector++;
117         }
118
119         puts("Erasing SPI flash...");
120         ret = spi_flash_erase(env_flash, env_new_offset,
121                                 sector * CONFIG_ENV_SECT_SIZE);
122         if (ret)
123                 goto done;
124
125         puts("Writing to SPI flash...");
126
127         ret = spi_flash_write(env_flash, env_new_offset,
128                 CONFIG_ENV_SIZE, &env_new);
129         if (ret)
130                 goto done;
131
132         if (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) {
133                 ret = spi_flash_write(env_flash, saved_offset,
134                                         saved_size, saved_buffer);
135                 if (ret)
136                         goto done;
137         }
138
139         ret = spi_flash_write(env_flash, env_offset + offsetof(env_t, flags),
140                                 sizeof(env_new.flags), &flag);
141         if (ret)
142                 goto done;
143
144         puts("done\n");
145
146         gd->env_valid = gd->env_valid == 2 ? 1 : 2;
147
148         printf("Valid environment: %d\n", (int)gd->env_valid);
149
150  done:
151         if (saved_buffer)
152                 free(saved_buffer);
153
154         return ret;
155 }
156
157 void env_relocate_spec(void)
158 {
159         int ret;
160         int crc1_ok = 0, crc2_ok = 0;
161         env_t *tmp_env1 = NULL;
162         env_t *tmp_env2 = NULL;
163         env_t *ep = NULL;
164
165         tmp_env1 = (env_t *)malloc(CONFIG_ENV_SIZE);
166         tmp_env2 = (env_t *)malloc(CONFIG_ENV_SIZE);
167
168         if (!tmp_env1 || !tmp_env2) {
169                 set_default_env("!malloc() failed");
170                 goto out;
171         }
172
173         env_flash = spi_flash_probe(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS,
174                         CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE);
175         if (!env_flash) {
176                 set_default_env("!spi_flash_probe() failed");
177                 goto out;
178         }
179
180         ret = spi_flash_read(env_flash, CONFIG_ENV_OFFSET,
181                                 CONFIG_ENV_SIZE, tmp_env1);
182         if (ret) {
183                 set_default_env("!spi_flash_read() failed");
184                 goto err_read;
185         }
186
187         if (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc)
188                 crc1_ok = 1;
189
190         ret = spi_flash_read(env_flash, CONFIG_ENV_OFFSET_REDUND,
191                                 CONFIG_ENV_SIZE, tmp_env2);
192         if (!ret) {
193                 if (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc)
194                         crc2_ok = 1;
195         }
196
197         if (!crc1_ok && !crc2_ok) {
198                 set_default_env("!bad CRC");
199                 goto err_read;
200         } else if (crc1_ok && !crc2_ok) {
201                 gd->env_valid = 1;
202         } else if (!crc1_ok && crc2_ok) {
203                 gd->env_valid = 2;
204         } else if (tmp_env1->flags == ACTIVE_FLAG &&
205                    tmp_env2->flags == OBSOLETE_FLAG) {
206                 gd->env_valid = 1;
207         } else if (tmp_env1->flags == OBSOLETE_FLAG &&
208                    tmp_env2->flags == ACTIVE_FLAG) {
209                 gd->env_valid = 2;
210         } else if (tmp_env1->flags == tmp_env2->flags) {
211                 gd->env_valid = 2;
212         } else if (tmp_env1->flags == 0xFF) {
213                 gd->env_valid = 2;
214         } else {
215                 /*
216                  * this differs from code in env_flash.c, but I think a sane
217                  * default path is desirable.
218                  */
219                 gd->env_valid = 2;
220         }
221
222         if (gd->env_valid == 1)
223                 ep = tmp_env1;
224         else
225                 ep = tmp_env2;
226
227         ret = env_import((char *)ep, 0);
228         if (!ret) {
229                 error("Cannot import environment: errno = %d\n", errno);
230                 set_default_env("env_import failed");
231         }
232
233 err_read:
234         spi_flash_free(env_flash);
235         env_flash = NULL;
236 out:
237         free(tmp_env1);
238         free(tmp_env2);
239 }
240 #else
241 int saveenv(void)
242 {
243         u32     saved_size, saved_offset, sector = 1;
244         char    *res, *saved_buffer = NULL;
245         int     ret = 1;
246         env_t   env_new;
247         ssize_t len;
248
249         if (!env_flash) {
250                 env_flash = spi_flash_probe(CONFIG_ENV_SPI_BUS,
251                         CONFIG_ENV_SPI_CS,
252                         CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE);
253                 if (!env_flash) {
254                         set_default_env("!spi_flash_probe() failed");
255                         return 1;
256                 }
257         }
258
259         /* Is the sector larger than the env (i.e. embedded) */
260         if (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) {
261                 saved_size = CONFIG_ENV_SECT_SIZE - CONFIG_ENV_SIZE;
262                 saved_offset = CONFIG_ENV_OFFSET + CONFIG_ENV_SIZE;
263                 saved_buffer = malloc(saved_size);
264                 if (!saved_buffer)
265                         goto done;
266
267                 ret = spi_flash_read(env_flash, saved_offset,
268                         saved_size, saved_buffer);
269                 if (ret)
270                         goto done;
271         }
272
273         if (CONFIG_ENV_SIZE > CONFIG_ENV_SECT_SIZE) {
274                 sector = CONFIG_ENV_SIZE / CONFIG_ENV_SECT_SIZE;
275                 if (CONFIG_ENV_SIZE % CONFIG_ENV_SECT_SIZE)
276                         sector++;
277         }
278
279         res = (char *)&env_new.data;
280         len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL);
281         if (len < 0) {
282                 error("Cannot export environment: errno = %d\n", errno);
283                 goto done;
284         }
285         env_new.crc = crc32(0, env_new.data, ENV_SIZE);
286
287         puts("Erasing SPI flash...");
288         ret = spi_flash_erase(env_flash, CONFIG_ENV_OFFSET,
289                 sector * CONFIG_ENV_SECT_SIZE);
290         if (ret)
291                 goto done;
292
293         puts("Writing to SPI flash...");
294         ret = spi_flash_write(env_flash, CONFIG_ENV_OFFSET,
295                 CONFIG_ENV_SIZE, &env_new);
296         if (ret)
297                 goto done;
298
299         if (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) {
300                 ret = spi_flash_write(env_flash, saved_offset,
301                         saved_size, saved_buffer);
302                 if (ret)
303                         goto done;
304         }
305
306         ret = 0;
307         puts("done\n");
308
309  done:
310         if (saved_buffer)
311                 free(saved_buffer);
312
313         return ret;
314 }
315
316 void env_relocate_spec(void)
317 {
318         char buf[CONFIG_ENV_SIZE];
319         int ret;
320
321         env_flash = spi_flash_probe(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS,
322                         CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE);
323         if (!env_flash) {
324                 set_default_env("!spi_flash_probe() failed");
325                 return;
326         }
327
328         ret = spi_flash_read(env_flash,
329                 CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE, buf);
330         if (ret) {
331                 set_default_env("!spi_flash_read() failed");
332                 goto out;
333         }
334
335         ret = env_import(buf, 1);
336         if (ret)
337                 gd->env_valid = 1;
338 out:
339         spi_flash_free(env_flash);
340         env_flash = NULL;
341 }
342 #endif
343
344 int env_init(void)
345 {
346         /* SPI flash isn't usable before relocation */
347         gd->env_addr = (ulong)&default_environment[0];
348         gd->env_valid = 1;
349
350         return 0;
351 }