Use the cached value of useDshotTelemetry to ensure consistent runtime use if dshot_b...
[betaflight.git] / src / main / drivers / flash_m25p16.c
blob25fde81d38ea142d189070e4903be10c57e0e5b9
1 /*
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)
8 * any later version.
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/>.
21 #include <stdbool.h>
22 #include <stdint.h>
23 #include <string.h>
25 #include "platform.h"
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"
38 #include "pg/flash.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
68 struct {
69 uint32_t jedecID;
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 },
84 // Micron M25P16
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 },
87 // Micron N25Q064
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 },
90 // Micron N25Q128
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 },
93 // Winbond W25Q80
94 // Datasheet: https://www.winbond.com/resource-files/w25q80dv%20dl_revh_10022015.pdf
95 { 0xEF4014, 104, 50, 16, 256 },
96 // Winbond W25Q16
97 // Datasheet: https://www.winbond.com/resource-files/w25q16dv_revi_nov1714_web.pdf
98 { 0xEF4015, 104, 50, 32, 256 },
99 // Winbond W25X32
100 // Datasheet: https://www.winbond.com/resource-files/w25x32a_revb_080709.pdf
101 { 0xEF3016, 133, 50, 64, 256 },
102 // Winbond W25Q32
103 // Datasheet: https://www.winbond.com/resource-files/w25q32jv%20dtr%20revf%2002242017.pdf?__locale=zh_TW
104 { 0xEF4016, 133, 50, 64, 256 },
105 // Winbond W25Q64
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*
109 // Winbond W25Q128
110 // Datasheet: https://www.winbond.com/resource-files/w25q128fv%20rev.l%2008242015.pdf
111 { 0xEF4018, 104, 50, 256, 256 },
112 // PUYA PY25Q128
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 },
115 // Zbit ZB25VQ128
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 },
121 // Winbond W25Q256
122 // Datasheet: https://www.winbond.com/resource-files/w25q256jv%20spi%20revb%2009202016.pdf
123 { 0xEF4019, 133, 50, 512, 256 },
124 // Cypress S25FL064L
125 // Datasheet: https://www.cypress.com/file/316661/download
126 { 0x016017, 133, 50, 128, 256 },
127 // Cypress S25FL128L
128 // Datasheet: https://www.cypress.com/file/316171/download
129 { 0x016018, 133, 50, 256, 256 },
130 // BergMicro W25Q32
131 // Datasheet: https://www.winbond.com/resource-files/w25q32jv%20dtr%20revf%2002242017.pdf?__locale=zh_TW
132 { 0xE04016, 133, 50, 1024, 16 },
133 // End of list
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;
143 #ifdef USE_QUADSPI
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)
151 uint8_t status = 0;
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];
159 } else {
160 #ifdef USE_QUADSPI
161 if (fdevice->io.mode == FLASHIO_QUADSPI) {
162 quadSpiReceive1LINE(fdevice->io.handle.quadSpi, M25P16_INSTRUCTION_READ_STATUS_REG, 0, &status, 1);
164 #endif
167 return status;
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)) {
175 return false;
179 // If couldBeBusy is false, don't bother to poll the flash chip for its status
180 if (!fdevice->couldBeBusy) {
181 return true;
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));
194 return true;
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;
205 uint8_t index;
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;
213 break;
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;
223 return false;
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;
237 #ifdef USE_QUADSPI
238 else if (fdevice->io.mode == FLASHIO_QUADSPI) {
239 fdevice->vTable = &m25p16Qspi_vTable;
241 #endif
242 return true;
245 void m25p16_configure(flashDevice_t *fdevice, uint32_t configurationFlags)
247 if (configurationFlags & FLASH_CF_SYSTEM_IS_MEMORY_MAPPED) {
248 return;
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));
266 #ifdef USE_QUADSPI
267 else if (fdevice->io.mode == FLASHIO_QUADSPI) {
268 quadSpiTransmit1LINE(fdevice->io.handle.quadSpi, W25Q256_INSTRUCTION_ENTER_4BYTE_ADDRESS_MODE, 0, NULL, 0);
270 #endif
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;
294 return BUS_READY;
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);
310 return BUS_READY;
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) {
323 return BUS_BUSY;
326 // Bus is now known not to be busy
327 fdevice->couldBeBusy = false;
329 return BUS_READY;
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(&sectorErase[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);
361 #ifdef USE_QUADSPI
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;
371 #endif
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);
393 #ifdef USE_QUADSPI
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;
403 #endif
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;
462 } else {
463 return 0;
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)
478 UNUSED(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);
505 #ifdef USE_QUADSPI
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) {
511 return 0;
514 uint32_t dataSize;
515 const uint8_t * pData;
517 if (bufferCount == 1) {
518 dataSize = bufferSizes[0];
519 if (dataSize > M25P16_PAGESIZE) {
520 return 0;
522 pData = buffers[0];
523 } else {
524 // Need to copy all buffers into page buffer
525 dataSize = 0;
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)) {
530 return 0;
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));
601 return length;
604 #ifdef USE_QUADSPI
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);
614 return 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,
642 #ifdef USE_QUADSPI
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 */
657 #endif