blob: 7f7f731992fd7edb90b6dd97be6e2b1f630d55dd [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Haavard Skinnemoeneb8951c2008-05-16 11:10:35 +02002/*
Wolfgang Denk460a9ff2010-06-20 23:33:59 +02003 * (C) Copyright 2000-2010
Haavard Skinnemoeneb8951c2008-05-16 11:10:35 +02004 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
5 *
6 * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
7 * Andreas Heppel <aheppel@sysgo.de>
8 *
9 * (C) Copyright 2008 Atmel Corporation
Haavard Skinnemoeneb8951c2008-05-16 11:10:35 +020010 */
11#include <common.h>
Simon Glass11c89f32017-05-17 17:18:03 -060012#include <dm.h>
Haavard Skinnemoeneb8951c2008-05-16 11:10:35 +020013#include <environment.h>
Mike Frysinger9af10d52008-12-11 06:23:37 -050014#include <malloc.h>
Simon Glassdef92152014-10-13 23:41:55 -060015#include <spi.h>
Haavard Skinnemoeneb8951c2008-05-16 11:10:35 +020016#include <spi_flash.h>
Wolfgang Denk460a9ff2010-06-20 23:33:59 +020017#include <search.h>
18#include <errno.h>
Gong Qianyu6d85a602016-01-26 15:06:42 +080019#include <dm/device-internal.h>
Haavard Skinnemoeneb8951c2008-05-16 11:10:35 +020020
Jean-Christophe PLAGNIOL-VILLARD7e1cda62008-09-10 22:48:06 +020021#ifndef CONFIG_ENV_SPI_BUS
Jean-Jacques Hiblot464eee12017-02-01 14:51:47 +010022# define CONFIG_ENV_SPI_BUS CONFIG_SF_DEFAULT_BUS
Haavard Skinnemoeneb8951c2008-05-16 11:10:35 +020023#endif
Jean-Christophe PLAGNIOL-VILLARD7e1cda62008-09-10 22:48:06 +020024#ifndef CONFIG_ENV_SPI_CS
Jean-Jacques Hiblot464eee12017-02-01 14:51:47 +010025# define CONFIG_ENV_SPI_CS CONFIG_SF_DEFAULT_CS
Haavard Skinnemoeneb8951c2008-05-16 11:10:35 +020026#endif
Jean-Christophe PLAGNIOL-VILLARD7e1cda62008-09-10 22:48:06 +020027#ifndef CONFIG_ENV_SPI_MAX_HZ
Jean-Jacques Hiblot464eee12017-02-01 14:51:47 +010028# define CONFIG_ENV_SPI_MAX_HZ CONFIG_SF_DEFAULT_SPEED
Haavard Skinnemoeneb8951c2008-05-16 11:10:35 +020029#endif
Jean-Christophe PLAGNIOL-VILLARD7e1cda62008-09-10 22:48:06 +020030#ifndef CONFIG_ENV_SPI_MODE
Jean-Jacques Hiblot464eee12017-02-01 14:51:47 +010031# define CONFIG_ENV_SPI_MODE CONFIG_SF_DEFAULT_MODE
Haavard Skinnemoeneb8951c2008-05-16 11:10:35 +020032#endif
33
Simon Glassc10a88e2017-08-03 12:21:58 -060034#ifndef CONFIG_SPL_BUILD
35#define CMD_SAVEENV
Ashish Kumara7bf68a2017-12-14 17:37:08 +053036#define INITENV
Simon Glassc10a88e2017-08-03 12:21:58 -060037#endif
38
Wolfgang Wegner0cae3d42010-04-23 17:22:55 +020039#ifdef CONFIG_ENV_OFFSET_REDUND
Simon Glassc10a88e2017-08-03 12:21:58 -060040#ifdef CMD_SAVEENV
Igor Grinbergc3c1bed2011-11-07 01:14:08 +000041static ulong env_offset = CONFIG_ENV_OFFSET;
42static ulong env_new_offset = CONFIG_ENV_OFFSET_REDUND;
Simon Glassc10a88e2017-08-03 12:21:58 -060043#endif
Wolfgang Wegner0cae3d42010-04-23 17:22:55 +020044
Igor Grinbergc3c1bed2011-11-07 01:14:08 +000045#define ACTIVE_FLAG 1
46#define OBSOLETE_FLAG 0
Stefano Babic5f6c2862010-10-27 11:06:20 +020047#endif /* CONFIG_ENV_OFFSET_REDUND */
Wolfgang Wegner0cae3d42010-04-23 17:22:55 +020048
Haavard Skinnemoeneb8951c2008-05-16 11:10:35 +020049DECLARE_GLOBAL_DATA_PTR;
50
Haavard Skinnemoeneb8951c2008-05-16 11:10:35 +020051static struct spi_flash *env_flash;
52
Andreas Fenkart52b94442017-04-08 11:59:31 +020053static int setup_flash_device(void)
Wolfgang Wegner0cae3d42010-04-23 17:22:55 +020054{
Gong Qianyu6d85a602016-01-26 15:06:42 +080055#ifdef CONFIG_DM_SPI_FLASH
56 struct udevice *new;
Andreas Fenkart52b94442017-04-08 11:59:31 +020057 int ret;
Gong Qianyu6d85a602016-01-26 15:06:42 +080058
Vignesh Rae569792016-07-06 10:04:28 +053059 /* speed and mode will be read from DT */
Gong Qianyu6d85a602016-01-26 15:06:42 +080060 ret = spi_flash_probe_bus_cs(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS,
Vignesh Rae569792016-07-06 10:04:28 +053061 0, 0, &new);
Gong Qianyu6d85a602016-01-26 15:06:42 +080062 if (ret) {
63 set_default_env("!spi_flash_probe_bus_cs() failed");
Simon Glass99778492017-08-03 12:22:17 -060064 return ret;
Gong Qianyu6d85a602016-01-26 15:06:42 +080065 }
66
67 env_flash = dev_get_uclass_priv(new);
68#else
Wolfgang Wegner0cae3d42010-04-23 17:22:55 +020069
70 if (!env_flash) {
Stefano Babic5f6c2862010-10-27 11:06:20 +020071 env_flash = spi_flash_probe(CONFIG_ENV_SPI_BUS,
72 CONFIG_ENV_SPI_CS,
73 CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE);
74 if (!env_flash) {
75 set_default_env("!spi_flash_probe() failed");
Simon Glass99778492017-08-03 12:22:17 -060076 return -EIO;
Stefano Babic5f6c2862010-10-27 11:06:20 +020077 }
Wolfgang Wegner0cae3d42010-04-23 17:22:55 +020078 }
Gong Qianyu6d85a602016-01-26 15:06:42 +080079#endif
Andreas Fenkart52b94442017-04-08 11:59:31 +020080 return 0;
81}
82
83#if defined(CONFIG_ENV_OFFSET_REDUND)
Simon Glassc10a88e2017-08-03 12:21:58 -060084#ifdef CMD_SAVEENV
Simon Glass082af922017-08-03 12:22:01 -060085static int env_sf_save(void)
Andreas Fenkart52b94442017-04-08 11:59:31 +020086{
87 env_t env_new;
88 char *saved_buffer = NULL, flag = OBSOLETE_FLAG;
Andreas Fenkartdf701912017-04-08 11:59:34 +020089 u32 saved_size, saved_offset, sector;
Andreas Fenkart52b94442017-04-08 11:59:31 +020090 int ret;
91
92 ret = setup_flash_device();
93 if (ret)
94 return ret;
Wolfgang Wegner0cae3d42010-04-23 17:22:55 +020095
Marek Vasutd73c1292014-03-05 19:59:50 +010096 ret = env_export(&env_new);
97 if (ret)
Simon Glass99778492017-08-03 12:22:17 -060098 return -EIO;
Tom Rinia840b8e2013-04-05 14:55:21 -040099 env_new.flags = ACTIVE_FLAG;
Wolfgang Denk460a9ff2010-06-20 23:33:59 +0200100
Simon Glass4bc2ad22017-08-03 12:21:56 -0600101 if (gd->env_valid == ENV_VALID) {
Stefano Babic5f6c2862010-10-27 11:06:20 +0200102 env_new_offset = CONFIG_ENV_OFFSET_REDUND;
103 env_offset = CONFIG_ENV_OFFSET;
104 } else {
105 env_new_offset = CONFIG_ENV_OFFSET;
106 env_offset = CONFIG_ENV_OFFSET_REDUND;
107 }
108
Wolfgang Wegner0cae3d42010-04-23 17:22:55 +0200109 /* Is the sector larger than the env (i.e. embedded) */
110 if (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) {
111 saved_size = CONFIG_ENV_SECT_SIZE - CONFIG_ENV_SIZE;
112 saved_offset = env_new_offset + CONFIG_ENV_SIZE;
Ravi Babu20d93fc2015-08-17 13:29:49 +0530113 saved_buffer = memalign(ARCH_DMA_MINALIGN, saved_size);
Wolfgang Wegner0cae3d42010-04-23 17:22:55 +0200114 if (!saved_buffer) {
Simon Glass99778492017-08-03 12:22:17 -0600115 ret = -ENOMEM;
Wolfgang Wegner0cae3d42010-04-23 17:22:55 +0200116 goto done;
117 }
118 ret = spi_flash_read(env_flash, saved_offset,
119 saved_size, saved_buffer);
120 if (ret)
121 goto done;
122 }
123
Andreas Fenkartdf701912017-04-08 11:59:34 +0200124 sector = DIV_ROUND_UP(CONFIG_ENV_SIZE, CONFIG_ENV_SECT_SIZE);
Wolfgang Wegner0cae3d42010-04-23 17:22:55 +0200125
126 puts("Erasing SPI flash...");
127 ret = spi_flash_erase(env_flash, env_new_offset,
128 sector * CONFIG_ENV_SECT_SIZE);
129 if (ret)
130 goto done;
131
132 puts("Writing to SPI flash...");
Wolfgang Wegner0cae3d42010-04-23 17:22:55 +0200133
Stefano Babic5f6c2862010-10-27 11:06:20 +0200134 ret = spi_flash_write(env_flash, env_new_offset,
Tom Rinia840b8e2013-04-05 14:55:21 -0400135 CONFIG_ENV_SIZE, &env_new);
Wolfgang Wegner0cae3d42010-04-23 17:22:55 +0200136 if (ret)
137 goto done;
138
139 if (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) {
140 ret = spi_flash_write(env_flash, saved_offset,
141 saved_size, saved_buffer);
142 if (ret)
143 goto done;
144 }
145
Igor Grinbergc3c1bed2011-11-07 01:14:08 +0000146 ret = spi_flash_write(env_flash, env_offset + offsetof(env_t, flags),
Tom Rinia840b8e2013-04-05 14:55:21 -0400147 sizeof(env_new.flags), &flag);
Stefano Babic5f6c2862010-10-27 11:06:20 +0200148 if (ret)
149 goto done;
Wolfgang Wegner0cae3d42010-04-23 17:22:55 +0200150
Wolfgang Wegner0cae3d42010-04-23 17:22:55 +0200151 puts("done\n");
152
Simon Glass4bc2ad22017-08-03 12:21:56 -0600153 gd->env_valid = gd->env_valid == ENV_REDUND ? ENV_VALID : ENV_REDUND;
Stefano Babic5f6c2862010-10-27 11:06:20 +0200154
thomas.langer@lantiq.com97dfdcf2011-03-29 02:35:14 +0000155 printf("Valid environment: %d\n", (int)gd->env_valid);
Stefano Babic5f6c2862010-10-27 11:06:20 +0200156
Wolfgang Wegner0cae3d42010-04-23 17:22:55 +0200157 done:
158 if (saved_buffer)
159 free(saved_buffer);
Igor Grinbergc3c1bed2011-11-07 01:14:08 +0000160
Wolfgang Wegner0cae3d42010-04-23 17:22:55 +0200161 return ret;
162}
Simon Glassc10a88e2017-08-03 12:21:58 -0600163#endif /* CMD_SAVEENV */
Wolfgang Wegner0cae3d42010-04-23 17:22:55 +0200164
Simon Glass99778492017-08-03 12:22:17 -0600165static int env_sf_load(void)
Wolfgang Wegner0cae3d42010-04-23 17:22:55 +0200166{
167 int ret;
Simon Goldschmidt8f9ce3a2018-01-31 14:47:13 +0100168 int read1_fail, read2_fail;
169 env_t *tmp_env1, *tmp_env2;
Wolfgang Wegner0cae3d42010-04-23 17:22:55 +0200170
Ravi Babu20d93fc2015-08-17 13:29:49 +0530171 tmp_env1 = (env_t *)memalign(ARCH_DMA_MINALIGN,
172 CONFIG_ENV_SIZE);
173 tmp_env2 = (env_t *)memalign(ARCH_DMA_MINALIGN,
174 CONFIG_ENV_SIZE);
Wolfgang Denk460a9ff2010-06-20 23:33:59 +0200175 if (!tmp_env1 || !tmp_env2) {
Wolfgang Denk460a9ff2010-06-20 23:33:59 +0200176 set_default_env("!malloc() failed");
Simon Glass99778492017-08-03 12:22:17 -0600177 ret = -EIO;
thomas.langer@lantiq.com97dfdcf2011-03-29 02:35:14 +0000178 goto out;
Wolfgang Wegner0cae3d42010-04-23 17:22:55 +0200179 }
180
Andreas Fenkarta2d18832017-04-08 11:59:32 +0200181 ret = setup_flash_device();
182 if (ret)
thomas.langer@lantiq.com97dfdcf2011-03-29 02:35:14 +0000183 goto out;
Wolfgang Wegner0cae3d42010-04-23 17:22:55 +0200184
Simon Goldschmidt8f9ce3a2018-01-31 14:47:13 +0100185 read1_fail = spi_flash_read(env_flash, CONFIG_ENV_OFFSET,
186 CONFIG_ENV_SIZE, tmp_env1);
187 read2_fail = spi_flash_read(env_flash, CONFIG_ENV_OFFSET_REDUND,
188 CONFIG_ENV_SIZE, tmp_env2);
Wolfgang Wegner0cae3d42010-04-23 17:22:55 +0200189
Simon Goldschmidt8f9ce3a2018-01-31 14:47:13 +0100190 ret = env_import_redund((char *)tmp_env1, read1_fail, (char *)tmp_env2,
191 read2_fail);
Wolfgang Wegner0cae3d42010-04-23 17:22:55 +0200192
Wolfgang Wegner0cae3d42010-04-23 17:22:55 +0200193 spi_flash_free(env_flash);
194 env_flash = NULL;
Wolfgang Wegner0cae3d42010-04-23 17:22:55 +0200195out:
Wolfgang Denk460a9ff2010-06-20 23:33:59 +0200196 free(tmp_env1);
197 free(tmp_env2);
Simon Glass99778492017-08-03 12:22:17 -0600198
199 return ret;
Wolfgang Wegner0cae3d42010-04-23 17:22:55 +0200200}
201#else
Simon Glassc10a88e2017-08-03 12:21:58 -0600202#ifdef CMD_SAVEENV
Simon Glass082af922017-08-03 12:22:01 -0600203static int env_sf_save(void)
Haavard Skinnemoeneb8951c2008-05-16 11:10:35 +0200204{
Andreas Fenkartdf701912017-04-08 11:59:34 +0200205 u32 saved_size, saved_offset, sector;
Marek Vasutd73c1292014-03-05 19:59:50 +0100206 char *saved_buffer = NULL;
Igor Grinbergc3c1bed2011-11-07 01:14:08 +0000207 int ret = 1;
Tom Rinia840b8e2013-04-05 14:55:21 -0400208 env_t env_new;
Gong Qianyu6d85a602016-01-26 15:06:42 +0800209
Andreas Fenkart52b94442017-04-08 11:59:31 +0200210 ret = setup_flash_device();
211 if (ret)
212 return ret;
Haavard Skinnemoeneb8951c2008-05-16 11:10:35 +0200213
Mike Frysinger9af10d52008-12-11 06:23:37 -0500214 /* Is the sector larger than the env (i.e. embedded) */
215 if (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) {
216 saved_size = CONFIG_ENV_SECT_SIZE - CONFIG_ENV_SIZE;
217 saved_offset = CONFIG_ENV_OFFSET + CONFIG_ENV_SIZE;
218 saved_buffer = malloc(saved_size);
Igor Grinbergc3c1bed2011-11-07 01:14:08 +0000219 if (!saved_buffer)
Mike Frysinger9af10d52008-12-11 06:23:37 -0500220 goto done;
Igor Grinbergc3c1bed2011-11-07 01:14:08 +0000221
Stefano Babic5f6c2862010-10-27 11:06:20 +0200222 ret = spi_flash_read(env_flash, saved_offset,
223 saved_size, saved_buffer);
Mike Frysinger9af10d52008-12-11 06:23:37 -0500224 if (ret)
225 goto done;
226 }
227
Marek Vasutd73c1292014-03-05 19:59:50 +0100228 ret = env_export(&env_new);
229 if (ret)
Stefano Babic5f6c2862010-10-27 11:06:20 +0200230 goto done;
Stefano Babic5f6c2862010-10-27 11:06:20 +0200231
Andreas Fenkartdf701912017-04-08 11:59:34 +0200232 sector = DIV_ROUND_UP(CONFIG_ENV_SIZE, CONFIG_ENV_SECT_SIZE);
233
Haavard Skinnemoeneb8951c2008-05-16 11:10:35 +0200234 puts("Erasing SPI flash...");
Stefano Babic5f6c2862010-10-27 11:06:20 +0200235 ret = spi_flash_erase(env_flash, CONFIG_ENV_OFFSET,
236 sector * CONFIG_ENV_SECT_SIZE);
Mike Frysinger9af10d52008-12-11 06:23:37 -0500237 if (ret)
238 goto done;
Haavard Skinnemoeneb8951c2008-05-16 11:10:35 +0200239
240 puts("Writing to SPI flash...");
Stefano Babic5f6c2862010-10-27 11:06:20 +0200241 ret = spi_flash_write(env_flash, CONFIG_ENV_OFFSET,
Tom Rinia840b8e2013-04-05 14:55:21 -0400242 CONFIG_ENV_SIZE, &env_new);
Mike Frysinger9af10d52008-12-11 06:23:37 -0500243 if (ret)
244 goto done;
Haavard Skinnemoeneb8951c2008-05-16 11:10:35 +0200245
Mike Frysinger9af10d52008-12-11 06:23:37 -0500246 if (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) {
Stefano Babic5f6c2862010-10-27 11:06:20 +0200247 ret = spi_flash_write(env_flash, saved_offset,
248 saved_size, saved_buffer);
Mike Frysinger9af10d52008-12-11 06:23:37 -0500249 if (ret)
250 goto done;
251 }
252
253 ret = 0;
Haavard Skinnemoeneb8951c2008-05-16 11:10:35 +0200254 puts("done\n");
Mike Frysinger9af10d52008-12-11 06:23:37 -0500255
256 done:
257 if (saved_buffer)
258 free(saved_buffer);
Igor Grinbergc3c1bed2011-11-07 01:14:08 +0000259
Mike Frysinger9af10d52008-12-11 06:23:37 -0500260 return ret;
Haavard Skinnemoeneb8951c2008-05-16 11:10:35 +0200261}
Simon Glassc10a88e2017-08-03 12:21:58 -0600262#endif /* CMD_SAVEENV */
Haavard Skinnemoeneb8951c2008-05-16 11:10:35 +0200263
Simon Glass99778492017-08-03 12:22:17 -0600264static int env_sf_load(void)
Haavard Skinnemoeneb8951c2008-05-16 11:10:35 +0200265{
266 int ret;
Ying Zhang354846f2014-01-24 15:50:07 +0800267 char *buf = NULL;
Haavard Skinnemoeneb8951c2008-05-16 11:10:35 +0200268
Ravi Babu20d93fc2015-08-17 13:29:49 +0530269 buf = (char *)memalign(ARCH_DMA_MINALIGN, CONFIG_ENV_SIZE);
Andreas Fenkarta2c65852017-04-08 11:59:33 +0200270 if (!buf) {
271 set_default_env("!malloc() failed");
Simon Glass99778492017-08-03 12:22:17 -0600272 return -EIO;
Wolfgang Denk460a9ff2010-06-20 23:33:59 +0200273 }
Haavard Skinnemoeneb8951c2008-05-16 11:10:35 +0200274
Andreas Fenkarta2c65852017-04-08 11:59:33 +0200275 ret = setup_flash_device();
276 if (ret)
277 goto out;
278
Wolfgang Denk460a9ff2010-06-20 23:33:59 +0200279 ret = spi_flash_read(env_flash,
280 CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE, buf);
281 if (ret) {
282 set_default_env("!spi_flash_read() failed");
Andreas Fenkarta2c65852017-04-08 11:59:33 +0200283 goto err_read;
Wolfgang Denk460a9ff2010-06-20 23:33:59 +0200284 }
Haavard Skinnemoeneb8951c2008-05-16 11:10:35 +0200285
Wolfgang Denk460a9ff2010-06-20 23:33:59 +0200286 ret = env_import(buf, 1);
Simon Goldschmidt32c3eb52018-01-31 14:47:10 +0100287 if (!ret)
Simon Glass4bc2ad22017-08-03 12:21:56 -0600288 gd->env_valid = ENV_VALID;
Andreas Fenkarta2c65852017-04-08 11:59:33 +0200289
290err_read:
Haavard Skinnemoeneb8951c2008-05-16 11:10:35 +0200291 spi_flash_free(env_flash);
292 env_flash = NULL;
Andreas Fenkarta2c65852017-04-08 11:59:33 +0200293out:
294 free(buf);
Simon Glass99778492017-08-03 12:22:17 -0600295
296 return ret;
Haavard Skinnemoeneb8951c2008-05-16 11:10:35 +0200297}
Wolfgang Wegner0cae3d42010-04-23 17:22:55 +0200298#endif
Haavard Skinnemoeneb8951c2008-05-16 11:10:35 +0200299
Ashish Kumara7bf68a2017-12-14 17:37:08 +0530300#if defined(INITENV) && defined(CONFIG_ENV_ADDR)
301static int env_sf_init(void)
302{
303 env_t *env_ptr = (env_t *)(CONFIG_ENV_ADDR);
304
305 if (crc32(0, env_ptr->data, ENV_SIZE) == env_ptr->crc) {
306 gd->env_addr = (ulong)&(env_ptr->data);
307 gd->env_valid = 1;
308 } else {
309 gd->env_addr = (ulong)&default_environment[0];
310 gd->env_valid = 1;
311 }
312
313 return 0;
314}
315#endif
316
Simon Glassc10a88e2017-08-03 12:21:58 -0600317U_BOOT_ENV_LOCATION(sf) = {
318 .location = ENVL_SPI_FLASH,
Simon Glassd8273ed2017-08-03 12:22:03 -0600319 ENV_NAME("SPI Flash")
Simon Glass082af922017-08-03 12:22:01 -0600320 .load = env_sf_load,
Simon Glassc10a88e2017-08-03 12:21:58 -0600321#ifdef CMD_SAVEENV
Simon Glass082af922017-08-03 12:22:01 -0600322 .save = env_save_ptr(env_sf_save),
Simon Glassc10a88e2017-08-03 12:21:58 -0600323#endif
Ashish Kumara7bf68a2017-12-14 17:37:08 +0530324#if defined(INITENV) && defined(CONFIG_ENV_ADDR)
325 .init = env_sf_init,
326#endif
Simon Glassc10a88e2017-08-03 12:21:58 -0600327};