-
Notifications
You must be signed in to change notification settings - Fork 2.8k
/
Copy pathota_stm32f.c
226 lines (200 loc) · 7.26 KB
/
ota_stm32f.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
#include "flash.h"
#include "log.h"
#include "ota.h"
#if MG_OTA == MG_OTA_STM32F
static bool mg_stm32f_write(void *, const void *, size_t);
static bool mg_stm32f_swap(void);
static struct mg_flash s_mg_flash_stm32f = {
(void *) 0x08000000, // Start
0, // Size, FLASH_SIZE_REG
0, // Irregular sector size
32, // Align, 256 bit
mg_stm32f_write,
mg_stm32f_swap,
};
#define MG_FLASH_BASE 0x40023c00
#define MG_FLASH_KEYR 0x04
#define MG_FLASH_SR 0x0c
#define MG_FLASH_CR 0x10
#define MG_FLASH_OPTCR 0x14
#define MG_FLASH_SIZE_REG_F7 0x1FF0F442
#define MG_FLASH_SIZE_REG_F4 0x1FFF7A22
#define STM_DBGMCU_IDCODE 0xE0042000
#define STM_DEV_ID (MG_REG(STM_DBGMCU_IDCODE) & (MG_BIT(12) - 1))
#define SYSCFG_MEMRMP 0x40013800
#define MG_FLASH_SIZE_REG_LOCATION \
((STM_DEV_ID >= 0x449) ? MG_FLASH_SIZE_REG_F7 : MG_FLASH_SIZE_REG_F4)
static size_t flash_size(void) {
return (MG_REG(MG_FLASH_SIZE_REG_LOCATION) & 0xFFFF) * 1024;
}
MG_IRAM static int is_dualbank(void) {
// only F42x/F43x series (0x419) support dual bank
return STM_DEV_ID == 0x419;
}
MG_IRAM static void flash_unlock(void) {
static bool unlocked = false;
if (unlocked == false) {
MG_REG(MG_FLASH_BASE + MG_FLASH_KEYR) = 0x45670123;
MG_REG(MG_FLASH_BASE + MG_FLASH_KEYR) = 0xcdef89ab;
unlocked = true;
}
}
#define MG_FLASH_CONFIG_16_64_128 1 // used by STM32F7
#define MG_FLASH_CONFIG_32_128_256 2 // used by STM32F4 and F2
MG_IRAM static bool flash_page_start(volatile uint32_t *dst) {
char *base = (char *) s_mg_flash_stm32f.start;
char *end = base + s_mg_flash_stm32f.size;
if (is_dualbank() && dst >= (uint32_t *) (base + (end - base) / 2)) {
dst = (uint32_t *) ((uint32_t) dst - (end - base) / 2);
}
uint32_t flash_config = MG_FLASH_CONFIG_16_64_128;
if (STM_DEV_ID >= 0x449) {
flash_config = MG_FLASH_CONFIG_32_128_256;
}
volatile char *p = (char *) dst;
if (p >= base && p < end) {
if (p < base + 16 * 1024 * 4 * flash_config) {
if ((p - base) % (16 * 1024 * flash_config) == 0) return true;
} else if (p == base + 16 * 1024 * 4 * flash_config) {
return true;
} else if ((p - base) % (128 * 1024 * flash_config) == 0) {
return true;
}
}
return false;
}
MG_IRAM static int flash_sector(volatile uint32_t *addr) {
char *base = (char *) s_mg_flash_stm32f.start;
char *end = base + s_mg_flash_stm32f.size;
bool addr_in_bank_2 = false;
if (is_dualbank() && addr >= (uint32_t *) (base + (end - base) / 2)) {
addr = (uint32_t *) ((uint32_t) addr - (end - base) / 2);
addr_in_bank_2 = true;
}
volatile char *p = (char *) addr;
uint32_t flash_config = MG_FLASH_CONFIG_16_64_128;
if (STM_DEV_ID >= 0x449) {
flash_config = MG_FLASH_CONFIG_32_128_256;
}
int sector = -1;
if (p >= base && p < end) {
if (p < base + 16 * 1024 * 4 * flash_config) {
sector = (p - base) / (16 * 1024 * flash_config);
} else if (p >= base + 64 * 1024 * flash_config &&
p < base + 128 * 1024 * flash_config) {
sector = 4;
} else {
sector = (p - base) / (128 * 1024 * flash_config) + 4;
}
}
if (sector == -1) return -1;
if (addr_in_bank_2) sector += 12; // a bank has 12 sectors
return sector;
}
MG_IRAM static bool flash_is_err(void) {
return MG_REG(MG_FLASH_BASE + MG_FLASH_SR) & ((MG_BIT(7) - 1) << 1);
}
MG_IRAM static void flash_wait(void) {
while (MG_REG(MG_FLASH_BASE + MG_FLASH_SR) & (MG_BIT(16))) (void) 0;
}
MG_IRAM static void flash_clear_err(void) {
flash_wait(); // Wait until ready
MG_REG(MG_FLASH_BASE + MG_FLASH_SR) = 0xf2; // Clear all errors
}
MG_IRAM static bool mg_stm32f_erase(void *addr) {
bool ok = false;
if (flash_page_start(addr) == false) {
MG_ERROR(("%p is not on a sector boundary", addr));
} else {
int sector = flash_sector(addr);
if (sector < 0) return false;
uint32_t sector_reg = sector;
if (is_dualbank() && sector >= 12) {
// 3.9.8 Flash control register (FLASH_CR) for F42xxx and F43xxx
// BITS[7:3]
sector_reg -= 12;
sector_reg |= MG_BIT(4);
}
flash_unlock();
flash_wait();
uint32_t cr = MG_BIT(1); // SER
cr |= MG_BIT(16); // STRT
cr |= (sector_reg & 31) << 3; // sector
MG_REG(MG_FLASH_BASE + MG_FLASH_CR) = cr;
ok = !flash_is_err();
MG_DEBUG(("Erase sector %lu @ %p %s. CR %#lx SR %#lx", sector, addr,
ok ? "ok" : "fail", MG_REG(MG_FLASH_BASE + MG_FLASH_CR),
MG_REG(MG_FLASH_BASE + MG_FLASH_SR)));
// After we have erased the sector, set CR flags for programming
// 2 << 8 is word write parallelism, bit(0) is PG. RM0385, section 3.7.5
MG_REG(MG_FLASH_BASE + MG_FLASH_CR) = MG_BIT(0) | (2 << 8);
flash_clear_err();
}
return ok;
}
MG_IRAM static bool mg_stm32f_swap(void) {
// STM32 F42x/F43x support dual bank, however, the memory mapping
// change will not be carried through a hard reset. Therefore, we will use
// the single bank approach for this family as well.
return true;
}
static bool s_flash_irq_disabled;
MG_IRAM static bool mg_stm32f_write(void *addr, const void *buf, size_t len) {
if ((len % s_mg_flash_stm32f.align) != 0) {
MG_ERROR(("%lu is not aligned to %lu", len, s_mg_flash_stm32f.align));
return false;
}
uint32_t *dst = (uint32_t *) addr;
uint32_t *src = (uint32_t *) buf;
uint32_t *end = (uint32_t *) ((char *) buf + len);
bool ok = true;
MG_ARM_DISABLE_IRQ();
flash_unlock();
flash_clear_err();
MG_REG(MG_FLASH_BASE + MG_FLASH_CR) = MG_BIT(0) | MG_BIT(9); // PG, 32-bit
flash_wait();
MG_DEBUG(("Writing flash @ %p, %lu bytes", addr, len));
while (ok && src < end) {
if (flash_page_start(dst) && mg_stm32f_erase(dst) == false) break;
*(volatile uint32_t *) dst++ = *src++;
MG_DSB(); // ensure flash is written with no errors
flash_wait();
if (flash_is_err()) ok = false;
}
if (!s_flash_irq_disabled) MG_ARM_ENABLE_IRQ();
MG_DEBUG(("Flash write %lu bytes @ %p: %s. CR %#lx SR %#lx", len, dst,
ok ? "ok" : "fail", MG_REG(MG_FLASH_BASE + MG_FLASH_CR),
MG_REG(MG_FLASH_BASE + MG_FLASH_SR)));
MG_REG(MG_FLASH_BASE + MG_FLASH_CR) &= ~MG_BIT(0); // Clear programming flag
return ok;
}
// just overwrite instead of swap
MG_IRAM void single_bank_swap(char *p1, char *p2, size_t size) {
// no stdlib calls here
mg_stm32f_write(p1, p2, size);
*(volatile unsigned long *) 0xe000ed0c = 0x5fa0004;
}
bool mg_ota_begin(size_t new_firmware_size) {
s_mg_flash_stm32f.size = flash_size();
return mg_ota_flash_begin(new_firmware_size, &s_mg_flash_stm32f);
}
bool mg_ota_write(const void *buf, size_t len) {
return mg_ota_flash_write(buf, len, &s_mg_flash_stm32f);
}
bool mg_ota_end(void) {
if (mg_ota_flash_end(&s_mg_flash_stm32f)) {
// Swap partitions. Pray power does not go away
MG_INFO(("Swapping partitions, size %u (%u sectors)",
s_mg_flash_stm32f.size, STM_DEV_ID == 0x449 ? 8 : 12));
MG_INFO(("Do NOT power off..."));
mg_log_level = MG_LL_NONE;
s_flash_irq_disabled = true;
char *p1 = (char *) s_mg_flash_stm32f.start;
char *p2 = p1 + s_mg_flash_stm32f.size / 2;
size_t size = s_mg_flash_stm32f.size / 2;
// Runs in RAM, will reset when finished
single_bank_swap(p1, p2, size);
}
return false;
}
#endif