rusEFI
The most advanced open source ECU
Loading...
Searching...
No Matches
flash_int.cpp
Go to the documentation of this file.
1/**
2 *
3 * http://www.chibios.com/forum/viewtopic.php?f=8&t=820
4 * https://github.com/tegesoft/flash-stm32f407
5 *
6 * @file flash_int.cpp
7 * @brief Lower-level code related to internal flash memory
8 */
9
10#include "pch.h"
11
12#ifndef EFI_STORAGE_INT_FLASH_DRIVER
13#define EFI_STORAGE_INT_FLASH_DRIVER TRUE
14#endif
15
16#if defined(EFI_BOOTLOADER) || EFI_STORAGE_INT_FLASH_DRIVER
17
18#include "flash_int.h"
19#include <string.h>
20
21#ifdef STM32H7XX
22 #undef FLASH_BASE
23
24 #ifdef STM32H743xx
25 // Use bank 2 on H743
26 #define FLASH_CR FLASH->CR2
27 #define FLASH_SR FLASH->SR2
28 #define FLASH_KEYR FLASH->KEYR2
29 #define FLASH_CCR FLASH->CCR2
30
31 // This is the start of the second bank, since H7 sector numbers are bank relative
32 #define FLASH_BASE 0x08100000
33 #endif
34
35 #ifdef STM32H723xx
36 // H723 is single banked
37 #define FLASH_CR FLASH->CR1
38 #define FLASH_SR FLASH->SR1
39 #define FLASH_KEYR FLASH->KEYR1
40 #define FLASH_CCR FLASH->CCR1
41
42 // This is the start of the bank, since H7 sector numbers are bank relative
43 #define FLASH_BASE 0x08000000
44 #endif
45
46 // I have no idea why ST changed the register name from STRT -> START
47 #define FLASH_CR_STRT FLASH_CR_START
48
49 // QW bit supercedes the older BSY bit
50 #define intFlashWaitWhileBusy() do { __DSB(); } while (FLASH_SR & FLASH_SR_QW);
51#else
52 #define FLASH_CR FLASH->CR
53 #define FLASH_SR FLASH->SR
54 #define FLASH_KEYR FLASH->KEYR
55
56 // Wait for the flash operation to finish
57 #define intFlashWaitWhileBusy() do { __DSB(); } while (FLASH->SR & FLASH_SR_BSY);
58#endif
59
61 flashaddr_t address = FLASH_BASE;
62 while (sector > 0) {
63 --sector;
64 address += flashSectorSize(sector);
65 }
66 return address;
67}
68
72
74 flashsector_t sector = 0;
75 while (address >= intFlashSectorEnd(sector))
76 ++sector;
77 return sector;
78}
79
80static void intFlashClearErrors() {
81#ifdef STM32H7XX
82 FLASH_CCR = 0xffffffff;
83#else
84 FLASH_SR = 0x0000ffff;
85#endif
86 __DSB();
87}
88
89static int intFlashCheckErrors() {
90 uint32_t sr = FLASH_SR;
91
92#ifdef FLASH_SR_OPERR
93 if (sr & FLASH_SR_OPERR)
94 return FLASH_RETURN_OPERROR;
95#endif
96 if (sr & FLASH_SR_WRPERR)
97 return FLASH_RETURN_WPERROR;
98#ifdef FLASH_SR_PGAERR
99 if (sr & FLASH_SR_PGAERR)
100 return FLASH_RETURN_ALIGNERROR;
101#endif
102#ifdef FLASH_SR_PGPERR
103 if (sr & FLASH_SR_PGPERR)
104 return FLASH_RETURN_PPARALLERROR;
105#endif
106#ifdef FLASH_SR_ERSERR
107 if (sr & FLASH_SR_ERSERR)
108 return FLASH_RETURN_ESEQERROR;
109#endif
110#ifdef FLASH_SR_PGSERR
111 if (sr & FLASH_SR_PGSERR)
112 return FLASH_RETURN_PSEQERROR;
113#endif
114#ifdef FLASH_SR_RDSERR
115 if (sr & FLASH_SR_RDSERR)
116 return FLASH_RETURN_SECURITYERROR;
117#endif
118#ifdef FLASH_SR_RDPERR
119 if (sr & FLASH_SR_RDPERR)
120 return FLASH_RETURN_SECURITYERROR;
121#endif
122#ifdef FLASH_SR_WRPERR
123 if (sr & FLASH_SR_WRPERR)
124 return FLASH_RETURN_SECURITYERROR;
125#endif
126#ifdef FLASH_SR_CRCRDERR
127 if (sr & FLASH_SR_CRCRDERR)
128 return FLASH_RETURN_CRCERROR;
129#endif
130
132}
133
134/**
135 * @brief Unlock the flash memory for write access.
136 * @return HAL_SUCCESS Unlock was successful.
137 * @return HAL_FAILED Unlock failed.
138 */
139static bool intFlashUnlock(void) {
140 /* Check if unlock is really needed */
141 if (!(FLASH_CR & FLASH_CR_LOCK))
142 return HAL_SUCCESS;
143
144 /* Write magic unlock sequence */
145 FLASH_KEYR = 0x45670123;
146 FLASH_KEYR = 0xCDEF89AB;
147
148 /* Check if unlock was successful */
149 if (FLASH_CR & FLASH_CR_LOCK)
150 return HAL_FAILED;
151 return HAL_SUCCESS;
152}
153
154/**
155 * @brief Lock the flash memory for write access.
156 */
157#define intFlashLock() { FLASH_CR |= FLASH_CR_LOCK; }
158
159#ifdef STM32F7XX
160static bool isDualBank(void) {
161#ifdef FLASH_OPTCR_nDBANK
162 // cleared bit indicates dual bank
163 return (FLASH->OPTCR & FLASH_OPTCR_nDBANK) == 0;
164#else
165 return 0;
166#endif
167}
168#endif
169
170/**
171 * @brief Erase the flash @p sector.
172 * @details The sector is checked for errors after erase.
173 * @note The sector is deleted regardless of its current state.
174 *
175 * @param sector Sector which is going to be erased.
176 * @return FLASH_RETURN_SUCCESS No error erasing the sector.
177 * @return FLASH_RETURN_BAD_FLASH Flash cell error.
178 * @return FLASH_RETURN_NO_PERMISSION Access denied.
179 */
181 int ret;
182 uint8_t sectorRegIdx = sector;
183#ifdef STM32F7XX
184 // On dual bank STM32F7, sector index doesn't match register value.
185 // High bit indicates bank, low 4 bits indicate sector within bank.
186 // Since each bank has 12 sectors, increment second-bank sector idx
187 // by 4 so that the first sector of the second bank (12) ends up with
188 // index 16 (0b10000)
189 if (isDualBank() && sectorRegIdx >= 12) {
190 sectorRegIdx -= 12;
191 /* bit 4 defines bank.
192 * Sectors starting from 12 are in bank #2 */
193 sectorRegIdx |= 0x10;
194 }
195#endif
196
197 /* Unlock flash for write access */
198 if (intFlashUnlock() == HAL_FAILED)
199 return FLASH_RETURN_NO_PERMISSION;
200
201 /* Wait for any busy flags. */
202 intFlashWaitWhileBusy();
203
204 /* Clearing error status bits.*/
206
207 /* Setup parallelism before any program/erase */
208 FLASH_CR &= ~FLASH_CR_PSIZE_MASK;
209 FLASH_CR |= FLASH_CR_PSIZE_VALUE;
210
211 /* Start deletion of sector.
212 * SNB(4:1) is defined as:
213 * 00000 sector 0
214 * 00001 sector 1
215 * ...
216 * 01011 sector 11 (the end of 1st bank, 1Mb border)
217 * 10000 sector 12 (start of 2nd bank)
218 * ...
219 * 11011 sector 23 (the end of 2nd bank, 2Mb border)
220 * others not allowed */
221 FLASH_CR &= ~FLASH_CR_SNB_Msk;
222 FLASH_CR |= (sectorRegIdx << FLASH_CR_SNB_Pos) & FLASH_CR_SNB_Msk;
223 /* sector erase */
224 FLASH_CR |= FLASH_CR_SER;
225 /* start erase operation */
226 FLASH_CR |= FLASH_CR_STRT;
227
228 /* Wait until it's finished. */
229 intFlashWaitWhileBusy();
230
231 /* Sector erase flag does not clear automatically. */
232 FLASH_CR &= ~FLASH_CR_SER;
233
234 /* Lock flash again */
235 intFlashLock()
236 ;
237
238 ret = intFlashCheckErrors();
239 if (ret != FLASH_RETURN_SUCCESS)
240 return ret;
241
242 /* Check deleted sector for errors */
243 if (intFlashIsErased(intFlashSectorBegin(sector), flashSectorSize(sector)) == FALSE)
244 return FLASH_RETURN_BAD_FLASH; /* Sector is not empty despite the erase cycle! */
245
246 /* Successfully deleted sector */
248}
249
250int intFlashErase(flashaddr_t address, size_t size) {
251 flashaddr_t endAddress = address + size - 1;
252 while (address <= endAddress) {
253 flashsector_t sector = intFlashSectorAt(address);
254 int err = intFlashSectorErase(sector);
255 if (err != FLASH_RETURN_SUCCESS)
256 return err;
257 address = intFlashSectorEnd(sector);
258 }
259
261}
262
263bool intFlashIsErased(flashaddr_t address, size_t size) {
264#if CORTEX_MODEL == 7
265 // If we have a cache, invalidate the relevant cache lines.
266 // They may still contain old data, leading us to believe that the
267 // flash erase failed.
268 SCB_InvalidateDCache_by_Addr((uint32_t*)address, size);
269#endif
270
271 /* Check for default set bits in the flash memory
272 * For efficiency, compare flashdata_t values as much as possible,
273 * then, fallback to byte per byte comparison. */
274 while (size >= sizeof(flashdata_t)) {
275 if (*(volatile flashdata_t*) address != (flashdata_t) (-1)) // flashdata_t being unsigned, -1 is 0xFF..FF
276 return false;
277 address += sizeof(flashdata_t);
278 size -= sizeof(flashdata_t);
279 }
280 while (size > 0) {
281 if (*(char*) address != 0xFF)
282 return false;
283 ++address;
284 --size;
285 }
286
287 return TRUE;
288}
289
290bool intFlashCompare(flashaddr_t address, const char* buffer, size_t size) {
291 /* For efficiency, compare flashdata_t values as much as possible,
292 * then, fallback to byte per byte comparison. */
293 while (size >= sizeof(flashdata_t)) {
294 if (*(volatile flashdata_t*) address != *(flashdata_t*) buffer)
295 return FALSE;
296 address += sizeof(flashdata_t);
297 buffer += sizeof(flashdata_t);
298 size -= sizeof(flashdata_t);
299 }
300 while (size > 0) {
301 if (*(volatile char*) address != *buffer)
302 return FALSE;
303 ++address;
304 ++buffer;
305 --size;
306 }
307
308 return TRUE;
309}
310
311int intFlashRead(flashaddr_t source, char* destination, size_t size) {
312#if CORTEX_MODEL == 7
313 // If we have a cache, invalidate the relevant cache lines.
314 // They may still contain old data, leading us to read invalid data.
315 SCB_InvalidateDCache_by_Addr((uint32_t*)source, size);
316#endif
317
318 memcpy(destination, (char*) source, size);
320}
321
322#ifdef STM32H7XX
323int intFlashWrite(flashaddr_t address, const char* buffer, size_t size) {
324 /* Unlock flash for write access */
325 if (intFlashUnlock() == HAL_FAILED)
326 return FLASH_RETURN_NO_PERMISSION;
327
328 /* Wait for any busy flags */
329 intFlashWaitWhileBusy();
330
331 /* Setup parallelism before program */
332 FLASH_CR &= ~FLASH_CR_PSIZE_MASK;
333 FLASH_CR |= FLASH_CR_PSIZE_VALUE;
334
335 // Round up to the next number of full 32 byte words
336 size_t flashWordCount = (size - 1) / 32 + 1;
337
338 // Read units of flashdata_t from the buffer, writing to flash
339 const flashdata_t* pRead = (const flashdata_t*)buffer;
340 flashdata_t* pWrite = (flashdata_t*)address;
341
342 for (size_t word = 0; word < flashWordCount; word++) {
343 /* Enter flash programming mode */
344 FLASH_CR |= FLASH_CR_PG;
345
346 // Flush pipelines
347 __ISB();
348 __DSB();
349
350 static_assert(sizeof(*pWrite) == 4, "Driver supports only 32bit PSIZE");
351
352 // Write 32 bytes/256bits
353 for (size_t i = 0; i < 8; i++) {
354 *pWrite++ = *pRead++;
355 }
356
357 // Flush pipelines
358 __ISB();
359 __DSB();
360
361 /* Wait for completion */
362 intFlashWaitWhileBusy();
363
364 /* Exit flash programming mode */
365 FLASH_CR &= ~FLASH_CR_PG;
366
367 // Flush pipelines
368 __ISB();
369 __DSB();
370 }
371
372 /* Lock flash again */
373 intFlashLock();
374
376}
377
378#else // not STM32H7XX
379static int intFlashWriteData(flashaddr_t address, const flashdata_t data) {
380 /* Clearing error status bits.*/
382
383 /* Enter flash programming mode */
384 FLASH->CR |= FLASH_CR_PG;
385
386 /* Write the data */
387 *(flashdata_t*) address = data;
388
389 // Cortex-M7 (STM32F7/H7) can execute out order - need to force a full flush
390 // so that we actually wait for the operation to complete!
391#if CORTEX_MODEL == 7
392 __DSB();
393#endif
394
395 /* Wait for completion */
396 intFlashWaitWhileBusy();
397
398 /* Exit flash programming mode */
399 FLASH->CR &= ~FLASH_CR_PG;
400
401 return intFlashCheckErrors();
402}
403
404int intFlashWrite(flashaddr_t address, const char* buffer, size_t size) {
405 int ret = FLASH_RETURN_SUCCESS;
406
407 /* Unlock flash for write access */
408 if (intFlashUnlock() == HAL_FAILED)
409 return FLASH_RETURN_NO_PERMISSION;
410
411 /* Wait for any busy flags */
412 intFlashWaitWhileBusy();
413
414 /* Setup parallelism before any program/erase */
415 FLASH->CR &= ~FLASH_CR_PSIZE_MASK;
416 FLASH->CR |= FLASH_CR_PSIZE_VALUE;
417
418 /* Check if the flash address is correctly aligned */
419 size_t alignOffset = address % sizeof(flashdata_t);
420// print("flash alignOffset=%d\r\n", alignOffset);
421 if (alignOffset != 0) {
422 /* Not aligned, thus we have to read the data in flash already present
423 * and update them with buffer's data */
424
425 /* Align the flash address correctly */
426 flashaddr_t alignedFlashAddress = address - alignOffset;
427
428 /* Read already present data */
429 flashdata_t tmp = *(volatile flashdata_t*) alignedFlashAddress;
430
431 /* Compute how much bytes one must update in the data read */
432 size_t chunkSize = sizeof(flashdata_t) - alignOffset;
433 if (chunkSize > size)
434 chunkSize = size; // this happens when both address and address + size are not aligned
435
436 /* Update the read data with buffer's data */
437 memcpy((char*) &tmp + alignOffset, buffer, chunkSize);
438
439 /* Write the new data in flash */
440 ret = intFlashWriteData(alignedFlashAddress, tmp);
441 if (ret != FLASH_RETURN_SUCCESS)
442 goto exit;
443
444 /* Advance */
445 address += chunkSize;
446 buffer += chunkSize;
447 size -= chunkSize;
448 }
449
450 /* Now, address is correctly aligned. One can copy data directly from
451 * buffer's data to flash memory until the size of the data remaining to be
452 * copied requires special treatment. */
453 while (size >= sizeof(flashdata_t)) {
454// print("flash write size=%d\r\n", size);
455 ret = intFlashWriteData(address, *(const flashdata_t*) buffer);
456 if (ret != FLASH_RETURN_SUCCESS)
457 goto exit;
458 address += sizeof(flashdata_t);
459 buffer += sizeof(flashdata_t);
460 size -= sizeof(flashdata_t);
461 }
462
463 /* Now, address is correctly aligned, but the remaining data are to
464 * small to fill a entier flashdata_t. Thus, one must read data already
465 * in flash and update them with buffer's data before writing an entire
466 * flashdata_t to flash memory. */
467 if (size > 0) {
468 flashdata_t tmp = *(volatile flashdata_t*) address;
469 memcpy(&tmp, buffer, size);
470 ret = intFlashWriteData(address, tmp);
471 if (ret != FLASH_RETURN_SUCCESS)
472 goto exit;
473 }
474
475exit:
476 /* Lock flash again */
477 intFlashLock()
478 ;
479
480 return ret;
481}
482#endif
483
484#endif /* EFI_STORAGE_INT_FLASH */
return FLASH_RETURN_SUCCESS
Definition flash_int.cpp:80
int intFlashWrite(flashaddr_t address, const char *buffer, size_t size)
Copy data from a buffer to the flash memory.
Definition flash_int.cpp:83
bool intFlashIsErased(flashaddr_t address, size_t size)
Check if the size bytes of flash memory starting at address are erased.
Definition flash_int.cpp:89
int intFlashRead(flashaddr_t source, char *destination, size_t size)
Copy data from the flash memory to a destination.
int size_t size
Definition flash_int.cpp:51
bool intFlashCompare(flashaddr_t address, const char *buffer, size_t size)
Check if the data in buffer are identical to the one in flash memory.
uint32_t flashdata_t
Definition flash_int.cpp:20
int intFlashErase(flashaddr_t address, size_t size)
Erase the sectors containing the span of size bytes starting at address.
uintptr_t flashaddr_t
Address in the flash memory.
Definition flash_int.h:90
uint32_t flashdata_t
Definition flash_int.h:58
size_t flashSectorSize(flashsector_t sector)
Get the size of sector.
Definition mpu_util.cpp:237
uint8_t flashsector_t
Index of a sector.
Definition flash_int.h:93
flashsector_t intFlashSectorAt(flashaddr_t address)
Definition flash_int.cpp:73
static bool isDualBank(void)
static int intFlashCheckErrors()
Definition flash_int.cpp:89
static int intFlashSectorErase(flashsector_t sector)
Erase the flash sector.
flashaddr_t intFlashSectorEnd(flashsector_t sector)
Definition flash_int.cpp:69
static void intFlashClearErrors()
Definition flash_int.cpp:80
flashaddr_t intFlashSectorBegin(flashsector_t sector)
Definition flash_int.cpp:60
static bool intFlashUnlock(void)
Unlock the flash memory for write access.
static int intFlashWriteData(flashaddr_t address, const flashdata_t data)
static BigBufferHandle buffer