flash/nor/rp2040: preparatory refactoring
[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 rp2040_stack_grab_and_prep(struct flash_bank *bank)
142 {
143 struct rp2040_flash_bank *priv = bank->driver_priv;
144 struct target *target = bank->target;
145
146 /* target_alloc_working_area always allocates multiples of 4 bytes, so no worry about alignment */
147 const int STACK_SIZE = 256;
148 int err = target_alloc_working_area(target, STACK_SIZE, &priv->stack);
149 if (err != ERROR_OK) {
150 LOG_ERROR("Could not allocate stack for flash programming code");
151 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
152 }
153
154 LOG_DEBUG("Connecting internal flash");
155 err = rp2040_call_rom_func(target, priv, priv->jump_connect_internal_flash, NULL, 0);
156 if (err != ERROR_OK) {
157 LOG_ERROR("RP2040 erase: failed to connect internal flash");
158 return err;
159 }
160
161 LOG_DEBUG("Kicking flash out of XIP mode");
162 err = rp2040_call_rom_func(target, priv, priv->jump_flash_exit_xip, NULL, 0);
163 if (err != ERROR_OK) {
164 LOG_ERROR("RP2040 erase: failed to exit flash XIP mode");
165 return err;
166 }
167
168 return ERROR_OK;
169 }
170
171 static int rp2040_flash_write(struct flash_bank *bank, const uint8_t *buffer, uint32_t offset, uint32_t count)
172 {
173 LOG_DEBUG("Writing %d bytes starting at 0x%" PRIx32, count, offset);
174
175 struct rp2040_flash_bank *priv = bank->driver_priv;
176 struct target *target = bank->target;
177 struct working_area *bounce;
178
179 int err = rp2040_stack_grab_and_prep(bank);
180 if (err != ERROR_OK)
181 return err;
182
183 const unsigned int chunk_size = target_get_working_area_avail(target);
184 if (target_alloc_working_area(target, chunk_size, &bounce) != ERROR_OK) {
185 LOG_ERROR("Could not allocate bounce buffer for flash programming. Can't continue");
186 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
187 }
188
189 LOG_DEBUG("Allocated flash bounce buffer @" TARGET_ADDR_FMT, bounce->address);
190
191 while (count > 0) {
192 uint32_t write_size = count > chunk_size ? chunk_size : count;
193 LOG_DEBUG("Writing %d bytes to offset 0x%" PRIx32, write_size, offset);
194 err = target_write_buffer(target, bounce->address, write_size, buffer);
195 if (err != ERROR_OK) {
196 LOG_ERROR("Could not load data into target bounce buffer");
197 break;
198 }
199 uint32_t args[3] = {
200 offset, /* addr */
201 bounce->address, /* data */
202 write_size /* count */
203 };
204 err = rp2040_call_rom_func(target, priv, priv->jump_flash_range_program, args, ARRAY_SIZE(args));
205 if (err != ERROR_OK) {
206 LOG_ERROR("Failed to invoke flash programming code on target");
207 break;
208 }
209
210 buffer += write_size;
211 offset += write_size;
212 count -= write_size;
213 }
214 target_free_working_area(target, bounce);
215
216 if (err != ERROR_OK)
217 return err;
218
219 /* Flash is successfully programmed. We can now do a bit of poking to make the flash
220 contents visible to us via memory-mapped (XIP) interface in the 0x1... memory region */
221 LOG_DEBUG("Flushing flash cache after write behind");
222 err = rp2040_call_rom_func(bank->target, priv, priv->jump_flush_cache, NULL, 0);
223 if (err != ERROR_OK) {
224 LOG_ERROR("RP2040 write: failed to flush flash cache");
225 return err;
226 }
227 LOG_DEBUG("Configuring SSI for execute-in-place");
228 err = rp2040_call_rom_func(bank->target, priv, priv->jump_enter_cmd_xip, NULL, 0);
229 if (err != ERROR_OK)
230 LOG_ERROR("RP2040 write: failed to flush flash cache");
231 return err;
232 }
233
234 static int rp2040_flash_erase(struct flash_bank *bank, unsigned int first, unsigned int last)
235 {
236 struct rp2040_flash_bank *priv = bank->driver_priv;
237 uint32_t start_addr = bank->sectors[first].offset;
238 uint32_t length = bank->sectors[last].offset + bank->sectors[last].size - start_addr;
239 LOG_DEBUG("RP2040 erase %d bytes starting at 0x%" PRIx32, length, start_addr);
240
241 int err = rp2040_stack_grab_and_prep(bank);
242 if (err != ERROR_OK)
243 return err;
244
245 LOG_DEBUG("Remote call flash_range_erase");
246
247 uint32_t args[4] = {
248 bank->sectors[first].offset, /* addr */
249 bank->sectors[last].offset + bank->sectors[last].size - bank->sectors[first].offset, /* count */
250 priv->dev->sectorsize, /* block_size */
251 priv->dev->erase_cmd /* block_cmd */
252 };
253
254 /*
255 The RP2040 Boot ROM provides a _flash_range_erase() API call documented in Section 2.8.3.1.3:
256 https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf
257 and the particular source code for said Boot ROM function can be found here:
258 https://github.com/raspberrypi/pico-bootrom/blob/master/bootrom/program_flash_generic.c
259
260 In theory, the function algorithm provides for erasing both a smaller "sector" (4096 bytes) and
261 an optional larger "block" (size and command provided in args). OpenOCD's spi.c only uses "block" sizes.
262 */
263
264 err = rp2040_call_rom_func(bank->target, priv, priv->jump_flash_range_erase, args, ARRAY_SIZE(args));
265
266 return err;
267 }
268
269 /* -----------------------------------------------------------------------------
270 Driver probing etc */
271
272 static int rp2040_ssel_active(struct target *target, bool active)
273 {
274 const target_addr_t qspi_ctrl_addr = 0x4001800c;
275 const uint32_t qspi_ctrl_outover_low = 2UL << 8;
276 const uint32_t qspi_ctrl_outover_high = 3UL << 8;
277 uint32_t state = (active) ? qspi_ctrl_outover_low : qspi_ctrl_outover_high;
278 uint32_t val;
279
280 int err = target_read_u32(target, qspi_ctrl_addr, &val);
281 if (err != ERROR_OK)
282 return err;
283
284 val = (val & ~qspi_ctrl_outover_high) | state;
285
286 err = target_write_u32(target, qspi_ctrl_addr, val);
287 if (err != ERROR_OK)
288 return err;
289
290 return ERROR_OK;
291 }
292
293 static int rp2040_spi_read_flash_id(struct target *target, uint32_t *devid)
294 {
295 uint32_t device_id = 0;
296 const target_addr_t ssi_dr0 = 0x18000060;
297
298 int err = rp2040_ssel_active(target, true);
299
300 /* write RDID request into SPI peripheral's FIFO */
301 for (int count = 0; (count < 4) && (err == ERROR_OK); count++)
302 err = target_write_u32(target, ssi_dr0, SPIFLASH_READ_ID);
303
304 /* by this time, there is a receive FIFO entry for every write */
305 for (int count = 0; (count < 4) && (err == ERROR_OK); count++) {
306 uint32_t status;
307 err = target_read_u32(target, ssi_dr0, &status);
308
309 device_id >>= 8;
310 device_id |= (status & 0xFF) << 24;
311 }
312
313 if (err == ERROR_OK)
314 *devid = device_id >> 8;
315
316 int err2 = rp2040_ssel_active(target, false);
317 if (err2 != ERROR_OK)
318 LOG_ERROR("SSEL inactive failed");
319
320 return err;
321 }
322
323 static int rp2040_flash_probe(struct flash_bank *bank)
324 {
325 struct rp2040_flash_bank *priv = bank->driver_priv;
326 struct target *target = bank->target;
327
328 int err = rp2040_lookup_symbol(target, FUNC_DEBUG_TRAMPOLINE, &priv->jump_debug_trampoline);
329 if (err != ERROR_OK) {
330 LOG_ERROR("Debug trampoline not found in RP2040 ROM.");
331 return err;
332 }
333 priv->jump_debug_trampoline &= ~1u; /* mask off thumb bit */
334
335 err = rp2040_lookup_symbol(target, FUNC_DEBUG_TRAMPOLINE_END, &priv->jump_debug_trampoline_end);
336 if (err != ERROR_OK) {
337 LOG_ERROR("Debug trampoline end not found in RP2040 ROM.");
338 return err;
339 }
340 priv->jump_debug_trampoline_end &= ~1u; /* mask off thumb bit */
341
342 err = rp2040_lookup_symbol(target, FUNC_FLASH_EXIT_XIP, &priv->jump_flash_exit_xip);
343 if (err != ERROR_OK) {
344 LOG_ERROR("Function FUNC_FLASH_EXIT_XIP not found in RP2040 ROM.");
345 return err;
346 }
347
348 err = rp2040_lookup_symbol(target, FUNC_CONNECT_INTERNAL_FLASH, &priv->jump_connect_internal_flash);
349 if (err != ERROR_OK) {
350 LOG_ERROR("Function FUNC_CONNECT_INTERNAL_FLASH not found in RP2040 ROM.");
351 return err;
352 }
353
354 err = rp2040_lookup_symbol(target, FUNC_FLASH_RANGE_ERASE, &priv->jump_flash_range_erase);
355 if (err != ERROR_OK) {
356 LOG_ERROR("Function FUNC_FLASH_RANGE_ERASE not found in RP2040 ROM.");
357 return err;
358 }
359
360 err = rp2040_lookup_symbol(target, FUNC_FLASH_RANGE_PROGRAM, &priv->jump_flash_range_program);
361 if (err != ERROR_OK) {
362 LOG_ERROR("Function FUNC_FLASH_RANGE_PROGRAM not found in RP2040 ROM.");
363 return err;
364 }
365
366 err = rp2040_lookup_symbol(target, FUNC_FLASH_FLUSH_CACHE, &priv->jump_flush_cache);
367 if (err != ERROR_OK) {
368 LOG_ERROR("Function FUNC_FLASH_FLUSH_CACHE not found in RP2040 ROM.");
369 return err;
370 }
371
372 err = rp2040_lookup_symbol(target, FUNC_FLASH_ENTER_CMD_XIP, &priv->jump_enter_cmd_xip);
373 if (err != ERROR_OK) {
374 LOG_ERROR("Function FUNC_FLASH_ENTER_CMD_XIP not found in RP2040 ROM.");
375 return err;
376 }
377
378 err = rp2040_stack_grab_and_prep(bank);
379 if (err != ERROR_OK)
380 return err;
381
382 uint32_t device_id = 0;
383 err = rp2040_spi_read_flash_id(target, &device_id);
384 if (err != ERROR_OK)
385 return err;
386
387 /* search for a SPI flash Device ID match */
388 priv->dev = NULL;
389 for (const struct flash_device *p = flash_devices; p->name ; p++)
390 if (p->device_id == device_id) {
391 priv->dev = p;
392 break;
393 }
394
395 if (!priv->dev) {
396 LOG_ERROR("Unknown flash device (ID 0x%08" PRIx32 ")", device_id);
397 return ERROR_FAIL;
398 }
399
400 LOG_INFO("Found flash device \'%s\' (ID 0x%08" PRIx32 ")",
401 priv->dev->name, priv->dev->device_id);
402
403 /* the Boot ROM flash_range_program() routine requires page alignment */
404 bank->write_start_alignment = priv->dev->pagesize;
405 bank->write_end_alignment = priv->dev->pagesize;
406
407 bank->size = priv->dev->size_in_bytes;
408
409 bank->num_sectors = bank->size / priv->dev->sectorsize;
410 LOG_INFO("RP2040 B0 Flash Probe: %d bytes @" TARGET_ADDR_FMT ", in %d sectors\n",
411 bank->size, bank->base, bank->num_sectors);
412 bank->sectors = alloc_block_array(0, priv->dev->sectorsize, bank->num_sectors);
413 if (!bank->sectors)
414 return ERROR_FAIL;
415
416 if (err == ERROR_OK)
417 priv->probed = true;
418
419 return err;
420 }
421
422 static int rp2040_flash_auto_probe(struct flash_bank *bank)
423 {
424 struct rp2040_flash_bank *priv = bank->driver_priv;
425
426 if (priv->probed)
427 return ERROR_OK;
428
429 return rp2040_flash_probe(bank);
430 }
431
432 static void rp2040_flash_free_driver_priv(struct flash_bank *bank)
433 {
434 free(bank->driver_priv);
435 bank->driver_priv = NULL;
436 }
437
438 /* -----------------------------------------------------------------------------
439 Driver boilerplate */
440
441 FLASH_BANK_COMMAND_HANDLER(rp2040_flash_bank_command)
442 {
443 struct rp2040_flash_bank *priv;
444 priv = malloc(sizeof(struct rp2040_flash_bank));
445 priv->probed = false;
446
447 /* Set up driver_priv */
448 bank->driver_priv = priv;
449
450 return ERROR_OK;
451 }
452
453 struct flash_driver rp2040_flash = {
454 .name = "rp2040_flash",
455 .flash_bank_command = rp2040_flash_bank_command,
456 .erase = rp2040_flash_erase,
457 .write = rp2040_flash_write,
458 .read = default_flash_read,
459 .probe = rp2040_flash_probe,
460 .auto_probe = rp2040_flash_auto_probe,
461 .erase_check = default_flash_blank_check,
462 .free_driver_priv = rp2040_flash_free_driver_priv
463 };

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)