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