1003 lines
23 KiB
C
1003 lines
23 KiB
C
/*
|
|
* (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_flash_data.h"
|
|
|
|
#include <environment.h>
|
|
#include <malloc.h>
|
|
|
|
#define SWAP32(x) ((((x) & 0x000000ff) << 24) | (((x) & 0x0000ff00) << 8)| (((x) & 0x00ff0000) >> 8) | (((x) & 0xff000000) >> 24))
|
|
|
|
#define SPI_WRITE_TIMEOUT 1 /*sec*/
|
|
#define SPI_ERASE_TIMEOUT 5 /*sec*/
|
|
|
|
#define SPI_PAGE_SIZE 256
|
|
|
|
#define SPI_MEM_ADDR(addr) (((addr) & 0x00FFFFFF) << 8)
|
|
|
|
#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 setted to 1, then d-cache will be bypassed when read 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);
|
|
|
|
#ifdef CONFIG_SHOW_BOOT_PROGRESS
|
|
printf("spi clock: val=0x%x ctl=0x%x\n",
|
|
(unsigned)val,
|
|
(unsigned)readl(RUBY_SYS_CTL_CTRL));
|
|
#endif
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* 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(void)
|
|
{
|
|
return SPI_FLASH_UBOOT_ADDR + spi_flash_uboot_size();
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
static inline void spi_flash_write_enable(void)
|
|
{
|
|
writel(0, RUBY_SPI_WRITE_EN);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
static 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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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 int spi_flash_locked(u32 flash_begin, u32 size)
|
|
{
|
|
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;
|
|
}
|
|
|
|
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());
|
|
}
|
|
}
|
|
|
|
static 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("SPI flash JEDEC id is unknown: 0x%x\n", (unsigned)jedec_id);
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static 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())) {
|
|
|
|
/* 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);
|
|
|
|
} else {
|
|
while (flash_addr < flash_end) {
|
|
|
|
/* Per-sector erase */
|
|
spi_flash_write_enable();
|
|
|
|
if (g_spi_flash->flags & SECTOR_ERASE_OP20) {
|
|
writel(SPI_MEM_ADDR(flash_addr), RUBY_SPI_SECTOR_ERASE_20);
|
|
} else {
|
|
writel(SPI_MEM_ADDR(flash_addr), RUBY_SPI_SECTOR_ERASE_D8);
|
|
}
|
|
|
|
flash_addr += g_spi_flash->sector_size;
|
|
|
|
ret = spi_flash_wait_ready(SPI_ERASE_TIMEOUT);
|
|
if (ret) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
spi_flash_deassert_cs();
|
|
|
|
return ret;
|
|
}
|
|
|
|
static 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;
|
|
}
|
|
|
|
static 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 */
|
|
|
|
spi_flash_write_enable();
|
|
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();
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int spi_flash_info(void)
|
|
{
|
|
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');
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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 (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"
|
|
);
|
|
|
|
void board_spi_flash_init(void)
|
|
{
|
|
spi_flash_probe();
|
|
|
|
#ifdef CONFIG_SHOW_BOOT_PROGRESS
|
|
(void)spi_flash_info();
|
|
#endif
|
|
}
|
|
|
|
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();
|
|
size = spi_flash_sector_roundup(CONFIG_ENV_SIZE);
|
|
|
|
printf("Erasing SPI flash env, %d bytes at 0x%x...", 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;
|
|
}
|
|
|
|
void env_relocate_spec (void)
|
|
{
|
|
u32 len, flash_addr = spi_flash_env_addr();
|
|
|
|
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)) );
|
|
}
|
|
|
|
/************************************************************************
|
|
* Initialize Environment use
|
|
*
|
|
* We are still running from ROM, so data use is limited
|
|
*/
|
|
int env_init(void)
|
|
{
|
|
u32 crc, len, ret_len, new, flash_addr;
|
|
unsigned off;
|
|
uchar buf[64];
|
|
|
|
if (gd->env_valid == 0) {
|
|
len = sizeof(ulong);
|
|
flash_addr = spi_flash_env_addr();
|
|
flash_addr += offsetof(env_t,crc);
|
|
/* read old CRC */
|
|
spi_flash_read( flash_addr, (u8 *)&crc, len, &ret_len);
|
|
new = 0;
|
|
len = ENV_SIZE;
|
|
printf("env size = 0x%x\n", len);
|
|
flash_addr = spi_flash_env_addr();
|
|
off = offsetof(env_t,data);
|
|
while (len > 0) {
|
|
int n = (len > sizeof(buf)) ? sizeof(buf) : len;
|
|
spi_flash_read( flash_addr+off, buf, n, &ret_len);
|
|
new = crc32 (new, buf, n);
|
|
len -= n;
|
|
off += n;
|
|
}
|
|
if (crc == new) {
|
|
printf("Valid CRC found in flash restoring env...\n");
|
|
// gd->env_addr = offsetof(env_t,data); - malloced later...
|
|
gd->env_valid = 1;
|
|
} else {
|
|
printf("Invalid CRC in flash using default env...\n");
|
|
gd->env_addr = (ulong)&default_environment[0];
|
|
gd->env_valid = 0;
|
|
}
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
// one-time env upgrade code
|
|
int do_updateenv (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
|
|
{
|
|
env_relocate_spec();
|
|
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
|
|
);
|