832 lines
22 KiB
C
832 lines
22 KiB
C
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
|
/*
|
|
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
|
|
*/
|
|
|
|
#include "../include/phynand.h"
|
|
|
|
int get_flash_type(struct amlnand_chip *aml_chip)
|
|
{
|
|
struct hw_controller *controller = &(aml_chip->controller);
|
|
struct chip_operation *operation = &(aml_chip->operation);
|
|
u8 dev_id[MAX_ID_LEN] = {0};
|
|
struct nand_flash *type = NULL;
|
|
int ret = 0, i, extid;
|
|
u8 flash_id0 = 0xff;
|
|
u8 onfi_param_data[512] = {0};
|
|
|
|
|
|
ret = operation->read_id(aml_chip,
|
|
0,
|
|
NAND_CMD_ID_ADDR_NORMAL,
|
|
&dev_id[0]);
|
|
if (ret < 0) {
|
|
aml_nand_dbg("read id failed and ret:0x%x", ret);
|
|
goto error_exit;
|
|
}
|
|
|
|
aml_nand_msg("NAND device id: %x %x %x %x %x %x %x %x",
|
|
dev_id[0],
|
|
dev_id[1],
|
|
dev_id[2],
|
|
dev_id[3],
|
|
dev_id[4],
|
|
dev_id[5],
|
|
dev_id[6],
|
|
dev_id[7]);
|
|
|
|
#ifdef AML_SLC_NAND_SUPPORT
|
|
/* Lookup the slc flash id */
|
|
for (i = 0; flash_ids_slc[i].name != NULL; i++) {
|
|
if (dev_id[1] == flash_ids_slc[i].id[1]) {
|
|
type = &flash_ids_slc[i];
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
if (type) {
|
|
aml_nand_msg("detect slc nand here");
|
|
controller->flash_type = NAND_TYPE_SLC;
|
|
} else {
|
|
#ifdef AML_MLC_NAND_SUPPORT
|
|
/* Lookup the mlc flash id */
|
|
for (i = 0; flash_ids_mlc[i].name != NULL; i++) {
|
|
if (!strncmp((char *)flash_ids_mlc[i].id,
|
|
(char *)dev_id,
|
|
strlen((const char *)flash_ids_mlc[i].id))) {
|
|
type = &flash_ids_mlc[i];
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
flash_id0 = dev_id[0];
|
|
if (!type) {
|
|
aml_nand_msg("no matched id");
|
|
ret = -NAND_ID_FAILURE;
|
|
goto error_exit;
|
|
}
|
|
controller->flash_type = NAND_TYPE_MLC;
|
|
}
|
|
|
|
memcpy(&(aml_chip->flash), type, sizeof(struct nand_flash));
|
|
controller->mfr_type = type->id[0];
|
|
|
|
aml_nand_dbg("check type->T_REA:%d, type->T_RHOH:%d, flash:%d %d",
|
|
type->T_REA,
|
|
type->T_RHOH,
|
|
aml_chip->flash.T_REA,
|
|
aml_chip->flash.T_RHOH);
|
|
|
|
#ifdef AML_SLC_NAND_SUPPORT
|
|
type = &aml_chip->flash;
|
|
|
|
/* Newer devices have all the information in additional id bytes */
|
|
if (!type->pagesize) {
|
|
/* The 3rd id byte holds MLC / multichip data */
|
|
controller->readbyte(controller);
|
|
/* The 4th id byte is the important one */
|
|
extid = controller->readbyte(controller);
|
|
/* Calc pagesize */
|
|
type->pagesize = 1024 << (extid & 0x3);
|
|
extid >>= 2;
|
|
/* Calc oobsize */
|
|
type->oobsize = (8 << (extid & 0x01)) * (type->pagesize>>9);
|
|
extid >>= 2;
|
|
/* Calc blocksize. Blocksize is multiples of 64KiB */
|
|
type->blocksize = (64 * 1024) << (extid & 0x03);
|
|
extid >>= 2;
|
|
/* Get buswidth information */
|
|
aml_nand_msg("detect nand buswidth:%s",
|
|
(extid & 0x02) ? "16bit" : "8bit");
|
|
if (extid & 0x02) {
|
|
aml_nand_msg("do not support 16bit buswidth yet");
|
|
ret = -NAND_ID_FAILURE;
|
|
goto error_exit;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef AML_MLC_NAND_SUPPORT
|
|
/* read onfi id */
|
|
ret = operation->read_id(aml_chip,
|
|
0,
|
|
NAND_CMD_ID_ADDR_ONFI,
|
|
&dev_id[0]);
|
|
if (ret < 0) {
|
|
aml_nand_msg("read id failed and ret:0x%x", ret);
|
|
goto error_exit;
|
|
}
|
|
|
|
controller->page_shift = ffs(aml_chip->flash.pagesize) - 1;
|
|
controller->block_shift = ffs(aml_chip->flash.blocksize) - 1;
|
|
controller->internal_page_nums =
|
|
((aml_chip->flash.chipsize<<(20 - controller->page_shift)) /
|
|
aml_chip->flash.internal_chipnr);
|
|
aml_nand_dbg("internal_page_nums =%d,internal_chipnr=%d",
|
|
controller->internal_page_nums,
|
|
aml_chip->flash.internal_chipnr);
|
|
if (!memcmp((char *)dev_id, "ONFI", 4)) {
|
|
/*judge micron/specteck nand by liuxianjun*/
|
|
if (operation->nand_read_param(aml_chip, 0, 0, &onfi_param_data[0]) == 0) { /*zero is valid*/
|
|
if (flash_id0 == 0x2c) {
|
|
aml_nand_msg("param data[32~43]: %x, %x, %x, %x,%x, %x",onfi_param_data[32],onfi_param_data[33],
|
|
onfi_param_data[34],onfi_param_data[35],onfi_param_data[36],onfi_param_data[37]);
|
|
aml_nand_dbg("read retry option[181~182]: %x, %x",onfi_param_data[181],onfi_param_data[182]);
|
|
if ((onfi_param_data[32] == 0x4D) && (onfi_param_data[33] == 0x49) && (onfi_param_data[34] == 0x43)
|
|
&& (onfi_param_data[35] == 0x52) && (onfi_param_data[36] == 0x4F) && (onfi_param_data[37] == 0x4E)) {
|
|
aml_nand_msg("nand Manufacturer: micron");
|
|
} else if ((onfi_param_data[32] == 0x53) && (onfi_param_data[33] == 0x50) && (onfi_param_data[34] == 0x45)
|
|
&& (onfi_param_data[35] == 0x43) && (onfi_param_data[36] == 0x54) && (onfi_param_data[37] == 0x45)) {
|
|
aml_nand_msg("nand Manufacturer: specteck");
|
|
} else {
|
|
aml_nand_msg("unknown Manufacturer ");
|
|
}
|
|
aml_nand_dbg("block num: 0x%x,0x%x,0x%x,0x%x",onfi_param_data[99],onfi_param_data[98],onfi_param_data[97],onfi_param_data[96]);
|
|
}
|
|
} else {
|
|
aml_nand_msg("read nand param data fail\n");
|
|
}
|
|
controller->onfi_mode = type->onfi_mode;
|
|
}
|
|
#endif
|
|
error_exit:
|
|
return ret;
|
|
}
|
|
|
|
int mt_L95B_nand_check(struct amlnand_chip *aml_chip)
|
|
{
|
|
struct nand_flash *flash = &(aml_chip->flash);
|
|
|
|
if ((flash->id[0] == 0x2C) &&
|
|
((flash->id[1] == 0x84) && (flash->id[2] == 0x64) &&
|
|
(flash->id[3] == 0x54) && (flash->id[4] == 0xA9)) ) {
|
|
return 0;
|
|
} else return -1;
|
|
}
|
|
|
|
int mt_L85C_nand_check(struct amlnand_chip *aml_chip)
|
|
{
|
|
struct nand_flash *flash = &(aml_chip->flash);
|
|
|
|
if ((flash->id[0] == 0x2C) &&
|
|
(flash->id[1] == 0x84) && (flash->id[2] == 0x64) &&
|
|
(flash->id[3] == 0x3c) && (flash->id[4] == 0xA9)
|
|
&& (flash->id[5] == 0x04)) {
|
|
return 0;
|
|
} else return -1;
|
|
}
|
|
|
|
int mt_L04A_nand_check(struct amlnand_chip *aml_chip)
|
|
{
|
|
struct nand_flash *flash = &(aml_chip->flash);
|
|
|
|
if ((flash->id[0] == 0x2C) &&
|
|
((flash->id[1] == 0x64) && (flash->id[2] == 0x44) &&
|
|
(flash->id[3] == 0x32) && (flash->id[4] == 0xA5)) ) {
|
|
return 0;
|
|
} else return -1;
|
|
}
|
|
|
|
int mt_L05B_nand_check(struct amlnand_chip *aml_chip)
|
|
{
|
|
struct nand_flash *flash = &(aml_chip->flash);
|
|
|
|
if ((flash->id[0] == 0x2C) &&
|
|
((flash->id[1] == 0x84) && (flash->id[2] == 0x44) &&
|
|
(flash->id[3] == 0x32) && (flash->id[4] == 0xAA)) ) {
|
|
return 0;
|
|
} else return -1;
|
|
}
|
|
|
|
static void micron_check_rand_mode (struct amlnand_chip *aml_chip)
|
|
{
|
|
struct hw_controller *controller = &(aml_chip->controller);
|
|
struct chip_operation *operation = &(aml_chip->operation);
|
|
//struct nand_flash *flash = &(aml_chip->flash);
|
|
struct en_slc_info *slc_info = &(controller->slc_info);
|
|
unsigned char rand_val[4] ={0, 0, 0, 0};
|
|
const int rand_addr = 0x92;
|
|
|
|
aml_nand_msg("%s()", __func__);
|
|
slc_info->micron_l0l3_mode = 0;
|
|
|
|
|
|
/* No Randomizer (L0L3) Support for 3D 100 series nand */
|
|
if ((mt_L04A_nand_check(aml_chip) == 0) ||
|
|
(mt_L05B_nand_check(aml_chip) == 0))
|
|
return;
|
|
|
|
operation->get_onfi_para(aml_chip, rand_val, rand_addr);
|
|
aml_nand_dbg("%s, %d,val:%d",__func__, __LINE__, rand_val[0]);
|
|
if (rand_val[0] == 1) {
|
|
rand_val[0] = 0;
|
|
operation->set_onfi_para(aml_chip, rand_val, rand_addr);
|
|
operation->get_onfi_para(aml_chip, rand_val, rand_addr);
|
|
aml_nand_dbg("%s, %d,val:%d",
|
|
__func__, __LINE__, rand_val[0]);
|
|
if (rand_val[0] == 0) slc_info->micron_l0l3_mode = 1;
|
|
|
|
rand_val[0] = 1;
|
|
operation->set_onfi_para(aml_chip, rand_val, rand_addr);
|
|
operation->get_onfi_para(aml_chip, rand_val, rand_addr);
|
|
aml_nand_dbg("%s, %d,val:%d", __func__, __LINE__, rand_val[0]);
|
|
} else if (rand_val[0] == 0) {
|
|
rand_val[0] = 1;
|
|
operation->set_onfi_para(aml_chip, rand_val, rand_addr);
|
|
operation->get_onfi_para(aml_chip, rand_val, rand_addr);
|
|
if (rand_val[0] == 1) {
|
|
aml_nand_dbg("%s, Set Randomizer val:%d ON",
|
|
__func__, rand_val[0]);
|
|
slc_info->micron_l0l3_mode = 1;
|
|
}
|
|
}
|
|
|
|
if (slc_info->micron_l0l3_mode)
|
|
aml_nand_msg("nand: Support for Randomizer MODE");
|
|
}
|
|
|
|
/*
|
|
* fill amlnand_chip struct.
|
|
* including chip partnum detect and multi-chip num detect function.
|
|
*
|
|
*/
|
|
static int amlnand_chip_scan(struct amlnand_chip *aml_chip)
|
|
{
|
|
struct hw_controller *controller = &(aml_chip->controller);
|
|
struct chip_operation *operation = &(aml_chip->operation);
|
|
u8 dev_id[MAX_ID_LEN] = {0};
|
|
u8 onfi_features[4] = {0};
|
|
int i, chip_num, ret = 0;
|
|
NAND_LINE
|
|
/* should setting nand pinmux first */
|
|
nand_get_chip(aml_chip);
|
|
NAND_LINE
|
|
ret = get_flash_type(aml_chip);
|
|
if (ret < 0) {
|
|
aml_nand_msg("get_chip_type and ret:%x", ret);
|
|
goto error_exit0;
|
|
}
|
|
|
|
controller->chip_num = MAX_CHIP_NUM;/* 1; */
|
|
controller->option |= NAND_CTRL_NONE_RB;
|
|
chip_num = 1;
|
|
NAND_LINE
|
|
/*ce0 is always valid.*/
|
|
aml_chip->ce_bit_mask |= 0x01;
|
|
|
|
/* Check for a chip array */
|
|
for (i = 1; i < MAX_CHIP_NUM; i++) {
|
|
memset(&dev_id[0], 0, MAX_ID_LEN);
|
|
ret = operation->read_id(aml_chip,
|
|
i,
|
|
NAND_CMD_ID_ADDR_NORMAL,
|
|
&dev_id[0]);
|
|
if (ret < 0) {
|
|
aml_nand_dbg("read id failed and ret:%d", ret);
|
|
continue;
|
|
}
|
|
|
|
aml_nand_msg("controller->flash_type =%d",
|
|
controller->flash_type);
|
|
if (((controller->flash_type == NAND_TYPE_SLC)
|
|
|| (controller->flash_type == NAND_TYPE_MLC))
|
|
&& (aml_chip->flash.id[1] == dev_id[1])) {
|
|
controller->ce_enable[chip_num] =
|
|
(((CE_PAD_DEFAULT >> i*4) & 0xf) << 10);
|
|
controller->rb_enable[chip_num] =
|
|
(((RB_PAD_DEFAULT >> i*4) & 0xf) << 10);
|
|
chip_num++;
|
|
aml_chip->ce_bit_mask |= (1 << i);
|
|
}
|
|
}
|
|
/* aml_nand_msg("nand chip ce mask %0x", aml_chip->ce_bit_mask); */
|
|
|
|
controller->chip_num = chip_num;
|
|
|
|
if (controller->chip_num > 1) {
|
|
if (controller->chip_num == MAX_CHIP_NUM) {
|
|
aml_chip->flash.option &= ~(NAND_MULTI_PLANE_MODE);
|
|
aml_nand_msg("detect %d NF chips,disable twoplane mode",
|
|
controller->chip_num);
|
|
} else
|
|
aml_nand_msg("detected %d NAND chips",
|
|
controller->chip_num);
|
|
}
|
|
|
|
if (controller->onfi_mode) {
|
|
operation->set_onfi_para(aml_chip,
|
|
(u8 *)&(controller->onfi_mode),
|
|
ONFI_TIMING_ADDR);
|
|
operation->get_onfi_para(aml_chip,
|
|
onfi_features,
|
|
ONFI_TIMING_ADDR);
|
|
if (onfi_features[0] != controller->onfi_mode) {
|
|
aml_chip->flash.T_REA = DEFAULT_T_REA;
|
|
aml_chip->flash.T_RHOH = DEFAULT_T_RHOH;
|
|
aml_nand_msg("onfi timing mode set failed: %x",
|
|
onfi_features[0]);
|
|
}
|
|
}
|
|
|
|
if (aml_chip->flash.new_type == MICRON_20NM)
|
|
micron_check_rand_mode(aml_chip);
|
|
|
|
ret = NAND_SUCCESS;
|
|
error_exit0:
|
|
NAND_LINE
|
|
/* should clear nand pinmux here */
|
|
nand_release_chip(aml_chip);
|
|
NAND_LINE
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* fill aml_nand_buf_init struct.
|
|
* malloc tmp buf and dma buf here.
|
|
*
|
|
*/
|
|
static int nand_buf_init(struct amlnand_chip *aml_chip)
|
|
{
|
|
struct hw_controller *controller = &(aml_chip->controller);
|
|
struct nand_flash *flash = &aml_chip->flash;
|
|
u32 buf_size;
|
|
int err = 0;
|
|
|
|
controller->ecc_unit = NAND_ECC_UNIT_SIZE;
|
|
//fixme, check controller init operations...
|
|
#ifndef AML_NAND_UBOOT
|
|
controller->data_buf = dma_alloc_coherent(NULL,
|
|
(flash->pagesize + flash->oobsize),
|
|
&controller->data_dma_addr, GFP_KERNEL);
|
|
#else /* AML_NAND_UBOOT */
|
|
controller->data_buf = aml_nand_malloc(flash->pagesize + flash->oobsize);
|
|
/*controller->data_buf = (u8 *)0x12000000;*/
|
|
#endif /* AML_NAND_UBOOT */
|
|
if (!controller->data_buf) {
|
|
aml_nand_msg("no memory for data buf, and need %x", (flash->pagesize + flash->oobsize));
|
|
err = -NAND_MALLOC_FAILURE;
|
|
goto exit_error0;
|
|
}
|
|
|
|
|
|
buf_size = (flash->pagesize /controller->ecc_unit)*PER_INFO_BYTE;
|
|
buf_size += 16;
|
|
#ifndef AML_NAND_UBOOT
|
|
controller->user_buf = dma_alloc_coherent(NULL,
|
|
buf_size,
|
|
&(controller->info_dma_addr),
|
|
GFP_KERNEL);/* amlnf_dma_malloc(buf_size, 1); */
|
|
#else /* AML_NAND_UBOOT */
|
|
controller->user_buf = aml_nand_malloc(buf_size);
|
|
/* controller->user_buf = (u8 *)0x12010000; */
|
|
|
|
#endif /* AML_NAND_UBOOT */
|
|
if (!controller->user_buf) {
|
|
aml_nand_msg("no memory for usr info buf, and need %x", buf_size);
|
|
err = -NAND_MALLOC_FAILURE;
|
|
goto exit_error1;
|
|
}
|
|
|
|
/* aml_nand_msg("controller->chip_num:%d\n",controller->chip_num); //1 */
|
|
//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;
|
|
|
|
//controller->page_buf = aml_nand_malloc(buf_size);
|
|
//if (!controller->page_buf) {
|
|
// aml_nand_msg("no memory for data buf, and need %x", buf_size);
|
|
// err = -NAND_MALLOC_FAILURE;
|
|
// goto exit_error2;
|
|
//}
|
|
|
|
buf_size = 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;
|
|
|
|
controller->oob_buf = aml_nand_malloc(buf_size);
|
|
/* controller->oob_buf = (u8 *)0x13000000; */
|
|
if (!controller->oob_buf) {
|
|
aml_nand_msg("no memory for data buf, and need %x", buf_size);
|
|
err = -NAND_MALLOC_FAILURE;
|
|
goto exit_error3;
|
|
}
|
|
|
|
return NAND_SUCCESS;
|
|
|
|
exit_error3:
|
|
//aml_nand_free(controller->page_buf);
|
|
|
|
//exit_error2:
|
|
#ifndef AML_NAND_UBOOT
|
|
amlnf_dma_free(controller->user_buf,
|
|
(flash->pagesize / controller->ecc_bytes)*sizeof(int),
|
|
1);
|
|
#else /* AML_NAND_UBOOT */
|
|
aml_nand_free(controller->user_buf);
|
|
#endif /* AML_NAND_UBOOT */
|
|
|
|
exit_error1:
|
|
#ifndef AML_NAND_UBOOT
|
|
amlnf_dma_free(controller->data_buf,
|
|
(flash->pagesize + flash->oobsize),
|
|
0);
|
|
#else /* AML_NAND_UBOOT */
|
|
aml_nand_free(controller->data_buf);
|
|
#endif /* AML_NAND_UBOOT */
|
|
|
|
exit_error0:
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* fill free malloc buf here.
|
|
*
|
|
*
|
|
*/
|
|
static void nand_buf_free(struct amlnand_chip *aml_chip)
|
|
{
|
|
struct hw_controller *controller = &aml_chip->controller;
|
|
#ifndef AML_NAND_UBOOT
|
|
struct nand_flash *flash = &aml_chip->flash;
|
|
|
|
amlnf_dma_free(controller->data_buf,
|
|
(flash->pagesize + flash->oobsize),
|
|
0);
|
|
amlnf_dma_free(controller->user_buf,
|
|
(flash->pagesize / controller->ecc_bytes)*sizeof(int),
|
|
1);
|
|
#else /* AML_NAND_UBOOT */
|
|
aml_nand_free(controller->data_buf);
|
|
aml_nand_free(controller->user_buf);
|
|
#endif /* AML_NAND_UBOOT */
|
|
controller->data_buf = NULL;
|
|
controller->user_buf = NULL;
|
|
|
|
//aml_nand_free(controller->page_buf);
|
|
aml_nand_free(controller->oob_buf);
|
|
//controller->page_buf = NULL;
|
|
controller->oob_buf = NULL;
|
|
}
|
|
|
|
/*
|
|
* check rb pin here.
|
|
* if without rb pin, then setting NAND_CTRL_NONE_RB mode
|
|
*
|
|
*/
|
|
static void aml_chip_rb_mode_confirm(struct amlnand_chip *aml_chip)
|
|
{
|
|
struct hw_controller *controller = &aml_chip->controller;
|
|
u32 por_cfg = 0, rb_mode = 0;
|
|
void __iomem *poc_reg = NULL;
|
|
|
|
poc_reg = (void __iomem *)aml_nand_dev->platform_data->poc_reg;
|
|
|
|
if (controller->chip_num > 2) {
|
|
aml_nand_msg("force NO RB pin and chip_num:%d over 2",
|
|
controller->chip_num);
|
|
rb_mode = 1;
|
|
} else {
|
|
por_cfg = amlnf_read_reg32(poc_reg);
|
|
|
|
aml_nand_msg("detect RB pin here and por_cfg:%x", por_cfg);
|
|
if (por_cfg&POC_NAND_NO_RB) {
|
|
aml_nand_msg("detect without RB pin here");
|
|
rb_mode = 1;
|
|
} else {
|
|
aml_nand_msg("detect with RB pin here");
|
|
controller->option &= ~NAND_CTRL_NONE_RB;
|
|
}
|
|
}
|
|
|
|
#ifdef AML_NAND_RB_IRQ
|
|
rb_mode = 1;
|
|
aml_nand_msg("force none rb mode for rb irq");
|
|
#endif
|
|
|
|
if (rb_mode) {
|
|
controller->rb_enable[0] = 0;
|
|
controller->option |= NAND_CTRL_NONE_RB;
|
|
}
|
|
}
|
|
|
|
void amlchip_dumpinfo(struct amlnand_chip *aml_chip)
|
|
{
|
|
struct hw_controller *controller = &(aml_chip->controller);
|
|
struct nand_flash *flash = &(aml_chip->flash);
|
|
|
|
/* flash info */
|
|
aml_nand_msg("flash info");
|
|
aml_nand_msg("name:%s, id:%2x %2x %2x %2x %2x %2x %2x %2x",
|
|
flash->name,
|
|
flash->id[0],
|
|
flash->id[1],
|
|
flash->id[2],
|
|
flash->id[3],
|
|
flash->id[4],
|
|
flash->id[5],
|
|
flash->id[6],
|
|
flash->id[7]);
|
|
aml_nand_msg("pgs:%x,blks:%x,oobs:%d,chips:%d,opt:0x%x,T_R:%d,T_RH:%d",
|
|
flash->pagesize,
|
|
flash->blocksize,
|
|
flash->oobsize,
|
|
flash->chipsize,
|
|
flash->option,
|
|
flash->T_REA,
|
|
flash->T_RHOH);
|
|
aml_nand_msg("hw controller info\n");
|
|
aml_nand_msg("chipn:%d,onfi:%d,pg_shift:%d,blk_shift:%d,option:0x%x",
|
|
controller->chip_num,
|
|
controller->onfi_mode,
|
|
controller->page_shift,
|
|
controller->block_shift,
|
|
controller->option);
|
|
aml_nand_msg("ecc_unit:%d, ecc_bytes:%d, ecc_steps:%d, ecc_max:%d",
|
|
controller->ecc_unit,
|
|
controller->ecc_bytes,
|
|
controller->ecc_steps,
|
|
controller->ecc_max);
|
|
aml_nand_msg("bch_mode:%d, user_mode:%d, oobavail:%d, oobtail:%d",
|
|
controller->bch_mode,
|
|
controller->user_mode,
|
|
controller->oobavail,
|
|
controller->oobtail);
|
|
}
|
|
|
|
int amlchip_opstest(struct amlnand_chip *aml_chip)
|
|
{
|
|
struct hw_controller *controller = &(aml_chip->controller);
|
|
struct chip_operation *operation = &(aml_chip->operation);
|
|
struct nand_flash *flash = &(aml_chip->flash);
|
|
struct chip_ops_para *ops_para = &(aml_chip->ops_para);
|
|
u64 addr, opslen = 0, len = 0;
|
|
u32 erase_shift, write_shift, writesize, erasesize;
|
|
int i, ret = 0;
|
|
u32 buf_size = 0;
|
|
u8 *page_buf;
|
|
|
|
/* should setting nand pinmux first */
|
|
nand_get_chip(aml_chip);
|
|
|
|
/* clear ops_para here */
|
|
memset(ops_para, 0, sizeof(struct chip_ops_para));
|
|
|
|
writesize = flash->pagesize;
|
|
erasesize = flash->blocksize;
|
|
|
|
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__);
|
|
return 0;
|
|
}
|
|
|
|
/* ops_para->option = (DEV_ECC_HW_MODE |DEV_SERIAL_CHIP_MODE ); */
|
|
ops_para->option = (DEV_ECC_HW_MODE |
|
|
NAND_MULTI_PLANE_MODE |
|
|
DEV_SERIAL_CHIP_MODE | DEV_USE_SHAREPAGE_MODE);
|
|
ops_para->data_buf = page_buf;
|
|
/* ops_para->data_buf = controller->page_buf; */
|
|
/* ops_para->oob_buf = controller->oobbuf; */
|
|
|
|
if (ops_para->option & DEV_MULTI_PLANE_MODE) {
|
|
writesize *= 2;
|
|
erasesize *= 2;
|
|
}
|
|
|
|
if (ops_para->option & DEV_MULTI_CHIP_MODE) {
|
|
writesize *= controller->chip_num;
|
|
erasesize *= controller->chip_num;
|
|
}
|
|
|
|
if (ops_para->option & DEV_USE_SHAREPAGE_MODE)
|
|
writesize *= 2;
|
|
|
|
erase_shift = ffs(erasesize) - 1;
|
|
write_shift = ffs(writesize) - 1;
|
|
|
|
#if 0
|
|
/* read */
|
|
/* start addr 0, read whole chip size */
|
|
opslen = addr = 0;
|
|
len = ((u64)(flash->chipsize*controller->chip_num))<<20;
|
|
aml_nand_dbg("TEST step1 read whole chip,len:%llx, total_page:%d",
|
|
len,
|
|
len>>write_shift);
|
|
|
|
while (1) {
|
|
memset(ops_para->data_buf, 0, writesize);
|
|
if (ops_para->option & DEV_SERIAL_CHIP_MODE) {
|
|
ops_para->chipnr =
|
|
(addr>>erase_shift)%controller->chip_num;
|
|
}
|
|
|
|
ops_para->page_addr =
|
|
(int)(addr >> write_shift)/controller->chip_num;
|
|
ret = operation->read_page(aml_chip);
|
|
if (ret < 0) {
|
|
aml_nand_msg("fail page_addr:%d", ops_para->page_addr);
|
|
break;
|
|
}
|
|
aml_nand_dbg("page:%d data: %x %x %x %x", ops_para->page_addr,
|
|
ops_para->data_buf[0],
|
|
ops_para->data_buf[1],
|
|
ops_para->data_buf[2],
|
|
ops_para->data_buf[3]);
|
|
|
|
addr += writesize;
|
|
opslen += writesize;
|
|
if (opslen >= len)
|
|
break;
|
|
|
|
if (ops_para->ecc_err)
|
|
aml_nand_msg("ecc failed at page_addr:%d",
|
|
ops_para->page_addr);
|
|
else if (ops_para->bit_flip)
|
|
aml_nand_msg("bit_flip at page_addr:%d",
|
|
ops_para->page_addr);
|
|
}
|
|
#endif
|
|
/* BUG(); */
|
|
#if 1
|
|
/* erase */
|
|
/* start addr 0, read whole chip size */
|
|
aml_nand_dbg("TEST step2 erase whole chip");
|
|
opslen = addr = 0;
|
|
/* len = ((u64)(flash->chipsize*controller->chip_num))<<20; */
|
|
len = 1<<erase_shift;
|
|
aml_nand_dbg("TEST step2 erase whole chip, len:%llx, total_page:%d",
|
|
len,
|
|
len>>write_shift);
|
|
while (1) {
|
|
if (ops_para->option & DEV_SERIAL_CHIP_MODE)
|
|
ops_para->chipnr =
|
|
(addr>>erase_shift)%controller->chip_num;
|
|
|
|
ops_para->page_addr =
|
|
(int)(addr >> write_shift)/controller->chip_num;
|
|
ret = operation->erase_block(aml_chip);
|
|
if (ret < 0)
|
|
aml_nand_msg("fail page_addr:%d", ops_para->page_addr);
|
|
break;
|
|
|
|
addr += erasesize;
|
|
opslen += erasesize;
|
|
if (opslen >= len)
|
|
break;
|
|
}
|
|
#endif
|
|
/* BUG(); */
|
|
/* write */
|
|
opslen = addr = 0;
|
|
/* len = ((u64)(flash->chipsize*controller->chip_num))<<20; */
|
|
aml_nand_dbg("TEST step3 write whole chip, len:%llx, total_page:%d",
|
|
len, len>>write_shift);
|
|
for (i = 0; i < writesize; i++)
|
|
ops_para->data_buf[i] = i;
|
|
|
|
while (1) {
|
|
if (ops_para->option & DEV_SERIAL_CHIP_MODE)
|
|
ops_para->chipnr =
|
|
(addr>>erase_shift)%controller->chip_num;
|
|
|
|
ops_para->page_addr =
|
|
(int)(addr >> write_shift)/controller->chip_num;
|
|
ret = operation->write_page(aml_chip);
|
|
if (ret < 0) {
|
|
aml_nand_msg("fail page_addr:%d", ops_para->page_addr);
|
|
break;
|
|
}
|
|
|
|
addr += writesize;
|
|
/* ops_para->data_buf += writesize; */
|
|
opslen += writesize;
|
|
if (opslen >= len)
|
|
break;
|
|
}
|
|
|
|
/* read */
|
|
/* start addr 0, read whole chip size */
|
|
opslen = addr = 0;
|
|
/* len = ((u64)(flash->chipsize*controller->chip_num))<<20; */
|
|
aml_nand_dbg("TEST step4 read whole chip, len:%llx, total_page:%d",
|
|
len,
|
|
len>>write_shift);
|
|
while (1) {
|
|
memset(ops_para->data_buf, 0, writesize);
|
|
if (ops_para->option & DEV_SERIAL_CHIP_MODE)
|
|
ops_para->chipnr =
|
|
(addr>>erase_shift)%controller->chip_num;
|
|
|
|
ops_para->page_addr =
|
|
(int)(addr >> write_shift)/controller->chip_num;
|
|
ret = operation->read_page(aml_chip);
|
|
if (ret < 0) {
|
|
aml_nand_msg("fail page_addr:%d", ops_para->page_addr);
|
|
break;
|
|
}
|
|
aml_nand_dbg("page:%d data: %x %x %x %x",
|
|
ops_para->page_addr,
|
|
ops_para->data_buf[0],
|
|
ops_para->data_buf[1],
|
|
ops_para->data_buf[2],
|
|
ops_para->data_buf[3]);
|
|
addr += writesize;
|
|
/* ops_para->data_buf += writesize; */
|
|
opslen += writesize;
|
|
if (opslen >= len)
|
|
break;
|
|
|
|
if (ops_para->ecc_err)
|
|
aml_nand_msg("ecc failed at page_addr:%d",
|
|
ops_para->page_addr);
|
|
else if (ops_para->bit_flip)
|
|
aml_nand_msg("bit_flip at page_addr:%d",
|
|
ops_para->page_addr);
|
|
}
|
|
/* should clear nand pinmux here */
|
|
nand_release_chip(aml_chip);
|
|
aml_nand_free(page_buf);
|
|
page_buf = NULL;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* fill amlnand_chip struct.
|
|
* including hw init, chip detect, option setting and operation function.
|
|
*
|
|
*/
|
|
u32 amlnand_chip_init(struct amlnand_chip *aml_chip)
|
|
{
|
|
struct hw_controller *controller = &(aml_chip->controller);
|
|
/* struct chip_operation *operation = &aml_chip->operation; */
|
|
int ret = 0;
|
|
NAND_LINE
|
|
ret = amlnand_chip_scan(aml_chip);
|
|
if (ret) {
|
|
aml_nand_msg("get_chip_type and ret:%x", ret);
|
|
goto error_exit0;
|
|
}
|
|
NAND_LINE
|
|
|
|
#ifdef AML_NAND_UBOOT
|
|
// /*only check nand circult is ok*/
|
|
if (aml_chip->init_flag == NAND_SCAN_ID_INIT)
|
|
goto error_exit0;
|
|
#endif /* AML_NAND_UBOOT */
|
|
|
|
/* amlchip_dumpinfo(aml_chip); */
|
|
|
|
ret = nand_buf_init(aml_chip);
|
|
if (ret) {
|
|
aml_nand_msg("buf init failed and ret:%x", ret);
|
|
goto error_exit0;
|
|
}
|
|
NAND_LINE
|
|
ret = controller->adjust_timing(controller);
|
|
if (ret) {
|
|
aml_nand_msg("adjust_timing failed and ret:%x", ret);
|
|
goto error_exit1;
|
|
}
|
|
NAND_LINE
|
|
if (get_cpu_type() < MESON_CPU_MAJOR_ID_M8)
|
|
aml_chip_rb_mode_confirm(aml_chip);
|
|
|
|
NAND_LINE
|
|
ret = controller->ecc_confirm(controller);
|
|
if (ret) {
|
|
aml_nand_msg("buf init failed and ret:%x", ret);
|
|
goto error_exit1;
|
|
}
|
|
NAND_LINE
|
|
#if (AML_NAND_DBG)
|
|
amlchip_dumpinfo(aml_chip);
|
|
#endif
|
|
|
|
/* basic operation test here, read/write/erase */
|
|
#if 0
|
|
amlchip_opstest(aml_chip);
|
|
#endif
|
|
|
|
return NAND_SUCCESS;
|
|
error_exit1:
|
|
nand_buf_free(aml_chip);
|
|
error_exit0:
|
|
return ret;
|
|
|
|
}
|
|
|