1d6aed566Sopenharmony_ci/* 2d6aed566Sopenharmony_ci * Copyright (c) 2020-2021 Huawei Device Co., Ltd. 3d6aed566Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License"); 4d6aed566Sopenharmony_ci * you may not use this file except in compliance with the License. 5d6aed566Sopenharmony_ci * You may obtain a copy of the License at 6d6aed566Sopenharmony_ci * 7d6aed566Sopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0 8d6aed566Sopenharmony_ci * 9d6aed566Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software 10d6aed566Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS, 11d6aed566Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12d6aed566Sopenharmony_ci * See the License for the specific language governing permissions and 13d6aed566Sopenharmony_ci * limitations under the License. 14d6aed566Sopenharmony_ci */ 15d6aed566Sopenharmony_ci/* 16d6aed566Sopenharmony_ci * simple CFI flash driver for QEMU riscv 'virt' machine, with: 17d6aed566Sopenharmony_ci * 18d6aed566Sopenharmony_ci * 32M = 1 region * 128 Erase Blocks * 256K(2 banks: 64 pages * 4096B) 19d6aed566Sopenharmony_ci * 32 bits, Intel command set 20d6aed566Sopenharmony_ci */ 21d6aed566Sopenharmony_ci 22d6aed566Sopenharmony_ci#include "stdint.h" 23d6aed566Sopenharmony_ci#include "stddef.h" 24d6aed566Sopenharmony_ci#include "los_arch_context.h" 25d6aed566Sopenharmony_ci#include "los_debug.h" 26d6aed566Sopenharmony_ci#include "los_compiler.h" 27d6aed566Sopenharmony_ci#include "cfiflash.h" 28d6aed566Sopenharmony_ci#include "cfiflash_internal.h" 29d6aed566Sopenharmony_ci 30d6aed566Sopenharmony_ci#define BIT_SHIFT8 8 31d6aed566Sopenharmony_ci#define BYTE_WORD_SHIFT 2 32d6aed566Sopenharmony_ci 33d6aed566Sopenharmony_cistatic uint32_t g_cfiDrvBase[CFIFLASH_MAX_NUM]; 34d6aed566Sopenharmony_ci 35d6aed566Sopenharmony_cistatic uint8_t *GetCfiDrvPriv(uint32_t pdrv) 36d6aed566Sopenharmony_ci{ 37d6aed566Sopenharmony_ci uint8_t *ret = NULL; 38d6aed566Sopenharmony_ci if (pdrv >= 0 && pdrv < CFIFLASH_MAX_NUM) { 39d6aed566Sopenharmony_ci ret = (uint8_t *)g_cfiDrvBase[pdrv]; 40d6aed566Sopenharmony_ci } 41d6aed566Sopenharmony_ci 42d6aed566Sopenharmony_ci if (ret == NULL) { 43d6aed566Sopenharmony_ci PRINT_ERR("Get CfiFlash driver base failed!\n"); 44d6aed566Sopenharmony_ci } 45d6aed566Sopenharmony_ci 46d6aed566Sopenharmony_ci return ret; 47d6aed566Sopenharmony_ci} 48d6aed566Sopenharmony_ci 49d6aed566Sopenharmony_cistatic int SetCfiDrvPriv(uint32_t pdrv, uint32_t priv) 50d6aed566Sopenharmony_ci{ 51d6aed566Sopenharmony_ci g_cfiDrvBase[pdrv] = priv; 52d6aed566Sopenharmony_ci return FLASH_OK; 53d6aed566Sopenharmony_ci} 54d6aed566Sopenharmony_ci 55d6aed566Sopenharmony_ciunsigned CfiFlashSec2Bytes(unsigned sector) 56d6aed566Sopenharmony_ci{ 57d6aed566Sopenharmony_ci return sector << CFIFLASH_SEC_SIZE_BITS; 58d6aed566Sopenharmony_ci} 59d6aed566Sopenharmony_ci 60d6aed566Sopenharmony_cistatic inline unsigned CfiFlashPageWordOffset(unsigned wordOffset) 61d6aed566Sopenharmony_ci{ 62d6aed566Sopenharmony_ci return wordOffset & CFIFLASH_PAGE_WORDS_MASK; 63d6aed566Sopenharmony_ci} 64d6aed566Sopenharmony_ci 65d6aed566Sopenharmony_cistatic inline unsigned CfiFlashEraseBlkWordAddr(unsigned wordOffset) 66d6aed566Sopenharmony_ci{ 67d6aed566Sopenharmony_ci return wordOffset & CFIFLASH_ERASEBLK_WORDMASK; 68d6aed566Sopenharmony_ci} 69d6aed566Sopenharmony_ci 70d6aed566Sopenharmony_cistatic inline unsigned W2B(unsigned words) 71d6aed566Sopenharmony_ci{ 72d6aed566Sopenharmony_ci return words << BYTE_WORD_SHIFT; 73d6aed566Sopenharmony_ci} 74d6aed566Sopenharmony_ci 75d6aed566Sopenharmony_cistatic inline unsigned B2W(unsigned bytes) 76d6aed566Sopenharmony_ci{ 77d6aed566Sopenharmony_ci return bytes >> BYTE_WORD_SHIFT; 78d6aed566Sopenharmony_ci} 79d6aed566Sopenharmony_ci 80d6aed566Sopenharmony_cistatic inline int CfiFlashQueryQRY(uint8_t *p) 81d6aed566Sopenharmony_ci{ 82d6aed566Sopenharmony_ci unsigned wordOffset = CFIFLASH_QUERY_QRY; 83d6aed566Sopenharmony_ci 84d6aed566Sopenharmony_ci if (p[W2B(wordOffset++)] == 'Q') { 85d6aed566Sopenharmony_ci if (p[W2B(wordOffset++)] == 'R') { 86d6aed566Sopenharmony_ci if (p[W2B(wordOffset)] == 'Y') { 87d6aed566Sopenharmony_ci return FLASH_OK; 88d6aed566Sopenharmony_ci } 89d6aed566Sopenharmony_ci } 90d6aed566Sopenharmony_ci } 91d6aed566Sopenharmony_ci return FLASH_ERROR; 92d6aed566Sopenharmony_ci} 93d6aed566Sopenharmony_ci 94d6aed566Sopenharmony_cistatic inline int CfiFlashQueryUint8(unsigned wordOffset, uint8_t expect, uint8_t *p) 95d6aed566Sopenharmony_ci{ 96d6aed566Sopenharmony_ci if (p[W2B(wordOffset)] != expect) { 97d6aed566Sopenharmony_ci PRINT_ERR("[%s]name:0x%x value:%u expect:%u \n", __func__, wordOffset, p[W2B(wordOffset)], expect); 98d6aed566Sopenharmony_ci return FLASH_ERROR; 99d6aed566Sopenharmony_ci } 100d6aed566Sopenharmony_ci return FLASH_OK; 101d6aed566Sopenharmony_ci} 102d6aed566Sopenharmony_ci 103d6aed566Sopenharmony_cistatic inline int CfiFlashQueryUint16(unsigned wordOffset, uint16_t expect, uint8_t *p) 104d6aed566Sopenharmony_ci{ 105d6aed566Sopenharmony_ci uint16_t v; 106d6aed566Sopenharmony_ci 107d6aed566Sopenharmony_ci v = (p[W2B(wordOffset + 1)] << BIT_SHIFT8) + p[W2B(wordOffset)]; 108d6aed566Sopenharmony_ci if (v != expect) { 109d6aed566Sopenharmony_ci PRINT_ERR("[%s]name:0x%x value:%u expect:%u \n", __func__, wordOffset, v, expect); 110d6aed566Sopenharmony_ci return FLASH_ERROR; 111d6aed566Sopenharmony_ci } 112d6aed566Sopenharmony_ci return FLASH_OK; 113d6aed566Sopenharmony_ci} 114d6aed566Sopenharmony_ci 115d6aed566Sopenharmony_cistatic inline int CfiFlashIsReady(unsigned wordOffset, uint32_t *p) 116d6aed566Sopenharmony_ci{ 117d6aed566Sopenharmony_ci dsb(); 118d6aed566Sopenharmony_ci p[wordOffset] = CFIFLASH_CMD_READ_STATUS; 119d6aed566Sopenharmony_ci dsb(); 120d6aed566Sopenharmony_ci return p[wordOffset] & CFIFLASH_STATUS_READY_MASK; 121d6aed566Sopenharmony_ci} 122d6aed566Sopenharmony_ci 123d6aed566Sopenharmony_ci/* all in word(4 bytes) measure */ 124d6aed566Sopenharmony_cistatic void CfiFlashWriteBuf(unsigned wordOffset, const uint32_t *buffer, size_t words, uint32_t *p) 125d6aed566Sopenharmony_ci{ 126d6aed566Sopenharmony_ci unsigned blkAddr = 0; 127d6aed566Sopenharmony_ci 128d6aed566Sopenharmony_ci /* first write might not be Page aligned */ 129d6aed566Sopenharmony_ci unsigned i = CFIFLASH_PAGE_WORDS - CfiFlashPageWordOffset(wordOffset); 130d6aed566Sopenharmony_ci unsigned wordCount = (i > words) ? words : i; 131d6aed566Sopenharmony_ci 132d6aed566Sopenharmony_ci while (words) { 133d6aed566Sopenharmony_ci /* command buffer-write begin to Erase Block address */ 134d6aed566Sopenharmony_ci blkAddr = CfiFlashEraseBlkWordAddr(wordOffset); 135d6aed566Sopenharmony_ci p[blkAddr] = CFIFLASH_CMD_BUFWRITE; 136d6aed566Sopenharmony_ci 137d6aed566Sopenharmony_ci /* write words count, 0-based */ 138d6aed566Sopenharmony_ci dsb(); 139d6aed566Sopenharmony_ci p[blkAddr] = wordCount - 1; 140d6aed566Sopenharmony_ci 141d6aed566Sopenharmony_ci /* program word data to actual address */ 142d6aed566Sopenharmony_ci for (i = 0; i < wordCount; i++, wordOffset++, buffer++) { 143d6aed566Sopenharmony_ci p[wordOffset] = *buffer; 144d6aed566Sopenharmony_ci } 145d6aed566Sopenharmony_ci 146d6aed566Sopenharmony_ci /* command buffer-write end to Erase Block address */ 147d6aed566Sopenharmony_ci p[blkAddr] = CFIFLASH_CMD_CONFIRM; 148d6aed566Sopenharmony_ci while (!CfiFlashIsReady(blkAddr, p)) { } 149d6aed566Sopenharmony_ci 150d6aed566Sopenharmony_ci words -= wordCount; 151d6aed566Sopenharmony_ci wordCount = (words >= CFIFLASH_PAGE_WORDS) ? CFIFLASH_PAGE_WORDS : words; 152d6aed566Sopenharmony_ci } 153d6aed566Sopenharmony_ci 154d6aed566Sopenharmony_ci p[0] = CFIFLASH_CMD_CLEAR_STATUS; 155d6aed566Sopenharmony_ci} 156d6aed566Sopenharmony_ci 157d6aed566Sopenharmony_ciint CfiFlashQuery(uint32_t pdrv) 158d6aed566Sopenharmony_ci{ 159d6aed566Sopenharmony_ci uint8_t *p = GetCfiDrvPriv(pdrv); 160d6aed566Sopenharmony_ci if (p == NULL) { 161d6aed566Sopenharmony_ci return FLASH_ERROR; 162d6aed566Sopenharmony_ci } 163d6aed566Sopenharmony_ci uint32_t *base = (uint32_t *)p; 164d6aed566Sopenharmony_ci base[CFIFLASH_QUERY_BASE] = CFIFLASH_QUERY_CMD; 165d6aed566Sopenharmony_ci 166d6aed566Sopenharmony_ci dsb(); 167d6aed566Sopenharmony_ci if (CfiFlashQueryQRY(p)) { 168d6aed566Sopenharmony_ci PRINT_ERR("[%s: %d]not supported CFI flash : not found QRY\n", __func__, __LINE__); 169d6aed566Sopenharmony_ci return FLASH_ERROR; 170d6aed566Sopenharmony_ci } 171d6aed566Sopenharmony_ci 172d6aed566Sopenharmony_ci if (CfiFlashQueryUint16(CFIFLASH_QUERY_VENDOR, CFIFLASH_EXPECT_VENDOR, p)) { 173d6aed566Sopenharmony_ci PRINT_ERR("[%s: %d]not supported CFI flash : unexpected VENDOR\n", __func__, __LINE__); 174d6aed566Sopenharmony_ci return FLASH_ERROR; 175d6aed566Sopenharmony_ci } 176d6aed566Sopenharmony_ci 177d6aed566Sopenharmony_ci if (CfiFlashQueryUint8(CFIFLASH_QUERY_SIZE, CFIFLASH_ONE_BANK_BITS, p)) { 178d6aed566Sopenharmony_ci PRINT_ERR("[%s: %d]not supported CFI flash : unexpected BANK_BITS\n", __func__, __LINE__); 179d6aed566Sopenharmony_ci return FLASH_ERROR; 180d6aed566Sopenharmony_ci } 181d6aed566Sopenharmony_ci 182d6aed566Sopenharmony_ci if (CfiFlashQueryUint16(CFIFLASH_QUERY_PAGE_BITS, CFIFLASH_EXPECT_PAGE_BITS, p)) { 183d6aed566Sopenharmony_ci PRINT_ERR("[%s: %d]not supported CFI flash : unexpected PAGE_BITS\n", __func__, __LINE__); 184d6aed566Sopenharmony_ci return FLASH_ERROR; 185d6aed566Sopenharmony_ci } 186d6aed566Sopenharmony_ci 187d6aed566Sopenharmony_ci if (CfiFlashQueryUint8(CFIFLASH_QUERY_ERASE_REGION, CFIFLASH_EXPECT_ERASE_REGION, p)) { 188d6aed566Sopenharmony_ci PRINT_ERR("[%s: %d]not supported CFI flash : unexpected ERASE_REGION\n", __func__, __LINE__); 189d6aed566Sopenharmony_ci return FLASH_ERROR; 190d6aed566Sopenharmony_ci } 191d6aed566Sopenharmony_ci 192d6aed566Sopenharmony_ci if (CfiFlashQueryUint16(CFIFLASH_QUERY_BLOCKS, CFIFLASH_EXPECT_BLOCKS, p)) { 193d6aed566Sopenharmony_ci PRINT_ERR("[%s: %d]not supported CFI flash : unexpected BLOCKS\n", __func__, __LINE__); 194d6aed566Sopenharmony_ci return FLASH_ERROR; 195d6aed566Sopenharmony_ci } 196d6aed566Sopenharmony_ci 197d6aed566Sopenharmony_ci if (CfiFlashQueryUint16(CFIFLASH_QUERY_BLOCK_SIZE, CFIFLASH_EXPECT_BLOCK_SIZE, p)) { 198d6aed566Sopenharmony_ci PRINT_ERR("[%s: %d]not supported CFI flash : unexpected BLOCK_SIZE\n", __func__, __LINE__); 199d6aed566Sopenharmony_ci return FLASH_ERROR; 200d6aed566Sopenharmony_ci } 201d6aed566Sopenharmony_ci 202d6aed566Sopenharmony_ci base[0] = CFIFLASH_CMD_RESET; 203d6aed566Sopenharmony_ci return FLASH_OK; 204d6aed566Sopenharmony_ci} 205d6aed566Sopenharmony_ci 206d6aed566Sopenharmony_ciint CfiFlashInit(uint32_t pdrv, uint32_t priv) 207d6aed566Sopenharmony_ci{ 208d6aed566Sopenharmony_ci return SetCfiDrvPriv(pdrv, priv); 209d6aed566Sopenharmony_ci} 210d6aed566Sopenharmony_ci 211d6aed566Sopenharmony_ciint32_t CfiFlashRead(uint32_t pdrv, uint32_t *buffer, uint32_t offset, uint32_t nbytes) 212d6aed566Sopenharmony_ci{ 213d6aed566Sopenharmony_ci uint32_t i = 0; 214d6aed566Sopenharmony_ci 215d6aed566Sopenharmony_ci if ((offset + nbytes) > CFIFLASH_CAPACITY) { 216d6aed566Sopenharmony_ci PRINT_ERR("flash over read, offset:%d, nbytes:%d\n", offset, nbytes); 217d6aed566Sopenharmony_ci return FLASH_ERROR; 218d6aed566Sopenharmony_ci } 219d6aed566Sopenharmony_ci 220d6aed566Sopenharmony_ci uint8_t *pbase = GetCfiDrvPriv(pdrv); 221d6aed566Sopenharmony_ci if (pbase == NULL) { 222d6aed566Sopenharmony_ci return FLASH_ERROR; 223d6aed566Sopenharmony_ci } 224d6aed566Sopenharmony_ci uint32_t *base = (uint32_t *)pbase; 225d6aed566Sopenharmony_ci 226d6aed566Sopenharmony_ci unsigned int words = B2W(nbytes); 227d6aed566Sopenharmony_ci unsigned int wordOffset = B2W(offset); 228d6aed566Sopenharmony_ci 229d6aed566Sopenharmony_ci uint32_t intSave = LOS_IntLock(); 230d6aed566Sopenharmony_ci for (i = 0; i < words; i++) { 231d6aed566Sopenharmony_ci buffer[i] = base[wordOffset + i]; 232d6aed566Sopenharmony_ci } 233d6aed566Sopenharmony_ci LOS_IntRestore(intSave); 234d6aed566Sopenharmony_ci return FLASH_OK; 235d6aed566Sopenharmony_ci} 236d6aed566Sopenharmony_ci 237d6aed566Sopenharmony_ciint32_t CfiFlashWrite(uint32_t pdrv, const uint32_t *buffer, uint32_t offset, uint32_t nbytes) 238d6aed566Sopenharmony_ci{ 239d6aed566Sopenharmony_ci if ((offset + nbytes) > CFIFLASH_CAPACITY) { 240d6aed566Sopenharmony_ci PRINT_ERR("flash over write, offset:%d, nbytes:%d\n", offset, nbytes); 241d6aed566Sopenharmony_ci return FLASH_ERROR; 242d6aed566Sopenharmony_ci } 243d6aed566Sopenharmony_ci 244d6aed566Sopenharmony_ci uint8_t *pbase = GetCfiDrvPriv(pdrv); 245d6aed566Sopenharmony_ci if (pbase == NULL) { 246d6aed566Sopenharmony_ci return FLASH_ERROR; 247d6aed566Sopenharmony_ci } 248d6aed566Sopenharmony_ci uint32_t *base = (uint32_t *)pbase; 249d6aed566Sopenharmony_ci 250d6aed566Sopenharmony_ci unsigned int words = B2W(nbytes); 251d6aed566Sopenharmony_ci unsigned int wordOffset = B2W(offset); 252d6aed566Sopenharmony_ci 253d6aed566Sopenharmony_ci uint32_t intSave = LOS_IntLock(); 254d6aed566Sopenharmony_ci CfiFlashWriteBuf(wordOffset, buffer, words, base); 255d6aed566Sopenharmony_ci LOS_IntRestore(intSave); 256d6aed566Sopenharmony_ci 257d6aed566Sopenharmony_ci return FLASH_OK; 258d6aed566Sopenharmony_ci} 259d6aed566Sopenharmony_ci 260d6aed566Sopenharmony_ciint32_t CfiFlashErase(uint32_t pdrv, uint32_t offset) 261d6aed566Sopenharmony_ci{ 262d6aed566Sopenharmony_ci if (offset > CFIFLASH_CAPACITY) { 263d6aed566Sopenharmony_ci PRINT_ERR("flash over erase, offset:%d\n", offset); 264d6aed566Sopenharmony_ci return FLASH_ERROR; 265d6aed566Sopenharmony_ci } 266d6aed566Sopenharmony_ci 267d6aed566Sopenharmony_ci uint8_t *pbase = GetCfiDrvPriv(pdrv); 268d6aed566Sopenharmony_ci if (pbase == NULL) { 269d6aed566Sopenharmony_ci return FLASH_ERROR; 270d6aed566Sopenharmony_ci } 271d6aed566Sopenharmony_ci uint32_t *base = (uint32_t *)pbase; 272d6aed566Sopenharmony_ci 273d6aed566Sopenharmony_ci uint32_t blkAddr = CfiFlashEraseBlkWordAddr(B2W(offset)); 274d6aed566Sopenharmony_ci 275d6aed566Sopenharmony_ci uint32_t intSave = LOS_IntLock(); 276d6aed566Sopenharmony_ci base[blkAddr] = CFIFLASH_CMD_ERASE; 277d6aed566Sopenharmony_ci dsb(); 278d6aed566Sopenharmony_ci base[blkAddr] = CFIFLASH_CMD_CONFIRM; 279d6aed566Sopenharmony_ci while (!CfiFlashIsReady(blkAddr, base)) { } 280d6aed566Sopenharmony_ci base[0] = CFIFLASH_CMD_CLEAR_STATUS; 281d6aed566Sopenharmony_ci LOS_IntRestore(intSave); 282d6aed566Sopenharmony_ci 283d6aed566Sopenharmony_ci return FLASH_OK; 284d6aed566Sopenharmony_ci}