xref: /nrf52832-nimble/rt-thread/components/drivers/spi/sfud/src/sfud.c (revision 104654410c56c573564690304ae786df310c91fc)
1 /*
2  * This file is part of the Serial Flash Universal Driver Library.
3  *
4  * Copyright (c) 2016-2018, Armink, <[email protected]>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files (the
8  * 'Software'), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be
15  * included in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  *
25  * Function: serial flash operate functions by SFUD lib.
26  * Created on: 2016-04-23
27  */
28 
29 #include "../inc/sfud.h"
30 #include <string.h>
31 
32 /* send dummy data for read data */
33 #define DUMMY_DATA                               0xFF
34 
35 #ifndef SFUD_FLASH_DEVICE_TABLE
36 #error "Please configure the flash device information table in (in sfud_cfg.h)."
37 #endif
38 
39 /* user configured flash device information table */
40 static sfud_flash flash_table[] = SFUD_FLASH_DEVICE_TABLE;
41 /* supported manufacturer information table */
42 static const sfud_mf mf_table[] = SFUD_MF_TABLE;
43 
44 #ifdef SFUD_USING_FLASH_INFO_TABLE
45 /* supported flash chip information table */
46 static const sfud_flash_chip flash_chip_table[] = SFUD_FLASH_CHIP_TABLE;
47 #endif
48 
49 #ifdef SFUD_USING_QSPI
50 /**
51  * flash read data mode
52  */
53 enum sfud_qspi_read_mode {
54     NORMAL_SPI_READ = 1 << 0,               /**< mormal spi read mode */
55     DUAL_OUTPUT = 1 << 1,                   /**< qspi fast read dual output */
56     DUAL_IO = 1 << 2,                       /**< qspi fast read dual input/output */
57     QUAD_OUTPUT = 1 << 3,                   /**< qspi fast read quad output */
58     QUAD_IO = 1 << 4,                       /**< qspi fast read quad input/output */
59 };
60 
61 /* QSPI flash chip's extended information table */
62 static const sfud_qspi_flash_ext_info qspi_flash_ext_info_table[] = SFUD_FLASH_EXT_INFO_TABLE;
63 #endif /* SFUD_USING_QSPI */
64 
65 static sfud_err software_init(const sfud_flash *flash);
66 static sfud_err hardware_init(sfud_flash *flash);
67 static sfud_err page256_or_1_byte_write(const sfud_flash *flash, uint32_t addr, size_t size, uint16_t write_gran,
68         const uint8_t *data);
69 static sfud_err aai_write(const sfud_flash *flash, uint32_t addr, size_t size, const uint8_t *data);
70 static sfud_err wait_busy(const sfud_flash *flash);
71 static sfud_err reset(const sfud_flash *flash);
72 static sfud_err read_jedec_id(sfud_flash *flash);
73 static sfud_err set_write_enabled(const sfud_flash *flash, bool enabled);
74 static sfud_err set_4_byte_address_mode(sfud_flash *flash, bool enabled);
75 static void make_adress_byte_array(const sfud_flash *flash, uint32_t addr, uint8_t *array);
76 
77 /* ../port/sfup_port.c */
78 extern void sfud_log_debug(const char *file, const long line, const char *format, ...);
79 extern void sfud_log_info(const char *format, ...);
80 
81 /**
82  * SFUD initialize by flash device
83  *
84  * @param flash flash device
85  *
86  * @return result
87  */
sfud_device_init(sfud_flash * flash)88 sfud_err sfud_device_init(sfud_flash *flash) {
89     sfud_err result = SFUD_SUCCESS;
90 
91     /* hardware initialize */
92     result = hardware_init(flash);
93     if (result == SFUD_SUCCESS) {
94         result = software_init(flash);
95     }
96     if (result == SFUD_SUCCESS) {
97         flash->init_ok = true;
98         SFUD_INFO("%s flash device is initialize success.", flash->name);
99     } else {
100         flash->init_ok = false;
101         SFUD_INFO("Error: %s flash device is initialize fail.", flash->name);
102     }
103 
104     return result;
105 }
106 
107 /**
108  * SFUD library initialize.
109  *
110  * @return result
111  */
sfud_init(void)112 sfud_err sfud_init(void) {
113     sfud_err cur_flash_result = SFUD_SUCCESS, all_flash_result = SFUD_SUCCESS;
114     size_t i;
115 
116     SFUD_DEBUG("Start initialize Serial Flash Universal Driver(SFUD) V%s.", SFUD_SW_VERSION);
117     SFUD_DEBUG("You can get the latest version on https://github.com/armink/SFUD .");
118     /* initialize all flash device in flash device table */
119     for (i = 0; i < sizeof(flash_table) / sizeof(sfud_flash); i++) {
120         /* initialize flash device index of flash device information table */
121         flash_table[i].index = i;
122         cur_flash_result = sfud_device_init(&flash_table[i]);
123 
124         if (cur_flash_result != SFUD_SUCCESS) {
125             all_flash_result = cur_flash_result;
126         }
127     }
128 
129     return all_flash_result;
130 }
131 
132 /**
133  * get flash device by its index which in the flash information table
134  *
135  * @param index the index which in the flash information table  @see flash_table
136  *
137  * @return flash device
138  */
sfud_get_device(size_t index)139 sfud_flash *sfud_get_device(size_t index) {
140     if (index < sfud_get_device_num()) {
141         return &flash_table[index];
142     } else {
143         return NULL;
144     }
145 }
146 
147 /**
148  * get flash device total number on flash device information table  @see flash_table
149  *
150  * @return flash device total number
151  */
sfud_get_device_num(void)152 size_t sfud_get_device_num(void) {
153     return sizeof(flash_table) / sizeof(sfud_flash);
154 }
155 
156 /**
157  * get flash device information table  @see flash_table
158  *
159  * @return flash device table pointer
160  */
sfud_get_device_table(void)161 const sfud_flash *sfud_get_device_table(void) {
162     return flash_table;
163 }
164 
165 #ifdef SFUD_USING_QSPI
qspi_set_read_cmd_format(sfud_flash * flash,uint8_t ins,uint8_t ins_lines,uint8_t addr_lines,uint8_t dummy_cycles,uint8_t data_lines)166 static void qspi_set_read_cmd_format(sfud_flash *flash, uint8_t ins, uint8_t ins_lines, uint8_t addr_lines,
167         uint8_t dummy_cycles, uint8_t data_lines) {
168     /* if medium size greater than 16Mb, use 4-Byte address, instruction should be added one */
169     if (flash->chip.capacity <= 0x1000000) {
170         flash->read_cmd_format.instruction = ins;
171         flash->read_cmd_format.address_size = 24;
172     } else {
173         flash->read_cmd_format.instruction = ins + 1;
174         flash->read_cmd_format.address_size = 32;
175     }
176 
177     flash->read_cmd_format.instruction_lines = ins_lines;
178     flash->read_cmd_format.address_lines = addr_lines;
179     flash->read_cmd_format.alternate_bytes_lines = 0;
180     flash->read_cmd_format.dummy_cycles = dummy_cycles;
181     flash->read_cmd_format.data_lines = data_lines;
182 }
183 
184 /**
185  * Enbale the fast read mode in QSPI flash mode. Default read mode is normal SPI mode.
186  *
187  * it will find the appropriate fast-read instruction to replace the read instruction(0x03)
188  * fast-read instruction @see SFUD_FLASH_EXT_INFO_TABLE
189  *
190  * @note When Flash is in QSPI mode, the method must be called after sfud_device_init().
191  *
192  * @param flash flash device
193  * @param data_line_width the data lines max width which QSPI bus supported, such as 1, 2, 4
194  *
195  * @return result
196  */
sfud_qspi_fast_read_enable(sfud_flash * flash,uint8_t data_line_width)197 sfud_err sfud_qspi_fast_read_enable(sfud_flash *flash, uint8_t data_line_width) {
198     size_t i = 0;
199     uint8_t read_mode = NORMAL_SPI_READ;
200     sfud_err result = SFUD_SUCCESS;
201 
202     SFUD_ASSERT(flash);
203     SFUD_ASSERT(data_line_width == 1 || data_line_width == 2 || data_line_width == 4);
204 
205     /* get read_mode, If don't found, the default is SFUD_QSPI_NORMAL_SPI_READ */
206     for (i = 0; i < sizeof(qspi_flash_ext_info_table) / sizeof(sfud_qspi_flash_ext_info); i++) {
207         if ((qspi_flash_ext_info_table[i].mf_id == flash->chip.mf_id)
208                 && (qspi_flash_ext_info_table[i].type_id == flash->chip.type_id)
209                 && (qspi_flash_ext_info_table[i].capacity_id == flash->chip.capacity_id)) {
210             read_mode = qspi_flash_ext_info_table[i].read_mode;
211         }
212     }
213 
214     /* determine qspi supports which read mode and set read_cmd_format struct */
215     switch (data_line_width) {
216     case 1:
217         qspi_set_read_cmd_format(flash, SFUD_CMD_READ_DATA, 1, 1, 0, 1);
218         break;
219     case 2:
220         if (read_mode & DUAL_IO) {
221             qspi_set_read_cmd_format(flash, SFUD_CMD_DUAL_IO_READ_DATA, 1, 2, 8, 2);
222         } else if (read_mode & DUAL_OUTPUT) {
223             qspi_set_read_cmd_format(flash, SFUD_CMD_DUAL_OUTPUT_READ_DATA, 1, 1, 8, 2);
224         } else {
225             qspi_set_read_cmd_format(flash, SFUD_CMD_READ_DATA, 1, 1, 0, 1);
226         }
227         break;
228     case 4:
229         if (read_mode & QUAD_IO) {
230             qspi_set_read_cmd_format(flash, SFUD_CMD_QUAD_IO_READ_DATA, 1, 4, 6, 4);
231         } else if (read_mode & QUAD_OUTPUT) {
232             qspi_set_read_cmd_format(flash, SFUD_CMD_QUAD_OUTPUT_READ_DATA, 1, 1, 8, 4);
233         } else {
234             qspi_set_read_cmd_format(flash, SFUD_CMD_READ_DATA, 1, 1, 0, 1);
235         }
236         break;
237     }
238 
239     return result;
240 }
241 #endif /* SFUD_USING_QSPI */
242 
243 /**
244  * hardware initialize
245  */
hardware_init(sfud_flash * flash)246 static sfud_err hardware_init(sfud_flash *flash) {
247     extern sfud_err sfud_spi_port_init(sfud_flash * flash);
248 
249     sfud_err result = SFUD_SUCCESS;
250     size_t i;
251 
252     SFUD_ASSERT(flash);
253 
254     result = sfud_spi_port_init(flash);
255     if (result != SFUD_SUCCESS) {
256         return result;
257     }
258 
259 #ifdef SFUD_USING_QSPI
260     /* set default read instruction */
261     flash->read_cmd_format.instruction = SFUD_CMD_READ_DATA;
262 #endif /* SFUD_USING_QSPI */
263 
264     /* SPI write read function must be initialize */
265     SFUD_ASSERT(flash->spi.wr);
266     /* if the user don't configure flash chip information then using SFDP parameter or static flash parameter table */
267     if (flash->chip.capacity == 0 || flash->chip.write_mode == 0 || flash->chip.erase_gran == 0
268             || flash->chip.erase_gran_cmd == 0) {
269         /* read JEDEC ID include manufacturer ID, memory type ID and flash capacity ID */
270         result = read_jedec_id(flash);
271         if (result != SFUD_SUCCESS) {
272             return result;
273         }
274 
275 #ifdef SFUD_USING_SFDP
276         extern bool sfud_read_sfdp(sfud_flash *flash);
277         /* read SFDP parameters */
278         if (sfud_read_sfdp(flash)) {
279             flash->chip.name = NULL;
280             flash->chip.capacity = flash->sfdp.capacity;
281             /* only 1 byte or 256 bytes write mode for SFDP */
282             if (flash->sfdp.write_gran == 1) {
283                 flash->chip.write_mode = SFUD_WM_BYTE;
284             } else {
285                 flash->chip.write_mode = SFUD_WM_PAGE_256B;
286             }
287             /* find the the smallest erase sector size for eraser. then will use this size for erase granularity */
288             flash->chip.erase_gran = flash->sfdp.eraser[0].size;
289             flash->chip.erase_gran_cmd = flash->sfdp.eraser[0].cmd;
290             for (i = 1; i < SFUD_SFDP_ERASE_TYPE_MAX_NUM; i++) {
291                 if (flash->sfdp.eraser[i].size != 0 && flash->chip.erase_gran > flash->sfdp.eraser[i].size) {
292                     flash->chip.erase_gran = flash->sfdp.eraser[i].size;
293                     flash->chip.erase_gran_cmd = flash->sfdp.eraser[i].cmd;
294                 }
295             }
296         } else {
297 #endif
298 
299 #ifdef SFUD_USING_FLASH_INFO_TABLE
300             /* read SFDP parameters failed then using SFUD library provided static parameter */
301             for (i = 0; i < sizeof(flash_chip_table) / sizeof(sfud_flash_chip); i++) {
302                 if ((flash_chip_table[i].mf_id == flash->chip.mf_id)
303                         && (flash_chip_table[i].type_id == flash->chip.type_id)
304                         && (flash_chip_table[i].capacity_id == flash->chip.capacity_id)) {
305                     flash->chip.name = flash_chip_table[i].name;
306                     flash->chip.capacity = flash_chip_table[i].capacity;
307                     flash->chip.write_mode = flash_chip_table[i].write_mode;
308                     flash->chip.erase_gran = flash_chip_table[i].erase_gran;
309                     flash->chip.erase_gran_cmd = flash_chip_table[i].erase_gran_cmd;
310                     break;
311                 }
312             }
313 #endif
314 
315 #ifdef SFUD_USING_SFDP
316         }
317 #endif
318 
319     }
320 
321     if (flash->chip.capacity == 0 || flash->chip.write_mode == 0 || flash->chip.erase_gran == 0
322             || flash->chip.erase_gran_cmd == 0) {
323         SFUD_INFO("Warning: This flash device is not found or not support.");
324         return SFUD_ERR_NOT_FOUND;
325     } else {
326         const char *flash_mf_name = NULL;
327         /* find the manufacturer information */
328         for (i = 0; i < sizeof(mf_table) / sizeof(sfud_mf); i++) {
329             if (mf_table[i].id == flash->chip.mf_id) {
330                 flash_mf_name = mf_table[i].name;
331                 break;
332             }
333         }
334         /* print manufacturer and flash chip name */
335         if (flash_mf_name && flash->chip.name) {
336             SFUD_INFO("Find a %s %s flash chip. Size is %ld bytes.", flash_mf_name, flash->chip.name,
337                     flash->chip.capacity);
338         } else if (flash_mf_name) {
339             SFUD_INFO("Find a %s flash chip. Size is %ld bytes.", flash_mf_name, flash->chip.capacity);
340         } else {
341             SFUD_INFO("Find a flash chip. Size is %ld bytes.", flash->chip.capacity);
342         }
343     }
344 
345     /* reset flash device */
346     result = reset(flash);
347     if (result != SFUD_SUCCESS) {
348         return result;
349     }
350 
351     /* I found when the flash write mode is supported AAI mode. The flash all blocks is protected,
352      * so need change the flash status to unprotected before write and erase operate. */
353     if (flash->chip.write_mode & SFUD_WM_AAI) {
354         result = sfud_write_status(flash, true, 0x00);
355         if (result != SFUD_SUCCESS) {
356             return result;
357         }
358     }
359 
360     /* if the flash is large than 16MB (256Mb) then enter in 4-Byte addressing mode */
361     if (flash->chip.capacity > (1L << 24)) {
362         result = set_4_byte_address_mode(flash, true);
363     } else {
364         flash->addr_in_4_byte = false;
365     }
366 
367     return result;
368 }
369 
370 /**
371  * software initialize
372  *
373  * @param flash flash device
374  *
375  * @return result
376  */
software_init(const sfud_flash * flash)377 static sfud_err software_init(const sfud_flash *flash) {
378     sfud_err result = SFUD_SUCCESS;
379 
380     SFUD_ASSERT(flash);
381 
382     return result;
383 }
384 
385 /**
386  * read flash data
387  *
388  * @param flash flash device
389  * @param addr start address
390  * @param size read size
391  * @param data read data pointer
392  *
393  * @return result
394  */
sfud_read(const sfud_flash * flash,uint32_t addr,size_t size,uint8_t * data)395 sfud_err sfud_read(const sfud_flash *flash, uint32_t addr, size_t size, uint8_t *data) {
396     sfud_err result = SFUD_SUCCESS;
397     const sfud_spi *spi = &flash->spi;
398     uint8_t cmd_data[5], cmd_size;
399 
400     SFUD_ASSERT(flash);
401     SFUD_ASSERT(data);
402     /* must be call this function after initialize OK */
403     SFUD_ASSERT(flash->init_ok);
404     /* check the flash address bound */
405     if (addr + size > flash->chip.capacity) {
406         SFUD_INFO("Error: Flash address is out of bound.");
407         return SFUD_ERR_ADDR_OUT_OF_BOUND;
408     }
409     /* lock SPI */
410     if (spi->lock) {
411         spi->lock(spi);
412     }
413 
414     result = wait_busy(flash);
415 
416     if (result == SFUD_SUCCESS) {
417 #ifdef SFUD_USING_QSPI
418         if (flash->read_cmd_format.instruction != SFUD_CMD_READ_DATA) {
419             result = spi->qspi_read(spi, addr, (sfud_qspi_read_cmd_format *)&flash->read_cmd_format, data, size);
420         } else
421 #endif
422         {
423             cmd_data[0] = SFUD_CMD_READ_DATA;
424             make_adress_byte_array(flash, addr, &cmd_data[1]);
425             cmd_size = flash->addr_in_4_byte ? 5 : 4;
426             result = spi->wr(spi, cmd_data, cmd_size, data, size);
427         }
428     }
429     /* unlock SPI */
430     if (spi->unlock) {
431         spi->unlock(spi);
432     }
433 
434     return result;
435 }
436 
437 /**
438  * erase all flash data
439  *
440  * @param flash flash device
441  *
442  * @return result
443  */
sfud_chip_erase(const sfud_flash * flash)444 sfud_err sfud_chip_erase(const sfud_flash *flash) {
445     sfud_err result = SFUD_SUCCESS;
446     const sfud_spi *spi = &flash->spi;
447     uint8_t cmd_data[4];
448 
449     SFUD_ASSERT(flash);
450     /* must be call this function after initialize OK */
451     SFUD_ASSERT(flash->init_ok);
452     /* lock SPI */
453     if (spi->lock) {
454         spi->lock(spi);
455     }
456 
457     /* set the flash write enable */
458     result = set_write_enabled(flash, true);
459     if (result != SFUD_SUCCESS) {
460         goto __exit;
461     }
462 
463     cmd_data[0] = SFUD_CMD_ERASE_CHIP;
464     /* dual-buffer write, like AT45DB series flash chip erase operate is different for other flash */
465     if (flash->chip.write_mode & SFUD_WM_DUAL_BUFFER) {
466         cmd_data[1] = 0x94;
467         cmd_data[2] = 0x80;
468         cmd_data[3] = 0x9A;
469         result = spi->wr(spi, cmd_data, 4, NULL, 0);
470     } else {
471         result = spi->wr(spi, cmd_data, 1, NULL, 0);
472     }
473     if (result != SFUD_SUCCESS) {
474         SFUD_INFO("Error: Flash chip erase SPI communicate error.");
475         goto __exit;
476     }
477     result = wait_busy(flash);
478 
479 __exit:
480     /* set the flash write disable */
481     set_write_enabled(flash, false);
482     /* unlock SPI */
483     if (spi->unlock) {
484         spi->unlock(spi);
485     }
486 
487     return result;
488 }
489 
490 /**
491  * erase flash data
492  *
493  * @note It will erase align by erase granularity.
494  *
495  * @param flash flash device
496  * @param addr start address
497  * @param size erase size
498  *
499  * @return result
500  */
sfud_erase(const sfud_flash * flash,uint32_t addr,size_t size)501 sfud_err sfud_erase(const sfud_flash *flash, uint32_t addr, size_t size) {
502     extern size_t sfud_sfdp_get_suitable_eraser(const sfud_flash *flash, uint32_t addr, size_t erase_size);
503 
504     sfud_err result = SFUD_SUCCESS;
505     const sfud_spi *spi = &flash->spi;
506     uint8_t cmd_data[5], cmd_size, cur_erase_cmd;
507     size_t cur_erase_size;
508 
509     SFUD_ASSERT(flash);
510     /* must be call this function after initialize OK */
511     SFUD_ASSERT(flash->init_ok);
512     /* check the flash address bound */
513     if (addr + size > flash->chip.capacity) {
514         SFUD_INFO("Error: Flash address is out of bound.");
515         return SFUD_ERR_ADDR_OUT_OF_BOUND;
516     }
517 
518     if (addr == 0 && size == flash->chip.capacity) {
519         return sfud_chip_erase(flash);
520     }
521 
522     /* lock SPI */
523     if (spi->lock) {
524         spi->lock(spi);
525     }
526 
527     /* loop erase operate. erase unit is erase granularity */
528     while (size) {
529         /* if this flash is support SFDP parameter, then used SFDP parameter supplies eraser */
530 #ifdef SFUD_USING_SFDP
531         size_t eraser_index;
532         if (flash->sfdp.available) {
533             /* get the suitable eraser for erase process from SFDP parameter */
534             eraser_index = sfud_sfdp_get_suitable_eraser(flash, addr, size);
535             cur_erase_cmd = flash->sfdp.eraser[eraser_index].cmd;
536             cur_erase_size = flash->sfdp.eraser[eraser_index].size;
537         } else {
538 #else
539         {
540 #endif
541             cur_erase_cmd = flash->chip.erase_gran_cmd;
542             cur_erase_size = flash->chip.erase_gran;
543         }
544         /* set the flash write enable */
545         result = set_write_enabled(flash, true);
546         if (result != SFUD_SUCCESS) {
547             goto __exit;
548         }
549 
550         cmd_data[0] = cur_erase_cmd;
551         make_adress_byte_array(flash, addr, &cmd_data[1]);
552         cmd_size = flash->addr_in_4_byte ? 5 : 4;
553         result = spi->wr(spi, cmd_data, cmd_size, NULL, 0);
554         if (result != SFUD_SUCCESS) {
555             SFUD_INFO("Error: Flash erase SPI communicate error.");
556             goto __exit;
557         }
558         result = wait_busy(flash);
559         if (result != SFUD_SUCCESS) {
560             goto __exit;
561         }
562         /* make erase align and calculate next erase address */
563         if (addr % cur_erase_size != 0) {
564             if (size > cur_erase_size - (addr % cur_erase_size)) {
565                 size -= cur_erase_size - (addr % cur_erase_size);
566                 addr += cur_erase_size - (addr % cur_erase_size);
567             } else {
568                 goto __exit;
569             }
570         } else {
571             if (size > cur_erase_size) {
572                 size -= cur_erase_size;
573                 addr += cur_erase_size;
574             } else {
575                 goto __exit;
576             }
577         }
578     }
579 
580 __exit:
581     /* set the flash write disable */
582     set_write_enabled(flash, false);
583     /* unlock SPI */
584     if (spi->unlock) {
585         spi->unlock(spi);
586     }
587 
588     return result;
589 }
590 
591 /**
592  * write flash data (no erase operate) for write 1 to 256 bytes per page mode or byte write mode
593  *
594  * @param flash flash device
595  * @param addr start address
596  * @param size write size
597  * @param write_gran write granularity bytes, only support 1 or 256
598  * @param data write data
599  *
600  * @return result
601  */
602 static sfud_err page256_or_1_byte_write(const sfud_flash *flash, uint32_t addr, size_t size, uint16_t write_gran,
603         const uint8_t *data) {
604     sfud_err result = SFUD_SUCCESS;
605     const sfud_spi *spi = &flash->spi;
606     static uint8_t cmd_data[5 + SFUD_WRITE_MAX_PAGE_SIZE];
607     uint8_t cmd_size;
608     size_t data_size;
609 
610     SFUD_ASSERT(flash);
611     /* only support 1 or 256 */
612     SFUD_ASSERT(write_gran == 1 || write_gran == 256);
613     /* must be call this function after initialize OK */
614     SFUD_ASSERT(flash->init_ok);
615     /* check the flash address bound */
616     if (addr + size > flash->chip.capacity) {
617         SFUD_INFO("Error: Flash address is out of bound.");
618         return SFUD_ERR_ADDR_OUT_OF_BOUND;
619     }
620     /* lock SPI */
621     if (spi->lock) {
622         spi->lock(spi);
623     }
624 
625     /* loop write operate. write unit is write granularity */
626     while (size) {
627         /* set the flash write enable */
628         result = set_write_enabled(flash, true);
629         if (result != SFUD_SUCCESS) {
630             goto __exit;
631         }
632         cmd_data[0] = SFUD_CMD_PAGE_PROGRAM;
633         make_adress_byte_array(flash, addr, &cmd_data[1]);
634         cmd_size = flash->addr_in_4_byte ? 5 : 4;
635 
636         /* make write align and calculate next write address */
637         if (addr % write_gran != 0) {
638             if (size > write_gran - (addr % write_gran)) {
639                 data_size = write_gran - (addr % write_gran);
640             } else {
641                 data_size = size;
642             }
643         } else {
644             if (size > write_gran) {
645                 data_size = write_gran;
646             } else {
647                 data_size = size;
648             }
649         }
650         size -= data_size;
651         addr += data_size;
652 
653         memcpy(&cmd_data[cmd_size], data, data_size);
654 
655         result = spi->wr(spi, cmd_data, cmd_size + data_size, NULL, 0);
656         if (result != SFUD_SUCCESS) {
657             SFUD_INFO("Error: Flash write SPI communicate error.");
658             goto __exit;
659         }
660         result = wait_busy(flash);
661         if (result != SFUD_SUCCESS) {
662             goto __exit;
663         }
664         data += data_size;
665     }
666 
667 __exit:
668     /* set the flash write disable */
669     set_write_enabled(flash, false);
670     /* unlock SPI */
671     if (spi->unlock) {
672         spi->unlock(spi);
673     }
674 
675     return result;
676 }
677 
678 /**
679  * write flash data (no erase operate) for auto address increment mode
680  *
681  * If the address is odd number, it will place one 0xFF before the start of data for protect the old data.
682  * If the latest remain size is 1, it will append one 0xFF at the end of data for protect the old data.
683  *
684  * @param flash flash device
685  * @param addr start address
686  * @param size write size
687  * @param data write data
688  *
689  * @return result
690  */
691 static sfud_err aai_write(const sfud_flash *flash, uint32_t addr, size_t size, const uint8_t *data) {
692     sfud_err result = SFUD_SUCCESS;
693     const sfud_spi *spi = &flash->spi;
694     uint8_t cmd_data[6], cmd_size;
695     bool first_write = true;
696 
697     SFUD_ASSERT(flash);
698     SFUD_ASSERT(flash->init_ok);
699     /* check the flash address bound */
700     if (addr + size > flash->chip.capacity) {
701         SFUD_INFO("Error: Flash address is out of bound.");
702         return SFUD_ERR_ADDR_OUT_OF_BOUND;
703     }
704     /* lock SPI */
705     if (spi->lock) {
706         spi->lock(spi);
707     }
708     /* The address must be even for AAI write mode. So it must write one byte first when address is odd. */
709     if (addr % 2 != 0) {
710         result = page256_or_1_byte_write(flash, addr++, 1, 1, data++);
711         if (result != SFUD_SUCCESS) {
712             goto __exit;
713         }
714         size--;
715     }
716     /* set the flash write enable */
717     result = set_write_enabled(flash, true);
718     if (result != SFUD_SUCCESS) {
719         goto __exit;
720     }
721     /* loop write operate. */
722     cmd_data[0] = SFUD_CMD_AAI_WORD_PROGRAM;
723     while (size >= 2) {
724         if (first_write) {
725             make_adress_byte_array(flash, addr, &cmd_data[1]);
726             cmd_size = flash->addr_in_4_byte ? 5 : 4;
727             cmd_data[cmd_size] = *data;
728             cmd_data[cmd_size + 1] = *(data + 1);
729             first_write = false;
730         } else {
731             cmd_size = 1;
732             cmd_data[1] = *data;
733             cmd_data[2] = *(data + 1);
734         }
735 
736         result = spi->wr(spi, cmd_data, cmd_size + 2, NULL, 0);
737         if (result != SFUD_SUCCESS) {
738             SFUD_INFO("Error: Flash write SPI communicate error.");
739             goto __exit;
740         }
741 
742         result = wait_busy(flash);
743         if (result != SFUD_SUCCESS) {
744             goto __exit;
745         }
746 
747         size -= 2;
748         addr += 2;
749         data += 2;
750     }
751     /* set the flash write disable for exit AAI mode */
752     result = set_write_enabled(flash, false);
753     /* write last one byte data when origin write size is odd */
754     if (result == SFUD_SUCCESS && size == 1) {
755         result = page256_or_1_byte_write(flash, addr, 1, 1, data);
756     }
757 
758 __exit:
759     if (result != SFUD_SUCCESS) {
760         set_write_enabled(flash, false);
761     }
762     /* unlock SPI */
763     if (spi->unlock) {
764         spi->unlock(spi);
765     }
766 
767     return result;
768 }
769 
770 /**
771  * write flash data (no erase operate)
772  *
773  * @param flash flash device
774  * @param addr start address
775  * @param size write size
776  * @param data write data
777  *
778  * @return result
779  */
780 sfud_err sfud_write(const sfud_flash *flash, uint32_t addr, size_t size, const uint8_t *data) {
781     sfud_err result = SFUD_SUCCESS;
782 
783     if (flash->chip.write_mode & SFUD_WM_PAGE_256B) {
784         result = page256_or_1_byte_write(flash, addr, size, 256, data);
785     } else if (flash->chip.write_mode & SFUD_WM_AAI) {
786         result = aai_write(flash, addr, size, data);
787     } else if (flash->chip.write_mode & SFUD_WM_DUAL_BUFFER) {
788         //TODO dual-buffer write mode
789     }
790 
791     return result;
792 }
793 
794 /**
795  * erase and write flash data
796  *
797  * @param flash flash device
798  * @param addr start address
799  * @param size write size
800  * @param data write data
801  *
802  * @return result
803  */
804 sfud_err sfud_erase_write(const sfud_flash *flash, uint32_t addr, size_t size, const uint8_t *data) {
805     sfud_err result = SFUD_SUCCESS;
806 
807     result = sfud_erase(flash, addr, size);
808 
809     if (result == SFUD_SUCCESS) {
810         result = sfud_write(flash, addr, size, data);
811     }
812 
813     return result;
814 }
815 
816 static sfud_err reset(const sfud_flash *flash) {
817     sfud_err result = SFUD_SUCCESS;
818     const sfud_spi *spi = &flash->spi;
819     uint8_t cmd_data[2];
820 
821     SFUD_ASSERT(flash);
822 
823     cmd_data[0] = SFUD_CMD_ENABLE_RESET;
824     result = spi->wr(spi, cmd_data, 1, NULL, 0);
825     if (result == SFUD_SUCCESS) {
826         result = wait_busy(flash);
827     } else {
828         SFUD_INFO("Error: Flash device reset failed.");
829         return result;
830     }
831 
832     cmd_data[1] = SFUD_CMD_RESET;
833     result = spi->wr(spi, &cmd_data[1], 1, NULL, 0);
834 
835     if (result == SFUD_SUCCESS) {
836         result = wait_busy(flash);
837     }
838 
839     if (result == SFUD_SUCCESS) {
840         SFUD_DEBUG("Flash device reset success.");
841     } else {
842         SFUD_INFO("Error: Flash device reset failed.");
843     }
844 
845     return result;
846 }
847 
848 static sfud_err read_jedec_id(sfud_flash *flash) {
849     sfud_err result = SFUD_SUCCESS;
850     const sfud_spi *spi = &flash->spi;
851     uint8_t cmd_data[1], recv_data[3];
852 
853     SFUD_ASSERT(flash);
854 
855     cmd_data[0] = SFUD_CMD_JEDEC_ID;
856     result = spi->wr(spi, cmd_data, sizeof(cmd_data), recv_data, sizeof(recv_data));
857     if (result == SFUD_SUCCESS) {
858         flash->chip.mf_id = recv_data[0];
859         flash->chip.type_id = recv_data[1];
860         flash->chip.capacity_id = recv_data[2];
861         SFUD_DEBUG("The flash device manufacturer ID is 0x%02X, memory type ID is 0x%02X, capacity ID is 0x%02X.",
862                 flash->chip.mf_id, flash->chip.type_id, flash->chip.capacity_id);
863     } else {
864         SFUD_INFO("Error: Read flash device JEDEC ID error.");
865     }
866 
867     return result;
868 }
869 
870 /**
871  * set the flash write enable or write disable
872  *
873  * @param flash flash device
874  * @param enabled true: enable  false: disable
875  *
876  * @return result
877  */
878 static sfud_err set_write_enabled(const sfud_flash *flash, bool enabled) {
879     sfud_err result = SFUD_SUCCESS;
880     uint8_t cmd, register_status;
881 
882     SFUD_ASSERT(flash);
883 
884     if (enabled) {
885         cmd = SFUD_CMD_WRITE_ENABLE;
886     } else {
887         cmd = SFUD_CMD_WRITE_DISABLE;
888     }
889 
890     result = flash->spi.wr(&flash->spi, &cmd, 1, NULL, 0);
891 
892     if (result == SFUD_SUCCESS) {
893         result = sfud_read_status(flash, &register_status);
894     }
895 
896     if (result == SFUD_SUCCESS) {
897         if (enabled && (register_status & SFUD_STATUS_REGISTER_WEL) == 0) {
898             SFUD_INFO("Error: Can't enable write status.");
899             return SFUD_ERR_WRITE;
900         } else if (!enabled && (register_status & SFUD_STATUS_REGISTER_WEL) == 1) {
901             SFUD_INFO("Error: Can't disable write status.");
902             return SFUD_ERR_WRITE;
903         }
904     }
905 
906     return result;
907 }
908 
909 /**
910  * enable or disable 4-Byte addressing for flash
911  *
912  * @note The 4-Byte addressing just supported for the flash capacity which is large then 16MB (256Mb).
913  *
914  * @param flash flash device
915  * @param enabled true: enable   false: disable
916  *
917  * @return result
918  */
919 static sfud_err set_4_byte_address_mode(sfud_flash *flash, bool enabled) {
920     sfud_err result = SFUD_SUCCESS;
921     uint8_t cmd;
922 
923     SFUD_ASSERT(flash);
924 
925     /* set the flash write enable */
926     result = set_write_enabled(flash, true);
927     if (result != SFUD_SUCCESS) {
928         return result;
929     }
930 
931     if (enabled) {
932         cmd = SFUD_CMD_ENTER_4B_ADDRESS_MODE;
933     } else {
934         cmd = SFUD_CMD_EXIT_4B_ADDRESS_MODE;
935     }
936 
937     result = flash->spi.wr(&flash->spi, &cmd, 1, NULL, 0);
938 
939     if (result == SFUD_SUCCESS) {
940         flash->addr_in_4_byte = enabled ? true : false;
941         SFUD_DEBUG("%s 4-Byte addressing mode success.", enabled ? "Enter" : "Exit");
942     } else {
943         SFUD_INFO("Error: %s 4-Byte addressing mode failed.", enabled ? "Enter" : "Exit");
944     }
945 
946     return result;
947 }
948 
949 /**
950  * read flash register status
951  *
952  * @param flash flash device
953  * @param status register status
954  *
955  * @return result
956  */
957 sfud_err sfud_read_status(const sfud_flash *flash, uint8_t *status) {
958     uint8_t cmd = SFUD_CMD_READ_STATUS_REGISTER;
959 
960     SFUD_ASSERT(flash);
961     SFUD_ASSERT(status);
962 
963     return flash->spi.wr(&flash->spi, &cmd, 1, status, 1);
964 }
965 
966 static sfud_err wait_busy(const sfud_flash *flash) {
967     sfud_err result = SFUD_SUCCESS;
968     uint8_t status;
969     size_t retry_times = flash->retry.times;
970 
971     SFUD_ASSERT(flash);
972 
973     while (true) {
974         result = sfud_read_status(flash, &status);
975         if (result == SFUD_SUCCESS && ((status & SFUD_STATUS_REGISTER_BUSY)) == 0) {
976             break;
977         }
978         /* retry counts */
979         SFUD_RETRY_PROCESS(flash->retry.delay, retry_times, result);
980     }
981 
982     if (result != SFUD_SUCCESS || ((status & SFUD_STATUS_REGISTER_BUSY)) != 0) {
983         SFUD_INFO("Error: Flash wait busy has an error.");
984     }
985 
986     return result;
987 }
988 
989 static void make_adress_byte_array(const sfud_flash *flash, uint32_t addr, uint8_t *array) {
990     uint8_t len, i;
991 
992     SFUD_ASSERT(flash);
993     SFUD_ASSERT(array);
994 
995     len = flash->addr_in_4_byte ? 4 : 3;
996 
997     for (i = 0; i < len; i++) {
998         array[i] = (addr >> ((len - (i + 1)) * 8)) & 0xFF;
999     }
1000 }
1001 
1002 /**
1003  * write status register
1004  *
1005  * @param flash flash device
1006  * @param is_volatile true: volatile mode, false: non-volatile mode
1007  * @param status register status
1008  *
1009  * @return result
1010  */
1011 sfud_err sfud_write_status(const sfud_flash *flash, bool is_volatile, uint8_t status) {
1012     sfud_err result = SFUD_SUCCESS;
1013     const sfud_spi *spi = &flash->spi;
1014     uint8_t cmd_data[2];
1015 
1016     SFUD_ASSERT(flash);
1017 
1018     if (is_volatile) {
1019         cmd_data[0] = SFUD_VOLATILE_SR_WRITE_ENABLE;
1020         result = spi->wr(spi, cmd_data, 1, NULL, 0);
1021     } else {
1022         result = set_write_enabled(flash, true);
1023     }
1024 
1025     if (result == SFUD_SUCCESS) {
1026         cmd_data[0] = SFUD_CMD_WRITE_STATUS_REGISTER;
1027         cmd_data[1] = status;
1028         result = spi->wr(spi, cmd_data, 2, NULL, 0);
1029     }
1030 
1031     if (result != SFUD_SUCCESS) {
1032         SFUD_INFO("Error: Write_status register failed.");
1033     }
1034 
1035     return result;
1036 }
1037