2 * This file is part of Cleanflight and Betaflight.
4 * Cleanflight and Betaflight are free software. You can redistribute
5 * this software and/or modify this software under the terms of the
6 * GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option)
10 * Cleanflight and Betaflight are distributed in the hope that they
11 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this software.
18 * If not, see <http://www.gnu.org/licenses/>.
27 #include "build/debug.h"
29 #ifdef USE_FLASH_M25P16
31 #include "drivers/bus_spi.h"
32 #include "drivers/bus_quadspi.h"
33 #include "drivers/flash.h"
34 #include "drivers/flash_impl.h"
35 #include "drivers/io.h"
36 #include "drivers/time.h"
40 #include "flash_m25p16.h"
42 #define M25P16_INSTRUCTION_RDID SPIFLASH_INSTRUCTION_RDID
43 #define M25P16_INSTRUCTION_READ_BYTES 0x03
44 #define M25P16_INSTRUCTION_QUAD_READ 0x6B
45 #define M25P16_INSTRUCTION_READ_STATUS_REG 0x05
46 #define M25P16_INSTRUCTION_WRITE_STATUS_REG 0x01
47 #define M25P16_INSTRUCTION_WRITE_ENABLE 0x06
48 #define M25P16_INSTRUCTION_WRITE_DISABLE 0x04
49 #define M25P16_INSTRUCTION_PAGE_PROGRAM 0x02
50 #define M25P16_INSTRUCTION_QPAGE_PROGRAM 0x32
51 #define M25P16_INSTRUCTION_SECTOR_ERASE 0xD8
52 #define M25P16_INSTRUCTION_BULK_ERASE 0xC7
54 #define M25P16_STATUS_FLAG_WRITE_IN_PROGRESS 0x01
55 #define M25P16_STATUS_FLAG_WRITE_ENABLED 0x02
57 #define W25Q256_INSTRUCTION_ENTER_4BYTE_ADDRESS_MODE 0xB7
59 #define M25P16_FAST_READ_DUMMY_CYCLES 8
61 // SPI transaction segment indicies for m25p16_pageProgramContinue()
62 enum {READ_STATUS
, WRITE_ENABLE
, PAGE_PROGRAM
, DATA1
, DATA2
};
64 static uint32_t maxClkSPIHz
;
65 static uint32_t maxReadClkSPIHz
;
67 // Table of recognised FLASH devices
70 uint16_t maxClkSPIMHz
;
71 uint16_t maxReadClkSPIMHz
;
72 flashSector_t sectors
;
73 uint16_t pagesPerSector
;
74 } m25p16FlashConfig
[] = {
75 // Macronix MX25L3206E
76 // Datasheet: https://docs.rs-online.com/5c85/0900766b814ac6f9.pdf
77 { 0xC22016, 86, 33, 64, 256 },
78 // Macronix MX25L6406E
79 // Datasheet: https://www.macronix.com/Lists/Datasheet/Attachments/7370/MX25L6406E,%203V,%2064Mb,%20v1.9.pdf
80 { 0xC22017, 86, 33, 128, 256 },
81 // Macronix MX25L25635E
82 // Datasheet: https://www.macronix.com/Lists/Datasheet/Attachments/7331/MX25L25635E,%203V,%20256Mb,%20v1.3.pdf
83 { 0xC22019, 80, 50, 512, 256 },
85 // Datasheet: https://www.micron.com/-/media/client/global/documents/products/data-sheet/nor-flash/serial-nor/m25p/m25p16.pdf
86 { 0x202015, 25, 20, 32, 256 },
88 // Datasheet: https://www.micron.com/-/media/client/global/documents/products/data-sheet/nor-flash/serial-nor/n25q/n25q_64a_3v_65nm.pdf
89 { 0x20BA17, 108, 54, 128, 256 },
91 // Datasheet: https://www.micron.com/-/media/client/global/documents/products/data-sheet/nor-flash/serial-nor/n25q/n25q_128mb_1_8v_65nm.pdf
92 { 0x20ba18, 108, 54, 256, 256 },
94 // Datasheet: https://www.winbond.com/resource-files/w25q80dv%20dl_revh_10022015.pdf
95 { 0xEF4014, 104, 50, 16, 256 },
97 // Datasheet: https://www.winbond.com/resource-files/w25q16dv_revi_nov1714_web.pdf
98 { 0xEF4015, 104, 50, 32, 256 },
100 // Datasheet: https://www.winbond.com/resource-files/w25x32a_revb_080709.pdf
101 { 0xEF3016, 133, 50, 64, 256 },
103 // Datasheet: https://www.winbond.com/resource-files/w25q32jv%20dtr%20revf%2002242017.pdf?__locale=zh_TW
104 { 0xEF4016, 133, 50, 64, 256 },
106 // Datasheet: https://www.winbond.com/resource-files/w25q64jv%20spi%20%20%20revc%2006032016%20kms.pdf
107 { 0xEF4017, 133, 50, 128, 256 }, // W25Q64JV-IQ/JQ
108 { 0xEF7017, 133, 50, 128, 256 }, // W25Q64JV-IM/JM*
110 // Datasheet: https://www.winbond.com/resource-files/w25q128fv%20rev.l%2008242015.pdf
111 { 0xEF4018, 104, 50, 256, 256 },
113 // Datasheet: https://www.puyasemi.com/download_path/%E6%95%B0%E6%8D%AE%E6%89%8B%E5%86%8C/Flash%20%E8%8A%AF%E7%89%87/PY25F128HA_datasheet_V1.1.pdf
114 { 0x852018, 133, 80, 256, 256 },
116 // Datasheet: http://zbitsemi.com/upload/file/20201010/20201010174048_82182.pdf
117 { 0x5E4018, 104, 50, 256, 256 },
118 // Winbond W25Q128_DTR
119 // Datasheet: https://www.winbond.com/resource-files/w25q128jv%20dtr%20revb%2011042016.pdf
120 { 0xEF7018, 66, 50, 256, 256 },
122 // Datasheet: https://www.winbond.com/resource-files/w25q256jv%20spi%20revb%2009202016.pdf
123 { 0xEF4019, 133, 50, 512, 256 },
125 // Datasheet: https://www.cypress.com/file/316661/download
126 { 0x016017, 133, 50, 128, 256 },
128 // Datasheet: https://www.cypress.com/file/316171/download
129 { 0x016018, 133, 50, 256, 256 },
131 // Datasheet: https://www.winbond.com/resource-files/w25q32jv%20dtr%20revf%2002242017.pdf?__locale=zh_TW
132 { 0xE04016, 133, 50, 1024, 16 },
134 { 0x000000, 0, 0, 0, 0 }
137 #define M25P16_PAGESIZE 256
139 STATIC_ASSERT(M25P16_PAGESIZE
< FLASH_MAX_PAGE_SIZE
, M25P16_PAGESIZE_too_small
);
141 const flashVTable_t m25p16_vTable
;
144 const flashVTable_t m25p16Qspi_vTable
;
145 // Buffer needed for assembling flash page when writing
146 static uint8_t m25p16_page_buffer
[M25P16_PAGESIZE
];
147 #endif /* USE_QUADSPI */
149 static uint8_t m25p16_readStatus(flashDevice_t
*fdevice
)
152 if (fdevice
->io
.mode
== FLASHIO_SPI
) {
153 STATIC_DMA_DATA_AUTO
uint8_t readStatus
[2] = { M25P16_INSTRUCTION_READ_STATUS_REG
, 0 };
154 STATIC_DMA_DATA_AUTO
uint8_t readyStatus
[2];
156 spiReadWriteBuf(fdevice
->io
.handle
.dev
, readStatus
, readyStatus
, sizeof(readStatus
));
158 status
= readyStatus
[1];
161 if (fdevice
->io
.mode
== FLASHIO_QUADSPI
) {
162 quadSpiReceive1LINE(fdevice
->io
.handle
.quadSpi
, M25P16_INSTRUCTION_READ_STATUS_REG
, 0, &status
, 1);
170 static bool m25p16_isReady(flashDevice_t
*fdevice
)
172 // If we're waiting on DMA completion, then SPI is busy
173 if (fdevice
->io
.mode
== FLASHIO_SPI
) {
174 if (fdevice
->io
.handle
.dev
->bus
->useDMA
&& spiIsBusy(fdevice
->io
.handle
.dev
)) {
179 // If couldBeBusy is false, don't bother to poll the flash chip for its status
180 if (!fdevice
->couldBeBusy
) {
184 // Poll the FLASH device to see if it's busy
185 fdevice
->couldBeBusy
= ((m25p16_readStatus(fdevice
) & M25P16_STATUS_FLAG_WRITE_IN_PROGRESS
) != 0);
187 return !fdevice
->couldBeBusy
;
190 static bool m25p16_waitForReady(flashDevice_t
*fdevice
)
192 while (!m25p16_isReady(fdevice
));
198 * Read chip identification and geometry information (into global `geometry`).
200 * Returns true if we get valid ident, false if something bad happened like there is no M25P16.
202 bool m25p16_identify(flashDevice_t
*fdevice
, uint32_t jedecID
)
204 flashGeometry_t
*geometry
= &fdevice
->geometry
;
207 for (index
= 0; m25p16FlashConfig
[index
].jedecID
; index
++) {
208 if (m25p16FlashConfig
[index
].jedecID
== jedecID
) {
209 maxClkSPIHz
= m25p16FlashConfig
[index
].maxClkSPIMHz
* 1000000;
210 maxReadClkSPIHz
= m25p16FlashConfig
[index
].maxReadClkSPIMHz
* 1000000;
211 geometry
->sectors
= m25p16FlashConfig
[index
].sectors
;
212 geometry
->pagesPerSector
= m25p16FlashConfig
[index
].pagesPerSector
;
217 if (m25p16FlashConfig
[index
].jedecID
== 0) {
218 // Unsupported chip or not an SPI NOR flash
219 geometry
->sectors
= 0;
220 geometry
->pagesPerSector
= 0;
221 geometry
->sectorSize
= 0;
222 geometry
->totalSize
= 0;
226 geometry
->flashType
= FLASH_TYPE_NOR
;
227 geometry
->pageSize
= M25P16_PAGESIZE
;
228 geometry
->sectorSize
= geometry
->pagesPerSector
* geometry
->pageSize
;
229 geometry
->totalSize
= geometry
->sectorSize
* geometry
->sectors
;
231 fdevice
->couldBeBusy
= true; // Just for luck we'll assume the chip could be busy even though it isn't specced to be
232 fdevice
->couldBeBusy
= true; // Just for luck we'll assume the chip could be busy even though it isn't specced to be
234 if (fdevice
->io
.mode
== FLASHIO_SPI
) {
235 fdevice
->vTable
= &m25p16_vTable
;
238 else if (fdevice
->io
.mode
== FLASHIO_QUADSPI
) {
239 fdevice
->vTable
= &m25p16Qspi_vTable
;
245 void m25p16_configure(flashDevice_t
*fdevice
, uint32_t configurationFlags
)
247 if (configurationFlags
& FLASH_CF_SYSTEM_IS_MEMORY_MAPPED
) {
251 if (fdevice
->io
.mode
== FLASHIO_SPI
) {
252 // Adjust the SPI bus clock frequency
253 spiSetClkDivisor(fdevice
->io
.handle
.dev
, spiCalculateDivider(maxReadClkSPIHz
));
256 flashGeometry_t
*geometry
= &fdevice
->geometry
;
257 if (geometry
->totalSize
> 16 * 1024 * 1024) {
258 fdevice
->isLargeFlash
= true;
260 // This routine blocks so no need to use static data
261 uint8_t modeSet
[] = { W25Q256_INSTRUCTION_ENTER_4BYTE_ADDRESS_MODE
};
263 if (fdevice
->io
.mode
== FLASHIO_SPI
) {
264 spiReadWriteBuf(fdevice
->io
.handle
.dev
, modeSet
, NULL
, sizeof(modeSet
));
267 else if (fdevice
->io
.mode
== FLASHIO_QUADSPI
) {
268 quadSpiTransmit1LINE(fdevice
->io
.handle
.quadSpi
, W25Q256_INSTRUCTION_ENTER_4BYTE_ADDRESS_MODE
, 0, NULL
, 0);
275 static void m25p16_setCommandAddress(uint8_t *buf
, uint32_t address
, bool useLongAddress
)
277 if (useLongAddress
) {
278 *buf
++ = (address
>> 24) & 0xff;
280 *buf
++ = (address
>> 16) & 0xff;
281 *buf
++ = (address
>> 8) & 0xff;
282 *buf
= address
& 0xff;
285 // Called in ISR context
286 // A write enable has just been issued
287 busStatus_e
m25p16_callbackWriteEnable(uint32_t arg
)
289 flashDevice_t
*fdevice
= (flashDevice_t
*)arg
;
291 // As a write has just occurred, the device could be busy
292 fdevice
->couldBeBusy
= true;
297 // Called in ISR context
298 // Write operation has just completed
299 busStatus_e
m25p16_callbackWriteComplete(uint32_t arg
)
301 flashDevice_t
*fdevice
= (flashDevice_t
*)arg
;
303 fdevice
->currentWriteAddress
+= fdevice
->callbackArg
;
305 // Call transfer completion callback
306 if (fdevice
->callback
) {
307 fdevice
->callback(fdevice
->callbackArg
);
313 // Called in ISR context
314 // Check if the status was busy and if so repeat the poll
315 busStatus_e
m25p16_callbackReady(uint32_t arg
)
317 flashDevice_t
*fdevice
= (flashDevice_t
*)arg
;
318 extDevice_t
*dev
= fdevice
->io
.handle
.dev
;
320 uint8_t readyPoll
= dev
->bus
->curSegment
->u
.buffers
.rxData
[1];
322 if (readyPoll
& M25P16_STATUS_FLAG_WRITE_IN_PROGRESS
) {
326 // Bus is now known not to be busy
327 fdevice
->couldBeBusy
= false;
334 * Erase a sector full of bytes to all 1's at the given byte offset in the flash chip.
336 static void m25p16_eraseSector(flashDevice_t
*fdevice
, uint32_t address
)
338 STATIC_DMA_DATA_AUTO
uint8_t sectorErase
[5] = { M25P16_INSTRUCTION_SECTOR_ERASE
};
339 STATIC_DMA_DATA_AUTO
uint8_t readStatus
[2] = { M25P16_INSTRUCTION_READ_STATUS_REG
, 0 };
340 STATIC_DMA_DATA_AUTO
uint8_t readyStatus
[2];
341 STATIC_DMA_DATA_AUTO
uint8_t writeEnable
[] = { M25P16_INSTRUCTION_WRITE_ENABLE
};
343 busSegment_t segments
[] = {
344 {.u
.buffers
= {readStatus
, readyStatus
}, sizeof(readStatus
), true, m25p16_callbackReady
},
345 {.u
.buffers
= {writeEnable
, NULL
}, sizeof(writeEnable
), true, m25p16_callbackWriteEnable
},
346 {.u
.buffers
= {sectorErase
, NULL
}, fdevice
->isLargeFlash
? 5 : 4, true, NULL
},
347 {.u
.link
= {NULL
, NULL
}, 0, true, NULL
},
350 // Ensure any prior DMA has completed before continuing
351 spiWait(fdevice
->io
.handle
.dev
);
353 m25p16_setCommandAddress(§orErase
[1], address
, fdevice
->isLargeFlash
);
355 spiSequence(fdevice
->io
.handle
.dev
, segments
);
357 // Block pending completion of SPI access, but the erase will be ongoing
358 spiWait(fdevice
->io
.handle
.dev
);
362 static void m25p16_eraseSectorQspi(flashDevice_t
*fdevice
, uint32_t address
)
364 m25p16_waitForReady(fdevice
);
366 quadSpiTransmit1LINE(fdevice
->io
.handle
.quadSpi
, M25P16_INSTRUCTION_WRITE_ENABLE
, 0, NULL
, 0);
367 quadSpiInstructionWithAddress1LINE(fdevice
->io
.handle
.quadSpi
, M25P16_INSTRUCTION_SECTOR_ERASE
, 0, address
, fdevice
->isLargeFlash
? 32 : 24);
369 fdevice
->couldBeBusy
= true;
373 static void m25p16_eraseCompletely(flashDevice_t
*fdevice
)
375 STATIC_DMA_DATA_AUTO
uint8_t readStatus
[2] = { M25P16_INSTRUCTION_READ_STATUS_REG
, 0 };
376 STATIC_DMA_DATA_AUTO
uint8_t readyStatus
[2];
377 STATIC_DMA_DATA_AUTO
uint8_t writeEnable
[] = { M25P16_INSTRUCTION_WRITE_ENABLE
};
378 STATIC_DMA_DATA_AUTO
uint8_t bulkErase
[] = { M25P16_INSTRUCTION_BULK_ERASE
};
380 busSegment_t segments
[] = {
381 {.u
.buffers
= {readStatus
, readyStatus
}, sizeof(readStatus
), true, m25p16_callbackReady
},
382 {.u
.buffers
= {writeEnable
, NULL
}, sizeof(writeEnable
), true, m25p16_callbackWriteEnable
},
383 {.u
.buffers
= {bulkErase
, NULL
}, sizeof(bulkErase
), true, NULL
},
384 {.u
.link
= {NULL
, NULL
}, 0, true, NULL
},
387 spiSequence(fdevice
->io
.handle
.dev
, segments
);
389 // Block pending completion of SPI access, but the erase will be ongoing
390 spiWait(fdevice
->io
.handle
.dev
);
394 static void m25p16_eraseCompletelyQspi(flashDevice_t
*fdevice
)
396 m25p16_waitForReady(fdevice
);
398 quadSpiTransmit1LINE(fdevice
->io
.handle
.quadSpi
, M25P16_INSTRUCTION_WRITE_ENABLE
, 0, NULL
, 0);
399 quadSpiTransmit1LINE(fdevice
->io
.handle
.quadSpi
, M25P16_INSTRUCTION_BULK_ERASE
, 0, NULL
, 0);
401 fdevice
->couldBeBusy
= true;
405 static void m25p16_pageProgramBegin(flashDevice_t
*fdevice
, uint32_t address
, void (*callback
)(uint32_t length
))
407 fdevice
->callback
= callback
;
408 fdevice
->currentWriteAddress
= address
;
412 static uint32_t m25p16_pageProgramContinue(flashDevice_t
*fdevice
, uint8_t const **buffers
, uint32_t *bufferSizes
, uint32_t bufferCount
)
414 // The segment list cannot be in automatic storage as this routine is non-blocking
415 STATIC_DMA_DATA_AUTO
uint8_t readStatus
[2] = { M25P16_INSTRUCTION_READ_STATUS_REG
, 0 };
416 STATIC_DMA_DATA_AUTO
uint8_t readyStatus
[2];
417 STATIC_DMA_DATA_AUTO
uint8_t writeEnable
[] = { M25P16_INSTRUCTION_WRITE_ENABLE
};
418 STATIC_DMA_DATA_AUTO
uint8_t pageProgram
[5] = { M25P16_INSTRUCTION_PAGE_PROGRAM
};
420 static busSegment_t segments
[] = {
421 {.u
.buffers
= {readStatus
, readyStatus
}, sizeof(readStatus
), true, m25p16_callbackReady
},
422 {.u
.buffers
= {writeEnable
, NULL
}, sizeof(writeEnable
), true, m25p16_callbackWriteEnable
},
423 {.u
.buffers
= {pageProgram
, NULL
}, 0, false, NULL
},
424 {.u
.link
= {NULL
, NULL
}, 0, true, NULL
},
425 {.u
.link
= {NULL
, NULL
}, 0, true, NULL
},
426 {.u
.link
= {NULL
, NULL
}, 0, true, NULL
},
429 // Ensure any prior DMA has completed before continuing
430 spiWait(fdevice
->io
.handle
.dev
);
432 // Patch the pageProgram segment
433 segments
[PAGE_PROGRAM
].len
= fdevice
->isLargeFlash
? 5 : 4;
434 m25p16_setCommandAddress(&pageProgram
[1], fdevice
->currentWriteAddress
, fdevice
->isLargeFlash
);
436 // Patch the data segments
437 segments
[DATA1
].u
.buffers
.txData
= (uint8_t *)buffers
[0];
438 segments
[DATA1
].len
= bufferSizes
[0];
439 fdevice
->callbackArg
= bufferSizes
[0];
441 /* As the DATA2 segment may be used as the terminating segment, the rxData and txData may be overwritten
442 * with a link to the following transaction (u.link.dev and u.link.segments respectively) so ensure that
443 * rxData is reinitialised otherwise it will remain pointing at a chained u.link.segments structure which
444 * would result in it being corrupted.
446 segments
[DATA2
].u
.buffers
.rxData
= (uint8_t *)NULL
;
448 if (bufferCount
== 1) {
449 segments
[DATA1
].negateCS
= true;
450 segments
[DATA1
].callback
= m25p16_callbackWriteComplete
;
451 // Mark segment following data as being of zero length
452 segments
[DATA2
].u
.buffers
.txData
= (uint8_t *)NULL
;
453 segments
[DATA2
].len
= 0;
454 } else if (bufferCount
== 2) {
455 segments
[DATA1
].negateCS
= false;
456 segments
[DATA1
].callback
= NULL
;
457 segments
[DATA2
].u
.buffers
.txData
= (uint8_t *)buffers
[1];
458 segments
[DATA2
].len
= bufferSizes
[1];
459 fdevice
->callbackArg
+= bufferSizes
[1];
460 segments
[DATA2
].negateCS
= true;
461 segments
[DATA2
].callback
= m25p16_callbackWriteComplete
;
466 spiSequence(fdevice
->io
.handle
.dev
, fdevice
->couldBeBusy
? &segments
[READ_STATUS
] : &segments
[WRITE_ENABLE
]);
468 if (fdevice
->callback
== NULL
) {
469 // No callback was provided so block
470 spiWait(fdevice
->io
.handle
.dev
);
473 return fdevice
->callbackArg
;
476 static void m25p16_pageProgramFinish(flashDevice_t
*fdevice
)
482 * Write bytes to a flash page. Address must not cross a page boundary.
484 * Bits can only be set to zero, not from zero back to one again. In order to set bits to 1, use the erase command.
486 * Length must be smaller than the page size.
488 * This will wait for the flash to become ready before writing begins.
490 * Datasheet indicates typical programming time is 0.8ms for 256 bytes, 0.2ms for 64 bytes, 0.05ms for 16 bytes.
491 * (Although the maximum possible write time is noted as 5ms).
493 * If you want to write multiple buffers (whose sum of sizes is still not more than the page size) then you can
494 * break this operation up into one beginProgram call, one or more continueProgram calls, and one finishProgram call.
496 static void m25p16_pageProgram(flashDevice_t
*fdevice
, uint32_t address
, const uint8_t *data
, uint32_t length
, void (*callback
)(uint32_t length
))
498 m25p16_pageProgramBegin(fdevice
, address
, callback
);
500 m25p16_pageProgramContinue(fdevice
, &data
, &length
, 1);
502 m25p16_pageProgramFinish(fdevice
);
506 // Page programming QSPI mode
508 static uint32_t m25p16_pageProgramContinueQspi(flashDevice_t
*fdevice
, uint8_t const **buffers
, uint32_t *bufferSizes
, uint32_t bufferCount
)
510 if (bufferCount
== 0) {
515 const uint8_t * pData
;
517 if (bufferCount
== 1) {
518 dataSize
= bufferSizes
[0];
519 if (dataSize
> M25P16_PAGESIZE
) {
524 // Need to copy all buffers into page buffer
526 uint8_t * pBuffer
= m25p16_page_buffer
;
527 for (uint32_t i
= 0; i
< bufferCount
; i
++) {
528 dataSize
+= bufferSizes
[i
];
529 if (dataSize
> sizeof(m25p16_page_buffer
)) {
532 memcpy(pBuffer
, buffers
[i
], bufferSizes
[i
]);
533 pBuffer
+= bufferSizes
[i
];
535 pData
= m25p16_page_buffer
;
538 m25p16_waitForReady(fdevice
);
540 quadSpiTransmit1LINE(fdevice
->io
.handle
.quadSpi
, M25P16_INSTRUCTION_WRITE_ENABLE
, 0, NULL
, 0);
542 quadSpiTransmitWithAddress4LINES(fdevice
->io
.handle
.quadSpi
, M25P16_INSTRUCTION_QPAGE_PROGRAM
, 0,
543 fdevice
->currentWriteAddress
, fdevice
->isLargeFlash
? 32 : 24, pData
, dataSize
);
545 fdevice
->currentWriteAddress
+= dataSize
;
547 if (fdevice
->callback
) {
548 fdevice
->callback(bufferSizes
[0]);
551 fdevice
->couldBeBusy
= true;
553 return bufferSizes
[0];
556 static void m25p16_pageProgramQspi(flashDevice_t
*fdevice
, uint32_t address
, const uint8_t *data
, uint32_t length
, void (*callback
)(uint32_t length
))
558 m25p16_pageProgramBegin(fdevice
, address
, callback
);
560 m25p16_pageProgramContinueQspi(fdevice
, &data
, &length
, 1);
562 m25p16_pageProgramFinish(fdevice
);
564 #endif /* USE_QUADSPI */
568 * Read `length` bytes into the provided `buffer` from the flash starting from the given `address` (which need not lie
569 * on a page boundary).
571 * The number of bytes actually read is returned, which can be zero if an error or timeout occurred.
573 static int m25p16_readBytes(flashDevice_t
*fdevice
, uint32_t address
, uint8_t *buffer
, uint32_t length
)
575 STATIC_DMA_DATA_AUTO
uint8_t readStatus
[2] = { M25P16_INSTRUCTION_READ_STATUS_REG
, 0 };
576 STATIC_DMA_DATA_AUTO
uint8_t readyStatus
[2];
577 STATIC_DMA_DATA_AUTO
uint8_t readBytes
[5] = { M25P16_INSTRUCTION_READ_BYTES
};
579 // Ensure any prior DMA has completed before continuing
580 spiWait(fdevice
->io
.handle
.dev
);
582 busSegment_t segments
[] = {
583 {.u
.buffers
= {readStatus
, readyStatus
}, sizeof(readStatus
), true, m25p16_callbackReady
},
584 {.u
.buffers
= {readBytes
, NULL
}, fdevice
->isLargeFlash
? 5 : 4, false, NULL
},
585 {.u
.buffers
= {NULL
, buffer
}, length
, true, NULL
},
586 {.u
.link
= {NULL
, NULL
}, 0, true, NULL
},
589 // Patch the readBytes command
590 m25p16_setCommandAddress(&readBytes
[1], address
, fdevice
->isLargeFlash
);
592 spiSetClkDivisor(fdevice
->io
.handle
.dev
, spiCalculateDivider(maxReadClkSPIHz
));
594 spiSequence(fdevice
->io
.handle
.dev
, fdevice
->couldBeBusy
? &segments
[0] : &segments
[1]);
596 // Block until code is re-factored to exploit non-blocking
597 spiWait(fdevice
->io
.handle
.dev
);
599 spiSetClkDivisor(fdevice
->io
.handle
.dev
, spiCalculateDivider(maxClkSPIHz
));
605 // Reading data QSPI mode
607 static int m25p16_readBytesQspi(flashDevice_t
*fdevice
, uint32_t address
, uint8_t *buffer
, uint32_t length
)
609 m25p16_waitForReady(fdevice
);
611 quadSpiReceiveWithAddress4LINES(fdevice
->io
.handle
.quadSpi
, M25P16_INSTRUCTION_QUAD_READ
, M25P16_FAST_READ_DUMMY_CYCLES
,
612 address
, fdevice
->isLargeFlash
? 32 : 24, buffer
, length
);
616 #endif /* USE_QUADSPI */
619 * Fetch information about the detected flash chip layout.
621 * Can be called before calling m25p16_init() (the result would have totalSize = 0).
623 static const flashGeometry_t
* m25p16_getGeometry(flashDevice_t
*fdevice
)
625 return &fdevice
->geometry
;
628 const flashVTable_t m25p16_vTable
= {
629 .configure
= m25p16_configure
,
630 .isReady
= m25p16_isReady
,
631 .waitForReady
= m25p16_waitForReady
,
632 .eraseSector
= m25p16_eraseSector
,
633 .eraseCompletely
= m25p16_eraseCompletely
,
634 .pageProgramBegin
= m25p16_pageProgramBegin
,
635 .pageProgramContinue
= m25p16_pageProgramContinue
,
636 .pageProgramFinish
= m25p16_pageProgramFinish
,
637 .pageProgram
= m25p16_pageProgram
,
638 .readBytes
= m25p16_readBytes
,
639 .getGeometry
= m25p16_getGeometry
,
643 const flashVTable_t m25p16Qspi_vTable
= {
644 .isReady
= m25p16_isReady
,
645 .waitForReady
= m25p16_waitForReady
,
646 .eraseSector
= m25p16_eraseSectorQspi
,
647 .eraseCompletely
= m25p16_eraseCompletelyQspi
,
648 .pageProgramBegin
= m25p16_pageProgramBegin
,
649 .pageProgramContinue
= m25p16_pageProgramContinueQspi
,
650 .pageProgramFinish
= m25p16_pageProgramFinish
,
651 .pageProgram
= m25p16_pageProgramQspi
,
652 .readBytes
= m25p16_readBytesQspi
,
653 .getGeometry
= m25p16_getGeometry
,
655 #endif /* USE_QUADSPI */