GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)
This commit is contained in:
@@ -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__ */
|
||||
+124
@@ -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__ */
|
||||
+276
@@ -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, ®);
|
||||
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, ®);
|
||||
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, ®);
|
||||
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, ®);
|
||||
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, ®);
|
||||
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;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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__ */
|
||||
+183
@@ -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 */
|
||||
+14
@@ -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__ */
|
||||
|
||||
+280
@@ -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 */
|
||||
+401
@@ -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");
|
||||
}
|
||||
+91
@@ -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;
|
||||
}
|
||||
|
||||
+91
@@ -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;
|
||||
}
|
||||
Executable
+259
@@ -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 */
|
||||
+102
@@ -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;
|
||||
}
|
||||
+202
@@ -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 */
|
||||
+100
@@ -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;
|
||||
}
|
||||
+181
@@ -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 */
|
||||
+92
@@ -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;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user