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