1843 lines
50 KiB
C
1843 lines
50 KiB
C
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
|
/*
|
|
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
|
|
*/
|
|
|
|
#include "../include/phynand.h"
|
|
|
|
#ifdef AML_NAND_UBOOT
|
|
//extern struct amlnf_partition amlnf_partitions;
|
|
extern struct amlnf_partition * amlnf_partitions;
|
|
#endif
|
|
|
|
#define DBG_WRITE_VERIFY (0)
|
|
|
|
#if (DBG_WRITE_VERIFY)
|
|
u8 * glb_verify_buffer = NULL;
|
|
#endif
|
|
|
|
enum chip_state_t get_chip_state(struct amlnand_chip *aml_chip)
|
|
{
|
|
return aml_chip->state;
|
|
}
|
|
|
|
|
|
void set_chip_state(struct amlnand_chip *aml_chip, enum chip_state_t state)
|
|
{
|
|
aml_chip->state = state;
|
|
}
|
|
|
|
static u32 amlnand_slc_addr_trs(struct amlnand_phydev *phydev)
|
|
{
|
|
struct amlnand_chip *aml_chip = (struct amlnand_chip *)phydev->priv;
|
|
struct nand_flash *flash = &(aml_chip->flash);
|
|
struct phydev_ops *devops = &(phydev->ops);
|
|
struct hw_controller *controller = &(aml_chip->controller);
|
|
struct en_slc_info *slc_info = &(controller->slc_info);
|
|
int real_page_per_blk, page_per_blk, blk_num, blk_num_in_dev, page_num;
|
|
/* u64 addr, readlen = 0; */
|
|
u32 page_addr = 0;
|
|
u32 real_erase_size, real_erase_shift, w_size_shift, e_size_shift;
|
|
int total_pages_in_dev;
|
|
|
|
real_erase_size = (phydev->erasesize << 1);
|
|
real_erase_shift = ffs(real_erase_size) - 1;
|
|
w_size_shift = phydev->writesize_shift;
|
|
e_size_shift = phydev->erasesize_shift;
|
|
|
|
real_page_per_blk = (1 << ((ffs(real_erase_size) - 1) - w_size_shift));
|
|
page_per_blk = (1 << (e_size_shift - w_size_shift));
|
|
|
|
blk_num = phydev->offset >> real_erase_shift;
|
|
blk_num_in_dev = devops->addr >> e_size_shift;
|
|
blk_num += blk_num_in_dev;
|
|
|
|
total_pages_in_dev = blk_num_in_dev * page_per_blk;
|
|
page_num = ((devops->addr >> w_size_shift) - total_pages_in_dev);
|
|
|
|
if ((flash->new_type > 0) && (flash->new_type < 10))
|
|
page_addr = blk_num * real_page_per_blk +
|
|
slc_info->pagelist[page_num];
|
|
else if (flash->new_type == SANDISK_19NM)
|
|
page_addr = blk_num * real_page_per_blk + (page_num << 1);
|
|
else { /* not support slc nand */
|
|
page_addr =
|
|
(int)((phydev->offset + devops->addr) >> w_size_shift);
|
|
aml_nand_msg("nand not support slc ");
|
|
}
|
|
|
|
#if 0
|
|
aml_nand_dbg(" devops->addr =%llx %d", devops->addr, devops->addr);
|
|
aml_nand_dbg("real_erase_size =%x", real_erase_size);
|
|
aml_nand_dbg("real_page_per_blk =%d", real_page_per_blk);
|
|
aml_nand_dbg("page_per_blk =%d", page_per_blk);
|
|
aml_nand_dbg("blk_num =%d", blk_num);
|
|
aml_nand_dbg("blk_num_in_dev =%d", blk_num_in_dev);
|
|
aml_nand_dbg("page_num =%d", page_num);
|
|
aml_nand_dbg("page_addr =%d", page_addr);
|
|
#endif
|
|
|
|
return page_addr;
|
|
}
|
|
|
|
/*
|
|
* fixme, pls use DBG_WRITE_VERIFY instead(20150918) before fix! yyh.
|
|
*/
|
|
static void nand_write_verify(struct amlnand_phydev *phydev)
|
|
{
|
|
struct amlnand_chip *aml_chip = (struct amlnand_chip *)phydev->priv;
|
|
struct phydev_ops *devops = &(phydev->ops);
|
|
/* struct hw_controller *controller = &(aml_chip->controller); */
|
|
struct chip_operation *operation = &(aml_chip->operation);
|
|
struct chip_ops_para *ops_para = &(aml_chip->ops_para);
|
|
int ret = 0;
|
|
u8 *verify_buf = NULL;
|
|
|
|
verify_buf = aml_nand_malloc(2 * phydev->writesize);
|
|
if (!verify_buf) {
|
|
aml_nand_msg("malloc failed for nand_read_verify");
|
|
return;
|
|
}
|
|
|
|
ops_para->data_buf = verify_buf;
|
|
ret = operation->read_page(aml_chip);
|
|
if ((ops_para->ecc_err) || (ret < 0))
|
|
aml_nand_msg("nand phy read failed at devops->addr : %llx",
|
|
devops->addr);
|
|
|
|
if (memcmp(verify_buf, devops->datbuf, phydev->writesize))
|
|
aml_nand_msg("nand write verify failed");
|
|
|
|
kfree(verify_buf);
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
*read data case:
|
|
*only data read function, if none ecc mode, buf should be data+oob
|
|
* operation type as below: oob_mode data_buf oob_buf ooblen
|
|
*1) read oob hw ecc mode 0 NULL available available
|
|
*2) read data and oob hw ecc mode 0 available available available
|
|
*3) read data and oob sw ecc mode 1 available available 0
|
|
*/
|
|
static int nand_read(struct amlnand_phydev *phydev)
|
|
{
|
|
struct amlnand_chip *aml_chip = (struct amlnand_chip *)phydev->priv;
|
|
struct phydev_ops *devops = &(phydev->ops);
|
|
struct hw_controller *controller = &(aml_chip->controller);
|
|
struct chip_operation *operation = &(aml_chip->operation);
|
|
struct chip_ops_para *ops_para = &(aml_chip->ops_para);
|
|
u64 addr, readlen = 0, len = 0;
|
|
int ret = 0;
|
|
|
|
if ((devops->addr + devops->len) > phydev->size) {
|
|
aml_nand_msg("out of space and addr:");
|
|
aml_nand_msg("%llx len:%llx pdev->offset:%llx pdev->size:%llx",
|
|
devops->addr,
|
|
devops->len,
|
|
phydev->offset,
|
|
phydev->size);
|
|
return -NAND_ARGUMENT_FAILURE;
|
|
}
|
|
|
|
if ((devops->len == 0) && (devops->ooblen == 0)) {
|
|
aml_nand_msg("len equal zero here");
|
|
return NAND_SUCCESS;
|
|
}
|
|
|
|
amlnand_get_device(aml_chip, CHIP_READING);
|
|
|
|
/* clear ops_para here */
|
|
memset(ops_para, 0, sizeof(struct chip_ops_para));
|
|
|
|
if (devops->len == 0) {
|
|
len = phydev->writesize;
|
|
ops_para->ooblen = devops->ooblen;
|
|
} else {
|
|
len = devops->len;
|
|
ops_para->ooblen = devops->ooblen;
|
|
}
|
|
|
|
addr = phydev->offset + devops->addr;
|
|
ops_para->data_buf = devops->datbuf;
|
|
ops_para->option = phydev->option;
|
|
ops_para->oob_buf = devops->oobbuf;
|
|
|
|
/* aml_nand_dbg("len =%llx",len); */
|
|
if (devops->mode == NAND_SOFT_ECC)
|
|
ops_para->option |= DEV_ECC_SOFT_MODE;
|
|
|
|
while (1) {
|
|
if (ops_para->option & DEV_SERIAL_CHIP_MODE) {
|
|
ops_para->chipnr =
|
|
(addr >> phydev->erasesize)
|
|
% controller->chip_num;
|
|
controller->select_chip(controller, ops_para->chipnr);
|
|
aml_nand_dbg("ops_para->chipnr =%d", ops_para->chipnr);
|
|
aml_nand_dbg("DEV_SERIAL_CHIP_MODE");
|
|
}
|
|
|
|
if (ops_para->option & DEV_SLC_MODE)
|
|
ops_para->page_addr = amlnand_slc_addr_trs(phydev);
|
|
else
|
|
ops_para->page_addr =
|
|
(int)(addr >> phydev->writesize_shift);
|
|
|
|
//aml_nand_msg("%s() page %x\n", __func__, ops_para->page_addr);
|
|
ret = operation->read_page(aml_chip);
|
|
if ((ops_para->ecc_err) || (ret < 0)) {
|
|
aml_nand_msg("phy read failed at devops->addr: %llx",
|
|
devops->addr);
|
|
break;
|
|
}
|
|
|
|
addr += phydev->writesize;
|
|
|
|
ops_para->data_buf += phydev->writesize;
|
|
readlen += phydev->writesize;
|
|
|
|
/* aml_nand_dbg("readlen =%llx",readlen); */
|
|
if (readlen >= len)
|
|
break;
|
|
}
|
|
|
|
devops->retlen = readlen;
|
|
|
|
amlnand_release_device(aml_chip);
|
|
|
|
if (!ret) {
|
|
if (ops_para->ecc_err)
|
|
ret = NAND_ECC_FAILURE;
|
|
else if (ops_para->bit_flip)
|
|
ret = -EUCLEAN; /* 117 */
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
#if (DBG_WRITE_VERIFY)
|
|
static int _nand_write(struct amlnand_phydev *phydev)
|
|
#else
|
|
static int nand_write(struct amlnand_phydev *phydev)
|
|
#endif
|
|
{
|
|
struct amlnand_chip *aml_chip = (struct amlnand_chip *)phydev->priv;
|
|
struct phydev_ops *devops = &(phydev->ops);
|
|
struct hw_controller *controller = &(aml_chip->controller);
|
|
struct chip_operation *operation = &(aml_chip->operation);
|
|
struct chip_ops_para *ops_para = &(aml_chip->ops_para);
|
|
u64 addr, writelen = 0, len = 0;
|
|
int ret = 0;
|
|
|
|
PHYDEV_LINE
|
|
if ((devops->addr + devops->len) > phydev->size) {
|
|
aml_nand_msg("out of space and addr:");
|
|
aml_nand_msg("%llx len:%llx pdev->offset:%llx pdev->size:%llx",
|
|
devops->addr,
|
|
devops->len,
|
|
phydev->offset,
|
|
phydev->size);
|
|
return -NAND_ARGUMENT_FAILURE;
|
|
}
|
|
|
|
if ((devops->len == 0) && (devops->ooblen == 0)) {
|
|
aml_nand_msg("len equal zero here");
|
|
return NAND_SUCCESS;
|
|
}
|
|
|
|
#ifndef AML_NAND_UBOOT
|
|
if (phydev->option & NAND_SHUT_DOWN) {
|
|
aml_nand_msg("nand is in shut down protect mode");
|
|
return NAND_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
amlnand_get_device(aml_chip, CHIP_WRITING);
|
|
|
|
len = devops->len;
|
|
/* clear ops_para here */
|
|
memset(ops_para, 0, sizeof(struct chip_ops_para));
|
|
addr = phydev->offset + devops->addr;
|
|
ops_para->option = phydev->option; //multi-plane is set
|
|
ops_para->data_buf = devops->datbuf;
|
|
ops_para->oob_buf = devops->oobbuf;
|
|
ops_para->ooblen = devops->ooblen;
|
|
if (devops->mode == NAND_SOFT_ECC)
|
|
ops_para->option |= DEV_ECC_SOFT_MODE;
|
|
|
|
while (1) {
|
|
if (ops_para->option & DEV_SERIAL_CHIP_MODE) {
|
|
ops_para->chipnr =
|
|
(addr>>phydev->erasesize_shift) %
|
|
controller->chip_num;
|
|
controller->select_chip(controller, ops_para->chipnr);
|
|
aml_nand_dbg("DEV_SERIAL_CHIP_MODE");
|
|
}
|
|
|
|
if (ops_para->option & DEV_SLC_MODE)
|
|
ops_para->page_addr = amlnand_slc_addr_trs(phydev);
|
|
else
|
|
ops_para->page_addr =
|
|
(int)(addr >> phydev->writesize_shift);
|
|
//aml_nand_msg("%s() page %x\n", __func__, ops_para->page_addr);
|
|
ret = operation->write_page(aml_chip);
|
|
if (ret < 0) {
|
|
aml_nand_msg("Write fail devops->addr:%llx,addr:%llx",
|
|
devops->addr,
|
|
addr);
|
|
break;
|
|
}
|
|
|
|
addr += phydev->writesize;
|
|
ops_para->data_buf += phydev->writesize;
|
|
writelen += phydev->writesize;
|
|
if (writelen >= len)
|
|
break;
|
|
}
|
|
|
|
devops->retlen = writelen;
|
|
|
|
if (aml_chip->debug_flag & NAND_WRITE_VERIFY)
|
|
nand_write_verify(phydev);
|
|
|
|
amlnand_release_device(aml_chip);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#if (DBG_WRITE_VERIFY)
|
|
static int nand_write(struct amlnand_phydev *phydev)
|
|
{
|
|
int ret;
|
|
u8 * tmp;
|
|
struct phydev_ops *devops = &(phydev->ops);
|
|
|
|
ret = _nand_write(phydev);
|
|
tmp = devops->datbuf;
|
|
devops->datbuf = glb_verify_buffer;
|
|
nand_read(phydev);
|
|
|
|
devops->datbuf = tmp;
|
|
printf(".");
|
|
if (memcmp(tmp, glb_verify_buffer, phydev->writesize)) {
|
|
aml_nand_msg("nand write verify failed");
|
|
while (1) ;
|
|
}
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
int nand_erase(struct amlnand_phydev *phydev)
|
|
{
|
|
struct amlnand_chip *aml_chip = (struct amlnand_chip *)phydev->priv;
|
|
struct phydev_ops *devops = &(phydev->ops);
|
|
struct hw_controller *controller = &(aml_chip->controller);
|
|
struct chip_operation *operation = &(aml_chip->operation);
|
|
struct chip_ops_para *ops_para = &(aml_chip->ops_para);
|
|
u64 addr = 0, eraselen = 0;
|
|
int ret = 0;
|
|
|
|
if ((devops->addr + devops->len) > phydev->size) {
|
|
aml_nand_msg("out of space and addr:");
|
|
aml_nand_msg("%llx len:%llx pdev->offset:%llx pdev->size:%llx",
|
|
devops->addr,
|
|
devops->len,
|
|
phydev->offset,
|
|
phydev->size);
|
|
return -NAND_ARGUMENT_FAILURE;
|
|
}
|
|
|
|
if (devops->len == 0) {
|
|
aml_nand_msg("len equal zero here");
|
|
return NAND_SUCCESS;
|
|
}
|
|
|
|
if (phydev->option & NAND_SHUT_DOWN) {
|
|
aml_nand_msg("nand is in shut down protect mod");
|
|
return NAND_SUCCESS;
|
|
}
|
|
|
|
amlnand_get_device(aml_chip, CHIP_ERASING);
|
|
|
|
/* clear ops_para here */
|
|
memset(ops_para, 0, sizeof(struct chip_ops_para));
|
|
addr = phydev->offset + devops->addr;
|
|
ops_para->option = phydev->option;
|
|
|
|
while (1) {
|
|
if (ops_para->option & DEV_SLC_MODE)
|
|
ops_para->page_addr = amlnand_slc_addr_trs(phydev);
|
|
else
|
|
ops_para->page_addr =
|
|
(int)(addr >> phydev->writesize_shift);
|
|
|
|
if (ops_para->option & DEV_SERIAL_CHIP_MODE) {
|
|
ops_para->chipnr =
|
|
(addr>>phydev->erasesize)%controller->chip_num;
|
|
controller->select_chip(controller, ops_para->chipnr);
|
|
}
|
|
|
|
ret = operation->erase_block(aml_chip);
|
|
if (ret < 0) {
|
|
aml_nand_msg("nand erase fail at addr :%x ",
|
|
ops_para->page_addr);
|
|
break;
|
|
}
|
|
|
|
addr += phydev->erasesize;
|
|
eraselen += phydev->erasesize;
|
|
|
|
if (eraselen >= devops->len)
|
|
break;
|
|
}
|
|
|
|
devops->retlen = eraselen;
|
|
|
|
amlnand_release_device(aml_chip);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int nand_block_isbad(struct amlnand_phydev *phydev)
|
|
{
|
|
struct amlnand_chip *aml_chip = (struct amlnand_chip *)phydev->priv;
|
|
/* struct nand_flash *flash = &(aml_chip->flash); */
|
|
struct phydev_ops *devops = &(phydev->ops);
|
|
struct hw_controller *controller = &(aml_chip->controller);
|
|
struct chip_operation *operation = &(aml_chip->operation);
|
|
struct chip_ops_para *ops_para = &(aml_chip->ops_para);
|
|
u64 addr = 0;
|
|
int ret = 0;
|
|
|
|
if (devops->addr > phydev->size) {
|
|
aml_nand_msg("out of space and addr:");
|
|
aml_nand_msg("%llx phydev->offset:%llx phydev->size:%llx",
|
|
devops->addr, phydev->offset, phydev->size);
|
|
return -NAND_ARGUMENT_FAILURE;
|
|
}
|
|
#ifndef AML_NAND_UBOOT
|
|
if (phydev->option & NAND_SHUT_DOWN) {
|
|
aml_nand_msg("nand is in shut down protect mod");
|
|
return NAND_SUCCESS;
|
|
}
|
|
#endif
|
|
amlnand_get_device(aml_chip, CHIP_READING);
|
|
|
|
/* clear ops_para here */
|
|
memset(ops_para, 0, sizeof(struct chip_ops_para));
|
|
addr = phydev->offset + devops->addr;
|
|
ops_para->option = phydev->option;
|
|
if (ops_para->option & DEV_SLC_MODE)
|
|
ops_para->page_addr = amlnand_slc_addr_trs(phydev);
|
|
else
|
|
ops_para->page_addr = (int)(addr >> phydev->writesize_shift);
|
|
|
|
if ((ops_para->option & DEV_SERIAL_CHIP_MODE))
|
|
ops_para->chipnr =
|
|
(addr >> phydev->erasesize_shift)
|
|
% controller->chip_num;
|
|
|
|
ret = operation->block_isbad(aml_chip);
|
|
if (ret < 0)
|
|
aml_nand_msg("fail page_addr:ret=%d, %x len:%llx",
|
|
ret,
|
|
ops_para->page_addr,
|
|
devops->len);
|
|
|
|
amlnand_release_device(aml_chip);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int nand_block_markbad(struct amlnand_phydev *phydev)
|
|
{
|
|
struct amlnand_chip *aml_chip = (struct amlnand_chip *)phydev->priv;
|
|
struct phydev_ops *devops = &(phydev->ops);
|
|
struct hw_controller *controller = &(aml_chip->controller);
|
|
struct chip_operation *operation = &(aml_chip->operation);
|
|
struct chip_ops_para *ops_para = &(aml_chip->ops_para);
|
|
u64 addr = 0;
|
|
int ret = 0;
|
|
|
|
if (devops->addr > phydev->size) {
|
|
aml_nand_msg("out of space and addr:");
|
|
aml_nand_msg("%llx phydev->offset:%llx phydev->size:%llx",
|
|
devops->addr,
|
|
phydev->offset,
|
|
phydev->size);
|
|
return -NAND_ARGUMENT_FAILURE;
|
|
}
|
|
#ifndef AML_NAND_UBOOT
|
|
if (phydev->option & NAND_SHUT_DOWN) {
|
|
aml_nand_msg("nand is in shut down protect mod");
|
|
return NAND_SUCCESS;
|
|
}
|
|
#endif
|
|
amlnand_get_device(aml_chip, CHIP_READING);
|
|
|
|
/* clear ops_para here */
|
|
memset(ops_para, 0, sizeof(struct chip_ops_para));
|
|
addr = phydev->offset + devops->addr;
|
|
ops_para->option = phydev->option;
|
|
/* ops_para->page_addr = (int)(addr >> phydev->writesize_shift); */
|
|
if (ops_para->option & DEV_SLC_MODE)
|
|
ops_para->page_addr = amlnand_slc_addr_trs(phydev);
|
|
else
|
|
ops_para->page_addr = (int)(addr >> phydev->writesize_shift);
|
|
|
|
if (ops_para->option & DEV_SERIAL_CHIP_MODE) {
|
|
ops_para->chipnr =
|
|
(addr>>phydev->erasesize)%controller->chip_num;
|
|
controller->select_chip(controller, ops_para->chipnr);
|
|
}
|
|
|
|
ret = operation->block_markbad(aml_chip);
|
|
if (ret < 0)
|
|
aml_nand_msg("nand mark bad failed at page %d",
|
|
ops_para->page_addr);
|
|
|
|
amlnand_release_device(aml_chip);
|
|
|
|
return ret;
|
|
}
|
|
static int block_modifybbt(struct amlnand_phydev *phydev, int value)
|
|
{
|
|
struct amlnand_chip *aml_chip = (struct amlnand_chip *)phydev->priv;
|
|
struct phydev_ops *devops = &(phydev->ops);
|
|
struct hw_controller *controller = &(aml_chip->controller);
|
|
struct chip_operation *operation = &(aml_chip->operation);
|
|
struct chip_ops_para *ops_para = &(aml_chip->ops_para);
|
|
u64 addr = 0;
|
|
int ret = 0;
|
|
|
|
if (devops->addr > phydev->size) {
|
|
aml_nand_msg("out of space and addr:");
|
|
aml_nand_msg("%llx phydev->offset:%llx phydev->size:%llx",
|
|
devops->addr,
|
|
phydev->offset,
|
|
phydev->size);
|
|
return -NAND_ARGUMENT_FAILURE;
|
|
}
|
|
|
|
amlnand_get_device(aml_chip, CHIP_READING);
|
|
|
|
/* clear ops_para here */
|
|
memset(ops_para, 0, sizeof(struct chip_ops_para));
|
|
addr = phydev->offset + devops->addr;
|
|
aml_nand_msg("addr = %lld", addr);
|
|
ops_para->option = phydev->option;
|
|
/* ops_para->page_addr = (int)(addr >> phydev->writesize_shift); */
|
|
if (ops_para->option & DEV_SLC_MODE)
|
|
ops_para->page_addr = amlnand_slc_addr_trs(phydev);
|
|
else
|
|
ops_para->page_addr = (int)(addr >> phydev->writesize_shift);
|
|
|
|
if (ops_para->option & DEV_SERIAL_CHIP_MODE) {
|
|
ops_para->chipnr =
|
|
(addr>>phydev->erasesize)%controller->chip_num;
|
|
controller->select_chip(controller, ops_para->chipnr);
|
|
}
|
|
|
|
ret = operation->blk_modify_bbt_chip_op(aml_chip, value);
|
|
if (ret < 0)
|
|
aml_nand_msg("nand mark bad failed at page %d",
|
|
ops_para->page_addr);
|
|
|
|
amlnand_release_device(aml_chip);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int update_bbt(struct amlnand_phydev *phydev)
|
|
{
|
|
struct amlnand_chip *aml_chip = (struct amlnand_chip *)phydev->priv;
|
|
struct phydev_ops *devops = &(phydev->ops);
|
|
struct hw_controller *controller = &(aml_chip->controller);
|
|
struct chip_operation *operation = &(aml_chip->operation);
|
|
struct chip_ops_para *ops_para = &(aml_chip->ops_para);
|
|
u64 addr = 0;
|
|
int ret = 0;
|
|
|
|
if (devops->addr > phydev->size) {
|
|
aml_nand_msg("out of space and addr:");
|
|
aml_nand_msg("%llx phydev->offset:%llx phydev->size:%llx",
|
|
devops->addr,
|
|
phydev->offset,
|
|
phydev->size);
|
|
return -NAND_ARGUMENT_FAILURE;
|
|
}
|
|
|
|
amlnand_get_device(aml_chip, CHIP_READING);
|
|
|
|
/* clear ops_para here */
|
|
memset(ops_para, 0, sizeof(struct chip_ops_para));
|
|
addr = phydev->offset + devops->addr;
|
|
ops_para->option = phydev->option;
|
|
/* ops_para->page_addr = (int)(addr >> phydev->writesize_shift); */
|
|
if (ops_para->option & DEV_SLC_MODE)
|
|
ops_para->page_addr = amlnand_slc_addr_trs(phydev);
|
|
else
|
|
ops_para->page_addr = (int)(addr >> phydev->writesize_shift);
|
|
|
|
if (ops_para->option & DEV_SERIAL_CHIP_MODE) {
|
|
ops_para->chipnr =
|
|
(addr>>phydev->erasesize)%controller->chip_num;
|
|
controller->select_chip(controller, ops_para->chipnr);
|
|
}
|
|
|
|
ret = operation->update_bbt_chip_op(aml_chip);
|
|
if (ret < 0)
|
|
aml_nand_msg("nand mark bad failed at page %d",
|
|
ops_para->page_addr);
|
|
|
|
amlnand_release_device(aml_chip);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int nand_test_block(struct amlnand_phydev *phydev)
|
|
{
|
|
struct amlnand_chip *aml_chip = (struct amlnand_chip *)phydev->priv;
|
|
struct phydev_ops *devops = &(phydev->ops);
|
|
struct hw_controller *controller = &(aml_chip->controller);
|
|
struct chip_operation *operation = &(aml_chip->operation);
|
|
struct chip_ops_para *ops_para = &(aml_chip->ops_para);
|
|
u64 addr = 0;
|
|
int ret = 0;
|
|
u32 tmp_addr = 0;
|
|
|
|
if (devops->addr > phydev->size) {
|
|
aml_nand_msg("out of space and addr:");
|
|
aml_nand_msg("%llx phydev->offset:%llx phydev->size:%llx",
|
|
devops->addr,
|
|
phydev->offset,
|
|
phydev->size);
|
|
return -NAND_ARGUMENT_FAILURE;
|
|
}
|
|
|
|
if (phydev->option & NAND_SHUT_DOWN) {
|
|
ml_nand_msg("nand is in shut down protect mod");
|
|
return NAND_SUCCESS;
|
|
}
|
|
|
|
amlnand_get_device(aml_chip, CHIP_READING);
|
|
|
|
/* clear ops_para here */
|
|
memset(ops_para, 0, sizeof(struct chip_ops_para));
|
|
addr = phydev->offset + devops->addr;
|
|
aml_nand_msg("addr = %lld", addr);
|
|
ops_para->option = phydev->option;
|
|
/* ops_para->page_addr = (int)(addr >> phydev->writesize_shift); */
|
|
if (ops_para->option & DEV_SLC_MODE)
|
|
ops_para->page_addr = amlnand_slc_addr_trs(phydev);
|
|
else
|
|
ops_para->page_addr = (int)(addr >> phydev->writesize_shift);
|
|
|
|
if (ops_para->option & DEV_SERIAL_CHIP_MODE) {
|
|
ops_para->chipnr =
|
|
(addr>>phydev->erasesize)%controller->chip_num;
|
|
controller->select_chip(controller, ops_para->chipnr);
|
|
}
|
|
|
|
tmp_addr = ops_para->page_addr;
|
|
ops_para->data_buf = devops->datbuf;
|
|
ret = operation->erase_block(aml_chip);
|
|
if (ret < 0) {
|
|
aml_nand_msg("nand erase_block failed at page %d",
|
|
ops_para->page_addr);
|
|
goto exit;
|
|
}
|
|
|
|
ops_para->page_addr = tmp_addr;
|
|
do {
|
|
ret = operation->write_page(aml_chip);
|
|
if (ret < 0) {
|
|
aml_nand_msg("nand erase_block failed at page %d",
|
|
ops_para->page_addr);
|
|
goto exit;
|
|
}
|
|
ops_para->page_addr += phydev->writesize;
|
|
} while (ops_para->page_addr >= devops->len);
|
|
|
|
ops_para->page_addr = tmp_addr;
|
|
do {
|
|
ret = operation->read_page(aml_chip);
|
|
if ((ops_para->ecc_err) || (ret < 0)) {
|
|
aml_nand_msg("phy read failed at devops->addr: %llx",
|
|
devops->addr);
|
|
goto exit;
|
|
}
|
|
ops_para->page_addr += phydev->writesize;
|
|
} while (ops_para->page_addr >= devops->len);
|
|
|
|
ops_para->page_addr = tmp_addr;
|
|
ret = operation->erase_block(aml_chip);
|
|
if (ret < 0) {
|
|
aml_nand_msg("nand erase_block failed at page %d",
|
|
ops_para->page_addr);
|
|
goto exit;
|
|
}
|
|
aml_nand_msg("nand test OK ");
|
|
|
|
exit:
|
|
amlnand_release_device(aml_chip);
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int phydev_init_erase(struct amlnand_chip *aml_chip)
|
|
{
|
|
int ret =0, percent=0,percent_complete = -1;
|
|
//uint64_t addr, off, size, chipsize, erase_addr, erase_len, erase_off;
|
|
uint64_t erase_addr, erase_len, erase_off;
|
|
struct amlnand_phydev *phydev = NULL;
|
|
struct phydev_ops *devops = NULL;
|
|
if (aml_chip->init_flag == NAND_BOOT_ERASE_PROTECT_CACHE) {
|
|
|
|
list_for_each_entry(phydev,&nphy_dev_list,list){
|
|
|
|
if ((!strncmp((char*)phydev->name, NAND_CODE_NAME, strlen((const char*)NAND_CODE_NAME))) \
|
|
||(!strncmp((char*)phydev->name, NAND_DATA_NAME, strlen((const char*)NAND_DATA_NAME)))){
|
|
devops = &phydev->ops;
|
|
erase_addr = 0;
|
|
erase_off = erase_addr;
|
|
erase_len = phydev->size;
|
|
percent=0;percent_complete = -1;
|
|
|
|
for (; erase_addr <erase_off + erase_len; erase_addr += phydev->erasesize) {
|
|
|
|
memset(devops, 0x0, sizeof(struct phydev_ops));
|
|
devops->addr = erase_addr;
|
|
devops->len = phydev->erasesize;
|
|
devops->mode = NAND_HW_ECC;
|
|
|
|
ret = phydev->block_isbad(phydev);
|
|
if (ret > 0) {
|
|
printf("\rSkipping bad block at 0x%08llx\n", erase_addr);
|
|
continue;
|
|
|
|
} else if (ret < 0) {
|
|
printf("\n:AMLNAND get bad block failed: ret=%d at addr=%llx\n",ret, erase_addr);
|
|
return -1;
|
|
}
|
|
|
|
ret = nand_erase(phydev);
|
|
if (ret < 0) {
|
|
printf("\nAMLNAND Erase failure: %d %llx\n", ret, erase_addr);
|
|
ret = phydev->block_markbad(phydev);
|
|
if (ret < 0)
|
|
printf("AMLNAND bad block mark failed: %llx\n", erase_addr);
|
|
continue;
|
|
}
|
|
|
|
percent = (erase_addr * 100) / (erase_off + erase_len);
|
|
if ((percent != percent_complete) && ((percent %10) == 0)) {
|
|
percent_complete = percent;
|
|
aml_nand_msg("nand erasing %d %% --%d %% complete",percent,percent+10);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void amldev_dumpinfo(struct amlnand_phydev *phydev)
|
|
{
|
|
/* flash info */
|
|
aml_nand_msg("device info");
|
|
aml_nand_msg("name:%s, offset:%llx, size:%llx, option:%x",
|
|
phydev->name,
|
|
phydev->offset,
|
|
phydev->size,
|
|
phydev->option);
|
|
aml_nand_msg("esize:%x,wsize:%x,oob:%x,esize_shift:%x,wsize_shift:%d",
|
|
phydev->erasesize,
|
|
phydev->writesize,
|
|
phydev->oobavail,
|
|
phydev->erasesize_shift,
|
|
phydev->writesize_shift);
|
|
}
|
|
|
|
#ifndef AML_NAND_UBOOT
|
|
ssize_t nand_page_read(struct class *class,
|
|
struct class_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
struct amlnand_phydev *phydev = NULL;
|
|
struct phydev_ops *devops = NULL;
|
|
|
|
u8 *datbuf, *p;
|
|
u64 offset , write_len;
|
|
loff_t off;
|
|
size_t ret;
|
|
int i;
|
|
|
|
phydev = container_of(class, struct amlnand_phydev, cls);
|
|
devops = &(phydev->ops);
|
|
|
|
ret = sscanf(buf, "%llx", &off);
|
|
|
|
datbuf = kmalloc(2*phydev->writesize, GFP_KERNEL);
|
|
if (!datbuf) {
|
|
aml_nand_msg("No memory for page buffer");
|
|
goto exit_erro;
|
|
}
|
|
p = datbuf;
|
|
|
|
memset(datbuf, 0x0, 2*phydev->writesize);
|
|
aml_nand_dbg("phydev->name =%s", phydev->name);
|
|
aml_nand_dbg("read page");
|
|
|
|
offset = 0;
|
|
write_len = phydev->writesize;
|
|
|
|
memset(devops, 0x0, sizeof(struct phydev_ops));
|
|
devops->addr = offset;
|
|
devops->len = phydev->writesize;
|
|
devops->datbuf = datbuf;
|
|
devops->oobbuf = NULL;
|
|
devops->mode = NAND_HW_ECC;
|
|
do {
|
|
if ((((u32)devops->addr % phydev->erasesize)) == 0) {
|
|
aml_nand_dbg("devops->addr = %llx", devops->addr);
|
|
ret = phydev->block_isbad(phydev);
|
|
if (ret > 0) {
|
|
aml_nand_dbg("\rSkipping bad block at %llx\n",
|
|
devops->addr);
|
|
devops->addr += phydev->erasesize;
|
|
continue;
|
|
} else if (ret < 0) {
|
|
aml_nand_dbg("get bad blk fail:");
|
|
aml_nand_dbg("ret=%d addr=%llx\n",
|
|
ret,
|
|
devops->addr);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
ret = phydev->read(phydev);
|
|
if (ret < 0)
|
|
aml_nand_dbg("nand read failed at %llx", devops->addr);
|
|
|
|
devops->addr += phydev->writesize;
|
|
datbuf += phydev->writesize;
|
|
} while (devops->addr < (offset + write_len));
|
|
|
|
i = 512;
|
|
aml_nand_dbg("read page");
|
|
while (i--) {
|
|
aml_nand_msg("\t%02x %02x %02x %02x %02x %02x %02x %02x",
|
|
p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
|
|
aml_nand_msg("\t%02x %02x %02x %02x %02x %02x %02x %02x",
|
|
p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
|
|
p += 16;
|
|
}
|
|
|
|
aml_nand_dbg("read page complete");
|
|
|
|
exit_erro:
|
|
kfree(datbuf);
|
|
|
|
return count;
|
|
}
|
|
|
|
void amlchip_resume(struct amlnand_phydev *phydev)
|
|
{
|
|
struct amlnand_chip *aml_chip = (struct amlnand_chip *)phydev->priv;
|
|
struct hw_controller *controller = &(aml_chip->controller);
|
|
struct chip_operation *operation = &(aml_chip->operation);
|
|
u8 onfi_features[4] = {0};
|
|
int i = 0, ret = 0;
|
|
|
|
if (!strncmp((char *)phydev->name,
|
|
NAND_CODE_NAME,
|
|
strlen((const char *)NAND_CODE_NAME))) {
|
|
nand_get_chip(aml_chip);
|
|
for (i = 0; i < controller->chip_num; i++) {
|
|
ret = controller->select_chip(controller, i);
|
|
if (ret < 0)
|
|
aml_nand_msg("select chip %d failed", i);
|
|
|
|
ret = nand_reset(aml_chip, i);
|
|
if (ret < 0)
|
|
aml_nand_dbg("reset failed %d", i);
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
/* if (aml_chip->state == CHIP_PM_SUSPENDED) */
|
|
amlnand_release_device(aml_chip);
|
|
aml_nand_dbg("nand resume entered\n");
|
|
}
|
|
return;
|
|
}
|
|
|
|
ssize_t nand_page_write(struct class *class,
|
|
struct class_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
struct amlnand_phydev *phydev = NULL;
|
|
struct phydev_ops *devops = NULL;
|
|
u_char *datbuf;
|
|
u64 offset , write_len;
|
|
loff_t off;
|
|
size_t ret;
|
|
|
|
phydev = container_of(class, struct amlnand_phydev, cls);
|
|
devops = &(phydev->ops);
|
|
|
|
aml_nand_dbg("phydev->name =%s", phydev->name);
|
|
aml_nand_dbg("write page");
|
|
ret = sscanf(buf, "%llx", &off);
|
|
datbuf = kmalloc(2*phydev->writesize, GFP_KERNEL);
|
|
if (!datbuf) {
|
|
aml_nand_msg("No memory for page buffer");
|
|
goto exit_erro;
|
|
}
|
|
memset(datbuf, 0xa5, 2*phydev->writesize);
|
|
|
|
offset = 0;
|
|
write_len = phydev->writesize;
|
|
|
|
memset(devops, 0x0, sizeof(struct phydev_ops));
|
|
devops->addr = offset;
|
|
devops->len = phydev->writesize;
|
|
devops->datbuf = datbuf;
|
|
devops->oobbuf = NULL;
|
|
devops->mode = NAND_HW_ECC;
|
|
do {
|
|
if ((((u32)devops->addr % phydev->erasesize)) == 0) {
|
|
aml_nand_dbg("devops->addr = %llx", devops->addr);
|
|
ret = phydev->block_isbad(phydev);
|
|
if (ret > 0) {
|
|
aml_nand_msg("\rSkipping bad block at %llx\n",
|
|
devops->addr);
|
|
devops->addr += phydev->erasesize;
|
|
continue;
|
|
} else if (ret < 0) {
|
|
aml_nand_msg("get bad blk fail:");
|
|
aml_nand_msg("ret=%d addr=%llx\n",
|
|
ret,
|
|
devops->addr);
|
|
return -1;
|
|
}
|
|
}
|
|
ret = phydev->write(phydev);
|
|
if (ret < 0)
|
|
aml_nand_msg("nand read failed at %llx", devops->addr);
|
|
|
|
devops->addr += phydev->writesize;
|
|
datbuf += phydev->writesize;
|
|
} while (devops->addr < (offset + write_len));
|
|
|
|
aml_nand_dbg("write page complete");
|
|
exit_erro:
|
|
kfree(datbuf);
|
|
return count;
|
|
}
|
|
|
|
/****
|
|
*verify_nand_page:
|
|
*read data immediately when write data, then compare;
|
|
*
|
|
*enbale this function: set the first bit of aml_chip->debug_flag to 1;
|
|
*****/
|
|
ssize_t verify_nand_page(struct class *class,
|
|
struct class_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
struct amlnand_phydev *phydev = NULL;
|
|
struct amlnand_chip *aml_chip = NULL;
|
|
u8 off;
|
|
size_t ret;
|
|
|
|
phydev = container_of(class, struct amlnand_phydev, cls);
|
|
aml_chip = (struct amlnand_chip *)phydev->priv;
|
|
|
|
ret = sscanf(buf, "%d", (int *)&off);
|
|
aml_chip->debug_flag = off;
|
|
|
|
aml_nand_msg("nand set aml_chip->debug_flag to %d",
|
|
aml_chip->debug_flag);
|
|
|
|
return count;
|
|
}
|
|
|
|
ssize_t dump_nand_page(struct class *class,
|
|
struct class_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
struct amlnand_phydev *phydev = NULL;
|
|
struct phydev_ops *devops = NULL;
|
|
u8 *datbuf, *p;
|
|
u64 offset , write_len;
|
|
u64 off;
|
|
size_t ret;
|
|
int i;
|
|
|
|
phydev = container_of(class, struct amlnand_phydev, cls);
|
|
devops = &(phydev->ops);
|
|
|
|
ret = sscanf(buf, "%llx", &off);
|
|
|
|
aml_nand_msg("dump page %llx", off);
|
|
datbuf = kmalloc(2*phydev->writesize, GFP_KERNEL);
|
|
if (!datbuf) {
|
|
aml_nand_msg("No memory for page buffer");
|
|
goto exit_erro;
|
|
}
|
|
p = datbuf;
|
|
memset(datbuf, 0x0, 2*phydev->writesize);
|
|
aml_nand_msg("phydev->name =%s", phydev->name);
|
|
|
|
offset = off;
|
|
write_len = phydev->writesize;
|
|
aml_nand_msg("offset %llx", offset);
|
|
|
|
memset(devops, 0x0, sizeof(struct phydev_ops));
|
|
devops->addr = offset;
|
|
devops->len = phydev->writesize;
|
|
devops->datbuf = datbuf;
|
|
devops->oobbuf = NULL;
|
|
devops->mode = NAND_SOFT_ECC;
|
|
do {
|
|
if ((((u32)devops->addr % phydev->erasesize)) == 0) {
|
|
aml_nand_dbg("devops->addr = %llx", devops->addr);
|
|
ret = phydev->block_isbad(phydev);
|
|
if (ret > 0) {
|
|
aml_nand_dbg("\rSkipping bad block at %llx\n",
|
|
devops->addr);
|
|
devops->addr += phydev->erasesize;
|
|
continue;
|
|
} else if (ret < 0) {
|
|
aml_nand_dbg("get bad blk fail:");
|
|
aml_nand_dbg("ret=%d addr=%llx\n",
|
|
ret,
|
|
devops->addr);
|
|
return -1;
|
|
}
|
|
}
|
|
ret = phydev->read(phydev);
|
|
if (ret < 0)
|
|
aml_nand_dbg("nand read failed at %llx", devops->addr);
|
|
|
|
devops->addr += phydev->writesize;
|
|
datbuf += phydev->writesize;
|
|
} while (devops->addr < (offset + write_len));
|
|
|
|
i = 512;
|
|
aml_nand_dbg("read page");
|
|
while (i--) {
|
|
aml_nand_msg("\t%02x %02x %02x %02x %02x %02x %02x %02x",
|
|
p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
|
|
aml_nand_msg("\t%02x %02x %02x %02x %02x %02x %02x %02x",
|
|
p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
|
|
p += 16;
|
|
}
|
|
|
|
aml_nand_dbg("read page complete");
|
|
exit_erro:
|
|
kfree(datbuf);
|
|
return count;
|
|
}
|
|
|
|
ssize_t show_nand_info(struct class *class,
|
|
struct class_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct amlnand_phydev *phydev = NULL;
|
|
aml_nand_dbg("phydev info:");
|
|
|
|
phydev = container_of(class, struct amlnand_phydev, cls);
|
|
|
|
amldev_dumpinfo(phydev);
|
|
return 0;
|
|
}
|
|
|
|
ssize_t show_bbt_table(struct class *class,
|
|
struct class_attribute *attr,
|
|
const char *buf,
|
|
size_t count)
|
|
{
|
|
struct amlnand_phydev *phydev = NULL;
|
|
struct amlnand_chip *aml_chip = NULL;
|
|
struct hw_controller *controller = NULL;
|
|
u16 *tmp_arr;
|
|
int start_block, chipnr, total_block;
|
|
int ret;
|
|
|
|
phydev = container_of(class, struct amlnand_phydev, cls);
|
|
aml_chip = (struct amlnand_chip *)phydev->priv;
|
|
controller = &(aml_chip->controller);
|
|
|
|
aml_nand_msg("show the block status !!!!");
|
|
|
|
ret = sscanf(buf, "%d", &total_block);
|
|
if (ret != 1)
|
|
return -EINVAL;
|
|
aml_nand_msg("set total_block %d", total_block);
|
|
|
|
for (chipnr = 0; chipnr < controller->chip_num; chipnr++) {
|
|
tmp_arr = &aml_chip->block_status->blk_status[chipnr][0];
|
|
for (start_block = total_block;
|
|
start_block < (total_block + 50);
|
|
start_block++)
|
|
aml_nand_msg(" aml_chip->block_status[%d][%d]=%d",
|
|
chipnr,
|
|
start_block,
|
|
tmp_arr[start_block]);
|
|
}
|
|
return count;
|
|
}
|
|
|
|
ssize_t change_test_sync_flag(struct class *class,
|
|
struct class_attribute *attr,
|
|
const char *buf,
|
|
size_t count)
|
|
{
|
|
int num;
|
|
int ret;
|
|
|
|
ret = sscanf(buf, "%x", &num);
|
|
if (ret != 1)
|
|
return -EINVAL;
|
|
aml_nand_msg("---------------test_flag=%d", num);
|
|
test_flag = num;
|
|
|
|
return count;
|
|
}
|
|
|
|
ssize_t show_amlnf_version_info(struct class *class,
|
|
struct class_attribute *attr,
|
|
char *buf)
|
|
{
|
|
aml_nand_dbg("show_nand_version_info v0.01");
|
|
return 0;
|
|
}
|
|
|
|
#endif /* AML_NAND_UBOOT */
|
|
|
|
static void show_phydev_info(void)
|
|
{
|
|
struct amlnand_phydev *phydev = NULL;
|
|
//struct amlnf_partition *partition = NULL;
|
|
//int i = 0;
|
|
char *config1, *config2;
|
|
|
|
list_for_each_entry(phydev, &nphy_dev_list, list) {
|
|
if (phydev == NULL)
|
|
break;
|
|
#if 0
|
|
for (i = 0; i < phydev->nr_partitions; i++) {
|
|
partition = &phydev->partitions[i];
|
|
aml_nand_msg("%s: name=%s,size=%llx",
|
|
phydev->name,
|
|
partition->name,
|
|
partition->size);
|
|
}
|
|
#endif
|
|
if (phydev->option & DEV_MULTI_CHIP_MODE)
|
|
config1 = "multi_chip";
|
|
else
|
|
config1 = "single_chip";
|
|
if (phydev->option & DEV_MULTI_PLANE_MODE)
|
|
config2 = "multi_plane";
|
|
else
|
|
config2 = "single_plane";
|
|
|
|
aml_nand_msg("%-10s: 0x%012llx-0x%012llx :partn=%d:%s %s",
|
|
phydev->name,
|
|
phydev->offset,
|
|
phydev->size,
|
|
phydev->nr_partitions,
|
|
config1,
|
|
config2);
|
|
}
|
|
}
|
|
|
|
|
|
/**********/
|
|
/* check list by name 1st, avoiding reinit */
|
|
int phydev_add_list(struct amlnand_phydev *new)
|
|
{
|
|
int ret = 0;
|
|
struct amlnand_phydev *dev;
|
|
list_for_each_entry(dev, &nphy_dev_list, list) {
|
|
//if (!(memcmp(new->name, dev->name, sizeof(new->name)))) {
|
|
if (!strncmp((char *)new->name,
|
|
(char *) dev->name,
|
|
strlen((char *)new->name))) {
|
|
ret = -1;
|
|
goto _out;
|
|
}
|
|
}
|
|
list_add_tail(&new->list, &nphy_dev_list);
|
|
|
|
_out:
|
|
return ret;
|
|
}
|
|
|
|
void show_phydev_list(void)
|
|
{
|
|
int i = 0;
|
|
struct amlnand_phydev *dev;
|
|
printk("%s\n", __func__);
|
|
list_for_each_entry(dev, &nphy_dev_list, list) {
|
|
printk("%d: %s\n", i , dev->name);
|
|
i++;
|
|
}
|
|
return;
|
|
}
|
|
|
|
static int init_phydev_ops(struct amlnand_phydev *phydev)
|
|
{
|
|
int ret = 0;
|
|
phydev->write = nand_write;
|
|
phydev->read = nand_read;
|
|
phydev->erase = nand_erase;
|
|
|
|
phydev->block_isbad = nand_block_isbad;
|
|
phydev->block_markbad = nand_block_markbad;
|
|
|
|
phydev->block_modifybbt = block_modifybbt;
|
|
phydev->update_bbt = update_bbt;
|
|
// phydev->phydev_test_block = nand_test_block;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*only init dev for u-boot*/
|
|
nand_page0_t *p_page0_buf = NULL;
|
|
int boot_dev_init(struct amlnand_chip *aml_chip)
|
|
{
|
|
int ret = 0;
|
|
struct amlnand_phydev *phydev = NULL;
|
|
struct nand_flash *flash = &(aml_chip->flash);
|
|
struct hw_controller *controller = &(aml_chip->controller);
|
|
|
|
PHYDEV_LINE
|
|
phydev = aml_nand_malloc(sizeof(struct amlnand_phydev));
|
|
if (phydev == NULL) {
|
|
aml_nand_msg("malloc failed need %x here",
|
|
(u32)(sizeof(struct amlnand_phydev)));
|
|
ret = -NAND_MALLOC_FAILURE;
|
|
goto _out;
|
|
}
|
|
memset(phydev, 0, sizeof(struct amlnand_phydev));
|
|
phydev->priv = aml_chip;
|
|
PHYDEV_LINE
|
|
phydev->writesize = flash->pagesize;
|
|
phydev->erasesize = flash->blocksize;
|
|
phydev->oobavail = controller->oobavail;
|
|
|
|
p_page0_buf = aml_nand_malloc(flash->pagesize);
|
|
if (p_page0_buf == NULL) {
|
|
aml_nand_msg("malloc failed0:%d", flash->pagesize);
|
|
return ret;
|
|
}
|
|
memset(p_page0_buf, 0, flash->pagesize);
|
|
PHYDEV_LINE
|
|
//fixme, phy name...
|
|
memcpy((char *)phydev->name, NAND_BOOT_NAME, strlen(NAND_BOOT_NAME));
|
|
PHYDEV_LINE
|
|
phydev->offset = 0;
|
|
phydev->size = (BOOT_COPY_NUM*BOOT_PAGES_PER_COPY);
|
|
phydev->size *= flash->pagesize;
|
|
/* phydev->size *= chip_num; */
|
|
PHYDEV_LINE
|
|
phydev->writesize_shift = ffs(phydev->writesize) - 1;
|
|
phydev->erasesize_shift = ffs(phydev->erasesize) - 1;
|
|
phydev->writesize_mask =
|
|
(phydev->size>>phydev->writesize_shift) - 1;
|
|
PHYDEV_LINE
|
|
init_phydev_ops(phydev);
|
|
PHYDEV_LINE
|
|
if ((flash->option & NAND_CHIP_SLC_MODE)
|
|
&& (!(phydev->option & DEV_MULTI_PLANE_MODE))) {
|
|
phydev->option |= DEV_SLC_MODE;
|
|
phydev->erasesize >>= 1;
|
|
phydev->erasesize_shift = ffs(phydev->erasesize) - 1;
|
|
phydev->size >>= 1;
|
|
aml_nand_msg(" DEV %s enable SLC mode", phydev->name);
|
|
}
|
|
PHYDEV_LINE
|
|
ret = phydev_add_list(phydev);
|
|
if (ret) {
|
|
kfree(phydev);
|
|
}
|
|
|
|
show_phydev_list();
|
|
_out:
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/******
|
|
*nand chip usage
|
|
* all dev size should
|
|
* uboot(nand boot) reserved code data
|
|
*/
|
|
extern struct list_head nphy_dev_list;
|
|
|
|
int aml_alloc_phydev(struct amlnand_phydev **phydev_pp,
|
|
struct amlnand_chip *aml_chip,
|
|
struct dev_para **dev_para,
|
|
int dev_idx)
|
|
{
|
|
int ret = 0;
|
|
struct hw_controller *controller = &(aml_chip->controller);
|
|
struct nand_flash *flash = &(aml_chip->flash);
|
|
struct amlnand_phydev* phydev_p = NULL;
|
|
|
|
*phydev_pp = aml_nand_malloc(sizeof(struct amlnand_phydev));
|
|
if (*phydev_pp == NULL) {
|
|
aml_nand_msg("malloc failed need %lx here",
|
|
sizeof(struct amlnand_phydev));
|
|
ret = -NAND_MALLOC_FAILURE;
|
|
return ret;
|
|
}
|
|
phydev_p = *phydev_pp;
|
|
memset(phydev_p, 0, sizeof(struct amlnand_phydev));
|
|
phydev_p->priv = aml_chip;
|
|
|
|
*dev_para = &aml_chip->config_ptr->dev_para[dev_idx];
|
|
memcpy((void *)&phydev_p->name, &(*dev_para)->name, MAX_DEVICE_NAME_LEN*sizeof(char));
|
|
/*set default parameter*/
|
|
phydev_p->writesize = flash->pagesize;
|
|
phydev_p->erasesize = flash->blocksize;
|
|
phydev_p->oobavail = controller->oobavail;
|
|
phydev_p->writesize_shift = ffs(phydev_p->writesize) - 1;
|
|
phydev_p->erasesize_shift = ffs(phydev_p->erasesize) - 1;
|
|
phydev_p->write = nand_write;
|
|
phydev_p->read = nand_read;
|
|
phydev_p->erase = nand_erase;
|
|
phydev_p->block_isbad = nand_block_isbad;
|
|
phydev_p->block_markbad = nand_block_markbad;
|
|
phydev_p->block_modifybbt = block_modifybbt;
|
|
phydev_p->update_bbt = update_bbt;
|
|
phydev_p->phydev_test_block = nand_test_block;
|
|
|
|
return ret;
|
|
}
|
|
|
|
int amlnand_phydev_init(struct amlnand_chip *aml_chip)
|
|
{
|
|
struct amlnand_phydev *phydev = NULL, *phydev_pre = NULL;
|
|
struct nand_flash *flash = &(aml_chip->flash);
|
|
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_config *config = aml_chip->config_ptr;
|
|
struct nand_arg_info phy_part = aml_chip->nand_phy_partition;
|
|
struct dev_para *dev_para = NULL;
|
|
struct amlnf_partition *partition = NULL;
|
|
u64 offset = 0, dev_size = 0, chip_size = 0, phydev_pre_size = 0;
|
|
u32 start_blk, total_blk, tmp_write_shift, tmp_erase_shift;
|
|
u32 tmp_offset = 0, tmp_blk = 0, pages_per_blk;
|
|
u8 boot_flag = 0, plane_num = 1, chip_num;
|
|
int i, j, k, ret = 0;
|
|
u32 adjust_blk = 0, cpu_type;
|
|
u64 tmp_value;
|
|
#if 0
|
|
/*when opened , need be free bad_blk; */
|
|
u64 *bad_blk = NULL;
|
|
bad_blk = aml_nand_malloc(128*sizeof(u64));
|
|
if (bad_blk == NULL) {
|
|
aml_nand_msg("malloc failed");
|
|
ret = -NAND_MALLOC_FAILURE;
|
|
goto exit_error0;
|
|
}
|
|
memset(bad_blk, 0, 128*sizeof(u64));
|
|
#endif
|
|
|
|
if (flash->option & NAND_MULTI_PLANE_MODE)
|
|
plane_num = 2;
|
|
else
|
|
plane_num = 1;
|
|
|
|
/* #ifndef AML_NAND_UBOOT */
|
|
/* INIT_LIST_HEAD (&nphy_dev_list); */
|
|
/* #endif */
|
|
|
|
chip_num = controller->chip_num;
|
|
|
|
chip_size = (flash->chipsize*chip_num);
|
|
chip_size = chip_size << 20;
|
|
if (config->dev_num == 0) {
|
|
aml_nand_msg("config get invalid: config->dev_num =%d",
|
|
config->dev_num);
|
|
ret = -NAND_FAILED;
|
|
goto exit_error0;
|
|
}
|
|
PHYDEV_LINE
|
|
/* if phy-partition saved is valid,
|
|
no need to calc and care about driver version.
|
|
*/
|
|
if (phy_part.arg_valid == 1) {
|
|
for (i = 0; i < config->dev_num; i++) {
|
|
ret = aml_alloc_phydev(&phydev, aml_chip, &dev_para, i);
|
|
phydev->offset =
|
|
aml_chip->phy_part_ptr->partition[i].phy_off;
|
|
phydev->size =
|
|
aml_chip->phy_part_ptr->partition[i].phy_len;
|
|
phydev->nr_partitions = dev_para->nr_partitions;
|
|
phydev->partitions = dev_para->partitions;
|
|
if ((dev_para->option & DEV_MULTI_CHIP_MODE)
|
|
&& (controller->chip_num > 1)) {
|
|
phydev->option |= DEV_MULTI_CHIP_MODE;
|
|
phydev->writesize *= controller->chip_num;
|
|
phydev->erasesize *= controller->chip_num;
|
|
phydev->oobavail *= controller->chip_num;
|
|
}
|
|
|
|
if ((dev_para->option & DEV_MULTI_PLANE_MODE)
|
|
&& (flash->option & NAND_MULTI_PLANE_MODE)) {
|
|
phydev->option |= DEV_MULTI_PLANE_MODE;
|
|
phydev->writesize <<= 1;
|
|
phydev->erasesize <<= 1;
|
|
phydev->oobavail <<= 1;
|
|
}
|
|
|
|
if ((dev_para->option & DEV_USE_SHAREPAGE_MODE)
|
|
&& (flash->option & NAND_USE_SHAREPAGE_MODE)) {
|
|
phydev->option |= DEV_USE_SHAREPAGE_MODE;
|
|
phydev->writesize <<= 1;
|
|
phydev->oobavail <<= 1;
|
|
}
|
|
phydev->writesize_shift = ffs(phydev->writesize) - 1;
|
|
phydev->erasesize_shift = ffs(phydev->erasesize) - 1;
|
|
printk("%s,%d,phydev->offset=%llx,phydev->size=%llx\n",
|
|
__func__,
|
|
__LINE__,
|
|
phydev->offset,phydev->size);
|
|
phydev_add_list(phydev);
|
|
}
|
|
} else {
|
|
for (i = 0; i < config->dev_num; i++) {
|
|
ret = aml_alloc_phydev(&phydev, aml_chip, &dev_para, i);
|
|
dev_size = 0;
|
|
|
|
aml_nand_msg("dev num: %d, name: %s",i,phydev->name);
|
|
tmp_write_shift = ffs(flash->pagesize) - 1;
|
|
tmp_erase_shift = ffs(flash->blocksize) - 1;
|
|
pages_per_blk = (1 << (tmp_erase_shift - tmp_write_shift));
|
|
|
|
/* set partitions and calculate dev size */
|
|
if (dev_para->nr_partitions) {
|
|
phydev->nr_partitions = dev_para->nr_partitions;
|
|
phydev->partitions = dev_para->partitions;
|
|
for (k = 0; k < dev_para->nr_partitions; k++) {
|
|
partition = &(dev_para->partitions[k]);
|
|
aml_nand_msg("partition[%d]-name:%s,size:%llx",
|
|
k,
|
|
partition->name,
|
|
partition->size);
|
|
}
|
|
PHYDEV_LINE
|
|
if (i != (config->dev_num - 1)) {
|
|
for (j = 0; j < dev_para->nr_partitions; j++) {
|
|
partition = &(dev_para->partitions[j]);
|
|
dev_size += partition->size;
|
|
}
|
|
if (!is_phydev_off_adjust()) { //no go
|
|
int adjust_shift =
|
|
ffs(ADJUST_SIZE_NFTL) - 1;
|
|
|
|
dev_size = dev_size
|
|
+ (dev_size >> adjust_shift);
|
|
}
|
|
/*
|
|
adjust dev_size for nftl
|
|
dev_size = dev_size \
|
|
+ ((u64)(dev_size)/(u64)(ADJUST_SIZE_NFTL));
|
|
*/
|
|
} else {
|
|
if ((phydev_pre->option & DEV_SLC_MODE)
|
|
&& (flash->option & NAND_CHIP_SLC_MODE)
|
|
&& (!(phydev->option & DEV_MULTI_PLANE_MODE)))
|
|
phydev_pre_size = phydev_pre->size << 1;
|
|
else
|
|
phydev_pre_size = phydev_pre->size;
|
|
|
|
dev_size = chip_size - phydev_pre->offset
|
|
- phydev_pre_size;
|
|
}
|
|
} else {
|
|
dev_size = dev_para->size;/*0*/
|
|
aml_nand_msg(" dev size: 0x%llx",dev_size);
|
|
}
|
|
|
|
if ((dev_para->option & DEV_SLC_MODE) &&
|
|
(flash->option & NAND_CHIP_SLC_MODE) &&
|
|
(!(dev_para->option & DEV_MULTI_PLANE_MODE))) {
|
|
dev_size <<= 1;
|
|
aml_nand_msg("DEV enable SLC mode");
|
|
}
|
|
|
|
if (!strncmp((char *)phydev->name,
|
|
NAND_BOOT_NAME,
|
|
strlen((const char *)NAND_BOOT_NAME))) {
|
|
boot_flag = 1;
|
|
|
|
phydev->offset = 0;
|
|
phydev->size = (BOOT_COPY_NUM * BOOT_PAGES_PER_COPY);
|
|
//printk("----------%llx\n", phydev->size);
|
|
phydev->size *= flash->pagesize;
|
|
//printk("----------%llx\n", phydev->size);
|
|
/* phydev->size *= chip_num; */
|
|
|
|
phydev->writesize_shift = ffs(phydev->writesize) - 1;
|
|
phydev->erasesize_shift = ffs(phydev->erasesize) - 1;
|
|
phydev->writesize_mask =
|
|
(phydev->size>>phydev->writesize_shift) - 1;
|
|
|
|
tmp_blk = phydev->size >> phydev->erasesize_shift;
|
|
} else { /* not boot phydev[nftl] */
|
|
if ((dev_para->option & DEV_MULTI_CHIP_MODE)
|
|
&& (chip_num > 1)) {
|
|
phydev->option |= DEV_MULTI_CHIP_MODE;
|
|
phydev->writesize *= chip_num;
|
|
phydev->erasesize *= chip_num;
|
|
phydev->oobavail *= chip_num;
|
|
}
|
|
|
|
if ((dev_para->option & DEV_MULTI_PLANE_MODE)
|
|
&& (flash->option & NAND_MULTI_PLANE_MODE)) {
|
|
phydev->option |= DEV_MULTI_PLANE_MODE;
|
|
phydev->writesize <<= 1;
|
|
phydev->erasesize <<= 1;
|
|
phydev->oobavail <<= 1;
|
|
}
|
|
if ((dev_para->option & DEV_USE_SHAREPAGE_MODE)
|
|
&& (flash->option & NAND_USE_SHAREPAGE_MODE)) {
|
|
phydev->option |= DEV_USE_SHAREPAGE_MODE;
|
|
phydev->writesize <<= 1;
|
|
phydev->oobavail <<= 1;
|
|
}
|
|
|
|
phydev->writesize_shift = ffs(phydev->writesize) - 1;
|
|
phydev->erasesize_shift = ffs(phydev->erasesize) - 1;
|
|
|
|
/****cal phydev off**/
|
|
if (((boot_flag == 1) && (i == 1))
|
|
|| ((boot_flag == 0) && (i == 0))) {
|
|
offset = start_blk = 0;
|
|
total_blk = RESERVED_BLOCK_CNT;
|
|
memset(ops_para, 0,
|
|
sizeof(struct chip_ops_para));
|
|
do {
|
|
tmp_value = offset>>tmp_erase_shift;
|
|
ops_para->chipnr =
|
|
((u32)tmp_value) % chip_num;
|
|
tmp_value += tmp_blk;
|
|
tmp_value /= chip_num;
|
|
tmp_value += tmp_blk - tmp_blk/chip_num;
|
|
tmp_value *= pages_per_blk;
|
|
ops_para->page_addr = (u32)tmp_value;
|
|
ret = operation->block_isbad(aml_chip);
|
|
if (ret == NAND_BLOCK_FACTORY_BAD) {
|
|
offset += flash->blocksize;
|
|
continue;
|
|
}
|
|
start_blk++;
|
|
offset += flash->blocksize;
|
|
} while (start_blk < total_blk);
|
|
/*reserve_end blk*/
|
|
tmp_value = (offset>>tmp_erase_shift) - 1;
|
|
tmp_value /= chip_num*plane_num;
|
|
tmp_value += 1;
|
|
total_blk = tmp_value * chip_num * plane_num;
|
|
aml_nand_msg("total_blk =%d", total_blk);
|
|
aml_nand_msg(" phydev_pre->size =%llx",
|
|
phydev_pre->size);
|
|
if (phydev_pre == NULL)
|
|
phydev->offset =
|
|
total_blk * flash->blocksize;
|
|
else
|
|
phydev->offset =
|
|
total_blk * flash->blocksize +
|
|
phydev_pre->size;
|
|
aml_nand_msg("phydev->offset =%llx",
|
|
phydev->offset);
|
|
} else {
|
|
if ((!(phydev->option & DEV_MULTI_PLANE_MODE)
|
|
&& (flash->option & NAND_CHIP_SLC_MODE)
|
|
&& (phydev_pre->option & DEV_SLC_MODE)))
|
|
phydev_pre_size = phydev_pre->size << 1;
|
|
else
|
|
phydev_pre_size = phydev_pre->size;
|
|
|
|
phydev->offset =
|
|
phydev_pre->offset + phydev_pre_size;
|
|
}
|
|
|
|
/****cal phydev size**/
|
|
if (i != (config->dev_num - 1)) {
|
|
start_blk = 0;
|
|
if (((boot_flag == 1) && (i == 1))
|
|
|| ((boot_flag == 0) && (i == 0)))
|
|
offset = phydev->offset;
|
|
else
|
|
offset = phydev_pre->offset +
|
|
phydev_pre->size;
|
|
tmp_offset = offset;
|
|
|
|
aml_nand_dbg("offset = %llx, %llx",
|
|
offset,
|
|
tmp_erase_shift);
|
|
aml_nand_dbg("adjust phy offset: %d",
|
|
ADJUST_BLOCK_NUM);
|
|
total_blk =
|
|
dev_size >> phydev->erasesize_shift;
|
|
|
|
cpu_type = get_cpu_type();
|
|
/* for compatible with history. */
|
|
if ((cpu_type == MESON_CPU_MAJOR_ID_M8)
|
|
&& (phydev->erasesize < 0x400000))
|
|
adjust_blk = 0;
|
|
else
|
|
adjust_blk = ADJUST_PART_SIZE - 1;
|
|
|
|
/*************adjust****************/
|
|
tmp_value = total_blk;
|
|
tmp_value += adjust_blk;
|
|
tmp_value /= ADJUST_PART_SIZE;
|
|
tmp_value += total_blk + ADJUST_BLOCK_NUM;
|
|
total_blk = tmp_value;
|
|
|
|
memset(ops_para,
|
|
0,
|
|
sizeof(struct chip_ops_para));
|
|
ops_para->option = phydev->option;
|
|
|
|
do {
|
|
tmp_value =
|
|
offset>>phydev->writesize_shift;
|
|
ops_para->page_addr = (u32)(tmp_value);
|
|
ret = operation->block_isbad(aml_chip);
|
|
if (ret == NAND_BLOCK_FACTORY_BAD) {
|
|
offset += phydev->erasesize;
|
|
continue;
|
|
}
|
|
start_blk++;
|
|
offset += phydev->erasesize;
|
|
} while (start_blk < total_blk);
|
|
|
|
tmp_value = offset - tmp_offset;
|
|
tmp_value =
|
|
(tmp_value >> phydev->erasesize_shift);
|
|
tmp_value -= 1;
|
|
tmp_value /= chip_num * plane_num;
|
|
tmp_value += 1;
|
|
total_blk = tmp_value * chip_num * plane_num;
|
|
//printk("%s() %d, total_blk 0x%x, erase_size 0x%x\n", __func__, __LINE__, total_blk, phydev->erasesize);
|
|
phydev->size = ((u64)total_blk *
|
|
(u64)phydev->erasesize);
|
|
} else
|
|
phydev->size = dev_size;
|
|
|
|
if ((phydev->offset + phydev->size) > chip_size) {
|
|
aml_nand_msg("nand dev size is out of space");
|
|
ret = -NAND_FAILED;
|
|
goto exit_error0;
|
|
}
|
|
#ifndef AML_NAND_UBOOT
|
|
phydev->suspend = phydev_suspend;
|
|
phydev->resume = phydev_resume;
|
|
#endif
|
|
}
|
|
|
|
phydev_pre = phydev;
|
|
|
|
if ((dev_para->option & DEV_SLC_MODE)
|
|
&& (flash->option & NAND_CHIP_SLC_MODE)
|
|
&& (!(phydev->option & DEV_MULTI_PLANE_MODE))) {
|
|
phydev->option |= DEV_SLC_MODE;
|
|
phydev->erasesize >>= 1;
|
|
phydev->erasesize_shift = ffs(phydev->erasesize) - 1;
|
|
phydev->size >>= 1;
|
|
aml_nand_msg(" DEV %s enable SLC mode", phydev->name);
|
|
}
|
|
|
|
aml_chip->phy_part_ptr->partition[i].phy_off = phydev->offset;
|
|
aml_chip->phy_part_ptr->partition[i].phy_len = phydev->size;
|
|
if (dev_para->nr_partitions) {
|
|
for (k = 0; k < dev_para->nr_partitions; k++) {
|
|
partition = &(dev_para->partitions[k]);
|
|
aml_chip->phy_part_ptr->partition[i].logic_len +=
|
|
partition->size;
|
|
}
|
|
aml_nand_msg("logic_len: 0x%llx",aml_chip->phy_part_ptr->partition[i].logic_len);
|
|
} else {
|
|
aml_chip->phy_part_ptr->partition[i].logic_len = dev_para->size;
|
|
aml_nand_msg("logic_len: 0x%llx",dev_para->size);
|
|
}
|
|
|
|
/* fixme, add new physic device */
|
|
//list_add_tail(&phydev->list, &nphy_dev_list);
|
|
phydev_add_list(phydev);
|
|
|
|
#if (AML_NAND_DBG)
|
|
amldev_dumpinfo(phydev);
|
|
#endif
|
|
aml_nand_dbg("####: %s :phydev->offset = %llx",
|
|
phydev_pre->name, phydev_pre->offset);
|
|
aml_nand_dbg("####: %s :phydev->size = %llx",
|
|
phydev_pre->name, phydev_pre->size);
|
|
aml_nand_dbg("####: %s :phydev->writesize = %x",
|
|
phydev_pre->name, phydev_pre->writesize);
|
|
aml_nand_dbg("####: %s :phydev->erasesize = %x",
|
|
phydev_pre->name, phydev_pre->erasesize);
|
|
|
|
}
|
|
/*save phy partitions to nand*/
|
|
aml_chip->phy_part_ptr->dev_num = config->dev_num;
|
|
aml_chip->phy_part_ptr->crc =
|
|
aml_info_checksum((u8 *)aml_chip->phy_part_ptr->partition,
|
|
(MAX_DEVICE_NUM * sizeof(struct _phy_partition)));
|
|
ret = amlnand_save_info_by_name(aml_chip,
|
|
(u8 *)&(aml_chip->nand_phy_partition),
|
|
(u8 *)aml_chip->phy_part_ptr,
|
|
(u8 *)PHY_PARTITION_HEAD_MAGIC,
|
|
sizeof(struct phy_partition_info));
|
|
if (ret < 0) {
|
|
aml_nand_msg("save nand phy partition failed and ret:%d",ret);
|
|
goto exit_error0;
|
|
}
|
|
}
|
|
|
|
show_phydev_info();
|
|
PHYDEV_LINE
|
|
#if 0
|
|
phydev = NULL;
|
|
list_for_each_entry(phydev, &nphy_dev_list, list) {
|
|
if (phydev == NULL)
|
|
break;
|
|
|
|
aml_nand_dbg("-----------------------------\n");
|
|
aml_nand_dbg("name:%s,offset:%llx,size:%llx,option:%x",
|
|
phydev->name,
|
|
phydev->offset,
|
|
phydev->size,
|
|
phydev->option);
|
|
aml_nand_dbg("es:%x,ws:%x,oob:%x,eshift:%x,wshift:%d",
|
|
phydev->erasesize,
|
|
phydev->writesize,
|
|
phydev->oobavail,
|
|
phydev->erasesize_shift,
|
|
phydev->writesize_shift);
|
|
aml_nand_dbg(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
|
|
relative_offset = 0;
|
|
bad_blk_cnt = 0;
|
|
devops = &(phydev->ops);
|
|
memset(bad_blk, 0, 128*sizeof(u64));
|
|
do {
|
|
memset(devops, 0x0, sizeof(struct phydev_ops));
|
|
memset(devops, 0x0, sizeof(struct phydev_ops));
|
|
devops->addr = relative_offset;
|
|
devops->len = phydev->erasesize;
|
|
devops->datbuf = NULL;
|
|
devops->oobbuf = NULL;
|
|
devops->mode = NAND_HW_ECC;
|
|
ret = nand_block_isbad(phydev);
|
|
if (ret == NAND_BLOCK_USED_BAD) {
|
|
if (bad_blk_cnt < 128) {
|
|
bad_blk[bad_blk_cnt] = relative_offset;
|
|
bad_blk_cnt++;
|
|
}
|
|
}
|
|
relative_offset += phydev->erasesize;
|
|
} while (relative_offset < phydev->size);
|
|
|
|
aml_nand_msg("(%s) bad blks %d", phydev->name, bad_blk_cnt);
|
|
|
|
if ((bad_blk_cnt * 32 >
|
|
(phydev->size >> phydev->erasesize_shift)) ||
|
|
(bad_blk_cnt > 10)) {
|
|
aml_nand_dbg("Too many new bad blks,try to repair..\n");
|
|
/*
|
|
ret = aml_repair_bbt(phydev,bad_blk,bad_blk_cnt);
|
|
*/
|
|
}
|
|
}
|
|
kfree(bad_blk);
|
|
#endif
|
|
#ifdef AML_NAND_UBOOT
|
|
/************fixed by liuxianjun**********
|
|
if (aml_chip->init_flag == NAND_BOOT_ERASE_PROTECT_CACHE) {
|
|
ret = phydev_init_erase(aml_chip);
|
|
if (ret < 0) {
|
|
aml_nand_msg("amlnand_phydev_init : phydev_init_erase failed");
|
|
}
|
|
}
|
|
**************************/
|
|
#endif /* AML_NAND_UBOOT */
|
|
return NAND_SUCCESS;
|
|
|
|
exit_error0:
|
|
return ret;
|
|
}
|
|
|
|
#ifdef AML_NAND_UBOOT
|
|
void amlnf_phy_exit(void)
|
|
{
|
|
|
|
struct amlnand_phydev *phydev = NULL;
|
|
struct amlnand_chip *aml_chip = NULL;
|
|
//struct list_head *entry;
|
|
int time= 0;
|
|
list_for_each_entry(phydev,&nphy_dev_list,list){
|
|
|
|
if (phydev) {
|
|
if (time == 0) {
|
|
aml_chip = (struct amlnand_chip *)phydev->priv;
|
|
if (aml_chip) {
|
|
if (aml_chip->block_status) {
|
|
kfree(aml_chip->block_status);
|
|
aml_chip->block_status = NULL;
|
|
}
|
|
if (aml_chip->user_page_buf) {
|
|
kfree(aml_chip->user_page_buf);
|
|
aml_chip->user_page_buf = NULL;
|
|
}
|
|
if (aml_chip->user_oob_buf) {
|
|
kfree(aml_chip->user_oob_buf);
|
|
aml_chip->user_oob_buf = NULL;
|
|
}
|
|
if (aml_chip->shipped_bbt_ptr) {
|
|
kfree(aml_chip->shipped_bbt_ptr);
|
|
aml_chip->shipped_bbt_ptr = NULL;
|
|
}
|
|
if (aml_chip->config_ptr) {
|
|
kfree(aml_chip->config_ptr);
|
|
aml_chip->config_ptr = NULL;
|
|
}
|
|
}
|
|
time++;
|
|
}
|
|
}
|
|
}
|
|
|
|
list_del_init(&nphy_dev_list);
|
|
|
|
}
|
|
#endif
|
|
|