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/>.
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"
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
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 },
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 },
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 },
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 },
88 // Datasheet: https://www.winbond.com/resource-files/w25q16dv_revi_nov1714_web.pdf
89 { 0xEF4015, 104, 50, 32, 256 },
91 // Datasheet: https://www.winbond.com/resource-files/w25q32jv%20dtr%20revf%2002242017.pdf?__locale=zh_TW
92 { 0xEF4016, 133, 50, 64, 256 },
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*
98 // Datasheet: https://www.winbond.com/resource-files/w25q128fv%20rev.l%2008242015.pdf
99 { 0xEF4018, 104, 50, 256, 256 },
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 },
107 // Datasheet: https://www.winbond.com/resource-files/w25q256jv%20spi%20revb%2009202016.pdf
108 { 0xEF4019, 133, 50, 512, 256 },
110 // Datasheet: https://www.cypress.com/file/316661/download
111 { 0x016017, 133, 50, 128, 256 },
113 // Datasheet: https://www.cypress.com/file/316171/download
114 { 0x016018, 133, 50, 256, 256 },
116 // Datasheet: https://www.winbond.com/resource-files/w25q32jv%20dtr%20revf%2002242017.pdf?__locale=zh_TW
117 { 0xE04016, 133, 50, 1024, 16 },
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
)) {
145 // If couldBeBusy is false, don't bother to poll the flash chip for its status
146 if (!fdevice
->couldBeBusy
) {
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
));
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
;
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
;
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;
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
;
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;
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
);
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
) {
266 // Bus is now known not to be busy
267 fdevice
->couldBeBusy
= false;
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(§orErase
[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
;
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
)
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
));
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
,