GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)

This commit is contained in:
lai
2026-09-06 03:52:57 +08:00
commit b1928b41c0
21813 changed files with 4413081 additions and 0 deletions
+21
View File
@@ -0,0 +1,21 @@
menu "Flash Controller"
config FMC
bool "Enable FMC - Flash Memory Controller"
depends on TARGET_XMFALCON || TARGET_XMORCA
help
lotus Flash Memory Controller support SPI Nor SPI Nand often used on
embedded chip. This option will provide the generic support for FMC drivers to register.
source "lotus/drivers/mtd/nand/Kconfig"
source "lotus/drivers/mtd/spi_nor/Kconfig"
config SHOW_FLASH_SPEED
bool "Show Flash R/W Speed in flash commands"
default n
depends on FMC_SPI_NAND || FMC_SPI_NOR || FMC_NAND
help
Support for testing flash speed.
endmenu
@@ -0,0 +1,6 @@
obj-$(CONFIG_FMC) += fmc_common.o
obj-$(CONFIG_TARGET_XMFALCON) += fmc_xmfalcon.o
obj-$(CONFIG_TARGET_XMORCA) += fmc_xmorca.o
obj-y += nand/
obj-y += spi_nor/
@@ -0,0 +1,131 @@
/*
* Copyright (c) LOTUS. All rights reserved.
*/
/*****************************************************************************/
#include <linux/lotus/fmc_common.h>
#include <common.h>
#include <asm/io.h>
#include <errno.h>
#define BUFF_LEN 20
/*****************************************************************************/
static unsigned char fmc_current_dev_type = FLASH_TYPE_DEFAULT;
static unsigned char fmc_boot_dev_type = FLASH_TYPE_DEFAULT;
/*****************************************************************************/
unsigned char fmc_ip_user;
unsigned char fmc_cs_user[CONFIG_FMC_MAX_CS_NUM];
/*****************************************************************************/
void *get_fmc_ip(void)
{
return &fmc_ip_user;
}
/*****************************************************************************/
unsigned char *get_cs_number(unsigned char cs)
{
return fmc_cs_user + cs;
}
/*****************************************************************************/
int fmc_ip_ver_check(void)
{
unsigned int fmc_ip_ver;
printf("Check Flash Memory Controller v200 ...");
fmc_ip_ver = readl(CONFIG_FMC_REG_BASE + FMC_VERSION);
if (fmc_ip_ver != FMC_VER_100) {
printf("\n");
return -EFAULT;
}
printf(" Found\n");
return 0;
}
/*****************************************************************************/
void fmc_dev_type_switch(unsigned char type)
{
unsigned int reg, spi_device_type, flash_type;
const char *str[] = {"SPI nor", "SPI nand", "Nand", "Boot"};
if (fmc_current_dev_type == type)
return;
fmc_pr(BT_DBG, "\t|*-Start switch current device type\n");
if (type > FLASH_TYPE_DEFAULT) {
fmc_pr(BT_DBG, "\t||-Switch unknown device type %d\n", type);
return;
}
if (fmc_boot_dev_type == FLASH_TYPE_DEFAULT) {
reg = readl((void *)(SYS_CTRL_REG_BASE + REG_SYSSTAT));
fmc_pr(BT_DBG, "\t||-Get system STATUS[%#x]%#x\n",
SYS_CTRL_REG_BASE + REG_SYSSTAT, reg);
fmc_boot_dev_type = get_spi_device_type(reg);
fmc_pr(BT_DBG, "\t||-Init boot device type to %s flash\n",
str[fmc_boot_dev_type]);
}
if (type == FLASH_TYPE_DEFAULT)
spi_device_type = fmc_boot_dev_type;
else
spi_device_type = type;
fmc_pr(BT_DBG, "\t||-Switch type to %s flash\n", str[type]);
reg = readl((void *)(CONFIG_FMC_REG_BASE + FMC_CFG));
fmc_pr(BT_DBG, "\t||-Get FMC CFG[%#x]%#x\n", FMC_CFG, reg);
flash_type = (reg & FLASH_SEL_MASK) >> FLASH_SEL_SHIFT;
if (spi_device_type != flash_type) {
reg &= ~FLASH_SEL_MASK;
reg |= fmc_cfg_flash_sel(spi_device_type);
writel(reg, (void *)(CONFIG_FMC_REG_BASE + FMC_CFG));
fmc_pr(BT_DBG, "\t||-Set FMC CFG[%#x]%#x\n", FMC_CFG, reg);
}
fmc_current_dev_type = spi_device_type;
fmc_pr(BT_DBG, "\t|*-End switch current device type\n");
}
/*****************************************************************************/
char *ulltostr(unsigned long long size)
{
int ix;
static char buffer[BUFF_LEN];
char *fmt[] = {"%u", "%uK", "%uM", "%uG", "%uT"};
/* 4 size type ,0x3ff Obtains the lower six bits*/
for (ix = 0; (ix < 4) && !(size & 0x3FF) && size; ix++)
size = (size >> 10); /* byte to kb, right left 10 */
sprintf(buffer, fmt[ix], size);
return buffer;
}
/*****************************************************************************/
void debug_register_dump(void)
{
unsigned int ix;
unsigned long base = CONFIG_FMC_REG_BASE;
printf("Register dump:");
/* 0x98 Register length , 4 is Byte */
for (ix = 0; ix <= 0x98; ix += 0x04) {
if (!(ix & 0x0F))
printf("\n0x%08lX: ", base + ix);
printf("%08X ", readl((void *)(uintptr_t)(base + ix)));
}
printf("\n");
}
/*****************************************************************************/
/* REG_SYSSTAT 0: 3 Bytes address boot mode; 1: 4Bytes address boot mode */
unsigned int get_fmc_boot_mode(void)
{
unsigned int regval;
unsigned int boot_mode;
regval = readl(SYS_CTRL_REG_BASE + REG_SYSSTAT);
boot_mode = get_spi_nor_addr_mode(regval);
return boot_mode;
}
@@ -0,0 +1,447 @@
/*
* Copyright (c) LOTUS. All rights reserved.
*/
#ifndef __FMC_SPI_IDS_H__
#define __FMC_SPI_IDS_H__
#include <linux/lotus/fmc_common.h>
/*****************************************************************************/
#define INFINITE 0xFFFFFFFF
/*****************************************************************************/
#define SPI_IF_READ_STD 0x01
#define SPI_IF_READ_FAST 0x02
#define SPI_IF_READ_DUAL 0x04
#define SPI_IF_READ_DUAL_ADDR 0x08
#define SPI_IF_READ_QUAD 0x10
#define SPI_IF_READ_QUAD_ADDR 0x20
#define SPI_IF_READ_QUAD_DTR 0x40
#define SPI_IF_WRITE_STD 0x01
#define SPI_IF_WRITE_DUAL 0x02
#define SPI_IF_WRITE_DUAL_ADDR 0x04
#define SPI_IF_WRITE_QUAD 0x08
#define SPI_IF_WRITE_QUAD_ADDR 0x10
#define SPI_IF_ERASE_SECTOR_4K 0x01
#define SPI_IF_ERASE_SECTOR_32K 0x02
#define SPI_IF_ERASE_SECTOR_64K 0x04
#define SPI_IF_ERASE_SECTOR_128K 0x08
#define SPI_IF_ERASE_SECTOR_256K 0x10
/*****************************************************************************/
#define FMC_SPI_NOR_SUPPORT_READ (SPI_IF_READ_STD | \
SPI_IF_READ_FAST | \
SPI_IF_READ_DUAL | \
SPI_IF_READ_DUAL_ADDR | \
SPI_IF_READ_QUAD | \
SPI_IF_READ_QUAD_ADDR | \
SPI_IF_READ_QUAD_DTR)
#define FMC_SPI_NOR_SUPPORT_WRITE (SPI_IF_WRITE_STD | \
SPI_IF_WRITE_DUAL | \
SPI_IF_WRITE_DUAL_ADDR | \
SPI_IF_WRITE_QUAD | \
SPI_IF_WRITE_QUAD_ADDR)
#define FMC_SPI_NOR_STR_MAX_DUMMY 7
#define FMC_SPI_NOR_DTR_MAX_DUMMY 12
/******************************************************************************/
#define FMC_SPI_NAND_SUPPORT_READ (SPI_IF_READ_STD | \
SPI_IF_READ_FAST | \
SPI_IF_READ_DUAL | \
SPI_IF_READ_DUAL_ADDR | \
SPI_IF_READ_QUAD | \
SPI_IF_READ_QUAD_ADDR)
#define FMC_SPI_NAND_SUPPORT_WRITE (SPI_IF_WRITE_STD | SPI_IF_WRITE_QUAD)
#define FMC_SPI_NAND_SUPPORT_MAX_DUMMY 8
/*****************************************************************************/
#define SPI_CMD_READ_STD 0x03 /* Standard read cache */
#define SPI_CMD_READ_STD4B 0x13 /* Standard read cache 4byte mode */
#define SPI_CMD_READ_FAST 0x0B /* Higher speed read cache */
#define SPI_CMD_READ_FAST4B 0x0C /* Higher speed read cache 4byte mode */
#define SPI_CMD_READ_DUAL 0x3B /* 2 IO read cache only date */
#define SPI_CMD_READ_DUAL4B 0x3C /* 2 IO read cache only date 4byte mode */
#define SPI_CMD_READ_DUAL_ADDR 0xBB /* 2 IO read cache date&addr */
#define SPI_CMD_READ_DUAL_ADDR4B 0xBC /* 2 IO read cache date&addr 4byte mode */
#define SPI_CMD_READ_QUAD 0x6B /* 4 IO read cache only date */
#define SPI_CMD_READ_QUAD4B 0x6C /* 4 IO read cache only date 4byte mode */
#define SPI_CMD_READ_QUAD_ADDR 0xEB /* 4 IO read cache date&addr */
#define SPI_CMD_READ_QUAD_ADDR4B 0xEC /* 4 IO read cache date&addr 4byte mode */
#define SPI_CMD_READ_QUAD_DTR 0xED /* 4DTR MODE */
#define SPI_CMD_READ_QUAD_DTR4B 0xEE /* 4DTR MODE 4byte mode */
#define SPI_CMD_READ_QUAD_DTR4B_WINBOND 0xEC /* 4DTR MODE */
#define SPI_CMD_WRITE_STD 0x02 /* Standard page program */
#define SPI_CMD_WRITE_STD4B 0x12 /* Standard page program 4byte mode */
#define SPI_CMD_WRITE_DUAL 0xA2 /* 2 IO program only date */
#define SPI_CMD_WRITE_DUAL4B 0xA2 /* 2 IO program only date 4byte mode */
#define SPI_CMD_WRITE_DUAL_ADDR 0xD2 /* 2 IO program date&addr */
#define SPI_CMD_WRITE_DUAL_ADDR4B 0xD2 /* 2 IO program date&addr 4byte mode */
#define SPI_CMD_WRITE_QUAD 0x32 /* 4 IO program only date */
#define SPI_CMD_WRITE_QUAD4B 0x34 /* 4 IO program only date 4byte mode */
#define SPI_CMD_WRITE_QUAD_ADDR 0x38 /* 4 IO program date&addr */
#define SPI_CMD_WRITE_QUAD_ADDR4B 0x3E /* 4 IO program date&addr 4byte mode */
#define SPI_CMD_SE_4K 0x20 /* 4KB sector Erase */
#define SPI_CMD_SE_4K4B 0x21 /* 4KB sector Erase 4byte mode */
#define SPI_CMD_SE_32K 0x52 /* 32KB sector Erase */
#define SPI_CMD_SE_32K4B 0x5C /* 32KB sector Erase 4byte mode */
#define SPI_CMD_SE_64K 0xD8 /* 64KB sector Erase */
#define SPI_CMD_SE_64K4B 0xDC /* 64KB sector Erase 4byte mode */
#define SPI_CMD_SE_128K 0xD8 /* 128KB sector Erase */
#define SPI_CMD_SE_128K4B 0xD8 /* 128KB sector Erase 4byte mode */
#define SPI_CMD_SE_256K 0xD8 /* 256KB sector Erase */
#define SPI_CMD_SE_256K4B 0xD8 /* 256KB sector Erase 4byte mode */
/*****************************************************************************/
#define set_read_std(_dummy_, _size_, _clk_) \
static struct spi_op read_std_##_dummy_##_size_##_clk_ = { \
SPI_IF_READ_STD, SPI_CMD_READ_STD, _dummy_, _size_, _clk_ }
#define set_read_std4b(_dummy_, _size_, _clk_) \
static struct spi_op read_std4b_##_dummy_##_size_##_clk_ = { \
SPI_IF_READ_STD, SPI_CMD_READ_STD4B, _dummy_, _size_, _clk_ }
#define set_read_fast(_dummy_, _size_, _clk_) \
static struct spi_op read_fast_##_dummy_##_size_##_clk_ = { \
SPI_IF_READ_FAST, SPI_CMD_READ_FAST, _dummy_, _size_, _clk_ }
#define set_read_fast4b(_dummy_, _size_, _clk_) \
static struct spi_op read_fast4b_##_dummy_##_size_##_clk_ = { \
SPI_IF_READ_FAST, SPI_CMD_READ_FAST4B, _dummy_, _size_, _clk_ }
#define set_read_dual(_dummy_, _size_, _clk_) \
static struct spi_op read_dual_##_dummy_##_size_##_clk_ = { \
SPI_IF_READ_DUAL, SPI_CMD_READ_DUAL, _dummy_, _size_, _clk_ }
#define set_read_dual4b(_dummy_, _size_, _clk_) \
static struct spi_op read_dual4b_##_dummy_##_size_##_clk_ = { \
SPI_IF_READ_DUAL, SPI_CMD_READ_DUAL4B, _dummy_, _size_, _clk_ }
#define set_read_dual_addr(_dummy_, _size_, _clk_) \
static struct spi_op read_dual_addr_##_dummy_##_size_##_clk_ = { \
SPI_IF_READ_DUAL_ADDR, SPI_CMD_READ_DUAL_ADDR, _dummy_, _size_, _clk_ }
#define set_read_dual_addr4b(_dummy_, _size_, _clk_) \
static struct spi_op read_dual_addr4b_##_dummy_##_size_##_clk_ = { \
SPI_IF_READ_DUAL_ADDR, SPI_CMD_READ_DUAL_ADDR4B, _dummy_, _size_, _clk_ }
#define set_read_quad(_dummy_, _size_, _clk_) \
static struct spi_op read_quad_##_dummy_##_size_##_clk_ = { \
SPI_IF_READ_QUAD, SPI_CMD_READ_QUAD, _dummy_, _size_, _clk_ }
#define set_read_quad4b(_dummy_, _size_, _clk_) \
static struct spi_op read_quad4b_##_dummy_##_size_##_clk_ = { \
SPI_IF_READ_QUAD, SPI_CMD_READ_QUAD4B, _dummy_, _size_, _clk_ }
#define set_read_quad_addr(_dummy_, _size_, _clk_) \
static struct spi_op read_quad_addr_##_dummy_##_size_##_clk_ = { \
SPI_IF_READ_QUAD_ADDR, SPI_CMD_READ_QUAD_ADDR, _dummy_, _size_, _clk_ }
#define set_read_quad_addr4b(_dummy_, _size_, _clk_) \
static struct spi_op read_quad_addr4b_##_dummy_##_size_##_clk_ = { \
SPI_IF_READ_QUAD_ADDR, SPI_CMD_READ_QUAD_ADDR4B, _dummy_, _size_, _clk_ }
#ifdef CONFIG_DTR_MODE_SUPPORT
#define set_read_quad_dtr(_dummy_, _size_, _clk_) \
static struct spi_op read_quad_dtr_##_dummy_##_size_##_clk_ = { \
SPI_IF_READ_QUAD_DTR, SPI_CMD_READ_QUAD_DTR, _dummy_, _size_, _clk_ }
#define set_read_quad_dtr4b(_dummy_, _size_, _clk_) \
static struct spi_op read_quad_dtr4b_##_dummy_##_size_##_clk_ = { \
SPI_IF_READ_QUAD_DTR, SPI_CMD_READ_QUAD_DTR4B, _dummy_, _size_, _clk_ }
#define set_read_quad_dtr4b_winbond(_dummy_, _size_, _clk_) \
static struct spi_op read_quad_dtr_winbond_##_dummy_##_size_##_clk_ = \
{SPI_IF_READ_QUAD_DTR, SPI_CMD_READ_QUAD_DTR4B_WINBOND, \
_dummy_, _size_, _clk_ }
#endif
/*****************************************************************************/
#define set_write_std(_dummy_, _size_, _clk_) \
static struct spi_op write_std_##_dummy_##_size_##_clk_ = { \
SPI_IF_WRITE_STD, SPI_CMD_WRITE_STD, _dummy_, _size_, _clk_ }
#define set_write_std4b(_dummy_, _size_, _clk_) \
static struct spi_op write_std4b_##_dummy_##_size_##_clk_ = { \
SPI_IF_WRITE_STD, SPI_CMD_WRITE_STD4B, _dummy_, _size_, _clk_ }
#define set_write_dual(_dummy_, _size_, _clk_) \
static struct spi_op write_dual_##_dummy_##_size_##_clk_ = { \
SPI_IF_WRITE_DUAL, SPI_CMD_WRITE_DUAL, _dummy_, _size_, _clk_ }
#define set_write_dual4b(_dummy_, _size_, _clk_) \
static struct spi_op write_dual4b_##_dummy_##_size_##_clk_ = { \
SPI_IF_WRITE_DUAL, SPI_CMD_WRITE_DUAL4B, _dummy_, _size_, _clk_ }
#define set_write_dual_addr(_dummy_, _size_, _clk_) \
static struct spi_op write_dual_addr_##_dummy_##_size_##_clk_ = { \
SPI_IF_WRITE_DUAL_ADDR, SPI_CMD_WRITE_DUAL_ADDR, _dummy_, _size_, _clk_ }
#define set_write_dual_addr4b(_dummy_, _size_, _clk_) \
static struct spi_op write_dual_addr4b_##_dummy_##_size_##_clk_ = { \
SPI_IF_WRITE_DUAL_ADDR, SPI_CMD_WRITE_DUAL_ADDR4B, _dummy_, _size_, _clk_ }
#define set_write_quad(_dummy_, _size_, _clk_) \
static struct spi_op write_quad_##_dummy_##_size_##_clk_ = { \
SPI_IF_WRITE_QUAD, SPI_CMD_WRITE_QUAD, _dummy_, _size_, _clk_ }
#define set_write_quad4b(_dummy_, _size_, _clk_) \
static struct spi_op write_quad4b_##_dummy_##_size_##_clk_ = { \
SPI_IF_WRITE_QUAD, SPI_CMD_WRITE_QUAD4B, _dummy_, _size_, _clk_ }
#define set_write_quad_addr(_dummy_, _size_, _clk_) \
static struct spi_op write_quad_addr_##_dummy_##_size_##_clk_ = { \
SPI_IF_WRITE_QUAD_ADDR, SPI_CMD_WRITE_QUAD_ADDR, _dummy_, _size_, _clk_ }
#define set_write_quad_addr4b(_dummy_, _size_, _clk_) \
static struct spi_op write_quad_addr4b_##_dummy_##_size_##_clk_ = { \
SPI_IF_WRITE_QUAD_ADDR, SPI_CMD_WRITE_QUAD_ADDR4B, _dummy_, _size_, _clk_ }
/*****************************************************************************/
#define set_erase_sector_4k(_dummy_, _size_, _clk_) \
static struct spi_op erase_sector_4k_##_dummy_##_size_##_clk_ = { \
SPI_IF_ERASE_SECTOR_4K, SPI_CMD_SE_4K, _dummy_, _size_, _clk_ }
#define set_erase_sector_4k4b(_dummy_, _size_, _clk_) \
static struct spi_op erase_sector_4k4b_##_dummy_##_size_##_clk_ = { \
SPI_IF_ERASE_SECTOR_4K, SPI_CMD_SE_4K4B, _dummy_, _size_, _clk_ }
#define set_erase_sector_32k(_dummy_, _size_, _clk_) \
static struct spi_op erase_sector_32k_##_dummy_##_size_##_clk_ = { \
SPI_IF_ERASE_SECTOR_32K, SPI_CMD_SE_32K, _dummy_, _size_, _clk_ }
#define set_erase_sector_32k4b(_dummy_, _size_, _clk_) \
static struct spi_op erase_sector_32k4b_##_dummy_##_size_##_clk_ = { \
SPI_IF_ERASE_SECTOR_32K, SPI_CMD_SE_32K4B, _dummy_, _size_, _clk_ }
#define set_erase_sector_64k(_dummy_, _size_, _clk_) \
static struct spi_op erase_sector_64k_##_dummy_##_size_##_clk_ = { \
SPI_IF_ERASE_SECTOR_64K, SPI_CMD_SE_64K, _dummy_, _size_, _clk_ }
#define set_erase_sector_64k4b(_dummy_, _size_, _clk_) \
static struct spi_op erase_sector_64k4b_##_dummy_##_size_##_clk_ = { \
SPI_IF_ERASE_SECTOR_64K, SPI_CMD_SE_64K4B, _dummy_, _size_, _clk_ }
#define set_erase_sector_128k(_dummy_, _size_, _clk_) \
static struct spi_op erase_sector_128k_##_dummy_##_size_##_clk_ = { \
SPI_IF_ERASE_SECTOR_128K, SPI_CMD_SE_128K, _dummy_, _size_, _clk_ }
#define set_erase_sector_128k4b(_dummy_, _size_, _clk_) \
static struct spi_op erase_sector_128k4b_##_dummy_##_size_##_clk_ = { \
SPI_IF_ERASE_SECTOR_128K, SPI_CMD_SE_128K4B, _dummy_, _size_, _clk_ }
#define set_erase_sector_256k(_dummy_, _size_, _clk_) \
static struct spi_op erase_sector_256k_##_dummy_##_size_##_clk_ = { \
SPI_IF_ERASE_SECTOR_256K, SPI_CMD_SE_256K, _dummy_, _size_, _clk_ }
#define set_erase_sector_256k4b(_dummy_, _size_, _clk_) \
static struct spi_op erase_sector_256k4b_##_dummy_##_size_##_clk_ = { \
SPI_IF_ERASE_SECTOR_256K, SPI_CMD_SE_256K4B, _dummy_, _size_, _clk_ }
/*****************************************************************************/
#define read_std(_dummy_, _size_, _clk_) read_std_##_dummy_##_size_##_clk_
#define read_std4b(_dummy_, _size_, _clk_) read_std4b_##_dummy_##_size_##_clk_
#define read_fast(_dummy_, _size_, _clk_) read_fast_##_dummy_##_size_##_clk_
#define read_fast4b(_dummy_, _size_, _clk_) \
read_fast4b_##_dummy_##_size_##_clk_
#define read_dual(_dummy_, _size_, _clk_) read_dual_##_dummy_##_size_##_clk_
#define read_dual4b(_dummy_, _size_, _clk_) \
read_dual4b_##_dummy_##_size_##_clk_
#define read_dual_addr(_dummy_, _size_, _clk_) \
read_dual_addr_##_dummy_##_size_##_clk_
#define read_dual_addr4b(_dummy_, _size_, _clk_) \
read_dual_addr4b_##_dummy_##_size_##_clk_
#define read_quad(_dummy_, _size_, _clk_) read_quad_##_dummy_##_size_##_clk_
#define read_quad4b(_dummy_, _size_, _clk_) \
read_quad4b_##_dummy_##_size_##_clk_
#define read_quad_addr(_dummy_, _size_, _clk_) \
read_quad_addr_##_dummy_##_size_##_clk_
#define read_quad_addr4b(_dummy_, _size_, _clk_) \
read_quad_addr4b_##_dummy_##_size_##_clk_
#ifdef CONFIG_DTR_MODE_SUPPORT
#define read_quad_dtr(_dummy_, _size_, _clk_) \
read_quad_dtr_##_dummy_##_size_##_clk_
#define read_quad_dtr4b(_dummy_, _size_, _clk_) \
read_quad_dtr4b_##_dummy_##_size_##_clk_
#define read_quad_dtr4b_winbond(_dummy_, _size_, _clk_) \
read_quad_dtr4b_winbond_##_dummy_##_size_##_clk_
#endif
/*****************************************************************************/
#define write_std(_dummy_, _size_, _clk_) write_std_##_dummy_##_size_##_clk_
#define write_std4b(_dummy_, _size_, _clk_) \
write_std4b_##_dummy_##_size_##_clk_
#define write_dual(_dummy_, _size_, _clk_) write_dual_##_dummy_##_size_##_clk_
#define write_dual4b(_dummy_, _size_, _clk_) \
write_dual4b_##_dummy_##_size_##_clk_
#define write_dual_addr(_dummy_, _size_, _clk_) \
write_dual_addr_##_dummy_##_size_##_clk_
#define write_dual_addr4b(_dummy_, _size_, _clk_) \
write_dual_addr4b_##_dummy_##_size_##_clk_
#define write_quad(_dummy_, _size_, _clk_) write_quad_##_dummy_##_size_##_clk_
#define write_quad4b(_dummy_, _size_, _clk_) \
write_quad4b_##_dummy_##_size_##_clk_
#define write_quad_addr(_dummy_, _size_, _clk_) \
write_quad_addr_##_dummy_##_size_##_clk_
#define write_quad_addr4b(_dummy_, _size_, _clk_) \
write_quad_addr4b_##_dummy_##_size_##_clk_
/*****************************************************************************/
#define erase_sector_4k(_dummy_, _size_, _clk_) \
erase_sector_4k_##_dummy_##_size_##_clk_
#define erase_sector_4k4b(_dummy_, _size_, _clk_) \
erase_sector_4k4b_##_dummy_##_size_##_clk_
#define erase_sector_32k(_dummy_, _size_, _clk_) \
erase_sector_32k_##_dummy_##_size_##_clk_
#define erase_sector_32k4b(_dummy_, _size_, _clk_) \
erase_sector_32k4b_##_dummy_##_size_##_clk_
#define erase_sector_64k(_dummy_, _size_, _clk_) \
erase_sector_64k_##_dummy_##_size_##_clk_
#define erase_sector_64k4b(_dummy_, _size_, _clk_) \
erase_sector_64k4b_##_dummy_##_size_##_clk_
#define erase_sector_128k(_dummy_, _size_, _clk_) \
erase_sector_128k_##_dummy_##_size_##_clk_
#define erase_sector_128k4b(_dummy_, _size_, _clk_) \
erase_sector_128k4b_##_dummy_##_size_##_clk_
#define erase_sector_256k(_dummy_, _size_, _clk_) \
erase_sector_256k_##_dummy_##_size_##_clk_
#define erase_sector_256k4b(_dummy_, _size_, _clk_) \
erase_sector_256k4b_##_dummy_##_size_##_clk_
/*****************************************************************************/
#define SPI_CMD_WREN 0x06 /* Write Enable */
#define SPI_CMD_WRDI 0x04 /* Write Disable */
/*****************************************************************************/
#define SPI_CMD_WRSR 0x01 /* Write Status Register */
#define SPI_CMD_WRSR2 0x31 /* Write Status Register-2 */
#define SPI_CMD_WRSR3 0x11 /* Write Status Register-3 */
#define SPI_CMD_RDSR 0x05 /* Read Status Register */
#define SPI_CMD_RDSR2 0x35 /* Read Status Register-2 */
#define SPI_CMD_RDSR3 0x15 /* Read Status Register-3 */
#define SPI_CMD_RDCR 0x35 /* Read Config Register */
#define SPI_CMD_RDID 0x9F /* Read Identification */
#define SPI_CMD_RD_SFDP 0x5A /* Read SFDP */
/*****************************************************************************/
#define SPI_CMD_GET_FEATURES 0x0F /* Get Features */
#define SPI_CMD_SET_FEATURE 0x1F /* Set Feature */
#define SPI_CMD_PAGE_READ 0x13 /* Page Read to Cache */
#define SPI_CMD_RESET 0xff /* Reset the device */
/*****************************************************************************/
#define SPI_CMD_EN4B 0xB7 /* enter 4 bytes mode and set 4 byte bit as '1' */
#define SPI_CMD_EX4B 0xE9 /* exit 4 bytes mode and clear 4 byte bit */
/*****************************************************************************/
#define MAX_SPI_OP 8
/*****************************************************************************/
/* SPI general operation parameter */
struct spi_op {
unsigned char iftype;
unsigned char cmd;
unsigned char dummy;
unsigned int size;
unsigned int clock;
};
struct spi_drv;
/* SPI interface all operation */
struct fmc_spi {
char *name;
unsigned int chipselect;
unsigned long long chipsize;
unsigned int erasesize;
#define SPI_NOR_3BYTE_ADDR_LEN 3 /* address len 3Bytes */
#define SPI_NOR_4BYTE_ADDR_LEN 4 /* address len 4Bytes for 32MB */
unsigned int addrcycle;
struct spi_op read[1];
struct spi_op write[1];
struct spi_op erase[MAX_SPI_OP];
void *host;
struct spi_drv *driver;
#ifdef CONFIG_DTR_MODE_SUPPORT
unsigned int dtr_mode_support;
/* @dtr_cookie: Some device must set some registers when wants to
* work on DTR mode, so this cookie tells us to set s.th */
unsigned int dtr_cookie;
#define DTR_MODE_SET_NONE 0x0 /* Need not set anything */
#define DTR_MODE_SET_ODS 0x1 /* Need to set output driver strength */
#endif
};
/* SPI interface special operation function hook */
struct spi_drv {
int (*wait_ready)(struct fmc_spi *spi);
int (*write_enable)(struct fmc_spi *spi);
int (*qe_enable)(struct fmc_spi *spi);
int (*bus_prepare)(struct fmc_spi *spi, int op);
int (*entry_4addr)(struct fmc_spi *spi, int en);
#ifdef CONFIG_DTR_MODE_SUPPORT
int (*dtr_set_device)(struct fmc_spi *spi, int dtr_en);
#endif
};
#ifndef MAX_SPI_NAND_ID_LEN
#define MAX_SPI_NAND_ID_LEN 1
#endif
struct spi_nand_info {
char *name;
unsigned char id[MAX_SPI_NAND_ID_LEN];
unsigned char id_len;
unsigned long long chipsize;
unsigned int erasesize;
unsigned int pagesize;
unsigned int oobsize;
#define BBP_LAST_PAGE 0x01
#define BBP_FIRST_PAGE 0x02
unsigned int badblock_pos;
struct spi_op *read[MAX_SPI_OP];
struct spi_op *write[MAX_SPI_OP];
struct spi_op *erase[MAX_SPI_OP];
struct spi_drv *driver;
};
#ifndef MAX_SPI_NOR_ID_LEN
#define MAX_SPI_NOR_ID_LEN 8
#endif
struct spi_nor_info {
char *name;
unsigned char id[MAX_SPI_NOR_ID_LEN];
unsigned int id_len;
unsigned long chipsize;
unsigned int erasesize;
unsigned int addrcycle;
struct spi_op *read[MAX_SPI_OP];
struct spi_op *write[MAX_SPI_OP];
struct spi_op *erase[MAX_SPI_OP];
struct spi_drv *driver;
};
/*****************************************************************************/
void fmc_set_fmc_system_clock(struct spi_op *op, int clk_en);
void fmc_get_fmc_best_2x_clock(unsigned int *clock);
#ifdef CONFIG_DTR_MODE_SUPPORT
void fmc_get_fmc_best_4x_clock(unsigned int *clock);
#endif
/*****************************************************************************/
#endif /* End of __FMC_SPI_IDS_H__ */
@@ -0,0 +1,128 @@
/*
* Copyright (c) LOTUS. All rights reserved.
*/
#include <common.h>
#include <asm/io.h>
#include <asm/arch/platform.h>
#include <linux/lotus/fmc_common.h>
#include "fmc_spi_ids.h"
#define REG_IO_BASE 0x100c0000
static void xmfalcon_io_config_spi_sfc(void)
{
/* set pad ctrl reg for spi */
//writel(0x401, REG_IO_BASE + 0x14); /* sfc_clk */
//writel(0x461, REG_IO_BASE + 0x18); /* sfc_hold_io0 */
//writel(0x461, REG_IO_BASE + 0x1c); /* sfc_miso_io1 */
//writel(0x461, REG_IO_BASE + 0x20); /* sfc_wp_io2 */
//writel(0x461, REG_IO_BASE + 0x24); /* sfc_mosi_io3 */
//writel(0x461, REG_IO_BASE + 0x28); /* sfc_csn */
}
/*****************************************************************************/
void fmc_set_fmc_system_clock(struct spi_op *op, int clk_en)
{
unsigned int old_val;
unsigned int regval;
old_val = regval = readl(CRG_REG_BASE + REG_FMC_CRG);
regval &= ~FMC_CLK_SEL_MASK;
if (op && op->clock) {
regval |= op->clock & FMC_CLK_SEL_MASK;
fmc_pr(DTR_DB, "\t|||*-get the setting clock value: %#x\n",
op->clock);
} else {
regval |= fmc_clk_sel(FMC_CLK_24M); /* Default Clock */
xmfalcon_io_config_spi_sfc();
}
if (clk_en)
regval |= FMC_CLK_ENABLE;
else
regval &= ~FMC_CLK_ENABLE;
if (regval != old_val) {
fmc_pr(DTR_DB, "\t|||*-setting system clock [%#x]%#x\n",
REG_FMC_CRG, regval);
writel(regval, (CRG_REG_BASE + REG_FMC_CRG));
}
}
/*****************************************************************************/
void fmc_get_fmc_best_2x_clock(unsigned int *clock)
{
int ix;
unsigned int clk_reg;
unsigned int clk_type;
const char *str[] = {"12", "50", "75", "100"};
unsigned int sys_2x_clk[] = {
clk_2x(24), fmc_clk_sel(FMC_CLK_24M),
clk_2x(100), fmc_clk_sel(FMC_CLK_100M),
clk_2x(150), fmc_clk_sel(FMC_CLK_150M),
clk_2x(200), fmc_clk_sel(FMC_CLK_200M),
0, 0,
};
if (!clock)
return;
clk_type = FMC_CLK_24M;
clk_reg = fmc_clk_sel(clk_type);
fmc_pr(QE_DBG, "\t|||*-matching flash clock %d\n", *clock);
for (ix = 0; (sys_2x_clk[ix] && ((ix + 1) < sizeof(sys_2x_clk)));
ix += _2B) {
if (*clock < sys_2x_clk[ix])
break;
clk_reg = sys_2x_clk[ix + 1];
clk_type = get_fmc_clk_type(clk_reg);
fmc_pr(QE_DBG, "\t||||-select system clock: %sMHz\n",
str[clk_type]);
}
#ifdef CONFIG_DTR_MODE_SUPPORT
fmc_pr(DTR_DB, "best system clock for SDR.\n");
#endif
fmc_pr(QE_DBG, "\t|||*-matched best system clock: %sMHz\n",
str[clk_type]);
*clock = clk_reg;
}
#ifdef CONFIG_DTR_MODE_SUPPORT
/*****************************************************************************/
void fmc_get_fmc_best_4x_clock(unsigned int *clock)
{
int ix;
unsigned int clk_reg;
unsigned int clk_type;
char *const str[] = {"6", "25", "37.5", "50", "75", "90"};
unsigned int sys_4x_clk[] = {
clk_4x(24), fmc_clk_sel(FMC_CLK_24M),
clk_4x(100), fmc_clk_sel(FMC_CLK_100M),
clk_4x(150), fmc_clk_sel(FMC_CLK_150M),
clk_4x(200), fmc_clk_sel(FMC_CLK_200M),
clk_4x(300), fmc_clk_sel(FMC_CLK_300M),
clk_4x(360), fmc_clk_sel(FMC_CLK_360M),
0, 0,
};
if (!clock)
return;
clk_type = FMC_CLK_24M;
clk_reg = fmc_clk_sel(clk_type);
fmc_pr(DTR_DB, "\t|||*-matching flash clock %d\n", *clock);
for (ix = 0; (sys_4x_clk[ix] && ((ix + 1) < sizeof(sys_4x_clk))); ix += _2B) {
if (*clock < sys_4x_clk[ix])
break;
clk_reg = sys_4x_clk[ix + 1];
clk_type = get_fmc_clk_type(clk_reg);
fmc_pr(DTR_DB, "\t||||-select system clock: %sMHz\n",
str[clk_type]);
}
fmc_pr(DTR_DB, "best system clock for DTR.\n");
fmc_pr(DTR_DB, "\t|||*-matched best system clock: %sMHz\n",
str[clk_type]);
*clock = clk_reg;
}
/*****************************************************************************/
#endif/* CONFIG_DTR_MODE_SUPPORT */
@@ -0,0 +1,127 @@
/*
* Copyright (c) LOTUS. All rights reserved.
*/
#include <common.h>
#include <asm/io.h>
#include <asm/arch/platform.h>
#include <linux/lotus/fmc_common.h>
#include "fmc_spi_ids.h"
#define REG_IO_BASE 0x100c0000
static void xmorca_io_config_spi_sfc(void)
{
/* set pad ctrl reg for spi */
//writel(0x401, REG_IO_BASE + 0x14); /* sfc_clk */
//writel(0x461, REG_IO_BASE + 0x18); /* sfc_hold_io0 */
//writel(0x461, REG_IO_BASE + 0x1c); /* sfc_miso_io1 */
//writel(0x461, REG_IO_BASE + 0x20); /* sfc_wp_io2 */
//writel(0x461, REG_IO_BASE + 0x24); /* sfc_mosi_io3 */
//writel(0x461, REG_IO_BASE + 0x28); /* sfc_csn */
}
/*****************************************************************************/
void fmc_set_fmc_system_clock(struct spi_op *op, int clk_en)
{
unsigned int old_val;
unsigned int regval;
old_val = regval = readl(CRG_REG_BASE + REG_FMC_CRG);
regval &= ~FMC_CLK_SEL_MASK;
if (op && op->clock) {
regval |= op->clock & FMC_CLK_SEL_MASK;
fmc_pr(DTR_DB, "\t|||*-get the setting clock value: %#x\n",
op->clock);
} else {
regval |= fmc_clk_sel(FMC_CLK_24M); /* Default Clock */
xmorca_io_config_spi_sfc();
}
if (clk_en)
regval |= FMC_CLK_ENABLE;
else
regval &= ~FMC_CLK_ENABLE;
if (regval != old_val) {
fmc_pr(DTR_DB, "\t|||*-setting system clock [%#x]%#x\n",
REG_FMC_CRG, regval);
writel(regval, (CRG_REG_BASE + REG_FMC_CRG));
}
}
/*****************************************************************************/
void fmc_get_fmc_best_2x_clock(unsigned int *clock)
{
int ix;
unsigned int clk_reg;
unsigned int clk_type;
const char *str[] = {"12","NULL", "NULL", "100", "75", "50"};
unsigned int sys_2x_clk[] = {
clk_2x(24), fmc_clk_sel(FMC_CLK_24M),
clk_2x(100), fmc_clk_sel(FMC_CLK_100M),
clk_2x(150), fmc_clk_sel(FMC_CLK_150M),
clk_2x(200), fmc_clk_sel(FMC_CLK_200M),
0, 0,
};
if (!clock)
return;
clk_type = FMC_CLK_24M;
clk_reg = fmc_clk_sel(clk_type);
fmc_pr(QE_DBG, "\t|||*-matching flash clock %d\n", *clock);
for (ix = 0; (sys_2x_clk[ix] && ((ix + 1) < sizeof(sys_2x_clk)));
ix += _2B) {
if (*clock < sys_2x_clk[ix])
break;
clk_reg = sys_2x_clk[ix + 1];
clk_type = get_fmc_clk_type(clk_reg);
fmc_pr(QE_DBG, "\t||||-select system clock: %sMHz\n",
str[clk_type]);
}
#ifdef CONFIG_DTR_MODE_SUPPORT
fmc_pr(DTR_DB, "best system clock for SDR.\n");
#endif
fmc_pr(QE_DBG, "\t|||*-matched best system clock: %sMHz\n",
str[clk_type]);
*clock = clk_reg;
}
#ifdef CONFIG_DTR_MODE_SUPPORT
/*****************************************************************************/
void fmc_get_fmc_best_4x_clock(unsigned int *clock)
{
int ix;
unsigned int clk_reg;
unsigned int clk_type;
char *const str[] = {"6", "NULL", "75", "50", "37.5", "25"};
unsigned int sys_4x_clk[] = {
clk_4x(24), fmc_clk_sel(FMC_CLK_24M),
clk_4x(100), fmc_clk_sel(FMC_CLK_100M),
clk_4x(150), fmc_clk_sel(FMC_CLK_150M),
clk_4x(200), fmc_clk_sel(FMC_CLK_200M),
clk_4x(300), fmc_clk_sel(FMC_CLK_300M),
0, 0,
};
if (!clock)
return;
clk_type = FMC_CLK_24M;
clk_reg = fmc_clk_sel(clk_type);
fmc_pr(DTR_DB, "\t|||*-matching flash clock %d\n", *clock);
for (ix = 0; (sys_4x_clk[ix] && ((ix + 1) < sizeof(sys_4x_clk))); ix += _2B) {
if (*clock < sys_4x_clk[ix])
break;
clk_reg = sys_4x_clk[ix + 1];
clk_type = get_fmc_clk_type(clk_reg);
fmc_pr(DTR_DB, "\t||||-select system clock: %sMHz\n",
str[clk_type]);
}
fmc_pr(DTR_DB, "best system clock for DTR.\n");
fmc_pr(DTR_DB, "\t|||*-matched best system clock: %sMHz\n",
str[clk_type]);
*clock = clk_reg;
}
/*****************************************************************************/
#endif/* CONFIG_DTR_MODE_SUPPORT */
@@ -0,0 +1,56 @@
config FMC_SPI_NAND
bool "lotus SPI Nand Flash Interface support"
depends on FMC
help
Enable the lotus SPI Nand flash support.
If unsure, say N
config SPI_NAND_MAX_CHIP_NUM
int "Support max number of SPI Nand flash chip (1, 2)"
depends on FMC_SPI_NAND
default 1
help
flash memory controller v100 device only support 1 or 2 SPI nand flash
chip, your should not config other value.
config NAND_MAX_CHIP_NUM
int "Support max number of Nand flash chip (1, 2)"
depends on FMC_NAND
default 1
help
flash memory controller v100 device only support 1 or 2 nand flash
chip, your should not config other value.
if FMC_SPI_NAND || FMC_NAND
choice
prompt "Page Size and Ecc Type Select"
default FMC100_AUTO_PAGESIZE_ECC
config FMC100_HARDWARE_PAGESIZE_ECC
bool "Hardware"
help
the configure of page size and ecc type lie on switch on the board.
so the page size and ecc type is controlled by Hardware see demo
board of SOC.
config FMC100_AUTO_PAGESIZE_ECC
bool "Auto"
help
auto-sensed the page size and ecc type value. driver will try each of
page size and ecc type one by one till flash can be read and wrote
accurately. so the page size and ecc type is match adaptively without
switch on the board
config FMC100_PAGESIZE_AUTO_ECC_NONE
bool "Pagesize Auto, Ecc None"
help
auto-sensed the page size and select ecc none. driver will try each
of page size one by one till flash can be read and wrote accurately.
so the page size is match adaptively without switch on the board
endchoice
endif
@@ -0,0 +1,5 @@
obj-$(CONFIG_CMD_NAND) += nfc_common.o
obj-$(CONFIG_CMD_NAND) +=match_table.o
obj-$(CONFIG_FMC_SPI_NAND) += fmc100/
@@ -0,0 +1,4 @@
ccflags-y += -I$(srctree)/lotus/drivers/mtd
obj-y += fmc100.o fmc100_os.o fmc_spi_nand_ids.o
@@ -0,0 +1,965 @@
/*
* Copyright (c) LOTUS. All rights reserved.
*/
#include "fmc100.h"
#include <common.h>
#include <asm/io.h>
#include <errno.h>
#include <malloc.h>
#include <linux/lotus/match_table.h>
#include <asm/arch/platform.h>
void fmc100_ecc0_switch(struct fmc_host* const host, unsigned char op)
{
unsigned int config;
#if EC_DBG
unsigned int cmp_cfg;
config = fmc_read(host, FMC_CFG);
fmc_pr(EC_DBG, "\t *-Get CFG[%#x]%#x\n", FMC_CFG, config);
if (op)
cmp_cfg = host->fmc_cfg;
else
cmp_cfg = host->fmc_cfg_ecc0;
if (cmp_cfg != config)
db_msg("Warning: FMC config[%#x] is different.\n",
cmp_cfg);
#endif
if (op == ENABLE) {
config = host->fmc_cfg_ecc0;
} else if (op == DISABLE) {
config = host->fmc_cfg;
} else {
db_msg("Error: Invalid opcode: %d\n", op);
return;
}
fmc_write(host, FMC_CFG, config);
fmc_pr(EC_DBG, "\t *-Set CFG[%#x]%#x\n", FMC_CFG, config);
}
static void set_dma_addr_reg(struct fmc_host *host)
{
unsigned int reg;
reg = host->dma_buffer;
fmc_write(host, FMC_DMA_SADDR_D0, reg);
fmc_pr(DMA_DB, "\t|-Set DMA_SADDR_D0[%#x]%#x\n", FMC_DMA_SADDR_D0, reg);
/* get hight 32 bits */
reg = ((unsigned long)host->dma_buffer & FMC_DMA_SADDRH_MASK) >> 32;
fmc_write(host, FMC_DMA_SADDRH_D0, reg);
fmc_pr(DMA_DB, "\t|-Set DMA_SADDRH_D0[%#x]%#x\n", FMC_DMA_SADDRH_D0, reg);
reg = host->dma_oob;
fmc_write(host, FMC_DMA_SADDR_OOB, reg);
fmc_pr(DMA_DB, "\t|-Set DMA_SADDR_OOB[%#x]%#x\n", FMC_DMA_SADDR_OOB,
reg);
/* get hight 32 bits */
reg = ((unsigned long)host->dma_oob & FMC_DMA_SADDRH_MASK) >> 32;
fmc_write(host, FMC_DMA_SADDRH_OOB, reg);
fmc_pr(DMA_DB, "\t|-Set DMA_SADDRH_OOB[%#x]%#x\n", FMC_DMA_SADDRH_OOB,
reg);
}
static void set_addr_reg(struct fmc_host *host)
{
unsigned int reg;
struct nand_chip *chip = host->chip;
unsigned char pages_per_block_shift;
unsigned int block_num;
unsigned int block_num_h;
unsigned int page_num;
pages_per_block_shift = chip->phys_erase_shift - chip->page_shift;
block_num = host->addr_value[1] >> pages_per_block_shift;
block_num_h = block_num >> REG_CNT_HIGH_BLOCK_NUM_SHIFT;
reg = fmc_addrh_set(block_num_h);
fmc_write(host, FMC_ADDRH, reg);
fmc_pr(REG_DB, "|-Set ADDRH[%#x]%#x\n", FMC_ADDRH, reg);
page_num = host->addr_value[1] - (block_num << pages_per_block_shift);
reg = ((block_num & REG_CNT_BLOCK_NUM_MASK) << REG_CNT_BLOCK_NUM_SHIFT) |
((page_num & REG_CNT_PAGE_NUM_MASK) << REG_CNT_PAGE_NUM_SHIFT);
fmc_write(host, FMC_ADDRL, reg);
fmc_pr(REG_DB, "|-Set ADDRL[%#x]%#x\n", FMC_ADDRL, reg);
}
/****************************************************************************/
static void set_cs_addr_reg(enum OP op, struct fmc_host *host)
{
unsigned int reg;
struct fmc_spi *spi = host->spi;
unsigned char iftype = 0;
unsigned char dummy = 0;
reg = FMC_INT_CLR_ALL;
fmc_write(host, FMC_INT_CLR, reg);
fmc_pr(WR_DBG, "|-Set INT_CLR[%#x]%#x\n", FMC_INT_CLR, reg);
if (op == READ) {
iftype = spi->read->iftype;
dummy = spi->read->dummy;
} else if (op == WRITE) {
iftype = spi->write->iftype;
} else {
iftype = spi->erase->iftype;
}
reg = op_cfg_fm_cs(host->cmd_op.cs) |
OP_CFG_OEN_EN |
op_cfg_mem_if_type(iftype) |
op_cfg_dummy_num(dummy);
fmc_write(host, FMC_OP_CFG, reg);
fmc_pr(REG_DB, "|-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg);
set_addr_reg(host);
}
static void pageprog_fmc_op(struct fmc_host* const host, struct fmc_spi* const spi)
{
unsigned int reg;
reg = op_ctrl_wr_opcode(spi->write->cmd) | op_ctrl_dma_op(OP_TYPE_DMA) |
op_ctrl_rw_op(RW_OP_WRITE) | OP_CTRL_DMA_OP_READY;
fmc_write(host, FMC_OP_CTRL, reg);
fmc_pr(WR_DBG, "|-Set OP_CTRL[%#x]%#x\n", FMC_OP_CTRL, reg);
fmc_dma_wait_int_finish(host);
}
static void fmc100_send_cmd_pageprog(struct fmc_host *host)
{
struct fmc_spi *spi = host->spi;
unsigned char *fmc_ip = NULL;
int ret;
#ifndef CONFIG_SYS_DCACHE_OFF
unsigned int dma_align_len;
#endif
fmc_pr(WR_DBG, "\n*-Enter Dma page program!\n");
fmc_ip = get_fmc_ip();
if (*fmc_ip) {
printf("Warning: FMC IP is busy, Please try again.\n");
udelay(1); /* delay 1 us */
return;
} else {
fmc_dev_type_switch(FLASH_TYPE_SPI_NAND);
(*fmc_ip)++;
}
ret = spi->driver->write_enable(spi);
if (ret) {
db_msg("Error: Dma program write enable failed! ret: %#x\n", ret);
goto end;
}
host->set_system_clock(spi->write, ENABLE);
if (ecc0_flag == 1) {
fmc100_ecc0_switch(host, ENABLE);
fmc_write(host, FMC_DMA_LEN, host->oobsize);
}
set_cs_addr_reg(WRITE, host);
if (ecc0_flag != 1)
*host->epm = 0x0000;
#ifndef CONFIG_SYS_DCACHE_OFF
dma_align_len = ((host->pagesize + host->oobsize +
CONFIG_SYS_CACHELINE_SIZE - 1) & ~(CONFIG_SYS_CACHELINE_SIZE - 1));
flush_dcache_range(host->dma_buffer, host->dma_buffer + dma_align_len);
#endif
set_dma_addr_reg(host);
pageprog_fmc_op(host, spi);
if (ecc0_flag == 1)
fmc100_ecc0_switch(host, DISABLE);
ret = spi->driver->wait_ready(spi);
if (ret)
db_msg("Error: Dma program wait ready failed! status: %#x\n", ret);
end:
(*fmc_ip)--;
fmc_pr(WR_DBG, "*-End Dma page program!\n");
}
static void fmc100_send_cmd_readstart(struct fmc_host *host)
{
unsigned int reg;
struct fmc_spi *spi = host->spi;
unsigned char *fmc_ip = NULL;
#ifndef CONFIG_SYS_DCACHE_OFF
unsigned int dma_align_len;
#endif
fmc_pr(RD_DBG, "\n\t*-Start Dma page read\n");
fmc_ip = get_fmc_ip();
if (*fmc_ip) {
printf("Warning: FMC IP is busy, Please try again.\n");
udelay(1); /* delay 1 us */
return;
} else {
fmc_dev_type_switch(FLASH_TYPE_SPI_NAND);
(*fmc_ip)++;
}
host->set_system_clock(spi->read, ENABLE);
if (ecc0_flag == 1 && (host->cmd_op.l_cmd != NAND_CMD_READOOB)) {
fmc100_ecc0_switch(host, ENABLE);
fmc_write(host, FMC_DMA_LEN, host->oobsize);
}
if (host->cmd_op.l_cmd == NAND_CMD_READOOB)
host->cmd_op.op_cfg = op_ctrl_rd_op_sel(RD_OP_READ_OOB);
else
host->cmd_op.op_cfg = op_ctrl_rd_op_sel(RD_OP_READ_ALL_PAGE);
set_cs_addr_reg(READ, host);
#ifndef CONFIG_SYS_DCACHE_OFF
dma_align_len = ((host->pagesize + host->oobsize +
CONFIG_SYS_CACHELINE_SIZE - 1) & ~(CONFIG_SYS_CACHELINE_SIZE - 1));
invalidate_dcache_range(host->dma_buffer, host->dma_buffer + dma_align_len);
#endif
set_dma_addr_reg(host);
reg = op_ctrl_rd_opcode(spi->read->cmd) |
host->cmd_op.op_cfg | op_ctrl_dma_op(OP_TYPE_DMA) |
op_ctrl_rw_op(RW_OP_READ) | OP_CTRL_DMA_OP_READY;
fmc_write(host, FMC_OP_CTRL, reg);
fmc_pr(RD_DBG, "\t|-Set OP_CTRL[%#x]%#x\n", FMC_OP_CTRL, reg);
fmc_dma_wait_int_finish(host);
if (ecc0_flag == 1 && (host->cmd_op.l_cmd != NAND_CMD_READOOB))
fmc100_ecc0_switch(host, DISABLE);
#ifndef CONFIG_SYS_DCACHE_OFF
invalidate_dcache_range(host->dma_buffer, host->dma_buffer + dma_align_len);
#endif
(*fmc_ip)--;
fmc_pr(RD_DBG, "\t*-End Dma page read\n");
}
static void erase_fmc_op(struct fmc_host* const host, struct fmc_spi* const spi)
{
unsigned int reg;
reg = FMC_INT_CLR_ALL;
fmc_write(host, FMC_INT_CLR, reg);
fmc_pr(ER_DBG, "\t|-Set INT_CLR[%#x]%#x\n", FMC_INT_CLR, reg);
reg = spi->erase->cmd;
fmc_write(host, FMC_CMD, fmc_cmd_cmd1(reg));
fmc_pr(ER_DBG, "\t|-Set CMD[%#x]%#x\n", FMC_CMD, reg);
reg = fmc_addrl_block_h_mask(host->addr_value[1]) |
fmc_addrl_block_l_mask(host->addr_value[0]);
fmc_write(host, FMC_ADDRL, reg);
fmc_pr(ER_DBG, "\t|-Set ADDRL[%#x]%#x\n", FMC_ADDRL, reg);
reg = op_cfg_fm_cs(host->cmd_op.cs) | OP_CFG_OEN_EN |
op_cfg_mem_if_type(spi->erase->iftype) |
op_cfg_addr_num(STD_OP_ADDR_NUM) |
op_cfg_dummy_num(spi->erase->dummy);
fmc_write(host, FMC_OP_CFG, reg);
fmc_pr(ER_DBG, "\t|-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg);
reg = fmc_op_cmd1_en(ENABLE) | fmc_op_addr_en(ENABLE) |
FMC_OP_REG_OP_START;
fmc_write(host, FMC_OP, reg);
fmc_pr(ER_DBG, "\t|-Set OP[%#x]%#x\n", FMC_OP, reg);
fmc_cmd_wait_cpu_finish(host);
}
static void fmc100_send_cmd_erase(struct fmc_host *host)
{
struct fmc_spi *spi = host->spi;
unsigned char *fmc_ip = NULL;
int ret;
if (ER_DBG)
printf("\n");
fmc_pr(ER_DBG, "\t*-Start send cmd erase!\n");
fmc_ip = get_fmc_ip();
if (*fmc_ip) {
printf("Warning: FMC IP is busy, Please try again.\n");
udelay(1); /* delay 1 us */
return;
} else {
fmc_dev_type_switch(FLASH_TYPE_SPI_NAND);
(*fmc_ip)++;
}
ret = spi->driver->write_enable(spi);
if (ret) {
db_msg("Error: Erase write enable failed! ret: %#x\n", ret);
goto end;
}
host->set_system_clock(spi->erase, ENABLE);
erase_fmc_op(host, spi);
ret = spi->driver->wait_ready(spi);
fmc_pr(ER_DBG, "\t|-Erase wait ready, ret: %#x\n", ret);
if (ret)
db_msg("Error: Erase wait ready fail! status: %#x\n", ret);
end:
(*fmc_ip)--;
fmc_pr(ER_DBG, "\t*-End send cmd erase!\n");
}
static void fmc100_send_cmd_status(struct fmc_host* const host)
{
unsigned int status;
unsigned char addr = STATUS_ADDR;
struct fmc_spi *spi = host->spi;
unsigned char *fmc_ip = get_fmc_ip();
if (*fmc_ip) {
printf("Warning: FMC IP is busy, Please try again.\n");
udelay(1); /* delay 1 us */
return;
} else {
fmc_dev_type_switch(FLASH_TYPE_SPI_NAND);
(*fmc_ip)++;
}
if (host->cmd_op.l_cmd == NAND_CMD_GET_FEATURES)
addr = PROTECT_ADDR;
if (spi_nand_feature_op(spi, GET_OP, addr, &status)) {
printf("get protect addr failed!\n");
(*fmc_ip)--;
return;
}
fmc_pr((ER_DBG || WR_DBG), "\t*-Get status[%#x]: %#x\n", addr, status);
(*fmc_ip)--;
}
static void fmc100_send_cmd_readid(struct fmc_host *host)
{
unsigned int reg;
fmc_pr(BT_DBG, "\t|*-Start send cmd read ID\n");
fmc100_ecc0_switch(host, ENABLE);
reg = fmc_cmd_cmd1(SPI_CMD_RDID);
fmc_write(host, FMC_CMD, reg);
fmc_pr(BT_DBG, "\t||-Set CMD[%#x]%#x\n", FMC_CMD, reg);
reg = READ_ID_ADDR;
fmc_write(host, FMC_ADDRL, reg);
fmc_pr(BT_DBG, "\t||-Set ADDRL[%#x]%#x\n", FMC_ADDRL, reg);
reg = op_cfg_fm_cs(host->cmd_op.cs) | OP_CFG_OEN_EN |
op_cfg_addr_num(READ_ID_ADDR_NUM);
fmc_write(host, FMC_OP_CFG, reg);
fmc_pr(BT_DBG, "\t||-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg);
reg = fmc_data_num_cnt(MAX_SPI_NAND_ID_LEN);
fmc_write(host, FMC_DATA_NUM, reg);
fmc_pr(BT_DBG, "\t||-Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, reg);
reg = fmc_op_cmd1_en(ENABLE) | fmc_op_addr_en(ENABLE) |
fmc_op_read_data_en(ENABLE) | FMC_OP_REG_OP_START;
fmc_write(host, FMC_OP, reg);
fmc_pr(BT_DBG, "\t||-Set OP[%#x]%#x\n", FMC_OP, reg);
host->addr_cycle = 0x0;
fmc_cmd_wait_cpu_finish(host);
fmc100_ecc0_switch(host, DISABLE);
fmc_pr(BT_DBG, "\t|*-End read flash ID\n");
}
static void fmc100_send_cmd_reset(struct fmc_host* const host)
{
unsigned int reg;
fmc_pr(BT_DBG, "\t|*-Start send cmd reset\n");
reg = fmc_cmd_cmd1(SPI_CMD_RESET);
fmc_write(host, FMC_CMD, reg);
fmc_pr(BT_DBG, "\t||-Set CMD[%#x]%#x\n", FMC_CMD, reg);
reg = op_cfg_fm_cs(host->cmd_op.cs) | OP_CFG_OEN_EN;
fmc_write(host, FMC_OP_CFG, reg);
fmc_pr(BT_DBG, "\t||-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg);
reg = fmc_op_cmd1_en(ENABLE) | FMC_OP_REG_OP_START;
fmc_write(host, FMC_OP, reg);
fmc_pr(BT_DBG, "\t||-Set OP[%#x]%#x\n", FMC_OP, reg);
fmc_cmd_wait_cpu_finish(host);
fmc_pr(BT_DBG, "\t|*-End send cmd reset\n");
}
static unsigned char fmc100_read_byte(struct mtd_info* const mtd)
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct fmc_host *host = chip->priv;
unsigned char value;
unsigned char ret_val = 0;
if (host->cmd_op.l_cmd == NAND_CMD_READID) {
value = readb(host->iobase + host->offset);
host->offset++;
if (host->cmd_op.data_no == host->offset)
host->cmd_op.l_cmd = 0;
return value;
}
if (host->cmd_op.cmd == NAND_CMD_STATUS) {
value = fmc_read(host, FMC_STATUS);
if (host->cmd_op.l_cmd == NAND_CMD_GET_FEATURES) {
fmc_pr((ER_DBG || WR_DBG), "\t\tRead BP status: %#x\n",
value);
if (any_bp_enable(value))
ret_val |= NAND_STATUS_WP;
host->cmd_op.l_cmd = NAND_CMD_STATUS;
}
if (!(value & STATUS_OIP_MASK))
ret_val |= NAND_STATUS_READY;
if ((chip->state == FL_ERASING) &&
(value & STATUS_E_FAIL_MASK)) {
fmc_pr(ER_DBG, "\t\tGet erase status: %#x\n", value);
ret_val |= NAND_STATUS_FAIL;
}
if ((chip->state == FL_WRITING) &&
(value & STATUS_P_FAIL_MASK)) {
fmc_pr(WR_DBG, "\t\tGet write status: %#x\n", value);
ret_val |= NAND_STATUS_FAIL;
}
return ret_val;
}
if (host->cmd_op.l_cmd == NAND_CMD_READOOB) {
value = readb((unsigned char *)
((unsigned char *)(uintptr_t)host->dma_oob + host->offset));
host->offset++;
return value;
}
host->offset++;
return readb(host->buffer + host->column + host->offset - 1);
}
static unsigned short fmc100_read_word(struct mtd_info *mtd)
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct fmc_host *host = chip->priv;
return readw(host->buffer + host->column + host->offset);
}
static void fmc100_write_buf(struct mtd_info* const mtd, const u_char* const buf, int len)
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct fmc_host *host = chip->priv;
if (buf == chip->oob_poi)
memcpy((unsigned char *)(uintptr_t)host->dma_oob, buf, len);
else
memcpy((unsigned char *)(uintptr_t)host->dma_buffer, buf, len);
return;
}
static void fmc100_read_buf(struct mtd_info* const mtd, u_char* const buf, int len)
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct fmc_host *host = chip->priv;
if (buf == chip->oob_poi)
memcpy(buf, (unsigned char *)(uintptr_t)host->dma_oob, len);
else
memcpy(buf, (unsigned char *)(uintptr_t)host->dma_buffer, len);
return;
}
static void fmc100_select_chip(struct mtd_info* const mtd, int chipselect)
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct fmc_host *host = chip->priv;
if (chipselect < 0)
return;
if (chipselect > CONFIG_SPI_NAND_MAX_CHIP_NUM)
db_bug("Error: Invalid chipselect: %d\n", chipselect);
if (host->mtd != mtd)
host->mtd = mtd;
if (!(chip->options & NAND_BROKEN_XD))
if ((chip->state == FL_ERASING) || (chip->state == FL_WRITING))
host->cmd_op.l_cmd = NAND_CMD_GET_FEATURES;
}
static void read_nand_id_op(struct fmc_host *host, unsigned int command)
{
host->offset = 0;
host->cmd_op.l_cmd = command & 0xff;
memset((u_char *)(host->iobase), 0, MAX_SPI_NAND_ID_LEN);
host->cmd_op.data_no = MAX_SPI_NAND_ID_LEN;
host->send_cmd_readid(host);
}
static void fmc100_cmdfunc(struct mtd_info* const mtd,
unsigned int command,
int column, int page_addr)
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct fmc_host *host = chip->priv;
switch (command) {
case NAND_CMD_RESET:
host->send_cmd_reset(host);
chip->dev_ready(mtd);
break;
case NAND_CMD_READID:
read_nand_id_op(host, command);
break;
case NAND_CMD_GET_FEATURES:
case NAND_CMD_STATUS:
host->cmd_op.l_cmd = command & 0xff;
host->cmd_op.cmd = NAND_CMD_STATUS;
host->send_cmd_status(host);
break;
case NAND_CMD_READOOB:
host->offset = 0;
host->cmd_op.l_cmd = command & 0xff;
/* use same command as normal read */
host->cmd_op.cmd = command & 0xff;
case NAND_CMD_READ0:
if (command == NAND_CMD_READ0)
host->cmd_op.l_cmd = command & 0xff;
host->addr_value[1] = page_addr;
host->send_cmd_readstart(host);
break;
case NAND_CMD_SEQIN:
host->addr_value[1] = page_addr;
break;
case NAND_CMD_PAGEPROG:
host->offset = 0;
host->send_cmd_pageprog(host);
break;
case NAND_CMD_ERASE1:
host->cmd_op.l_cmd = command & 0xff;
host->addr_value[0] = page_addr;
/* page_addr to block_addr, move right 16 bits */
host->addr_value[1] = (unsigned int)page_addr >> 16;
/* erase operation need a seral of command sequences */
host->send_cmd_erase(host);
break;
case NAND_CMD_ERASE2:
case NAND_CMD_READSTART:
break;
default:
printf("%s not support command 0x%08x:\n", mtd->name, command);
break;
}
}
static int fmc100_dev_ready(struct mtd_info* const mtd)
{
unsigned int reg;
/* just a big number, so move 12 bits */
unsigned long deadline = 1 << 12;
struct nand_chip *chip = mtd_to_nand(mtd);
struct fmc_host *host = chip->priv;
do {
reg = op_cfg_fm_cs(host->cmd_op.cs) | OP_CFG_OEN_EN;
fmc_write(host, FMC_OP_CFG, reg);
reg = fmc_op_read_status_en(ENABLE) | FMC_OP_REG_OP_START;
fmc_write(host, FMC_OP, reg);
fmc_cmd_wait_cpu_finish(host);
reg = fmc_read(host, FMC_STATUS);
if (!(reg & STATUS_OIP_MASK))
return 1;
udelay(1); /* delay 1 us */
} while (deadline--);
#ifndef CONFIG_SYS_NAND_QUIET_TEST
printf("Warning: Wait SPI nand ready timeout, status: %#x\n", reg);
#endif
return 0;
}
/*
* 'host->epm' only use the first oobfree[0] field, it looks very simple, But..
*/
static struct nand_ecclayout nand_ecc_default = {
.oobfree = {{2, 30} }
};
#ifdef CONFIG_FS_MAY_NOT_YAFFS2
static struct nand_ecclayout nand_ecc_2k16bit = {
.oobfree = {{2, 6} }
};
static struct nand_ecclayout nand_ecc_4k16bit = {
.oobfree = {{2, 14} }
};
#endif
static struct nand_config_info fmc_spi_nand_config_table[] = {
{NAND_PAGE_4K, NAND_ECC_24BIT, 200, &nand_ecc_default},
#ifdef CONFIG_FS_MAY_NOT_YAFFS2
{NAND_PAGE_4K, NAND_ECC_16BIT, 128, &nand_ecc_4k16bit},
#endif
{NAND_PAGE_4K, NAND_ECC_8BIT, 128, &nand_ecc_default},
{NAND_PAGE_4K, NAND_ECC_0BIT, 32, &nand_ecc_default},
{NAND_PAGE_2K, NAND_ECC_24BIT, 128, &nand_ecc_default},
#ifdef CONFIG_FS_MAY_NOT_YAFFS2
{NAND_PAGE_2K, NAND_ECC_16BIT, 64, &nand_ecc_2k16bit},
#endif
{NAND_PAGE_2K, NAND_ECC_8BIT, 64, &nand_ecc_default},
{NAND_PAGE_2K, NAND_ECC_0BIT, 32, &nand_ecc_default},
{0, 0, 0, NULL},
};
/*
* Auto-sensed the page size and ecc type value. driver will try each of page
* size and ecc type one by one till flash can be read and wrote accurately.
* so the page size and ecc type is match adaptively without switch on the board
*/
static struct nand_config_info *fmc100_get_config_type_info(struct mtd_info *mtd)
{
struct nand_config_info *best = NULL;
struct nand_config_info *info = fmc_spi_nand_config_table;
struct nand_chip *chip = mtd_to_nand(mtd);
for (; info->layout; info++) {
if (match_page_type_to_size(info->pagetype) != mtd->writesize)
continue;
if (mtd->oobsize < info->oobsize)
continue;
if (!best || (best->ecctype < info->ecctype))
best = info;
}
/* All SPI NAND are small-page,SLC */
chip->bits_per_cell = 1;
return best;
}
static void fmc100_set_oob_info(struct mtd_info* const mtd,
struct nand_config_info* const info)
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct fmc_host *host = chip->priv;
if (info->ecctype != NAND_ECC_0BIT)
mtd->oobsize = info->oobsize;
host->oobsize = mtd->oobsize;
host->dma_oob = host->dma_buffer + host->pagesize;
host->bbm = (u_char *)(host->buffer + host->pagesize +
FMC_BAD_BLOCK_POS);
chip->ecc.layout = info->layout;
/* EB bytes locate in the bottom two of CTRL(30) */
host->epm = (u_short *)(host->buffer + host->pagesize +
chip->ecc.layout->oobfree[0].offset + EB_NORMAL);
#ifdef CONFIG_FS_MAY_NOT_YAFFS2
if (info->ecctype == NAND_ECC_16BIT) {
if (host->pagesize == _2K)
/* EB bits locate in the bottom two of CTRL(6) */
host->epm = (u_short *)(host->buffer + host->pagesize +
chip->ecc.layout->oobfree[0].offset + EB_2K_16_BIT);
else if (host->pagesize == _4K)
/* EB bit locate in the bottom two of CTRL(14) */
host->epm = (u_short *)(host->buffer + host->pagesize +
chip->ecc.layout->oobfree[0].offset + EB_4K_16_BIT);
}
#endif
}
static unsigned int fmc100_get_ecc_reg(struct fmc_host* const host,
struct nand_config_info* const info)
{
host->ecctype = info->ecctype;
return fmc_cfg_ecc_type(match_ecc_type_to_reg(info->ecctype));
}
static unsigned int fmc100_get_page_reg(struct fmc_host* const host,
struct nand_config_info* const info)
{
host->pagesize = match_page_type_to_size(info->pagetype);
return fmc_cfg_page_size(match_page_type_to_reg(info->pagetype));
}
static int fmc100_get_block_reg(struct fmc_host* const host,
struct nand_config_info* const info, unsigned int* const block_val)
{
unsigned int block_reg = 0;
unsigned int page_per_block;
struct mtd_info *mtd = NULL;
if (info == NULL || host == NULL || host->mtd == NULL) {
printf("para err!\n");
return -1;
}
mtd = host->mtd;
host->block_page_mask = ((mtd->erasesize / mtd->writesize) - 1);
page_per_block = mtd->erasesize / match_page_type_to_size(info->pagetype);
switch (page_per_block) {
case _64_PAGES:
block_reg = BLOCK_SIZE_64_PAGE;
break;
case _128_PAGES:
block_reg = BLOCK_SIZE_128_PAGE;
break;
case _256_PAGES:
block_reg = BLOCK_SIZE_256_PAGE;
break;
case _512_PAGES:
block_reg = BLOCK_SIZE_512_PAGE;
break;
default:
db_msg("Can't support block %#x and page %#x size\n",
mtd->erasesize, mtd->writesize);
}
*block_val = fmc_cfg_block_size(block_reg);
return 0;
}
static void fmc100_set_fmc_cfg_reg(struct mtd_info* const mtd,
struct nand_config_info* const type_info)
{
struct nand_chip *chip = NULL;
struct fmc_host *host = NULL;
unsigned int page_reg;
unsigned int ecc_reg;
unsigned int block_reg;
unsigned int reg_fmc_cfg;
if (mtd == NULL || type_info == NULL)
return;
chip = mtd_to_nand(mtd);
if (chip == NULL || chip->priv == NULL)
return;
host = chip->priv;
ecc_reg = fmc100_get_ecc_reg(host, type_info);
page_reg = fmc100_get_page_reg(host, type_info);
if (fmc100_get_block_reg(host, type_info, &block_reg))
return;
reg_fmc_cfg = fmc_read(host, FMC_CFG);
reg_fmc_cfg &= ~(PAGE_SIZE_MASK | ECC_TYPE_MASK | BLOCK_SIZE_MASK);
reg_fmc_cfg |= ecc_reg | page_reg | block_reg;
fmc_write(host, FMC_CFG, reg_fmc_cfg);
/* max number of correctible bit errors per ecc step */
mtd->ecc_strength = host->ecctype;
/* Save value of FMC_CFG and FMC_CFG_ECC0 to turn on/off ECC */
host->fmc_cfg = reg_fmc_cfg;
host->fmc_cfg_ecc0 = (host->fmc_cfg & ~ECC_TYPE_MASK) | ECC_TYPE_0BIT;
fmc_pr(BT_DBG, "\t|-Save FMC_CFG[%#x]: %#x and FMC_CFG_ECC0: %#x\n",
FMC_CFG, host->fmc_cfg, host->fmc_cfg_ecc0);
}
static int fmc100_set_config_info(struct mtd_info* const mtd)
{
struct nand_config_info *type_info = NULL;
fmc_pr(BT_DBG, "\t*-Start match PageSize and EccType\n");
type_info = fmc100_get_config_type_info(mtd);
if (!type_info)
db_bug(ERR_STR_DRIVER "pagesize: %d and oobsize: %d.\n",
mtd->writesize, mtd->oobsize);
/* Set the page_size, ecc_type, block_size of FMC_CFG[0x0] register */
fmc100_set_fmc_cfg_reg(mtd, type_info);
fmc_pr(BT_DBG, "\t|- PageSize %s EccType %s OOB Size %d\n",
nand_page_name(type_info->pagetype),
nand_ecc_name(type_info->ecctype), type_info->oobsize);
fmc100_set_oob_info(mtd, type_info);
fmc_pr(BT_DBG, "\t*-End match PageSize and EccType\n");
return 0;
}
static void fmc100_chip_init(struct nand_chip* const chip)
{
if (!chip->IO_ADDR_R)
chip->IO_ADDR_R = (void __iomem *)CONFIG_FMC_BUFFER_BASE;
chip->IO_ADDR_W = chip->IO_ADDR_R;
memset((char *)chip->IO_ADDR_R, 0xff, FMC100_BUFFER_LEN);
chip->read_byte = fmc100_read_byte;
chip->read_word = fmc100_read_word;
chip->write_buf = fmc100_write_buf;
chip->read_buf = fmc100_read_buf;
chip->select_chip = fmc100_select_chip;
chip->cmdfunc = fmc100_cmdfunc;
chip->dev_ready = fmc100_dev_ready;
chip->chip_delay = FMC_CHIP_DELAY;
chip->options = NAND_BBT_SCANNED | NAND_BROKEN_XD;
chip->ecc.layout = NULL;
chip->ecc.mode = NAND_ECC_NONE;
}
/*****************************************************************************/
int host_data_init(struct fmc_host *host)
{
unsigned long align_mask;
host->addr_cycle = 0;
host->addr_value[0] = 0;
host->addr_value[1] = 0;
host->cache_addr_value[0] = ~0;
host->cache_addr_value[1] = ~0;
fmc_pr(BT_DBG, "\t|||-Malloc memory for dma buffer\n");
host->buforg = kmalloc((FMC100_BUFFER_LEN + FMC_DMA_ALIGN),
GFP_KERNEL);
if (!host->buforg) {
db_msg("Error: Can't malloc memory for SPI Nand driver.\n");
return -ENOMEM;
}
memset(host->buforg, 0xff, FMC100_BUFFER_LEN + FMC_DMA_ALIGN);
/* DMA need 32 bytes alignment */
align_mask = FMC_DMA_ALIGN - 1;
host->dma_buffer = (uintptr_t)(host->buforg + align_mask) & ~align_mask;
host->buffer = (char *)(uintptr_t)host->dma_buffer;
memset(host->buffer, 0xff, FMC100_BUFFER_LEN);
host->send_cmd_pageprog = fmc100_send_cmd_pageprog;
host->send_cmd_status = fmc100_send_cmd_status;
host->send_cmd_readstart = fmc100_send_cmd_readstart;
host->send_cmd_erase = fmc100_send_cmd_erase;
host->send_cmd_readid = fmc100_send_cmd_readid;
host->send_cmd_reset = fmc100_send_cmd_reset;
host->set_system_clock = fmc_set_fmc_system_clock;
return 0;
}
/*****************************************************************************/
int fmc100_host_init(struct fmc_host *host)
{
unsigned int reg;
unsigned int flash_type;
int ret;
if (!host)
return -1;
fmc_pr(BT_DBG, "\t||*-Start SPI Nand host init\n");
host->iobase = (void __iomem *)CONFIG_FMC_BUFFER_BASE;
host->regbase = (void __iomem *)CONFIG_FMC_REG_BASE;
if (!host->iobase || !host->regbase)
return -1;
reg = fmc_read(host, FMC_CFG);
flash_type = (reg & FLASH_SEL_MASK) >> FLASH_SEL_SHIFT;
if (flash_type != FLASH_TYPE_SPI_NAND) {
db_msg("Error: Flash type isn't SPI Nand. reg: %#x\n", reg);
return -ENODEV;
}
if ((reg & OP_MODE_MASK) == OP_MODE_BOOT) {
reg |= fmc_cfg_op_mode(OP_MODE_NORMAL);
fmc_write(host, FMC_CFG, reg);
fmc_pr(BT_DBG, "\t|||-Set CFG[%#x]%#x\n", FMC_CFG, reg);
}
host->fmc_cfg = reg;
host->fmc_cfg_ecc0 = (reg & ~ECC_TYPE_MASK) | ECC_TYPE_0BIT;
reg = fmc_read(host, FMC_GLOBAL_CFG);
if (reg & FMC_GLOBAL_CFG_WP_ENABLE) {
reg &= ~FMC_GLOBAL_CFG_WP_ENABLE;
fmc_write(host, FMC_GLOBAL_CFG, reg);
}
ret = host_data_init(host);
if (ret)
return ret;
/* ecc0_flag for ecc0 read/write */
ecc0_flag = 0;
reg = timing_cfg_tcsh(CS_HOLD_TIME) |
timing_cfg_tcss(CS_SETUP_TIME) |
timing_cfg_tshsl(CS_DESELECT_TIME);
fmc_write(host, FMC_SPI_TIMING_CFG, reg);
fmc_pr(BT_DBG, "\t|||-Set TIMING[%#x]%#x\n", FMC_SPI_TIMING_CFG, reg);
reg = ALL_BURST_ENABLE;
fmc_write(host, FMC_DMA_AHB_CTRL, reg);
fmc_pr(BT_DBG, "\t|||-Set DMA_AHB[%#x]%#x\n", FMC_DMA_AHB_CTRL, reg);
fmc_pr(BT_DBG, "\t|||-Register SPI Nand ID table and ecc probe\n");
fmc_spi_nand_ids_register();
nand_oob_resize = fmc100_set_config_info;
fmc_pr(BT_DBG, "\t||*-End SPI Nand host init\n");
return 0;
}
/*****************************************************************************/
void fmc100_spi_nand_init(struct fmc_host *host)
{
struct nand_chip *chip = host->chip;
fmc_pr(BT_DBG, "\t|*-Start fmc100 SPI Nand init\n");
/* Set system clock and enable controller */
fmc_pr(BT_DBG, "\t||-Set system clock and Enable Controller\n");
if (host->set_system_clock)
host->set_system_clock(NULL, ENABLE);
/* Fmc nand_chip struct init */
fmc_pr(BT_DBG, "\t||-fmc100 struct nand_chip init\n");
chip->priv = host;
fmc100_chip_init(chip);
fmc_pr(BT_DBG, "\t|*-End fmc100 SPI Nand init\n");
}
@@ -0,0 +1,109 @@
/*
* Copyright (c) LOTUS. All rights reserved.
*/
#ifndef __FMC100_H__
#define __FMC100_H__
#include <nand.h>
#include <linux/lotus/fmc_common.h>
#include <linux/lotus/nfc_common.h>
#include "fmc_spi_ids.h"
#include <cpu_func.h>
/*****************************************************************************/
/* These macroes are for debug only, reg option is slower then dma option */
#undef FMC100_SPI_NAND_SUPPORT_REG_READ
#undef FMC100_SPI_NAND_SUPPORT_REG_WRITE
/*****************************************************************************/
#define REG_CNT_HIGH_BLOCK_NUM_SHIFT 10
#define REG_CNT_BLOCK_NUM_MASK 0x3ff
#define REG_CNT_BLOCK_NUM_SHIFT 22
#define REG_CNT_PAGE_NUM_MASK 0x3f
#define REG_CNT_PAGE_NUM_SHIFT 16
#define REG_CNT_WRAP_MASK 0xf
#define REG_CNT_WRAP_SHIFT 12
#define REG_CNT_ECC_OFFSET_MASK 0xfff
#define REG_CNT_ECC_8BIT_OFFSET 1054
#define REG_CNT_ECC_16BIT_OFFSET 1056
#define REG_CNT_ECC_24BIT_OFFSET 1082
#define ERR_STR_DRIVER "Driver does not support this configure "
#define ERR_STR_CHECK "Please make sure the hardware configuration is correct"
/*****************************************************************************/
#define SPI_NAND_MAX_PAGESIZE 4096
#define SPI_NAND_MAX_OOBSIZE 256
#define FMC100_BUFFER_LEN (SPI_NAND_MAX_PAGESIZE + SPI_NAND_MAX_OOBSIZE)
#define FMC100_ADDR_CYCLE_MASK 0x2
/*****************************************************************************/
struct fmc_host {
struct mtd_info *mtd;
struct nand_chip *chip;
struct fmc_spi spi[CONFIG_SPI_NAND_MAX_CHIP_NUM];
struct fmc_cmd_op cmd_op;
void __iomem *iobase;
void __iomem *regbase;
unsigned int fmc_cfg;
unsigned int fmc_cfg_ecc0;
unsigned int offset;
struct device *dev;
/* This is maybe an un-aligment address, only for malloc or free */
char *buforg;
char *buffer;
unsigned long dma_buffer;
unsigned long dma_oob;
unsigned int addr_cycle;
unsigned int addr_value[2]; /* 2 addr */
unsigned int cache_addr_value[2]; /* 2 addr */
unsigned int column;
unsigned int block_page_mask;
unsigned int ecctype;
unsigned int pagesize;
unsigned int oobsize;
int add_partition;
/* BOOTROM read two bytes to detect the bad block flag */
#define FMC_BAD_BLOCK_POS 0
unsigned char *bbm; /* nand bad block mark */
unsigned short *epm; /* nand empty page mark */
unsigned int uc_er;
void (*send_cmd_pageprog)(struct fmc_host *host);
void (*send_cmd_status)(struct fmc_host *host);
void (*send_cmd_readstart)(struct fmc_host *host);
void (*send_cmd_erase)(struct fmc_host *host);
void (*send_cmd_readid)(struct fmc_host *host);
void (*send_cmd_reset)(struct fmc_host *host);
void (*set_system_clock)(struct spi_op *op, int clk_en);
};
void fmc100_ecc0_switch(struct fmc_host *host, unsigned char op);
int fmc100_host_init(struct fmc_host *host);
void fmc100_spi_nand_init(struct fmc_host *host);
void fmc_spi_nand_ids_register(void);
char spi_nand_feature_op(struct fmc_spi* const spi, unsigned char op,
unsigned char addr, unsigned int* const val);
#endif /* End of __FMC100_H__ */
@@ -0,0 +1,124 @@
/*
* Copyright (c) LOTUS. All rights reserved.
*/
#include "fmc100_os.h"
#include <common.h>
/*****************************************************************************/
static struct fmc_host fmc100_host = {
.chip = NULL,
};
/*****************************************************************************/
static void fmc100_driver_probe(struct nand_chip *chip, unsigned char cs)
{
int ret = 0;
struct fmc_host *host = &fmc100_host;
fmc_pr(BT_DBG, "\t*-Start SPI Nand flash driver probe\n");
if (!host->chip) {
/* Fmc ip version check */
if (fmc_ip_ver_check())
db_bug("Error: fmc IP version unknown!\n");
/* Fmc current SPI device type check */
fmc_dev_type_switch(FLASH_TYPE_SPI_NAND);
/* Fmc SPI nand init */
memset((char *)host, 0, sizeof(struct fmc_host));
ret = fmc100_host_init(host);
if (ret) {
db_msg("Error: Host init failed, result: %d\n", ret);
/* Change SPI device type to default */
fmc_dev_type_switch(FLASH_TYPE_DEFAULT);
return;
}
} else {
fmc_pr(BT_DBG, "\t*-SPI Nand host is initialized.\n");
}
host->cmd_op.cs = cs;
host->chip = chip;
fmc100_spi_nand_init(host);
fmc_pr(BT_DBG, "\t*-End SPI Nand flash driver probe.\n");
return;
}
/*****************************************************************************/
static int fmc100_spi_nand_pre_probe(struct nand_chip *chip)
{
uint8_t nand_maf_id;
struct mtd_info *mtd = nand_to_mtd(chip);
struct fmc_host *host = chip->priv;
/* Reset the chip first */
host->send_cmd_reset(host);
chip->dev_ready(mtd);
/* Check the ID */
host->offset = 0;
memset((unsigned char *)(chip->IO_ADDR_R), 0, 0x10);
host->send_cmd_readid(host);
nand_maf_id = readb(chip->IO_ADDR_R);
if (nand_maf_id == 0x00 || nand_maf_id == 0xff) {
printf("Cannot found a valid SPI Nand Device\n");
return 1;
}
return 0;
}
/*****************************************************************************/
int board_nand_init(struct nand_chip *chip)
{
unsigned char chip_num = CONFIG_SPI_NAND_MAX_CHIP_NUM;
static unsigned char cs = 0;
unsigned char *fmc_cs = NULL;
if (get_boot_media() != BOOT_MEDIA_NAND) {
return 1;
}
for (cs = 0; chip_num && (cs < CONFIG_FMC_MAX_CS_NUM); cs++) {
fmc_cs = get_cs_number(cs);
if (*fmc_cs) {
fmc_pr(BT_DBG, "\t\t*-Current CS(%d) is occupied.\n",
cs);
continue;
}
fmc100_driver_probe(chip, cs);
chip_num--;
}
if (chip_num)
return 1;
if (fmc100_spi_nand_pre_probe(chip))
return 1;
return 0;
}
/*****************************************************************************/
static int fmc100_spi_nand_get_ecctype(void)
{
struct fmc_host *host = &fmc100_host;
if (!host->chip) {
printf("SPI Nand flash uninitialized.\n");
return -1;
}
return match_ecc_type_to_yaffs(fmc100_host.ecctype);
}
/*****************************************************************************/
int nand_get_ecctype(void)
{
return fmc100_spi_nand_get_ecctype();
}
@@ -0,0 +1,13 @@
/*
* Copyright (c) LOTUS. All rights reserved.
*/
#ifndef __FMC100_OS_H__
#define __FMC100_OS_H__
#include "fmc100.h"
/*****************************************************************************/
int board_nand_init(struct nand_chip *chip);
#endif /* End of __FMC100_OS_H__ */
@@ -0,0 +1,276 @@
/*
* Copyright (c) LOTUS. All rights reserved.
*/
/*
* Send set/get features command to SPI Nand flash
*/
void spi_nand_set_cmd(struct fmc_host* const host, u_char op, u_char addr,
u_char val)
{
unsigned int reg;
reg = fmc_cmd_cmd1(op ? SPI_CMD_SET_FEATURE : SPI_CMD_GET_FEATURES);
fmc_write(host, FMC_CMD, reg);
fmc_pr(FT_DBG, "\t||||-Set CMD[%#x]%#x\n", FMC_CMD, reg);
fmc_write(host, FMC_ADDRL, addr);
fmc_pr(FT_DBG, "\t||||-Set ADDRL[%#x]%#x\n", FMC_ADDRL, addr);
reg = op_cfg_fm_cs(host->cmd_op.cs) | OP_CFG_OEN_EN |
op_cfg_addr_num(FEATURES_OP_ADDR_NUM);
fmc_write(host, FMC_OP_CFG, reg);
fmc_pr(FT_DBG, "\t||||-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg);
reg = fmc_data_num_cnt(FEATURES_DATA_LEN);
fmc_write(host, FMC_DATA_NUM, reg);
fmc_pr(FT_DBG, "\t||||-Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, reg);
reg = fmc_op_cmd1_en(ENABLE) | fmc_op_addr_en(ENABLE) |
FMC_OP_REG_OP_START;
if (op == SET_OP) {
reg |= fmc_op_write_data_en(ENABLE);
writeb(val, host->iobase);
fmc_pr(FT_DBG, "\t||||-Write IO[%p]%#x\n", host->iobase,
*(u_char *)host->iobase);
} else {
reg |= fmc_op_read_data_en(ENABLE);
}
fmc_write(host, FMC_OP, reg);
fmc_pr(FT_DBG, "\t||||-Set OP[%#x]%#x\n", FMC_OP, reg);
fmc_cmd_wait_cpu_finish(host);
}
char spi_nand_feature_op(struct fmc_spi* const spi, unsigned char op,
unsigned char addr, unsigned int* const val)
{
unsigned int reg;
const char *str[] = {"Get", "Set"};
struct fmc_host *host = NULL;
unsigned char regval;
if (spi == NULL || spi->host == NULL || val == NULL) {
printf("para err!\n");
return -1;
}
host = (struct fmc_host *)spi->host;
if ((op == GET_OP) && (addr == STATUS_ADDR)) {
fmc_pr(SR_DBG, "\n\t\t|*-Start Get Status\n");
reg = op_cfg_fm_cs(host->cmd_op.cs) | OP_CFG_OEN_EN;
fmc_write(host, FMC_OP_CFG, reg);
fmc_pr(SR_DBG, "\t\t||-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg);
reg = fmc_op_read_status_en(ENABLE) | FMC_OP_REG_OP_START;
fmc_write(host, FMC_OP, reg);
fmc_pr(SR_DBG, "\t\t||-Set OP[%#x]%#x\n", FMC_OP, reg);
fmc_cmd_wait_cpu_finish(host);
*val = fmc_read(host, FMC_STATUS);
fmc_pr(SR_DBG, "\t\t|*-End Get Status, result: %#x\n", *val);
return 0;
}
fmc_pr(FT_DBG, "\t|||*-Start %s feature, addr[%#x]\n", str[op], addr);
fmc100_ecc0_switch(host, ENABLE);
regval = *val & 0xff;
spi_nand_set_cmd(host, op, addr, regval);
if (op == GET_OP) {
if (host->iobase == NULL) {
printf("para err!");
return -1;
}
*val = readb(host->iobase);
fmc_pr(FT_DBG, "\t||||-Read IO[%p]%#x\n", host->iobase,
*(u_char *)host->iobase);
}
fmc100_ecc0_switch(host, DISABLE);
fmc_pr(FT_DBG, "\t|||*-End %s Feature[%#x]:%#x\n", str[op], addr, *val);
return 0;
}
/*
* Read status[C0H]:[0]bit OIP, judge whether the device is busy or not
*/
static int spi_general_wait_ready(struct fmc_spi* const spi)
{
unsigned int status;
/* just get a big number, so move left 12 bits */
unsigned int deadline = 1 << 12;
struct fmc_host *host = (struct fmc_host *)spi->host;
do {
if (spi_nand_feature_op(spi, GET_OP, STATUS_ADDR, &status)) {
printf("get feature failed!\n");
return 1;
}
if (!(status & STATUS_OIP_MASK)) {
if ((host->cmd_op.l_cmd == NAND_CMD_ERASE2) && (status & STATUS_E_FAIL_MASK))
return status;
if ((host->cmd_op.l_cmd == NAND_CMD_PAGEPROG) && (status & STATUS_P_FAIL_MASK))
return status;
return 0;
}
udelay(1); /* delay 1 us */
} while (deadline--);
db_msg("Error: SPI Nand wait ready timeout, status: %#x\n", status);
return 1;
}
static void write_enable_fmc_op(struct fmc_host* const host)
{
unsigned int reg;
reg = fmc_read(host, FMC_GLOBAL_CFG);
fmc_pr(WE_DBG, "\t||-Get GLOBAL_CFG[%#x]%#x\n", FMC_GLOBAL_CFG, reg);
if (reg & FMC_GLOBAL_CFG_WP_ENABLE) {
reg &= ~FMC_GLOBAL_CFG_WP_ENABLE;
fmc_write(host, FMC_GLOBAL_CFG, reg);
fmc_pr(WE_DBG, "\t||-Set GLOBAL_CFG[%#x]%#x\n",
FMC_GLOBAL_CFG, reg);
}
reg = fmc_cmd_cmd1(SPI_CMD_WREN);
fmc_write(host, FMC_CMD, reg);
fmc_pr(WE_DBG, "\t||-Set CMD[%#x]%#x\n", FMC_CMD, reg);
reg = op_cfg_fm_cs(host->cmd_op.cs) | OP_CFG_OEN_EN;
fmc_write(host, FMC_OP_CFG, reg);
fmc_pr(WE_DBG, "\t||-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg);
reg = fmc_op_cmd1_en(ENABLE) | FMC_OP_REG_OP_START;
fmc_write(host, FMC_OP, reg);
fmc_pr(WE_DBG, "\t||-Set OP[%#x]%#x\n", FMC_OP, reg);
fmc_cmd_wait_cpu_finish(host);
}
/*
* Send write enable cmd to SPI Nand, status[C0H]:[2]bit WEL must be set 1
*/
static int spi_general_write_enable(struct fmc_spi *spi)
{
unsigned int reg;
struct fmc_host *host = (struct fmc_host *)spi->host;
int ret;
if (WE_DBG)
printf("\n");
fmc_pr(WE_DBG, "\t|*-Start Write Enable\n");
ret = spi_nand_feature_op(spi, GET_OP, STATUS_ADDR, &reg);
if (ret) {
db_msg("Error: Get status reg failed,line:%d\n", __LINE__);
return ret;
}
if (reg & STATUS_WEL_MASK) {
fmc_pr(WE_DBG, "\t||-Write Enable was opened! reg: %#x\n", reg);
return 0;
}
write_enable_fmc_op(host);
#if WE_DBG
spi->driver->wait_ready(spi);
ret = spi_nand_feature_op(spi, GET_OP, STATUS_ADDR, &reg);
if (ret) {
db_msg("Error: Get status reg failed,line:%d\n", __LINE__);
return ret;
}
if (reg & STATUS_WEL_MASK) {
fmc_pr(WE_DBG, "\t||-Write Enable success. reg: %#x\n", reg);
} else {
db_msg("Error: Write Enable failed! reg: %#x\n", reg);
return ret;
}
#endif
fmc_pr(WE_DBG, "\t|*-End Write Enable\n");
return 0;
}
/*
* judge whether SPI Nand support QUAD read/write or not
*/
static int spi_is_quad(struct fmc_spi *spi)
{
const char *if_str[] = {"STD", "DUAL", "DIO", "QUAD", "QIO"};
fmc_pr(QE_DBG, "\t\t|||*-SPI read iftype: %s write iftype: %s\n",
if_str[spi->read->iftype], if_str[spi->write->iftype]);
if ((spi->read->iftype == IF_TYPE_QUAD) ||
(spi->read->iftype == IF_TYPE_QIO) ||
(spi->write->iftype == IF_TYPE_QUAD) ||
(spi->write->iftype == IF_TYPE_QIO))
return 1;
return 0;
}
/*
* Send set features cmd to SPI Nand, feature[B0H]:[0]bit QE would be set
*/
static int spi_general_qe_enable(struct fmc_spi *spi)
{
unsigned int reg;
int op;
int ret;
const char *str[] = {"Disable", "Enable"};
fmc_pr(QE_DBG, "\t||*-Start SPI Nand flash QE\n");
op = spi_is_quad(spi);
fmc_pr(QE_DBG, "\t|||*-End Quad check, SPI Nand %s Quad.\n", str[op]);
ret = spi_nand_feature_op(spi, GET_OP, FEATURE_ADDR, &reg);
if (ret) {
db_msg("Error: Get feature reg failed,line:%d\n", __LINE__);
return ret;
}
fmc_pr(QE_DBG, "\t|||-Get [%#x]feature: %#x\n", FEATURE_ADDR, reg);
if ((reg & FEATURE_QE_ENABLE) == op) {
fmc_pr(QE_DBG, "\t||*-SPI Nand quad was %sd!\n", str[op]);
return op;
}
if (op == ENABLE)
reg |= FEATURE_QE_ENABLE;
else
reg &= ~FEATURE_QE_ENABLE;
ret = spi_nand_feature_op(spi, SET_OP, FEATURE_ADDR, &reg);
if (ret) {
db_msg("Error: set feature reg failed,line:%d\n", __LINE__);
return ret;
}
fmc_pr(QE_DBG, "\t|||-SPI Nand %s Quad\n", str[op]);
spi->driver->wait_ready(spi);
ret = spi_nand_feature_op(spi, GET_OP, FEATURE_ADDR, &reg);
if (ret) {
db_msg("Error: Get feature reg failed,line:%d\n", __LINE__);
return ret;
}
if ((reg & FEATURE_QE_ENABLE) == op)
fmc_pr(QE_DBG, "\t|||-SPI Nand %s Quad succeed!\n", str[op]);
else
db_msg("Error: %s Quad failed! reg: %#x\n", str[op], reg);
fmc_pr(QE_DBG, "\t||*-End SPI Nand %s Quad.\n", str[op]);
return op;
}
/* some spi nand flash don't QUAD enable */
static int spi_do_not_qe_enable(struct fmc_spi *spi)
{
return 0;
}
@@ -0,0 +1,92 @@
/*
* Copyright (c) LOTUS. All rights reserved.
*/
#include <linux/string.h>
#include <linux/lotus/match_table.h>
int reg2type(const struct match_reg_type *table, int length, int reg, int def)
{
while (length-- > 0) {
if (table->reg == reg)
return table->type;
table++;
}
return def;
}
int type2reg(const struct match_reg_type *table, int length, int type, int def)
{
while (length-- > 0) {
if (table->type == type)
return table->reg;
table++;
}
return def;
}
int str2type(const struct match_type_str *table, int length, const char *str,
int size, int def)
{
while (length-- > 0) {
if (!strncmp(table->str, str, size))
return table->type;
table++;
}
return def;
}
const char *type2str(const struct match_type_str *table, int length, int type,
const char *def)
{
while (length-- > 0) {
if (table->type == type)
return table->str;
table++;
}
return def;
}
int match_reg_to_type(const struct match_t *table, int nr_table, int reg, int def)
{
while (nr_table-- > 0) {
if (table->reg == reg)
return table->type;
table++;
}
return def;
}
int match_type_to_reg(const struct match_t *table, int nr_table, int type, int def)
{
while (nr_table-- > 0) {
if (table->type == type)
return table->reg;
table++;
}
return def;
}
int match_data_to_type(const struct match_t *table, int nr_table, const char *data,
int size, int def)
{
while (nr_table-- > 0) {
if (!memcmp(table->data, data, size))
return table->type;
table++;
}
return def;
}
void *match_type_to_data(const struct match_t *table, int nr_table, int type,
void *def)
{
while (nr_table-- > 0) {
if (table->type == type)
return table->data;
table++;
}
return def;
}
@@ -0,0 +1,165 @@
/*
* Copyright (c) LOTUS. All rights reserved.
*/
#include <linux/lotus/nfc_common.h>
#include <linux/lotus/match_table.h>
/*****************************************************************************/
struct nand_flash_dev *(*get_flash_type)(
struct mtd_info *mtd,
struct nand_chip *chip,
unsigned char *id) = NULL;
int (*nand_oob_resize)(struct mtd_info *mtd) = NULL;
/*****************************************************************************/
#if defined(CONFIG_FMC_SPI_NAND) || defined(CONFIG_FMC_NAND)
static struct match_type_str ecc2name[] = {
{NAND_ECC_0BIT, "none" },
{NAND_ECC_8BIT, "4bit/512" },
{NAND_ECC_16BIT, "8bit/512" },
{NAND_ECC_24BIT, "24bit/1K" },
{NAND_ECC_28BIT, "28bit/1K" },
{NAND_ECC_40BIT, "40bit/1K" },
{NAND_ECC_42BIT, "42bit/1K" },
{NAND_ECC_64BIT, "64bit/1K" },
};
const char *nand_ecc_name(int type)
{
return type2str(ecc2name, ARRAY_SIZE(ecc2name), type, "unknown");
}
/*****************************************************************************/
static struct match_type_str page2name[] = {
{ NAND_PAGE_512B, "512" },
{ NAND_PAGE_2K, "2K" },
{ NAND_PAGE_4K, "4K" },
{ NAND_PAGE_8K, "8K" },
{ NAND_PAGE_16K, "16K" },
{ NAND_PAGE_32K, "32K" },
};
const char *nand_page_name(int type)
{
return type2str(page2name, ARRAY_SIZE(page2name), type, "unknown");
}
/*****************************************************************************/
static struct match_reg_type page2size[] = {
{ _512B, NAND_PAGE_512B },
{ _2K, NAND_PAGE_2K },
{ _4K, NAND_PAGE_4K },
{ _8K, NAND_PAGE_8K },
{ _16K, NAND_PAGE_16K },
{ _32K, NAND_PAGE_32K },
};
int nandpage_size2type(int size)
{
return reg2type(page2size, ARRAY_SIZE(page2size), size, NAND_PAGE_2K);
}
int nandpage_type2size(int size)
{
return type2reg(page2size, ARRAY_SIZE(page2size), size, NAND_PAGE_2K);
}
#endif
/*****************************************************************************/
#define ET_ECC_NONE 0x00
#define ET_ECC_4BIT 0x02
#define ET_ECC_8BIT 0x03
#define ET_ECC_24BIT 0x04
#define ET_ECC_40BIT1K 0x05
#define ET_ECC_64BIT1K 0x06
static struct match_reg_type ecc_yaffs_type_t[] = {
{ET_ECC_NONE, NAND_ECC_0BIT},
{ET_ECC_4BIT, NAND_ECC_8BIT},
{ET_ECC_8BIT, NAND_ECC_16BIT},
{ET_ECC_24BIT, NAND_ECC_24BIT},
{ET_ECC_40BIT1K, NAND_ECC_40BIT},
{ET_ECC_64BIT1K, NAND_ECC_64BIT}
};
unsigned char match_ecc_type_to_yaffs(unsigned char type)
{
return type2reg(ecc_yaffs_type_t, ARRAY_SIZE(ecc_yaffs_type_t), type,
ET_ECC_4BIT);
}
/*****************************************************************************/
static struct match_t page_table[] = {
{NAND_PAGE_2K, PAGE_SIZE_2KB, "2K"},
{NAND_PAGE_4K, PAGE_SIZE_4KB, "4K"},
{NAND_PAGE_8K, PAGE_SIZE_8KB, "8K"},
{NAND_PAGE_16K, PAGE_SIZE_16KB, "16K"},
};
unsigned char match_page_reg_to_type(unsigned char reg)
{
return match_reg_to_type(page_table, ARRAY_SIZE(page_table), reg,
NAND_PAGE_2K);
}
unsigned char match_page_type_to_reg(unsigned char type)
{
return match_type_to_reg(page_table, ARRAY_SIZE(page_table), type,
PAGE_SIZE_2KB);
}
const char *match_page_type_to_str(unsigned char type)
{
return match_type_to_data(page_table, ARRAY_SIZE(page_table), type,
"unknown");
}
/*****************************************************************************/
static struct match_t ecc_table[] = {
{NAND_ECC_0BIT, ECC_TYPE_0BIT, "none"},
{NAND_ECC_8BIT, ECC_TYPE_8BIT, "4bit/512"},
{NAND_ECC_16BIT, ECC_TYPE_16BIT, "8bit/512"},
{NAND_ECC_24BIT, ECC_TYPE_24BIT, "24bit/1K"},
{NAND_ECC_28BIT, ECC_TYPE_28BIT, "28bit/1K"},
{NAND_ECC_40BIT, ECC_TYPE_40BIT, "40bit/1K"},
{NAND_ECC_64BIT, ECC_TYPE_64BIT, "64bit/1K"},
};
unsigned char match_ecc_reg_to_type(unsigned char reg)
{
return match_reg_to_type(ecc_table, ARRAY_SIZE(ecc_table), reg,
NAND_ECC_8BIT);
}
unsigned char match_ecc_type_to_reg(unsigned char type)
{
return match_type_to_reg(ecc_table, ARRAY_SIZE(ecc_table), type,
ECC_TYPE_8BIT);
}
const char *match_ecc_type_to_str(unsigned char type)
{
return match_type_to_data(ecc_table, ARRAY_SIZE(ecc_table), type,
"unknown");
}
/*****************************************************************************/
static struct match_t page_type_size_table[] = {
{NAND_PAGE_2K, _2K, NULL},
{NAND_PAGE_4K, _4K, NULL},
{NAND_PAGE_8K, _8K, NULL},
{NAND_PAGE_16K, _16K, NULL},
};
unsigned char match_page_size_to_type(unsigned int size)
{
return match_reg_to_type(page_type_size_table,
ARRAY_SIZE(page_type_size_table), size, NAND_PAGE_2K);
}
unsigned int match_page_type_to_size(unsigned char type)
{
return match_type_to_reg(page_type_size_table,
ARRAY_SIZE(page_type_size_table), type, _2K);
}
@@ -0,0 +1,29 @@
config FMC_SPI_NOR
bool "lotus SPI Nor Flash Interface support"
depends on FMC
help
Enable the lotus SPI Nor flash support.
If unsure, say N
config SPI_BLOCK_PROTECT
bool "Spi Nor Device BP(Block Protect) Support"
depends on FMC_SPI_NOR
help
SFC supports BP(Block Protect) feature to preestablish a series
area to avoid writing and erasing, except to reading. With this macro
definition we can get the BP info which was setted before. The
BOTTOM/TOP bit is setted to BOTTOM, it means the lock area starts
from 0 address.
If unsure, say N
config DTR_MODE_SUPPORT
bool "Spi Nor Device DTR mode Support"
depends on FMC_SPI_NOR
default n
help
To support DTR mode
If unsure, say N
@@ -0,0 +1,4 @@
obj-y = spi_nor.o
obj-$(CONFIG_FMC_SPI_NOR) += fmc100/
@@ -0,0 +1,4 @@
ccflags-y += -I$(srctree)/lotus/drivers/mtd
obj-y += fmc100.o fmc100_os.o fmc_spi_nor_ids.o
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,169 @@
/*
* Copyright (c) LOTUS. All rights reserved.
*/
#ifndef __FMC100_H__
#define __FMC100_H__
#include <spi_flash.h>
#include <linux/lotus/fmc_common.h>
#include "fmc_spi_ids.h"
#include <cpu_func.h>
/* These macroes are for debug only, reg read is slower then dma read,
so we don't define it */
#undef FMC100_SPI_NOR_SUPPORT_REG_READ
#undef FMC100_SPI_NOR_SUPPORT_REG_WRITE
#define FMC100_DMA_WR_MAX_SIZE 4096
#define FMC100_DMA_WR_MASK (FMC100_DMA_WR_MAX_SIZE - 1)
#define FMC100_DMA_RD_MAX_SIZE (_2M)
#define FMC100_DMA_RD_MASK (FMC100_DMA_RD_MAX_SIZE - 1)
#define FMC100_REG_RD_MAX_SIZE (_16K)
#define FMC100_REG_RD_MASK (FMC100_REG_RD_MAX_SIZE - 1)
#define SPI_NOR_CR_SHIFT 8 /* Config Register shift(bit) */
#define SPI_NOR_CR_4BYTE_SHIFT 5
#define SPI_NOR_CR_4BYTE_MASK (1 << SPI_NOR_CR_4BYTE_SHIFT)
#define spi_nor_get_4byte_by_cr(cr) (((cr) & SPI_NOR_CR_4BYTE_MASK) \
>> SPI_NOR_CR_4BYTE_SHIFT)
#define SPI_NOR_CR_QE_SHIFT 1
#define SPI_NOR_CR_QE_MASK (1 << SPI_NOR_CR_QE_SHIFT)
#define spi_nor_get_qe_by_cr(cr) (((cr) & SPI_NOR_CR_QE_MASK) \
>> SPI_NOR_CR_QE_SHIFT)
#define SPI_NOR_CR_RST_HOLD_SHIFT 7
#define SPI_NOR_CR_RST_HOLD_MASK (1 << SPI_NOR_CR_RST_HOLD_SHIFT)
#define SPI_NOR_CR_HOLD_MASK (~(1 << SPI_NOR_CR_RST_HOLD_SHIFT))
#define spi_nor_get_rst_hold_by_cr(cr) (((cr) & SPI_NOR_CR_RST_HOLD_MASK) \
>> SPI_NOR_CR_RST_HOLD_SHIFT)
#define spi_nor_set_rst_by_cr(cr) ((cr) | SPI_NOR_CR_RST_HOLD_MASK)
#define spi_nor_set_hold_by_cr(cr) ((cr) & SPI_NOR_CR_HOLD_MASK)
#ifdef CONFIG_SPI_BLOCK_PROTECT
#define DEBUG_SPI_NOR_BP 0
#define SPI_NOR_SR_SRWD_SHIFT 7
#define SPI_NOR_SR_SRWD_MASK (1 << SPI_NOR_SR_SRWD_SHIFT)
#define SPI_NOR_SR_BP0_SHIFT 2
#define SPI_NOR_SR_BP_WIDTH_4 0xf
#define SPI_NOR_SR_BP_MASK_4 (SPI_NOR_SR_BP_WIDTH_4 << SPI_NOR_SR_BP0_SHIFT)
#define SPI_NOR_SR_BP_WIDTH_3 0x7
#define SPI_NOR_SR_BP_MASK_3 (SPI_NOR_SR_BP_WIDTH_3 << SPI_NOR_SR_BP0_SHIFT)
#define SPI_NOR_SR_TB_SHIFT 3
#define SPI_NOR_SR_TB_MASK (1 << SPI_NOR_SR_TB_SHIFT)
#define SPI_NOR_SR_TB_SHIFT_S 5
#define SPI_NOR_SR_TB_MASK_S (1 << SPI_NOR_SR_TB_SHIFT_S)
#define spi_bp_bottom_rdcr_set_s(config) ((config) | \
(0x01 << SPI_NOR_SR_TB_SHIFT_S))
#define spi_bp_bottom_rdcr_set(config) ((config) | \
(0x01 << SPI_NOR_SR_TB_SHIFT))
#define spi_bp_bottom_rdsr_set_1(bp_num) (0x1 << (2 + bp_num))
#define spi_bp_bottom_rdsr_set_0(bp_num) (~(0x1 << (2 + bp_num)))
#define lock_level_max(bp_num) (((0x01) << bp_num) - 1)
#endif /* CONFIG_SPI_BLOCK_PROTECT */
#ifdef CONFIG_DTR_MODE_SUPPORT
#define DTR_DUMMY_CYCLES_4 4
#define DTR_DUMMY_CYCLES_6 6
#define DTR_DUMMY_CYCLES_8 8
#define DTR_DUMMY_CYCLES_10 10
#define dtr_rdcr_dc_mask(_val) (_val)
#define DTR_RDSR_DC_SHIFT 14
#define DTR_RDCR_DC_SHIFT 6
#define dtr_rdcr_dc_bit_clr(_reg) ((_reg) & (~(3 << DTR_RDSR_DC_SHIFT)))
#define dtr_gd_dc_bit_clr(_reg) ((_reg) & (0xfe))
#define dtr_py_dc_bit_clr(_reg) ((_reg) & (0xf7))
#define dtr_xmc_dc_bit_clr(_reg) ((_reg) & (~(3)))
#define DTR_MODE_REQUEST_SHIFT 2
#define DTR_TRAINING_POINT_NUM 12
#define DTR_TRAINING_POINT_MASK 12
#define dtr_training_point_clr(_reg) ((_reg) & (~(0xf << 12)))
#define DTR_TRAINING_CMP_ADDR_SHIFT (CONFIG_BOOT_HEAD_SIZE)
#define DTR_TRAINING_CMP_ADDR_S (CONFIG_SYS_TEXT_BASE_ORI + \
DTR_TRAINING_CMP_ADDR_SHIFT)
#define DTR_TRAINING_CMP_LEN 0x100
#define SFDP_BUF_LEN 0x33
#define SFDP_DTR_BIT_SHIFT 3
#define SFDP_DTR_BYTE_SHIFT 0x32
#define SFDP_DTR_BIT_MASK 0x1
#define DEVICE_ID_SUPPORT_DTR_WINBOND 0x70
#endif /* CONFIG_DTR_MODE_SUPPORT */
/* MXIC Config Register's dummy cycle bits */
#define CR_DUMMY_CYCLE (0x03 << 6)
#define SPI_CMD_RDCR_MX 0x15 /* MXIC Read Config Register */
#define DTR_MODE_REQUEST_SHIFT 2
#define SPI_NOR_SR_WIP_MASK (1 << 0)
struct fmc_host {
struct spi_flash spi_nor_flash[1];
struct mtd_info_ex *spi_nor_info;
struct fmc_spi spi[CONFIG_SPI_NOR_MAX_CHIP_NUM];
void *regbase;
void *iobase;
void (*set_system_clock)(struct spi_op *op, int clk_en);
void (*set_host_addr_mode)(struct fmc_host *host, int enable);
#ifdef CONFIG_SPI_BLOCK_PROTECT
unsigned int start_addr;
unsigned int end_addr;
unsigned char cmp;
unsigned int bp_num;
/* the BT bit location, decide the data num count */
unsigned int bt_loc;
unsigned char level;
#endif
#ifdef CONFIG_DTR_MODE_SUPPORT
unsigned int dtr_mode_en;
unsigned int dtr_training_flag;
#endif
};
#ifdef CONFIG_SPI_BLOCK_PROTECT
unsigned short fmc100_set_spi_lock_info(struct fmc_host *host);
void fmc100_get_bp_lock_level(struct fmc_host *host);
void fmc100_spi_lock(struct fmc_host *host, unsigned char level);
void fmc100_spi_flash_lock(unsigned char cmp, unsigned char level,
unsigned char op);
unsigned short fmc100_handle_bp_rdcr_info(struct fmc_host *host,
u_char cmd);
unsigned char fmc100_bp_to_level(struct fmc_host *host);
unsigned short fmc100_handle_bp_rdsr_info(struct fmc_host *host,
u_char cmd);
#endif
unsigned char spi_general_get_flash_register(struct fmc_spi *spi,
u_char cmd);
#define spiflash_to_host(_spiflash) ((struct fmc_host *)(_spiflash))
#ifdef CONFIG_DTR_MODE_SUPPORT
void fmc_dtr_mode_ctrl(struct fmc_spi *spi, int dtr_en);
unsigned int spi_dtr_training(struct fmc_host *host);
void spi_dtr_to_sdr_switch(struct fmc_spi *spi);
int spi_dtr_dummy_training_set(struct fmc_host *host, int dtr_en);
void fmc_check_spi_dtr_support(struct fmc_spi *spi, u_char *ids, int len);
unsigned int spi_mxic_check_spi_dtr_support(struct fmc_spi *spi);
#endif
void fmc100_read_ids(const struct fmc_spi *, u_char, u_char* const);
void fmc100_op_reg(struct fmc_spi *spi, unsigned char opcode,
unsigned int len, unsigned char optype);
int fmc_spi_nor_probe(struct mtd_info_ex *mtd, struct fmc_spi *spi);
int fmc100_spi_nor_init(struct fmc_host *);
struct spi_flash *fmc100_spi_nor_scan(struct fmc_host *host);
#endif /* End of __FMC100_H__ */
@@ -0,0 +1,183 @@
/*
* Copyright (c) LOTUS. All rights reserved.
*/
#include "fmc100_os.h"
#include <common.h>
#include <linux/mtd/mtd.h>
#include <asm/io.h>
static struct fmc_host fmc100_host;
static struct mtd_info_ex fmc100_spi_nor_info = {.type = 0, };
static void fmc100_driver_shutdown(void)
{
unsigned int start_up_addr_mode = get_fmc_boot_mode();
if (start_up_addr_mode == SPI_NOR_ADDR_MODE_3_BYTES) {
int ix;
struct fmc_host *host = &fmc100_host;
struct fmc_spi *spi = host->spi;
struct mtd_info_ex *spi_nor_info = &fmc100_spi_nor_info;
fmc_dev_type_switch(FLASH_TYPE_SPI_NOR);
for (ix = 0; ix < spi_nor_info->numchips; ix++, spi++) {
/* 4 byte addr mode */
if (spi->addrcycle == 4) {
spi->driver->wait_ready(spi);
spi->driver->entry_4addr(spi, DISABLE);
}
}
}
}
static int fmc100_driver_probe(void)
{
int ret;
struct fmc_host *host = &fmc100_host;
fmc_pr(BT_DBG, "\t|*-Start SPI nor flash driver probe\n");
/* FMC ip version check */
ret = fmc_ip_ver_check();
if (ret) {
fmc_pr(BT_DBG, "\t|*-IP version unknown, result: %d\n", ret);
return ret;
}
fmc_pr(BT_DBG, "\t||-SPI nor host init\n");
memset((char *)host, 0, sizeof(struct fmc_host));
ret = fmc100_spi_nor_init(host);
if (ret) {
fmc_pr(BT_DBG, "Error: SPI Nor init failed, ret: %d\n", ret);
goto end;
}
end:
fmc_pr(BT_DBG, "\t|*-End SPI nor flash driver probe\n");
return ret;
}
struct mtd_info_ex *fmc100_get_spi_nor_info(struct spi_flash *spi_nor_flash)
{
if (fmc100_spi_nor_info.type == 0) {
if (fmc100_spi_nor_probe(NULL) == NULL)
return NULL;
}
return &fmc100_spi_nor_info;
}
static void fmc100_probe_spi_size(struct spi_flash* const spi_nor_flash)
{
struct fmc_host *host = &fmc100_host;
struct fmc_spi *spi = host->spi;
unsigned int ix;
unsigned int total = 0;
struct mtd_info_ex *spi_nor_info = host->spi_nor_info;
fmc_pr(BT_DBG, "\t|*-Start probe SPI nor flash total size\n");
for (ix = 0; ix < spi_nor_info->numchips; ix++, spi++) {
fmc_pr(BT_DBG, "\t||-SPI nor flash[%d]: %dMB\n", ix,
(u_int)byte_to_mb(spi->chipsize));
total += spi->chipsize;
}
spi_nor_flash->size = total;
fmc_pr(BT_DBG, "\t|*-Probe SPI nor total size: %dMB, chip num: %d\n",
byte_to_mb(spi_nor_flash->size), spi_nor_info->numchips);
}
struct spi_flash *fmc100_spi_nor_probe(struct mtd_info_ex **spi_nor_info)
{
static struct spi_flash *spi_nor_flash = NULL;
fmc_pr(BT_DBG, "\t*-Start SPI Nor flash probe\n");
if (spi_nor_flash) {
fmc_pr(BT_DBG, "\t*-SPI Nor flash is initialized.\n");
return spi_nor_flash;
}
/* Check current SPI device type whether SPI nor */
fmc_dev_type_switch(FLASH_TYPE_SPI_NOR);
fmc_pr(BT_DBG, "\t|-SPI Nor flash driver probe\n");
if (!fmc100_driver_probe()) {
struct fmc_host *host = &fmc100_host;
fmc_pr(BT_DBG, "\t|-SPI nor flash scanning\n");
host->spi_nor_info = &fmc100_spi_nor_info;
spi_nor_flash = fmc100_spi_nor_scan(host);
if (spi_nor_flash) {
*spi_nor_info =
fmc100_get_spi_nor_info(spi_nor_flash);
if (*spi_nor_info == NULL)
return NULL;
fmc100_probe_spi_size(spi_nor_flash);
printf("SPI Nor total size: %uMB\n",
byte_to_mb(spi_nor_flash->size));
fmc_pr(BT_DBG, "\t|-Add func hook for Reset cmd\n");
add_shutdown(fmc100_driver_shutdown);
goto end;
}
}
spi_nor_flash = NULL;
fmc100_spi_nor_info.type = 0;
end:
/* Change SPI device type to default */
fmc_dev_type_switch(FLASH_TYPE_DEFAULT);
fmc_pr(BT_DBG, "\t*-End SPI Nor flash probe\n");
return spi_nor_flash;
}
#ifdef CONFIG_SPI_BLOCK_PROTECT
void fmc100_spi_flash_lock(unsigned char cmp, unsigned char level,
unsigned char op)
{
struct fmc_host *host = &fmc100_host;
struct spi_flash *nor = host->spi_nor_flash;
host->cmp = cmp;
if (op == BP_OP_GET) {
puts("Get spi lock information\n");
if (host->level) {
if (host->level == nor->bp_level_max)
puts("all blocks are locked.\n");
else
printf("level: %d\n", host->level);
printf("Spi is locked. lock address[0 => %#x]\n",
host->end_addr);
} else {
puts("all blocks are unlocked.\n");
}
return;
}
if (op == BP_OP_SET) {
if (level) {
if (level == nor->bp_level_max)
puts("lock all blocks.\n");
else
printf("lock level: %d\n", level);
} else {
puts("unlock all block.\n");
}
fmc100_spi_lock(host, level);
return;
}
printf("%s ERROR: Invalid optin argument!", __func__);
}
#endif /* CONFIG_SPI_BLOCK_PROTECT */
@@ -0,0 +1,14 @@
/*
* Copyright (c) LOTUS. All rights reserved.
*/
#ifndef __FMC100_OS_H__
#define __FMC100_OS_H__
#include <linux/lotus/fmc.h>
#include "fmc100.h"
/*****************************************************************************/
#endif /* End of __FMC100_OS_H__ */
@@ -0,0 +1,280 @@
/*
* Copyright (c) LOTUS. All rights reserved.
*/
#define GD_SPI_CMD_RDSR1 0x35 /* Read Status Register-1 */
/*
* enable QE bit if QUAD read write is supported by GD "25qxxx" SPI
*/
#ifndef CONFIG_DTR_MODE_SUPPORT
static void clear_dtr_mode_gd(struct fmc_spi *spi)
{
unsigned int regval;
struct fmc_host *host = (struct fmc_host *)spi->host;
unsigned char config;
unsigned short reg;
config = spi_general_get_flash_register(spi, SPI_CMD_RDSR3);
fmc_pr(DTR_DB, "Get Status Register-3[%#x]\n", config);
regval = fmc_read(host, FMC_GLOBAL_CFG);
if ((regval >> DTR_MODE_REQUEST_SHIFT) & 0x1) {
regval &= (~(1 << DTR_MODE_REQUEST_SHIFT));
regval = fmc_write(host, FMC_GLOBAL_CFG, regval);
}
if (config & 0x1) {
config &= (0xfe);
reg = ((unsigned short)config);
writew(reg, host->iobase);
spi->driver->write_enable(spi);
fmc100_op_reg(spi, SPI_CMD_WRSR3, sizeof(unsigned char),
fmc_op_write_data_en(ENABLE));
}
}
#endif
static void set_cmd(struct fmc_spi* const spi, u8 cmd, u8 len)
{
struct fmc_host *host = (struct fmc_host *)spi->host;
unsigned int regval;
regval = fmc_cmd_cmd1(cmd);
fmc_write(host, FMC_CMD, regval);
fmc_pr(QE_DBG, "\t|-Set CMD[%#x]%#x\n", FMC_CMD, regval);
regval = op_cfg_fm_cs(spi->chipselect) | OP_CFG_OEN_EN;
fmc_write(host, FMC_OP_CFG, regval);
fmc_pr(QE_DBG, "\t|-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, regval);
regval = fmc_data_num_cnt(len);
fmc_write(host, FMC_DATA_NUM, regval);
fmc_pr(QE_DBG, "\t|-Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, regval);
regval = fmc_op_cmd1_en(ENABLE) |
fmc_op_write_data_en(ENABLE) |
FMC_OP_REG_OP_START;
fmc_write(host, FMC_OP, regval);
fmc_pr(QE_DBG, "\t|-Set OP[%#x]%#x\n", FMC_OP, regval);
fmc_cmd_wait_cpu_finish(host);
spi->driver->wait_ready(spi);
}
static int spi_gd25q256_entry_4addr(struct fmc_spi *spi, int enable)
{
return 0;
}
static int gd_16pin_qe_enable(struct fmc_spi * const spi, int op)
{
struct fmc_host *host = (struct fmc_host *)spi->host;
unsigned char config;
unsigned char status;
const char *str[] = {"Disable", "Enable"};
#ifndef CONFIG_DTR_MODE_SUPPORT
clear_dtr_mode_gd(spi);
#endif
config = spi_general_get_flash_register(spi, GD_SPI_CMD_RDSR1);
fmc_pr(QE_DBG, "\t|-Read GD SR-1[%#x], val: %#x\n", GD_SPI_CMD_RDSR1,
config);
if (op && (op == spi_nor_get_qe_by_cr(config))) {
fmc_pr(QE_DBG, "\t* Quad was %sd, status:%#x\n", str[op],
config);
return op;
}
/* First, we enable/disable QE for 16Pin GD flash, use WRSR[01h] cmd */
fmc_pr(QE_DBG, "\t|-First, 16Pin GD flash %s Quad.\n", str[op]);
status = spi_general_get_flash_register(spi, SPI_CMD_RDSR);
fmc_pr(QE_DBG, "\t|-Read Status Register[%#x]%#x\n", SPI_CMD_RDSR,
status);
spi->driver->write_enable(spi);
if (op)
config |= SPI_NOR_CR_QE_MASK;
else
config &= ~SPI_NOR_CR_QE_MASK;
writeb(status, host->iobase);
writeb(config, host->iobase + SPI_NOR_SR_LEN);
fmc_pr(QE_DBG, "\t|-Write IO[%p]%#x\n", host->iobase,
*(unsigned short *)host->iobase);
set_cmd(spi, SPI_CMD_WRSR, SPI_NOR_SR_LEN + SPI_NOR_CR_LEN);
config = spi_general_get_flash_register(spi, GD_SPI_CMD_RDSR1);
fmc_pr(QE_DBG, "\t|-Read GD SR-1[%#x], val: %#x\n", GD_SPI_CMD_RDSR1,
config);
if (op == spi_nor_get_qe_by_cr(config)) {
fmc_pr(QE_DBG, "\t|-16P %s Quad success reg: %#x\n", str[op],
config);
return op;
} else {
fmc_pr(QE_DBG, "\t|-16P %s Quad failed, reg: %#x\n", str[op],
config);
}
return 0;
}
static void gd_8pin_qe_enable(struct fmc_spi * const spi, int op)
{
unsigned char config;
unsigned char status;
const char *str[] = {"Disable", "Enable"};
struct fmc_host *host = (struct fmc_host *)spi->host;
fmc_pr(QE_DBG, "\t|-Second, 8Pin GD flash %s Quad.\n", str[op]);
status = spi_general_get_flash_register(spi, SPI_CMD_RDSR);
fmc_pr(QE_DBG, "\t|-Read Status Register[%#x]:%#x\n", SPI_CMD_RDSR,
status);
if (!(status & STATUS_WEL_MASK))
spi->driver->write_enable(spi);
config = spi_general_get_flash_register(spi, SPI_CMD_RDSR2);
fmc_pr(QE_DBG, "\t|-Read SR-2[%#x], val: %#x\n", SPI_CMD_RDSR2,
config);
if (op && (op == spi_nor_get_qe_by_cr(config))) {
fmc_pr(QE_DBG, "\t* Quad was %sd, status:%#x\n", str[op],
config);
return;
}
if (op)
config |= SPI_NOR_CR_QE_MASK;
else
config &= ~SPI_NOR_CR_QE_MASK;
writeb(config, host->iobase);
fmc_pr(QE_DBG, "\t|-Write IO[%p]%#x\n", host->iobase,
*(unsigned char *)host->iobase);
set_cmd(spi, SPI_CMD_WRSR2, SPI_NOR_CR_LEN);
config = spi_general_get_flash_register(spi, SPI_CMD_RDSR2);
fmc_pr(QE_DBG, "\t|-Read GD SR-2[%#x], val: %#x\n", SPI_CMD_RDSR2,
config);
if (op == spi_nor_get_qe_by_cr(config))
fmc_pr(QE_DBG, "\t|-8P %s Quad success, reg: %#x.\n", str[op],
config);
else
db_msg("Error: %s Quad failed, reg: %#x\n", str[op], config);
return;
}
static int spi_gd25qxxx_qe_enable(struct fmc_spi *spi)
{
unsigned char op;
const char *str[] = {"Disable", "Enable"};
if (!spi || !spi->host)
return -1;
op = spi_is_quad(spi);
fmc_pr(QE_DBG, "\t*-Start GD SPI nor %s Quad.\n", str[op]);
/* First, we enable/disable QE for 16Pin GD flash, use WRSR[01h] cmd */
if (gd_16pin_qe_enable(spi, op))
goto qe_end;
/* Second, we enable/disable QE for 8Pin GD flash, use WRSR2[31h] cmd */
gd_8pin_qe_enable(spi, op);
qe_end:
/* Enable the reset pin when working on dual mode for 8PIN */
if (!op)
spi_nor_reset_pin_enable(spi, ENABLE);
fmc_pr(QE_DBG, "\t*-End GD SPI nor %s Quad end.\n", str[op]);
return op;
}
#ifdef CONFIG_DTR_MODE_SUPPORT
void spi_gd_set_reg(struct fmc_spi *spi)
{
unsigned int regval;
struct fmc_host *host = (struct fmc_host *)spi->host;
regval = fmc_cmd_cmd1(SPI_CMD_WRSR3);
fmc_write(host, FMC_CMD, regval);
fmc_pr(DTR_DB, " Set CMD[%#x]%#x\n", FMC_CMD, regval);
regval = op_cfg_fm_cs(spi->chipselect) | OP_CFG_OEN_EN;
fmc_write(host, FMC_OP_CFG, regval);
fmc_pr(DTR_DB, " Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, regval);
regval = fmc_data_num_cnt(SPI_NOR_SR_LEN);
fmc_write(host, FMC_DATA_NUM, regval);
fmc_pr(DTR_DB, " Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, regval);
regval = fmc_op_cmd1_en(ENABLE) |
fmc_op_write_data_en(ENABLE) |
FMC_OP_REG_OP_START;
fmc_write(host, FMC_OP, regval);
fmc_pr(DTR_DB, " Set OP[%#x]%#x\n", FMC_OP, regval);
}
int spi_gd_output_driver_strength_set(struct fmc_spi *spi, int dtr_en)
{
unsigned char config;
unsigned char reg;
unsigned char val;
unsigned int ix;
struct fmc_host *host = NULL;
/* DC | Numbers of Dummy clock cycles| Quad IO DTR Read */
/* 0(default)| 8 | 66 */
/* 1 | 10 | 80 */
unsigned int str_dummy[] = {
DTR_DUMMY_CYCLES_8, dtr_rdcr_dc_mask(0),
DTR_DUMMY_CYCLES_10, dtr_rdcr_dc_mask(1),
0, 0,
};
val = 0;
if (!spi || !spi->driver)
return -1;
host = (struct fmc_host *)spi->host;
if (!host)
return -1;
/* get the RDCR and RDSR */
spi->driver->wait_ready(spi);
/* setting the DC value to match high system clock */
config = spi_general_get_flash_register(spi, SPI_CMD_RDSR3);
fmc_pr(DTR_DB, "Get Status Register-3[%#x]\n", config);
if (dtr_en == ENABLE) {
/* setting DC value */
fmc_pr(DTR_DB, "Get the dummy value[%#x]\n", spi->read->dummy);
/* Only the element with an even number of arrays is required, so increase is 2 */
for (ix = 0; str_dummy[ix]; ix += _2B) {
if (spi->read->dummy < str_dummy[ix])
break;
val = (unsigned char)str_dummy[ix + 1];
}
} else {
val = dtr_rdcr_dc_mask(0);
}
reg = dtr_gd_dc_bit_clr(config) | val;
fmc_pr(DTR_DB, "Get the reg value[%#x]\n", reg);
spi->driver->write_enable(spi);
writew(reg, host->iobase);
fmc_pr(DTR_DB, "Write IO[%p]%#x\n", host->iobase,
*(unsigned short *)host->iobase);
spi_gd_set_reg(spi);
fmc_cmd_wait_cpu_finish(host);
config = spi_general_get_flash_register(spi, SPI_CMD_RDSR3);
fmc_pr(DTR_DB, "Get Status Register-3[%#x]\n", config);
if ((config & 0x1) != (unsigned char)val) {
printf("* Set DC dummy fail.\n");
return -1;
}
return 0;
}
#endif /* CONFIG_DTR_MODE_SUPPORT */
@@ -0,0 +1,401 @@
/*
* Copyright (c) LOTUS. All rights reserved.
*/
/*
Get status/config register value from SPI Nor flash
*/
unsigned char spi_general_get_flash_register(struct fmc_spi * const spi, u_char cmd)
{
unsigned char status;
unsigned int reg;
struct fmc_host *host = (struct fmc_host *)spi->host;
host->set_system_clock(NULL, ENABLE);
fmc_pr(SR_DBG, "\t * Start get flash Register[%#x]\n", cmd);
reg = op_cfg_fm_cs(spi->chipselect) | OP_CFG_OEN_EN;
fmc_write(host, FMC_OP_CFG, reg);
if (cmd == SPI_CMD_RDSR) {
reg = fmc_op_read_status_en(ENABLE) | FMC_OP_REG_OP_START;
goto cmd_config_done;
}
fmc_write(host, FMC_CMD, cmd);
fmc_pr(SR_DBG, "\t Set CMD[%#x]%#x\n", FMC_CMD, cmd);
reg = fmc_data_num_cnt(SPI_NOR_CR_LEN);
fmc_write(host, FMC_DATA_NUM, reg);
fmc_pr(SR_DBG, "\t Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, reg);
reg = fmc_op_cmd1_en(ENABLE) | fmc_op_read_data_en(ENABLE) |
FMC_OP_REG_OP_START;
cmd_config_done:
fmc_write(host, FMC_OP, reg);
fmc_pr(SR_DBG, "\t Set OP[%#x]%#x\n", FMC_OP, reg);
fmc_cmd_wait_cpu_finish(host);
if (cmd == SPI_CMD_RDSR)
status = fmc_read(host, FMC_STATUS);
else
status = readb(host->iobase);
fmc_pr(SR_DBG, "\t * End get flash Register[%#x], val: %#x\n", cmd,
status);
return status;
}
/*
Read status[C0H]:[0]bit OIP, judge whether the device is busy or not
*/
static int spi_general_wait_ready(struct fmc_spi * const spi)
{
unsigned char status;
/* need a big number,so move left 20 bit */
unsigned int deadline = 1 << 20;
if (!spi || !spi->host)
return -1;
do {
status = spi_general_get_flash_register(spi, SPI_CMD_RDSR);
if (!(status & SPI_NOR_SR_WIP_MASK))
return 0;
udelay(1); /* delay 1 us */
} while (deadline--);
db_msg("Error: SPI nor wait ready timeout, status[%#x]\n", status);
return 1;
}
/*
Send write enable cmd to SPI Nor, status[C0H]:[2]bit WEL must be set 1
*/
static int spi_general_write_enable(struct fmc_spi *spi)
{
unsigned char status;
unsigned int reg;
if (!spi || !spi->driver || !spi->host) {
printf("%s:spi data is NULL, please check input parameter\n", __func__);
return -1;
}
struct fmc_host *host = (struct fmc_host *)spi->host;
if (WE_DBG)
printf("\n");
fmc_pr(WE_DBG, "\t * Start Write Enable\n");
status = spi_general_get_flash_register(spi, SPI_CMD_RDSR);
fmc_pr(WE_DBG, "\t Read Status Register[%#x]:%#x\n", SPI_CMD_RDSR,
status);
if (status & STATUS_WEL_MASK) {
fmc_pr(WE_DBG, "\t Write Enable was opened! reg: %#x\n",
status);
return 0;
}
reg = fmc_read(host, FMC_GLOBAL_CFG);
if (reg & FMC_GLOBAL_CFG_WP_ENABLE) {
reg &= ~FMC_GLOBAL_CFG_WP_ENABLE;
fmc_write(host, FMC_GLOBAL_CFG, reg);
fmc_pr(WE_DBG, "\t Set GLOBAL_CFG[%#x]%#x\n",
FMC_GLOBAL_CFG, reg);
}
reg = fmc_cmd_cmd1(SPI_CMD_WREN);
fmc_write(host, FMC_CMD, reg);
fmc_pr(WE_DBG, "\t Set CMD[%#x]%#x\n", FMC_CMD, reg);
reg = op_cfg_fm_cs(spi->chipselect) | OP_CFG_OEN_EN;
fmc_write(host, FMC_OP_CFG, reg);
fmc_pr(WE_DBG, "\t Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg);
reg = fmc_op_cmd1_en(ENABLE) | FMC_OP_REG_OP_START;
fmc_write(host, FMC_OP, reg);
fmc_pr(WE_DBG, "\t Set OP[%#x]%#x\n", FMC_OP, reg);
fmc_cmd_wait_cpu_finish(host);
spi->driver->wait_ready(spi);
reg = spi_general_get_flash_register(spi, SPI_CMD_RDSR);
if (reg & STATUS_WEL_MASK) {
fmc_pr(WE_DBG, "\t Write Enable success.reg: %#x\n", reg);
} else {
db_msg("Error: Write Enable failed! status: %#x\n", reg);
return status;
}
fmc_pr(WE_DBG, "\t * End Write Enable\n");
return 0;
}
/*
enable 4byte address for SPI which memory more than 16M
*/
static int spi_general_entry_4addr(struct fmc_spi *spi, int enable)
{
unsigned char status;
unsigned int reg;
const char *str[] = {"Disable", "Enable"};
struct fmc_host *host = NULL;
if (!spi || !spi->driver)
return -1;
host = (struct fmc_host*)spi->host;
if (!host)
return -1;
fmc_pr(AC_DBG, "\t* Start SPI Nor flash %s 4-byte mode.\n",
str[enable]);
if (spi->addrcycle != SPI_NOR_4BYTE_ADDR_LEN) {
fmc_pr(AC_DBG, "\t* Flash isn't support entry 4-byte mode.\n");
return 0;
}
status = spi_general_get_flash_register(spi, SPI_CMD_RDSR3);
fmc_pr(AC_DBG, "\t Read Status Register-3[%#x]:%#x\n", SPI_CMD_RDSR3,
status);
if (spi_nor_get_4byte_by_cr(status) == enable) {
fmc_pr(AC_DBG, "\t* 4-byte was %sd, reg:%#x\n", str[enable],
status);
return 0;
}
if (enable)
reg = SPI_CMD_EN4B;
else
reg = SPI_CMD_EX4B;
fmc_write(host, FMC_CMD, fmc_cmd_cmd1(reg));
fmc_pr(AC_DBG, "\t Set CMD[%#x]%#x\n", FMC_CMD, reg);
reg = op_cfg_fm_cs(spi->chipselect) | OP_CFG_OEN_EN;
fmc_write(host, FMC_OP_CFG, reg);
fmc_pr(AC_DBG, "\t Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg);
reg = fmc_op_cmd1_en(ENABLE) | FMC_OP_REG_OP_START;
fmc_write(host, FMC_OP, reg);
fmc_pr(AC_DBG, "\t Set OP[%#x]%#x\n", FMC_OP, reg);
fmc_cmd_wait_cpu_finish(host);
spi->driver->wait_ready(spi);
status = spi_general_get_flash_register(spi, SPI_CMD_RDSR3);
fmc_pr(AC_DBG, "\t Read SR-3[%#x]:%#x\n", SPI_CMD_RDSR3,
status);
if (spi_nor_get_4byte_by_cr(status) != enable) {
db_msg("Error: %s 4-byte failed! SR3:%#x\n",
str[enable], status);
return status;
}
fmc_pr(AC_DBG, "\t %s 4-byte success, SR3:%#x\n", str[enable], status);
fmc_pr(AC_DBG, "\t* End SPI Nor flash %s 4-byte mode.\n", str[enable]);
return 0;
}
/*
judge whether SPI Nor support QUAD read write or not
*/
unsigned char spi_is_quad(const struct fmc_spi * const spi)
{
char *const if_str[] = {"STD", "DUAL", "DIO", "QUAD", "QIO", "DTR"};
if (!spi) {
printf("%s:spi is NULL, please check input parameter\n", __func__);
return 0;
}
fmc_pr(QE_DBG, "\t\t|*-SPI read iftype: %s write iftype: %s\n",
if_str[spi->read->iftype], if_str[spi->write->iftype]);
if ((spi->read->iftype == IF_TYPE_QUAD) ||
(spi->read->iftype == IF_TYPE_QIO) ||
(spi->write->iftype == IF_TYPE_QUAD) ||
#ifdef CONFIG_DTR_MODE_SUPPORT
(spi->read->iftype == IF_TYPE_DTR) ||
#endif
(spi->write->iftype == IF_TYPE_QIO)
)
return 1;
return 0;
}
static void spi_general_set_cmd(struct fmc_spi * const spi)
{
unsigned int reg;
struct fmc_host *host = (struct fmc_host *)spi->host;
reg = fmc_cmd_cmd1(SPI_CMD_WRSR);
fmc_write(host, FMC_CMD, reg);
fmc_pr(QE_DBG, "\t|-Set CMD[%#x]%#x\n", FMC_CMD, reg);
reg = op_cfg_fm_cs(spi->chipselect) | OP_CFG_OEN_EN;
fmc_write(host, FMC_OP_CFG, reg);
fmc_pr(QE_DBG, "\t|-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg);
reg = fmc_data_num_cnt(SPI_NOR_SR_LEN + SPI_NOR_CR_LEN);
fmc_write(host, FMC_DATA_NUM, reg);
fmc_pr(QE_DBG, "\t|-Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, reg);
reg = fmc_op_cmd1_en(ENABLE) |
fmc_op_write_data_en(ENABLE) |
FMC_OP_REG_OP_START;
fmc_write(host, FMC_OP, reg);
fmc_pr(QE_DBG, "\t|-Set OP[%#x]%#x\n", FMC_OP, reg);
fmc_cmd_wait_cpu_finish(host);
}
/*
* enable QE bit if QUAD read write is supported by SPI
*/
static int spi_general_qe_enable(struct fmc_spi *spi)
{
unsigned char status;
unsigned char config;
unsigned char op;
const char *str[] = {"Disable", "Enable"};
struct fmc_host *host = NULL;
if (!spi || !spi->driver)
return -1;
host = (struct fmc_host *)spi->host;
if (!host || !host->iobase)
return -1;
op = spi_is_quad(spi);
fmc_pr(QE_DBG, "\t*-Start SPI Nor %s Quad.\n", str[op]);
config = spi_general_get_flash_register(spi, SPI_CMD_RDCR);
fmc_pr(QE_DBG, "\t|-Read Config Register[%#x]%#x\n", SPI_CMD_RDCR,
config);
if (op == spi_nor_get_qe_by_cr(config)) {
fmc_pr(QE_DBG, "\t* Quad was %sd, config:%#x\n", str[op],
config);
return op;
}
status = spi_general_get_flash_register(spi, SPI_CMD_RDSR);
fmc_pr(QE_DBG, "\t|-Read Status Register[%#x]%#x\n", SPI_CMD_RDSR,
status);
spi->driver->write_enable(spi);
if (op)
config |= SPI_NOR_CR_QE_MASK;
else
config &= ~SPI_NOR_CR_QE_MASK;
writeb(status, host->iobase);
writeb(config, host->iobase + SPI_NOR_SR_LEN);
fmc_pr(QE_DBG, "\t|-Write IO[%p]%#x\n", host->iobase,
*(unsigned short *)host->iobase);
spi_general_set_cmd(spi);
spi->driver->wait_ready(spi);
config = spi_general_get_flash_register(spi, SPI_CMD_RDCR);
if (op == spi_nor_get_qe_by_cr(config)) {
fmc_pr(QE_DBG, "\t|-%s Quad success, config: %#x\n", str[op],
config);
} else {
db_msg("Error: %s Quad failed! reg: %#x\n", str[op], config);
}
fmc_pr(QE_DBG, "\t* End SPI Nor %s Quad.\n", str[op]);
return op;
}
/*
some chip don't QUAD enable
*/
static int spi_do_not_qe_enable(struct fmc_spi *spi)
{
return 0;
}
static void reset_pin_enable_fmc_op(struct fmc_host* const host,
struct fmc_spi* const spi)
{
unsigned int regval;
regval = fmc_cmd_cmd1(SPI_CMD_WRSR3);
fmc_write(host, FMC_CMD, regval);
fmc_pr(RST_DB, "\t|-Set CMD[%#x]%#x\n", FMC_CMD, regval);
regval = op_cfg_fm_cs(spi->chipselect) | OP_CFG_OEN_EN;
fmc_write(host, FMC_OP_CFG, regval);
fmc_pr(RST_DB, "\t|-Set OP_CFG[%#x]%#x\n",
FMC_OP_CFG, regval);
regval = fmc_data_num_cnt(SPI_NOR_CR_LEN);
fmc_write(host, FMC_DATA_NUM, regval);
fmc_pr(RST_DB, "\t|-Set DATA_NUM[%#x]%#x\n",
FMC_DATA_NUM, regval);
regval = fmc_op_cmd1_en(ENABLE) |
fmc_op_write_data_en(ENABLE) |
FMC_OP_REG_OP_START;
fmc_write(host, FMC_OP, regval);
fmc_pr(RST_DB, "\t|-Set OP[%#x]%#x\n", FMC_OP, regval);
fmc_cmd_wait_cpu_finish(host);
}
/*
* some chip set the mux HOLD#/RESET#/IO3 pin to RESET#, as it is HOLD# default.
*/
static void spi_nor_reset_pin_enable(struct fmc_spi *spi, int enable)
{
unsigned char config;
const char *str[] = {"HOLD#", "RESET#"};
struct fmc_host *host = NULL;
if (!spi || !spi->driver)
return;
host = (struct fmc_host *)spi->host;
if (!host || !host->iobase)
return;
config = spi_general_get_flash_register(spi, SPI_CMD_RDSR3);
fmc_pr(RST_DB, "\t|-Read SR-3[%#x], val: %#x\n",
SPI_CMD_RDSR3, config);
if (enable == spi_nor_get_rst_hold_by_cr(config)) {
fmc_pr(RST_DB, " Device has worked on %s.\n", str[enable]);
return;
}
fmc_pr(RST_DB, " Start to enable %s function.\n", str[enable]);
spi->driver->write_enable(spi);
if (enable)
config = spi_nor_set_rst_by_cr(config);
else
config = spi_nor_set_hold_by_cr(config);
writeb(config, host->iobase);
fmc_pr(RST_DB, "\t|-Write IO[%p]%#x\n", host->iobase,
*(unsigned char *)host->iobase);
reset_pin_enable_fmc_op(host, spi);
spi->driver->wait_ready(spi);
config = spi_general_get_flash_register(spi, SPI_CMD_RDSR3);
fmc_pr(RST_DB, "\t|-Read SR-3[%#x], val: %#x\n",
SPI_CMD_RDSR3, config);
if (enable == spi_nor_get_rst_hold_by_cr(config))
fmc_pr(RST_DB, "\t|- Set the MUX pin to RESET# success!\n");
else
fmc_pr(RST_DB, "\t|- The MUX pin works on HOLD# or DNU!\n");
}
@@ -0,0 +1,91 @@
/*
* Copyright (c) LOTUS. All rights reserved.
*/
#define SPI_NOR_ISSI_QE_SHIFT 6
#define SPI_NOR_ISSI_SRWD_MASK (1 << 7)
#define SPI_NOR_ISSI_QE_MASK (1 << SPI_NOR_ISSI_QE_SHIFT)
#define spi_nor_issi_get_qe(sr) (((sr) & SPI_NOR_ISSI_QE_MASK) \
>> SPI_NOR_ISSI_QE_SHIFT)
static void spi_issi_set_cmd(struct fmc_spi * const spi)
{
unsigned int reg;
struct fmc_host *host = (struct fmc_host *)spi->host;
reg = fmc_cmd_cmd1(SPI_CMD_WRSR);
fmc_write(host, FMC_CMD, reg);
fmc_pr(QE_DBG, "\t|-Set CMD[%#x]%#x\n", FMC_CMD, reg);
reg = op_cfg_fm_cs(spi->chipselect) | OP_CFG_OEN_EN;
fmc_write(host, FMC_OP_CFG, reg);
fmc_pr(QE_DBG, "\t|-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg);
reg = fmc_data_num_cnt(SPI_NOR_SR_LEN);
fmc_write(host, FMC_DATA_NUM, reg);
fmc_pr(QE_DBG, "\t|-Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, reg);
reg = fmc_op_cmd1_en(ENABLE) |
fmc_op_write_data_en(ENABLE) |
FMC_OP_REG_OP_START;
fmc_write(host, FMC_OP, reg);
fmc_pr(QE_DBG, "\t|-Set OP[%#x]%#x\n", FMC_OP, reg);
fmc_cmd_wait_cpu_finish(host);
}
static int spi_issi_qe_enable(struct fmc_spi *spi)
{
unsigned char config;
unsigned char op;
const char *str[] = {"Disable", "Enable"};
struct fmc_host *host = NULL;
if (!spi || !spi->driver)
return -1;
host = (struct fmc_host *)spi->host;
if (!host || !host->iobase)
return -1;
op = spi_is_quad(spi);
fmc_pr(QE_DBG, "\t*-Start SPI Nor %s Quad.\n", str[op]);
config = spi_general_get_flash_register(spi, SPI_CMD_RDSR);
fmc_pr(QE_DBG, "\t|-Read Config Register[%#x]%#x\n", SPI_CMD_RDSR,
config);
if (op == spi_nor_issi_get_qe(config)) {
fmc_pr(QE_DBG, "\t* Quad was %sd, config:%#x\n", str[op],
config);
return op;
}
spi->driver->write_enable(spi);
config = spi_general_get_flash_register(spi, SPI_CMD_RDSR);
config &= ~SPI_NOR_ISSI_SRWD_MASK;
if (op)
config |= SPI_NOR_ISSI_QE_MASK;
else
config &= ~SPI_NOR_ISSI_QE_MASK;
writeb(config, host->iobase);
fmc_pr(QE_DBG, "\t|-Write IO[%p]%#x\n", host->iobase,
*(unsigned short *)host->iobase);
spi_issi_set_cmd(spi);
spi->driver->wait_ready(spi);
config = spi_general_get_flash_register(spi, SPI_CMD_RDSR);
if (op == spi_nor_issi_get_qe(config)) {
fmc_pr(QE_DBG, "\t|-%s Quad success, config: %#x\n", str[op],
config);
} else {
db_msg("Error: %s Quad failed! reg: %#x\n", str[op], config);
}
fmc_pr(QE_DBG, "\t* End SPI Nor %s Quad.\n", str[op]);
return op;
}
@@ -0,0 +1,91 @@
/*
* Copyright (c) LOTUS. All rights reserved.
*/
#define SPI_CMD_FIRST_RESET_4ADDR 0x66
#define SPI_CMD_SECOND_RESET_4ADDR 0x99
#define SPI_CMD_FLAG_SR_MICRON 0x70 /* READ FLAG STATUS REGISTER */
#define SPI_CMD_RD_RDCR_MICRON 0xB5 /* READ NONVOLATILE CONFIGURATION
REGISTER */
#define SPI_CMD_WR_RDCR_MICRON 0xB1 /* WRITE NONVOLATILE CONFIGURATION
REGISTER */
#define SPI_NOR_ADS_MASK 0x1
#define spi_nor_get_4byte_by_flag_sr(sr) ((sr) & SPI_NOR_ADS_MASK)
#define spi_nor_ads_set_4byte(cr) ((cr) & (~SPI_NOR_ADS_MASK))
#define spi_nor_ads_get_4byte(cr) ((cr) & SPI_NOR_ADS_MASK)
static void entry_4addr_fmc_op(struct fmc_host* const host,
struct fmc_spi* const spi,
int enable)
{
unsigned int reg;
if (enable)
reg = SPI_CMD_EN4B;
else
reg = SPI_CMD_EX4B;
fmc_write(host, FMC_CMD, fmc_cmd_cmd1(reg));
fmc_pr(AC_DBG, "\t Set CMD[%#x]%#x\n", FMC_CMD, reg);
reg = op_cfg_fm_cs(spi->chipselect) | OP_CFG_OEN_EN;
fmc_write(host, FMC_OP_CFG, reg);
fmc_pr(AC_DBG, "\t Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg);
reg = fmc_op_cmd1_en(ENABLE) | FMC_OP_REG_OP_START;
fmc_write(host, FMC_OP, reg);
fmc_pr(AC_DBG, "\t Set OP[%#x]%#x\n", FMC_OP, reg);
fmc_cmd_wait_cpu_finish(host);
}
static int spi_micron_entry_4addr(struct fmc_spi *spi, int enable)
{
unsigned char status;
const char *str[] = {"Disable", "Enable"};
struct fmc_host *host = NULL;
if (!spi || !spi->driver)
return -1;
host = (struct fmc_host *)spi->host;
if (!host)
return -1;
fmc_pr(AC_DBG, "\t* Start SPI Nor %s 4-byte mode.\n",
str[enable]);
if (spi->addrcycle != SPI_NOR_4BYTE_ADDR_LEN) {
fmc_pr(AC_DBG, "\t* Not support 4B mode.\n");
return 0;
}
status = spi_general_get_flash_register(spi, SPI_CMD_FLAG_SR_MICRON);
fmc_pr(AC_DBG, "\t Read flag status register[%#x]:%#x\n",
SPI_CMD_FLAG_SR_MICRON, status);
if (spi_nor_get_4byte_by_flag_sr(status) == enable) {
fmc_pr(AC_DBG, "\t* 4-byte was %sd, reg:%#x\n", str[enable],
status);
return 0;
}
spi->driver->write_enable(spi);
entry_4addr_fmc_op(host, spi, enable);
spi->driver->wait_ready(spi);
status = spi_general_get_flash_register(spi,
SPI_CMD_FLAG_SR_MICRON);
fmc_pr(AC_DBG, "\t Read flag status register[%#x]:%#x\n",
SPI_CMD_FLAG_SR_MICRON, status);
if (spi_nor_get_4byte_by_flag_sr(status) != enable) {
db_msg("Error: %s 4-byte failed! SR3:%#x\n",
str[enable], status);
return status;
}
fmc_pr(AC_DBG, "\t %s 4-byte success, SR3:%#x\n", str[enable], status);
fmc_pr(AC_DBG, "\t* End SPI Nor flash %s 4-byte mode.\n", str[enable]);
return 0;
}
@@ -0,0 +1,259 @@
/*
* Copyright (c) LOTUS. All rights reserved.
*/
/* MXIC QE(bit) include in Status Register */
#define MX_SPI_NOR_SR_QE_SHIFT 6
#define MX_SPI_NOR_SR_QE_MASK (1 << MX_SPI_NOR_SR_QE_SHIFT)
#define mx_spi_nor_get_qe_by_sr(sr) (((sr) & MX_SPI_NOR_SR_QE_MASK) >> MX_SPI_NOR_SR_QE_SHIFT)
/*
* enable QE bit if 4X R/W is supported by MXIC "25L(256/257)35(E/F)" SPI
*/
#ifndef CONFIG_DTR_MODE_SUPPORT
static void clear_dtr_mode(struct fmc_spi *spi, unsigned char status)
{
unsigned int regval;
struct fmc_host *host = (struct fmc_host *)spi->host;
unsigned char config;
unsigned short reg;
config = spi_general_get_flash_register(spi, SPI_CMD_RDCR_MX);
regval = fmc_read(host, FMC_GLOBAL_CFG);
if ((regval >> DTR_MODE_REQUEST_SHIFT) & 0x1) {
regval &= (~(1 << DTR_MODE_REQUEST_SHIFT));
regval = fmc_write(host, FMC_GLOBAL_CFG, regval);
}
if (config & CR_DUMMY_CYCLE) {
config &= (~CR_DUMMY_CYCLE);
reg = ((unsigned short)config << SPI_NOR_CR_SHIFT) | status;
writew(reg, host->iobase);
spi->driver->write_enable(spi);
fmc100_op_reg(spi, SPI_CMD_WRSR, sizeof(unsigned short), fmc_op_write_data_en(ENABLE));
}
}
#endif
static void spi_mx25l25635e_set_cmd(struct fmc_spi * const spi)
{
unsigned int regval;
struct fmc_host *host = (struct fmc_host *)spi->host;
regval = fmc_cmd_cmd1(SPI_CMD_WRSR);
fmc_write(host, FMC_CMD, regval);
fmc_pr(QE_DBG, "\t||-Set CMD[%#x]%#x\n", FMC_CMD, regval);
regval = op_cfg_fm_cs(spi->chipselect) | OP_CFG_OEN_EN;
fmc_write(host, FMC_OP_CFG, regval);
fmc_pr(QE_DBG, "\t||-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, regval);
regval = fmc_data_num_cnt(SPI_NOR_SR_LEN);
fmc_write(host, FMC_DATA_NUM, regval);
fmc_pr(QE_DBG, "\t||-Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, regval);
regval = fmc_op_cmd1_en(ENABLE) | fmc_op_write_data_en(ENABLE) |
FMC_OP_REG_OP_START;
fmc_write(host, FMC_OP, regval);
fmc_pr(QE_DBG, "\t||-Set OP[%#x]%#x\n", FMC_OP, regval);
fmc_cmd_wait_cpu_finish(host);
}
static int spi_mx25l25635e_qe_enable(struct fmc_spi *spi)
{
unsigned char status;
unsigned char op;
const char *str[] = {"Disable", "Enable"};
struct fmc_host *host = NULL;
if (!spi || !spi->driver)
return -1;
host = (struct fmc_host *)spi->host;
if (!host || !host->iobase)
return -1;
op = spi_is_quad(spi);
fmc_pr(QE_DBG, "\t|*-Start MXIC SPI Nor %s Quad.\n", str[op]);
status = spi_general_get_flash_register(spi, SPI_CMD_RDSR);
fmc_pr(QE_DBG, "\t||-Read Status Register[%#x]%#x\n", SPI_CMD_RDSR,
status);
#ifndef CONFIG_DTR_MODE_SUPPORT
clear_dtr_mode(spi, status);
#endif
if (mx_spi_nor_get_qe_by_sr(status) == op) {
fmc_pr(QE_DBG, "\t|*-Quad was %sd, status:%#x\n", str[op],
status);
return op;
}
spi->driver->write_enable(spi);
if (op)
status |= MX_SPI_NOR_SR_QE_MASK;
else
status &= ~MX_SPI_NOR_SR_QE_MASK;
writeb(status, host->iobase);
fmc_pr(QE_DBG, "\t||-Write IO[%p]%#x\n", host->iobase,
*(unsigned char *)host->iobase);
spi_mx25l25635e_set_cmd(spi);
spi->driver->wait_ready(spi);
status = spi_general_get_flash_register(spi, SPI_CMD_RDSR);
if (mx_spi_nor_get_qe_by_sr(status) == op)
fmc_pr(QE_DBG, "\t||-%s Quad success, status:%#x.\n", str[op],
status);
else
db_msg("Error: %s Quad failed! reg: %#x\n", str[op], status);
fmc_pr(QE_DBG, "\t|*-End MXIC SPI Nor %s Quad.\n", str[op]);
return op;
}
#ifdef CONFIG_DTR_MODE_SUPPORT
void spi_mxic_set_reg(struct fmc_spi *spi)
{
unsigned int regval;
struct fmc_host *host = (struct fmc_host *)spi->host;
regval = fmc_cmd_cmd1(SPI_CMD_WRSR);
fmc_write(host, FMC_CMD, regval);
fmc_pr(DTR_DB, " Set CMD[%#x]%#x\n", FMC_CMD, regval);
regval = op_cfg_fm_cs(spi->chipselect) | OP_CFG_OEN_EN;
fmc_write(host, FMC_OP_CFG, regval);
fmc_pr(DTR_DB, " Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, regval);
regval = fmc_data_num_cnt(SPI_NOR_SR_LEN + SPI_NOR_CR_LEN);
fmc_write(host, FMC_DATA_NUM, regval);
fmc_pr(DTR_DB, " Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, regval);
regval = fmc_op_cmd1_en(ENABLE) |
fmc_op_write_data_en(ENABLE) |
FMC_OP_REG_OP_START;
fmc_write(host, FMC_OP, regval);
fmc_pr(DTR_DB, " Set OP[%#x]%#x\n", FMC_OP, regval);
}
int spi_mxic_output_driver_strength_set(struct fmc_spi *spi, int dtr_en)
{
unsigned char status;
unsigned char config;
unsigned short reg;
unsigned short val;
unsigned int ix;
struct fmc_host *host = NULL;
/* DC[1:0] | Numbers of Dummy clock cycles| Quad IO DTR Read */
/* 00(default)| 6 | 54 */
/* 01 | 6 | 54 */
/* 10 | 8 | 70/80R */
/* 11 | 10 | 84/100R */
unsigned int str_dummy[] = {
DTR_DUMMY_CYCLES_6, dtr_rdcr_dc_mask(0),
DTR_DUMMY_CYCLES_6, dtr_rdcr_dc_mask(1),
DTR_DUMMY_CYCLES_8, dtr_rdcr_dc_mask(2),
DTR_DUMMY_CYCLES_10, dtr_rdcr_dc_mask(3),
0, 0,
};
val = 0;
if (!spi || !spi->driver)
return -1;
host = (struct fmc_host *)spi->host;
if (!host)
return -1;
/* get the RDCR and RDSR */
spi->driver->wait_ready(spi);
/* setting the DC value to match high system clock */
config = spi_general_get_flash_register(spi, SPI_CMD_RDCR_MX);
fmc_pr(DTR_DB, "Get Config Register[%#x]\n", config);
/* check the QE value */
status = spi_general_get_flash_register(spi, SPI_CMD_RDSR);
fmc_pr(DTR_DB, "Get Status Register[%#x]\n", status);
reg = ((unsigned short)config << SPI_NOR_CR_SHIFT) | status;
if (dtr_en == ENABLE) {
/* setting DC value */
fmc_pr(DTR_DB, "Get the dummy value[%#x]\n", spi->read->dummy);
/* Only the element with an even number of arrays is required, so increase is 2 */
for (ix = 0; str_dummy[ix]; ix += _2B) {
if (spi->read->dummy < str_dummy[ix])
break;
val = (unsigned short)str_dummy[ix + 1];
}
} else {
val = dtr_rdcr_dc_mask(0);
}
reg = dtr_rdcr_dc_bit_clr(reg) | (val << DTR_RDSR_DC_SHIFT);
spi->driver->write_enable(spi);
writew(reg, host->iobase);
fmc_pr(DTR_DB, "Write IO[%p]%#x\n", host->iobase,
*(unsigned short *)host->iobase);
spi_mxic_set_reg(spi);
fmc_cmd_wait_cpu_finish(host);
config = spi_general_get_flash_register(spi, SPI_CMD_RDCR_MX);
if ((config >> DTR_RDCR_DC_SHIFT) != (unsigned char)val) {
printf("* Set DC dummy fail.\n");
return -1;
}
return 0;
}
unsigned int spi_mxic_check_spi_dtr_support(struct fmc_spi *spi)
{
unsigned int regval;
unsigned int rd_sfdp_dummy = 1;
unsigned int sfdp_addrcycle = 3;
struct fmc_host *host = (struct fmc_host *)spi->host;
/* get the RDCR and RDSR */
spi->driver->wait_ready(spi);
/* Read the Serial Flash Discoverable Parameter (SFDP) */
fmc_write(host, FMC_CMD, SPI_CMD_RD_SFDP);
fmc_pr(DTR_DB, "\t Set CMD[%#x]%#x\n", FMC_CMD, SPI_CMD_RD_SFDP);
regval = op_cfg_fm_cs(spi->chipselect) |
OP_CFG_OEN_EN |
op_cfg_addr_num(sfdp_addrcycle) |
op_cfg_dummy_num(rd_sfdp_dummy);
fmc_write(host, FMC_OP_CFG, regval);
fmc_pr(DTR_DB, "\t\t Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, regval);
regval = fmc_data_num_cnt(SFDP_BUF_LEN);
fmc_write(host, FMC_DATA_NUM, regval);
fmc_pr(DTR_DB, "\t Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, regval);
regval = fmc_op_dummy_en(ENABLE) |
fmc_op_cmd1_en(ENABLE) |
fmc_op_addr_en(ENABLE) |
fmc_op_read_data_en(ENABLE) |
FMC_OP_REG_OP_START;
fmc_write(host, FMC_OP, regval);
fmc_pr(DTR_DB, "\t Set OP[%#x]%#x\n", FMC_OP, regval);
fmc_cmd_wait_cpu_finish(host);
regval = readb((char *)host->iobase + SFDP_DTR_BYTE_SHIFT);
fmc_pr(DTR_DB, "\t the dtr_mode_support is: %#x\n", regval);
/* get the DTR mode support bit */
spi->dtr_mode_support = (regval >> SFDP_DTR_BIT_SHIFT)
& SFDP_DTR_BIT_MASK;
return spi->dtr_mode_support;
}
#endif /* CONFIG_DTR_MODE_SUPPORT */
@@ -0,0 +1,102 @@
/*
* Copyright (c) LOTUS. All rights reserved.
*/
#define SPI_NOR_NM_QE_SHIFT 2
#define SPI_NOR_NM_QE_MASK (1 << SPI_NOR_NM_QE_SHIFT)
#define spi_nor_get_qe_by_nm(cr) (((cr) & SPI_NOR_NM_QE_MASK) \
>> SPI_NOR_NM_QE_SHIFT)
static int spi_nm25q128_entry_4addr(struct fmc_spi *spi, int enable)
{
return 0;
}
static void spi_nm25q128_set_op(const struct fmc_spi *spi)
{
unsigned int regval;
struct fmc_host *host = (struct fmc_host *)spi->host;
regval = fmc_cmd_cmd1(SPI_CMD_WRSR2);
fmc_write(host, FMC_CMD, regval);
fmc_pr(QE_DBG, "\t Set CMD[%#x]%#x\n", FMC_CMD, regval);
regval = op_cfg_fm_cs(spi->chipselect) | OP_CFG_OEN_EN;
fmc_write(host, FMC_OP_CFG, regval);
fmc_pr(QE_DBG, "\t Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, regval);
regval = fmc_data_num_cnt(SPI_NOR_SR_LEN);
fmc_write(host, FMC_DATA_NUM, regval);
fmc_pr(QE_DBG, "\t Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, regval);
regval = fmc_op_cmd1_en(ENABLE) |
fmc_op_write_data_en(ENABLE) |
FMC_OP_REG_OP_START;
fmc_write(host, FMC_OP, regval);
fmc_pr(QE_DBG, "\t Set OP[%#x]%#x\n", FMC_OP, regval);
fmc_cmd_wait_cpu_finish(host);
}
/*
enable QE bit if QUAD read write is supported by NM25Q128EVBSIG
*/
static int spi_nm25q128_qe_enable(struct fmc_spi *spi)
{
unsigned char status, op;
const char *str[] = {"Disable", "Enable"};
struct fmc_host *host = NULL;
if (!spi || !spi->driver)
return -1;
host = (struct fmc_host *)spi->host;
if (!host || !host->iobase)
return -1;
op = spi_is_quad(spi);
fmc_pr(QE_DBG, "\t* Start SPI Nor NM25Q(128/64)EVBSIG %s Quad.\n", str[op]);
status = spi_general_get_flash_register(spi, SPI_CMD_RDSR2);
fmc_pr(QE_DBG, "\t Read Status Register-2[%#x]%#x\n", SPI_CMD_RDSR2,
status);
if (op == spi_nor_get_qe_by_nm(status)) {
fmc_pr(QE_DBG, "\t* Quad was %s status:%#x\n", str[op], status);
goto QE_END;
}
spi->driver->write_enable(spi);
if (op)
status |= SPI_NOR_NM_QE_MASK;
else
status &= ~SPI_NOR_NM_QE_MASK;
writeb(status, host->iobase);
fmc_pr(QE_DBG, "\t Write IO[%p]%#x\n", host->iobase,
*(unsigned char *)host->iobase);
/* There is new cmd for Write Status Register 2 by NM25Q(128/64)EVBSIG */
spi_nm25q128_set_op(spi);
/* wait the flash have switched quad mode success */
spi->driver->wait_ready(spi);
status = spi_general_get_flash_register(spi, SPI_CMD_RDSR2);
fmc_pr(QE_DBG, "\t Read Status Register-2[%#x]:%#x\n",
SPI_CMD_RDSR2, status);
if (op == spi_nor_get_qe_by_nm(status)) {
fmc_pr(QE_DBG, "\t %s Quad success. status:%#x\n",
str[op], status);
} else {
db_msg("Error: %s Quad failed! reg:%#x\n", str[op],
status);
}
QE_END:
/* Enable the reset pin when working on dual mode for 8PIN */
if (!op)
spi_nor_reset_pin_enable(spi, ENABLE);
fmc_pr(QE_DBG, "\t* End SPI Nor NM25Q(128/64)EVBSIG %s Quad.\n", str[op]);
return op;
}
@@ -0,0 +1,202 @@
/*
* Copyright (c) LOTUS. All rights reserved.
*/
static void puya_qe_enable_fmc_op(struct fmc_host * const host, struct fmc_spi * const spi)
{
unsigned int regval;
regval = fmc_cmd_cmd1(SPI_CMD_WRSR2);
fmc_write(host, FMC_CMD, regval);
fmc_pr(QE_DBG, "\t Set CMD[%#x]%#x\n", FMC_CMD, regval);
regval = op_cfg_fm_cs(spi->chipselect);
fmc_write(host, FMC_OP_CFG, regval);
fmc_pr(QE_DBG, "\t Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, regval);
regval = fmc_data_num_cnt(SPI_NOR_SR_LEN);
fmc_write(host, FMC_DATA_NUM, regval);
fmc_pr(QE_DBG, "\t Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, regval);
regval = fmc_op_cmd1_en(ENABLE) |
fmc_op_write_data_en(ENABLE) | FMC_OP_REG_OP_START;
fmc_write(host, FMC_OP, regval);
fmc_pr(QE_DBG, "\t Set OP[%#x]%#x\n", FMC_OP, regval);
fmc_cmd_wait_cpu_finish(host);
}
/*
* enable QE bit if QUAD read write is supported by puya'a P25Q128H,it is as same as W25Q(128/256)FV
* opcode type name
* 0x35 RD SR(S15-S8)
* 0x31 WR SR(S15-S8)
*/
static int spi_puya_qe_enable(struct fmc_spi *spi)
{
unsigned char status;
unsigned char op;
const char *str[] = {"Disable", "Enable"};
struct fmc_host *host = NULL;
if (!spi || !spi->driver)
return -1;
host = (struct fmc_host *)spi->host;
if (!host || !host->iobase)
return -1;
op = spi_is_quad(spi);
fmc_pr(QE_DBG, "\t* Start SPI Nor %s Quad.\n", str[op]);
status = spi_general_get_flash_register(spi, SPI_CMD_RDSR2);
fmc_pr(QE_DBG, "\t Read Status Register-2[%#x]%#x\n", SPI_CMD_RDSR2,
status);
if (spi_nor_get_qe_by_cr(status) == op) {
fmc_pr(QE_DBG, "\t* Quad was %s status:%#x\n", str[op], status);
goto QE_END;
}
spi->driver->write_enable(spi);
if (op)
status |= SPI_NOR_CR_QE_MASK;
else
status &= ~SPI_NOR_CR_QE_MASK;
writeb(status, host->iobase);
fmc_pr(QE_DBG, "\t Write IO[%#lx]%#x\n", (uintptr_t)host->iobase,
*(unsigned char *)host->iobase);
/* There is new cmd for Write Status Register 2 by W25Q(128/256)FV */
puya_qe_enable_fmc_op(host, spi);
/* wait the flash have switched quad mode success */
spi->driver->wait_ready(spi);
status = spi_general_get_flash_register(spi, SPI_CMD_RDSR2);
fmc_pr(QE_DBG, "\t Read Status Register-2[%#x]:%#x\n",
SPI_CMD_RDSR2, status);
if (spi_nor_get_qe_by_cr(status) == op)
fmc_pr(QE_DBG, "\t %s Quad success. status:%#x\n",
str[op], status);
else
db_msg("Error: %s Quad failed! reg:%#x\n", str[op], status);
QE_END:
return op;
}
static int spi_puya_entry_4addr(struct fmc_spi *spi, int enable)
{
const char *str[] = {"Disable", "Enable"};
struct fmc_host *host = NULL;
if (!spi || !spi->driver)
return -1;
host = (struct fmc_host*)spi->host;
if (!host)
return -1;
fmc_pr(AC_DBG, "\t* Start SPI Nor flash %s 4-byte mode.\n",
str[enable]);
if (spi->addrcycle != SPI_NOR_4BYTE_ADDR_LEN) {
fmc_pr(AC_DBG, "\t* Flash isn't support entry 4-byte mode.\n");
return 0;
}
if (!enable)
{
/* reset cmd same sa w25q256fv */
spi_w25q256fv_set_cmd(spi, SPI_CMD_FIRST_RESET_4ADDR);
spi_w25q256fv_set_cmd(spi, SPI_CMD_SECOND_RESET_4ADDR);
fmc_pr(AC_DBG, "\tnow PY25Q256HB start software reset\n");
udelay(30); /* delay 30 us */
}
fmc_pr(AC_DBG, "\t* End SPI Nor flash %s 4-byte mode.\n", str[enable]);
return 0;
}
#ifdef CONFIG_DTR_MODE_SUPPORT
void spi_py_set_reg(struct fmc_spi *spi)
{
unsigned int regval;
struct fmc_host *host = (struct fmc_host *)spi->host;
regval = fmc_cmd_cmd1(SPI_CMD_WRSR3);
fmc_write(host, FMC_CMD, regval);
fmc_pr(DTR_DB, " Set CMD[%#x]%#x\n", FMC_CMD, regval);
regval = op_cfg_fm_cs(spi->chipselect) | OP_CFG_OEN_EN;
fmc_write(host, FMC_OP_CFG, regval);
fmc_pr(DTR_DB, " Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, regval);
regval = fmc_data_num_cnt(SPI_NOR_SR_LEN);
fmc_write(host, FMC_DATA_NUM, regval);
fmc_pr(DTR_DB, " Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, regval);
regval = fmc_op_cmd1_en(ENABLE) |
fmc_op_write_data_en(ENABLE) |
FMC_OP_REG_OP_START;
fmc_write(host, FMC_OP, regval);
fmc_pr(DTR_DB, " Set OP[%#x]%#x\n", FMC_OP, regval);
}
int spi_py_output_driver_strength_set(struct fmc_spi *spi, int dtr_en)
{
unsigned char config;
unsigned char reg;
unsigned char val;
unsigned int ix;
struct fmc_host *host = NULL;
/* DC | Numbers of Dummy clock cycles| Quad IO DTR Read */
/* 0(default)| 8 | 80 */
/* 1 | 10 | 100 */
unsigned int str_dummy[] = {
DTR_DUMMY_CYCLES_8, dtr_rdcr_dc_mask(0),
DTR_DUMMY_CYCLES_10, dtr_rdcr_dc_mask(1),
0, 0,
};
val = 0;
if (!spi || !spi->driver)
return -1;
host = (struct fmc_host *)spi->host;
if (!host)
return -1;
/* get the RDCR and RDSR */
spi->driver->wait_ready(spi);
/* setting the DC value to match high system clock */
config = spi_general_get_flash_register(spi, SPI_CMD_RDSR3);
fmc_pr(DTR_DB, "Get Status Register-3[%#x]\n", config);
if (dtr_en == ENABLE) {
/* setting DC value */
fmc_pr(DTR_DB, "Get the dummy value[%#x]\n", spi->read->dummy);
/* Only the element with an even number of arrays is required, so increase is 2 */
for (ix = 0; str_dummy[ix]; ix += _2B) {
if (spi->read->dummy < str_dummy[ix])
break;
val = (unsigned char)str_dummy[ix + 1];
}
} else {
val = dtr_rdcr_dc_mask(0);
}
reg = dtr_py_dc_bit_clr(config) | (val << 3);
fmc_pr(DTR_DB, "Get the reg value[%#x]\n", reg);
spi->driver->write_enable(spi);
writew(reg, host->iobase);
fmc_pr(DTR_DB, "Write IO[%p]%#x\n", host->iobase,
*(unsigned short *)host->iobase);
spi_py_set_reg(spi);
fmc_cmd_wait_cpu_finish(host);
config = spi_general_get_flash_register(spi, SPI_CMD_RDSR3);
fmc_pr(DTR_DB, "Get Status Register-3[%#x]\n", config);
if (((config & 0x8) >> 3) != (unsigned char)val) {
printf("* Set DC dummy fail.\n");
return -1;
}
return 0;
}
#endif /* CONFIG_DTR_MODE_SUPPORT */
@@ -0,0 +1,100 @@
/*
* Copyright (c) LOTUS. All rights reserved.
*/
/* SpanSion SPI Nor Flash "S25FL256S" Bank Address Register command */
#define SS_SPI_CMD_BRRD 0x16 /* Read Bank Register */
#define SS_SPI_CMD_BRWR 0x17 /* Write Bank Register */
/* Bank Address Register length(byte) */
#define SS_SPI_NOR_BR_LEN 1
/* Extended Address Enable bit[7] include in Bank Address Register */
#define SS_SPI_NOR_BR_EAE_SHIFT 7
#define SS_SPI_NOR_BR_EAE_MASK (1 << SS_SPI_NOR_BR_EAE_SHIFT)
#define ss_spi_nor_get_eae_by_br(br) (((br) & SS_SPI_NOR_BR_EAE_MASK) >> SS_SPI_NOR_BR_EAE_SHIFT)
static void spi_s25fl256s_set_cmd(const struct fmc_spi *spi)
{
unsigned int regval;
struct fmc_host *host = (struct fmc_host *)spi->host;
regval = fmc_cmd_cmd1(SS_SPI_CMD_BRWR);
fmc_write(host, FMC_CMD, regval);
fmc_pr(AC_DBG, "\t Set CMD[%#x]%#x\n", FMC_CMD, regval);
regval = op_cfg_fm_cs(spi->chipselect);
fmc_write(host, FMC_OP_CFG, regval);
fmc_pr(AC_DBG, "\t Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, regval);
regval = fmc_data_num_cnt(SS_SPI_NOR_BR_LEN);
fmc_write(host, FMC_DATA_NUM, regval);
fmc_pr(AC_DBG, "\t Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, regval);
regval = fmc_op_cmd1_en(ENABLE) |
fmc_op_write_data_en(ENABLE) |
FMC_OP_REG_OP_START;
fmc_write(host, FMC_OP, regval);
fmc_pr(AC_DBG, "\t Set OP[%#x]%#x\n", FMC_OP, regval);
fmc_cmd_wait_cpu_finish(host);
}
/*
enable 4byte address mode for SpanSion "s25fl256" SPI Nor
*/
static int spi_s25fl256s_entry_4addr(struct fmc_spi *spi, int enable)
{
unsigned char bank;
const char *str[] = {"Disable", "Enable"};
struct fmc_host *host = NULL;
if (!spi || !spi->driver)
return -1;
host = (struct fmc_host *)spi->host;
if (!host || !host->iobase)
return -1;
fmc_pr(AC_DBG, "\t* Start SpanSion SPI Nor %s 4-byte mode.\n",
str[enable]);
if (spi->addrcycle != SPI_NOR_4BYTE_ADDR_LEN) {
fmc_pr(AC_DBG, "\t* Flash isn't support 4-byte mode.\n");
return 0;
}
/* Read old Bank Register value */
bank = spi_general_get_flash_register(spi, SS_SPI_CMD_BRRD);
fmc_pr(AC_DBG, "\t Read Bank Register[%#x]%#x\n", SS_SPI_CMD_BRRD,
bank);
if (ss_spi_nor_get_eae_by_br(bank) == enable) {
fmc_pr(AC_DBG, "\t* 4-byte was %sd, bank:%#x\n", str[enable], bank);
return 0;
}
/* Write new Bank Register value */
if (enable)
bank |= SS_SPI_NOR_BR_EAE_MASK;
else
bank &= ~SS_SPI_NOR_BR_EAE_MASK;
writeb(bank, host->iobase);
fmc_pr(AC_DBG, "\t Write IO[%p]%#x\n", host->iobase,
*(unsigned char *)host->iobase);
spi_s25fl256s_set_cmd(spi);
spi->driver->wait_ready(spi);
/* Check out Bank Register value */
bank = spi_general_get_flash_register(spi, SS_SPI_CMD_BRRD);
fmc_pr(AC_DBG, "\t Read Bank Register[%#x]%#x\n", SS_SPI_CMD_BRRD,
bank);
if (ss_spi_nor_get_eae_by_br(bank) != enable) {
db_msg("Error: %s 4bytes failed! bank: %#x\n", str[enable], bank);
return bank;
}
fmc_pr(AC_DBG, "\t %s 4byte success, bank:%#x.\n", str[enable], bank);
fmc_pr(AC_DBG, "\t* End SpanSion SPI Nor %s 4-byte mode.\n",
str[enable]);
return 0;
}
@@ -0,0 +1,181 @@
/*
* Copyright (c) LOTUS. All rights reserved.
*/
#define W25Q256FV_CR_4BYTE_MASK 0x1
#define WB_SPI_NOR_SR_ADS_MASK 1
#define wb_spi_nor_get_4byte_by_sr(sr) ((sr) & WB_SPI_NOR_SR_ADS_MASK)
#define SPI_CMD_FIRST_RESET_4ADDR 0x66
#define SPI_CMD_SECOND_RESET_4ADDR 0x99
static void spi_w25q256fv_set_cmd(const struct fmc_spi *spi, u8 cmd)
{
unsigned int regval;
struct fmc_host *host = (struct fmc_host *)spi->host;
regval = fmc_cmd_cmd1(cmd);
fmc_write(host, FMC_CMD, regval);
fmc_pr(AC_DBG, "\t Set CMD[%#x]%#x\n", FMC_CMD, regval);
regval = op_cfg_fm_cs(spi->chipselect) | OP_CFG_OEN_EN;
fmc_write(host, FMC_OP_CFG, regval);
fmc_pr(AC_DBG, "\t Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, regval);
regval = fmc_op_cmd1_en(ENABLE) | FMC_OP_REG_OP_START;
fmc_write(host, FMC_OP, regval);
fmc_pr(AC_DBG, "\t Set OP[%#x]%#x\n", FMC_OP, regval);
fmc_cmd_wait_cpu_finish(host);
}
static int spi_w25q256jv_entry_4addr(struct fmc_spi *spi, int enable)
{
return 0;
}
static int spi_w25q256fv_entry_4addr(struct fmc_spi *spi, int enable)
{
unsigned char status;
const char *str[] = {"Disable", "Enable"};
if (!spi || !spi->host)
return -1;
fmc_pr(AC_DBG, "\t* Start W25Q256FV SPI Nor %s 4-byte mode.\n",
str[enable]);
if (spi->addrcycle != SPI_NOR_4BYTE_ADDR_LEN) {
fmc_pr(AC_DBG, "\t* W25Q(128/256)FV not support 4B mode.\n");
return 0;
}
status = spi_general_get_flash_register(spi, SPI_CMD_RDSR3);
fmc_pr(AC_DBG, "\t Read Status Register-3[%#x]:%#x\n", SPI_CMD_RDSR3,
status);
if (wb_spi_nor_get_4byte_by_sr(status) == enable) {
fmc_pr(AC_DBG, "\t* 4-byte was %sd, reg:%#x\n", str[enable],
status);
return 0;
}
if (enable) {
spi_w25q256fv_set_cmd(spi, SPI_CMD_EN4B);
if (!spi->driver)
return -1;
spi->driver->wait_ready(spi);
status = spi_general_get_flash_register(spi, SPI_CMD_RDSR3);
fmc_pr(AC_DBG, "\t Get Status Register 3[%#x]:%#x\n",
SPI_CMD_RDSR3, status);
if (status & W25Q256FV_CR_4BYTE_MASK) {
fmc_pr(AC_DBG, "\t Enter 4-byte success, reg[%#x]\n",
status);
} else {
db_msg("Error: Enter 4-byte failed! [%#x]\n", status);
}
} else {
/* reset cmd */
spi_w25q256fv_set_cmd(spi, SPI_CMD_FIRST_RESET_4ADDR);
spi_w25q256fv_set_cmd(spi, SPI_CMD_SECOND_RESET_4ADDR);
fmc_pr(AC_DBG, "\tnow W25Q256FV start software reset\n");
udelay(30); /* delay 30 us */
}
fmc_pr(AC_DBG, "\t* End W25Q256FV enter 4-byte mode.\n");
return 0;
}
static void spi_w25q256fv_set_op(const struct fmc_spi *spi)
{
unsigned int regval;
struct fmc_host *host = (struct fmc_host *)spi->host;
regval = fmc_cmd_cmd1(SPI_CMD_WRSR2);
fmc_write(host, FMC_CMD, regval);
fmc_pr(QE_DBG, "\t Set CMD[%#x]%#x\n", FMC_CMD, regval);
regval = op_cfg_fm_cs(spi->chipselect) | OP_CFG_OEN_EN;
fmc_write(host, FMC_OP_CFG, regval);
fmc_pr(QE_DBG, "\t Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, regval);
regval = fmc_data_num_cnt(SPI_NOR_SR_LEN);
fmc_write(host, FMC_DATA_NUM, regval);
fmc_pr(QE_DBG, "\t Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, regval);
regval = fmc_op_cmd1_en(ENABLE) |
fmc_op_write_data_en(ENABLE) |
FMC_OP_REG_OP_START;
fmc_write(host, FMC_OP, regval);
fmc_pr(QE_DBG, "\t Set OP[%#x]%#x\n", FMC_OP, regval);
fmc_cmd_wait_cpu_finish(host);
}
/*
enable QE bit if QUAD read write is supported by W25Q(128/256)FV
*/
static int spi_w25q256fv_qe_enable(struct fmc_spi *spi)
{
unsigned char status, op;
const char *str[] = {"Disable", "Enable"};
struct fmc_host *host = NULL;
if (!spi || !spi->driver)
return -1;
host = (struct fmc_host *)spi->host;
if (!host || !host->iobase)
return -1;
op = spi_is_quad(spi);
fmc_pr(QE_DBG, "\t* Start SPI Nor W25Q(128/256)FV %s Quad.\n", str[op]);
status = spi_general_get_flash_register(spi, SPI_CMD_RDSR2);
fmc_pr(QE_DBG, "\t Read Status Register-2[%#x]%#x\n", SPI_CMD_RDSR2,
status);
if (op == spi_nor_get_qe_by_cr(status)) {
fmc_pr(QE_DBG, "\t* Quad was %s status:%#x\n", str[op], status);
goto QE_END;
}
spi->driver->write_enable(spi);
if (op)
status |= SPI_NOR_CR_QE_MASK;
else
status &= ~SPI_NOR_CR_QE_MASK;
writeb(status, host->iobase);
fmc_pr(QE_DBG, "\t Write IO[%p]%#x\n", host->iobase,
*(unsigned char *)host->iobase);
/* There is new cmd for Write Status Register 2 by W25Q(128/256)FV */
spi_w25q256fv_set_op(spi);
/* wait the flash have switched quad mode success */
spi->driver->wait_ready(spi);
status = spi_general_get_flash_register(spi, SPI_CMD_RDSR2);
fmc_pr(QE_DBG, "\t Read Status Register-2[%#x]:%#x\n",
SPI_CMD_RDSR2, status);
if (op == spi_nor_get_qe_by_cr(status)) {
fmc_pr(QE_DBG, "\t %s Quad success. status:%#x\n",
str[op], status);
} else {
db_msg("Error: %s Quad failed! reg:%#x\n", str[op],
status);
}
QE_END:
/* Enable the reset pin when working on dual mode for 8PIN */
if (!op)
spi_nor_reset_pin_enable(spi, ENABLE);
fmc_pr(QE_DBG, "\t* End SPI Nor W25Q(128/256)FV %s Quad.\n", str[op]);
return op;
}
@@ -0,0 +1,90 @@
/*
* Copyright (c) LOTUS. All rights reserved.
*/
#ifdef CONFIG_DTR_MODE_SUPPORT
void spi_xmc_set_reg(struct fmc_spi *spi)
{
unsigned int regval;
struct fmc_host *host = (struct fmc_host *)spi->host;
regval = fmc_cmd_cmd1(SPI_CMD_WRSR3);
fmc_write(host, FMC_CMD, regval);
fmc_pr(DTR_DB, " Set CMD[%#x]%#x\n", FMC_CMD, regval);
regval = op_cfg_fm_cs(spi->chipselect) | OP_CFG_OEN_EN;
fmc_write(host, FMC_OP_CFG, regval);
fmc_pr(DTR_DB, " Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, regval);
regval = fmc_data_num_cnt(SPI_NOR_SR_LEN);
fmc_write(host, FMC_DATA_NUM, regval);
fmc_pr(DTR_DB, " Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, regval);
regval = fmc_op_cmd1_en(ENABLE) |
fmc_op_write_data_en(ENABLE) |
FMC_OP_REG_OP_START;
fmc_write(host, FMC_OP, regval);
fmc_pr(DTR_DB, " Set OP[%#x]%#x\n", FMC_OP, regval);
}
int spi_xmc_output_driver_strength_set(struct fmc_spi *spi, int dtr_en)
{
unsigned char config;
unsigned short val;
unsigned int ix;
struct fmc_host *host = NULL;
/* DC[1:0] | Numbers of Dummy clock cycles| Quad IO DTR Read */
/* 00(default)| 8 | 90 */
/* 01 | 4 | 66 */
/* 10 | 6 | 66 */
/* 11 | 10 | 108 */
unsigned int str_dummy[] = {
DTR_DUMMY_CYCLES_4, dtr_rdcr_dc_mask(1),
DTR_DUMMY_CYCLES_6, dtr_rdcr_dc_mask(2),
DTR_DUMMY_CYCLES_8, dtr_rdcr_dc_mask(0),
DTR_DUMMY_CYCLES_10, dtr_rdcr_dc_mask(3),
0, 0,
};
val = 0;
if (!spi || !spi->driver)
return -1;
host = (struct fmc_host *)spi->host;
if (!host)
return -1;
/* get the RDCR and RDSR */
spi->driver->wait_ready(spi);
/* setting the DC value to match high system clock */
config = spi_general_get_flash_register(spi, SPI_CMD_RDCR_MX);
fmc_pr(DTR_DB, "Get Config Register[%#x]\n", config);
if (dtr_en == ENABLE) {
/* setting DC value */
fmc_pr(DTR_DB, "Get the dummy value[%#x]\n", spi->read->dummy);
/* Only the element with an even number of arrays is required, so increase is 2 */
for (ix = 0; str_dummy[ix]; ix += _2B) {
if (spi->read->dummy < str_dummy[ix])
break;
val = (unsigned short)str_dummy[ix + 1];
}
} else {
val = dtr_rdcr_dc_mask(0);
}
config = dtr_xmc_dc_bit_clr(config) | val;
spi->driver->write_enable(spi);
writeb(config, host->iobase);
fmc_pr(DTR_DB, "Write IO[%p]%#x\n", host->iobase,
*(unsigned short *)host->iobase);
spi_xmc_set_reg(spi);
fmc_cmd_wait_cpu_finish(host);
config = spi_general_get_flash_register(spi, SPI_CMD_RDCR_MX);
if ((config & 3) != (unsigned char)val) {
printf("* Set DC dummy fail.\n");
return -1;
}
return 0;
}
#endif /* CONFIG_DTR_MODE_SUPPORT */
@@ -0,0 +1,92 @@
/*
* Copyright (c) LOTUS. All rights reserved.
*/
#define XTX_READ_SR_H 0x35
#define XTX_READ_SR_L 0x05
static void spi_xtx_set_op(const struct fmc_spi *spi)
{
unsigned int regval;
struct fmc_host *host = (struct fmc_host *)spi->host;
regval = fmc_cmd_cmd1(SPI_CMD_WRSR);
fmc_write(host, FMC_CMD, regval);
fmc_pr(QE_DBG, "\t Set CMD[%#x]%#x\n", FMC_CMD, regval);
regval = op_cfg_fm_cs(spi->chipselect) | OP_CFG_OEN_EN;
fmc_write(host, FMC_OP_CFG, regval);
fmc_pr(QE_DBG, "\t Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, regval);
regval = fmc_data_num_cnt(sizeof(unsigned short));
fmc_write(host, FMC_DATA_NUM, regval);
fmc_pr(QE_DBG, "\t Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, regval);
regval = fmc_op_cmd1_en(ENABLE) | fmc_op_write_data_en(ENABLE) |
FMC_OP_REG_OP_START;
fmc_write(host, FMC_OP, regval);
fmc_pr(QE_DBG, "\t Set OP[%#x]%#x\n", FMC_OP, regval);
fmc_cmd_wait_cpu_finish(host);
}
/*
enable QE bit if QUAD read write is supported by xtx's flash
*/
static int spi_xtx_qe_enable(struct fmc_spi *spi)
{
unsigned char status_h;
unsigned char status_l;
unsigned char op;
unsigned short reg;
const char *str[] = {"Disable", "Enable"};
struct fmc_host *host = NULL;
if (!spi || !spi->driver)
return -1;
host = (struct fmc_host *)spi->host;
if (!host || !host->iobase)
return -1;
op = spi_is_quad(spi);
fmc_pr(QE_DBG, "\t* Start SPI Nor xtx %s Quad.\n", str[op]);
status_h = spi_general_get_flash_register(spi, XTX_READ_SR_H);
fmc_pr(QE_DBG, "\t Read Status Register-h[%#x]%#x\n", XTX_READ_SR_H,
status_h);
if (op == spi_nor_get_qe_by_cr(status_h)) {
fmc_pr(QE_DBG, "\t* Quad was %s status:%#x\n", str[op], status_h);
goto QE_END;
}
spi->driver->write_enable(spi);
status_l = spi_general_get_flash_register(spi, XTX_READ_SR_L);
if (op)
status_h |= SPI_NOR_CR_QE_MASK;
else
status_h &= ~SPI_NOR_CR_QE_MASK;
/* Move left to 8 bit to assign a value to the upper bits */
reg = ((unsigned short)status_h << 8) | status_l;
writew(reg, host->iobase);
fmc_pr(QE_DBG, "\t Write IO[%p]%#x\n", host->iobase,
*(unsigned short *)host->iobase);
spi_xtx_set_op(spi);
/* wait the flash have switched quad mode success */
spi->driver->wait_ready(spi);
status_h = spi_general_get_flash_register(spi, XTX_READ_SR_H);
fmc_pr(QE_DBG, "\t Read Status Register-h[%#x]:%#x\n",
XTX_READ_SR_H, status_h);
if (op == spi_nor_get_qe_by_cr(status_h)) {
fmc_pr(QE_DBG, "\t %s Quad success. status_h:%#x\n",
str[op], status_h);
} else {
db_msg("Error: %s Quad failed! reg:%#x\n", str[op],
status_h);
}
QE_END:
return status_h;
}
@@ -0,0 +1,68 @@
/*
* Copyright (c) LOTUS. All rights reserved.
*/
#include <common.h>
#include <malloc.h>
#include <asm/io.h>
#include <spi_flash.h>
#include <errno.h>
#include <linux/mtd/mtd.h>
#include <linux/lotus/fmc.h>
/*****************************************************************************/
static struct spi_flash *spiflash;
static struct mtd_info_ex *spiinfo_ex;
/*****************************************************************************/
struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int spi_mode)
{
if (get_boot_media() != BOOT_MEDIA_SPIFLASH) {
return NULL;
}
if (spiflash)
return spiflash;
#ifdef CONFIG_FMC_SPI_NOR
spiflash = fmc100_spi_nor_probe(&spiinfo_ex);
spiflash->erase_size = spiinfo_ex->erasesize;
#endif
return spiflash;
}
/*****************************************************************************/
struct mtd_info_ex *get_spiflash_info(void)
{
if (spiinfo_ex)
return spiinfo_ex;
#ifdef CONFIG_FMC_SPI_NOR
spiinfo_ex = fmc100_get_spi_nor_info(spiflash);
#endif
return spiinfo_ex;
}
/*****************************************************************************/
void spi_flash_free(struct spi_flash *flash)
{
}
/*****************************************************************************/
#ifdef CONFIG_SPI_BLOCK_PROTECT
void spi_flash_lock(unsigned char cmp, unsigned char level, unsigned char op)
{
cmp = BP_CMP_BOTTOM;
if (spiflash->lock)
spiflash->lock(cmp, level, op);
return;
}
/*****************************************************************************/
#endif /* CONFIG_SPI_BLOCK_PROTECT */