Merge pull request #11494 from haslinghuis/dshot_gpio
[betaflight.git] / src / main / drivers / flash_m25p16.c
blob7300a6aa393a41476cdd5933ce0abad11dc36381
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>
24 #include "platform.h"
26 #include "build/debug.h"
28 #ifdef USE_FLASH_M25P16
30 #include "drivers/bus_spi.h"
31 #include "drivers/flash.h"
32 #include "drivers/flash_impl.h"
33 #include "drivers/io.h"
34 #include "drivers/time.h"
36 #include "pg/flash.h"
38 #include "flash_m25p16.h"
40 #define M25P16_INSTRUCTION_RDID SPIFLASH_INSTRUCTION_RDID
41 #define M25P16_INSTRUCTION_READ_BYTES 0x03
42 #define M25P16_INSTRUCTION_READ_STATUS_REG 0x05
43 #define M25P16_INSTRUCTION_WRITE_STATUS_REG 0x01
44 #define M25P16_INSTRUCTION_WRITE_ENABLE 0x06
45 #define M25P16_INSTRUCTION_WRITE_DISABLE 0x04
46 #define M25P16_INSTRUCTION_PAGE_PROGRAM 0x02
47 #define M25P16_INSTRUCTION_SECTOR_ERASE 0xD8
48 #define M25P16_INSTRUCTION_BULK_ERASE 0xC7
50 #define M25P16_STATUS_FLAG_WRITE_IN_PROGRESS 0x01
51 #define M25P16_STATUS_FLAG_WRITE_ENABLED 0x02
53 #define W25Q256_INSTRUCTION_ENTER_4BYTE_ADDRESS_MODE 0xB7
55 // SPI transaction segment indicies for m25p16_pageProgramContinue()
56 enum {READ_STATUS, WRITE_ENABLE, PAGE_PROGRAM, DATA1, DATA2};
58 static uint32_t maxClkSPIHz;
59 static uint32_t maxReadClkSPIHz;
61 // Table of recognised FLASH devices
62 struct {
63 uint32_t jedecID;
64 uint16_t maxClkSPIMHz;
65 uint16_t maxReadClkSPIMHz;
66 flashSector_t sectors;
67 uint16_t pagesPerSector;
68 } m25p16FlashConfig[] = {
69 // Macronix MX25L3206E
70 // Datasheet: https://docs.rs-online.com/5c85/0900766b814ac6f9.pdf
71 { 0xC22016, 86, 33, 64, 256 },
72 // Macronix MX25L6406E
73 // Datasheet: https://www.macronix.com/Lists/Datasheet/Attachments/7370/MX25L6406E,%203V,%2064Mb,%20v1.9.pdf
74 { 0xC22017, 86, 33, 128, 256 },
75 // Macronix MX25L25635E
76 // Datasheet: https://www.macronix.com/Lists/Datasheet/Attachments/7331/MX25L25635E,%203V,%20256Mb,%20v1.3.pdf
77 { 0xC22019, 80, 50, 512, 256 },
78 // Micron M25P16
79 // Datasheet: https://www.micron.com/-/media/client/global/documents/products/data-sheet/nor-flash/serial-nor/m25p/m25p16.pdf
80 { 0x202015, 25, 20, 32, 256 },
81 // Micron N25Q064
82 // Datasheet: https://www.micron.com/-/media/client/global/documents/products/data-sheet/nor-flash/serial-nor/n25q/n25q_64a_3v_65nm.pdf
83 { 0x20BA17, 108, 54, 128, 256 },
84 // Micron N25Q128
85 // Datasheet: https://www.micron.com/-/media/client/global/documents/products/data-sheet/nor-flash/serial-nor/n25q/n25q_128mb_1_8v_65nm.pdf
86 { 0x20ba18, 108, 54, 256, 256 },
87 // Winbond W25Q16
88 // Datasheet: https://www.winbond.com/resource-files/w25q16dv_revi_nov1714_web.pdf
89 { 0xEF4015, 104, 50, 32, 256 },
90 // Winbond W25Q32
91 // Datasheet: https://www.winbond.com/resource-files/w25q32jv%20dtr%20revf%2002242017.pdf?__locale=zh_TW
92 { 0xEF4016, 133, 50, 64, 256 },
93 // Winbond W25Q64
94 // Datasheet: https://www.winbond.com/resource-files/w25q64jv%20spi%20%20%20revc%2006032016%20kms.pdf
95 { 0xEF4017, 133, 50, 128, 256 }, // W25Q64JV-IQ/JQ
96 { 0xEF7017, 133, 50, 128, 256 }, // W25Q64JV-IM/JM*
97 // Winbond W25Q128
98 // Datasheet: https://www.winbond.com/resource-files/w25q128fv%20rev.l%2008242015.pdf
99 { 0xEF4018, 104, 50, 256, 256 },
100 // Zbit ZB25VQ128
101 // Datasheet: http://zbitsemi.com/upload/file/20201010/20201010174048_82182.pdf
102 { 0x5E4018, 104, 50, 256, 256 },
103 // Winbond W25Q128_DTR
104 // Datasheet: https://www.winbond.com/resource-files/w25q128jv%20dtr%20revb%2011042016.pdf
105 { 0xEF7018, 66, 50, 256, 256 },
106 // Winbond W25Q256
107 // Datasheet: https://www.winbond.com/resource-files/w25q256jv%20spi%20revb%2009202016.pdf
108 { 0xEF4019, 133, 50, 512, 256 },
109 // Cypress S25FL064L
110 // Datasheet: https://www.cypress.com/file/316661/download
111 { 0x016017, 133, 50, 128, 256 },
112 // Cypress S25FL128L
113 // Datasheet: https://www.cypress.com/file/316171/download
114 { 0x016018, 133, 50, 256, 256 },
115 // BergMicro W25Q32
116 // Datasheet: https://www.winbond.com/resource-files/w25q32jv%20dtr%20revf%2002242017.pdf?__locale=zh_TW
117 { 0xE04016, 133, 50, 1024, 16 },
118 // End of list
119 { 0x000000, 0, 0, 0, 0 }
122 #define M25P16_PAGESIZE 256
124 STATIC_ASSERT(M25P16_PAGESIZE < FLASH_MAX_PAGE_SIZE, M25P16_PAGESIZE_too_small);
126 const flashVTable_t m25p16_vTable;
128 static uint8_t m25p16_readStatus(flashDevice_t *fdevice)
130 STATIC_DMA_DATA_AUTO uint8_t readStatus[2] = { M25P16_INSTRUCTION_READ_STATUS_REG, 0 };
131 STATIC_DMA_DATA_AUTO uint8_t readyStatus[2];
133 spiReadWriteBuf(fdevice->io.handle.dev, readStatus, readyStatus, sizeof(readStatus));
135 return readyStatus[1];
138 static bool m25p16_isReady(flashDevice_t *fdevice)
140 // If we're waiting on DMA completion, then SPI is busy
141 if (fdevice->io.handle.dev->bus->useDMA && spiIsBusy(fdevice->io.handle.dev)) {
142 return false;
145 // If couldBeBusy is false, don't bother to poll the flash chip for its status
146 if (!fdevice->couldBeBusy) {
147 return true;
150 // Poll the FLASH device to see if it's busy
151 fdevice->couldBeBusy = ((m25p16_readStatus(fdevice) & M25P16_STATUS_FLAG_WRITE_IN_PROGRESS) != 0);
153 return !fdevice->couldBeBusy;
156 static bool m25p16_waitForReady(flashDevice_t *fdevice)
158 while (!m25p16_isReady(fdevice));
160 return true;
164 * Read chip identification and geometry information (into global `geometry`).
166 * Returns true if we get valid ident, false if something bad happened like there is no M25P16.
169 bool m25p16_detect(flashDevice_t *fdevice, uint32_t chipID)
171 flashGeometry_t *geometry = &fdevice->geometry;
172 uint8_t index;
174 for (index = 0; m25p16FlashConfig[index].jedecID; index++) {
175 if (m25p16FlashConfig[index].jedecID == chipID) {
176 maxClkSPIHz = m25p16FlashConfig[index].maxClkSPIMHz * 1000000;
177 maxReadClkSPIHz = m25p16FlashConfig[index].maxReadClkSPIMHz * 1000000;
178 geometry->sectors = m25p16FlashConfig[index].sectors;
179 geometry->pagesPerSector = m25p16FlashConfig[index].pagesPerSector;
180 break;
184 if (m25p16FlashConfig[index].jedecID == 0) {
185 // Unsupported chip or not an SPI NOR flash
186 geometry->sectors = 0;
187 geometry->pagesPerSector = 0;
188 geometry->sectorSize = 0;
189 geometry->totalSize = 0;
190 return false;
193 geometry->flashType = FLASH_TYPE_NOR;
194 geometry->pageSize = M25P16_PAGESIZE;
195 geometry->sectorSize = geometry->pagesPerSector * geometry->pageSize;
196 geometry->totalSize = geometry->sectorSize * geometry->sectors;
198 // Adjust the SPI bus clock frequency
199 spiSetClkDivisor(fdevice->io.handle.dev, spiCalculateDivider(maxReadClkSPIHz));
201 if (geometry->totalSize > 16 * 1024 * 1024) {
202 fdevice->isLargeFlash = true;
204 // This routine blocks so no need to use static data
205 uint8_t modeSet[] = { W25Q256_INSTRUCTION_ENTER_4BYTE_ADDRESS_MODE };
207 spiReadWriteBuf(fdevice->io.handle.dev, modeSet, NULL, sizeof(modeSet));
210 fdevice->couldBeBusy = true; // Just for luck we'll assume the chip could be busy even though it isn't specced to be
211 fdevice->vTable = &m25p16_vTable;
212 return true;
215 static void m25p16_setCommandAddress(uint8_t *buf, uint32_t address, bool useLongAddress)
217 if (useLongAddress) {
218 *buf++ = (address >> 24) & 0xff;
220 *buf++ = (address >> 16) & 0xff;
221 *buf++ = (address >> 8) & 0xff;
222 *buf = address & 0xff;
225 // Called in ISR context
226 // A write enable has just been issued
227 busStatus_e m25p16_callbackWriteEnable(uint32_t arg)
229 flashDevice_t *fdevice = (flashDevice_t *)arg;
231 // As a write has just occurred, the device could be busy
232 fdevice->couldBeBusy = true;
234 return BUS_READY;
237 // Called in ISR context
238 // Write operation has just completed
239 busStatus_e m25p16_callbackWriteComplete(uint32_t arg)
241 flashDevice_t *fdevice = (flashDevice_t *)arg;
243 fdevice->currentWriteAddress += fdevice->callbackArg;
245 // Call transfer completion callback
246 if (fdevice->callback) {
247 fdevice->callback(fdevice->callbackArg);
250 return BUS_READY;
253 // Called in ISR context
254 // Check if the status was busy and if so repeat the poll
255 busStatus_e m25p16_callbackReady(uint32_t arg)
257 flashDevice_t *fdevice = (flashDevice_t *)arg;
258 extDevice_t *dev = fdevice->io.handle.dev;
260 uint8_t readyPoll = dev->bus->curSegment->u.buffers.rxData[1];
262 if (readyPoll & M25P16_STATUS_FLAG_WRITE_IN_PROGRESS) {
263 return BUS_BUSY;
266 // Bus is now known not to be busy
267 fdevice->couldBeBusy = false;
269 return BUS_READY;
274 * Erase a sector full of bytes to all 1's at the given byte offset in the flash chip.
276 static void m25p16_eraseSector(flashDevice_t *fdevice, uint32_t address)
278 STATIC_DMA_DATA_AUTO uint8_t sectorErase[5] = { M25P16_INSTRUCTION_SECTOR_ERASE };
279 STATIC_DMA_DATA_AUTO uint8_t readStatus[2] = { M25P16_INSTRUCTION_READ_STATUS_REG, 0 };
280 STATIC_DMA_DATA_AUTO uint8_t readyStatus[2];
281 STATIC_DMA_DATA_AUTO uint8_t writeEnable[] = { M25P16_INSTRUCTION_WRITE_ENABLE };
283 busSegment_t segments[] = {
284 {.u.buffers = {readStatus, readyStatus}, sizeof(readStatus), true, m25p16_callbackReady},
285 {.u.buffers = {writeEnable, NULL}, sizeof(writeEnable), true, m25p16_callbackWriteEnable},
286 {.u.buffers = {sectorErase, NULL}, fdevice->isLargeFlash ? 5 : 4, true, NULL},
287 {.u.link = {NULL, NULL}, 0, true, NULL},
290 // Ensure any prior DMA has completed before continuing
291 spiWait(fdevice->io.handle.dev);
293 m25p16_setCommandAddress(&sectorErase[1], address, fdevice->isLargeFlash);
295 spiSequence(fdevice->io.handle.dev, segments);
297 // Block pending completion of SPI access, but the erase will be ongoing
298 spiWait(fdevice->io.handle.dev);
301 static void m25p16_eraseCompletely(flashDevice_t *fdevice)
303 STATIC_DMA_DATA_AUTO uint8_t readStatus[2] = { M25P16_INSTRUCTION_READ_STATUS_REG, 0 };
304 STATIC_DMA_DATA_AUTO uint8_t readyStatus[2];
305 STATIC_DMA_DATA_AUTO uint8_t writeEnable[] = { M25P16_INSTRUCTION_WRITE_ENABLE };
306 STATIC_DMA_DATA_AUTO uint8_t bulkErase[] = { M25P16_INSTRUCTION_BULK_ERASE };
308 busSegment_t segments[] = {
309 {.u.buffers = {readStatus, readyStatus}, sizeof(readStatus), true, m25p16_callbackReady},
310 {.u.buffers = {writeEnable, NULL}, sizeof(writeEnable), true, m25p16_callbackWriteEnable},
311 {.u.buffers = {bulkErase, NULL}, sizeof(bulkErase), true, NULL},
312 {.u.link = {NULL, NULL}, 0, true, NULL},
315 spiSequence(fdevice->io.handle.dev, segments);
317 // Block pending completion of SPI access, but the erase will be ongoing
318 spiWait(fdevice->io.handle.dev);
321 static void m25p16_pageProgramBegin(flashDevice_t *fdevice, uint32_t address, void (*callback)(uint32_t length))
323 fdevice->callback = callback;
324 fdevice->currentWriteAddress = address;
328 static uint32_t m25p16_pageProgramContinue(flashDevice_t *fdevice, uint8_t const **buffers, uint32_t *bufferSizes, uint32_t bufferCount)
330 // The segment list cannot be in automatic storage as this routine is non-blocking
331 STATIC_DMA_DATA_AUTO uint8_t readStatus[2] = { M25P16_INSTRUCTION_READ_STATUS_REG, 0 };
332 STATIC_DMA_DATA_AUTO uint8_t readyStatus[2];
333 STATIC_DMA_DATA_AUTO uint8_t writeEnable[] = { M25P16_INSTRUCTION_WRITE_ENABLE };
334 STATIC_DMA_DATA_AUTO uint8_t pageProgram[5] = { M25P16_INSTRUCTION_PAGE_PROGRAM };
336 static busSegment_t segments[] = {
337 {.u.buffers = {readStatus, readyStatus}, sizeof(readStatus), true, m25p16_callbackReady},
338 {.u.buffers = {writeEnable, NULL}, sizeof(writeEnable), true, m25p16_callbackWriteEnable},
339 {.u.buffers = {pageProgram, NULL}, 0, false, NULL},
340 {.u.link = {NULL, NULL}, 0, true, NULL},
341 {.u.link = {NULL, NULL}, 0, true, NULL},
342 {.u.link = {NULL, NULL}, 0, true, NULL},
345 // Ensure any prior DMA has completed before continuing
346 spiWait(fdevice->io.handle.dev);
348 // Patch the pageProgram segment
349 segments[PAGE_PROGRAM].len = fdevice->isLargeFlash ? 5 : 4;
350 m25p16_setCommandAddress(&pageProgram[1], fdevice->currentWriteAddress, fdevice->isLargeFlash);
352 // Patch the data segments
353 segments[DATA1].u.buffers.txData = (uint8_t *)buffers[0];
354 segments[DATA1].len = bufferSizes[0];
355 fdevice->callbackArg = bufferSizes[0];
357 if (bufferCount == 1) {
358 segments[DATA1].negateCS = true;
359 segments[DATA1].callback = m25p16_callbackWriteComplete;
360 // Mark segment following data as being of zero length
361 segments[DATA2].u.buffers.txData = (uint8_t *)NULL;
362 segments[DATA2].len = 0;
363 } else if (bufferCount == 2) {
364 segments[DATA1].negateCS = false;
365 segments[DATA1].callback = NULL;
366 segments[DATA2].u.buffers.txData = (uint8_t *)buffers[1];
367 segments[DATA2].len = bufferSizes[1];
368 fdevice->callbackArg += bufferSizes[1];
369 segments[DATA2].negateCS = true;
370 segments[DATA2].callback = m25p16_callbackWriteComplete;
371 } else {
372 return 0;
375 spiSequence(fdevice->io.handle.dev, fdevice->couldBeBusy ? &segments[READ_STATUS] : &segments[WRITE_ENABLE]);
377 if (fdevice->callback == NULL) {
378 // No callback was provided so block
379 spiWait(fdevice->io.handle.dev);
382 return fdevice->callbackArg;
385 static void m25p16_pageProgramFinish(flashDevice_t *fdevice)
387 UNUSED(fdevice);
391 * Write bytes to a flash page. Address must not cross a page boundary.
393 * 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.
395 * Length must be smaller than the page size.
397 * This will wait for the flash to become ready before writing begins.
399 * Datasheet indicates typical programming time is 0.8ms for 256 bytes, 0.2ms for 64 bytes, 0.05ms for 16 bytes.
400 * (Although the maximum possible write time is noted as 5ms).
402 * If you want to write multiple buffers (whose sum of sizes is still not more than the page size) then you can
403 * break this operation up into one beginProgram call, one or more continueProgram calls, and one finishProgram call.
405 static void m25p16_pageProgram(flashDevice_t *fdevice, uint32_t address, const uint8_t *data, uint32_t length, void (*callback)(uint32_t length))
407 m25p16_pageProgramBegin(fdevice, address, callback);
409 m25p16_pageProgramContinue(fdevice, &data, &length, 1);
411 m25p16_pageProgramFinish(fdevice);
415 * Read `length` bytes into the provided `buffer` from the flash starting from the given `address` (which need not lie
416 * on a page boundary).
418 * The number of bytes actually read is returned, which can be zero if an error or timeout occurred.
420 static int m25p16_readBytes(flashDevice_t *fdevice, uint32_t address, uint8_t *buffer, uint32_t length)
422 STATIC_DMA_DATA_AUTO uint8_t readStatus[2] = { M25P16_INSTRUCTION_READ_STATUS_REG, 0 };
423 STATIC_DMA_DATA_AUTO uint8_t readyStatus[2];
424 STATIC_DMA_DATA_AUTO uint8_t readBytes[5] = { M25P16_INSTRUCTION_READ_BYTES };
426 // Ensure any prior DMA has completed before continuing
427 spiWait(fdevice->io.handle.dev);
429 busSegment_t segments[] = {
430 {.u.buffers = {readStatus, readyStatus}, sizeof(readStatus), true, m25p16_callbackReady},
431 {.u.buffers = {readBytes, NULL}, fdevice->isLargeFlash ? 5 : 4, false, NULL},
432 {.u.buffers = {NULL, buffer}, length, true, NULL},
433 {.u.link = {NULL, NULL}, 0, true, NULL},
436 // Patch the readBytes command
437 m25p16_setCommandAddress(&readBytes[1], address, fdevice->isLargeFlash);
439 spiSetClkDivisor(fdevice->io.handle.dev, spiCalculateDivider(maxReadClkSPIHz));
441 spiSequence(fdevice->io.handle.dev, fdevice->couldBeBusy ? &segments[0] : &segments[1]);
443 // Block until code is re-factored to exploit non-blocking
444 spiWait(fdevice->io.handle.dev);
446 spiSetClkDivisor(fdevice->io.handle.dev, spiCalculateDivider(maxClkSPIHz));
448 return length;
452 * Fetch information about the detected flash chip layout.
454 * Can be called before calling m25p16_init() (the result would have totalSize = 0).
456 static const flashGeometry_t* m25p16_getGeometry(flashDevice_t *fdevice)
458 return &fdevice->geometry;
461 const flashVTable_t m25p16_vTable = {
462 .isReady = m25p16_isReady,
463 .waitForReady = m25p16_waitForReady,
464 .eraseSector = m25p16_eraseSector,
465 .eraseCompletely = m25p16_eraseCompletely,
466 .pageProgramBegin = m25p16_pageProgramBegin,
467 .pageProgramContinue = m25p16_pageProgramContinue,
468 .pageProgramFinish = m25p16_pageProgramFinish,
469 .pageProgram = m25p16_pageProgram,
470 .readBytes = m25p16_readBytes,
471 .getGeometry = m25p16_getGeometry,
473 #endif