/** * @file disk_norflash.c * * Copyright (c) 2021 Semidrive Semiconductor. * All rights reserved. * * Description: * * Revision History: * ----------------- */ #include #include #include #include #include #include #include static inline int norflash_dev_read(struct disk_norflash_info *norflash_info, disk_addr_t addr, uint8_t *buf, disk_size_t length) { if (norflash_info->config.norflash_read) return norflash_info->config.norflash_read( norflash_info->config.norflash_dev, addr, buf, length); else return -DISK_ERROR_READ_ACCESS; } static inline int norflash_dev_write(struct disk_norflash_info *norflash_info, disk_addr_t addr, const uint8_t *buf, disk_size_t length) { if (norflash_info->config.norflash_write) return norflash_info->config.norflash_write( norflash_info->config.norflash_dev, addr, buf, length); else return -DISK_ERROR_WRITE_ACCESS; } static inline int norflash_dev_erase(struct disk_norflash_info *norflash_info, disk_addr_t addr, disk_size_t length) { if (norflash_info->config.norflash_erase) return norflash_info->config.norflash_erase( norflash_info->config.norflash_dev, addr, length); else return -DISK_ERROR_ERASE_ACCESS; } static int disk_norflash_read(struct disk_info *info, uint8_t *dst, disk_addr_t addr, disk_size_t size) { int ret = 0; struct disk_norflash_info *norflash_info = info->private_data; ssdk_printf(SSDK_DEBUG, "norflash disk read:%s mem:%p addr:%lld size:%lld\n", info->disk_name, dst, addr, size); disk_size_t tmp_len = 0; while (size) { tmp_len = size; if (!IS_ALIGNED(dst, CONFIG_ARCH_CACHE_LINE)) { tmp_len = MIN(ROUNDUP((addr_t)dst, CONFIG_ARCH_CACHE_LINE) - (addr_t)dst, size); } tmp_len = tmp_len > CONFIG_ARCH_CACHE_LINE ? (tmp_len - tmp_len % CONFIG_ARCH_CACHE_LINE) : tmp_len; if (tmp_len < CONFIG_ARCH_CACHE_LINE) { xSemaphoreTake(norflash_info->disk_norflash_mutex, portMAX_DELAY); if (norflash_dev_read(norflash_info, addr, norflash_info->buff_sector, ROUNDUP(tmp_len, 4))) { ret = -1; goto error; } memcpy(dst, norflash_info->buff_sector, tmp_len); xSemaphoreGive(norflash_info->disk_norflash_mutex); } else { if (norflash_dev_read(norflash_info, addr, dst, tmp_len)) { return -1; } } addr += tmp_len; size -= tmp_len; dst += tmp_len; } return 0; error: xSemaphoreGive(norflash_info->disk_norflash_mutex); return ret; } #if CONFIG_DISK_NOR_FLASH_BYTES_API_SUPPORT static int disk_norflash_write(struct disk_info *info, const uint8_t *src, disk_addr_t addr, disk_size_t size) { int ret = 0; struct disk_norflash_info *norflash_info = info->private_data; ssdk_printf(SSDK_DEBUG, "norflash disk write:%s mem:%p addr:%lld size:%lld\n", info->disk_name, src, addr, size); uint32_t sector_size = norflash_info->config.sector_size; uint32_t tmp_len; uint8_t *buf; while (size) { buf = norflash_info->buff_sector; tmp_len = size; if (!IS_ALIGNED(addr, sector_size)) { tmp_len = MIN(ROUNDUP(addr, sector_size) - addr, size); } else if (IS_ALIGNED(src, CONFIG_ARCH_CACHE_LINE) && size >= sector_size) { buf = (uint8_t *)src; } tmp_len = tmp_len > sector_size ? (tmp_len - tmp_len % sector_size) : tmp_len; if (src == buf) { if (norflash_dev_erase(norflash_info, addr, tmp_len)) { return -1; } if (norflash_dev_write(norflash_info, addr, buf, tmp_len)) { return -1; } } else { if (tmp_len < sector_size) { xSemaphoreTake(norflash_info->disk_norflash_mutex, portMAX_DELAY); if (norflash_dev_read(norflash_info, ROUNDDOWN(addr, sector_size), buf, sector_size)) { ret = -1; goto error; } if (norflash_dev_erase(norflash_info, ROUNDDOWN(addr, sector_size), sector_size)) { return -1; } } else { if (norflash_dev_erase(norflash_info, addr, tmp_len)) { return -1; } xSemaphoreTake(norflash_info->disk_norflash_mutex, portMAX_DELAY); } for (disk_size_t size_tmp = 0; size_tmp < ROUNDUP(tmp_len, sector_size); size_tmp += sector_size) { memcpy(buf + ((addr + size_tmp) % sector_size), src, MIN(sector_size, tmp_len)); if (norflash_dev_write(norflash_info, ROUNDDOWN(addr + size_tmp, sector_size), buf, sector_size)) { ret = -1; goto error; } } xSemaphoreGive(norflash_info->disk_norflash_mutex); } addr += tmp_len; src += tmp_len; size -= tmp_len; } return 0; error: xSemaphoreGive(norflash_info->disk_norflash_mutex); return ret; } #endif static int disk_norflash_read_block(struct disk_info *info, uint8_t *dst, block_t block, block_count_t read_count, block_size_t blk_sz) { ssdk_printf(SSDK_DEBUG, "disk norflash read block:%s mem:%p block:%lld" "read_count:%d blk_sz:%d addr:%lld size:%d\n", info->disk_name, dst, block, read_count, blk_sz, block * blk_sz, read_count * blk_sz); return disk_norflash_read(info, dst, (disk_addr_t)block * blk_sz, (disk_size_t)read_count * blk_sz); } static int disk_norflash_write_block(struct disk_info *info, const uint8_t *src, block_t block, block_count_t write_count, block_size_t blk_sz) { disk_size_t write_len; disk_size_t remain_len = write_count * blk_sz; disk_addr_t addr = block * blk_sz; struct disk_norflash_info *norflash_info = info->private_data; ssdk_printf(SSDK_DEBUG, "disk norflash write block:%s mem:%p block:%lld" "write_count:%d blk_sz:%d addr:%lld size:%d\n", info->disk_name, src, block, write_count, blk_sz, block * blk_sz, write_count * blk_sz); while (remain_len) { write_len = remain_len; if (!IS_ALIGNED(src, CONFIG_ARCH_CACHE_LINE)) { write_len = MIN(ROUNDUP((addr_t)src, CONFIG_ARCH_CACHE_LINE) - (addr_t)src, write_len); } write_len = write_len > CONFIG_ARCH_CACHE_LINE ? ROUNDDOWN(write_len, CONFIG_ARCH_CACHE_LINE) : write_len; if (write_len < CONFIG_ARCH_CACHE_LINE) { xSemaphoreTake(norflash_info->disk_norflash_mutex, portMAX_DELAY); memcpy(norflash_info->buff_sector, src, write_len); if (norflash_dev_write(norflash_info, addr, norflash_info->buff_sector, ROUNDUP(write_len, 4))) { xSemaphoreGive(norflash_info->disk_norflash_mutex); return -1; } xSemaphoreGive(norflash_info->disk_norflash_mutex); } else { if (norflash_dev_write(norflash_info, addr, src, write_len)) { return -1; } } addr += write_len; src += write_len; remain_len -= write_len; } return 0; } #if CONFIG_DISK_NOR_FLASH_BYTES_API_SUPPORT static int disk_norflash_erase(struct disk_info *info, disk_addr_t addr, disk_size_t size, uint32_t flag) { int ret = 0; struct disk_norflash_info *norflash_info = info->private_data; ssdk_printf(SSDK_DEBUG, "norflash disk %s erase,addr:%lld size:%lld:" "flag:%d\n", info->disk_name, addr, size, flag); uint32_t sector_size = norflash_info->config.sector_size; uint32_t tmp_len; uint8_t *buf; while (size) { buf = norflash_info->buff_sector; tmp_len = size; if (!IS_ALIGNED(addr, sector_size)) { tmp_len = MIN(ROUNDUP(addr, sector_size) - addr, size); } tmp_len = tmp_len > sector_size ? (tmp_len - tmp_len % sector_size) : tmp_len; if (tmp_len < sector_size) { xSemaphoreTake(norflash_info->disk_norflash_mutex, portMAX_DELAY); if (norflash_dev_read(norflash_info, ROUNDDOWN(addr, sector_size), buf, sector_size)) { ret = -1; goto error; } if (norflash_dev_erase(norflash_info, ROUNDDOWN(addr, sector_size), sector_size) < 0) { ret = -1; goto error; } memset(buf + addr % sector_size, 0xff, tmp_len); if (norflash_dev_write(norflash_info, ROUNDDOWN(addr, sector_size), buf, sector_size)) { ret = -1; goto error; } xSemaphoreGive(norflash_info->disk_norflash_mutex); } else { if (norflash_dev_erase(norflash_info, addr, tmp_len)) { return -1; } } addr += tmp_len; size -= tmp_len; } return 0; error: xSemaphoreGive(norflash_info->disk_norflash_mutex); return ret; } #endif static int disk_norflash_erase_group(struct disk_info *info, uint32_t erase_block, uint32_t erase_count, uint32_t erase_sz, uint32_t flag) { return norflash_dev_erase(info->private_data, (disk_addr_t)erase_block * erase_sz, (disk_size_t)erase_count * erase_sz); } static int disk_norflash_status(struct disk_info *info) { return DISK_STATUS_OK; } static int disk_norflash_ioctl(struct disk_info *info, uint32_t cmd, void *buff) { return 0; } static struct disk_operations disk_norflash_ops = { #if CONFIG_DISK_NOR_FLASH_BYTES_API_SUPPORT .disk_read = disk_norflash_read, .disk_write = disk_norflash_write, .disk_erase = disk_norflash_erase, #else .disk_read = NULL, .disk_write = NULL, .disk_erase = NULL, #endif .disk_read_block = disk_norflash_read_block, .disk_write_block = disk_norflash_write_block, .disk_erase_group = disk_norflash_erase_group, .disk_status = disk_norflash_status, .disk_ioctl = disk_norflash_ioctl, }; int register_norflash_disk(struct disk_norflash_info *norflash_info) { int ret = 0; struct disk_info *info = &norflash_info->info; struct disk_norflash_config *norflash_config = &norflash_info->config; #ifdef CONFIG_DISK_NOR_FLASH_BYTES_API_SUPPORT norflash_info->buff_size = norflash_config->sector_size; #else norflash_info->buff_size = CONFIG_ARCH_CACHE_LINE; #endif norflash_info->buff_sector = (uint8_t *)pvPortMallocAligned( norflash_info->buff_size, CONFIG_ARCH_CACHE_LINE); if (NULL == norflash_info->buff_sector) { ssdk_printf(SSDK_ERR, "norflash disk %s buff malloc error\n", norflash_config->disk_name); return -1; } norflash_info->disk_norflash_mutex = xSemaphoreCreateMutex(); /* base on config init info */ info->disk_name = norflash_config->disk_name; info->disk_size = norflash_config->disk_size; info->disk_offset = 0; info->erase_size = norflash_config->sector_size; info->mem_align_size = CONFIG_ARCH_CACHE_LINE; info->block_align_size = 4; info->private_data = norflash_info; info->disk_ops = &disk_norflash_ops; #ifdef CONFIG_DISK_CACHE_NOOP struct disk_cache_noop *cache_noop = &norflash_info->cache_noop; /* base on config cache,cache in disk dev layer??? */ cache_noop->cache_align_size = 4u * 1024u; cache_noop->cache.private_data = cache_noop; ret = register_disk_cache_noop(cache_noop); if (ret < 0) { ssdk_printf(SSDK_ERR, "norflash disk register cache noop:%d\n", ret); return ret; } #endif ssdk_printf(SSDK_INFO, "register norflash disk:%s disk_size:%lld" "erase_size:%d sector_size:%d\n", info->disk_name, info->disk_size, info->erase_size, norflash_config->sector_size); xSemaphoreTake(g_disk_mutex, portMAX_DELAY); list_add_tail(&g_disk_list, &info->node); info->use_count = 0; xSemaphoreGive(g_disk_mutex); return ret; } int unregister_norflash_disk(struct disk_norflash_info *norflash_info) { int ret = 0; struct disk_info *info = &norflash_info->info; ssdk_printf(SSDK_INFO, "unregister norflash disk:%s\n", info->disk_name); #ifdef CONFIG_DISK_CACHE_NOOP struct disk_cache_noop *cache_noop = &norflash_info->cache_noop; /* unregister disk cache noop */ ret = unregister_disk_cache_noop(cache_noop); if (ret < 0) { ssdk_printf(SSDK_ERR, "norflash disk unregister cache noop:%d\n", ret); return ret; } #endif vPortFree(norflash_info->buff_sector); vSemaphoreDelete(norflash_info->disk_norflash_mutex); xSemaphoreTake(g_disk_mutex, portMAX_DELAY); if (info->use_count) { xSemaphoreGive(g_disk_mutex); ret = -DISK_ERROR_BUSY; ssdk_printf(SSDK_ERR, "norflash disk unregister:%d\n", ret); return ret; } if (list_in_list(&info->node)) list_delete(&info->node); xSemaphoreGive(g_disk_mutex); return ret; }