983 lines
28 KiB
C
983 lines
28 KiB
C
|
|
/*
|
||
|
|
* Copyright (c) Hunan Goke,Chengdu Goke,Shandong Goke. 2021. All rights reserved.
|
||
|
|
*/
|
||
|
|
|
||
|
|
#include "fmc100.h"
|
||
|
|
#include <common.h>
|
||
|
|
#include <asm/io.h>
|
||
|
|
#include <errno.h>
|
||
|
|
#include <malloc.h>
|
||
|
|
#include <match_table.h>
|
||
|
|
#include <asm/arch/platform.h>
|
||
|
|
|
||
|
|
/*****************************************************************************/
|
||
|
|
void fmc100_ecc0_switch(struct fmc_host *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 u32 select_addr_of(unsigned char only_oob, struct fmc_host *host)
|
||
|
|
{
|
||
|
|
unsigned int addr_of = 0;
|
||
|
|
|
||
|
|
if (only_oob)
|
||
|
|
switch (host->ecctype) {
|
||
|
|
case NAND_ECC_8BIT:
|
||
|
|
addr_of = REG_CNT_ECC_8BIT_OFFSET;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case NAND_ECC_16BIT:
|
||
|
|
addr_of = REG_CNT_ECC_16BIT_OFFSET;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case NAND_ECC_24BIT:
|
||
|
|
addr_of = REG_CNT_ECC_24BIT_OFFSET;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case NAND_ECC_0BIT:
|
||
|
|
default:
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
return addr_of;
|
||
|
|
}
|
||
|
|
|
||
|
|
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(unsigned int addr_of, 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) |
|
||
|
|
(addr_of & REG_CNT_ECC_OFFSET_MASK);
|
||
|
|
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, unsigned char only_oob,
|
||
|
|
struct fmc_host *host)
|
||
|
|
{
|
||
|
|
unsigned int reg;
|
||
|
|
struct fmc_spi *spi = host->spi;
|
||
|
|
unsigned char iftype = 0;
|
||
|
|
unsigned char dummy = 0;
|
||
|
|
unsigned int addr_of = 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;
|
||
|
|
addr_of = select_addr_of(only_oob, host);
|
||
|
|
} 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(addr_of, host);
|
||
|
|
}
|
||
|
|
|
||
|
|
/*****************************************************************************/
|
||
|
|
static void fmc100_send_cmd_pageprog(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(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)++;
|
||
|
|
}
|
||
|
|
|
||
|
|
reg = spi->driver->write_enable(spi);
|
||
|
|
if (reg) {
|
||
|
|
db_msg("Error: Dma program write enable failed! reg: %#x\n", reg);
|
||
|
|
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, 0, 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);
|
||
|
|
|
||
|
|
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);
|
||
|
|
|
||
|
|
if (ecc0_flag == 1)
|
||
|
|
fmc100_ecc0_switch(host, DISABLE);
|
||
|
|
|
||
|
|
reg = spi->driver->wait_ready(spi);
|
||
|
|
if (reg)
|
||
|
|
db_msg("Error: Dma program wait ready failed! status: %#x\n", reg);
|
||
|
|
|
||
|
|
end:
|
||
|
|
(*fmc_ip)--;
|
||
|
|
fmc_pr(WR_DBG, "*-End Dma page program!\n");
|
||
|
|
}
|
||
|
|
/*****************************************************************************/
|
||
|
|
static void fmc100_send_cmd_readstart(struct fmc_host *host)
|
||
|
|
{
|
||
|
|
unsigned char only_oob = 0;
|
||
|
|
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) {
|
||
|
|
only_oob = 1;
|
||
|
|
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, only_oob, 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 fmc100_send_cmd_erase(struct fmc_host *host)
|
||
|
|
{
|
||
|
|
unsigned int reg;
|
||
|
|
struct fmc_spi *spi = host->spi;
|
||
|
|
unsigned char *fmc_ip = NULL;
|
||
|
|
|
||
|
|
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)++;
|
||
|
|
}
|
||
|
|
|
||
|
|
reg = spi->driver->write_enable(spi);
|
||
|
|
if (reg) {
|
||
|
|
db_msg("Error: Erase write enable failed! reg: %#x\n", reg);
|
||
|
|
goto end;
|
||
|
|
}
|
||
|
|
|
||
|
|
host->set_system_clock(spi->erase, ENABLE);
|
||
|
|
|
||
|
|
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);
|
||
|
|
|
||
|
|
reg = spi->driver->wait_ready(spi);
|
||
|
|
fmc_pr(ER_DBG, "\t|-Erase wait ready, reg: %#x\n", reg);
|
||
|
|
if (reg)
|
||
|
|
db_msg("Error: Erase wait ready fail! status: %#x\n", reg);
|
||
|
|
|
||
|
|
end:
|
||
|
|
(*fmc_ip)--;
|
||
|
|
|
||
|
|
fmc_pr(ER_DBG, "\t*-End send cmd erase!\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
/*****************************************************************************/
|
||
|
|
static void fmc100_send_cmd_status(struct fmc_host *host)
|
||
|
|
{
|
||
|
|
unsigned char 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;
|
||
|
|
|
||
|
|
status = spi_nand_feature_op(spi, GET_OP, addr, 0);
|
||
|
|
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 *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 *mtd)
|
||
|
|
{
|
||
|
|
struct nand_chip *chip = mtd_to_nand(mtd);
|
||
|
|
struct fmc_host *host = chip->priv;
|
||
|
|
unsigned char value = 0;
|
||
|
|
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 *mtd, const u_char *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 *mtd, u_char *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 *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 fmc100_cmdfunc(struct mtd_info *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:
|
||
|
|
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);
|
||
|
|
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 *mtd)
|
||
|
|
{
|
||
|
|
unsigned int reg = 0;
|
||
|
|
/* 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 *mtd,
|
||
|
|
struct nand_config_info *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 (best->ecctype == NAND_ECC_16BIT)
|
||
|
|
if (host->pagesize == _2K)
|
||
|
|
/* EB bits locate in the bottom two of CTRL(4) */
|
||
|
|
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 *host,
|
||
|
|
struct nand_config_info *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 *host,
|
||
|
|
struct nand_config_info *info)
|
||
|
|
{
|
||
|
|
host->pagesize = match_page_type_to_size(info->pagetype);
|
||
|
|
|
||
|
|
return fmc_cfg_page_size(match_page_type_to_reg(info->pagetype));
|
||
|
|
}
|
||
|
|
|
||
|
|
/*****************************************************************************/
|
||
|
|
static unsigned int fmc100_get_block_reg(struct fmc_host *host,
|
||
|
|
struct nand_config_info *info)
|
||
|
|
{
|
||
|
|
unsigned int block_reg = 0;
|
||
|
|
unsigned int page_per_block;
|
||
|
|
struct mtd_info *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);
|
||
|
|
}
|
||
|
|
|
||
|
|
return fmc_cfg_block_size(block_reg);
|
||
|
|
}
|
||
|
|
|
||
|
|
/*****************************************************************************/
|
||
|
|
static void fmc100_set_fmc_cfg_reg(struct mtd_info *mtd,
|
||
|
|
struct nand_config_info *type_info)
|
||
|
|
{
|
||
|
|
struct nand_chip *chip = mtd_to_nand(mtd);
|
||
|
|
struct fmc_host *host = chip->priv;
|
||
|
|
unsigned int page_reg, ecc_reg, block_reg, reg_fmc_cfg;
|
||
|
|
|
||
|
|
ecc_reg = fmc100_get_ecc_reg(host, type_info);
|
||
|
|
page_reg = fmc100_get_page_reg(host, type_info);
|
||
|
|
block_reg = fmc100_get_block_reg(host, type_info);
|
||
|
|
|
||
|
|
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 *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 *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;
|
||
|
|
|
||
|
|
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;
|
||
|
|
|
||
|
|
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");
|
||
|
|
}
|