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"
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
[] = {
45 #ifdef USE_FLASH_M25P16
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
,
59 #ifdef USE_FLASH_W25N01G
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
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);
88 flash
= &flashDrivers
[idx
];
95 bool flashIsReady(void)
97 // prevent the machine cycle from crashing if there is no external flash memory
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)
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
144 return flash
->getGeometry();
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.
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) {
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
);
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
);
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
);
217 flashPartitionSet(FLASH_PARTITION_TYPE_FLASHFS
, startSector
, endSector
);
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
) {
233 const flashPartition_t
*flashPartitionFindByIndex(uint8_t index
)
235 if (index
>= flashPartitions
) {
239 return &flashPartitionTable
.partitions
[index
];
242 void flashPartitionSet(uint8_t type
, uint32_t startSector
, uint32_t endSector
)
244 flashPartition_t
*entry
= flashPartitionFindByType(type
);
247 if (flashPartitions
== FLASH_MAX_PARTITIONS
- 1) {
250 entry
= &flashPartitionTable
.partitions
[flashPartitions
++];
254 entry
->startSector
= startSector
;
255 entry
->endSector
= endSector
;
258 // Must be in sync with FLASH_PARTITION_TYPE
259 static const char *flashPartitionNames
[] = {
269 const char *flashPartitionGetTypeName(flashPartitionType_e type
)
271 if (type
< ARRAYLEN(flashPartitionNames
)) {
272 return flashPartitionNames
[type
];
280 memset(&flashPartitionTable
, 0, sizeof(flashPartitionTable
));
282 bool haveFlash
= flashDeviceInit();
285 flashConfigurePartitions();
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
);
309 flashEraseCompletely();
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