Merge pull request #10558 from iNavFlight/MrD_Correct-comments-on-OSD-symbols
[inav.git] / src / main / drivers / flash_m25p16.c
blob9842a80995d11e12602cfe3dab407df97840185c
1 /*
2 * This file is part of Cleanflight.
4 * Cleanflight is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * Cleanflight is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
18 #include <stdbool.h>
19 #include <stdint.h>
21 #include "platform.h"
23 #ifdef USE_FLASH_M25P16
25 #include "flash_m25p16.h"
26 #include "drivers/io.h"
27 #include "drivers/bus.h"
28 #include "drivers/time.h"
30 #if defined(USE_QUADSPI)
31 #include "drivers/bus_quadspi.h"
32 #include "drivers/bus_quadspi_impl.h"
33 #endif
35 #define M25P16_INSTRUCTION_RDID 0x9F
36 #define M25P16_INSTRUCTION_READ_BYTES 0x03
37 #define M25P16_INSTRUCTION_QUAD_READ 0x6B
38 #define M25P16_INSTRUCTION_READ_STATUS_REG 0x05
39 #define M25P16_INSTRUCTION_WRITE_STATUS_REG 0x01
40 #define M25P16_INSTRUCTION_WRITE_ENABLE 0x06
41 #define M25P16_INSTRUCTION_WRITE_DISABLE 0x04
42 #define M25P16_INSTRUCTION_PAGE_PROGRAM 0x02
43 #define M25P16_INSTRUCTION_QPAGE_PROGRAM 0x32
44 #define M25P16_INSTRUCTION_SECTOR_ERASE 0xD8
45 #define M25P16_INSTRUCTION_BULK_ERASE 0xC7
47 #define M25P16_STATUS_FLAG_WRITE_IN_PROGRESS 0x01
48 #define M25P16_STATUS_FLAG_WRITE_ENABLED 0x02
50 #define W25Q256_INSTRUCTION_ENTER_4BYTE_ADDRESS_MODE 0xB7
52 #define M25P16_FAST_READ_DUMMY_CYCLES 8
54 struct {
55 uint32_t jedecID;
56 flashSector_t sectors;
57 uint16_t pagesPerSector;
58 } m25p16FlashConfig[] = {
59 // Macronix MX25L3206E
60 // Datasheet: https://docs.rs-online.com/5c85/0900766b814ac6f9.pdf
61 {0xC22016, 64, 256},
62 // Macronix MX25L6406E
63 // Datasheet: https://www.macronix.com/Lists/Datasheet/Attachments/7370/MX25L6406E,%203V,%2064Mb,%20v1.9.pdf
64 {0xC22017, 128, 256},
65 // Macronix MX25L25635E
66 // Datasheet: https://www.macronix.com/Lists/Datasheet/Attachments/7331/MX25L25635E,%203V,%20256Mb,%20v1.3.pdf
67 {0xC22019, 512, 256},
68 // Micron M25P16
69 // Datasheet: https://www.micron.com/-/media/client/global/documents/products/data-sheet/nor-flash/serial-nor/m25p/m25p16.pdf
70 {0x202015, 32, 256},
71 // Micron N25Q064
72 // Datasheet: https://www.micron.com/-/media/client/global/documents/products/data-sheet/nor-flash/serial-nor/n25q/n25q_64a_3v_65nm.pdf
73 {0x20BA17, 128, 256},
74 // Micron N25Q128
75 // Datasheet: https://www.micron.com/-/media/client/global/documents/products/data-sheet/nor-flash/serial-nor/n25q/n25q_128mb_1_8v_65nm.pdf
76 {0x20ba18, 256, 256},
77 // Winbond W25Q80
78 // Datasheet: https://www.winbond.com/resource-files/w25q80dv%20dl_revh_10022015.pdf
79 {0xEF4014, 16, 256},
80 // Winbond W25Q16
81 // Datasheet: https://www.winbond.com/resource-files/w25q16dv_revi_nov1714_web.pdf
82 {0xEF4015, 32, 256},
83 // Winbond W25X32
84 // Datasheet: https://www.winbond.com/resource-files/w25x32a_revb_080709.pdf
85 {0xEF3016, 64, 256},
86 // Winbond W25Q32
87 // Datasheet: https://www.winbond.com/resource-files/w25q32jv%20dtr%20revf%2002242017.pdf?__locale=zh_TW
88 {0xEF4016, 64, 256},
89 // Winbond W25Q64
90 // Datasheet: https://www.winbond.com/resource-files/w25q64jv%20spi%20%20%20revc%2006032016%20kms.pdf
91 {0xEF4017, 128, 256}, // W25Q64JV-IQ/JQ
92 {0xEF7017, 128, 256}, // W25Q64JV-IM/JM*
93 // Winbond W25Q128
94 // Datasheet: https://www.winbond.com/resource-files/w25q128fv%20rev.l%2008242015.pdf
95 {0xEF4018, 256, 256},
96 // Zbit ZB25VQ128
97 // Datasheet: http://zbitsemi.com/upload/file/20201010/20201010174048_82182.pdf
98 {0x5E4018, 256, 256},
99 // Winbond W25Q128_DTR
100 // Datasheet: https://www.winbond.com/resource-files/w25q128jv%20dtr%20revb%2011042016.pdf
101 {0xEF7018, 256, 256},
102 // Winbond W25Q256
103 // Datasheet: https://www.winbond.com/resource-files/w25q256jv%20spi%20revb%2009202016.pdf
104 {0xEF4019, 512, 256},
105 // Cypress S25FL064L
106 // Datasheet: https://www.cypress.com/file/316661/download
107 {0x016017, 128, 256},
108 // Cypress S25FL128L
109 // Datasheet: https://www.cypress.com/file/316171/download
110 {0x016018, 256, 256},
111 // BergMicro W25Q32
112 // Datasheet: https://www.winbond.com/resource-files/w25q32jv%20dtr%20revf%2002242017.pdf?__locale=zh_TW
113 {0xE04016, 1024, 16},
114 // JEDEC_ID_EON_W25Q64
115 {0x1C3017, 128, 256},
116 // JEDEC_ID_SPANSION_S25FL116
117 {0x014015, 32, 256 },
118 // End of list
119 {0x000000, 0, 0}};
121 // The timeout we expect between being able to issue page program instructions
122 #define DEFAULT_TIMEOUT_MILLIS 6
124 // These take sooooo long:
125 #define SECTOR_ERASE_TIMEOUT_MILLIS 5000
126 #define BULK_ERASE_TIMEOUT_MILLIS 21000
128 static flashGeometry_t geometry = {.pageSize = M25P16_PAGESIZE};
130 #if !defined(M25P16_QUADSPI_DEVICE)
131 static busDevice_t * busDev = NULL;
132 #else
133 static QUADSPI_TypeDef * qspi = NULL;
134 #endif /* !defined(M25P16_QUADSPI_DEVICE) */
136 static bool isLargeFlash = false;
137 static uint32_t timeoutAt = 0;
140 * Whether we've performed an action that could have made the device busy for writes.
142 * This allows us to avoid polling for writable status when it is definitely ready already.
144 static bool couldBeBusy = false;
147 * Send the given command byte to the device.
149 static void m25p16_performOneByteCommand(uint8_t command)
151 #if !defined(M25P16_QUADSPI_DEVICE)
152 busTransfer(busDev, NULL, &command, 1);
153 #else
154 quadSpiTransmit1LINE(qspi, command, 0, NULL, 0);
155 #endif /* !defined(M25P16_QUADSPI_DEVICE) */
159 * The flash requires this write enable command to be sent before commands that would cause
160 * a write like program and erase.
162 static void m25p16_writeEnable(void)
164 m25p16_performOneByteCommand(M25P16_INSTRUCTION_WRITE_ENABLE);
166 // Assume that we're about to do some writing, so the device is just about to become busy
167 couldBeBusy = true;
170 static uint8_t m25p16_readStatus(void)
172 uint8_t status;
173 #if !defined(M25P16_QUADSPI_DEVICE)
174 uint8_t command[2] = { M25P16_INSTRUCTION_READ_STATUS_REG, 0 };
175 uint8_t in[2];
177 busTransfer(busDev, in, command, sizeof(command));
178 status = in[1];
179 #else
180 quadSpiReceive1LINE(qspi, M25P16_INSTRUCTION_READ_STATUS_REG, 0, &status, 1);
181 #endif /* !defined(M25P16_QUADSPI_DEVICE) */
183 return status;
186 bool m25p16_isReady(void)
188 // If couldBeBusy is false, don't bother to poll the flash chip for its status
189 couldBeBusy = couldBeBusy && ((m25p16_readStatus() & M25P16_STATUS_FLAG_WRITE_IN_PROGRESS) != 0);
191 return !couldBeBusy;
194 static void m25p16_setTimeout(uint32_t timeoutMillis)
196 uint32_t now = millis();
197 timeoutAt = now + timeoutMillis;
200 bool m25p16_waitForReady(uint32_t timeoutMillis)
202 uint32_t timeoutAtUse;
203 if (timeoutMillis == 0) {
204 timeoutAtUse = timeoutAt;
206 else {
207 timeoutAtUse = millis() + timeoutMillis;
210 while (!m25p16_isReady()) {
211 uint32_t now = millis();
212 if (now >= timeoutAtUse) {
213 return false;
217 timeoutAt = 0;
218 return true;
222 * Read chip identification and geometry information (into global `geometry`).
224 * Returns true if we get valid ident, false if something bad happened like there is no M25P16.
226 static bool m25p16_readIdentification(void)
228 uint8_t in[4];
229 uint32_t chipID;
231 delay(50); // short delay required after initialisation of SPI device instance.
233 /* Just in case transfer fails and writes nothing, so we don't try to verify the ID against random garbage
234 * from the stack:
236 in[1] = 0;
238 #if !defined(M25P16_QUADSPI_DEVICE)
239 uint8_t out[] = { M25P16_INSTRUCTION_RDID, 0, 0, 0 };
241 busTransfer(busDev, in, out, sizeof(out));
242 #else
243 bool status = quadSpiReceive1LINE(qspi, M25P16_INSTRUCTION_RDID, 0, &in[1], sizeof(in) - 1);
244 if (!status) {
245 return false;
247 #endif /* !defined(M25P16_QUADSPI_DEVICE) */
249 // Manufacturer, memory type, and capacity
250 chipID = (in[1] << 16) | (in[2] << 8) | (in[3]);
252 geometry.sectors = 0;
253 geometry.pagesPerSector = 0;
254 geometry.sectorSize = 0;
255 geometry.totalSize = 0;
257 for(int i = 0; m25p16FlashConfig[i].jedecID != 0; ++i) {
258 if(m25p16FlashConfig[i].jedecID == chipID) {
259 geometry.sectors = m25p16FlashConfig[i].sectors;
260 geometry.pagesPerSector = m25p16FlashConfig[i].pagesPerSector;
264 if(geometry.sectors == 0) {
265 return false;
268 geometry.flashType = FLASH_TYPE_NOR;
269 geometry.pageSize = M25P16_PAGESIZE;
270 geometry.sectorSize = geometry.pagesPerSector * geometry.pageSize;
271 geometry.totalSize = geometry.sectorSize * geometry.sectors;
273 if (geometry.totalSize > 16 * 1024 * 1024) {
274 isLargeFlash = true;
275 m25p16_performOneByteCommand(W25Q256_INSTRUCTION_ENTER_4BYTE_ADDRESS_MODE);
278 couldBeBusy = true; // Just for luck we'll assume the chip could be busy even though it isn't specced to be
280 return true;
284 * Initialize the driver, must be called before any other routines.
286 * Attempts to detect a connected m25p16. If found, true is returned and device capacity can be fetched with
287 * m25p16_getGeometry().
289 bool m25p16_init(int flashNumToUse)
291 bool detected;
292 #if !defined(M25P16_QUADSPI_DEVICE)
293 // SPI Mode
294 busDev = busDeviceInit(BUSTYPE_SPI, DEVHW_M25P16, flashNumToUse, OWNER_FLASH);
295 if (busDev == NULL) {
296 return false;
299 #ifndef M25P16_SPI_SHARED
300 busSetSpeed(busDev, BUS_SPEED_FAST);
301 #endif
302 #else
303 // QUADSPI Mode
304 UNUSED(flashNumToUse);
305 quadSpiPinConfigure(M25P16_QUADSPI_DEVICE);
306 quadSpiInitDevice(M25P16_QUADSPI_DEVICE);
308 qspi = quadSpiInstanceByDevice(M25P16_QUADSPI_DEVICE);
309 quadSpiSetDivisor(qspi, QUADSPI_CLOCK_INITIALISATION);
310 #endif /* M25P16_QUADSPI_DEVICE */
312 detected = m25p16_readIdentification();
314 #if defined(M25P16_QUADSPI_DEVICE)
315 if (detected) {
316 quadSpiSetDivisor(qspi, QUADSPI_CLOCK_ULTRAFAST);
318 #endif
320 return detected;
323 void m25p16_setCommandAddress(uint8_t *buf, uint32_t address, bool useLongAddress)
325 if (useLongAddress) {
326 *buf++ = (address >> 24) & 0xff;
329 *buf++ = (address >> 16) & 0xff;
330 *buf++ = (address >> 8) & 0xff;
331 *buf = address & 0xff;
335 * Erase a sector full of bytes to all 1's at the given byte offset in the flash chip.
337 void m25p16_eraseSector(uint32_t address)
339 if (!m25p16_waitForReady(0)) {
340 return;
343 m25p16_writeEnable();
345 #if !defined(M25P16_QUADSPI_DEVICE)
346 uint8_t out[5] = { M25P16_INSTRUCTION_SECTOR_ERASE };
347 m25p16_setCommandAddress(&out[1], address, isLargeFlash);
349 busTransfer(busDev, NULL, out, isLargeFlash ? 5 : 4);
350 #else
351 quadSpiInstructionWithAddress1LINE(qspi, M25P16_INSTRUCTION_SECTOR_ERASE, 0, address, isLargeFlash ? 32 : 24);
352 #endif /* !defined(M25P16_QUADSPI_DEVICE) */
354 m25p16_setTimeout(SECTOR_ERASE_TIMEOUT_MILLIS);
357 void m25p16_eraseCompletely(void)
359 if (!m25p16_waitForReady(0)) {
360 return;
363 m25p16_writeEnable();
365 m25p16_performOneByteCommand(M25P16_INSTRUCTION_BULK_ERASE);
367 m25p16_setTimeout(BULK_ERASE_TIMEOUT_MILLIS);
371 * Write bytes to a flash page. Address must not cross a page boundary.
373 * 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.
375 * Length must be smaller than the page size.
377 * This will wait for the flash to become ready before writing begins.
379 * Datasheet indicates typical programming time is 0.8ms for 256 bytes, 0.2ms for 64 bytes, 0.05ms for 16 bytes.
380 * (Although the maximum possible write time is noted as 5ms).
382 * If you want to write multiple buffers (whose sum of sizes is still not more than the page size) then you can
383 * break this operation up into one beginProgram call, one or more continueProgram calls, and one finishProgram call.
385 uint32_t m25p16_pageProgram(uint32_t address, const uint8_t *data, int length)
387 if (!m25p16_waitForReady(0)) {
388 // return same address to indicate timeout
389 return address;
392 m25p16_writeEnable();
394 #if !defined(M25P16_QUADSPI_DEVICE)
395 uint8_t command[5] = { M25P16_INSTRUCTION_PAGE_PROGRAM };
397 busTransferDescriptor_t txn[2] = {
398 { NULL, command, isLargeFlash ? 5 : 4 },
399 { NULL, data, length }
402 m25p16_setCommandAddress(&command[1], address, isLargeFlash);
404 busTransferMultiple(busDev, txn, 2);
405 #else
406 quadSpiTransmitWithAddress4LINES(qspi, M25P16_INSTRUCTION_QPAGE_PROGRAM, 0, address, isLargeFlash ? 32 : 24, data, length);
407 #endif /* !defined(M25P16_QUADSPI_DEVICE) */
409 m25p16_setTimeout(DEFAULT_TIMEOUT_MILLIS);
411 return address + length;
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 * Waits up to DEFAULT_TIMEOUT_MILLIS milliseconds for the flash to become ready before reading.
420 * The number of bytes actually read is returned, which can be zero if an error or timeout occurred.
422 int m25p16_readBytes(uint32_t address, uint8_t *buffer, int length)
424 if (!m25p16_waitForReady(0)) {
425 return 0;
428 #if !defined(M25P16_QUADSPI_DEVICE)
429 uint8_t command[5] = { M25P16_INSTRUCTION_READ_BYTES };
431 busTransferDescriptor_t txn[2] = {
432 { NULL, command, isLargeFlash ? 5 : 4 },
433 { buffer, NULL, length }
436 m25p16_setCommandAddress(&command[1], address, isLargeFlash);
438 busTransferMultiple(busDev, txn, 2);
439 #else
440 quadSpiReceiveWithAddress4LINES(qspi, M25P16_INSTRUCTION_QUAD_READ, M25P16_FAST_READ_DUMMY_CYCLES,
441 address, isLargeFlash ? 32 : 24, buffer, length);
442 #endif /* !defined(M25P16_QUADSPI_DEVICE) */
444 return length;
448 * Fetch information about the detected flash chip layout.
450 * Can be called before calling m25p16_init() (the result would have totalSize = 0).
452 const flashGeometry_t* m25p16_getGeometry(void)
454 return &geometry;
457 #endif