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