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

This commit is contained in:
lai
2026-09-06 03:52:57 +08:00
commit b1928b41c0
21813 changed files with 4413081 additions and 0 deletions
@@ -0,0 +1,56 @@
config FMC_SPI_NAND
bool "lotus SPI Nand Flash Interface support"
depends on FMC
help
Enable the lotus SPI Nand flash support.
If unsure, say N
config SPI_NAND_MAX_CHIP_NUM
int "Support max number of SPI Nand flash chip (1, 2)"
depends on FMC_SPI_NAND
default 1
help
flash memory controller v100 device only support 1 or 2 SPI nand flash
chip, your should not config other value.
config NAND_MAX_CHIP_NUM
int "Support max number of Nand flash chip (1, 2)"
depends on FMC_NAND
default 1
help
flash memory controller v100 device only support 1 or 2 nand flash
chip, your should not config other value.
if FMC_SPI_NAND || FMC_NAND
choice
prompt "Page Size and Ecc Type Select"
default FMC100_AUTO_PAGESIZE_ECC
config FMC100_HARDWARE_PAGESIZE_ECC
bool "Hardware"
help
the configure of page size and ecc type lie on switch on the board.
so the page size and ecc type is controlled by Hardware see demo
board of SOC.
config FMC100_AUTO_PAGESIZE_ECC
bool "Auto"
help
auto-sensed the page size and ecc type value. driver will try each of
page size and ecc type one by one till flash can be read and wrote
accurately. so the page size and ecc type is match adaptively without
switch on the board
config FMC100_PAGESIZE_AUTO_ECC_NONE
bool "Pagesize Auto, Ecc None"
help
auto-sensed the page size and select ecc none. driver will try each
of page size one by one till flash can be read and wrote accurately.
so the page size is match adaptively without switch on the board
endchoice
endif
@@ -0,0 +1,5 @@
obj-$(CONFIG_CMD_NAND) += nfc_common.o
obj-$(CONFIG_CMD_NAND) +=match_table.o
obj-$(CONFIG_FMC_SPI_NAND) += fmc100/
@@ -0,0 +1,4 @@
ccflags-y += -I$(srctree)/lotus/drivers/mtd
obj-y += fmc100.o fmc100_os.o fmc_spi_nand_ids.o
@@ -0,0 +1,965 @@
/*
* Copyright (c) LOTUS. All rights reserved.
*/
#include "fmc100.h"
#include <common.h>
#include <asm/io.h>
#include <errno.h>
#include <malloc.h>
#include <linux/lotus/match_table.h>
#include <asm/arch/platform.h>
void fmc100_ecc0_switch(struct fmc_host* const host, unsigned char op)
{
unsigned int config;
#if EC_DBG
unsigned int cmp_cfg;
config = fmc_read(host, FMC_CFG);
fmc_pr(EC_DBG, "\t *-Get CFG[%#x]%#x\n", FMC_CFG, config);
if (op)
cmp_cfg = host->fmc_cfg;
else
cmp_cfg = host->fmc_cfg_ecc0;
if (cmp_cfg != config)
db_msg("Warning: FMC config[%#x] is different.\n",
cmp_cfg);
#endif
if (op == ENABLE) {
config = host->fmc_cfg_ecc0;
} else if (op == DISABLE) {
config = host->fmc_cfg;
} else {
db_msg("Error: Invalid opcode: %d\n", op);
return;
}
fmc_write(host, FMC_CFG, config);
fmc_pr(EC_DBG, "\t *-Set CFG[%#x]%#x\n", FMC_CFG, config);
}
static void set_dma_addr_reg(struct fmc_host *host)
{
unsigned int reg;
reg = host->dma_buffer;
fmc_write(host, FMC_DMA_SADDR_D0, reg);
fmc_pr(DMA_DB, "\t|-Set DMA_SADDR_D0[%#x]%#x\n", FMC_DMA_SADDR_D0, reg);
/* get hight 32 bits */
reg = ((unsigned long)host->dma_buffer & FMC_DMA_SADDRH_MASK) >> 32;
fmc_write(host, FMC_DMA_SADDRH_D0, reg);
fmc_pr(DMA_DB, "\t|-Set DMA_SADDRH_D0[%#x]%#x\n", FMC_DMA_SADDRH_D0, reg);
reg = host->dma_oob;
fmc_write(host, FMC_DMA_SADDR_OOB, reg);
fmc_pr(DMA_DB, "\t|-Set DMA_SADDR_OOB[%#x]%#x\n", FMC_DMA_SADDR_OOB,
reg);
/* get hight 32 bits */
reg = ((unsigned long)host->dma_oob & FMC_DMA_SADDRH_MASK) >> 32;
fmc_write(host, FMC_DMA_SADDRH_OOB, reg);
fmc_pr(DMA_DB, "\t|-Set DMA_SADDRH_OOB[%#x]%#x\n", FMC_DMA_SADDRH_OOB,
reg);
}
static void set_addr_reg(struct fmc_host *host)
{
unsigned int reg;
struct nand_chip *chip = host->chip;
unsigned char pages_per_block_shift;
unsigned int block_num;
unsigned int block_num_h;
unsigned int page_num;
pages_per_block_shift = chip->phys_erase_shift - chip->page_shift;
block_num = host->addr_value[1] >> pages_per_block_shift;
block_num_h = block_num >> REG_CNT_HIGH_BLOCK_NUM_SHIFT;
reg = fmc_addrh_set(block_num_h);
fmc_write(host, FMC_ADDRH, reg);
fmc_pr(REG_DB, "|-Set ADDRH[%#x]%#x\n", FMC_ADDRH, reg);
page_num = host->addr_value[1] - (block_num << pages_per_block_shift);
reg = ((block_num & REG_CNT_BLOCK_NUM_MASK) << REG_CNT_BLOCK_NUM_SHIFT) |
((page_num & REG_CNT_PAGE_NUM_MASK) << REG_CNT_PAGE_NUM_SHIFT);
fmc_write(host, FMC_ADDRL, reg);
fmc_pr(REG_DB, "|-Set ADDRL[%#x]%#x\n", FMC_ADDRL, reg);
}
/****************************************************************************/
static void set_cs_addr_reg(enum OP op, struct fmc_host *host)
{
unsigned int reg;
struct fmc_spi *spi = host->spi;
unsigned char iftype = 0;
unsigned char dummy = 0;
reg = FMC_INT_CLR_ALL;
fmc_write(host, FMC_INT_CLR, reg);
fmc_pr(WR_DBG, "|-Set INT_CLR[%#x]%#x\n", FMC_INT_CLR, reg);
if (op == READ) {
iftype = spi->read->iftype;
dummy = spi->read->dummy;
} else if (op == WRITE) {
iftype = spi->write->iftype;
} else {
iftype = spi->erase->iftype;
}
reg = op_cfg_fm_cs(host->cmd_op.cs) |
OP_CFG_OEN_EN |
op_cfg_mem_if_type(iftype) |
op_cfg_dummy_num(dummy);
fmc_write(host, FMC_OP_CFG, reg);
fmc_pr(REG_DB, "|-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg);
set_addr_reg(host);
}
static void pageprog_fmc_op(struct fmc_host* const host, struct fmc_spi* const spi)
{
unsigned int reg;
reg = op_ctrl_wr_opcode(spi->write->cmd) | op_ctrl_dma_op(OP_TYPE_DMA) |
op_ctrl_rw_op(RW_OP_WRITE) | OP_CTRL_DMA_OP_READY;
fmc_write(host, FMC_OP_CTRL, reg);
fmc_pr(WR_DBG, "|-Set OP_CTRL[%#x]%#x\n", FMC_OP_CTRL, reg);
fmc_dma_wait_int_finish(host);
}
static void fmc100_send_cmd_pageprog(struct fmc_host *host)
{
struct fmc_spi *spi = host->spi;
unsigned char *fmc_ip = NULL;
int ret;
#ifndef CONFIG_SYS_DCACHE_OFF
unsigned int dma_align_len;
#endif
fmc_pr(WR_DBG, "\n*-Enter Dma page program!\n");
fmc_ip = get_fmc_ip();
if (*fmc_ip) {
printf("Warning: FMC IP is busy, Please try again.\n");
udelay(1); /* delay 1 us */
return;
} else {
fmc_dev_type_switch(FLASH_TYPE_SPI_NAND);
(*fmc_ip)++;
}
ret = spi->driver->write_enable(spi);
if (ret) {
db_msg("Error: Dma program write enable failed! ret: %#x\n", ret);
goto end;
}
host->set_system_clock(spi->write, ENABLE);
if (ecc0_flag == 1) {
fmc100_ecc0_switch(host, ENABLE);
fmc_write(host, FMC_DMA_LEN, host->oobsize);
}
set_cs_addr_reg(WRITE, host);
if (ecc0_flag != 1)
*host->epm = 0x0000;
#ifndef CONFIG_SYS_DCACHE_OFF
dma_align_len = ((host->pagesize + host->oobsize +
CONFIG_SYS_CACHELINE_SIZE - 1) & ~(CONFIG_SYS_CACHELINE_SIZE - 1));
flush_dcache_range(host->dma_buffer, host->dma_buffer + dma_align_len);
#endif
set_dma_addr_reg(host);
pageprog_fmc_op(host, spi);
if (ecc0_flag == 1)
fmc100_ecc0_switch(host, DISABLE);
ret = spi->driver->wait_ready(spi);
if (ret)
db_msg("Error: Dma program wait ready failed! status: %#x\n", ret);
end:
(*fmc_ip)--;
fmc_pr(WR_DBG, "*-End Dma page program!\n");
}
static void fmc100_send_cmd_readstart(struct fmc_host *host)
{
unsigned int reg;
struct fmc_spi *spi = host->spi;
unsigned char *fmc_ip = NULL;
#ifndef CONFIG_SYS_DCACHE_OFF
unsigned int dma_align_len;
#endif
fmc_pr(RD_DBG, "\n\t*-Start Dma page read\n");
fmc_ip = get_fmc_ip();
if (*fmc_ip) {
printf("Warning: FMC IP is busy, Please try again.\n");
udelay(1); /* delay 1 us */
return;
} else {
fmc_dev_type_switch(FLASH_TYPE_SPI_NAND);
(*fmc_ip)++;
}
host->set_system_clock(spi->read, ENABLE);
if (ecc0_flag == 1 && (host->cmd_op.l_cmd != NAND_CMD_READOOB)) {
fmc100_ecc0_switch(host, ENABLE);
fmc_write(host, FMC_DMA_LEN, host->oobsize);
}
if (host->cmd_op.l_cmd == NAND_CMD_READOOB)
host->cmd_op.op_cfg = op_ctrl_rd_op_sel(RD_OP_READ_OOB);
else
host->cmd_op.op_cfg = op_ctrl_rd_op_sel(RD_OP_READ_ALL_PAGE);
set_cs_addr_reg(READ, host);
#ifndef CONFIG_SYS_DCACHE_OFF
dma_align_len = ((host->pagesize + host->oobsize +
CONFIG_SYS_CACHELINE_SIZE - 1) & ~(CONFIG_SYS_CACHELINE_SIZE - 1));
invalidate_dcache_range(host->dma_buffer, host->dma_buffer + dma_align_len);
#endif
set_dma_addr_reg(host);
reg = op_ctrl_rd_opcode(spi->read->cmd) |
host->cmd_op.op_cfg | op_ctrl_dma_op(OP_TYPE_DMA) |
op_ctrl_rw_op(RW_OP_READ) | OP_CTRL_DMA_OP_READY;
fmc_write(host, FMC_OP_CTRL, reg);
fmc_pr(RD_DBG, "\t|-Set OP_CTRL[%#x]%#x\n", FMC_OP_CTRL, reg);
fmc_dma_wait_int_finish(host);
if (ecc0_flag == 1 && (host->cmd_op.l_cmd != NAND_CMD_READOOB))
fmc100_ecc0_switch(host, DISABLE);
#ifndef CONFIG_SYS_DCACHE_OFF
invalidate_dcache_range(host->dma_buffer, host->dma_buffer + dma_align_len);
#endif
(*fmc_ip)--;
fmc_pr(RD_DBG, "\t*-End Dma page read\n");
}
static void erase_fmc_op(struct fmc_host* const host, struct fmc_spi* const spi)
{
unsigned int reg;
reg = FMC_INT_CLR_ALL;
fmc_write(host, FMC_INT_CLR, reg);
fmc_pr(ER_DBG, "\t|-Set INT_CLR[%#x]%#x\n", FMC_INT_CLR, reg);
reg = spi->erase->cmd;
fmc_write(host, FMC_CMD, fmc_cmd_cmd1(reg));
fmc_pr(ER_DBG, "\t|-Set CMD[%#x]%#x\n", FMC_CMD, reg);
reg = fmc_addrl_block_h_mask(host->addr_value[1]) |
fmc_addrl_block_l_mask(host->addr_value[0]);
fmc_write(host, FMC_ADDRL, reg);
fmc_pr(ER_DBG, "\t|-Set ADDRL[%#x]%#x\n", FMC_ADDRL, reg);
reg = op_cfg_fm_cs(host->cmd_op.cs) | OP_CFG_OEN_EN |
op_cfg_mem_if_type(spi->erase->iftype) |
op_cfg_addr_num(STD_OP_ADDR_NUM) |
op_cfg_dummy_num(spi->erase->dummy);
fmc_write(host, FMC_OP_CFG, reg);
fmc_pr(ER_DBG, "\t|-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg);
reg = fmc_op_cmd1_en(ENABLE) | fmc_op_addr_en(ENABLE) |
FMC_OP_REG_OP_START;
fmc_write(host, FMC_OP, reg);
fmc_pr(ER_DBG, "\t|-Set OP[%#x]%#x\n", FMC_OP, reg);
fmc_cmd_wait_cpu_finish(host);
}
static void fmc100_send_cmd_erase(struct fmc_host *host)
{
struct fmc_spi *spi = host->spi;
unsigned char *fmc_ip = NULL;
int ret;
if (ER_DBG)
printf("\n");
fmc_pr(ER_DBG, "\t*-Start send cmd erase!\n");
fmc_ip = get_fmc_ip();
if (*fmc_ip) {
printf("Warning: FMC IP is busy, Please try again.\n");
udelay(1); /* delay 1 us */
return;
} else {
fmc_dev_type_switch(FLASH_TYPE_SPI_NAND);
(*fmc_ip)++;
}
ret = spi->driver->write_enable(spi);
if (ret) {
db_msg("Error: Erase write enable failed! ret: %#x\n", ret);
goto end;
}
host->set_system_clock(spi->erase, ENABLE);
erase_fmc_op(host, spi);
ret = spi->driver->wait_ready(spi);
fmc_pr(ER_DBG, "\t|-Erase wait ready, ret: %#x\n", ret);
if (ret)
db_msg("Error: Erase wait ready fail! status: %#x\n", ret);
end:
(*fmc_ip)--;
fmc_pr(ER_DBG, "\t*-End send cmd erase!\n");
}
static void fmc100_send_cmd_status(struct fmc_host* const host)
{
unsigned int status;
unsigned char addr = STATUS_ADDR;
struct fmc_spi *spi = host->spi;
unsigned char *fmc_ip = get_fmc_ip();
if (*fmc_ip) {
printf("Warning: FMC IP is busy, Please try again.\n");
udelay(1); /* delay 1 us */
return;
} else {
fmc_dev_type_switch(FLASH_TYPE_SPI_NAND);
(*fmc_ip)++;
}
if (host->cmd_op.l_cmd == NAND_CMD_GET_FEATURES)
addr = PROTECT_ADDR;
if (spi_nand_feature_op(spi, GET_OP, addr, &status)) {
printf("get protect addr failed!\n");
(*fmc_ip)--;
return;
}
fmc_pr((ER_DBG || WR_DBG), "\t*-Get status[%#x]: %#x\n", addr, status);
(*fmc_ip)--;
}
static void fmc100_send_cmd_readid(struct fmc_host *host)
{
unsigned int reg;
fmc_pr(BT_DBG, "\t|*-Start send cmd read ID\n");
fmc100_ecc0_switch(host, ENABLE);
reg = fmc_cmd_cmd1(SPI_CMD_RDID);
fmc_write(host, FMC_CMD, reg);
fmc_pr(BT_DBG, "\t||-Set CMD[%#x]%#x\n", FMC_CMD, reg);
reg = READ_ID_ADDR;
fmc_write(host, FMC_ADDRL, reg);
fmc_pr(BT_DBG, "\t||-Set ADDRL[%#x]%#x\n", FMC_ADDRL, reg);
reg = op_cfg_fm_cs(host->cmd_op.cs) | OP_CFG_OEN_EN |
op_cfg_addr_num(READ_ID_ADDR_NUM);
fmc_write(host, FMC_OP_CFG, reg);
fmc_pr(BT_DBG, "\t||-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg);
reg = fmc_data_num_cnt(MAX_SPI_NAND_ID_LEN);
fmc_write(host, FMC_DATA_NUM, reg);
fmc_pr(BT_DBG, "\t||-Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, reg);
reg = fmc_op_cmd1_en(ENABLE) | fmc_op_addr_en(ENABLE) |
fmc_op_read_data_en(ENABLE) | FMC_OP_REG_OP_START;
fmc_write(host, FMC_OP, reg);
fmc_pr(BT_DBG, "\t||-Set OP[%#x]%#x\n", FMC_OP, reg);
host->addr_cycle = 0x0;
fmc_cmd_wait_cpu_finish(host);
fmc100_ecc0_switch(host, DISABLE);
fmc_pr(BT_DBG, "\t|*-End read flash ID\n");
}
static void fmc100_send_cmd_reset(struct fmc_host* const host)
{
unsigned int reg;
fmc_pr(BT_DBG, "\t|*-Start send cmd reset\n");
reg = fmc_cmd_cmd1(SPI_CMD_RESET);
fmc_write(host, FMC_CMD, reg);
fmc_pr(BT_DBG, "\t||-Set CMD[%#x]%#x\n", FMC_CMD, reg);
reg = op_cfg_fm_cs(host->cmd_op.cs) | OP_CFG_OEN_EN;
fmc_write(host, FMC_OP_CFG, reg);
fmc_pr(BT_DBG, "\t||-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg);
reg = fmc_op_cmd1_en(ENABLE) | FMC_OP_REG_OP_START;
fmc_write(host, FMC_OP, reg);
fmc_pr(BT_DBG, "\t||-Set OP[%#x]%#x\n", FMC_OP, reg);
fmc_cmd_wait_cpu_finish(host);
fmc_pr(BT_DBG, "\t|*-End send cmd reset\n");
}
static unsigned char fmc100_read_byte(struct mtd_info* const mtd)
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct fmc_host *host = chip->priv;
unsigned char value;
unsigned char ret_val = 0;
if (host->cmd_op.l_cmd == NAND_CMD_READID) {
value = readb(host->iobase + host->offset);
host->offset++;
if (host->cmd_op.data_no == host->offset)
host->cmd_op.l_cmd = 0;
return value;
}
if (host->cmd_op.cmd == NAND_CMD_STATUS) {
value = fmc_read(host, FMC_STATUS);
if (host->cmd_op.l_cmd == NAND_CMD_GET_FEATURES) {
fmc_pr((ER_DBG || WR_DBG), "\t\tRead BP status: %#x\n",
value);
if (any_bp_enable(value))
ret_val |= NAND_STATUS_WP;
host->cmd_op.l_cmd = NAND_CMD_STATUS;
}
if (!(value & STATUS_OIP_MASK))
ret_val |= NAND_STATUS_READY;
if ((chip->state == FL_ERASING) &&
(value & STATUS_E_FAIL_MASK)) {
fmc_pr(ER_DBG, "\t\tGet erase status: %#x\n", value);
ret_val |= NAND_STATUS_FAIL;
}
if ((chip->state == FL_WRITING) &&
(value & STATUS_P_FAIL_MASK)) {
fmc_pr(WR_DBG, "\t\tGet write status: %#x\n", value);
ret_val |= NAND_STATUS_FAIL;
}
return ret_val;
}
if (host->cmd_op.l_cmd == NAND_CMD_READOOB) {
value = readb((unsigned char *)
((unsigned char *)(uintptr_t)host->dma_oob + host->offset));
host->offset++;
return value;
}
host->offset++;
return readb(host->buffer + host->column + host->offset - 1);
}
static unsigned short fmc100_read_word(struct mtd_info *mtd)
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct fmc_host *host = chip->priv;
return readw(host->buffer + host->column + host->offset);
}
static void fmc100_write_buf(struct mtd_info* const mtd, const u_char* const buf, int len)
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct fmc_host *host = chip->priv;
if (buf == chip->oob_poi)
memcpy((unsigned char *)(uintptr_t)host->dma_oob, buf, len);
else
memcpy((unsigned char *)(uintptr_t)host->dma_buffer, buf, len);
return;
}
static void fmc100_read_buf(struct mtd_info* const mtd, u_char* const buf, int len)
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct fmc_host *host = chip->priv;
if (buf == chip->oob_poi)
memcpy(buf, (unsigned char *)(uintptr_t)host->dma_oob, len);
else
memcpy(buf, (unsigned char *)(uintptr_t)host->dma_buffer, len);
return;
}
static void fmc100_select_chip(struct mtd_info* const mtd, int chipselect)
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct fmc_host *host = chip->priv;
if (chipselect < 0)
return;
if (chipselect > CONFIG_SPI_NAND_MAX_CHIP_NUM)
db_bug("Error: Invalid chipselect: %d\n", chipselect);
if (host->mtd != mtd)
host->mtd = mtd;
if (!(chip->options & NAND_BROKEN_XD))
if ((chip->state == FL_ERASING) || (chip->state == FL_WRITING))
host->cmd_op.l_cmd = NAND_CMD_GET_FEATURES;
}
static void read_nand_id_op(struct fmc_host *host, unsigned int command)
{
host->offset = 0;
host->cmd_op.l_cmd = command & 0xff;
memset((u_char *)(host->iobase), 0, MAX_SPI_NAND_ID_LEN);
host->cmd_op.data_no = MAX_SPI_NAND_ID_LEN;
host->send_cmd_readid(host);
}
static void fmc100_cmdfunc(struct mtd_info* const mtd,
unsigned int command,
int column, int page_addr)
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct fmc_host *host = chip->priv;
switch (command) {
case NAND_CMD_RESET:
host->send_cmd_reset(host);
chip->dev_ready(mtd);
break;
case NAND_CMD_READID:
read_nand_id_op(host, command);
break;
case NAND_CMD_GET_FEATURES:
case NAND_CMD_STATUS:
host->cmd_op.l_cmd = command & 0xff;
host->cmd_op.cmd = NAND_CMD_STATUS;
host->send_cmd_status(host);
break;
case NAND_CMD_READOOB:
host->offset = 0;
host->cmd_op.l_cmd = command & 0xff;
/* use same command as normal read */
host->cmd_op.cmd = command & 0xff;
case NAND_CMD_READ0:
if (command == NAND_CMD_READ0)
host->cmd_op.l_cmd = command & 0xff;
host->addr_value[1] = page_addr;
host->send_cmd_readstart(host);
break;
case NAND_CMD_SEQIN:
host->addr_value[1] = page_addr;
break;
case NAND_CMD_PAGEPROG:
host->offset = 0;
host->send_cmd_pageprog(host);
break;
case NAND_CMD_ERASE1:
host->cmd_op.l_cmd = command & 0xff;
host->addr_value[0] = page_addr;
/* page_addr to block_addr, move right 16 bits */
host->addr_value[1] = (unsigned int)page_addr >> 16;
/* erase operation need a seral of command sequences */
host->send_cmd_erase(host);
break;
case NAND_CMD_ERASE2:
case NAND_CMD_READSTART:
break;
default:
printf("%s not support command 0x%08x:\n", mtd->name, command);
break;
}
}
static int fmc100_dev_ready(struct mtd_info* const mtd)
{
unsigned int reg;
/* just a big number, so move 12 bits */
unsigned long deadline = 1 << 12;
struct nand_chip *chip = mtd_to_nand(mtd);
struct fmc_host *host = chip->priv;
do {
reg = op_cfg_fm_cs(host->cmd_op.cs) | OP_CFG_OEN_EN;
fmc_write(host, FMC_OP_CFG, reg);
reg = fmc_op_read_status_en(ENABLE) | FMC_OP_REG_OP_START;
fmc_write(host, FMC_OP, reg);
fmc_cmd_wait_cpu_finish(host);
reg = fmc_read(host, FMC_STATUS);
if (!(reg & STATUS_OIP_MASK))
return 1;
udelay(1); /* delay 1 us */
} while (deadline--);
#ifndef CONFIG_SYS_NAND_QUIET_TEST
printf("Warning: Wait SPI nand ready timeout, status: %#x\n", reg);
#endif
return 0;
}
/*
* 'host->epm' only use the first oobfree[0] field, it looks very simple, But..
*/
static struct nand_ecclayout nand_ecc_default = {
.oobfree = {{2, 30} }
};
#ifdef CONFIG_FS_MAY_NOT_YAFFS2
static struct nand_ecclayout nand_ecc_2k16bit = {
.oobfree = {{2, 6} }
};
static struct nand_ecclayout nand_ecc_4k16bit = {
.oobfree = {{2, 14} }
};
#endif
static struct nand_config_info fmc_spi_nand_config_table[] = {
{NAND_PAGE_4K, NAND_ECC_24BIT, 200, &nand_ecc_default},
#ifdef CONFIG_FS_MAY_NOT_YAFFS2
{NAND_PAGE_4K, NAND_ECC_16BIT, 128, &nand_ecc_4k16bit},
#endif
{NAND_PAGE_4K, NAND_ECC_8BIT, 128, &nand_ecc_default},
{NAND_PAGE_4K, NAND_ECC_0BIT, 32, &nand_ecc_default},
{NAND_PAGE_2K, NAND_ECC_24BIT, 128, &nand_ecc_default},
#ifdef CONFIG_FS_MAY_NOT_YAFFS2
{NAND_PAGE_2K, NAND_ECC_16BIT, 64, &nand_ecc_2k16bit},
#endif
{NAND_PAGE_2K, NAND_ECC_8BIT, 64, &nand_ecc_default},
{NAND_PAGE_2K, NAND_ECC_0BIT, 32, &nand_ecc_default},
{0, 0, 0, NULL},
};
/*
* Auto-sensed the page size and ecc type value. driver will try each of page
* size and ecc type one by one till flash can be read and wrote accurately.
* so the page size and ecc type is match adaptively without switch on the board
*/
static struct nand_config_info *fmc100_get_config_type_info(struct mtd_info *mtd)
{
struct nand_config_info *best = NULL;
struct nand_config_info *info = fmc_spi_nand_config_table;
struct nand_chip *chip = mtd_to_nand(mtd);
for (; info->layout; info++) {
if (match_page_type_to_size(info->pagetype) != mtd->writesize)
continue;
if (mtd->oobsize < info->oobsize)
continue;
if (!best || (best->ecctype < info->ecctype))
best = info;
}
/* All SPI NAND are small-page,SLC */
chip->bits_per_cell = 1;
return best;
}
static void fmc100_set_oob_info(struct mtd_info* const mtd,
struct nand_config_info* const info)
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct fmc_host *host = chip->priv;
if (info->ecctype != NAND_ECC_0BIT)
mtd->oobsize = info->oobsize;
host->oobsize = mtd->oobsize;
host->dma_oob = host->dma_buffer + host->pagesize;
host->bbm = (u_char *)(host->buffer + host->pagesize +
FMC_BAD_BLOCK_POS);
chip->ecc.layout = info->layout;
/* EB bytes locate in the bottom two of CTRL(30) */
host->epm = (u_short *)(host->buffer + host->pagesize +
chip->ecc.layout->oobfree[0].offset + EB_NORMAL);
#ifdef CONFIG_FS_MAY_NOT_YAFFS2
if (info->ecctype == NAND_ECC_16BIT) {
if (host->pagesize == _2K)
/* EB bits locate in the bottom two of CTRL(6) */
host->epm = (u_short *)(host->buffer + host->pagesize +
chip->ecc.layout->oobfree[0].offset + EB_2K_16_BIT);
else if (host->pagesize == _4K)
/* EB bit locate in the bottom two of CTRL(14) */
host->epm = (u_short *)(host->buffer + host->pagesize +
chip->ecc.layout->oobfree[0].offset + EB_4K_16_BIT);
}
#endif
}
static unsigned int fmc100_get_ecc_reg(struct fmc_host* const host,
struct nand_config_info* const info)
{
host->ecctype = info->ecctype;
return fmc_cfg_ecc_type(match_ecc_type_to_reg(info->ecctype));
}
static unsigned int fmc100_get_page_reg(struct fmc_host* const host,
struct nand_config_info* const info)
{
host->pagesize = match_page_type_to_size(info->pagetype);
return fmc_cfg_page_size(match_page_type_to_reg(info->pagetype));
}
static int fmc100_get_block_reg(struct fmc_host* const host,
struct nand_config_info* const info, unsigned int* const block_val)
{
unsigned int block_reg = 0;
unsigned int page_per_block;
struct mtd_info *mtd = NULL;
if (info == NULL || host == NULL || host->mtd == NULL) {
printf("para err!\n");
return -1;
}
mtd = host->mtd;
host->block_page_mask = ((mtd->erasesize / mtd->writesize) - 1);
page_per_block = mtd->erasesize / match_page_type_to_size(info->pagetype);
switch (page_per_block) {
case _64_PAGES:
block_reg = BLOCK_SIZE_64_PAGE;
break;
case _128_PAGES:
block_reg = BLOCK_SIZE_128_PAGE;
break;
case _256_PAGES:
block_reg = BLOCK_SIZE_256_PAGE;
break;
case _512_PAGES:
block_reg = BLOCK_SIZE_512_PAGE;
break;
default:
db_msg("Can't support block %#x and page %#x size\n",
mtd->erasesize, mtd->writesize);
}
*block_val = fmc_cfg_block_size(block_reg);
return 0;
}
static void fmc100_set_fmc_cfg_reg(struct mtd_info* const mtd,
struct nand_config_info* const type_info)
{
struct nand_chip *chip = NULL;
struct fmc_host *host = NULL;
unsigned int page_reg;
unsigned int ecc_reg;
unsigned int block_reg;
unsigned int reg_fmc_cfg;
if (mtd == NULL || type_info == NULL)
return;
chip = mtd_to_nand(mtd);
if (chip == NULL || chip->priv == NULL)
return;
host = chip->priv;
ecc_reg = fmc100_get_ecc_reg(host, type_info);
page_reg = fmc100_get_page_reg(host, type_info);
if (fmc100_get_block_reg(host, type_info, &block_reg))
return;
reg_fmc_cfg = fmc_read(host, FMC_CFG);
reg_fmc_cfg &= ~(PAGE_SIZE_MASK | ECC_TYPE_MASK | BLOCK_SIZE_MASK);
reg_fmc_cfg |= ecc_reg | page_reg | block_reg;
fmc_write(host, FMC_CFG, reg_fmc_cfg);
/* max number of correctible bit errors per ecc step */
mtd->ecc_strength = host->ecctype;
/* Save value of FMC_CFG and FMC_CFG_ECC0 to turn on/off ECC */
host->fmc_cfg = reg_fmc_cfg;
host->fmc_cfg_ecc0 = (host->fmc_cfg & ~ECC_TYPE_MASK) | ECC_TYPE_0BIT;
fmc_pr(BT_DBG, "\t|-Save FMC_CFG[%#x]: %#x and FMC_CFG_ECC0: %#x\n",
FMC_CFG, host->fmc_cfg, host->fmc_cfg_ecc0);
}
static int fmc100_set_config_info(struct mtd_info* const mtd)
{
struct nand_config_info *type_info = NULL;
fmc_pr(BT_DBG, "\t*-Start match PageSize and EccType\n");
type_info = fmc100_get_config_type_info(mtd);
if (!type_info)
db_bug(ERR_STR_DRIVER "pagesize: %d and oobsize: %d.\n",
mtd->writesize, mtd->oobsize);
/* Set the page_size, ecc_type, block_size of FMC_CFG[0x0] register */
fmc100_set_fmc_cfg_reg(mtd, type_info);
fmc_pr(BT_DBG, "\t|- PageSize %s EccType %s OOB Size %d\n",
nand_page_name(type_info->pagetype),
nand_ecc_name(type_info->ecctype), type_info->oobsize);
fmc100_set_oob_info(mtd, type_info);
fmc_pr(BT_DBG, "\t*-End match PageSize and EccType\n");
return 0;
}
static void fmc100_chip_init(struct nand_chip* const chip)
{
if (!chip->IO_ADDR_R)
chip->IO_ADDR_R = (void __iomem *)CONFIG_FMC_BUFFER_BASE;
chip->IO_ADDR_W = chip->IO_ADDR_R;
memset((char *)chip->IO_ADDR_R, 0xff, FMC100_BUFFER_LEN);
chip->read_byte = fmc100_read_byte;
chip->read_word = fmc100_read_word;
chip->write_buf = fmc100_write_buf;
chip->read_buf = fmc100_read_buf;
chip->select_chip = fmc100_select_chip;
chip->cmdfunc = fmc100_cmdfunc;
chip->dev_ready = fmc100_dev_ready;
chip->chip_delay = FMC_CHIP_DELAY;
chip->options = NAND_BBT_SCANNED | NAND_BROKEN_XD;
chip->ecc.layout = NULL;
chip->ecc.mode = NAND_ECC_NONE;
}
/*****************************************************************************/
int host_data_init(struct fmc_host *host)
{
unsigned long align_mask;
host->addr_cycle = 0;
host->addr_value[0] = 0;
host->addr_value[1] = 0;
host->cache_addr_value[0] = ~0;
host->cache_addr_value[1] = ~0;
fmc_pr(BT_DBG, "\t|||-Malloc memory for dma buffer\n");
host->buforg = kmalloc((FMC100_BUFFER_LEN + FMC_DMA_ALIGN),
GFP_KERNEL);
if (!host->buforg) {
db_msg("Error: Can't malloc memory for SPI Nand driver.\n");
return -ENOMEM;
}
memset(host->buforg, 0xff, FMC100_BUFFER_LEN + FMC_DMA_ALIGN);
/* DMA need 32 bytes alignment */
align_mask = FMC_DMA_ALIGN - 1;
host->dma_buffer = (uintptr_t)(host->buforg + align_mask) & ~align_mask;
host->buffer = (char *)(uintptr_t)host->dma_buffer;
memset(host->buffer, 0xff, FMC100_BUFFER_LEN);
host->send_cmd_pageprog = fmc100_send_cmd_pageprog;
host->send_cmd_status = fmc100_send_cmd_status;
host->send_cmd_readstart = fmc100_send_cmd_readstart;
host->send_cmd_erase = fmc100_send_cmd_erase;
host->send_cmd_readid = fmc100_send_cmd_readid;
host->send_cmd_reset = fmc100_send_cmd_reset;
host->set_system_clock = fmc_set_fmc_system_clock;
return 0;
}
/*****************************************************************************/
int fmc100_host_init(struct fmc_host *host)
{
unsigned int reg;
unsigned int flash_type;
int ret;
if (!host)
return -1;
fmc_pr(BT_DBG, "\t||*-Start SPI Nand host init\n");
host->iobase = (void __iomem *)CONFIG_FMC_BUFFER_BASE;
host->regbase = (void __iomem *)CONFIG_FMC_REG_BASE;
if (!host->iobase || !host->regbase)
return -1;
reg = fmc_read(host, FMC_CFG);
flash_type = (reg & FLASH_SEL_MASK) >> FLASH_SEL_SHIFT;
if (flash_type != FLASH_TYPE_SPI_NAND) {
db_msg("Error: Flash type isn't SPI Nand. reg: %#x\n", reg);
return -ENODEV;
}
if ((reg & OP_MODE_MASK) == OP_MODE_BOOT) {
reg |= fmc_cfg_op_mode(OP_MODE_NORMAL);
fmc_write(host, FMC_CFG, reg);
fmc_pr(BT_DBG, "\t|||-Set CFG[%#x]%#x\n", FMC_CFG, reg);
}
host->fmc_cfg = reg;
host->fmc_cfg_ecc0 = (reg & ~ECC_TYPE_MASK) | ECC_TYPE_0BIT;
reg = fmc_read(host, FMC_GLOBAL_CFG);
if (reg & FMC_GLOBAL_CFG_WP_ENABLE) {
reg &= ~FMC_GLOBAL_CFG_WP_ENABLE;
fmc_write(host, FMC_GLOBAL_CFG, reg);
}
ret = host_data_init(host);
if (ret)
return ret;
/* ecc0_flag for ecc0 read/write */
ecc0_flag = 0;
reg = timing_cfg_tcsh(CS_HOLD_TIME) |
timing_cfg_tcss(CS_SETUP_TIME) |
timing_cfg_tshsl(CS_DESELECT_TIME);
fmc_write(host, FMC_SPI_TIMING_CFG, reg);
fmc_pr(BT_DBG, "\t|||-Set TIMING[%#x]%#x\n", FMC_SPI_TIMING_CFG, reg);
reg = ALL_BURST_ENABLE;
fmc_write(host, FMC_DMA_AHB_CTRL, reg);
fmc_pr(BT_DBG, "\t|||-Set DMA_AHB[%#x]%#x\n", FMC_DMA_AHB_CTRL, reg);
fmc_pr(BT_DBG, "\t|||-Register SPI Nand ID table and ecc probe\n");
fmc_spi_nand_ids_register();
nand_oob_resize = fmc100_set_config_info;
fmc_pr(BT_DBG, "\t||*-End SPI Nand host init\n");
return 0;
}
/*****************************************************************************/
void fmc100_spi_nand_init(struct fmc_host *host)
{
struct nand_chip *chip = host->chip;
fmc_pr(BT_DBG, "\t|*-Start fmc100 SPI Nand init\n");
/* Set system clock and enable controller */
fmc_pr(BT_DBG, "\t||-Set system clock and Enable Controller\n");
if (host->set_system_clock)
host->set_system_clock(NULL, ENABLE);
/* Fmc nand_chip struct init */
fmc_pr(BT_DBG, "\t||-fmc100 struct nand_chip init\n");
chip->priv = host;
fmc100_chip_init(chip);
fmc_pr(BT_DBG, "\t|*-End fmc100 SPI Nand init\n");
}
@@ -0,0 +1,109 @@
/*
* Copyright (c) LOTUS. All rights reserved.
*/
#ifndef __FMC100_H__
#define __FMC100_H__
#include <nand.h>
#include <linux/lotus/fmc_common.h>
#include <linux/lotus/nfc_common.h>
#include "fmc_spi_ids.h"
#include <cpu_func.h>
/*****************************************************************************/
/* These macroes are for debug only, reg option is slower then dma option */
#undef FMC100_SPI_NAND_SUPPORT_REG_READ
#undef FMC100_SPI_NAND_SUPPORT_REG_WRITE
/*****************************************************************************/
#define REG_CNT_HIGH_BLOCK_NUM_SHIFT 10
#define REG_CNT_BLOCK_NUM_MASK 0x3ff
#define REG_CNT_BLOCK_NUM_SHIFT 22
#define REG_CNT_PAGE_NUM_MASK 0x3f
#define REG_CNT_PAGE_NUM_SHIFT 16
#define REG_CNT_WRAP_MASK 0xf
#define REG_CNT_WRAP_SHIFT 12
#define REG_CNT_ECC_OFFSET_MASK 0xfff
#define REG_CNT_ECC_8BIT_OFFSET 1054
#define REG_CNT_ECC_16BIT_OFFSET 1056
#define REG_CNT_ECC_24BIT_OFFSET 1082
#define ERR_STR_DRIVER "Driver does not support this configure "
#define ERR_STR_CHECK "Please make sure the hardware configuration is correct"
/*****************************************************************************/
#define SPI_NAND_MAX_PAGESIZE 4096
#define SPI_NAND_MAX_OOBSIZE 256
#define FMC100_BUFFER_LEN (SPI_NAND_MAX_PAGESIZE + SPI_NAND_MAX_OOBSIZE)
#define FMC100_ADDR_CYCLE_MASK 0x2
/*****************************************************************************/
struct fmc_host {
struct mtd_info *mtd;
struct nand_chip *chip;
struct fmc_spi spi[CONFIG_SPI_NAND_MAX_CHIP_NUM];
struct fmc_cmd_op cmd_op;
void __iomem *iobase;
void __iomem *regbase;
unsigned int fmc_cfg;
unsigned int fmc_cfg_ecc0;
unsigned int offset;
struct device *dev;
/* This is maybe an un-aligment address, only for malloc or free */
char *buforg;
char *buffer;
unsigned long dma_buffer;
unsigned long dma_oob;
unsigned int addr_cycle;
unsigned int addr_value[2]; /* 2 addr */
unsigned int cache_addr_value[2]; /* 2 addr */
unsigned int column;
unsigned int block_page_mask;
unsigned int ecctype;
unsigned int pagesize;
unsigned int oobsize;
int add_partition;
/* BOOTROM read two bytes to detect the bad block flag */
#define FMC_BAD_BLOCK_POS 0
unsigned char *bbm; /* nand bad block mark */
unsigned short *epm; /* nand empty page mark */
unsigned int uc_er;
void (*send_cmd_pageprog)(struct fmc_host *host);
void (*send_cmd_status)(struct fmc_host *host);
void (*send_cmd_readstart)(struct fmc_host *host);
void (*send_cmd_erase)(struct fmc_host *host);
void (*send_cmd_readid)(struct fmc_host *host);
void (*send_cmd_reset)(struct fmc_host *host);
void (*set_system_clock)(struct spi_op *op, int clk_en);
};
void fmc100_ecc0_switch(struct fmc_host *host, unsigned char op);
int fmc100_host_init(struct fmc_host *host);
void fmc100_spi_nand_init(struct fmc_host *host);
void fmc_spi_nand_ids_register(void);
char spi_nand_feature_op(struct fmc_spi* const spi, unsigned char op,
unsigned char addr, unsigned int* const val);
#endif /* End of __FMC100_H__ */
@@ -0,0 +1,124 @@
/*
* Copyright (c) LOTUS. All rights reserved.
*/
#include "fmc100_os.h"
#include <common.h>
/*****************************************************************************/
static struct fmc_host fmc100_host = {
.chip = NULL,
};
/*****************************************************************************/
static void fmc100_driver_probe(struct nand_chip *chip, unsigned char cs)
{
int ret = 0;
struct fmc_host *host = &fmc100_host;
fmc_pr(BT_DBG, "\t*-Start SPI Nand flash driver probe\n");
if (!host->chip) {
/* Fmc ip version check */
if (fmc_ip_ver_check())
db_bug("Error: fmc IP version unknown!\n");
/* Fmc current SPI device type check */
fmc_dev_type_switch(FLASH_TYPE_SPI_NAND);
/* Fmc SPI nand init */
memset((char *)host, 0, sizeof(struct fmc_host));
ret = fmc100_host_init(host);
if (ret) {
db_msg("Error: Host init failed, result: %d\n", ret);
/* Change SPI device type to default */
fmc_dev_type_switch(FLASH_TYPE_DEFAULT);
return;
}
} else {
fmc_pr(BT_DBG, "\t*-SPI Nand host is initialized.\n");
}
host->cmd_op.cs = cs;
host->chip = chip;
fmc100_spi_nand_init(host);
fmc_pr(BT_DBG, "\t*-End SPI Nand flash driver probe.\n");
return;
}
/*****************************************************************************/
static int fmc100_spi_nand_pre_probe(struct nand_chip *chip)
{
uint8_t nand_maf_id;
struct mtd_info *mtd = nand_to_mtd(chip);
struct fmc_host *host = chip->priv;
/* Reset the chip first */
host->send_cmd_reset(host);
chip->dev_ready(mtd);
/* Check the ID */
host->offset = 0;
memset((unsigned char *)(chip->IO_ADDR_R), 0, 0x10);
host->send_cmd_readid(host);
nand_maf_id = readb(chip->IO_ADDR_R);
if (nand_maf_id == 0x00 || nand_maf_id == 0xff) {
printf("Cannot found a valid SPI Nand Device\n");
return 1;
}
return 0;
}
/*****************************************************************************/
int board_nand_init(struct nand_chip *chip)
{
unsigned char chip_num = CONFIG_SPI_NAND_MAX_CHIP_NUM;
static unsigned char cs = 0;
unsigned char *fmc_cs = NULL;
if (get_boot_media() != BOOT_MEDIA_NAND) {
return 1;
}
for (cs = 0; chip_num && (cs < CONFIG_FMC_MAX_CS_NUM); cs++) {
fmc_cs = get_cs_number(cs);
if (*fmc_cs) {
fmc_pr(BT_DBG, "\t\t*-Current CS(%d) is occupied.\n",
cs);
continue;
}
fmc100_driver_probe(chip, cs);
chip_num--;
}
if (chip_num)
return 1;
if (fmc100_spi_nand_pre_probe(chip))
return 1;
return 0;
}
/*****************************************************************************/
static int fmc100_spi_nand_get_ecctype(void)
{
struct fmc_host *host = &fmc100_host;
if (!host->chip) {
printf("SPI Nand flash uninitialized.\n");
return -1;
}
return match_ecc_type_to_yaffs(fmc100_host.ecctype);
}
/*****************************************************************************/
int nand_get_ecctype(void)
{
return fmc100_spi_nand_get_ecctype();
}
@@ -0,0 +1,13 @@
/*
* Copyright (c) LOTUS. All rights reserved.
*/
#ifndef __FMC100_OS_H__
#define __FMC100_OS_H__
#include "fmc100.h"
/*****************************************************************************/
int board_nand_init(struct nand_chip *chip);
#endif /* End of __FMC100_OS_H__ */
@@ -0,0 +1,276 @@
/*
* Copyright (c) LOTUS. All rights reserved.
*/
/*
* Send set/get features command to SPI Nand flash
*/
void spi_nand_set_cmd(struct fmc_host* const host, u_char op, u_char addr,
u_char val)
{
unsigned int reg;
reg = fmc_cmd_cmd1(op ? SPI_CMD_SET_FEATURE : SPI_CMD_GET_FEATURES);
fmc_write(host, FMC_CMD, reg);
fmc_pr(FT_DBG, "\t||||-Set CMD[%#x]%#x\n", FMC_CMD, reg);
fmc_write(host, FMC_ADDRL, addr);
fmc_pr(FT_DBG, "\t||||-Set ADDRL[%#x]%#x\n", FMC_ADDRL, addr);
reg = op_cfg_fm_cs(host->cmd_op.cs) | OP_CFG_OEN_EN |
op_cfg_addr_num(FEATURES_OP_ADDR_NUM);
fmc_write(host, FMC_OP_CFG, reg);
fmc_pr(FT_DBG, "\t||||-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg);
reg = fmc_data_num_cnt(FEATURES_DATA_LEN);
fmc_write(host, FMC_DATA_NUM, reg);
fmc_pr(FT_DBG, "\t||||-Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, reg);
reg = fmc_op_cmd1_en(ENABLE) | fmc_op_addr_en(ENABLE) |
FMC_OP_REG_OP_START;
if (op == SET_OP) {
reg |= fmc_op_write_data_en(ENABLE);
writeb(val, host->iobase);
fmc_pr(FT_DBG, "\t||||-Write IO[%p]%#x\n", host->iobase,
*(u_char *)host->iobase);
} else {
reg |= fmc_op_read_data_en(ENABLE);
}
fmc_write(host, FMC_OP, reg);
fmc_pr(FT_DBG, "\t||||-Set OP[%#x]%#x\n", FMC_OP, reg);
fmc_cmd_wait_cpu_finish(host);
}
char spi_nand_feature_op(struct fmc_spi* const spi, unsigned char op,
unsigned char addr, unsigned int* const val)
{
unsigned int reg;
const char *str[] = {"Get", "Set"};
struct fmc_host *host = NULL;
unsigned char regval;
if (spi == NULL || spi->host == NULL || val == NULL) {
printf("para err!\n");
return -1;
}
host = (struct fmc_host *)spi->host;
if ((op == GET_OP) && (addr == STATUS_ADDR)) {
fmc_pr(SR_DBG, "\n\t\t|*-Start Get Status\n");
reg = op_cfg_fm_cs(host->cmd_op.cs) | OP_CFG_OEN_EN;
fmc_write(host, FMC_OP_CFG, reg);
fmc_pr(SR_DBG, "\t\t||-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg);
reg = fmc_op_read_status_en(ENABLE) | FMC_OP_REG_OP_START;
fmc_write(host, FMC_OP, reg);
fmc_pr(SR_DBG, "\t\t||-Set OP[%#x]%#x\n", FMC_OP, reg);
fmc_cmd_wait_cpu_finish(host);
*val = fmc_read(host, FMC_STATUS);
fmc_pr(SR_DBG, "\t\t|*-End Get Status, result: %#x\n", *val);
return 0;
}
fmc_pr(FT_DBG, "\t|||*-Start %s feature, addr[%#x]\n", str[op], addr);
fmc100_ecc0_switch(host, ENABLE);
regval = *val & 0xff;
spi_nand_set_cmd(host, op, addr, regval);
if (op == GET_OP) {
if (host->iobase == NULL) {
printf("para err!");
return -1;
}
*val = readb(host->iobase);
fmc_pr(FT_DBG, "\t||||-Read IO[%p]%#x\n", host->iobase,
*(u_char *)host->iobase);
}
fmc100_ecc0_switch(host, DISABLE);
fmc_pr(FT_DBG, "\t|||*-End %s Feature[%#x]:%#x\n", str[op], addr, *val);
return 0;
}
/*
* Read status[C0H]:[0]bit OIP, judge whether the device is busy or not
*/
static int spi_general_wait_ready(struct fmc_spi* const spi)
{
unsigned int status;
/* just get a big number, so move left 12 bits */
unsigned int deadline = 1 << 12;
struct fmc_host *host = (struct fmc_host *)spi->host;
do {
if (spi_nand_feature_op(spi, GET_OP, STATUS_ADDR, &status)) {
printf("get feature failed!\n");
return 1;
}
if (!(status & STATUS_OIP_MASK)) {
if ((host->cmd_op.l_cmd == NAND_CMD_ERASE2) && (status & STATUS_E_FAIL_MASK))
return status;
if ((host->cmd_op.l_cmd == NAND_CMD_PAGEPROG) && (status & STATUS_P_FAIL_MASK))
return status;
return 0;
}
udelay(1); /* delay 1 us */
} while (deadline--);
db_msg("Error: SPI Nand wait ready timeout, status: %#x\n", status);
return 1;
}
static void write_enable_fmc_op(struct fmc_host* const host)
{
unsigned int reg;
reg = fmc_read(host, FMC_GLOBAL_CFG);
fmc_pr(WE_DBG, "\t||-Get GLOBAL_CFG[%#x]%#x\n", FMC_GLOBAL_CFG, reg);
if (reg & FMC_GLOBAL_CFG_WP_ENABLE) {
reg &= ~FMC_GLOBAL_CFG_WP_ENABLE;
fmc_write(host, FMC_GLOBAL_CFG, reg);
fmc_pr(WE_DBG, "\t||-Set GLOBAL_CFG[%#x]%#x\n",
FMC_GLOBAL_CFG, reg);
}
reg = fmc_cmd_cmd1(SPI_CMD_WREN);
fmc_write(host, FMC_CMD, reg);
fmc_pr(WE_DBG, "\t||-Set CMD[%#x]%#x\n", FMC_CMD, reg);
reg = op_cfg_fm_cs(host->cmd_op.cs) | OP_CFG_OEN_EN;
fmc_write(host, FMC_OP_CFG, reg);
fmc_pr(WE_DBG, "\t||-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg);
reg = fmc_op_cmd1_en(ENABLE) | FMC_OP_REG_OP_START;
fmc_write(host, FMC_OP, reg);
fmc_pr(WE_DBG, "\t||-Set OP[%#x]%#x\n", FMC_OP, reg);
fmc_cmd_wait_cpu_finish(host);
}
/*
* Send write enable cmd to SPI Nand, status[C0H]:[2]bit WEL must be set 1
*/
static int spi_general_write_enable(struct fmc_spi *spi)
{
unsigned int reg;
struct fmc_host *host = (struct fmc_host *)spi->host;
int ret;
if (WE_DBG)
printf("\n");
fmc_pr(WE_DBG, "\t|*-Start Write Enable\n");
ret = spi_nand_feature_op(spi, GET_OP, STATUS_ADDR, &reg);
if (ret) {
db_msg("Error: Get status reg failed,line:%d\n", __LINE__);
return ret;
}
if (reg & STATUS_WEL_MASK) {
fmc_pr(WE_DBG, "\t||-Write Enable was opened! reg: %#x\n", reg);
return 0;
}
write_enable_fmc_op(host);
#if WE_DBG
spi->driver->wait_ready(spi);
ret = spi_nand_feature_op(spi, GET_OP, STATUS_ADDR, &reg);
if (ret) {
db_msg("Error: Get status reg failed,line:%d\n", __LINE__);
return ret;
}
if (reg & STATUS_WEL_MASK) {
fmc_pr(WE_DBG, "\t||-Write Enable success. reg: %#x\n", reg);
} else {
db_msg("Error: Write Enable failed! reg: %#x\n", reg);
return ret;
}
#endif
fmc_pr(WE_DBG, "\t|*-End Write Enable\n");
return 0;
}
/*
* judge whether SPI Nand support QUAD read/write or not
*/
static int spi_is_quad(struct fmc_spi *spi)
{
const char *if_str[] = {"STD", "DUAL", "DIO", "QUAD", "QIO"};
fmc_pr(QE_DBG, "\t\t|||*-SPI read iftype: %s write iftype: %s\n",
if_str[spi->read->iftype], if_str[spi->write->iftype]);
if ((spi->read->iftype == IF_TYPE_QUAD) ||
(spi->read->iftype == IF_TYPE_QIO) ||
(spi->write->iftype == IF_TYPE_QUAD) ||
(spi->write->iftype == IF_TYPE_QIO))
return 1;
return 0;
}
/*
* Send set features cmd to SPI Nand, feature[B0H]:[0]bit QE would be set
*/
static int spi_general_qe_enable(struct fmc_spi *spi)
{
unsigned int reg;
int op;
int ret;
const char *str[] = {"Disable", "Enable"};
fmc_pr(QE_DBG, "\t||*-Start SPI Nand flash QE\n");
op = spi_is_quad(spi);
fmc_pr(QE_DBG, "\t|||*-End Quad check, SPI Nand %s Quad.\n", str[op]);
ret = spi_nand_feature_op(spi, GET_OP, FEATURE_ADDR, &reg);
if (ret) {
db_msg("Error: Get feature reg failed,line:%d\n", __LINE__);
return ret;
}
fmc_pr(QE_DBG, "\t|||-Get [%#x]feature: %#x\n", FEATURE_ADDR, reg);
if ((reg & FEATURE_QE_ENABLE) == op) {
fmc_pr(QE_DBG, "\t||*-SPI Nand quad was %sd!\n", str[op]);
return op;
}
if (op == ENABLE)
reg |= FEATURE_QE_ENABLE;
else
reg &= ~FEATURE_QE_ENABLE;
ret = spi_nand_feature_op(spi, SET_OP, FEATURE_ADDR, &reg);
if (ret) {
db_msg("Error: set feature reg failed,line:%d\n", __LINE__);
return ret;
}
fmc_pr(QE_DBG, "\t|||-SPI Nand %s Quad\n", str[op]);
spi->driver->wait_ready(spi);
ret = spi_nand_feature_op(spi, GET_OP, FEATURE_ADDR, &reg);
if (ret) {
db_msg("Error: Get feature reg failed,line:%d\n", __LINE__);
return ret;
}
if ((reg & FEATURE_QE_ENABLE) == op)
fmc_pr(QE_DBG, "\t|||-SPI Nand %s Quad succeed!\n", str[op]);
else
db_msg("Error: %s Quad failed! reg: %#x\n", str[op], reg);
fmc_pr(QE_DBG, "\t||*-End SPI Nand %s Quad.\n", str[op]);
return op;
}
/* some spi nand flash don't QUAD enable */
static int spi_do_not_qe_enable(struct fmc_spi *spi)
{
return 0;
}
@@ -0,0 +1,92 @@
/*
* Copyright (c) LOTUS. All rights reserved.
*/
#include <linux/string.h>
#include <linux/lotus/match_table.h>
int reg2type(const struct match_reg_type *table, int length, int reg, int def)
{
while (length-- > 0) {
if (table->reg == reg)
return table->type;
table++;
}
return def;
}
int type2reg(const struct match_reg_type *table, int length, int type, int def)
{
while (length-- > 0) {
if (table->type == type)
return table->reg;
table++;
}
return def;
}
int str2type(const struct match_type_str *table, int length, const char *str,
int size, int def)
{
while (length-- > 0) {
if (!strncmp(table->str, str, size))
return table->type;
table++;
}
return def;
}
const char *type2str(const struct match_type_str *table, int length, int type,
const char *def)
{
while (length-- > 0) {
if (table->type == type)
return table->str;
table++;
}
return def;
}
int match_reg_to_type(const struct match_t *table, int nr_table, int reg, int def)
{
while (nr_table-- > 0) {
if (table->reg == reg)
return table->type;
table++;
}
return def;
}
int match_type_to_reg(const struct match_t *table, int nr_table, int type, int def)
{
while (nr_table-- > 0) {
if (table->type == type)
return table->reg;
table++;
}
return def;
}
int match_data_to_type(const struct match_t *table, int nr_table, const char *data,
int size, int def)
{
while (nr_table-- > 0) {
if (!memcmp(table->data, data, size))
return table->type;
table++;
}
return def;
}
void *match_type_to_data(const struct match_t *table, int nr_table, int type,
void *def)
{
while (nr_table-- > 0) {
if (table->type == type)
return table->data;
table++;
}
return def;
}
@@ -0,0 +1,165 @@
/*
* Copyright (c) LOTUS. All rights reserved.
*/
#include <linux/lotus/nfc_common.h>
#include <linux/lotus/match_table.h>
/*****************************************************************************/
struct nand_flash_dev *(*get_flash_type)(
struct mtd_info *mtd,
struct nand_chip *chip,
unsigned char *id) = NULL;
int (*nand_oob_resize)(struct mtd_info *mtd) = NULL;
/*****************************************************************************/
#if defined(CONFIG_FMC_SPI_NAND) || defined(CONFIG_FMC_NAND)
static struct match_type_str ecc2name[] = {
{NAND_ECC_0BIT, "none" },
{NAND_ECC_8BIT, "4bit/512" },
{NAND_ECC_16BIT, "8bit/512" },
{NAND_ECC_24BIT, "24bit/1K" },
{NAND_ECC_28BIT, "28bit/1K" },
{NAND_ECC_40BIT, "40bit/1K" },
{NAND_ECC_42BIT, "42bit/1K" },
{NAND_ECC_64BIT, "64bit/1K" },
};
const char *nand_ecc_name(int type)
{
return type2str(ecc2name, ARRAY_SIZE(ecc2name), type, "unknown");
}
/*****************************************************************************/
static struct match_type_str page2name[] = {
{ NAND_PAGE_512B, "512" },
{ NAND_PAGE_2K, "2K" },
{ NAND_PAGE_4K, "4K" },
{ NAND_PAGE_8K, "8K" },
{ NAND_PAGE_16K, "16K" },
{ NAND_PAGE_32K, "32K" },
};
const char *nand_page_name(int type)
{
return type2str(page2name, ARRAY_SIZE(page2name), type, "unknown");
}
/*****************************************************************************/
static struct match_reg_type page2size[] = {
{ _512B, NAND_PAGE_512B },
{ _2K, NAND_PAGE_2K },
{ _4K, NAND_PAGE_4K },
{ _8K, NAND_PAGE_8K },
{ _16K, NAND_PAGE_16K },
{ _32K, NAND_PAGE_32K },
};
int nandpage_size2type(int size)
{
return reg2type(page2size, ARRAY_SIZE(page2size), size, NAND_PAGE_2K);
}
int nandpage_type2size(int size)
{
return type2reg(page2size, ARRAY_SIZE(page2size), size, NAND_PAGE_2K);
}
#endif
/*****************************************************************************/
#define ET_ECC_NONE 0x00
#define ET_ECC_4BIT 0x02
#define ET_ECC_8BIT 0x03
#define ET_ECC_24BIT 0x04
#define ET_ECC_40BIT1K 0x05
#define ET_ECC_64BIT1K 0x06
static struct match_reg_type ecc_yaffs_type_t[] = {
{ET_ECC_NONE, NAND_ECC_0BIT},
{ET_ECC_4BIT, NAND_ECC_8BIT},
{ET_ECC_8BIT, NAND_ECC_16BIT},
{ET_ECC_24BIT, NAND_ECC_24BIT},
{ET_ECC_40BIT1K, NAND_ECC_40BIT},
{ET_ECC_64BIT1K, NAND_ECC_64BIT}
};
unsigned char match_ecc_type_to_yaffs(unsigned char type)
{
return type2reg(ecc_yaffs_type_t, ARRAY_SIZE(ecc_yaffs_type_t), type,
ET_ECC_4BIT);
}
/*****************************************************************************/
static struct match_t page_table[] = {
{NAND_PAGE_2K, PAGE_SIZE_2KB, "2K"},
{NAND_PAGE_4K, PAGE_SIZE_4KB, "4K"},
{NAND_PAGE_8K, PAGE_SIZE_8KB, "8K"},
{NAND_PAGE_16K, PAGE_SIZE_16KB, "16K"},
};
unsigned char match_page_reg_to_type(unsigned char reg)
{
return match_reg_to_type(page_table, ARRAY_SIZE(page_table), reg,
NAND_PAGE_2K);
}
unsigned char match_page_type_to_reg(unsigned char type)
{
return match_type_to_reg(page_table, ARRAY_SIZE(page_table), type,
PAGE_SIZE_2KB);
}
const char *match_page_type_to_str(unsigned char type)
{
return match_type_to_data(page_table, ARRAY_SIZE(page_table), type,
"unknown");
}
/*****************************************************************************/
static struct match_t ecc_table[] = {
{NAND_ECC_0BIT, ECC_TYPE_0BIT, "none"},
{NAND_ECC_8BIT, ECC_TYPE_8BIT, "4bit/512"},
{NAND_ECC_16BIT, ECC_TYPE_16BIT, "8bit/512"},
{NAND_ECC_24BIT, ECC_TYPE_24BIT, "24bit/1K"},
{NAND_ECC_28BIT, ECC_TYPE_28BIT, "28bit/1K"},
{NAND_ECC_40BIT, ECC_TYPE_40BIT, "40bit/1K"},
{NAND_ECC_64BIT, ECC_TYPE_64BIT, "64bit/1K"},
};
unsigned char match_ecc_reg_to_type(unsigned char reg)
{
return match_reg_to_type(ecc_table, ARRAY_SIZE(ecc_table), reg,
NAND_ECC_8BIT);
}
unsigned char match_ecc_type_to_reg(unsigned char type)
{
return match_type_to_reg(ecc_table, ARRAY_SIZE(ecc_table), type,
ECC_TYPE_8BIT);
}
const char *match_ecc_type_to_str(unsigned char type)
{
return match_type_to_data(ecc_table, ARRAY_SIZE(ecc_table), type,
"unknown");
}
/*****************************************************************************/
static struct match_t page_type_size_table[] = {
{NAND_PAGE_2K, _2K, NULL},
{NAND_PAGE_4K, _4K, NULL},
{NAND_PAGE_8K, _8K, NULL},
{NAND_PAGE_16K, _16K, NULL},
};
unsigned char match_page_size_to_type(unsigned int size)
{
return match_reg_to_type(page_type_size_table,
ARRAY_SIZE(page_type_size_table), size, NAND_PAGE_2K);
}
unsigned int match_page_type_to_size(unsigned char type)
{
return match_type_to_reg(page_type_size_table,
ARRAY_SIZE(page_type_size_table), type, _2K);
}