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 arm 'virt' machine, with:
17 *
18 * 64M = 1 region * 256 Erase Blocks * 256K(2 banks: 64 pages * 4096B)
19 * 32 bits, Intel command set
20 */
21
22 #include "user_copy.h"
23 #include "disk.h"
24 #include "cfiflash.h"
25 #include "cfiflash_internal.h"
26
27 #define BIT_SHIFT8 8
28 #define WORD_ALIGN 4
29 #define BYTE_WORD_SHIFT 2
30
CfiFlashSec2Bytes(unsigned sector)31 static inline unsigned CfiFlashSec2Bytes(unsigned sector)
32 {
33 return sector << CFIFLASH_SEC_SIZE_BITS;
34 }
35
CfiFlashPageWordOffset(unsigned wordOffset)36 static inline unsigned CfiFlashPageWordOffset(unsigned wordOffset)
37 {
38 return wordOffset & CFIFLASH_PAGE_WORDS_MASK;
39 }
40
CfiFlashEraseBlkWordAddr(unsigned wordOffset)41 static inline unsigned CfiFlashEraseBlkWordAddr(unsigned wordOffset)
42 {
43 return wordOffset & CFIFLASH_ERASEBLK_WORDMASK;
44 }
45
W2B(unsigned words)46 static inline unsigned W2B(unsigned words)
47 {
48 return words << BYTE_WORD_SHIFT;
49 }
50
B2W(unsigned bytes)51 static inline unsigned B2W(unsigned bytes)
52 {
53 return bytes >> BYTE_WORD_SHIFT;
54 }
55
CfiFlashQueryQRY(uint8_t * p)56 static inline int CfiFlashQueryQRY(uint8_t *p)
57 {
58 unsigned wordOffset = CFIFLASH_QUERY_QRY;
59
60 if (p[W2B(wordOffset++)] == 'Q') {
61 if (p[W2B(wordOffset++)] == 'R') {
62 if (p[W2B(wordOffset)] == 'Y') {
63 return 0;
64 }
65 }
66 }
67 return -1;
68 }
69
CfiFlashQueryUint8(unsigned wordOffset,uint8_t expect,uint8_t * p)70 static inline int CfiFlashQueryUint8(unsigned wordOffset, uint8_t expect, uint8_t *p)
71 {
72 if (p[W2B(wordOffset)] != expect) {
73 return -1;
74 }
75 return 0;
76 }
77
CfiFlashQueryUint16(unsigned wordOffset,uint16_t expect,uint8_t * p)78 static inline int CfiFlashQueryUint16(unsigned wordOffset, uint16_t expect, uint8_t *p)
79 {
80 uint16_t v;
81
82 v = (p[W2B(wordOffset + 1)] << BIT_SHIFT8) + p[W2B(wordOffset)];
83 if (v != expect) {
84 return -1;
85 }
86 return 0;
87 }
88
CfiFlashIsReady(unsigned wordOffset,uint32_t * p)89 static inline int CfiFlashIsReady(unsigned wordOffset, uint32_t *p)
90 {
91 DSB;
92 p[wordOffset] = CFIFLASH_CMD_READ_STATUS;
93 DSB;
94 return p[wordOffset] & CFIFLASH_STATUS_READY_MASK;
95 }
96
97 /* all in word(4 bytes) measure */
CfiFlashWriteBuf(unsigned wordOffset,const uint32_t * buffer,size_t words,uint32_t * p)98 static void CfiFlashWriteBuf(unsigned wordOffset, const uint32_t *buffer, size_t words, uint32_t *p)
99 {
100 unsigned i, blkAddr, wordCount;
101
102 /* first write might not be Page aligned */
103 i = CFIFLASH_PAGE_WORDS - CfiFlashPageWordOffset(wordOffset);
104 wordCount = (i > words) ? words : i;
105
106 while (words) {
107 /* command buffer-write begin to Erase Block address */
108 blkAddr = CfiFlashEraseBlkWordAddr(wordOffset);
109 p[blkAddr] = CFIFLASH_CMD_BUFWRITE;
110
111 /* write words count, 0-based */
112 DSB;
113 p[blkAddr] = wordCount - 1;
114
115 /* program word data to actual address */
116 for (i = 0; i < wordCount; i++, wordOffset++, buffer++) {
117 p[wordOffset] = *buffer;
118 }
119
120 /* command buffer-write end to Erase Block address */
121 p[blkAddr] = CFIFLASH_CMD_CONFIRM;
122 while (!CfiFlashIsReady(blkAddr, p)) { }
123
124 words -= wordCount;
125 wordCount = (words >= CFIFLASH_PAGE_WORDS) ? CFIFLASH_PAGE_WORDS : words;
126 }
127
128 p[0] = CFIFLASH_CMD_CLEAR_STATUS;
129 }
130
CfiFlashQuery(uint8_t * p)131 static int CfiFlashQuery(uint8_t *p)
132 {
133 uint32_t *base = (uint32_t *)p;
134 base[CFIFLASH_QUERY_BASE] = CFIFLASH_QUERY_CMD;
135
136 if (CfiFlashQueryQRY(p)) {
137 goto ERR_OUT;
138 }
139
140 if (CfiFlashQueryUint16(CFIFLASH_QUERY_VENDOR, CFIFLASH_EXPECT_VENDOR, p)) {
141 goto ERR_OUT;
142 }
143
144 if (CfiFlashQueryUint8(CFIFLASH_QUERY_SIZE, CFIFLASH_ONE_BANK_BITS, p)) {
145 goto ERR_OUT;
146 }
147
148 if (CfiFlashQueryUint16(CFIFLASH_QUERY_PAGE_BITS, CFIFLASH_EXPECT_PAGE_BITS, p)) {
149 goto ERR_OUT;
150 }
151
152 if (CfiFlashQueryUint8(CFIFLASH_QUERY_ERASE_REGION, CFIFLASH_EXPECT_ERASE_REGION, p)) {
153 goto ERR_OUT;
154 }
155
156 if (CfiFlashQueryUint16(CFIFLASH_QUERY_BLOCKS, CFIFLASH_EXPECT_BLOCKS, p)) {
157 goto ERR_OUT;
158 }
159
160 if (CfiFlashQueryUint16(CFIFLASH_QUERY_BLOCK_SIZE, CFIFLASH_EXPECT_BLOCK_SIZE, p)) {
161 goto ERR_OUT;
162 }
163
164 base[0] = CFIFLASH_CMD_RESET;
165 return 0;
166
167 ERR_OUT:
168 dprintf("[%s]not supported CFI flash\n", __FUNCTION__);
169 return -1;
170 }
171
CfiFlashInit(uint8_t * p)172 int CfiFlashInit(uint8_t *p)
173 {
174 struct MtdDev *slot = GetCfiMtdDev();
175
176 dprintf("[%s]CFI flash init start ...\n", __FUNCTION__);
177 if (CfiFlashQuery(p)) {
178 return -1;
179 }
180
181 /* slot 0 used as MTD device for jffs2 rootfs, slot 1 as block device */
182 if (slot->priv == NULL) {
183 slot->priv = p;
184 } else if (*(uint16_t *)&p[BS_SIG55AA] == BS_SIG55AA_VALUE) {
185 int id = los_alloc_diskid_byname(CFI_BLK_DRIVER);
186 (void)los_disk_init(CFI_BLK_DRIVER, GetCfiBlkOps(), p, id, NULL);
187 } else {
188 return 0;
189 }
190
191 dprintf("[%s]CFI flash init end ...\n", __FUNCTION__);
192 return 0;
193 }
194
CfiBlkOpen(struct Vnode * vnode)195 int CfiBlkOpen(struct Vnode *vnode)
196 {
197 return 0;
198 }
199
CfiBlkClose(struct Vnode * vnode)200 int CfiBlkClose(struct Vnode *vnode)
201 {
202 return 0;
203 }
204
CfiPreRead(char * buffer,unsigned bytes,char ** newbuf)205 static ssize_t CfiPreRead(char *buffer, unsigned bytes, char **newbuf)
206 {
207 if (LOS_IsUserAddressRange((VADDR_T)buffer, bytes)) {
208 *newbuf = LOS_MemAlloc(m_aucSysMem0, bytes);
209 if (*newbuf == NULL) {
210 dprintf("[%s]fatal memory allocation error\n", __FUNCTION__);
211 return -ENOMEM;
212 }
213 } else if ((VADDR_T)buffer + bytes < (VADDR_T)buffer) {
214 dprintf("[%s]invalid argument: buffer=%#x, size=%#x\n", __FUNCTION__, buffer, bytes);
215 return -EFAULT;
216 } else {
217 *newbuf = buffer;
218 }
219 return 0;
220 }
221
CfiPostRead(char * buffer,char * newbuf,unsigned bytes,ssize_t ret)222 static ssize_t CfiPostRead(char *buffer, char *newbuf, unsigned bytes, ssize_t ret)
223 {
224 if (newbuf != buffer) {
225 if (LOS_ArchCopyToUser(buffer, newbuf, bytes) != 0) {
226 dprintf("[%s]LOS_ArchCopyToUser error\n", __FUNCTION__);
227 ret = -EFAULT;
228 }
229
230 if (LOS_MemFree(m_aucSysMem0, newbuf) != 0) {
231 dprintf("[%s]LOS_MemFree error\n", __FUNCTION__);
232 ret = -EFAULT;
233 }
234 }
235 return ret;
236 }
237
CfiBlkRead(struct Vnode * vnode,unsigned char * buffer,unsigned long long startSector,unsigned int nSectors)238 ssize_t CfiBlkRead(struct Vnode *vnode, unsigned char *buffer,
239 unsigned long long startSector, unsigned int nSectors)
240 {
241 unsigned int i, wordOffset, bytes;
242 uint32_t *p;
243 uint32_t *base = ((struct drv_data*)(vnode->data))->priv;
244 ssize_t ret;
245
246 bytes = CfiFlashSec2Bytes(nSectors);
247 wordOffset = B2W(CfiFlashSec2Bytes(startSector));
248
249 if ((ret = CfiPreRead((char*)buffer, bytes, (char**)&p))) {
250 return ret;
251 }
252
253 for (i = 0; i < B2W(bytes); i++) {
254 p[i] = base[wordOffset + i];
255 }
256 ret = nSectors;
257
258 return CfiPostRead((char*)buffer, (char*)p, bytes, ret);
259 }
260
CfiPreWrite(const char * buffer,unsigned bytes,char ** newbuf)261 static ssize_t CfiPreWrite(const char *buffer, unsigned bytes, char **newbuf)
262 {
263 if (LOS_IsUserAddressRange((VADDR_T)buffer, bytes)) {
264 *newbuf = LOS_MemAlloc(m_aucSysMem0, bytes);
265 if (*newbuf == NULL) {
266 dprintf("[%s]fatal memory allocation error\n", __FUNCTION__);
267 return -ENOMEM;
268 }
269
270 if (LOS_ArchCopyFromUser(*newbuf, buffer, bytes)) {
271 dprintf("[%s]LOS_ArchCopyFromUser error\n", __FUNCTION__);
272 LOS_MemFree(m_aucSysMem0, *newbuf);
273 return -EFAULT;
274 }
275 } else if ((VADDR_T)buffer + bytes < (VADDR_T)buffer) {
276 dprintf("[%s]invalid argument\n", __FUNCTION__);
277 return -EFAULT;
278 } else {
279 *newbuf = (char*)buffer;
280 }
281 return 0;
282 }
283
CfiPostWrite(const char * buffer,char * newbuf,ssize_t ret)284 static ssize_t CfiPostWrite(const char *buffer, char *newbuf, ssize_t ret)
285 {
286 if (newbuf != buffer) {
287 if (LOS_MemFree(m_aucSysMem0, newbuf) != 0) {
288 dprintf("[%s]LOS_MemFree error\n", __FUNCTION__);
289 return -EFAULT;
290 }
291 }
292 return ret;
293 }
294
CfiBlkWrite(struct Vnode * vnode,const unsigned char * buffer,unsigned long long startSector,unsigned int nSectors)295 ssize_t CfiBlkWrite(struct Vnode *vnode, const unsigned char *buffer,
296 unsigned long long startSector, unsigned int nSectors)
297 {
298 unsigned int wordOffset, bytes;
299 unsigned char *p;
300 ssize_t ret;
301
302 bytes = CfiFlashSec2Bytes(nSectors);
303 wordOffset = B2W(CfiFlashSec2Bytes(startSector));
304
305 if ((ret = CfiPreWrite((const char*)buffer, bytes, (char**)&p))) {
306 return ret;
307 }
308
309 CfiFlashWriteBuf(wordOffset, (uint32_t *)p, B2W(bytes), ((struct drv_data*)(vnode->data))->priv);
310 ret = nSectors;
311
312 return CfiPostWrite((const char*)buffer, (char*)p, ret);
313 }
314
CfiBlkGeometry(struct Vnode * vnode,struct geometry * geometry)315 int CfiBlkGeometry(struct Vnode *vnode, struct geometry *geometry)
316 {
317 geometry->geo_available = TRUE,
318 geometry->geo_mediachanged = FALSE;
319 geometry->geo_writeenabled = TRUE;
320 geometry->geo_nsectors = CFIFLASH_SECTORS;
321 geometry->geo_sectorsize = CFIFLASH_SEC_SIZE;
322
323 return 0;
324 }
325
CfiMtdErase(struct MtdDev * mtd,UINT64 start,UINT64 bytes,UINT64 * failAddr)326 int CfiMtdErase(struct MtdDev *mtd, UINT64 start, UINT64 bytes, UINT64 *failAddr)
327 {
328 uint32_t blkAddr, count, i;
329 uint32_t *p = mtd->priv;
330
331 blkAddr = CfiFlashEraseBlkWordAddr(B2W(start));
332 count = (CfiFlashEraseBlkWordAddr(B2W(start + bytes - 1)) - blkAddr) / CFIFLASH_ERASEBLK_WORDS + 1;
333
334 for (i = 0; i < count; i++) {
335 p[blkAddr] = CFIFLASH_CMD_ERASE;
336 DSB;
337 p[blkAddr] = CFIFLASH_CMD_CONFIRM;
338 while (!CfiFlashIsReady(blkAddr, p)) { }
339
340 blkAddr += CFIFLASH_ERASEBLK_WORDS;
341 }
342
343 p[0] = CFIFLASH_CMD_CLEAR_STATUS;
344 return 0;
345 }
346
CfiMtdRead(struct MtdDev * mtd,UINT64 start,UINT64 bytes,const char * buf)347 int CfiMtdRead(struct MtdDev *mtd, UINT64 start, UINT64 bytes, const char *buf)
348 {
349 UINT64 i;
350 char *p;
351 ssize_t ret;
352 uint8_t *base = mtd->priv;
353
354 if ((ret = CfiPreRead((char*)buf, bytes, &p))) {
355 return ret;
356 }
357
358 for (i = 0; i < bytes; i++) {
359 p[i] = base[start + i];
360 }
361 ret = (int)bytes;
362
363 return CfiPostRead((char*)buf, p, bytes, ret);
364 }
365
CfiMtdWrite(struct MtdDev * mtd,UINT64 start,UINT64 bytes,const char * buf)366 int CfiMtdWrite(struct MtdDev *mtd, UINT64 start, UINT64 bytes, const char *buf)
367 {
368 char *p;
369 ssize_t ret;
370
371 if (!IS_ALIGNED(start, WORD_ALIGN) || !IS_ALIGNED(bytes, WORD_ALIGN)) {
372 dprintf("[%s]not aligned with 4B: start=%#0llx, bytes=%#0llx\n", __FUNCTION__, start, bytes);
373 return -EINVAL;
374 }
375
376 if ((ret = CfiPreWrite(buf, bytes, &p))) {
377 return ret;
378 }
379
380 CfiFlashWriteBuf((int)B2W(start), (uint32_t *)p, (size_t)B2W(bytes), mtd->priv);
381 ret = (int)bytes;
382
383 return CfiPostWrite(buf, p, ret);
384 }
385