KT24-1110_65E-HA-651B/apps/common/device/nandflash/nandflash.c
2024-11-10 18:44:17 +08:00

684 lines
18 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

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.

#include "nandflash.h"
#include "timer.h"
#include "app_config.h"
#include "asm/clock.h"
#if TCFG_NANDFLASH_DEV_ENABLE
#undef LOG_TAG_CONST
#define LOG_TAG "[FLASH]"
#define LOG_ERROR_ENABLE
#define LOG_INFO_ENABLE
#include "debug.h"
#define NAND_FLASH_TIMEOUT 500
#define NAND_PAGE_SIZE 2048
#define MAX_NANDFLASH_PART_NUM 4
#define NAND_BLOCK_SIZE 0x20000
struct nandflash_partition {
const char *name;
u32 start_addr;
u32 size;
struct device device;
};
static struct nandflash_partition nor_part[MAX_NANDFLASH_PART_NUM];
struct nandflash_info {
u32 flash_id;
int spi_num;
int spi_err;
u8 spi_cs_io;
u8 spi_r_width;
u8 part_num;
u8 open_cnt;
struct nandflash_partition *const part_list;
OS_MUTEX mutex;
u32 max_end_addr;
};
static struct nandflash_info _nandflash = {
.spi_num = (int) - 1,
.part_list = nor_part,
};
int nand_flash_read(u32 addr, u8 *buf, u32 len);
int nand_flash_erase(u32 addr);
#define spi_cs_init() \
do { \
gpio_set_die(_nandflash.spi_cs_io, 1); \
gpio_set_direction(_nandflash.spi_cs_io, 0); \
gpio_write(_nandflash.spi_cs_io, 1); \
} while (0)
#define spi_cs_uninit() \
do { \
gpio_set_die(_nandflash.spi_cs_io, 0); \
gpio_set_direction(_nandflash.spi_cs_io, 1); \
gpio_set_pull_up(_nandflash.spi_cs_io, 0); \
gpio_set_pull_down(_nandflash.spi_cs_io, 0); \
} while (0)
#define spi_cs_h() gpio_write(_nandflash.spi_cs_io, 1)
#define spi_cs_l() gpio_write(_nandflash.spi_cs_io, 0)
#define spi_read_byte() spi_recv_byte(_nandflash.spi_num, &_nandflash.spi_err)
#define spi_write_byte(x) spi_send_byte(_nandflash.spi_num, x)
#define spi_dma_read(x, y) spi_dma_recv(_nandflash.spi_num, x, y)
#define spi_dma_write(x, y) spi_dma_send(_nandflash.spi_num, x, y)
#define spi_set_width(x) spi_set_bit_mode(_nandflash.spi_num, x)
static struct nandflash_partition *nandflash_find_part(const char *name)
{
struct nandflash_partition *part = NULL;
u32 idx;
for (idx = 0; idx < MAX_NANDFLASH_PART_NUM; idx++) {
part = &_nandflash.part_list[idx];
if (part->name == NULL) {
continue;
}
if (!strcmp(part->name, name)) {
return part;
}
}
return NULL;
}
static struct nandflash_partition *nandflash_new_part(const char *name, u32 addr, u32 size)
{
struct nandflash_partition *part;
u32 idx;
for (idx = 0; idx < MAX_NANDFLASH_PART_NUM; idx++) {
part = &_nandflash.part_list[idx];
if (part->name == NULL) {
break;
}
}
if (part->name != NULL) {
log_error("create nandflash part fail\n");
return NULL;
}
memset(part, 0, sizeof(*part));
part->name = name;
part->start_addr = addr;
part->size = size;
if (part->start_addr + part->size > _nandflash.max_end_addr) {
_nandflash.max_end_addr = part->start_addr + part->size;
}
_nandflash.part_num++;
return part;
}
static void nandflash_delete_part(const char *name)
{
struct nandflash_partition *part;
u32 idx;
for (idx = 0; idx < MAX_NANDFLASH_PART_NUM; idx++) {
part = &_nandflash.part_list[idx];
if (part->name == NULL) {
continue;
}
if (!strcmp(part->name, name)) {
part->name = NULL;
_nandflash.part_num--;
}
}
}
static int nandflash_verify_part(struct nandflash_partition *p)
{
struct nandflash_partition *part = NULL;
u32 idx;
for (idx = 0; idx < MAX_NANDFLASH_PART_NUM; idx++) {
part = &_nandflash.part_list[idx];
if (part->name == NULL) {
continue;
}
if ((p->start_addr >= part->start_addr) && (p->start_addr < part->start_addr + part->size)) {
if (strcmp(p->name, part->name) != 0) {
return -1;
}
}
}
return 0;
}
u16 nand_flash_read_id()
{
u16 id;
spi_cs_l();//SPI_CS(0);
spi_write_byte(0xff); //命令头
spi_cs_h();//SPI_CS(1);
os_time_dly(1);
spi_cs_l();//SPI_CS(0)
spi_write_byte(GD_READ_ID);
spi_write_byte(0x0);
id = spi_read_byte();
id <<= 8;
id |= spi_read_byte();
spi_cs_h();//SPI_CS(1);yy
return id;
}
static void nand_write_enable()
{
spi_cs_l();//SPI_CS(0);
spi_write_byte(GD_WRITE_ENABLE);
spi_cs_h();//SPI_CS(1);
}
static void nand_write_disable()
{
spi_cs_l();//SPI_CS(0);
spi_write_byte(GD_WRITE_DISABLE);
spi_cs_h();//SPI_CS(1);
}
static void nand_set_features(u8 cmd, u8 dat)
{
nand_write_enable();
spi_cs_l();//SPI_CS(0);
spi_write_byte(GD_SET_FEATURES); //命令头
spi_write_byte(cmd); //发送protection寄存器地址
spi_write_byte(dat); //需要设置的数据
spi_cs_h();;//SPI_CS(1);
nand_write_disable();
}
static u8 nand_get_features(u8 cmd)
{
spi_cs_l();
spi_write_byte(GD_GET_FEATURES); //命令头
spi_write_byte(cmd); //发送protection寄存器地址
cmd = spi_read_byte();
spi_cs_h();
return cmd;
}
static u8 nand_flash_wait_ok(u32 timeout)
{
u8 sta;
do {
sta = nand_get_features(GD_GET_STATUS);
if ((sta & NAND_STATUS_OIP) == 0) {
break;
}
#if 0
if (sta & NAND_STATUS_OIP) { //ing
continue;
}
if (sta & NAND_STATUS_E_FAIL) { //erase fail
break;
}
if ((sta & NAND_STATUS_ECCS) == 0x20) { //ecc error
break;
}
#endif
} while (timeout--);
if (timeout == 0) {
puts("nand_flash_wait_ok timeout!\r\n");
sta = -1;
}
return 0;
}
void nand_flash_reset(void)
{
spi_cs_l();
spi_write_byte(0xff); //命令头
spi_cs_h();
}
static u16 block_page_get(u32 addr, u32 *cache_addr)
{
u32 page = 0;
u32 block = 0, bp_mix = 0;
//<地址超1页范围
if (addr >= NAND_PAGE_SIZE) {
if (addr >= NAND_BLOCK_SIZE) {
while (addr >= NAND_BLOCK_SIZE) {
block++;
addr -= NAND_BLOCK_SIZE;
}
goto _page_count;
} else {
goto _page_count;
}
}
//<地址不超1页范围
else {
goto _end_count;
}
_page_count:
while (addr >= NAND_PAGE_SIZE) {
page++;
addr -= NAND_PAGE_SIZE;
}
_end_count:
*cache_addr = addr;
bp_mix = (block << 6) | page; //16位构成里高8位为block号码低8位里高两位依然为block低6位为page号码
//printf_u16(g_real_page);
//printf_u16(bp_mix);
//printf_u16(block);
//printf_u16(page);
return bp_mix;
}
#define block_change(x) (x)
int nand_flash_erase(u32 address)
{
u32 bp_mix = 0;
u32 cache_addr;
// puts("erase\n");
bp_mix = block_page_get(address, &cache_addr);
nand_write_enable();
spi_cs_l();//SPI_CS(0);
spi_write_byte(GD_BLOCK_ERASE);
spi_write_byte(0xff); //8位dummy bits(值任意)
spi_write_byte(bp_mix >> 8);
spi_write_byte(bp_mix);
spi_cs_h();//SPI_CS(1);
nand_write_disable();
nand_flash_wait_ok(NAND_FLASH_TIMEOUT);
return nand_get_features(GD_GET_STATUS);
}
static void nand_flash_erase_all()
{
nand_set_features(0xA0, 0x00);
for (int i = 0; i <= 1024; i++) {
nand_flash_erase(128000 * i);
}
r_printf("nandflash erase all!!!");
}
static void nand_page_read_to_cache(u32 block_page_addr) //PageRead
{
spi_cs_l();//SPI_CS(0);
spi_write_byte(GD_PAGE_READ_CACHE); //PageRead to cache
spi_write_byte(0xff); //send the Page/Block Add
spi_write_byte((u8)((block_page_addr) >> 8));
spi_write_byte((u8)block_page_addr);
spi_cs_h();//SPI_CS(1);
nand_flash_wait_ok(NAND_FLASH_TIMEOUT);
}
static void nand_read_from_cache(u8 *buf, u32 cache_addr, u32 len) //ReadCache
{
/* cache_addr |= 0x4000; */
spi_cs_l();//SPI_CS(0);
spi_write_byte(GD_READ_FROM_CACHE); //PageRead to cache
spi_write_byte((u8)((cache_addr) >> 8));
spi_write_byte((u8)cache_addr);
spi_write_byte(0xFF); //send 1byte dummy clk
spi_dma_read(buf, len);
spi_cs_h();//SPI_CS(1);
}
#if 1
static void nand_program_load(u8 *buf, u32 cache_addr, u32 len)
{
spi_cs_l();//SPI_CS(0);
spi_write_byte(GD_PROGRAM_LOAD); //PageLoad to cache ,change the data
spi_write_byte((u8)((cache_addr) >> 8));
spi_write_byte((u8)cache_addr);
spi_dma_write(buf, len); //将数据放到总线上
spi_cs_h();//SPI_CS(1);
}
static int nand_program_excute(u32 block_page_addr)
{
int reg;
nand_write_enable();
spi_cs_l();//SPI_CS(0);
spi_write_byte(GD_PROGRAM_EXECUTE);
spi_write_byte(0xff); //send the Page/Block Add
spi_write_byte((u8)((block_page_addr) >> 8));
spi_write_byte((u8)block_page_addr);
spi_cs_h();//SPI_CS(1);
nand_write_disable();
reg = nand_flash_wait_ok(NAND_FLASH_TIMEOUT);
return reg;
}
static int nand_write(u32 addr, u16 len, u8 *buf)
{
int reg;
u32 bp_mix = 0;
u32 cache_addr;
bp_mix = block_page_get(addr, &cache_addr);
bp_mix = block_change(bp_mix);
//printf_u16(cache_addr);
nand_program_load(buf, cache_addr, len);
reg = nand_program_excute(bp_mix);
return reg;
}/**/
static void nand_program_load_random_data(u8 *buf, u16 cache_addr, u16 len)
{
spi_cs_l();//SPI_CS(0);
spi_write_byte(GD_PROGRAM_LOAD_RANDOM_DATA);
spi_write_byte((u8)((cache_addr) >> 8));
spi_write_byte((u8)cache_addr);
spi_dma_write(buf, len); //将数据放到总线上
spi_cs_h();//SPI_CS(1);
nand_flash_wait_ok(NAND_FLASH_TIMEOUT);
}
int nand_internal_data_move(u32 addr, u16 len, u8 *buf)
{
u32 bp_mix = 0;
u32 cache_addr;
bp_mix = block_page_get(addr, &cache_addr);
bp_mix = block_change(bp_mix);
//<1、PAGE READ to cache
nand_page_read_to_cache(bp_mix);
//<2、PROGRAM LOAD RANDOM DATA
nand_program_load_random_data(buf, cache_addr, len);
//<3、WRITE ENABLE
//<4、PROGRAM EXECUTE
nand_program_excute(bp_mix);
//<5、GET FEATURE
return nand_get_features(GD_GET_STATUS);
}
int nand_flash_write(u32 offset, u8 *buf, u16 len)
{
/* printf("%s() %x l: %x @:%x \n",__func__,buf,len,offset); */
int reg;
int _len = len;
u8 *_buf = (u8 *)buf;
os_mutex_pend(&_nandflash.mutex, 0);
u32 first_page_len = NAND_PAGE_SIZE - (offset % NAND_PAGE_SIZE);
/* printf("first_page_len %x %x \n", first_page_len, offset % NAND_PAGE_SIZE); */
first_page_len = _len > first_page_len ? first_page_len : _len;
reg = nand_write(offset, first_page_len, _buf);
if (reg) {
goto __exit;
}
_len -= first_page_len;
_buf += first_page_len;
offset += first_page_len;
while (_len) {
u32 cnt = _len > NAND_PAGE_SIZE ? NAND_PAGE_SIZE : _len;
reg = nand_write(offset, cnt, _buf);
if (reg) {
goto __exit;
}
_len -= cnt;
offset += cnt;
_buf += cnt;
}
__exit:
os_mutex_post(&_nandflash.mutex);
return 0;
}
#endif
static void nand_read(u32 addr, u32 len, u8 *buf)
{
u32 bp_mix = 0;
u32 cache_addr;
bp_mix = block_page_get(addr, &cache_addr);
bp_mix = block_change(bp_mix);
nand_page_read_to_cache(bp_mix);
nand_read_from_cache(buf, cache_addr, len);
}
int nand_flash_read(u32 offset, u8 *buf, u32 len)
{
/* printf("%s() %x l: %x @:%x \n",__func__,buf,len,offset); */
int reg = 0;
int _len = len;
u8 *_buf = (u8 *)buf;
os_mutex_pend(&_nandflash.mutex, 0);
u32 first_page_len = NAND_PAGE_SIZE - (offset % NAND_PAGE_SIZE);
/* printf("first_page_len %x %x \n", first_page_len, offset % NAND_PAGE_SIZE); */
first_page_len = _len > first_page_len ? first_page_len : _len;
nand_read(offset, first_page_len, _buf);
_len -= first_page_len;
_buf += first_page_len;
offset += first_page_len;
while (_len) {
u32 cnt = _len > NAND_PAGE_SIZE ? NAND_PAGE_SIZE : _len;
nand_read(offset, cnt, _buf);
_len -= cnt;
offset += cnt;
_buf += cnt;
}
os_mutex_post(&_nandflash.mutex);
return reg;
}
int _nandflash_init(const char *name, struct nandflash_dev_platform_data *pdata)
{
log_info("nandflash_init ! %x %x", pdata->spi_cs_port, pdata->spi_read_width);
if (_nandflash.spi_num == (int) - 1) {
_nandflash.spi_num = pdata->spi_hw_num;
_nandflash.spi_cs_io = pdata->spi_cs_port;
_nandflash.spi_r_width = pdata->spi_read_width;
_nandflash.flash_id = 0;
os_mutex_create(&_nandflash.mutex);
_nandflash.max_end_addr = 0;
_nandflash.part_num = 0;
}
ASSERT(_nandflash.spi_num == pdata->spi_hw_num);
ASSERT(_nandflash.spi_cs_io == pdata->spi_cs_port);
ASSERT(_nandflash.spi_r_width == pdata->spi_read_width);
struct nandflash_partition *part;
part = nandflash_find_part(name);
if (!part) {
part = nandflash_new_part(name, pdata->start_addr, pdata->size);
ASSERT(part, "not enough nandflash partition memory in array\n");
ASSERT(nandflash_verify_part(part) == 0, "nandflash partition %s overlaps\n", name);
log_info("nandflash new partition %s\n", part->name);
} else {
ASSERT(0, "nandflash partition name already exists\n");
}
return 0;
}
static void clock_critical_enter()
{
}
static void clock_critical_exit()
{
if (!(_nandflash.flash_id == 0 || _nandflash.flash_id == 0xffff)) {
spi_set_baud(_nandflash.spi_num, spi_get_baud(_nandflash.spi_num));
}
}
CLOCK_CRITICAL_HANDLE_REG(spi_nandflash, clock_critical_enter, clock_critical_exit);
int _nandflash_open(void *arg)
{
int reg = 0;
os_mutex_pend(&_nandflash.mutex, 0);
log_info("nandflash open\n");
if (!_nandflash.open_cnt) {
spi_cs_init();
spi_open(_nandflash.spi_num);
_nandflash.flash_id = nand_flash_read_id();
log_info("nandflash_read_id: 0x%x\n", _nandflash.flash_id);
if ((_nandflash.flash_id == 0) || (_nandflash.flash_id == 0xffff)) {
log_error("read nandflash id error !\n");
reg = -ENODEV;
goto __exit;
}
log_info("nandflash open success !\n");
}
if (_nandflash.flash_id == 0 || _nandflash.flash_id == 0xffff) {
log_error("re-open nandflash id error !\n");
reg = -EFAULT;
goto __exit;
}
_nandflash.open_cnt++;
nand_flash_erase_all();
__exit:
os_mutex_post(&_nandflash.mutex);
return reg;
}
int _nandflash_close(void)
{
os_mutex_pend(&_nandflash.mutex, 0);
log_info("nandflash close\n");
if (_nandflash.open_cnt) {
_nandflash.open_cnt--;
}
if (!_nandflash.open_cnt) {
spi_close(_nandflash.spi_num);
spi_cs_uninit();
log_info("nandflash close done\n");
}
os_mutex_post(&_nandflash.mutex);
return 0;
}
int _nandflash_ioctl(u32 cmd, u32 arg, u32 unit, void *_part)
{
int reg = 0;
struct nandflash_partition *part = _part;
os_mutex_pend(&_nandflash.mutex, 0);
switch (cmd) {
case IOCTL_GET_STATUS:
*(u32 *)arg = 1;
break;
case IOCTL_GET_ID:
*((u32 *)arg) = _nandflash.flash_id;
break;
case IOCTL_GET_BLOCK_SIZE:
*(u32 *)arg = 512;
break;
case IOCTL_ERASE_BLOCK:
reg = nand_flash_erase(arg);
break;
case IOCTL_GET_CAPACITY:
*(u32 *)arg = 128 * 1024 / unit;
break;
case IOCTL_FLUSH:
break;
case IOCTL_CMD_RESUME:
break;
case IOCTL_CMD_SUSPEND:
break;
default:
reg = -EINVAL;
break;
}
__exit:
os_mutex_post(&_nandflash.mutex);
return reg;
}
/*************************************************************************************
* 挂钩 device_api
************************************************************************************/
static int nandflash_dev_init(const struct dev_node *node, void *arg)
{
struct nandflash_dev_platform_data *pdata = arg;
return _nandflash_init(node->name, pdata);
}
static int nandflash_dev_open(const char *name, struct device **device, void *arg)
{
struct nandflash_partition *part;
part = nandflash_find_part(name);
if (!part) {
log_error("no nandflash partition is found\n");
return -ENODEV;
}
*device = &part->device;
(*device)->private_data = part;
if (atomic_read(&part->device.ref)) {
return 0;
}
return _nandflash_open(arg);
}
static int nandflash_dev_close(struct device *device)
{
return _nandflash_close();
}
static int nandflash_dev_read(struct device *device, void *buf, u32 len, u32 offset)
{
int reg;
offset = offset * 512;
len = len * 512;
/* r_printf("flash read sector = %d, num = %d\n", offset, len); */
struct nandflash_partition *part;
part = (struct nandflash_partition *)device->private_data;
if (!part) {
log_error("nandflash partition invalid\n");
return -EFAULT;
}
offset += part->start_addr;
reg = nand_flash_read(offset, buf, len);
if (reg) {
r_printf(">>>[r error]:\n");
len = 0;
}
len = len / 512;
return len;
}
static int nandflash_dev_write(struct device *device, void *buf, u32 len, u32 offset)
{
/* r_printf("flash write sector = %d, num = %d\n", offset, len); */
int reg = 0;
offset = offset * 512;
len = len * 512;
struct nandflash_partition *part = device->private_data;
if (!part) {
log_error("nandflash partition invalid\n");
return -EFAULT;
}
offset += part->start_addr;
reg = nand_flash_write(offset, buf, len);
if (reg) {
r_printf(">>>[w error]:\n");
len = 0;
}
len = len / 512;
return len;
}
static bool nandflash_dev_online(const struct dev_node *node)
{
return 1;
}
static int nandflash_dev_ioctl(struct device *device, u32 cmd, u32 arg)
{
struct nandflash_partition *part = device->private_data;
if (!part) {
log_error("nandflash partition invalid\n");
return -EFAULT;
}
return _nandflash_ioctl(cmd, arg, 512, part);
}
const struct device_operations nandflash_dev_ops = {
.init = nandflash_dev_init,
.online = nandflash_dev_online,
.open = nandflash_dev_open,
.read = nandflash_dev_read,
.write = nandflash_dev_write,
.ioctl = nandflash_dev_ioctl,
.close = nandflash_dev_close,
};
#endif