Merge git://git.denx.de/u-boot-spi
[oweals/u-boot.git] / 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 <dm.h>
14 #include <environment.h>
15 #include <malloc.h>
16 #include <spi.h>
17 #include <spi_flash.h>
18 #include <search.h>
19 #include <errno.h>
20 #include <dm/device-internal.h>
21
22 #ifndef CONFIG_ENV_SPI_BUS
23 # define CONFIG_ENV_SPI_BUS     CONFIG_SF_DEFAULT_BUS
24 #endif
25 #ifndef CONFIG_ENV_SPI_CS
26 # define CONFIG_ENV_SPI_CS      CONFIG_SF_DEFAULT_CS
27 #endif
28 #ifndef CONFIG_ENV_SPI_MAX_HZ
29 # define CONFIG_ENV_SPI_MAX_HZ  CONFIG_SF_DEFAULT_SPEED
30 #endif
31 #ifndef CONFIG_ENV_SPI_MODE
32 # define CONFIG_ENV_SPI_MODE    CONFIG_SF_DEFAULT_MODE
33 #endif
34
35 #ifndef CONFIG_SPL_BUILD
36 #define CMD_SAVEENV
37 #define INITENV
38 #endif
39
40 #ifdef CONFIG_ENV_OFFSET_REDUND
41 #ifdef CMD_SAVEENV
42 static ulong env_offset         = CONFIG_ENV_OFFSET;
43 static ulong env_new_offset     = CONFIG_ENV_OFFSET_REDUND;
44 #endif
45
46 #define ACTIVE_FLAG     1
47 #define OBSOLETE_FLAG   0
48 #endif /* CONFIG_ENV_OFFSET_REDUND */
49
50 DECLARE_GLOBAL_DATA_PTR;
51
52 static struct spi_flash *env_flash;
53
54 static int setup_flash_device(void)
55 {
56 #ifdef CONFIG_DM_SPI_FLASH
57         struct udevice *new;
58         int     ret;
59
60         /* speed and mode will be read from DT */
61         ret = spi_flash_probe_bus_cs(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS,
62                                      0, 0, &new);
63         if (ret) {
64                 set_default_env("!spi_flash_probe_bus_cs() failed");
65                 return ret;
66         }
67
68         env_flash = dev_get_uclass_priv(new);
69 #else
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 -EIO;
78                 }
79         }
80 #endif
81         return 0;
82 }
83
84 #if defined(CONFIG_ENV_OFFSET_REDUND)
85 #ifdef CMD_SAVEENV
86 static int env_sf_save(void)
87 {
88         env_t   env_new;
89         char    *saved_buffer = NULL, flag = OBSOLETE_FLAG;
90         u32     saved_size, saved_offset, sector;
91         int     ret;
92
93         ret = setup_flash_device();
94         if (ret)
95                 return ret;
96
97         ret = env_export(&env_new);
98         if (ret)
99                 return -EIO;
100         env_new.flags   = ACTIVE_FLAG;
101
102         if (gd->env_valid == ENV_VALID) {
103                 env_new_offset = CONFIG_ENV_OFFSET_REDUND;
104                 env_offset = CONFIG_ENV_OFFSET;
105         } else {
106                 env_new_offset = CONFIG_ENV_OFFSET;
107                 env_offset = CONFIG_ENV_OFFSET_REDUND;
108         }
109
110         /* Is the sector larger than the env (i.e. embedded) */
111         if (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) {
112                 saved_size = CONFIG_ENV_SECT_SIZE - CONFIG_ENV_SIZE;
113                 saved_offset = env_new_offset + CONFIG_ENV_SIZE;
114                 saved_buffer = memalign(ARCH_DMA_MINALIGN, saved_size);
115                 if (!saved_buffer) {
116                         ret = -ENOMEM;
117                         goto done;
118                 }
119                 ret = spi_flash_read(env_flash, saved_offset,
120                                         saved_size, saved_buffer);
121                 if (ret)
122                         goto done;
123         }
124
125         sector = DIV_ROUND_UP(CONFIG_ENV_SIZE, CONFIG_ENV_SECT_SIZE);
126
127         puts("Erasing SPI flash...");
128         ret = spi_flash_erase(env_flash, env_new_offset,
129                                 sector * CONFIG_ENV_SECT_SIZE);
130         if (ret)
131                 goto done;
132
133         puts("Writing to SPI flash...");
134
135         ret = spi_flash_write(env_flash, env_new_offset,
136                 CONFIG_ENV_SIZE, &env_new);
137         if (ret)
138                 goto done;
139
140         if (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) {
141                 ret = spi_flash_write(env_flash, saved_offset,
142                                         saved_size, saved_buffer);
143                 if (ret)
144                         goto done;
145         }
146
147         ret = spi_flash_write(env_flash, env_offset + offsetof(env_t, flags),
148                                 sizeof(env_new.flags), &flag);
149         if (ret)
150                 goto done;
151
152         puts("done\n");
153
154         gd->env_valid = gd->env_valid == ENV_REDUND ? ENV_VALID : ENV_REDUND;
155
156         printf("Valid environment: %d\n", (int)gd->env_valid);
157
158  done:
159         if (saved_buffer)
160                 free(saved_buffer);
161
162         return ret;
163 }
164 #endif /* CMD_SAVEENV */
165
166 static int env_sf_load(void)
167 {
168         int ret;
169         int crc1_ok = 0, crc2_ok = 0;
170         env_t *tmp_env1 = NULL;
171         env_t *tmp_env2 = NULL;
172         env_t *ep = NULL;
173
174         tmp_env1 = (env_t *)memalign(ARCH_DMA_MINALIGN,
175                         CONFIG_ENV_SIZE);
176         tmp_env2 = (env_t *)memalign(ARCH_DMA_MINALIGN,
177                         CONFIG_ENV_SIZE);
178         if (!tmp_env1 || !tmp_env2) {
179                 set_default_env("!malloc() failed");
180                 ret = -EIO;
181                 goto out;
182         }
183
184         ret = setup_flash_device();
185         if (ret)
186                 goto out;
187
188         ret = spi_flash_read(env_flash, CONFIG_ENV_OFFSET,
189                                 CONFIG_ENV_SIZE, tmp_env1);
190         if (ret) {
191                 set_default_env("!spi_flash_read() failed");
192                 goto err_read;
193         }
194
195         if (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc)
196                 crc1_ok = 1;
197
198         ret = spi_flash_read(env_flash, CONFIG_ENV_OFFSET_REDUND,
199                                 CONFIG_ENV_SIZE, tmp_env2);
200         if (!ret) {
201                 if (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc)
202                         crc2_ok = 1;
203         }
204
205         if (!crc1_ok && !crc2_ok) {
206                 set_default_env("!bad CRC");
207                 ret = -EIO;
208                 goto err_read;
209         } else if (crc1_ok && !crc2_ok) {
210                 gd->env_valid = ENV_VALID;
211         } else if (!crc1_ok && crc2_ok) {
212                 gd->env_valid = ENV_REDUND;
213         } else if (tmp_env1->flags == ACTIVE_FLAG &&
214                    tmp_env2->flags == OBSOLETE_FLAG) {
215                 gd->env_valid = ENV_VALID;
216         } else if (tmp_env1->flags == OBSOLETE_FLAG &&
217                    tmp_env2->flags == ACTIVE_FLAG) {
218                 gd->env_valid = ENV_REDUND;
219         } else if (tmp_env1->flags == tmp_env2->flags) {
220                 gd->env_valid = ENV_VALID;
221         } else if (tmp_env1->flags == 0xFF) {
222                 gd->env_valid = ENV_VALID;
223         } else if (tmp_env2->flags == 0xFF) {
224                 gd->env_valid = ENV_REDUND;
225         } else {
226                 /*
227                  * this differs from code in env_flash.c, but I think a sane
228                  * default path is desirable.
229                  */
230                 gd->env_valid = ENV_VALID;
231         }
232
233         if (gd->env_valid == ENV_VALID)
234                 ep = tmp_env1;
235         else
236                 ep = tmp_env2;
237
238         ret = env_import((char *)ep, 0);
239         if (!ret) {
240                 pr_err("Cannot import environment: errno = %d\n", errno);
241                 set_default_env("!env_import failed");
242         }
243
244 err_read:
245         spi_flash_free(env_flash);
246         env_flash = NULL;
247 out:
248         free(tmp_env1);
249         free(tmp_env2);
250
251         return ret;
252 }
253 #else
254 #ifdef CMD_SAVEENV
255 static int env_sf_save(void)
256 {
257         u32     saved_size, saved_offset, sector;
258         char    *saved_buffer = NULL;
259         int     ret = 1;
260         env_t   env_new;
261
262         ret = setup_flash_device();
263         if (ret)
264                 return ret;
265
266         /* Is the sector larger than the env (i.e. embedded) */
267         if (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) {
268                 saved_size = CONFIG_ENV_SECT_SIZE - CONFIG_ENV_SIZE;
269                 saved_offset = CONFIG_ENV_OFFSET + CONFIG_ENV_SIZE;
270                 saved_buffer = malloc(saved_size);
271                 if (!saved_buffer)
272                         goto done;
273
274                 ret = spi_flash_read(env_flash, saved_offset,
275                         saved_size, saved_buffer);
276                 if (ret)
277                         goto done;
278         }
279
280         ret = env_export(&env_new);
281         if (ret)
282                 goto done;
283
284         sector = DIV_ROUND_UP(CONFIG_ENV_SIZE, CONFIG_ENV_SECT_SIZE);
285
286         puts("Erasing SPI flash...");
287         ret = spi_flash_erase(env_flash, CONFIG_ENV_OFFSET,
288                 sector * CONFIG_ENV_SECT_SIZE);
289         if (ret)
290                 goto done;
291
292         puts("Writing to SPI flash...");
293         ret = spi_flash_write(env_flash, CONFIG_ENV_OFFSET,
294                 CONFIG_ENV_SIZE, &env_new);
295         if (ret)
296                 goto done;
297
298         if (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) {
299                 ret = spi_flash_write(env_flash, saved_offset,
300                         saved_size, saved_buffer);
301                 if (ret)
302                         goto done;
303         }
304
305         ret = 0;
306         puts("done\n");
307
308  done:
309         if (saved_buffer)
310                 free(saved_buffer);
311
312         return ret;
313 }
314 #endif /* CMD_SAVEENV */
315
316 static int env_sf_load(void)
317 {
318         int ret;
319         char *buf = NULL;
320
321         buf = (char *)memalign(ARCH_DMA_MINALIGN, CONFIG_ENV_SIZE);
322         if (!buf) {
323                 set_default_env("!malloc() failed");
324                 return -EIO;
325         }
326
327         ret = setup_flash_device();
328         if (ret)
329                 goto out;
330
331         ret = spi_flash_read(env_flash,
332                 CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE, buf);
333         if (ret) {
334                 set_default_env("!spi_flash_read() failed");
335                 goto err_read;
336         }
337
338         ret = env_import(buf, 1);
339         if (ret)
340                 gd->env_valid = ENV_VALID;
341
342 err_read:
343         spi_flash_free(env_flash);
344         env_flash = NULL;
345 out:
346         free(buf);
347
348         return ret;
349 }
350 #endif
351
352 #if defined(INITENV) && defined(CONFIG_ENV_ADDR)
353 static int env_sf_init(void)
354 {
355         env_t *env_ptr = (env_t *)(CONFIG_ENV_ADDR);
356
357         if (crc32(0, env_ptr->data, ENV_SIZE) == env_ptr->crc) {
358                 gd->env_addr    = (ulong)&(env_ptr->data);
359                 gd->env_valid   = 1;
360         } else {
361                 gd->env_addr = (ulong)&default_environment[0];
362                 gd->env_valid = 1;
363         }
364
365         return 0;
366 }
367 #endif
368
369 U_BOOT_ENV_LOCATION(sf) = {
370         .location       = ENVL_SPI_FLASH,
371         ENV_NAME("SPI Flash")
372         .load           = env_sf_load,
373 #ifdef CMD_SAVEENV
374         .save           = env_save_ptr(env_sf_save),
375 #endif
376 #if defined(INITENV) && defined(CONFIG_ENV_ADDR)
377         .init           = env_sf_init,
378 #endif
379 };