1102 lines
27 KiB
C
1102 lines
27 KiB
C
/****************************************************************************
|
|
|
|
Copyright(c) 2019 by Aerospace C.Power (Chongqing) Microelectronics. ALL RIGHTS RESERVED.
|
|
|
|
This Information is proprietary to Aerospace C.Power (Chongqing) Microelectronics and MAY NOT
|
|
be copied by any method or incorporated into another program without
|
|
the express written consent of Aerospace C.Power. This Information or any portion
|
|
thereof remains the property of Aerospace C.Power. The Information contained herein
|
|
is believed to be accurate and Aerospace C.Power assumes no responsibility or
|
|
liability for its use in any way and conveys no license or title under
|
|
any patent or copyright and makes no representation or warranty that this
|
|
Information is free from patent or copyright infringement.
|
|
|
|
****************************************************************************/
|
|
/* os shim includes */
|
|
#include "os_types.h"
|
|
#include "iot_errno_api.h"
|
|
#include "ndfc.h"
|
|
#if 0
|
|
/* os shim includes */
|
|
#include "iot_config.h"
|
|
|
|
#include "hw_reg_api.h"
|
|
#include "chip_reg_base.h"
|
|
|
|
#include "ds35x1gaxxx.h"
|
|
#include "ap_clk_core_rf.h"
|
|
#include "dmc_reg_rf.h"
|
|
#include "ndfc_rf.h"
|
|
#include "os_mem_api.h"
|
|
#include "gpio_mtx.h"
|
|
|
|
int32_t iot_printf(const char *fmt, ...);
|
|
|
|
enum ndfc_signal_e
|
|
{
|
|
SIG_I_D0 = 62,
|
|
SIG_I_D1 = 63,
|
|
SIG_I_D2 = 64,
|
|
SIG_I_D3 = 65,
|
|
SIG_O_CLK = 96,
|
|
SIG_O_CS = 97,
|
|
SIG_O_D0 = 98,
|
|
SIG_O_D1 = 99,
|
|
SIG_O_D2 = 100,
|
|
SIG_O_D3 = 101,
|
|
|
|
SIG_NONE = 0xFF
|
|
};
|
|
|
|
enum ndfc_controller_mode_e
|
|
{
|
|
MODE_PROGRAM = 0,
|
|
MODE_ERASE = 1,
|
|
MODE_RANDRW = 2,
|
|
MODE_REGRW = 3,
|
|
MODE_FLASHREAD = 4,
|
|
MODE_PAGEREAD = 5,
|
|
MODE_PAGEWRITE = 6,
|
|
};
|
|
|
|
enum ndfc_spi_read_write_e
|
|
{
|
|
SPI_REG_R,
|
|
SPI_REG_W,
|
|
SPI_PROG_R,
|
|
SPI_PROG_W,
|
|
SPI_DATA_R,
|
|
SPI_DATA_W,
|
|
SPI_CACHE_R,
|
|
SPI_CACHE_W,
|
|
SPI_PROG_EXE,
|
|
};
|
|
|
|
typedef struct ndfc_flash_info_t
|
|
{
|
|
uint8_t mf_id;
|
|
uint8_t dev_id;
|
|
uint8_t data_xn;
|
|
uint8_t resv[1];
|
|
uint32_t page_num; /* Page number per block. */
|
|
uint32_t block_num; /* Total block number. */
|
|
uint32_t page_size; /* Bytes in signal page. */
|
|
}ndfc_flash_t;
|
|
|
|
typedef struct
|
|
{
|
|
uint8_t mode; /* Operation Mode */
|
|
uint8_t cmd; /* Specifies the Instruction to be sent */
|
|
uint8_t spi_rw; /* SPI write or read. enum ndfc_spi_read_write_e */
|
|
uint8_t dummy_len; /* Dummy bits len. */
|
|
uint32_t dlen_byte; /* spi trans data length, unit is byte */
|
|
uint32_t addr; /* Specifies the Address to be sent */
|
|
uint32_t alen_bits; /* length of address. */
|
|
uint32_t erase_wait_time;
|
|
uint32_t read_wait_time;
|
|
uint32_t prog_wait_time; /* program or erase wait time,
|
|
unit is clock cycle num */
|
|
uint32_t status; /* Status of flash abouot current command. */
|
|
uint32_t reg_rw; /* Data of register write to/read from on flash. */
|
|
uint8_t *dma_buf; /* Buffer for page read/write. */
|
|
} ndfc_op_t;
|
|
|
|
typedef struct ndfc_context_t
|
|
{
|
|
ndfc_flash_t flash;
|
|
ndfc_gpio_t gpio;
|
|
ndfc_op_t op;
|
|
uint8_t *dma_buf;
|
|
}ndfc_t;
|
|
|
|
ndfc_t g_ndfc_context;
|
|
|
|
#define NDFC_DEF_WAIT_CYCLE 0x30
|
|
|
|
#define NDFC_DEF_CMD_LEN 8
|
|
|
|
#define cpu_nop() __asm volatile ("nop\n")
|
|
|
|
static void ndfc_set_reg_field
|
|
(uint32_t mask, uint32_t offset, uint32_t addr, uint32_t value)
|
|
{
|
|
uint32_t tmp;
|
|
tmp = NDFC_RF_READ_REG(addr);
|
|
tmp &= ~mask;
|
|
tmp |= (((value) << offset) & mask);
|
|
NDFC_RF_WRITE_REG(addr, tmp);
|
|
return;
|
|
}
|
|
|
|
#define ndfc_set_register_field(FILD, addr, value) \
|
|
ndfc_set_reg_field(FILD##_MASK, FILD##_OFFSET, addr, (uint32_t)value)
|
|
|
|
|
|
#define ndfc_set_page_size(size) \
|
|
ndfc_set_register_field(CFG_NDFC_PAGE_SIZE, CFG_NDFC_CTRL0_ADDR, size)
|
|
|
|
#define ndfc_start() \
|
|
ndfc_set_register_field(SW_NDFC_ENA, CFG_NDFC_CMD0_ADDR, 0x1)
|
|
|
|
#define ndfc_busy() \
|
|
((0 != (NDFC_RF_READ_REG(CFG_NDFC_CMD0_ADDR) & SW_NDFC_ENA_MASK)) ? 1 : 0)
|
|
|
|
#define ndfc_set_mode(mode) \
|
|
ndfc_set_register_field(SW_NDFC_MODE, CFG_NDFC_CMD0_ADDR, mode)
|
|
|
|
#define ndfc_set_dma_buf(buf) \
|
|
ndfc_set_register_field(SW_DMA_START_ADDR, CFG_NDFC_CMD2_ADDR, buf)
|
|
|
|
#define ndfc_set_buf_len(len) \
|
|
ndfc_set_register_field(SW_NDFC_DLEN, CFG_NDFC_CMD0_ADDR, len)
|
|
|
|
#define ndfc_set_command(cmd) \
|
|
ndfc_set_register_field(SW_NDFC_CMD, CFG_NDFC_CMD1_ADDR, cmd)
|
|
|
|
#define ndfc_set_command_len(len) \
|
|
ndfc_set_register_field(CFG_CMD_LEN, CFG_NDFC_SWM_CFG1_ADDR, len)
|
|
|
|
#define ndfc_set_addr(addr) \
|
|
ndfc_set_register_field(SW_NDFC_ADDR, CFG_NDFC_CMD3_ADDR, addr)
|
|
|
|
#define ndfc_set_addr_bits(len) \
|
|
ndfc_set_register_field(CFG_ADDR_LEN, CFG_NDFC_SWM_CFG1_ADDR, len)
|
|
|
|
#define ndfc_set_dummy_bits(len) \
|
|
ndfc_set_register_field(CFG_DUMMY_LEN, CFG_NDFC_SWM_CFG1_ADDR, len)
|
|
|
|
#define ndfc_set_read_wait_cycle(cycle) \
|
|
ndfc_set_register_field(CFG_PAGE_RD_WAIT_TIME, CFG_NDFC_WAIT_TIME_ADDR, cycle)
|
|
|
|
#define ndfc_set_prog_wait_cycle(cycle) \
|
|
ndfc_set_register_field(CFG_PROG_WAIT_TIME, CFG_NDFC_CFG1_ADDR, cycle)
|
|
|
|
#define ndfc_set_erase_wait_cycle(cycle) \
|
|
ndfc_set_register_field(CFG_ERASE_WAIT_TIME, CFG_NDFC_CFG2_ADDR, cycle)
|
|
|
|
#define ndfc_set_reg_value(value) \
|
|
ndfc_set_register_field(SW_NDFC_WDATA, CFG_NDFC_WDATA_ADDR, value)
|
|
|
|
#define ndfc_get_reg_value() \
|
|
REG_FIELD_GET(SW_NDFC_RDATA, NDFC_RF_READ_REG(CFG_NDFC_RDATA_ADDR))
|
|
|
|
#define ndfc_set_cmd_page_read(cmd) \
|
|
ndfc_set_register_field(CFG_PAGE_RD_CMD, CFG_NDFC_CMD4_ADDR, cmd)
|
|
|
|
#define ndfc_set_cmd_program(cmd) \
|
|
ndfc_set_register_field(CFG_PROG_EXE_CMD, CFG_NDFC_CMD4_ADDR, cmd)
|
|
|
|
#define ndfc_set_cmd_oip(cmd) \
|
|
ndfc_set_register_field(CFG_OIP_CMD, CFG_NDFC_SWM_CFG2_ADDR, cmd)
|
|
|
|
#define ndfc_set_cmd_write_ena(cmd) \
|
|
ndfc_set_register_field(CFG_WR_EN_CMD, CFG_NDFC_SWM_CFG2_ADDR, cmd)
|
|
|
|
#define ndfc_set_cmd_write_dis(cmd) \
|
|
ndfc_set_register_field(CFG_WR_DIS_CMD, CFG_NDFC_SWM_CFG2_ADDR, cmd)
|
|
|
|
#define ndfc_flash_get_col_addr(offset) \
|
|
((offset) % (g_ndfc_context.flash.page_size))
|
|
|
|
#define ndfc_flash_get_page_addr(offset) \
|
|
((offset) / (g_ndfc_context.flash.page_size) \
|
|
% (g_ndfc_context.flash.page_num))
|
|
|
|
#define ndfc_flash_get_block_addr(offset) \
|
|
(((offset) \
|
|
/ (g_ndfc_context.flash.page_size * g_ndfc_context.flash.page_num)))
|
|
|
|
static ndfc_op_t *ndfc_alloc_op(void)
|
|
{
|
|
ndfc_op_t *p_op = &(g_ndfc_context.op);
|
|
|
|
os_mem_set(p_op, 0x0, sizeof(*p_op));
|
|
|
|
p_op->dma_buf = g_ndfc_context.dma_buf;
|
|
|
|
p_op->spi_rw = SPI_REG_W;
|
|
|
|
return p_op;
|
|
}
|
|
|
|
#define ndfc_free_op(op) /* Do nothing. */
|
|
|
|
static uint32_t ndfc_flash_get_status(uint32_t *p_status);
|
|
|
|
static void ndfc_set_data_mode(uint32_t mode)
|
|
{
|
|
uint32_t value, bits;
|
|
|
|
if(DATA_MODE_X4 == mode) {
|
|
bits = DATA_RD_DATA_QUAD_MODE_MASK;
|
|
} else if (DATA_MODE_X2 == mode) {
|
|
bits = DATA_RD_DATA_DUAL_MODE_MASK;
|
|
} else {
|
|
/* Default DATA_MODE_X1 */
|
|
bits = 0;
|
|
}
|
|
|
|
value = NDFC_RF_READ_REG(CFG_NDFC_DATA_RD_CFG0_ADDR);
|
|
value &= ~(DATA_RD_DATA_QUAD_MODE_MASK | DATA_RD_DATA_DUAL_MODE_MASK);
|
|
value |= bits;
|
|
NDFC_RF_WRITE_REG(CFG_NDFC_DATA_RD_CFG0_ADDR, value);
|
|
|
|
value = NDFC_RF_READ_REG(CFG_NDFC_PROG_LOAD_CFG0_ADDR);
|
|
value &= ~(DATA_RD_DATA_QUAD_MODE_MASK | DATA_RD_DATA_DUAL_MODE_MASK);
|
|
value |= bits;
|
|
NDFC_RF_WRITE_REG(CFG_NDFC_PROG_LOAD_CFG0_ADDR, value);
|
|
|
|
return;
|
|
}
|
|
|
|
static void ndfc_set_spi_wr_mode(uint32_t mode)
|
|
{
|
|
uint32_t addr = 0xFFFFFFFF, mask, value, tmp;
|
|
|
|
if(SPI_REG_R == mode) {
|
|
addr = CFG_NDFC_SWM_CFG0_ADDR;
|
|
mask = CFG_SPI_RD_MASK | CFG_SPI_WR_MASK;
|
|
value = CFG_SPI_RD_MASK;
|
|
} else if (SPI_REG_W == mode) {
|
|
addr = CFG_NDFC_SWM_CFG0_ADDR;
|
|
mask = CFG_SPI_RD_MASK | CFG_SPI_WR_MASK;
|
|
value = CFG_SPI_WR_MASK;
|
|
} else if (SPI_PROG_R == mode) {
|
|
addr = CFG_NDFC_PROG_LOAD_CFG0_ADDR;
|
|
mask = PROG_LOAD_SPI_WR_MASK | PROG_LOAD_SPI_RD_MASK;
|
|
value = PROG_LOAD_SPI_RD_MASK;
|
|
} else if (SPI_PROG_W == mode) {
|
|
addr = CFG_NDFC_PROG_LOAD_CFG0_ADDR;
|
|
mask = PROG_LOAD_SPI_WR_MASK | PROG_LOAD_SPI_RD_MASK;
|
|
value = PROG_LOAD_SPI_WR_MASK;
|
|
} else if (SPI_DATA_R == mode) {
|
|
addr = CFG_NDFC_DATA_RD_CFG0_ADDR;
|
|
mask = DATA_RD_SPI_WR_MASK | DATA_RD_SPI_RD_MASK;
|
|
value = DATA_RD_SPI_RD_MASK;
|
|
} else if (SPI_DATA_W == mode) {
|
|
addr = CFG_NDFC_DATA_RD_CFG0_ADDR;
|
|
mask = DATA_RD_SPI_WR_MASK | DATA_RD_SPI_RD_MASK;
|
|
value = DATA_RD_SPI_WR_MASK;
|
|
} else if (SPI_CACHE_R == mode) {
|
|
addr = CFG_NDFC_CACHE_CFG0_ADDR;
|
|
mask = CACHE_SPI_WR_MASK | CACHE_SPI_RD_MASK;
|
|
value = CACHE_SPI_RD_MASK;
|
|
} else if (SPI_CACHE_W == mode) {
|
|
addr = CFG_NDFC_CACHE_CFG0_ADDR;
|
|
mask = CACHE_SPI_WR_MASK | CACHE_SPI_RD_MASK;
|
|
value = CACHE_SPI_RD_MASK;
|
|
} else if (SPI_PROG_EXE == mode) {
|
|
addr = CFG_NDFC_PAGE_EXE_CFG0_ADDR;
|
|
mask = PROG_EXE_SPI_WR_MASK | PROG_EXE_SPI_RD_MASK;
|
|
value = PROG_EXE_SPI_WR_MASK;
|
|
}
|
|
/* else() do nothing. */
|
|
|
|
/* The default value of addr means no mode matched. */
|
|
if(0xFFFFFFFF != addr) {
|
|
tmp = NDFC_RF_READ_REG(addr);
|
|
tmp &= ~mask;
|
|
tmp |= value;
|
|
NDFC_RF_WRITE_REG(addr, tmp);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void ndfc_gpio_config(ndfc_gpio_t *p_gpio)
|
|
{
|
|
gpio_sig_info_t info;
|
|
|
|
info.sig_type = 6;
|
|
|
|
info.CFG[0].func = gpio_pin_func_get(p_gpio->clk);
|
|
info.CFG[0].gpio = p_gpio->clk;
|
|
info.CFG[0].type = IO_TYPE_OUT;
|
|
info.CFG[0].outid = SIG_O_CLK;
|
|
info.CFG[0].inid = SIG_NONE;
|
|
|
|
info.CFG[1].func = gpio_pin_func_get(p_gpio->cs);
|
|
info.CFG[1].gpio = p_gpio->cs;
|
|
info.CFG[1].type = IO_TYPE_OUT;
|
|
info.CFG[1].outid = SIG_O_CS;
|
|
info.CFG[1].inid = SIG_NONE;
|
|
|
|
info.CFG[2].func = gpio_pin_func_get(p_gpio->d0);
|
|
info.CFG[2].gpio = p_gpio->d0;
|
|
info.CFG[2].type = IO_TYPE_IO;
|
|
info.CFG[2].outid = SIG_O_D0;
|
|
info.CFG[2].inid = SIG_I_D0;
|
|
|
|
info.CFG[3].func = gpio_pin_func_get(p_gpio->d1);
|
|
info.CFG[3].gpio = p_gpio->d1;
|
|
info.CFG[3].type = IO_TYPE_IO;
|
|
info.CFG[3].outid = SIG_O_D1;
|
|
info.CFG[3].inid = SIG_I_D1;
|
|
|
|
info.CFG[4].func = gpio_pin_func_get(p_gpio->d2);
|
|
info.CFG[4].gpio = p_gpio->d2;
|
|
info.CFG[4].type = IO_TYPE_IO;
|
|
info.CFG[4].outid = SIG_O_D2;
|
|
info.CFG[4].inid = SIG_I_D2;
|
|
|
|
info.CFG[5].func = gpio_pin_func_get(p_gpio->d3);
|
|
info.CFG[5].gpio = p_gpio->d3;
|
|
info.CFG[5].type = IO_TYPE_IO;
|
|
info.CFG[5].outid = SIG_O_D3;
|
|
info.CFG[5].inid = SIG_I_D3;
|
|
|
|
gpio_module_pin_select(&info);
|
|
|
|
gpio_module_sig_select(&info, GPIO_MTX_MODE_MATRIX);
|
|
|
|
return;
|
|
}
|
|
|
|
static void ndfc_clk_freq_80m(void)
|
|
{
|
|
// to do: modify ndfc clk
|
|
return;
|
|
uint32_t data;
|
|
data = AP_CLK_CORE_RF_READ_REG(CFG_CLK_OD_SEL_ADDR);
|
|
REG_FIELD_SET(CLK_NAND_DIV, data, 4);
|
|
AP_CLK_CORE_RF_WRITE_REG(CFG_CLK_OD_SEL_ADDR, data);
|
|
}
|
|
|
|
void ndfc_set_dmc_pad(void)
|
|
{
|
|
uint32_t data;
|
|
|
|
data = DMC_RF_READ_REG(CFG_DMC_PAD_SEL1_ADDR);
|
|
REG_FIELD_SET(DUMMY1_PAD_SEL, data, 2);
|
|
REG_FIELD_SET(DUMMY0_PAD_SEL, data, 2);
|
|
DMC_RF_WRITE_REG(CFG_DMC_PAD_SEL1_ADDR, data);
|
|
|
|
return;
|
|
}
|
|
|
|
#define NDFC_CONTROLLER_WAIT_MAX 750000000
|
|
static uint32_t ndfc_skip_controller_busy(uint32_t wait)
|
|
{
|
|
uint32_t controller_wait = wait > 0 ? wait : NDFC_CONTROLLER_WAIT_MAX;
|
|
|
|
while(ndfc_busy() && ((--controller_wait) > 0)) cpu_nop();
|
|
|
|
return (controller_wait > 0) ? ERR_OK : ERR_FAIL;
|
|
}
|
|
|
|
#define NDFC_FLACH_WAIT_MAX 2000
|
|
static uint32_t ndfc_skip_flash_busy(void)
|
|
{
|
|
uint32_t flash_wait = NDFC_FLACH_WAIT_MAX, ret, status, busy = 1;;
|
|
ndfc_flash_t *p_flash = &(g_ndfc_context.flash);
|
|
|
|
if(DATA_MODE_X4 == p_flash->data_xn) {
|
|
flash_wait <<= 3;
|
|
} else if (DATA_MODE_X2 == p_flash->data_xn) {
|
|
flash_wait <<= 2;
|
|
}
|
|
|
|
while(flash_wait > 0) {
|
|
ret = ndfc_flash_get_status(&status);
|
|
|
|
if(ERR_OK == ret) {
|
|
if(0 == (status & 0x1)) {
|
|
/* BUSY */
|
|
busy = 0;
|
|
break;
|
|
}
|
|
flash_wait -= 1;
|
|
} else {
|
|
flash_wait -= (flash_wait > 100) ? 100 : flash_wait;
|
|
}
|
|
}
|
|
|
|
if(status & 0x0C) {
|
|
/* PROG / ERASE failed. */
|
|
iot_printf("\nFLASH STATUS ERR#0x%02X!", status);
|
|
busy = 1;
|
|
} else if (busy) {
|
|
iot_printf("\nFLASH BUSY!");
|
|
}
|
|
|
|
return busy ? ERR_FAIL : ERR_OK;
|
|
}
|
|
|
|
static uint32_t ndfc_flash_operate(ndfc_op_t *p_op)
|
|
{
|
|
uint32_t tmp;
|
|
ndfc_flash_t *p_flash = &(g_ndfc_context.flash);
|
|
|
|
if(0 == p_op->erase_wait_time) {
|
|
p_op->erase_wait_time = p_flash->page_size * p_flash->page_num * 8;
|
|
}
|
|
|
|
if(0 == p_op->prog_wait_time) {
|
|
p_op->prog_wait_time = ((p_op->dlen_byte * 8) < NDFC_DEF_WAIT_CYCLE)
|
|
? NDFC_DEF_WAIT_CYCLE : (p_op->dlen_byte * 8);
|
|
}
|
|
|
|
if(0 == p_op->read_wait_time) {
|
|
p_op->read_wait_time = ((p_op->dlen_byte * 8) < NDFC_DEF_WAIT_CYCLE)
|
|
? NDFC_DEF_WAIT_CYCLE : (p_op->dlen_byte * 8);
|
|
}
|
|
|
|
ndfc_set_spi_wr_mode(p_op->spi_rw);
|
|
|
|
ndfc_set_dma_buf(p_op->dma_buf);
|
|
ndfc_set_buf_len(p_op->dlen_byte);
|
|
ndfc_set_mode(p_op->mode);
|
|
ndfc_set_command_len(NDFC_DEF_CMD_LEN);
|
|
ndfc_set_addr(p_op->addr);
|
|
ndfc_set_addr_bits(p_op->alen_bits);
|
|
ndfc_set_dummy_bits(p_op->dummy_len);
|
|
ndfc_set_prog_wait_cycle(p_op->prog_wait_time);
|
|
ndfc_set_erase_wait_cycle(p_op->erase_wait_time);
|
|
ndfc_set_read_wait_cycle(p_op->read_wait_time);
|
|
ndfc_set_reg_value(p_op->reg_rw);
|
|
|
|
ndfc_set_command(p_op->cmd);
|
|
|
|
ndfc_start();
|
|
|
|
/* Wait ndfc finish operation. */
|
|
tmp = ndfc_skip_controller_busy(0);
|
|
|
|
p_op->reg_rw = ndfc_get_reg_value();
|
|
|
|
if(ERR_OK != tmp) {
|
|
iot_printf("\n!!! NDFC TIME OUT !!!");
|
|
}
|
|
|
|
return tmp;
|
|
}
|
|
|
|
uint32_t ndfc_flash_set_write_ability(uint32_t enable)
|
|
{
|
|
uint32_t ret;
|
|
ndfc_op_t *p_op = ndfc_alloc_op();
|
|
|
|
p_op->cmd = enable ? WRITE_EN_CMD : WRITE_DIS_CMD;
|
|
p_op->mode = MODE_REGRW;
|
|
p_op->spi_rw = SPI_REG_W;
|
|
|
|
ret = ndfc_flash_operate(p_op);
|
|
|
|
ndfc_free_op(p_op);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static uint32_t ndfc_flash_get_feature(uint32_t ft_addr, uint32_t *p_feature)
|
|
{
|
|
uint32_t ret;
|
|
ndfc_op_t *p_op = ndfc_alloc_op();
|
|
|
|
p_op->cmd = GET_FEATURE_CMD;
|
|
p_op->spi_rw = SPI_REG_R;
|
|
p_op->dlen_byte = 1;
|
|
p_op->alen_bits = 8;
|
|
p_op->addr = (ft_addr << 16);
|
|
p_op->mode = MODE_REGRW;
|
|
|
|
ret = ndfc_flash_operate(p_op);
|
|
|
|
ndfc_free_op(p_op);
|
|
|
|
*p_feature = p_op->reg_rw;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static uint32_t ndfc_flash_set_feature(uint32_t ft_addr, uint32_t ft_val)
|
|
{
|
|
uint32_t ret;
|
|
ndfc_op_t *p_op = ndfc_alloc_op();
|
|
|
|
p_op->cmd = SET_FEATURE_CMD;
|
|
p_op->spi_rw = SPI_REG_W;
|
|
p_op->dlen_byte = 1;
|
|
p_op->reg_rw = ft_val;
|
|
p_op->alen_bits = 8;
|
|
p_op->addr = (ft_addr << 16);
|
|
p_op->mode = MODE_REGRW;
|
|
|
|
ret = ndfc_flash_operate(p_op);
|
|
|
|
ndfc_free_op(p_op);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void ndfc_flash_reset(void)
|
|
{
|
|
ndfc_op_t *p_op = ndfc_alloc_op();
|
|
|
|
p_op->cmd = RESET_CMD;
|
|
p_op->spi_rw = SPI_REG_W;
|
|
p_op->mode = MODE_REGRW;
|
|
|
|
ndfc_flash_operate(p_op);
|
|
|
|
ndfc_free_op(p_op);
|
|
|
|
ndfc_skip_flash_busy();
|
|
|
|
return;
|
|
}
|
|
|
|
static void ndfc_flash_set_qe_mode(uint32_t enable)
|
|
{
|
|
uint32_t data;
|
|
|
|
ndfc_flash_get_feature(OTP_ADDR, &data);
|
|
|
|
if(enable) {
|
|
data |= 0x1;
|
|
} else {
|
|
data &= ~0x1;
|
|
}
|
|
|
|
data &= 0xFF;
|
|
|
|
ndfc_flash_set_feature(OTP_ADDR, data);
|
|
|
|
return;
|
|
}
|
|
|
|
static uint32_t ndfc_flash_get_status(uint32_t *p_status)
|
|
{
|
|
uint32_t ret;
|
|
|
|
ret = ndfc_flash_get_feature(STATUS_ADDR, p_status);
|
|
*p_status &= 0xFF;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static uint32_t ndfc_flash_unlock_all(void)
|
|
{
|
|
return ndfc_flash_set_feature(BLOCK_LOCK_ADDR, 0x0);
|
|
}
|
|
|
|
static void ndfc_flash_page_read_to_cache(uint32_t offset)
|
|
{
|
|
ndfc_op_t *p_op;
|
|
uint32_t addr = 0;
|
|
|
|
p_op = ndfc_alloc_op();
|
|
|
|
addr = ndfc_flash_get_page_addr(offset) & 0x3F;
|
|
addr |= ((ndfc_flash_get_block_addr(offset) & 0x3FF) << 6);
|
|
|
|
p_op->cmd = PAGE_READ_CMD;
|
|
p_op->addr = addr;
|
|
p_op->dlen_byte = 0;
|
|
/* 24bits addr: 8bits dummy + 10bit block addr + 6bit page addr */
|
|
p_op->alen_bits = 24;
|
|
p_op->spi_rw = SPI_DATA_R;
|
|
p_op->mode = MODE_REGRW;
|
|
|
|
ndfc_flash_operate(p_op);
|
|
|
|
ndfc_free_op(p_op);
|
|
}
|
|
|
|
static void ndfc_flash_read_random_data(uint32_t offset, uint32_t force_xn)
|
|
{
|
|
uint32_t cmd_xn, qe_ena, xn;
|
|
ndfc_flash_t *p_flash = &(g_ndfc_context.flash);
|
|
ndfc_op_t *p_op;
|
|
uint32_t addr = 0;
|
|
|
|
if(DATA_MODE_INVALID != force_xn) {
|
|
xn = force_xn;
|
|
} else {
|
|
xn = p_flash->data_xn;
|
|
}
|
|
|
|
if(DATA_MODE_X4 == xn) {
|
|
qe_ena = 1;
|
|
cmd_xn = READ_FROM_CACHE_4_CMD;
|
|
} else if (DATA_MODE_X2 == xn) {
|
|
qe_ena = 0;
|
|
cmd_xn = READ_FROM_CACHE_2_CMD;
|
|
} else {
|
|
qe_ena = 0;
|
|
xn = DATA_MODE_X1;
|
|
cmd_xn = READ_FROM_CACHE_CMD;
|
|
}
|
|
|
|
ndfc_flash_set_qe_mode(qe_ena);
|
|
|
|
ndfc_set_data_mode(xn);
|
|
|
|
p_op = ndfc_alloc_op();
|
|
|
|
/* reg addr: 10bits block addr + 6bits page addr + 12bits column addr */
|
|
addr = ndfc_flash_get_col_addr(offset);
|
|
addr |= (ndfc_flash_get_page_addr(offset) << 12);
|
|
addr |= (ndfc_flash_get_block_addr(offset) << 18);
|
|
|
|
p_op->cmd = cmd_xn;
|
|
p_op->addr = addr;
|
|
p_op->dlen_byte = p_flash->page_size;
|
|
/* 32bits addr: 4bits dummy + 12bits column addr + 16bits dummy */
|
|
p_op->alen_bits = 32;
|
|
p_op->spi_rw = SPI_DATA_R;
|
|
p_op->mode = MODE_RANDRW;
|
|
|
|
os_mem_set(p_op->dma_buf, 0x0, p_op->dlen_byte);
|
|
|
|
ndfc_flash_operate(p_op);
|
|
|
|
ndfc_free_op(p_op);
|
|
}
|
|
|
|
|
|
static void ndfc_flash_prog_load(uint32_t offset, uint32_t force_xn)
|
|
{
|
|
uint32_t cmd_xn, qe_ena, xn;
|
|
ndfc_flash_t *p_flash = &(g_ndfc_context.flash);
|
|
ndfc_op_t *p_op;
|
|
uint32_t addr = 0;
|
|
|
|
if(DATA_MODE_INVALID != force_xn) {
|
|
xn = force_xn;
|
|
} else {
|
|
xn = p_flash->data_xn;
|
|
}
|
|
|
|
if(DATA_MODE_X4 == xn) {
|
|
qe_ena = 1;
|
|
cmd_xn = PROGRAM_LOAD_4_CMD;
|
|
} else {
|
|
xn = DATA_MODE_X1;
|
|
qe_ena = 0;
|
|
cmd_xn = PROGRAM_LOAD_CMD;
|
|
}
|
|
|
|
ndfc_set_data_mode(xn);
|
|
|
|
ndfc_flash_set_qe_mode(qe_ena);
|
|
|
|
p_op = ndfc_alloc_op();
|
|
|
|
/* reg addr: 10bits block addr + 6bits page addr + 12bits column addr */
|
|
addr = ndfc_flash_get_col_addr(offset);
|
|
addr |= (ndfc_flash_get_page_addr(offset) << 12);
|
|
addr |= (ndfc_flash_get_block_addr(offset) << 18);
|
|
|
|
p_op->cmd = cmd_xn;
|
|
p_op->addr = addr;
|
|
p_op->dlen_byte = p_flash->page_size;
|
|
/* 16bits addr: 3bits dummy + 1bit plane + 12bits address */
|
|
p_op->alen_bits = 16;
|
|
p_op->spi_rw = SPI_PROG_W;
|
|
p_op->mode = MODE_PROGRAM;
|
|
|
|
ndfc_flash_operate(p_op);
|
|
|
|
ndfc_free_op(p_op);
|
|
}
|
|
|
|
static void ndfc_flash_prog_exe(uint32_t offset)
|
|
{
|
|
ndfc_op_t *p_op;
|
|
uint32_t addr = 0;
|
|
|
|
p_op = ndfc_alloc_op();
|
|
|
|
/* reg addr: 10bits block addr + 6bits page addr + 12bits column addr */
|
|
addr = ndfc_flash_get_col_addr(offset);
|
|
addr |= (ndfc_flash_get_page_addr(offset) << 12);
|
|
addr |= (ndfc_flash_get_block_addr(offset) << 18);
|
|
|
|
p_op->cmd = PROGRAM_EXECUTE_CMD;
|
|
p_op->addr = addr;
|
|
p_op->dlen_byte = 0;
|
|
/* 16bits addr: 8bits dummy + 10bits block address + 6bits page address */
|
|
p_op->alen_bits = 24;
|
|
p_op->spi_rw = SPI_PROG_EXE;
|
|
p_op->mode = MODE_PAGEWRITE;
|
|
|
|
ndfc_flash_operate(p_op);
|
|
|
|
ndfc_free_op(p_op);
|
|
}
|
|
|
|
static void ndfc_flash_read_page(uint32_t offset, uint32_t force_xn)
|
|
{
|
|
// page read to cache
|
|
ndfc_flash_page_read_to_cache(offset);
|
|
|
|
// get feature command to read the status
|
|
ndfc_skip_flash_busy();
|
|
|
|
// random data read from (0 - page_size), here read whole page
|
|
ndfc_flash_read_random_data(offset, force_xn);
|
|
|
|
return;
|
|
}
|
|
|
|
static void ndfc_flash_write_page(uint32_t offset, uint32_t force_xn)
|
|
{
|
|
// write enable
|
|
ndfc_flash_set_write_ability(1);
|
|
|
|
// program load
|
|
ndfc_flash_prog_load(offset, force_xn);
|
|
|
|
// program execute
|
|
ndfc_flash_prog_exe(offset);
|
|
|
|
// get feature command to read the status
|
|
ndfc_skip_flash_busy();
|
|
|
|
return;
|
|
}
|
|
|
|
static void ndfc_flash_erase_block(uint32_t offset)
|
|
{
|
|
//uint32_t block;
|
|
uint32_t addr = 0;
|
|
ndfc_op_t *p_op = ndfc_alloc_op();
|
|
//ndfc_flash_t *p_flash = &(g_ndfc_context.flash);
|
|
|
|
addr = (ndfc_flash_get_block_addr(offset) << 18);
|
|
|
|
p_op->cmd = BLOCK_ERASE_CMD;
|
|
p_op->addr = addr;
|
|
p_op->alen_bits = 24; /* 8BITS dummy + 16BITS address. */
|
|
p_op->mode = MODE_ERASE;
|
|
|
|
ndfc_flash_operate(p_op);
|
|
|
|
ndfc_skip_flash_busy();
|
|
|
|
ndfc_free_op(p_op);
|
|
|
|
return;
|
|
}
|
|
|
|
static uint32_t ndfc_flash_get_id(void)
|
|
{
|
|
uint32_t id;
|
|
ndfc_op_t *p_op = ndfc_alloc_op();
|
|
|
|
/* Get Flash ID. */
|
|
p_op->cmd = READ_ID_CMD;
|
|
p_op->spi_rw = SPI_REG_R;
|
|
p_op->dlen_byte = 2;
|
|
p_op->dummy_len = 8;
|
|
p_op->mode = MODE_REGRW;
|
|
|
|
ndfc_flash_operate(p_op);
|
|
|
|
id = p_op->reg_rw;
|
|
|
|
ndfc_free_op(p_op);
|
|
|
|
return id;
|
|
}
|
|
|
|
uint32_t iot_ndfc_flash_get_param_page(void *p_data, uint32_t lenth)
|
|
{
|
|
uint32_t r_len;
|
|
ndfc_flash_t *p_flash = &(g_ndfc_context.flash);
|
|
|
|
ndfc_flash_set_feature(OTP_ADDR, 0x40);
|
|
|
|
ndfc_flash_read_page(PRAMTR_PAGE_ADDR * p_flash->page_size, DATA_MODE_X1);
|
|
|
|
r_len = g_ndfc_context.flash.page_size > lenth ? lenth
|
|
: g_ndfc_context.flash.page_size;
|
|
|
|
if((NULL != p_data) && (r_len > 0)) {
|
|
os_mem_cpy(p_data, g_ndfc_context.dma_buf, lenth);
|
|
}
|
|
|
|
ndfc_flash_set_feature(OTP_ADDR, 0x10);
|
|
|
|
return 0;
|
|
}
|
|
|
|
uint32_t iot_ndfc_flash_get_id(void)
|
|
{
|
|
uint32_t id;
|
|
ndfc_flash_t *p_flash = &(g_ndfc_context.flash);
|
|
|
|
id = (p_flash->mf_id << 8) | p_flash->dev_id;
|
|
|
|
return id;
|
|
}
|
|
|
|
uint32_t iot_ndfc_flash_get_flash_size(void)
|
|
{
|
|
uint32_t size;
|
|
ndfc_flash_t *p_flash = &(g_ndfc_context.flash);
|
|
|
|
size = p_flash->page_size * p_flash->page_num * p_flash->block_num;
|
|
|
|
return size;
|
|
}
|
|
|
|
uint32_t iot_ndfc_flash_get_page_size(void)
|
|
{
|
|
ndfc_flash_t *p_flash = &(g_ndfc_context.flash);
|
|
|
|
return p_flash->page_size;
|
|
}
|
|
|
|
/* Page number per block. */
|
|
uint32_t iot_ndfc_flash_get_page_num(void)
|
|
{
|
|
ndfc_flash_t *p_flash = &(g_ndfc_context.flash);
|
|
|
|
return p_flash->page_num;
|
|
}
|
|
|
|
uint32_t iot_ndfc_flash_get_block_size(void)
|
|
{
|
|
ndfc_flash_t *p_flash = &(g_ndfc_context.flash);
|
|
|
|
return (p_flash->page_size * p_flash->page_num);
|
|
}
|
|
|
|
uint32_t iot_ndfc_flash_get_block_num(void)
|
|
{
|
|
ndfc_flash_t *p_flash = &(g_ndfc_context.flash);
|
|
|
|
return p_flash->block_num;
|
|
}
|
|
|
|
uint32_t iot_ndfc_flash_read(uint8_t *p_buf, uint32_t offset, uint32_t size)
|
|
{
|
|
uint32_t cur_len, tot_len;
|
|
ndfc_flash_t *p_flash = &(g_ndfc_context.flash);
|
|
uint8_t *p_src = g_ndfc_context.dma_buf;
|
|
|
|
tot_len = 0;
|
|
|
|
while(size > 0) {
|
|
ndfc_flash_read_page(offset, DATA_MODE_INVALID);
|
|
|
|
cur_len = p_flash->page_size - (offset % p_flash->page_size);
|
|
|
|
cur_len = (cur_len > size) ? size : cur_len;
|
|
|
|
os_mem_cpy(p_buf+tot_len, p_src+(offset%p_flash->page_size), cur_len);
|
|
|
|
tot_len += cur_len;
|
|
|
|
offset += cur_len;
|
|
|
|
size -= cur_len;
|
|
}
|
|
|
|
return ERR_OK;
|
|
}
|
|
|
|
uint32_t iot_ndfc_flash_write(uint8_t *p_buf, uint32_t offset, uint32_t size)
|
|
{
|
|
uint32_t cur_len, tot_len;
|
|
ndfc_flash_t *p_flash = &(g_ndfc_context.flash);
|
|
uint8_t *p_dst = g_ndfc_context.dma_buf;
|
|
|
|
if(NULL == p_buf) {
|
|
return ERR_FAIL;
|
|
}
|
|
|
|
if(0 == size) {
|
|
return ERR_OK;
|
|
}
|
|
|
|
tot_len = 0;
|
|
|
|
if(offset % p_flash->page_size) {
|
|
//page = ndfc_flash_get_page_addr(offset);
|
|
|
|
ndfc_flash_read_page(offset, DATA_MODE_INVALID);
|
|
|
|
cur_len = p_flash->page_size - (offset % p_flash->page_size);
|
|
|
|
cur_len = (cur_len > size) ? size : cur_len;
|
|
|
|
os_mem_cpy(p_dst + (offset % p_flash->page_size), p_buf, cur_len);
|
|
|
|
//ndfc_flash_set_write_ability(1);
|
|
|
|
ndfc_flash_write_page(offset, DATA_MODE_INVALID);
|
|
|
|
tot_len += cur_len;
|
|
|
|
offset += cur_len;
|
|
|
|
size -= cur_len;
|
|
} else {
|
|
//ndfc_flash_set_write_ability(1);
|
|
}
|
|
|
|
while(size > p_flash->page_size) {
|
|
cur_len = p_flash->page_size;
|
|
|
|
os_mem_cpy(p_dst, p_buf + tot_len, cur_len);
|
|
|
|
ndfc_flash_write_page(offset, DATA_MODE_INVALID);
|
|
|
|
tot_len += cur_len;
|
|
|
|
offset += cur_len;
|
|
|
|
size -= cur_len;
|
|
}
|
|
|
|
if(size > 0) {
|
|
ndfc_flash_read_page(offset, DATA_MODE_INVALID);
|
|
|
|
cur_len = size;
|
|
|
|
os_mem_cpy(p_dst, p_buf + tot_len, cur_len);
|
|
|
|
//ndfc_flash_set_write_ability(1);
|
|
|
|
ndfc_flash_write_page(offset, DATA_MODE_INVALID);
|
|
|
|
tot_len += cur_len;
|
|
|
|
offset += cur_len;
|
|
|
|
size -= cur_len;
|
|
}
|
|
|
|
//ndfc_flash_set_write_ability(0);
|
|
|
|
return ERR_OK;
|
|
}
|
|
|
|
uint32_t iot_ndfc_flash_erase_block(uint32_t offset)
|
|
{
|
|
ndfc_flash_erase_block(offset);
|
|
|
|
return ERR_OK;
|
|
}
|
|
|
|
uint32_t iot_ndfc_get_int_status(void)
|
|
{
|
|
uint32_t status;
|
|
#ifdef CFG_NDFC_INTERRUPT_ADDR
|
|
status = NDFC_RF_READ_REG(CFG_NDFC_INTERRUPT_ADDR);
|
|
#else
|
|
status = NDFC_RF_READ_REG(0x120);
|
|
#endif
|
|
return status;
|
|
}
|
|
|
|
uint32_t iot_ndfc_init(ndfc_cfg_t *p_cfg)
|
|
{
|
|
uint32_t data;
|
|
|
|
os_mem_cpy(&(g_ndfc_context.gpio), &(p_cfg->gpio),
|
|
sizeof(g_ndfc_context.gpio));
|
|
|
|
ndfc_gpio_config(&(p_cfg->gpio));
|
|
|
|
ndfc_clk_freq_80m();
|
|
|
|
ndfc_set_cmd_oip(GET_FEATURE_CMD);
|
|
|
|
//ndfc_set_dmc_pad();
|
|
|
|
/* TODO read from flash. */
|
|
//g_ndfc_context.flash.page_size = DATA_FIELD_LEN + SPARE_FIELD_LEN;
|
|
g_ndfc_context.flash.page_size = DATA_FIELD_LEN;
|
|
g_ndfc_context.flash.page_num = PAGE_PER_BLOCK;
|
|
g_ndfc_context.flash.block_num = BLOCK_PER_CHIP;
|
|
g_ndfc_context.flash.data_xn = p_cfg->data_mode;
|
|
|
|
g_ndfc_context.dma_buf =
|
|
(uint8_t *)os_mem_malloc(0, g_ndfc_context.flash.page_size);
|
|
|
|
if(NULL == g_ndfc_context.dma_buf) {
|
|
return ERR_FAIL;
|
|
}
|
|
|
|
ndfc_set_page_size(g_ndfc_context.flash.page_size);
|
|
|
|
ndfc_flash_reset();
|
|
|
|
ndfc_flash_unlock_all();
|
|
|
|
data = ndfc_flash_get_id();
|
|
|
|
g_ndfc_context.flash.dev_id = (data & 0xFF00) >> 8;
|
|
g_ndfc_context.flash.mf_id = data & 0xFF;
|
|
|
|
/* Set data array ready. */
|
|
ndfc_flash_set_feature(OTP_ADDR, 0x10);
|
|
|
|
return ERR_OK;
|
|
}
|
|
#else
|
|
uint32_t iot_ndfc_init(ndfc_cfg_t *p_cfg)
|
|
{
|
|
return ERR_FAIL;
|
|
}
|
|
|
|
uint32_t iot_ndfc_get_int_status(void)
|
|
{
|
|
return ERR_FAIL;
|
|
}
|
|
|
|
uint32_t iot_ndfc_flash_erase_block(uint32_t offset)
|
|
{
|
|
return ERR_FAIL;
|
|
}
|
|
|
|
uint32_t iot_ndfc_flash_write(uint8_t *p_buf, uint32_t offset, uint32_t size)
|
|
{
|
|
return ERR_FAIL;
|
|
}
|
|
|
|
uint32_t iot_ndfc_flash_read(uint8_t *p_buf, uint32_t offset, uint32_t size)
|
|
{
|
|
return ERR_FAIL;
|
|
}
|
|
|
|
uint32_t iot_ndfc_flash_get_id(void)
|
|
{
|
|
return ERR_FAIL;
|
|
}
|
|
|
|
uint32_t iot_ndfc_flash_get_flash_size(void)
|
|
{
|
|
return ERR_FAIL;
|
|
}
|
|
|
|
uint32_t iot_ndfc_flash_get_page_size(void)
|
|
{
|
|
return ERR_FAIL;
|
|
}
|
|
|
|
uint32_t iot_ndfc_flash_get_page_num(void)
|
|
{
|
|
return ERR_FAIL;
|
|
}
|
|
|
|
uint32_t iot_ndfc_flash_get_block_size(void)
|
|
{
|
|
return ERR_FAIL;
|
|
}
|
|
|
|
uint32_t iot_ndfc_flash_get_block_num(void)
|
|
{
|
|
return ERR_FAIL;
|
|
}
|
|
|
|
uint32_t iot_ndfc_flash_get_param_page(void *p_data, uint32_t lenth)
|
|
{
|
|
return ERR_FAIL;
|
|
}
|
|
|
|
#endif
|