blob: eecd81159c8826aa03acf633e726c557a1796be8 [file] [log] [blame]
/*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
* *
This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* *
This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
* *
You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
* *
Description:
*/
#include <common.h>
#include <environment.h>
#include <nand.h>
#include <asm/io.h>
#include <malloc.h>
#include <linux/err.h>
#include <linux/bitops.h>
#include <asm/cache.h>
// #include <asm/reboot.h>
#include <asm/arch/clock.h>
#include "aml_mtd.h"
extern struct hw_controller *controller;
extern uint8_t nand_boot_flag;
/*external defined variable*/
extern int info_disprotect;
/* protect flag inside */
static int rsv_protect = 1;
static inline void _aml_rsv_disprotect(void)
{
rsv_protect = 0;
}
static inline void _aml_rsv_protect(void)
{
rsv_protect = 1;
}
static inline int _aml_rsv_isprotect(void)
{
return rsv_protect;
}
//#define CONFIG_DBG_BITMAP 1
struct rsv_info {
char name[8];
struct aml_nandrsv_info_t rsv_info;
unsigned int blocks;
unsigned int size;
};
#define BBT_INFO_INDEX 0
#define ENV_INFO_INDEX 1
#define KEY_INFO_INDEX 2
#define DTB_INFO_INDEX 3
#define DDR_INFO_INDEX 4
#define INFO_DATA(n, b, s) { .name = (n), .blocks = (b), .size = (s) }
static struct rsv_info info[] = {
INFO_DATA(BBT_NAND_MAGIC, NAND_BBT_BLOCK_NUM, 0),
#ifndef CONFIG_ENV_IS_IN_NAND
INFO_DATA(ENV_NAND_MAGIC, NAND_ENV_BLOCK_NUM, CONFIG_ENV_SIZE),
#endif
INFO_DATA(KEY_NAND_MAGIC, NAND_KEY_BLOCK_NUM, 0),
INFO_DATA(DTB_NAND_MAGIC, NAND_DTB_BLOCK_NUM, 0),
INFO_DATA(DDR_NAND_MAGIC, NAND_DDR_BLOCK_NUM, 2048),
};
static struct free_node_t *get_free_node(struct mtd_info *mtd)
{
struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd);
unsigned int index;
#ifdef CONFIG_DBG_BITMAP
printk("%s %d: bitmap=%llx\n", __func__, __LINE__,
aml_chip->freeNodeBitmask);
#endif
index = find_first_zero_bit((void *)&aml_chip->freeNodeBitmask, 64);
if (index >= RESERVED_BLOCK_NUM) {
printk("%s %d: index=%d is greater than max! error",
__func__, __LINE__, index);
return NULL;
}
test_and_set_bit(index, (void *)&aml_chip->freeNodeBitmask);
#ifdef CONFIG_DBG_BITMAP
printk("%s %d: bitmap=%llx\n", __func__, __LINE__,
aml_chip->freeNodeBitmask);
#endif
return aml_chip->free_node[index];
}
static void release_free_node(struct mtd_info *mtd,
struct free_node_t *free_node)
{
struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd);
unsigned int index_save = free_node->index;
printk("%s %d: bitmap=%llx\n", __func__, __LINE__,
aml_chip->freeNodeBitmask);
if (free_node->index > RESERVED_BLOCK_NUM)
printk("%s %d: index=%d is greater than max! error",
__func__, __LINE__, free_node->index);
test_and_clear_bit(free_node->index,
(void *)&aml_chip->freeNodeBitmask);
/*memset zero to protect from dead-loop*/
memset(free_node, 0, sizeof(struct free_node_t));
free_node->index = index_save;
printk("%s %d: bitmap=%llx\n", __func__, __LINE__,
aml_chip->freeNodeBitmask);
}
int aml_nand_rsv_erase_protect(struct mtd_info *mtd, unsigned int block_addr)
{
struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd);
/* skip protect if we disprotect it in porpose */
if (!_aml_rsv_isprotect())
return 0;
if (aml_chip->aml_nandkey_info != NULL) {
if (aml_chip->aml_nandkey_info->valid)
if ((!(info_disprotect & DISPROTECT_KEY))
&& ((block_addr >= aml_chip->aml_nandkey_info->start_block)
&& (block_addr < aml_chip->aml_nandkey_info->end_block)))
return -1; /*need skip key blocks*/
}
if (aml_chip->aml_nandbbt_info != NULL) {
if (aml_chip->aml_nandbbt_info->valid)
if ((block_addr >= aml_chip->aml_nandbbt_info->start_block)
&&(block_addr < aml_chip->aml_nandbbt_info->end_block))
return -1; /*need skip bbt blocks*/
}
return 0;
}
/* fixme, mxic's bad block identify is not checked yet! */
//only read bad block labeled ops
int aml_nand_scan_shipped_bbt(struct mtd_info *mtd)
{
struct nand_chip * chip = mtd->priv;
struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd);
unsigned char *data_buf;
int32_t read_cnt, page, pages_per_blk;
loff_t addr, offset;
int start_blk =0, total_blk =0,i, j, bad_blk_cnt =0, phys_erase_shift;
int realpage, col0_data=0, col0_oob=0, valid_page_num = 1;
int col_data_sandisk[6] = {0}, bad_sandisk_flag=0;
if (!fls(mtd->erasesize)) {
printk("%s, %d !!!!ERROR, bit shift is zero\n",
__func__,__LINE__);
return -ENOMEM;
}
phys_erase_shift = fls(mtd->erasesize) - 1;
chip->pagebuf = -1;
pages_per_blk = (1 << (chip->phys_erase_shift - chip->page_shift));
data_buf = malloc(mtd->writesize);
if (data_buf == NULL) {
printk("%s %d malloc failed\n",__func__,__LINE__);
return -ENOMEM;
}
memset(data_buf, 0, mtd->writesize);
/*need scan factory bad block in bootloader area*/
start_blk = 0;
total_blk = (int)(mtd->size >> phys_erase_shift);
/* fixme, need check the total block number avoid mtd->size was changed outside! */
printk("scaning flash total block %d\n", total_blk);
do {
offset = mtd->erasesize;
offset *= start_blk;
for (i=0; i < controller->chip_num; i++) {
//if (aml_chip->valid_chip[i]) {
for (read_cnt = 0; read_cnt < 3; read_cnt++) {
if (read_cnt == 2) {
if (aml_chip->mfr_type == NAND_MFR_AMD)
addr = offset + mtd->writesize;
else
break;
} else {
if (aml_chip->mfr_type == NAND_MFR_SANDISK) {
addr = offset + read_cnt*mtd->writesize;
} else
addr = offset +
(pages_per_blk - 1) * read_cnt * mtd->writesize;
}
realpage = (int)(addr >> chip->page_shift);
page = realpage & chip->pagemask;
if (page != -1) {
valid_page_num=mtd->writesize>>chip->page_shift;
valid_page_num /= aml_chip->plane_num;
aml_chip->page_addr = page/ valid_page_num;
if (unlikely(aml_chip->page_addr >= aml_chip->internal_page_nums)) {
aml_chip->page_addr -= aml_chip->internal_page_nums;
aml_chip->page_addr |=
(1 << aml_chip->internal_chip_shift)*aml_chip->internal_chipnr;
}
}
if (aml_chip->plane_num == 2) {
chip->select_chip(mtd, i);
aml_chip->aml_nand_wait_devready(aml_chip, i);
if (aml_nand_get_fbb_issue()) {
chip->cmd_ctrl(mtd,
NAND_CMD_SEQIN, NAND_CTRL_CLE);
chip->cmd_ctrl(mtd,
0, NAND_CTRL_ALE);
}
aml_chip->aml_nand_command(aml_chip,
NAND_CMD_READ0,
0x00,aml_chip->page_addr, i);
if (!aml_chip->aml_nand_wait_devready(aml_chip, i))
printk ("%s, %d,selected chip%d not ready\n",
__func__, __LINE__, i);
if (aml_chip->ops_mode & AML_CHIP_NONE_RB)
chip->cmd_ctrl(mtd,
NAND_CMD_READ0 & 0xff,
NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
udelay(2);
aml_chip->aml_nand_command(aml_chip,
NAND_CMD_TWOPLANE_READ1,
0x00, aml_chip->page_addr, i);
udelay(2);
if (aml_chip->mfr_type == NAND_MFR_SANDISK) {
for (j = 0; j < 6; j++)
col_data_sandisk[j] = chip->read_byte(mtd);
} else
col0_data = chip->read_byte(mtd);
aml_chip->aml_nand_command(aml_chip,
NAND_CMD_TWOPLANE_READ2,
aml_chip->page_size,
aml_chip->page_addr, i);
//aml_chip->aml_nand_wait_devready(aml_chip, i);
udelay(2);
if (aml_chip->mfr_type == NAND_MFR_SANDISK)
col0_oob = 0x0;
else
col0_oob = chip->read_byte(mtd);
//printk("col0_oob=%x\n",col0_oob);
} else if (aml_chip->plane_num == 1) {
chip->select_chip(mtd, i);
//nand_get_chip();
//aml_chip->aml_nand_select_chip(aml_chip, i);
if (aml_nand_get_fbb_issue()) {
chip->cmd_ctrl(mtd,
NAND_CMD_SEQIN, NAND_CTRL_CLE);
chip->cmd_ctrl(mtd,
0, NAND_CTRL_ALE);
}
aml_chip->aml_nand_command(aml_chip,
NAND_CMD_READ0, 0x00,
aml_chip->page_addr , i);
udelay(2);
if (!aml_chip->aml_nand_wait_devready(aml_chip, i))
printk ("%s, %d,selected chip%d not ready\n",
__func__, __LINE__, i);
if (aml_chip->ops_mode & AML_CHIP_NONE_RB)
chip->cmd_ctrl(mtd,
NAND_CMD_READ0 & 0xff,
NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
udelay(2);
if (aml_chip->mfr_type == NAND_MFR_SANDISK) {
for (j = 0; j < 6; j++)
col_data_sandisk[j] =
chip->read_byte(mtd);
} else
col0_data = chip->read_byte(mtd);
//printk("col0_data =%x\n",col0_data);
if (aml_chip->mfr_type != NAND_MFR_SANDISK)
aml_chip->aml_nand_command(aml_chip,
NAND_CMD_RNDOUT,
aml_chip->page_size, -1, i);
udelay(2);
if (aml_chip->mfr_type == NAND_MFR_SANDISK)
col0_oob = 0x0;
else
col0_oob = chip->read_byte(mtd);
//printk("col0_oob =%x\n",col0_oob);
}
if ((aml_chip->mfr_type == 0xC8 )) {
if ((col0_oob != 0xFF) || (col0_data != 0xFF)) {
printk("detect factory Bad block:%llx blk:%d chip:%d\n",
(uint64_t)addr, start_blk, i);
aml_chip->nand_bbt_info->nand_bbt[bad_blk_cnt++] =
start_blk | 0x8000;
aml_chip->block_status[start_blk] = NAND_FACTORY_BAD;
break;
}
}
if (aml_chip->mfr_type == NAND_MFR_AMD ) {
if (col0_oob != 0xFF) {
printk("detect factory Bad block:%llx blk:%d chip:%d\n",
(uint64_t)addr, start_blk, i);
aml_chip->nand_bbt_info->nand_bbt[bad_blk_cnt++] =
start_blk | 0x8000;
aml_chip->block_status[start_blk] = NAND_FACTORY_BAD;
break;
}
}
if ((col0_oob == 0xFF))
continue;
if (col0_oob != 0xFF) {
printk("%s:%d factory ship bbt found\n", __func__, __LINE__);
if (aml_chip->mfr_type == 0xc2 ) {
if (col0_oob != 0xFF) {
printk("detect factory Bad block:%llx blk=%d chip=%d\n",
(uint64_t)addr, start_blk, i);
aml_chip->nand_bbt_info->nand_bbt[bad_blk_cnt++] = start_blk|0x8000;
aml_chip->block_status[start_blk] = NAND_FACTORY_BAD;
break;
}
}
if (aml_chip->mfr_type == NAND_MFR_DOSILICON ||
aml_chip->mfr_type == NAND_MFR_ATO ||
aml_chip->mfr_type == NAND_MFR_HYNIX ||
aml_chip->mfr_type == NAND_MFR_ZETTA) {
if (col0_oob != 0xFF) {
pr_info("detect a fbb:%llx blk=%d chip=%d\n",
(uint64_t)addr, start_blk, i);
aml_chip->nand_bbt_info->
nand_bbt[bad_blk_cnt++] =
start_blk|0x8000;
aml_chip->block_status[start_blk] =
NAND_FACTORY_BAD;
break;
}
}
if (aml_chip->mfr_type == 0xef ) {
if (col0_oob != 0xFF) {
printk("detect factory Bad block:%llx blk=%d chip=%d\n",
(uint64_t)addr, start_blk, i);
aml_chip->nand_bbt_info->nand_bbt[bad_blk_cnt++] = start_blk|0x8000;
aml_chip->block_status[start_blk] = NAND_FACTORY_BAD;
break;
}
}
if ((aml_chip->mfr_type == NAND_MFR_SANDISK) ) {
for (j = 0; j < 6; j++) {
if (col_data_sandisk[j] == 0x0) {
bad_sandisk_flag = 1;
break;
}
}
if (bad_sandisk_flag ) {
printk("detect factory Bad block:%llx blk=%d chip=%d\n",
(uint64_t)addr, start_blk, i);
aml_chip->nand_bbt_info->nand_bbt[bad_blk_cnt++] = start_blk|0x8000;
aml_chip->block_status[start_blk] = NAND_FACTORY_BAD;
bad_sandisk_flag=0;
break;
}
}
if ((aml_chip->mfr_type == NAND_MFR_SAMSUNG ) ) {
if ((col0_oob != 0xFF) && (col0_data != 0xFF)) {
printk("detect factory Bad block:%llx blk=%d chip=%d\n",
(uint64_t)addr, start_blk, i);
aml_chip->nand_bbt_info->nand_bbt[bad_blk_cnt++] = start_blk|0x8000;
aml_chip->block_status[start_blk] = NAND_FACTORY_BAD;
break;
}
}
if ((aml_chip->mfr_type == NAND_MFR_TOSHIBA ) ) {
if ((col0_oob != 0xFF) && (col0_data != 0xFF)) {
printk("detect factory Bad block:%llx blk=%d chip=%d\n",
(uint64_t)addr, start_blk, i);
aml_chip->nand_bbt_info->nand_bbt[bad_blk_cnt++] = start_blk|0x8000;
aml_chip->block_status[start_blk] = NAND_FACTORY_BAD;
break;
}
}
if (aml_chip->mfr_type == NAND_MFR_MICRON ) {
if (col0_oob == 0x0) {
printk("detect factory Bad block:%llx blk=%d chip=%d\n",
(uint64_t)addr, start_blk, i);
aml_chip->nand_bbt_info->nand_bbt[bad_blk_cnt++] = start_blk|0x8000;
aml_chip->block_status[start_blk] = NAND_FACTORY_BAD;
break;
}
}
}
}
//}
}
} while((++start_blk) < total_blk);
printk("aml_nand_scan_bbt: factory Bad block bad_blk_cnt=%d\n",
bad_blk_cnt);
free(data_buf);
return 0;
}
int aml_nand_read_rsv_info (struct mtd_info *mtd,
struct aml_nandrsv_info_t *nandrsv_info, size_t offset, u_char * buf)
{
struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd);
struct oobinfo_t *oobinfo;
int error = 0, ret = 0;
loff_t addr = 0;
size_t amount_loaded = 0;
size_t len;
unsigned char *data_buf;
unsigned char oob_buf[sizeof(struct oobinfo_t)];
int page, realpage, chipnr;
struct nand_chip *chip = mtd->priv;
READ_RSV_AGAIN:
addr = nandrsv_info->valid_node->phy_blk_addr;
addr *= mtd->erasesize;
addr += nandrsv_info->valid_node->phy_page_addr * mtd->writesize;
printk("%s:%d,read %s info to %llx\n",__func__, __LINE__,
nandrsv_info->name, addr);
data_buf = aml_chip->rsv_data_buf;
oobinfo = (struct oobinfo_t *)oob_buf;
while (amount_loaded < nandrsv_info->size ) {
memset((unsigned char *)data_buf,
0x0, mtd->writesize);
memset((unsigned char *)oob_buf,
0x0, sizeof(struct oobinfo_t));
chipnr = (int)(addr >> chip->chip_shift);
chip->select_chip(mtd, chipnr);
realpage = (int)(addr >> chip->page_shift);
page = realpage & chip->pagemask;
chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
error = chip->ecc.read_page(mtd, chip, data_buf,
1, page);
chip->select_chip(mtd, -1);
if ((error != 0) && (error != -EUCLEAN)) {
printk("blk good but read failed: %llx, %d\n",
(uint64_t)addr, error);
ret = aml_nand_scan_rsv_info(mtd, nandrsv_info);
if (ret == -1)
return 1;
else
goto READ_RSV_AGAIN;
}
memcpy(oob_buf, chip->oob_poi, mtd->oobavail);
if (memcmp(oobinfo->name, nandrsv_info->name, 4))
printk("invalid nand info %s magic: %llx\n",
nandrsv_info->name, (uint64_t)addr);
addr += mtd->writesize;
page++;
len = min_t( uint32_t, mtd->writesize,
(nandrsv_info->size - amount_loaded));
memcpy(buf + amount_loaded, data_buf, len);
amount_loaded += mtd->writesize;
}
if (amount_loaded < nandrsv_info->size)
return 1;
#if 0
uint64_t dump_len =0;
unsigned char *tmp = NULL;
if(!strncmp(nandrsv_info->name,
KEY_NAND_MAGIC, strlen(nandrsv_info->name))) {
tmp = buf;
dump_len = nandrsv_info->size / 16;
while (dump_len--) {
printk("\t%02x %02x %02x %02x %02x %02x %02x %02x"
" %02x %02x %02x %02x %02x %02x %02x %02x\n",
tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6], tmp[7],
tmp[8], tmp[9], tmp[10], tmp[11], tmp[12], tmp[13], tmp[14],
tmp[15]);
tmp += 16;
}
}
#endif
return 0;
}
int aml_nand_ext_read_rsv_info(struct mtd_info *mtd,
struct aml_nandrsv_info_t *info,
size_t offset, u_char *buf)
{
if (!info->valid) {
printk("%s %d: %s is invalid! read exit!",
__func__,
__LINE__,
info->name);
return 1;
}
if (aml_nand_read_rsv_info(mtd, info, offset, (u_char *)buf))
return 1;
return 0;
}
static int aml_nand_write_rsv(struct mtd_info *mtd,
struct aml_nandrsv_info_t *nandrsv_info, loff_t offset, u_char *buf)
{
struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd);
struct oobinfo_t *oobinfo;
int error = 0;
loff_t addr = 0;
size_t amount_saved = 0;
size_t len;
unsigned char *data_buf;
unsigned char oob_buf[sizeof(struct oobinfo_t)];
struct nand_chip *chip = mtd->priv;
int page, chipnr;
data_buf = aml_chip->rsv_data_buf;
addr = offset;
oobinfo = (struct oobinfo_t *)oob_buf;
memcpy(oobinfo->name, nandrsv_info->name, 4);
oobinfo->ec = nandrsv_info->valid_node->ec;
oobinfo->timestamp = nandrsv_info->valid_node->timestamp;
#if 0
uint64_t dump_len =0;
unsigned char *tmp = NULL;
if(!strncmp(nandrsv_info->name,
KEY_NAND_MAGIC, strlen(nandrsv_info->name))) {
tmp = buf;
dump_len = nandrsv_info->size / 16;
while (dump_len--) {
printk("\t%02x %02x %02x %02x %02x %02x %02x %02x"
" %02x %02x %02x %02x %02x %02x %02x %02x\n",
tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6], tmp[7],
tmp[8], tmp[9], tmp[10], tmp[11], tmp[12], tmp[13], tmp[14],
tmp[15]);
tmp += 16;
}
}
#endif
while (amount_saved < nandrsv_info->size ) {
memset((unsigned char *)data_buf,
0x0, mtd->writesize);
len = min_t(uint32_t,mtd->writesize,
nandrsv_info->size - amount_saved);
memcpy((unsigned char *)data_buf,
buf + amount_saved, len);
page = (int)(addr >> chip->page_shift);
chipnr = (int)(addr >> chip->chip_shift);
memset(chip->oob_poi, 0xff, mtd->oobsize);
memcpy(chip->oob_poi, oob_buf, mtd->oobsize);
chip->select_chip(mtd, chipnr);
error = chip->write_page(mtd, chip, 0, len, data_buf,
1, page, 0, 0);
chip->select_chip(mtd, -1);
if (error) {
printk("blk check good but write failed: %llx, %d\n",
(uint64_t)addr, error);
return 1;
}
addr += mtd->writesize;
amount_saved += mtd->writesize;
}
if (amount_saved < nandrsv_info->size)
return 1;
return 0;
}
int aml_nand_erase_rsv_info(struct mtd_info *mtd,
struct aml_nandrsv_info_t *nandrsv_info)
{
struct free_node_t *tmp_node = NULL;
int error = 0;
loff_t addr = 0;
int chipnr, page;
struct nand_chip *chip = mtd->priv;
printk("erasing %s: \n", nandrsv_info->name);
if (nandrsv_info->valid) {
addr = nandrsv_info->valid_node->phy_blk_addr;
addr *= mtd->erasesize;
_aml_rsv_disprotect();
//error = mtd->_erase(mtd, &erase_info);
page = (int)(addr >> chip->page_shift);
chipnr = (int)(addr >> chip->chip_shift);
chip->select_chip(mtd, chipnr);
chip->erase_cmd(mtd, page & chip->pagemask);
error = chip->waitfunc(mtd, chip);
if (error & NAND_STATUS_FAIL)
error = -EIO;
else
error = 0;
chip->select_chip(mtd, -1);
_aml_rsv_protect();
printk("erasing valid info block: %llx \n", addr);
nandrsv_info->valid_node->ec++;
nandrsv_info->valid_node->phy_page_addr = 0;
nandrsv_info->valid_node->timestamp = 1;
}
tmp_node = nandrsv_info->free_node;
while (tmp_node != NULL) {
addr = tmp_node->phy_blk_addr;
addr *= mtd->erasesize;
_aml_rsv_disprotect();
//error = mtd->_erase(mtd, &erase_info);
page = (int)(addr >> chip->page_shift);
chipnr = (int)(addr >> chip->chip_shift);
chip->select_chip(mtd, chipnr);
chip->erase_cmd(mtd, page & chip->pagemask);
error = chip->waitfunc(mtd, chip);
if (error & NAND_STATUS_FAIL)
error = -EIO;
else
error = 0;
chip->select_chip(mtd, -1);
_aml_rsv_protect();
printk("erasing free info block: %llx \n", addr);
tmp_node->ec = -1;
tmp_node->dirty_flag = 0;
tmp_node = tmp_node->next;
}
return error;
}
int aml_nand_ext_erase_rsv_info(struct mtd_info *mtd,
struct aml_nandrsv_info_t *info)
{
if (aml_nand_erase_rsv_info(mtd, info))
return 1;
return 0;
}
int aml_nand_save_rsv_info(struct mtd_info *mtd,
struct aml_nandrsv_info_t *nandrsv_info ,u_char *buf)
{
struct free_node_t *free_node = NULL, *tmp_node = NULL;
int error = 0, pages_per_blk, i = 1;
loff_t addr = 0;
int chipnr, page;
struct nand_chip *chip = mtd->priv;
pages_per_blk = mtd->erasesize / mtd->writesize;
/*solve these abnormals caused by power off and ecc error*/
if ((nandrsv_info->valid_node->status & POWER_ABNORMAL_FLAG)
|| (nandrsv_info->valid_node->status & ECC_ABNORMAL_FLAG))
nandrsv_info->valid_node->phy_page_addr = pages_per_blk;
if ((mtd->writesize < nandrsv_info->size))
i = (nandrsv_info->size + mtd->writesize - 1) / mtd->writesize;
printk("%s:%d, %s: valid=%d, pages=%d\n",__func__, __LINE__,
nandrsv_info->name, nandrsv_info->valid, i);
RE_SEARCH:
if (nandrsv_info->valid) {
//printk("%s:%d,phy_page_addr=%d,pages=%d\n",__func__, __LINE__,
// nandrsv_info->valid_node->phy_page_addr, i);
nandrsv_info->valid_node->phy_page_addr += i;
if ((nandrsv_info->valid_node->phy_page_addr+i)>pages_per_blk) {
if ((nandrsv_info->valid_node->phy_page_addr -i) == pages_per_blk) {
addr = nandrsv_info->valid_node->phy_blk_addr;
addr *= mtd->erasesize;
_aml_rsv_disprotect();
//error = mtd->_erase(mtd, &erase_info);
page = (int)(addr >> chip->page_shift);
chipnr = (int)(addr >> chip->chip_shift);
chip->select_chip(mtd, chipnr);
chip->erase_cmd(mtd, page & chip->pagemask);
error = chip->waitfunc(mtd, chip);
if (error & NAND_STATUS_FAIL)
error = -EIO;
else
error = 0;
chip->select_chip(mtd, -1);
_aml_rsv_protect();
nandrsv_info->valid_node->ec++;
printk("---erase bad info block:%llx \n",addr);
}
//free_node = kzalloc(sizeof(struct free_node_t),
// GFP_KERNEL);
free_node = get_free_node(mtd);
if (free_node == NULL)
return -ENOMEM;
free_node->phy_blk_addr =
nandrsv_info->valid_node->phy_blk_addr;
free_node->ec = nandrsv_info->valid_node->ec;
tmp_node = nandrsv_info->free_node;
while (tmp_node->next != NULL) {
tmp_node = tmp_node->next;
}
tmp_node->next = free_node;
tmp_node = nandrsv_info->free_node;
nandrsv_info->valid_node->phy_blk_addr =
tmp_node->phy_blk_addr;
nandrsv_info->valid_node->phy_page_addr = 0;
nandrsv_info->valid_node->ec = tmp_node->ec;
nandrsv_info->valid_node->timestamp += 1;
nandrsv_info->free_node = tmp_node->next;
release_free_node(mtd, tmp_node);
}
} else {
tmp_node = nandrsv_info->free_node;
nandrsv_info->valid_node->phy_blk_addr = tmp_node->phy_blk_addr;
nandrsv_info->valid_node->phy_page_addr = 0;
nandrsv_info->valid_node->ec = tmp_node->ec;
nandrsv_info->valid_node->timestamp += 1;
nandrsv_info->free_node = tmp_node->next;
release_free_node(mtd, tmp_node);
}
addr = nandrsv_info->valid_node->phy_blk_addr;
addr *= mtd->erasesize;
addr += nandrsv_info->valid_node->phy_page_addr * mtd->writesize;
printk("%s:%d,save info to %llx\n",__func__, __LINE__, addr);
if (nandrsv_info->valid_node->phy_page_addr == 0) {
chipnr = (int)(addr >> chip->chip_shift);
chip->select_chip(mtd, chipnr);
error = chip->block_bad(mtd, addr, 1);
chip->select_chip(mtd, -1);
if (error != 0) {
/*
bad block here, need fix it
because of info_blk list may be include bad block,
so we need check it and done here. if don't,
some bad blocks may be erase here
and env will lost or too much ecc error
*/
printk("have bad block in info_blk list!!!!\n");
nandrsv_info->valid_node->phy_page_addr =
pages_per_blk - i ;
goto RE_SEARCH;
}
_aml_rsv_disprotect();
page = (int)(addr >> chip->page_shift);
//chipnr = (int)(addr >> chip->chip_shift);
chip->select_chip(mtd, chipnr);
chip->erase_cmd(mtd, page & chip->pagemask);
error = chip->waitfunc(mtd, chip);
if (error & NAND_STATUS_FAIL)
error = -EIO;
else
error = 0;
chip->select_chip(mtd, -1);
_aml_rsv_protect();
if (error) {
printk("env free blk erase failed %d\n", error);
chip->select_chip(mtd, chipnr);
chip->block_markbad(mtd, addr);
chip->select_chip(mtd, -1);
return error;
}
nandrsv_info->valid_node->ec++;
}
if (aml_nand_write_rsv(mtd, nandrsv_info, addr, (u_char *) buf)) {
printk("update nand env FAILED!\n");
return 1;
}
/* update valid infos */
if (!nandrsv_info->valid)
nandrsv_info->valid = 1;
/* clear status when write successfully*/
nandrsv_info->valid_node->status = 0;
return error;
}
int aml_nand_ext_save_rsv_info(struct mtd_info *mtd,
struct aml_nandrsv_info_t *info,
u_char *buf)
{
if (!info->init) {
printk("%s %d %s not init\n",
__func__, __LINE__, info->name);
return 1;
}
if (aml_nand_save_rsv_info(mtd, info, buf))
return 1;
return 0;
}
static void aml_nand_rsv_info_size_fill(struct mtd_info *mtd)
{
struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd);
struct nand_chip *chip = mtd->priv;
int subtract = -1;
#ifndef CONFIG_ENV_IS_IN_NAND
subtract = 0;
#endif
info[BBT_INFO_INDEX].size = mtd->size >> chip->phys_erase_shift;
info[KEY_INFO_INDEX + subtract].size = aml_chip->keysize;
info[DTB_INFO_INDEX + subtract].size = aml_chip->dtbsize;
}
static void aml_nand_rsv_info_ptr_fill(struct mtd_info *mtd)
{
struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd);
int subtract = -1;
aml_chip->aml_nandbbt_info = &info[BBT_INFO_INDEX].rsv_info;
#ifndef CONFIG_ENV_IS_IN_NAND
aml_chip->aml_nandenv_info = &info[ENV_INFO_INDEX].rsv_info;
subtract = 0;
#endif
aml_chip->aml_nandkey_info = &info[KEY_INFO_INDEX + subtract].rsv_info;
aml_chip->aml_nanddtb_info = &info[DTB_INFO_INDEX + subtract].rsv_info;
aml_chip->aml_nandddr_info = &info[DDR_INFO_INDEX + subtract].rsv_info;
}
static int aml_nand_rsv_info_alloc_init(struct mtd_info *mtd,
char *name,
struct aml_nandrsv_info_t **rsv_info,
unsigned int blocks, unsigned int size)
{
struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd);
struct aml_nandrsv_info_t *rsvinfo = *rsv_info;
struct nand_chip *chip = mtd->priv;
unsigned int limit_blk, pages_per_blk_shift;
pages_per_blk_shift = (chip->phys_erase_shift - chip->page_shift);
limit_blk = (BOOT_TOTAL_PAGES >> pages_per_blk_shift) +
RESERVED_BLOCK_NUM;
if (aml_chip->vernier + blocks > limit_blk) {
pr_info("ERROR: total blk number is over the limit\n");
return -ERANGE;
}
rsvinfo->mtd = mtd;
rsvinfo->valid_node = kzalloc(sizeof(struct valid_node_t), GFP_KERNEL);
if (rsvinfo->valid_node == NULL)
return -ENOMEM;
rsvinfo->valid_node->phy_blk_addr = -1;
rsvinfo->start_block = aml_chip->vernier;
aml_chip->vernier += blocks;
rsvinfo->end_block = aml_chip->vernier;
rsvinfo->size = size;
memcpy(rsvinfo->name, name, 4);
return 0;
}
int aml_nand_rsv_info_init(struct mtd_info *mtd)
{
struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd);
struct nand_chip *chip = mtd->priv;
struct aml_nandrsv_info_t *rsv_info;
unsigned int pages_per_blk_shift, bbt_start_block;
int i, ret;
pages_per_blk_shift = (chip->phys_erase_shift - chip->page_shift);
bbt_start_block = BOOT_TOTAL_PAGES >> pages_per_blk_shift;
bbt_start_block += NAND_GAP_BLOCK_NUM;
aml_chip->rsv_data_buf = kzalloc(mtd->writesize, GFP_KERNEL);
if (aml_chip->rsv_data_buf == NULL)
return -ENOMEM;
aml_chip->vernier = bbt_start_block;
aml_chip->freeNodeBitmask = 0;
for (i = 0; i < RESERVED_BLOCK_NUM; i++) {
aml_chip->free_node[i] =
kzalloc(sizeof(struct free_node_t), GFP_KERNEL);
aml_chip->free_node[i]->index = i;
}
/*block status*/
aml_chip->block_status =
kzalloc((mtd->size >> chip->phys_erase_shift), GFP_KERNEL);
if (aml_chip->block_status == NULL) {
printk("no memory for flash block status\n");
return -ENOMEM;
}
memset(aml_chip->block_status,
0, (mtd->size >> chip->phys_erase_shift));
aml_nand_rsv_info_size_fill(mtd);
for (i = 0; i < ARRAY_SIZE(info); i++) {
rsv_info = &info[i].rsv_info;
ret = aml_nand_rsv_info_alloc_init(mtd, info[i].name,
&rsv_info,
info[i].blocks,
info[i].size);
if (ret) {
printk("%s info alloc init failed\n", info[i].name);
return ret;
}
printk("%s=%d\n", info[i].name, info[i].rsv_info.start_block);
}
aml_nand_rsv_info_ptr_fill(mtd);
printk("bbt_start=%d ", aml_chip->aml_nandbbt_info->start_block);
#ifndef CONFIG_ENV_IS_IN_NAND
printk("env_start=%d ", aml_chip->aml_nandenv_info->start_block);
#endif
printk("key_start=%d ", aml_chip->aml_nandkey_info->start_block);
printk("dtb_start=%d ", aml_chip->aml_nanddtb_info->start_block);
printk("ddr_start=%d ", aml_chip->aml_nandddr_info->start_block);
printk("\n");
return 0;
}
int aml_nand_free_rsv_info(struct mtd_info *mtd,
struct aml_nandrsv_info_t *nandrsv_info)
{
struct free_node_t *tmp_node, *next_node = NULL;
int error = 0;
loff_t addr = 0;
int page, chipnr;
struct nand_chip *chip = mtd->priv;
pr_info("free %s\n", nandrsv_info->name);
if (nandrsv_info->valid) {
addr = nandrsv_info->valid_node->phy_blk_addr;
addr *= mtd->erasesize;
_aml_rsv_disprotect();
//error = mtd->_erase(mtd, &erase_info);
page = (int)(addr >> chip->page_shift);
chipnr = (int)(addr >> chip->chip_shift);
chip->select_chip(mtd, chipnr);
chip->erase_cmd(mtd, page & chip->pagemask);
error = chip->waitfunc(mtd, chip);
if (error & NAND_STATUS_FAIL)
error = -EIO;
else
error = 0;
chip->select_chip(mtd, -1);
_aml_rsv_protect();
pr_info("erasing valid info block: %llx\n", addr);
nandrsv_info->valid_node->phy_blk_addr = -1;
nandrsv_info->valid_node->ec = -1;
nandrsv_info->valid_node->phy_page_addr = 0;
nandrsv_info->valid_node->timestamp = 0;
nandrsv_info->valid_node->status = 0;
nandrsv_info->valid = 0;
}
tmp_node = nandrsv_info->free_node;
while (tmp_node != NULL) {
next_node = tmp_node->next;
release_free_node(mtd, tmp_node);
tmp_node = next_node;
}
nandrsv_info->free_node = NULL;
return error;
}
int aml_nand_scan_rsv_info(struct mtd_info *mtd,
struct aml_nandrsv_info_t *nandrsv_info)
{
struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd);
struct nand_chip *chip = &aml_chip->chip;
struct oobinfo_t *oobinfo;
struct free_node_t *free_node, *tmp_node = NULL;
unsigned char oob_buf[sizeof(struct oobinfo_t)];
loff_t offset;
unsigned char *data_buf, good_addr[256] = {0};
int start_blk, max_scan_blk, i , k, scan_status = 0, env_status=0;
int phys_erase_shift, pages_per_blk, page_num;
int error = 0, ret = 0;
int page, realpage, chipnr;
data_buf = aml_chip->rsv_data_buf;
oobinfo = (struct oobinfo_t *)oob_buf;
RE_RSV_INFO_EXT:
memset(good_addr, 0 , 256);
max_scan_blk = nandrsv_info->end_block;
start_blk = nandrsv_info->start_block;
printk("%s: info size=0x%x max_scan_blk=%d, start_blk=%d\n",
nandrsv_info->name, nandrsv_info->size, max_scan_blk, start_blk);
do {
offset = mtd->erasesize;
offset *= start_blk;
scan_status = 0;
RE_RSV_INFO:
memset((unsigned char *)data_buf,
0x0, mtd->writesize);
memset((unsigned char *)oob_buf,
0x0, sizeof(struct oobinfo_t));
//error = mtd->_read_oob(mtd, offset, &aml_oob_ops);
realpage = (int)(offset >> chip->page_shift);
page = realpage & chip->pagemask;
chipnr = (int)(offset >> chip->chip_shift);
chip->select_chip(mtd, chipnr);
chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
error = chip->ecc.read_page(mtd, chip, data_buf,
1, page);
chip->select_chip(mtd, -1);
if ((error != 0) && (error != -EUCLEAN)) {
printk("blk check good but read failed: %llx, %d\n",
(uint64_t)offset, error);
offset += nandrsv_info->size;
if ((scan_status++ > 6) || (!(offset % mtd->erasesize))) {
printk("ECC error, scan ONE block exit\n");
scan_status = 0;
continue;
}
goto RE_RSV_INFO;
}
memcpy(oob_buf, chip->oob_poi, mtd->oobavail);
nandrsv_info->init = 1;
nandrsv_info->valid_node->status = 0;
if (!memcmp(oobinfo->name, nandrsv_info->name, 4)) {
nandrsv_info->valid = 1;
if (nandrsv_info->valid_node->phy_blk_addr >= 0) {
free_node = get_free_node(mtd);
if (free_node == NULL)
return -ENOMEM;
free_node->dirty_flag = 1;
if (oobinfo->timestamp > nandrsv_info->valid_node->timestamp) {
free_node->phy_blk_addr =
nandrsv_info->valid_node->phy_blk_addr;
free_node->ec =
nandrsv_info->valid_node->ec;
nandrsv_info->valid_node->phy_blk_addr =
start_blk;
nandrsv_info->valid_node->phy_page_addr=
0;
nandrsv_info->valid_node->ec =
oobinfo->ec;
nandrsv_info->valid_node->timestamp =
oobinfo->timestamp;
} else {
free_node->phy_blk_addr = start_blk;
free_node->ec = oobinfo->ec;
}
if (nandrsv_info->free_node == NULL)
nandrsv_info->free_node = free_node;
else {
tmp_node = nandrsv_info->free_node;
while (tmp_node->next != NULL) {
tmp_node = tmp_node->next;
}
tmp_node->next = free_node;
}
} else {
nandrsv_info->valid_node->phy_blk_addr =
start_blk;
nandrsv_info->valid_node->phy_page_addr = 0;
nandrsv_info->valid_node->ec = oobinfo->ec;
nandrsv_info->valid_node->timestamp =
oobinfo->timestamp;
}
} else {
free_node = get_free_node(mtd);
if (free_node == NULL)
return -ENOMEM;
free_node->phy_blk_addr = start_blk;
free_node->ec = oobinfo->ec;
if (nandrsv_info->free_node == NULL)
nandrsv_info->free_node = free_node;
else {
tmp_node = nandrsv_info->free_node;
while (tmp_node->next != NULL) {
tmp_node = tmp_node->next;
}
tmp_node->next = free_node;
}
}
} while ((++start_blk) < max_scan_blk);
printk("%s : phy_blk_addr=%d, ec=%d, phy_page_addr=%d, timestamp=%d\n",
nandrsv_info->name,
nandrsv_info->valid_node->phy_blk_addr,
nandrsv_info->valid_node->ec,
nandrsv_info->valid_node->phy_page_addr,
nandrsv_info->valid_node->timestamp);
printk("%s free list: \n", nandrsv_info->name);
tmp_node = nandrsv_info->free_node;
while (tmp_node != NULL) {
printk("blockN=%d, ec=%d, dirty_flag=%d\n",
tmp_node->phy_blk_addr,
tmp_node->ec,
tmp_node->dirty_flag);
tmp_node = tmp_node->next;
}
/*second stage*/
phys_erase_shift = fls(mtd->erasesize) - 1;
pages_per_blk = (1 << (phys_erase_shift - chip->page_shift));
page_num = nandrsv_info->size / mtd->writesize;
if (page_num == 0)
page_num++;
printk("%s %d: page_num=%d\n", __func__, __LINE__, page_num);
if (nandrsv_info->valid == 1) {
printk("%s %d\n", __func__, __LINE__);
for (i = 0; i < pages_per_blk; i++) {
memset((unsigned char *)data_buf,
0x0, mtd->writesize);
memset((unsigned char *)oob_buf,
0x0, sizeof(struct oobinfo_t));
offset = nandrsv_info->valid_node->phy_blk_addr;
offset *= mtd->erasesize;
offset += i * mtd->writesize;
//error = mtd->_read_oob(mtd, offset, &aml_oob_ops);
realpage = (int)(offset >> chip->page_shift);
page = realpage & chip->pagemask;
chipnr = (int)(offset >> chip->chip_shift);
chip->select_chip(mtd, chipnr);
chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
error = chip->ecc.read_page(mtd, chip, data_buf,
1, page);
chip->select_chip(mtd, -1);
if ((error != 0) && (error != -EUCLEAN)) {
printk("blk good but read failed:%llx,%d\n",
(uint64_t)offset, error);
nandrsv_info->valid_node->status |= ECC_ABNORMAL_FLAG;
ret = -1;
continue;
}
memcpy(oob_buf, chip->oob_poi, mtd->oobavail);
if (!memcmp(oobinfo->name, nandrsv_info->name, 4)) {
good_addr[i] = 1;
nandrsv_info->valid_node->phy_page_addr = i;
} else
break;
}
}
if ((mtd->writesize<nandrsv_info->size) && (nandrsv_info->valid ==1)) {
i = nandrsv_info->valid_node->phy_page_addr;
if (((i + 1) % page_num) != 0) {
ret = -1;
nandrsv_info->valid_node->status |= POWER_ABNORMAL_FLAG;
printk("find %s incomplete\n", nandrsv_info->name);
}
if (ret == -1) {
for (i = 0; i < (pages_per_blk / page_num); i++) {
env_status =0;
for (k = 0; k < page_num; k++) {
if (!good_addr[ k + i * page_num]) {
env_status = 1;
break;
}
}
if (!env_status) {
printk("find %d page ok\n", i*page_num);
nandrsv_info->valid_node->phy_page_addr=
k + i * page_num -1;
ret = 0;
}
}
}
if (ret == -1) {
nandrsv_info->valid_node->status = 0;
aml_nand_free_rsv_info(mtd, nandrsv_info);
goto RE_RSV_INFO_EXT;
}
i = (nandrsv_info->size + mtd->writesize - 1) / mtd->writesize;
nandrsv_info->valid_node->phy_page_addr -= (i - 1);
}
if (nandrsv_info->valid != 1)
ret = -1;
offset = nandrsv_info->valid_node->phy_blk_addr;
offset *= mtd->erasesize;
offset += nandrsv_info->valid_node->phy_page_addr * mtd->writesize;
printk("%s valid addr: %llx\n", nandrsv_info->name, (uint64_t)offset);
return ret;
}
int aml_nand_rsv_info_check_except_bbt(struct mtd_info *mtd)
{
int ret = 0, i;
for (i = ENV_INFO_INDEX; i < ARRAY_SIZE(info); i++) {
ret = aml_nand_scan_rsv_info(mtd, &info[i].rsv_info);
if (ret !=0 && ret != -1)
printk("%s %d\n", __func__, __LINE__);
if (info[i].rsv_info.valid == 0)
printk("%s %d NO %s exist\n",
__func__, __LINE__, info[i].name);
}
return ret;
}
int aml_nand_bbt_check(struct mtd_info *mtd)
{
struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd);
struct aml_nand_chip *aml_chip_boot = mtd_to_nand_chip(&nand_info[0]);
int phys_erase_shift;
int ret =0;
int8_t *buf = NULL;
if (!fls(mtd->erasesize)) {
printk("%s, %d !!!!ERROR, bit shift is zero\n",
__func__,__LINE__);
return -ENOMEM;
}
phys_erase_shift = fls(mtd->erasesize) - 1;
ret = aml_nand_scan_rsv_info(mtd, aml_chip->aml_nandbbt_info);
if ((ret !=0) && ((ret != (-1)))) {
printk("%s %d\n", __func__, __LINE__);
goto exit_error;
}
ret = 0;
buf = aml_chip->block_status;
if (aml_chip->aml_nandbbt_info->valid == 1) {
/*read bbt*/
printk("%s %d bbt is valid, reading.\n", __func__, __LINE__);
aml_nand_read_rsv_info(mtd,
aml_chip->aml_nandbbt_info, 0, (u_char *)buf);
} else {
printk("%s %d bbt is invalid, scanning.\n", __func__, __LINE__);
/*no bbt haven't been found, abnormal or clean nand! rebuild*/
aml_chip->nand_bbt_info =
malloc(sizeof(struct aml_nand_bbt_info));
if (!aml_chip->nand_bbt_info) {
ret = -ENOMEM;
goto exit_error;
}
memset(aml_chip->nand_bbt_info,
0, sizeof(struct aml_nand_bbt_info));
memset(aml_chip->block_status,
0, (mtd->size >> phys_erase_shift));
aml_nand_scan_shipped_bbt(mtd);
aml_nand_ext_save_rsv_info(mtd,
aml_chip->aml_nandbbt_info, (u_char *)buf);
if (aml_chip->nand_bbt_info)
free(aml_chip->nand_bbt_info);
}
/*make uboot bbt perspective the same with normal bbt*/
aml_chip_boot->block_status = aml_chip->block_status;
exit_error:
return ret;
}
int aml_nand_scan_bbt(struct mtd_info *mtd)
{
return 0;
}