]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - processor-sdk/pdk.git/blob - packages/ti/board/src/flash/nor/qspi/nor_qspi.c
da4aeef7adb8de34fece689d63c454cc1e6d5b54
[processor-sdk/pdk.git] / packages / ti / board / src / flash / nor / qspi / nor_qspi.c
1 /*\r
2  * Copyright (c) 2016 - 2019, Texas Instruments Incorporated\r
3  * All rights reserved.\r
4  *\r
5  * Redistribution and use in source and binary forms, with or without\r
6  * modification, are permitted provided that the following conditions\r
7  * are met:\r
8  *\r
9  * *  Redistributions of source code must retain the above copyright\r
10  *    notice, this list of conditions and the following disclaimer.\r
11  *\r
12  * *  Redistributions in binary form must reproduce the above copyright\r
13  *    notice, this list of conditions and the following disclaimer in the\r
14  *    documentation and/or other materials provided with the distribution.\r
15  *\r
16  * *  Neither the name of Texas Instruments Incorporated nor the names of\r
17  *    its contributors may be used to endorse or promote products derived\r
18  *    from this software without specific prior written permission.\r
19  *\r
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,\r
22  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\r
23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\r
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\r
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\r
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;\r
27  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\r
28  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\r
29  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,\r
30  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
31  *\r
32  */\r
33 \r
34 #include "board_utils.h"\r
35 #include <ti/board/src/flash/nor/qspi/nor_qspi.h>\r
36 #include <ti/csl/soc.h>\r
37 \r
38 #define SPI_CONFIG_OFFSET     CSL_SPI_CNT\r
39 \r
40 static NOR_HANDLE Nor_qspiOpen(uint32_t norIntf, uint32_t portNum, void *params);\r
41 static void Nor_qspiClose(NOR_HANDLE handle);\r
42 static NOR_STATUS Nor_qspiRead(NOR_HANDLE handle, uint32_t addr,\r
43                                uint32_t len, uint8_t *buf, uint32_t mode);\r
44 static NOR_STATUS Nor_qspiWrite(NOR_HANDLE handle, uint32_t addr,\r
45                                 uint32_t len, uint8_t *buf, uint32_t mode);\r
46 static NOR_STATUS Nor_qspiErase(NOR_HANDLE handle, int32_t erLoc, bool blkErase);\r
47 \r
48 /* NOR function table for NOR QSPI interface implementation */\r
49 const NOR_FxnTable Nor_qspiFxnTable =\r
50 {\r
51     &Nor_qspiOpen,\r
52     &Nor_qspiClose,\r
53     &Nor_qspiRead,\r
54     &Nor_qspiWrite,\r
55     &Nor_qspiErase,\r
56 };\r
57 \r
58 NOR_Info Nor_qspiInfo =\r
59 {\r
60     0,                          /* hwHandle */\r
61     0,                          /* manufacturerId */\r
62     0,                          /* deviceId */\r
63     0,                          /* busWidth */\r
64     NOR_NUM_BLOCKS,            /* blockCnt */\r
65     NOR_NUM_PAGES_PER_BLOCK,   /* pageCnt */\r
66     NOR_PAGE_SIZE,             /* pageSize */\r
67     0,                         /* baseAddr */\r
68     NOR_SECTOR_SIZE            /* sectorSize */\r
69 };\r
70 \r
71 static NOR_STATUS NOR_qspiCmdRead(SPI_Handle handle, uint8_t *cmdBuf,\r
72                             uint32_t cmdLen, uint8_t *rxBuf, uint32_t rxLen)\r
73 {\r
74     SPI_Transaction  transaction;\r
75     uint32_t         transferType = SPI_TRANSACTION_TYPE_READ;\r
76     bool             ret;\r
77 \r
78     /* Update the mode and transfer type with the required values */\r
79     SPI_control(handle, SPI_V0_CMD_SETCONFIGMODE, NULL);\r
80     SPI_control(handle, SPI_V0_CMD_TRANSFERMODE_RW, (void *)&transferType);\r
81 \r
82     transaction.txBuf = (void *)cmdBuf;\r
83     transaction.rxBuf = (void *)rxBuf;\r
84     transaction.count = cmdLen + rxLen;\r
85 \r
86     ret = SPI_transfer(handle, &transaction);\r
87     if (ret == true)\r
88     {\r
89         return NOR_PASS;\r
90     }\r
91         else\r
92     {\r
93         return NOR_FAIL;\r
94     }\r
95 }\r
96 \r
97 static NOR_STATUS Nor_qspiReadId(SPI_Handle handle)\r
98 {\r
99     NOR_STATUS  retVal;\r
100     uint8_t     idCode[NOR_RDID_NUM_BYTES];\r
101     uint8_t     cmd = NOR_CMD_RDID;\r
102     uint32_t    manfID, devID;\r
103 \r
104     retVal = NOR_qspiCmdRead(handle, &cmd, 1, idCode, NOR_RDID_NUM_BYTES);\r
105     if (retVal == NOR_PASS)\r
106     {\r
107         manfID = (uint32_t)idCode[0];\r
108         devID = ((uint32_t)idCode[1] << 8) | ((uint32_t)idCode[2]);\r
109         if ((manfID == NOR_MANF_ID) && (devID == NOR_DEVICE_ID))\r
110         {\r
111             Nor_qspiInfo.manufacturerId = manfID;\r
112             Nor_qspiInfo.deviceId = devID;\r
113         }\r
114         else\r
115         {\r
116             retVal = NOR_FAIL;\r
117         }\r
118     }\r
119 \r
120     return (retVal);\r
121 }\r
122 \r
123 NOR_HANDLE Nor_qspiOpen(uint32_t norIntf, uint32_t portNum, void *params)\r
124 {\r
125     SPI_Params      spiParams;  /* SPI params structure */\r
126     SPI_Handle      hwHandle;  /* SPI handle */\r
127     NOR_HANDLE      norHandle = 0;\r
128 \r
129     /* Init SPI driver */\r
130     SPI_init();\r
131 \r
132     if (params)\r
133     {\r
134                 memcpy(&spiParams, params, sizeof(SPI_Params));\r
135     }\r
136     else\r
137     {\r
138         /* Use default SPI config params if no params provided */\r
139                 SPI_Params_init(&spiParams);\r
140     }\r
141     hwHandle = (SPI_Handle)SPI_open(portNum + SPI_CONFIG_OFFSET, &spiParams);\r
142 \r
143     if (hwHandle)\r
144     {\r
145         if (Nor_qspiReadId(hwHandle) == NOR_PASS)\r
146         {\r
147             Nor_qspiInfo.hwHandle = (uint32_t)hwHandle;\r
148             norHandle = (NOR_HANDLE)(&Nor_qspiInfo);\r
149         }\r
150         }\r
151 \r
152     return (norHandle);\r
153 }\r
154 \r
155 void Nor_qspiClose(NOR_HANDLE handle)\r
156 {\r
157     NOR_Info    *norQspiInfo;\r
158     SPI_Handle   spiHandle;\r
159 \r
160     if (handle)\r
161     {\r
162         norQspiInfo = (NOR_Info *)handle;\r
163         spiHandle = (SPI_Handle)norQspiInfo->hwHandle;\r
164 \r
165         if (spiHandle)\r
166         {\r
167             SPI_close(spiHandle);\r
168         }\r
169     }\r
170 }\r
171 \r
172 static NOR_STATUS Nor_qspiCmdWrite(SPI_Handle handle, uint8_t *cmdBuf,\r
173                                         uint32_t cmdLen, uint32_t dataLen)\r
174 {\r
175     SPI_Transaction  transaction;\r
176     uint32_t         transferType = SPI_TRANSACTION_TYPE_WRITE;\r
177     bool             ret;\r
178 \r
179     /* Update the mode and transfer type with the required values */\r
180     SPI_control(handle, SPI_V0_CMD_SETCONFIGMODE, NULL);\r
181     SPI_control(handle, SPI_V0_CMD_TRANSFERMODE_RW, (void *)&transferType);\r
182 \r
183     transaction.txBuf = (void *)cmdBuf; /* Buffer includes command and write data */\r
184     transaction.count = cmdLen + dataLen;\r
185     transaction.rxBuf = NULL;\r
186     transaction.arg = (void *)dataLen;\r
187 \r
188     ret = SPI_transfer(handle, &transaction);\r
189     if (ret == true)\r
190     {\r
191         return NOR_PASS;\r
192     }\r
193         else\r
194     {\r
195         return NOR_FAIL;\r
196     }\r
197 }\r
198 \r
199 static NOR_STATUS Nor_qspiWaitReady(SPI_Handle handle, uint32_t timeOut)\r
200 {\r
201     uint8_t         status;\r
202     uint8_t         cmd = NOR_CMD_RDSR;\r
203 \r
204     do\r
205     {\r
206         if (NOR_qspiCmdRead(handle, &cmd, 1, &status, 1))\r
207         {\r
208             return NOR_FAIL;\r
209         }\r
210         if ((status & NOR_SR_WIP) == 0)\r
211         {\r
212             break;\r
213         }\r
214 \r
215         timeOut--;\r
216         if (!timeOut) {\r
217             break;\r
218         }\r
219 \r
220     } while (1);\r
221 \r
222     if ((status & NOR_SR_WIP) == 0)\r
223     {\r
224         return NOR_PASS;\r
225     }\r
226 \r
227     /* Timed out */\r
228     return NOR_FAIL;\r
229 }\r
230 \r
231 static NOR_STATUS Nor_qspiQuadModeCtrl(SPI_Handle handle,\r
232                                        uint8_t enable)\r
233 {\r
234     uint8_t status;\r
235     uint8_t cmd[3];\r
236 \r
237     /* Write enable command */\r
238     cmd[0] = NOR_CMD_WREN;\r
239     if (Nor_qspiCmdWrite(handle, cmd, 1, 0))\r
240     {\r
241         goto err;\r
242     }\r
243 \r
244     /* Read status register */\r
245     cmd[0] = NOR_CMD_RDSR;\r
246     status = 0;\r
247     if (NOR_qspiCmdRead(handle, cmd, 1, &status, 1))\r
248     {\r
249         goto err;\r
250     }\r
251 \r
252     cmd[0] = NOR_CMD_WRR;\r
253     cmd[1] = status;\r
254 \r
255     /* The first byte will be written to the status register, while the\r
256        second byte will be written to the configuration register */\r
257     if (enable)\r
258     {\r
259         /* Write enabled, quad enabled, no protected blocks */\r
260         cmd[2] = 0x02;\r
261     }\r
262     else\r
263     {\r
264         /* Write enabled, quad disabled, no protected block */\r
265         cmd[2] = 0x0;\r
266     }\r
267 \r
268     if (Nor_qspiCmdWrite(handle, cmd, 1, 2)) /* 1 byte command and 2 bytes write data */\r
269     {\r
270         goto err;\r
271     }\r
272 \r
273     if (Nor_qspiWaitReady(handle, NOR_WRR_WRITE_TIMEOUT))\r
274     {\r
275         goto err;\r
276     }\r
277 \r
278     cmd[0] = NOR_CMD_RDCR;\r
279     status = 0;\r
280     if (NOR_qspiCmdRead(handle, cmd, 1, &status, 1))\r
281     {\r
282         goto err;\r
283     }\r
284 \r
285     if (status != cmd[2])\r
286     {\r
287         goto err;\r
288     }\r
289 \r
290     return NOR_PASS;\r
291 \r
292 err :\r
293     return NOR_FAIL;\r
294 }\r
295 \r
296 static SPI_Transaction transaction;\r
297 NOR_STATUS Nor_qspiRead(NOR_HANDLE handle, uint32_t addr,\r
298                         uint32_t len, uint8_t *buf, uint32_t mode)\r
299 {\r
300     NOR_Info        *norQspiInfo;\r
301     uint32_t         command;\r
302     uint32_t         dummyCycles;\r
303     uint32_t         rx_lines;\r
304     SPI_Handle       spiHandle;\r
305     bool             ret;\r
306 \r
307     if (!handle)\r
308     {\r
309         return NOR_FAIL;\r
310     }\r
311 \r
312     norQspiInfo = (NOR_Info *)handle;\r
313     if (!norQspiInfo->hwHandle)\r
314     {\r
315         return NOR_FAIL;\r
316     }\r
317     spiHandle = (SPI_Handle)norQspiInfo->hwHandle;\r
318 \r
319     /* Validate address input */\r
320     if ((addr + len) > NOR_SIZE)\r
321     {\r
322         return NOR_FAIL;\r
323     }\r
324 \r
325     /* To set or unset the QUAD bit in CR1 register */\r
326     if (mode != QSPI_FLASH_SINGLE_READ)\r
327     {\r
328         if (Nor_qspiQuadModeCtrl(spiHandle, 1))\r
329         {\r
330             return NOR_FAIL;\r
331         }\r
332     }\r
333     else\r
334     {\r
335         if (Nor_qspiQuadModeCtrl(spiHandle, 0))\r
336         {\r
337             return NOR_FAIL;\r
338         }\r
339     }\r
340 \r
341     switch(mode)\r
342     {\r
343         case QSPI_FLASH_SINGLE_READ :\r
344             command     = NOR_CMD_READ;\r
345             dummyCycles = NOR_SINGLE_READ_DUMMY_CYCLE;\r
346             rx_lines    = QSPI_XFER_LINES_SINGLE;\r
347             break;\r
348         case QSPI_FLASH_DUAL_READ :\r
349             command     = NOR_CMD_DUAL_READ;\r
350             dummyCycles = NOR_DUAL_READ_DUMMY_CYCLE;\r
351             rx_lines    = QSPI_XFER_LINES_DUAL;\r
352             break;\r
353         case QSPI_FLASH_QUAD_READ :\r
354             command     = NOR_CMD_QUAD_READ;\r
355             dummyCycles = NOR_QUAD_READ_DUMMY_CYCLE;\r
356             rx_lines    = QSPI_XFER_LINES_QUAD;\r
357             break;\r
358         default :\r
359             command     = NOR_CMD_READ;\r
360             dummyCycles = NOR_SINGLE_READ_DUMMY_CYCLE;\r
361             rx_lines    = QSPI_XFER_LINES_SINGLE;\r
362             break;\r
363     }\r
364 \r
365     /* Update the indirect read command, rx lines and read dummy cycles */\r
366     SPI_control(spiHandle, SPI_V0_CMD_SETINDXFERMODE, NULL);\r
367     SPI_control(spiHandle, SPI_V0_CMD_IND_TRANSFER_CMD, (void *)&command);\r
368     SPI_control(spiHandle, SPI_V0_CMD_SETXFERLINES, (void *)&rx_lines);\r
369     SPI_control(spiHandle, SPI_V0_CMD_RD_DUMMY_CLKS, (void *)&dummyCycles);\r
370 \r
371     transaction.arg   = (void *)addr;\r
372     transaction.txBuf = NULL;\r
373     transaction.rxBuf = (void *)buf;\r
374     transaction.count = len;\r
375 \r
376     ret = SPI_transfer(spiHandle, &transaction);\r
377     if (ret == true)\r
378     {\r
379         return NOR_PASS;\r
380     }\r
381         else\r
382     {\r
383         return NOR_FAIL;\r
384     }\r
385 }\r
386 \r
387 NOR_STATUS Nor_qspiWrite(NOR_HANDLE handle, uint32_t addr, uint32_t len,\r
388                          uint8_t *buf, uint32_t mode)\r
389 {\r
390     NOR_Info        *norQspiInfo;\r
391     SPI_Handle       spiHandle;\r
392     uint32_t         command;\r
393     uint32_t         tx_lines;\r
394     bool             ret;\r
395     uint32_t         byteAddr;\r
396     uint32_t         pageSize;\r
397     uint32_t         chunkLen;\r
398     uint32_t         actual;\r
399     uint8_t          cmdWren = NOR_CMD_WREN;\r
400 \r
401     if (!handle)\r
402     {\r
403         return NOR_FAIL;\r
404     }\r
405 \r
406     norQspiInfo = (NOR_Info *)handle;\r
407     if (!norQspiInfo->hwHandle)\r
408     {\r
409         return NOR_FAIL;\r
410     }\r
411     spiHandle = (SPI_Handle)norQspiInfo->hwHandle;\r
412 \r
413     /* Validate address input */\r
414     if ((addr + len) > NOR_SIZE)\r
415     {\r
416         return NOR_FAIL;\r
417     }\r
418 \r
419     if (mode == QSPI_FLASH_QUAD_PAGE_PROG)\r
420     {\r
421         if (Nor_qspiQuadModeCtrl(spiHandle, 1))\r
422         {\r
423             return NOR_FAIL;\r
424         }\r
425     }\r
426     else\r
427     {\r
428         if (Nor_qspiQuadModeCtrl(spiHandle, 0))\r
429         {\r
430             return NOR_FAIL;\r
431         }\r
432     }\r
433 \r
434     switch(mode)\r
435     {\r
436         case QSPI_FLASH_SINGLE_PAGE_PROG :\r
437             command = NOR_CMD_PAGE_PROG;\r
438             tx_lines = QSPI_XFER_LINES_SINGLE;\r
439             break;\r
440         case QSPI_FLASH_QUAD_PAGE_PROG:\r
441             command = NOR_CMD_QUAD_PAGE_PROG;\r
442             tx_lines = QSPI_XFER_LINES_QUAD;\r
443             break;\r
444         default :\r
445             command = NOR_CMD_PAGE_PROG;\r
446             tx_lines = QSPI_XFER_LINES_SINGLE;\r
447             break;\r
448     }\r
449 \r
450     /* The QSPI Flash Controller will automatically issue\r
451        the WREN command before triggering a write command via the direct or\r
452        indirect access controllers (DAC/INDAC) – i.e the user does not need\r
453        to perform this operation.\r
454     */\r
455     pageSize    = NOR_PAGE_SIZE;\r
456     byteAddr    = addr & (NOR_PAGE_SIZE - 1); /* % page_size; */\r
457 \r
458     for (actual = 0; actual < len; actual += chunkLen)\r
459     {\r
460         /* Send Write Enable command */\r
461         if (Nor_qspiCmdWrite(spiHandle, &cmdWren, 1, 0))\r
462         {\r
463             return NOR_FAIL;\r
464         }\r
465 \r
466         /* Update the indirect write command and tx lines */\r
467         SPI_control(spiHandle, SPI_V0_CMD_SETINDXFERMODE, NULL);\r
468         SPI_control(spiHandle, SPI_V0_CMD_IND_TRANSFER_CMD, (void *)&command);\r
469         SPI_control(spiHandle, SPI_V0_CMD_SETXFERLINES, (void *)&tx_lines);\r
470 \r
471         /* Send Page Program command */\r
472         chunkLen = ((len - actual) < (pageSize - byteAddr) ?\r
473                 (len - actual) : (pageSize - byteAddr));\r
474 \r
475         transaction.arg   = (void *)addr;\r
476         transaction.txBuf = (void *)(buf + actual);\r
477         transaction.rxBuf = NULL;\r
478         transaction.count = chunkLen;\r
479 \r
480         ret = SPI_transfer(spiHandle, &transaction);\r
481         if (ret == false)\r
482         {\r
483             return NOR_FAIL;\r
484         }\r
485 \r
486         if (Nor_qspiWaitReady(spiHandle, NOR_PAGE_PROG_TIMEOUT)) {\r
487             return NOR_FAIL;\r
488         }\r
489 \r
490         addr += chunkLen;\r
491         byteAddr = 0;\r
492 \r
493         BOARD_delay(10);\r
494     }\r
495 \r
496     return NOR_PASS;\r
497 }\r
498 \r
499 NOR_STATUS Nor_qspiErase(NOR_HANDLE handle, int32_t erLoc, bool blkErase)\r
500 {\r
501     uint8_t         cmd[5];\r
502     uint32_t        cmdLen;\r
503     uint32_t        address = 0;\r
504     uint8_t         cmdWren  = NOR_CMD_WREN;\r
505     NOR_Info       *norQspiInfo;\r
506     SPI_Handle      spiHandle;\r
507 #if defined (iceK2G)\r
508     uint8_t highAddress = 1;\r
509     uint32_t  offset;\r
510     uint8_t status = 0;\r
511 #endif\r
512 \r
513     if (!handle)\r
514     {\r
515         return NOR_FAIL;\r
516     }\r
517 \r
518     norQspiInfo = (NOR_Info *)handle;\r
519     if (!norQspiInfo->hwHandle)\r
520     {\r
521         return NOR_FAIL;\r
522     }\r
523     spiHandle = (SPI_Handle)norQspiInfo->hwHandle;\r
524 #if defined (iceK2G)\r
525     /* Computes Hybrid Sector Size for S25FL256S flash device */\r
526     if (blkErase == false)\r
527     {\r
528         cmd[0] = NOR_CMD_RDCR;\r
529         status = 0;\r
530         /* Read the TBPARM bit */\r
531         if (NOR_qspiCmdRead(spiHandle, cmd, 1, &status, 1))\r
532         {\r
533             return NOR_FAIL;\r
534         }\r
535         if ((status & NOR_CR_TBPARM) == 1)\r
536         {\r
537             highAddress = 1; /* physical sectors at top */\r
538         }\r
539         else\r
540         {\r
541             highAddress = 0; /* physical sectors at bottom */\r
542         }\r
543         offset = erLoc * NOR_SECTOR_SIZE;\r
544         if (highAddress == 0)\r
545         {\r
546             if (offset >= (NOR_NUM_4KSECTORS * NOR_SECTOR_SIZE))\r
547             {\r
548                 blkErase = true;\r
549                 /* Re-calculate the erase location with the updated sector size */\r
550                 erLoc = offset / NOR_HYBRID_SECTOR_SIZE;\r
551             }\r
552             else\r
553             {\r
554                 blkErase = false;\r
555             }\r
556         }\r
557         else\r
558         {\r
559             if (offset < (NOR_HYBRID_SECTOR_SIZE * (NOR_NUM_SECTORS - NOR_NUM_4KSECTORS)))\r
560             {\r
561                 blkErase = true;\r
562                 /* Re-calculate the erase location with the updated sector size */\r
563                 erLoc = offset / NOR_HYBRID_SECTOR_SIZE;\r
564             }\r
565             else\r
566             {\r
567                 blkErase = false;\r
568             }\r
569         }\r
570     }\r
571 #endif\r
572     if (erLoc == NOR_BE_SECTOR_NUM)\r
573     {\r
574         cmd[0]  = NOR_CMD_BULK_ERASE;\r
575         cmdLen = 1;\r
576     }\r
577     else\r
578     {\r
579         if (blkErase == true)\r
580                 {\r
581             if (erLoc >= NOR_NUM_BLOCKS)\r
582             {\r
583 #if defined (iceK2G)\r
584                 if (highAddress == 1)\r
585 #endif\r
586                     return NOR_FAIL;\r
587             }\r
588             address   = erLoc * NOR_BLOCK_SIZE;\r
589             cmd[0] = NOR_CMD_BLOCK_ERASE;\r
590         }\r
591         else\r
592         {\r
593             if (erLoc >= NOR_NUM_SECTORS)\r
594             {\r
595                 return NOR_FAIL;\r
596             }\r
597             address   = erLoc * NOR_SECTOR_SIZE;\r
598             cmd[0] = NOR_CMD_SECTOR_ERASE;\r
599         }\r
600         cmd[1] = (address >> 16) & 0xff; /* 64MB flash device */\r
601         cmd[2] = (address >>  8) & 0xff;\r
602         cmd[3] = (address >>  0) & 0xff;\r
603 \r
604         cmdLen = 4;\r
605     }\r
606 \r
607     if (Nor_qspiCmdWrite(spiHandle, &cmdWren, 1, 0))\r
608     {\r
609         return NOR_FAIL;\r
610     }\r
611 \r
612     if (Nor_qspiWaitReady(spiHandle, NOR_WRR_WRITE_TIMEOUT))\r
613     {\r
614         return NOR_FAIL;\r
615     }\r
616 \r
617     if (Nor_qspiCmdWrite(spiHandle, cmd, cmdLen, 0))\r
618     {\r
619         return NOR_FAIL;\r
620     }\r
621 \r
622     if (Nor_qspiWaitReady(spiHandle, NOR_BULK_ERASE_TIMEOUT))\r
623     {\r
624         return NOR_FAIL;\r
625     }\r
626 \r
627     return NOR_PASS;\r
628 }\r