417 lines
8.8 KiB
C
417 lines
8.8 KiB
C
#include <common.h>
|
|
#include <watchdog.h>
|
|
#include <jffs2/jffs2.h>
|
|
#include <asm/addrspace.h>
|
|
#include <asm/types.h>
|
|
#include "ar7240_soc.h"
|
|
#include "ar7240_flash.h"
|
|
|
|
/*
|
|
* globals
|
|
*/
|
|
flash_info_t flash_info[CFG_MAX_FLASH_BANKS];
|
|
|
|
#undef display
|
|
#define display(x) ;
|
|
|
|
#define SIZE_INBYTES_4MBYTES (4 * 1024 * 1024)
|
|
#define SIZE_INBYTES_8MBYTES (2 * SIZE_INBYTES_4MBYTES)
|
|
#define SIZE_INBYTES_16MBYTES (2 * SIZE_INBYTES_8MBYTES)
|
|
|
|
#define SIZE_INBYTES_4KBYTES (4 * 1024)
|
|
#define SIZE_INBYTES_64KBYTES (16 * SIZE_INBYTES_4KBYTES)
|
|
|
|
#ifndef DEFAULT_FLASH_SIZE_IN_MB
|
|
#error "DEFAULT_FLASH_SIZE_IN_MB not defined!"
|
|
#endif
|
|
|
|
|
|
/*
|
|
* statics
|
|
*/
|
|
static void ar7240_spi_write_enable(void);
|
|
static void ar7240_spi_poll(void);
|
|
#if !defined(ATH_SST_FLASH)
|
|
static void ar7240_spi_write_page(uint32_t addr, uint8_t * data, int len);
|
|
#endif
|
|
static void ar7240_spi_sector_erase(uint32_t addr);
|
|
|
|
static ulong read_id(void)
|
|
{
|
|
unsigned int flashid = 0;
|
|
|
|
ar7240_reg_wr_nf(AR7240_SPI_FS, 1);
|
|
ar7240_reg_wr_nf(AR7240_SPI_WRITE, AR7240_SPI_CS_DIS);
|
|
|
|
ar7240_spi_bit_banger(0x9F);
|
|
|
|
ar7240_spi_delay_8();
|
|
ar7240_spi_delay_8();
|
|
ar7240_spi_delay_8();
|
|
ar7240_spi_delay_8();
|
|
|
|
flashid = ar7240_reg_rd(AR7240_SPI_RD_STATUS);
|
|
|
|
/*
|
|
* We have 3 bytes:
|
|
* - manufacture ID (1b)
|
|
* - product ID (2b)
|
|
*/
|
|
flashid = flashid >> 8;
|
|
|
|
ar7240_spi_done();
|
|
|
|
return((ulong)flashid);
|
|
}
|
|
|
|
#ifdef ATH_SST_FLASH
|
|
void ar7240_spi_flash_unblock(void)
|
|
{
|
|
ar7240_spi_write_enable();
|
|
ar7240_spi_bit_banger(AR7240_SPI_CMD_WRITE_SR);
|
|
ar7240_spi_bit_banger(0x0);
|
|
ar7240_spi_go();
|
|
ar7240_spi_poll();
|
|
}
|
|
#endif
|
|
|
|
static void flash_set_geom(int size, int sector_count, int sector_size){
|
|
int i;
|
|
flash_info_t *info = &flash_info[0];
|
|
|
|
info->size = size;
|
|
info->sector_count = sector_count;
|
|
info->sector_size = sector_size;
|
|
|
|
for(i = 0; i < info->sector_count; i++){
|
|
info->start[i] = CFG_FLASH_BASE + (i * info->sector_size);
|
|
}
|
|
}
|
|
|
|
unsigned long flash_init(void)
|
|
{
|
|
flash_info_t *info;
|
|
|
|
info = &flash_info[0];
|
|
|
|
// spi flash clock
|
|
ar7240_reg_wr(AR7240_SPI_FS, 0x01);
|
|
|
|
ar7240_reg_wr(AR7240_SPI_CLOCK, 0x43);
|
|
|
|
ar7240_reg_wr(AR7240_SPI_FS, 0x0);
|
|
|
|
// get flash id
|
|
info->flash_id = read_id();
|
|
|
|
puts("FLASH: ");
|
|
|
|
// fill flash info based on JEDEC ID
|
|
switch(info->flash_id) {
|
|
/*
|
|
* 4M flash chips
|
|
*/
|
|
case 0x010215: // tested
|
|
flash_set_geom(SIZE_INBYTES_4MBYTES, 64, SIZE_INBYTES_64KBYTES);
|
|
puts("Spansion S25FL032P (4 MB)");
|
|
break;
|
|
|
|
case 0x1F4700:
|
|
flash_set_geom(SIZE_INBYTES_4MBYTES, 64, SIZE_INBYTES_64KBYTES);
|
|
puts("Atmel AT25DF321 (4 MB)");
|
|
break;
|
|
|
|
case 0x1C3016: // tested
|
|
flash_set_geom(SIZE_INBYTES_4MBYTES, 64, SIZE_INBYTES_64KBYTES);
|
|
puts("EON EN25Q32 (4 MB)");
|
|
break;
|
|
|
|
case 0x1C3116: // tested
|
|
flash_set_geom(SIZE_INBYTES_4MBYTES, 64, SIZE_INBYTES_64KBYTES);
|
|
puts("EON EN25F32 (4 MB)");
|
|
break;
|
|
|
|
case 0x202016:
|
|
flash_set_geom(SIZE_INBYTES_4MBYTES, 64, SIZE_INBYTES_64KBYTES);
|
|
puts("Micron M25P32 (4 MB)");
|
|
break;
|
|
|
|
case 0xEF4016:
|
|
flash_set_geom(SIZE_INBYTES_4MBYTES, 64, SIZE_INBYTES_64KBYTES);
|
|
puts("Winbond W25Q32 (4 MB)");
|
|
break;
|
|
|
|
case 0xC22016:
|
|
flash_set_geom(SIZE_INBYTES_4MBYTES, 64, SIZE_INBYTES_64KBYTES);
|
|
puts("Macronix MX25L320 (4 MB)");
|
|
break;
|
|
|
|
/*
|
|
* 8M flash chips
|
|
*/
|
|
case 0x010216:
|
|
flash_set_geom(SIZE_INBYTES_8MBYTES, 128, SIZE_INBYTES_64KBYTES);
|
|
puts("Spansion S25FL064P (8 MB)");
|
|
break;
|
|
|
|
case 0x1F4800:
|
|
flash_set_geom(SIZE_INBYTES_8MBYTES, 128, SIZE_INBYTES_64KBYTES);
|
|
puts("Atmel AT25DF641 (8 MB)");
|
|
break;
|
|
|
|
case 0x1C3017: // tested
|
|
flash_set_geom(SIZE_INBYTES_8MBYTES, 128, SIZE_INBYTES_64KBYTES);
|
|
puts("EON EN25Q64 (8 MB)");
|
|
break;
|
|
|
|
case 0x202017:
|
|
flash_set_geom(SIZE_INBYTES_8MBYTES, 128, SIZE_INBYTES_64KBYTES);
|
|
puts("Micron M25P64 (8 MB)");
|
|
break;
|
|
|
|
case 0xEF4017: // tested
|
|
flash_set_geom(SIZE_INBYTES_8MBYTES, 128, SIZE_INBYTES_64KBYTES);
|
|
puts("Winbond W25Q64 (8 MB)");
|
|
break;
|
|
|
|
case 0xC22017: // tested
|
|
case 0xC22617:
|
|
flash_set_geom(SIZE_INBYTES_8MBYTES, 128, SIZE_INBYTES_64KBYTES);
|
|
puts("Macronix MX25L64 (8 MB)");
|
|
break;
|
|
|
|
/*
|
|
* 16M flash chips
|
|
*/
|
|
case 0xEF4018: // tested
|
|
flash_set_geom(SIZE_INBYTES_16MBYTES, 256, SIZE_INBYTES_64KBYTES);
|
|
puts("Winbond W25Q128 (16 MB)");
|
|
break;
|
|
|
|
case 0xC22018:
|
|
case 0xC22618:
|
|
flash_set_geom(SIZE_INBYTES_16MBYTES, 256, SIZE_INBYTES_64KBYTES);
|
|
puts("Macronix MX25L128 (16 MB)");
|
|
break;
|
|
|
|
case 0x012018:
|
|
flash_set_geom(SIZE_INBYTES_16MBYTES, 256, SIZE_INBYTES_64KBYTES);
|
|
puts("Spansion S25FL127S (16 MB)");
|
|
break;
|
|
|
|
case 0x20BA18:
|
|
flash_set_geom(SIZE_INBYTES_16MBYTES, 256, SIZE_INBYTES_64KBYTES);
|
|
puts("Micron N25Q128 (16 MB)");
|
|
break;
|
|
|
|
/*
|
|
* Unknown flash
|
|
*/
|
|
default:
|
|
#if (DEFAULT_FLASH_SIZE_IN_MB == 4)
|
|
flash_set_geom(SIZE_INBYTES_4MBYTES, 64, SIZE_INBYTES_64KBYTES);
|
|
puts("Unknown type (using only 4 MB)\n");
|
|
#elif (DEFAULT_FLASH_SIZE_IN_MB == 8)
|
|
flash_set_geom(SIZE_INBYTES_8MBYTES, 128, SIZE_INBYTES_64KBYTES);
|
|
puts("Unknown type (using only 8 MB)\n");
|
|
#elif (DEFAULT_FLASH_SIZE_IN_MB == 16)
|
|
flash_set_geom(SIZE_INBYTES_16MBYTES, 256, SIZE_INBYTES_64KBYTES);
|
|
puts("Unknown type (using only 16 MB)\n");
|
|
#endif
|
|
printf("\nPlease, send request to add support\nfor your FLASH - JEDEC ID: 0x%06lX\n", info->flash_id);
|
|
info->flash_id = FLASH_CUSTOM;
|
|
break;
|
|
}
|
|
|
|
puts("\n");
|
|
|
|
return(info->size);
|
|
}
|
|
|
|
void
|
|
flash_print_info(flash_info_t *info)
|
|
{
|
|
printf("The hell do you want flinfo for??\n");
|
|
}
|
|
|
|
int
|
|
flash_erase(flash_info_t *info, int s_first, int s_last)
|
|
{
|
|
int i, sector_size = info->size / info->sector_count;
|
|
|
|
#ifdef FLASH_DEBUG
|
|
printf("\nFirst %#x last %#x sector size %#x\n",
|
|
s_first, s_last, sector_size);
|
|
#endif
|
|
|
|
for (i = s_first; i <= s_last; i++) {
|
|
#ifdef FLASH_DEBUG
|
|
printf("\b\b\b\b%4d", i);
|
|
#else
|
|
puts(".");
|
|
|
|
#ifdef CONFIG_SHOW_ACTIVITY
|
|
extern void show_activity(int arg);
|
|
show_activity(3);
|
|
#endif
|
|
#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG)
|
|
WATCHDOG_RESET();
|
|
#endif
|
|
|
|
#endif
|
|
ar7240_spi_sector_erase(i * sector_size);
|
|
}
|
|
ar7240_spi_done();
|
|
printf("\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Write a buffer from memory to flash:
|
|
* 0. Assumption: Caller has already erased the appropriate sectors.
|
|
* 1. call page programming for every 256 bytes
|
|
*/
|
|
#ifdef ATH_SST_FLASH
|
|
void
|
|
ar7240_spi_flash_chip_erase(void)
|
|
{
|
|
ar7240_spi_write_enable();
|
|
ar7240_spi_bit_banger(AR7240_SPI_CMD_CHIP_ERASE);
|
|
ar7240_spi_go();
|
|
ar7240_spi_poll();
|
|
}
|
|
|
|
int
|
|
write_buff(flash_info_t *info, uchar *src, ulong dst, ulong len)
|
|
{
|
|
uint32_t val;
|
|
|
|
dst = dst - CFG_FLASH_BASE;
|
|
printf("write len: %lu dst: 0x%x src: %p\n", len, dst, src);
|
|
|
|
for (; len; len--, dst++, src++) {
|
|
ar7240_spi_write_enable(); // dont move this above 'for'
|
|
ar7240_spi_bit_banger(AR7240_SPI_CMD_PAGE_PROG);
|
|
ar7240_spi_send_addr(dst);
|
|
|
|
val = *src & 0xff;
|
|
ar7240_spi_bit_banger(val);
|
|
|
|
ar7240_spi_go();
|
|
ar7240_spi_poll();
|
|
}
|
|
/*
|
|
* Disable the Function Select
|
|
* Without this we can't read from the chip again
|
|
*/
|
|
ar7240_reg_wr(AR7240_SPI_FS, 0);
|
|
|
|
if (len) {
|
|
// how to differentiate errors ??
|
|
return ERR_PROG_ERROR;
|
|
} else {
|
|
return ERR_OK;
|
|
}
|
|
}
|
|
#else
|
|
int
|
|
write_buff(flash_info_t *info, uchar *source, ulong addr, ulong len)
|
|
{
|
|
int total = 0, len_this_lp, bytes_this_page, counter = 0;
|
|
ulong dst;
|
|
uchar *src;
|
|
|
|
#ifdef FLASH_DEBUG
|
|
printf("write addr: %x\n", addr);
|
|
#endif
|
|
addr = addr - CFG_FLASH_BASE;
|
|
|
|
while (total < len) {
|
|
src = source + total;
|
|
dst = addr + total;
|
|
bytes_this_page =
|
|
AR7240_SPI_PAGE_SIZE - (addr & AR7240_SPI_PAGE_SIZE-1);
|
|
len_this_lp =
|
|
((len - total) >
|
|
bytes_this_page) ? bytes_this_page : (len - total);
|
|
ar7240_spi_write_page(dst, src, len_this_lp);
|
|
total += len_this_lp;
|
|
if(counter>=255)
|
|
{
|
|
puts(".");
|
|
counter = 0;
|
|
#ifdef CONFIG_SHOW_ACTIVITY
|
|
extern void show_activity(int arg);
|
|
show_activity(3);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
counter++;
|
|
}
|
|
}
|
|
|
|
ar7240_spi_done();
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
ar7240_spi_write_enable()
|
|
{
|
|
ar7240_reg_wr_nf(AR7240_SPI_FS, 1);
|
|
ar7240_reg_wr_nf(AR7240_SPI_WRITE, AR7240_SPI_CS_DIS);
|
|
ar7240_spi_bit_banger(AR7240_SPI_CMD_WREN);
|
|
ar7240_spi_go();
|
|
}
|
|
|
|
static void
|
|
ar7240_spi_poll()
|
|
{
|
|
int rd;
|
|
|
|
do {
|
|
ar7240_reg_wr_nf(AR7240_SPI_WRITE, AR7240_SPI_CS_DIS);
|
|
ar7240_spi_bit_banger(AR7240_SPI_CMD_RD_STATUS);
|
|
ar7240_spi_delay_8();
|
|
rd = (ar7240_reg_rd(AR7240_SPI_RD_STATUS) & 1);
|
|
} while (rd);
|
|
}
|
|
|
|
#if !defined(ATH_SST_FLASH)
|
|
static void
|
|
ar7240_spi_write_page(uint32_t addr, uint8_t *data, int len)
|
|
{
|
|
int i;
|
|
uint8_t ch;
|
|
|
|
display(0x77);
|
|
ar7240_spi_write_enable();
|
|
ar7240_spi_bit_banger(AR7240_SPI_CMD_PAGE_PROG);
|
|
ar7240_spi_send_addr(addr);
|
|
|
|
for (i = 0; i < len; i++) {
|
|
ch = *(data + i);
|
|
ar7240_spi_bit_banger(ch);
|
|
}
|
|
|
|
ar7240_spi_go();
|
|
display(0x66);
|
|
ar7240_spi_poll();
|
|
display(0x6d);
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
ar7240_spi_sector_erase(uint32_t addr)
|
|
{
|
|
ar7240_spi_write_enable();
|
|
ar7240_spi_bit_banger(AR7240_SPI_CMD_SECTOR_ERASE);
|
|
ar7240_spi_send_addr(addr);
|
|
ar7240_spi_go();
|
|
display(0x7d);
|
|
ar7240_spi_poll();
|
|
}
|