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

913 lines
26 KiB
C
Raw Permalink 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 "norflash.h"
#include "app_config.h"
#include "asm/clock.h"
#include "system/timer.h"
#if defined(TCFG_NORFLASH_DEV_ENABLE) && TCFG_NORFLASH_DEV_ENABLE
#undef LOG_TAG_CONST
#define LOG_TAG "[FLASH]"
#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;
int spi_err;
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,
};
int _norflash_read(u32 addr, u8 *buf, u32 len, u8 cache);
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);
#define spi_cs_init() \
do { \
gpio_set_die(_norflash.spi_cs_io, 1); \
gpio_set_direction(_norflash.spi_cs_io, 0); \
gpio_write(_norflash.spi_cs_io, 1); \
} while (0)
#define spi_cs_uninit() \
do { \
gpio_set_die(_norflash.spi_cs_io, 0); \
gpio_set_direction(_norflash.spi_cs_io, 1); \
gpio_set_pull_up(_norflash.spi_cs_io, 0); \
gpio_set_pull_down(_norflash.spi_cs_io, 0); \
} while (0)
#define spi_cs_h() gpio_write(_norflash.spi_cs_io, 1)
#define spi_cs_l() gpio_write(_norflash.spi_cs_io, 0)
#define spi_read_byte() spi_recv_byte(_norflash.spi_num, &_norflash.spi_err)
#define spi_write_byte(x) spi_send_byte(_norflash.spi_num, x)
#define spi_dma_read(x, y) spi_dma_recv(_norflash.spi_num, x, y)
#define spi_dma_write(x, y) spi_dma_send(_norflash.spi_num, x, y)
#define spi_set_width(x) spi_set_bit_mode(_norflash.spi_num, x)
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 1
#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 int _norflash_wait_ok()
{
u32 timeout = 8 * 1000 * 1000 / 100;
while (timeout--) {
spi_cs_l();
spi_write_byte(WINBOND_READ_SR1);
u8 reg_1 = spi_read_byte();
spi_cs_h();
if (!(reg_1 & BIT(0))) {
break;
}
delay(100);
}
if (timeout == 0) {
log_error("norflash_wait_ok timeout!\r\n");
return 1;
}
return 0;
}
static void _norflash_send_write_enable()
{
spi_cs_l();
spi_write_byte(WINBOND_WRITE_ENABLE);
spi_cs_h();
}
static u8 is4byte_mode;
static void _norflash_send_addr(u32 addr)
{
if (is4byte_mode) {
spi_write_byte(addr >> 24);
}
spi_write_byte(addr >> 16);
spi_write_byte(addr >> 8);
spi_write_byte(addr);
}
static u32 _norflash_read_id()
{
u8 id[3];
spi_cs_l();
spi_write_byte(WINBOND_JEDEC_ID);
for (u8 i = 0; i < sizeof(id); i++) {
id[i] = spi_read_byte();
}
spi_cs_h();
return id[0] << 16 | id[1] << 8 | id[2];
}
int _norflash_init(const char *name, struct norflash_dev_platform_data *pdata)
{
log_info("norflash_init ! %x %x", pdata->spi_cs_port, pdata->spi_read_width);
if (_norflash.spi_num == (int) - 1) {
_norflash.spi_num = pdata->spi_hw_num;
_norflash.spi_cs_io = pdata->spi_cs_port;
_norflash.spi_r_width = pdata->spi_read_width;
_norflash.flash_id = 0;
_norflash.flash_capacity = 0;
os_mutex_create(&_norflash.mutex);
_norflash.max_end_addr = 0;
_norflash.part_num = 0;
}
ASSERT(_norflash.spi_num == pdata->spi_hw_num);
ASSERT(_norflash.spi_cs_io == pdata->spi_cs_port);
ASSERT(_norflash.spi_r_width == pdata->spi_read_width);
struct norflash_partition *part;
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()
{
if (!(_norflash.flash_id == 0 || _norflash.flash_id == 0xffff)) {
spi_set_baud(_norflash.spi_num, spi_get_baud(_norflash.spi_num));
}
}
CLOCK_CRITICAL_HANDLE_REG(spi_norflash, clock_critical_enter, clock_critical_exit);
void norflash_enter_4byte_addr()
{
spi_cs_l();
spi_write_byte(0xb7);
spi_cs_h();
}
void norflash_exit_4byte_addr()
{
spi_cs_l();
spi_write_byte(0xe9);
spi_cs_h();
}
int _norflash_open(void *arg)
{
int reg = 0;
os_mutex_pend(&_norflash.mutex, 0);
log_info("norflash open\n");
if (!_norflash.open_cnt) {
spi_cs_init();
spi_open(_norflash.spi_num);
_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;
}
_norflash.flash_capacity = 64 * _pow(2, (_norflash.flash_id & 0xff) - 0x10) * 1024;
log_info("norflash_capacity: 0x%x\n", _norflash.flash_capacity);
is4byte_mode = 0;
if (_norflash.flash_capacity > 16 * 1024 * 1024) {
norflash_enter_4byte_addr();
is4byte_mode = 1;
}
#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;
}
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(FLASH_SECTOR_ERASER, flash_cache_addr);
_norflash_write_pages(flash_cache_addr, flash_cache_buf, 4096);
}
free(flash_cache_buf);
flash_cache_buf = NULL;
#endif
spi_close(_norflash.spi_num);
spi_cs_uninit();
log_info("norflash close done\n");
}
os_mutex_post(&_norflash.mutex);
return 0;
}
int _norflash_read(u32 addr, u8 *buf, u32 len, u8 cache)
{
int reg = 0;
u32 align_addr;
os_mutex_pend(&_norflash.mutex, 0);
/* y_printf("flash read addr = %d, len = %d\n", addr, len); */
#if FLASH_CACHE_ENABLE
if (!cache) {
goto __no_cache1;
}
u32 r_len = 4096 - (addr % 4096);
if ((addr >= flash_cache_addr) && (addr < (flash_cache_addr + 4096))) {
if (len <= r_len) {
memcpy(buf, flash_cache_buf + (addr - flash_cache_addr), len);
goto __exit;
} else {
memcpy(buf, flash_cache_buf + (addr - flash_cache_addr), r_len);
addr += r_len;
buf += r_len;
len -= r_len;
}
}
__no_cache1:
#endif
spi_cs_l();
if (_norflash.spi_r_width == 2) {
spi_write_byte(WINBOND_FAST_READ_DUAL_OUTPUT);
_norflash_send_addr(addr);
spi_write_byte(0);
spi_set_width(SPI_MODE_UNIDIR_2BIT);
spi_dma_read(buf, len);
spi_set_width(SPI_MODE_BIDIR_1BIT);
} else if (_norflash.spi_r_width == 4) {
spi_write_byte(0x6b);
_norflash_send_addr(addr);
spi_write_byte(0);
spi_set_width(SPI_MODE_UNIDIR_4BIT);
spi_dma_read(buf, len);
spi_set_width(SPI_MODE_BIDIR_1BIT);
} else {
spi_write_byte(WINBOND_FAST_READ_DATA);
_norflash_send_addr(addr);
spi_write_byte(0);
spi_dma_read(buf, len);
}
spi_cs_h();
//#if FLASH_CACHE_ENABLE
// if (!cache) {
// goto __no_cache2;
// }
// align_addr = (addr + len) / 4096 * 4096;
// if ((int)len - (int)((addr + len) - align_addr) >= 4096) {
// align_addr -= 4096;
// if (flash_cache_addr != align_addr) {
// flash_cache_addr = align_addr;
// memcpy(flash_cache_buf, buf + (align_addr - addr), 4096);
// }
// }
//__no_cache2:
//#endif
__exit:
os_mutex_post(&_norflash.mutex);
return reg;
}
static int _norflash_write_pages(u32 addr, u8 *buf, u32 len)
{
/* y_printf("flash write addr = %d, num = %d\n", addr, len); */
int reg;
u32 first_page_len = 256 - (addr % 256);
first_page_len = len > first_page_len ? first_page_len : len;
_norflash_send_write_enable();
spi_cs_l();
spi_write_byte(WINBOND_PAGE_PROGRAM);
_norflash_send_addr(addr) ;
spi_dma_write(buf, first_page_len);
spi_cs_h();
reg = _norflash_wait_ok();
if (reg) {
return 1;
}
addr += first_page_len;
buf += first_page_len;
len -= first_page_len;
while (len) {
u32 cnt = len > 256 ? 256 : len;
_norflash_send_write_enable();
spi_cs_l();
spi_write_byte(WINBOND_PAGE_PROGRAM);
_norflash_send_addr(addr) ;
spi_dma_write(buf, cnt);
spi_cs_h();
reg = _norflash_wait_ok();
if (reg) {
return 1;
}
addr += cnt;
buf += cnt;
len -= cnt;
}
return 0;
}
#if FLASH_CACHE_ENABLE
static void _norflash_cache_sync_timer(void *priv)
{
int reg = 0;
os_mutex_pend(&_norflash.mutex, 0);
if (flash_cache_is_dirty) {
flash_cache_is_dirty = 0;
reg = _norflash_eraser(FLASH_SECTOR_ERASER, flash_cache_addr);
if (reg) {
goto __exit;
}
reg = _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
int _norflash_write(u32 addr, void *buf, u32 len, u8 cache)
{
int reg = 0;
os_mutex_pend(&_norflash.mutex, 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
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(FLASH_SECTOR_ERASER, flash_cache_addr);
if (reg) {
goto __exit;
}
reg = _norflash_write_pages(flash_cache_addr, flash_cache_buf, 4096);
if (reg) {
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(FLASH_SECTOR_ERASER, align_addr);
if (reg) {
goto __exit;
}
reg = _norflash_write_pages(align_addr, flash_cache_buf, 4096);
if (reg) {
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(FLASH_SECTOR_ERASER, addr);
if (reg) {
goto __exit;
}
reg = _norflash_write_pages(addr, flash_cache_buf, 4096);
if (reg) {
goto __exit;
}
}
addr += cnt;
w_buf += cnt;
w_len -= cnt;
}
#else
reg = _norflash_write_pages(addr, w_buf, w_len);
#endif
__exit:
os_mutex_post(&_norflash.mutex);
return reg;
}
int _norflash_eraser(u8 eraser, u32 addr)
{
u8 eraser_cmd;
switch (eraser) {
case FLASH_PAGE_ERASER:
eraser_cmd = WINBOND_PAGE_ERASE;
addr = addr / 256 * 256;
break;
case FLASH_SECTOR_ERASER:
eraser_cmd = WINBOND_SECTOR_ERASE;
//r_printf(">>>[test]:addr = %d\n", addr);
addr = addr / 4096 * 4096;
break;
case FLASH_BLOCK_ERASER:
eraser_cmd = WINBOND_BLOCK_ERASE;
addr = addr / 65536 * 65536;
break;
case FLASH_CHIP_ERASER:
eraser_cmd = WINBOND_CHIP_ERASE;
break;
}
_norflash_send_write_enable();
spi_cs_l();
spi_write_byte(eraser_cmd);
if (eraser_cmd != WINBOND_CHIP_ERASE) {
_norflash_send_addr(addr);
}
spi_cs_h();
return _norflash_wait_ok();
}
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(FLASH_PAGE_ERASER, arg * unit + part->start_addr);
break;
case IOCTL_ERASE_SECTOR:
reg = _norflash_eraser(FLASH_SECTOR_ERASER, arg * unit + part->start_addr);
break;
case IOCTL_ERASE_BLOCK:
reg = _norflash_eraser(FLASH_BLOCK_ERASER, arg * unit + part->start_addr);
break;
case IOCTL_ERASE_CHIP:
reg = _norflash_eraser(FLASH_CHIP_ERASER, 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_dev_init(const struct dev_node *node, void *arg)
{
struct norflash_dev_platform_data *pdata = arg;
return _norflash_init(node->name, pdata);
}
static int norflash_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_dev_close(struct device *device)
{
return _norflash_close();
}
static int norflash_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_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_dev_online(const struct dev_node *node)
{
return 1;
}
static int norflash_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);
}
#if 0 //下面这组接口不再使用
int hook_norflash_init(struct norflash_dev_platform_data *pdata)
{
return _norflash_init("xxxx", pdata);
}
int hook_norflash_open(void)
{
return _norflash_open(NULL);
}
int hook_norflash_spirec_read(u8 *buf, u32 addr, u32 len)
{
/* return _norflash_read(addr, buf, len); */
return _norflash_read(addr, buf, len, 0);
}
int hook_norflash_spirec_write(u8 *buf, u32 addr, u32 len)
{
return _norflash_write_pages(addr, buf, len);
}
void hook_norflash_spirec_eraser(u32 addr)
{
_norflash_eraser(FLASH_SECTOR_ERASER, addr);
}
#endif
/*
* 1. 外部调用时以512字节为单位的地址和长度且需要驱动write自己处理擦除
* 请使用norflash_dev_ops接口否则使用本文件内的其他ops
*
* 2. 如果不需要驱动自己处理擦除可以把宏FLASH_CACHE_ENABLE清零或者把
* norflash_dev_read()里面调用的_norflash_read()的实参cache填0
* norflash_dev_write()同理
*
* 3. norflash_dev_ops可以被多个设备名注册每个设备名被认为是不同分区所以
* 需要填不同的分区起始地址和大小,若分区地址有重叠或者最大分区结束地址大于
* flash容量会触发ASSERT()
*
* 4. 关于IOCTL_GET_CAPACITY有多个分区注册时返回分区的大小如果只注册了1
* 个分区,分区起始地址 == 0时返回flash容量起始地址 != 0时返回分区大小
* norflash_dev_ops返回的长度以512字节为单位
*
* 5. 本文件内的各个ops可以同时使用
*/
const struct device_operations norflash_dev_ops = {
.init = norflash_dev_init,
.online = norflash_dev_online,
.open = norflash_dev_open,
.read = norflash_dev_read,
.write = norflash_dev_write,
.ioctl = norflash_dev_ioctl,
.close = norflash_dev_close,
};
static int norfs_dev_init(const struct dev_node *node, void *arg)
{
struct norflash_dev_platform_data *pdata = arg;
return _norflash_init(node->name, pdata);
}
static int norfs_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 norfs_dev_close(struct device *device)
{
return _norflash_close();
}
static int norfs_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 norfs_dev_write(struct device *device, void *buf, u32 len, u32 offset)
{
/* printf("flash write sector = %d, num = %d\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 norfs_dev_online(const struct dev_node *node)
{
return 1;
}
static int norfs_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自己不处理擦除
* 请使用norfs_dev_ops接口否则使用本文件内的其他ops。注意有些文件系
* 统需要满足这个条件的驱动,如果期望修改成驱动内部处理擦除,需要测试所
* 有关联的文件系统是否支持或者新建一个符合需求的ops
*
* 2. 如果需要驱动自己处理擦除需要把宏FLASH_CACHE_ENABLE置1
* norfs_dev_read()里面调用的_norflash_read()的实参cache填1
* norfs_dev_write()同理
*
* 3. norfs_dev_ops可以被多个设备名注册每个设备名被认为是不同分区所以
* 需要填不同的分区起始地址和大小,若分区地址有重叠或者最大分区结束地址大于
* flash容量会触发ASSERT()
*
* 4. 关于IOCTL_GET_CAPACITY有多个分区注册时返回分区的大小如果只注册了1
* 个分区,分区起始地址 == 0时返回flash容量起始地址 != 0时返回分区大小
* norfs_dev_ops返回的长度以1字节为单位
*
* 5. 本文件内的各个ops可以同时使用
*/
const struct device_operations norfs_dev_ops = {
.init = norfs_dev_init,
.online = norfs_dev_online,
.open = norfs_dev_open,
.read = norfs_dev_read,
.write = norfs_dev_write,
.ioctl = norfs_dev_ioctl,
.close = norfs_dev_close,
};
/*
* 对ops的读写单位有另外需求或者驱动内部是否支持擦除可以参照上面的ops
* 不同条件自由组合建立新的ops
*/
#endif