Spieler splodiert.
[hackover2013-badge-firmware.git] / drivers / storage / spiflash / w25q16bv / w25q16bv.c
1 /**************************************************************************/
2 /*!
3 @file w25q16bv.c
4 @author K. Townsend (microBuilder.eu)
5
6 @section DESCRIPTION
7
8 Driver for Winbond's 16 MBit SPI/Quad-SPI Flash (W25Q16BV)
9
10 @section LICENSE
11
12 Software License Agreement (BSD License)
13
14 Copyright (c) 2010, microBuilder SARL
15 All rights reserved.
16
17 Redistribution and use in source and binary forms, with or without
18 modification, are permitted provided that the following conditions are met:
19 1. Redistributions of source code must retain the above copyright
20 notice, this list of conditions and the following disclaimer.
21 2. Redistributions in binary form must reproduce the above copyright
22 notice, this list of conditions and the following disclaimer in the
23 documentation and/or other materials provided with the distribution.
24 3. Neither the name of the copyright holders nor the
25 names of its contributors may be used to endorse or promote products
26 derived from this software without specific prior written permission.
27
28 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
29 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
30 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
31 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
32 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
33 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
34 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
35 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
37 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 */
39 /**************************************************************************/
40
41 #include "w25q16bv.h"
42 #include "../spiflash.h"
43 #include "core/ssp/ssp.h"
44 #include "core/gpio/gpio.h"
45 #include "core/systick/systick.h"
46
47 // Macros to toggle the CS/SSEL line on the SPI bus
48 #define W25Q16BV_SELECT() gpioSetValue(0, 2, 0)
49 #define W25Q16BV_DESELECT() gpioSetValue(0, 2, 1)
50
51 // Flag to indicate whether the SPI flash has been initialised or not
52 static bool _w25q16bvInitialised = false;
53
54 /**************************************************************************/
55 /* HW Specific Functions */
56 /* ------------------------------------------------------------------- */
57 /* These functions are specific to the chip being used, and are */
58 /* seperate from the 'generic' functions defined in spiflash.h */
59 /* */
60 /**************************************************************************/
61
62 /**************************************************************************/
63 /*!
64 This function both reads and writes data. For write operations, include data
65 to be written as argument. For read ops, use dummy data as arg. Returned
66 data is read byte val.
67 */
68 /**************************************************************************/
69 uint8_t w25q16bv_TransferByte(uint8_t data)
70 {
71 /* Move on only if NOT busy and TX FIFO not full */
72 while ((SSP_SSP0SR & (SSP_SSP0SR_TNF_MASK | SSP_SSP0SR_BSY_MASK)) != SSP_SSP0SR_TNF_NOTFULL);
73 SSP_SSP0DR = data;
74
75 /* Wait until the busy bit is cleared and receive buffer is not empty */
76 while ((SSP_SSP0SR & (SSP_SSP0SR_BSY_MASK | SSP_SSP0SR_RNE_MASK)) != SSP_SSP0SR_RNE_NOTEMPTY);
77
78 // Read the queue
79 return SSP_SSP0DR;
80 }
81
82 /**************************************************************************/
83 /*!
84 @brief Gets the value of the Read Status Register (0x05)
85
86 @return The 8-bit value returned by the Read Status Register
87 */
88 /**************************************************************************/
89 uint8_t w25q16bvGetStatus()
90 {
91 uint8_t status;
92
93 W25Q16BV_SELECT();
94 w25q16bv_TransferByte(W25Q16BV_CMD_READSTAT1); // Send read status 1 cmd
95 status = w25q16bv_TransferByte(0xFF); // Dummy write
96 W25Q16BV_DESELECT();
97
98 return status & (W25Q16BV_STAT1_BUSY | W25Q16BV_STAT1_WRTEN);
99 }
100
101 /**************************************************************************/
102 /*!
103 @brief Waits for the SPI flash to indicate that it is ready (not
104 busy) or until a timeout occurs.
105
106 @return An error message indicating that a timeoout occured
107 (SPIFLASH_ERROR_TIMEOUT_READY) or an OK signal to indicate that
108 the SPI flash is ready (SPIFLASH_ERROR_OK)
109 */
110 /**************************************************************************/
111 spiflashError_e w25q16bvWaitForReady()
112 {
113 uint32_t timeout = 0;
114 uint8_t status;
115
116 while ( timeout < SSP_MAX_TIMEOUT )
117 {
118 status = w25q16bvGetStatus() & W25Q16BV_STAT1_BUSY;
119 if (status == 0)
120 {
121 break;
122 }
123 timeout++;
124 }
125 if ( timeout == SSP_MAX_TIMEOUT )
126 {
127 return SPIFLASH_ERROR_TIMEOUT_READY;
128 }
129
130 return SPIFLASH_ERROR_OK;
131 }
132
133 /**************************************************************************/
134 /*!
135 @brief Gets the unique 64-bit ID assigned to this IC (useful for
136 security purposes to detect if the flash was changed, etc.)
137
138 @param[out] *buffer
139 Pointer to the uint8_t buffer that will store the 8 byte
140 long unique ID
141
142 @note The unique ID is return in bit order 63..0
143 */
144 /**************************************************************************/
145 void w25q16bvGetUniqueID(uint8_t *buffer)
146 {
147 uint8_t i;
148
149 W25Q16BV_SELECT();
150 w25q16bv_TransferByte(W25Q16BV_CMD_READUNIQUEID); // Unique ID cmd
151 w25q16bv_TransferByte(0xFF); // Dummy write
152 w25q16bv_TransferByte(0xFF); // Dummy write
153 w25q16bv_TransferByte(0xFF); // Dummy write
154 w25q16bv_TransferByte(0xFF); // Dummy write
155 // Read 8 bytes worth of data
156 for (i = 0; i < 8; i++)
157 {
158 buffer[i] = w25q16bv_TransferByte(0xFF);
159 }
160 W25Q16BV_DESELECT();
161 }
162
163 /**************************************************************************/
164 /* Generic spiflash.h Functions */
165 /* ------------------------------------------------------------------- */
166 /* These functions are part of the common interface defined in */
167 /* spiflash.h and must be implemented by any spi flash device */
168 /* */
169 /**************************************************************************/
170
171 /**************************************************************************/
172 /*!
173 @brief Tries to initialise the flash device, and setups any HW
174 required by the SPI flash
175 */
176 /**************************************************************************/
177 void spiflashInit (void)
178 {
179 // Initialise the SPI bus
180 sspInit(0, sspClockPolarity_Low, sspClockPhase_RisingEdge);
181
182 // ToDo: Set the WP, Hold, etc. pins to an appropriate state
183
184 _w25q16bvInitialised = TRUE;
185 }
186
187 /**************************************************************************/
188 /*!
189 @brief Gets an instance of spiflashSizeInfo_t that describes the
190 storage limits of the SPI flash like page size (minimum write
191 size) and sector size (minimum erase size).
192 */
193 /**************************************************************************/
194 spiflashSizeInfo_t spiflashGetSizeInfo(void)
195 {
196 spiflashSizeInfo_t size;
197 size.pageSize = W25Q16BV_PAGESIZE;
198 size.pageCount = W25Q16BV_PAGES;
199 size.sectorSize = W25Q16BV_SECTORSIZE;
200 size.sectorCount = W25Q16BV_SECTORS;
201 return size;
202 }
203
204 /**************************************************************************/
205 /*!
206 @brief Gets the 8-bit manufacturer ID and device ID for the flash
207
208 @param[out] *manufID
209 Pointer to the uint8_t that will store the manufacturer ID
210 @param[out] *deviceID
211 Pointer to the uint8_t that will store the device ID
212 */
213 /**************************************************************************/
214 void spiflashGetManufacturerInfo (uint8_t *manufID, uint8_t *deviceID)
215 {
216 if (!_w25q16bvInitialised) spiflashInit();
217
218 // W25Q16BV_CMD_MANUFDEVID (0x90) provides both the JEDEC manufacturer
219 // ID and the device ID
220
221 W25Q16BV_SELECT();
222 w25q16bv_TransferByte(W25Q16BV_CMD_MANUFDEVID);
223 w25q16bv_TransferByte(0x00); // Dummy write
224 w25q16bv_TransferByte(0x00); // Dummy write
225 w25q16bv_TransferByte(0x00); // Dummy write
226 *manufID = w25q16bv_TransferByte(0xFF);
227 *deviceID = w25q16bv_TransferByte(0xFF);
228 W25Q16BV_DESELECT();
229 }
230
231 /**************************************************************************/
232 /*!
233 @brief Sets the write flag on the SPI flash, and if required puts the
234 WP pin in an appropriate state
235
236 @param[in] enable
237 True (1) to enable writing, false (0) to disable it
238 */
239 /**************************************************************************/
240 void spiflashWriteEnable (bool enable)
241 {
242 if (!_w25q16bvInitialised) spiflashInit();
243
244 // ToDo: Put the WP pin in an appropriate state if required
245
246 W25Q16BV_SELECT();
247 w25q16bv_TransferByte(enable ? W25Q16BV_CMD_WRITEENABLE : W25Q16BV_CMD_WRITEDISABLE);
248 W25Q16BV_DESELECT();
249 }
250
251 /**************************************************************************/
252 /*!
253 @brief Reads the specified number of bytes from the supplied address.
254
255 This function will read one or more bytes starting at the supplied
256 address. Please note that bufferLength is zero-based, meaning you
257 should supply '0' to read a single byte, '3' to read 4 bytes of data,
258 etc.
259
260 @param[in] address
261 The 24-bit address where the read will start.
262 @param[out] *buffer
263 Pointer to the buffer that will store the read results
264 @param[in] len
265 Length of the buffer.
266
267 @section EXAMPLE
268
269 @code
270 uint8_t buffer[64];
271 spiflashError_e error;
272 error = spiflashReadBuffer (0, buffer, 64);
273 if (error)
274 {
275 // Check what went wrong
276 switch (error)
277 {
278 case SPIFLASH_ERROR_ADDROUTOFRANGE:
279 // Specified starting address is out of range
280 break;
281 case SPIFLASH_ERROR_TIMEOUT_READY:
282 // Timed out waiting for flash to return ready state
283 break;
284 case SPIFLASH_ERROR_ADDROVERFLOW:
285 // Ran over the upper address during read
286 break;
287 }
288 }
289 @endcode
290 */
291 /**************************************************************************/
292 spiflashError_e spiflashReadBuffer (uint32_t address, uint8_t *buffer, uint32_t len)
293 {
294 if (!_w25q16bvInitialised) spiflashInit();
295
296 uint32_t a, i;
297 a = i = 0;
298
299 // Make sure the address is valid
300 if (address >= W25Q16BV_MAXADDRESS)
301 {
302 return SPIFLASH_ERROR_ADDROUTOFRANGE;
303 }
304
305 // Wait until the device is ready or a timeout occurs
306 if (w25q16bvWaitForReady())
307 return SPIFLASH_ERROR_TIMEOUT_READY;
308
309 // Send the read data command
310 W25Q16BV_SELECT();
311 w25q16bv_TransferByte(W25Q16BV_CMD_READDATA); // 0x03
312 w25q16bv_TransferByte((address >> 16) & 0xFF); // address upper 8
313 w25q16bv_TransferByte((address >> 8) & 0xFF); // address mid 8
314 w25q16bv_TransferByte(address & 0xFF); // address lower 8
315 // Fill response buffer
316 for (a = address; a < address + len; a++, i++)
317 {
318 if (a > W25Q16BV_MAXADDRESS)
319 {
320 // Oops ... we're at the end of the flash memory
321 return SPIFLASH_ERROR_ADDROVERFLOW;
322 }
323 buffer[i] = w25q16bv_TransferByte(0xFF);
324 }
325 W25Q16BV_DESELECT();
326
327 return SPIFLASH_ERROR_OK;
328 }
329
330 /**************************************************************************/
331 /*!
332 @brief Erases the contents of a single sector
333
334 @param[in] sectorNumber
335 The sector number to erase (zero-based).
336
337 @section EXAMPLE
338
339 @code
340 spiflashError_e error;
341 error = spiflashEraseSector(0);
342 if (error)
343 {
344 // Check what went wrong
345 switch (error)
346 {
347 case SPIFLASH_ERROR_ADDROUTOFRANGE:
348 // Specified starting address is out of range
349 break;
350 case SPIFLASH_ERROR_PROTECTIONERR:
351 // Couldn't set the write enable bit
352 break;
353 case SPIFLASH_ERROR_TIMEOUT_READY:
354 // Timed out waiting for flash to return ready state
355 break;
356 }
357 }
358 @endcode
359 */
360 /**************************************************************************/
361 spiflashError_e spiflashEraseSector (uint32_t sectorNumber)
362 {
363 if (!_w25q16bvInitialised) spiflashInit();
364
365 // Make sure the address is valid
366 if (sectorNumber >= W25Q16BV_SECTORS)
367 {
368 return SPIFLASH_ERROR_ADDROUTOFRANGE;
369 }
370
371 // Wait until the device is ready or a timeout occurs
372 if (w25q16bvWaitForReady())
373 return SPIFLASH_ERROR_TIMEOUT_READY;
374
375 // Make sure the chip is write enabled
376 spiflashWriteEnable (TRUE);
377
378 // Make sure the write enable latch is actually set
379 uint8_t status;
380 status = w25q16bvGetStatus();
381 if (!(status & W25Q16BV_STAT1_WRTEN))
382 {
383 // Throw a write protection error (write enable latch not set)
384 return SPIFLASH_ERROR_PROTECTIONERR;
385 }
386
387 // Send the erase sector command
388 uint32_t address = sectorNumber * W25Q16BV_SECTORSIZE;
389 W25Q16BV_SELECT();
390 w25q16bv_TransferByte(W25Q16BV_CMD_SECTERASE4);
391 w25q16bv_TransferByte((address >> 16) & 0xFF); // address upper 8
392 w25q16bv_TransferByte((address >> 8) & 0xFF); // address mid 8
393 w25q16bv_TransferByte(address & 0xFF); // address lower 8
394 W25Q16BV_DESELECT();
395
396 // Wait until the busy bit is cleared before exiting
397 // This can take up to 400ms according to the datasheet
398 while (w25q16bvGetStatus() & W25Q16BV_STAT1_BUSY);
399
400 return SPIFLASH_ERROR_OK;
401 }
402
403 /**************************************************************************/
404 /*!
405 @brief Erases the entire flash chip
406
407 @section EXAMPLE
408
409 @code
410 spiflashError_e error;
411 error = spiflashEraseChip();
412 if (error)
413 {
414 // Check what went wrong
415 switch (error)
416 {
417 case SPIFLASH_ERROR_PROTECTIONERR:
418 // Couldn't set the write enable bit
419 break;
420 case SPIFLASH_ERROR_TIMEOUT_READY:
421 // Timed out waiting for flash to return ready state
422 break;
423 }
424 }
425 @endcode
426 */
427 /**************************************************************************/
428 spiflashError_e spiflashEraseChip (void)
429 {
430 if (!_w25q16bvInitialised) spiflashInit();
431
432 // Wait until the device is ready or a timeout occurs
433 if (w25q16bvWaitForReady())
434 return SPIFLASH_ERROR_TIMEOUT_READY;
435
436 // Make sure the chip is write enabled
437 spiflashWriteEnable (TRUE);
438
439 // Make sure the write enable latch is actually set
440 uint8_t status;
441 status = w25q16bvGetStatus();
442 if (!(status & W25Q16BV_STAT1_WRTEN))
443 {
444 // Throw a write protection error (write enable latch not set)
445 return SPIFLASH_ERROR_PROTECTIONERR;
446 }
447
448 // Send the erase chip command
449 W25Q16BV_SELECT();
450 w25q16bv_TransferByte(W25Q16BV_CMD_CHIPERASE);
451 W25Q16BV_DESELECT();
452
453 // Wait until the busy bit is cleared before exiting
454 // This can take up to 10 seconds according to the datasheet!
455 while (w25q16bvGetStatus() & W25Q16BV_STAT1_BUSY);
456
457 return SPIFLASH_ERROR_OK;
458 }
459
460 /**************************************************************************/
461 /*!
462 @brief Writes up to 256 bytes of data to the specified page.
463
464 @note Before writing data to a page, make sure that the 4K sector
465 containing the specific page has been erased, otherwise the
466 data will be meaningless.
467
468 @param[in] address
469 The 24-bit address where the write will start.
470 @param[out] *buffer
471 Pointer to the buffer that will store the read results
472 @param[in] len
473 Length of the buffer. Valid values are from 1 to 256,
474 within the limits of the starting address and page length.
475
476 @section EXAMPLE
477
478 @code
479 spiflashError_e error;
480 uint8_t buffer[256];
481
482 buffer[0] = 0x12;
483 buffer[1] = 0x34;
484 buffer[2] = 0x56;
485 buffer[3] = 0x78;
486 buffer[4] = 0xDE;
487 buffer[5] = 0xAD;
488 buffer[6] = 0xC0;
489 buffer[7] = 0xDE;
490
491 error = spiflashWritePage (0, buffer, 8);
492 if (error)
493 {
494 // Check what went wrong
495 switch (error)
496 {
497 case SPIFLASH_ERROR_ADDROUTOFRANGE:
498 // Specified starting address is out of range
499 break;
500 case SPIFLASH_ERROR_DATAEXCEEDSPAGESIZE:
501 // Supplied data exceeds max page size
502 break;
503 case SPIFLASH_ERROR_PAGEWRITEOVERFLOW:
504 // The data length plus the start address offset exceeeds page limits
505 break;
506 case SPIFLASH_ERROR_TIMEOUT_READY:
507 // Timeout waiting for ready status (can be pre or post write)
508 break;
509 case SPIFLASH_ERROR_PROTECTIONERR:
510 // Unable to set write latch
511 break;
512 }
513 }
514 @endcode
515 */
516 /**************************************************************************/
517 spiflashError_e spiflashWritePage (uint32_t address, uint8_t *buffer, uint32_t len)
518 {
519 uint8_t status;
520 uint32_t i;
521
522 if (!_w25q16bvInitialised) spiflashInit();
523
524 // Make sure the address is valid
525 if (address >= W25Q16BV_MAXADDRESS)
526 {
527 return SPIFLASH_ERROR_ADDROUTOFRANGE;
528 }
529
530 // Make sure that the supplied data is no larger than the page size
531 if (len > W25Q16BV_PAGESIZE)
532 {
533 return SPIFLASH_ERROR_DATAEXCEEDSPAGESIZE;
534 }
535
536 // Make sure that the data won't wrap around to the beginning of the sector
537 if ((address % W25Q16BV_PAGESIZE) + len > W25Q16BV_PAGESIZE)
538 {
539 // If you try to write to a page beyond the last byte, it will
540 // wrap around to the start of the page, almost certainly
541 // messing up your data
542 return SPIFLASH_ERROR_PAGEWRITEOVERFLOW;
543 }
544
545 // Wait until the device is ready or a timeout occurs
546 if (w25q16bvWaitForReady())
547 return SPIFLASH_ERROR_TIMEOUT_READY;
548
549 // Make sure the chip is write enabled
550 spiflashWriteEnable (TRUE);
551
552 // Make sure the write enable latch is actually set
553 status = w25q16bvGetStatus();
554 if (!(status & W25Q16BV_STAT1_WRTEN))
555 {
556 // Throw a write protection error (write enable latch not set)
557 return SPIFLASH_ERROR_PROTECTIONERR;
558 }
559
560 // Send page write command (0x02) plus 24-bit address
561 W25Q16BV_SELECT();
562 w25q16bv_TransferByte(W25Q16BV_CMD_PAGEPROG); // 0x02
563 w25q16bv_TransferByte((address >> 16) & 0xFF); // address upper 8
564 w25q16bv_TransferByte((address >> 8) & 0xFF); // address mid 8
565 if (len == 256)
566 {
567 // If len = 256 bytes, lower 8 bits must be 0 (see datasheet 11.2.17)
568 w25q16bv_TransferByte(0);
569 }
570 else
571 {
572 w25q16bv_TransferByte(address & 0xFF); // address lower 8
573 }
574 // Transfer data
575 for (i = 0; i < len; i++)
576 {
577 w25q16bv_TransferByte(buffer[i]);
578 }
579 // Write only occurs after the CS line is de-asserted
580 W25Q16BV_DESELECT();
581
582 // Wait at least 3ms (max page program time according to datasheet)
583 systickDelay(3);
584
585 // Wait until the device is ready or a timeout occurs
586 if (w25q16bvWaitForReady())
587 return SPIFLASH_ERROR_TIMEOUT_READY;
588
589 return SPIFLASH_ERROR_OK;
590 }
591
592 /**************************************************************************/
593 /*!
594 @brief Writes a continuous stream of data that will automatically
595 cross page boundaries.
596
597 @note Before writing data, make sure that the appropriate sectors
598 have been erased, otherwise the data will be meaningless.
599
600 @param[in] address
601 The 24-bit address where the write will start.
602 @param[out] *buffer
603 Pointer to the buffer that will store the read results
604 @param[in] len
605 Length of the buffer, within the limits of the starting
606 address and size of the flash device.
607
608 @section EXAMPLE
609
610 @code
611 spiflashError_e error;
612 uint8_t buffer[256];
613
614 buffer[0] = 0x12;
615 buffer[1] = 0x34;
616 buffer[2] = 0x56;
617 buffer[3] = 0x78;
618 buffer[4] = 0xDE;
619 buffer[5] = 0xAD;
620 buffer[6] = 0xC0;
621 buffer[7] = 0xDE;
622
623 error = spiflashWrite (0, buffer, 8);
624 if (error)
625 {
626 // Check what went wrong
627 switch (error)
628 {
629 case SPIFLASH_ERROR_ADDROUTOFRANGE:
630 // Specified starting address is out of range
631 break;
632 case SPIFLASH_ERROR_DATAEXCEEDSPAGESIZE:
633 // Supplied data exceeds max page size
634 break;
635 case SPIFLASH_ERROR_PAGEWRITEOVERFLOW:
636 // The data length plus the start address offset exceeeds page limits
637 break;
638 case SPIFLASH_ERROR_TIMEOUT_READY:
639 // Timeout waiting for ready status (can be pre or post write)
640 break;
641 case SPIFLASH_ERROR_PROTECTIONERR:
642 // Unable to set write latch
643 break;
644 }
645 }
646 @endcode
647 */
648 /**************************************************************************/
649 spiflashError_e spiflashWrite (uint32_t address, uint8_t *buffer, uint32_t len)
650 {
651 uint32_t bytestowrite;
652 uint32_t bufferoffset;
653 spiflashError_e error;
654
655 // There's no point duplicating most error checks here since they will all be
656 // done in the underlying call to spiflashWritePage
657
658 // If the data is only on one page we can take a shortcut
659 if ((address % W25Q16BV_PAGESIZE) + len <= W25Q16BV_PAGESIZE)
660 {
661 // Only one page ... write and be done with it
662 return spiflashWritePage(address, buffer, len);
663 }
664
665 // Block spans multiple pages
666 bufferoffset = 0;
667 while(len)
668 {
669 // Figure out how many bytes need to be written to this page
670 bytestowrite = W25Q16BV_PAGESIZE - (address % W25Q16BV_PAGESIZE);
671 // Write the current page
672 error = spiflashWritePage(address, buffer+bufferoffset, bytestowrite);
673 // Abort if we returned an error
674 if (error)
675 return error;
676 // Adjust address and len, and buffer offset
677 address += bytestowrite;
678 len -= bytestowrite;
679 bufferoffset+=bytestowrite;
680 // If the next page is the last one, write it and exit
681 // otherwise stay in the the loop and keep writing
682 if (len <= W25Q16BV_PAGESIZE)
683 {
684 // Write the last frame and then quit
685 error = spiflashWritePage(address, buffer+bufferoffset, len);
686 // Abort if we returned an error
687 if (error)
688 return error;
689 // set len to zero to gracefully exit loop
690 len = 0;
691 }
692 }
693
694 return SPIFLASH_ERROR_OK;
695 }
696
697
This page took 0.099424 seconds and 5 git commands to generate.