/* * Copyright (c) 2022 Winner Microelectronics Co., Ltd. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @file wm_internal_fls.c * * @brief flash Driver Module * * @author dave * * Copyright (c) 2015 Winner Microelectronics Co., Ltd. */ #include #include #include #include "wm_dbg.h" #include "wm_mem.h" #include "list.h" #include "wm_regs.h" #include "wm_internal_flash.h" #include "wm_flash_map.h" static struct tls_inside_fls *inside_fls = NULL; /**System parameter, default for 2M flash*/ unsigned int TLS_FLASH_PARAM_DEFAULT = (0x81FB000UL); unsigned int TLS_FLASH_PARAM1_ADDR = (0x81FC000UL); unsigned int TLS_FLASH_PARAM2_ADDR = (0x81FD000UL); unsigned int TLS_FLASH_PARAM_RESTORE_ADDR = (0x81FE000UL); unsigned int TLS_FLASH_OTA_FLAG_ADDR = (0x81FF000UL); unsigned int TLS_FLASH_END_ADDR = (0x81FFFFFUL); static vu32 read_first_value(void) { return M32(RSA_BASE_ADDRESS); } static void writeEnable(void) { M32(HR_FLASH_CMD_ADDR) = 0x6; M32(HR_FLASH_CMD_START) = CMD_START_Msk; } unsigned char readRID(void) { M32(HR_FLASH_CMD_ADDR) = 0x2c09F; M32(HR_FLASH_CMD_START) = CMD_START_Msk; return read_first_value() & 0xFF; } static void writeBpBit_for_1wreg(char cmp, char bp4, char bp3, char bp2, char bp1, char bp0) { int status = 0; int bpstatus = 0; M32(HR_FLASH_CMD_ADDR) = 0x0C005; M32(HR_FLASH_CMD_START) = CMD_START_Msk; status = read_first_value() & 0xFF; M32(HR_FLASH_CMD_ADDR) = 0x0C035; M32(HR_FLASH_CMD_START) = CMD_START_Msk; status |= (read_first_value() & 0xFF) << 8; /* Write Enable */ M32(HR_FLASH_CMD_ADDR) = 0x6; M32(HR_FLASH_CMD_START) = CMD_START_Msk; bpstatus = (bp4 << 6) | (bp3 << 5) | (bp2 << 4) | (bp1 << 3) | (bp0 << 2); status = (status & 0xBF83) | bpstatus | (cmp << 14); M32(RSA_BASE_ADDRESS) = status; M32(HR_FLASH_CMD_ADDR) = 0x1A001; M32(HR_FLASH_CMD_START) = CMD_START_Msk; } static void writeBpBit_for_2wreg(char cmp, char bp4, char bp3, char bp2, char bp1, char bp0) { int status = 0; int bpstatus = 0; M32(HR_FLASH_CMD_ADDR) = 0x0C005; M32(HR_FLASH_CMD_START) = CMD_START_Msk; status = read_first_value() & 0xFF; M32(HR_FLASH_CMD_ADDR) = 0x0C035; M32(HR_FLASH_CMD_START) = CMD_START_Msk; status |= (read_first_value() & 0xFF) << 8; /* Write Enable */ M32(HR_FLASH_CMD_ADDR) = 0x6; M32(HR_FLASH_CMD_START) = CMD_START_Msk; bpstatus = (bp4 << 6) | (bp3 << 5) | (bp2 << 4) | (bp1 << 3) | (bp0 << 2); bpstatus = (status & 0x83) | bpstatus; M32(RSA_BASE_ADDRESS) = bpstatus; M32(HR_FLASH_CMD_ADDR) = 0xA001; M32(HR_FLASH_CMD_START) = CMD_START_Msk; M32(HR_FLASH_CMD_ADDR) = 0x6; M32(HR_FLASH_CMD_START) = CMD_START_Msk; status = ((status>>8) & 0xBF) | (cmp << 6); M32(RSA_BASE_ADDRESS) = status; M32(HR_FLASH_CMD_ADDR) = 0xA031; M32(HR_FLASH_CMD_START) = CMD_START_Msk; } static void writeESMTBpBit(char cmp, char bp4, char bp3, char bp2, char bp1, char bp0) { int status = 0; int bpstatus = 0; M32(HR_FLASH_CMD_ADDR) = 0x0C005; M32(HR_FLASH_CMD_START) = CMD_START_Msk; status = read_first_value() & 0xFF; bpstatus = (bp4 << 6) | (bp3 << 5) | (bp2 << 4) | (bp1 << 3) | (bp0 << 2); status = (status & 0x83) | bpstatus; /* Write Enable */ M32(HR_FLASH_CMD_ADDR) = 0x6; M32(HR_FLASH_CMD_START) = CMD_START_Msk; bpstatus = (bp4 << 6) | (bp3 << 5) | (bp2 << 4) | (bp1 << 3) | (bp0 << 2); status = (status & 0x83) | bpstatus | (cmp << 14); M32(RSA_BASE_ADDRESS) = status; M32(HR_FLASH_CMD_ADDR) = 0x0A001; M32(HR_FLASH_CMD_START) = CMD_START_Msk; M32(HR_FLASH_CMD_ADDR) = 0x0C085; M32(HR_FLASH_CMD_START) = CMD_START_Msk; status = read_first_value() & 0xFF; /* Write Enable */ M32(HR_FLASH_CMD_ADDR) = 0x6; M32(HR_FLASH_CMD_START) = CMD_START_Msk; status = (status & 0xBF) | (cmp << 6); M32(RSA_BASE_ADDRESS) = status; M32(HR_FLASH_CMD_ADDR) = 0x0A0C1; M32(HR_FLASH_CMD_START) = CMD_START_Msk; } static int flashunlock(void) { switch (readRID()) { case SPIFLASH_MID_GD: case SPIFLASH_MID_TSINGTENG: writeBpBit_for_1wreg(0, 0, 0, 0, 0, 0); break; case SPIFLASH_MID_PUYA: case SPIFLASH_MID_XTX: case SPIFLASH_MID_BOYA: case SPIFLASH_MID_FUDANMICRO: case SPIFLASH_MID_XMC: writeBpBit_for_2wreg(0, 0, 0, 0, 0, 0); break; case SPIFLASH_MID_ESMT: writeESMTBpBit(0, 0, 0, 0, 0, 0); break; default: return -1; } return 0; } static int flashlock(void) { switch (readRID()) { case SPIFLASH_MID_GD: case SPIFLASH_MID_TSINGTENG: writeBpBit_for_1wreg(0, 1, 1, 0, 1, 0); break; case SPIFLASH_MID_PUYA: case SPIFLASH_MID_XTX: case SPIFLASH_MID_BOYA: case SPIFLASH_MID_FUDANMICRO: case SPIFLASH_MID_XMC: writeBpBit_for_2wreg(0, 1, 1, 0, 1, 0); break; case SPIFLASH_MID_ESMT: writeESMTBpBit(0, 1, 1, 0, 1, 0); break; default: return -1; /* do not clear QIO Mode */ } return 0; } static void writeLbBit_for_1wreg(unsigned int val) { int status = 0; M32(HR_FLASH_CMD_ADDR) = 0x0C005; M32(HR_FLASH_CMD_START) = CMD_START_Msk; status = read_first_value() & 0xFF; M32(HR_FLASH_CMD_ADDR) = 0x0C035; M32(HR_FLASH_CMD_START) = CMD_START_Msk; status |= (read_first_value() & 0xFF) << 8; /* Write Enable */ M32(HR_FLASH_CMD_ADDR) = 0x6; M32(HR_FLASH_CMD_START) = CMD_START_Msk; status |= (val); M32(RSA_BASE_ADDRESS) = status; M32(HR_FLASH_CMD_ADDR) = 0x1A001; M32(HR_FLASH_CMD_START) = CMD_START_Msk; } static void writeLbBit_for_2wreg(unsigned int val) { int status = 0; M32(HR_FLASH_CMD_ADDR) = 0x0C005; M32(HR_FLASH_CMD_START) = CMD_START_Msk; status = read_first_value() & 0xFF; M32(HR_FLASH_CMD_ADDR) = 0x0C035; M32(HR_FLASH_CMD_START) = CMD_START_Msk; status |= (read_first_value() & 0xFF) << 8; /* Write Enable */ M32(HR_FLASH_CMD_ADDR) = 0x6; M32(HR_FLASH_CMD_START) = CMD_START_Msk; status |= (val); status = (status>>8); M32(RSA_BASE_ADDRESS) = status; M32(HR_FLASH_CMD_ADDR) = 0xA031; M32(HR_FLASH_CMD_START) = CMD_START_Msk; } static int programSR(unsigned int cmd, unsigned long addr, unsigned char *buf, unsigned int sz) { unsigned long base_addr = 0; unsigned int size = 0; unsigned char *buf_tmp = buf; if (sz > INSIDE_FLS_PAGE_SIZE) { sz = INSIDE_FLS_PAGE_SIZE; } base_addr = RSA_BASE_ADDRESS; size = sz; while (size) { M32(base_addr) = *((unsigned long *)buf_tmp); base_addr += 4; // 4:byte alignment buf_tmp += 4; // 4:byte alignment size -= 4; // 4:byte alignment } writeEnable(); M32(HR_FLASH_CMD_ADDR) = cmd | ((sz - 1) << 16); // 16:byte alignment M32(HR_FLASH_ADDR) = (addr & 0x1FFFFFF); M32(HR_FLASH_CMD_START) = CMD_START_Msk; return 0; } static int programPage (unsigned long adr, unsigned long sz, unsigned char *buf) { programSR(0x80009002, adr, buf, sz); return(0); } static int eraseSR(unsigned int cmd, unsigned long addr) { /* Write Enable */ writeEnable(); M32(HR_FLASH_CMD_ADDR) = cmd; M32(HR_FLASH_ADDR) = (addr & 0x1FFFFFF); M32(HR_FLASH_CMD_START) = CMD_START_Msk; return 0; } static int eraseSector (unsigned long adr) { eraseSR(0x80000820, adr); return (0); // Finished without Errors } /* only for w800 c400 flash */ static int erasePage (unsigned long addr) { eraseSR(0x80000881, addr); return (0); // Finished without Errors } static unsigned int getFlashDensity(void) { unsigned char density = 0; M32(HR_FLASH_CMD_ADDR) = 0x2c09F; M32(HR_FLASH_CMD_START) = CMD_START_Msk; density = ((read_first_value() & 0xFFFFFF) >> 16) & 0xFF; if (density && (density <= 28)) { return (1 << density); } return 0; } int __readByCMD(unsigned char cmd, unsigned long addr, unsigned char *buf, unsigned long sz) { int i = 0; int word = sz / 4; // 4:byte alignment int byte = sz % 4; // 4:byte alignment unsigned char cmd_tmp = cmd; unsigned char *buf_tmp = buf; unsigned long addr_read; if (!(M32(HR_FLASH_CR)&0x1)) { /* non-QIO mode, only single line command can be used */ if (cmd_tmp > 0x0B) { cmd_tmp = 0x0B; } } switch (cmd_tmp) { case 0x03: M32(HR_FLASH_CMD_ADDR) = 0x8000C003 | (((sz - 1) & 0x3FF) << 16); // 16:byte alignment M32(HR_FLASH_ADDR) = addr & 0x1FFFFFF; M32(HR_FLASH_CMD_START) = CMD_START_Msk; break; case 0x0B: if ((M32(HR_FLASH_CR) & 0x2) == 0x2) { M32(HR_FLASH_CMD_ADDR) = 0xB400C00B | (((sz - 1) & 0x3FF) << 16); // 16:byte alignment } else { M32(HR_FLASH_CMD_ADDR) = 0xBC00C00B | (((sz - 1) & 0x3FF) << 16); // 16:byte alignment } M32(HR_FLASH_ADDR) = addr & 0x1FFFFFF; M32(HR_FLASH_CMD_START) = CMD_START_Msk; break; case 0xBB: M32(HR_FLASH_CMD_ADDR) = 0xE400C0BB | (((sz - 1) & 0x3FF) << 16); // 16:byte alignment M32(HR_FLASH_ADDR) = addr & 0x1FFFFFF; M32(HR_FLASH_CMD_START) = CMD_START_Msk; break; case 0xEB: M32(HR_FLASH_CMD_ADDR) = 0xEC00C0EB | (((sz - 1) & 0x3FF) << 16); // 16:byte alignment M32(HR_FLASH_ADDR) = addr & 0x1FFFFFF; M32(HR_FLASH_CMD_START) = CMD_START_Msk; break; default: return -1; } addr_read = RSA_BASE_ADDRESS; for (i = 0; i < word; i ++) { M32(buf_tmp) = M32(addr_read); buf_tmp += 4; // 4:byte alignment addr_read += 4; // 4:byte alignment } if (byte > 0) { M32(buf_tmp) = M32(addr_read); buf_tmp += 3; // 3:point last byte byte = 4 - byte; // 4:byte alignment while (byte) { *buf_tmp = 0; buf_tmp --; byte --; } } return 0; } /** * @brief This function is used to read data from the flash. * * @param[in] cmd 0xEB in QSPI mode; 0x0b in SPI mode. * @param[in] addr is byte offset addr for read from the flash. * @param[in] buf is user for data buffer of flash read * @param[in] len is byte length for read. * * @retval TLS_FLS_STATUS_OK if read sucsess * @retval TLS_FLS_STATUS_EPERM if inside fls does not initialized. * * @note None */ int readByCMD(unsigned char cmd, unsigned long addr, unsigned char *buf, unsigned long sz) { if (inside_fls == NULL) { return TLS_FLS_STATUS_EPERM; } tls_os_sem_acquire(inside_fls->fls_lock, 0); __readByCMD(cmd, addr, buf, sz); tls_os_sem_release(inside_fls->fls_lock); return TLS_FLS_STATUS_OK; } int flashRead(unsigned long addr, unsigned char *buf, unsigned long sz) { #define INSIDE_FLS_MAX_RD_SIZE (1024) unsigned int flash_addr; unsigned int sz_pagenum = 0; unsigned int sz_remain = 0; int i = 0; int page_offset = addr & (INSIDE_FLS_PAGE_SIZE - 1); unsigned char *buf_tmp = buf; if ((page_offset == 0) && (((unsigned int)buf_tmp&0x3) == 0) && ((sz&0x3) == 0)) { /* Use 4-bytes aligned and buf must be 4 times, sz must be 4 times */ flash_addr = addr; unsigned int max_size = 0; if (sz >= 512) { max_size = INSIDE_FLS_MAX_RD_SIZE; } else { max_size = INSIDE_FLS_PAGE_SIZE; } sz_pagenum = sz / max_size; sz_remain = sz % max_size; for (i = 0; i < sz_pagenum; i++) { __readByCMD(0xEB, flash_addr, (unsigned char *)buf_tmp, max_size); buf_tmp += max_size; flash_addr += max_size; } if (sz_remain) { __readByCMD(0xEB, flash_addr, (unsigned char *)buf_tmp, sz_remain); } } else { char *cache = tls_mem_alloc(INSIDE_FLS_PAGE_SIZE); if (cache == NULL) { TLS_DBGPRT_ERR("allocate sector cache memory fail!\n"); return TLS_FLS_STATUS_ENOMEM; } flash_addr = addr & ~(INSIDE_FLS_PAGE_SIZE - 1); __readByCMD(0xEB, flash_addr, (unsigned char *)cache, INSIDE_FLS_PAGE_SIZE); if (sz > INSIDE_FLS_PAGE_SIZE - page_offset) { MEMCPY(buf_tmp, cache + page_offset, INSIDE_FLS_PAGE_SIZE - page_offset); buf_tmp += INSIDE_FLS_PAGE_SIZE - page_offset; flash_addr += INSIDE_FLS_PAGE_SIZE; sz_pagenum = (sz - (INSIDE_FLS_PAGE_SIZE - page_offset)) / INSIDE_FLS_PAGE_SIZE; sz_remain = (sz - (INSIDE_FLS_PAGE_SIZE - page_offset)) % INSIDE_FLS_PAGE_SIZE; for (i = 0; i < sz_pagenum; i++) { __readByCMD(0xEB, flash_addr, (unsigned char *)cache, INSIDE_FLS_PAGE_SIZE); MEMCPY(buf_tmp, cache, INSIDE_FLS_PAGE_SIZE); buf_tmp += INSIDE_FLS_PAGE_SIZE; flash_addr += INSIDE_FLS_PAGE_SIZE; } if (sz_remain) { __readByCMD(0xEB, flash_addr, (unsigned char *)cache, sz_remain + (4- sz_remain%4)); MEMCPY(buf_tmp, cache, sz_remain); } } else { MEMCPY(buf_tmp, cache + page_offset, sz); } tls_mem_free(cache); } return 0; } /** * @brief This function is used to unlock flash protect area [0x0~0x2000]. * * @param None * * @return 0-success,non-zero-failure * * @note None */ int tls_flash_unlock(void) { int ret = 0; if (inside_fls == NULL) { return TLS_FLS_STATUS_EPERM; } tls_os_sem_acquire(inside_fls->fls_lock, 0); ret = flashunlock(); tls_os_sem_release(inside_fls->fls_lock); return ret; } /** * @brief This function is used to lock flash protect area [0x0~0x2000]. * * @param None * * @return 0-success,non-zero-failure * * @note None */ int tls_flash_lock(void) { int ret = 0; if (inside_fls == NULL) { return TLS_FLS_STATUS_EPERM; } tls_os_sem_acquire(inside_fls->fls_lock, 0); ret = flashlock(); tls_os_sem_release(inside_fls->fls_lock); return ret; } /** * @brief This function is used to semaphore protect. * * @param None * * @return None * * @note None */ void tls_fls_sem_lock(void) { if (inside_fls == NULL) { return; } tls_os_sem_acquire(inside_fls->fls_lock, 0); } /** * @brief This function is used to semaphore protect cancel. * * @param None * * @return None * * @note None */ void tls_fls_sem_unlock(void) { if (inside_fls == NULL) { return; } tls_os_sem_release(inside_fls->fls_lock); } /** * @brief This function is used to read the unique id of the internal flash. * * @param[out] uuid Specified the address to save the uuid, the length must be greater than or equals to 18 bytes. * * @retval TLS_FLS_STATUS_OK if read sucsess * @retval TLS_FLS_STATUS_EIO if read fail * * @note The uuid's length must be greater than or equals to 18 bytes. */ int tls_fls_read_unique_id(unsigned char *uuid) { unsigned int value = 0; unsigned int addr_read = 0; int i = 0; int len; unsigned char FLASH_BUF[20]; unsigned char *addr = &FLASH_BUF[0]; int dumy_bytes = 0; int uni_bytes = 0; unsigned char rid; int word; int byte; if (inside_fls == NULL) { return TLS_FLS_STATUS_EPERM; } tls_os_sem_acquire(inside_fls->fls_lock, 0); memset(uuid, 0xFF, 18); rid = readRID(); switch (rid) { case SPIFLASH_MID_GD: case SPIFLASH_MID_PUYA: case SPIFLASH_MID_TSINGTENG: dumy_bytes = 4; uni_bytes = 16; break; case SPIFLASH_MID_WINBOND: case SPIFLASH_MID_FUDANMICRO: case SPIFLASH_MID_BOYA: case SPIFLASH_MID_XMC: dumy_bytes = 4; uni_bytes = 8; break; case SPIFLASH_MID_ESMT: case SPIFLASH_MID_XTX: default: tls_os_sem_release(inside_fls->fls_lock); return -1; } uuid[0] = rid; uuid[1] = (unsigned char)(uni_bytes & 0xFF); len = dumy_bytes + uni_bytes; word = len/4; byte = len%4; value = 0xC04B|((len-1) << 16); M32(HR_FLASH_CMD_ADDR) = value; M32(HR_FLASH_CMD_START) = CMD_START_Msk; addr_read = RSA_BASE_ADDRESS; for (i = 0;i < word; i ++) { M32(addr) = M32(addr_read); addr += 4; addr_read += 4; } if (byte > 0) { M32(addr) = M32(addr_read); addr += 3; // point last byte while (byte) { *addr = 0; addr --; byte --; } } addr = &FLASH_BUF[0]; memcpy(&uuid[2], addr + dumy_bytes, uni_bytes); tls_os_sem_release(inside_fls->fls_lock); return 0; } int tls_fls_otp_read(u32 addr, u8 *buf, u32 len) { int err; int word = len/4; int byte = len%4; unsigned long addr_read = 0xBC00C048; volatile unsigned long value; unsigned long addr_offset = 0; unsigned long sz_need = len; if (inside_fls == NULL) { TLS_DBGPRT_ERR("flash driver module not beed installed!\n"); return TLS_FLS_STATUS_EPERM; } tls_os_sem_acquire(inside_fls->fls_lock, 0); if (buf) { addr_offset = addr % 16; sz_need = (addr_offset + len + 16) / 16 * 16; addr = addr / 16 * 16; } M32(HR_FLASH_CMD_ADDR) = addr_read|(((sz_need-1)&0x3FF)<<16); M32(HR_FLASH_ADDR) = (addr&0x1FFFFFF); M32(HR_FLASH_CMD_START) = tls_reg_read32(HR_FLASH_CMD_START) | CMD_START_Msk; if (buf) { addr_read = RSA_BASE_ADDRESS + (addr_offset / 4 * 4); int i = (4 - addr_offset % 4) % 4; if (i > len) { byte = len; } else { byte = i; } if (byte) { value = M32(addr_read); memcpy_s(buf, sizeof(buf), ((char *)&value) + 4 - i, byte); addr_read += 4; buf += byte; } word = (len - byte) / 4; for (i = 0;i < word; i ++) { value = M32(addr_read); memcpy_s(buf, sizeof(buf), (char*)&value, 4); buf += 4; addr_read += 4; } byte = (len - byte) % 4; if (byte > 0) { value = M32(addr_read); memcpy_s(buf, sizeof(buf), (char *)&value, byte); } } err = TLS_FLS_STATUS_OK; tls_os_sem_release(inside_fls->fls_lock); return err; } int tls_fls_otp_write(u32 addr, u8 *buf, u32 len) { int ret = 0; unsigned int erasecmd = 0x80000844; unsigned int writecmd = 0x80009042; uint32_t eraseAddr = 0; uint16_t eraseSize = 0; uint16_t pageSize = 0; unsigned char flashid = 0; int l = 0; unsigned char *backbuf = NULL; unsigned long size = 0; unsigned long p = 0; unsigned char *q = NULL; if (!buf) { return TLS_FLS_STATUS_EINVAL; } if (inside_fls == NULL) { TLS_DBGPRT_ERR("flash driver module not beed installed!\n"); return TLS_FLS_STATUS_EPERM; } eraseSize = inside_fls->OTPWRParam.eraseSize; pageSize = inside_fls->OTPWRParam.pageSize; if (eraseSize == 0 || pageSize == 0) { TLS_DBGPRT_ERR("flash type is not supported!\n"); return TLS_FLS_STATUS_ENOSUPPORT; } eraseAddr = addr & ~(eraseSize - 1); if (addr < eraseAddr || len > eraseSize - (addr - eraseAddr)) { return TLS_FLS_STATUS_EINVAL; } TLS_DBGPRT_INFO("addr 0x%x, eraseAddr 0x%x, eraseSize 0x%x, pageSize 0x%x\n", addr, eraseAddr, eraseSize, pageSize); backbuf = tls_mem_alloc(eraseSize); if (!backbuf) { ret = TLS_FLS_STATUS_ENOMEM; goto out; } p = eraseAddr; q = backbuf; size = eraseSize; while (size > 0) { l = size > pageSize ? pageSize : size; if (tls_fls_otp_read(p, q, l) != TLS_FLS_STATUS_OK) { ret = TLS_FLS_STATUS_EPERM; goto out; } q += l; p += l; size -= l; } tls_os_sem_acquire(inside_fls->fls_lock, 0); eraseSR(erasecmd, eraseAddr); memcpy_s(backbuf + (addr - eraseAddr), sizeof(backbuf + (addr - eraseAddr)), buf, len); p = eraseAddr; q = backbuf; size = eraseSize; while (size > 0) { l = size > pageSize ? pageSize : size; programSR(writecmd, p, q, l); q += l; p += l; size -= l; } tls_os_sem_release(inside_fls->fls_lock); out: if (backbuf) { tls_mem_free(backbuf); } return ret; } int tls_fls_otp_lock(void) { if (inside_fls == NULL) { TLS_DBGPRT_ERR("flash driver module not beed installed!\n"); return TLS_FLS_STATUS_EPERM; } tls_os_sem_acquire(inside_fls->fls_lock, 0); switch (inside_fls->flashid) { case SPIFLASH_MID_GD: case SPIFLASH_MID_TSINGTENG: writeLbBit_for_1wreg((1<<10)); break; case SPIFLASH_MID_FUDANMICRO: writeLbBit_for_2wreg((1<<10)); break; case SPIFLASH_MID_BOYA: case SPIFLASH_MID_XMC: case SPIFLASH_MID_WINBOND: case SPIFLASH_MID_PUYA: writeLbBit_for_2wreg((7<<11)); break; case SPIFLASH_MID_XTX: case SPIFLASH_MID_ESMT: default: TLS_DBGPRT_ERR("flash is not supported!\n"); return TLS_FLS_STATUS_ENOSUPPORT; } tls_os_sem_release(inside_fls->fls_lock); return 0; } /** * @brief This function is used to read data from the flash. * * @param[in] addr is byte offset addr for read from the flash. * @param[in] buf is user for data buffer of flash read * @param[in] len is byte length for read. * * @retval TLS_FLS_STATUS_OK if read sucsess * @retval TLS_FLS_STATUS_EIO if read fail * * @note None */ int tls_fls_read(u32 addr, u8 *buf, u32 len) { int err; if (inside_fls == NULL) { TLS_DBGPRT_ERR("flash driver module not beed installed!\n"); return TLS_FLS_STATUS_EPERM; } if (((addr & (INSIDE_FLS_BASE_ADDR - 1)) >= inside_fls->density) || (len == 0) || (buf == NULL)) { return TLS_FLS_STATUS_EINVAL; } tls_os_sem_acquire(inside_fls->fls_lock, 0); flashRead(addr, buf, len); err = TLS_FLS_STATUS_OK; tls_os_sem_release(inside_fls->fls_lock); return err; } /** * @brief This function is used to write data to the flash. * * @param[in] addr is byte offset addr for write to the flash * @param[in] buf is the data buffer want to write to flash * @param[in] len is the byte length want to write * * @retval TLS_FLS_STATUS_OK if write flash success * @retval TLS_FLS_STATUS_EPERM if flash struct point is null * @retval TLS_FLS_STATUS_ENODRV if flash driver is not installed * @retval TLS_FLS_STATUS_EINVAL if argument is invalid * @retval TLS_FLS_STATUS_EIO if io error * * @note None */ int tls_fls_write(u32 addr, u8 *buf, u32 len) { u8 *cache; unsigned int secpos; unsigned int secoff; unsigned int secremain; unsigned int i; unsigned int offaddr; if (inside_fls == NULL) { TLS_DBGPRT_ERR("flash driver module not beed installed!\n"); return TLS_FLS_STATUS_EPERM; } if (((addr & (INSIDE_FLS_BASE_ADDR - 1)) >= inside_fls->density) || (len == 0) || (buf == NULL)) { return TLS_FLS_STATUS_EINVAL; } tls_os_sem_acquire(inside_fls->fls_lock, 0); cache = tls_mem_alloc(INSIDE_FLS_SECTOR_SIZE); if (cache == NULL) { tls_os_sem_release(inside_fls->fls_lock); TLS_DBGPRT_ERR("allocate sector cache memory fail!\n"); return TLS_FLS_STATUS_ENOMEM; } offaddr = addr & (INSIDE_FLS_BASE_ADDR - 1); // Offset of 0X08000000 secpos = offaddr / INSIDE_FLS_SECTOR_SIZE; // Section addr secoff = (offaddr % INSIDE_FLS_SECTOR_SIZE); // Offset in section secremain = INSIDE_FLS_SECTOR_SIZE - secoff; // ����ʣ��ռ��С if (len <= secremain) { secremain = len; // Not bigger with remain size in section } flashRead(secpos * INSIDE_FLS_SECTOR_SIZE, cache, INSIDE_FLS_SECTOR_SIZE); while (1) { eraseSector(secpos * INSIDE_FLS_SECTOR_SIZE); for (i = 0; i < secremain; i++) { // ���� cache[i + secoff] = buf[i]; } for (i = 0; i < (INSIDE_FLS_SECTOR_SIZE / INSIDE_FLS_PAGE_SIZE); i++) { programPage(secpos * INSIDE_FLS_SECTOR_SIZE + i * INSIDE_FLS_PAGE_SIZE, \ INSIDE_FLS_PAGE_SIZE, &cache[i * INSIDE_FLS_PAGE_SIZE]); // Write } if (len == secremain) { break; // д������� } else { // д��δ���� secpos++; // ������ַ��1 secoff = 0; // ƫ��λ��Ϊ0 buf += secremain; // ָ��ƫ�� len -= secremain; if (len > (INSIDE_FLS_SECTOR_SIZE)) { secremain = INSIDE_FLS_SECTOR_SIZE; // ��һ����������д���� } else { secremain = len; // Next section will finish flashRead(secpos * INSIDE_FLS_SECTOR_SIZE, cache, INSIDE_FLS_SECTOR_SIZE); } } } tls_mem_free(cache); tls_os_sem_release(inside_fls->fls_lock); return TLS_FLS_STATUS_OK; } /** * @brief This function is used to write data into the flash without erase. * * @param[in] addr Specifies the starting address to write to * @param[in] buf Pointer to a byte array that is to be written * @param[in] len Specifies the length of the data to be written * * @retval TLS_FLS_STATUS_OK if write flash success * @retval TLS_FLS_STATUS_EPERM if flash struct point is null * @retval TLS_FLS_STATUS_ENODRV if flash driver is not installed * @retval TLS_FLS_STATUS_EINVAL if argument is invalid * * @note Erase action should be excuted by API tls_fls_erase in user layer. */ int tls_fls_write_without_erase(u32 addr, u8 *buf, u32 len) { u8 *cache; unsigned int pagepos; unsigned int pageoff; unsigned int pageremain; unsigned int i; unsigned int offaddr; if (inside_fls == NULL) { TLS_DBGPRT_ERR("flash driver module not beed installed!\n"); return TLS_FLS_STATUS_EPERM; } if (((addr & (INSIDE_FLS_BASE_ADDR - 1)) >= inside_fls->density) || (len == 0) || (buf == NULL)) { return TLS_FLS_STATUS_EINVAL; } tls_os_sem_acquire(inside_fls->fls_lock, 0); cache = tls_mem_alloc(INSIDE_FLS_PAGE_SIZE); if (cache == NULL) { tls_os_sem_release(inside_fls->fls_lock); TLS_DBGPRT_ERR("allocate page cache memory fail!\n"); return TLS_FLS_STATUS_ENOMEM; } offaddr = addr & (INSIDE_FLS_BASE_ADDR - 1); // Offset of 0X08000000 pagepos = offaddr / INSIDE_FLS_PAGE_SIZE; // Page addr pageoff = (offaddr % INSIDE_FLS_PAGE_SIZE); // Offset in page pageremain = INSIDE_FLS_PAGE_SIZE - pageoff; // size remained in one page if (len <= pageremain) { pageremain = len; // Not bigger with remain size in one page } flashRead(pagepos * INSIDE_FLS_PAGE_SIZE, cache, INSIDE_FLS_PAGE_SIZE); while (1) { for (i = 0; i < pageremain; i++) { cache[i + pageoff] = buf[i]; } programPage(pagepos * INSIDE_FLS_PAGE_SIZE, INSIDE_FLS_PAGE_SIZE, &cache[0]); // Write if (len == pageremain) { // page program over break; } else { pagepos++; // next page pageoff = 0; // page offset set to zero buf += pageremain; // buffer modified len -= pageremain; // len decrease if (len > (INSIDE_FLS_PAGE_SIZE)) { pageremain = INSIDE_FLS_PAGE_SIZE; // size next to write } else { pageremain = len; // last data to write flashRead(pagepos * INSIDE_FLS_PAGE_SIZE, cache, INSIDE_FLS_PAGE_SIZE); } } } tls_mem_free(cache); tls_os_sem_release(inside_fls->fls_lock); return TLS_FLS_STATUS_OK; } /** * @brief This function is used to erase the appoint sector * * @param[in] sector sector num of the flash, 4K byte a sector * * @retval TLS_FLS_STATUS_OK if read sucsess * @retval other if read fail * * @note None */ int tls_fls_erase(u32 sector) { u32 addr; if (inside_fls == NULL) { TLS_DBGPRT_ERR("flash driver module not beed installed!\n"); return TLS_FLS_STATUS_EPERM; } if (sector >= (inside_fls->density / INSIDE_FLS_SECTOR_SIZE + INSIDE_FLS_BASE_ADDR / INSIDE_FLS_SECTOR_SIZE)) { TLS_DBGPRT_ERR("the sector to be erase overflow!\n"); return TLS_FLS_STATUS_EINVAL; } tls_os_sem_acquire(inside_fls->fls_lock, 0); addr = sector * INSIDE_FLS_SECTOR_SIZE; eraseSector(addr); tls_os_sem_release(inside_fls->fls_lock); return TLS_FLS_STATUS_OK; } static u8 *gsflscache = NULL; static u32 gsSector = 0; /** * @brief This function is used to flush the appoint sector * * @param None * * @return None * * @note None */ static void tls_fls_flush_sector(void) { u32 addr; if (gsSector < (inside_fls->density / INSIDE_FLS_SECTOR_SIZE + INSIDE_FLS_BASE_ADDR / INSIDE_FLS_SECTOR_SIZE)) { addr = gsSector * INSIDE_FLS_SECTOR_SIZE; eraseSector(addr); for (int i = 0; i < INSIDE_FLS_SECTOR_SIZE / INSIDE_FLS_PAGE_SIZE; i++) { programPage(gsSector * INSIDE_FLS_SECTOR_SIZE + i * INSIDE_FLS_PAGE_SIZE, INSIDE_FLS_PAGE_SIZE, &gsflscache[i * INSIDE_FLS_PAGE_SIZE]); } } } /** * @brief This function is used to fast write flash initialize * * @param None * * @retval TLS_FLS_STATUS_OK sucsess * @retval other fail * * @note None */ int tls_fls_fast_write_init(void) { if (inside_fls == NULL) { TLS_DBGPRT_ERR("flash driver module not beed installed!\n"); return TLS_FLS_STATUS_EPERM; } if (gsflscache != NULL) { TLS_DBGPRT_ERR("tls_fls_fast_write_init installed!\n"); return -1; } gsflscache = tls_mem_alloc(INSIDE_FLS_SECTOR_SIZE); if (gsflscache == NULL) { TLS_DBGPRT_ERR("tls_fls_fast_write_init malloc err!\n"); return -1; } return TLS_FLS_STATUS_OK; } /** * @brief This function is used to destroy fast write flash * * @param None * * @return None * * @note None */ void tls_fls_fast_write_destroy(void) { if (gsflscache != NULL) { if (inside_fls == NULL) { TLS_DBGPRT_ERR("flash driver module not beed installed!\n"); return; } else { tls_os_sem_acquire(inside_fls->fls_lock, 0); tls_fls_flush_sector(); tls_os_sem_release(inside_fls->fls_lock); } tls_mem_free(gsflscache); gsflscache = NULL; } } /** * @brief This function is used to fast write data to the flash. * * @param[in] addr is byte offset addr for write to the flash * @param[in] buf is the data buffer want to write to flash * @param[in] length is the byte length want to write * * @retval TLS_FLS_STATUS_OK success * @retval other fail * * @note None */ int tls_fls_fast_write(u32 addr, u8 *buf, u32 length) { u32 sector, offset, maxlen, len; if (inside_fls == NULL) { TLS_DBGPRT_ERR("flash driver module not beed installed!\n"); return TLS_FLS_STATUS_EPERM; } if (((addr & (INSIDE_FLS_BASE_ADDR - 1)) >= inside_fls->density) || (length == 0) || (buf == NULL)) { return TLS_FLS_STATUS_EINVAL; } tls_os_sem_acquire(inside_fls->fls_lock, 0); sector = addr / INSIDE_FLS_SECTOR_SIZE; offset = addr % INSIDE_FLS_SECTOR_SIZE; maxlen = INSIDE_FLS_SECTOR_SIZE; if ((sector != gsSector) && (gsSector != 0)) { tls_fls_flush_sector(); } gsSector = sector; if (offset > 0) { maxlen -= offset; } while (length > 0) { len = (length > maxlen) ? maxlen : length; MEMCPY(gsflscache + offset, buf, len); if (offset + len >= INSIDE_FLS_SECTOR_SIZE) { tls_fls_flush_sector(); gsSector++; } offset = 0; maxlen = INSIDE_FLS_SECTOR_SIZE; buf += len; length -= len; } tls_os_sem_release(inside_fls->fls_lock); return TLS_FLS_STATUS_OK; } /** * @brief This function is used to erase flash all chip * * @param None * * @retval TLS_FLS_STATUS_OK sucsess * @retval other fail * * @note None */ int tls_fls_chip_erase(void) { int i, j; u8 *cache; if (inside_fls == NULL) { TLS_DBGPRT_ERR("flash driver module not beed installed!\n"); return TLS_FLS_STATUS_EPERM; } tls_os_sem_acquire(inside_fls->fls_lock, 0); cache = tls_mem_alloc(INSIDE_FLS_SECTOR_SIZE); if (cache == NULL) { tls_os_sem_release(inside_fls->fls_lock); TLS_DBGPRT_ERR("allocate sector cache memory fail!\n"); return TLS_FLS_STATUS_ENOMEM; } for (i = 0; i < (inside_fls->density - (INSIDE_FLS_SECBOOT_ADDR & 0xFFFFF)) / INSIDE_FLS_SECTOR_SIZE; i ++) { flashRead(INSIDE_FLS_SECBOOT_ADDR + i * INSIDE_FLS_SECTOR_SIZE, cache, INSIDE_FLS_SECTOR_SIZE); for (j = 0; j < INSIDE_FLS_SECTOR_SIZE; j++) { if (cache[j] != 0xFF) { eraseSector(INSIDE_FLS_SECBOOT_ADDR + i * INSIDE_FLS_SECTOR_SIZE); break; } } } tls_mem_free(cache); tls_os_sem_release(inside_fls->fls_lock); return TLS_FLS_STATUS_OK; } /** * @brief This function is used to get flash param * * @param[in] type the type of the param need to get * @param[out] param point to addr of out param * * @retval TLS_FLS_STATUS_OK sucsess * @retval other fail * * @note None */ int tls_fls_get_param(u8 type, void *param) { int err; if (inside_fls == NULL) { TLS_DBGPRT_ERR("flash driver module not beed installed!\n"); return TLS_FLS_STATUS_EPERM; } if (param == NULL) { return TLS_FLS_STATUS_EINVAL; } tls_os_sem_acquire(inside_fls->fls_lock, 0); err = TLS_FLS_STATUS_OK; switch (type) { case TLS_FLS_PARAM_TYPE_ID: *((u32 *) param) = 0x2013; break; case TLS_FLS_PARAM_TYPE_SIZE: *((u32 *) param) = inside_fls->density; break; case TLS_FLS_PARAM_TYPE_PAGE_SIZE: *((u32 *) param) = INSIDE_FLS_PAGE_SIZE; break; case TLS_FLS_PARAM_TYPE_PROG_SIZE: *((u32 *) param) = INSIDE_FLS_PAGE_SIZE; break; case TLS_FLS_PARAM_TYPE_SECTOR_SIZE: *((u32 *) param) = INSIDE_FLS_SECTOR_SIZE; break; default: TLS_DBGPRT_WARNING("invalid parameter ID!\n"); err = TLS_FLS_STATUS_EINVAL; break; } tls_os_sem_release(inside_fls->fls_lock); return err; } /** * @brief This function is used to initialize the flash module * * @param None * * @retval TLS_FLS_STATUS_OK sucsess * @retval other fail * * @note None */ int tls_fls_init(void) { struct tls_inside_fls *fls; int err; if (inside_fls != NULL) { TLS_DBGPRT_ERR("flash driver module has been installed!\n"); return TLS_FLS_STATUS_EBUSY; } fls = (struct tls_inside_fls *) tls_mem_alloc(sizeof(struct tls_inside_fls)); if (fls == NULL) { TLS_DBGPRT_ERR("allocate @inside_fls fail!\n"); return TLS_FLS_STATUS_ENOMEM; } memset_s(fls, sizeof(fls), 0, sizeof(*fls)); err = tls_os_sem_create(&fls->fls_lock, 1); if (err != TLS_OS_SUCCESS) { tls_mem_free(fls); TLS_DBGPRT_ERR("create semaphore @fls_lock fail!\n"); return TLS_FLS_STATUS_ENOMEM; } fls->flashid = readRID(); fls->density = getFlashDensity(); fls->OTPWRParam.pageSize = 256; switch (fls->flashid) { case SPIFLASH_MID_GD: fls->OTPWRParam.eraseSize = 1024; break; case SPIFLASH_MID_FUDANMICRO: fls->OTPWRParam.eraseSize = 1024; if (fls->density <= (1 << 20)) { // 8Mbit fls->OTPWRParam.eraseSize = 256; } break; case SPIFLASH_MID_TSINGTENG: case SPIFLASH_MID_BOYA: case SPIFLASH_MID_XMC: case SPIFLASH_MID_WINBOND: fls->OTPWRParam.eraseSize = 256; break; case SPIFLASH_MID_PUYA: fls->OTPWRParam.eraseSize = 512; break; case SPIFLASH_MID_XTX: case SPIFLASH_MID_ESMT: fls->OTPWRParam.eraseSize = 0; // not support break; default: tls_mem_free(fls); TLS_DBGPRT_ERR("flash is not supported!\n"); return TLS_FLS_STATUS_ENOSUPPORT; } inside_fls = fls; return TLS_FLS_STATUS_OK; } int tls_fls_exit(void) { TLS_DBGPRT_FLASH_INFO("Not support flash driver module uninstalled!\n"); return TLS_FLS_STATUS_EPERM; } /** * @brief This function is used to initialize system parameter postion by flash density * * @param None * * @retval None * * @note must be called before function tls_param_init */ void tls_fls_sys_param_postion_init(void) { unsigned int density = 0; int err; err = tls_fls_get_param(TLS_FLS_PARAM_TYPE_SIZE, (void *)&density); if (TLS_FLS_STATUS_OK == err) { TLS_FLASH_END_ADDR = (FLASH_BASE_ADDR|density) - 1; TLS_FLASH_OTA_FLAG_ADDR = (FLASH_BASE_ADDR|density) - 0x1000; TLS_FLASH_PARAM_RESTORE_ADDR = (FLASH_BASE_ADDR|density) - 0x2000; TLS_FLASH_PARAM2_ADDR = (FLASH_BASE_ADDR|density) - 0x3000; TLS_FLASH_PARAM1_ADDR = (FLASH_BASE_ADDR|density) - 0x4000; TLS_FLASH_PARAM_DEFAULT = (FLASH_BASE_ADDR|density) - 0x5000; } else { TLS_DBGPRT_ERR("system parameter postion use default!\n"); } }