blob: 33f3717142a6ef91457204f98855585166db56c5 [file] [log] [blame]
#include <common.h>
#include <nand.h>
#include <asm/io.h>
#include <malloc.h>
#include <linux/err.h>
#include <asm/cache.h>
//#include <asm/arch/secure_apb.h>
#include <asm/arch-g12a/cpu_id.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/rawnand.h>
#include <linux/mtd/nand_ecc.h>
#include "aml_nand.h"
#include "version.h"
extern struct mtd_info *nand_info[CONFIG_SYS_MAX_NAND_DEVICE];
extern struct hw_controller *controller;
/* provide a policy that caluate the bakups of bootloader */
int get_boot_num(struct mtd_info *mtd, size_t rwsize)
{
struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd);
size_t bad_blk_len_low = 0, bad_blk_len_up = 0, skip;
size_t aviable_space;
size_t block_len, block_off;
loff_t block_start;
loff_t offset = 0;
int ret = 1; /*inital for only one copy*/
if (!rwsize) { /*not need to policy call, only one */
ret = 1;
return ret;
}
/* algin with page size */
rwsize = ((rwsize + mtd->writesize - 1)/mtd->writesize)*mtd->writesize;
while (offset < mtd->size) {
block_start = offset & ~(loff_t)(mtd->erasesize - 1);
block_off = offset & (mtd->erasesize - 1);
block_len = mtd->erasesize - block_off;
if (nand_block_isbad(mtd, block_start)) {
if ( offset < mtd->size / 2) /*no understand*/
bad_blk_len_low += block_len;
else if (offset > mtd->size / 2)
bad_blk_len_up += block_len;
else {
bad_blk_len_up = offset;
}
}
offset += block_len;
}
printk("rwsize:0x%zx skip_low:0x%zx skip_up:0x%zx\n",
rwsize, bad_blk_len_low, bad_blk_len_up);
skip = bad_blk_len_low + bad_blk_len_up;
aviable_space = mtd->size - skip - 2 * mtd->writesize; /*no understand*/
if (rwsize*2 <= aviable_space) {
ret = 1;
if (rwsize + mtd->writesize + bad_blk_len_low > mtd->size / 2)
return 1; /*1st must be write*/
if (rwsize + mtd->writesize + bad_blk_len_up <= mtd->size / 2)
ret ++;
} else /*needn't consider bad block length, unlikly so many bad blocks*/
ret = 1;
aml_chip->boot_copy_num = ret;
printk("self-adaption boot count:%d\n", ret);
return ret;
}
/*set nand info into page0_buf for romboot.*/
void nand_info_page_prepare(struct aml_nand_chip *aml_chip, u8 *page0_buf)
{
struct nand_chip *chip = &aml_chip->chip;
struct mtd_info *mtd = &chip->mtd;
struct aml_nand_chip *aml_chip_normal = mtd_to_nand_chip(nand_info[1]);
int nand_read_info;
u32 configure_data;
nand_page0_t *p_nand_page0 = NULL;
ext_info_t *p_ext_info = NULL;
nand_setup_t *p_nand_setup = NULL;
int each_boot_pages, boot_num, bbt_pages;
unsigned int pages_per_blk_shift ,bbt_size;
#ifdef CONFIG_DISCRETE_BOOTLOADER
fip_info_t *p_fip_info = NULL;
#endif
pages_per_blk_shift = (chip->phys_erase_shift - chip->page_shift);
bbt_size = aml_chip_normal->rsv->bbt->size;
#ifdef CONFIG_DISCRETE_BOOTLOADER
boot_num = CONFIG_TPL_COPY_NUM;
/* fip size */
each_boot_pages = CONFIG_TPL_SIZE_PER_COPY / mtd->writesize;
#else
/* fixme, boot number self-adapt but not macro */
boot_num = (!aml_chip->boot_copy_num)? 1: aml_chip->boot_copy_num;
each_boot_pages = BOOT_TOTAL_PAGES / boot_num;
#endif /* CONFIG_DISCRETE_BOOTLOADER */
p_nand_page0 = (nand_page0_t *) page0_buf;
p_nand_setup = &p_nand_page0->nand_setup;
p_ext_info = &p_nand_page0->ext_info;
configure_data = NFC_CMD_N2M(aml_chip->ran_mode,
aml_chip->bch_mode, 0, (chip->ecc.size >> 3),
chip->ecc.steps);
memset(p_nand_page0, 0x0, sizeof(nand_page0_t));
/* info_cfg->ext = (configure_data | (1<<23) |(1<<22) | (2<<20)); */
/*p_nand_setup->cfg.d32 = (configure_data|(1<<23) | (1<<22) | (2<<20) | (1<<19));*/
/* randomizer mode depends on chip's config */
p_nand_setup->cfg.d32 = (configure_data|(1<<23) | (1<<22) | (2<<20));
printk("cfg.d32 0x%x\n", p_nand_setup->cfg.d32);
/* need finish here for romboot retry */
p_nand_setup->id = 0;
p_nand_setup->max = 0;
/*fixme, alreay memset 0 */
memset(p_nand_page0->page_list,
0,
NAND_PAGELIST_CNT);
/* chip_num occupy the lowest 2 bit */
nand_read_info = controller->chip_num;
p_ext_info->read_info = nand_read_info;
p_ext_info->page_per_blk = aml_chip->block_size / aml_chip->page_size;
/* fixme, only ce0 is enabled! */
p_ext_info->ce_mask = 0x01;
/* xlc is not in using for now */
p_ext_info->xlc = 1;
p_ext_info->boot_num = boot_num;
p_ext_info->each_boot_pages = each_boot_pages;
bbt_pages =
(bbt_size + mtd->writesize - 1) / mtd->writesize;
p_ext_info->bbt_occupy_pages = bbt_pages;
p_ext_info->bbt_start_block =
(BOOT_TOTAL_PAGES >> pages_per_blk_shift) + NAND_GAP_BLOCK_NUM;
#ifdef CONFIG_DISCRETE_BOOTLOADER
p_fip_info = &p_nand_page0->fip_info;
p_fip_info->version = 1;
p_fip_info->mode = NAND_FIPMODE_DISCRETE;
/* in pages, fixme, should it stored in amlchip? */
p_fip_info->fip_start =
1024 + NAND_RSV_BLOCK_NUM * p_ext_info->page_per_blk;
printk("bl: version %d, mode %d, start 0x%x\n",
p_fip_info->version, p_fip_info->mode, p_fip_info->fip_start);
#endif
printk("page_per_blk = 0x%x bbt_pages = 0x%x \n",
p_ext_info->page_per_blk, bbt_pages);
printk("boot_num = %d each_boot_pages = %d\n", boot_num,
each_boot_pages);
}
/* mtd support interface:
* function:int (*_erase) (struct mtd_info *mtd, struct erase_info *instr);
*/
int m3_nand_boot_erase_cmd(struct mtd_info *mtd, int page)
{
struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd);
struct nand_chip *chip = mtd->priv;
loff_t ofs;
ofs = (page << chip->page_shift);
if (chip->block_bad(mtd, ofs))
return 0;
aml_chip->aml_nand_select_chip(aml_chip, 0);
aml_chip->aml_nand_command(aml_chip,
NAND_CMD_ERASE1, -1, page, 0);
aml_chip->aml_nand_command(aml_chip,
NAND_CMD_ERASE2, -1, -1, 0);
chip->waitfunc(mtd, chip);
return 0;
}
/* mtd support interface:
* chip->ecc.read_page
* function:int (*read_page)(struct mtd_info *mtd, struct nand_chip *chip,
* uint8_t *buf, int oob_required, int page);
*/
int m3_nand_boot_read_page_hwecc(struct mtd_info *mtd,
struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
{
struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd);
uint8_t *oob_buf = chip->oob_poi;
unsigned nand_page_size = chip->ecc.steps * chip->ecc.size;
unsigned pages_per_blk_shift =chip->phys_erase_shift - chip->page_shift;
int user_byte_num = (chip->ecc.steps * aml_chip->user_byte_mode);
int bch_mode = aml_chip->bch_mode, ran_mode=0;
int error = 0, i = 0, stat = 0;
int ecc_size, configure_data_w, pages_per_blk_w, configure_data;
int pages_per_blk, read_page;
int en_slc = 0;
/* using info page structure */
nand_page0_t *p_nand_page0 = NULL;
ext_info_t *p_ext_info = NULL;
nand_setup_t * p_nand_setup = NULL;
int each_boot_pages, boot_num;
loff_t ofs;
#ifdef CONFIG_DISCRETE_BOOTLOADER
boot_num = CONFIG_BL2_COPY_NUM;
#else
boot_num = (!aml_chip->boot_copy_num)? 1: aml_chip->boot_copy_num;
#endif
each_boot_pages = BOOT_TOTAL_PAGES/boot_num;
if (page >= (each_boot_pages * boot_num)) {
memset(buf, 0, (1 << chip->page_shift));
printk("nand boot read out of uboot failed, page:%d\n", page);
goto exit;
}
/* nand page info */
if ((page % each_boot_pages) == 0) {
if (aml_chip->bch_mode == NAND_ECC_BCH_SHORT)
configure_data_w =
NFC_CMD_N2M(aml_chip->ran_mode,
NAND_ECC_BCH60_1K, 1, (chip->ecc.size >> 3), chip->ecc.steps);
else
configure_data_w =
NFC_CMD_N2M(aml_chip->ran_mode,
aml_chip->bch_mode, 0, (chip->ecc.size >> 3), chip->ecc.steps);
ecc_size = chip->ecc.size; //backup ecc size
if (aml_chip->bch_mode != NAND_ECC_BCH_SHORT) {
nand_page_size =
(mtd->writesize / 512) * NAND_ECC_UNIT_SHORT;
bch_mode = NAND_ECC_BCH_SHORT;
chip->ecc.size = NAND_ECC_UNIT_SHORT;
} else
bch_mode = aml_chip->bch_mode;
chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
memset(buf, 0xff, (1 << chip->page_shift));
/* read back page0 and check it */
if (aml_chip->valid_chip[0]) {
if (!aml_chip->aml_nand_wait_devready(aml_chip, i)) {
printk("don't found selected chip:%d ready\n",
i);
error = -EBUSY;
}
if (aml_chip->ops_mode & AML_CHIP_NONE_RB)
chip->cmd_ctrl(mtd, NAND_CMD_READ0 & 0xff,
NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
if (en_slc == 0) {
ran_mode = aml_chip->ran_mode;
aml_chip->ran_mode = 1;
}
error = aml_chip->aml_nand_dma_read(aml_chip,
buf, nand_page_size, bch_mode);
if (error) {
printk(" page0 aml_nand_dma_read failed\n");
}
aml_chip->aml_nand_get_user_byte(aml_chip,
oob_buf, user_byte_num);
stat = aml_chip->aml_nand_hwecc_correct(aml_chip,
buf, nand_page_size, oob_buf);
if (stat < 0) {
if(aml_chip->ran_mode
&& (aml_chip->zero_cnt < aml_chip->ecc_max)) {
memset(buf, 0xff, nand_page_size);
memset(oob_buf, 0xff, user_byte_num);
} else {
mtd->ecc_stats.failed++;
printk("page0 read ecc failed at blk0 chip0\n");
}
} else
mtd->ecc_stats.corrected += stat;
if (en_slc == 0)
aml_chip->ran_mode = ran_mode;
} else {
printk("nand boot page 0 no valid chip failed\n");
error = -ENODEV;
//goto exit;
}
//check page 0 info here
p_nand_page0 = (nand_page0_t *) buf;
p_nand_setup = &p_nand_page0->nand_setup;
p_ext_info = &p_nand_page0->ext_info;
configure_data = p_nand_setup->cfg.b.cmd;
pages_per_blk = p_ext_info->page_per_blk;
pages_per_blk_w =
(1 << (chip->phys_erase_shift - chip->page_shift));
if ((pages_per_blk_w != pages_per_blk)
|| (configure_data != configure_data_w))
printk("page%d warnning, configure:0x%x-0x%x "
"pages_per_blk:0x%x-0x%x\n",
page, configure_data_w, configure_data,
pages_per_blk_w, pages_per_blk);
#ifdef CONFIG_DISCRETE_BOOTLOADER
/* fixme, check fip_info_t */
printk(" TODO: check fip info\n");
#endif
bch_mode = aml_chip->bch_mode;
chip->ecc.size = ecc_size;
nand_page_size = chip->ecc.steps * chip->ecc.size;
}
read_page = page;
read_page++;
READ_BAD_BLOCK:
ofs = (read_page << chip->page_shift);
if (!(ofs % mtd->erasesize)) {
if (chip->block_bad(mtd, ofs)) {
read_page +=
1 << (chip->phys_erase_shift-chip->page_shift);
goto READ_BAD_BLOCK;
}
}
chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, read_page);
memset(buf, 0xff, (1 << chip->page_shift));
if (aml_chip->valid_chip[0]) {
if (!aml_chip->aml_nand_wait_devready(aml_chip, 0)) {
printk("don't found selected chip0 ready, page: %d \n",
page);
error = -EBUSY;
goto exit;
}
if (aml_chip->ops_mode & AML_CHIP_NONE_RB)
chip->cmd_ctrl(mtd, NAND_CMD_READ0 & 0xff,
NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
error = aml_chip->aml_nand_dma_read(aml_chip,
buf, nand_page_size, bch_mode);
if (error) {
error = -ENODEV;
printk("aml_nand_dma_read failed: page:%d \n", page);
goto exit;
}
aml_chip->aml_nand_get_user_byte(aml_chip,
oob_buf, user_byte_num);
stat = aml_chip->aml_nand_hwecc_correct(aml_chip,
buf, nand_page_size, oob_buf);
if (stat < 0) {
error = -ENODEV;
mtd->ecc_stats.failed++;
printk("read data ecc failed at page%d blk%d chip%d\n",
page, (page >> pages_per_blk_shift), i);
} else
mtd->ecc_stats.corrected += stat;
} else
error = -ENODEV;
exit:
return error;
}
/* mtd support interface:
* chip->ecc.write_page
* function:int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
* uint8_t *buf, int oob_required, int page);
*/
int m3_nand_boot_write_page_hwecc(struct mtd_info *mtd,
struct nand_chip *chip, const uint8_t *buf, int oob_required,
int page)
{
struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd);
uint8_t *oob_buf = chip->oob_poi;
unsigned nand_page_size = chip->ecc.steps * chip->ecc.size;
int user_byte_num = (chip->ecc.steps * aml_chip->user_byte_mode);
int error = 0, i = 0, bch_mode, ecc_size;
int each_boot_pages, boot_num;
#ifdef CONFIG_DISCRETE_BOOTLOADER
boot_num = CONFIG_BL2_COPY_NUM;
#else
boot_num = (!aml_chip->boot_copy_num)? 1: aml_chip->boot_copy_num;
#endif
each_boot_pages = BOOT_TOTAL_PAGES/boot_num;
ecc_size = chip->ecc.size;
if (((aml_chip->page_addr % each_boot_pages) == 0)
&& (aml_chip->bch_mode != NAND_ECC_BCH_SHORT)) {
nand_page_size = (mtd->writesize / 512) * NAND_ECC_UNIT_SHORT;
bch_mode = NAND_ECC_BCH_SHORT;
chip->ecc.size = NAND_ECC_UNIT_SHORT;
} else
bch_mode = aml_chip->bch_mode;
/* setting magic for romboot checks. */
for (i = 0; i < mtd->oobavail; i += 2) {
oob_buf[i] = 0x55;
oob_buf[i+1] = 0xaa;
}
i = 0;
if (aml_chip->valid_chip[i]) {
aml_chip->aml_nand_select_chip(aml_chip, i);
aml_chip->aml_nand_set_user_byte(aml_chip,
oob_buf, user_byte_num);
error = aml_chip->aml_nand_dma_write(aml_chip,
(unsigned char *)buf, nand_page_size, bch_mode);
if (error)
goto exit;
aml_chip->aml_nand_command(aml_chip,
NAND_CMD_PAGEPROG, -1, -1, i);
} else {
error = -ENODEV;
goto exit;
}
exit:
if (((aml_chip->page_addr % each_boot_pages) == 0)
&& (aml_chip->bch_mode != NAND_ECC_BCH_SHORT))
chip->ecc.size = ecc_size;
return error;
}
/* mtd support interface:
* chip->write_page
* function: int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
* uint32_t offset, int data_len, const uint8_t *buf,
* int oob_required, int page, int cached, int raw);
*/
int m3_nand_boot_write_page(struct mtd_info *mtd, struct nand_chip *chip,
uint32_t offset, int data_len, const uint8_t *buf,
int oob_required, int page, int raw)
{
struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd);
int status, write_page, ran_mode=0;
int en_slc = 0, each_boot_pages, boot_num;
loff_t ofs;
#ifdef CONFIG_DISCRETE_BOOTLOADER
boot_num = CONFIG_BL2_COPY_NUM;
#else
boot_num = (!aml_chip->boot_copy_num)? 1: aml_chip->boot_copy_num;
#endif
each_boot_pages = BOOT_TOTAL_PAGES / boot_num;
/* actual page to be written */
write_page = page;
/* zero page of each copy */
if ((write_page % each_boot_pages) == 0) {
nand_info_page_prepare(aml_chip, chip->buffers->databuf);
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, write_page);
/* must enable ran_mode for info page */
if (en_slc == 0) {
ran_mode = aml_chip->ran_mode;
aml_chip->ran_mode = 1;
}
chip->ecc.write_page(mtd, chip, chip->buffers->databuf, 0, 0);
if (en_slc == 0)
aml_chip->ran_mode = ran_mode;
status = chip->waitfunc(mtd, chip);
if (status & NAND_STATUS_FAIL) {
printk("uboot wr 0 page=0x%x, status=0x%x\n",
page, status);
return -EIO;
}
}
/* +1 for skipping nand info page */
if (en_slc) {
} else
write_page++;
WRITE_BAD_BLOCK:
ofs = (write_page << chip->page_shift);
if (!(ofs % mtd->erasesize)) {
if (chip->block_bad(mtd, ofs)) {
write_page +=
1 << (chip->phys_erase_shift-chip->page_shift);
goto WRITE_BAD_BLOCK;
}
}
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, write_page);
if (unlikely(raw))
chip->ecc.write_page_raw(mtd, chip, buf, 0, 0);
else
chip->ecc.write_page(mtd, chip, buf, 0, 0);
if (!(chip->options & NAND_CACHEPRG)) {
status = chip->waitfunc(mtd, chip);
if (status & NAND_STATUS_FAIL) {
printk("uboot wr page=0x%x, status=0x%x\n",
page,status);
return -EIO;
}
} else
status = chip->waitfunc(mtd, chip);
return 0;
}