/** * @file disk_mmm.c * * Copyright (c) 2021 Semidrive Semiconductor. * All rights reserved. * * Description: * * Revision History: * ----------------- */ #include #include #include #include #include #include #include /* Maximum value of a single transmission */ #define MMC_MAX_XFER_SIZE (0x1000000) /* mmc erase value */ #define MMC_DEFAULT_FLAG(flag) \ ((flag == DISK_ERASE_DEFAULT) || (flag == DISK_ERASE_ZERO)) /* MMC physical layer API */ static int mmc_dev_read(struct disk_mmc_info *mmc_info, uint8_t *dst, block_t sector, block_count_t count) { if (mmc_info->config.mmc_read) return mmc_info->config.mmc_read(mmc_info->config.mmc_dev, dst, sector, count); else return -DISK_ERROR_READ_ACCESS; } static int mmc_dev_write(struct disk_mmc_info *mmc_info, const uint8_t *src, block_t sector, block_count_t count) { if (mmc_info->config.mmc_write) return mmc_info->config.mmc_write(mmc_info->config.mmc_dev, src, sector, count); else return -DISK_ERROR_WRITE_ACCESS; } static int mmc_dev_erase(struct disk_mmc_info *mmc_info, block_t block, block_count_t count) { if (mmc_info->config.mmc_erase) return mmc_info->config.mmc_erase(mmc_info->config.mmc_dev, block, count); else return -DISK_ERROR_ERASE_ACCESS; } /* MMC buff reads */ static int mmc_buff_read(struct disk_mmc_info *mmc_info, uint8_t *buff_dst, block_t sector, block_count_t sector_count, uint8_t *dst, disk_addr_t addr, disk_size_t size) { int ret = 0; uint32_t sector_size = mmc_info->config.sector_size; /* Protect the buffer */ xSemaphoreTake(mmc_info->disk_mmc_mutex, portMAX_DELAY); ret = mmc_dev_read(mmc_info, buff_dst, sector, sector_count); if (ret) goto error; memcpy(dst, buff_dst + addr - ROUNDDOWN(addr, sector_size), size); xSemaphoreGive(mmc_info->disk_mmc_mutex); return 0; error: xSemaphoreGive(mmc_info->disk_mmc_mutex); return ret; } static int disk_mmc_read(struct disk_info *info, uint8_t *dst, disk_addr_t addr, disk_size_t size) { int ret = 0; struct disk_mmc_info *mmc_info = info->private_data; uint8_t *buf = NULL; disk_size_t remaining = size; uint32_t sector_size = mmc_info->config.sector_size; uint32_t buff_size = mmc_info->buff_size; disk_addr_t sector_addr = 0; disk_size_t sector_count = 0; disk_size_t read_len = 0; disk_size_t max_read_length = 0; while (remaining) { /* Process the unaligned portion of the address */ if (!IS_ALIGNED(addr, sector_size)) { ssdk_printf(SSDK_DEBUG, "mmc disk read %s address sector unaligned portion\n", info->disk_name); buf = mmc_info->buff_sector; sector_addr = addr / sector_size; sector_count = 1; read_len = MIN(ROUNDUP(addr, sector_size) - addr, remaining); ssdk_printf(SSDK_DEBUG, "mmc disk read addr:%llx read_len:%lld\n", addr, read_len); } /* Address & size aligned portion of sector,Memory CACHE_LINE alignment */ else if ((remaining > sector_size) && IS_ALIGNED(dst, CONFIG_ARCH_CACHE_LINE)) { ssdk_printf(SSDK_DEBUG, "mmc disk read %s address sector aligned & dst " "CACHE_LINE aligned portion\n", info->disk_name); buf = dst; sector_addr = addr / sector_size; max_read_length = MIN(mmc_info->mmc_max_xfer_size, remaining); sector_count = max_read_length / sector_size; read_len = sector_count * sector_size; ssdk_printf(SSDK_DEBUG, "mmc disk read addr:%llx read_len:%lld\n", addr, read_len); } /* Address & size aligned portion of sector,Memory CACHE_LINE is not aligned */ else { ssdk_printf(SSDK_DEBUG, "mmc disk read %s remaining portion\n", info->disk_name); buf = mmc_info->buff_sector; sector_addr = addr / sector_size; read_len = MIN(buff_size, remaining); sector_count = ROUNDUP(read_len, sector_size) / sector_size; ssdk_printf(SSDK_DEBUG, "mmc disk read addr:%llx read_len:%lld\n", addr, read_len); } if (buf == dst) { ret = mmc_dev_read(mmc_info, buf, sector_addr, sector_count); if (ret) return ret; } else { ret = mmc_buff_read(mmc_info, buf, sector_addr, sector_count, dst, addr, read_len); if (ret) return ret; } /* Address & size offset */ dst += read_len; addr += read_len; remaining -= read_len; } return 0; } static int mmc_buff_write(struct disk_mmc_info *mmc_info, uint8_t *buff_src, block_t sector, block_count_t sector_count, uint8_t *src, disk_addr_t addr, disk_size_t size) { int ret = 0; uint32_t sector_size = mmc_info->config.sector_size; /* Protect the buffer */ xSemaphoreTake(mmc_info->disk_mmc_mutex, portMAX_DELAY); if (IS_ALIGNED(addr, sector_size) && IS_ALIGNED(size, sector_size)) { memcpy(buff_src, src, size); } else { ret = mmc_dev_read(mmc_info, buff_src, sector, sector_count); if (ret) goto error; memcpy(buff_src + addr - ROUNDDOWN(addr, sector_size), src, size); } ret = mmc_dev_write(mmc_info, buff_src, sector, sector_count); if (ret) goto error; xSemaphoreGive(mmc_info->disk_mmc_mutex); return 0; error: xSemaphoreGive(mmc_info->disk_mmc_mutex); return ret; } static int disk_mmc_write(struct disk_info *info, const uint8_t *src, disk_addr_t addr, disk_size_t size) { int ret = 0; struct disk_mmc_info *mmc_info = info->private_data; uint8_t *buf = NULL; disk_size_t remaining = size; uint32_t sector_size = mmc_info->config.sector_size; uint32_t buff_size = mmc_info->buff_size; disk_addr_t sector_addr = 0; disk_size_t sector_count = 0; disk_size_t write_len = 0; disk_size_t max_read_length = 0; while (remaining) { /* Process the unaligned portion of the address */ if (!IS_ALIGNED(addr, sector_size)) { ssdk_printf(SSDK_DEBUG, "mmc disk write %s address sector unaligned portion\n", info->disk_name); buf = mmc_info->buff_sector; sector_addr = addr / sector_size; sector_count = 1; write_len = MIN(ROUNDUP(addr, sector_size) - addr, remaining); ssdk_printf(SSDK_DEBUG, "mmc disk write addr:%llx write_len:%lld\n", addr, write_len); } /* Address & size aligned portion of sector,Memory CACHE_LINE alignment */ else if ((remaining > sector_size) && IS_ALIGNED(src, CONFIG_ARCH_CACHE_LINE)) { ssdk_printf(SSDK_DEBUG, "mmc disk write %s address sector aligned & dst " "CACHE_LINE aligned portion\n", info->disk_name); buf = (uint8_t *)src; sector_addr = addr / sector_size; max_read_length = MIN(mmc_info->mmc_max_xfer_size, remaining); sector_count = max_read_length / sector_size; write_len = sector_count * sector_size; ssdk_printf(SSDK_DEBUG, "mmc disk write addr:%llx write_len:%lld\n", addr, write_len); } else { ssdk_printf(SSDK_DEBUG, "mmc disk write %s remaining portion\n", info->disk_name); buf = mmc_info->buff_sector; sector_addr = addr / sector_size; write_len = MIN(buff_size, remaining); if (!IS_ALIGNED(write_len, sector_size) && (write_len > sector_size)) write_len = ROUNDDOWN(write_len, sector_size); sector_count = ROUNDUP(write_len, sector_size) / sector_size; ssdk_printf(SSDK_DEBUG, "mmc disk write addr:%llx write_len:%lld\n", addr, write_len); } if (buf == src) { ret = mmc_dev_write(mmc_info, buf, sector_addr, sector_count); if (ret) return ret; } else { ret = mmc_buff_write(mmc_info, buf, sector_addr, sector_count, (uint8_t *)src, addr, write_len); if (ret) return ret; } /* Address & size offset */ src += write_len; addr += write_len; remaining -= write_len; } return 0; } static int disk_mmc_read_block(struct disk_info *info, uint8_t *dst, block_t block, block_count_t count, block_size_t blk_sz) { struct disk_mmc_info *mmc_info = info->private_data; uint32_t sector_size = mmc_info->config.sector_size; uint32_t blk_to_sector = blk_sz / sector_size; return mmc_dev_read(mmc_info, dst, block * blk_to_sector, count * blk_to_sector); } static int disk_mmc_write_block(struct disk_info *info, const uint8_t *src, block_t block, block_count_t count, block_size_t blk_sz) { struct disk_mmc_info *mmc_info = info->private_data; uint32_t sector_size = mmc_info->config.sector_size; uint32_t blk_to_sector = blk_sz / sector_size; return mmc_dev_write(mmc_info, src, block * blk_to_sector, count * blk_to_sector); } static int mmc_buff_erase(struct disk_mmc_info *mmc_info, uint8_t *buff, block_t sector, block_count_t sector_count, disk_addr_t addr, disk_size_t size, char flag) { int ret = 0; uint32_t sector_size = mmc_info->config.sector_size; /* Protect the buff_sector buffer */ xSemaphoreTake(mmc_info->disk_mmc_mutex, portMAX_DELAY); if (IS_ALIGNED(addr, sector_size) && IS_ALIGNED(size, sector_size)) { memset(buff, flag, size); } else { ret = mmc_dev_read(mmc_info, buff, sector, sector_count); if (ret) goto error; memset(buff + addr - ROUNDDOWN(addr, sector_size), flag, size); } ret = mmc_dev_write(mmc_info, buff, sector, sector_count); if (ret) goto error; xSemaphoreGive(mmc_info->disk_mmc_mutex); return 0; error: xSemaphoreGive(mmc_info->disk_mmc_mutex); return ret; } static int disk_mmc_erase(struct disk_info *info, disk_addr_t addr, disk_size_t size, uint32_t flag) { int ret = 0; struct disk_mmc_info *mmc_info = info->private_data; uint8_t *buf = NULL; disk_size_t remaining = size; uint32_t sector_size = mmc_info->config.sector_size; uint32_t buff_size = mmc_info->buff_size; uint32_t scr_data_erase = mmc_info->config.scr_data_erase; uint32_t erase_size = info->erase_size; disk_addr_t sector_addr = 0; disk_size_t sector_count = 0; disk_size_t erase_count = 0; disk_size_t erase_len = 0; /* configure c_flag */ char c_flag = 0; if (MMC_DEFAULT_FLAG(flag)) c_flag = 0x0; else c_flag = 0xff; while (remaining) { /* Process the sector unaligned portion of the address */ if (!IS_ALIGNED(addr, sector_size)) { ssdk_printf(SSDK_DEBUG, "mmc disk erase %s address sector unaligned portion\n", info->disk_name); buf = mmc_info->buff_sector; sector_addr = addr / sector_size; sector_count = 1; erase_len = MIN(ROUNDUP(addr, sector_size) - addr, remaining); ssdk_printf(SSDK_DEBUG, "mmc disk erase addr:%llx erase_len:%lld\n", addr, erase_len); } /* Process the erase_size unaligned portion of the address */ else if (!IS_ALIGNED(addr, erase_size) && (remaining > erase_size)) { ssdk_printf( SSDK_DEBUG, "mmc disk erase %s address erase_size unaligned portion\n", info->disk_name); buf = mmc_info->buff_sector; sector_addr = addr / sector_size; erase_len = MIN(buff_size, remaining); erase_len = MIN(ROUNDUP(addr, erase_size) - addr, erase_len); sector_count = erase_len / sector_size; erase_len = sector_count * sector_size; ssdk_printf(SSDK_DEBUG, "mmc disk erase addr:%llx erase_len:%lld\n", addr, erase_len); } /* Address & size aligned portion of sector,Memory CACHE_LINE alignment */ else if ((remaining > erase_size) && MMC_DEFAULT_FLAG(flag) && !scr_data_erase) { ssdk_printf(SSDK_DEBUG, "mmc disk erase %s address erase_size aligned & size " "erase_size aligned portion\n", info->disk_name); buf = NULL; sector_addr = addr / sector_size; erase_count = remaining / erase_size; erase_len = erase_count * erase_size; ssdk_printf(SSDK_DEBUG, "mmc disk erase addr:%llx erase_len:%lld\n", addr, erase_len); } else if ((remaining > erase_size) && !MMC_DEFAULT_FLAG(flag) && scr_data_erase) { ssdk_printf(SSDK_DEBUG, "mmc disk erase %s address erase_size aligned & size " "erase_size aligned portion\n", info->disk_name); buf = NULL; sector_addr = addr / sector_size; erase_count = remaining / erase_size; erase_len = erase_count * erase_size; ssdk_printf(SSDK_DEBUG, "mmc disk erase addr:%llx erase_len:%lld\n", addr, erase_len); } /* Address & size aligned portion of sector,Memory CACHE_LINE is not aligned */ else { ssdk_printf(SSDK_DEBUG, "mmc disk erase %s remaining portion\n", info->disk_name); buf = mmc_info->buff_sector; sector_addr = addr / sector_size; erase_len = MIN(buff_size, remaining); if (!IS_ALIGNED(erase_len, sector_size) && (erase_len > sector_size)) erase_len = ROUNDDOWN(erase_len, sector_size); sector_count = ROUNDUP(erase_len, sector_size) / sector_size; ssdk_printf(SSDK_DEBUG, "mmc disk erase addr:%llx erase_len:%lld\n", addr, erase_len); } if (NULL == buf) { ret = mmc_dev_erase(mmc_info, sector_addr, erase_len / sector_size); if (ret) return ret; } else { ret = mmc_buff_erase(mmc_info, buf, sector_addr, sector_count, addr, erase_len, c_flag); if (ret) return ret; } /* Address & size offset */ addr += erase_len; remaining -= erase_len; } return 0; } static int disk_mmc_erase_group(struct disk_info *info, uint32_t erase_block, uint32_t count, uint32_t erase_sz, uint32_t flag) { struct disk_mmc_info *mmc_info = info->private_data; uint32_t sector_size = mmc_info->config.sector_size; uint32_t scr_data_erase = mmc_info->config.scr_data_erase; if (erase_sz != info->erase_size) { ssdk_printf(SSDK_DEBUG, "mmc disk %s erase group erase_sz error,erase_sz:0x%x " "erase_size:0x%x \n", info->disk_name, erase_sz, info->erase_size); return -DISK_ERROR_INVALID_PARAMETER; } if (!IS_ALIGNED(erase_sz, sector_size)) { ssdk_printf(SSDK_DEBUG, "mmc disk %s erase group erase_sz error,erase_sz:0x%x " "sector_size:0x%x \n", info->disk_name, erase_sz, sector_size); return -DISK_ERROR_INVALID_PARAMETER; } uint32_t sector_addr = erase_block * erase_sz / sector_size; switch (flag) { case DISK_ERASE_DEFAULT: case DISK_ERASE_ZERO: if (!scr_data_erase) { return mmc_dev_erase(mmc_info, sector_addr, count * erase_sz / sector_size); } else { return disk_mmc_erase(info, (disk_addr_t)erase_block * erase_sz, (disk_size_t)count * erase_sz, flag); } case DISK_ERASE_ONE: if (scr_data_erase) { return mmc_dev_erase(mmc_info, sector_addr, count * erase_sz / sector_size); } else { return disk_mmc_erase(info, (disk_addr_t)erase_block * erase_sz, (disk_size_t)count * erase_sz, flag); } default: ssdk_printf(SSDK_ERR, "disk mmc erase flag error\n"); return -1; } } static int disk_mmc_status(struct disk_info *info) { return DISK_STATUS_OK; } static int disk_mmc_ioctl(struct disk_info *info, uint32_t cmd, void *buff) { struct disk_mmc_info *mmc_info = info->private_data; switch (cmd) { case MMC_IOCTL_PON: uint32_t pon_type = 0; memcpy(&pon_type, buff, sizeof(pon_type)); if (mmc_info->config.mmc_pon) return mmc_info->config.mmc_pon(mmc_info->config.mmc_dev, pon_type); else return DISK_ERROR_INVALID_PARAMETER; break; default: ssdk_printf(SSDK_ERR, "disk mmc ioctl cmd error:%d\n", cmd); return DISK_ERROR_INVALID_PARAMETER; } return 0; } struct disk_operations disk_mmc_ops = { .disk_read = disk_mmc_read, .disk_write = disk_mmc_write, .disk_read_block = disk_mmc_read_block, .disk_write_block = disk_mmc_write_block, .disk_erase = disk_mmc_erase, .disk_erase_group = disk_mmc_erase_group, .disk_status = disk_mmc_status, .disk_ioctl = disk_mmc_ioctl, }; int register_mmc_disk(struct disk_mmc_info *mmc_info) { int ret = 0; struct disk_info *info = &mmc_info->info; struct disk_mmc_config *mmc_config = &mmc_info->config; /* init mmc_info */ mmc_info->buff_size = 4 * 1024; if (!IS_ALIGNED(mmc_info->buff_size, mmc_config->sector_size)) { ssdk_printf( SSDK_ERR, "mmc disk %s mmc_max_xfer_size not aligned to sector_size\n", mmc_config->disk_name); return -1; } mmc_info->buff_sector = (uint8_t *)pvPortMallocAligned( mmc_info->buff_size, CONFIG_ARCH_CACHE_LINE); if (NULL == mmc_info->buff_sector) { ssdk_printf(SSDK_ERR, "mmc disk %s buff malloc error\n", mmc_config->disk_name); return -1; } mmc_info->disk_mmc_mutex = xSemaphoreCreateMutex(); mmc_info->mmc_max_xfer_size = MMC_MAX_XFER_SIZE; if (!IS_ALIGNED(mmc_info->mmc_max_xfer_size, mmc_config->sector_size)) { ssdk_printf( SSDK_ERR, "mmc disk %s mmc_max_xfer_size not aligned to sector_size\n", mmc_config->disk_name); return -1; } /* base on config init info */ info->disk_name = mmc_config->disk_name; info->disk_size = mmc_config->disk_size; info->disk_offset = 0; info->erase_size = mmc_config->erase_size; info->mem_align_size = CONFIG_ARCH_CACHE_LINE; info->block_align_size = mmc_config->sector_size; info->private_data = mmc_info; info->disk_ops = &disk_mmc_ops; #ifdef CONFIG_DISK_CACHE_NOOP struct disk_cache_noop *cache_noop = &mmc_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, "mmc disk register cache noop:%d\n", ret); return ret; } #endif ssdk_printf(SSDK_INFO, "register mmc disk:%s disk_size:%lld erase_size:0x%x " "sector_size:0x%x\n", info->disk_name, info->disk_size, info->erase_size, mmc_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_mmc_disk(struct disk_mmc_info *mmc_info) { int ret = 0; struct disk_info *info = &mmc_info->info; ssdk_printf(SSDK_INFO, "unregister mmc disk:%s\n", info->disk_name); #ifdef CONFIG_DISK_CACHE_NOOP struct disk_cache_noop *cache_noop = &mmc_info->cache_noop; /* unregister disk cache noop */ ret = unregister_disk_cache_noop(cache_noop); if (ret < 0) { ssdk_printf(SSDK_ERR, "mmc disk unregister cache noop:%d\n", ret); return ret; } #endif vPortFree(mmc_info->buff_sector); vSemaphoreDelete(mmc_info->disk_mmc_mutex); xSemaphoreTake(g_disk_mutex, portMAX_DELAY); if (info->use_count) { xSemaphoreGive(g_disk_mutex); ret = -DISK_ERROR_BUSY; ssdk_printf(SSDK_ERR, "mmc disk unregister:%d\n", ret); return ret; } if (list_in_list(&info->node)) list_delete(&info->node); xSemaphoreGive(g_disk_mutex); return ret; }