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)
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/>.
27 #include "build/debug.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;
45 static bool flashSpiInit(void)
47 #ifdef USE_FLASH_M25P16
48 return m25p16_init(0);
54 bool flashDeviceInit(void)
57 return flashSpiInit();
63 bool flashIsReady(void)
65 #ifdef USE_FLASH_M25P16
66 return m25p16_isReady();
71 bool flashWaitForReady(timeMs_t timeoutMillis
)
73 #ifdef USE_FLASH_M25P16
74 return m25p16_waitForReady(timeoutMillis
);
79 void flashEraseSector(uint32_t address
)
81 #ifdef USE_FLASH_M25P16
82 return m25p16_eraseSector(address
);
86 void flashEraseCompletely(void)
88 #ifdef USE_FLASH_M25P16
89 return m25p16_eraseCompletely();
94 void flashPageProgramBegin(uint32_t address
)
96 #ifdef USE_FLASH_M25P16
97 return m25p16_pageProgramBegin(address
);
101 void flashPageProgramContinue(const uint8_t *data
, int length
)
103 #ifdef USE_FLASH_M25P16
104 return m25p16_pageProgramContinue(data
, length
);
108 void flashPageProgramFinish(void)
110 #ifdef USE_FLASH_M25P16
111 return m25p16_pageProgramFinish();
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
);
123 int flashReadBytes(uint32_t address
, uint8_t *buffer
, int length
)
125 #ifdef USE_FLASH_M25P16
126 return m25p16_readBytes(address
, buffer
, length
);
131 void flashFlush(void)
135 const flashGeometry_t
*flashGetGeometry(void)
137 #ifdef USE_FLASH_M25P16
138 return m25p16_getGeometry();
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.
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) {
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
);
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
);
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
);
211 flashPartitionSet(FLASH_PARTITION_TYPE_FLASHFS
, startSector
, endSector
);
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
) {
227 const flashPartition_t
*flashPartitionFindByIndex(uint8_t index
)
229 if (index
>= flashPartitions
) {
233 return &flashPartitionTable
.partitions
[index
];
236 void flashPartitionSet(uint8_t type
, uint32_t startSector
, uint32_t endSector
)
238 flashPartition_t
*entry
= flashPartitionFindByType(type
);
241 if (flashPartitions
== FLASH_MAX_PARTITIONS
- 1) {
244 entry
= &flashPartitionTable
.partitions
[flashPartitions
++];
248 entry
->startSector
= startSector
;
249 entry
->endSector
= endSector
;
252 // Must be in sync with FLASH_PARTITION_TYPE
253 static const char *flashPartitionNames
[] = {
263 const char *flashPartitionGetTypeName(flashPartitionType_e type
)
265 if (type
< ARRAYLEN(flashPartitionNames
)) {
266 return flashPartitionNames
[type
];
274 memset(&flashPartitionTable
, 0x00, sizeof(flashPartitionTable
));
276 bool haveFlash
= flashDeviceInit();
278 flashConfigurePartitions();
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
);
301 flashEraseCompletely();
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