/* SPDX-License-Identifier: GPL-2.0 */ /** * rawnand_chip.c * * Copyright (C) 2019 Allwinner. * * 2019.9.11 cuizhikui * from * eNand * nand flash driver scan module * Copyright(C),2008-2009, SoftWinners Microelectronic Co.,Ltd. * All Rigths Reserved * File name: nand_chip_function.c/nand_chip_common.c/nand_chip_interface.c * nand_super_chip_function.c/nand_super_chip_common.c/ * nand_super_chip_interface.c * Author : * Version : * Date : 2013-11-20 * Description : * Others : None at present * */ #include "../../nfd/nand_osal_for_linux.h" #include "../nand_errno.h" #include "controller/ndfc_base.h" #include "rawnand.h" #include "rawnand_base.h" #include "rawnand_cfg.h" #include "rawnand_debug.h" #include "rawnand_ids.h" #include "rawnand_ops.h" #include "rawnand_readretry.h" #include "controller/ndfc_base.h" #include "controller/ndfc_ops.h" #include "rawnand_boot.h" #include #define SECTOR_SIZE 512 //the size of a sector, based on byte struct _nand_storage_info *g_nsi; struct _nand_storage_info g_nsi_data; rawnand_storage_info_t *g_nand_storage_info; rawnand_storage_info_t g_nand_storage_info_data; struct nand_chip_info nci_data[MAX_CHIP_PER_CHANNEL * 2]; struct _nand_super_storage_info *g_nssi; struct _nand_super_storage_info g_nssi_data = {0}; struct nand_super_chip_info nsci_data[MAX_CHIP_PER_CHANNEL * 2] = {0}; /* **Name : nci_add_to_nsi **Description : **Parameter : **Return : 0:ok -1:fail **Note : */ int nci_add_to_nsi(struct _nand_storage_info *nsi, struct nand_chip_info *node) { struct nand_chip_info *nci; node->chip_no = 0; if (nsi->nci == NULL) { nsi->nci = node; return NAND_OP_TRUE; } nci = nsi->nci; node->chip_no = 1; while (nci->nsi_next != NULL) { nci = nci->nsi_next; node->chip_no++; } nci->nsi_next = node; return NAND_OP_TRUE; } /** * nci_delete_from_nsi: */ void nci_delete_from_nsi(struct _nand_storage_info *nsi) { struct nand_chip_info *tail = NULL; struct nand_chip_info *next = NULL; RAWNAND_DBG("%s %d nsi->nci:%p\n", __func__, __LINE__, nsi->nci); for (tail = nsi->nci; tail != NULL; tail = next) { next = tail->nsi_next; nand_free(tail); } nsi->nci = NULL; } void delete_nsi(void) { /*nci delete in nctri*/ /*nci_delete_from_nsi(g_nsi);*/ g_nsi = NULL; } /* **Name : **Description : **Parameter : **Return : 0:ok -1:fail **Note : */ int nsci_add_to_nssi(struct _nand_super_storage_info *nssi, struct nand_super_chip_info *node) { struct nand_super_chip_info *nsci; if (nssi == NULL) { RAWNAND_ERR("%s nssi is null\n", __func__); return ERR_NO_12; } node->chip_no = 0; if (nssi->nsci == NULL) { nssi->nsci = node; return NAND_OP_TRUE; } node->chip_no = 1; nsci = nssi->nsci; while (nsci->nssi_next != NULL) { nsci = nsci->nssi_next; node->chip_no++; } nsci->nssi_next = node; return NAND_OP_TRUE; } int nsci_delete_from_nssi(struct nand_super_chip_info *node) { struct nand_super_chip_info *tail = NULL; struct nand_super_chip_info *next = NULL; struct nand_super_chip_info *prev = NULL; if (g_nssi == NULL) { RAWNAND_ERR("%s g_nssi is null\n", __func__); return ERR_NO_12; } node->chip_no = 0; if (node == g_nssi->nsci) { g_nssi->nsci = g_nssi->nsci->nssi_next; return NAND_OP_TRUE; } for (tail = g_nssi->nsci; tail != NULL; tail = next) { next = tail->nssi_next; if (node == tail) { prev->nssi_next = tail->nssi_next; tail->chip_no = 0; } prev = tail; } return NAND_OP_TRUE; } int delete_nssi(void) { struct nand_super_chip_info *tail = NULL; struct nand_super_chip_info *next = NULL; if (g_nssi == NULL) { RAWNAND_ERR("%s g_nssi is null\n", __func__); return ERR_NO_12; } for (tail = g_nssi->nsci; tail != NULL; tail = next) { next = tail->nssi_next; tail->chip_no = 0; g_nssi->nsci = next; } g_nssi = NULL; return NAND_OP_TRUE; } /* **Name : **Description : **Parameter : **Return : 0:ok -1:fail **Note : */ int nci_add_to_nctri(struct nand_controller_info *nctri, struct nand_chip_info *node) { struct nand_chip_info *nci; node->nctri_chip_no = 0; if (nctri->nci == NULL) { nctri->nci = node; return NAND_OP_TRUE; } nci = nctri->nci; node->nctri_chip_no = 1; while (nci->nctri_next != NULL) { nci = nci->nctri_next; node->nctri_chip_no++; } nci->nctri_next = node; return NAND_OP_TRUE; } /** * free nci in nctri list * */ void nci_delete_from_nctri(struct nand_controller_info *nctri) { struct nand_chip_info *tail = NULL; struct nand_chip_info *next = NULL; for (tail = nctri->nci; tail != NULL; tail = next) { next = tail->nctri_next; nand_free(tail); } nctri->nci = NULL; } /* *Name : *Description : *Parameter : *Return : 0:ok -1:fail *Note : */ struct nand_chip_info *nci_get_from_nctri(struct nand_controller_info *nctri, unsigned int num) { int i; struct nand_chip_info *nci; for (i = 0, nci = nctri->nci; i < num; i++) nci = nci->nctri_next; return nci; } /* *Name : *Description : *Parameter : *Return : 0:ok -1:fail *Note : */ struct nand_chip_info *nci_get_from_nsi(struct _nand_storage_info *nsi, unsigned int num) { int i; struct nand_chip_info *nci; for (i = 0, nci = nsi->nci; i < num; i++) nci = nci->nsi_next; return nci; } /* *Name : *Description : *Parameter : *Return : 0:ok -1:fail *Note : */ struct nand_super_chip_info *nsci_get_from_nssi(struct _nand_super_storage_info *nssi, unsigned int num) { int i; struct nand_super_chip_info *nsci; for (i = 0, nsci = nssi->nsci; i < num; i++) nsci = nsci->nssi_next; return nsci; } /* *Name : *Description : *Parameter : *Return : 0:ok -1:fail *Note : */ int init_nci_from_id(struct nand_chip_info *nci, struct sunxi_nand_flash_device *npi) { memcpy(nci->id, npi->id, 8); nci->npi = npi; nci->opt_phy_op_par = &phy_op_para[npi->cmd_set_no]; nci->nfc_init_ddr_info = &def_ddr_info[npi->ddr_info_no]; nci->blk_cnt_per_chip = npi->die_cnt_per_chip * npi->blk_cnt_per_die; nci->sector_cnt_per_page = npi->sect_cnt_per_page; nci->page_cnt_per_blk = npi->page_cnt_per_blk; nci->page_offset_for_next_blk = npi->page_cnt_per_blk; nci->ecc_mode = npi->ecc_mode; nci->max_erase_times = npi->max_blk_erase_times; // nci->driver_no = npi->driver_no; nci->randomizer = (npi->operation_opt & NAND_RANDOM) ? 1 : 0; nci->lsb_page_type = ((npi->operation_opt) & NAND_LSB_PAGE_TYPE) >> 12; nci->multi_plane_block_offset = npi->multi_plane_block_offset; nci->bad_block_flag_position = npi->bad_block_flag_position; nci->random_cmd2_send_flag = npi->random_cmd2_send_flag; nci->random_addr_num = npi->random_addr_num; nci->nand_real_page_size = npi->nand_real_page_size; /* nand interface */ nci->interface_type = npi->ddr_type; //0x0: sdr; 0x2: nvddr; 0x3: tgddr; 0x12: nvddr2; 0x13: tgddr2; if (g_phy_cfg->phy_interface_cfg == 1) { if ((nci->interface_type == 0x3) || (nci->interface_type == 0x13)) { nci->interface_type = 0x03; } else if ((nci->interface_type == 0x2) || (nci->interface_type == 0x12)) { nci->interface_type = 0x02; } else { nci->interface_type = 0x00; } } /* *if (npi->operation_opt & NAND_PAIRED_PAGE_SYNC) * NAND_Check_3DNand(); */ if (npi->ddr_opt & NAND_VCCQ_1P8V) { nand_vccq_1p8v_enable(); } nci->frequency = npi->access_freq; #ifdef FPGA_PLATFORM nci->frequency = 6; #endif nci->timing_mode = 0x0; nci->itf_cfg.onfi_cfg.support_change_onfi_timing_mode = (npi->ddr_opt & NAND_ONFI_TIMING_MODE) ? 1 : 0; nci->itf_cfg.onfi_cfg.support_ddr2_specific_cfg = (npi->ddr_opt & NAND_ONFI_DDR2_CFG) ? 1 : 0; nci->itf_cfg.onfi_cfg.support_io_driver_strength = (npi->ddr_opt & NAND_ONFI_IO_DRIVER_STRENGTH) ? 1 : 0; nci->itf_cfg.onfi_cfg.support_rb_pull_down_strength = (npi->ddr_opt & NAND_ONFI_RB_STRENGTH) ? 1 : 0; nci->support_toggle_only = (npi->operation_opt & NAND_TOGGLE_ONLY) ? 1 : 0; nci->itf_cfg.toggle_cfg.support_specific_setting = (npi->ddr_opt & NAND_TOGGLE_SPECIFIC_CFG) ? 1 : 0; nci->itf_cfg.toggle_cfg.support_io_driver_strength_setting = (npi->ddr_opt & NAND_TOGGLE_IO_DRIVER_STRENGTH) ? 1 : 0; nci->itf_cfg.toggle_cfg.support_vendor_specific_setting = (npi->ddr_opt & NAND_TOGGLE_VENDOR_SPECIFIC_CFG) ? 1 : 0; nci->ecc_sector = 1; nci->sdata_bytes_per_page = (nci->sector_cnt_per_page >> nci->ecc_sector) * 4; if (nci->sdata_bytes_per_page > 16) nci->sdata_bytes_per_page = 16; nci->nand_physic_erase_block = rawnand_ops.erase_single_block; nci->nand_physic_read_page = rawnand_ops.read_single_page; nci->nand_physic_write_page = rawnand_ops.write_single_page; nci->nand_physic_bad_block_check = rawnand_ops.single_bad_block_check; nci->nand_physic_bad_block_mark = rawnand_ops.single_bad_block_mark; nci->nand_read_boot0_page = rawnand_boot0_ops.read_boot0_page; nci->nand_write_boot0_page = rawnand_boot0_ops.write_boot0_page; nci->nand_read_boot0_one = rawnand_boot0_ops.read_boot0_one; if (rawnand_boot0_ops.write_boot0_one == NULL) { selected_write_boot0_one(nci->npi->selected_write_boot0_no); } nci->nand_write_boot0_one = rawnand_boot0_ops.write_boot0_one; nci->is_lsb_page = chose_lsb_func(((nci->npi->operation_opt >> LSB_PAGE_POS) & 0xff)); nci->sharedpage_pairedwrite = (nci->npi->operation_opt & NAND_PAIRED_PAGE_SYNC) ? 1 : 0; nci->sharedpage_offset = nci->npi->sharedpage_offset; /*according to id table, update the interface setting bind*/ rawnand_update_timings_ift_ops(nci->npi->mfr_id); return NAND_OP_TRUE; } /* *Name : *Description : *Parameter : *Return : 0:ok -1:fail *Note : */ int init_nsci_from_nctri(struct _nand_super_storage_info *nssi, struct nand_super_chip_info *nsci, struct nand_controller_info *nctri, unsigned int channel_num, unsigned int chip_no, unsigned int nsci_num_in_nctri) { int rb1, rb0; struct nand_chip_info *nci; if (chip_no >= nsci_num_in_nctri) { chip_no -= nsci_num_in_nctri; nctri = nctri->next; } if (channel_num == 1) { nsci->channel_num = 1; nsci->dual_channel = 0; nssi->support_dual_channel = 0; nci = nci_get_from_nctri(nctri, chip_no); nsci->nci_first = nci; nci->nsci = nsci; } else { /*must be channel_num == 2*/ nsci->channel_num = 2; nci = nci_get_from_nctri(nctri, chip_no); nsci->nci_first = nci; nci->nsci = nsci; if (nssi->support_dual_channel != 0) { nsci->dual_channel = 1; nci = nci_get_from_nctri(nctri, chip_no); nsci->d_channel_nci_1 = nci; nci->nsci = nsci; nci = nci_get_from_nctri(nctri->next, chip_no); nsci->d_channel_nci_2 = nci; nci->nsci = nsci; } } if (nssi->support_v_interleave != 0) { nsci->vertical_interleave = 1; if (nctri->chip_cnt == 2) { nci = nci_get_from_nctri(nctri, chip_no); nsci->v_intl_nci_1 = nci; nci->nsci = nsci; nci = nci_get_from_nctri(nctri, chip_no + 1); nsci->v_intl_nci_2 = nci; nci->nsci = nsci; } else { /*must be nctri->chip_cnt == 4*/ rb0 = nctri->rb[0]; rb1 = nctri->rb[1]; // rb2 = nctri->rb[2]; // rb3 = nctri->rb[3]; if (rb0 == rb1) { /*rb0 == rb1*/ nci = nci_get_from_nctri(nctri, chip_no); nsci->v_intl_nci_1 = nci; nci->nsci = nsci; nci = nci_get_from_nctri(nctri, chip_no + 2); nsci->v_intl_nci_2 = nci; nci->nsci = nsci; } else { /*must be rb0 == rb2*/ nci = nci_get_from_nctri(nctri, (chip_no << 1)); nsci->v_intl_nci_1 = nci; nci->nsci = nsci; nci = nci_get_from_nctri(nctri, (chip_no << 1) + 1); nsci->v_intl_nci_2 = nci; nci->nsci = nsci; } } } nsci->driver_no = nsci->nci_first->driver_no; nsci->blk_cnt_per_super_chip = nctri->nci->blk_cnt_per_chip; nsci->sector_cnt_per_super_page = nctri->nci->sector_cnt_per_page; nsci->page_cnt_per_super_blk = nctri->nci->page_cnt_per_blk; nsci->page_offset_for_next_super_blk = nctri->nci->page_offset_for_next_blk; if (nsci->dual_channel == 1) { nsci->sector_cnt_per_super_page <<= 1; } if ((nssi->support_two_plane != 0) && (nsci->nci_first->npi->operation_opt & NAND_MULTI_PROGRAM) && (nsci->nci_first->multi_plane_block_offset == 1)) { nsci->two_plane = 1; } else { nsci->two_plane = 0; } if (nsci->sector_cnt_per_super_page > MAX_SECTORS_PER_PAGE_FOR_TWO_PLANE) { nsci->two_plane = 0; } if (nctri->nci->sector_cnt_per_page == 4) { nsci->two_plane = 1; } if (nsci->two_plane == 1) { nsci->blk_cnt_per_super_chip >>= 1; nsci->sector_cnt_per_super_page <<= 1; } else { nssi->support_two_plane = 0; } if (nssi->support_two_plane == 0) { nssi->plane_cnt = 1; } else { nssi->plane_cnt = 2; } if (nsci->vertical_interleave == 1) { nsci->page_cnt_per_super_blk <<= 1; nsci->page_offset_for_next_super_blk <<= 1; } nsci->spare_bytes = 16; nsci->nand_physic_erase_super_block = rawnand_ops.erase_super_block; nsci->nand_physic_read_super_page = rawnand_ops.read_super_page; nsci->nand_physic_write_super_page = rawnand_ops.write_super_page; nsci->nand_physic_super_bad_block_check = rawnand_ops.super_bad_block_check; nsci->nand_physic_super_bad_block_mark = rawnand_ops.super_bad_block_mark; return NAND_OP_TRUE; } /* *Name : *Description : *Parameter : *Return : 0:ok -1:fail *Note : */ void nand_enable_chip(struct nand_chip_info *nci) { struct nand_controller_info *nctri = nci->nctri; unsigned int chip_no = nci->nctri_chip_no; ndfc_select_chip(nctri, nctri->ce[chip_no]); ndfc_select_rb(nctri, nctri->rb[chip_no]); } /* *Name : *Description : *Parameter : *Return : 0:ok -1:fail *Note : */ void nand_disable_chip(struct nand_chip_info *nci) { // select invalid CE signal, disable all chips ndfc_select_chip(nci->nctri, 0xf); // select invalid RB signal, no any more RB busy to ready interrupt ndfc_select_rb(nci->nctri, 0x3); return; } /* *Name : *Description : *Parameter : *Return : 0:ok -1:fail *Note : */ int nand_reset_chip(struct nand_chip_info *nci) { int ret = 0; struct _nctri_cmd_seq *cmd_seq = &nci->nctri->nctri_cmd_seq; nand_enable_chip(nci); ndfc_clean_cmd_seq(cmd_seq); cmd_seq->cmd_type = CMD_TYPE_NORMAL; cmd_seq->nctri_cmd[0].cmd_valid = 1; cmd_seq->nctri_cmd[0].cmd = CMD_RESET; //??? don't support onfi ddr interface cmd_seq->nctri_cmd[0].cmd_send = 1; cmd_seq->nctri_cmd[0].cmd_wait_rb = 1; ret = ndfc_execute_cmd(nci->nctri, cmd_seq); if (ret) { RAWNAND_ERR("nand_reset_chip, reset failed!\n"); } nand_disable_chip(nci); return ret; } /* *Name : *Description : *Parameter : *Return : 0:ok -1:fail *Note : */ int nand_sync_reset_chip(struct nand_chip_info *nci) { int ret = 0; struct _nctri_cmd_seq *cmd_seq = &nci->nctri->nctri_cmd_seq; nand_enable_chip(nci); ndfc_clean_cmd_seq(cmd_seq); cmd_seq->cmd_type = CMD_TYPE_NORMAL; cmd_seq->nctri_cmd[0].cmd_valid = 1; cmd_seq->nctri_cmd[0].cmd = CMD_SYNC_RESET; //support onfi ddr interface cmd_seq->nctri_cmd[0].cmd_send = 1; cmd_seq->nctri_cmd[0].cmd_wait_rb = 1; ret = ndfc_execute_cmd(nci->nctri, cmd_seq); if (ret) { RAWNAND_ERR("nand_reset_chip, reset failed!\n"); } nand_disable_chip(nci); return ret; } /* *Name : *Description : *Parameter : *Return : 0:ok -1:fail *Note : */ int nand_first_reset_chip(struct nand_chip_info *nci, unsigned int chip_no) { int ret = 0; struct _nctri_cmd_seq *cmd_seq = &nci->nctri->nctri_cmd_seq; // enable chip, don't select rb signal ndfc_select_chip(nci->nctri, chip_no); ndfc_clean_cmd_seq(cmd_seq); cmd_seq->cmd_type = CMD_TYPE_NORMAL; cmd_seq->nctri_cmd[0].cmd_valid = 1; cmd_seq->nctri_cmd[0].cmd = CMD_RESET; //??? don't support onfi ddr interface cmd_seq->nctri_cmd[0].cmd_send = 1; cmd_seq->nctri_cmd[0].cmd_wait_rb = 0; ret = ndfc_execute_cmd(nci->nctri, cmd_seq); if (ret) { RAWNAND_ERR("nand_first_reset_chip, reset failed!\n"); goto ERROR; } // wait all rb ready, because we don't know which rb sigal connect to current chip ret = ndfc_wait_all_rb_ready(nci->nctri); if (ret) { RAWNAND_ERR("nand_first_reset_chip, ndfc_wait_all_rb_ready timeout\n"); goto ERROR; } nand_disable_chip(nci); return NAND_OP_TRUE; ERROR: nand_disable_chip(nci); return NAND_OP_FALSE; } /* *Name : *Description : *Parameter : *Return : 0:ok -1:fail *Note : */ int nand_read_chip_status(struct nand_chip_info *nci, u8 *pstatus) { int ret = 0; struct _nctri_cmd_seq *cmd_seq = &nci->nctri->nctri_cmd_seq; nand_enable_chip(nci); ndfc_repeat_mode_enable(nci->nctri); ndfc_clean_cmd_seq(cmd_seq); cmd_seq->cmd_type = CMD_TYPE_NORMAL; cmd_seq->nctri_cmd[0].cmd_valid = 1; cmd_seq->nctri_cmd[0].cmd = CMD_READ_STA; cmd_seq->nctri_cmd[0].cmd_send = 1; cmd_seq->nctri_cmd[0].cmd_trans_data_nand_bus = 1; cmd_seq->nctri_cmd[0].cmd_swap_data = 1; cmd_seq->nctri_cmd[0].cmd_swap_data_dma = 0; cmd_seq->nctri_cmd[0].cmd_direction = 0; //read cmd_seq->nctri_cmd[0].cmd_mdata_len = 1; cmd_seq->nctri_cmd[0].cmd_mdata_addr = pstatus; ret = ndfc_execute_cmd(nci->nctri, cmd_seq); if (ret) { RAWNAND_ERR("nand_reset_chip, read chip status failed!\n"); } ndfc_repeat_mode_disable(nci->nctri); nand_disable_chip(nci); return ret; } /* *Name : *Description : *Parameter : *Return : 0:ok -1:fail *Note : */ int nand_read_chip_status_ready(struct nand_chip_info *nci) { int ret; int timeout = 0xffff; u8 status; while (1) { ret = nand_read_chip_status(nci, &status); if (ret) { RAWNAND_ERR("read chip status failed!\n"); ret = -NAND_OP_FALSE; break; } if (status & NAND_STATUS_READY) break; if (timeout-- < 0) { RAWNAND_ERR("wait nand status ready timeout,chip=%x, status=%x\n", nci->chip_no, status); ret = ERR_TIMEOUT; break; } } if (status & NAND_OPERATE_FAIL) { RAWNAND_ERR("read chip status failed %x %x!\n", nci->chip_no, status); ret = -NAND_OP_FALSE; //ret = 0; } return ret; } /* *Name : *Description : *Parameter : *Return : 0:ok -1:fail *Note : */ int is_chip_rb_ready(struct nand_chip_info *nci) { struct nand_controller_info *nctri = nci->nctri; unsigned int chip_no = nci->chip_no; return ndfc_get_rb_sta(nctri, nctri->rb[chip_no]); } /* *Name : *Description : *Parameter : *Return : 0:ok -1:fail *Note : */ int nand_read_id(struct nand_chip_info *nci, unsigned char *id) { int ret = 0; struct _nctri_cmd_seq *cmd_seq = &nci->nctri->nctri_cmd_seq; nand_enable_chip(nci); ndfc_repeat_mode_enable(nci->nctri); ndfc_disable_randomize(nci->nctri); ndfc_clean_cmd_seq(cmd_seq); cmd_seq->cmd_type = CMD_TYPE_NORMAL; cmd_seq->nctri_cmd[0].cmd_valid = 1; cmd_seq->nctri_cmd[0].cmd = CMD_READ_ID; cmd_seq->nctri_cmd[0].cmd_send = 1; cmd_seq->nctri_cmd[0].cmd_acnt = 1; cmd_seq->nctri_cmd[0].cmd_addr[0] = 0x0; cmd_seq->nctri_cmd[0].cmd_trans_data_nand_bus = 1; cmd_seq->nctri_cmd[0].cmd_swap_data = 1; cmd_seq->nctri_cmd[0].cmd_swap_data_dma = 0; cmd_seq->nctri_cmd[0].cmd_direction = 0; //read cmd_seq->nctri_cmd[0].cmd_mdata_len = 8; cmd_seq->nctri_cmd[0].cmd_mdata_addr = id; ret = ndfc_execute_cmd(nci->nctri, cmd_seq); if (ret) { RAWNAND_ERR("nand_reset_chip, read chip id failed!\n"); } ndfc_repeat_mode_disable(nci->nctri); nand_disable_chip(nci); return ret; } int rawnand_read_parameter_page(struct nand_chip_info *nci, unsigned char *p) { int ret = 0; struct _nctri_cmd_seq *cmd_seq = &nci->nctri->nctri_cmd_seq; nand_enable_chip(nci); ndfc_repeat_mode_enable(nci->nctri); ndfc_disable_randomize(nci->nctri); ndfc_clean_cmd_seq(cmd_seq); cmd_seq->cmd_type = CMD_TYPE_NORMAL; cmd_seq->nctri_cmd[0].cmd_valid = 1; cmd_seq->nctri_cmd[0].cmd = CMD_READ_PARAMETER; cmd_seq->nctri_cmd[0].cmd_send = 1; cmd_seq->nctri_cmd[0].cmd_wait_rb = 1; cmd_seq->nctri_cmd[0].cmd_acnt = 1; cmd_seq->nctri_cmd[0].cmd_addr[0] = MICRON_ONFI_PARAMETER_ADDR; cmd_seq->nctri_cmd[0].cmd_trans_data_nand_bus = 1; cmd_seq->nctri_cmd[0].cmd_swap_data = 1; cmd_seq->nctri_cmd[0].cmd_swap_data_dma = 0; cmd_seq->nctri_cmd[0].cmd_direction = 0; //read cmd_seq->nctri_cmd[0].cmd_mdata_len = REVISION_FEATURES_BLOCK_INFO_PARAMETER_LEN; cmd_seq->nctri_cmd[0].cmd_mdata_addr = p; ret = ndfc_execute_cmd(nci->nctri, cmd_seq); if (ret) { RAWNAND_ERR("nand_reset_chip, read chip parameter page failed!\n"); } ndfc_repeat_mode_disable(nci->nctri); nand_disable_chip(nci); return ret; } /* *Name : *Description : *Parameter : *Return : 0:ok -1:fail *Note : */ int nand_first_read_id(struct nand_chip_info *nci, unsigned int chip_no, unsigned char *id) { int ret = 0; struct _nctri_cmd_seq *cmd_seq = &nci->nctri->nctri_cmd_seq; // enable chip, don't select rb signal ndfc_select_chip(nci->nctri, chip_no); ndfc_clean_cmd_seq(cmd_seq); cmd_seq->cmd_type = CMD_TYPE_NORMAL; cmd_seq->nctri_cmd[0].cmd_valid = 1; cmd_seq->nctri_cmd[0].cmd = CMD_READ_ID; cmd_seq->nctri_cmd[0].cmd_send = 1; cmd_seq->nctri_cmd[0].cmd_acnt = 1; cmd_seq->nctri_cmd[0].cmd_addr[0] = 0x0; cmd_seq->nctri_cmd[0].cmd_trans_data_nand_bus = 1; cmd_seq->nctri_cmd[0].cmd_swap_data = 1; cmd_seq->nctri_cmd[0].cmd_swap_data_dma = 0; cmd_seq->nctri_cmd[0].cmd_direction = 0; //read cmd_seq->nctri_cmd[0].cmd_mdata_len = 8; cmd_seq->nctri_cmd[0].cmd_mdata_addr = (u8 *)id; ret = ndfc_execute_cmd(nci->nctri, cmd_seq); if (ret) { RAWNAND_ERR("nand_reset_chip, read chip id failed!\n"); } nand_disable_chip(nci); if (ret) return NAND_OP_FALSE; else return NAND_OP_TRUE; } /* *Name : *Description : *Parameter : *Return : 0:ok -1:fail *Note : */ int nand_get_feature(struct nand_chip_info *nci, u8 *addr, u8 *feature) { int ret = 0; struct _nctri_cmd_seq *cmd_seq = &nci->nctri->nctri_cmd_seq; nand_enable_chip(nci); ndfc_repeat_mode_enable(nci->nctri); ndfc_disable_randomize(nci->nctri); ndfc_clean_cmd_seq(cmd_seq); cmd_seq->cmd_type = CMD_TYPE_NORMAL; cmd_seq->nctri_cmd[0].cmd = CMD_GET_FEATURE; cmd_seq->nctri_cmd[0].cmd_valid = 1; cmd_seq->nctri_cmd[0].cmd_send = 1; cmd_seq->nctri_cmd[0].cmd_acnt = 1; cmd_seq->nctri_cmd[0].cmd_addr[0] = addr[0]; cmd_seq->nctri_cmd[0].cmd_wait_rb = 1; cmd_seq->nctri_cmd[0].cmd_trans_data_nand_bus = 1; cmd_seq->nctri_cmd[0].cmd_swap_data = 1; cmd_seq->nctri_cmd[0].cmd_swap_data_dma = 0; cmd_seq->nctri_cmd[0].cmd_direction = 0; //read cmd_seq->nctri_cmd[0].cmd_mdata_len = 4; cmd_seq->nctri_cmd[0].cmd_mdata_addr = feature; ret = ndfc_execute_cmd(nci->nctri, cmd_seq); if (ret) { RAWNAND_ERR("nand_reset_chip, read chip id failed!\n"); } ndfc_repeat_mode_disable(nci->nctri); nand_disable_chip(nci); return ret; } int nand_set_read_retry_K9GCGD8U0F(struct nand_chip_info *nci, u8 *addr, u8 *feature) { int ret = 0; struct _nctri_cmd_seq *cmd_seq = &nci->nctri->nctri_cmd_seq; nand_enable_chip(nci); ndfc_repeat_mode_enable(nci->nctri); ndfc_disable_randomize(nci->nctri); ndfc_clean_cmd_seq(cmd_seq); cmd_seq->cmd_type = CMD_TYPE_NORMAL; cmd_seq->nctri_cmd[0].cmd = 0xD5; cmd_seq->nctri_cmd[0].cmd_valid = 1; cmd_seq->nctri_cmd[0].cmd_send = 1; cmd_seq->nctri_cmd[0].cmd_wait_rb = 1; cmd_seq->nctri_cmd[0].cmd_acnt = 2; cmd_seq->nctri_cmd[0].cmd_addr[0] = 0x00; cmd_seq->nctri_cmd[0].cmd_addr[1] = 0x89; cmd_seq->nctri_cmd[0].cmd_trans_data_nand_bus = 1; cmd_seq->nctri_cmd[0].cmd_swap_data = 1; cmd_seq->nctri_cmd[0].cmd_swap_data_dma = 0; cmd_seq->nctri_cmd[0].cmd_direction = 1; //write cmd_seq->nctri_cmd[0].cmd_mdata_len = 4; cmd_seq->nctri_cmd[0].cmd_mdata_addr = feature; ret = ndfc_execute_cmd(nci->nctri, cmd_seq); if (ret) { RAWNAND_ERR("nand_reset_chip, read chip id failed!\n"); } ndfc_repeat_mode_disable(nci->nctri); nand_disable_chip(nci); return ret; } int nand_get_read_retry_K9GCGD8U0F(struct nand_chip_info *nci, u8 *addr, u8 *feature) { int ret = 0; struct _nctri_cmd_seq *cmd_seq = &nci->nctri->nctri_cmd_seq; nand_enable_chip(nci); ndfc_repeat_mode_enable(nci->nctri); ndfc_disable_randomize(nci->nctri); ndfc_clean_cmd_seq(cmd_seq); cmd_seq->cmd_type = CMD_TYPE_NORMAL; cmd_seq->nctri_cmd[0].cmd = 0xD4; cmd_seq->nctri_cmd[0].cmd_valid = 1; cmd_seq->nctri_cmd[0].cmd_send = 1; cmd_seq->nctri_cmd[0].cmd_wait_rb = 1; cmd_seq->nctri_cmd[0].cmd_acnt = 2; cmd_seq->nctri_cmd[0].cmd_addr[0] = 0x00; cmd_seq->nctri_cmd[0].cmd_addr[1] = 0x89; cmd_seq->nctri_cmd[0].cmd_trans_data_nand_bus = 1; cmd_seq->nctri_cmd[0].cmd_swap_data = 1; cmd_seq->nctri_cmd[0].cmd_swap_data_dma = 0; cmd_seq->nctri_cmd[0].cmd_direction = 0; //read cmd_seq->nctri_cmd[0].cmd_mdata_len = 4; cmd_seq->nctri_cmd[0].cmd_mdata_addr = feature; ret = ndfc_execute_cmd(nci->nctri, cmd_seq); if (ret) { RAWNAND_ERR("nand_reset_chip, read chip id failed!\n"); } ndfc_repeat_mode_disable(nci->nctri); nand_disable_chip(nci); return ret; } /* *Name : *Description : *Parameter : *Return : 0:ok -1:fail *Note : */ int nand_set_feature(struct nand_chip_info *nci, u8 *addr, u8 *feature) { int ret = 0; struct _nctri_cmd_seq *cmd_seq = &nci->nctri->nctri_cmd_seq; nand_enable_chip(nci); ndfc_repeat_mode_enable(nci->nctri); ndfc_disable_randomize(nci->nctri); ndfc_clean_cmd_seq(cmd_seq); cmd_seq->cmd_type = CMD_TYPE_NORMAL; cmd_seq->nctri_cmd[0].cmd = CMD_SET_FEATURE; cmd_seq->nctri_cmd[0].cmd_valid = 1; cmd_seq->nctri_cmd[0].cmd_send = 1; cmd_seq->nctri_cmd[0].cmd_acnt = 1; cmd_seq->nctri_cmd[0].cmd_addr[0] = addr[0]; cmd_seq->nctri_cmd[0].cmd_wait_rb = 1; cmd_seq->nctri_cmd[0].cmd_trans_data_nand_bus = 1; cmd_seq->nctri_cmd[0].cmd_swap_data = 1; cmd_seq->nctri_cmd[0].cmd_swap_data_dma = 0; cmd_seq->nctri_cmd[0].cmd_direction = 1; //write cmd_seq->nctri_cmd[0].cmd_mdata_len = 4; cmd_seq->nctri_cmd[0].cmd_mdata_addr = feature; ret = ndfc_execute_cmd(nci->nctri, cmd_seq); if (ret) { RAWNAND_ERR("nand_reset_chip, read chip id failed!\n"); } ndfc_repeat_mode_disable(nci->nctri); nand_disable_chip(nci); return ret; } /* *Name : *Description : *Parameter : *Return : 0:ok -1:fail *Note : */ unsigned int get_row_addr(unsigned int page_offset_for_next_blk, unsigned int block, unsigned int page) { unsigned int maddr; if (page_offset_for_next_blk == 32) maddr = (block << 5) + page; else if (page_offset_for_next_blk == 64) maddr = (block << 6) + page; else if (page_offset_for_next_blk == 128) maddr = (block << 7) + page; else if (page_offset_for_next_blk == 256) maddr = (block << 8) + page; else if (page_offset_for_next_blk == 512) maddr = (block << 9) + page; else if (page_offset_for_next_blk == 792) maddr = ((block * 792) + page); else if (page_offset_for_next_blk == 1024) maddr = (block << 10) + page; else if (page_offset_for_next_blk == 388) { maddr = (block << 9) + page; //RAWNAND_ERR("pb %d!\n", page_offset_for_next_blk); } else { maddr = 0xffffffff; RAWNAND_ERR("unknow page per block %d!\n", page_offset_for_next_blk); } return maddr; } unsigned int get_row_addr_2(unsigned int page_offset_for_next_blk, unsigned int block, unsigned int page) { unsigned int row = 0; row += page; if (block > 344) { row = (row | (1 << 10)); row = (row | ((block - 344) << 11)); } else { row = (row& ~(1 << 10)); row = (row | (block << 11)); } return row; } /* *Name : *Description : *Parameter : *Return : 0:ok -1:fail *Note : */ int fill_cmd_addr(u32 col_addr, u32 col_cycle, u32 row_addr, u32 row_cycle, u8 *abuf) { s32 i; if (col_cycle) { for (i = 0; i < col_cycle; i++) abuf[i] = (col_addr >> (i * 8)) & 0xff; } if (row_cycle) { for (i = 0 + col_cycle; i < col_cycle + row_cycle; i++) abuf[i] = (row_addr >> ((i - col_cycle) * 8)) & 0xff; } return 0; } /* *Name : *Description : *Parameter : *Return : 0:ok -1:fail *Note : */ void set_default_batch_read_cmd_seq(struct _nctri_cmd_seq *cmd_seq) { cmd_seq->cmd_type = CMD_TYPE_BATCH; cmd_seq->ecc_layout = ECC_LAYOUT_INTERLEAVE; cmd_seq->nctri_cmd[0].cmd = CMD_READ_PAGE_CMD1; cmd_seq->nctri_cmd[0].cmd_wait_rb = 1; cmd_seq->nctri_cmd[1].cmd = CMD_READ_PAGE_CMD2; cmd_seq->nctri_cmd[2].cmd = CMD_CHANGE_READ_ADDR_CMD1; cmd_seq->nctri_cmd[3].cmd = CMD_CHANGE_READ_ADDR_CMD2; cmd_seq->nctri_cmd[0].cmd_valid = 1; cmd_seq->nctri_cmd[1].cmd_valid = 1; cmd_seq->nctri_cmd[2].cmd_valid = 1; cmd_seq->nctri_cmd[3].cmd_valid = 1; } /* *Name : *Description : *Parameter : *Return : 0:ok -1:fail *Note : */ void set_default_batch_write_cmd_seq(struct _nctri_cmd_seq *cmd_seq, u32 write_cmd1, u32 write_cmd2) { cmd_seq->cmd_type = CMD_TYPE_BATCH; cmd_seq->ecc_layout = ECC_LAYOUT_INTERLEAVE; cmd_seq->nctri_cmd[0].cmd = write_cmd1; cmd_seq->nctri_cmd[0].cmd_wait_rb = 1; cmd_seq->nctri_cmd[1].cmd = write_cmd2; cmd_seq->nctri_cmd[2].cmd = CMD_CHANGE_WRITE_ADDR_CMD; cmd_seq->nctri_cmd[0].cmd_valid = 1; cmd_seq->nctri_cmd[1].cmd_valid = 1; cmd_seq->nctri_cmd[2].cmd_valid = 1; } /* *Name : *Description : *Parameter : *Return : 0:ok -1:fail *Note : */ s32 _cal_nand_onfi_timing_mode(u32 mode, u32 dclk) { s32 tmode = -1; if (mode == 0x0) { /*SDR/Async mode*/ if (dclk < 15) tmode = 0; else if (dclk < 24) tmode = 1; else if (dclk < 30) tmode = 2; //30.5->30 else if (dclk < 36) tmode = 3; //36.5->36 else if (dclk < 45) tmode = 4; else if (dclk <= 50) tmode = 5; else RAWNAND_ERR("wrong dclk(%d) in mode(%d)\n", dclk, mode); } else if (mode == 0x2) { /*nv-ddr*/ if (dclk < 26) tmode = 0; //26.5->26 else if (dclk < 41) tmode = 1; //41.5->41 else if (dclk < 58) tmode = 2; //58.5->58 else if (dclk < 75) tmode = 3; else if (dclk < 91) tmode = 4; //91.5->91 else if (dclk <= 100) tmode = 5; else RAWNAND_ERR("wrong dclk(%d) in mode(%d)\n", dclk, mode); } else if (mode == 0x12) { /*nv-ddr2*/ if (dclk < 36) tmode = 0; //36.5->36 else if (dclk < 53) tmode = 1; else if (dclk < 74) tmode = 2; //74.5->74 else if (dclk < 91) tmode = 3; //91.5->91 else if (dclk < 116) tmode = 4; //116.5->116 else if (dclk < 149) tmode = 5; //149.5->149 else if (dclk < 183) tmode = 6; else if (dclk < 200) tmode = 7; else RAWNAND_ERR("wrong dclk(%d) in mode(%d)\n", dclk, mode); } else { tmode = 0; } return tmode; } /* *Name : *Description : *Parameter : *Return : 0:ok -1:fail *Note : */ s32 _change_nand_onfi_timing_mode(struct nand_chip_info *nci, u32 if_type, u32 timing_mode) { u8 addr; u8 p[4]; if (!SUPPORT_CHANGE_ONFI_TIMING_MODE) { RAWNAND_ERR("don't support change onfi timing mode. if_type: %d\n", if_type); return ERR_NO_71; } if ((if_type != SDR) && (if_type != ONFI_DDR) && (if_type != ONFI_DDR2)) { RAWNAND_ERR("wrong onfi interface type: %d\n", if_type); return ERR_NO_70; } if ((if_type == SDR) && (timing_mode > 5)) { RAWNAND_ERR("wrong onfi timing mode(%d) in interface type(%d)\n", if_type, timing_mode); return ERR_NO_69; } if ((if_type == ONFI_DDR) && (timing_mode > 5)) { RAWNAND_ERR("wrong onfi timing mode(%d) in interface type(%d)\n", if_type, timing_mode); return ERR_NO_68; } if ((if_type == ONFI_DDR2) && (timing_mode > 7)) { RAWNAND_ERR("wrong onfi timing mode(%d) in interface type(%d)\n", if_type, timing_mode); return ERR_NO_67; } addr = 0x01; //feature address 01h, Timing Mode p[0] = 0; if (if_type == ONFI_DDR) p[0] = (0x1U << 4) | (timing_mode & 0xf); else if (if_type == ONFI_DDR2) p[0] = (0x2U << 4) | (timing_mode & 0xf); else p[0] = (timing_mode & 0xf); p[1] = 0x0; p[2] = 0x0; p[3] = 0x0; nand_set_feature(nci, &addr, p); //aw_delay(0x100); //max tITC is 1us return 0; } /* *Name : *Description : *Parameter : *Return : 0:ok -1:fail *Note : */ s32 _setup_ddr_nand_force_to_sdr_para(struct nand_chip_info *nci) { u8 addr; u8 p[4]; /*u8 pr[4]; //read back value*/ /*sandisk/toshiba nand flash*/ addr = 0x80; //Sandisk: This address (80h) is a vendor-specific setting used to turn on or turn off Toggle Mode #if 0 nand_get_feature(nci, &addr, pr); ndfc_set_legacy_interface(nci->nctri); nand_get_feature(nci, &addr, pr); //RAWNAND_ERR("get feature(addr 0x80) 0x%x 0x%x 0x%x 0x%x!\n",pr[0],pr[1],pr[2],pr[3]); if (pr[0] == 0x00) { ndfc_set_toggle_interface(nci->nctri); } else { ndfc_set_legacy_interface(nci->nctri); } #endif p[0] = 0x1; //disable toggle mode p[1] = 0x0; p[2] = 0x0; p[3] = 0x0; nand_set_feature(nci, &addr, p); ndfc_set_legacy_interface(nci->nctri); #if 0 nand_get_feature(nci, &addr, pr); //RAWNAND_ERR("get feature(addr 0x80) 0x%x 0x%x 0x%x 0x%x!\n",pr[0],pr[1],pr[2],pr[3]); #endif return 0; } /* *Name : *Description : *Parameter : *Return : 0:ok -1:fail *Note : */ s32 _check_scan_data(u32 first_check, u32 chip, u32 *scan_good_blk_no, u8 *main_buf) { s32 ret; u32 b, start_blk = 4, blk_cnt = 5; u8 oob_buf[64]; u32 buf_size = 32768; u32 buf_flag = 0; if (main_buf == NULL) { main_buf = nand_get_temp_buf(buf_size); if (!main_buf) { RAWNAND_ERR("check scan data, main_buf %p is null!\n", main_buf); return ERR_NO_63; } buf_flag = 1; } if (first_check) { scan_good_blk_no[chip] = 0xffff; for (b = start_blk; b < start_blk + blk_cnt; b++) { //ret = nand_read_scan_data(0, b, 0, g_nsi->nci->sector_cnt_per_page, main_buf, oob_buf); ret = nand_read_scan_data(0, b, 0, g_nsi->nci->sector_cnt_per_page, NULL, oob_buf); if ((oob_buf[0] == 0xff) && (oob_buf[1] == 0xff) && (oob_buf[2] == 0xff) && (oob_buf[3] == 0xff)) { scan_good_blk_no[chip] = b; ret = 1; break; } if (ret >= 0) { ret = 0; scan_good_blk_no[chip] = b; break; } } RAWNAND_DBG("check scan data, first_check %u %d!\n", scan_good_blk_no[chip], ret); } else { //ret = nand_read_scan_data(0, scan_good_blk_no[chip], 0, g_nsi->nci->sector_cnt_per_page, main_buf, oob_buf); ret = nand_read_scan_data(0, scan_good_blk_no[chip], 0, g_nsi->nci->sector_cnt_per_page, NULL, oob_buf); if (ret >= 0) { ret = 0; } if ((oob_buf[0] == 0xff) && (oob_buf[1] == 0xff) && (oob_buf[2] == 0xff) && (oob_buf[3] == 0xff)) { ret = 1; } } if (buf_flag != 0) { nand_free_temp_buf(main_buf); } return ret; } /* *Name : *Description : *Parameter : *Return : 0:ok -1:fail *Note : */ s32 _change_all_nand_parameter(struct nand_controller_info *nctri, u32 ddr_type, u32 pre_ddr_type, u32 dclk) { struct nand_chip_info *nci = nctri->nci; //__s32 ret; //__u32 bank; //__u8 chip, rb; __u32 ddr_change_mode = 0; __u32 tmode; s32 ret; //NFC_CMD_LIST reset_cmd; /*check parameter*/ if (((ddr_type == ONFI_DDR) || (ddr_type == ONFI_DDR2)) && (pre_ddr_type == SDR)) //Async => ONFI DDR/DDR2 ddr_change_mode = 1; else if ((ddr_type == SDR) && ((pre_ddr_type == ONFI_DDR) || (pre_ddr_type == ONFI_DDR2))) //ONFI DDR/DDR2 => Async ddr_change_mode = 2; else if (((ddr_type == TOG_DDR) || (ddr_type == TOG_DDR2)) && (pre_ddr_type == SDR)) //Async => Toggle DDR/DDR2 ddr_change_mode = 3; else if ((ddr_type == SDR) && ((pre_ddr_type == TOG_DDR) || (pre_ddr_type == TOG_DDR2))) //Toggle DDR/DDR2 => Async ddr_change_mode = 4; else if (((ddr_type == TOG_DDR) && (pre_ddr_type == TOG_DDR2)) || ((ddr_type == TOG_DDR2) && (pre_ddr_type == TOG_DDR))) //Toggle DDR2 <=> Toggle DDR ddr_change_mode = 5; else if (ddr_type == pre_ddr_type) ddr_change_mode = 6; else { RAWNAND_ERR("_change_nand_parameter: wrong input para, " "ddr_type %d, pre_ddr_type %d\n", ddr_type, pre_ddr_type); return ERR_NO_62; } tmode = _cal_nand_onfi_timing_mode(ddr_type, dclk); nci->timing_mode = tmode; /*change nand flash parameter*/ while (nci) { nand_enable_chip(nci); //RAWNAND_DBG("%s: ch: %d chip: %d rb: %d\n", __func__, NandIndex, chip, rb); if (ddr_change_mode == 1) { /* Async => ONFI DDR/DDR2 */ RAWNAND_DBG("mode 1 : Async => ONFI DDR/DDR2\n"); ret = rawnand_async_to_onfi_ddr_or_ddr2_set(nci, ddr_type); if (ret != NAND_OP_TRUE) { RAWNAND_ERR("rawnand err: %s rawnand async set to onfi %s fail!\n", __func__, ddr_type == ONFI_DDR ? "ddr" : "ddr2"); return NAND_OP_FALSE; } } else if (ddr_change_mode == 2) { /* ONFI DDR/DDR2 => Async */ ret = rawnand_onfi_ddr_or_ddr2_to_async_set(nci, ddr_type, pre_ddr_type); if (ret != NAND_OP_TRUE) { RAWNAND_ERR("rawnand err: %s rawnand onfi %s set to async fail!\n", __func__, pre_ddr_type == ONFI_DDR ? "ddr" : "ddr2"); return NAND_OP_FALSE; } } else if (ddr_change_mode == 3) { /* Async => Toggle DDR/DDR2 */ ret = rawnand_async_to_toggle_ddr_or_ddr2_set(nci, ddr_type); if (ret != NAND_OP_TRUE) { RAWNAND_ERR("rawnand err: %s rawnand async set to toggle %s fail\n", __func__, ddr_type == TOG_DDR ? "ddr" : "ddr2"); return NAND_OP_FALSE; } } else if (ddr_change_mode == 4) { /* Toggle DDR/DDR2 => Async */ RAWNAND_DBG("mode 4 : Toggle DDR/DDR2 => Async\n"); ret = rawnand_toggle_ddr_or_ddr2_to_async_set(nci, ddr_type, pre_ddr_type); if (ret != NAND_OP_TRUE) { RAWNAND_ERR("rawnand err: %s rawnand toggle %s set to async fail\n", __func__, ddr_type == TOG_DDR ? "ddr" : "ddr2"); return NAND_OP_FALSE; } } else if (ddr_change_mode == 5) { /* Toggle DDR2 <=> Toggle DDR */ RAWNAND_DBG("mode 5 : Toggle DDR2 <=> Toggle DDR\n"); ret = rawnand_toggle_ddr2_to_toggle_ddr_set(nci); if (ret != NAND_OP_TRUE) { RAWNAND_ERR("rawnand err: %s rawnand toggle ddr2 set to toggle ddr fail\n", __func__); return NAND_OP_FALSE; } } else if (ddr_change_mode == 6) { RAWNAND_DBG("mode 6: unchanged, rawnand set to %s\n", ddr_type == SDR ? "sdr" : (ddr_type == ONFI_DDR ? "onfi ddr" : (ddr_type == ONFI_DDR2 ? "onfi ddr2" : (ddr_type == TOG_DDR ? "toggle ddr" : (ddr_type == TOG_DDR2 ? "toggle ddr2" : "null"))))); ret = rawnand_itf_unchanged_set(nci, ddr_type, pre_ddr_type); if (ret != NAND_OP_TRUE) { RAWNAND_ERR("rawnand err: %s rawnand set to %s fail\n", __func__, ddr_type == SDR ? "sdr" : (ddr_type == ONFI_DDR ? "onfi ddr" : (ddr_type == ONFI_DDR2 ? "onfi ddr2" : (ddr_type == TOG_DDR ? "toggle ddr" : (ddr_type == TOG_DDR2 ? "toggle ddr2" : "null"))))); return NAND_OP_FALSE; } } else { ; } nci = nci->nctri_next; } return 0; } /* *Name : *Description : *Parameter : *Return : 0:ok -1:fail *Note : */ s32 _get_right_timing_para(struct nand_controller_info *nctri, u32 ddr_type, u32 *good_sdr_edo, u32 *good_ddr_edo, u32 *good_ddr_delay) { // u32 chip = 0; struct nand_chip_info *nci = nctri->nci; //chip 0 u32 *scan_blk_no = nctri->ddr_scan_blk_no; u32 edo, delay, edo_cnt, delay_cnt; u32 edo_delay[2][3], tmp_edo_delay[3]; u32 good_cnt, tmp_good_cnt, store_index; s32 err_flag; u32 good_flag; u32 index, i, j; u8 tmpChipID[16]; u32 param[2]; s32 ret; u32 sclk0_bak, sclk1_bak; u32 sdr_edo, ddr_edo, ddr_delay, tmp; u8 *main_buf; for (i = 0; i < 2; i++) { for (j = 0; j < 3; j++) edo_delay[i][j] = 0xffffffff; } for (j = 0; j < 3; j++) tmp_edo_delay[j] = 0xffffffff; good_flag = 0; index = 0; store_index = 0; good_cnt = 0; tmp_good_cnt = 0; edo_cnt = 0; delay_cnt = 0; param[0] = 0xffffffff; param[1] = 0xffffffff; if (ddr_type == SDR) { edo_cnt = 3; delay_cnt = 1; if (SUPPORT_SCAN_EDO_FOR_SDR_NAND == 0) { RAWNAND_DBG("_get right timing para, set edo to 1 for sdr nand.\n"); *good_sdr_edo = 1; return 0; } } else { delay_cnt = 64; if (NDFC_VERSION_V2 == nctri->type) edo_cnt = 32; else if (NDFC_VERSION_V1 == nctri->type) edo_cnt = 16; //32 else { RAWNAND_ERR("wrong ndfc version, %d\n", nctri->type); return ERR_NO_61; } } main_buf = nand_get_temp_buf(32768); if (!main_buf) { RAWNAND_ERR("main_buf %p is null!\n", main_buf); return ERR_NO_60; } for (edo = 0; edo < edo_cnt; edo++) { good_flag = 0; for (delay = 0; delay < delay_cnt; delay += 2) { if (ddr_type == SDR) { sdr_edo = edo; ddr_edo = edo; tmp = sdr_edo << 8; ddr_delay = delay; } else { sdr_edo = edo; ddr_edo = edo; ddr_delay = delay; tmp = (ddr_edo << 8) | ddr_delay; } ndfc_change_nand_interface(nctri, ddr_type, sdr_edo, ddr_edo, ddr_delay); nctri->ddr_timing_ctl[0] = tmp; //read id ret = nand_read_id(nci, tmpChipID); //chip 0 if (ret) { RAWNAND_ERR("read id failed! ---> continue\n"); continue; } if ((nctri->nci->id[0] == tmpChipID[0]) && (nctri->nci->id[1] == tmpChipID[1]) && (nctri->nci->id[2] == tmpChipID[2]) && (nctri->nci->id[3] == tmpChipID[3])) { err_flag = _check_scan_data(0, 0, scan_blk_no, main_buf); if (err_flag == 0) { RAWNAND_DBG("ddr edo:0x%x, delay:0x%x is good\n", edo, ddr_delay); good_flag = 1; if (ddr_type == SDR) { /*sdr mode*/ if (edo == 0) { nand_get_clk(&aw_ndfc, nctri->channel_id, &sclk0_bak, &sclk1_bak); RAWNAND_DBG("sclk0 %d MHz, edo %d\n", sclk0_bak, edo); if (sclk0_bak < 12) //less 12MHz break; else good_flag = 0; } break; } if ((index != 0) && (index != 1)) RAWNAND_ERR("wrong index!\n"); if (store_index == 0) { if (edo_delay[index][0] == 0xffffffff) { /* first found */ edo_delay[index][0] = edo; //k; edo_delay[index][1] = delay; //m; edo_delay[index][2] = delay; //m; } else { edo_delay[index][2] = delay; //m; } good_cnt++; } else if (store_index == 1) { if (tmp_edo_delay[0] == 0xffffffff) { /*first found*/ tmp_edo_delay[0] = edo; //k; tmp_edo_delay[1] = delay; //m; tmp_edo_delay[2] = delay; //m; } else { tmp_edo_delay[2] = delay; //m; } tmp_good_cnt++; } } else { /* id is ok, but data is wrong */ RAWNAND_DBG("ddr edo:0x%x, delay:0x%x is wrong:%d\n", edo, ddr_delay, err_flag); if (good_cnt == 0) { store_index = 0; } else if (tmp_good_cnt == 0) { //store good {edo, delay} to tmp_edo_delay[] store_index = 1; } else if (tmp_good_cnt > good_cnt) { //move tmp_edo_delay[] to edo_delay[][] edo_delay[index][0] = tmp_edo_delay[0]; edo_delay[index][1] = tmp_edo_delay[1]; edo_delay[index][2] = tmp_edo_delay[2]; good_cnt = tmp_good_cnt; //clear tmp_edo_delay[] for next valid group store_index = 1; tmp_good_cnt = 0; for (j = 0; j < 3; j++) tmp_edo_delay[j] = 0xffffffff; } else { store_index = 1; tmp_good_cnt = 0; for (j = 0; j < 3; j++) tmp_edo_delay[j] = 0xffffffff; } } } else { /* read id wrong */ //RAWNAND_ERR("timing_para read id wrong!\n"); if (good_cnt == 0) { store_index = 0; } else if (tmp_good_cnt == 0) { //store good {edo, delay} to tmp_edo_delay[] store_index = 1; } else if (tmp_good_cnt > good_cnt) { //move tmp_edo_delay[] to edo_delay[][] edo_delay[index][0] = tmp_edo_delay[0]; edo_delay[index][1] = tmp_edo_delay[1]; edo_delay[index][2] = tmp_edo_delay[2]; good_cnt = tmp_good_cnt; //clear tmp_edo_delay[] for next valid group store_index = 1; tmp_good_cnt = 0; for (j = 0; j < 3; j++) tmp_edo_delay[j] = 0xffffffff; } else { store_index = 1; tmp_good_cnt = 0; for (j = 0; j < 3; j++) tmp_edo_delay[j] = 0xffffffff; } } } if (good_flag) { if (ddr_type == SDR) //sdr mode break; if (index == 0) { if (good_cnt >= GOOD_DDR_EDO_DELAY_CHAIN_TH) //8 groups of {edo, delay} is enough break; index = 1; store_index = 0; good_cnt = 0; tmp_good_cnt = 0; for (j = 0; j < 3; j++) tmp_edo_delay[j] = 0xffffffff; } else { break; } } } if (ddr_type == SDR) { if (good_flag) { *good_sdr_edo = edo; } goto RET; } if ((edo_delay[0][0] == 0xffffffff) && (edo_delay[1][0] == 0xffffffff)) { good_flag = 0; RAWNAND_ERR("can't find a good edo, delay chain. index %d: %d %d %d\n", index, edo_delay[index][0], edo_delay[index][1], edo_delay[index][2]); } else if ((edo_delay[0][0] != 0xffffffff) && (edo_delay[1][0] != 0xffffffff)) { i = edo_delay[0][2] - edo_delay[0][1]; j = edo_delay[1][2] - edo_delay[1][1]; if (j > i) { param[0] = edo_delay[1][0]; param[1] = (edo_delay[1][1] + edo_delay[1][2]) / 2 + 1; if (j >= GOOD_DDR_EDO_DELAY_CHAIN_TH) good_flag = 1; else good_flag = 0; } else { param[0] = edo_delay[0][0]; param[1] = (edo_delay[0][1] + edo_delay[0][2]) / 2 + 1; if (j >= GOOD_DDR_EDO_DELAY_CHAIN_TH) good_flag = 1; else good_flag = 0; } RAWNAND_DBG("(0x%x, 0x%x - 0x%x), (0x%x, 0x%x - 0x%x)\n", edo_delay[0][0], edo_delay[0][1], edo_delay[0][2], edo_delay[1][0], edo_delay[1][1], edo_delay[1][2]); if (good_flag) RAWNAND_DBG("%d good edo: 0x%x, good delay chain: 0x%x\n", __LINE__, param[0], param[1]); else RAWNAND_ERR("can't find a good edo, delay chain !\n"); } else if ((edo_delay[0][0] != 0xffffffff) && (edo_delay[1][0] == 0xffffffff)) { i = edo_delay[0][2] - edo_delay[0][1]; param[0] = edo_delay[0][0]; param[1] = (edo_delay[0][1] + edo_delay[0][2]) / 2 + 1; RAWNAND_DBG("(0x%x, 0x%x - 0x%x) \n", edo_delay[0][0], edo_delay[0][1], edo_delay[0][2]); if (i >= GOOD_DDR_EDO_DELAY_CHAIN_TH) good_flag = 1; else good_flag = 0; if (good_flag) RAWNAND_DBG("%d good edo: 0x%x, good delay chain: 0x%x\n", __LINE__, param[0], param[1]); else RAWNAND_ERR("can't find a good edo, delay chain!!\n"); } else { good_flag = 0; RAWNAND_ERR("scan error!!!!!!!\n"); } *good_ddr_edo = param[0]; *good_ddr_delay = param[1]; RET: nand_free_temp_buf(main_buf); if (good_flag == 0) { return ERR_NO_59; } return 0; } /* *Name : *Description : *Parameter : *Return : 0:ok -1:fail *Note : */ int set_cmd_with_nand_bus(struct nand_chip_info *nci, u8 *cmd, u32 wait_rb, u8 *addr, u8 *dat, u32 dat_len, u32 counter) { int ret, i; u8 *p_dat = dat; struct _nctri_cmd_seq *cmd_seq = &nci->nctri->nctri_cmd_seq; ndfc_repeat_mode_enable(nci->nctri); if (nci->randomizer) { ndfc_disable_randomize(nci->nctri); } ndfc_clean_cmd_seq(cmd_seq); cmd_seq->cmd_type = CMD_TYPE_NORMAL; //RAWNAND_DBG("set cmd with nand bus:"); for (i = 0; i < counter; i++) { cmd_seq->nctri_cmd[i].cmd_valid = 1; if (cmd != NULL) { cmd_seq->nctri_cmd[i].cmd = cmd[i]; cmd_seq->nctri_cmd[i].cmd_send = 1; if (wait_rb != 0) { cmd_seq->nctri_cmd[i].cmd_wait_rb = 1; } } if ((addr == NULL) && (dat == NULL)) { //RAWNAND_DBG("cmd:0x%x;",cmd[i]); } if (addr != NULL) { cmd_seq->nctri_cmd[i].cmd_acnt = 1; cmd_seq->nctri_cmd[i].cmd_addr[0] = addr[i]; //RAWNAND_DBG("addr:0x%x;",addr[i]); } if (dat != NULL) { cmd_seq->nctri_cmd[i].cmd_trans_data_nand_bus = 1; cmd_seq->nctri_cmd[i].cmd_swap_data = 1; cmd_seq->nctri_cmd[i].cmd_direction = 1; cmd_seq->nctri_cmd[i].cmd_mdata_len = dat_len; cmd_seq->nctri_cmd[i].cmd_mdata_addr = p_dat; // RAWNAND_DBG("data:"); // for(j=0;jnctri, cmd_seq); ndfc_repeat_mode_disable(nci->nctri); if (ret != 0) { RAWNAND_ERR("set cmd with nand bus fail, cmd:0x%x;dat_len:0x%x;counter:0x%x;!\n", cmd[0], dat_len, counter); return ret; } return 0; } /* *Name : *Description : *Parameter : *Return : 0:ok -1:fail *Note : */ int get_data_with_nand_bus_one_cmd(struct nand_chip_info *nci, u8 *cmd, u8 *addr, u8 *dat, u32 dat_len) { int ret, j; u8 *p_dat = dat; struct _nctri_cmd_seq *cmd_seq = &nci->nctri->nctri_cmd_seq; if (dat_len > 1024) { RAWNAND_ERR("dat_len is too long %d!\n", dat_len); return ERR_NO_133; } ndfc_repeat_mode_enable(nci->nctri); if (nci->randomizer) { ndfc_disable_randomize(nci->nctri); } ndfc_clean_cmd_seq(cmd_seq); cmd_seq->cmd_type = CMD_TYPE_NORMAL; //RAWNAND_DBG("set cmd with nand bus:"); cmd_seq->nctri_cmd[0].cmd_valid = 1; if (cmd != NULL) { cmd_seq->nctri_cmd[0].cmd = *cmd; cmd_seq->nctri_cmd[0].cmd_send = 1; cmd_seq->nctri_cmd[0].cmd_wait_rb = 1; } if ((addr == NULL) && (dat == NULL)) { RAWNAND_DBG("cmd:0x%x;", *cmd); } if (addr != NULL) { cmd_seq->nctri_cmd[0].cmd_acnt = 1; cmd_seq->nctri_cmd[0].cmd_addr[0] = *addr; //RAWNAND_DBG("addr:0x%x;",addr[i]); } if (dat != NULL) { cmd_seq->nctri_cmd[0].cmd_trans_data_nand_bus = 1; cmd_seq->nctri_cmd[0].cmd_swap_data = 1; cmd_seq->nctri_cmd[0].cmd_direction = 0; cmd_seq->nctri_cmd[0].cmd_mdata_len = dat_len; cmd_seq->nctri_cmd[0].cmd_mdata_addr = p_dat; //RAWNAND_DBG("data:"); for (j = 0; j < dat_len; j++) { //RAWNAND_DBG("0x%x,",p_dat[j]); } } //RAWNAND_DBG("\n"); ret = ndfc_execute_cmd(nci->nctri, cmd_seq); ndfc_repeat_mode_disable(nci->nctri); if (ret != 0) { RAWNAND_ERR("get_data_with nand_bus_one_cmd fail, cmd:0x%x;dat_len:0x%x!\n", cmd[0], dat_len); return ret; } return 0; } /* *Name : *Description : *Parameter : *Return : 0:ok -1:fail *Note : */ int set_one_cmd(struct nand_chip_info *nci, u8 cmd, u32 wait_rb) { int ret; u8 cmd_8[1]; cmd_8[0] = cmd; ret = set_cmd_with_nand_bus(nci, cmd_8, wait_rb, NULL, NULL, 0, 1); return ret; } /* *Name : *Description : *Parameter : *Return : 0:ok -1:fail *Note : */ int set_one_addr(struct nand_chip_info *nci, u8 addr) { int ret; u8 addr_8[1]; addr_8[0] = addr; ret = set_cmd_with_nand_bus(nci, NULL, 0, addr_8, NULL, 0, 1); return ret; } /* *Name : *Description : *Parameter : *Return : 0:ok -1:fail *Note : */ int switch_ddrtype_from_ddr_to_sdr(struct nand_controller_info *nctri) { int cfg, ddr_type, ddr_type_bak; ddr_type = SDR; if ((nctri->nci->support_toggle_only == 0) && (nctri->nci->interface_type > 0)) { // RAWNAND_DBG("switch_ddrtype_from_ddr_to_sdr start !\n"); ddr_type_bak = nctri->nci->interface_type; nctri->nci->interface_type = SDR; nctri->nreg_bak.reg_timing_ctl = 0x100; cfg = nctri->nreg_bak.reg_ctl; cfg &= ~(0x3U << 18); cfg |= (ddr_type & 0x3) << 18; if (nctri->type == NDFC_VERSION_V2) { cfg &= ~(0x1 << 28); cfg |= ((ddr_type >> 4) & 0x1) << 28; } nctri->nreg_bak.reg_ctl = cfg; _change_all_nand_parameter(nctri, ddr_type, ddr_type_bak, 40); nctri->nci->interface_type = ddr_type_bak; } return 0; } /* *Name : *Description : *Parameter : *Return : 0:ok -1:fail *Note : */ int get_dummy_byte(int physic_page_size, int ecc_mode, int ecc_block_cnt, int user_data_size) { int ecc_code_size_per_1K, valid_size, dummy_byte; u8 ecc_tab[16] = {16, 24, 28, 32, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80}; int ecc_bit; ecc_bit = ecc_tab[ecc_mode]; ecc_code_size_per_1K = 14 * ecc_bit / 8; valid_size = (1024 + ecc_code_size_per_1K) * ecc_block_cnt + user_data_size; dummy_byte = physic_page_size - valid_size; return dummy_byte; } /* *Name : *Description : *Parameter : *Return : 0:ok -1:fail *Note : */ int get_random_cmd2(struct _nand_physic_op_par *npo) { return (npo->page % 3); } /* *Name : *Description : *Parameter : *Return : 0:ok -1:fail *Note : */ int get_data_block_cnt(unsigned int sect_bitmap) { int i, count = 0; for (i = 0; i < 32; i++) { if ((sect_bitmap >> i) & 0x1) count++; } return count; } /* *Name : *Description : *Parameter : *Return : 0:ok -1:fail *Note : */ int get_data_block_cnt_for_boot0_ecccode(struct nand_chip_info *nci, u8 ecc_mode) { int size_increased, cnt, i; u8 ecc_tab[16] = {16, 24, 28, 32, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80}; int ecc_bit_boot0, ecc_bit_normal; ecc_bit_boot0 = ecc_tab[ecc_mode]; ecc_bit_normal = ecc_tab[nci->ecc_mode]; size_increased = 14 * (ecc_bit_boot0 - ecc_bit_normal) / 8; cnt = 1; for (i = (nci->sector_cnt_per_page / 2 - 1); i > 0; i--) { if ((size_increased * i) < (1024 * cnt)) break; cnt++; } return cnt; } /* *Name : *Description : *Parameter : *Return : 0:ok -1:fail *Note : */ int nand_reset_super_chip(struct nand_super_chip_info *nsci, unsigned int super_chip_no) { return 0; } /* *Name : *Description : *Parameter : *Return : 0:ok -1:fail *Note : */ int nand_read_super_chip_status(struct nand_super_chip_info *nsci, unsigned int super_chip_no) { return 0; } /* *Name : *Description : *Parameter : *Return : 0:ok -1:fail *Note : */ int is_super_chip_rb_ready(struct nand_super_chip_info *nsci, unsigned int super_chip_no) { return 0; } /* *Name : *Description : *Parameter : *Return : 0:ok -1:fail *Note : */ int nand_wait_all_rb_ready(void) { int ret = 0; struct nand_controller_info *nctri = g_nctri; while (nctri != NULL) { ret |= ndfc_wait_all_rb_ready(nctri); nctri = nctri->next; } return ret; } char *rawnand_get_chip_name(struct nand_chip_info *chip) { struct sunxi_nand_flash_device *info = chip->npi; return info->name; } void rawnand_get_chip_id(struct nand_chip_info *chip, unsigned char *id, int cnt) { struct sunxi_nand_flash_device *info = chip->npi; memcpy(id, info->id, cnt); } unsigned int rawnand_get_chip_die_cnt(struct nand_chip_info *chip) { struct sunxi_nand_flash_device *info = chip->npi; return info->die_cnt_per_chip; } int rawnand_get_chip_page_size(struct nand_chip_info *chip, enum size_type type) { struct sunxi_nand_flash_device *info = chip->npi; if (likely(type == SECTOR)) { return info->sect_cnt_per_page; } else if (unlikely(type == BYTE)) { return info->sect_cnt_per_page * SECTOR_SIZE; } else { pr_err("no this type:%d single page size in BYTE(0)@byte " "SECTOR(1)@sector", type); return ERR_NO_22; } return ERR_NO_22; } int rawnand_get_chip_block_size(struct nand_chip_info *chip, enum size_type type) { struct sunxi_nand_flash_device *info = chip->npi; if (likely(type == PAGE)) { return info->page_cnt_per_blk; } else if (unlikely(type == SECTOR)) { return info->page_cnt_per_blk * info->sect_cnt_per_page; } else if (unlikely(type == BYTE)) { return info->page_cnt_per_blk * info->sect_cnt_per_page * SECTOR_SIZE; } else { pr_err("no this type:%d single block size in BYTE(0)@byte " "SECTOR(1)@sector PAGE(2)@page", type); return ERR_NO_22; } return ERR_NO_22; } int rawnand_get_chip_die_size(struct nand_chip_info *chip, enum size_type type) { struct sunxi_nand_flash_device *info = chip->npi; if (likely(type == BLOCK)) return info->blk_cnt_per_die; else if (unlikely(type == BYTE)) return info->blk_cnt_per_die * info->page_cnt_per_blk * info->sect_cnt_per_page * SECTOR_SIZE; else if (unlikely(type == SECTOR)) return info->blk_cnt_per_die * info->page_cnt_per_blk * info->sect_cnt_per_page; else if (unlikely(type == PAGE)) return info->blk_cnt_per_die * info->page_cnt_per_blk; else { pr_err("no this type:%d die size in BYTE(0)@byte " "SECTOR(1)@sector PAGE(2)@page BLOCK(3)@block", type); return ERR_NO_22; } return ERR_NO_22; } unsigned int rawnand_get_super_chip_cnt(struct nand_super_chip_info *schip) { if (!g_nssi) { RAWNAND_INFO("rawnand hw not init\n"); return 0; } return g_nssi->super_chip_cnt; } unsigned int rawnand_get_super_chip_size(struct nand_super_chip_info *schip) { if (!schip) { RAWNAND_INFO("rawnand hw not init\n"); return 0; } return schip->blk_cnt_per_super_chip; } unsigned int rawnand_get_super_chip_block_size(struct nand_super_chip_info *schip) { if (!schip) { RAWNAND_INFO("rawnand hw not init\n"); return 0; } return schip->page_cnt_per_super_blk; } unsigned int rawnand_get_super_chip_page_size(struct nand_super_chip_info *schip) { if (!schip) { RAWNAND_INFO("rawnand hw not init\n"); return 0; } return schip->sector_cnt_per_super_page; } unsigned int rawnand_get_super_chip_spare_size(struct nand_super_chip_info *schip) { if (!schip) { RAWNAND_INFO("rawnand hw not init\n"); return 0; } return schip->spare_bytes; } unsigned long long rawnand_get_chip_opt(struct nand_chip_info *chip) { struct sunxi_nand_flash_device *info = chip->npi; return info->operation_opt; } unsigned int rawnand_get_chip_ecc_mode(struct nand_chip_info *chip) { struct sunxi_nand_flash_device *info = chip->npi; return info->ecc_mode; } unsigned int rawnand_get_chip_freq(struct nand_chip_info *chip) { struct sunxi_nand_flash_device *info = chip->npi; return info->access_freq; } /** * nand_chip_init: rawnand chip init * @nci: rawnand chip info * @c : number of the chip in the channel */ static int rawnand_chip_init(struct nand_chip_info *nci, int c) { struct sunxi_nand_flash_device *nand_id_tbl = NULL; unsigned char id[8] = {0}; if (nci == NULL) { RAWNAND_ERR("rawnand err: %s nci is null\n", __func__); return ERR_NO_12; } if (nand_first_reset_chip(nci, c) != NAND_OP_TRUE) { RAWNAND_ERR("rawnand err: %s first reset chip fail\n", __func__); return ERR_NO_13; } if (nand_first_read_id(nci, c, id)) { RAWNAND_ERR("rawnand err:%s first read id fail\n", __func__); return ERR_NO_14; } nand_id_tbl = sunxi_search_id(nci, id); if (nand_id_tbl == NULL) { RAWNAND_ERR("rawnand not support chip %d: " "%02x %02x %02x %02x %02x %02x %02x %02x\n", c, id[0], id[1], id[2], id[3], id[4], id[5], id[6], id[7]); return ERR_NO_15; } else { RAWNAND_INFO("rawnand support chip %d: " "%02x %02x %02x %02x %02x %02x %02x %02x\n", c, id[0], id[1], id[2], id[3], id[4], id[5], id[6], id[7]); } if (init_nci_from_id(nci, nand_id_tbl) != NAND_OP_TRUE) { RAWNAND_ERR("rawnand err: %s init nci fail\n", __func__); return ERR_NO_16; } return NAND_OP_TRUE; } /** * nand_chips_init: rawnand chips init * * @chip: chip info * @info: nand info * * */ int rawnand_chips_init(struct nand_chip_info *nci) { struct nand_chip_info *inci = NULL; struct nand_chip_info *inci_temp = NULL; int c = 0; int ret = 0; struct _nand_storage_info *nsi = g_nsi; struct nand_controller_info *nctri = in_container_of(nci, struct nand_controller_info, nci); if (nsi == NULL) { RAWNAND_ERR("%s no memory for nsi\n", __func__); return ERR_NO_12; } for (c = 0; c < MAX_CHIP_PER_CHANNEL; c++) { inci = nand_malloc(sizeof(struct nand_chip_info)); if (inci == NULL) { RAWNAND_ERR("rawnand err: cannot get mem for nci"); return ERR_NO_12; /*-ENOMEM*/ } memset(inci, 0, sizeof(struct nand_chip_info)); inci->nctri = nctri; inci->chip_no = nsi->chip_cnt; /*init chip*/ ret = rawnand_chip_init(inci, c); if (ret == ERR_NO_15) { nand_free(inci); if (nsi->chip_cnt == 0) { RAWNAND_ERR("no rawnand found!!!\n"); return ret = NAND_OP_FALSE; } else return ret = NAND_OP_TRUE; } else if (ret != NAND_OP_TRUE) { break; } nsi->chip_cnt++; nci_add_to_nsi(nsi, inci); nctri->chip_cnt++; nctri->chip_connect_info |= (0x1U << c); nci_add_to_nctri(nctri, inci); /*the channel's chip should use the same id item*/ if (c > 0) { if (inci_temp->npi->id_number != inci->npi->id_number) { RAWNAND_ERR("%s chip@%d and chip@%d no use the " "same id item\n", __func__, c - 1, c); goto err0; } } inci_temp = inci; } if (nctri->chip_cnt == 0) { RAWNAND_ERR("no rawnand found!\n"); ret = NAND_OP_FALSE; } else ret = NAND_OP_TRUE; return ret; err0: nci_delete_from_nsi(nsi); nci_delete_from_nctri(nctri); return NAND_OP_FALSE; } /** * rawnand_chip_special_init: init different flash special request eg. readretry * @type: readretry type * NAND_READRETRY_NO: flash that no readretry * NAND_READRETRY_HYNIX_16NM: hynix 16nm flash that has readretry * NAND_READRETRY_HYNIX_20NM: hynix 20nm flash that has readretry * NAND_READRETRY_HYNIX_26NM: hynix 26nm flash that has readretry * NAND_READRETRY_MICRON: micron flash that has readretry * NAND_READRETRY_SAMSUNG: samsung flash that has readretry * NAND_READRETRY_TOSHIBA: toshiba flash that has readretry * NADN_READRETRY_SANDISK: sandisk flash that has readretry * NAND_READRETRY_SANDISK_A19: sandisk A19 flash that has readretry */ void rawnand_chip_special_init(enum nand_readretry_type type) { switch (type) { case NAND_READRETRY_NO: generic_special_init(); break; case NAND_READRETRY_HYNIX_16NM: hynix16nm_special_init(); break; case NAND_READRETRY_HYNIX_20NM: hynix20nm_special_init(); break; case NAND_READRETRY_HYNIX_26NM: hynix26nm_special_init(); break; case NAND_READRETRY_MICRON: micron_special_init(); break; case NAND_READRETRY_SAMSUNG: samsung_special_init(); break; case NAND_READRETRY_TOSHIBA: toshiba_special_init(); break; case NAND_READRETRY_SANDISK: sandisk_special_init(); break; case NAND_READRETRY_SANDISK_A19: sandisk_A19_special_init(); break; default: generic_special_init(); break; } } /** * rawnand_chip_special_exit: exit different flash special request eg. readretry * @type: readretry type * NAND_READRETRY_NO: flash that no readretry * NAND_READRETRY_HYNIX_16NM: hynix 16nm flash that has readretry * NAND_READRETRY_HYNIX_20NM: hynix 20nm flash that has readretry * NAND_READRETRY_HYNIX_26NM: hynix 26nm flash that has readretry * NAND_READRETRY_MICRON: micron flash that has readretry * NAND_READRETRY_SAMSUNG: samsung flash that has readretry * NAND_READRETRY_TOSHIBA: toshiba flash that has readretry * NADN_READRETRY_SANDISK: sandisk flash that has readretry * NAND_READRETRY_SANDISK_A19: sandisk A19 flash that has readretry */ void rawnand_chip_special_exit(enum nand_readretry_type type) { switch (type) { case NAND_READRETRY_NO: generic_special_exit(); break; case NAND_READRETRY_HYNIX_16NM: hynix16nm_special_exit(); break; case NAND_READRETRY_HYNIX_20NM: hynix20nm_special_exit(); break; case NAND_READRETRY_HYNIX_26NM: hynix26nm_special_exit(); break; case NAND_READRETRY_MICRON: micron_special_exit(); break; case NAND_READRETRY_SAMSUNG: samsung_special_exit(); break; case NAND_READRETRY_TOSHIBA: toshiba_special_exit(); break; case NAND_READRETRY_SANDISK: sandisk_special_exit(); break; case NAND_READRETRY_SANDISK_A19: sandisk_A19_special_exit(); break; default: generic_special_exit(); break; } } /** * rawnand_sp_chips_init: build nand super storage info * @nsci: nand super chip info * * 规则: * 1.一个通道内需要贴同一种flash * 2.两个通道应该贴同样数目和类型的flash * * 单通道 * 1.支持 two-plane * 2.支持 vertical_interleave * 3.如果超级页超过32k,不支持two-plane * 4.vertical_interleave 通道内rb不相同的chip配对 * * 双通道 * 1.支持 two-plane * 2.支持dual_channel * 3.支持vertical_interleave * 4.如果超级页超过32k,不支持two-plane * 5.dual_channel 通道间chip0和chip0配对 * 6.vertical_interleave 通道内rb不相同的chip配对 */ int rawnand_sp_chips_init(struct nand_super_chip_info *nsci) { int rb1, rb2, rb0, ret; int i, channel_num, nsci_num, nsci_num_in_nctri; struct nand_controller_info *nctri_temp; struct nand_super_chip_info *insci; struct nand_controller_info *nctri = g_nctri; struct _nand_super_storage_info *nssi = in_container_of(nsci, struct _nand_super_storage_info, nsci); if (nctri == NULL) { RAWNAND_ERR("rawnand err: %s nctri is null\n", __func__); } ret = get_nand_structure(nssi); if (ret != 0) { RAWNAND_ERR("%s get nand struction fail\n", __func__); return ret; } nsci_num = 0; for (channel_num = 0, nctri_temp = nctri; nctri_temp; nctri_temp = nctri_temp->next) { if (nctri_temp->chip_cnt != 0) channel_num++; } if (nctri->chip_cnt == 1) { nssi->support_v_interleave = 0; } else if (nctri->chip_cnt == 2) { rb0 = nctri->rb[0]; rb1 = nctri->rb[1]; if (rb0 == rb1) { nssi->support_v_interleave = 0; } } else { /*must be nctri->chip_cnt == 4*/ rb0 = nctri->rb[0]; rb1 = nctri->rb[1]; rb2 = nctri->rb[2]; // rb3 = nctri->rb[3]; if ((rb0 == rb1) && (rb0 == rb2)) { nssi->support_v_interleave = 0; } } if (channel_num == 1) { if ((nctri->chip_cnt == 1) || (nctri->chip_cnt == 2) || (nctri->chip_cnt == 4)) { if (nssi->support_v_interleave != 0) { nsci_num = nctri->chip_cnt / 4 + 1; } else { nsci_num = nctri->chip_cnt; } } else { RAWNAND_ERR("not support chip_cnt1 %u\n", nctri->chip_cnt); return NAND_OP_FALSE; } nsci_num_in_nctri = nsci_num; } else if (channel_num == 2) { if ((nctri->chip_cnt == 1) || (nctri->chip_cnt == 2) || (nctri->chip_cnt == 4)) { if (nssi->support_dual_channel != 0) { nsci_num = nctri->chip_cnt; } else { nsci_num = nctri->chip_cnt << 1; } nsci_num_in_nctri = nctri->chip_cnt; if (nssi->support_v_interleave != 0) { nsci_num >>= 1; nsci_num_in_nctri >>= 1; } } else { RAWNAND_ERR("not support chip_cnt2 %u\n", nctri->chip_cnt); return NAND_OP_FALSE; } } else { RAWNAND_ERR("not support channel_num %d\n", channel_num); return NAND_OP_FALSE; } for (i = 0; i < nsci_num; i++) { insci = &nsci_data[i]; if (insci == NULL) { RAWNAND_ERR("no memory for nssi\n"); } memset(insci, 0, sizeof(struct nand_super_chip_info)); init_nsci_from_nctri(nssi, insci, nctri, channel_num, i, nsci_num_in_nctri); nsci_add_to_nssi(nssi, insci); insci = insci->nssi_next; } nssi->super_chip_cnt = nsci_num; if (nsci_num == 0) { RAWNAND_ERR("not support chip_cnt %d %d\n", channel_num, nctri->chip_cnt); return NAND_OP_FALSE; } else { RAWNAND_DBG("%s %d\n", __func__, __LINE__); return NAND_OP_TRUE; } } /** * nand_chips_cleanup: rawnand exit * * @chip: chip info * * */ void rawnand_chips_cleanup(struct nand_chip_info *chip) { return; } /** * nand_chips_super_standby: rawnand super standby * * @chip: chip info * * */ int rawnand_chips_super_standby(struct nand_chip_info *chip) { return 0; } /** * nand_chips_super_resume: rawnand super resume * * @chip: chip info * * */ int rawnand_chips_super_resume(struct nand_chip_info *chip) { return 0; } /** * nand_chips_normal_standby: rawnand normal standby * * @chip: chip info * * */ int rawnand_chips_normal_standby(struct nand_chip_info *chip) { return 0; } /** * nand_chips_normal_resume: rawnand normal resume * * @chip: chip info * * */ int rawnand_chips_normal_resume(struct nand_chip_info *chip) { return 0; } /* *struct physic_special_ops special_ops = { * .nand_physic_special_init = NULL, * .nand_physic_special_exit = NULL, *}; */ /* *struct nand_chips_ops rawnand_chips_ops = { * .nand_chips_init = rawnand_chips_init, * .nand_chips_cleanup = rawnand_chips_cleanup, *#ifdef SUPPORT_SUPER_STANDBY * .nand_chips_standby = rawnand_chips_super_standby, * .nand_chips_resume = rawnand_chips_super_resume, *#else * .nand_chips_standby = rawnand_chips_super_standby, * .nand_chips_resume = rawnand_chips_super_resume, *#endif *}; */