blob: 34348928733dd9ea6f6f42f5338baf89bbc6d0c1 [file] [log] [blame]
/*
* (C) Copyright 2010 Quantenna Communications Inc.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include "ruby.h"
#include "ruby_spi_api.h"
#include "ruby_spi_flash_data.h"
#include "spi_api.h"
#include <environment.h>
#include <malloc.h>
#include <asm/errno.h>
#define SPI_PAGE_SIZE 256
#define ENV_BASE_SIZE (CONFIG_ENV_BASE_SIZE - ENV_HEADER_SIZE)
#if defined(FLASH_SUPPORT_256KB)
#define SPI_FLASH_256KB_SIZE (256 * 1024)
#endif
#define SPI_FLASH_UBOOT_ADDR 0
#define SPI_FLASH_FULL_UBOOT_SIZE (128*1024) /* Normal Ruby u-boot partition is just under 128k */
#define SPI_FLASH_MINI_UBOOT_SIZE (20*1024) /* Ruby mini u-boot is 20k */
#define SPI_FLASH_TEST 1
#define SPI_FLASH_READ_BYPASS 0 /* if set to 1, then d-cache will be bypassed when reading from flash */
#if SPI_FLASH_READ_BYPASS
#define SPI_FLASH_COPY_FROM(mem_addr, flash_addr, size) \
io_copy_from(mem_addr, RUBY_SPI_FLASH_ADDR + flash_addr, size)
#define SPI_FLASH_COPY_TO(flash_addr, mem_addr, size) \
io_copy_to(RUBY_SPI_FLASH_ADDR + flash_addr, mem_addr, size)
#define SPI_FLASH_WRITE_PREPARE(start, stop)
#else
#define SPI_FLASH_COPY_FROM(mem_addr, flash_addr, size) \
memcpy(mem_addr, (void*)(RUBY_SPI_FLASH_ADDR + flash_addr), size)
#define SPI_FLASH_COPY_TO(flash_addr, mem_addr, size) \
io_copy_to(RUBY_SPI_FLASH_ADDR + flash_addr, mem_addr, size)
#define SPI_FLASH_WRITE_PREPARE(start, stop) \
spi_flash_cache_inv(RUBY_SPI_FLASH_ADDR + start, RUBY_SPI_FLASH_ADDR + stop)
#endif
/* Global data. Only 1 flash can exist. */
static struct flash_info *g_spi_flash = NULL;
static u8 *g_spi_lock = NULL;
DECLARE_GLOBAL_DATA_PTR;
/* references to names in env_common.c */
extern uchar default_environment[];
extern int default_environment_size;
char * env_name_spec = "SPI Flash";
env_t *env_ptr = NULL;
#if 0
// this is for SPI1
static void spi_gpio_config(void)
{
gpio_config(RUBY_GPIO_SPI_MISO, RUBY_GPIO_ALT_OUTPUT);
gpio_config(RUBY_GPIO_SPI_MOSI, RUBY_GPIO_ALT_INPUT);
gpio_config(RUBY_GPIO_SPI_SCK, RUBY_GPIO_ALT_OUTPUT);
gpio_config(RUBY_GPIO_SPI_nCS, RUBY_GPIO_ALT_OUTPUT);
}
#endif
static void spi_ctrl_clock_config(u32 val)
{
writel(RUBY_SYS_CTL_MASK_SPICLK, RUBY_SYS_CTL_MASK);
writel(RUBY_SYS_CTL_SPICLK(val), RUBY_SYS_CTL_CTRL);
writel(0x0, RUBY_SYS_CTL_MASK);
}
static void spi_clock_config(unsigned freq)
{
DECLARE_GLOBAL_DATA_PTR;
if (freq >= (gd->bus_clk / 2)) {
spi_ctrl_clock_config(0x0);
} else if (freq >= (gd->bus_clk / 4)) {
spi_ctrl_clock_config(0x1);
} else if(freq >= (gd->bus_clk / 8)) {
spi_ctrl_clock_config(0x2);
} else {
spi_ctrl_clock_config(0x3);
}
}
static inline unsigned long spi_flash_align_begin(unsigned long addr, unsigned long step)
{
return (addr & (~(step - 1)));
}
static inline unsigned long spi_flash_align_end(unsigned long addr, unsigned long step)
{
return ((addr + step - 1) & (~(step - 1)));
}
static inline void spi_flash_cache_inv(unsigned long begin, unsigned long end)
{
invalidate_dcache_range(
spi_flash_align_begin(begin, ARC_DCACHE_LINE_LEN),
spi_flash_align_end(end, ARC_DCACHE_LINE_LEN));
}
unsigned spi_flash_size(void)
{
unsigned ret = 0;
if (g_spi_flash) {
ret = g_spi_flash->sector_size * g_spi_flash->n_sectors;
}
return ret;
}
unsigned spi_flash_sector_size(void)
{
unsigned ret = 0;
if (g_spi_flash) {
ret = g_spi_flash->sector_size;
}
return ret;
}
#if !defined(TOPAZ_TINY_UBOOT)
static unsigned long spi_flash_sector_roundup(unsigned long size)
{
unsigned long sector_size = spi_flash_sector_size();
if (!sector_size) {
return 0;
}
size = size + (sector_size - 1);
size = size - (size % sector_size);
return size;
}
static unsigned long spi_flash_uboot_size(void)
{
unsigned long flash_size = spi_flash_size();
if (!flash_size) {
return 0;
}
#if defined (FLASH_SUPPORT_256KB)
if (flash_size == SPI_FLASH_256KB_SIZE)
return SPI_FLASH_MINI_UBOOT_SIZE;
#endif
/*
* Bit of a hack. We don't have partition tables in uboot;
* Use flash size as a heuristic to determine how big
* u-boot is in flash. If the flash is smaller than u-boot,
* we can't be using a full size u-boot.
*/
if (flash_size < SPI_FLASH_FULL_UBOOT_SIZE) {
return SPI_FLASH_MINI_UBOOT_SIZE;
}
return SPI_FLASH_FULL_UBOOT_SIZE;
}
static unsigned long spi_flash_env_addr(int index)
{
return SPI_FLASH_UBOOT_ADDR + spi_flash_uboot_size() + index * spi_flash_sector_roundup(CONFIG_ENV_SIZE);
}
#endif
static inline u32 spi_flash_id(void)
{
return (SWAP32(readl(RUBY_SPI_READ_ID)) & 0xFFFFFF);
}
static inline void spi_flash_deassert_cs(void)
{
spi_flash_id();
}
void spi_flash_write_enable(void)
{
writel(0, RUBY_SPI_WRITE_EN);
}
static inline void spi_flash_write_disable(void)
{
writel(0, RUBY_SPI_WRITE_DIS);
}
static inline u32 spi_flash_status(void)
{
return (SWAP32(readl(RUBY_SPI_READ_STATUS)) & 0xFF);
}
static inline int spi_flash_ready(void)
{
return !(spi_flash_status() & RUBY_SPI_WR_IN_PROGRESS);
}
#if !defined(TOPAZ_EP_MINI_UBOOT)
int spi_flash_wait_ready(int sec)
{
DECLARE_GLOBAL_DATA_PTR;
int ret = -1;
int i;
for(i = 0; i < sec; ++i) {
ulong stamp = get_timer(0);
while(1) {
if(spi_flash_ready()) {
ret = 0;
goto done;
} else if (get_timer(stamp) > CONFIG_SYS_HZ) {
break;
}
}
}
done:
return ret;
}
#endif /* TOPAZ_EP_MINI_UBOOT */
static inline u32 spi_flash_lock_begin_sector(u32 begin)
{
return begin / g_spi_flash->sector_size + (!!(begin % g_spi_flash->sector_size));
}
static inline u32 spi_flash_lock_end_sector(u32 end)
{
return min(end, spi_flash_size()) / g_spi_flash->sector_size;
}
#if !defined(TOPAZ_EP_MINI_UBOOT)
static int spi_flash_locked(u32 flash_begin, u32 size)
{
#if defined(TOPAZ_TINY_UBOOT)
return 0;
#else
int ret = 0;
if (g_spi_flash && g_spi_lock) {
const u32 i_begin = spi_flash_lock_begin_sector(flash_begin);
const u32 i_end = spi_flash_lock_end_sector(flash_begin + size);
u32 i;
for (i = i_begin; i < i_end; ++i) {
if (g_spi_lock[i]) {
ret = 1;
break;
}
}
}
return ret;
#endif
}
#endif
#if !defined(TOPAZ_EP_MINI_UBOOT) && !defined(TOPAZ_TINY_UBOOT)
static void spi_flash_lock_fill(u32 flash_begin, u32 size, u8 val)
{
if (g_spi_flash && g_spi_lock) {
const u32 i_begin = spi_flash_lock_begin_sector(flash_begin);
const u32 i_end = spi_flash_lock_end_sector(flash_begin + size);
u32 i;
for (i = i_begin; i < i_end; ++i) {
g_spi_lock[i] = val;
}
}
}
static inline void spi_flash_lock(u32 flash_addr, u32 size)
{
spi_flash_lock_fill(flash_addr, size, 1);
}
static inline void spi_flash_unlock(u32 flash_addr, u32 size)
{
spi_flash_lock_fill(flash_addr, size, 0);
}
static void spi_flash_lock_prepare(void)
{
if (!g_spi_lock && g_spi_flash) {
/* Heap is allocated after board is inited.
* So this snippet must be called later.
*/
g_spi_lock = malloc(g_spi_flash->n_sectors);
spi_flash_unlock(0, spi_flash_size());
spi_flash_lock(SPI_FLASH_UBOOT_ADDR, spi_flash_uboot_size());
}
}
#endif /* TOPAZ_EP_MINI_UBOOT */
void spi_flash_probe(void)
{
int i;
u32 jedec_id;
if (g_spi_lock) {
free(g_spi_lock);
g_spi_lock = NULL;
}
g_spi_flash = NULL;
spi_clock_config(FREQ_UNKNOWN);
jedec_id = spi_flash_id();
for(i = 0; i < ARRAY_SIZE(flash_data); ++i) {
if(jedec_id == flash_data[i].jedec_id) {
g_spi_flash = flash_data + i;
spi_clock_config(g_spi_flash->freq);
break;
}
}
if(!g_spi_flash) {
printf("JEDEC unknown: 0x%x\n", (unsigned)jedec_id);
return;
}
}
#if !defined(TOPAZ_EP_MINI_UBOOT) && !defined(TOPAZ_TINY_UBOOT)
void spi_protect_mode_on(void)
{
if (g_spi_flash){
if (spi_protect_mode(g_spi_flash) == EOPNOTSUPP){
g_spi_flash->single_unprotect_mode = NOT_SUPPORTED;
}
}
}
void spi_protect_mode_off(void)
{
if (g_spi_flash){
g_spi_flash->single_unprotect_mode = NOT_SUPPORTED;
}
}
void spi_unprotect_global(void)
{
if (g_spi_flash){
spi_unprotect_all(g_spi_flash);
}
}
#endif
static int spi_flash_read_op_check(u32 flash_addr, u32 size)
{
int ret = 0;
if (!g_spi_flash) {
/* No flash chip detected during probe. */
ret = -1;
} else if(flash_addr >= spi_flash_size()) {
/* Adress beyond flash address space. */
ret = -2;
} else if(!spi_flash_ready()) {
/* Flash is not ready. Why? */
ret = -3;
}
return ret;
}
#if !defined(TOPAZ_EP_MINI_UBOOT)
static int spi_flash_erase_op_check(u32 flash_addr, u32 size)
{
int ret = spi_flash_read_op_check(flash_addr, size);
if (ret) {
/* Already error. */
} else if (flash_addr % g_spi_flash->sector_size) {
/* Although it is legal to have erase address
* inside sector, it is not very safe.
* Simple mistake - and neighbour sector erased.
*/
ret = -4;
} else if (size % g_spi_flash->sector_size) {
/* Same as previous case - legal but not safe. */
ret = -5;
} else if(spi_flash_locked(flash_addr, size)) {
printf("Erase within locked area is not permitted. Unlock first !\n");
ret = -6;
}
return ret;
}
static int spi_flash_write_op_check(u32 flash_addr, u32 size)
{
int ret = spi_flash_read_op_check(flash_addr, size);
if (ret) {
/* Already error. */
} else if(spi_flash_locked(flash_addr, size)) {
printf("Write within locked area is not permitted. Unlock first !\n");
ret = -4;
}
return ret;
}
#if !defined(TOPAZ_TINY_UBOOT)
static
#endif
int spi_flash_erase(u32 flash_addr, u32 size)
{
int ret = spi_flash_erase_op_check(flash_addr, size);
if (!ret) {
u32 flash_end = min(flash_addr + size, spi_flash_size());
SPI_FLASH_WRITE_PREPARE(flash_addr, flash_end);
if ((flash_addr == 0) && (size >= spi_flash_size())) {
/* Unprotec All Regions */
ret = spi_unprotect_all(g_spi_flash);
if (ret){
printf("ERROR: unprot all\n");
return (ret);
}
/* Bulk erase */
spi_flash_write_enable();
writel(0, RUBY_SPI_BULK_ERASE);
ret = spi_flash_wait_ready(SPI_ERASE_TIMEOUT * g_spi_flash->n_sectors);
if (ret) {
return (ret);
}
} else {
while (flash_addr < flash_end) {
/* Per Sector Unprotect/erase */
if (spi_device_erase(g_spi_flash, flash_addr) < 0){
break;
}
flash_addr += g_spi_flash->sector_size;
ret = spi_flash_wait_ready(SPI_ERASE_TIMEOUT);
if (ret) {
break;
}
}
}
}
spi_flash_deassert_cs();
/* Global Protect */
spi_flash_wait_ready(SPI_ERASE_TIMEOUT);
ret = spi_protect_all(g_spi_flash);
if (ret) {
printf("ERROR: Failed to protect all regions of the Flash \n");
}
return ret;
}
#endif /* TOPAZ_EP_MINI_UBOOT */
int spi_flash_read(u32 flash_addr, u8 *mem_addr, u32 size, u32 *ret_size)
{
int ret = spi_flash_read_op_check(flash_addr, size);
if (!ret) {
*ret_size = min(size, spi_flash_size() - flash_addr);
SPI_FLASH_COPY_FROM(mem_addr, flash_addr, *ret_size);
}
spi_flash_deassert_cs();
return ret;
}
#if !defined(TOPAZ_EP_MINI_UBOOT)
int spi_flash_write(u32 flash_addr, u8 *mem_addr, u32 size, u32 *ret_size)
{
int ret = spi_flash_write_op_check(flash_addr, size);
if (!ret) {
u32 flash_end = min(flash_addr + size, spi_flash_size());
SPI_FLASH_WRITE_PREPARE(flash_addr, flash_end);
*ret_size = flash_end - flash_addr;
while (flash_addr < flash_end) {
/* Per-page programming */
u32 write_sz = min(
SPI_PAGE_SIZE - (flash_addr % SPI_PAGE_SIZE), /* do not exceed page boundary */
flash_end - flash_addr); /* do not exceed requested range */
/* Unprotect Sector */
ret = spi_unprotect_sector(g_spi_flash,flash_addr);
if (ret) {
printf("ERROR: Failed to unprotect Sector %x \n", flash_addr);
break;
}
spi_flash_write_enable();
if ( spi_flash_size() > RUBY_SPI_BOUNDARY_4B ){
writel(SPI_MEM_ADDR_4B(flash_addr), RUBY_SPI_PAGE_PROGRAM_4B);
}
else {
writel(SPI_MEM_ADDR(flash_addr), RUBY_SPI_PAGE_PROGRAM);
}
SPI_FLASH_COPY_TO((void*)flash_addr, mem_addr, write_sz);
writel(0, RUBY_SPI_COMMIT);
flash_addr += write_sz;
mem_addr += write_sz;
ret = spi_flash_wait_ready(SPI_WRITE_TIMEOUT);
if (ret) {
break;
}
}
}
spi_flash_deassert_cs();
/* Global Protect */
spi_flash_wait_ready(SPI_WRITE_TIMEOUT);
ret = spi_protect_all(g_spi_flash);
if (ret){
printf("ERROR: Failed to protect all regions of the Flash \n");
}
return ret;
}
#endif
#if !defined(TOPAZ_EP_MINI_UBOOT) && !defined(TOPAZ_TINY_UBOOT)
int spi_flash_info(void)
{
#ifdef CONFIG_SHOW_BOOT_PROGRESS
if (!g_spi_flash) {
printf("No SPI flash.\n");
} else {
int i;
printf("SPI flash info:\n");
printf("\tname : %s\n", g_spi_flash->name);
printf("\tjedec_id : 0x%x\n", (unsigned)g_spi_flash->jedec_id);
printf("\tsector size : %u\n", (unsigned)g_spi_flash->sector_size);
printf("\tnumber of sector : %u\n", (unsigned)g_spi_flash->n_sectors);
printf("\tfrequency : %u\n", (unsigned)g_spi_flash->freq);
printf("\tflags : 0x%x\n", (unsigned)g_spi_flash->flags);
printf("\tlock : ");
if (g_spi_lock) {
for (i = 0; i < g_spi_flash->n_sectors; ++i) {
putc(g_spi_lock[i] ? '1' : '0');
}
}
putc('\n');
}
#endif
return 0;
}
#if SPI_FLASH_TEST
static int spi_flash_test_cmp(u32 flash_addr, u32 size, u8 val)
{
int ret = 0;
u32 i = 0;
u8 read_buf[64];
int check_latch = 1;
for (i = flash_addr; i < size; i += sizeof(read_buf)) {
int j;
u32 ret_size;
int read_ret;
u32 read_size = min(sizeof(read_buf), size - i);
memset(read_buf, ~val, sizeof(read_buf));
read_ret = spi_flash_read(i, read_buf, read_size, &ret_size);
if (read_ret) {
ret = -1;
printf("Read failed: %d\n", read_ret);
goto done;
}
if (ret_size != read_size) {
ret = -2;
printf("Read failed: ret_size=%u, read_size=%u\n",
(unsigned)ret_size, (unsigned)read_size);
goto done;
}
for (j = 0; j < ret_size; ++j) {
if (read_buf[j] != val) {
ret = -3;
if (check_latch) {
printf("Byte %u value is wrong: 0x%x (must be 0x%x) (%u cache-line)\n",
(unsigned)(i + j), (unsigned)read_buf[j],
(unsigned)val, (unsigned)((i + j) / ARC_DCACHE_LINE_LEN));
check_latch = 0;
}
} else if (!check_latch) {
printf("Byte %u value is correct: 0x%x (%u cache-line)\n",
(unsigned)(i + j), (unsigned)val,
(unsigned)((i + j) / ARC_DCACHE_LINE_LEN));
check_latch = 1;
}
}
}
done:
return ret;
}
static int spi_flash_test_set(u32 flash_addr, u32 size, u8 val)
{
int ret = 0;
u32 i = 0;
u8 *page_buf = malloc(SPI_PAGE_SIZE);
for (i = flash_addr; i < size; i += SPI_PAGE_SIZE) {
u32 ret_size;
int write_ret;
memset(page_buf, val, SPI_PAGE_SIZE);
write_ret = spi_flash_write(i, page_buf, SPI_PAGE_SIZE, &ret_size);
if (write_ret) {
printf("Write failed: %d\n", write_ret);
ret = -1;
goto done;
}
if (ret_size != SPI_PAGE_SIZE) {
printf("Write failed: ret_size=%u\n", (unsigned)ret_size);
ret = -2;
goto done;
}
}
done:
free(page_buf);
return ret;
}
static int spi_flash_test_speed(void)
{
int ret = 0;
u32 size = spi_flash_size();
u8 *read_buf = malloc(SPI_PAGE_SIZE);
unsigned long long time = 0;
unsigned timestamp = get_timer(0);
int i;
for (i = 0; i < size; i += SPI_PAGE_SIZE) {
u32 ret_size = 0;
u32 read_size = min(SPI_PAGE_SIZE, size - i);
ret = spi_flash_read(i, read_buf, read_size, &ret_size);
if (ret) {
printf("Read failed: %d\n", ret);
goto done;
}
time += get_timer(timestamp);
timestamp = get_timer(0);
}
done:
printf("Ticks: %llu\n", time);
free(read_buf);
return ret;
}
#endif // #if SPI_FLASH_TEST
static int spi_flash_test(void)
{
int ret = 0;
#if SPI_FLASH_TEST
int i;
if (!g_spi_flash) {
printf("No SPI flash.\n");
ret = -1;
goto done;
}
printf("TEST POINT 1\n");
ret = spi_flash_erase(0x0, spi_flash_size());
if (ret) {
printf("Bulk erase is failed: %d\n", ret);
ret = -2;
goto done;
}
printf("TEST POINT 2\n");
ret = spi_flash_test_cmp(0x0, spi_flash_size(), 0xFF);
if (ret) {
printf("Check after bulk erase is failed: %d\n", ret);
ret = -3;
goto done;
}
printf("TEST POINT 3\n");
ret = spi_flash_test_set(0x0, spi_flash_size(), 0xAA);
if (ret) {
printf("Fill with constant is failed: %d\n", ret);
ret = -4;
goto done;
}
printf("TEST POINT 4\n");
ret = spi_flash_test_cmp(0x0, spi_flash_size(), 0xAA);
if (ret) {
printf("Check constant filling is failed: %d\n", ret);
ret = -5;
goto done;
}
printf("TEST POINT 5\n");
i = 0;
while(i < g_spi_flash->n_sectors) {
int num = min(i + 1, g_spi_flash->n_sectors - i);
ret = spi_flash_erase(i * g_spi_flash->sector_size, num * g_spi_flash->sector_size);
if (ret) {
printf("Per-sector erase is failed (%d:%d): %d\n", (int)i, (int)num, ret);
ret = -6;
goto done;
}
i += num;
}
printf("TEST POINT 6\n");
ret = spi_flash_test_cmp(0x0, spi_flash_size(), 0xFF);
if (ret) {
printf("Check after per-sector erase is failed: %d\n", ret);
ret = -7;
goto done;
}
printf("TEST POINT 7\n");
for (i = 0; i < 20; ++i) {
u32 addr = get_timer(0) % (spi_flash_size() - g_spi_flash->sector_size);
u32 sector_addr = addr / g_spi_flash->sector_size * g_spi_flash->sector_size;
u32 size = 2 * (sector_addr + g_spi_flash->sector_size - addr);
if (size) {
printf("Random test: addr=0x%x size=%u : ", (unsigned)addr, (unsigned)size);
ret = spi_flash_erase(sector_addr, 2 * g_spi_flash->sector_size);
if (ret) {
printf("2 sector erase is failed 0x%x: %d\n", (unsigned)sector_addr, ret);
ret = -7;
goto done;
}
ret = spi_flash_test_set(addr, size, 0x55);
if (ret) {
printf("Fill with constant is failed: %d\n", ret);
ret = -8;
goto done;
}
ret = spi_flash_test_cmp(addr, size, 0x55);
if (ret) {
printf("Check constant filling is failed: %d\n", ret);
ret = -8;
goto done;
}
ret = spi_flash_test_cmp(sector_addr, addr - sector_addr, 0xFF);
if (ret) {
printf("Check that constant filling is not out-of-boundary failed: %d\n", ret);
ret = -9;
goto done;
}
ret = spi_flash_test_cmp(addr + size,
sector_addr + 2 * g_spi_flash->sector_size - addr - size, 0xFF);
if (ret) {
printf("Check that constant filling is not out-of-boundary failed: %d\n", ret);
ret = -10;
goto done;
}
printf("OK\n");
}
}
printf("TEST POINT 8\n");
ret = spi_flash_test_speed();
if (ret) {
printf("Speed test failed: %d\n", ret);
ret = -11;
goto done;
}
printf("TEST POINT 9\n");
done:
if (ret) {
printf("Failure: ret=%d\n", ret);
} else {
printf("Success!\n");
}
#endif // #if SPI_FLASH_TEST
return ret;
}
#if !defined(TOPAZ_TINY_UBOOT)
int do_spi_flash(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
{
int ret = 0;
if (argc < 2) {
printf("At least one argument to command please.\n");
ret = -1;
} else {
char *cmd = argv[1];
spi_flash_lock_prepare();
if (strcmp(cmd, "test") == 0) {
ret = spi_flash_test();
} else if (strcmp(cmd, "info") == 0) {
ret = spi_flash_info();
} else if (strcmp(cmd, "erase") == 0) {
u32 flash_addr = (argc >= 3) ? simple_strtoul(argv[2], NULL, 0) : 0;
u32 size = (argc >= 4) ? simple_strtoul(argv[3], NULL, 0) : spi_flash_size();
printf("Erase: flash=0x%x size=%u\n", (unsigned)flash_addr, (unsigned)size);
ret = spi_flash_erase(flash_addr, size);
} else if (strcmp(cmd, "lock") == 0) {
u32 flash_addr = (argc >= 3) ? simple_strtoul(argv[2], NULL, 0) : 0;
u32 size = (argc >= 4) ? simple_strtoul(argv[3], NULL, 0) : spi_flash_size();
printf("Lock: flash=0x%x size=%u\n", (unsigned)flash_addr, (unsigned)size);
spi_flash_lock(flash_addr, size);
} else if (strcmp(cmd, "unlock") == 0) {
u32 flash_addr = (argc >= 3) ? simple_strtoul(argv[2], NULL, 0) : 0;
u32 size = (argc >= 4) ? simple_strtoul(argv[3], NULL, 0) : spi_flash_size();
printf("Unlock: flash=0x%x size=%u\n", (unsigned)flash_addr, (unsigned)size);
spi_flash_unlock(flash_addr, size);
} else if (strcmp(cmd, "unlock_hw") == 0) {
uint32_t spi_ctrl_val;
spi_flash_write_enable();
spi_ctrl_val = readl(RUBY_SPI_CONTROL);
writel((spi_ctrl_val & 0xffffff00) | 0x1, RUBY_SPI_CONTROL);
writel(0, TOPAZ_SPI_GBLOCK_UNLOCK);
writel(spi_ctrl_val, RUBY_SPI_CONTROL);
spi_flash_wait_ready(SPI_UNLOCK_TIMEOUT);
spi_flash_write_disable();
} else if (argc != 5) {
printf("Not enough arguments.\n");
ret = -2;
} else {
u32 ret_size = 0;
u32 flash_addr = simple_strtoul(argv[2], NULL, 0);
u8 *mem_addr = (u8*)simple_strtoul(argv[3], NULL, 0);
u32 size = simple_strtoul(argv[4], NULL, 0);
if (strcmp(cmd, "read") == 0) {
printf("Read: ");
ret = spi_flash_read(flash_addr, mem_addr, size, &ret_size);
} else if (strcmp(cmd, "write") == 0) {
printf("Write: ");
ret = spi_flash_write(flash_addr, mem_addr, size, &ret_size);
} else {
printf("Unsupported '%s' command: ", cmd);
ret = -3;
}
if (!ret) {
printf("flash=0x%x mem=0x%x size=%u ret_size=%u\n",
(unsigned)flash_addr, (unsigned)mem_addr, (unsigned)size, (unsigned)ret_size);
}
}
if (ret) {
printf("'%s' : failed : ret=%d\n", cmd, (int)ret);
}
}
return ret;
}
U_BOOT_CMD(spi_flash, 5, 0, do_spi_flash,
"SPI flash sub-system",
"spi_flash info - show available SPI flash\n"
"spi_flash test - test flash (DESTROY ALL DATA ON FLASH!)\n"
"spi_flash read flash_addr mem_addr size - read data from flash to memory\n"
"spi_flash write flash_addr mem_addr size - write data from memory to flash\n"
"spi_flash erase [flash_addr len] - erase flash\n"
"spi_flash lock [flash_addr len] - lock flash (granularity is sector size)\n"
"spi_flash unlock [flash_addr len] - unlock flash (granularity is sector size)\n"
"spi_flash unlock_hw - unlock flash hardware protection\n"
);
#endif
#endif /* TOPAZ_EP_MINI_UBOOT */
void board_spi_flash_init(void)
{
#ifndef PLATFORM_NOSPI
spi_flash_probe();
if ( spi_flash_size() > RUBY_SPI_BOUNDARY_4B ){
writel(RUBY_SPI_ADDRESS_MODE_4B, RUBY_SPI_CONTROL);
}
#else /* flash-less build */
printf("Use default environment on flash-less build\n");
gd->env_addr = (ulong) &default_environment[0];
gd->env_valid = 0;
#endif
}
#if defined(TOPAZ_EP_MINI_UBOOT) || defined(TOPAZ_TINY_UBOOT)
int saveenv(void)
{
return 0;
}
#else
int saveenv(void)
{
u32 size, flash_addr, ret_size;
int ret;
if ((gd->env_valid == 0) || (env_ptr == NULL)) {
puts("Environment SPI flash not initialized properly!!!\n");
return 1;
}
flash_addr = spi_flash_env_addr(0);
size = spi_flash_sector_roundup(CONFIG_ENV_SIZE);
printf("Erasing SPI flash env, %u bytes at 0x%x...\n", size, flash_addr);
ret = spi_flash_erase( flash_addr, size );
if ( ret ) {
printf("Error along the way #1 - %d\n", ret);
goto done;
}
size = CONFIG_ENV_SIZE;
printf("Writing to SPI flash...");
ret = spi_flash_write( flash_addr, (u8 *)env_ptr, size, &ret_size );
if ( ret ) {
printf("Error along the way #2 - %d\n", ret);
goto done;
}
ret = 0;
puts("done\n");
done:
return ret;
}
#endif /* TOPAZ_EP_MINI_UBOOT */
#if !defined(TOPAZ_TINY_UBOOT)
void env_relocate_spec (int index)
{
u32 len, flash_addr = spi_flash_env_addr(index);
spi_flash_read( flash_addr, (u8 *)env_ptr, CONFIG_ENV_SIZE, &len);
}
uchar env_get_char_spec (int index)
{
return ( *((uchar *)(gd->env_addr + index)) );
}
int env_verify (u32 env_addr)
{
u32 crc, len, ret_len, new, addr;
uchar buf[64];
u32 env_size[] = {ENV_SIZE, F64K_ENV_PARTITION_SIZE - ENV_HEADER_SIZE, ENV_BASE_SIZE};
u32 idx = 0;
len = sizeof(crc);
addr = env_addr + offsetof(env_t, crc);
/* read old CRC */
spi_flash_read(addr, (u8 *)&crc, len, &ret_len);
if (ret_len != len) {
return -1;
}
do {
new = 0;
ret_len = 0;
len = env_size[idx++];
addr = env_addr + offsetof(env_t, data);
while (len > 0) {
int n = min(len, sizeof(buf));
spi_flash_read(addr, buf, n, &ret_len);
if (ret_len != n) {
return -1;
}
new = crc32(new, buf, n);
len -= n;
addr += n;
}
} while ((crc != new) && (idx < ARRAY_SIZE(env_size)));
return (crc != new);
}
/************************************************************************
* Initialize Environment use
*
* We are still running from ROM, so data use is limited
*/
int env_init(void)
{
u32 env_addr;
u32 env_addr_backup, env_end;
if (gd->env_valid == 0) {
env_addr = spi_flash_env_addr(0);
/* No ENV & CAL backup for mini-uboot */
if (spi_flash_uboot_size() == SPI_FLASH_FULL_UBOOT_SIZE) {
#if defined(FLASH_SUPPORT_256KB)
env_addr_backup = spi_flash_env_addr(2);
env_end = spi_flash_env_addr(4);
#else
env_addr_backup = spi_flash_env_addr(1);
env_end = spi_flash_env_addr(2);
#endif
} else {
env_addr_backup = 0;
env_end = spi_flash_env_addr(1);
}
if (env_verify(env_addr) == 0) {
printf("Valid CRC found in flash restoring env...\n");
// gd->env_addr = offsetof(env_t,data); - malloced later...
gd->env_valid = 1;
} else if (env_end <= IMAGES_START_ADDR
&& env_end < spi_flash_size()
&& env_verify(env_addr_backup) == 0) {
printf("Valid CRC found in flash - restoring backup...\n");
// gd->env_addr = offsetof(env_t,data); - malloced later...
gd->env_valid = 2;
} else {
printf("Invalid CRC in flash using default env...\n");
gd->env_addr = (ulong) &default_environment[0];
gd->env_valid = 0;
}
}
return (0);
}
#endif
#if !defined(TOPAZ_EP_MINI_UBOOT) && !defined(TOPAZ_TINY_UBOOT)
// one-time env upgrade code
int do_updateenv(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
env_relocate_spec(0);
env_ptr->crc = crc32(0, env_ptr->data, ENV_SIZE);
gd->env_valid = 1;
printf ("Upgrading Environment ...\n");
saveenv();
return 0;
}
U_BOOT_CMD(
upgradeenv, 1, 0, do_updateenv,
"upgrade environment",
NULL
);
int do_updatecrc(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
env_crc_update();
gd->env_valid = 1;
return 0;
}
U_BOOT_CMD(
updatecrc, 1, 0, do_updatecrc,
"update CRC",
NULL
);
#endif /* TOPAZ_EP_MINI_UBOOT */