gk7205v200-uboot/drivers/mtd/nand/fmc100/fmc100_spi_general.c
2025-08-07 17:13:54 +08:00

255 lines
7.0 KiB
C
Executable File

/*
* Copyright (c) Hunan Goke,Chengdu Goke,Shandong Goke. 2021. All rights reserved.
*/
/*
* Send set/get features command to SPI Nand flash
*/
/*****************************************************************************/
void spi_nand_set_cmd(struct fmc_host *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);
}
/*****************************************************************************/
unsigned char spi_nand_feature_op(struct fmc_spi *spi, unsigned char op,
unsigned char addr, unsigned char val)
{
unsigned int reg = 0;
const char *str[] = {"Get", "Set"};
struct fmc_host *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 val;
}
fmc_pr(FT_DBG, "\t|||*-Start %s feature, addr[%#x]\n", str[op], addr);
fmc100_ecc0_switch(host, ENABLE);
spi_nand_set_cmd(host, op, addr, val);
if (op == GET_OP) {
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 val;
}
/*****************************************************************************/
/*
* Read status[C0H]:[0]bit OIP, judge whether the device is busy or not
*/
static int spi_general_wait_ready(struct fmc_spi *spi)
{
unsigned char 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 {
status = spi_nand_feature_op(spi, GET_OP, STATUS_ADDR, 0);
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;
}
/*****************************************************************************/
/*
* 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;
if (WE_DBG)
printf("\n");
fmc_pr(WE_DBG, "\t|*-Start Write Enable\n");
reg = spi_nand_feature_op(spi, GET_OP, STATUS_ADDR, 0);
if (reg & STATUS_WEL_MASK) {
fmc_pr(WE_DBG, "\t||-Write Enable was opened! reg: %#x\n",
reg);
return 0;
}
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);
#if WE_DBG
spi->driver->wait_ready(spi);
reg = spi_nand_feature_op(spi, GET_OP, STATUS_ADDR, 0);
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 reg;
}
#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;
unsigned int op;
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]);
reg = spi_nand_feature_op(spi, GET_OP, FEATURE_ADDR, 0);
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;
spi_nand_feature_op(spi, SET_OP, FEATURE_ADDR, reg);
fmc_pr(QE_DBG, "\t|||-SPI Nand %s Quad\n", str[op]);
spi->driver->wait_ready(spi);
reg = spi_nand_feature_op(spi, GET_OP, FEATURE_ADDR, 0);
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;
}