Refactor missing prototypes 2 (#14170)
[betaflight.git] / src / main / drivers / flash / flash_m25p16.c
blob1fcc66197ee129b664afb7b70998edff8d7fe10e
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/flash.h"
34 #include "drivers/flash/flash_impl.h"
35 #include "drivers/io.h"
36 #include "drivers/time.h"
38 #include "pg/flash.h"
40 #include "drivers/flash/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 // Datasheet: https://www.winbond.com/resource-files/W25Q64JW%20RevE%2003102021%20Plus.pdf
110 { 0xEF6017, 104, 50, 128, 256 }, // W25Q64JW-IQ/JQ
111 { 0xEF8017, 104, 50, 128, 256 }, // W25Q64JW-IM/JM*
112 // Winbond W25Q128
113 // Datasheet: https://www.winbond.com/resource-files/w25q128fv%20rev.l%2008242015.pdf
114 { 0xEF4018, 104, 50, 256, 256 },
115 // Winbond W25Q512
116 // Datasheet: https://www.winbond.com/resource-files/W25Q512JV%20SPI%20RevB%2006252019%20KMS.pdf
117 { 0xEF4020, 133, 50, 1024, 256 },
118 // PUYA PY25Q128
119 // 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
120 { 0x852018, 133, 80, 256, 256 },
121 // Zbit ZB25VQ128
122 // Datasheet: http://zbitsemi.com/upload/file/20201010/20201010174048_82182.pdf
123 { 0x5E4018, 104, 50, 256, 256 },
124 // Winbond W25Q128_DTR
125 // Datasheet: https://www.winbond.com/resource-files/w25q128jv%20dtr%20revb%2011042016.pdf
126 { 0xEF7018, 66, 50, 256, 256 },
127 // Winbond W25Q256
128 // Datasheet: https://www.winbond.com/resource-files/w25q256jv%20spi%20revb%2009202016.pdf
129 { 0xEF4019, 133, 50, 512, 256 },
130 // Cypress S25FL064L
131 // Datasheet: https://www.cypress.com/file/316661/download
132 { 0x016017, 133, 50, 128, 256 },
133 // Cypress S25FL128L
134 // Datasheet: https://www.cypress.com/file/316171/download
135 { 0x016018, 133, 50, 256, 256 },
136 // BergMicro W25Q32
137 // Datasheet: https://www.winbond.com/resource-files/w25q32jv%20dtr%20revf%2002242017.pdf?__locale=zh_TW
138 { 0xE04016, 133, 50, 1024, 16 },
139 // End of list
140 { 0x000000, 0, 0, 0, 0 }
143 #define M25P16_PAGESIZE 256
145 STATIC_ASSERT(M25P16_PAGESIZE < FLASH_MAX_PAGE_SIZE, M25P16_PAGESIZE_too_small);
147 const flashVTable_t m25p16_vTable;
149 #ifdef USE_QUADSPI
150 const flashVTable_t m25p16Qspi_vTable;
151 // Buffer needed for assembling flash page when writing
152 static uint8_t m25p16_page_buffer[M25P16_PAGESIZE];
153 #endif /* USE_QUADSPI */
155 static uint8_t m25p16_readStatus(flashDevice_t *fdevice)
157 uint8_t status = 0;
158 if (fdevice->io.mode == FLASHIO_SPI) {
159 STATIC_DMA_DATA_AUTO uint8_t readStatus[2] = { M25P16_INSTRUCTION_READ_STATUS_REG, 0 };
160 STATIC_DMA_DATA_AUTO uint8_t readyStatus[2];
162 spiReadWriteBuf(fdevice->io.handle.dev, readStatus, readyStatus, sizeof(readStatus));
164 status = readyStatus[1];
165 } else {
166 #ifdef USE_QUADSPI
167 if (fdevice->io.mode == FLASHIO_QUADSPI) {
168 quadSpiReceive1LINE(fdevice->io.handle.quadSpi, M25P16_INSTRUCTION_READ_STATUS_REG, 0, &status, 1);
170 #endif
173 return status;
176 static bool m25p16_isReady(flashDevice_t *fdevice)
178 // If we're waiting on DMA completion, then SPI is busy
179 if (fdevice->io.mode == FLASHIO_SPI) {
180 if (fdevice->io.handle.dev->bus->useDMA && spiIsBusy(fdevice->io.handle.dev)) {
181 return false;
185 // If couldBeBusy is false, don't bother to poll the flash chip for its status
186 if (!fdevice->couldBeBusy) {
187 return true;
190 // Poll the FLASH device to see if it's busy
191 fdevice->couldBeBusy = ((m25p16_readStatus(fdevice) & M25P16_STATUS_FLAG_WRITE_IN_PROGRESS) != 0);
193 return !fdevice->couldBeBusy;
196 static bool m25p16_waitForReady(flashDevice_t *fdevice)
198 while (!m25p16_isReady(fdevice));
200 return true;
204 * Read chip identification and geometry information (into global `geometry`).
206 * Returns true if we get valid ident, false if something bad happened like there is no M25P16.
208 bool m25p16_identify(flashDevice_t *fdevice, uint32_t jedecID)
210 flashGeometry_t *geometry = &fdevice->geometry;
211 uint8_t index;
213 for (index = 0; m25p16FlashConfig[index].jedecID; index++) {
214 if (m25p16FlashConfig[index].jedecID == jedecID) {
215 maxClkSPIHz = m25p16FlashConfig[index].maxClkSPIMHz * 1000000;
216 maxReadClkSPIHz = m25p16FlashConfig[index].maxReadClkSPIMHz * 1000000;
217 geometry->sectors = m25p16FlashConfig[index].sectors;
218 geometry->pagesPerSector = m25p16FlashConfig[index].pagesPerSector;
219 break;
223 if (m25p16FlashConfig[index].jedecID == 0) {
224 // Unsupported chip or not an SPI NOR flash
225 geometry->sectors = 0;
226 geometry->pagesPerSector = 0;
227 geometry->sectorSize = 0;
228 geometry->totalSize = 0;
229 return false;
232 geometry->flashType = FLASH_TYPE_NOR;
233 geometry->pageSize = M25P16_PAGESIZE;
234 geometry->sectorSize = geometry->pagesPerSector * geometry->pageSize;
235 geometry->totalSize = geometry->sectorSize * geometry->sectors;
237 fdevice->couldBeBusy = true; // Just for luck we'll assume the chip could be busy even though it isn't specced to be
238 fdevice->couldBeBusy = true; // Just for luck we'll assume the chip could be busy even though it isn't specced to be
240 if (fdevice->io.mode == FLASHIO_SPI) {
241 fdevice->vTable = &m25p16_vTable;
243 #ifdef USE_QUADSPI
244 else if (fdevice->io.mode == FLASHIO_QUADSPI) {
245 fdevice->vTable = &m25p16Qspi_vTable;
247 #endif
248 return true;
251 static void m25p16_configure(flashDevice_t *fdevice, uint32_t configurationFlags)
253 if (configurationFlags & FLASH_CF_SYSTEM_IS_MEMORY_MAPPED) {
254 return;
257 if (fdevice->io.mode == FLASHIO_SPI) {
258 // Adjust the SPI bus clock frequency
259 spiSetClkDivisor(fdevice->io.handle.dev, spiCalculateDivider(maxReadClkSPIHz));
262 flashGeometry_t *geometry = &fdevice->geometry;
263 if (geometry->totalSize > 16 * 1024 * 1024) {
264 fdevice->isLargeFlash = true;
266 // This routine blocks so no need to use static data
267 uint8_t modeSet[] = { W25Q256_INSTRUCTION_ENTER_4BYTE_ADDRESS_MODE };
269 if (fdevice->io.mode == FLASHIO_SPI) {
270 spiReadWriteBuf(fdevice->io.handle.dev, modeSet, NULL, sizeof(modeSet));
272 #ifdef USE_QUADSPI
273 else if (fdevice->io.mode == FLASHIO_QUADSPI) {
274 quadSpiTransmit1LINE(fdevice->io.handle.quadSpi, W25Q256_INSTRUCTION_ENTER_4BYTE_ADDRESS_MODE, 0, NULL, 0);
276 #endif
280 static void m25p16_setCommandAddress(uint8_t *buf, uint32_t address, bool useLongAddress)
282 if (useLongAddress) {
283 *buf++ = (address >> 24) & 0xff;
285 *buf++ = (address >> 16) & 0xff;
286 *buf++ = (address >> 8) & 0xff;
287 *buf = address & 0xff;
290 // Called in ISR context
291 // A write enable has just been issued
292 static busStatus_e m25p16_callbackWriteEnable(uint32_t arg)
294 flashDevice_t *fdevice = (flashDevice_t *)arg;
296 // As a write has just occurred, the device could be busy
297 fdevice->couldBeBusy = true;
299 return BUS_READY;
302 // Called in ISR context
303 // Write operation has just completed
304 static busStatus_e m25p16_callbackWriteComplete(uint32_t arg)
306 flashDevice_t *fdevice = (flashDevice_t *)arg;
308 fdevice->currentWriteAddress += fdevice->callbackArg;
310 // Call transfer completion callback
311 if (fdevice->callback) {
312 fdevice->callback(fdevice->callbackArg);
315 return BUS_READY;
318 // Called in ISR context
319 // Check if the status was busy and if so repeat the poll
320 static busStatus_e m25p16_callbackReady(uint32_t arg)
322 flashDevice_t *fdevice = (flashDevice_t *)arg;
323 extDevice_t *dev = fdevice->io.handle.dev;
325 uint8_t readyPoll = dev->bus->curSegment->u.buffers.rxData[1];
327 if (readyPoll & M25P16_STATUS_FLAG_WRITE_IN_PROGRESS) {
328 return BUS_BUSY;
331 // Bus is now known not to be busy
332 fdevice->couldBeBusy = false;
334 return BUS_READY;
338 * Erase a sector full of bytes to all 1's at the given byte offset in the flash chip.
340 static void m25p16_eraseSector(flashDevice_t *fdevice, uint32_t address)
342 STATIC_DMA_DATA_AUTO uint8_t sectorErase[5] = { M25P16_INSTRUCTION_SECTOR_ERASE };
343 STATIC_DMA_DATA_AUTO uint8_t readStatus[2] = { M25P16_INSTRUCTION_READ_STATUS_REG, 0 };
344 STATIC_DMA_DATA_AUTO uint8_t readyStatus[2];
345 STATIC_DMA_DATA_AUTO uint8_t writeEnable[] = { M25P16_INSTRUCTION_WRITE_ENABLE };
347 busSegment_t segments[] = {
348 {.u.buffers = {readStatus, readyStatus}, sizeof(readStatus), true, m25p16_callbackReady},
349 {.u.buffers = {writeEnable, NULL}, sizeof(writeEnable), true, m25p16_callbackWriteEnable},
350 {.u.buffers = {sectorErase, NULL}, fdevice->isLargeFlash ? 5 : 4, true, NULL},
351 {.u.link = {NULL, NULL}, 0, true, NULL},
354 // Ensure any prior DMA has completed before continuing
355 spiWait(fdevice->io.handle.dev);
357 m25p16_setCommandAddress(&sectorErase[1], address, fdevice->isLargeFlash);
359 spiSequence(fdevice->io.handle.dev, segments);
361 // Block pending completion of SPI access, but the erase will be ongoing
362 spiWait(fdevice->io.handle.dev);
365 #ifdef USE_QUADSPI
366 static void m25p16_eraseSectorQspi(flashDevice_t *fdevice, uint32_t address)
368 m25p16_waitForReady(fdevice);
370 quadSpiTransmit1LINE(fdevice->io.handle.quadSpi, M25P16_INSTRUCTION_WRITE_ENABLE, 0, NULL, 0);
371 quadSpiInstructionWithAddress1LINE(fdevice->io.handle.quadSpi, M25P16_INSTRUCTION_SECTOR_ERASE, 0, address, fdevice->isLargeFlash ? 32 : 24);
373 fdevice->couldBeBusy = true;
375 #endif
377 static void m25p16_eraseCompletely(flashDevice_t *fdevice)
379 STATIC_DMA_DATA_AUTO uint8_t readStatus[2] = { M25P16_INSTRUCTION_READ_STATUS_REG, 0 };
380 STATIC_DMA_DATA_AUTO uint8_t readyStatus[2];
381 STATIC_DMA_DATA_AUTO uint8_t writeEnable[] = { M25P16_INSTRUCTION_WRITE_ENABLE };
382 STATIC_DMA_DATA_AUTO uint8_t bulkErase[] = { M25P16_INSTRUCTION_BULK_ERASE };
384 busSegment_t segments[] = {
385 {.u.buffers = {readStatus, readyStatus}, sizeof(readStatus), true, m25p16_callbackReady},
386 {.u.buffers = {writeEnable, NULL}, sizeof(writeEnable), true, m25p16_callbackWriteEnable},
387 {.u.buffers = {bulkErase, NULL}, sizeof(bulkErase), true, NULL},
388 {.u.link = {NULL, NULL}, 0, true, NULL},
391 spiSequence(fdevice->io.handle.dev, segments);
393 // Block pending completion of SPI access, but the erase will be ongoing
394 spiWait(fdevice->io.handle.dev);
397 #ifdef USE_QUADSPI
398 static void m25p16_eraseCompletelyQspi(flashDevice_t *fdevice)
400 m25p16_waitForReady(fdevice);
402 quadSpiTransmit1LINE(fdevice->io.handle.quadSpi, M25P16_INSTRUCTION_WRITE_ENABLE, 0, NULL, 0);
403 quadSpiTransmit1LINE(fdevice->io.handle.quadSpi, M25P16_INSTRUCTION_BULK_ERASE, 0, NULL, 0);
405 fdevice->couldBeBusy = true;
407 #endif
409 static void m25p16_pageProgramBegin(flashDevice_t *fdevice, uint32_t address, void (*callback)(uint32_t length))
411 fdevice->callback = callback;
412 fdevice->currentWriteAddress = address;
415 static uint32_t m25p16_pageProgramContinue(flashDevice_t *fdevice, uint8_t const **buffers, const uint32_t *bufferSizes, uint32_t bufferCount)
417 // The segment list cannot be in automatic storage as this routine is non-blocking
418 STATIC_DMA_DATA_AUTO uint8_t readStatus[2] = { M25P16_INSTRUCTION_READ_STATUS_REG, 0 };
419 STATIC_DMA_DATA_AUTO uint8_t readyStatus[2];
420 STATIC_DMA_DATA_AUTO uint8_t writeEnable[] = { M25P16_INSTRUCTION_WRITE_ENABLE };
421 STATIC_DMA_DATA_AUTO uint8_t pageProgram[5] = { M25P16_INSTRUCTION_PAGE_PROGRAM };
423 static busSegment_t segments[] = {
424 {.u.buffers = {readStatus, readyStatus}, sizeof(readStatus), true, m25p16_callbackReady},
425 {.u.buffers = {writeEnable, NULL}, sizeof(writeEnable), true, m25p16_callbackWriteEnable},
426 {.u.buffers = {pageProgram, NULL}, 0, false, NULL},
427 {.u.link = {NULL, NULL}, 0, true, NULL},
428 {.u.link = {NULL, NULL}, 0, true, NULL},
429 {.u.link = {NULL, NULL}, 0, true, NULL},
432 // Ensure any prior DMA has completed before continuing
433 spiWait(fdevice->io.handle.dev);
435 // Patch the pageProgram segment
436 segments[PAGE_PROGRAM].len = fdevice->isLargeFlash ? 5 : 4;
437 m25p16_setCommandAddress(&pageProgram[1], fdevice->currentWriteAddress, fdevice->isLargeFlash);
439 // Patch the data segments
440 segments[DATA1].u.buffers.txData = (uint8_t *)buffers[0];
441 segments[DATA1].len = bufferSizes[0];
442 fdevice->callbackArg = bufferSizes[0];
444 /* As the DATA2 segment may be used as the terminating segment, the rxData and txData may be overwritten
445 * with a link to the following transaction (u.link.dev and u.link.segments respectively) so ensure that
446 * rxData is reinitialised otherwise it will remain pointing at a chained u.link.segments structure which
447 * would result in it being corrupted.
449 segments[DATA2].u.buffers.rxData = (uint8_t *)NULL;
451 if (bufferCount == 1) {
452 segments[DATA1].negateCS = true;
453 segments[DATA1].callback = m25p16_callbackWriteComplete;
454 // Mark segment following data as being of zero length
455 segments[DATA2].u.buffers.txData = (uint8_t *)NULL;
456 segments[DATA2].len = 0;
457 } else if (bufferCount == 2) {
458 segments[DATA1].negateCS = false;
459 segments[DATA1].callback = NULL;
460 segments[DATA2].u.buffers.txData = (uint8_t *)buffers[1];
461 segments[DATA2].len = bufferSizes[1];
462 fdevice->callbackArg += bufferSizes[1];
463 segments[DATA2].negateCS = true;
464 segments[DATA2].callback = m25p16_callbackWriteComplete;
465 } else {
466 return 0;
469 spiSequence(fdevice->io.handle.dev, fdevice->couldBeBusy ? &segments[READ_STATUS] : &segments[WRITE_ENABLE]);
471 if (fdevice->callback == NULL) {
472 // No callback was provided so block
473 spiWait(fdevice->io.handle.dev);
476 return fdevice->callbackArg;
479 static void m25p16_pageProgramFinish(flashDevice_t *fdevice)
481 UNUSED(fdevice);
485 * Write bytes to a flash page. Address must not cross a page boundary.
487 * 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.
489 * Length must be smaller than the page size.
491 * This will wait for the flash to become ready before writing begins.
493 * Datasheet indicates typical programming time is 0.8ms for 256 bytes, 0.2ms for 64 bytes, 0.05ms for 16 bytes.
494 * (Although the maximum possible write time is noted as 5ms).
496 * If you want to write multiple buffers (whose sum of sizes is still not more than the page size) then you can
497 * break this operation up into one beginProgram call, one or more continueProgram calls, and one finishProgram call.
499 static void m25p16_pageProgram(flashDevice_t *fdevice, uint32_t address, const uint8_t *data, uint32_t length, void (*callback)(uint32_t length))
501 m25p16_pageProgramBegin(fdevice, address, callback);
503 m25p16_pageProgramContinue(fdevice, &data, &length, 1);
505 m25p16_pageProgramFinish(fdevice);
508 #ifdef USE_QUADSPI
509 // Page programming QSPI mode
511 static uint32_t m25p16_pageProgramContinueQspi(flashDevice_t *fdevice, uint8_t const **buffers, const uint32_t *bufferSizes, uint32_t bufferCount)
513 if (bufferCount == 0) {
514 return 0;
517 uint32_t dataSize;
518 const uint8_t * pData;
520 if (bufferCount == 1) {
521 dataSize = bufferSizes[0];
522 if (dataSize > M25P16_PAGESIZE) {
523 return 0;
525 pData = buffers[0];
526 } else {
527 // Need to copy all buffers into page buffer
528 dataSize = 0;
529 uint8_t * pBuffer = m25p16_page_buffer;
530 for (uint32_t i = 0; i < bufferCount; i++) {
531 dataSize += bufferSizes[i];
532 if (dataSize > sizeof(m25p16_page_buffer)) {
533 return 0;
535 memcpy(pBuffer, buffers[i], bufferSizes[i]);
536 pBuffer += bufferSizes[i];
538 pData = m25p16_page_buffer;
541 m25p16_waitForReady(fdevice);
543 quadSpiTransmit1LINE(fdevice->io.handle.quadSpi, M25P16_INSTRUCTION_WRITE_ENABLE, 0, NULL, 0);
545 quadSpiTransmitWithAddress4LINES(fdevice->io.handle.quadSpi, M25P16_INSTRUCTION_QPAGE_PROGRAM, 0,
546 fdevice->currentWriteAddress, fdevice->isLargeFlash ? 32 : 24, pData, dataSize);
548 fdevice->currentWriteAddress += dataSize;
550 if (fdevice->callback) {
551 fdevice->callback(bufferSizes[0]);
554 fdevice->couldBeBusy = true;
556 return bufferSizes[0];
559 static void m25p16_pageProgramQspi(flashDevice_t *fdevice, uint32_t address, const uint8_t *data, uint32_t length, void (*callback)(uint32_t length))
561 m25p16_pageProgramBegin(fdevice, address, callback);
563 m25p16_pageProgramContinueQspi(fdevice, &data, &length, 1);
565 m25p16_pageProgramFinish(fdevice);
567 #endif /* USE_QUADSPI */
570 * Read `length` bytes into the provided `buffer` from the flash starting from the given `address` (which need not lie
571 * on a page boundary).
573 * The number of bytes actually read is returned, which can be zero if an error or timeout occurred.
575 static int m25p16_readBytes(flashDevice_t *fdevice, uint32_t address, uint8_t *buffer, uint32_t length)
577 STATIC_DMA_DATA_AUTO uint8_t readStatus[2] = { M25P16_INSTRUCTION_READ_STATUS_REG, 0 };
578 STATIC_DMA_DATA_AUTO uint8_t readyStatus[2];
579 STATIC_DMA_DATA_AUTO uint8_t readBytes[5] = { M25P16_INSTRUCTION_READ_BYTES };
581 // Ensure any prior DMA has completed before continuing
582 spiWait(fdevice->io.handle.dev);
584 busSegment_t segments[] = {
585 {.u.buffers = {readStatus, readyStatus}, sizeof(readStatus), true, m25p16_callbackReady},
586 {.u.buffers = {readBytes, NULL}, fdevice->isLargeFlash ? 5 : 4, false, NULL},
587 {.u.buffers = {NULL, buffer}, length, true, NULL},
588 {.u.link = {NULL, NULL}, 0, true, NULL},
591 // Patch the readBytes command
592 m25p16_setCommandAddress(&readBytes[1], address, fdevice->isLargeFlash);
594 spiSetClkDivisor(fdevice->io.handle.dev, spiCalculateDivider(maxReadClkSPIHz));
596 spiSequence(fdevice->io.handle.dev, fdevice->couldBeBusy ? &segments[0] : &segments[1]);
598 // Block until code is re-factored to exploit non-blocking
599 spiWait(fdevice->io.handle.dev);
601 spiSetClkDivisor(fdevice->io.handle.dev, spiCalculateDivider(maxClkSPIHz));
603 return length;
606 #ifdef USE_QUADSPI
607 // Reading data QSPI mode
609 static int m25p16_readBytesQspi(flashDevice_t *fdevice, uint32_t address, uint8_t *buffer, uint32_t length)
611 m25p16_waitForReady(fdevice);
613 quadSpiReceiveWithAddress4LINES(fdevice->io.handle.quadSpi, M25P16_INSTRUCTION_QUAD_READ, M25P16_FAST_READ_DUMMY_CYCLES,
614 address, fdevice->isLargeFlash ? 32 : 24, buffer, length);
616 return length;
618 #endif /* USE_QUADSPI */
621 * Fetch information about the detected flash chip layout.
623 * Can be called before calling m25p16_init() (the result would have totalSize = 0).
625 static const flashGeometry_t* m25p16_getGeometry(flashDevice_t *fdevice)
627 return &fdevice->geometry;
630 const flashVTable_t m25p16_vTable = {
631 .configure = m25p16_configure,
632 .isReady = m25p16_isReady,
633 .waitForReady = m25p16_waitForReady,
634 .eraseSector = m25p16_eraseSector,
635 .eraseCompletely = m25p16_eraseCompletely,
636 .pageProgramBegin = m25p16_pageProgramBegin,
637 .pageProgramContinue = m25p16_pageProgramContinue,
638 .pageProgramFinish = m25p16_pageProgramFinish,
639 .pageProgram = m25p16_pageProgram,
640 .readBytes = m25p16_readBytes,
641 .getGeometry = m25p16_getGeometry,
644 #ifdef USE_QUADSPI
645 const flashVTable_t m25p16Qspi_vTable = {
646 .isReady = m25p16_isReady,
647 .waitForReady = m25p16_waitForReady,
648 .eraseSector = m25p16_eraseSectorQspi,
649 .eraseCompletely = m25p16_eraseCompletelyQspi,
650 .pageProgramBegin = m25p16_pageProgramBegin,
651 .pageProgramContinue = m25p16_pageProgramContinueQspi,
652 .pageProgramFinish = m25p16_pageProgramFinish,
653 .pageProgram = m25p16_pageProgramQspi,
654 .readBytes = m25p16_readBytesQspi,
655 .getGeometry = m25p16_getGeometry,
657 #endif /* USE_QUADSPI */
659 #endif