#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