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
250/* Programmable voltage detector helpers */
251static void intFlashSetPVD() {
252#if defined(STM32F4XX)
253 /* Set threshold */
254 PWR->CR = (PWR->CR & ~PWR_CR_PLS_Msk) | PWR_CR_PLS_VALUE;
255 /* Enable PVD */
256 PWR->CR |= PWR_CR_PVDE;
257#elif defined(STM32F7XX)
258 /* Set threshold */
259 PWR->CR1 = (PWR->CR1 & ~PWR_CR1_PLS_Msk) | PWR_CR1_PLS_VALUE;
260 /* Enable PVD */
261 PWR->CR1 |= PWR_CR1_PVDE;
262#elif defined(STM32H7XX)
263 /* Set threshold */
264 PWR->CR1 = (PWR->CR1 & ~PWR_CR1_PLS_Msk) | PWR_CR1_PLS_VALUE;
265 /* Enable PVD */
266 PWR->CR1 |= PWR_CR1_PVDEN;
267#endif
268}
269
270static bool intFlashGetPVDStatus() {
271 /* Return true if Vdd is higher then selected threshold */
272#if defined(STM32F4XX)
273 return !(PWR->CSR & PWR_CSR_PVDO);
274#else
275 return !(PWR->CSR1 & PWR_CSR1_PVDO);
276#endif
277}
278
279int intFlashErase(flashaddr_t address, size_t size) {
280 flashaddr_t endAddress = address + size - 1;
281 while (address <= endAddress) {
282 flashsector_t sector = intFlashSectorAt(address);
283 int err = intFlashSectorErase(sector);
284 if (err != FLASH_RETURN_SUCCESS)
285 return err;
286 address = intFlashSectorEnd(sector);
287 }
288
290}
291
292bool intFlashIsErased(flashaddr_t address, size_t size) {
293#if CORTEX_MODEL == 7
294 // If we have a cache, invalidate the relevant cache lines.
295 // They may still contain old data, leading us to believe that the
296 // flash erase failed.
297 SCB_InvalidateDCache_by_Addr((uint32_t*)address, size);
298#endif
299
300 /* Check for default set bits in the flash memory
301 * For efficiency, compare flashdata_t values as much as possible,
302 * then, fallback to byte per byte comparison. */
303 while (size >= sizeof(flashdata_t)) {
304 if (*(volatile flashdata_t*) address != (flashdata_t) (-1)) // flashdata_t being unsigned, -1 is 0xFF..FF
305 return false;
306 address += sizeof(flashdata_t);
307 size -= sizeof(flashdata_t);
308 }
309 while (size > 0) {
310 if (*(char*) address != 0xFF)
311 return false;
312 ++address;
313 --size;
314 }
315
316 return TRUE;
317}
318
319bool intFlashCompare(flashaddr_t address, const char* buffer, size_t size) {
320 /* For efficiency, compare flashdata_t values as much as possible,
321 * then, fallback to byte per byte comparison. */
322 while (size >= sizeof(flashdata_t)) {
323 if (*(volatile flashdata_t*) address != *(flashdata_t*) buffer)
324 return FALSE;
325 address += sizeof(flashdata_t);
326 buffer += sizeof(flashdata_t);
327 size -= sizeof(flashdata_t);
328 }
329 while (size > 0) {
330 if (*(volatile char*) address != *buffer)
331 return FALSE;
332 ++address;
333 ++buffer;
334 --size;
335 }
336
337 return TRUE;
338}
339
340int intFlashRead(flashaddr_t source, char* destination, size_t size) {
341#if CORTEX_MODEL == 7
342 // If we have a cache, invalidate the relevant cache lines.
343 // They may still contain old data, leading us to read invalid data.
344 SCB_InvalidateDCache_by_Addr((uint32_t*)source, size);
345#endif
346
347 memcpy(destination, (char*) source, size);
349}
350
351#ifdef STM32H7XX
352int intFlashWrite(flashaddr_t address, const char* buffer, size_t size) {
354 if (!intFlashGetPVDStatus()) {
355 return FLASH_RETURN_LOWVOLTAGEERROR;
356 }
357
358 /* Unlock flash for write access */
359 if (intFlashUnlock() == HAL_FAILED)
360 return FLASH_RETURN_NO_PERMISSION;
361
362 /* Wait for any busy flags */
363 intFlashWaitWhileBusy();
364
365 /* Setup parallelism before program */
366 FLASH_CR &= ~FLASH_CR_PSIZE_MASK;
367 FLASH_CR |= FLASH_CR_PSIZE_VALUE;
368
369 // Round up to the next number of full 32 byte words
370 size_t flashWordCount = (size - 1) / 32 + 1;
371
372 // Read units of flashdata_t from the buffer, writing to flash
373 const flashdata_t* pRead = (const flashdata_t*)buffer;
374 flashdata_t* pWrite = (flashdata_t*)address;
375
376 for (size_t word = 0; word < flashWordCount; word++) {
377 if (!intFlashGetPVDStatus()) {
378 intFlashLock();
379 return FLASH_RETURN_LOWVOLTAGEERROR;
380 }
381
382 /* Enter flash programming mode */
383 FLASH_CR |= FLASH_CR_PG;
384
385 // Flush pipelines
386 __ISB();
387 __DSB();
388
389 static_assert(sizeof(*pWrite) == 4, "Driver supports only 32bit PSIZE");
390
391 // Write 32 bytes/256bits
392 for (size_t i = 0; i < 8; i++) {
393 *pWrite++ = *pRead++;
394 }
395
396 // Flush pipelines
397 __ISB();
398 __DSB();
399
400 /* Wait for completion */
401 intFlashWaitWhileBusy();
402
403 /* Exit flash programming mode */
404 FLASH_CR &= ~FLASH_CR_PG;
405
406 // Flush pipelines
407 __ISB();
408 __DSB();
409 }
410
411 /* Lock flash again */
412 intFlashLock();
413
415}
416
417#else // not STM32H7XX
418static int intFlashWriteData(flashaddr_t address, const flashdata_t data) {
419 /* Clearing error status bits.*/
421
422 /* Enter flash programming mode */
423 FLASH->CR |= FLASH_CR_PG;
424
425 /* Write the data */
426 *(flashdata_t*) address = data;
427
428 // Cortex-M7 (STM32F7/H7) can execute out order - need to force a full flush
429 // so that we actually wait for the operation to complete!
430#if CORTEX_MODEL == 7
431 __DSB();
432#endif
433
434 /* Wait for completion */
435 intFlashWaitWhileBusy();
436
437 /* Exit flash programming mode */
438 FLASH->CR &= ~FLASH_CR_PG;
439
440 return intFlashCheckErrors();
441}
442
443int intFlashWrite(flashaddr_t address, const char* buffer, size_t size) {
445 if (!intFlashGetPVDStatus()) {
446 return FLASH_RETURN_LOWVOLTAGEERROR;
447 }
448
449 int ret = FLASH_RETURN_SUCCESS;
450
451 /* Unlock flash for write access */
452 if (intFlashUnlock() == HAL_FAILED)
453 return FLASH_RETURN_NO_PERMISSION;
454
455 /* Wait for any busy flags */
456 intFlashWaitWhileBusy();
457
458 /* Setup parallelism before any program/erase */
459 FLASH->CR &= ~FLASH_CR_PSIZE_MASK;
460 FLASH->CR |= FLASH_CR_PSIZE_VALUE;
461
462 while (size) {
463 if (!intFlashGetPVDStatus()) {
464 intFlashLock();
465 return FLASH_RETURN_LOWVOLTAGEERROR;
466 }
467
468 /* Check if the flash address is correctly aligned */
469 size_t alignOffset = address % sizeof(flashdata_t);
470 //print("flash alignOffset=%d\r\n", alignOffset);
471 if (alignOffset != 0) {
472 /* Not aligned, thus we have to read the data in flash already present
473 * and update them with buffer's data */
474
475 /* Align the flash address correctly */
476 flashaddr_t alignedFlashAddress = address - alignOffset;
477
478 /* Read already present data */
479 flashdata_t tmp = *(volatile flashdata_t*) alignedFlashAddress;
480
481 /* Compute how much bytes one must update in the data read */
482 size_t chunkSize = sizeof(flashdata_t) - alignOffset;
483 if (chunkSize > size)
484 chunkSize = size; // this happens when both address and address + size are not aligned
485
486 /* Update the read data with buffer's data */
487 memcpy((char*) &tmp + alignOffset, buffer, chunkSize);
488
489 /* Write the new data in flash */
490 ret = intFlashWriteData(alignedFlashAddress, tmp);
491 if (ret != FLASH_RETURN_SUCCESS)
492 goto exit;
493
494 /* Advance */
495 address += chunkSize;
496 buffer += chunkSize;
497 size -= chunkSize;
498 } else if (size >= sizeof(flashdata_t)) {
499 /* Now, address is correctly aligned. One can copy data directly from
500 * buffer's data to flash memory until the size of the data remaining to be
501 * copied requires special treatment. */
502 //print("flash write size=%d\r\n", size);
503 ret = intFlashWriteData(address, *(const flashdata_t*) buffer);
504 if (ret != FLASH_RETURN_SUCCESS)
505 goto exit;
506 address += sizeof(flashdata_t);
507 buffer += sizeof(flashdata_t);
508 size -= sizeof(flashdata_t);
509 } else /* if (size > 0) */ {
510 /* Now, address is correctly aligned, but the remaining data are to
511 * small to fill a entier flashdata_t. Thus, one must read data already
512 * in flash and update them with buffer's data before writing an entire
513 * flashdata_t to flash memory. */
514 flashdata_t tmp = *(volatile flashdata_t*) address;
515 memcpy(&tmp, buffer, size);
516 ret = intFlashWriteData(address, tmp);
517 if (ret != FLASH_RETURN_SUCCESS)
518 goto exit;
519 size = 0;
520 }
521 }
522
523exit:
524 /* Lock flash again */
525 intFlashLock()
526 ;
527
528 return ret;
529}
530#endif
531
532#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:111
uint32_t flashdata_t
Definition flash_int.h:64
size_t flashSectorSize(flashsector_t sector)
Get the size of sector.
Definition mpu_util.cpp:242
uint8_t flashsector_t
Index of a sector.
Definition flash_int.h:114
flashsector_t intFlashSectorAt(flashaddr_t address)
Definition flash_int.cpp:73
static bool isDualBank(void)
static bool intFlashGetPVDStatus()
static int intFlashCheckErrors()
Definition flash_int.cpp:89
static void intFlashSetPVD()
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