1
0
This repository has been archived on 2024-07-22. You can view files and clone it, but cannot push or open issues or pull requests.
TP-Link_Archer-XR500v/EN7526G_3.18Kernel_SDK/bootrom/bootram/flash/spiflash.c
2024-07-22 01:58:46 -03:00

1303 lines
32 KiB
C
Executable File

#include <asm/types.h>
#include <asm/io.h>
#include <asm/tc3162.h>
#include "spiflash.h"
#ifdef SPI_DRAM_TEST_CMD
#include "dramtest.h"
#endif
/* debugging */
/* #define SPIFLASH_DEBUG */
#define TC_SOC
static char module_name[] = "spiflash";
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#define FALSE 0
#define TRUE 1
#define ID_MASK 0xffff
#define MANUFACTURER_ID(id) ((id >> 16) & ID_MASK)
#define DEVICE_ID(id) (id & ID_MASK)
#define SIZE_64KiB 0x10000
#define SIZE_64MiB 0x4000000
#define SIZE_32MiB 0x2000000
#define SIZE_16MiB 0x1000000
#define SIZE_8MiB 0x800000
#define SIZE_4MiB 0x400000
#define SIZE_2MiB 0x200000
/* Manufacturers */
#define MANUFACTURER_ST 0x0020
#define MANUFACTURER_WINBOND 0x00ef
#define MANUFACTURER_SST 0x00bf
#define MANUFACTURER_MXIC 0x00c2
#define MANUFACTURER_SPANSION 0x0001
#define MANUFACTURER_EON 0x001c
#define MANUFACTURER_NUMONYX 0x0020
#define MANUFACTURER_GIGADEVICE 0x00c8
/* ST */
#define M25P16 0x2015
#define M25P32 0x2016
#define M25P64 0x2017
/* Winbond */
#define W25X16 0x3015
#define W25X32 0x3016
#define W25X64 0x3017
#define W25X128 0x3018
#define W25Q16 0x4015
#define W25Q32 0x4016
#define W25Q64 0x4017
#define W25Q128 0x4018
/* SST */
#define SST25VF032B 0x254a
/* MXIC */
#define MX25L3205D 0x2016
#define MX25L6405D 0x2017
#define MX25L12805D 0x2018
#define MX25L25635E 0x2019
/* SPANSION */
#define S25FL016A 0x0214
#define S25FL032A 0x0215
#define S25FL064A 0x0216
#define S25FL128P 0x2018
#define N25Q064 0xba17
#define S25FL016K 0x4015
/* EON */
#define EN25Q64 0x3017
/* GIGA */
#define GD25Q64 0x4017
#define GD25Q32 0x4016
#define GD25Q128 0x4018
/* define SPI mode */
#define SPI_STD_RD 0x0
#define SPI_STD_FAST_RD 0x1
#define SPI_FAST_RD_DUAL_O 0x2
#define SPI_FAST_RD_DUAL_IO 0x3
#define SPI_FAST_RD_QUAD_O 0x4
#define SPI_FAST_RD_QUAD_IO 0x5
#define SPI_BURST_RD_QUAD_IO 0x6
#ifdef TC_SOC
#define SPI_REG_BASE 0xbfbc0000
#define SPI_REG_MASTER 0xbfbc0028
#define SPI_REG_MOREBUF 0xbfbc002c
#define SPI_REG_SPACE_CR 0xbfbc003c
#define SPI_FLASH_DATA2 0x0C
#define SPI_FLASH_DATA3 0x10
#define SPI_FLASH_DATA4 0x14
#define SPI_FLASH_DATA5 0x18
#define SPI_FLASH_DATA6 0x1C
#define SPI_FLASH_DATA7 0x20
#define SPI_FLASH_DATA8 0x24
#endif
#ifndef CONFIG_DUAL_IMAGE
struct spi_flash_info {
const u16 mfr_id;
const u16 dev_id;
const u16 extra_id;
const char *name;
const int DeviceSize;
const int EraseSize;
int mode;
};
#endif
#define printf prom_printf
struct mtd_info {
unsigned long offset; /* Offset within the memory */
unsigned long size; /* Total size of the MTD */
unsigned long erasesize;
};
static __u32 spiflash_regread32(int reg);
static void spiflash_regwrite32(int reg, __u32 data);
static __u32 spiflash_sendcmd (int op);
int spiflash_init (unsigned long rom_base);
void spiflash_exit (void);
static int spiflash_probe (void);
static int spiflash_erase_internal (struct mtd_info *mtd,unsigned long addr, unsigned long len);
static int spiflash_read_internal (struct mtd_info *mtd, unsigned long from,unsigned long len,unsigned long *retlen,unsigned char *buf);
static int spiflash_write_internal (struct mtd_info *mtd,unsigned long to,unsigned long len,unsigned long *retlen,const unsigned char *buf);
/* Mapping of generic opcodes to STM serial flash opcodes */
struct opcodes {
__u16 code;
__s8 tx_cnt;
__s8 rx_cnt;
} stm_opcodes[] = {
{STM_OP_WR_ENABLE, 1, 0},
{STM_OP_WR_DISABLE, 1, 0},
{STM_OP_RD_STATUS, 1, 1},
{STM_OP_WR_STATUS, 2, 0},
{STM_OP_RD_DATA, 4, 4},
{STM_OP_FAST_RD_DATA, 1, 0},
{STM_OP_PAGE_PGRM, 8, 0},
{STM_OP_SECTOR_ERASE, 4, 0},
{STM_OP_BULK_ERASE, 1, 0},
{STM_OP_DEEP_PWRDOWN, 1, 0},
{STM_OP_RD_SIG, 4, 1},
{STM_OP_RD_ID, 1, 3},
{STM_OP_RD_STATUS2, 1, 1},
{STM_OP_HPM, 1, 0},
{STM_OP_EN4B, 1, 0}
};
struct mtd_info mtd;
unsigned long spidbg = 0;
static __u8 byte_program_mode = 0;
//#ifdef CONFIG_DUAL_IMAGE
#if 0
static struct spi_flash_info flash_tables[];
static unsigned char flash_index;
struct spi_flash_info
getFlashTableEntry(unsigned char index)
{
return flash_tables[index];
}
unsigned char
getFlashIndex(void)
{
return flash_index;
}
#endif
static __u32
spiflash_regread32(int reg)
{
volatile __u32 *addr = (__u32 *)(CR_SPI_BASE + reg);
__u32 data = *addr;
if (spidbg)
printf ("REGREAD addr = 0x%08x data= %08x\n", addr, data);
#ifdef SPIFLASH_DEBUG
printf ("REGREAD addr = 0x%08x data= %08x\n", addr, *addr);
#endif
return (data);
}
static void
spiflash_regwrite32(int reg, __u32 data)
{
volatile __u32 *addr = (__u32 *)(CR_SPI_BASE + reg);
if (spidbg)
printf ("REGWRITE addr = 0x%08x data= %08x\n", addr, data);
#ifdef SPIFLASH_DEBUG
printf ("REGWRITE addr = 0x%08x data= %08x\n", addr, data);
#endif
*addr = data;
return;
}
static __u32
spiflash_sendcmd (int op)
{
__u32 reg;
__u32 mask;
struct opcodes *ptr_opcode;
ptr_opcode = &stm_opcodes[op];
do {
reg = spiflash_regread32(SPI_FLASH_CTL);
} while (reg & SPI_CTL_BUSY);
spiflash_regwrite32(SPI_FLASH_OPCODE, ptr_opcode->code);
reg = (reg & ~SPI_CTL_TX_RX_CNT_MASK) | ptr_opcode->tx_cnt |
(ptr_opcode->rx_cnt << 4) | SPI_CTL_START;
spiflash_regwrite32(SPI_FLASH_CTL, reg);
if (ptr_opcode->rx_cnt > 0) {
do {
reg = spiflash_regread32(SPI_FLASH_CTL);
} while (reg & SPI_CTL_BUSY);
reg = (__u32) spiflash_regread32(SPI_FLASH_DATA);
switch (ptr_opcode->rx_cnt) {
case 1:
mask = 0x000000ff;
break;
case 2:
mask = 0x0000ffff;
break;
case 3:
mask = 0x00ffffff;
break;
default:
mask = 0xffffffff;
break;
}
reg &= mask;
} else {
reg = 0;
}
return reg;
}
/* Probe SPI flash device
* Function returns 0 for failure.
* and flashconfig_tbl array index for success.
*/
static u32
spiflash_read_id (void)
{
u32 flash_id;
flash_id = spiflash_sendcmd(SPI_RD_ID);
flash_id = (flash_id & 0xff) << 16 | (flash_id & 0xff00) | (flash_id >> 16) & 0xff;
return flash_id;
}
static int
spiflash_erase_oneblock (struct mtd_info *mtd, unsigned long addr, unsigned long len)
{
struct opcodes *ptr_opcode;
__u32 temp, reg;
int finished = FALSE;
/* sanity checks */
if (addr + len > mtd->size) return (-1);
#ifdef SPIFLASH_DEBUG
printf ("spiflash_eraseone_block (addr = 0x%08x, len = %d)\n", addr, len);
#endif
printf("erase addr=%x size=%x\n", addr, mtd->erasesize);
ptr_opcode = &stm_opcodes[SPI_SECTOR_ERASE];
temp = ((__u32)addr << 8) | (__u32)(ptr_opcode->code);
spiflash_sendcmd(SPI_WRITE_ENABLE);
do {
reg = spiflash_regread32(SPI_FLASH_CTL);
} while (reg & SPI_CTL_BUSY);
spiflash_regwrite32(SPI_FLASH_OPCODE, temp);
if(mtd->size >= SIZE_32MiB){
reg = ((__u32)addr & 0xff000000) |(reg & 0x00ffff00) | (ptr_opcode->tx_cnt+1) | SPI_CTL_START;
}else{
reg = (reg & ~SPI_CTL_TX_RX_CNT_MASK) | ptr_opcode->tx_cnt | SPI_CTL_START;
}
spiflash_regwrite32(SPI_FLASH_CTL, reg);
do {
reg = spiflash_sendcmd(SPI_RD_STATUS);
if (!(reg & SPI_STATUS_WIP)) {
finished = TRUE;
}
} while (!finished);
return (0);
}
static int
spiflash_erase_internal (struct mtd_info *mtd, unsigned long addr, unsigned long len)
{
int ret;
#if 0
if(addr & (mtd->erasesize - 1))
return -1;
if(len & (mtd->erasesize - 1))
return -1;
#endif
#ifdef SPIFLASH_DEBUG
printf ("spiflash_erase (addr = 0x%08x, len = %d)\n", addr, len);
#endif
if (addr & (mtd->erasesize - 1))
addr = addr - (addr & (mtd->erasesize - 1));
if (len & (mtd->erasesize - 1))
len = len - (len & (mtd->erasesize - 1)) + mtd->erasesize;
if(len + addr > mtd->size)
return -1;
while (len) {
#ifdef SPIFLASH_DEBUG
printf ("spiflash_erase (addr = 0x%08x, len = %d)\n", addr, len);
#endif
ret = spiflash_erase_oneblock(mtd, addr, len);
if (ret < 0) return ret;
addr += mtd->erasesize;
len -= mtd->erasesize;
}
return 0;
}
static int
spiflash_read_internal (struct mtd_info *mtd, unsigned long from,unsigned long len,unsigned long *retlen,unsigned char *buf)
{
unsigned char *read_addr;
#ifdef SPIFLASH_DEBUG
printf ("spiflash_read (from = 0x%08x, len = %d)\n", from, len);
#endif
/* sanity checks */
if (!len) return (0);
if (from + len > mtd->size) return (-1);
/* we always read len bytes */
*retlen = len;
memcpy(buf, (char *)((mtd->offset + from)), len);
return (0);
}
static int
spiflash_write_internal (struct mtd_info *mtd,unsigned long to,unsigned long len,unsigned long *retlen,const unsigned char *buf)
{
int done = FALSE, page_offset, bytes_left, finished;
int cnt = 0;
#ifdef TC_SOC
__u32 xact_len, spi_data[8], opcode, reg;
__u32 reg_value;
unsigned char words, bytes, finalrun, i, j;
#else
__u32 xact_len, spi_data = 0, opcode, reg;
#endif
#ifdef SPIFLASH_DEBUG
printf ("spiflash_write (to = 0x%08x, len = %d)\n", to, len);
#endif
*retlen = 0;
/* sanity checks */
if (!len) return (0);
if (to + len > mtd->size) return (-1);
printf("program from %x to %x\n", to, to+len);
opcode = stm_opcodes[SPI_PAGE_PROGRAM].code;
bytes_left = len;
while (done == FALSE) {
cnt++;
if ((cnt & 0x3ff) == 0)
printf(".");
if (byte_program_mode)
xact_len = MIN(bytes_left, sizeof(__u8));
else
#ifdef TC_SOC
xact_len = MIN(bytes_left, sizeof(__u32)<<3);
#else
xact_len = MIN(bytes_left, sizeof(__u32));
#endif
/* 32-bit writes cannot span across a page boundary
* (256 bytes). This types of writes require two page
* program operations to handle it correctly. The STM part
* will write the overflow data to the beginning of the
* current page as opposed to the subsequent page.
*/
page_offset = (to & (STM_PAGE_SIZE - 1)) + xact_len;
if (page_offset > STM_PAGE_SIZE) {
xact_len -= (page_offset - STM_PAGE_SIZE);
}
spiflash_sendcmd(SPI_WRITE_ENABLE);
do {
reg = spiflash_regread32(SPI_FLASH_CTL);
} while (reg & SPI_CTL_BUSY);
#ifdef TC_SOC
words = xact_len >> 2;
// if(!(xact_len % 4))
if(!(xact_len & 0x00000003))
words--;
bytes = 3;
// finalrun = xact_len % 4 - 1;
finalrun = (xact_len & 0x00000003) - 1;
if(finalrun == 0xFF)
finalrun = 3;
for(i = 0; i <= words; i++){
spi_data[i] = 0;//Make sure the initial value of spi_data[i] is 0
if(i == words)
bytes = finalrun;
for(j = 0; j <= bytes; j++){
spi_data[i] |= (buf[j + (i<<2)] << (j<<3));
}
}
#else
switch (xact_len) {
case 1:
spi_data = (__u8) *buf;
break;
case 2:
spi_data = (buf[1] << 8) | buf[0];
break;
case 3:
spi_data = (buf[2] << 16) | (buf[1] << 8) | buf[0];
break;
case 4:
spi_data = (buf[3] << 24) | (buf[2] << 16) |
(buf[1] << 8) | buf[0];
break;
default:
printf("spiflash_write: default case\n");
break;
}
#endif
#ifdef TC_SOC
if (!byte_program_mode){
reg_value = VPint(SPI_REG_MASTER);
// VPint(SPI_REG_MASTER) = 0x38804;//Set bit [2] to 1 to enter more buffer mode
VPint(SPI_REG_MASTER) = reg_value | (1 << 2);
// VPint(SPI_REG_MOREBUF) = 0x20000100;//Set bits [8:0] to 128 (data bit counts) and bits[29:24] to 32(comman bit counts)
/* write exact data size into flash */
if(mtd->size >= SIZE_32MiB){
VPint(SPI_REG_MOREBUF) = 0x28000000|(xact_len<<3);//Set bits [8:0] to data bit counts and bits[29:24] to 40(command bit counts)
//printf("command more %x\n",VPint(SPI_REG_MOREBUF));
}else{
VPint(SPI_REG_MOREBUF) = 0x20000000|(xact_len<<3);//Set bits [8:0] to data bit counts and bits[29:24] to 32(command bit counts)
}
}
#endif
#ifdef TC_SOC
spiflash_regwrite32(SPI_FLASH_DATA, spi_data[0]);
if (!byte_program_mode){
spiflash_regwrite32(SPI_FLASH_DATA2, spi_data[1]);
spiflash_regwrite32(SPI_FLASH_DATA3, spi_data[2]);
spiflash_regwrite32(SPI_FLASH_DATA4, spi_data[3]);
spiflash_regwrite32(SPI_FLASH_DATA5, spi_data[4]);
spiflash_regwrite32(SPI_FLASH_DATA6, spi_data[5]);
spiflash_regwrite32(SPI_FLASH_DATA7, spi_data[6]);
spiflash_regwrite32(SPI_FLASH_DATA8, spi_data[7]);
if(mtd->size >= SIZE_32MiB){
opcode = ((__u32)to);
}else{
opcode = (0x02 << 24) | ((__u32)to);
}
}
else
#else
spiflash_regwrite32(SPI_FLASH_DATA, spi_data);
#endif
opcode = (opcode & SPI_OPCODE_MASK) | ((__u32)to << 8);
spiflash_regwrite32(SPI_FLASH_OPCODE, opcode);
//size is big than 32M. use four address. shnwind.
if(mtd->size >= SIZE_32MiB){
#ifdef TC_SOC
reg = ((0x02 << 24) | (reg & 0x00ffff00)) | (xact_len + 5) | SPI_CTL_START;
#else
reg = ((__u32)to & 0xff000000 ) | (reg & 0x00ffff00) | (xact_len + 5) | SPI_CTL_START;
#endif
}else{
reg = (reg & ~SPI_CTL_TX_RX_CNT_MASK) | (xact_len + 4) | SPI_CTL_START;
}
spiflash_regwrite32(SPI_FLASH_CTL, reg);
finished = FALSE;
#ifdef TC_SOC
if (!byte_program_mode){
while(VPint(SPI_REG_BASE) & 0x10000);//Make sure the bit spi_master_busy is 0 and then continue
VPint(SPI_REG_MOREBUF) = 0x00000000;//Set the default value back
// VPint(SPI_REG_MASTER) = 0x38800;//Set the default value back
VPint(SPI_REG_MASTER) = reg_value;
}
#endif
do {
//delay1ms(1);
reg = spiflash_sendcmd(SPI_RD_STATUS);
if (!(reg & SPI_STATUS_WIP)) {
finished = TRUE;
}
} while (!finished);
bytes_left -= xact_len;
to += xact_len;
buf += xact_len;
*retlen += xact_len;
if (bytes_left == 0) {
done = TRUE;
}
}
printf("\n");
VPint(CR_SPI_CR)=1;
return (0);
}
static struct spi_flash_info flash_tables[] = {
{
mfr_id: MANUFACTURER_ST,
dev_id: M25P64,
name: "ST M25P64",
DeviceSize: SIZE_8MiB,
EraseSize: SIZE_64KiB,
mode: SPI_STD_FAST_RD,
},
{
mfr_id: MANUFACTURER_ST,
dev_id: M25P32,
name: "ST M25P32",
DeviceSize: SIZE_4MiB,
EraseSize: SIZE_64KiB,
mode: SPI_STD_FAST_RD,
},
{
mfr_id: MANUFACTURER_ST,
dev_id: M25P16,
name: "ST M25P16",
DeviceSize: SIZE_2MiB,
EraseSize: SIZE_64KiB,
mode: SPI_STD_FAST_RD,
},
{
mfr_id: MANUFACTURER_WINBOND,
dev_id: W25X128,
name: "Winbond W25X128",
DeviceSize: SIZE_16MiB,
EraseSize: SIZE_64KiB,
mode: SPI_FAST_RD_DUAL_O,
},
{
mfr_id: MANUFACTURER_WINBOND,
dev_id: W25X64,
name: "Winbond W25X64",
DeviceSize: SIZE_8MiB,
EraseSize: SIZE_64KiB,
mode: SPI_FAST_RD_DUAL_O,
},
{
mfr_id: MANUFACTURER_WINBOND,
dev_id: W25X32,
name: "Winbond W25X32",
DeviceSize: SIZE_4MiB,
EraseSize: SIZE_64KiB,
mode: SPI_FAST_RD_DUAL_O,
},
{
mfr_id: MANUFACTURER_WINBOND,
dev_id: W25X16,
name: "Winbond W25X16",
DeviceSize: SIZE_2MiB,
EraseSize: SIZE_64KiB,
mode: SPI_FAST_RD_DUAL_O,
},
{
mfr_id: MANUFACTURER_WINBOND,
dev_id: W25Q128,
name: "Winbond W25Q128",
DeviceSize: SIZE_16MiB,
EraseSize: SIZE_64KiB,
mode: SPI_FAST_RD_QUAD_IO,
},
{
mfr_id: MANUFACTURER_WINBOND,
dev_id: W25Q64,
name: "Winbond W25Q64",
DeviceSize: SIZE_8MiB,
EraseSize: SIZE_64KiB,
mode: SPI_FAST_RD_QUAD_IO,
},
{
mfr_id: MANUFACTURER_WINBOND,
dev_id: W25Q32,
name: "Winbond W25Q32",
DeviceSize: SIZE_4MiB,
EraseSize: SIZE_64KiB,
mode: SPI_FAST_RD_QUAD_IO,
},
{
mfr_id: MANUFACTURER_WINBOND,
dev_id: W25Q16,
name: "Winbond W25Q16",
DeviceSize: SIZE_2MiB,
EraseSize: SIZE_64KiB,
mode: SPI_FAST_RD_QUAD_IO,
},
{
mfr_id: MANUFACTURER_SST,
dev_id: SST25VF032B,
name: "SST 25VF032B",
DeviceSize: SIZE_4MiB,
EraseSize: SIZE_64KiB,
mode: SPI_STD_FAST_RD,
},
{
mfr_id: MANUFACTURER_MXIC,
dev_id: MX25L3205D,
name: "MX25L3205D",
DeviceSize: SIZE_4MiB,
EraseSize: SIZE_64KiB,
mode: SPI_STD_FAST_RD,
},
{
mfr_id: MANUFACTURER_MXIC,
dev_id: MX25L6405D,
name: "MX25L6405D",
DeviceSize: SIZE_8MiB,
EraseSize: SIZE_64KiB,
mode: SPI_STD_FAST_RD,
},
{
mfr_id: MANUFACTURER_MXIC,
dev_id: MX25L12805D,
name: "MX25L12805D",
DeviceSize: SIZE_16MiB,
EraseSize: SIZE_64KiB,
mode: SPI_STD_FAST_RD,
},
{
mfr_id: MANUFACTURER_MXIC,
dev_id: MX25L25635E,
name: "MX25L25635DE",
DeviceSize: SIZE_32MiB,
EraseSize: SIZE_64KiB,
mode: SPI_FAST_RD_QUAD_IO,
},
{
mfr_id: MANUFACTURER_SPANSION,
dev_id: S25FL064A,
name: "S25FL064A",
DeviceSize: SIZE_8MiB,
EraseSize: SIZE_64KiB,
mode: SPI_STD_FAST_RD,
},
{
mfr_id: MANUFACTURER_SPANSION,
dev_id: S25FL032A,
name: "S25FL032A",
DeviceSize: SIZE_4MiB,
EraseSize: SIZE_64KiB,
mode: SPI_STD_FAST_RD,
},
{
mfr_id: MANUFACTURER_SPANSION,
dev_id: S25FL016A,
name: "S25FL016A",
DeviceSize: SIZE_2MiB,
EraseSize: SIZE_64KiB,
mode: SPI_STD_FAST_RD,
},
{
mfr_id: MANUFACTURER_SPANSION,
dev_id: S25FL128P,
name: "S25FL128P",
DeviceSize: SIZE_16MiB,
EraseSize: SIZE_64KiB,
mode: SPI_STD_FAST_RD,
},
{
mfr_id: MANUFACTURER_EON,
dev_id: EN25Q64,
name: "EN25Q64",
DeviceSize: SIZE_8MiB,
EraseSize: SIZE_64KiB,
mode: SPI_FAST_RD_QUAD_IO,
},
{
mfr_id: MANUFACTURER_NUMONYX,
dev_id: N25Q064,
name: "N25Q064",
DeviceSize: SIZE_8MiB,
EraseSize: SIZE_64KiB,
mode: SPI_STD_RD,
},
{
mfr_id: MANUFACTURER_SPANSION,
dev_id: S25FL016K,
name: "S25FL016K",
DeviceSize: SIZE_2MiB,
EraseSize: SIZE_64KiB,
mode: SPI_FAST_RD_DUAL_IO,
},
{
mfr_id: MANUFACTURER_GIGADEVICE,
dev_id: GD25Q64,
name: "GD25Q64",
DeviceSize: SIZE_8MiB,
EraseSize: SIZE_64KiB,
mode: SPI_FAST_RD_DUAL_IO,
},
{
mfr_id: MANUFACTURER_GIGADEVICE,
dev_id: GD25Q32,
name: "GD25Q32",
DeviceSize: SIZE_4MiB,
EraseSize: SIZE_64KiB,
mode: SPI_FAST_RD_DUAL_IO,
},
{
mfr_id: MANUFACTURER_GIGADEVICE,
dev_id: GD25Q128,
name: "GD25Q128",
DeviceSize: SIZE_16MiB,
EraseSize: SIZE_64KiB,
mode: SPI_FAST_RD_QUAD_IO,
},
};
static void spiflash_unlock(void)
{
struct opcodes *ptr_opcode;
__u32 reg;
int finished = FALSE;
__u32 status_reg;
status_reg = spiflash_sendcmd(SPI_RD_STATUS);
ptr_opcode = &stm_opcodes[SPI_WR_STATUS];
spiflash_sendcmd(SPI_WRITE_ENABLE);
do {
reg = spiflash_regread32(SPI_FLASH_CTL);
} while (reg & SPI_CTL_BUSY);
status_reg &= ~0x1c;
spiflash_regwrite32(SPI_FLASH_DATA, status_reg);
spiflash_regwrite32(SPI_FLASH_OPCODE, ptr_opcode->code);
reg = (reg & ~SPI_CTL_TX_RX_CNT_MASK) | ptr_opcode->tx_cnt | SPI_CTL_START;
spiflash_regwrite32(SPI_FLASH_CTL, reg);
}
static void spiflash_quadenable(void)
{
struct opcodes *ptr_opcode;
__u32 reg;
__u32 reg2;
int finished = FALSE;
__u32 status_reg;
__u32 status_reg2;
reg = spiflash_regread32(SPI_FLASH_MM);
reg |= 0x2;
spiflash_regwrite32(SPI_FLASH_MM, reg);
status_reg = spiflash_sendcmd(SPI_RD_STATUS);
status_reg2 = spiflash_sendcmd(SPI_RD_STATUS2);
ptr_opcode = &stm_opcodes[SPI_WR_STATUS];
spiflash_sendcmd(SPI_WRITE_ENABLE);
do {
reg = spiflash_regread32(SPI_FLASH_CTL);
} while (reg & SPI_CTL_BUSY);
status_reg2 |= STM_STATUS_QE;
spiflash_regwrite32(SPI_FLASH_DATA, status_reg);
reg2 = ((status_reg & 0xff)<<24) | ((status_reg2 & 0xff)<<16) | ptr_opcode->code;
spiflash_regwrite32(SPI_FLASH_OPCODE, reg2);
reg = (reg & ~SPI_CTL_TX_RX_CNT_MASK) | (ptr_opcode->tx_cnt+1) | SPI_CTL_START;
spiflash_regwrite32(SPI_FLASH_CTL, reg);
}
void spiflash_qe(int hpm)
{
if (hpm)
spiflash_sendcmd(SPI_HPM_ENABLE);
else
spiflash_sendcmd(SPI_RD_SIG);
spiflash_quadenable();
}
void spiflash_rdst(void)
{
__u32 status_reg;
__u32 status_reg2;
spidbg = 1;
status_reg = spiflash_sendcmd(SPI_RD_STATUS);
printf ("status_reg =%08x\n", status_reg);
status_reg2 = spiflash_sendcmd(SPI_RD_STATUS2);
printf ("status_reg2=%08x\n", status_reg2);
spidbg = 0;
}
static int spiflash_probe(void)
{
int i;
unsigned long flash_id;
flash_id = spiflash_read_id();
for (i=0; i < (sizeof(flash_tables)/sizeof(struct spi_flash_info)); i++) {
if ((MANUFACTURER_ID(flash_id) == flash_tables[i].mfr_id) &&
(DEVICE_ID(flash_id) == flash_tables[i].dev_id)) {
if (MANUFACTURER_ID(flash_id) == MANUFACTURER_SST) {
spiflash_unlock();
byte_program_mode = 1;
}
return i;
}
}
return -1;
}
int spiflash_erase(unsigned long addr, unsigned long size)
{
return spiflash_erase_internal(&mtd, addr, size);
}
int spiflash_read(unsigned long from,
unsigned long len, unsigned long *retlen, unsigned char *buf)
{
return spiflash_read_internal(&mtd, from, len, retlen, buf);
}
int spiflash_write(unsigned long to,
unsigned long len, unsigned long *retlen, const unsigned char *buf)
{
return spiflash_write_internal(&mtd, to, len, retlen, buf);
}
#define SPI_ADDR_SIZE_16BITS 1
#define SPI_ADDR_SIZE_24BITS 2
#ifdef TCSUPPORT_DUAL_IMAGE_ENHANCE
int offset = 0;
#endif
int
spiflash_init (unsigned long rom_base)
{
int index;
int spi_read_bytes;
/* turn off flash write protect */
//tc_outl(CR_GPIO_DATA, tc_inl(CR_GPIO_DATA) | (1 << 5));
mtd.offset = rom_base;
mtd.size = 0;
index = spiflash_probe();
if (index < 0) {
printf ("%s: Found no SPI flash device\n", module_name);
return -1;
}
if(isRT63365 || isMT751020){
//printf("SPI value %x\n",(VPint(0xbfb0008c) & (1<<27)));
if(VPint(0xbfb0008c) & (1<<27)){ //4 byte mode
VPint(SPI_REG_BASE) |= ((1<<19)|(1<<20)); //set addr support spi_addr ext.
}else{
if(flash_tables[index].DeviceSize >= SIZE_32MiB){ //3 to 4 byte mode
VPint(SPI_REG_BASE) |= ((1<<19)|(1<<20));//set addr support spi_addr ext.
VPint(SPI_REG_BASE + 0x30) &= ~((1<<8)|(1<<9)); //set support 25 bits.
spiflash_sendcmd(SPI_EN4B); //send En4B command
}
}
}else{
if(flash_tables[index].DeviceSize >= SIZE_32MiB){
return -1;
}
}
//#ifdef CONFIG_DUAL_IMAGE
#if 0
flash_index = index;
#endif
#ifdef TC3262
/* TC3182LDV doesn't support quad mode */
if (isTC3182 || isRT65168 || isRT63165 || isRT63365 || isMT751020) {
#else
/* Note: TC3162U doesn't support quad mode */
if (isTC3162U || isRT63260 || isTC3162L5P5) { //TC3162L2M does not support Quad Mode neither, added by fredli, bug#10664
#endif
switch (flash_tables[index].mode) {
case SPI_FAST_RD_QUAD_O:
flash_tables[index].mode = SPI_FAST_RD_DUAL_O;
break;
case SPI_FAST_RD_QUAD_IO:
flash_tables[index].mode = SPI_FAST_RD_DUAL_IO;
break;
case SPI_BURST_RD_QUAD_IO:
flash_tables[index].mode = SPI_FAST_RD_DUAL_IO;
break;
}
}
/* set spi serial mode */
switch (flash_tables[index].mode) {
case SPI_STD_RD:
spi_read_bytes = 4;
break;
case SPI_STD_FAST_RD:
case SPI_FAST_RD_DUAL_O:
case SPI_FAST_RD_DUAL_IO:
case SPI_FAST_RD_QUAD_O:
spi_read_bytes = 5;
break;
case SPI_FAST_RD_QUAD_IO:
spi_read_bytes = 7;
break;
case SPI_BURST_RD_QUAD_IO:
spi_read_bytes = 5;
break;
default:
spi_read_bytes = 4;
break;
}
if(flash_tables[index].DeviceSize >= SIZE_32MiB){
spi_read_bytes++;
}
switch (flash_tables[index].mode) {
case SPI_FAST_RD_QUAD_O:
case SPI_FAST_RD_QUAD_IO:
case SPI_BURST_RD_QUAD_IO:
spiflash_quadenable();
printf("SPI Flash Quad Enable\r\n");
break;
}
tc_outl(CR_SPI_QUECTL, (tc_inl(CR_SPI_QUECTL) & 0xffffff00) | (spi_read_bytes<<4) | (flash_tables[index].mode));
#ifdef TC3262
/* don't adjust TC3182 or TC3262's spi clock */
if (isRT63365){
/*63368 Flash space clk = 233/(5+2) Mhz, 63365 Flash space clk = 166/(4+2) Mhz*/
if (isRT63368)
VPint(SPI_REG_SPACE_CR) = (VPint(SPI_REG_SPACE_CR) & 0xfffffff0) | (0x5);
else
VPint(SPI_REG_SPACE_CR) = (VPint(SPI_REG_SPACE_CR) & 0xfffffff0) | (0x4);
}else if (isRT65168 || isTC3182){
/*3182 65168 Flash space clk = 102/(1+2) Mhz*/
VPint(SPI_REG_SPACE_CR) = (VPint(SPI_REG_SPACE_CR) & 0xfffffff0) | (0x1);
}
#else
/* Set Register Space SPI clock frequency is hclk/5 */
VPint(SPI_REG_MASTER) = (VPint(SPI_REG_MASTER) & 0xf000ffff) | (0x3 << 16);
/* Set Flash Space SPI clock frequency is hclk/5 */
if (isTC3162L5P5)
VPint(SPI_REG_SPACE_CR) = (VPint(SPI_REG_SPACE_CR) & 0xfffff000) | 0x3;
#endif
printf("Found SPI Flash %dMiB %s at 0x%x\r\n",
(flash_tables[index].DeviceSize)/(1024*1024), flash_tables[index].name, rom_base);
mtd.size = flash_tables[index].DeviceSize;
mtd.erasesize = flash_tables[index].EraseSize;
#ifdef TCSUPPORT_DUAL_IMAGE_ENHANCE
#ifdef TCSUPPORT_DUAL_IMAGE_8M
offset = 0x3e0000;
#else
offset = flash_tables[index].DeviceSize/2;
#endif
#endif
return 0;
}
#ifdef SPI_DRAM_TEST_CMD
unsigned long spiflash_sizeGet(void)
{
int index;
index = spiflash_probe();
if (index < 0) {
printf ("%s: Found no SPI flash device\n", module_name);
return -1;
}
return flash_tables[index].DeviceSize;
}
int spiflash_cmp(void *dstAddr, void *srcAddr, unsigned long size, unsigned char rByte)
{
unsigned char *dstAddr1;
unsigned char *srcAddr1;
unsigned short *dstAddr2;
unsigned short *srcAddr2;
unsigned long *dstAddr4;
unsigned long *srcAddr4;
if((dstAddr == 0) || (dstAddr == 0))
{
prom_printf("spiflash_cmp: input data ERROR!\n");
return -1;
}
switch(rByte)
{
case 4:
dstAddr4 = (unsigned long*)dstAddr;
srcAddr4 = (unsigned long*)srcAddr;
prom_printf("spiflash_cmp: dstAddr=0x%x srcAddr=0x%x read byte=%d size=0x%x\n", dstAddr4, srcAddr4, rByte, size);
size = size >> 2;
break;
case 2:
dstAddr2 = (unsigned short*)dstAddr;
srcAddr2 = (unsigned short*)srcAddr;
prom_printf("spiflash_cmp: dstAddr=0x%x srcAddr=0x%x read byte=%d size=0x%x\n", dstAddr2, srcAddr2, rByte, size);
size = size >> 1;
break;
case 1:
dstAddr1 = (unsigned char*)dstAddr;
srcAddr1 = (unsigned char*)srcAddr;
prom_printf("spiflash_cmp: dstAddr=0x%x srcAddr=0x%x read byte=%d size=0x%x\n", dstAddr1, srcAddr1, rByte, size);
break;
default:
prom_printf("spiflash_cmp: ERROR! read byte=%d \n", rByte);
return -1;
}
/* compare flash with dram content */
while(size)
{
switch(rByte)
{
case 4:
if(*dstAddr4 != *srcAddr4)
{
prom_printf("spiflash_cmp ERROR! dstAddr=0x%x result=0x%X srcAddr=0x%X result=0x%X byte=%d\n", dstAddr4, *dstAddr4, srcAddr4, *srcAddr4, rByte);
return -1;
}
dstAddr4++;
srcAddr4++;
break;
case 2:
if(*dstAddr2 != *srcAddr2)
{
prom_printf("spiflash_cmp ERROR! dstAddr=0x%x result=0x%X srcAddr=0x%X result=0x%X byte=%d\n", dstAddr2, *dstAddr2, srcAddr2, *srcAddr2, rByte);
return -1;
}
dstAddr2++;
srcAddr2++;
break;
case 1:
if(*dstAddr1 != *srcAddr1)
{
prom_printf("spiflash_cmp ERROR! dstAdd4=0x%x result=0x%X srcAddr=0x%X result=0x%X byte=%d\n", dstAddr1, *dstAddr1, srcAddr1, *srcAddr1, rByte);
return -1;
}
dstAddr1++;
srcAddr1++;
break;
default:
prom_printf("spiflash_cmp: ERROR! read byte=%d \n", rByte);
return -1;
}
size--;
}
return 0;
}
int spiFlashTest(spiFlashTest_info_t *info, int isInputPat)
{
#define DEF_DRAM_ADDR 0x80020000
int ret = 0;
unsigned char defWByte = 1;
unsigned char defRByte[] = {1, 2 , 4 , 0};
int i,j;
unsigned char defPat[4] = {0x5a, 0xa5, 0x00, 0xff};
unsigned long retlen;
unsigned long testSize;
unsigned long flashBaseAddrBlock1 = 0;
unsigned long flashBaseAddrBlock2 = 0;
unsigned long flashVirAddr[2] = {0};
unsigned long addr;
if(info == 0)
{
prom_printf("spiFlashTest: ERROR! no test info \n");
return -1;
}
if(isMT751020){
flashBaseAddrBlock1 = 0xbc000000;
}else{
flashBaseAddrBlock1 = 0xb0000000;
}
flashBaseAddrBlock2 = 0xbfc00000;
flashVirAddr[0] = flashBaseAddrBlock1;
flashVirAddr[1] = flashBaseAddrBlock2;
/* the max size of block start with 0xbfc0000 is 4MB */
testSize = info->size;
if(info->startAddr + info->size > SIZE_4MiB)
{
testSize = SIZE_4MiB - info->startAddr;
}
if(isInputPat) /* use the input pattern and the default write byte is 1 */
{
dram_pat_set(DEF_DRAM_ADDR, info->size, info->pattern, ORI_PAT, defWByte);
}
else /* use the default patterns, incremental pattern */
{
dram_pat_set(DEF_DRAM_ADDR, info->size, 0, INCR_PAT, defWByte);
}
/* erase and write dram contents to flash and then compare them by 1-byte-read */
prom_printf("Write to flash from %X to %X with %X bytes\n", info->startAddr, DEF_DRAM_ADDR, info->size);
flash_erase(info->startAddr, info->size);
flash_write(info->startAddr, info->size, &retlen, DEF_DRAM_ADDR);
for(j=0; defRByte[j]!=0; j++)
{
ret += spiflash_cmp(flashBaseAddrBlock1 + info->startAddr, DEF_DRAM_ADDR, info->size, defRByte[j]);
ret += spiflash_cmp(flashBaseAddrBlock2 + info->startAddr, DEF_DRAM_ADDR, testSize, defRByte[j]);
}
prom_printf("\n");
/* use the default patterns, "anti-incremental patten, 0x5a, 0xa5, 0x0, 0xff" */
if(isInputPat == 0)
{
dram_pat_set(DEF_DRAM_ADDR, info->size, 0, ANTI_INCR_PAT, defWByte);
prom_printf("Write to flash from %X to %X with %X bytes\n", info->startAddr, DEF_DRAM_ADDR, info->size);
flash_erase(info->startAddr, info->size);
flash_write(info->startAddr, info->size, &retlen, DEF_DRAM_ADDR);
ret += spiflash_cmp(flashBaseAddrBlock1 + info->startAddr, DEF_DRAM_ADDR, info->size, 1);
ret += spiflash_cmp(flashBaseAddrBlock2 + info->startAddr, DEF_DRAM_ADDR, testSize, 1);
prom_printf("\n");
for(i=0; i<4; i++)
{
dram_pat_set(DEF_DRAM_ADDR, info->size, defPat[i], ORI_PAT, defWByte);
prom_printf("Write to flash from %X to %X with %X bytes\n", info->startAddr, DEF_DRAM_ADDR, info->size);
flash_erase(info->startAddr, info->size);
flash_write(info->startAddr, info->size, &retlen, DEF_DRAM_ADDR);
ret += spiflash_cmp(flashBaseAddrBlock1 + info->startAddr, DEF_DRAM_ADDR, info->size, 1);
ret += spiflash_cmp(flashBaseAddrBlock2 + info->startAddr, DEF_DRAM_ADDR, testSize, 1);
prom_printf("\n");
}
}
/* copy the content in flash 0~FLASH_START_TEST to dram and compare them */
prom_printf("TEST flash 0x0~0x%x\n\n",FLASH_START_TEST);
for(i=0; i<2; i++)
{
addr = flashVirAddr[i];
memcpy4(DEF_DRAM_ADDR, addr, FLASH_START_TEST);
for(j=0; defRByte[j]!=0; j++)
{
ret += spiflash_cmp(addr, DEF_DRAM_ADDR, FLASH_START_TEST, defRByte[j]);
}
}
if(ret != 0)
{
prom_printf("#flashTest FAIL!\n\n");
return -1;
}
else
{
prom_printf("#flashTest SUCCESS!\n\n");
return 0;
}
}
int gdmaTestFlash(gdmaTest_info_t *info)
{
#define DEF_DRAM_ADDR 0xa0020000
#define DRAM_BASE_ADDR_KSEG1 0xa0000000
#define TEST_LENGTH 0x10000
int ret = 0;
int i;
unsigned char defPat[4] = {0x5a, 0xa5, 0x00, 0xff};
unsigned long retlen;
unsigned long flashAddr;
unsigned long virFlashAddr;
unsigned long virDramAddr;
unsigned long flashBaseAddrBlock1 = 0;
if(info == 0)
{
prom_printf("gdmaTestFlash: ERROR! no test info \n");
return -1;
}
if(isMT751020){
flashBaseAddrBlock1 = 0xbc000000;
}else{
flashBaseAddrBlock1 = 0xb0000000;
}
flashAddr = info->sa - PHYSADDR(flashBaseAddrBlock1);
virFlashAddr = flashAddr + flashBaseAddrBlock1;
virDramAddr = flashAddr + DRAM_BASE_ADDR_KSEG1;
/* use the default patterns, incremental pattern */
dram_pat_set(DEF_DRAM_ADDR, TEST_LENGTH, 0, INCR_PAT, 1);
/* erase and write dram contents to flash */
prom_printf("Write to flash from %X to %X with %X bytes\n", DEF_DRAM_ADDR, flashAddr, TEST_LENGTH);
flash_erase(flashAddr, TEST_LENGTH);
flash_write(flashAddr, TEST_LENGTH, &retlen, DEF_DRAM_ADDR);
gdmaTestCpy(info->sa, info->da, info->len, info->burst_size, info->wswap);
ret += memcmp((const unsigned char *) virDramAddr, virFlashAddr, info->len);
/* use the default patterns, "anti-incremental patten, 0x5a, 0xa5, 0x0, 0xff" */
dram_pat_set(DEF_DRAM_ADDR, TEST_LENGTH, 0, ANTI_INCR_PAT, 1);
prom_printf("Write to flash from %X to %X with %X bytes\n", flashAddr, DEF_DRAM_ADDR, TEST_LENGTH);
flash_erase(flashAddr, TEST_LENGTH);
flash_write(flashAddr, TEST_LENGTH, &retlen, DEF_DRAM_ADDR);
gdmaTestCpy(info->sa, info->da, info->len, info->burst_size, info->wswap);
ret += memcmp(virDramAddr, virFlashAddr, info->len);
for(i=0; i<4; i++)
{
dram_pat_set(DEF_DRAM_ADDR, TEST_LENGTH, defPat[i], ORI_PAT, 1);
prom_printf("Write to flash from %X to %X with %X bytes\n", flashAddr, DEF_DRAM_ADDR, TEST_LENGTH);
flash_erase(flashAddr, TEST_LENGTH);
flash_write(flashAddr, TEST_LENGTH, &retlen, DEF_DRAM_ADDR);
gdmaTestCpy(info->sa, info->da, info->len, info->burst_size, info->wswap);
ret += memcmp(virDramAddr, virFlashAddr, info->len);
}
if(ret != 0)
{
prom_printf("gdmaTestFlash FAIL!\n\n");
return -1;
}
else
{
prom_printf("gdmaTestFlash SUCCESS!\n\n");
return 0;
}
}
#endif