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
629 \r