931 lines
26 KiB
C
931 lines
26 KiB
C
/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
|
|
/*
|
|
* drivers/nand/phy/boot_operation.c
|
|
*
|
|
* Copyright (C) 2020 Amlogic, Inc. All rights reserved.
|
|
*
|
|
*/
|
|
|
|
#include "../include/phynand.h"
|
|
|
|
|
|
extern void uboot_set_ran_mode(struct amlnand_phydev *phydev);
|
|
extern void nand_boot_info_prepare(struct amlnand_phydev *phydev, unsigned char * page0_buf);
|
|
extern int mt_L95B_nand_check(struct amlnand_chip *aml_chip);
|
|
extern int mt_L85C_nand_check(struct amlnand_chip *aml_chip);
|
|
|
|
|
|
static int read_uboot(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);
|
|
struct en_slc_info *slc_info = &(controller->slc_info);
|
|
|
|
u32 configure_data, pages_per_blk, configure_data_w;
|
|
u32 pages_per_blk_w, page_size, tmp_size;
|
|
u8 tmp_bch_mode, tmp_user_mode, tmp_ecc_limit, tmp_ecc_max;
|
|
u16 tmp_ecc_unit, tmp_ecc_bytes, tmp_ecc_steps;
|
|
u64 addr, readlen = 0, len = 0;
|
|
int ret = 0;
|
|
u32 tmp_value, en_slc = 0, tmp_index;
|
|
u32 boot_num = 1, each_boot_pages , i;
|
|
u32 valid_pages = BOOT_COPY_NUM * BOOT_PAGES_PER_COPY;
|
|
|
|
// aml_nand_msg("devops->addr:%llx,devops->len:%llx,phydev->size:%llx",devops->addr,devops->len,phydev->size); //0,b8000,1000000
|
|
if ((devops->addr + devops->len) > phydev->size) {
|
|
aml_nand_msg("read uboot:out of space");
|
|
aml_nand_msg("addr:%llx len:%llx offset:%llx 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;
|
|
}
|
|
|
|
tmp_ecc_unit = controller->ecc_unit;
|
|
tmp_ecc_bytes = controller->ecc_bytes;
|
|
tmp_bch_mode = controller->bch_mode;
|
|
tmp_user_mode = controller->user_mode;
|
|
tmp_ecc_limit = controller->ecc_cnt_limit;
|
|
tmp_ecc_max = controller->ecc_max;
|
|
tmp_ecc_steps = controller->ecc_steps;
|
|
|
|
// aml_nand_msg("devops->ooblen:%x,controller->bch_mode:%d,phydev->offset:%llx",devops->ooblen,controller->bch_mode,phydev->offset); //0,7,0
|
|
|
|
if (controller->bch_mode == NAND_ECC_BCH_SHORT)
|
|
page_size = (flash->pagesize / 512) * NAND_ECC_UNIT_SHORT;
|
|
|
|
tmp_size = phydev->writesize;
|
|
/* phydev->writesize = page_size; */
|
|
|
|
/* 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_msg("addr:%llx,ops_para->data_buf:%p,ops_para->oob_buf:%p,ops_para->option:%d,len:%llx",addr,ops_para->data_buf,ops_para->oob_buf,ops_para->option,len);
|
|
if (devops->mode == NAND_SOFT_ECC)
|
|
ops_para->option |= DEV_ECC_SOFT_MODE;
|
|
|
|
if ((flash->new_type) &&
|
|
((flash->new_type < 10) || (flash->new_type == SANDISK_19NM) ||
|
|
(slc_info->micron_l0l3_mode == 1))) {
|
|
ops_para->option |= DEV_SLC_MODE;
|
|
en_slc = 1;
|
|
addr >>= 1;
|
|
}
|
|
|
|
len = ((((u32)len+flash->pagesize)-1)/flash->pagesize)*flash->pagesize;
|
|
aml_nand_msg("valid_pages=%d en_slc=%d len = %llx addr=%llx", valid_pages, /*1024,0,b8000, 0*/
|
|
en_slc, len, addr );
|
|
valid_pages = (en_slc)?(valid_pages>>1):valid_pages;
|
|
for (i = 1;
|
|
i < (((u64)valid_pages * flash->pagesize) / len + 1); i++) {
|
|
if ((((u64)valid_pages * flash->pagesize) / (2 * i) >= len) &&
|
|
boot_num < 4)
|
|
boot_num <<= 1;
|
|
else
|
|
break;
|
|
}
|
|
each_boot_pages = valid_pages/boot_num;
|
|
each_boot_pages = (en_slc)?(each_boot_pages<<1):each_boot_pages;
|
|
aml_nand_msg("boot_num = %d each_boot_pages = %d", boot_num,each_boot_pages); /*4,256*/
|
|
|
|
pages_per_blk = flash->blocksize / flash->pagesize;
|
|
configure_data = NFC_CMD_N2M(controller->ran_mode,
|
|
controller->bch_mode,
|
|
0,
|
|
(controller->ecc_unit >> 3),
|
|
controller->ecc_steps);
|
|
while (1) {
|
|
if ((((u32)addr / flash->pagesize) %
|
|
each_boot_pages) == 0) {
|
|
uboot_set_ran_mode(phydev);
|
|
page_size = (flash->pagesize / 512) *
|
|
NAND_ECC_UNIT_SHORT;
|
|
phydev->writesize = page_size;
|
|
//aml_nand_msg("phydev->writesize0:%x",phydev->writesize);
|
|
#if 1
|
|
controller->ecc_unit = NAND_ECC_UNIT_SHORT;
|
|
controller->ecc_bytes = NAND_BCH60_1K_ECC_SIZE;
|
|
controller->bch_mode = NAND_ECC_BCH_SHORT;
|
|
controller->user_mode = 2;
|
|
controller->ecc_cnt_limit = 55;
|
|
controller->ecc_max = 60;
|
|
controller->ecc_steps =
|
|
(flash->pagesize+flash->oobsize)/512;
|
|
#endif
|
|
} else
|
|
controller->ran_mode = 1;
|
|
|
|
/* controller->chip_num; */
|
|
ops_para->page_addr = ((u32)addr / flash->pagesize);
|
|
if ((ops_para->option & DEV_SLC_MODE)) {
|
|
tmp_value = ops_para->page_addr;
|
|
tmp_value &= (~((pages_per_blk >> 1) - 1));
|
|
/*128-->256; 256-->512 ......*/
|
|
tmp_value <<= 0x01;
|
|
tmp_index =
|
|
ops_para->page_addr % (pages_per_blk >> 1);
|
|
if ((flash->new_type > 0) && (flash->new_type < 10 ||
|
|
(slc_info->micron_l0l3_mode == 1)))
|
|
ops_para->page_addr = tmp_value |
|
|
(slc_info->pagelist[tmp_index]);
|
|
if (flash->new_type == SANDISK_19NM)
|
|
ops_para->page_addr = tmp_value |
|
|
(tmp_index << 1);
|
|
}
|
|
ret = operation->read_page(aml_chip);
|
|
if ((ops_para->ecc_err) || (ret < 0)) {
|
|
aml_nand_msg("fail page_addr:%d", ops_para->page_addr);
|
|
break;
|
|
}
|
|
|
|
/* check info page */
|
|
if ((!strncmp((char *)phydev->name,
|
|
NAND_BOOT_NAME,
|
|
strlen((const char *)NAND_BOOT_NAME)))
|
|
&& (((((u32)addr / flash->pagesize))%/*each_boot_pages*/
|
|
each_boot_pages) == 0)) {
|
|
controller->ran_mode = 1;
|
|
memcpy((u8 *)(&configure_data_w),
|
|
ops_para->data_buf,
|
|
sizeof(int));
|
|
memcpy((u8 *)(&pages_per_blk_w),
|
|
ops_para->data_buf+4,
|
|
sizeof(int));
|
|
|
|
aml_nand_msg("configure_data:%x, pages_per_blk:%x",
|
|
configure_data,
|
|
pages_per_blk);
|
|
|
|
addr += flash->pagesize;
|
|
#if 1
|
|
controller->ecc_unit = tmp_ecc_unit;
|
|
controller->ecc_bytes = tmp_ecc_bytes;
|
|
controller->bch_mode = tmp_bch_mode;
|
|
controller->user_mode = tmp_user_mode;
|
|
controller->ecc_cnt_limit = tmp_ecc_limit;
|
|
controller->ecc_max = tmp_ecc_max;
|
|
controller->ecc_steps = tmp_ecc_steps;
|
|
phydev->writesize = tmp_size;
|
|
|
|
/*aml_nand_msg("phydev->writesize1:%x,addr:%llx",phydev->writesize,addr); */
|
|
#endif
|
|
continue;
|
|
}
|
|
|
|
addr += flash->pagesize;
|
|
ops_para->data_buf += phydev->writesize;
|
|
readlen += phydev->writesize;
|
|
/* aml_nand_msg("phydev->writesize1:%x,addr:%llx,readlen:%llx,ops_para->data_buf:%p",phydev->writesize,addr,readlen,ops_para->data_buf); */
|
|
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 = NAND_BITFLIP_FAILURE;
|
|
}*/
|
|
}
|
|
|
|
controller->ran_mode = 1;
|
|
controller->ecc_unit = tmp_ecc_unit;
|
|
controller->ecc_bytes = tmp_ecc_bytes;
|
|
controller->bch_mode = tmp_bch_mode;
|
|
controller->user_mode = tmp_user_mode;
|
|
controller->ecc_cnt_limit = tmp_ecc_limit;
|
|
controller->ecc_max = tmp_ecc_max;
|
|
controller->ecc_steps = tmp_ecc_steps;
|
|
phydev->writesize = tmp_size;
|
|
|
|
return ret;
|
|
}
|
|
|
|
int roomboot_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);
|
|
u64 offset , write_len;
|
|
u8 *buffer, tmp_user_mode =0;
|
|
int ret = 0;
|
|
int oob_set = 0;
|
|
|
|
offset = devops->addr;
|
|
write_len = devops->len;
|
|
buffer = devops->datbuf;
|
|
|
|
memset(devops, 0x0, sizeof(struct phydev_ops));
|
|
devops->addr = offset;
|
|
devops->len = write_len;
|
|
devops->datbuf = buffer;
|
|
devops->oobbuf = NULL;
|
|
devops->mode = NAND_HW_ECC;
|
|
|
|
amlnand_get_device(aml_chip, CHIP_READING);
|
|
if (controller->oob_mod) {
|
|
oob_set = controller->oob_mod;
|
|
NFC_CLR_OOB_MODE(controller, 3<<26);
|
|
controller->oob_mod = 0;
|
|
tmp_user_mode = controller->user_mode;
|
|
controller->user_mode = 2;
|
|
}
|
|
|
|
//aml_nand_msg("devops->addr111:%llx,devops->len:%llx,devops->datbuf:%p",devops->addr,devops->len,devops->datbuf);
|
|
ret = read_uboot(phydev);
|
|
if (ret < 0)
|
|
aml_nand_dbg("nand read failed at %llx", devops->addr);
|
|
|
|
if (oob_set) {
|
|
controller->oob_mod = oob_set;
|
|
NFC_SET_OOB_MODE(controller, 3<<26);
|
|
controller->user_mode = tmp_user_mode;
|
|
}
|
|
|
|
amlnand_release_device(aml_chip);
|
|
return ret;
|
|
}
|
|
|
|
void _dump_mem_u8(uint8_t * buf, uint32_t len)
|
|
{
|
|
uint32_t i;
|
|
if (buf == NULL)
|
|
return;
|
|
printk("%s, %p, %d", __func__, buf, len);
|
|
for (i = 0; i < len/sizeof(uint8_t); i++) {
|
|
|
|
if ( i % 16 == 0)
|
|
printk("\n0x%p: ", buf+i);
|
|
printk("%02x ", buf[i]);
|
|
}
|
|
printk("\n");
|
|
return;
|
|
}
|
|
|
|
static int write_uboot(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);
|
|
struct en_slc_info *slc_info = &(controller->slc_info);
|
|
u8 *fill_buf = NULL;
|
|
u8 *oob_buf = NULL, *page0_buf = NULL;
|
|
u8 tmp_bch_mode, tmp_user_mode, tmp_ecc_limit, tmp_ecc_max;
|
|
u8 tmp_rand;
|
|
u32 configure_data, pages_per_blk;
|
|
u32 oobsize, page_size, tmp_size, priv_lsb, ops_tem;
|
|
u16 tmp_ecc_unit, tmp_ecc_bytes, tmp_ecc_steps;
|
|
u64 addr, writelen = 0, len = 0;
|
|
int i, ret = 0;
|
|
u8 *lazy_buf = devops->datbuf;
|
|
/* u8 *tmp_buf; */
|
|
char write_boot_status[BOOT_COPY_NUM] = {0}, err = 0;
|
|
u32 tmp_value, tmp_index, fill_len=0, boot_pages;
|
|
nand_page0_t * p_nand_page0 = NULL;
|
|
ext_info_t * p_ext_info = NULL;
|
|
unsigned char rand_val[4] = {0, 0, 0, 0}, rand_flag = 0;
|
|
u32 page_no =0;
|
|
|
|
BOOT_LINE
|
|
if ((devops->addr + devops->len) > phydev->size) {
|
|
aml_nand_msg("writeboot:out of space and addr:");
|
|
aml_nand_msg("%llx len:%llx offset:%llx 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;
|
|
}
|
|
|
|
if (devops->addr != 0) {
|
|
aml_nand_msg("addr do not equal zero here for uboot write");
|
|
return NAND_SUCCESS;
|
|
}
|
|
|
|
tmp_ecc_unit = controller->ecc_unit;
|
|
tmp_ecc_bytes = controller->ecc_bytes;
|
|
tmp_bch_mode = controller->bch_mode;
|
|
tmp_user_mode = controller->user_mode;
|
|
tmp_ecc_limit = controller->ecc_cnt_limit;
|
|
tmp_ecc_max = controller->ecc_max;
|
|
tmp_ecc_steps = controller->ecc_steps;
|
|
tmp_rand = controller->ran_mode;
|
|
|
|
if (controller->bch_mode == NAND_ECC_BCH_SHORT)
|
|
page_size = (flash->pagesize / 512) * NAND_ECC_UNIT_SHORT;
|
|
|
|
//oobsize = controller->ecc_steps*controller->user_mode;
|
|
oobsize = ((flash->pagesize+flash->oobsize)/512) *
|
|
controller->user_mode;/*for infopage0 oobsize >data page oobsize*/
|
|
BOOT_LINE
|
|
tmp_size = phydev->writesize;
|
|
/* phydev->writesize = page_size; */
|
|
|
|
amlnand_get_device(aml_chip, CHIP_WRITING);
|
|
|
|
oob_buf = aml_nand_malloc(oobsize);
|
|
if (oob_buf == NULL) {
|
|
aml_nand_msg("malloc failed and oobavail:%d", phydev->oobavail);
|
|
ret = -NAND_MALLOC_FAILURE;
|
|
goto error_exit;
|
|
}
|
|
memset(oob_buf, 0x0, oobsize);
|
|
|
|
page0_buf = aml_nand_malloc(flash->pagesize);
|
|
if (page0_buf == NULL) {
|
|
aml_nand_msg("malloc failed and oobavail:%d", flash->pagesize);
|
|
ret = -NAND_MALLOC_FAILURE;
|
|
goto error_exit;
|
|
}
|
|
memset(page0_buf, 0x0, flash->pagesize);
|
|
len = devops->len;
|
|
|
|
/*len = ((len + 0x200000 - 1) / 0x200000) * 0x200000 - flash->pagesize;*/
|
|
for (i = 0; i < oobsize; i += 2) {
|
|
oob_buf[i] = 0x55;
|
|
oob_buf[i+1] = 0xaa;
|
|
}
|
|
|
|
fill_len = flash->pagesize + flash->oobsize;
|
|
fill_buf = aml_nand_malloc(fill_len);
|
|
if (fill_buf == NULL) {
|
|
aml_nand_msg("malloc failed and oobavail:%d", fill_len);
|
|
ret = -NAND_MALLOC_FAILURE;
|
|
goto error_exit;
|
|
}
|
|
memset(fill_buf, 0xff, fill_len);
|
|
|
|
BOOT_LINE
|
|
/* 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->data_buf = devops->datbuf;
|
|
ops_para->oob_buf = oob_buf;
|
|
|
|
BOOT_LINE
|
|
|
|
if ((flash->new_type)
|
|
&& ((flash->new_type < 10)
|
|
|| (flash->new_type == SANDISK_19NM)
|
|
|| (slc_info->micron_l0l3_mode == 1)))
|
|
ops_para->option |= DEV_SLC_MODE;
|
|
|
|
configure_data = NFC_CMD_N2M(controller->ran_mode,
|
|
controller->bch_mode,
|
|
0,
|
|
(controller->ecc_unit >> 3),
|
|
controller->ecc_steps);
|
|
|
|
pages_per_blk = flash->blocksize / flash->pagesize;
|
|
aml_nand_msg("configure_data:%x, pages_per_blk:%x %x",
|
|
configure_data,
|
|
pages_per_blk, ops_para->option);
|
|
|
|
nand_boot_info_prepare(phydev, page0_buf);
|
|
BOOT_LINE
|
|
|
|
p_nand_page0 = (nand_page0_t *) page0_buf;
|
|
p_ext_info = &p_nand_page0->ext_info;
|
|
|
|
for (i = 0; i < p_ext_info->boot_num; i++) {
|
|
BOOT_LINE
|
|
writelen = 0;
|
|
addr = 0;
|
|
addr += (u64)flash->pagesize * p_ext_info->each_boot_pages * i;
|
|
boot_pages = p_ext_info->each_boot_pages;
|
|
if (ops_para->option & DEV_SLC_MODE) {
|
|
addr >>= 1;
|
|
boot_pages = p_ext_info->each_boot_pages >> 1;
|
|
}
|
|
ops_para->data_buf = lazy_buf;
|
|
devops->datbuf = lazy_buf;
|
|
while (1) {
|
|
if (((((u32)addr/flash->pagesize))%boot_pages) == 0) { /* first page special write */
|
|
uboot_set_ran_mode(phydev);
|
|
ops_para->data_buf = page0_buf;
|
|
|
|
page_size =
|
|
(flash->pagesize / 512) * NAND_ECC_UNIT_SHORT;
|
|
phydev->writesize = page_size;
|
|
#if 1
|
|
controller->ecc_unit = NAND_ECC_UNIT_SHORT;
|
|
controller->ecc_bytes = NAND_BCH60_1K_ECC_SIZE;
|
|
controller->bch_mode = NAND_ECC_BCH_SHORT;
|
|
controller->user_mode = 2;
|
|
controller->ecc_cnt_limit = 55;
|
|
controller->ecc_max = 60;
|
|
controller->ecc_steps =
|
|
(flash->pagesize+flash->oobsize)/512;
|
|
#endif
|
|
}
|
|
ops_para->page_addr = ((u32)addr / flash->pagesize);
|
|
ops_tem = ops_para->page_addr;
|
|
if ((ops_para->option & DEV_SLC_MODE)) {
|
|
tmp_value = ops_para->page_addr;
|
|
tmp_value &= (~((pages_per_blk >> 1) - 1));
|
|
/*128-->256; 256-->512 ......*/
|
|
tmp_value <<= 0x01;
|
|
tmp_index = ops_para->page_addr % (pages_per_blk >> 1);
|
|
if ((flash->new_type > 0)
|
|
&& (flash->new_type < 10
|
|
|| (flash->new_type == MICRON_20NM)))
|
|
ops_para->page_addr = tmp_value |
|
|
(slc_info->pagelist[tmp_index]);
|
|
if (flash->new_type == SANDISK_19NM)
|
|
ops_para->page_addr = tmp_value |
|
|
(tmp_index << 1);
|
|
}
|
|
#if 1
|
|
if (flash->new_type == HYNIX_1YNM ||
|
|
slc_info->micron_l0l3_mode == 1) {
|
|
if ((ops_tem % (pages_per_blk >> 1)) > 1) {
|
|
tmp_value = ops_tem;
|
|
tmp_value &= (~((pages_per_blk >> 1) - 1));
|
|
/*128-->256; 256-->512 ......*/
|
|
tmp_value <<= 0x01;
|
|
tmp_index = ops_tem % (pages_per_blk >> 1);
|
|
priv_lsb = tmp_value |
|
|
(slc_info->pagelist[tmp_index - 1]);
|
|
ops_tem = ops_para->page_addr;
|
|
rand_flag = 0;
|
|
if (flash->new_type == MICRON_20NM &&
|
|
ops_tem > (priv_lsb+1)) {
|
|
rand_val[0] = 0;
|
|
rand_flag = 1;
|
|
operation->set_onfi_para(aml_chip, rand_val, 0x92);
|
|
}
|
|
while (ops_tem > (priv_lsb+1)) {
|
|
ops_para->data_buf = fill_buf;
|
|
controller->bch_mode =
|
|
NAND_ECC_NONE;
|
|
controller->ran_mode = 0;
|
|
ops_para->page_addr =
|
|
priv_lsb+1;
|
|
operation->write_page(aml_chip);
|
|
priv_lsb++;
|
|
}
|
|
if (rand_flag == 1) {
|
|
rand_val[0] = 1;
|
|
operation->set_onfi_para(aml_chip, rand_val, 0x92);
|
|
}
|
|
ops_para->page_addr = ops_tem;
|
|
ops_para->data_buf = devops->datbuf;
|
|
controller->ecc_unit = tmp_ecc_unit;
|
|
controller->ecc_bytes = tmp_ecc_bytes;
|
|
controller->bch_mode = tmp_bch_mode;
|
|
controller->user_mode = tmp_user_mode;
|
|
controller->ecc_cnt_limit =
|
|
tmp_ecc_limit;
|
|
controller->ecc_max = tmp_ecc_max;
|
|
controller->ecc_steps = tmp_ecc_steps;
|
|
phydev->writesize = tmp_size;
|
|
controller->ran_mode = tmp_rand;
|
|
}
|
|
}
|
|
#endif
|
|
if (((((u32)addr / flash->pagesize)) %
|
|
boot_pages) != 0)
|
|
ops_para->data_buf = devops->datbuf;
|
|
ret = operation->write_page(aml_chip);
|
|
if (ret < 0) {
|
|
write_boot_status[i] = 1;
|
|
aml_nand_msg("fail page_addr:%d",
|
|
ops_para->page_addr);
|
|
break;
|
|
}
|
|
|
|
if ((((u32)addr / flash->pagesize) %
|
|
boot_pages) == 0) {
|
|
controller->ran_mode = 1;
|
|
addr += flash->pagesize;
|
|
ops_para->data_buf = devops->datbuf;
|
|
#if 1
|
|
controller->ecc_unit = tmp_ecc_unit;
|
|
controller->ecc_bytes = tmp_ecc_bytes;
|
|
controller->bch_mode = tmp_bch_mode;
|
|
controller->user_mode = tmp_user_mode;
|
|
controller->ecc_cnt_limit = tmp_ecc_limit;
|
|
controller->ecc_max = tmp_ecc_max;
|
|
controller->ecc_steps = tmp_ecc_steps;
|
|
phydev->writesize = tmp_size;
|
|
#endif
|
|
continue;
|
|
}
|
|
|
|
addr += flash->pagesize;
|
|
devops->datbuf += phydev->writesize;
|
|
writelen += phydev->writesize;
|
|
|
|
if ((writelen >= devops->len)
|
|
&& (writelen < phydev->erasesize))
|
|
devops->datbuf = fill_buf;
|
|
#if 0
|
|
if ((writelen >= (len-flash->pagesize))
|
|
|| ((ops_para->option & DEV_SLC_MODE)
|
|
&& ((u32)addr%(flash->blocksize>>1) == 0))
|
|
|| (((ops_para->option & DEV_SLC_MODE) == 0)
|
|
&& ((u32)addr%flash->blocksize == 0)))
|
|
break;
|
|
#else
|
|
if (writelen >= len) {
|
|
page_no = (u32)addr/ flash->pagesize;
|
|
page_no = page_no % L04A_PAGES_IN_BLK;
|
|
aml_nand_msg("page_no:%d",page_no);
|
|
if (flash->option & NAND_USE_SHAREPAGE_MODE) {
|
|
/*page_no = (u32)addr/ flash->pagesize;
|
|
page_no = page_no % L04A_PAGES_IN_BLK;
|
|
aml_nand_msg("page_no:%d",page_no);
|
|
*/
|
|
if ((page_no >= NAND_PAGE_NO1) && (page_no <= NAND_PAGE_NO2) &&
|
|
(page_no % 2 != 0)) {
|
|
/* memset(fill_buf, 0xa5, fill_len); */
|
|
ops_para->data_buf = fill_buf; /*fill 0xff*/
|
|
ops_para->page_addr = ((u32)addr / flash->pagesize);
|
|
aml_nand_msg("write upperpage next no:%d",ops_para->page_addr);
|
|
ret = operation->write_page(aml_chip);
|
|
if (ret < 0) {
|
|
write_boot_status[i] = 1;
|
|
aml_nand_msg("fail page_addr:%d",
|
|
ops_para->page_addr);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*********L95B*************/
|
|
if ((mt_L95B_nand_check(aml_chip) == 0)
|
|
|| (mt_L85C_nand_check(aml_chip) == 0)) {
|
|
int j = 0;
|
|
for (j = 0; j < 11; j++) {
|
|
memset(fill_buf, 0x5a,fill_len);
|
|
ops_para->data_buf = fill_buf;
|
|
ops_para->page_addr = ((u32)addr / flash->pagesize);
|
|
aml_nand_msg("write extra page: %d",ops_para->page_addr);
|
|
ret = operation->write_page(aml_chip);
|
|
if (ret < 0) {
|
|
write_boot_status[i] = 1;
|
|
aml_nand_msg("write fail page_addr:%d",
|
|
ops_para->page_addr);
|
|
break;
|
|
}
|
|
addr += flash->pagesize;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
|
|
#endif
|
|
}
|
|
}
|
|
BOOT_LINE
|
|
for (i = 0; i < p_ext_info->boot_num; i++)
|
|
err += write_boot_status[i];
|
|
|
|
if (err < 2)
|
|
ret = 0;
|
|
else
|
|
ret = 1;
|
|
devops->retlen = writelen;
|
|
|
|
error_exit:
|
|
amlnand_release_device(aml_chip);
|
|
controller->ran_mode = 1;
|
|
controller->ecc_unit = tmp_ecc_unit;
|
|
controller->ecc_bytes = tmp_ecc_bytes;
|
|
controller->bch_mode = tmp_bch_mode;
|
|
controller->user_mode = tmp_user_mode;
|
|
controller->ecc_cnt_limit = tmp_ecc_limit;
|
|
controller->ecc_max = tmp_ecc_max;
|
|
controller->ecc_steps = tmp_ecc_steps;
|
|
phydev->writesize = tmp_size;
|
|
kfree(fill_buf);
|
|
fill_buf = NULL;
|
|
|
|
kfree(page0_buf);
|
|
page0_buf = NULL;
|
|
kfree(oob_buf);
|
|
oob_buf = NULL;
|
|
return ret;
|
|
}
|
|
|
|
int roomboot_nand_write(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 offset , write_len, addr;
|
|
u8 *buffer, tmp_user_mode =0;
|
|
int ret = 0;
|
|
int oob_set = 0;
|
|
|
|
offset = devops->addr;
|
|
write_len = devops->len;
|
|
buffer = devops->datbuf;
|
|
|
|
if (offset != 0) {
|
|
aml_nand_msg("Wrong addr begin");
|
|
return -1;
|
|
}
|
|
|
|
/*align with page size*/
|
|
write_len = ((((u32)write_len + phydev->writesize)-1)/
|
|
phydev->writesize)*phydev->writesize;
|
|
|
|
if ((offset & (phydev->writesize - 1)) != 0
|
|
|| (write_len & (phydev->writesize - 1)) != 0) {
|
|
aml_nand_msg("Attempt to write non page aligned data\n");
|
|
return -NAND_WRITE_FAILED;
|
|
}
|
|
|
|
BOOT_LINE
|
|
|
|
if ((offset + write_len) > phydev->size) {
|
|
aml_nand_msg("Attemp to write out side the dev area");
|
|
return -NAND_WRITE_FAILED;
|
|
}
|
|
|
|
if (controller->oob_mod) {
|
|
oob_set = controller->oob_mod;
|
|
NFC_CLR_OOB_MODE(controller, 3<<26);
|
|
controller->oob_mod = 0;
|
|
tmp_user_mode = controller->user_mode;
|
|
controller->user_mode = 2;
|
|
}
|
|
|
|
memset(ops_para, 0, sizeof(struct chip_ops_para));
|
|
addr = phydev->offset + devops->addr;
|
|
ops_para->chipnr = 0;
|
|
ops_para->option = phydev->option;
|
|
if ((flash->new_type) && (flash->new_type == SANDISK_19NM))
|
|
ops_para->option |= DEV_SLC_MODE;
|
|
|
|
do {
|
|
ops_para->page_addr = ((u32)addr / flash->pagesize);
|
|
controller->select_chip(controller, ops_para->chipnr);
|
|
ret = operation->block_isbad(aml_chip);
|
|
if (ret == NAND_BLOCK_FACTORY_BAD) {
|
|
aml_nand_msg("blk is shipped bad block at page %d",
|
|
ops_para->page_addr);
|
|
addr += phydev->erasesize;
|
|
continue;
|
|
}
|
|
BOOT_LINE
|
|
nand_get_chip(aml_chip);
|
|
BOOT_LINE
|
|
ret = operation->erase_block(aml_chip);
|
|
BOOT_LINE
|
|
nand_release_chip(aml_chip);
|
|
if (ret < 0)
|
|
aml_nand_msg("nand erase fail at addr page %d",
|
|
ops_para->page_addr);
|
|
/* break; */
|
|
addr += phydev->erasesize;
|
|
} while (addr < (1024 * (u64)flash->pagesize));
|
|
BOOT_LINE
|
|
memset(devops, 0x0, sizeof(struct phydev_ops));
|
|
devops->addr = offset;
|
|
devops->len = write_len;
|
|
devops->datbuf = buffer; //!!
|
|
devops->oobbuf = NULL;
|
|
devops->mode = NAND_HW_ECC;
|
|
|
|
aml_nand_dbg("devops->addr =%llx", devops->addr);
|
|
aml_nand_dbg("devops->len =%llx", devops->len);
|
|
BOOT_LINE
|
|
ret = write_uboot(phydev);
|
|
BOOT_LINE
|
|
if (ret < 0) {
|
|
aml_nand_dbg("nand write failed at %llx", devops->addr);
|
|
goto exit_error0;
|
|
}
|
|
if (oob_set) {
|
|
controller->oob_mod = oob_set;
|
|
NFC_SET_OOB_MODE(controller, 3<<26);
|
|
controller->user_mode = tmp_user_mode;
|
|
}
|
|
return ret;
|
|
exit_error0:
|
|
return ret;
|
|
}
|
|
|
|
#ifndef AML_NAND_UBOOT
|
|
static int uboot_open(struct inode * inode, struct file * filp)
|
|
{
|
|
aml_nand_dbg("uboot_open");
|
|
return 0;
|
|
}
|
|
/*
|
|
* This function reads the u-boot environment variables.
|
|
* The f_pos points directly to the env location.
|
|
*/
|
|
static ssize_t uboot_read(struct file *file,
|
|
char __user *buf,
|
|
size_t count,
|
|
loff_t *ppos)
|
|
{
|
|
struct amlnand_phydev *phydev = uboot_phydev;
|
|
struct amlnand_chip *aml_chip = phydev->priv;
|
|
struct phydev_ops *devops = &(phydev->ops);
|
|
|
|
u8 *data_buf;
|
|
int ret;
|
|
size_t align_count = 0;
|
|
align_count =
|
|
((((u32)count + phydev->writesize)-1)/phydev->writesize)
|
|
*phydev->writesize;
|
|
data_buf = aml_nand_malloc(align_count);
|
|
if (!data_buf) {
|
|
aml_nand_dbg("malloc buf for rom_write failed");
|
|
goto err_exit0;
|
|
}
|
|
memset(data_buf, 0x0, align_count);
|
|
|
|
|
|
memset(devops, 0x0, sizeof(struct phydev_ops));
|
|
devops->addr = 0x0;
|
|
/* devops->len = UBOOT_WRITE_SIZE; */
|
|
devops->len = align_count;
|
|
devops->mode = NAND_HW_ECC;
|
|
devops->datbuf = data_buf;
|
|
amlnand_get_device(aml_chip, CHIP_WRITING);
|
|
|
|
ret = roomboot_nand_read(phydev);
|
|
if (ret < 0) {
|
|
aml_nand_dbg("uboot_write failed");
|
|
count = 0;
|
|
}
|
|
amlnand_release_device(aml_chip);
|
|
ret = copy_to_user(buf, data_buf, count);
|
|
err_exit0:
|
|
|
|
aml_nand_free(data_buf);
|
|
data_buf = NULL;
|
|
|
|
return count;
|
|
}
|
|
|
|
static ssize_t uboot_write(struct file *file, const char __user *buf,
|
|
size_t count, loff_t *ppos)
|
|
{
|
|
struct amlnand_phydev *phydev = uboot_phydev;
|
|
struct amlnand_chip *aml_chip = phydev->priv;
|
|
struct phydev_ops *devops = &(phydev->ops);
|
|
|
|
u8 *data_buf;
|
|
int ret;
|
|
size_t align_count = 0;
|
|
align_count =
|
|
((((u32)count + phydev->writesize)-1)/phydev->writesize)
|
|
*phydev->writesize;
|
|
data_buf = aml_nand_malloc(align_count);
|
|
if (!data_buf) {
|
|
aml_nand_dbg("malloc buf for rom_write failed");
|
|
goto err_exit0;
|
|
}
|
|
memset(data_buf, 0x0, align_count);
|
|
ret = copy_from_user(data_buf, buf, count);
|
|
|
|
memset(devops, 0x0, sizeof(struct phydev_ops));
|
|
devops->addr = 0x0;
|
|
devops->len = align_count;
|
|
devops->mode = NAND_HW_ECC;
|
|
devops->datbuf = data_buf;
|
|
amlnand_get_device(aml_chip, CHIP_WRITING);
|
|
|
|
ret = roomboot_nand_write(phydev);
|
|
if (ret < 0) {
|
|
aml_nand_dbg("uboot_write failed");
|
|
count = 0;
|
|
}
|
|
amlnand_release_device(aml_chip);
|
|
|
|
err_exit0:
|
|
|
|
if (data_buf) {
|
|
aml_nand_free(data_buf);
|
|
data_buf = NULL;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static int uboot_close(struct inode *inode, struct file *file)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int uboot_suspend(struct device *dev, pm_message_t state)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int uboot_resume(struct device *dev)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
|
|
static struct class uboot_class = {
|
|
|
|
.name = "bootloader",
|
|
.owner = THIS_MODULE,
|
|
.suspend = uboot_suspend,
|
|
.resume = uboot_resume,
|
|
};
|
|
|
|
static const struct file_operations uboot_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = uboot_open,
|
|
.read = uboot_read,
|
|
.write = uboot_write,
|
|
.release = uboot_close,
|
|
};
|
|
|
|
int boot_device_register(struct amlnand_phydev *phydev)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (!strncmp((char *)phydev->name,
|
|
NAND_BOOT_NAME,
|
|
strlen((const char *)NAND_BOOT_NAME))) {
|
|
|
|
aml_nand_dbg("boot device register");
|
|
|
|
uboot_phydev = phydev;
|
|
ret = alloc_chrdev_region(&uboot_devno, 0, 1, "bootloader");
|
|
if (ret < 0) {
|
|
aml_nand_dbg("uboot_phydev:failed to allocate chrdev.");
|
|
goto exit_error0;
|
|
}
|
|
cdev_init(&uboot_phydev->uboot_cdev, &uboot_fops);
|
|
uboot_phydev->uboot_cdev.owner = THIS_MODULE;
|
|
ret = cdev_add(&uboot_phydev->uboot_cdev, uboot_devno, 1);
|
|
if (ret) {
|
|
aml_nand_dbg("uboot_phydev: failed to add device.");
|
|
goto exit_error0;
|
|
}
|
|
|
|
ret = class_register(&uboot_class);
|
|
if (ret < 0) {
|
|
aml_nand_dbg("class_register(&uboot_class) failed!\n");
|
|
goto exit_error0;
|
|
}
|
|
|
|
devp = device_create(&uboot_class,
|
|
NULL,
|
|
uboot_devno,
|
|
NULL,
|
|
"bootloader");
|
|
if (IS_ERR(devp)) {
|
|
aml_nand_dbg("uboot_phydev:fail to create node\n");
|
|
ret = PTR_ERR(devp);
|
|
goto exit_error0;
|
|
}
|
|
}
|
|
return NAND_SUCCESS;
|
|
exit_error0:
|
|
return ret;
|
|
}
|
|
#endif /* AML_NAND_UBOOT */
|