初版
This commit is contained in:
@@ -0,0 +1,256 @@
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma bss_seg(".ftl_device.data.bss")
|
||||
#pragma data_seg(".ftl_device.data")
|
||||
#pragma const_seg(".ftl_device.text.const")
|
||||
#pragma code_seg(".ftl_device.text")
|
||||
#endif
|
||||
#include "device/ioctl_cmds.h"
|
||||
#include "device/device.h"
|
||||
#include "nandflash.h"
|
||||
#include "ftl_api.h"
|
||||
#include "app_config.h"
|
||||
|
||||
#if (defined TCFG_NANDFLASH_DEV_ENABLE && TCFG_NANDFLASH_DEV_ENABLE)
|
||||
static u8 ftl_device_init = 0;
|
||||
u32 nandflash_g_capacity = 0;
|
||||
static struct device ftl_device;
|
||||
#if TCFG_ICACHE_RUN_FTL_BUF
|
||||
extern void *cache_malloc(int size);
|
||||
extern void cache_free(void *buf);
|
||||
void *ftl_page_buf_alloc(size_t size)
|
||||
{
|
||||
void *ptr = cache_malloc(size);
|
||||
memset((u8 *)ptr, 0, size);
|
||||
return ptr;
|
||||
|
||||
}
|
||||
void ftl_page_buf_free(void *p)
|
||||
{
|
||||
cache_free(p);
|
||||
}
|
||||
#endif//TCFG_ICACHE_RUN_FTL_BUF
|
||||
static u32 get_first_one(u32 n)
|
||||
{
|
||||
u32 pos = 0;
|
||||
for (pos = 0; pos < 32; pos++) {
|
||||
if (n & BIT(pos)) {
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
static enum ftl_error_t ftl_port_page_read(u16 block, u8 page, u16 offset, u8 *buf, int len)
|
||||
{
|
||||
int ret = nand_flash_read_page(block, page, offset, buf, len);
|
||||
if (ret == 0) {
|
||||
return FTL_ERR_NO;
|
||||
}
|
||||
if (ret == 1) {
|
||||
return FTL_ERR_1BIT_ECC;
|
||||
}
|
||||
return FTL_ERR_READ;
|
||||
}
|
||||
|
||||
static enum ftl_error_t ftl_port_page_write(u16 block, u8 page, u8 *buf, int len)
|
||||
{
|
||||
int ret = nand_flash_write_page(block, page, buf, len);
|
||||
if (ret == 0) {
|
||||
return FTL_ERR_NO;
|
||||
}
|
||||
if (ret == 1) {
|
||||
return FTL_ERR_1BIT_ECC;
|
||||
}
|
||||
return FTL_ERR_WRITE;
|
||||
}
|
||||
|
||||
static enum ftl_error_t ftl_port_erase_block(u32 addr)
|
||||
{
|
||||
int ret = nand_flash_erase(addr);
|
||||
if (ret == 0) {
|
||||
return FTL_ERR_NO;
|
||||
}
|
||||
return FTL_ERR_WRITE;
|
||||
}
|
||||
struct nandflash_partition {
|
||||
const char *name;
|
||||
u32 start_addr;
|
||||
u32 size;
|
||||
struct device device;
|
||||
};
|
||||
|
||||
static char ftl_re_devname[24];
|
||||
static struct device *ftl_re_device = NULL;
|
||||
static int ftl_dev_init(const struct dev_node *node, void *arg)
|
||||
{
|
||||
memset(ftl_re_devname, 0, 24);
|
||||
if (arg) {
|
||||
ASSERT(strlen(arg) < 24, "arg(len=%d):%s\n", (u32)strlen(arg), arg);
|
||||
memcpy(ftl_re_devname, arg, strlen(arg));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ftl_get_nand_info(struct ftl_nand_flash *ftl);
|
||||
static int ftl_dev_open(const char *name, struct device **device, void *arg)
|
||||
{
|
||||
if (strlen(ftl_re_devname)) {
|
||||
ftl_re_device = dev_open(ftl_re_devname, NULL);
|
||||
ASSERT(ftl_re_device, "ftl_re_device not find name:%s\n", ftl_re_devname);
|
||||
} else {
|
||||
ASSERT(0, "ftl_re_devname is null");
|
||||
}
|
||||
|
||||
/* dev_open("nand_flash", NULL); */
|
||||
if (!ftl_device_init) {
|
||||
struct ftl_nand_flash flash = {0};
|
||||
ftl_get_nand_info(&flash);
|
||||
flash.page_size_shift = get_first_one(flash.page_size);
|
||||
flash.block_size_shift = get_first_one(flash.page_size * flash.page_num);
|
||||
flash.page_read = ftl_port_page_read;
|
||||
flash.page_write = ftl_port_page_write;
|
||||
flash.erase_block = ftl_port_erase_block;
|
||||
|
||||
nandflash_g_capacity = flash.plane_number * (flash.logic_block_num << (flash.block_size_shift - 9));
|
||||
/* printf("%s %d", __func__, nandflash_g_capacity); */
|
||||
struct ftl_config config = {
|
||||
.page_buf_num = 4,
|
||||
.delayed_write_msec = 10,
|
||||
};
|
||||
ftl_init(&flash, &config);
|
||||
ftl_device_init = 1;
|
||||
}
|
||||
struct nandflash_partition *part = NULL;
|
||||
part = (struct nandflash_partition *)ftl_re_device->private_data;
|
||||
if (!part) {
|
||||
printf("Error: no nandflash partition is found!\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
*device = &ftl_device;
|
||||
(*device)->private_data = part;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ftl_dev_close(struct device *device)
|
||||
{
|
||||
ftl_uninit();
|
||||
ftl_device_init = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ftl_dev_read(struct device *device, void *buf, u32 len, u32 offset)
|
||||
{
|
||||
enum ftl_error_t error;
|
||||
|
||||
#if ((defined TCFG_VIRFAT_FLASH_ENABLE && TCFG_VIRFAT_FLASH_ENABLE)&&(!TCFG_VIRFAT_INSERT_FLASH_ENABLE))
|
||||
|
||||
ASSERT(ftl_re_devname);
|
||||
struct nandflash_partition *part = NULL;
|
||||
part = (struct nandflash_partition *)ftl_re_device->private_data;
|
||||
ASSERT(part);
|
||||
/* printf("ftl nandflash start:%d size:%d ", part->start_addr, part->size); */
|
||||
offset += part->start_addr;
|
||||
ASSERT(offset + len < part->size, "%s offset:%d len:%d size:%d start:%d", __func__, offset, len, part->size, part->start_addr);
|
||||
/* printf("%s offset:%d start_addr:%d len:%d", __func__, offset, part->start_addr, len); */
|
||||
/* 虚拟文件系统,上层已经乘以 512 */
|
||||
int rlen = ftl_read_bytes(offset, buf, len, &error);
|
||||
/* put_buf(buf, len); */
|
||||
if (rlen < 0) {
|
||||
return 0;
|
||||
}
|
||||
return rlen; // 使用virfat时,virfat处理512
|
||||
|
||||
#else
|
||||
int rlen = ftl_read_bytes(offset * 512, buf, len * 512, &error); // nandflash_ftl fat16
|
||||
|
||||
if (rlen < 0) {
|
||||
return 0;
|
||||
}
|
||||
return rlen / 512; // 使用 nandflash_ftl 时要偏移512
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
static int ftl_dev_write(struct device *device, void *buf, u32 len, u32 offset)
|
||||
{
|
||||
enum ftl_error_t error;
|
||||
#if ((defined TCFG_VIRFAT_FLASH_ENABLE && TCFG_VIRFAT_FLASH_ENABLE)&&(!TCFG_VIRFAT_INSERT_FLASH_ENABLE))
|
||||
ASSERT(ftl_re_devname);
|
||||
struct nandflash_partition *part = NULL;
|
||||
part = (struct nandflash_partition *)ftl_re_device->private_data;
|
||||
ASSERT(part);
|
||||
/* printf("ftl nandflash start:%d size:%d ", part->start_addr, part->size); */
|
||||
offset += part->start_addr;
|
||||
ASSERT(offset + len < part->size, "%s offset:%d len:%d size:%d start:%d", __func__, offset, len, part->size, part->start_addr);
|
||||
|
||||
/* 虚拟文件系统,上层已经乘以 512 */
|
||||
int wlen = ftl_write_bytes(offset, buf, len, &error);
|
||||
if (wlen < 0) {
|
||||
return 0;
|
||||
}
|
||||
return wlen;
|
||||
|
||||
#else
|
||||
int wlen = ftl_write_bytes(offset * 512, buf, len * 512, &error);
|
||||
if (wlen < 0) {
|
||||
return 0;
|
||||
}
|
||||
return wlen / 512;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool ftl_dev_online(const struct dev_node *node)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
u32 ftl_get_nand_id();
|
||||
static int ftl_dev_ioctl(struct device *device, u32 cmd, u32 arg)
|
||||
{
|
||||
int reg = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case IOCTL_GET_STATUS:
|
||||
*(u32 *)arg = 1;
|
||||
break;
|
||||
case IOCTL_GET_ID:
|
||||
*((u32 *)arg) = ftl_get_nand_id();
|
||||
break;
|
||||
case IOCTL_GET_BLOCK_SIZE:
|
||||
*(u32 *)arg = 512;//usb fat
|
||||
break;
|
||||
case IOCTL_ERASE_BLOCK:
|
||||
ftl_erase_lgc_addr(arg * 512);
|
||||
break;
|
||||
case IOCTL_GET_CAPACITY:
|
||||
*(u32 *)arg = nandflash_g_capacity;
|
||||
break;
|
||||
case IOCTL_FLUSH:
|
||||
break;
|
||||
case IOCTL_CMD_RESUME:
|
||||
break;
|
||||
case IOCTL_CMD_SUSPEND:
|
||||
break;
|
||||
case IOCTL_CHECK_WRITE_PROTECT:
|
||||
*(u32 *)arg = 0;
|
||||
break;
|
||||
default:
|
||||
reg = -EINVAL;
|
||||
break;
|
||||
}
|
||||
return reg;
|
||||
}
|
||||
|
||||
const struct device_operations nandflash_ftl_ops = {
|
||||
.init = ftl_dev_init,
|
||||
.online = ftl_dev_online,
|
||||
.open = ftl_dev_open,
|
||||
.read = ftl_dev_read,
|
||||
.write = ftl_dev_write,
|
||||
.ioctl = ftl_dev_ioctl,
|
||||
.close = ftl_dev_close,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,383 @@
|
||||
#include "device/ioctl_cmds.h"
|
||||
#include "device/device.h"
|
||||
#include "nandflash.h"
|
||||
#include "ftl_api.h"
|
||||
#include "app_config.h"
|
||||
|
||||
|
||||
#if (defined TCFG_NANDFLASH_DEV_ENABLE && TCFG_NANDFLASH_DEV_ENABLE)
|
||||
|
||||
/* 这里引用 norflash_sfc.h 头文件,是因为需要使用 norflash_sfc_dev_platform_data 结构体,同时保证板级配置一致 */
|
||||
#include "norflash_sfc.h"
|
||||
|
||||
/* 这里容量直接 extern ftl_device.c 中的定义,因为这里必须通过ftl才能访问nandflash,因此 ftl_device 应该先运行 */
|
||||
extern u32 nandflash_g_capacity;
|
||||
//==============================================================================================================//
|
||||
// part
|
||||
//==============================================================================================================//
|
||||
|
||||
/* 以下内容参考 Norflash_sfc.c 实现,保证 vm 区操作与norflash一致 */
|
||||
#define MAX_NORFLASH_PART_NUM 4
|
||||
|
||||
struct nandflash_partition {
|
||||
const char *name;
|
||||
u32 start_addr;
|
||||
u32 size;
|
||||
struct device device;
|
||||
};
|
||||
static struct nandflash_partition nandflash_part[MAX_NORFLASH_PART_NUM];
|
||||
|
||||
struct norflash_info {
|
||||
u32 flash_id;
|
||||
u32 flash_capacity;
|
||||
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 norflash_info _norflash = {
|
||||
.spi_num = (int) - 1,
|
||||
.part_list = nandflash_part,
|
||||
};
|
||||
|
||||
static struct nandflash_partition *norflash_find_part(const char *name)
|
||||
{
|
||||
struct nandflash_partition *part = NULL;
|
||||
u32 idx;
|
||||
for (idx = 0; idx < MAX_NORFLASH_PART_NUM; idx++) {
|
||||
part = &_norflash.part_list[idx];
|
||||
if (part->name == NULL) {
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(part->name, name)) {
|
||||
return part;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct nandflash_partition *norflash_new_part(const char *name, u32 addr, u32 size)
|
||||
{
|
||||
struct nandflash_partition *part;
|
||||
u32 idx;
|
||||
for (idx = 0; idx < MAX_NORFLASH_PART_NUM; idx++) {
|
||||
part = &_norflash.part_list[idx];
|
||||
if (part->name == NULL) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (part->name != NULL) {
|
||||
printf("create norflash 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 > _norflash.max_end_addr) {
|
||||
_norflash.max_end_addr = part->start_addr + part->size;
|
||||
}
|
||||
_norflash.part_num++;
|
||||
return part;
|
||||
}
|
||||
|
||||
static int norflash_verify_part(struct nandflash_partition *p)
|
||||
{
|
||||
struct nandflash_partition *part = NULL;
|
||||
u32 idx;
|
||||
for (idx = 0; idx < MAX_NORFLASH_PART_NUM; idx++) {
|
||||
part = &_norflash.part_list[idx];
|
||||
if (part->name == NULL ||
|
||||
0 == strcmp(p->name, "update_noenc") ||
|
||||
0 == strcmp(part->name, "update_noenc")) {
|
||||
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;
|
||||
}
|
||||
//==============================================================================================================//
|
||||
// bytes对齐
|
||||
//==============================================================================================================//
|
||||
static int ftl_dev_init(const struct dev_node *node, void *arg)
|
||||
{
|
||||
struct nandflash_partition *part = NULL;
|
||||
struct norflash_sfc_dev_platform_data *pdata = arg;
|
||||
part = norflash_find_part(node->name);
|
||||
|
||||
if (!part) {
|
||||
part = norflash_new_part(node->name, pdata->start_addr, pdata->size);
|
||||
ASSERT(part, "not enough norflash partition memory in array\n");
|
||||
ASSERT(norflash_verify_part(part) == 0, "norflash partition %s overlaps\n", node->name);
|
||||
printf("norflash new partition %s\n", part->name);
|
||||
} else {
|
||||
ASSERT(0, "norflash partition name already exists\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ftl_dev_open(const char *name, struct device **device, void *arg)
|
||||
{
|
||||
/* 这里使用 ftl 设备进行操作,使用时ftl设备应该已经挂载成功。 */
|
||||
struct nandflash_partition *part = NULL;
|
||||
part = norflash_find_part(name);
|
||||
if (!part) {
|
||||
printf("Error: no nandflash partition is found!\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
*device = &part->device;
|
||||
(*device)->private_data = part;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ftl_dev_close(struct device *device)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ftl_dev_read(struct device *device, void *buf, u32 len, u32 offset)
|
||||
{
|
||||
enum ftl_error_t error;
|
||||
struct nandflash_partition *part = device->private_data;
|
||||
|
||||
/* 防止访问的地址超过vm区配置 */
|
||||
ASSERT((offset + len) <= part->size, "read nandflash addr over limit: 0x%x\n", (offset + len));
|
||||
|
||||
/* 偏移到vm区起始地址 */
|
||||
offset += part->start_addr;
|
||||
/* printf("@@@ [%s] %s: start: 0x%x, offset: 0x%x\n", __FILE__, __func__, part->start_addr, offset); */
|
||||
|
||||
int rlen = ftl_read_bytes(offset, buf, len, &error);
|
||||
if (rlen < 0) {
|
||||
return 0;
|
||||
}
|
||||
return rlen;
|
||||
}
|
||||
|
||||
static int ftl_dev_write(struct device *device, void *buf, u32 len, u32 offset)
|
||||
{
|
||||
enum ftl_error_t error;
|
||||
struct nandflash_partition *part = device->private_data;
|
||||
|
||||
/* 防止访问的地址超过vm区配置 */
|
||||
ASSERT((offset + len) <= part->size, "write nandflash addr over limit: 0x%x\n", (offset + len));
|
||||
|
||||
/* 偏移到vm区起始地址 */
|
||||
offset += part->start_addr;
|
||||
/* printf("@@@ [%s] %s: start: 0x%x, offset: 0x%x\n", __FILE__, __func__, part->start_addr, offset); */
|
||||
|
||||
int wlen = ftl_write_bytes(offset, buf, len, &error);
|
||||
if (wlen < 0) {
|
||||
return 0;
|
||||
}
|
||||
return wlen;
|
||||
}
|
||||
|
||||
static bool ftl_dev_online(const struct dev_node *node)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
u32 ftl_get_nand_id();
|
||||
static int ftl_dev_ioctl(struct device *device, u32 cmd, u32 arg)
|
||||
{
|
||||
int reg = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case IOCTL_GET_STATUS:
|
||||
*(u32 *)arg = 1;
|
||||
break;
|
||||
case IOCTL_GET_ID:
|
||||
*((u32 *)arg) = ftl_get_nand_id();
|
||||
break;
|
||||
case IOCTL_GET_BLOCK_SIZE:
|
||||
*(u32 *)arg = 512;//usb fat
|
||||
break;
|
||||
case IOCTL_ERASE_BLOCK:
|
||||
break;
|
||||
case IOCTL_GET_CAPACITY:
|
||||
*(u32 *)arg = nandflash_g_capacity;
|
||||
break;
|
||||
case IOCTL_FLUSH:
|
||||
break;
|
||||
case IOCTL_CMD_RESUME:
|
||||
break;
|
||||
case IOCTL_CMD_SUSPEND:
|
||||
break;
|
||||
case IOCTL_CHECK_WRITE_PROTECT:
|
||||
*(u32 *)arg = 0;
|
||||
break;
|
||||
default:
|
||||
reg = -EINVAL;
|
||||
break;
|
||||
}
|
||||
return reg;
|
||||
}
|
||||
|
||||
const struct device_operations nandflash_fs_ftl_ops = {
|
||||
.init = ftl_dev_init,
|
||||
.online = ftl_dev_online,
|
||||
.open = ftl_dev_open,
|
||||
.read = ftl_dev_read,
|
||||
.write = ftl_dev_write,
|
||||
.ioctl = ftl_dev_ioctl,
|
||||
.close = ftl_dev_close,
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================================================//
|
||||
// 512bytes对齐,用于fat文件系统
|
||||
//==============================================================================================================//
|
||||
static int ftl_sfc_dev_init(const struct dev_node *node, void *arg)
|
||||
{
|
||||
struct nandflash_partition *part = NULL;
|
||||
struct norflash_sfc_dev_platform_data *pdata = arg;
|
||||
part = norflash_find_part(node->name);
|
||||
|
||||
if (!part) {
|
||||
part = norflash_new_part(node->name, pdata->start_addr, pdata->size);
|
||||
ASSERT(part, "not enough norflash partition memory in array\n");
|
||||
ASSERT(norflash_verify_part(part) == 0, "norflash partition %s overlaps\n", node->name);
|
||||
printf("norflash new partition %s\n", part->name);
|
||||
} else {
|
||||
ASSERT(0, "norflash partition name already exists\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ftl_sfc_dev_open(const char *name, struct device **device, void *arg)
|
||||
{
|
||||
/* 这里使用 ftl 设备进行操作,使用时ftl设备应该已经挂载成功。 */
|
||||
struct nandflash_partition *part = NULL;
|
||||
part = norflash_find_part(name);
|
||||
if (!part) {
|
||||
printf("Error: no nandflash partition is found!\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ASSERT((part->size + part->start_addr) <= (nandflash_g_capacity * 512), "part->size %x need <= %x\n", part->size, part->size - ((part->size + part->start_addr) - nandflash_g_capacity * 512));
|
||||
*device = &part->device;
|
||||
(*device)->private_data = part;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ftl_sfc_dev_close(struct device *device)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ftl_sfc_dev_read(struct device *device, void *buf, u32 len, u32 offset)
|
||||
{
|
||||
enum ftl_error_t error;
|
||||
struct nandflash_partition *part = device->private_data;
|
||||
|
||||
offset = offset * 512;
|
||||
len = len * 512;
|
||||
|
||||
/* 防止访问的地址超过vm区配置 */
|
||||
ASSERT((offset + len) <= part->size, "read nandflash addr over limit: 0x%x\n", (offset + len));
|
||||
|
||||
/* 偏移到vm区起始地址 */
|
||||
offset += part->start_addr;
|
||||
/* printf("@@@ [%s] %s: start: 0x%x, offset: 0x%x\n", __FILE__, __func__, part->start_addr, offset); */
|
||||
/* printf("@@@ [] %s: start: 0x%x, offset: 0x%x, len: %d\n", __func__, part->start_addr, offset, len); */
|
||||
|
||||
int rlen = ftl_read_bytes(offset, buf, len, &error);
|
||||
if (rlen < 0) {
|
||||
return 0;
|
||||
}
|
||||
return rlen / 512;
|
||||
}
|
||||
|
||||
static int ftl_sfc_dev_write(struct device *device, void *buf, u32 len, u32 offset)
|
||||
{
|
||||
enum ftl_error_t error;
|
||||
struct nandflash_partition *part = device->private_data;
|
||||
|
||||
offset = offset * 512;
|
||||
len = len * 512;
|
||||
|
||||
/* 防止访问的地址超过vm区配置 */
|
||||
ASSERT((offset + len) <= part->size, "write nandflash addr over limit: 0x%x\n", (offset + len));
|
||||
|
||||
/* 偏移到vm区起始地址 */
|
||||
offset += part->start_addr;
|
||||
/* printf("@@@ [%s] %s: start: 0x%x, offset: 0x%x\n", __FILE__, __func__, part->start_addr, offset); */
|
||||
|
||||
int wlen = ftl_write_bytes(offset, buf, len, &error);
|
||||
if (wlen < 0) {
|
||||
return 0;
|
||||
}
|
||||
return wlen / 512;
|
||||
}
|
||||
|
||||
static bool ftl_sfc_dev_online(const struct dev_node *node)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
u32 ftl_get_nand_id();
|
||||
static int ftl_sfc_dev_ioctl(struct device *device, u32 cmd, u32 arg)
|
||||
{
|
||||
int reg = 0;
|
||||
struct nandflash_partition *part = device->private_data;
|
||||
switch (cmd) {
|
||||
case IOCTL_GET_STATUS:
|
||||
*(u32 *)arg = 1;
|
||||
break;
|
||||
case IOCTL_GET_ID:
|
||||
*((u32 *)arg) = ftl_get_nand_id();
|
||||
break;
|
||||
case IOCTL_GET_BLOCK_SIZE:
|
||||
*(u32 *)arg = 512;//usb fat
|
||||
break;
|
||||
case IOCTL_ERASE_BLOCK:
|
||||
break;
|
||||
case IOCTL_GET_CAPACITY:
|
||||
*(u32 *)arg = part->size / 512;
|
||||
break;
|
||||
case IOCTL_FLUSH:
|
||||
break;
|
||||
case IOCTL_CMD_RESUME:
|
||||
break;
|
||||
case IOCTL_CMD_SUSPEND:
|
||||
break;
|
||||
case IOCTL_CHECK_WRITE_PROTECT:
|
||||
*(u32 *)arg = 0;
|
||||
break;
|
||||
default:
|
||||
reg = -EINVAL;
|
||||
break;
|
||||
}
|
||||
return reg;
|
||||
}
|
||||
|
||||
const struct device_operations nandflash_sfc_ftl_ops = {
|
||||
.init = ftl_sfc_dev_init,
|
||||
.online = ftl_sfc_dev_online,
|
||||
.open = ftl_sfc_dev_open,
|
||||
.read = ftl_sfc_dev_read,
|
||||
.write = ftl_sfc_dev_write,
|
||||
.ioctl = ftl_sfc_dev_ioctl,
|
||||
.close = ftl_sfc_dev_close,
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,317 @@
|
||||
#include "includes.h"
|
||||
#include "app_config.h"
|
||||
#include "norflash_sfc.h"
|
||||
#include "tzflash_api.h"
|
||||
|
||||
#undef LOG_TAG_CONST
|
||||
#define LOG_TAG "[FLASH_INDIDE]"
|
||||
#define LOG_ERROR_ENABLE
|
||||
#define LOG_INFO_ENABLE
|
||||
#include "debug.h"
|
||||
|
||||
/* extern void sfc_protect_lock(u8 index); */
|
||||
/* extern void sfc_protect_release(u8 index); */
|
||||
|
||||
#define norflash_mutex_init() //
|
||||
#define norflash_mutex_enter() //sfc_protect_lock(0)
|
||||
#define norflash_mutex_exit() //sfc_protect_release(0)
|
||||
extern void ble_busy_wait_idle(u8 type);
|
||||
static struct list_head head = LIST_HEAD_INIT(head);
|
||||
|
||||
struct norflash_partition {
|
||||
struct list_head entry;
|
||||
const char *name;
|
||||
u32 start_addr;
|
||||
u32 size;
|
||||
u8 igore_overlaps; //忽略分区检查
|
||||
struct device device;
|
||||
};
|
||||
|
||||
static struct norflash_partition *norflash_find_part(const char *name)
|
||||
{
|
||||
struct norflash_partition *p, *n;
|
||||
local_irq_disable();
|
||||
list_for_each_entry_safe(p, n, &head, entry) {
|
||||
if (!strcmp(p->name, name)) {
|
||||
local_irq_enable();
|
||||
return p;
|
||||
}
|
||||
}
|
||||
local_irq_enable();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct norflash_partition *norflash_new_part(const char *name, u32 addr, u32 size, u8 igore_overlap)
|
||||
{
|
||||
struct norflash_partition *part = zalloc(sizeof(struct norflash_partition));
|
||||
ASSERT(part, "norflash_partition malloc err");
|
||||
part->name = name;
|
||||
part->start_addr = addr;
|
||||
part->size = size;
|
||||
part->igore_overlaps = igore_overlap;
|
||||
local_irq_disable();
|
||||
list_add_tail(&part->entry, &head);
|
||||
local_irq_enable();
|
||||
return part;
|
||||
}
|
||||
|
||||
static void norflash_delete_part(const char *name)
|
||||
{
|
||||
struct norflash_partition *p, *n;
|
||||
local_irq_disable();
|
||||
list_for_each_entry_safe(p, n, &head, entry) {
|
||||
if (!strcmp(p->name, name)) {
|
||||
list_del(&p->entry);
|
||||
free(p);
|
||||
local_irq_enable();
|
||||
return ;
|
||||
}
|
||||
}
|
||||
local_irq_enable();
|
||||
return ;
|
||||
}
|
||||
|
||||
static int norflash_verify_part(struct norflash_partition *p)
|
||||
{
|
||||
|
||||
if (p->igore_overlaps) {
|
||||
return 0;
|
||||
}
|
||||
struct norflash_partition *part, *n;
|
||||
local_irq_disable();
|
||||
list_for_each_entry_safe(part, n, &head, entry) {
|
||||
if (part->igore_overlaps) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
local_irq_enable();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
extern u32 sdfile_flash_addr2cpu_addr(u32 offset);
|
||||
extern u32 sdfile_cpu_addr2flash_addr(u32 offset);
|
||||
|
||||
static int _norflash_init(const char *name, struct norflash_sfc_dev_platform_data *pdata)
|
||||
{
|
||||
log_info("norflash_sfc_init >>>>");
|
||||
if (pdata->path) {
|
||||
FILE *fp = fopen((const char *)pdata->path, "r");
|
||||
if (!fp) {
|
||||
log_error("fopen err :%s \n", pdata->path);
|
||||
ASSERT(0);
|
||||
return false;
|
||||
}
|
||||
struct vfs_attr attr = {0};
|
||||
fget_attrs(fp, &attr);
|
||||
u32 part_addr = attr.sclust;
|
||||
u32 part_len = attr.fsize;
|
||||
part_addr = sdfile_cpu_addr2flash_addr(part_addr);
|
||||
ASSERT(pdata->start_addr == part_addr, "%x %x\n", pdata->start_addr, part_addr);
|
||||
ASSERT(part_len <= pdata->size, "%x ,%x\n", pdata->start_addr, part_addr);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
|
||||
struct norflash_partition *part;
|
||||
part = norflash_find_part(name);
|
||||
|
||||
if (!part) {
|
||||
part = norflash_new_part(name, pdata->start_addr, pdata->size, pdata->igore_overlaps);
|
||||
ASSERT(part, "not enough norflash partition memory in array\n");
|
||||
ASSERT(norflash_verify_part(part) == 0, "norflash partition %s overlaps\n", name);
|
||||
log_info("norflash new partition %s\n", part->name);
|
||||
} else {
|
||||
ASSERT(0, "norflash partition name already exists\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int norflash_sfc_fs_dev_init(const struct dev_node *node, void *arg)
|
||||
{
|
||||
struct norflash_sfc_dev_platform_data *pdata = arg;
|
||||
|
||||
return _norflash_init(node->name, pdata);
|
||||
}
|
||||
|
||||
static int norflash_sfc_fs_dev_open(const char *name, struct device **device, void *arg)
|
||||
{
|
||||
struct norflash_partition *part;
|
||||
part = norflash_find_part(name);
|
||||
if (!part) {
|
||||
log_error("no norflash partition is found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
*device = &part->device;
|
||||
(*device)->private_data = part;
|
||||
if (atomic_read(&part->device.ref)) {
|
||||
return 0;
|
||||
}
|
||||
return 0;//_norflash_open(arg);
|
||||
}
|
||||
static int norflash_sfc_fs_dev_close(struct device *device)
|
||||
{
|
||||
return 0;//_norflash_close();
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int norflash_sfc_fs_dev_read(struct device *device, void *buf, u32 len, u32 offset)
|
||||
{
|
||||
u32 rets;//, reti;
|
||||
__asm__ volatile("%0 = rets":"=r"(rets));
|
||||
int reg;
|
||||
struct norflash_partition *part;
|
||||
part = (struct norflash_partition *)device->private_data;
|
||||
if (!part) {
|
||||
log_error("norflash partition invalid\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
if (offset + len > part->size) {
|
||||
log_error("__func__ %s %x %x %x\n", __func__, rets, offset, len);
|
||||
ASSERT(0);
|
||||
}
|
||||
offset += part->start_addr;
|
||||
norflash_mutex_enter();
|
||||
u32 cpu_addr = sdfile_flash_addr2cpu_addr(offset);
|
||||
memcpy((u8 *)buf, (u8 *)cpu_addr, len);
|
||||
norflash_mutex_exit();
|
||||
return len;
|
||||
}
|
||||
|
||||
static int norflash_sfc_fs_dev_write(struct device *device, void *buf, u32 len, u32 offset)
|
||||
{
|
||||
u32 rets;//, reti;
|
||||
__asm__ volatile("%0 = rets":"=r"(rets));
|
||||
int reg = 0;
|
||||
struct norflash_partition *part = device->private_data;
|
||||
if (!part) {
|
||||
log_error("norflash partition invalid\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
if (offset + len > part->size) {
|
||||
log_error("__func__ %s %x %x %x\n", __func__, rets, offset, len);
|
||||
ASSERT(0);
|
||||
}
|
||||
offset += part->start_addr;
|
||||
/* norflash_mutex_enter(); */
|
||||
ASSERT(!cpu_in_irq());
|
||||
ASSERT(!cpu_irq_disabled());
|
||||
reg = tzflash_write(buf, len, offset);//写操作里面已经集合的互斥,这里仅仅检查中断
|
||||
/* norflash_mutex_exit(); */
|
||||
return reg;
|
||||
}
|
||||
|
||||
static bool norflash_sfc_fs_dev_online(const struct dev_node *node)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int norflash_sfc_fs_dev_ioctl(struct device *device, u32 cmd, u32 arg)
|
||||
{
|
||||
u32 rets;//, reti;
|
||||
__asm__ volatile("%0 = rets":"=r"(rets));
|
||||
struct norflash_partition *part = device->private_data;
|
||||
struct sfc_no_enc_wr *no_enc_wr;
|
||||
int reg = 0;
|
||||
if (!part) {
|
||||
log_error("norflash partition invalid\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case IOCTL_GET_PART_INFO:
|
||||
u32 *info = (u32 *)arg;
|
||||
u32 *start_addr = &info[0];
|
||||
u32 *part_size = &info[1];
|
||||
*start_addr = part->start_addr;
|
||||
*part_size = part->size;
|
||||
break;
|
||||
case IOCTL_GET_CAPACITY:
|
||||
*(u32 *)arg = part->size;
|
||||
break;
|
||||
case IOCTL_ERASE_PAGE:
|
||||
ASSERT(!cpu_in_irq());
|
||||
ASSERT(!cpu_irq_disabled());
|
||||
//写操作里面已经集合的互斥,这里仅仅检查中断
|
||||
#if TCFG_FLASH_ERASE_WAIT_BLE
|
||||
//擦除前需等待ble idle
|
||||
ble_busy_wait_idle(IOCTL_ERASE_PAGE);
|
||||
#endif
|
||||
return tzflash_ioctl(cmd, arg + part->start_addr);
|
||||
break;
|
||||
case IOCTL_ERASE_SECTOR:
|
||||
if ((arg * 1) + 4096 > part->size) {
|
||||
log_error("__func__ %s %x %x %x\n", __func__, rets, arg, 1);
|
||||
ASSERT(0);
|
||||
}
|
||||
|
||||
ASSERT(!cpu_in_irq());
|
||||
ASSERT(!cpu_irq_disabled());
|
||||
#if TCFG_FLASH_ERASE_WAIT_BLE
|
||||
//擦除前需等待ble idle
|
||||
ble_busy_wait_idle(IOCTL_ERASE_SECTOR);
|
||||
#endif
|
||||
//写操作里面已经集合的互斥,这里仅仅检查中断
|
||||
return tzflash_ioctl(cmd, arg + part->start_addr);
|
||||
break;
|
||||
case IOCTL_ERASE_BLOCK:
|
||||
if ((arg * 1) + (64 * 1024) > part->size) {
|
||||
log_error("__func__ %s %x %x %x\n", __func__, rets, arg, 1);
|
||||
ASSERT(0);
|
||||
}
|
||||
|
||||
ASSERT(!cpu_in_irq());
|
||||
ASSERT(!cpu_irq_disabled());
|
||||
#if TCFG_FLASH_ERASE_WAIT_BLE
|
||||
//擦除前需等待ble idle
|
||||
ble_busy_wait_idle(IOCTL_ERASE_BLOCK);
|
||||
#endif
|
||||
//写操作里面已经集合的互斥,这里仅仅检查中断
|
||||
return tzflash_ioctl(cmd, arg + part->start_addr);
|
||||
break;
|
||||
#if 0
|
||||
case IOCTL_SFC_NORFLASH_READ_NO_ENC:
|
||||
no_enc_wr = (struct sfc_no_enc_wr *)arg;
|
||||
norflash_mutex_enter();
|
||||
u32 cpu_addr = sdfile_flash_addr2cpu_addr(no_enc_wr->addr);
|
||||
memcpy((u8 *)no_enc_wr->buf, (u8 *)cpu_addr, no_enc_wr->len);
|
||||
norflash_mutex_exit();
|
||||
reg = 0;
|
||||
break;
|
||||
case IOCTL_SFC_NORFLASH_WRITE_NO_ENC:
|
||||
no_enc_wr = (struct sfc_no_enc_wr *)arg;
|
||||
/* norflash_mutex_enter(); */
|
||||
ASSERT(!cpu_in_irq());
|
||||
ASSERT(!cpu_irq_disabled());
|
||||
//写操作里面已经集合的互斥,这里仅仅检查中断
|
||||
reg = tzflash_write(no_enc_wr->buf, no_enc_wr->len, no_enc_wr->addr);
|
||||
/* norflash_mutex_exit(); */
|
||||
break;
|
||||
#endif
|
||||
case IOCTL_FLUSH:
|
||||
break;
|
||||
default:
|
||||
return tzflash_ioctl(cmd, arg);
|
||||
|
||||
}
|
||||
|
||||
return reg;
|
||||
}
|
||||
|
||||
//按1字节单位读写
|
||||
const struct device_operations inside_norflash_fs_dev_ops = {
|
||||
.init = norflash_sfc_fs_dev_init,
|
||||
.online = norflash_sfc_fs_dev_online,
|
||||
.open = norflash_sfc_fs_dev_open,
|
||||
.read = norflash_sfc_fs_dev_read,
|
||||
.write = norflash_sfc_fs_dev_write,
|
||||
.ioctl = norflash_sfc_fs_dev_ioctl,
|
||||
.close = norflash_sfc_fs_dev_close,
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,20 @@
|
||||
#ifndef _ESPI_NOR_FLASH_H_
|
||||
#define _ESPI_NOR_FLASH_H_
|
||||
|
||||
//!!!根据701客户历史反馈情况,不建议直接使用这部分接口,建议使用分区dev接口,避免用户读写异常破坏了整个flash
|
||||
|
||||
#define ESPI_NORFLASH_READ_FREQ 85000000
|
||||
#define ESPI_NORFLASH_WRITE_FREQ 85000000
|
||||
|
||||
extern void espi_norflash_init(u8 read_mode, u8 width);
|
||||
extern u32 espi_norflash_read(u8 *buf, u32 addr, u32 len);//len:any//no continue,no 0单线 //no bbh
|
||||
extern u32 espi_norflash_write(u8 *buf, u32 addr, u32 len);
|
||||
extern int espi_norflash_ioctl(u32 cmd, u32 arg);
|
||||
|
||||
|
||||
extern void norflash_espi_mutex_enter();
|
||||
extern void norflash_espi_mutex_exit();
|
||||
//外挂flash 直接cpu 寻址请注意加上互斥
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,722 @@
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma bss_seg(".norflash_sfc.data.bss")
|
||||
#pragma data_seg(".norflash_sfc.data")
|
||||
#pragma const_seg(".norflash_sfc.text.const")
|
||||
#pragma code_seg(".norflash_sfc.text")
|
||||
#endif
|
||||
#include "system/includes.h"
|
||||
#include "app_config.h"
|
||||
#include "norflash_sfc.h"
|
||||
#include "norflash_espi.h"
|
||||
|
||||
#if (defined(TCFG_NORFLASH_SFC_DEV_ENABLE) && TCFG_NORFLASH_SFC_DEV_ENABLE)
|
||||
|
||||
#undef LOG_TAG_CONST
|
||||
#define LOG_TAG "[FLASH_SFC]"
|
||||
#define LOG_ERROR_ENABLE
|
||||
#define LOG_INFO_ENABLE
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
|
||||
#define MAX_NORFLASH_PART_NUM 4
|
||||
|
||||
struct norflash_partition {
|
||||
const char *name;
|
||||
u32 start_addr;
|
||||
u32 size;
|
||||
struct device device;
|
||||
};
|
||||
|
||||
static struct norflash_partition nor_part[MAX_NORFLASH_PART_NUM];
|
||||
|
||||
struct norflash_info {
|
||||
u32 flash_id;
|
||||
u32 flash_capacity;
|
||||
int spi_num;
|
||||
u8 spi_cs_io;
|
||||
u8 spi_r_width;
|
||||
u8 part_num;
|
||||
u8 open_cnt;
|
||||
struct norflash_partition *const part_list;
|
||||
OS_MUTEX mutex;
|
||||
u32 max_end_addr;
|
||||
};
|
||||
|
||||
static struct norflash_info _norflash = {
|
||||
.spi_num = (int) - 1,
|
||||
.part_list = nor_part,
|
||||
};
|
||||
|
||||
static int _norflash_read(u32 addr, u8 *buf, u32 len, u8 cache);
|
||||
static int _norflash_eraser(u8 eraser, u32 addr);
|
||||
static void _norflash_cache_sync_timer(void *priv);
|
||||
static int _norflash_write_pages(u32 addr, u8 *buf, u32 len);
|
||||
|
||||
static struct norflash_partition *norflash_find_part(const char *name)
|
||||
{
|
||||
struct norflash_partition *part = NULL;
|
||||
u32 idx;
|
||||
for (idx = 0; idx < MAX_NORFLASH_PART_NUM; idx++) {
|
||||
part = &_norflash.part_list[idx];
|
||||
if (part->name == NULL) {
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(part->name, name)) {
|
||||
return part;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct norflash_partition *norflash_new_part(const char *name, u32 addr, u32 size)
|
||||
{
|
||||
struct norflash_partition *part;
|
||||
u32 idx;
|
||||
for (idx = 0; idx < MAX_NORFLASH_PART_NUM; idx++) {
|
||||
part = &_norflash.part_list[idx];
|
||||
if (part->name == NULL) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (part->name != NULL) {
|
||||
log_error("create norflash 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 > _norflash.max_end_addr) {
|
||||
_norflash.max_end_addr = part->start_addr + part->size;
|
||||
}
|
||||
_norflash.part_num++;
|
||||
return part;
|
||||
}
|
||||
|
||||
static void norflash_delete_part(const char *name)
|
||||
{
|
||||
struct norflash_partition *part;
|
||||
u32 idx;
|
||||
for (idx = 0; idx < MAX_NORFLASH_PART_NUM; idx++) {
|
||||
part = &_norflash.part_list[idx];
|
||||
if (part->name == NULL) {
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(part->name, name)) {
|
||||
part->name = NULL;
|
||||
_norflash.part_num--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int norflash_verify_part(struct norflash_partition *p)
|
||||
{
|
||||
struct norflash_partition *part = NULL;
|
||||
u32 idx;
|
||||
for (idx = 0; idx < MAX_NORFLASH_PART_NUM; idx++) {
|
||||
part = &_norflash.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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#define FLASH_CACHE_ENABLE 0
|
||||
|
||||
#if FLASH_CACHE_ENABLE
|
||||
static u32 flash_cache_addr;
|
||||
static u8 *flash_cache_buf = NULL; //缓存4K的数据,与flash里的数据一样。
|
||||
static u8 flash_cache_is_dirty;
|
||||
static u16 flash_cache_timer;
|
||||
|
||||
#define FLASH_CACHE_SYNC_T_INTERVAL 60
|
||||
|
||||
static int _check_0xff(u8 *buf, u32 len)
|
||||
{
|
||||
for (u32 i = 0; i < len; i ++) {
|
||||
if ((*(buf + i)) != 0xff) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static u32 _pow(u32 num, int n)
|
||||
{
|
||||
u32 powint = 1;
|
||||
int i;
|
||||
for (i = 1; i <= n; i++) {
|
||||
powint *= num;
|
||||
}
|
||||
return powint;
|
||||
}
|
||||
|
||||
static u32 _norflash_read_id()
|
||||
{
|
||||
u32 id = 0;
|
||||
espi_norflash_ioctl(IOCTL_GET_ID, (u32)&id);
|
||||
return id;
|
||||
}
|
||||
|
||||
static int _norflash_init(const char *name, struct norflash_sfc_dev_platform_data *pdata)
|
||||
{
|
||||
log_info("norflash_sfc_init >>>>");
|
||||
|
||||
struct norflash_partition *part;
|
||||
const struct sfc_spi_platform_data *sfc_spi_pdata;
|
||||
|
||||
if (_norflash.spi_num == (int) - 1) {
|
||||
_norflash.spi_num = 1;//pdata->spi_hw_num;
|
||||
_norflash.flash_id = 0;
|
||||
_norflash.flash_capacity = 0;
|
||||
os_mutex_create(&_norflash.mutex);
|
||||
_norflash.max_end_addr = 0;
|
||||
_norflash.part_num = 0;
|
||||
sfc_spi_pdata = pdata->sfc_spi_pdata;
|
||||
espi_norflash_init(sfc_spi_pdata->sfc_read_mode, sfc_spi_pdata->sfc_data_width);
|
||||
|
||||
}
|
||||
part = norflash_find_part(name);
|
||||
|
||||
if (!part) {
|
||||
part = norflash_new_part(name, pdata->start_addr, pdata->size);
|
||||
ASSERT(part, "not enough norflash partition memory in array\n");
|
||||
ASSERT(norflash_verify_part(part) == 0, "norflash partition %s overlaps\n", name);
|
||||
log_info("norflash new partition %s\n", part->name);
|
||||
} else {
|
||||
ASSERT(0, "norflash partition name already exists\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* static void clock_critical_enter() */
|
||||
/* { */
|
||||
/* */
|
||||
/* } */
|
||||
/* static void clock_critical_exit() */
|
||||
/* { */
|
||||
/* } */
|
||||
/* CLOCK_CRITICAL_HANDLE_REG(spi_norflash, clock_critical_enter, clock_critical_exit); */
|
||||
|
||||
static int _norflash_open(void *arg)
|
||||
{
|
||||
int reg = 0;
|
||||
os_mutex_pend(&_norflash.mutex, 0);
|
||||
log_info("norflash open\n");
|
||||
if (!_norflash.open_cnt) {
|
||||
|
||||
_norflash.flash_id = _norflash_read_id();
|
||||
|
||||
|
||||
log_info("norflash_read_id: 0x%x\n", _norflash.flash_id);
|
||||
if ((_norflash.flash_id == 0) || (_norflash.flash_id == 0xffffff)) {
|
||||
log_error("read norflash id error !\n");
|
||||
reg = -ENODEV;
|
||||
|
||||
|
||||
goto __exit;
|
||||
}
|
||||
|
||||
|
||||
espi_norflash_ioctl(IOCTL_GET_CAPACITY, (u32)&_norflash.flash_capacity);
|
||||
log_info("norflash_capacity: 0x%x\n", _norflash.flash_capacity);
|
||||
|
||||
#if FLASH_CACHE_ENABLE
|
||||
flash_cache_buf = (u8 *)malloc(4096);
|
||||
ASSERT(flash_cache_buf, "flash_cache_buf is not ok\n");
|
||||
flash_cache_addr = 4096;//先给一个大于4096的数
|
||||
_norflash_read(0, flash_cache_buf, 4096, 1);
|
||||
flash_cache_addr = 0;
|
||||
#endif
|
||||
log_info("norflash open success !\n");
|
||||
}
|
||||
if (_norflash.flash_id == 0 || _norflash.flash_id == 0xffffff) {
|
||||
log_error("re-open norflash id error !\n");
|
||||
reg = -EFAULT;
|
||||
goto __exit;
|
||||
}
|
||||
ASSERT(_norflash.max_end_addr <= _norflash.flash_capacity, "max partition end address is greater than flash capacity\n");
|
||||
_norflash.open_cnt++;
|
||||
|
||||
__exit:
|
||||
os_mutex_post(&_norflash.mutex);
|
||||
return reg;
|
||||
}
|
||||
|
||||
static int _norflash_close(void)
|
||||
{
|
||||
os_mutex_pend(&_norflash.mutex, 0);
|
||||
log_info("norflash close\n");
|
||||
if (_norflash.open_cnt) {
|
||||
_norflash.open_cnt--;
|
||||
}
|
||||
if (!_norflash.open_cnt) {
|
||||
#if FLASH_CACHE_ENABLE
|
||||
if (flash_cache_is_dirty) {
|
||||
flash_cache_is_dirty = 0;
|
||||
_norflash_eraser(IOCTL_ERASE_SECTOR, flash_cache_addr);
|
||||
_norflash_write_pages(flash_cache_addr, flash_cache_buf, 4096);
|
||||
}
|
||||
free(flash_cache_buf);
|
||||
flash_cache_buf = NULL;
|
||||
#endif
|
||||
log_info("norflash close done\n");
|
||||
}
|
||||
os_mutex_post(&_norflash.mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _norflash_read(u32 addr, u8 *buf, u32 len, u8 cache)
|
||||
{
|
||||
int ret = 0;
|
||||
ret = espi_norflash_read(buf, addr, len);
|
||||
/* ret = sfc_spi_read(addr, buf, len); */
|
||||
if (ret != len) {
|
||||
ret = -1;
|
||||
} else {
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int _norflash_write_pages(u32 addr, u8 *buf, u32 len)
|
||||
{
|
||||
/* y_printf("flash write addr = %d, num = %d\n", addr, len); */
|
||||
int ret = 0;
|
||||
/* ret = sfc_spi_write_pages(addr, buf, len); */
|
||||
ret = espi_norflash_write(buf, addr, len);
|
||||
if (ret != len) {
|
||||
ret = -1;
|
||||
} else {
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if FLASH_CACHE_ENABLE
|
||||
static void _norflash_cache_sync_timer(void *priv)
|
||||
{
|
||||
int reg;
|
||||
os_mutex_pend(&_norflash.mutex, 0);
|
||||
if (flash_cache_is_dirty) {
|
||||
flash_cache_is_dirty = 0;
|
||||
reg = _norflash_eraser(IOCTL_ERASE_SECTOR, flash_cache_addr);
|
||||
if (reg) {
|
||||
goto __exit;
|
||||
}
|
||||
_norflash_write_pages(flash_cache_addr, flash_cache_buf, 4096);
|
||||
}
|
||||
if (flash_cache_timer) {
|
||||
sys_timeout_del(flash_cache_timer);
|
||||
flash_cache_timer = 0;
|
||||
}
|
||||
__exit:
|
||||
os_mutex_post(&_norflash.mutex);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int _norflash_write(u32 addr, void *buf, u32 len, u8 cache)
|
||||
{
|
||||
int reg = 0;
|
||||
u8 *w_buf = (u8 *)buf;
|
||||
u32 w_len = len;
|
||||
/* y_printf("flash write addr = %d, num = %d\n", addr, len); */
|
||||
#if FLASH_CACHE_ENABLE
|
||||
os_mutex_pend(&_norflash.mutex, 0);
|
||||
if (!cache) {
|
||||
reg = _norflash_write_pages(addr, w_buf, w_len);
|
||||
goto __exit;
|
||||
}
|
||||
u32 align_addr = addr / 4096 * 4096;
|
||||
u32 align_len = 4096 - (addr - align_addr);
|
||||
align_len = w_len > align_len ? align_len : w_len;
|
||||
if (align_addr != flash_cache_addr) {
|
||||
if (flash_cache_is_dirty) {
|
||||
flash_cache_is_dirty = 0;
|
||||
reg = _norflash_eraser(IOCTL_ERASE_SECTOR, flash_cache_addr);
|
||||
if (reg) {
|
||||
line_inf;
|
||||
goto __exit;
|
||||
}
|
||||
reg = _norflash_write_pages(flash_cache_addr, flash_cache_buf, 4096);
|
||||
if (reg) {
|
||||
line_inf;
|
||||
goto __exit;
|
||||
}
|
||||
}
|
||||
_norflash_read(align_addr, flash_cache_buf, 4096, 0);
|
||||
flash_cache_addr = align_addr;
|
||||
}
|
||||
memcpy(flash_cache_buf + (addr - align_addr), w_buf, align_len);
|
||||
if ((addr + align_len) % 4096) {
|
||||
flash_cache_is_dirty = 1;
|
||||
if (flash_cache_timer) {
|
||||
sys_timer_re_run(flash_cache_timer);
|
||||
} else {
|
||||
flash_cache_timer = sys_timeout_add(0, _norflash_cache_sync_timer, FLASH_CACHE_SYNC_T_INTERVAL);
|
||||
}
|
||||
} else {
|
||||
flash_cache_is_dirty = 0;
|
||||
reg = _norflash_eraser(IOCTL_ERASE_SECTOR, align_addr);
|
||||
if (reg) {
|
||||
line_inf;
|
||||
goto __exit;
|
||||
}
|
||||
reg = _norflash_write_pages(align_addr, flash_cache_buf, 4096);
|
||||
if (reg) {
|
||||
line_inf;
|
||||
goto __exit;
|
||||
}
|
||||
}
|
||||
addr += align_len;
|
||||
w_buf += align_len;
|
||||
w_len -= align_len;
|
||||
while (w_len) {
|
||||
u32 cnt = w_len > 4096 ? 4096 : w_len;
|
||||
_norflash_read(addr, flash_cache_buf, 4096, 0);
|
||||
flash_cache_addr = addr;
|
||||
memcpy(flash_cache_buf, w_buf, cnt);
|
||||
if ((addr + cnt) % 4096) {
|
||||
flash_cache_is_dirty = 1;
|
||||
if (flash_cache_timer) {
|
||||
sys_timer_re_run(flash_cache_timer);
|
||||
} else {
|
||||
flash_cache_timer = sys_timeout_add(0, _norflash_cache_sync_timer, FLASH_CACHE_SYNC_T_INTERVAL);
|
||||
}
|
||||
} else {
|
||||
flash_cache_is_dirty = 0;
|
||||
reg = _norflash_eraser(IOCTL_ERASE_SECTOR, addr);
|
||||
if (reg) {
|
||||
line_inf;
|
||||
goto __exit;
|
||||
}
|
||||
reg = _norflash_write_pages(addr, flash_cache_buf, 4096);
|
||||
if (reg) {
|
||||
line_inf;
|
||||
goto __exit;
|
||||
}
|
||||
}
|
||||
addr += cnt;
|
||||
w_buf += cnt;
|
||||
w_len -= cnt;
|
||||
}
|
||||
|
||||
__exit:
|
||||
os_mutex_post(&_norflash.mutex);
|
||||
#else
|
||||
reg = _norflash_write_pages(addr, w_buf, w_len);
|
||||
#endif
|
||||
return reg;
|
||||
}
|
||||
|
||||
static int _norflash_eraser(u8 eraser, u32 addr)
|
||||
{
|
||||
int ret = 0;
|
||||
ret = espi_norflash_ioctl(eraser, addr);
|
||||
/* ret = sfc_spi_eraser(eraser, addr); */
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _norflash_ioctl(u32 cmd, u32 arg, u32 unit, void *_part)
|
||||
{
|
||||
int reg = 0;
|
||||
struct norflash_partition *part = _part;
|
||||
/* os_mutex_pend(&_norflash.mutex, 0); */
|
||||
switch (cmd) {
|
||||
case IOCTL_GET_STATUS:
|
||||
*(u32 *)arg = 1;
|
||||
break;
|
||||
case IOCTL_GET_ID:
|
||||
*((u32 *)arg) = _norflash.flash_id;
|
||||
break;
|
||||
case IOCTL_GET_CAPACITY:
|
||||
if (_norflash.flash_capacity == 0) {
|
||||
*(u32 *)arg = 0;
|
||||
} else if (_norflash.part_num == 1 && part->start_addr == 0) {
|
||||
*(u32 *)arg = _norflash.flash_capacity / unit;
|
||||
} else {
|
||||
*(u32 *)arg = part->size / unit;
|
||||
}
|
||||
break;
|
||||
case IOCTL_GET_BLOCK_SIZE:
|
||||
*(u32 *)arg = 512;
|
||||
break;
|
||||
case IOCTL_ERASE_PAGE:
|
||||
reg = _norflash_eraser(IOCTL_ERASE_PAGE, arg * unit + part->start_addr);
|
||||
break;
|
||||
case IOCTL_ERASE_SECTOR:
|
||||
reg = _norflash_eraser(IOCTL_ERASE_SECTOR, arg * unit + part->start_addr);
|
||||
break;
|
||||
case IOCTL_ERASE_BLOCK:
|
||||
reg = _norflash_eraser(IOCTL_ERASE_BLOCK, arg * unit + part->start_addr);
|
||||
break;
|
||||
case IOCTL_ERASE_CHIP:
|
||||
reg = _norflash_eraser(IOCTL_ERASE_CHIP, 0);
|
||||
break;
|
||||
case IOCTL_FLUSH:
|
||||
break;
|
||||
case IOCTL_CMD_RESUME:
|
||||
break;
|
||||
case IOCTL_CMD_SUSPEND:
|
||||
break;
|
||||
case IOCTL_GET_PART_INFO:
|
||||
u32 *info = (u32 *)arg;
|
||||
u32 *start_addr = &info[0];
|
||||
u32 *part_size = &info[1];
|
||||
*start_addr = part->start_addr;
|
||||
*part_size = part->size;
|
||||
break;
|
||||
default:
|
||||
reg = -EINVAL;
|
||||
break;
|
||||
}
|
||||
//__exit:
|
||||
/* os_mutex_post(&_norflash.mutex); */
|
||||
return reg;
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************************
|
||||
* 挂钩 device_api
|
||||
************************************************************************************/
|
||||
|
||||
static int norflash_sfc_dev_init(const struct dev_node *node, void *arg)
|
||||
{
|
||||
struct norflash_sfc_dev_platform_data *pdata = arg;
|
||||
|
||||
return _norflash_init(node->name, pdata);
|
||||
}
|
||||
|
||||
static int norflash_sfc_dev_open(const char *name, struct device **device, void *arg)
|
||||
{
|
||||
struct norflash_partition *part;
|
||||
part = norflash_find_part(name);
|
||||
if (!part) {
|
||||
log_error("no norflash partition is found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
*device = &part->device;
|
||||
(*device)->private_data = part;
|
||||
if (atomic_read(&part->device.ref)) {
|
||||
return 0;
|
||||
}
|
||||
return _norflash_open(arg);
|
||||
}
|
||||
static int norflash_sfc_dev_close(struct device *device)
|
||||
{
|
||||
return _norflash_close();
|
||||
}
|
||||
static int norflash_sfc_dev_read(struct device *device, void *buf, u32 len, u32 offset)
|
||||
{
|
||||
int reg;
|
||||
/* printf("flash read sector = %d, num = %d\n", offset, len); */
|
||||
offset = offset * 512;
|
||||
len = len * 512;
|
||||
struct norflash_partition *part;
|
||||
part = (struct norflash_partition *)device->private_data;
|
||||
if (!part) {
|
||||
log_error("norflash partition invalid\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
offset += part->start_addr;
|
||||
reg = _norflash_read(offset, buf, len, 1);
|
||||
if (reg) {
|
||||
r_printf(">>>[r error]:\n");
|
||||
len = 0;
|
||||
}
|
||||
|
||||
len = len / 512;
|
||||
return len;
|
||||
}
|
||||
static int norflash_sfc_dev_write(struct device *device, void *buf, u32 len, u32 offset)
|
||||
{
|
||||
/* printf("flash write sector = %d, num = %d\n", offset, len); */
|
||||
int reg = 0;
|
||||
offset = offset * 512;
|
||||
len = len * 512;
|
||||
struct norflash_partition *part = device->private_data;
|
||||
if (!part) {
|
||||
log_error("norflash partition invalid\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
offset += part->start_addr;
|
||||
reg = _norflash_write(offset, buf, len, 1);
|
||||
if (reg) {
|
||||
r_printf(">>>[w error]:\n");
|
||||
len = 0;
|
||||
}
|
||||
len = len / 512;
|
||||
return len;
|
||||
}
|
||||
static bool norflash_sfc_dev_online(const struct dev_node *node)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
static int norflash_sfc_dev_ioctl(struct device *device, u32 cmd, u32 arg)
|
||||
{
|
||||
struct norflash_partition *part = device->private_data;
|
||||
if (!part) {
|
||||
log_error("norflash partition invalid\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
return _norflash_ioctl(cmd, arg, 512, part);
|
||||
}
|
||||
|
||||
/*
|
||||
* 1. 外部调用时以512字节为单位的地址和长度,且需要驱动write自己处理擦除,
|
||||
* 请使用norflash_sfc_dev_ops接口,否则使用本文件内的其他ops
|
||||
*
|
||||
* 2. 如果不需要驱动自己处理擦除,可以把宏FLASH_CACHE_ENABLE清零,或者把
|
||||
* norflash_sfc_dev_read()里面调用的_norflash_read()的实参cache填0,
|
||||
* norflash_sfc_dev_write()同理
|
||||
*
|
||||
* 3. norflash_sfc_dev_ops可以被多个设备名注册,每个设备名被认为是不同分区,所以
|
||||
* 需要填不同的分区起始地址和大小,若分区地址有重叠或者最大分区结束地址大于
|
||||
* flash容量,会触发ASSERT()
|
||||
*
|
||||
* 4. 关于IOCTL_GET_CAPACITY,有多个分区注册时返回分区的大小,如果只注册了1
|
||||
* 个分区,分区起始地址 == 0时返回flash容量,起始地址 != 0时返回分区大小,
|
||||
* norflash_sfc_dev_ops返回的长度以512字节为单位
|
||||
*
|
||||
* 5. 本文件内的各个ops可以同时使用
|
||||
*/
|
||||
//按512字节单位读写
|
||||
const struct device_operations norflash_sfc_dev_ops = {
|
||||
.init = norflash_sfc_dev_init,
|
||||
.online = norflash_sfc_dev_online,
|
||||
.open = norflash_sfc_dev_open,
|
||||
.read = norflash_sfc_dev_read,
|
||||
.write = norflash_sfc_dev_write,
|
||||
.ioctl = norflash_sfc_dev_ioctl,
|
||||
.close = norflash_sfc_dev_close,
|
||||
};
|
||||
|
||||
|
||||
static int norflash_sfc_fs_dev_init(const struct dev_node *node, void *arg)
|
||||
{
|
||||
struct norflash_sfc_dev_platform_data *pdata = arg;
|
||||
|
||||
return _norflash_init(node->name, pdata);
|
||||
}
|
||||
|
||||
static int norflash_sfc_fs_dev_open(const char *name, struct device **device, void *arg)
|
||||
{
|
||||
struct norflash_partition *part;
|
||||
part = norflash_find_part(name);
|
||||
if (!part) {
|
||||
log_error("no norflash partition is found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
*device = &part->device;
|
||||
(*device)->private_data = part;
|
||||
if (atomic_read(&part->device.ref)) {
|
||||
return 0;
|
||||
}
|
||||
return _norflash_open(arg);
|
||||
}
|
||||
static int norflash_sfc_fs_dev_close(struct device *device)
|
||||
{
|
||||
return _norflash_close();
|
||||
}
|
||||
static int norflash_sfc_fs_dev_read(struct device *device, void *buf, u32 len, u32 offset)
|
||||
{
|
||||
int reg;
|
||||
/* printf("flash read sector = %d, num = %d\n", offset, len); */
|
||||
struct norflash_partition *part;
|
||||
part = (struct norflash_partition *)device->private_data;
|
||||
if (!part) {
|
||||
log_error("norflash partition invalid\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
offset += part->start_addr;
|
||||
reg = _norflash_read(offset, buf, len, 0);
|
||||
if (reg) {
|
||||
r_printf(">>>[r error]:\n");
|
||||
len = 0;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int norflash_sfc_fs_dev_write(struct device *device, void *buf, u32 len, u32 offset)
|
||||
{
|
||||
//printf("flash write addr = 0x%x, len = 0x%x\n", offset, len);
|
||||
int reg = 0;
|
||||
struct norflash_partition *part = device->private_data;
|
||||
if (!part) {
|
||||
log_error("norflash partition invalid\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
offset += part->start_addr;
|
||||
reg = _norflash_write(offset, buf, len, 0);
|
||||
if (reg) {
|
||||
r_printf(">>>[w error]:\n");
|
||||
len = 0;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
static bool norflash_sfc_fs_dev_online(const struct dev_node *node)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int norflash_sfc_fs_dev_ioctl(struct device *device, u32 cmd, u32 arg)
|
||||
{
|
||||
struct norflash_partition *part = device->private_data;
|
||||
if (!part) {
|
||||
log_error("norflash partition invalid\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
return _norflash_ioctl(cmd, arg, 1, part);
|
||||
}
|
||||
|
||||
/*
|
||||
* 1. 外部调用时以1字节为单位的地址和长度,且驱动write自己不处理擦除,
|
||||
* 请使用norflash_sfc_fs_dev_ops接口,否则使用本文件内的其他ops。注意:有些文件系
|
||||
* 统需要满足这个条件的驱动,如果期望修改成驱动内部处理擦除,需要测试所
|
||||
* 有关联的文件系统是否支持,或者新建一个符合需求的ops
|
||||
*
|
||||
* 2. 如果需要驱动自己处理擦除,需要把宏FLASH_CACHE_ENABLE置1,且
|
||||
* norflash_sfc_fs_dev_read()里面调用的_norflash_read()的实参cache填1,
|
||||
* norflash_sfc_fs_dev_write()同理
|
||||
*
|
||||
* 3. norflash_sfc_fs_dev_ops可以被多个设备名注册,每个设备名被认为是不同分区,所以
|
||||
* 需要填不同的分区起始地址和大小,若分区地址有重叠或者最大分区结束地址大于
|
||||
* flash容量,会触发ASSERT()
|
||||
*
|
||||
* 4. 关于IOCTL_GET_CAPACITY,有多个分区注册时返回分区的大小,如果只注册了1
|
||||
* 个分区,分区起始地址 == 0时返回flash容量,起始地址 != 0时返回分区大小
|
||||
* norflash_sfc_fs_dev_ops返回的长度以1字节为单位
|
||||
*
|
||||
* 5. 本文件内的各个ops可以同时使用
|
||||
*/
|
||||
//按1字节单位读写
|
||||
const struct device_operations norflash_sfc_fs_dev_ops = {
|
||||
.init = norflash_sfc_fs_dev_init,
|
||||
.online = norflash_sfc_fs_dev_online,
|
||||
.open = norflash_sfc_fs_dev_open,
|
||||
.read = norflash_sfc_fs_dev_read,
|
||||
.write = norflash_sfc_fs_dev_write,
|
||||
.ioctl = norflash_sfc_fs_dev_ioctl,
|
||||
.close = norflash_sfc_fs_dev_close,
|
||||
};
|
||||
|
||||
;
|
||||
|
||||
|
||||
|
||||
#endif /* #if (defined(TCFG_NORFLASH_SFC_DEV_ENABLE) && TCFG_NORFLASH_SFC_DEV_ENABLE) */
|
||||
Reference in New Issue
Block a user