Blackbox device type 'file' (SITL) considered working when file handler is available
[inav.git] / src / main / drivers / flash.c
blob145aeafd56b3f07cde9971195e0ae24b4f4df582
1 /*
2 * This file is part of INAV.
4 * INAV is 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 * INAV is distributed in the hope that it
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_FLASHFS
31 #include "flash.h"
32 #include "flash_m25p16.h"
33 #include "flash_w25n01g.h"
35 #include "common/time.h"
37 #include "drivers/bus_spi.h"
38 #include "drivers/io.h"
39 #include "drivers/time.h"
41 static flashDriver_t flashDrivers[] = {
43 #ifdef USE_SPI
45 #ifdef USE_FLASH_M25P16
47 .init = m25p16_init,
48 .isReady = m25p16_isReady,
49 .waitForReady = m25p16_waitForReady,
50 .eraseSector = m25p16_eraseSector,
51 .eraseCompletely = m25p16_eraseCompletely,
52 .pageProgram = m25p16_pageProgram,
53 .readBytes = m25p16_readBytes,
54 .getGeometry = m25p16_getGeometry,
55 .flush = NULL
57 #endif
59 #ifdef USE_FLASH_W25N01G
61 .init = w25n01g_init,
62 .isReady = w25n01g_isReady,
63 .waitForReady = w25n01g_waitForReady,
64 .eraseSector = w25n01g_eraseSector,
65 .eraseCompletely = w25n01g_eraseCompletely,
66 .pageProgram = w25n01g_pageProgram,
67 .readBytes = w25n01g_readBytes,
68 .getGeometry = w25n01g_getGeometry,
69 .flush = w25n01g_flush
71 #endif
73 #endif
77 static flashDriver_t *flash;
79 static bool flashDeviceInit(void)
81 bool detected = false;
83 for (uint32_t idx = 0; idx < ARRAYLEN(flashDrivers); idx++) //idx = ARRAYLEN may cause overflow
85 detected = flashDrivers[idx].init(0);
86 if (detected)
88 flash = &flashDrivers[idx];
89 break;
92 return detected;
95 bool flashIsReady(void)
97 // prevent the machine cycle from crashing if there is no external flash memory
98 if (flash == NULL) {
99 return false;
102 return flash->isReady();
105 bool flashWaitForReady(timeMs_t timeoutMillis)
107 return flash->waitForReady(timeoutMillis);
110 void flashEraseSector(uint32_t address)
112 flash->eraseSector(address);
115 void flashEraseCompletely(void)
117 flash->eraseCompletely();
120 uint32_t flashPageProgram(uint32_t address, const uint8_t *data, int length)
122 return flash->pageProgram(address, data, length);
125 int flashReadBytes(uint32_t address, uint8_t *buffer, int length)
127 return flash->readBytes(address, buffer, length);
130 void flashFlush(void)
132 flash->flush();
135 const flashGeometry_t *flashGetGeometry(void)
137 static flashGeometry_t fgNone = {0};
139 // prevent the machine cycle from crashing if there is no external flash memory
140 if (flash == NULL) {
141 return &fgNone;
144 return flash->getGeometry();
148 * Flash partitioning
150 * Partition table is not currently stored on the flash, in-memory only.
152 * Partitions are required so that Badblock management (inc spare blocks), FlashFS (Blackbox Logging), Configuration and Firmware can be kept separate and tracked.
154 * XXX FIXME
155 * XXX Note that Flash FS must start at sector 0.
156 * XXX There is existing blackbox/flash FS code the relies on this!!!
157 * XXX This restriction can and will be fixed by creating a set of flash operation functions that take partition as an additional parameter.
160 static flashPartitionTable_t flashPartitionTable;
161 static int flashPartitions = 0;
163 static __attribute__((unused)) void createPartition(flashPartitionType_e type, uint32_t size, flashSector_t *endSector)
165 const flashGeometry_t *flashGeometry = flashGetGeometry();
166 flashSector_t partitionSectors = (size / flashGeometry->sectorSize);
168 if (size % flashGeometry->sectorSize > 0) {
169 partitionSectors++; // needs a portion of a sector.
172 flashSector_t startSector = (*endSector + 1) - partitionSectors; // + 1 for inclusive
174 flashPartitionSet(type, startSector, *endSector);
176 *endSector = startSector - 1;
179 static void flashConfigurePartitions(void)
181 const flashGeometry_t *flashGeometry = flashGetGeometry();
182 if (flashGeometry->totalSize == 0) {
183 return;
186 flashSector_t startSector = 0;
187 flashSector_t endSector = flashGeometry->sectors - 1; // 0 based index
189 const flashPartition_t *badBlockPartition = flashPartitionFindByType(FLASH_PARTITION_TYPE_BADBLOCK_MANAGEMENT);
190 if (badBlockPartition) {
191 endSector = badBlockPartition->startSector - 1;
194 #if defined(FIRMWARE_SIZE)
195 createPartition(FLASH_PARTITION_TYPE_FIRMWARE, FIRMWARE_SIZE * 1024, &endSector);
196 #endif
198 #if defined(MSP_FIRMWARE_UPDATE)
199 createPartition(FLASH_PARTITION_TYPE_FIRMWARE_UPDATE_META, flashGeometry->sectorSize, &endSector);
200 createPartition(FLASH_PARTITION_TYPE_UPDATE_FIRMWARE, MCU_FLASH_SIZE * 1024, &endSector);
201 createPartition(FLASH_PARTITION_TYPE_FULL_BACKUP, MCU_FLASH_SIZE * 1024, &endSector);
202 #endif
204 #if defined(CONFIG_IN_EXTERNAL_FLASH)
205 uint32_t configSize = EEPROM_SIZE;
206 flashSector_t configSectors = (configSize / flashGeometry->sectorSize);
208 if (configSize % flashGeometry->sectorSize > 0) {
209 configSectors++; // needs a portion of a sector.
211 configSize = configSectors * flashGeometry->sectorSize;
213 createPartition(FLASH_PARTITION_TYPE_CONFIG, configSize, &endSector);
214 #endif
216 #ifdef USE_FLASHFS
217 flashPartitionSet(FLASH_PARTITION_TYPE_FLASHFS, startSector, endSector);
218 #endif
221 flashPartition_t *flashPartitionFindByType(flashPartitionType_e type)
223 for (int index = 0; index < FLASH_MAX_PARTITIONS; index++) {
224 flashPartition_t *candidate = &flashPartitionTable.partitions[index];
225 if (candidate->type == type) {
226 return candidate;
230 return NULL;
233 const flashPartition_t *flashPartitionFindByIndex(uint8_t index)
235 if (index >= flashPartitions) {
236 return NULL;
239 return &flashPartitionTable.partitions[index];
242 void flashPartitionSet(uint8_t type, uint32_t startSector, uint32_t endSector)
244 flashPartition_t *entry = flashPartitionFindByType(type);
246 if (!entry) {
247 if (flashPartitions == FLASH_MAX_PARTITIONS - 1) {
248 return;
250 entry = &flashPartitionTable.partitions[flashPartitions++];
253 entry->type = type;
254 entry->startSector = startSector;
255 entry->endSector = endSector;
258 // Must be in sync with FLASH_PARTITION_TYPE
259 static const char *flashPartitionNames[] = {
260 "UNKNOWN ",
261 "PARTITION",
262 "FLASHFS ",
263 "BBMGMT ",
264 "FIRMWARE ",
265 "CONFIG ",
266 "FW UPDT ",
269 const char *flashPartitionGetTypeName(flashPartitionType_e type)
271 if (type < ARRAYLEN(flashPartitionNames)) {
272 return flashPartitionNames[type];
275 return NULL;
278 bool flashInit(void)
280 memset(&flashPartitionTable, 0, sizeof(flashPartitionTable));
282 bool haveFlash = flashDeviceInit();
284 if (haveFlash) {
285 flashConfigurePartitions();
288 return haveFlash;
291 int flashPartitionCount(void)
293 return flashPartitions;
296 uint32_t flashPartitionSize(flashPartition_t *partition)
298 const flashGeometry_t * const geometry = flashGetGeometry();
299 return (partition->endSector - partition->startSector + 1) * geometry->sectorSize;
302 void flashPartitionErase(flashPartition_t *partition)
304 const flashGeometry_t * const geometry = flashGetGeometry();
306 // if there's a single FLASHFS partition and it uses the entire flash then do a full erase
307 const bool doFullErase = (flashPartitionCount() == 1) && (FLASH_PARTITION_SECTOR_COUNT(partition) == geometry->sectors);
308 if (doFullErase) {
309 flashEraseCompletely();
310 return;
313 for (unsigned i = partition->startSector; i <= partition->endSector; i++) {
314 uint32_t flashAddress = geometry->sectorSize * i;
315 flashEraseSector(flashAddress);
316 flashWaitForReady(0);
319 #endif // USE_FLASH_CHIP