2912 lines
80 KiB
C
2912 lines
80 KiB
C
/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
|
|
/*
|
|
* drivers/nand/phy/chip_operation.c
|
|
*
|
|
* Copyright (C) 2020 Amlogic, Inc. All rights reserved.
|
|
*
|
|
*/
|
|
|
|
#include "../include/phynand.h"
|
|
extern int mt_L04A_nand_check(struct amlnand_chip *aml_chip);
|
|
extern int mt_L05B_nand_check(struct amlnand_chip *aml_chip);
|
|
|
|
static int _read_page_single_plane(struct amlnand_chip *aml_chip,
|
|
u8 chipnr, u8 *buf, u8 *oob_buf,
|
|
u32 page_addr);
|
|
static int write_page_two_plane(struct amlnand_chip *aml_chip,
|
|
int chipnr,
|
|
u8 *buf,
|
|
u8 *oob_buf,
|
|
u32 plane0_page_addr,
|
|
u32 plane1_page_addr);
|
|
static int write_page_single_plane(struct amlnand_chip *aml_chip,
|
|
u8 chipnr, u8 *buf, u8 *oob_buf,
|
|
u32 page_addr);
|
|
|
|
static int check_cmdfifo_size(struct hw_controller *controller)
|
|
{
|
|
int time_out_cnt = 0, retry_cnt = 0;
|
|
|
|
RETRY:
|
|
do {
|
|
if (NFC_CMDFIFO_SIZE(controller) <= 0)
|
|
break;
|
|
} while (time_out_cnt++ <= AML_DMA_BUSY_TIMEOUT);
|
|
|
|
if (time_out_cnt >= AML_DMA_BUSY_TIMEOUT) {
|
|
if (retry_cnt++ > 3) {
|
|
aml_nand_msg("check cmdfifo size timeout!");
|
|
return -NAND_FAILED;
|
|
} else {
|
|
aml_nand_dbg("check cmdfifo size timeout retry_cnt:%d",
|
|
retry_cnt);
|
|
udelay(10);
|
|
goto RETRY;
|
|
}
|
|
}
|
|
return NAND_SUCCESS;
|
|
}
|
|
|
|
|
|
/************************************************************
|
|
* nand_check_wp - [GENERIC] check if the chip is write protected
|
|
* Check, if the chip is write protected.
|
|
* First of all, check controller->option, then read status, check nand status
|
|
*
|
|
*************************************************************/
|
|
static int check_wp(struct amlnand_chip *aml_chip)
|
|
{
|
|
struct hw_controller *controller = &(aml_chip->controller);
|
|
int i, time_out_cnt = 0;
|
|
|
|
/* force WP for readonly add for shutdown protect */
|
|
if (controller->option & NAND_CTRL_FORCE_WP) {
|
|
aml_nand_msg("check force WP here");
|
|
return -NAND_WP_FAILURE;
|
|
}
|
|
|
|
/* Check the WP bit */
|
|
for (i = 0; i < controller->chip_num; i++) {
|
|
controller->select_chip(controller, i);
|
|
/* read status */
|
|
controller->cmd_ctrl(controller, NAND_CMD_STATUS,
|
|
NAND_CTRL_CLE);
|
|
NFC_SEND_CMD_IDLE(controller, NAND_TWHR_TIME_CYCLE);
|
|
NFC_SEND_CMD_IDLE(controller, 0);
|
|
NFC_SEND_CMD_IDLE(controller, 0);
|
|
do {
|
|
if (NFC_CMDFIFO_SIZE(controller) <= 0)
|
|
break;
|
|
udelay(2);
|
|
} while (time_out_cnt++ <= AML_NAND_READ_BUSY_TIMEOUT);
|
|
|
|
if (time_out_cnt >= AML_NAND_READ_BUSY_TIMEOUT) {
|
|
aml_nand_msg("cmd fifo size failed to clear!");
|
|
aml_nand_msg("NFC_CMDFIFO_SIZE():%d" ,
|
|
NFC_CMDFIFO_SIZE(controller));
|
|
return -NAND_CMD_FAILURE;
|
|
}
|
|
|
|
if (controller->readbyte(controller) & NAND_STATUS_WP)
|
|
return -NAND_WP_FAILURE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ecc_read_retry_handle(struct amlnand_chip *aml_chip,
|
|
u8 chipnr,
|
|
u8 *tmp_buf,
|
|
u8 user_byte_num,
|
|
u32 page_size,
|
|
u8 slc_mode,
|
|
u8 up_page)
|
|
{
|
|
struct hw_controller *controller = &(aml_chip->controller);
|
|
struct nand_flash *flash = &(aml_chip->flash);
|
|
struct chip_ops_para *ops_para = &(aml_chip->ops_para);
|
|
struct read_retry_info *retry_info = &(controller->retry_info);
|
|
u8 need_retry;
|
|
u8 *retry_cnt;
|
|
int retry_op_cnt, retry_cnt_lp, retry_cnt_up, ret;
|
|
|
|
retry_op_cnt = retry_cnt_lp = retry_info->retry_cnt_lp;
|
|
retry_cnt_up = retry_info->retry_cnt_up;
|
|
retry_cnt = &aml_chip->g_retry_cnt;
|
|
|
|
if ((flash->new_type == HYNIX_20NM_8GB)
|
|
|| (flash->new_type == HYNIX_20NM_4GB)
|
|
|| (flash->new_type == HYNIX_1YNM))
|
|
retry_op_cnt = retry_info->retry_cnt_lp *
|
|
retry_info->retry_cnt_lp;
|
|
|
|
/* get usr bytes */
|
|
controller->get_usr_byte(controller, tmp_buf, user_byte_num);
|
|
ret = controller->hwecc_correct(controller, page_size, tmp_buf);
|
|
if (ret == NAND_ECC_FAILURE) {
|
|
/* check rand_mode and 0xff page */
|
|
if (controller->zero_cnt < controller->ecc_max)
|
|
return RETURN_PAGE_ALL_0XFF;
|
|
/* ecc fail */
|
|
need_retry = 0;
|
|
if (retry_info->flag && (slc_mode == 0)) {
|
|
if ((flash->new_type != SANDISK_19NM)
|
|
&& ((*retry_cnt)++ < retry_op_cnt)) {
|
|
need_retry = 1;
|
|
} else if (flash->new_type == SANDISK_19NM) {
|
|
if (up_page) {
|
|
if ((*retry_cnt)++ < retry_cnt_up)
|
|
need_retry = 1;
|
|
} else if ((*retry_cnt)++ < retry_cnt_lp)
|
|
need_retry = 1;
|
|
}
|
|
if (need_retry) {
|
|
aml_nand_dbg("read retry");
|
|
aml_nand_dbg("chip:%d cnt:%d",
|
|
chipnr,
|
|
*retry_cnt);
|
|
ret = retry_info->handle(controller, chipnr);
|
|
if (ret < 0) {
|
|
aml_nand_msg("read retry");
|
|
aml_nand_msg("fail at plane0_page");
|
|
}
|
|
/* next round */
|
|
return RETURN_PAGE_NEED_READRETRY;
|
|
}
|
|
}
|
|
ret = -1;
|
|
} else {
|
|
if ((*retry_cnt)) {
|
|
if (flash->new_type != SANDISK_19NM) {
|
|
if ((*retry_cnt) > (retry_op_cnt-2)) {
|
|
aml_nand_dbg("detect bitflip page:%d",
|
|
controller->page_addr);
|
|
ops_para->bit_flip++;
|
|
}
|
|
} else {
|
|
if (up_page) {
|
|
if ((*retry_cnt) > (retry_cnt_up-2)) {
|
|
aml_nand_dbg("detect bitflip page:%d",
|
|
controller->page_addr);
|
|
ops_para->bit_flip++;
|
|
}
|
|
} else {
|
|
if ((*retry_cnt) > (retry_cnt_lp-2)) {
|
|
aml_nand_dbg("detect bitflip page:%d",
|
|
controller->page_addr);
|
|
ops_para->bit_flip++;
|
|
}
|
|
}
|
|
}
|
|
} else if ((controller->ecc_cnt_cur > controller->ecc_cnt_limit)
|
|
&& (flash->new_type == 0)) {
|
|
aml_nand_dbg("detect bitflip page:%d, chip:%d",
|
|
controller->page_addr, chipnr);
|
|
ops_para->bit_flip++;
|
|
}
|
|
ret = 0;
|
|
}
|
|
/* exit after all retry effort */
|
|
if ((*retry_cnt) && retry_info->exit) {
|
|
ret |= retry_info->exit(controller, chipnr);
|
|
if (ret < 0) {
|
|
aml_nand_msg("retry exit failed");
|
|
aml_nand_msg("flash->new_type:%d, cnt:%d",
|
|
flash->new_type,
|
|
*retry_cnt);
|
|
}
|
|
}
|
|
/* zero retry... */
|
|
*retry_cnt = 0;
|
|
return ret;
|
|
}
|
|
|
|
#if (AML_CFG_2PLANE_READ_EN)
|
|
static int read_page_two_plane(struct amlnand_chip *aml_chip,
|
|
u8 chipnr,
|
|
u8 *buf,
|
|
u8 *oob_buf,
|
|
u32 plane0_page_addr,
|
|
u32 plane1_page_addr)
|
|
{
|
|
struct hw_controller *controller = &(aml_chip->controller);
|
|
struct nand_flash *flash = &(aml_chip->flash);
|
|
struct chip_ops_para *ops_para = &(aml_chip->ops_para);
|
|
//u8 *buf = ops_para->data_buf;
|
|
u8 *tmp_buf = controller->oob_buf;
|
|
u32 pages_per_blk_shift;
|
|
u32 page_size, page_addr, tmp_page;
|
|
u32 page0_addr, page1_addr;
|
|
u8 plane0_retry_flag = 0, all_ff_flag = 0;
|
|
u8 bch_mode, user_byte_num, slc_mode, up_page = 0;
|
|
int ret = 0;
|
|
int new_oob = 0;
|
|
PHY_NAND_LINE
|
|
page0_addr = plane0_page_addr;
|
|
page1_addr = plane1_page_addr;
|
|
page_addr = ops_para->page_addr;
|
|
pages_per_blk_shift =
|
|
(controller->block_shift - controller->page_shift);
|
|
|
|
if ((controller->oob_mod) && (oob_buf)
|
|
&& (!ops_para->data_buf))
|
|
new_oob = 1;
|
|
PHY_NAND_LINE
|
|
/* for ecc mode */
|
|
if ((ops_para->option & DEV_ECC_SOFT_MODE)
|
|
|| (controller->bch_mode == NAND_ECC_NONE)) {
|
|
bch_mode = NAND_ECC_NONE;
|
|
page_size = flash->pagesize + flash->oobsize;
|
|
user_byte_num = flash->oobsize;
|
|
} else {
|
|
bch_mode = controller->bch_mode;
|
|
user_byte_num = controller->ecc_steps * controller->user_mode;
|
|
page_size = controller->ecc_steps * controller->ecc_unit;
|
|
}
|
|
|
|
if (new_oob)
|
|
page_size = controller->ecc_unit;
|
|
|
|
if (ops_para->option & DEV_SLC_MODE) {
|
|
/* aml_nand_dbg("enable SLC mode"); */
|
|
if (flash->new_type == SANDISK_19NM)
|
|
slc_mode = 1;
|
|
else if ((flash->new_type > 0) && (flash->new_type < 10))
|
|
slc_mode = 2;
|
|
else
|
|
slc_mode = 0;
|
|
} else {
|
|
slc_mode = 0;
|
|
if (flash->new_type == SANDISK_19NM) {
|
|
tmp_page = page_addr % (1 << pages_per_blk_shift);
|
|
if (((tmp_page % 2 == 0)
|
|
&& (tmp_page != 0))
|
|
|| (tmp_page == ((1 << pages_per_blk_shift)-1)))
|
|
up_page = 1;
|
|
else
|
|
up_page = 0;
|
|
}
|
|
}
|
|
/* setting global retry_cnt to 0 */
|
|
aml_chip->g_retry_cnt = 0;
|
|
PHY_NAND_LINE
|
|
ret = controller->quene_rb(controller, chipnr);
|
|
if (ret) {
|
|
aml_nand_msg("quene rb busy here");
|
|
goto error_exit0;
|
|
}
|
|
PHY_NAND_LINE
|
|
RETRY_PLANE_CMD:
|
|
if ((controller->mfr_type == NAND_MFR_MICRON)
|
|
|| (controller->mfr_type == NAND_MFR_INTEL)) {
|
|
/* plane0 */
|
|
controller->cmd_ctrl(controller, 0x00, NAND_CTRL_CLE);
|
|
controller->cmd_ctrl(controller, 0x00, NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, 0x00, NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, page0_addr, NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, page0_addr>>8, NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, page0_addr>>16, NAND_CTRL_ALE);
|
|
PHY_NAND_LINE
|
|
controller->cmd_ctrl(controller, 0x32, NAND_CTRL_CLE);
|
|
NFC_SEND_CMD_IDLE(controller, NAND_TWB_TIME_CYCLE);
|
|
ret = controller->quene_rb(controller, chipnr);
|
|
if (ret)
|
|
aml_nand_msg("send 0x32 cmd Rb failed\n");
|
|
PHY_NAND_LINE
|
|
/* plane1 */
|
|
controller->cmd_ctrl(controller, 0x00, NAND_CTRL_CLE);
|
|
controller->cmd_ctrl(controller, 0x00, NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, 0x00, NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, page1_addr, NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, page1_addr>>8, NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, page1_addr>>16, NAND_CTRL_ALE);
|
|
/* read start */
|
|
controller->cmd_ctrl(controller, 0x30, NAND_CTRL_CLE);
|
|
|
|
} else {
|
|
PHY_NAND_LINE
|
|
/* plane0 */
|
|
controller->cmd_ctrl(controller, 0x60, NAND_CTRL_CLE);
|
|
controller->cmd_ctrl(controller, page0_addr, NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, page0_addr>>8, NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, page0_addr>>16, NAND_CTRL_ALE);
|
|
/* plane1 */
|
|
controller->cmd_ctrl(controller, 0x60, NAND_CTRL_CLE);
|
|
controller->cmd_ctrl(controller, page1_addr, NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, page1_addr>>8, NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, page1_addr>>16, NAND_CTRL_ALE);
|
|
/* read start */
|
|
controller->cmd_ctrl(controller, 0x30, NAND_CTRL_CLE);
|
|
|
|
/* NFC_SEND_CMD_IDLE(controller, NAND_TWB_TIME_CYCLE); */
|
|
}
|
|
PHY_NAND_LINE
|
|
/* wait twb here */
|
|
NFC_SEND_CMD_IDLE(controller, NAND_TWB_TIME_CYCLE);
|
|
if (check_cmdfifo_size(controller)) {
|
|
aml_nand_msg("check cmdfifo size timeout");
|
|
BUG();
|
|
}
|
|
PHY_NAND_LINE
|
|
ret = controller->quene_rb(controller, chipnr);
|
|
if (ret) {
|
|
aml_nand_msg("quene rb busy here");
|
|
goto error_exit0;
|
|
}
|
|
PHY_NAND_LINE
|
|
if (controller->option & NAND_CTRL_NONE_RB) {
|
|
controller->cmd_ctrl(controller, NAND_CMD_READ0, NAND_CTRL_CLE);
|
|
NFC_SEND_CMD_IDLE(controller, NAND_TWB_TIME_CYCLE);
|
|
}
|
|
PHY_NAND_LINE
|
|
if ((controller->mfr_type == NAND_MFR_MICRON)
|
|
|| (controller->mfr_type == NAND_MFR_INTEL)) {
|
|
PHY_NAND_LINE
|
|
if (plane0_retry_flag == 0) {
|
|
PHY_NAND_LINE
|
|
controller->page_addr = page0_addr;
|
|
ret = controller->dma_read(controller,
|
|
page_size,
|
|
bch_mode);
|
|
if (ret) {
|
|
aml_nand_msg("quene dma busy here");
|
|
BUG();
|
|
goto error_exit0;
|
|
}
|
|
PHY_NAND_LINE
|
|
all_ff_flag = 0;
|
|
/* for ecc soft mode */
|
|
if (bch_mode != NAND_ECC_NONE) {
|
|
PHY_NAND_LINE
|
|
ret = ecc_read_retry_handle(aml_chip,
|
|
chipnr,
|
|
tmp_buf,
|
|
user_byte_num,
|
|
page_size,
|
|
slc_mode,
|
|
up_page);
|
|
if (ret == RETURN_PAGE_ALL_0XFF) {
|
|
PHY_NAND_LINE
|
|
all_ff_flag = 1;
|
|
} else if (ret == RETURN_PAGE_NEED_READRETRY) {
|
|
PHY_NAND_LINE
|
|
goto RETRY_PLANE_CMD;
|
|
} else if (ret < 0) {
|
|
aml_nand_msg("uncorrect ecc here!");
|
|
aml_nand_msg("at page:%d,pl0 chip:%d",
|
|
page_addr,
|
|
chipnr);
|
|
ops_para->ecc_err++;
|
|
|
|
}
|
|
plane0_retry_flag = 1;
|
|
}
|
|
if (ops_para->data_buf) {
|
|
if (all_ff_flag) {
|
|
memset(buf, 0xff, page_size);
|
|
} else {
|
|
memcpy(buf,
|
|
controller->data_buf,
|
|
page_size);
|
|
}
|
|
buf += page_size;
|
|
}
|
|
if (oob_buf) {
|
|
if (all_ff_flag)
|
|
memset(tmp_buf, 0xff, user_byte_num);
|
|
|
|
tmp_buf += user_byte_num;
|
|
}
|
|
all_ff_flag = 0;
|
|
}
|
|
if (new_oob)
|
|
goto new_oob_mod;
|
|
controller->cmd_ctrl(controller, 0x06, NAND_CTRL_CLE);
|
|
controller->cmd_ctrl(controller, 0x00, NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, 0x00, NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, page1_addr, NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, page1_addr>>8, NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, page1_addr>>16, NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, 0xe0, NAND_CTRL_CLE);
|
|
PHY_NAND_LINE
|
|
NFC_SEND_CMD_IDLE(controller, NAND_TWHR_TIME_CYCLE);
|
|
PHY_NAND_LINE
|
|
controller->page_addr = page1_addr;
|
|
ret = controller->dma_read(controller, page_size, bch_mode);
|
|
if (ret) {
|
|
aml_nand_msg("dma error here");
|
|
BUG();
|
|
goto error_exit0;
|
|
}
|
|
|
|
if (bch_mode != NAND_ECC_NONE) {
|
|
PHY_NAND_LINE
|
|
ret = ecc_read_retry_handle(aml_chip,
|
|
chipnr,
|
|
tmp_buf,
|
|
user_byte_num,
|
|
page_size,
|
|
slc_mode,
|
|
up_page);
|
|
if (ret == RETURN_PAGE_ALL_0XFF) {
|
|
all_ff_flag = 1;
|
|
} else if (ret == RETURN_PAGE_NEED_READRETRY) {
|
|
goto RETRY_PLANE_CMD;
|
|
} else if (ret < 0) {
|
|
aml_nand_msg("uncorrect ecc here");
|
|
aml_nand_msg("at page:%d, pl1 chip:%d",
|
|
page_addr,
|
|
chipnr);
|
|
ops_para->ecc_err++;
|
|
|
|
}
|
|
plane0_retry_flag = 0;
|
|
}
|
|
} else { /* not micron intel below */
|
|
if (plane0_retry_flag == 0) {
|
|
PHY_NAND_LINE
|
|
controller->cmd_ctrl(controller, 0x00, NAND_CTRL_CLE);
|
|
controller->cmd_ctrl(controller, 0x00, NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, 0x00, NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, page0_addr,
|
|
NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, page0_addr>>8,
|
|
NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, page0_addr>>16,
|
|
NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, 0x05, NAND_CTRL_CLE);
|
|
controller->cmd_ctrl(controller, 0x00, NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, 0x00, NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, 0xe0, NAND_CTRL_CLE);
|
|
|
|
NFC_SEND_CMD_IDLE(controller, NAND_TWHR_TIME_CYCLE);
|
|
PHY_NAND_LINE
|
|
controller->page_addr = page0_addr;
|
|
ret = controller->dma_read(controller,
|
|
page_size,
|
|
bch_mode);
|
|
PHY_NAND_LINE
|
|
if (ret) {
|
|
aml_nand_msg("dma error here");
|
|
BUG();
|
|
goto error_exit0;
|
|
}
|
|
|
|
if (bch_mode != NAND_ECC_NONE) {
|
|
ret = ecc_read_retry_handle(aml_chip,
|
|
chipnr,
|
|
tmp_buf,
|
|
user_byte_num,
|
|
page_size,
|
|
slc_mode,
|
|
up_page);
|
|
if (ret == RETURN_PAGE_ALL_0XFF) {
|
|
all_ff_flag = 1;
|
|
} else if (ret == RETURN_PAGE_NEED_READRETRY) {
|
|
goto RETRY_PLANE_CMD;
|
|
} else if (ret < 0) {
|
|
aml_nand_msg("NAND uncorrect ecc here");
|
|
aml_nand_msg("at page:%d, pl0 chip:%d",
|
|
page_addr,
|
|
chipnr);
|
|
ops_para->ecc_err++;
|
|
|
|
}
|
|
plane0_retry_flag = 1;
|
|
}
|
|
if (buf) {
|
|
if (all_ff_flag)
|
|
memset(buf, 0xff, page_size);
|
|
else
|
|
memcpy(buf,
|
|
controller->data_buf,
|
|
page_size);
|
|
buf += page_size;
|
|
}
|
|
if (oob_buf) {
|
|
if (all_ff_flag)
|
|
memset(tmp_buf, 0xff, user_byte_num);
|
|
|
|
tmp_buf += user_byte_num;
|
|
}
|
|
all_ff_flag = 0;
|
|
}
|
|
controller->cmd_ctrl(controller, 0x00, NAND_CTRL_CLE);
|
|
controller->cmd_ctrl(controller, 0x00, NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, 0x00, NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, page1_addr, NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, page1_addr>>8, NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, page1_addr>>16, NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, 0x05, NAND_CTRL_CLE);
|
|
controller->cmd_ctrl(controller, 0x00, NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, 0x00, NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, 0xe0, NAND_CTRL_CLE);
|
|
PHY_NAND_LINE
|
|
NFC_SEND_CMD_IDLE(controller, NAND_TWHR_TIME_CYCLE);
|
|
PHY_NAND_LINE
|
|
controller->page_addr = page1_addr;
|
|
ret = controller->dma_read(controller, page_size, bch_mode);
|
|
if (ret) {
|
|
aml_nand_msg("dma error here");
|
|
BUG();
|
|
goto error_exit0;
|
|
}
|
|
PHY_NAND_LINE
|
|
if (bch_mode != NAND_ECC_NONE) {
|
|
ret = ecc_read_retry_handle(aml_chip,
|
|
chipnr,
|
|
tmp_buf,
|
|
user_byte_num,
|
|
page_size,
|
|
slc_mode,
|
|
up_page);
|
|
if (ret == RETURN_PAGE_ALL_0XFF) {
|
|
all_ff_flag = 1;
|
|
} else if (ret == RETURN_PAGE_NEED_READRETRY) {
|
|
goto RETRY_PLANE_CMD;
|
|
} else if (ret < 0) {
|
|
aml_nand_msg("NAND uncorrect ecc here");
|
|
aml_nand_msg("at page:%d, pl1 chip:%d",
|
|
page_addr,
|
|
chipnr);
|
|
ops_para->ecc_err++;
|
|
|
|
}
|
|
plane0_retry_flag = 0;
|
|
}
|
|
}
|
|
if (ops_para->data_buf) {
|
|
if (all_ff_flag)
|
|
memset(buf, 0xff, page_size);
|
|
else
|
|
memcpy(buf, controller->data_buf, page_size);
|
|
//buf += page_size; /* fixme, why buffer plus here? */
|
|
}
|
|
|
|
if (oob_buf) {
|
|
if (all_ff_flag)
|
|
memset(tmp_buf, 0xff, user_byte_num);
|
|
//tmp_buf += user_byte_num;
|
|
}
|
|
all_ff_flag = 0;
|
|
|
|
if (check_cmdfifo_size(controller)) {
|
|
aml_nand_msg("check cmdfifo size timeout");
|
|
BUG();
|
|
}
|
|
PHY_NAND_LINE
|
|
new_oob_mod:
|
|
if (oob_buf) {
|
|
memcpy(oob_buf,
|
|
controller->oob_buf,
|
|
BYTES_OF_USER_PER_PAGE);
|
|
if (ops_para->ecc_err)
|
|
memset(oob_buf, 0x22, BYTES_OF_USER_PER_PAGE);
|
|
}
|
|
PHY_NAND_LINE
|
|
return NAND_SUCCESS;
|
|
error_exit0:
|
|
return ret;
|
|
}
|
|
/* AML_CFG_2PLANE_READ_EN */
|
|
#else
|
|
/* using 2 single plane read simulate 2plane */
|
|
extern void _dump_mem_u8(uint8_t * buf, uint32_t len);
|
|
static int read_page_two_plane(struct amlnand_chip *aml_chip,
|
|
u8 chipnr,
|
|
u32 p0_addr,
|
|
u32 p1_addr)
|
|
{
|
|
int ret = 0;
|
|
u32 page_size, user_byte_num, new_oob;
|
|
struct hw_controller *controller = &(aml_chip->controller);
|
|
struct nand_flash *flash = &(aml_chip->flash);
|
|
struct chip_ops_para *ops_para = &(aml_chip->ops_para);
|
|
|
|
/* pointer for restore */
|
|
u8* oob_buffer = controller->oob_buf;
|
|
u8* data_buffer = ops_para->data_buf;
|
|
|
|
new_oob = 0;
|
|
|
|
/* for ecc mode */
|
|
if ((ops_para->option & DEV_ECC_SOFT_MODE)
|
|
|| (controller->bch_mode == NAND_ECC_NONE)) {
|
|
page_size = flash->pagesize + flash->oobsize;
|
|
user_byte_num = flash->oobsize;
|
|
} else {
|
|
user_byte_num = controller->ecc_steps * controller->user_mode;
|
|
page_size = controller->ecc_steps * controller->ecc_unit;
|
|
}
|
|
|
|
if ((controller->oob_mod) && (ops_para->oob_buf)
|
|
&& (!ops_para->data_buf))
|
|
new_oob = 1;
|
|
//printf("p0, oob %p, data %p\n", controller->oob_buf, ops_para->data_buf);
|
|
|
|
ret = _read_page_single_plane(aml_chip, chipnr, p0_addr) ;
|
|
if (new_oob)
|
|
goto _out;
|
|
//_dump_mem_u8(ops_para->data_buf, 128);
|
|
/* move buffer ahead. */
|
|
//printf("p0-, oob %p, data %p\n", controller->oob_buf, ops_para->data_buf);
|
|
|
|
controller->oob_buf += user_byte_num; /* fixme, maybe not right!*/
|
|
if (ops_para->data_buf)
|
|
ops_para->data_buf += page_size;
|
|
//printf("p1, oob %p, data %p\n", controller->oob_buf, ops_para->data_buf);
|
|
|
|
ret |= _read_page_single_plane(aml_chip, chipnr, p1_addr) ;
|
|
//printf("p1-, oob %p, data %p\n", controller->oob_buf, ops_para->data_buf);
|
|
//_dump_mem_u8(ops_para->data_buf, 128);
|
|
|
|
/* restore buffer location */
|
|
controller->oob_buf = oob_buffer;
|
|
ops_para->data_buf = data_buffer;
|
|
//printf("p1=, oob %p, data %p\n", controller->oob_buf, ops_para->data_buf);
|
|
_out:
|
|
/* fill oob buffer */
|
|
if (ops_para->oob_buf) {
|
|
memcpy(ops_para->oob_buf,
|
|
controller->oob_buf,
|
|
BYTES_OF_USER_PER_PAGE);
|
|
if (ops_para->ecc_err)
|
|
memset(ops_para->oob_buf, 0x22, BYTES_OF_USER_PER_PAGE);
|
|
}
|
|
return ret;
|
|
}
|
|
/* AML_CFG_2PLANE_READ_EN */
|
|
#endif
|
|
|
|
|
|
static int _read_page_single_plane(struct amlnand_chip *aml_chip,
|
|
u8 chipnr, u8 *buf, u8 *oob_buf,
|
|
u32 page_addr)
|
|
{
|
|
struct hw_controller *controller = &(aml_chip->controller);
|
|
struct nand_flash *flash = &(aml_chip->flash);
|
|
struct chip_ops_para *ops_para = &(aml_chip->ops_para);
|
|
struct en_slc_info *slc_info = &(controller->slc_info);
|
|
u8 *tmp_buf = controller->oob_buf;
|
|
u32 pages_per_blk_shift;
|
|
u32 page_size, tmp_page;
|
|
u8 all_ff_flag = 0;
|
|
u8 bch_mode, user_byte_num, slc_mode, up_page = 0;
|
|
int ret = 0;
|
|
int new_oob = 0;
|
|
|
|
pages_per_blk_shift =
|
|
(controller->block_shift - controller->page_shift);
|
|
|
|
if (controller->oob_mod && (oob_buf)
|
|
&& (!buf))
|
|
new_oob = 1;
|
|
PHY_NAND_LINE
|
|
/* for ecc mode */
|
|
if ((ops_para->option & DEV_ECC_SOFT_MODE)
|
|
|| (controller->bch_mode == NAND_ECC_NONE)) {
|
|
bch_mode = NAND_ECC_NONE;
|
|
page_size = flash->pagesize + flash->oobsize;
|
|
user_byte_num = flash->oobsize;
|
|
} else {
|
|
bch_mode = controller->bch_mode;
|
|
user_byte_num = controller->ecc_steps * controller->user_mode;
|
|
page_size = controller->ecc_steps * controller->ecc_unit;
|
|
}
|
|
|
|
if (new_oob)
|
|
page_size = controller->ecc_unit;
|
|
|
|
if (ops_para->option & DEV_SLC_MODE) {
|
|
/* aml_nand_dbg("enable SLC mode"); */
|
|
if (flash->new_type == SANDISK_19NM)
|
|
slc_mode = 1;
|
|
else if ((flash->new_type > 0) && (flash->new_type < 10))
|
|
slc_mode = 2;
|
|
else
|
|
slc_mode = 0;
|
|
} else {
|
|
slc_mode = 0;
|
|
if (flash->new_type == SANDISK_19NM) {
|
|
tmp_page = page_addr % (1 << pages_per_blk_shift);
|
|
if (((tmp_page % 2 == 0)
|
|
&& (tmp_page != 0))
|
|
|| (tmp_page == ((1 << pages_per_blk_shift)-1)))
|
|
up_page = 1;
|
|
else
|
|
up_page = 0;
|
|
}
|
|
}
|
|
PHY_NAND_LINE
|
|
/* setting global retry_cnt to 0 */
|
|
aml_chip->g_retry_cnt = 0;
|
|
|
|
ret = controller->quene_rb(controller, chipnr);
|
|
if (ret) {
|
|
aml_nand_msg("quene rb busy here");
|
|
goto error_exit0;
|
|
}
|
|
|
|
if (slc_mode == 1) {
|
|
ret = slc_info->enter(controller);
|
|
if (ret < 0)
|
|
aml_nand_msg("slc enter failed here");
|
|
}
|
|
RETRY_CMD:
|
|
PHY_NAND_LINE
|
|
controller->cmd_ctrl(controller, NAND_CMD_READ0, NAND_CTRL_CLE);
|
|
controller->cmd_ctrl(controller, 0x0, NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, 0x0, NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, page_addr, NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, page_addr>>8, NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, page_addr>>16, NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, NAND_CMD_READSTART, NAND_CTRL_CLE);
|
|
/* NFC_SEND_CMD_IDLE(controller, NAND_TWB_TIME_CYCLE); */
|
|
|
|
/* wait twb here */
|
|
NFC_SEND_CMD_IDLE(controller, NAND_TWB_TIME_CYCLE);
|
|
if (check_cmdfifo_size(controller)) {
|
|
aml_nand_msg("check cmdfifo size timeout");
|
|
BUG();
|
|
}
|
|
PHY_NAND_LINE
|
|
ret = controller->quene_rb(controller, chipnr);
|
|
if (ret) {
|
|
aml_nand_msg("quene rb busy here");
|
|
goto error_exit0;
|
|
}
|
|
PHY_NAND_LINE
|
|
if (controller->option & NAND_CTRL_NONE_RB) {
|
|
controller->cmd_ctrl(controller, NAND_CMD_READ0, NAND_CTRL_CLE);
|
|
NFC_SEND_CMD_IDLE(controller, NAND_TWB_TIME_CYCLE);
|
|
}
|
|
PHY_NAND_LINE
|
|
/* aml_nand_dbg("page_addr:%d", ops_para->page_addr); */
|
|
/* transfer random seed. */
|
|
controller->page_addr = page_addr;
|
|
ret = controller->dma_read(controller, page_size, bch_mode);
|
|
if (ret) {
|
|
aml_nand_msg("dma error here");
|
|
BUG();
|
|
goto error_exit0;
|
|
}
|
|
PHY_NAND_LINE
|
|
if (bch_mode != NAND_ECC_NONE) {
|
|
PHY_NAND_LINE
|
|
ret = ecc_read_retry_handle(aml_chip,
|
|
chipnr,
|
|
tmp_buf,
|
|
user_byte_num,
|
|
page_size,
|
|
slc_mode,
|
|
up_page);
|
|
if (ret == RETURN_PAGE_ALL_0XFF) {
|
|
PHY_NAND_LINE
|
|
all_ff_flag = 1;
|
|
} else if (ret == RETURN_PAGE_NEED_READRETRY) {
|
|
PHY_NAND_LINE
|
|
goto RETRY_CMD;
|
|
} else if (ret < 0) {
|
|
PHY_NAND_LINE
|
|
aml_nand_msg("uncorrect ecc here at page:%d, chip:%d",
|
|
page_addr,
|
|
chipnr);
|
|
ops_para->ecc_err++;
|
|
|
|
}
|
|
}
|
|
if (buf) {
|
|
if (all_ff_flag)
|
|
memset(buf, 0xff, page_size);
|
|
else
|
|
memcpy(buf, controller->data_buf, page_size);
|
|
//buf += page_size;
|
|
}
|
|
PHY_NAND_LINE
|
|
if (oob_buf) {
|
|
if (all_ff_flag)
|
|
memset(tmp_buf, 0xff, user_byte_num);
|
|
//tmp_buf += user_byte_num;
|
|
}
|
|
all_ff_flag = 0;
|
|
|
|
if (check_cmdfifo_size(controller)) {
|
|
aml_nand_msg("check cmdfifo size timeout");
|
|
BUG();
|
|
}
|
|
#if 0 /*move this outsides */
|
|
if (oob_buf) {
|
|
memcpy(oob_buf,
|
|
controller->oob_buf,
|
|
BYTES_OF_USER_PER_PAGE);
|
|
if (ops_para->ecc_err)
|
|
memset(oob_buf, 0x22, BYTES_OF_USER_PER_PAGE);
|
|
}
|
|
#endif //
|
|
return NAND_SUCCESS;
|
|
error_exit0:
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int read_page_single_plane(struct amlnand_chip *aml_chip,
|
|
u8 chipnr, u8 *buf, u8 *oob_buf,
|
|
u32 page_addr)
|
|
{
|
|
int ret;
|
|
struct hw_controller *controller = &(aml_chip->controller);
|
|
struct chip_ops_para *ops_para = &(aml_chip->ops_para);
|
|
|
|
ret = _read_page_single_plane( aml_chip, chipnr, buf, oob_buf, page_addr);
|
|
|
|
if (oob_buf) {
|
|
memcpy(oob_buf,
|
|
controller->oob_buf,
|
|
BYTES_OF_USER_PER_PAGE);
|
|
if (ops_para->ecc_err)
|
|
memset(oob_buf, 0x22, BYTES_OF_USER_PER_PAGE);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/************************************************************
|
|
* read_page, all parameters saved in aml_chip->ops_para,
|
|
* refer to struct chip_ops_para define.
|
|
* support read way of hwecc/raw, data/oob only, data+oob
|
|
* for operation mode, contains multi-plane/multi-chip
|
|
*************************************************************/
|
|
static int read_page(struct amlnand_chip *aml_chip)
|
|
{
|
|
struct hw_controller *controller = &(aml_chip->controller);
|
|
struct chip_ops_para *ops_para = &(aml_chip->ops_para);
|
|
u32 plane0_page_addr, plane1_page_addr, page_addr;
|
|
u32 plane_page_addr, plane_blk_addr, pages_per_blk_shift;
|
|
u8 i, chip_num , plane_num;
|
|
int ret = 0;
|
|
#if (__DEBUG_L04__)
|
|
struct nand_flash *flash = &(aml_chip->flash);
|
|
u8 *buf = ops_para->data_buf;
|
|
u8 *oob_buf = ops_para->oob_buf;
|
|
#endif
|
|
|
|
PHY_NAND_LINE
|
|
if ((!ops_para->oob_buf) && (!ops_para->data_buf)) {
|
|
aml_nand_msg("buf & oob_buf should never be NULL");
|
|
ret = -NAND_ARGUMENT_FAILURE;
|
|
goto error_exit0;
|
|
}
|
|
|
|
/* for multi-chip mode */
|
|
if (ops_para->option & DEV_MULTI_CHIP_MODE)
|
|
chip_num = controller->chip_num;
|
|
else
|
|
chip_num = 1;
|
|
|
|
if (ops_para->option & DEV_USE_SHAREPAGE_MODE)
|
|
ops_para->page_addr <<= 1;
|
|
|
|
plane0_page_addr = plane1_page_addr = 0;
|
|
|
|
page_addr = ops_para->page_addr;
|
|
|
|
/* aml_nand_msg("ops_para->option:%d,ops_para->page_addr:%x",ops_para->option,ops_para->page_addr); */
|
|
controller->page_addr = ops_para->page_addr;
|
|
pages_per_blk_shift =
|
|
(controller->block_shift - controller->page_shift);
|
|
if (unlikely(page_addr >= controller->internal_page_nums)) {
|
|
controller->page_addr -= controller->internal_page_nums;
|
|
controller->page_addr |= controller->internal_page_nums *
|
|
aml_chip->flash.internal_chipnr;
|
|
page_addr = controller->page_addr;
|
|
}
|
|
PHY_NAND_LINE
|
|
/* for multi-plane mode */
|
|
if (ops_para->option & DEV_MULTI_PLANE_MODE) {
|
|
plane_num = 2;
|
|
plane_page_addr = page_addr & ((1 << pages_per_blk_shift)-1);
|
|
plane_blk_addr = (page_addr >> (pages_per_blk_shift));
|
|
plane_blk_addr <<= 1;
|
|
plane0_page_addr = (plane_blk_addr << pages_per_blk_shift) |
|
|
plane_page_addr;
|
|
plane_blk_addr += 1;
|
|
plane1_page_addr = (plane_blk_addr << pages_per_blk_shift) |
|
|
plane_page_addr;
|
|
} else
|
|
plane_num = 1;
|
|
|
|
PHY_NAND_LINE
|
|
/* aml_nand_msg("chip_num:%d,plane_num:%x",chip_num,plane_num); */
|
|
if (chip_num > 1) {
|
|
for (i = 0; i < chip_num; i++) {
|
|
if (plane_num == 2) {
|
|
ret = read_page_two_plane(aml_chip,
|
|
i, buf, oob_buf,
|
|
plane0_page_addr,
|
|
plane1_page_addr);
|
|
#if (__DEBUG_L04__)
|
|
if (ops_para->option & DEV_USE_SHAREPAGE_MODE) {
|
|
buf += flash->pagesize * 2;
|
|
oob_buf += BYTES_OF_USER_PER_PAGE;
|
|
ret = read_page_two_plane(aml_chip,
|
|
i, buf, oob_buf,
|
|
plane0_page_addr + 1,
|
|
plane1_page_addr + 1);
|
|
}
|
|
#endif
|
|
} else {
|
|
ret = read_page_single_plane(aml_chip,
|
|
i, buf, oob_buf,
|
|
page_addr);
|
|
if (ops_para->option & DEV_USE_SHAREPAGE_MODE) {
|
|
if (buf)
|
|
buf += flash->pagesize;
|
|
if (oob_buf)
|
|
oob_buf += BYTES_OF_USER_PER_PAGE;
|
|
ret = read_page_single_plane(aml_chip,
|
|
i, buf, NULL,
|
|
page_addr + 1);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
PHY_NAND_LINE
|
|
if (plane_num == 2) {
|
|
PHY_NAND_LINE
|
|
ret = read_page_two_plane(aml_chip,
|
|
ops_para->chipnr, buf, oob_buf,
|
|
plane0_page_addr,
|
|
plane1_page_addr);
|
|
#if (__DEBUG_L04__)
|
|
if (ops_para->option & DEV_USE_SHAREPAGE_MODE) {
|
|
buf += flash->pagesize * 2;
|
|
oob_buf += BYTES_OF_USER_PER_PAGE;
|
|
ret = read_page_two_plane(aml_chip,
|
|
ops_para->chipnr, buf, oob_buf,
|
|
plane0_page_addr + 1,
|
|
plane1_page_addr + 1);
|
|
}
|
|
#endif
|
|
} else {
|
|
PHY_NAND_LINE
|
|
ret = read_page_single_plane(aml_chip,
|
|
ops_para->chipnr, buf, oob_buf,
|
|
page_addr);
|
|
if (ops_para->option & DEV_USE_SHAREPAGE_MODE) {
|
|
if (buf)
|
|
buf += flash->pagesize;
|
|
if (oob_buf)
|
|
oob_buf += BYTES_OF_USER_PER_PAGE;
|
|
ret = read_page_single_plane(aml_chip,
|
|
ops_para->chipnr, buf, NULL,
|
|
page_addr + 1);
|
|
}
|
|
}
|
|
}
|
|
error_exit0:
|
|
return ret;
|
|
}
|
|
|
|
/************************************************************
|
|
* write_page, all parameters saved in aml_chip->ops_para.
|
|
* support read way of hwecc/raw, data/oob only, data+oob
|
|
* for operation mode, contains multi-plane/multi-chip
|
|
*
|
|
*************************************************************/
|
|
|
|
static int write_page(struct amlnand_chip *aml_chip)
|
|
{
|
|
struct hw_controller *controller = &(aml_chip->controller);
|
|
struct chip_ops_para *ops_para = &(aml_chip->ops_para);
|
|
u32 plane0_page_addr, plane1_page_addr, page_addr;
|
|
u32 plane_page_addr, plane_blk_addr, pages_per_blk_shift;
|
|
u8 i, chip_num , plane_num;
|
|
int ret = 0;
|
|
#if (__DEBUG_L04__)
|
|
struct nand_flash *flash = &(aml_chip->flash);
|
|
u8 *buf = ops_para->data_buf;
|
|
u8 *oob_buf = ops_para->oob_buf;
|
|
#endif
|
|
|
|
PHY_NAND_LINE
|
|
if (!ops_para->data_buf) {
|
|
aml_nand_msg("buf & oob_buf should never be NULL");
|
|
ret = -NAND_ARGUMENT_FAILURE;
|
|
goto error_exit0;
|
|
}
|
|
|
|
/* for multi-chip mode */
|
|
if (ops_para->option & DEV_MULTI_CHIP_MODE)
|
|
chip_num = controller->chip_num;
|
|
else
|
|
chip_num = 1;
|
|
|
|
plane0_page_addr = plane1_page_addr = 0;
|
|
|
|
if (ops_para->option & DEV_USE_SHAREPAGE_MODE)
|
|
ops_para->page_addr <<= 1;
|
|
|
|
page_addr = ops_para->page_addr;
|
|
controller->page_addr = ops_para->page_addr;
|
|
pages_per_blk_shift =
|
|
(controller->block_shift - controller->page_shift);
|
|
if (unlikely(page_addr >= controller->internal_page_nums)) {
|
|
controller->page_addr -= controller->internal_page_nums;
|
|
controller->page_addr |= controller->internal_page_nums *
|
|
aml_chip->flash.internal_chipnr;
|
|
page_addr = controller->page_addr;
|
|
}
|
|
PHY_NAND_LINE
|
|
/* for multi-plane mode */
|
|
if (ops_para->option & DEV_MULTI_PLANE_MODE) {
|
|
plane_num = 2;
|
|
plane_page_addr = page_addr & ((1 << pages_per_blk_shift)-1);
|
|
plane_blk_addr = (page_addr >> (pages_per_blk_shift));
|
|
plane_blk_addr <<= 1;
|
|
plane0_page_addr = (plane_blk_addr << pages_per_blk_shift) |
|
|
plane_page_addr;
|
|
plane_blk_addr += 1;
|
|
plane1_page_addr = (plane_blk_addr << pages_per_blk_shift) |
|
|
plane_page_addr;
|
|
} else
|
|
plane_num = 1;
|
|
|
|
PHY_NAND_LINE
|
|
if (chip_num > 1) {
|
|
for (i = 0; i < chip_num; i++) {
|
|
if (plane_num == 2) {
|
|
ret = write_page_two_plane(aml_chip,
|
|
i, buf, oob_buf,
|
|
plane0_page_addr,
|
|
plane1_page_addr);
|
|
#if (__DEBUG_L04__)
|
|
if (ops_para->option & DEV_USE_SHAREPAGE_MODE) {
|
|
if (buf)
|
|
buf += flash->pagesize*2;
|
|
if (oob_buf)
|
|
oob_buf += BYTES_OF_USER_PER_PAGE;
|
|
ret = write_page_two_plane(aml_chip,
|
|
i, buf, oob_buf,
|
|
plane0_page_addr + 1,
|
|
plane1_page_addr + 1);
|
|
}
|
|
#endif
|
|
|
|
} else {
|
|
ret = write_page_single_plane(aml_chip,
|
|
i, buf, oob_buf,
|
|
page_addr);
|
|
if (ops_para->option & DEV_USE_SHAREPAGE_MODE) {
|
|
if (buf)
|
|
buf += flash->pagesize;
|
|
if (oob_buf)
|
|
oob_buf += BYTES_OF_USER_PER_PAGE;
|
|
ret = write_page_single_plane(aml_chip,
|
|
i, buf, oob_buf,
|
|
page_addr + 1);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
PHY_NAND_LINE
|
|
if (plane_num == 2) {
|
|
PHY_NAND_LINE
|
|
ret = write_page_two_plane(aml_chip,
|
|
ops_para->chipnr, buf, oob_buf,
|
|
plane0_page_addr,
|
|
plane1_page_addr);
|
|
#if (__DEBUG_L04__)
|
|
if (ops_para->option & DEV_USE_SHAREPAGE_MODE) {
|
|
if (buf)
|
|
buf += flash->pagesize*2;
|
|
if (oob_buf)
|
|
oob_buf += BYTES_OF_USER_PER_PAGE;
|
|
ret = write_page_two_plane(aml_chip,
|
|
ops_para->chipnr, buf, oob_buf,
|
|
plane0_page_addr + 1,
|
|
plane1_page_addr + 1);
|
|
}
|
|
#endif
|
|
} else {
|
|
PHY_NAND_LINE
|
|
ret = write_page_single_plane(aml_chip,
|
|
ops_para->chipnr, buf, oob_buf,
|
|
page_addr);
|
|
if (ops_para->option & DEV_USE_SHAREPAGE_MODE) {
|
|
if (buf)
|
|
buf += flash->pagesize;
|
|
if (oob_buf)
|
|
oob_buf += BYTES_OF_USER_PER_PAGE;
|
|
ret = write_page_single_plane(aml_chip,
|
|
ops_para->chipnr, buf, oob_buf,
|
|
page_addr + 1);
|
|
}
|
|
}
|
|
}
|
|
error_exit0:
|
|
return ret;
|
|
}
|
|
|
|
static int write_page_two_plane(struct amlnand_chip *aml_chip,
|
|
int chipnr,
|
|
u8 *buf,
|
|
u8 *oob_buf,
|
|
u32 plane0_page_addr,
|
|
u32 plane1_page_addr)
|
|
{
|
|
struct hw_controller *controller = &(aml_chip->controller);
|
|
struct nand_flash *flash = &(aml_chip->flash);
|
|
struct chip_ops_para *ops_para = &(aml_chip->ops_para);
|
|
struct en_slc_info *slc_info = &(controller->slc_info);
|
|
|
|
struct chip_operation *operation = &(aml_chip->operation);
|
|
unsigned char retry_val[4] ={0, 0, 0, 0};
|
|
unsigned char addr = 0x89;
|
|
|
|
u32 column;
|
|
u32 page_size;
|
|
u8 bch_mode, user_byte_num = 0;
|
|
u8 slc_mode, status, st_cnt;
|
|
int ret = 0;
|
|
|
|
if ((mt_L04A_nand_check(aml_chip) == 0) ||
|
|
(mt_L05B_nand_check(aml_chip) == 0)) {
|
|
operation->get_onfi_para(aml_chip, retry_val, addr);
|
|
if (retry_val[0] != 0) {
|
|
aml_nand_msg("!!!read retry value:%d,when write",
|
|
retry_val[0]);
|
|
retry_val[0] = 0;
|
|
operation->set_onfi_para(aml_chip, retry_val, addr);
|
|
operation->get_onfi_para(aml_chip, retry_val, addr);
|
|
aml_nand_msg("!!!set read retry value:%d,when write",
|
|
retry_val[0]);
|
|
}
|
|
}
|
|
|
|
/* for ecc mode */
|
|
if ((ops_para->option & DEV_ECC_SOFT_MODE)
|
|
|| (controller->bch_mode == NAND_ECC_NONE)) {
|
|
bch_mode = NAND_ECC_NONE;
|
|
page_size = flash->pagesize + flash->oobsize;
|
|
} else {
|
|
bch_mode = controller->bch_mode;
|
|
user_byte_num = controller->ecc_steps * controller->user_mode;
|
|
page_size = controller->ecc_steps*controller->ecc_unit;
|
|
|
|
if (!oob_buf) { /* empty oob buffer, set defaut */
|
|
memset(controller->oob_buf,
|
|
0xa5,
|
|
user_byte_num);
|
|
controller->oob_buf[0] = 0xff;
|
|
oob_buf = controller->oob_buf;
|
|
} else {
|
|
memcpy(controller->oob_buf,
|
|
oob_buf,
|
|
user_byte_num);
|
|
//oob_buf = controller->oob_buf;
|
|
}
|
|
}
|
|
|
|
if (ops_para->option & DEV_SLC_MODE) {
|
|
/* aml_nand_dbg("enable SLC mode"); */
|
|
if (flash->new_type == SANDISK_19NM)
|
|
slc_mode = 1;
|
|
else if ((flash->new_type > 0) && (flash->new_type < 10))
|
|
slc_mode = 2;
|
|
else
|
|
slc_mode = 0;
|
|
} else
|
|
slc_mode = 0;
|
|
|
|
column = 0;
|
|
|
|
ret = controller->quene_rb(controller, chipnr);
|
|
if (ret) {
|
|
aml_nand_msg("quene rb busy here");
|
|
goto error_exit0;
|
|
}
|
|
|
|
/* plane0 */
|
|
controller->cmd_ctrl(controller, NAND_CMD_SEQIN,
|
|
NAND_CTRL_CLE);
|
|
controller->cmd_ctrl(controller, column,
|
|
NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, column>>8,
|
|
NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, plane0_page_addr,
|
|
NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, plane0_page_addr>>8,
|
|
NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, plane0_page_addr>>16,
|
|
NAND_CTRL_ALE);
|
|
|
|
if (bch_mode != NAND_ECC_NONE)
|
|
controller->set_usr_byte(controller,
|
|
oob_buf,
|
|
user_byte_num);
|
|
|
|
controller->page_addr = plane0_page_addr;
|
|
ret = controller->dma_write(controller,
|
|
buf,
|
|
page_size,
|
|
bch_mode);
|
|
if (ret) {
|
|
aml_nand_msg("dma error here");
|
|
goto error_exit0;
|
|
}
|
|
|
|
controller->cmd_ctrl(controller,
|
|
NAND_CMD_DUMMY_PROGRAM,
|
|
NAND_CTRL_CLE);
|
|
|
|
NFC_SEND_CMD_IDLE(controller, NAND_TADL_TIME_CYCLE);
|
|
|
|
ret = controller->quene_rb(controller, chipnr);
|
|
if (ret) {
|
|
aml_nand_msg("quene rb busy here");
|
|
goto error_exit0;
|
|
}
|
|
|
|
oob_buf += user_byte_num;
|
|
buf += page_size;
|
|
|
|
if ((controller->mfr_type == NAND_MFR_HYNIX)
|
|
|| (controller->mfr_type == NAND_MFR_SAMSUNG))
|
|
controller->cmd_ctrl(controller,
|
|
NAND_CMD_TWOPLANE_WRITE2,
|
|
NAND_CTRL_CLE);
|
|
else
|
|
controller->cmd_ctrl(controller,
|
|
NAND_CMD_TWOPLANE_WRITE2_MICRO,
|
|
NAND_CTRL_CLE);
|
|
|
|
controller->cmd_ctrl(controller, column, NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, column>>8,
|
|
NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, plane1_page_addr,
|
|
NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, plane1_page_addr>>8,
|
|
NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, plane1_page_addr>>16,
|
|
NAND_CTRL_ALE);
|
|
|
|
NFC_SEND_CMD_IDLE(controller, NAND_TADL_TIME_CYCLE);
|
|
|
|
if (bch_mode != NAND_ECC_NONE)
|
|
controller->set_usr_byte(controller,
|
|
oob_buf,
|
|
user_byte_num);
|
|
|
|
controller->page_addr = plane1_page_addr;
|
|
ret = controller->dma_write(controller,
|
|
buf,
|
|
page_size,
|
|
bch_mode);
|
|
if (ret) {
|
|
aml_nand_msg("dma error here");
|
|
goto error_exit0;
|
|
}
|
|
/* start */
|
|
controller->cmd_ctrl(controller, NAND_CMD_PAGEPROG,
|
|
NAND_CTRL_CLE);
|
|
NFC_SEND_CMD_IDLE(controller, NAND_TWB_TIME_CYCLE);
|
|
oob_buf += user_byte_num;
|
|
|
|
buf += page_size;
|
|
|
|
if (check_cmdfifo_size(controller)) {
|
|
aml_nand_msg("check cmdfifo size timeout");
|
|
BUG();
|
|
}
|
|
|
|
st_cnt = 0;
|
|
#ifdef AML_NAND_RB_IRQ
|
|
if (i == 0)
|
|
ret = controller->quene_rb_irq(controller, chipnr);
|
|
else
|
|
ret = controller->quene_rb(controller, chipnr);
|
|
#else
|
|
ret = controller->quene_rb(controller, chipnr);
|
|
#endif
|
|
if (ret) {
|
|
aml_nand_msg("quene rb busy here");
|
|
goto error_exit0;
|
|
}
|
|
|
|
controller->cmd_ctrl(controller, NAND_CMD_STATUS,
|
|
NAND_CTRL_CLE);
|
|
NFC_SEND_CMD_IDLE(controller, NAND_TWHR_TIME_CYCLE);
|
|
WSTATUS_TRY:
|
|
status = controller->readbyte(controller);
|
|
if (status == NAND_STATUS_FAILED) {
|
|
aml_nand_msg("write falied at page:%d, status:0x%x",
|
|
plane0_page_addr,
|
|
status);
|
|
ret = -NAND_WRITE_FAILED;
|
|
goto error_exit0;
|
|
} else if (status != NAND_STATUS_RIGHT) {
|
|
aml_nand_msg("status failed page:%d,status:0x%x try:%d",
|
|
plane0_page_addr,
|
|
status,
|
|
st_cnt);
|
|
if (st_cnt++ < NAND_STATUS_MAX_TRY) {
|
|
NFC_SEND_CMD_IDLE(controller,
|
|
NAND_TWB_TIME_CYCLE);
|
|
goto WSTATUS_TRY;
|
|
}
|
|
aml_nand_msg("status still failed");
|
|
}
|
|
|
|
/* for hynix nand, reset reg def value */
|
|
if (slc_mode == 2) {
|
|
ret = slc_info->exit(controller);
|
|
if (ret < 0)
|
|
aml_nand_msg("slc enter failed here");
|
|
}
|
|
|
|
return NAND_SUCCESS;
|
|
error_exit0:
|
|
return ret;
|
|
}
|
|
|
|
static int write_page_single_plane(struct amlnand_chip *aml_chip,
|
|
u8 chipnr, u8 *buf, u8 *oob_buf,
|
|
u32 page_addr)
|
|
{
|
|
struct hw_controller *controller = &(aml_chip->controller);
|
|
struct nand_flash *flash = &(aml_chip->flash);
|
|
struct chip_ops_para *ops_para = &(aml_chip->ops_para);
|
|
struct en_slc_info *slc_info = &(controller->slc_info);
|
|
u32 column;
|
|
u32 page_size;
|
|
u8 bch_mode, user_byte_num;
|
|
u8 slc_mode, status, st_cnt;
|
|
int ret = 0;
|
|
struct chip_operation *operation = &(aml_chip->operation);
|
|
unsigned char retry_val[4] ={0, 0, 0, 0};
|
|
unsigned char addr = 0x89;
|
|
|
|
if (!buf) {
|
|
aml_nand_msg("buf should never be NULL");
|
|
ret = -NAND_ARGUMENT_FAILURE;
|
|
goto error_exit0;
|
|
}
|
|
if (aml_chip->nand_status != NAND_STATUS_NORMAL) {
|
|
aml_nand_msg("nand status unusual: do not write anything!!!!!");
|
|
return NAND_SUCCESS;
|
|
}
|
|
if ((mt_L04A_nand_check(aml_chip) == 0) ||
|
|
(mt_L05B_nand_check(aml_chip) == 0)) {
|
|
operation->get_onfi_para(aml_chip, retry_val, addr);
|
|
if (retry_val[0] != 0) {
|
|
aml_nand_msg("!!!read retry value1:%d,when write",
|
|
retry_val[0]);
|
|
retry_val[0] = 0;
|
|
operation->set_onfi_para(aml_chip, retry_val, addr);
|
|
operation->get_onfi_para(aml_chip, retry_val, addr);
|
|
aml_nand_msg("!!!set read retry value1:%d,when write",
|
|
retry_val[0]);
|
|
}
|
|
}
|
|
|
|
user_byte_num = 0;
|
|
|
|
if (ops_para->option & DEV_SLC_MODE) {
|
|
/* aml_nand_dbg("enable SLC mode"); */
|
|
if (flash->new_type == SANDISK_19NM)
|
|
slc_mode = 1;
|
|
else if ((flash->new_type > 0) && (flash->new_type < 10))
|
|
slc_mode = 2;
|
|
else
|
|
slc_mode = 0;
|
|
} else
|
|
slc_mode = 0;
|
|
|
|
controller->page_addr = page_addr;
|
|
if (unlikely(controller->page_addr >= controller->internal_page_nums)) {
|
|
controller->page_addr -= controller->internal_page_nums;
|
|
controller->page_addr |= controller->internal_page_nums *
|
|
aml_chip->flash.internal_chipnr;
|
|
}
|
|
|
|
|
|
/* for ecc mode */
|
|
if ((ops_para->option & DEV_ECC_SOFT_MODE)
|
|
|| (controller->bch_mode == NAND_ECC_NONE)) {
|
|
bch_mode = NAND_ECC_NONE;
|
|
page_size = flash->pagesize + flash->oobsize;
|
|
} else {
|
|
bch_mode = controller->bch_mode;
|
|
user_byte_num = controller->ecc_steps*controller->user_mode;
|
|
page_size = controller->ecc_steps*controller->ecc_unit;
|
|
if (!oob_buf) { /* empty oob buffer, set defaut */
|
|
memset(controller->oob_buf,
|
|
0xa5,
|
|
user_byte_num);
|
|
controller->oob_buf[0] = 0xff;
|
|
oob_buf = controller->oob_buf;
|
|
} else {
|
|
memcpy(controller->oob_buf,
|
|
oob_buf,
|
|
user_byte_num);
|
|
//oob_buf = controller->oob_buf;
|
|
}
|
|
|
|
}
|
|
|
|
column = 0;
|
|
ret = controller->quene_rb(controller, chipnr);
|
|
if (ret) {
|
|
aml_nand_msg("quene rb busy here");
|
|
goto error_exit0;
|
|
}
|
|
|
|
if (slc_mode) {
|
|
ret = slc_info->enter(controller);
|
|
if (ret < 0)
|
|
aml_nand_msg("slc enter failed here");
|
|
}
|
|
|
|
ret = controller->quene_rb(controller, chipnr);
|
|
if (ret)
|
|
aml_nand_msg("quene rb busy here");
|
|
|
|
controller->cmd_ctrl(controller, NAND_CMD_SEQIN,
|
|
NAND_CTRL_CLE);
|
|
controller->cmd_ctrl(controller, column,
|
|
NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, column>>8,
|
|
NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, controller->page_addr,
|
|
NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller,
|
|
controller->page_addr>>8, NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller,
|
|
controller->page_addr>>16, NAND_CTRL_ALE);
|
|
|
|
NFC_SEND_CMD_IDLE(controller, NAND_TADL_TIME_CYCLE);
|
|
|
|
if (bch_mode != NAND_ECC_NONE)
|
|
controller->set_usr_byte(controller,
|
|
oob_buf,
|
|
user_byte_num);
|
|
|
|
ret = controller->dma_write(controller,
|
|
buf,
|
|
page_size,
|
|
bch_mode);
|
|
if (ret) {
|
|
aml_nand_msg("dma error here");
|
|
goto error_exit0;
|
|
}
|
|
|
|
/* start */
|
|
controller->cmd_ctrl(controller, NAND_CMD_PAGEPROG,
|
|
NAND_CTRL_CLE);
|
|
NFC_SEND_CMD_IDLE(controller, NAND_TWB_TIME_CYCLE);
|
|
oob_buf += user_byte_num;
|
|
buf += page_size;
|
|
|
|
if (check_cmdfifo_size(controller)) {
|
|
aml_nand_msg("check cmdfifo size timeout");
|
|
BUG();
|
|
}
|
|
|
|
st_cnt = 0;
|
|
#ifdef AML_NAND_RB_IRQ
|
|
if (i == 0)
|
|
ret = controller->quene_rb_irq(controller, chipnr);
|
|
else
|
|
ret = controller->quene_rb(controller, chipnr);
|
|
#else
|
|
ret = controller->quene_rb(controller, chipnr);
|
|
#endif
|
|
if (ret) {
|
|
aml_nand_msg("quene rb busy here");
|
|
goto error_exit0;
|
|
}
|
|
|
|
controller->cmd_ctrl(controller, NAND_CMD_STATUS,
|
|
NAND_CTRL_CLE);
|
|
NFC_SEND_CMD_IDLE(controller, NAND_TWHR_TIME_CYCLE);
|
|
WSTATUS_TRY:
|
|
status = controller->readbyte(controller);
|
|
if (status == NAND_STATUS_FAILED) {
|
|
aml_nand_msg("write falied at page:%d, status:0x%x",
|
|
page_addr,
|
|
status);
|
|
ret = -NAND_WRITE_FAILED;
|
|
goto error_exit0;
|
|
} else if (status != NAND_STATUS_RIGHT) {
|
|
aml_nand_msg("status failed page:%d,status:0x%x try:%d",
|
|
page_addr,
|
|
status,
|
|
st_cnt);
|
|
if (st_cnt++ < NAND_STATUS_MAX_TRY) {
|
|
NFC_SEND_CMD_IDLE(controller,
|
|
NAND_TWB_TIME_CYCLE);
|
|
goto WSTATUS_TRY;
|
|
}
|
|
aml_nand_msg("status still failed");
|
|
}
|
|
|
|
/* for hynix nand, reset reg def value */
|
|
if (slc_mode == 2) {
|
|
ret = slc_info->exit(controller);
|
|
if (ret < 0)
|
|
aml_nand_msg("slc enter failed here");
|
|
}
|
|
|
|
return NAND_SUCCESS;
|
|
error_exit0:
|
|
return ret;
|
|
}
|
|
|
|
static int set_block_status(struct amlnand_chip *aml_chip,
|
|
u8 chipnr,
|
|
u32 addr,
|
|
int value)
|
|
{
|
|
/* struct hw_controller *controller = &aml_chip->controller; */
|
|
/* struct nand_flash *flash = &aml_chip->flash; */
|
|
u32 blk_addr = addr;
|
|
u16 *tmp_status = NULL;
|
|
|
|
tmp_status = &aml_chip->block_status->blk_status[chipnr][0];
|
|
tmp_status[blk_addr] = value;
|
|
aml_nand_msg(" NAND bbt set Bad block at %d\n", blk_addr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int get_block_status(struct amlnand_chip *aml_chip,
|
|
u8 chipnr,
|
|
u32 addr)
|
|
{
|
|
/* struct hw_controller *controller = &aml_chip->controller; */
|
|
/* struct nand_flash *flash = &aml_chip->flash; */
|
|
u32 blk_addr = addr;
|
|
u16 *tmp_status = NULL;
|
|
|
|
tmp_status = &aml_chip->block_status->blk_status[chipnr][0];
|
|
#if 0
|
|
aml_nand_dbg("!!!!!show the block statusXXX ");
|
|
int chip = 0, start_block, total_blk = 50;
|
|
u16 *tmp_arr;
|
|
for (chip = 0; chip < controller->chip_num; chip++) {
|
|
tmp_arr = &aml_chip->block_status->blk_status[chip][0];
|
|
for (start_block = 0;
|
|
start_block < total_blk;
|
|
start_block++)
|
|
aml_nand_msg(" tmp_arr[%d][%d]=%d",
|
|
chip,
|
|
start_block,
|
|
tmp_arr[start_block]);
|
|
}
|
|
#endif
|
|
|
|
if (tmp_status[blk_addr] == NAND_BLOCK_USED_BAD) {
|
|
aml_nand_dbg(" NAND bbt detect Bad block at chip %d blk %d ",
|
|
chipnr, blk_addr);
|
|
return NAND_BLOCK_USED_BAD;
|
|
} else if (tmp_status[blk_addr] == NAND_BLOCK_FACTORY_BAD) {
|
|
/*
|
|
aml_nand_dbg(" NAND bbt detect factory
|
|
Bad block at chip %d blk %d",
|
|
chipnr, blk_addr);
|
|
*/
|
|
return NAND_BLOCK_FACTORY_BAD;
|
|
} else if (tmp_status[blk_addr] == NAND_BLOCK_GOOD)
|
|
return NAND_BLOCK_GOOD;
|
|
else{
|
|
aml_nand_msg("blk status is wrong at chip%d blk=%d tmp[%d]=%d",
|
|
chipnr, blk_addr,
|
|
blk_addr, tmp_status[blk_addr]);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/************************************************************
|
|
* block_isbad, all parameters saved in aml_chip->ops_para.
|
|
* for operation mode, contains multi-plane/multi-chip
|
|
* supposed chip bbt has been installed
|
|
*
|
|
*************************************************************/
|
|
static int block_isbad(struct amlnand_chip *aml_chip)
|
|
{
|
|
struct hw_controller *controller = &aml_chip->controller;
|
|
struct nand_flash *flash = &aml_chip->flash;
|
|
struct chip_ops_para *ops_para = &aml_chip->ops_para;
|
|
struct chip_operation *operation = &aml_chip->operation;
|
|
|
|
u32 blk_addr, page_per_blk_shift;
|
|
u8 chipnr;
|
|
//u8 chip_num = 1;
|
|
u8 oob_buf[8];
|
|
int ret = 0;
|
|
u32 buf_size = 0;
|
|
u8* page_buf;
|
|
#if 0
|
|
if (ops_para->option & DEV_MULTI_CHIP_MODE) {
|
|
chip_num = controller->chip_num;
|
|
/* aml_nand_dbg(" chip_num =%d ",chip_num); */
|
|
}
|
|
#endif //0
|
|
page_per_blk_shift = ffs(flash->blocksize) - ffs(flash->pagesize);
|
|
|
|
if (ops_para->option & DEV_USE_SHAREPAGE_MODE)
|
|
ops_para->page_addr <<= 1;
|
|
blk_addr = ops_para->page_addr >> page_per_blk_shift;
|
|
chipnr = ops_para->chipnr;
|
|
if (unlikely(controller->page_addr >= controller->internal_page_nums)) {
|
|
controller->page_addr -= controller->internal_page_nums;
|
|
controller->page_addr |= controller->internal_page_nums *
|
|
aml_chip->flash.internal_chipnr;
|
|
}
|
|
/* aml_nand_dbg("ops_para->page_addr = %d",ops_para->page_addr); */
|
|
/* aml_nand_dbg("chipnr= %d",chipnr); */
|
|
/* aml_nand_dbg("blk_addr= %d",blk_addr); */
|
|
|
|
if (aml_chip->block_status != NULL) {
|
|
if ((ops_para->option & DEV_MULTI_PLANE_MODE)
|
|
&& (!(ops_para->option & DEV_MULTI_CHIP_MODE))) {
|
|
blk_addr <<= 1;
|
|
/*
|
|
aml_nand_dbg(" DEV_MULTI_PLANE_MODE && !DEV_MULTI_CHIP_MODE");
|
|
*/
|
|
ret = get_block_status(aml_chip, chipnr, blk_addr);
|
|
if (ret == NAND_BLOCK_GOOD) {
|
|
/* plane 0 is good , check plane 1 */
|
|
if ((blk_addr % 2) == 0)
|
|
ret = get_block_status(aml_chip,
|
|
chipnr,
|
|
(blk_addr+1));
|
|
else/* plane 1 is good, check plane 0 */
|
|
ret = get_block_status(aml_chip,
|
|
chipnr,
|
|
(blk_addr - 1));
|
|
}
|
|
} else if ((!(ops_para->option & DEV_MULTI_PLANE_MODE))
|
|
&& ((ops_para->option & DEV_MULTI_CHIP_MODE))) {
|
|
/*
|
|
aml_nand_dbg(" !DEV_MULTI_PLANE_MODE && DEV_MULTI_CHIP_MODE");
|
|
*/
|
|
for (chipnr = 0;
|
|
chipnr < controller->chip_num;
|
|
chipnr++) {
|
|
ret = get_block_status(aml_chip, chipnr,
|
|
blk_addr);
|
|
if (ret != NAND_BLOCK_GOOD)
|
|
break;
|
|
}
|
|
} else if ((ops_para->option & DEV_MULTI_PLANE_MODE)
|
|
&& (ops_para->option & DEV_MULTI_CHIP_MODE)) {
|
|
/*
|
|
aml_nand_dbg("DEV_MULTI_PLANE_MODE &&
|
|
DEV_MULTI_CHIP_MODE");
|
|
*/
|
|
blk_addr <<= 1;
|
|
ret = get_block_status(aml_chip, chipnr, blk_addr);
|
|
if (ret == 0) {
|
|
for (chipnr = 0;
|
|
chipnr < controller->chip_num;
|
|
chipnr++) {
|
|
ret = get_block_status(aml_chip,
|
|
chipnr,
|
|
blk_addr);
|
|
if (ret != NAND_BLOCK_GOOD)
|
|
break;
|
|
if ((blk_addr % 2) == 0) {
|
|
/*
|
|
plane 0 is good , check plane 1
|
|
*/
|
|
ret = get_block_status(aml_chip,
|
|
chipnr,
|
|
(blk_addr+1));
|
|
if (ret != NAND_BLOCK_GOOD)
|
|
break;
|
|
} else {
|
|
/*
|
|
plane 1 is good, check plane 0
|
|
*/
|
|
ret = get_block_status(aml_chip,
|
|
chipnr,
|
|
(blk_addr - 1));
|
|
if (ret != NAND_BLOCK_GOOD)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else
|
|
ret = get_block_status(aml_chip, chipnr, blk_addr);
|
|
} else {
|
|
|
|
buf_size = (flash->pagesize + flash->oobsize) * controller->chip_num;
|
|
if (flash->option & NAND_MULTI_PLANE_MODE)
|
|
buf_size <<= 1;
|
|
|
|
if (flash->option & NAND_USE_SHAREPAGE_MODE)
|
|
buf_size <<= 1;
|
|
|
|
page_buf = aml_nand_malloc(buf_size);
|
|
if (!page_buf) {
|
|
aml_nand_msg("no memory for data buf, and need %x", buf_size);
|
|
printf( "%s: line:%d\n", __func__, __LINE__);
|
|
while (1) ;
|
|
ret = -NAND_MALLOC_FAILURE;
|
|
return 0;
|
|
}
|
|
/*ops_para->data_buf = controller->page_buf; */
|
|
ops_para->data_buf = page_buf;
|
|
|
|
ops_para->oob_buf = controller->oob_buf;
|
|
ops_para->ooblen = sizeof(oob_buf);
|
|
|
|
memset(ops_para->data_buf, 0x0, flash->pagesize);
|
|
memset(ops_para->oob_buf, 0x0, sizeof(oob_buf));
|
|
|
|
nand_get_chip(aml_chip);
|
|
|
|
ret = operation->read_page(aml_chip);
|
|
|
|
nand_release_chip(aml_chip);
|
|
aml_nand_free(page_buf);
|
|
page_buf = NULL;
|
|
|
|
if ((ret < 0) || (ops_para->ecc_err)) {
|
|
aml_nand_msg("nand read page failed at %d chip %d",
|
|
ops_para->page_addr, ops_para->chipnr);
|
|
ret = -NAND_READ_FAILED;
|
|
goto exit_error0;
|
|
}
|
|
|
|
if (ops_para->oob_buf[0] == 0) {
|
|
aml_nand_msg("nand detect bad blk at %d chip %d",
|
|
blk_addr, ops_para->chipnr);
|
|
ret = -NAND_BAD_BLOCK_FAILURE;
|
|
goto exit_error0;
|
|
}
|
|
}
|
|
return ret;
|
|
exit_error0:
|
|
return ret;
|
|
}
|
|
|
|
/************************************************************
|
|
* block_isbad, all parameters saved in aml_chip->ops_para.
|
|
* for operation mode, contains multi-plane/multi-chip
|
|
* supposed chip bbt has been installed
|
|
*
|
|
*************************************************************/
|
|
static int block_markbad(struct amlnand_chip *aml_chip)
|
|
{
|
|
struct hw_controller *controller = &aml_chip->controller;
|
|
struct nand_flash *flash = &aml_chip->flash;
|
|
struct chip_ops_para *ops_para = &aml_chip->ops_para;
|
|
struct chip_operation *operation = &aml_chip->operation;
|
|
|
|
u32 blk_addr, pages_per_blk_shift;
|
|
u8 chipnr;
|
|
//u8 chip_num = 1;
|
|
u16 *tmp_status;
|
|
int ret;
|
|
u32 buf_size = 0;
|
|
u8* page_buf;
|
|
#if 0
|
|
if (ops_para->option & DEV_MULTI_CHIP_MODE)
|
|
chip_num = controller->chip_num;
|
|
#endif
|
|
pages_per_blk_shift = ffs(flash->blocksize) - ffs(flash->pagesize);
|
|
if (ops_para->option & DEV_USE_SHAREPAGE_MODE)
|
|
ops_para->page_addr <<= 1;
|
|
blk_addr = ops_para->page_addr >> pages_per_blk_shift;
|
|
chipnr = ops_para->chipnr;
|
|
if (unlikely(controller->page_addr >= controller->internal_page_nums)) {
|
|
controller->page_addr -= controller->internal_page_nums;
|
|
controller->page_addr |= controller->internal_page_nums *
|
|
aml_chip->flash.internal_chipnr;
|
|
}
|
|
aml_nand_dbg("blk_addr =%d", blk_addr);
|
|
|
|
if ((aml_chip->nand_bbtinfo.arg_valid)
|
|
&& (aml_chip->block_status != NULL)) {
|
|
tmp_status = &aml_chip->block_status->blk_status[chipnr][0];
|
|
if ((tmp_status[blk_addr] == NAND_BLOCK_USED_BAD)
|
|
|| (tmp_status[blk_addr] == NAND_BLOCK_FACTORY_BAD))
|
|
return 0;
|
|
else if (tmp_status[blk_addr] == NAND_BLOCK_GOOD) {
|
|
if ((ops_para->option & DEV_MULTI_PLANE_MODE)
|
|
&& (!(ops_para->option & DEV_MULTI_CHIP_MODE))) {
|
|
blk_addr <<= 1;
|
|
if ((blk_addr % 2) == 0) {
|
|
/* plane 0 is good , set plane 1 */
|
|
ret = set_block_status(aml_chip,
|
|
chipnr, (blk_addr),
|
|
NAND_BLOCK_USED_BAD);
|
|
ret = set_block_status(aml_chip,
|
|
chipnr, (blk_addr+1),
|
|
NAND_BLOCK_USED_BAD);
|
|
aml_nand_dbg("set blk bad at chip%d blk=%d",
|
|
chipnr, blk_addr);
|
|
aml_nand_dbg("set blk bad at chip %d blk %d",
|
|
chipnr, (blk_addr+1));
|
|
} else {/* plane 1 is good, set plane 0 */
|
|
ret = set_block_status(aml_chip, chipnr,
|
|
(blk_addr),
|
|
NAND_BLOCK_USED_BAD);
|
|
ret = set_block_status(aml_chip, chipnr,
|
|
(blk_addr - 1),
|
|
NAND_BLOCK_USED_BAD);
|
|
aml_nand_dbg("set blk bad at chip %d blk %d",
|
|
chipnr, blk_addr);
|
|
aml_nand_dbg("set blk bad at chip %d blk %d",
|
|
chipnr, (blk_addr-1));
|
|
}
|
|
} else if ((!(ops_para->option & DEV_MULTI_PLANE_MODE))
|
|
&& ((ops_para->option & DEV_MULTI_CHIP_MODE))) {
|
|
for (chipnr = 0;
|
|
chipnr < controller->chip_num;
|
|
chipnr++) {
|
|
ret = set_block_status(aml_chip,
|
|
chipnr,
|
|
blk_addr,
|
|
NAND_BLOCK_USED_BAD);
|
|
aml_nand_dbg(" set blk bad at chip %d blk %d",
|
|
chipnr, blk_addr);
|
|
}
|
|
} else if ((ops_para->option & DEV_MULTI_PLANE_MODE)
|
|
&& (ops_para->option & DEV_MULTI_CHIP_MODE)) {
|
|
blk_addr <<= 1;
|
|
for (chipnr = 0;
|
|
chipnr < controller->chip_num;
|
|
chipnr++) {
|
|
if ((blk_addr % 2) == 0) {
|
|
/*
|
|
plane 0 is good , set plane 1
|
|
*/
|
|
ret = set_block_status(aml_chip,
|
|
chipnr,
|
|
(blk_addr+1),
|
|
NAND_BLOCK_USED_BAD);
|
|
aml_nand_dbg("set blk bad at chip %d blk %d",
|
|
chipnr,
|
|
(blk_addr+1));
|
|
} else {
|
|
/* plane 1 is good, set plane 0 */
|
|
ret = set_block_status(aml_chip,
|
|
chipnr,
|
|
(blk_addr - 1),
|
|
NAND_BLOCK_USED_BAD);
|
|
aml_nand_dbg("set blk bad,chip%d blk %d",
|
|
chipnr,
|
|
(blk_addr-1));
|
|
}
|
|
/* multi_chip , set every chip_blk */
|
|
ret = set_block_status(aml_chip,
|
|
chipnr,
|
|
blk_addr,
|
|
NAND_BLOCK_USED_BAD);
|
|
aml_nand_dbg("set blk bad at chip%d blk=%d",
|
|
chipnr, blk_addr);
|
|
}
|
|
} else {
|
|
ret = set_block_status(aml_chip,
|
|
chipnr,
|
|
blk_addr,
|
|
NAND_BLOCK_USED_BAD);
|
|
aml_nand_dbg(" set blk bad at chip %d blk %d",
|
|
chipnr,
|
|
blk_addr);
|
|
}
|
|
|
|
ret = amlnand_update_bbt(aml_chip);
|
|
if (ret < 0)
|
|
aml_nand_msg("nand update bbt failed");
|
|
|
|
}
|
|
}
|
|
#if 0
|
|
/* show the changed block status */
|
|
aml_nand_dbg("show the changed block status ");
|
|
for (chipnr = 0; chipnr < controller->chip_num; chipnr++) {
|
|
tmp_arr = &aml_chip->block_status->blk_status[chipnr][0];
|
|
for (start_block = 0; start_block < 100; start_block++)
|
|
aml_nand_msg(" tmp_arr[%d][%d]=%d",
|
|
chipnr,
|
|
start_block,
|
|
tmp_arr[start_block]);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
memset((u8 *)ops_para, 0x0, sizeof(struct chip_ops_para));
|
|
*/
|
|
/* ops_para->page_addr = blk_addr << pages_per_blk_shift; */
|
|
/* ops_para->chipnr = blk_addr % controller->chip_num; */
|
|
controller->select_chip(controller, ops_para->chipnr);
|
|
|
|
buf_size = (flash->pagesize + flash->oobsize) * controller->chip_num;
|
|
if (flash->option & NAND_MULTI_PLANE_MODE)
|
|
buf_size <<= 1;
|
|
|
|
if (flash->option & NAND_USE_SHAREPAGE_MODE)
|
|
buf_size <<= 1;
|
|
|
|
page_buf = aml_nand_malloc(buf_size);
|
|
if (!page_buf) {
|
|
aml_nand_msg("no memory for data buf, and need %x", buf_size);
|
|
printf( "%s: line:%d\n", __func__, __LINE__);
|
|
while (1) ;
|
|
ret = -NAND_MALLOC_FAILURE;
|
|
return 0;
|
|
}
|
|
//ops_para->data_buf = controller->page_buf;
|
|
ops_para->data_buf = page_buf;
|
|
ops_para->oob_buf = controller->oob_buf;
|
|
memset((u8 *)ops_para->data_buf, 0x0, flash->pagesize);
|
|
memset((u8 *)ops_para->oob_buf, 0x0, flash->oobsize);
|
|
|
|
if (aml_chip->state == CHIP_READY)
|
|
nand_get_chip(aml_chip);
|
|
|
|
ret = operation->write_page(aml_chip);
|
|
|
|
if (aml_chip->state == CHIP_READY)
|
|
nand_release_chip(aml_chip);
|
|
|
|
aml_nand_free(page_buf);
|
|
page_buf = NULL;
|
|
|
|
if (ret < 0) {
|
|
aml_nand_msg("%s() %d: nand write failed", __func__, __LINE__);
|
|
ret = -NAND_WRITE_FAILED;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/************************************************************
|
|
* erase_block, all parameters saved in aml_chip->ops_para.
|
|
* for operation mode, contains multi-plane/multi-chip
|
|
* supposed chip bbt has been installed
|
|
*
|
|
*************************************************************/
|
|
static int erase_block(struct amlnand_chip *aml_chip)
|
|
{
|
|
struct hw_controller *controller = &(aml_chip->controller);
|
|
struct nand_flash *flash = &(aml_chip->flash);
|
|
struct chip_ops_para *ops_para = &(aml_chip->ops_para);
|
|
struct en_slc_info *slc_info = &(controller->slc_info);
|
|
u32 plane_page_addr, plane_blk_addr, pages_per_blk_shift;
|
|
u32 plane0_page_addr, plane1_page_addr, chipnr;
|
|
u8 slc_mode, chip_num = 1, plane_num = 1, status, st_cnt;
|
|
int i, ret = 0;
|
|
|
|
/* aml_nand_dbg("page_addr:%d", ops_para->page_addr); */
|
|
if (aml_chip->nand_status != NAND_STATUS_NORMAL) {
|
|
aml_nand_msg("nand status unusual: do not erase anything!!!!!");
|
|
return NAND_SUCCESS;
|
|
}
|
|
|
|
if (ops_para->option & DEV_MULTI_CHIP_MODE)
|
|
chip_num = controller->chip_num;
|
|
|
|
if (ops_para->option & DEV_SLC_MODE) {
|
|
aml_nand_dbg("enable SLC mode");
|
|
if (flash->new_type == SANDISK_19NM)
|
|
slc_mode = 1;
|
|
else
|
|
slc_mode = 0;
|
|
} else
|
|
slc_mode = 0;
|
|
|
|
if (ops_para->option & DEV_USE_SHAREPAGE_MODE)
|
|
ops_para->page_addr <<= 1;
|
|
|
|
controller->page_addr = ops_para->page_addr;
|
|
|
|
pages_per_blk_shift = ffs(flash->blocksize) - ffs(flash->pagesize);
|
|
if (unlikely(controller->page_addr >= controller->internal_page_nums)) {
|
|
controller->page_addr -= controller->internal_page_nums;
|
|
controller->page_addr |= controller->internal_page_nums *
|
|
aml_chip->flash.internal_chipnr;
|
|
}
|
|
plane0_page_addr = plane1_page_addr = 0;
|
|
if (ops_para->option & DEV_MULTI_PLANE_MODE) {
|
|
plane_num = 2;
|
|
plane_page_addr = (controller->page_addr &
|
|
((1 << pages_per_blk_shift) - 1));
|
|
plane_blk_addr = (controller->page_addr >> pages_per_blk_shift);
|
|
plane_blk_addr <<= 1;
|
|
plane0_page_addr = (plane_blk_addr << pages_per_blk_shift) |
|
|
plane_page_addr;
|
|
plane1_page_addr =
|
|
((plane_blk_addr + 1) << pages_per_blk_shift) |
|
|
plane_page_addr;
|
|
}
|
|
|
|
for (i = 0; i < chip_num; i++) {
|
|
chipnr = (chip_num > 1) ? i : ops_para->chipnr;
|
|
ret = controller->quene_rb(controller, chipnr);
|
|
if (ret) {
|
|
aml_nand_msg("quene rb busy,chipnr:%d,page_addr:%d",
|
|
chipnr,
|
|
controller->page_addr);
|
|
goto error_exit0;
|
|
}
|
|
|
|
if (plane_num == 2) {
|
|
controller->cmd_ctrl(controller, NAND_CMD_ERASE1,
|
|
NAND_CTRL_CLE);
|
|
controller->cmd_ctrl(controller, plane0_page_addr,
|
|
NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, plane0_page_addr>>8,
|
|
NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, plane0_page_addr>>16,
|
|
NAND_CTRL_ALE);
|
|
if ((controller->mfr_type == NAND_MFR_MICRON)
|
|
|| (controller->mfr_type == NAND_MFR_MICRON)) {
|
|
controller->cmd_ctrl(controller,
|
|
NAND_CMD_ERASE1_END,
|
|
NAND_CTRL_CLE);
|
|
NFC_SEND_CMD_IDLE(controller,
|
|
NAND_TWB_TIME_CYCLE);
|
|
ret = controller->quene_rb(controller, chipnr);
|
|
if (ret) {
|
|
aml_nand_msg("rb busy,chip%d,paddr:%d",
|
|
chipnr,
|
|
controller->page_addr);
|
|
goto error_exit0;
|
|
}
|
|
}
|
|
|
|
controller->cmd_ctrl(controller, NAND_CMD_ERASE1,
|
|
NAND_CTRL_CLE);
|
|
controller->cmd_ctrl(controller, plane1_page_addr,
|
|
NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, plane1_page_addr>>8,
|
|
NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, plane1_page_addr>>16,
|
|
NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, NAND_CMD_ERASE2,
|
|
NAND_CTRL_CLE);
|
|
NFC_SEND_CMD_IDLE(controller, NAND_TWB_TIME_CYCLE);
|
|
} else {
|
|
if (slc_mode) {
|
|
ret = slc_info->enter(controller);
|
|
if (ret < 0)
|
|
aml_nand_msg("slc enter failed here");
|
|
}
|
|
controller->cmd_ctrl(controller, NAND_CMD_ERASE1,
|
|
NAND_CTRL_CLE);
|
|
controller->cmd_ctrl(controller, controller->page_addr,
|
|
NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller,
|
|
controller->page_addr>>8, NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller,
|
|
controller->page_addr>>16, NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller,
|
|
NAND_CMD_ERASE2, NAND_CTRL_CLE);
|
|
|
|
NFC_SEND_CMD_IDLE(controller, NAND_TWB_TIME_CYCLE);
|
|
}
|
|
|
|
if (check_cmdfifo_size(controller)) {
|
|
aml_nand_msg("check cmdfifo size timeout");
|
|
BUG();
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < chip_num; i++) {
|
|
chipnr = (chip_num > 1) ? i : ops_para->chipnr;
|
|
st_cnt = 0;
|
|
#ifdef AML_NAND_RB_IRQ
|
|
if (i == 0)
|
|
ret = controller->quene_rb_irq(controller, chipnr);
|
|
else
|
|
ret = controller->quene_rb(controller, chipnr);
|
|
#else
|
|
ret = controller->quene_rb(controller, chipnr);
|
|
#endif
|
|
if (ret) {
|
|
aml_nand_msg("quene rb busy,chipnr:%d,page_addr:%d",
|
|
chipnr,
|
|
controller->page_addr);
|
|
goto error_exit0;
|
|
}
|
|
|
|
controller->cmd_ctrl(controller,
|
|
NAND_CMD_STATUS, NAND_CTRL_CLE);
|
|
NFC_SEND_CMD_IDLE(controller, NAND_TWHR_TIME_CYCLE);
|
|
|
|
ESTATUS_TRY:
|
|
status = controller->readbyte(controller);
|
|
if (status == NAND_STATUS_FAILED) {
|
|
aml_nand_msg("erase failed, chip%d page:0x%x,status:0x%x",
|
|
chipnr,
|
|
controller->page_addr,
|
|
status);
|
|
ret = -NAND_WRITE_FAILED;
|
|
goto error_exit0;
|
|
} else if (status != NAND_STATUS_RIGHT) {
|
|
aml_nand_msg("erase status failed");
|
|
aml_nand_msg("chipnr:%d page:%d,status:0x%x and try:%d",
|
|
chipnr,
|
|
controller->page_addr,
|
|
status,
|
|
st_cnt);
|
|
if (st_cnt++ < NAND_STATUS_MAX_TRY) {
|
|
NFC_SEND_CMD_IDLE(controller,
|
|
NAND_TWB_TIME_CYCLE);
|
|
goto ESTATUS_TRY;
|
|
}
|
|
aml_nand_msg("erase status still failed ");
|
|
}
|
|
}
|
|
return NAND_SUCCESS;
|
|
error_exit0:
|
|
return ret;
|
|
}
|
|
/************************************************************
|
|
* test_block, all parameters saved in aml_chip->ops_para.
|
|
* for operation mode, contains multi-plane/multi-chip
|
|
* supposed chip bbt has been installed
|
|
* something wrong, don't work well!!! Do not use it.
|
|
*************************************************************/
|
|
static int test_block_chip_op(struct amlnand_chip *aml_chip)
|
|
{
|
|
struct chip_ops_para *ops_para = &aml_chip->ops_para;
|
|
struct nand_flash *flash = &aml_chip->flash;
|
|
|
|
u8 phys_erase_shift, phys_page_shift;
|
|
u32 pages_per_blk, pages_read, blk_addr = 0;
|
|
u8 oob_buf[8];
|
|
int ret = 0, t = 0;
|
|
u8 *dat_buf = NULL;
|
|
|
|
blk_addr = ops_para->page_addr;
|
|
dat_buf = aml_nand_malloc(flash->pagesize);
|
|
if (!dat_buf) {
|
|
aml_nand_msg("test_block: malloc failed");
|
|
ret = -1;
|
|
goto exit;
|
|
}
|
|
memset(dat_buf, 0xa5, flash->pagesize);
|
|
|
|
phys_erase_shift = ffs(flash->blocksize) - 1;
|
|
phys_page_shift = ffs(flash->pagesize) - 1;
|
|
pages_per_blk = (1 << (phys_erase_shift - phys_page_shift));
|
|
pages_read = pages_per_blk;
|
|
|
|
/* erase */
|
|
aml_nand_msg("erase addr = %d", ops_para->page_addr);
|
|
ret = erase_block(aml_chip);
|
|
if (ret < 0) {
|
|
aml_nand_msg("nand blk erase failed");
|
|
ret = -1;
|
|
goto exit;
|
|
}
|
|
aml_nand_msg("nand blk %d erase OK", blk_addr);
|
|
|
|
#if 1
|
|
/* read */
|
|
memset((u8 *)ops_para, 0x0, sizeof(struct chip_ops_para));
|
|
ops_para->page_addr = blk_addr;
|
|
for (t = 0; t < pages_read; t++) {
|
|
memset(aml_chip->user_page_buf, 0x0, flash->pagesize);
|
|
ops_para->page_addr += t;
|
|
ops_para->data_buf = aml_chip->user_page_buf;
|
|
ops_para->oob_buf = aml_chip->user_oob_buf;
|
|
ops_para->ooblen = sizeof(oob_buf);
|
|
|
|
ret = read_page(aml_chip);
|
|
if (ret < 0) {
|
|
aml_nand_msg("nand read %d failed", blk_addr);
|
|
ret = -1;
|
|
goto exit;
|
|
}
|
|
}
|
|
aml_nand_msg("nand blk read OK");
|
|
|
|
/* write */
|
|
memset((u8 *)ops_para, 0x0, sizeof(struct chip_ops_para));
|
|
ops_para->page_addr = blk_addr;
|
|
for (t = 0; t < pages_read; t++) {
|
|
memset(aml_chip->user_page_buf, 0xa5, flash->pagesize);
|
|
ops_para->page_addr += t;
|
|
ops_para->data_buf = aml_chip->user_page_buf;
|
|
ops_para->oob_buf = aml_chip->user_oob_buf;
|
|
ops_para->ooblen = sizeof(oob_buf);
|
|
|
|
ret = write_page(aml_chip);
|
|
if (ret < 0) {
|
|
aml_nand_msg("%s() %d: nand write failed", __func__, __LINE__);
|
|
ret = -1;
|
|
goto exit;
|
|
}
|
|
}
|
|
aml_nand_msg("nand blk %d write OK", blk_addr);
|
|
/* read */
|
|
memset((u8 *)ops_para, 0x0, sizeof(struct chip_ops_para));
|
|
ops_para->page_addr = blk_addr;
|
|
for (t = 0; t < pages_read; t++) {
|
|
memset(aml_chip->user_page_buf, 0x0, flash->pagesize);
|
|
ops_para->page_addr += t;
|
|
ops_para->data_buf = aml_chip->user_page_buf;
|
|
ops_para->oob_buf = aml_chip->user_oob_buf;
|
|
ops_para->ooblen = sizeof(oob_buf);
|
|
|
|
ret = read_page(aml_chip);
|
|
if (ret < 0) {
|
|
aml_nand_msg("nand read failed");
|
|
ret = -1;
|
|
goto exit;
|
|
}
|
|
}
|
|
aml_nand_msg("nand blk %d read OK", blk_addr);
|
|
/* erase */
|
|
ops_para->page_addr = blk_addr;
|
|
ret = erase_block(aml_chip);
|
|
if (ret < 0) {
|
|
aml_nand_msg("nand blk erase failed");
|
|
ret = -1;
|
|
goto exit;
|
|
}
|
|
aml_nand_msg("nand blk %d erase OK", blk_addr);
|
|
#endif
|
|
exit:
|
|
/* nand_release_chip(aml_chip); */
|
|
if (dat_buf) {
|
|
aml_nand_free(dat_buf);
|
|
dat_buf = NULL;
|
|
}
|
|
if (!ret)
|
|
aml_nand_msg("blk test OK");
|
|
|
|
return ret;
|
|
}
|
|
|
|
/************************************************************
|
|
* test_block, all parameters saved in aml_chip->ops_para.
|
|
* for operation mode, contains multi-plane/multi-chip
|
|
* supposed chip bbt has been installed
|
|
*
|
|
*************************************************************/
|
|
static int test_block_reserved(struct amlnand_chip *aml_chip, int tst_blk)
|
|
{
|
|
struct hw_controller *controller = &aml_chip->controller;
|
|
/* struct chip_operation *operation = & aml_chip->operation; */
|
|
struct chip_ops_para *ops_para = &aml_chip->ops_para;
|
|
struct nand_flash *flash = &aml_chip->flash;
|
|
struct en_slc_info *slc_info = &(controller->slc_info);
|
|
|
|
u8 phys_erase_shift, phys_page_shift, nand_boot;
|
|
u32 offset, pages_per_blk, pages_read;
|
|
u8 oob_buf[8];
|
|
u16 tmp_blk;
|
|
int ret = 0, t = 0;
|
|
u32 tmp_value;
|
|
u8 *dat_buf = NULL;
|
|
|
|
if (!flash->blocksize || !flash->pagesize)
|
|
return -1;
|
|
|
|
dat_buf = aml_nand_malloc(flash->pagesize);
|
|
if (!dat_buf) {
|
|
aml_nand_msg("test_block: malloc failed");
|
|
ret = -1;
|
|
goto exit;
|
|
}
|
|
memset(dat_buf, 0xa5, flash->pagesize);
|
|
|
|
nand_boot = 1;
|
|
|
|
if (nand_boot)
|
|
offset = (1024 * flash->pagesize);
|
|
|
|
phys_erase_shift = ffs(flash->blocksize) - 1;
|
|
phys_page_shift = ffs(flash->pagesize) - 1;
|
|
pages_per_blk = (1 << (phys_erase_shift - phys_page_shift));
|
|
|
|
tmp_blk = (offset >> phys_erase_shift);
|
|
|
|
if ((flash->new_type) && ((flash->new_type < 10)
|
|
|| (flash->new_type == SANDISK_19NM)))
|
|
ops_para->option |= DEV_SLC_MODE;
|
|
|
|
if (ops_para->option & DEV_SLC_MODE)
|
|
pages_read = pages_per_blk >> 1;
|
|
else
|
|
pages_read = pages_per_blk;
|
|
|
|
nand_get_chip(aml_chip);
|
|
|
|
/* erase */
|
|
tmp_value = tst_blk - tst_blk % controller->chip_num;
|
|
tmp_value /= controller->chip_num;
|
|
tmp_value += tmp_blk - tmp_blk/controller->chip_num;
|
|
ops_para->page_addr = tmp_value * pages_per_blk;
|
|
ops_para->chipnr = tst_blk % controller->chip_num;
|
|
controller->select_chip(controller, ops_para->chipnr);
|
|
ret = erase_block(aml_chip);
|
|
if (ret < 0) {
|
|
aml_nand_msg("nand blk %d erase failed", tst_blk);
|
|
ret = -1;
|
|
goto exit;
|
|
}
|
|
/* write */
|
|
for (t = 0; t < pages_read; t++) {
|
|
memset((u8 *)ops_para,
|
|
0x0,
|
|
sizeof(struct chip_ops_para));
|
|
if ((flash->new_type) && ((flash->new_type < 10)
|
|
|| (flash->new_type == SANDISK_19NM)))
|
|
ops_para->option |= DEV_SLC_MODE;
|
|
tmp_value = tst_blk - tst_blk % controller->chip_num;
|
|
tmp_value /= controller->chip_num;
|
|
tmp_value += tmp_blk - tmp_blk/controller->chip_num;
|
|
ops_para->page_addr = t + tmp_value * pages_per_blk;
|
|
ops_para->chipnr = tst_blk % controller->chip_num;
|
|
controller->select_chip(controller, ops_para->chipnr);
|
|
|
|
if ((ops_para->option & DEV_SLC_MODE)) {
|
|
tmp_value = ~(pages_per_blk - 1);
|
|
tmp_value &= ops_para->page_addr;
|
|
if ((flash->new_type > 0) && (flash->new_type < 10))
|
|
ops_para->page_addr = tmp_value |
|
|
(slc_info->pagelist[ops_para->page_addr % 256]);
|
|
if (flash->new_type == SANDISK_19NM)
|
|
ops_para->page_addr = tmp_value |
|
|
((ops_para->page_addr % pages_per_blk) << 1);
|
|
}
|
|
|
|
memset(aml_chip->user_page_buf, 0xa5, flash->pagesize);
|
|
ops_para->data_buf = aml_chip->user_page_buf;
|
|
ops_para->oob_buf = aml_chip->user_oob_buf;
|
|
ops_para->ooblen = sizeof(oob_buf);
|
|
|
|
ret = write_page(aml_chip);
|
|
if (ret < 0) {
|
|
aml_nand_msg("%s() %d: nand write failed", __func__, __LINE__);
|
|
ret = -1;
|
|
goto exit;
|
|
}
|
|
}
|
|
/* read */
|
|
for (t = 0; t < pages_read; t++) {
|
|
memset((u8 *)ops_para, 0x0,
|
|
sizeof(struct chip_ops_para));
|
|
if ((flash->new_type) && ((flash->new_type < 10)
|
|
|| (flash->new_type == SANDISK_19NM)))
|
|
ops_para->option |= DEV_SLC_MODE;
|
|
tmp_value = tst_blk - tst_blk % controller->chip_num;
|
|
tmp_value /= controller->chip_num;
|
|
tmp_value += tmp_blk - tmp_blk/controller->chip_num;
|
|
ops_para->page_addr = (t + tmp_value * pages_per_blk);
|
|
ops_para->chipnr = tst_blk % controller->chip_num;
|
|
controller->select_chip(controller, ops_para->chipnr);
|
|
|
|
if ((ops_para->option & DEV_SLC_MODE)) {
|
|
tmp_value = ~(pages_per_blk - 1);
|
|
tmp_value &= ops_para->page_addr;
|
|
if ((flash->new_type > 0) && (flash->new_type < 10))
|
|
ops_para->page_addr = tmp_value |
|
|
(slc_info->pagelist[ops_para->page_addr % 256]);
|
|
if (flash->new_type == SANDISK_19NM)
|
|
ops_para->page_addr = tmp_value |
|
|
((ops_para->page_addr % pages_per_blk) << 1);
|
|
}
|
|
|
|
memset(aml_chip->user_page_buf, 0x0, flash->pagesize);
|
|
ops_para->data_buf = aml_chip->user_page_buf;
|
|
ops_para->oob_buf = aml_chip->user_oob_buf;
|
|
ops_para->ooblen = sizeof(oob_buf);
|
|
|
|
ret = read_page(aml_chip);
|
|
if (ret < 0) {
|
|
aml_nand_msg("nand read failed");
|
|
ret = -1;
|
|
goto exit;
|
|
}
|
|
/*
|
|
aml_nand_dbg("tst_blk %d aml_chip->user_page_buf: ",tst_blk);
|
|
*/
|
|
/* show_data_buf(aml_chip->user_page_buf); */
|
|
/* aml_nand_dbg("tst_blk %d dat_buf: ",tst_blk); */
|
|
/* show_data_buf(dat_buf); */
|
|
if (memcmp(aml_chip->user_page_buf,
|
|
dat_buf,
|
|
flash->pagesize)) {
|
|
ret = -1;
|
|
aml_nand_msg("blk %d, page %d : test failed",
|
|
tst_blk,
|
|
t);
|
|
goto exit;
|
|
}
|
|
}
|
|
/* erase */
|
|
tmp_value = tst_blk - tst_blk % controller->chip_num;
|
|
tmp_value /= controller->chip_num;
|
|
tmp_value += tmp_blk - tmp_blk/controller->chip_num;
|
|
ops_para->page_addr = tmp_value * pages_per_blk;
|
|
ops_para->chipnr = tst_blk % controller->chip_num;
|
|
controller->select_chip(controller, ops_para->chipnr);
|
|
ret = erase_block(aml_chip);
|
|
if (ret < 0) {
|
|
aml_nand_msg("nand blk %d erase failed", tst_blk);
|
|
ret = -1;
|
|
goto exit;
|
|
}
|
|
|
|
exit:
|
|
nand_release_chip(aml_chip);
|
|
|
|
if (dat_buf) {
|
|
aml_nand_free(dat_buf);
|
|
dat_buf = NULL;
|
|
}
|
|
if (!ret)
|
|
aml_nand_msg("blk %d test OK", tst_blk);
|
|
|
|
return ret;
|
|
}
|
|
/************************************************************
|
|
* all parameters saved in aml_chip->ops_para.
|
|
* for operation mode, contains multi-plane/multi-chip
|
|
* supposed chip bbt has been installed
|
|
*
|
|
*************************************************************/
|
|
static int blk_modify_bbt_chip_op(struct amlnand_chip *aml_chip, int value)
|
|
{
|
|
struct hw_controller *controller = &aml_chip->controller;
|
|
struct nand_flash *flash = &aml_chip->flash;
|
|
struct chip_ops_para *ops_para = &aml_chip->ops_para;
|
|
/* struct chip_operation *operation = & aml_chip->operation; */
|
|
|
|
u32 blk_addr, page_per_blk_shift;
|
|
u8 chipnr;
|
|
/* u8 oob_buf[8]; */
|
|
int ret = 0;
|
|
#if 0
|
|
u8 chip_num = 1;
|
|
if (ops_para->option & DEV_MULTI_CHIP_MODE)
|
|
chip_num = controller->chip_num;
|
|
#endif
|
|
page_per_blk_shift = ffs(flash->blocksize) - ffs(flash->pagesize);
|
|
if (ops_para->option & DEV_USE_SHAREPAGE_MODE)
|
|
ops_para->page_addr <<= 1;
|
|
blk_addr = ops_para->page_addr >> page_per_blk_shift;
|
|
chipnr = ops_para->chipnr;
|
|
if (unlikely(controller->page_addr >= controller->internal_page_nums)) {
|
|
controller->page_addr -= controller->internal_page_nums;
|
|
controller->page_addr |= controller->internal_page_nums *
|
|
aml_chip->flash.internal_chipnr;
|
|
}
|
|
|
|
if ((ops_para->option & DEV_MULTI_PLANE_MODE)
|
|
&& (!(ops_para->option & DEV_MULTI_CHIP_MODE))) {
|
|
blk_addr <<= 1;
|
|
/*
|
|
aml_nand_dbg(" DEV_MULTI_PLANE_MODE && !DEV_MULTI_CHIP_MODE");
|
|
*/
|
|
ret = set_block_status(aml_chip, chipnr, blk_addr, value);
|
|
if (ret == 0) {
|
|
if ((blk_addr % 2) == 0)
|
|
/* plane 0 is good , check plane 1 */
|
|
ret = set_block_status(aml_chip,
|
|
chipnr,
|
|
(blk_addr+1),
|
|
value);
|
|
else /* plane 1 is good, check plane 0 */
|
|
ret = set_block_status(aml_chip,
|
|
chipnr,
|
|
(blk_addr - 1),
|
|
value);
|
|
}
|
|
} else if ((!(ops_para->option & DEV_MULTI_PLANE_MODE))
|
|
&& ((ops_para->option & value))) {
|
|
/*
|
|
aml_nand_dbg(" !DEV_MULTI_PLANE_MODE && DEV_MULTI_CHIP_MODE");
|
|
*/
|
|
for (chipnr = 0; chipnr < controller->chip_num; chipnr++) {
|
|
ret = set_block_status(aml_chip,
|
|
chipnr,
|
|
blk_addr,
|
|
value);
|
|
if (ret != 0)
|
|
break;
|
|
}
|
|
} else if ((ops_para->option & DEV_MULTI_PLANE_MODE)
|
|
&& (ops_para->option & DEV_MULTI_CHIP_MODE)) {
|
|
/*
|
|
aml_nand_dbg(" DEV_MULTI_PLANE_MODE && DEV_MULTI_CHIP_MODE");
|
|
*/
|
|
blk_addr <<= 1;
|
|
ret = set_block_status(aml_chip, chipnr, blk_addr, value);
|
|
if (ret == 0) {
|
|
for (chipnr = 0;
|
|
chipnr < controller->chip_num;
|
|
chipnr++) {
|
|
ret = set_block_status(aml_chip,
|
|
chipnr,
|
|
blk_addr,
|
|
value);
|
|
if (ret != 0)
|
|
break;
|
|
if ((blk_addr % 2) == 0) {
|
|
/* plane 0 is good , check plane 1 */
|
|
ret = set_block_status(aml_chip,
|
|
chipnr,
|
|
(blk_addr+1),
|
|
value);
|
|
if (ret != 0)
|
|
break;
|
|
} else {/* plane 1 is good, check plane 0 */
|
|
ret = set_block_status(aml_chip,
|
|
chipnr,
|
|
(blk_addr - 1),
|
|
value);
|
|
if (ret != 0)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else
|
|
ret = set_block_status(aml_chip, chipnr, blk_addr, value);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/************************************************************
|
|
* all parameters saved in aml_chip->ops_para.
|
|
* for operation mode, contains multi-plane/multi-chip
|
|
* supposed chip bbt has been installed
|
|
*
|
|
*************************************************************/
|
|
static int update_bbt_chip_op(struct amlnand_chip *aml_chip)
|
|
{
|
|
struct hw_controller *controller = &aml_chip->controller;
|
|
struct chip_ops_para *ops_para = &aml_chip->ops_para;
|
|
/* struct chip_operation *operation = & aml_chip->operation; */
|
|
|
|
/* u8 chip_num = 1; */
|
|
/* u16 * tmp_status; */
|
|
int ret;
|
|
#if 0
|
|
if (ops_para->option & DEV_MULTI_CHIP_MODE)
|
|
chip_num = controller->chip_num;
|
|
#endif
|
|
|
|
if (ops_para->option & DEV_USE_SHAREPAGE_MODE)
|
|
ops_para->page_addr <<= 1;
|
|
//blk_addr = ops_para->page_addr >> pages_per_blk_shift;
|
|
//chipnr = ops_para->chipnr;
|
|
if (unlikely(controller->page_addr >= controller->internal_page_nums)) {
|
|
controller->page_addr -=
|
|
controller->internal_page_nums;
|
|
controller->page_addr |= controller->internal_page_nums *
|
|
aml_chip->flash.internal_chipnr;
|
|
}
|
|
|
|
aml_nand_msg("###nand update start!!!!\n");
|
|
|
|
ret = amlnand_update_bbt(aml_chip);
|
|
if (ret < 0)
|
|
aml_nand_msg("nand update bbt failed");
|
|
|
|
return ret;
|
|
}
|
|
|
|
/************************************************************
|
|
* get_onfi_features, for onfi feature operation
|
|
*
|
|
*************************************************************/
|
|
static int get_onfi_features(struct amlnand_chip *aml_chip,
|
|
u8 *buf, int addr)
|
|
{
|
|
struct hw_controller *controller = &(aml_chip->controller);
|
|
int i, j;
|
|
int time_out_cnt = 0;
|
|
|
|
for (i = 0; i < controller->chip_num; i++) {
|
|
controller->select_chip(controller, i);
|
|
if (controller->quene_rb(controller, i) < 0) {
|
|
aml_nand_dbg("Get features quene rb failed here");
|
|
return -NAND_BUSY_FAILURE;
|
|
}
|
|
controller->cmd_ctrl(controller, NAND_CMD_GET_FEATURES,
|
|
NAND_CTRL_CLE);
|
|
controller->cmd_ctrl(controller, addr, NAND_CTRL_ALE);
|
|
NFC_SEND_CMD_IDLE(controller, NAND_TWB_TIME_CYCLE);
|
|
NFC_SEND_CMD_IDLE(controller, 0);
|
|
do {
|
|
if (NFC_CMDFIFO_SIZE(controller) <= 0)
|
|
break;
|
|
udelay(2);
|
|
} while (time_out_cnt++ <= AML_NAND_READ_BUSY_TIMEOUT);
|
|
udelay(1); //wait 1us
|
|
for (j = 0; j < 4; j++)
|
|
buf[j] = controller->readbyte(controller);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/************************************************************
|
|
* set_onfi_features, for onfi feature operation
|
|
*
|
|
*************************************************************/
|
|
static int set_onfi_features(struct amlnand_chip *aml_chip,
|
|
u8 *buf,
|
|
int addr)
|
|
{
|
|
struct hw_controller *controller = &(aml_chip->controller);
|
|
int i, j;
|
|
int time_out_cnt = 0;
|
|
|
|
for (i = 0; i < controller->chip_num; i++) {
|
|
controller->select_chip(controller, i);
|
|
if (controller->quene_rb(controller, i) < 0) {
|
|
aml_nand_dbg("Set features quene rb failed here");
|
|
return -NAND_BUSY_FAILURE;
|
|
}
|
|
controller->cmd_ctrl(controller, NAND_CMD_SET_FEATURES,
|
|
NAND_CTRL_CLE);
|
|
controller->cmd_ctrl(controller, addr, NAND_CTRL_ALE);
|
|
NFC_SEND_CMD_IDLE(controller, NAND_TADL_TIME_CYCLE);
|
|
NFC_SEND_CMD_IDLE(controller, 0);
|
|
|
|
for (j = 0; j < 4; j++)
|
|
controller->writebyte(controller, buf[j]);
|
|
|
|
/*
|
|
NFC_SEND_CMD_RB(controller, controller->chip_selected, 20);
|
|
*/
|
|
do {
|
|
if (NFC_CMDFIFO_SIZE(controller) <= 0)
|
|
break;
|
|
udelay(2);
|
|
} while (time_out_cnt++ <= AML_NAND_READ_BUSY_TIMEOUT);
|
|
|
|
if (time_out_cnt >= AML_NAND_READ_BUSY_TIMEOUT)
|
|
return -NAND_BUSY_FAILURE;
|
|
}
|
|
|
|
return NAND_SUCCESS;
|
|
}
|
|
|
|
/************************************************************
|
|
* nand_reset, send reset command, and assume nand selected here
|
|
*
|
|
*************************************************************/
|
|
int nand_hardreset(struct amlnand_chip *aml_chip, u8 chipnr)
|
|
{
|
|
struct hw_controller *controller = &(aml_chip->controller);
|
|
int status;
|
|
|
|
if (controller->quene_rb(controller, chipnr) < 0) {
|
|
aml_nand_msg("%s(): quene rb failed here", __func__);
|
|
return -NAND_BUSY_FAILURE;
|
|
}
|
|
|
|
//Delay
|
|
mdelay(5);
|
|
|
|
controller->cmd_ctrl(controller, 0x78, NAND_CTRL_CLE);
|
|
controller->cmd_ctrl(controller, chipnr>> 0, NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, 0, NAND_CTRL_ALE);
|
|
controller->cmd_ctrl(controller, 0, NAND_CTRL_ALE);
|
|
NFC_SEND_CMD_IDLE(controller, NAND_TWHR_TIME_CYCLE);
|
|
NFC_SEND_CMD_IDLE(controller, 0);
|
|
NFC_SEND_CMD_IDLE(controller, 0);
|
|
|
|
while (NFC_CMDFIFO_SIZE(controller));
|
|
|
|
status = (int)controller->readbyte(controller);
|
|
while ((status & 0x60) != 0x60) {
|
|
udelay(1);
|
|
status = (int)controller->readbyte(controller);
|
|
}
|
|
|
|
/* hardreset */
|
|
NFC_SEND_CMD_IDLE(controller, 0);
|
|
controller->cmd_ctrl(controller, 0xFD, NAND_CTRL_CLE);
|
|
NFC_SEND_CMD_IDLE(controller, NAND_TWB_TIME_CYCLE);
|
|
NFC_SEND_CMD_IDLE(controller, 0);
|
|
|
|
/* DataSheet says 3 ms of tPOR */
|
|
mdelay(3);
|
|
|
|
return NAND_SUCCESS;
|
|
}
|
|
int nand_reset(struct amlnand_chip *aml_chip, u8 chipnr)
|
|
{
|
|
struct hw_controller *controller = &(aml_chip->controller);
|
|
int status;
|
|
|
|
/* fixme, read back controller status. */
|
|
|
|
/* reset */
|
|
NFC_SEND_CMD_IDLE(controller, 0);
|
|
controller->cmd_ctrl(controller, NAND_CMD_RESET, NAND_CTRL_CLE);
|
|
NFC_SEND_CMD_IDLE(controller, NAND_TWB_TIME_CYCLE);
|
|
NFC_SEND_CMD_IDLE(controller, 0);
|
|
|
|
/* read status */
|
|
/* controller->cmd_ctrl(controller, NAND_CMD_STATUS, NAND_CTRL_CLE); */
|
|
/* NFC_SEND_CMD_IDLE(controller, NAND_TWHR_TIME_CYCLE); */
|
|
|
|
/* NFC_SEND_CMD_IDLE(controller, 0); */
|
|
|
|
if (controller->quene_rb(controller, chipnr) < 0) {
|
|
aml_nand_dbg("quene rb failed here");
|
|
return -NAND_BUSY_FAILURE;
|
|
}
|
|
|
|
controller->cmd_ctrl(controller, NAND_CMD_STATUS, NAND_CTRL_CLE);
|
|
NFC_SEND_CMD_IDLE(controller, NAND_TWHR_TIME_CYCLE);
|
|
NFC_SEND_CMD_IDLE(controller, 0);
|
|
NFC_SEND_CMD_IDLE(controller, 0);
|
|
|
|
while (NFC_CMDFIFO_SIZE(controller));
|
|
|
|
status = (int)controller->readbyte(controller);
|
|
if (status & NAND_STATUS_READY)
|
|
return NAND_SUCCESS;
|
|
|
|
aml_nand_dbg("check status failed and status:0x%x", status);
|
|
|
|
return -NAND_BUSY_FAILURE;
|
|
}
|
|
|
|
/************************************************************
|
|
* read_id
|
|
* @id_addr: for 00H and 20H;
|
|
* @buf: chip id stored into buf;
|
|
*************************************************************/
|
|
static int read_id(struct amlnand_chip *aml_chip, u8 chipnr,
|
|
u8 id_addr,
|
|
u8 *buf)
|
|
{
|
|
struct hw_controller *controller = &(aml_chip->controller);
|
|
int i, ret = 0;
|
|
|
|
if (buf == NULL) {
|
|
aml_nand_msg("buf must not be NULL here");
|
|
ret = -NAND_ARGUMENT_FAILURE;
|
|
goto error_exit0;
|
|
}
|
|
|
|
ret = controller->select_chip(controller, chipnr);
|
|
if (ret < 0) {
|
|
aml_nand_msg("select chip %d failed", chipnr);
|
|
goto error_exit0;
|
|
}
|
|
|
|
ret = nand_reset(aml_chip, chipnr);
|
|
if (ret < 0) {
|
|
if (chipnr == 0)
|
|
aml_nand_msg("reset failed");
|
|
else
|
|
aml_nand_dbg("reset failed");
|
|
goto error_exit0;
|
|
}
|
|
|
|
/* send id cmd */
|
|
controller->cmd_ctrl(controller, NAND_CMD_READID, NAND_CTRL_CLE);
|
|
controller->cmd_ctrl(controller, id_addr, NAND_CTRL_ALE);
|
|
NFC_SEND_CMD_IDLE(controller, NAND_TWHR_TIME_CYCLE);
|
|
NFC_SEND_CMD_IDLE(controller, 0);
|
|
|
|
/* Read manufacturer and device IDs */
|
|
for (i = 0; i < MAX_ID_LEN; i++)
|
|
buf[i] = controller->readbyte(controller);
|
|
error_exit0:
|
|
return ret;
|
|
}
|
|
|
|
static int nand_read_param(struct amlnand_chip *aml_chip, u8 chip_nr,
|
|
u8 param_addr, u8 *param)
|
|
{
|
|
struct hw_controller *controller = &(aml_chip->controller);
|
|
int ret = 0, i = 0, j = 0;
|
|
int status = 0;
|
|
int chip_num = 1;
|
|
|
|
memset(param, 0, 512);
|
|
|
|
if ( controller->chip_num )
|
|
chip_num = 1; // TBD: controller->chip_num;
|
|
|
|
/* Read From one chip now */
|
|
for (i=0; i < chip_num; i++) {
|
|
controller->select_chip(controller, i);
|
|
controller->cmd_ctrl(controller, NAND_CMD_PARAM, NAND_CTRL_CLE);
|
|
controller->cmd_ctrl(controller, param_addr, NAND_CTRL_ALE);
|
|
|
|
NFC_SEND_CMD_IDLE(controller, NAND_TWB_TIME_CYCLE);
|
|
NFC_SEND_CMD_IDLE(controller, 0);
|
|
ndelay(600);
|
|
|
|
if (controller->quene_rb(controller, i) < 0) {
|
|
aml_nand_msg("%s: quene rb failed here", __func__);
|
|
return -NAND_BUSY_FAILURE;
|
|
}
|
|
|
|
controller->cmd_ctrl(controller, NAND_CMD_STATUS, NAND_CTRL_CLE);
|
|
NFC_SEND_CMD_IDLE(controller, NAND_TWHR_TIME_CYCLE);
|
|
NFC_SEND_CMD_IDLE(controller, 0);
|
|
NFC_SEND_CMD_IDLE(controller, 0);
|
|
ndelay(600);
|
|
while (NFC_CMDFIFO_SIZE(controller) > 0);
|
|
status = (int)controller->readbyte(controller);
|
|
while (!(status & NAND_STATUS_READY)) {
|
|
status = (int)controller->readbyte(controller);
|
|
}
|
|
controller->cmd_ctrl(controller, 0x0, NAND_CTRL_CLE);
|
|
NFC_SEND_CMD_IDLE(controller, 0);
|
|
|
|
for (j=0; j<512; j++)
|
|
param[j] = controller->readbyte(controller);
|
|
}
|
|
aml_nand_dbg("param data0[32~43]: %x, %x, %x, %x,%x, %x",param[0],param[1],
|
|
param[2],param[3],param[4],param[5]);
|
|
aml_nand_dbg("param data0[32~43]: %x, %x, %x, %x,%x, %x",param[32],param[33],
|
|
param[34],param[35],param[36],param[37]);
|
|
/* TBD: NEED to add crc to validate JEDEC */
|
|
return ret;
|
|
}
|
|
|
|
int amlnand_init_operation(struct amlnand_chip *aml_chip)
|
|
{
|
|
struct chip_operation *operation = &(aml_chip->operation);
|
|
|
|
if (!operation->reset)
|
|
operation->reset = nand_reset;
|
|
if (!operation->read_id)
|
|
operation->read_id = read_id;
|
|
if (!operation->nand_read_param)
|
|
operation->nand_read_param = nand_read_param;
|
|
if (!operation->set_onfi_para)
|
|
operation->set_onfi_para = set_onfi_features;
|
|
if (!operation->get_onfi_para)
|
|
operation->get_onfi_para = get_onfi_features;
|
|
|
|
if (!operation->check_wp)
|
|
operation->check_wp = check_wp;
|
|
|
|
if (!operation->erase_block)
|
|
operation->erase_block = erase_block;
|
|
|
|
if (!operation->test_block_chip_op)
|
|
operation->test_block_chip_op = test_block_chip_op;
|
|
|
|
if (!operation->test_block_reserved)
|
|
operation->test_block_reserved = test_block_reserved;
|
|
|
|
if (!operation->block_isbad)
|
|
operation->block_isbad = block_isbad;
|
|
|
|
if (!operation->block_markbad)
|
|
operation->block_markbad = block_markbad;
|
|
|
|
if (!operation->read_page)
|
|
operation->read_page = read_page;
|
|
|
|
if (!operation->write_page)
|
|
operation->write_page = write_page;
|
|
|
|
if (!operation->blk_modify_bbt_chip_op)
|
|
operation->blk_modify_bbt_chip_op = blk_modify_bbt_chip_op;
|
|
|
|
if (!operation->update_bbt_chip_op)
|
|
operation->update_bbt_chip_op = update_bbt_chip_op;
|
|
|
|
return NAND_SUCCESS;
|
|
|
|
}
|
|
|