rt-thread/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand.c

570 lines
22 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright : (C) 2022 Phytium Information Technology, Inc.
* All Rights Reserved.
*
* This program is OPEN SOURCE software: you can redistribute it and/or modify it
* under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd,
* either version 1.0 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the Phytium Public License for more details.
*
*
* FilePath: fnand.c
* Date: 2022-05-10 14:53:42
* LastEditTime: 2022-05-10 08:56:27
* Description:  This file is for functions in this file are the minimum required functions
* for this driver.
*
* Modify History:
* Ver   Who        Date         Changes
* ----- ------     --------    --------------------------------------
* 1.0 huanghe 2022/05/10 first release
*/
#include "fnand.h"
#include "fnand_hw.h"
#include <stdio.h>
#include <string.h>
#include "fnand_id.h"
#include "fnand_common_cmd.h"
#include "fdebug.h"
#define FNAND_DEBUG_TAG "FNAND"
#define FNAND_DEBUG_I(format, ...) FT_DEBUG_PRINT_I(FNAND_DEBUG_TAG, format, ##__VA_ARGS__)
#define FNAND_DEBUG_W(format, ...) FT_DEBUG_PRINT_W(FNAND_DEBUG_TAG, format, ##__VA_ARGS__)
#define FNAND_DEBUG_E(format, ...) FT_DEBUG_PRINT_E(FNAND_DEBUG_TAG, format, ##__VA_ARGS__)
extern void FNandHwInit(uintptr_t base_address, FNandInterMode inter_mode);
extern void FNandHwReset(uintptr_t base_address);
extern void FNandEnable(uintptr_t base_address);
extern FError FNandToggleInit(FNand *instance_p, u32 chip_addr);
extern FError FNandOnfiInit(FNand *instance_p, u32 chip_addr);
extern FError FNandTimingInterfaceUpdate(FNand *instance_p, u32 chip_addr);
extern void FNandIsrEnable(FNand *instance_p, u32 int_mask);
/**
* @name: FNandScan
* @msg: Nand scanning
* @note:
* @param {FNand} *instance_p is the pointer to the FNand instance.
* @return {FT_SUCCESS} Scan nand is ok
*/
FError FNandScan(FNand *instance_p)
{
FASSERT(instance_p != NULL);
FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY);
return FNandDetect(instance_p);
}
u32 FNandCheckBusy(FNand *instance_p)
{
FASSERT(instance_p != NULL);
FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY);
FNandConfig *config_p;
config_p = &instance_p->config;
return FNAND_READREG(config_p->base_address, FNAND_STATE_OFFSET) & FNAND_STATE_BUSY_OFFSET;
}
FError FNandSendCmd(FNand *instance_p, struct FNandDmaDescriptor *descriptor_p, FNandOperationType isr_type)
{
FNandConfig *config_p;
u32 timeout_cnt = 0;
FASSERT(instance_p != NULL);
FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY);
FASSERT(isr_type < FNAND_TYPE_NUM);
config_p = &instance_p->config;
FNandHwReset(config_p->base_address);
if (0 != FNandCheckBusy(instance_p))
{
FNAND_DEBUG_E("Nand is busy");
return FNAND_IS_BUSY;
}
/* write dma addr to register */
FNAND_WRITEREG(config_p->base_address, FNAND_MADDR0_OFFSET, ((uintptr)descriptor_p) & FNAND_MADDR0_DT_LOW_ADDR_MASK);
#ifdef __aarch64__
/* 将高位地址填入寄存器 */
FNAND_CLEARBIT(config_p->base_address, FNAND_MADDR1_OFFSET, FNAND_MADDR1_DT_HIGH_8BITADDR_MASK);
FNAND_SETBIT(config_p->base_address, FNAND_MADDR1_OFFSET, ((uintptr)descriptor_p >> 32) & FNAND_MADDR1_DT_HIGH_8BITADDR_MASK);
#else
FNAND_CLEARBIT(config_p->base_address, FNAND_MADDR1_OFFSET, FNAND_MADDR1_DT_HIGH_8BITADDR_MASK);
#endif
/* 中断模式操作 */
if (instance_p->work_mode == FNAND_WORK_MODE_ISR)
{
if (isr_type == FNAND_CMD_TYPE)
{
FNandIsrEnable(instance_p, FNAND_INTRMASK_CMD_FINISH_MASK);
}
else if (isr_type == FNAND_WRITE_PAGE_TYPE)
{
FNandIsrEnable(instance_p, FNAND_INTRMASK_PGFINISH_MASK);
}
else if (isr_type == FNAND_READ_PAGE_TYPE)
{
FNandIsrEnable(instance_p, FNAND_INTRMASK_DMA_PGFINISH_MASK);
}
else if (isr_type == FNAND_WAIT_ECC_TYPE)
{
FNandIsrEnable(instance_p, FNAND_INTRMASK_ECC_FINISH_MASK);
}
}
FNAND_SETBIT(config_p->base_address, FNAND_MADDR1_OFFSET, FNAND_MADDR1_DMA_EN_MASK);
if (instance_p->work_mode == FNAND_WORK_MODE_ISR && (instance_p->wait_irq_fun_p != NULL))
{
if (instance_p->wait_irq_fun_p)
{
if (instance_p->wait_irq_fun_p(instance_p->wait_args) != FT_SUCCESS)
{
FNAND_DEBUG_E("wait_irq_fun_p is failed");
return FNAND_ERR_IRQ_OP_FAILED;
}
}
else
{
FNAND_DEBUG_E("The member wait_irq_fun_p of instance_p is null");
FNAND_WRITEREG(config_p->base_address, FNAND_INTRMASK_OFFSET, FNAND_INTRMASK_ALL_INT_MASK);
return FNAND_ERR_IRQ_LACK_OF_CALLBACK;
}
return FT_SUCCESS ;
}
else
{
if (isr_type == FNAND_CMD_TYPE)
{
while (0 == (FNAND_READREG(config_p->base_address, FNAND_STATE_OFFSET) & FNAND_STATE_CMD_PGFINISH_OFFSET))
{
if (timeout_cnt++ >= 0xffffff)
{
FNAND_DEBUG_E("FNAND_CMD_TYPE is sending timeout");
return FNAND_OP_TIMEOUT;
}
}
}
else if (isr_type == FNAND_WRITE_PAGE_TYPE)
{
while (0 == (FNAND_READREG(config_p->base_address, FNAND_STATE_OFFSET) & FNAND_STATE_PG_PGFINISH_OFFSET))
{
if (timeout_cnt++ >= 0xffffff)
{
FNAND_DEBUG_E("FNAND_CMD_TYPE is sending timeout");
return FNAND_OP_TIMEOUT;
}
}
}
else if (isr_type == FNAND_READ_PAGE_TYPE)
{
while (0 == (FNAND_READREG(config_p->base_address, FNAND_STATE_OFFSET) & FNAND_STATE_DMA_PGFINISH_OFFSET))
{
if (timeout_cnt++ >= 0xffffff)
{
FNAND_DEBUG_E("FNAND_CMD_TYPE is sending timeout");
return FNAND_OP_TIMEOUT;
}
}
}
else if (isr_type == FNAND_WAIT_ECC_TYPE)
{
while (0 == (FNAND_READREG(config_p->base_address, FNAND_STATE_OFFSET) & FNAND_STATE_ECC_FINISH_OFFSET))
{
if (timeout_cnt++ >= 0xffffff)
{
FNAND_DEBUG_E("FNAND_CMD_TYPE is sending timeout");
return FNAND_OP_TIMEOUT;
}
}
}
}
return FT_SUCCESS;
}
/**
* @name: FNandOperationWaitIrqRegister
* @msg: When nand is sent in interrupt mode, the action that waits while the operation completes
* @note:
* @param {FNand} *instance_p is the pointer to the FNand instance.
* @param {FNandOperationWaitIrqCallback} wait_irq_fun_p , When the user adds this function, return FT_SUCCESS reports success, otherwise failure
* @param {void} *wait_args
* @return {*}
*/
void FNandOperationWaitIrqRegister(FNand *instance_p, FNandOperationWaitIrqCallback wait_irq_fun_p, void *wait_args)
{
FASSERT(instance_p != NULL);
FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY);
instance_p->wait_irq_fun_p = wait_irq_fun_p;
instance_p->wait_args = wait_args;
}
/**
* @name: FNandCfgInitialize
* @msg: Initialize the NAND controller
* @param {FNand} *instance_p is the pointer to the FNand instance.
* @param {FNandConfig} * points to the FNand device configuration structure.
* @return {FError} FT_SUCCESS if successful
* @note:
*/
FError FNandCfgInitialize(FNand *instance_p,
FNandConfig *config_p)
{
u32 i;
FError ret;
/* Assert arguments */
FASSERT(instance_p != NULL);
FASSERT(config_p != NULL);
/* Clear instance memory and make copy of configuration */
memset(instance_p, 0, sizeof(FNand));
instance_p->config = *config_p;
instance_p->is_ready = FT_COMPONENT_IS_READY;
/* lsd config */
FNAND_CLEARBIT(FLSD_CONFIG_BASE, 0xc0, 1);
instance_p->work_mode = FNAND_WORK_MODE_ISR ; /* 默认采用中断模式 */
for (i = 0; i < FNAND_CONNECT_MAX_NUM; i++)
{
instance_p->inter_mode[i] = FNAND_ASYN_SDR; /* 初始化阶段以异步模式启动 */
instance_p->timing_mode[i] = FNAND_TIMING_MODE0 ;
/* 初始化时序配置 */
ret = FNandTimingInterfaceUpdate(instance_p, i);
if (ret != FT_SUCCESS)
{
FNAND_DEBUG_E("%s, FNandTimingInterfaceUpdate is error", __func__);
return ret;
}
}
FNandHwInit(instance_p->config.base_address, instance_p->inter_mode[0]);
FNandHwReset(instance_p->config.base_address);
/* init ecc strength */
FNAND_CLEARBIT(instance_p->config.base_address, FNAND_CTRL0_OFFSET, FNAND_CTRL0_ECC_CORRECT_MAKE(7UL)); /* clear all ecc_correct */
if (instance_p->config.ecc_strength == 0x8)
{
FNAND_SETBIT(instance_p->config.base_address, FNAND_CTRL0_OFFSET, FNAND_CTRL0_ECC_CORRECT_MAKE(7UL));
}
else if (instance_p->config.ecc_strength == 0x4)
{
FNAND_SETBIT(instance_p->config.base_address, FNAND_CTRL0_OFFSET, FNAND_CTRL0_ECC_CORRECT_MAKE(3UL));
}
else if (instance_p->config.ecc_strength == 0x2)
{
FNAND_SETBIT(instance_p->config.base_address, FNAND_CTRL0_OFFSET, FNAND_CTRL0_ECC_CORRECT_MAKE(1UL));
}
else
{
FNAND_SETBIT(instance_p->config.base_address, FNAND_CTRL0_OFFSET, FNAND_CTRL0_ECC_CORRECT_MAKE(0UL));
}
FNandEnable(instance_p->config.base_address);
/* init bbm */
FNandInitBbtDesc(instance_p);
return (FT_SUCCESS);
}
/**
* @name: FNandWritePage
* @msg: Write operations one page at a time, including writing page data and spare data
* @note:
* @param {FNand} *instance_p is the pointer to the FNand instance.
* @param {u32} page_addr is the address to which the page needs to be written
* @param {u8} *buffer is page writes a pointer to the buffer
* @param {u32} page_copy_offset is the offset of the page writing , Buffer write data to 0 + page_copy_offset
* @param {u32} length is page data write length
* @param {u8} *oob_buffer is the data buffer pointer needs to be written to the spare space
* @param {u32} oob_copy_offset is the offset of the spare space writing , Buffer write data to page length + oob_copy_offset
* @param {u32} oob_length is the length to be written to the spare space
* @param {u32} chip_addr chip address
* @return {FError} FT_SUCCESS ,write page is successful
*/
FError FNandWritePage(FNand *instance_p, u32 page_addr, u8 *buffer, u32 page_copy_offset, u32 length, u8 *oob_buffer, u32 oob_copy_offset, u32 oob_length, u32 chip_addr)
{
FASSERT(instance_p != NULL);
FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY);
FASSERT(chip_addr < FNAND_CONNECT_MAX_NUM);
FASSERT(instance_p->write_hw_ecc_p);
FNandOpData op_data =
{
.page_addr = page_addr,
.page_buf = NULL, /* page 数据缓存空间 */
.page_offset = 0, /* 从offset开始拷贝页数据 */
.page_length = 0, /* 从offset开始拷贝页数据的长度 */
.obb_required = 0, /* obb 是否读取的标志位,1 需要操作oob 区域 */
.oob_buf = NULL, /* obb 数据缓存空间 */
.oob_offset = 0, /* 从offset开始拷贝页数据 */
.oob_length = 0, /* 从offset开始拷贝页数据的长度 */
.chip_addr = chip_addr, /* 芯片地址 */
};
if (buffer && (length > 0))
{
op_data.page_buf = buffer;
op_data.page_length = length;
op_data.page_offset = page_copy_offset;
}
if (oob_buffer && (oob_length > 0))
{
op_data.obb_required = 1;
op_data.oob_buf = oob_buffer;
op_data.oob_length = oob_length;
op_data.oob_offset = oob_copy_offset;
}
return instance_p->write_hw_ecc_p(instance_p, &op_data);
}
/**
* @name: FNandWritePage
* @msg: Write operations one page at a time, including writing page data and spare data ,without hw ecc
* @note:
* @param {FNand} *instance_p is the pointer to the FNand instance.
* @param {u32} page_addr is the address to which the page needs to be written
* @param {u8} *buffer is page writes a pointer to the buffer
* @param {u32} page_copy_offset is the offset of the page writing , Buffer write data to 0 + page_copy_offset
* @param {u32} length is page data write length
* @param {u8} *oob_buffer is the data buffer pointer needs to be written to the spare space
* @param {u32} oob_copy_offset is the offset of the spare space writing , Buffer write data to page length + oob_copy_offset
* @param {u32} oob_length is the length to be written to the spare space
* @param {u32} chip_addr chip address
* @return {FError} FT_SUCCESS ,write page is successful
*/
FError FNandWritePageRaw(FNand *instance_p, u32 page_addr, u8 *buffer, u32 page_copy_offset, u32 length, u8 *oob_buffer, u32 oob_copy_offset, u32 oob_length, u32 chip_addr)
{
FASSERT(instance_p != NULL);
FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY);
FASSERT(chip_addr < FNAND_CONNECT_MAX_NUM);
FASSERT(instance_p->write_hw_ecc_p);
FNandOpData op_data =
{
.page_addr = page_addr,
.page_buf = NULL, /* page 数据缓存空间 */
.page_offset = 0, /* 从offset开始拷贝页数据 */
.page_length = 0, /* 从offset开始拷贝页数据的长度 */
.obb_required = 0, /* obb 是否读取的标志位,1 需要操作oob 区域 */
.oob_buf = NULL, /* obb 数据缓存空间 */
.oob_offset = 0, /* 从offset开始拷贝页数据 */
.oob_length = 0, /* 从offset开始拷贝页数据的长度 */
.chip_addr = chip_addr, /* 芯片地址 */
};
if (buffer && (length > 0))
{
op_data.page_buf = buffer;
op_data.page_length = length;
op_data.page_offset = page_copy_offset;
}
if (oob_buffer && (oob_length > 0))
{
op_data.obb_required = 1;
op_data.oob_buf = oob_buffer;
op_data.oob_length = oob_length;
op_data.oob_offset = oob_copy_offset;
}
return instance_p->write_p(instance_p, &op_data);
}
/**
* @name: FNandReadPage
* @msg: Read operations one page at a time, including reading page data and spare space data
* @note:
* @param {FNand} *instance_p is the pointer to the FNand instance.
* @param {u32} page_addr is the address to which the page needs to be readed
* @param {u8} *buffer is the buffer used by the user to read page data
* @param {u32} page_copy_offset is the offset of the page reading , Buffer read data from 0 + page_copy_offset of per page
* @param {u32} length is page data read length
* @param {u8} *oob_buffer is buffer that read data from the spare space
* @param {u32} oob_copy_offset is the offset of the spare space reading , Buffer reads data from page length + oob_copy_offset
* @param {u32} oob_length is the length to be written to the spare space
* @param {u32} chip_addr chip address
* @return {FError} FT_SUCCESS is read successful
*/
FError FNandReadPage(FNand *instance_p, u32 page_addr, u8 *buffer, u32 page_copy_offset, u32 length, u8 *oob_buffer, u32 oob_copy_offset, u32 oob_length, u32 chip_addr)
{
FASSERT(instance_p != NULL);
FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY);
FASSERT(chip_addr < FNAND_CONNECT_MAX_NUM);
FASSERT(instance_p->read_hw_ecc_p);
FNandOpData op_data =
{
.page_addr = page_addr,
.page_buf = NULL, /* page 数据缓存空间 */
.page_offset = 0, /* 从offset开始拷贝页数据 */
.page_length = 0, /* 从offset开始拷贝页数据的长度 */
.obb_required = 0, /* obb 是否读取的标志位,1 需要操作oob 区域 */
.oob_buf = NULL, /* obb 数据缓存空间 */
.oob_offset = 0, /* 从offset开始拷贝页数据 */
.oob_length = 0, /* 从offset开始拷贝页数据的长度 */
.chip_addr = chip_addr, /* 芯片地址 */
};
/* clear buffer */
if (buffer && (length > 0))
{
op_data.page_buf = buffer;
op_data.page_length = length;
op_data.page_offset = page_copy_offset;
}
if (oob_buffer && (oob_length > 0))
{
op_data.obb_required = 1;
op_data.oob_buf = oob_buffer;
op_data.oob_length = oob_length;
op_data.oob_offset = oob_copy_offset;
}
return instance_p->read_hw_ecc_p(instance_p, &op_data);
}
FError FNandReadPageRaw(FNand *instance_p, u32 page_addr, u8 *buffer, u32 page_copy_offset, u32 length, u8 *oob_buffer, u32 oob_copy_offset, u32 oob_length, u32 chip_addr)
{
FASSERT(instance_p != NULL);
FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY);
FASSERT(chip_addr < FNAND_CONNECT_MAX_NUM);
FASSERT(instance_p->read_hw_ecc_p);
FNandOpData op_data =
{
.page_addr = page_addr,
.page_buf = NULL, /* page 数据缓存空间 */
.page_offset = 0, /* 从offset开始拷贝页数据 */
.page_length = 0, /* 从offset开始拷贝页数据的长度 */
.obb_required = 0, /* obb 是否读取的标志位,1 需要操作oob 区域 */
.oob_buf = NULL, /* obb 数据缓存空间 */
.oob_offset = 0, /* 从offset开始拷贝页数据 */
.oob_length = 0, /* 从offset开始拷贝页数据的长度 */
.chip_addr = chip_addr, /* 芯片地址 */
};
/* clear buffer */
if (buffer && (length > 0))
{
op_data.page_buf = buffer;
op_data.page_length = length;
op_data.page_offset = page_copy_offset;
}
if (oob_buffer && (oob_length > 0))
{
op_data.obb_required = 1;
op_data.oob_buf = oob_buffer;
op_data.oob_length = oob_length;
op_data.oob_offset = oob_copy_offset;
}
return instance_p->read_p(instance_p, &op_data);
}
/**
* @name: FNandEraseBlock
* @msg: erase block data
* @note: 擦除之后增加read status 命令进行检查。70h
* @param {FNand} *instance_p is the pointer to the FNand instance.
* @param {u32} block is block number
* @param {u32} chip_addr is chip address
* @return {FError} FT_SUCCESS is erase is successful
*/
FError FNandEraseBlock(FNand *instance_p, u32 block, u32 chip_addr)
{
u32 page_address;
FASSERT(instance_p != NULL);
FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY);
FASSERT(chip_addr < FNAND_CONNECT_MAX_NUM);
page_address = block * instance_p->nand_geometry[chip_addr].pages_per_block;
return instance_p->erase_p(instance_p, page_address, chip_addr);
}
/**
* @name: FNandReadPageOOb
* @msg: Read spare space fo per page
* @note:
* @param {FNand} *instance_p is the instance pointer
* @param {u32} page_addr is the Row Address of the spare space needs to be read
* @param {u8} *oob_buffer is the buffer used by the user to read spare space data
* @param {u32} oob_copy_offset is the offset of the spare space reading , Buffer reads data from page length + page_copy_offset
* @param {u32} oob_length is the length of data retrieved from spare space
* @param {u32} chip_addr is chip address
* @return {FError} FT_SUCCESS is read successful
*/
FError FNandReadPageOOb(FNand *instance_p, u32 page_addr, u8 *oob_buffer, u32 oob_copy_offset, u32 oob_length, u32 chip_addr)
{
FASSERT(instance_p != NULL);
FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY);
FASSERT(chip_addr < FNAND_CONNECT_MAX_NUM);
FNandOpData op_data =
{
.page_addr = page_addr,
.page_buf = NULL, /* page 数据缓存空间 */
.page_offset = 0, /* 从offset开始拷贝页数据 */
.page_length = 0, /* 从offset开始拷贝页数据的长度 */
.obb_required = 1, /* obb 是否读取的标志位,1 需要操作oob 区域 */
.oob_buf = oob_buffer, /* obb 数据缓存空间 */
.oob_offset = oob_copy_offset, /* 从offset开始拷贝页数据 */
.oob_length = oob_length, /* 从offset开始拷贝页数据的长度 */
.chip_addr = chip_addr, /* 芯片地址 */
};
return instance_p->read_oob_p(instance_p, &op_data);
}
/**
* @name: FNandWritePageOOb
* @msg: write data to the spare space
* @note:
* @param {FNand} *instance_p is the instance pointer
* @param {u32} page_addr is the Row Address of the spare space needs to be write
* @param {u8} *oob_buffer is buffer that writes data to the spare space
* @param {u32} page_copy_offset is the offset of the spare space writing , Buffer write data to page length + page_copy_offset
* @param {u32} oob_length is the length to be written to the spare space
* @param {u32} chip_addr is chip address
* @return {FError} FT_SUCCESS is write successful
*/
FError FNandWritePageOOb(FNand *instance_p, u32 page_addr, u8 *oob_buffer, u32 page_copy_offset, u32 oob_length, u32 chip_addr)
{
FASSERT(instance_p != NULL);
FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY);
FASSERT(chip_addr < FNAND_CONNECT_MAX_NUM);
FNandOpData op_data =
{
.page_addr = page_addr,
.page_buf = NULL, /* page 数据缓存空间 */
.page_offset = 0, /* 从offset开始拷贝页数据 */
.page_length = 0, /* 从offset开始拷贝页数据的长度 */
.obb_required = 1, /* obb 是否读取的标志位,1 需要操作oob 区域 */
.oob_buf = oob_buffer, /* obb 数据缓存空间 */
.oob_offset = page_copy_offset, /* 从offset开始拷贝页数据 */
.oob_length = oob_length, /* 从offset开始拷贝页数据的长度 */
.chip_addr = chip_addr, /* 芯片地址 */
};
return instance_p->write_oob_p(instance_p, &op_data);
}