aboutsummaryrefslogtreecommitdiff
path: root/contrib/loaders/flash/stm32/stm32l4x.c
blob: b657ec9aeab31401d94a4074b7b3ed6d982ff3a7 (plain)
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
// SPDX-License-Identifier: GPL-2.0-or-later

/**
 * Copyright (C) 2021 Tarek BOCHKATI
 *   tarek.bouchkati@st.com
 */

#define OPENOCD_CONTRIB_LOADERS_FLASH_STM32_STM32L4X

#include <stdint.h>
#include "../../../../src/flash/nor/stm32l4x.h"

static inline __attribute__((always_inline))
void copy_buffer_u32(uint32_t *dst, uint32_t *src, int len)
{
	for (int i = 0; i < len; i++)
		dst[i] = src[i];
}

/* this function is assumes that fifo_size is multiple of flash_word_size
 * this condition is ensured by target_run_flash_async_algorithm
 */

void write(volatile struct stm32l4_work_area *work_area,
		   uint8_t *fifo_end,
		   uint8_t *target_address,
		   uint32_t count)
{
	volatile uint32_t *flash_sr = (uint32_t *) work_area->params.flash_sr_addr;
	volatile uint32_t *flash_cr = (uint32_t *) work_area->params.flash_cr_addr;

	/* optimization to avoid reading from memory each time */
	uint8_t *rp_cache  = work_area->fifo.rp;

	/* fifo_start is used to wrap when we reach fifo_end */
	uint8_t *fifo_start = rp_cache;

	/* enable flash programming */
	*flash_cr = FLASH_PG;

	while (count) {
		/* optimization to avoid reading from memory each time */
		uint8_t *wp_cache  = work_area->fifo.wp;
		if (wp_cache == 0)
			break; /* aborted by target_run_flash_async_algorithm */

		int32_t fifo_size = wp_cache - rp_cache;
		if (fifo_size < 0) {
			/* consider the linear fifo, we will wrap later */
			fifo_size = fifo_end - rp_cache;
		}

		/* wait for at least a flash word */
		while (fifo_size >= work_area->params.flash_word_size) {
			copy_buffer_u32((uint32_t *)target_address,
					(uint32_t *)rp_cache,
					work_area->params.flash_word_size / 4);

			/* update target_address and rp_cache */
			target_address += work_area->params.flash_word_size;
			rp_cache += work_area->params.flash_word_size;

			/* wait for the busy flag */
			while (*flash_sr & work_area->params.flash_sr_bsy_mask)
				;

			if (*flash_sr & FLASH_ERROR) {
				work_area->fifo.rp = 0; /* set rp to zero 0 on error */
				goto write_end;
			}

			/* wrap if reach the fifo_end, and update rp in memory */
			if (rp_cache >= fifo_end)
				rp_cache = fifo_start;

			/* flush the rp cache value,
			 * so target_run_flash_async_algorithm can fill the circular fifo */
			work_area->fifo.rp = rp_cache;

			/* update fifo_size and count */
			fifo_size -= work_area->params.flash_word_size;
			count--;
		}
	}

write_end:
	/* disable flash programming */
	*flash_cr = 0;

	/* soft break the loader */
	__asm("bkpt 0");
}

/* by enabling this define 'DEBUG':
 * the main() function can help help debugging the loader algo
 * note: the application should be linked into RAM */

/* #define DEBUG */

#ifdef DEBUG
/* device selector: STM32L5 | STM32U5 | STM32WB | STM32WL | STM32WL_CPU2 | STM32G0Bx | ... */
#define STM32U5

/* when using a secure device, and want to test the secure programming enable this define */
/* #define SECURE */

#if defined(STM32U5)
#  define FLASH_WORD_SIZE   16
#else
#  define FLASH_WORD_SIZE   8
#endif

#if defined(STM32WB) || defined(STM32WL)
#  define FLASH_BASE        0x58004000
#else
#  define FLASH_BASE        0x40022000
#endif

#if defined(STM32G0Bx)
#  define FLASH_BSY_MASK      (FLASH_BSY | FLASH_BSY2)
#else
#  define FLASH_BSY_MASK      FLASH_BSY
#endif

#if defined(STM32L5) || defined(STM32U5)
#  ifdef SECURE
#    define FLASH_KEYR_OFFSET 0x0c
#    define FLASH_SR_OFFSET   0x24
#    define FLASH_CR_OFFSET   0x2c
#  else
#    define FLASH_KEYR_OFFSET 0x08
#    define FLASH_SR_OFFSET   0x20
#    define FLASH_CR_OFFSET   0x28
#  endif
#elif defined(STM32WL_CPU2)
#  define FLASH_KEYR_OFFSET 0x08
#  define FLASH_SR_OFFSET   0x60
#  define FLASH_CR_OFFSET   0x64
#else
#  define FLASH_KEYR_OFFSET 0x08
#  define FLASH_SR_OFFSET   0x10
#  define FLASH_CR_OFFSET   0x14
#endif

#define FLASH_KEYR        (uint32_t *)((FLASH_BASE) + (FLASH_KEYR_OFFSET))
#define FLASH_SR          (uint32_t *)((FLASH_BASE) + (FLASH_SR_OFFSET))
#define FLASH_CR          (uint32_t *)((FLASH_BASE) + (FLASH_CR_OFFSET))

int main()
{
	const uint32_t count = 2;
	const uint32_t buf_size = count * FLASH_WORD_SIZE;
	const uint32_t work_area_size = sizeof(struct stm32l4_work_area) + buf_size;

	uint8_t work_area_buf[work_area_size];
	struct stm32l4_work_area *workarea = (struct stm32l4_work_area *)work_area_buf;

	/* fill the workarea struct */
	workarea->params.flash_sr_addr = (uint32_t)(FLASH_SR);
	workarea->params.flash_cr_addr = (uint32_t)(FLASH_CR);
	workarea->params.flash_word_size = FLASH_WORD_SIZE;
	workarea->params.flash_sr_bsy_mask = FLASH_BSY_MASK;
	/* note: the workarea->stack is not used, in this configuration */

	/* programming the existing memory raw content in workarea->fifo.buf */
	/* feel free to fill the memory with magical values ... */

	workarea->fifo.wp =  (uint8_t *)(&workarea->fifo.buf + buf_size);
	workarea->fifo.rp =  (uint8_t *)&workarea->fifo.buf;

	/* unlock the flash */
	*FLASH_KEYR = KEY1;
	*FLASH_KEYR = KEY2;

	/* erase sector 0 */
	*FLASH_CR = FLASH_PER | FLASH_STRT;
	while (*FLASH_SR & FLASH_BSY)
		;

	/* flash address, should be aligned to FLASH_WORD_SIZE */
	uint8_t *target_address = (uint8_t *) 0x8000000;

	write(workarea,
		  (uint8_t *)(workarea + work_area_size),
		  target_address,
		  count);

	while (1)
		;
}
#endif /* DEBUG */