| |
| |
| #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); |
| |
| 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; |
| |
| 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; |
| |
| 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; |
| |
| 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))) { |
| 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, |
| en_slc, len, addr ); |
| valid_pages = (en_slc)?(valid_pages>>1):valid_pages; |
| for (i = 1; |
| i < ((valid_pages*flash->pagesize)/len + 1); i++) { |
| if (((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); |
| |
| 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 >> 1)) == 0) { |
| uboot_set_ran_mode(phydev); |
| 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 |
| } 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)) |
| 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 >> 1)) == 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); |
| /* |
| if ((pages_per_blk_w != pages_per_blk) |
| || (configure_data_w != configure_data)){ |
| aml_nand_msg("boot check data failed, |
| and configure_data_w:0x%x, |
| pages_per_blk_w:0x%x", |
| configure_data_w, |
| pages_per_blk_w); |
| }*/ |
| 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; |
| #endif |
| continue; |
| } |
| |
| addr += flash->pagesize; |
| ops_para->data_buf += phydev->writesize; |
| readlen += phydev->writesize; |
| |
| 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; |
| } |
| |
| 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; |
| |
| 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; |
| 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; |
| 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))) |
| 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", |
| configure_data, |
| pages_per_blk); |
| |
| 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 += 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) { |
| 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)) |
| 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) { |
| 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; |
| 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++; |
| } |
| 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) |
| 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 * 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 funcion reads the u-boot envionment 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 */ |