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}