blob: 88e520a22c21aeee863da90a64b5222415ea80bb [file] [log] [blame]
/*****************************************************************
**
** Copyright (C) 2012 Amlogic,Inc. All rights reserved
**
** Filename : chip.c
** Revision : 1.001
** Author: Benjamin Zhao
** Description:
** chip init/bbt/config/scan function, mainly for nand phy driver.
**
**
*****************************************************************/
#include "../include/phynand.h"
static 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;
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
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);
aml_nand_msg("detect NAND device: %s", type->name);
#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))
controller->onfi_mode = type->onfi_mode;
#endif
error_exit:
return ret;
}
/*
* 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;
}
/*
memcmp((char*)&(aml_chip->flash.id[0]),
(char*)&dev_id[0], MAX_ID_LEN)
*/
aml_nand_dbg("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]);
}
}
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);
#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);
#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;
}
buf_size = (flash->pagesize + flash->oobsize) * controller->chip_num;
if (flash->option & NAND_MULTI_PLANE_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;
controller->oob_buf = aml_nand_malloc(buf_size);
if (!controller->oob_buf) {
aml_nand_msg("no memory for data buf, and need %x", buf_size);
err = -NAND_MALLOC_FAILURE;
goto exit_error3;
}
/*
if (request_irq(INT_NAND,
(irq_handler_t)nand_interrupt_monitor,
0,
"anl_nand",
aml_chip)) {
printk("request SDIO irq error!!!\n");
return -1;
}
*/
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;
/* 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;
/* 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);
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;
}
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);
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;
}