Merge remote-tracking branch 'upstream/master' into abo_RTH_sanity_fix
[inav.git] / src / main / drivers / flash.c
blob647ac675a0744c6b75e55d96bb545b482cbd772e
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"
34 #include "common/time.h"
36 #include "drivers/bus_spi.h"
37 #include "drivers/io.h"
38 #include "drivers/time.h"
40 static flashPartitionTable_t flashPartitionTable;
41 static int flashPartitions = 0;
44 #ifdef USE_SPI
45 static bool flashSpiInit(void)
47 #ifdef USE_FLASH_M25P16
48 return m25p16_init(0);
49 #endif
50 return false;
52 #endif // USE_SPI
54 bool flashDeviceInit(void)
56 #ifdef USE_SPI
57 return flashSpiInit();
58 #endif
60 return false;
63 bool flashIsReady(void)
65 #ifdef USE_FLASH_M25P16
66 return m25p16_isReady();
67 #endif
68 return false;
71 bool flashWaitForReady(timeMs_t timeoutMillis)
73 #ifdef USE_FLASH_M25P16
74 return m25p16_waitForReady(timeoutMillis);
75 #endif
76 return false;
79 void flashEraseSector(uint32_t address)
81 #ifdef USE_FLASH_M25P16
82 return m25p16_eraseSector(address);
83 #endif
86 void flashEraseCompletely(void)
88 #ifdef USE_FLASH_M25P16
89 return m25p16_eraseCompletely();
90 #endif
93 #if 0
94 void flashPageProgramBegin(uint32_t address)
96 #ifdef USE_FLASH_M25P16
97 return m25p16_pageProgramBegin(address);
98 #endif
101 void flashPageProgramContinue(const uint8_t *data, int length)
103 #ifdef USE_FLASH_M25P16
104 return m25p16_pageProgramContinue(data, length);
105 #endif
108 void flashPageProgramFinish(void)
110 #ifdef USE_FLASH_M25P16
111 return m25p16_pageProgramFinish();
112 #endif
114 #endif
116 uint32_t flashPageProgram(uint32_t address, const uint8_t *data, int length)
118 #ifdef USE_FLASH_M25P16
119 return m25p16_pageProgram(address, data, length);
120 #endif
123 int flashReadBytes(uint32_t address, uint8_t *buffer, int length)
125 #ifdef USE_FLASH_M25P16
126 return m25p16_readBytes(address, buffer, length);
127 #endif
128 return 0;
131 void flashFlush(void)
135 const flashGeometry_t *flashGetGeometry(void)
137 #ifdef USE_FLASH_M25P16
138 return m25p16_getGeometry();
139 #endif
141 return NULL;
145 * Flash partitioning
147 * Partition table is not currently stored on the flash, in-memory only.
149 * Partitions are required so that Badblock management (inc spare blocks), FlashFS (Blackbox Logging), Configuration and Firmware can be kept separate and tracked.
151 * XXX FIXME
152 * XXX Note that Flash FS must start at sector 0.
153 * XXX There is existing blackbox/flash FS code the relies on this!!!
154 * XXX This restriction can and will be fixed by creating a set of flash operation functions that take partition as an additional parameter.
157 static __attribute__((unused)) void createPartition(flashPartitionType_e type, uint32_t size, flashSector_t *endSector)
159 const flashGeometry_t *flashGeometry = flashGetGeometry();
160 flashSector_t partitionSectors = (size / flashGeometry->sectorSize);
162 if (size % flashGeometry->sectorSize > 0) {
163 partitionSectors++; // needs a portion of a sector.
166 flashSector_t startSector = (*endSector + 1) - partitionSectors; // + 1 for inclusive
168 flashPartitionSet(type, startSector, *endSector);
170 *endSector = startSector - 1;
173 static void flashConfigurePartitions(void)
175 const flashGeometry_t *flashGeometry = flashGetGeometry();
176 if (flashGeometry->totalSize == 0) {
177 return;
180 flashSector_t startSector = 0;
181 flashSector_t endSector = flashGeometry->sectors - 1; // 0 based index
183 const flashPartition_t *badBlockPartition = flashPartitionFindByType(FLASH_PARTITION_TYPE_BADBLOCK_MANAGEMENT);
184 if (badBlockPartition) {
185 endSector = badBlockPartition->startSector - 1;
188 #if defined(FIRMWARE_SIZE)
189 createPartition(FLASH_PARTITION_TYPE_FIRMWARE, FIRMWARE_SIZE * 1024, &endSector);
190 #endif
192 #if defined(MSP_FIRMWARE_UPDATE)
193 createPartition(FLASH_PARTITION_TYPE_FIRMWARE_UPDATE_META, flashGeometry->sectorSize, &endSector);
194 createPartition(FLASH_PARTITION_TYPE_UPDATE_FIRMWARE, MCU_FLASH_SIZE * 1024, &endSector);
195 createPartition(FLASH_PARTITION_TYPE_FULL_BACKUP, MCU_FLASH_SIZE * 1024, &endSector);
196 #endif
198 #if defined(CONFIG_IN_EXTERNAL_FLASH)
199 uint32_t configSize = EEPROM_SIZE;
200 flashSector_t configSectors = (configSize / flashGeometry->sectorSize);
202 if (configSize % flashGeometry->sectorSize > 0) {
203 configSectors++; // needs a portion of a sector.
205 configSize = configSectors * flashGeometry->sectorSize;
207 createPartition(FLASH_PARTITION_TYPE_CONFIG, configSize, &endSector);
208 #endif
210 #ifdef USE_FLASHFS
211 flashPartitionSet(FLASH_PARTITION_TYPE_FLASHFS, startSector, endSector);
212 #endif
215 flashPartition_t *flashPartitionFindByType(uint8_t type)
217 for (int index = 0; index < FLASH_MAX_PARTITIONS; index++) {
218 flashPartition_t *candidate = &flashPartitionTable.partitions[index];
219 if (candidate->type == type) {
220 return candidate;
224 return NULL;
227 const flashPartition_t *flashPartitionFindByIndex(uint8_t index)
229 if (index >= flashPartitions) {
230 return NULL;
233 return &flashPartitionTable.partitions[index];
236 void flashPartitionSet(uint8_t type, uint32_t startSector, uint32_t endSector)
238 flashPartition_t *entry = flashPartitionFindByType(type);
240 if (!entry) {
241 if (flashPartitions == FLASH_MAX_PARTITIONS - 1) {
242 return;
244 entry = &flashPartitionTable.partitions[flashPartitions++];
247 entry->type = type;
248 entry->startSector = startSector;
249 entry->endSector = endSector;
252 // Must be in sync with FLASH_PARTITION_TYPE
253 static const char *flashPartitionNames[] = {
254 "UNKNOWN ",
255 "PARTITION",
256 "FLASHFS ",
257 "BBMGMT ",
258 "FIRMWARE ",
259 "CONFIG ",
260 "FW UPDT ",
263 const char *flashPartitionGetTypeName(flashPartitionType_e type)
265 if (type < ARRAYLEN(flashPartitionNames)) {
266 return flashPartitionNames[type];
269 return NULL;
272 bool flashInit(void)
274 memset(&flashPartitionTable, 0x00, sizeof(flashPartitionTable));
276 bool haveFlash = flashDeviceInit();
278 flashConfigurePartitions();
280 return haveFlash;
283 int flashPartitionCount(void)
285 return flashPartitions;
288 uint32_t flashPartitionSize(flashPartition_t *partition)
290 const flashGeometry_t * const geometry = flashGetGeometry();
291 return (partition->endSector - partition->startSector + 1) * geometry->sectorSize;
294 void flashPartitionErase(flashPartition_t *partition)
296 const flashGeometry_t * const geometry = flashGetGeometry();
298 // if there's a single FLASHFS partition and it uses the entire flash then do a full erase
299 const bool doFullErase = (flashPartitionCount() == 1) && (FLASH_PARTITION_SECTOR_COUNT(partition) == geometry->sectors);
300 if (doFullErase) {
301 flashEraseCompletely();
302 return;
305 for (unsigned i = partition->startSector; i <= partition->endSector; i++) {
306 uint32_t flashAddress = geometry->sectorSize * i;
307 flashEraseSector(flashAddress);
308 flashWaitForReady(0);
311 #endif // USE_FLASH_CHIP