183d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
20649cd0dSSimon Glass /*
30649cd0dSSimon Glass * (C) Copyright 2000-2010
40649cd0dSSimon Glass * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
50649cd0dSSimon Glass *
60649cd0dSSimon Glass * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
70649cd0dSSimon Glass * Andreas Heppel <aheppel@sysgo.de>
80649cd0dSSimon Glass *
90649cd0dSSimon Glass * (C) Copyright 2008 Atmel Corporation
100649cd0dSSimon Glass */
110649cd0dSSimon Glass #include <common.h>
120649cd0dSSimon Glass #include <dm.h>
130649cd0dSSimon Glass #include <environment.h>
140649cd0dSSimon Glass #include <malloc.h>
150649cd0dSSimon Glass #include <spi.h>
160649cd0dSSimon Glass #include <spi_flash.h>
170649cd0dSSimon Glass #include <search.h>
180649cd0dSSimon Glass #include <errno.h>
190649cd0dSSimon Glass #include <dm/device-internal.h>
200649cd0dSSimon Glass
214415f1d1SSimon Glass #ifndef CONFIG_SPL_BUILD
224415f1d1SSimon Glass #define CMD_SAVEENV
23b500c92bSAshish Kumar #define INITENV
244415f1d1SSimon Glass #endif
254415f1d1SSimon Glass
260649cd0dSSimon Glass #ifdef CONFIG_ENV_OFFSET_REDUND
274415f1d1SSimon Glass #ifdef CMD_SAVEENV
280649cd0dSSimon Glass static ulong env_offset = CONFIG_ENV_OFFSET;
290649cd0dSSimon Glass static ulong env_new_offset = CONFIG_ENV_OFFSET_REDUND;
304415f1d1SSimon Glass #endif
310649cd0dSSimon Glass
320649cd0dSSimon Glass #define ACTIVE_FLAG 1
330649cd0dSSimon Glass #define OBSOLETE_FLAG 0
340649cd0dSSimon Glass #endif /* CONFIG_ENV_OFFSET_REDUND */
350649cd0dSSimon Glass
360649cd0dSSimon Glass DECLARE_GLOBAL_DATA_PTR;
370649cd0dSSimon Glass
380649cd0dSSimon Glass static struct spi_flash *env_flash;
390649cd0dSSimon Glass
setup_flash_device(void)400649cd0dSSimon Glass static int setup_flash_device(void)
410649cd0dSSimon Glass {
420649cd0dSSimon Glass #ifdef CONFIG_DM_SPI_FLASH
430649cd0dSSimon Glass struct udevice *new;
440649cd0dSSimon Glass int ret;
450649cd0dSSimon Glass
460649cd0dSSimon Glass /* speed and mode will be read from DT */
470649cd0dSSimon Glass ret = spi_flash_probe_bus_cs(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS,
4825a17652SKonstantin Porotchkin CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE,
4925a17652SKonstantin Porotchkin &new);
500649cd0dSSimon Glass if (ret) {
51c5d548a9SYaniv Levinsky set_default_env("spi_flash_probe_bus_cs() failed", 0);
52c5951991SSimon Glass return ret;
530649cd0dSSimon Glass }
540649cd0dSSimon Glass
550649cd0dSSimon Glass env_flash = dev_get_uclass_priv(new);
560649cd0dSSimon Glass #else
570649cd0dSSimon Glass
580649cd0dSSimon Glass if (!env_flash) {
590649cd0dSSimon Glass env_flash = spi_flash_probe(CONFIG_ENV_SPI_BUS,
600649cd0dSSimon Glass CONFIG_ENV_SPI_CS,
610649cd0dSSimon Glass CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE);
620649cd0dSSimon Glass if (!env_flash) {
63c5d548a9SYaniv Levinsky set_default_env("spi_flash_probe() failed", 0);
64c5951991SSimon Glass return -EIO;
650649cd0dSSimon Glass }
660649cd0dSSimon Glass }
670649cd0dSSimon Glass #endif
680649cd0dSSimon Glass return 0;
690649cd0dSSimon Glass }
700649cd0dSSimon Glass
710649cd0dSSimon Glass #if defined(CONFIG_ENV_OFFSET_REDUND)
724415f1d1SSimon Glass #ifdef CMD_SAVEENV
env_sf_save(void)73e5bce247SSimon Glass static int env_sf_save(void)
740649cd0dSSimon Glass {
750649cd0dSSimon Glass env_t env_new;
760649cd0dSSimon Glass char *saved_buffer = NULL, flag = OBSOLETE_FLAG;
770649cd0dSSimon Glass u32 saved_size, saved_offset, sector;
780649cd0dSSimon Glass int ret;
790649cd0dSSimon Glass
800649cd0dSSimon Glass ret = setup_flash_device();
810649cd0dSSimon Glass if (ret)
820649cd0dSSimon Glass return ret;
830649cd0dSSimon Glass
840649cd0dSSimon Glass ret = env_export(&env_new);
850649cd0dSSimon Glass if (ret)
86c5951991SSimon Glass return -EIO;
870649cd0dSSimon Glass env_new.flags = ACTIVE_FLAG;
880649cd0dSSimon Glass
89203e94f6SSimon Glass if (gd->env_valid == ENV_VALID) {
900649cd0dSSimon Glass env_new_offset = CONFIG_ENV_OFFSET_REDUND;
910649cd0dSSimon Glass env_offset = CONFIG_ENV_OFFSET;
920649cd0dSSimon Glass } else {
930649cd0dSSimon Glass env_new_offset = CONFIG_ENV_OFFSET;
940649cd0dSSimon Glass env_offset = CONFIG_ENV_OFFSET_REDUND;
950649cd0dSSimon Glass }
960649cd0dSSimon Glass
970649cd0dSSimon Glass /* Is the sector larger than the env (i.e. embedded) */
980649cd0dSSimon Glass if (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) {
990649cd0dSSimon Glass saved_size = CONFIG_ENV_SECT_SIZE - CONFIG_ENV_SIZE;
1000649cd0dSSimon Glass saved_offset = env_new_offset + CONFIG_ENV_SIZE;
1010649cd0dSSimon Glass saved_buffer = memalign(ARCH_DMA_MINALIGN, saved_size);
1020649cd0dSSimon Glass if (!saved_buffer) {
103c5951991SSimon Glass ret = -ENOMEM;
1040649cd0dSSimon Glass goto done;
1050649cd0dSSimon Glass }
106*9ba5e5bcSHeiko Schocher ret = spi_flash_read(env_flash, saved_offset,
1070649cd0dSSimon Glass saved_size, saved_buffer);
1080649cd0dSSimon Glass if (ret)
1090649cd0dSSimon Glass goto done;
1100649cd0dSSimon Glass }
1110649cd0dSSimon Glass
1120649cd0dSSimon Glass sector = DIV_ROUND_UP(CONFIG_ENV_SIZE, CONFIG_ENV_SECT_SIZE);
1130649cd0dSSimon Glass
1140649cd0dSSimon Glass puts("Erasing SPI flash...");
1150649cd0dSSimon Glass ret = spi_flash_erase(env_flash, env_new_offset,
1160649cd0dSSimon Glass sector * CONFIG_ENV_SECT_SIZE);
1170649cd0dSSimon Glass if (ret)
1180649cd0dSSimon Glass goto done;
1190649cd0dSSimon Glass
1200649cd0dSSimon Glass puts("Writing to SPI flash...");
1210649cd0dSSimon Glass
1220649cd0dSSimon Glass ret = spi_flash_write(env_flash, env_new_offset,
1230649cd0dSSimon Glass CONFIG_ENV_SIZE, &env_new);
1240649cd0dSSimon Glass if (ret)
1250649cd0dSSimon Glass goto done;
1260649cd0dSSimon Glass
1270649cd0dSSimon Glass if (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) {
1280649cd0dSSimon Glass ret = spi_flash_write(env_flash, saved_offset,
1290649cd0dSSimon Glass saved_size, saved_buffer);
1300649cd0dSSimon Glass if (ret)
1310649cd0dSSimon Glass goto done;
1320649cd0dSSimon Glass }
1330649cd0dSSimon Glass
1340649cd0dSSimon Glass ret = spi_flash_write(env_flash, env_offset + offsetof(env_t, flags),
1350649cd0dSSimon Glass sizeof(env_new.flags), &flag);
1360649cd0dSSimon Glass if (ret)
1370649cd0dSSimon Glass goto done;
1380649cd0dSSimon Glass
1390649cd0dSSimon Glass puts("done\n");
1400649cd0dSSimon Glass
141203e94f6SSimon Glass gd->env_valid = gd->env_valid == ENV_REDUND ? ENV_VALID : ENV_REDUND;
1420649cd0dSSimon Glass
1430649cd0dSSimon Glass printf("Valid environment: %d\n", (int)gd->env_valid);
1440649cd0dSSimon Glass
1450649cd0dSSimon Glass done:
1460649cd0dSSimon Glass if (saved_buffer)
1470649cd0dSSimon Glass free(saved_buffer);
1480649cd0dSSimon Glass
1490649cd0dSSimon Glass return ret;
1500649cd0dSSimon Glass }
1514415f1d1SSimon Glass #endif /* CMD_SAVEENV */
1520649cd0dSSimon Glass
env_sf_load(void)153c5951991SSimon Glass static int env_sf_load(void)
1540649cd0dSSimon Glass {
1550649cd0dSSimon Glass int ret;
15680719938SSimon Goldschmidt int read1_fail, read2_fail;
15780719938SSimon Goldschmidt env_t *tmp_env1, *tmp_env2;
1580649cd0dSSimon Glass
1590649cd0dSSimon Glass tmp_env1 = (env_t *)memalign(ARCH_DMA_MINALIGN,
1600649cd0dSSimon Glass CONFIG_ENV_SIZE);
1610649cd0dSSimon Glass tmp_env2 = (env_t *)memalign(ARCH_DMA_MINALIGN,
1620649cd0dSSimon Glass CONFIG_ENV_SIZE);
1630649cd0dSSimon Glass if (!tmp_env1 || !tmp_env2) {
164c5d548a9SYaniv Levinsky set_default_env("malloc() failed", 0);
165c5951991SSimon Glass ret = -EIO;
1660649cd0dSSimon Glass goto out;
1670649cd0dSSimon Glass }
1680649cd0dSSimon Glass
1690649cd0dSSimon Glass ret = setup_flash_device();
1700649cd0dSSimon Glass if (ret)
1710649cd0dSSimon Glass goto out;
1720649cd0dSSimon Glass
173*9ba5e5bcSHeiko Schocher read1_fail = spi_flash_read(env_flash, CONFIG_ENV_OFFSET,
1740649cd0dSSimon Glass CONFIG_ENV_SIZE, tmp_env1);
175*9ba5e5bcSHeiko Schocher read2_fail = spi_flash_read(env_flash, CONFIG_ENV_OFFSET_REDUND,
1760649cd0dSSimon Glass CONFIG_ENV_SIZE, tmp_env2);
1770649cd0dSSimon Glass
17880719938SSimon Goldschmidt ret = env_import_redund((char *)tmp_env1, read1_fail, (char *)tmp_env2,
17980719938SSimon Goldschmidt read2_fail);
1800649cd0dSSimon Glass
1810649cd0dSSimon Glass spi_flash_free(env_flash);
1820649cd0dSSimon Glass env_flash = NULL;
1830649cd0dSSimon Glass out:
1840649cd0dSSimon Glass free(tmp_env1);
1850649cd0dSSimon Glass free(tmp_env2);
186c5951991SSimon Glass
187c5951991SSimon Glass return ret;
1880649cd0dSSimon Glass }
1890649cd0dSSimon Glass #else
1904415f1d1SSimon Glass #ifdef CMD_SAVEENV
env_sf_save(void)191e5bce247SSimon Glass static int env_sf_save(void)
1920649cd0dSSimon Glass {
1930649cd0dSSimon Glass u32 saved_size, saved_offset, sector;
1940649cd0dSSimon Glass char *saved_buffer = NULL;
1950649cd0dSSimon Glass int ret = 1;
1960649cd0dSSimon Glass env_t env_new;
1970649cd0dSSimon Glass
1980649cd0dSSimon Glass ret = setup_flash_device();
1990649cd0dSSimon Glass if (ret)
2000649cd0dSSimon Glass return ret;
2010649cd0dSSimon Glass
2020649cd0dSSimon Glass /* Is the sector larger than the env (i.e. embedded) */
2030649cd0dSSimon Glass if (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) {
2040649cd0dSSimon Glass saved_size = CONFIG_ENV_SECT_SIZE - CONFIG_ENV_SIZE;
2050649cd0dSSimon Glass saved_offset = CONFIG_ENV_OFFSET + CONFIG_ENV_SIZE;
2060649cd0dSSimon Glass saved_buffer = malloc(saved_size);
2070649cd0dSSimon Glass if (!saved_buffer)
2080649cd0dSSimon Glass goto done;
2090649cd0dSSimon Glass
210*9ba5e5bcSHeiko Schocher ret = spi_flash_read(env_flash, saved_offset,
2110649cd0dSSimon Glass saved_size, saved_buffer);
2120649cd0dSSimon Glass if (ret)
2130649cd0dSSimon Glass goto done;
2140649cd0dSSimon Glass }
2150649cd0dSSimon Glass
2160649cd0dSSimon Glass ret = env_export(&env_new);
2170649cd0dSSimon Glass if (ret)
2180649cd0dSSimon Glass goto done;
2190649cd0dSSimon Glass
2200649cd0dSSimon Glass sector = DIV_ROUND_UP(CONFIG_ENV_SIZE, CONFIG_ENV_SECT_SIZE);
2210649cd0dSSimon Glass
2220649cd0dSSimon Glass puts("Erasing SPI flash...");
2230649cd0dSSimon Glass ret = spi_flash_erase(env_flash, CONFIG_ENV_OFFSET,
2240649cd0dSSimon Glass sector * CONFIG_ENV_SECT_SIZE);
2250649cd0dSSimon Glass if (ret)
2260649cd0dSSimon Glass goto done;
2270649cd0dSSimon Glass
2280649cd0dSSimon Glass puts("Writing to SPI flash...");
2290649cd0dSSimon Glass ret = spi_flash_write(env_flash, CONFIG_ENV_OFFSET,
2300649cd0dSSimon Glass CONFIG_ENV_SIZE, &env_new);
2310649cd0dSSimon Glass if (ret)
2320649cd0dSSimon Glass goto done;
2330649cd0dSSimon Glass
2340649cd0dSSimon Glass if (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) {
2350649cd0dSSimon Glass ret = spi_flash_write(env_flash, saved_offset,
2360649cd0dSSimon Glass saved_size, saved_buffer);
2370649cd0dSSimon Glass if (ret)
2380649cd0dSSimon Glass goto done;
2390649cd0dSSimon Glass }
2400649cd0dSSimon Glass
2410649cd0dSSimon Glass ret = 0;
2420649cd0dSSimon Glass puts("done\n");
2430649cd0dSSimon Glass
2440649cd0dSSimon Glass done:
2450649cd0dSSimon Glass if (saved_buffer)
2460649cd0dSSimon Glass free(saved_buffer);
2470649cd0dSSimon Glass
2480649cd0dSSimon Glass return ret;
2490649cd0dSSimon Glass }
2504415f1d1SSimon Glass #endif /* CMD_SAVEENV */
2510649cd0dSSimon Glass
env_sf_load(void)252c5951991SSimon Glass static int env_sf_load(void)
2530649cd0dSSimon Glass {
2540649cd0dSSimon Glass int ret;
2550649cd0dSSimon Glass char *buf = NULL;
2560649cd0dSSimon Glass
2570649cd0dSSimon Glass buf = (char *)memalign(ARCH_DMA_MINALIGN, CONFIG_ENV_SIZE);
2580649cd0dSSimon Glass if (!buf) {
259c5d548a9SYaniv Levinsky set_default_env("malloc() failed", 0);
260c5951991SSimon Glass return -EIO;
2610649cd0dSSimon Glass }
2620649cd0dSSimon Glass
2630649cd0dSSimon Glass ret = setup_flash_device();
2640649cd0dSSimon Glass if (ret)
2650649cd0dSSimon Glass goto out;
2660649cd0dSSimon Glass
267*9ba5e5bcSHeiko Schocher ret = spi_flash_read(env_flash,
268*9ba5e5bcSHeiko Schocher CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE, buf);
2690649cd0dSSimon Glass if (ret) {
270*9ba5e5bcSHeiko Schocher set_default_env("spi_flash_read() failed", 0);
2710649cd0dSSimon Glass goto err_read;
2720649cd0dSSimon Glass }
2730649cd0dSSimon Glass
2740649cd0dSSimon Glass ret = env_import(buf, 1);
27542a1820bSSimon Goldschmidt if (!ret)
276203e94f6SSimon Glass gd->env_valid = ENV_VALID;
2770649cd0dSSimon Glass
2780649cd0dSSimon Glass err_read:
2790649cd0dSSimon Glass spi_flash_free(env_flash);
2800649cd0dSSimon Glass env_flash = NULL;
2810649cd0dSSimon Glass out:
2820649cd0dSSimon Glass free(buf);
283c5951991SSimon Glass
284c5951991SSimon Glass return ret;
2850649cd0dSSimon Glass }
2860649cd0dSSimon Glass #endif
2870649cd0dSSimon Glass
288119c01c2SRajesh Bhagat #ifdef CONFIG_ENV_ADDR
env_sf_get_env_addr(void)289119c01c2SRajesh Bhagat __weak void *env_sf_get_env_addr(void)
290119c01c2SRajesh Bhagat {
291119c01c2SRajesh Bhagat return (void *)CONFIG_ENV_ADDR;
292119c01c2SRajesh Bhagat }
293119c01c2SRajesh Bhagat #endif
294119c01c2SRajesh Bhagat
295b500c92bSAshish Kumar #if defined(INITENV) && defined(CONFIG_ENV_ADDR)
env_sf_init(void)296b500c92bSAshish Kumar static int env_sf_init(void)
297b500c92bSAshish Kumar {
298119c01c2SRajesh Bhagat env_t *env_ptr = (env_t *)env_sf_get_env_addr();
299b500c92bSAshish Kumar
300b500c92bSAshish Kumar if (crc32(0, env_ptr->data, ENV_SIZE) == env_ptr->crc) {
301b500c92bSAshish Kumar gd->env_addr = (ulong)&(env_ptr->data);
302b500c92bSAshish Kumar gd->env_valid = 1;
303b500c92bSAshish Kumar } else {
304b500c92bSAshish Kumar gd->env_addr = (ulong)&default_environment[0];
305b500c92bSAshish Kumar gd->env_valid = 1;
306b500c92bSAshish Kumar }
307b500c92bSAshish Kumar
308b500c92bSAshish Kumar return 0;
309b500c92bSAshish Kumar }
310b500c92bSAshish Kumar #endif
311b500c92bSAshish Kumar
3124415f1d1SSimon Glass U_BOOT_ENV_LOCATION(sf) = {
3134415f1d1SSimon Glass .location = ENVL_SPI_FLASH,
314ac358bebSSimon Glass ENV_NAME("SPI Flash")
315e5bce247SSimon Glass .load = env_sf_load,
3164415f1d1SSimon Glass #ifdef CMD_SAVEENV
317e5bce247SSimon Glass .save = env_save_ptr(env_sf_save),
3184415f1d1SSimon Glass #endif
319b500c92bSAshish Kumar #if defined(INITENV) && defined(CONFIG_ENV_ADDR)
320b500c92bSAshish Kumar .init = env_sf_init,
321b500c92bSAshish Kumar #endif
3224415f1d1SSimon Glass };
323