flash/rp2040: don't initialize to NULL fields in struct
[openocd.git] / src / flash / nor / rp2040.c
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
6
7 #include "imp.h"
8 #include <helper/binarybuffer.h>
9 #include <target/algorithm.h>
10 #include <target/armv7m.h>
11 #include "spi.h"
12
13 /* NOTE THAT THIS CODE REQUIRES FLASH ROUTINES in BOOTROM WITH FUNCTION TABLE PTR AT 0x00000010
14 Your gdbinit should load the bootrom.elf if appropriate */
15
16 /* this is 'M' 'u', 1 (version) */
17 #define BOOTROM_MAGIC 0x01754d
18 #define BOOTROM_MAGIC_ADDR 0x00000010
19
20 /* Call a ROM function via the debug trampoline
21 Up to four arguments passed in r0...r3 as per ABI
22 Function address is passed in r7
23 the trampoline is needed because OpenOCD "algorithm" code insists on sw breakpoints. */
24
25 #define MAKE_TAG(a, b) (((b)<<8) | a)
26 #define FUNC_DEBUG_TRAMPOLINE MAKE_TAG('D', 'T')
27 #define FUNC_DEBUG_TRAMPOLINE_END MAKE_TAG('D', 'E')
28 #define FUNC_FLASH_EXIT_XIP MAKE_TAG('E', 'X')
29 #define FUNC_CONNECT_INTERNAL_FLASH MAKE_TAG('I', 'F')
30 #define FUNC_FLASH_RANGE_ERASE MAKE_TAG('R', 'E')
31 #define FUNC_FLASH_RANGE_PROGRAM MAKE_TAG('R', 'P')
32 #define FUNC_FLASH_FLUSH_CACHE MAKE_TAG('F', 'C')
33 #define FUNC_FLASH_ENTER_CMD_XIP MAKE_TAG('C', 'X')
34
35 struct rp2040_flash_bank {
36 /* flag indicating successful flash probe */
37 bool probed;
38 /* stack used by Boot ROM calls */
39 struct working_area *stack;
40 /* function jump table populated by rp2040_flash_probe() */
41 uint16_t jump_debug_trampoline;
42 uint16_t jump_debug_trampoline_end;
43 uint16_t jump_flash_exit_xip;
44 uint16_t jump_connect_internal_flash;
45 uint16_t jump_flash_range_erase;
46 uint16_t jump_flash_range_program;
47 uint16_t jump_flush_cache;
48 uint16_t jump_enter_cmd_xip;
49 /* detected model of SPI flash */
50 const struct flash_device *dev;
51 };
52
53 static uint32_t rp2040_lookup_symbol(struct target *target, uint32_t tag, uint16_t *symbol)
54 {
55 uint32_t magic;
56 int err = target_read_u32(target, BOOTROM_MAGIC_ADDR, &magic);
57 if (err != ERROR_OK)
58 return err;
59
60 magic &= 0xffffff; /* ignore bootrom version */
61 if (magic != BOOTROM_MAGIC) {
62 if (!((magic ^ BOOTROM_MAGIC)&0xffff))
63 LOG_ERROR("Incorrect RP2040 BOOT ROM version");
64 else
65 LOG_ERROR("RP2040 BOOT ROM not found");
66 return ERROR_FAIL;
67 }
68
69 /* dereference the table pointer */
70 uint16_t table_entry;
71 err = target_read_u16(target, BOOTROM_MAGIC_ADDR + 4, &table_entry);
72 if (err != ERROR_OK)
73 return err;
74
75 uint16_t entry_tag;
76 do {
77 err = target_read_u16(target, table_entry, &entry_tag);
78 if (err != ERROR_OK)
79 return err;
80 if (entry_tag == tag) {
81 /* 16 bit symbol is next */
82 return target_read_u16(target, table_entry + 2, symbol);
83 }
84 table_entry += 4;
85 } while (entry_tag);
86 return ERROR_FAIL;
87 }
88
89 static int rp2040_call_rom_func(struct target *target, struct rp2040_flash_bank *priv,
90 uint16_t func_offset, uint32_t argdata[], unsigned int n_args)
91 {
92 char *regnames[4] = { "r0", "r1", "r2", "r3" };
93
94 assert(n_args <= ARRAY_SIZE(regnames)); /* only allow register arguments */
95
96 if (!priv->stack) {
97 LOG_ERROR("no stack for flash programming code");
98 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
99 }
100 target_addr_t stacktop = priv->stack->address + priv->stack->size;
101
102 LOG_DEBUG("Calling ROM func @0x%" PRIx16 " with %d arguments", func_offset, n_args);
103 LOG_DEBUG("Calling on core \"%s\"", target->cmd_name);
104
105 struct reg_param args[ARRAY_SIZE(regnames) + 2];
106 struct armv7m_algorithm alg_info;
107
108 for (unsigned int i = 0; i < n_args; ++i) {
109 init_reg_param(&args[i], regnames[i], 32, PARAM_OUT);
110 buf_set_u32(args[i].value, 0, 32, argdata[i]);
111 }
112 /* Pass function pointer in r7 */
113 init_reg_param(&args[n_args], "r7", 32, PARAM_OUT);
114 buf_set_u32(args[n_args].value, 0, 32, func_offset);
115 init_reg_param(&args[n_args + 1], "sp", 32, PARAM_OUT);
116 buf_set_u32(args[n_args + 1].value, 0, 32, stacktop);
117
118
119 for (unsigned int i = 0; i < n_args + 2; ++i)
120 LOG_DEBUG("Set %s = 0x%" PRIx32, args[i].reg_name, buf_get_u32(args[i].value, 0, 32));
121
122 /* Actually call the function */
123 alg_info.common_magic = ARMV7M_COMMON_MAGIC;
124 alg_info.core_mode = ARM_MODE_THREAD;
125 int err = target_run_algorithm(
126 target,
127 0, NULL, /* No memory arguments */
128 n_args + 1, args, /* User arguments + r7 */
129 priv->jump_debug_trampoline, priv->jump_debug_trampoline_end,
130 3000, /* 3s timeout */
131 &alg_info
132 );
133 for (unsigned int i = 0; i < n_args + 2; ++i)
134 destroy_reg_param(&args[i]);
135 if (err != ERROR_OK)
136 LOG_ERROR("Failed to invoke ROM function @0x%" PRIx16 "\n", func_offset);
137 return err;
138
139 }
140
141 static int stack_grab_and_prep(struct flash_bank *bank)
142 {
143 struct rp2040_flash_bank *priv = bank->driver_priv;
144
145 /* target_alloc_working_area always allocates multiples of 4 bytes, so no worry about alignment */
146 const int STACK_SIZE = 256;
147 int err = target_alloc_working_area(bank->target, STACK_SIZE, &priv->stack);
148 if (err != ERROR_OK) {
149 LOG_ERROR("Could not allocate stack for flash programming code");
150 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
151 }
152
153 LOG_DEBUG("Connecting internal flash");
154 err = rp2040_call_rom_func(bank->target, priv, priv->jump_connect_internal_flash, NULL, 0);
155 if (err != ERROR_OK) {
156 LOG_ERROR("RP2040 erase: failed to connect internal flash");
157 return err;
158 }
159
160 LOG_DEBUG("Kicking flash out of XIP mode");
161 err = rp2040_call_rom_func(bank->target, priv, priv->jump_flash_exit_xip, NULL, 0);
162 if (err != ERROR_OK) {
163 LOG_ERROR("RP2040 erase: failed to exit flash XIP mode");
164 return err;
165 }
166
167 return ERROR_OK;
168 }
169
170 static int rp2040_flash_write(struct flash_bank *bank, const uint8_t *buffer, uint32_t offset, uint32_t count)
171 {
172 LOG_DEBUG("Writing %d bytes starting at 0x%" PRIx32, count, offset);
173
174 struct rp2040_flash_bank *priv = bank->driver_priv;
175 struct target *target = bank->target;
176 struct working_area *bounce;
177
178 int err = stack_grab_and_prep(bank);
179 if (err != ERROR_OK)
180 return err;
181
182 const unsigned int chunk_size = target_get_working_area_avail(target);
183 if (target_alloc_working_area(target, chunk_size, &bounce) != ERROR_OK) {
184 LOG_ERROR("Could not allocate bounce buffer for flash programming. Can't continue");
185 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
186 }
187
188 LOG_DEBUG("Allocated flash bounce buffer @" TARGET_ADDR_FMT, bounce->address);
189
190 while (count > 0) {
191 uint32_t write_size = count > chunk_size ? chunk_size : count;
192 LOG_DEBUG("Writing %d bytes to offset 0x%" PRIx32, write_size, offset);
193 err = target_write_buffer(target, bounce->address, write_size, buffer);
194 if (err != ERROR_OK) {
195 LOG_ERROR("Could not load data into target bounce buffer");
196 break;
197 }
198 uint32_t args[3] = {
199 offset, /* addr */
200 bounce->address, /* data */
201 write_size /* count */
202 };
203 err = rp2040_call_rom_func(target, priv, priv->jump_flash_range_program, args, ARRAY_SIZE(args));
204 if (err != ERROR_OK) {
205 LOG_ERROR("Failed to invoke flash programming code on target");
206 break;
207 }
208
209 buffer += write_size;
210 offset += write_size;
211 count -= write_size;
212 }
213 target_free_working_area(target, bounce);
214
215 if (err != ERROR_OK)
216 return err;
217
218 /* Flash is successfully programmed. We can now do a bit of poking to make the flash
219 contents visible to us via memory-mapped (XIP) interface in the 0x1... memory region */
220 LOG_DEBUG("Flushing flash cache after write behind");
221 err = rp2040_call_rom_func(bank->target, priv, priv->jump_flush_cache, NULL, 0);
222 if (err != ERROR_OK) {
223 LOG_ERROR("RP2040 write: failed to flush flash cache");
224 return err;
225 }
226 LOG_DEBUG("Configuring SSI for execute-in-place");
227 err = rp2040_call_rom_func(bank->target, priv, priv->jump_enter_cmd_xip, NULL, 0);
228 if (err != ERROR_OK)
229 LOG_ERROR("RP2040 write: failed to flush flash cache");
230 return err;
231 }
232
233 static int rp2040_flash_erase(struct flash_bank *bank, unsigned int first, unsigned int last)
234 {
235 struct rp2040_flash_bank *priv = bank->driver_priv;
236 uint32_t start_addr = bank->sectors[first].offset;
237 uint32_t length = bank->sectors[last].offset + bank->sectors[last].size - start_addr;
238 LOG_DEBUG("RP2040 erase %d bytes starting at 0x%" PRIx32, length, start_addr);
239
240 int err = stack_grab_and_prep(bank);
241 if (err != ERROR_OK)
242 return err;
243
244 LOG_DEBUG("Remote call flash_range_erase");
245
246 uint32_t args[4] = {
247 bank->sectors[first].offset, /* addr */
248 bank->sectors[last].offset + bank->sectors[last].size - bank->sectors[first].offset, /* count */
249 priv->dev->sectorsize, /* block_size */
250 priv->dev->erase_cmd /* block_cmd */
251 };
252
253 /*
254 The RP2040 Boot ROM provides a _flash_range_erase() API call documented in Section 2.8.3.1.3:
255 https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf
256 and the particular source code for said Boot ROM function can be found here:
257 https://github.com/raspberrypi/pico-bootrom/blob/master/bootrom/program_flash_generic.c
258
259 In theory, the function algorithm provides for erasing both a smaller "sector" (4096 bytes) and
260 an optional larger "block" (size and command provided in args). OpenOCD's spi.c only uses "block" sizes.
261 */
262
263 err = rp2040_call_rom_func(bank->target, priv, priv->jump_flash_range_erase, args, ARRAY_SIZE(args));
264
265 return err;
266 }
267
268 /* -----------------------------------------------------------------------------
269 Driver probing etc */
270
271 static int rp2040_ssel_active(struct target *target, bool active)
272 {
273 const target_addr_t qspi_ctrl_addr = 0x4001800c;
274 const uint32_t qspi_ctrl_outover_low = 2UL << 8;
275 const uint32_t qspi_ctrl_outover_high = 3UL << 8;
276 uint32_t state = (active) ? qspi_ctrl_outover_low : qspi_ctrl_outover_high;
277 uint32_t val;
278
279 int err = target_read_u32(target, qspi_ctrl_addr, &val);
280 if (err != ERROR_OK)
281 return err;
282
283 val = (val & ~qspi_ctrl_outover_high) | state;
284
285 err = target_write_u32(target, qspi_ctrl_addr, val);
286 if (err != ERROR_OK)
287 return err;
288
289 return ERROR_OK;
290 }
291
292 static int rp2040_flash_probe(struct flash_bank *bank)
293 {
294 struct rp2040_flash_bank *priv = bank->driver_priv;
295 struct target *target = bank->target;
296
297 int err = rp2040_lookup_symbol(target, FUNC_DEBUG_TRAMPOLINE, &priv->jump_debug_trampoline);
298 if (err != ERROR_OK) {
299 LOG_ERROR("Debug trampoline not found in RP2040 ROM.");
300 return err;
301 }
302 priv->jump_debug_trampoline &= ~1u; /* mask off thumb bit */
303
304 err = rp2040_lookup_symbol(target, FUNC_DEBUG_TRAMPOLINE_END, &priv->jump_debug_trampoline_end);
305 if (err != ERROR_OK) {
306 LOG_ERROR("Debug trampoline end not found in RP2040 ROM.");
307 return err;
308 }
309 priv->jump_debug_trampoline_end &= ~1u; /* mask off thumb bit */
310
311 err = rp2040_lookup_symbol(target, FUNC_FLASH_EXIT_XIP, &priv->jump_flash_exit_xip);
312 if (err != ERROR_OK) {
313 LOG_ERROR("Function FUNC_FLASH_EXIT_XIP not found in RP2040 ROM.");
314 return err;
315 }
316
317 err = rp2040_lookup_symbol(target, FUNC_CONNECT_INTERNAL_FLASH, &priv->jump_connect_internal_flash);
318 if (err != ERROR_OK) {
319 LOG_ERROR("Function FUNC_CONNECT_INTERNAL_FLASH not found in RP2040 ROM.");
320 return err;
321 }
322
323 err = rp2040_lookup_symbol(target, FUNC_FLASH_RANGE_ERASE, &priv->jump_flash_range_erase);
324 if (err != ERROR_OK) {
325 LOG_ERROR("Function FUNC_FLASH_RANGE_ERASE not found in RP2040 ROM.");
326 return err;
327 }
328
329 err = rp2040_lookup_symbol(target, FUNC_FLASH_RANGE_PROGRAM, &priv->jump_flash_range_program);
330 if (err != ERROR_OK) {
331 LOG_ERROR("Function FUNC_FLASH_RANGE_PROGRAM not found in RP2040 ROM.");
332 return err;
333 }
334
335 err = rp2040_lookup_symbol(target, FUNC_FLASH_FLUSH_CACHE, &priv->jump_flush_cache);
336 if (err != ERROR_OK) {
337 LOG_ERROR("Function FUNC_FLASH_FLUSH_CACHE not found in RP2040 ROM.");
338 return err;
339 }
340
341 err = rp2040_lookup_symbol(target, FUNC_FLASH_ENTER_CMD_XIP, &priv->jump_enter_cmd_xip);
342 if (err != ERROR_OK) {
343 LOG_ERROR("Function FUNC_FLASH_ENTER_CMD_XIP not found in RP2040 ROM.");
344 return err;
345 }
346
347 err = stack_grab_and_prep(bank);
348 if (err != ERROR_OK)
349 return err;
350
351 uint32_t device_id = 0;
352 const target_addr_t ssi_dr0 = 0x18000060;
353
354 err = rp2040_ssel_active(target, true);
355
356 /* write RDID request into SPI peripheral's FIFO */
357 for (int count = 0; (count < 4) && (err == ERROR_OK); count++)
358 err = target_write_u32(target, ssi_dr0, SPIFLASH_READ_ID);
359
360 /* by this time, there is a receive FIFO entry for every write */
361 for (int count = 0; (count < 4) && (err == ERROR_OK); count++) {
362 uint32_t status;
363 err = target_read_u32(target, ssi_dr0, &status);
364
365 device_id >>= 8;
366 device_id |= (status & 0xFF) << 24;
367 }
368 device_id >>= 8;
369
370 err = rp2040_ssel_active(target, false);
371 if (err != ERROR_OK) {
372 LOG_ERROR("SSEL inactive failed");
373 return err;
374 }
375
376 /* search for a SPI flash Device ID match */
377 priv->dev = NULL;
378 for (const struct flash_device *p = flash_devices; p->name ; p++)
379 if (p->device_id == device_id) {
380 priv->dev = p;
381 break;
382 }
383
384 if (!priv->dev) {
385 LOG_ERROR("Unknown flash device (ID 0x%08" PRIx32 ")", device_id);
386 return ERROR_FAIL;
387 }
388
389 LOG_INFO("Found flash device \'%s\' (ID 0x%08" PRIx32 ")",
390 priv->dev->name, priv->dev->device_id);
391
392 /* the Boot ROM flash_range_program() routine requires page alignment */
393 bank->write_start_alignment = priv->dev->pagesize;
394 bank->write_end_alignment = priv->dev->pagesize;
395
396 bank->size = priv->dev->size_in_bytes;
397
398 bank->num_sectors = bank->size / priv->dev->sectorsize;
399 LOG_INFO("RP2040 B0 Flash Probe: %d bytes @" TARGET_ADDR_FMT ", in %d sectors\n",
400 bank->size, bank->base, bank->num_sectors);
401 bank->sectors = alloc_block_array(0, priv->dev->sectorsize, bank->num_sectors);
402 if (!bank->sectors)
403 return ERROR_FAIL;
404
405 if (err == ERROR_OK)
406 priv->probed = true;
407
408 return err;
409 }
410
411 static int rp2040_flash_auto_probe(struct flash_bank *bank)
412 {
413 struct rp2040_flash_bank *priv = bank->driver_priv;
414
415 if (priv->probed)
416 return ERROR_OK;
417
418 return rp2040_flash_probe(bank);
419 }
420
421 static void rp2040_flash_free_driver_priv(struct flash_bank *bank)
422 {
423 free(bank->driver_priv);
424 bank->driver_priv = NULL;
425 }
426
427 /* -----------------------------------------------------------------------------
428 Driver boilerplate */
429
430 FLASH_BANK_COMMAND_HANDLER(rp2040_flash_bank_command)
431 {
432 struct rp2040_flash_bank *priv;
433 priv = malloc(sizeof(struct rp2040_flash_bank));
434 priv->probed = false;
435
436 /* Set up driver_priv */
437 bank->driver_priv = priv;
438
439 return ERROR_OK;
440 }
441
442 struct flash_driver rp2040_flash = {
443 .name = "rp2040_flash",
444 .flash_bank_command = rp2040_flash_bank_command,
445 .erase = rp2040_flash_erase,
446 .write = rp2040_flash_write,
447 .read = default_flash_read,
448 .probe = rp2040_flash_probe,
449 .auto_probe = rp2040_flash_auto_probe,
450 .erase_check = default_flash_blank_check,
451 .free_driver_priv = rp2040_flash_free_driver_priv
452 };

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)