2699 lines
66 KiB
C
Executable File
2699 lines
66 KiB
C
Executable File
/* SPDX-License-Identifier: GPL-2.0 */
|
||
/**
|
||
* rawnand_chip.c
|
||
*
|
||
* Copyright (C) 2019 Allwinner.
|
||
*
|
||
* 2019.9.11 cuizhikui<cuizhikui@allwinnertech.com>
|
||
* 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 <linux/sunxi-boot.h>
|
||
|
||
|
||
#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;j<dat_len;j++)
|
||
// {
|
||
// RAWNAND_DBG("0x%x,",p_dat[j]);
|
||
// }
|
||
|
||
p_dat += dat_len;
|
||
}
|
||
}
|
||
|
||
//RAWNAND_DBG("\n");
|
||
|
||
ret = ndfc_execute_cmd(nci->nctri, 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
|
||
*};
|
||
*/
|