stm32l4x: Fix stm32l4x dual bank support
[openocd.git] / src / flash / nor / aducm360.c
1 /***************************************************************************
2 * Copyright (C) 2015 by Ivan Buliev *
3 * i.buliev@mikrosistemi.com *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
17 ***************************************************************************/
18
19 /***************************************************************************
20 * This version for ADuCM360 is largely based on the following flash *
21 * drivers: *
22 * - aduc702x.c *
23 * Copyright (C) 2008 by Kevin McGuire *
24 * Copyright (C) 2008 by Marcel Wijlaars *
25 * Copyright (C) 2009 by Michael Ashton *
26 * and *
27 * - stm32f1x.c *
28 * Copyright (C) 2005 by Dominic Rath *
29 * Dominic.Rath@gmx.de *
30 * *
31 * Copyright (C) 2008 by Spencer Oliver *
32 * spen@spen-soft.co.uk *
33 * *
34 * Copyright (C) 2011 by Andreas Fritiofson *
35 * andreas.fritiofson@gmail.com *
36 ***************************************************************************/
37
38 #ifdef HAVE_CONFIG_H
39 #include "config.h"
40 #endif
41
42 #include "imp.h"
43 #include <helper/binarybuffer.h>
44 #include <helper/time_support.h>
45 #include <target/algorithm.h>
46 #include <target/armv7m.h>
47
48 static int aducm360_build_sector_list(struct flash_bank *bank);
49 static int aducm360_check_flash_completion(struct target *target, unsigned int timeout_ms);
50 static int aducm360_set_write_enable(struct target *target, int enable);
51
52 #define ADUCM360_FLASH_BASE 0x40002800
53 #define ADUCM360_FLASH_FEESTA 0x0000
54 #define ADUCM360_FLASH_FEECON0 0x0004
55 #define ADUCM360_FLASH_FEECMD 0x0008
56 #define ADUCM360_FLASH_FEEADR0L 0x0010
57 #define ADUCM360_FLASH_FEEADR0H 0x0014
58 #define ADUCM360_FLASH_FEEADR1L 0x0018
59 #define ADUCM360_FLASH_FEEADR1H 0x001C
60 #define ADUCM360_FLASH_FEEKEY 0x0020
61 #define ADUCM360_FLASH_FEEPROL 0x0028
62 #define ADUCM360_FLASH_FEEPROH 0x002C
63 #define ADUCM360_FLASH_FEESIGL 0x0030
64 #define ADUCM360_FLASH_FEESIGH 0x0034
65 #define ADUCM360_FLASH_FEECON1 0x0038
66 #define ADUCM360_FLASH_FEEADRAL 0x0048
67 #define ADUCM360_FLASH_FEEADRAH 0x004C
68 #define ADUCM360_FLASH_FEEAEN0 0x0078
69 #define ADUCM360_FLASH_FEEAEN1 0x007C
70 #define ADUCM360_FLASH_FEEAEN2 0x0080
71
72 /* flash bank aducm360 0 0 0 0 <target#> */
73 FLASH_BANK_COMMAND_HANDLER(aducm360_flash_bank_command)
74 {
75 bank->base = 0x00000000;
76 bank->size = 0x00020000;
77
78 aducm360_build_sector_list(bank);
79
80 return ERROR_OK;
81 }
82
83 #define FLASH_SECTOR_SIZE 512
84
85 /* ----------------------------------------------------------------------- */
86 static int aducm360_build_sector_list(struct flash_bank *bank)
87 {
88 int i = 0;
89 uint32_t offset = 0;
90
91 /* sector size is 512 */
92 bank->num_sectors = bank->size / FLASH_SECTOR_SIZE;
93 bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors);
94 for (i = 0; i < bank->num_sectors; ++i) {
95 bank->sectors[i].offset = offset;
96 bank->sectors[i].size = FLASH_SECTOR_SIZE;
97 offset += bank->sectors[i].size;
98 bank->sectors[i].is_erased = -1;
99 bank->sectors[i].is_protected = 0;
100 }
101
102 return ERROR_OK;
103 }
104
105 /* ----------------------------------------------------------------------- */
106 static int aducm360_protect_check(struct flash_bank *bank)
107 {
108 LOG_WARNING("aducm360_protect_check not implemented.");
109 return ERROR_OK;
110 }
111
112 /* ----------------------------------------------------------------------- */
113 static int aducm360_mass_erase(struct target *target)
114 {
115 uint32_t value;
116 int res = ERROR_OK;
117
118 /* Clear any old status */
119 target_read_u32(target, ADUCM360_FLASH_BASE + ADUCM360_FLASH_FEESTA, &value);
120
121 /* Enable the writing to the flash*/
122 aducm360_set_write_enable(target, 1);
123
124 /* Unlock for writing */
125 target_write_u32(target, ADUCM360_FLASH_BASE+ADUCM360_FLASH_FEEKEY, 0x0000F456);
126 target_write_u32(target, ADUCM360_FLASH_BASE+ADUCM360_FLASH_FEEKEY, 0x0000F123);
127 /* Issue the 'MASSERASE' command */
128 target_write_u32(target, ADUCM360_FLASH_BASE+ADUCM360_FLASH_FEECMD, 0x00000003);
129
130 /* Check the result */
131 res = aducm360_check_flash_completion(target, 3500);
132 if (res != ERROR_OK) {
133 LOG_ERROR("mass erase failed.");
134 aducm360_set_write_enable(target, 0);
135 res = ERROR_FLASH_OPERATION_FAILED;
136 }
137
138 return res;
139 }
140
141 /* ----------------------------------------------------------------------- */
142 static int aducm360_page_erase(struct target *target, uint32_t padd)
143 {
144 uint32_t value;
145 int res = ERROR_OK;
146
147 /* Clear any old status */
148 target_read_u32(target, ADUCM360_FLASH_BASE + ADUCM360_FLASH_FEESTA, &value);
149
150 /* Enable the writing to the flash*/
151 aducm360_set_write_enable(target, 1);
152
153 /* Unlock for writing */
154 target_write_u32(target, ADUCM360_FLASH_BASE+ADUCM360_FLASH_FEEKEY, 0x0000F456);
155 target_write_u32(target, ADUCM360_FLASH_BASE+ADUCM360_FLASH_FEEKEY, 0x0000F123);
156 /* Write the sector address */
157 target_write_u32(target, ADUCM360_FLASH_BASE+ADUCM360_FLASH_FEEADR0L, padd & 0xFFFF);
158 target_write_u32(target, ADUCM360_FLASH_BASE+ADUCM360_FLASH_FEEADR0H, (padd>>16) & 0xFFFF);
159 /* Issue the 'ERASEPAGE' command */
160 target_write_u32(target, ADUCM360_FLASH_BASE+ADUCM360_FLASH_FEECMD, 0x00000001);
161
162 /* Check the result */
163 res = aducm360_check_flash_completion(target, 50);
164 if (res != ERROR_OK) {
165 LOG_ERROR("page erase failed at 0x%08" PRIx32, padd);
166 aducm360_set_write_enable(target, 0);
167 res = ERROR_FLASH_OPERATION_FAILED;
168 }
169
170 return res;
171 }
172
173 /* ----------------------------------------------------------------------- */
174 static int aducm360_erase(struct flash_bank *bank, int first, int last)
175 {
176 int res = ERROR_OK;
177 int i;
178 int count;
179 struct target *target = bank->target;
180 uint32_t padd;
181
182 if (((first | last) == 0) || ((first == 0) && (last >= bank->num_sectors))) {
183 res = aducm360_mass_erase(target);
184 } else {
185 count = last - first + 1;
186 for (i = 0; i < count; ++i) {
187 padd = bank->base + ((first+i)*FLASH_SECTOR_SIZE);
188 res = aducm360_page_erase(target, padd);
189 if (res != ERROR_OK)
190 break;
191 }
192 }
193
194 return res;
195 }
196
197 /* ----------------------------------------------------------------------- */
198 static int aducm360_protect(struct flash_bank *bank, int set, int first, int last)
199 {
200 LOG_ERROR("aducm360_protect not implemented.");
201 return ERROR_FLASH_OPERATION_FAILED;
202 }
203
204 /* ----------------------------------------------------------------------- */
205 static int aducm360_write_block_sync(
206 struct flash_bank *bank,
207 const uint8_t *buffer,
208 uint32_t offset,
209 uint32_t count)
210 {
211 struct target *target = bank->target;
212 uint32_t target_buffer_size = 8192;
213 struct working_area *helper;
214 struct working_area *target_buffer;
215 uint32_t address = bank->base + offset;
216 struct reg_param reg_params[8];
217 int retval = ERROR_OK;
218 uint32_t entry_point = 0, exit_point = 0;
219 uint32_t res;
220 struct armv7m_algorithm armv7m_algo;
221
222 static const uint32_t aducm360_flash_write_code[] = {
223 /* helper.code */
224 0x88AF4D10, 0x0704F047, 0x682F80AF, 0x600E6806,
225 0xF017882F, 0xF43F0F08, 0xF851AFFB, 0x42B77B04,
226 0x800DF040, 0x0004F100, 0xF47F3A04, 0x686FAFEF,
227 0x0704F027, 0xF04F80AF, 0xF0000400, 0xF04FB802,
228 0xBE000480, 0x40002800, 0x00015000, 0x20000000,
229 0x00013000
230 };
231
232 LOG_DEBUG("'aducm360_write_block_sync' requested, dst:0x%08" PRIx32 ", count:0x%08" PRIx32 "bytes.",
233 address, count);
234
235 /* ----- Check the destination area for a Long Word alignment ----- */
236 if (((count%4) != 0) || ((offset%4) != 0)) {
237 LOG_ERROR("write block must be multiple of four bytes in offset & length");
238 return ERROR_FAIL;
239 }
240
241 /* ----- Allocate space in the target's RAM for the helper code ----- */
242 if (target_alloc_working_area(target, sizeof(aducm360_flash_write_code),
243 &helper) != ERROR_OK) {
244 LOG_WARNING("no working area available, can't do block memory writes");
245 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
246 }
247
248 /* ----- Upload the helper code to the space in the target's RAM ----- */
249 uint8_t code[sizeof(aducm360_flash_write_code)];
250 target_buffer_set_u32_array(target, code, ARRAY_SIZE(aducm360_flash_write_code),
251 aducm360_flash_write_code);
252 retval = target_write_buffer(target, helper->address, sizeof(code), code);
253 if (retval != ERROR_OK)
254 return retval;
255 entry_point = helper->address;
256
257 /* ----- Allocate space in the target's RAM for the user application's object code ----- */
258 while (target_alloc_working_area_try(target, target_buffer_size, &target_buffer) != ERROR_OK) {
259 LOG_WARNING("couldn't allocate a buffer space of 0x%08" PRIx32 "bytes in the target's SRAM.",
260 target_buffer_size);
261 target_buffer_size /= 2;
262 if (target_buffer_size <= 256) { /* No room available */
263 LOG_WARNING("no large enough working area available, can't do block memory writes");
264 target_free_working_area(target, helper);
265 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
266 }
267 }
268
269 /* ----- Prepare the target for the helper ----- */
270 armv7m_algo.common_magic = ARMV7M_COMMON_MAGIC;
271 armv7m_algo.core_mode = ARM_MODE_THREAD;
272
273 init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT); /*SRC */
274 init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT); /*DST */
275 init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT); /*COUNT */
276 init_reg_param(&reg_params[3], "r3", 32, PARAM_OUT); /*not used */
277 init_reg_param(&reg_params[4], "r4", 32, PARAM_IN); /*RESULT */
278
279 /* ===== Execute the Main Programming Loop! ===== */
280 while (count > 0) {
281 uint32_t thisrun_count = (count > target_buffer_size) ? target_buffer_size : count;
282
283 /* ----- Upload the chunk ----- */
284 retval = target_write_buffer(target, target_buffer->address, thisrun_count, buffer);
285 if (retval != ERROR_OK)
286 break;
287 /* Set the arguments for the helper */
288 buf_set_u32(reg_params[0].value, 0, 32, target_buffer->address); /*SRC */
289 buf_set_u32(reg_params[1].value, 0, 32, address); /*DST */
290 buf_set_u32(reg_params[2].value, 0, 32, thisrun_count); /*COUNT */
291 buf_set_u32(reg_params[3].value, 0, 32, 0); /*NOT USED*/
292
293 retval = target_run_algorithm(target, 0, NULL, 5,
294 reg_params, entry_point, exit_point, 10000, &armv7m_algo);
295 if (retval != ERROR_OK) {
296 LOG_ERROR("error executing aducm360 flash write algorithm");
297 break;
298 }
299
300 res = buf_get_u32(reg_params[4].value, 0, 32);
301 if (res) {
302 LOG_ERROR("aducm360 fast sync algorithm reports an error (%02X)", res);
303 retval = ERROR_FAIL;
304 break;
305 }
306
307 buffer += thisrun_count;
308 address += thisrun_count;
309 count -= thisrun_count;
310 }
311
312 target_free_working_area(target, target_buffer);
313 target_free_working_area(target, helper);
314
315 destroy_reg_param(&reg_params[0]);
316 destroy_reg_param(&reg_params[1]);
317 destroy_reg_param(&reg_params[2]);
318 destroy_reg_param(&reg_params[3]);
319 destroy_reg_param(&reg_params[4]);
320
321 return retval;
322 }
323
324 /* ----------------------------------------------------------------------- */
325 static int aducm360_write_block_async(
326 struct flash_bank *bank,
327 const uint8_t *buffer,
328 uint32_t offset,
329 uint32_t count)
330 {
331 struct target *target = bank->target;
332 uint32_t target_buffer_size = 1024;
333 struct working_area *helper;
334 struct working_area *target_buffer;
335 uint32_t address = bank->base + offset;
336 struct reg_param reg_params[9];
337 int retval = ERROR_OK;
338 uint32_t entry_point = 0, exit_point = 0;
339 uint32_t res;
340 uint32_t wcount;
341 struct armv7m_algorithm armv7m_algo;
342
343 static const uint32_t aducm360_flash_write_code[] = {
344 /* helper.code */
345 0x4050F8DF, 0xF04588A5, 0x80A50504, 0x8000F8D0,
346 0x0F00F1B8, 0x8016F000, 0x45476847, 0xAFF6F43F,
347 0x6B04F857, 0x6B04F842, 0xF0158825, 0xF43F0F08,
348 0x428FAFFB, 0xF100BF28, 0x60470708, 0xB10B3B01,
349 0xBFE4F7FF, 0xF02588A5, 0x80A50504, 0x0900F04F,
350 0xBE00BF00, 0x40002800, 0x20000000, 0x20000100,
351 0x00013000
352 };
353
354 LOG_DEBUG("'aducm360_write_block_async' requested, dst:0x%08" PRIx32 ", count:0x%08" PRIx32 "bytes.",
355 address, count);
356
357 /* ----- Check the destination area for a Long Word alignment ----- */
358 if (((count%4) != 0) || ((offset%4) != 0)) {
359 LOG_ERROR("write block must be multiple of four bytes in offset & length");
360 return ERROR_FAIL;
361 }
362 wcount = count/4;
363
364 /* ----- Allocate space in the target's RAM for the helper code ----- */
365 if (target_alloc_working_area(target, sizeof(aducm360_flash_write_code),
366 &helper) != ERROR_OK) {
367 LOG_WARNING("no working area available, can't do block memory writes");
368 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
369 }
370
371 /* ----- Upload the helper code to the space in the target's RAM ----- */
372 uint8_t code[sizeof(aducm360_flash_write_code)];
373 target_buffer_set_u32_array(target, code, ARRAY_SIZE(aducm360_flash_write_code),
374 aducm360_flash_write_code);
375 retval = target_write_buffer(target, helper->address, sizeof(code), code);
376 if (retval != ERROR_OK)
377 return retval;
378 entry_point = helper->address;
379
380 /* ----- Allocate space in the target's RAM for the user application's object code ----- */
381 while (target_alloc_working_area_try(target, target_buffer_size, &target_buffer) != ERROR_OK) {
382 LOG_WARNING("couldn't allocate a buffer space of 0x%08" PRIx32 "bytes in the target's SRAM.",
383 target_buffer_size);
384 target_buffer_size /= 2;
385 if (target_buffer_size <= 256) { /* No room available */
386 LOG_WARNING("no large enough working area available, can't do block memory writes");
387 target_free_working_area(target, helper);
388 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
389 }
390 }
391
392 /* ----- Prepare the target for the helper ----- */
393 armv7m_algo.common_magic = ARMV7M_COMMON_MAGIC;
394 armv7m_algo.core_mode = ARM_MODE_THREAD;
395
396 init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT); /*SRCBEG */
397 init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT); /*SRCEND */
398 init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT); /*DST */
399 init_reg_param(&reg_params[3], "r3", 32, PARAM_OUT); /*COUNT (LWs)*/
400 init_reg_param(&reg_params[4], "r9", 32, PARAM_IN); /*RESULT */
401
402 buf_set_u32(reg_params[0].value, 0, 32, target_buffer->address);
403 buf_set_u32(reg_params[1].value, 0, 32, target_buffer->address + target_buffer->size);
404 buf_set_u32(reg_params[2].value, 0, 32, address);
405 buf_set_u32(reg_params[3].value, 0, 32, wcount);
406
407 retval = target_run_flash_async_algorithm(target, buffer, wcount, 4,
408 0, NULL,
409 5, reg_params,
410 target_buffer->address, target_buffer->size,
411 entry_point, exit_point,
412 &armv7m_algo);
413 if (retval != ERROR_OK) {
414 LOG_ERROR("error executing aducm360 flash write algorithm");
415 } else {
416 res = buf_get_u32(reg_params[4].value, 0, 32); /*RESULT*/
417 if (res) {
418 LOG_ERROR("aducm360 fast async algorithm reports an error (%02X)", res);
419 retval = ERROR_FAIL;
420 }
421 }
422
423 target_free_working_area(target, target_buffer);
424 target_free_working_area(target, helper);
425
426 destroy_reg_param(&reg_params[0]);
427 destroy_reg_param(&reg_params[1]);
428 destroy_reg_param(&reg_params[2]);
429 destroy_reg_param(&reg_params[3]);
430 destroy_reg_param(&reg_params[4]);
431
432 return retval;
433 }
434
435 /* ----------------------------------------------------------------------- */
436 /* If this fn returns ERROR_TARGET_RESOURCE_NOT_AVAILABLE, then the caller can fall
437 * back to another mechanism that does not require onboard RAM
438 *
439 * Caller should not check for other return values specifically
440 */
441 static int aducm360_write_block(struct flash_bank *bank,
442 const uint8_t *buffer,
443 uint32_t offset,
444 uint32_t count)
445 {
446 int choice = 0;
447
448 switch (choice) {
449 case 0:
450 return aducm360_write_block_sync(bank, buffer, offset, count);
451 break;
452 case 1:
453 return aducm360_write_block_async(bank, buffer, offset, count);
454 break;
455 default:
456 LOG_ERROR("aducm360_write_block was cancelled (no writing method was chosen)!");
457 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
458 }
459 }
460
461 /* ----------------------------------------------------------------------- */
462 #define FEESTA_WRDONE 0x00000008
463
464 static int aducm360_write_modified(struct flash_bank *bank,
465 const uint8_t *buffer,
466 uint32_t offset,
467 uint32_t count)
468 {
469 uint32_t value;
470 int res = ERROR_OK;
471 uint32_t i, j, a, d;
472 struct target *target = bank->target;
473
474 LOG_DEBUG("performing slow write (offset=0x%08" PRIx32 ", count=0x%08" PRIx32 ")...",
475 offset, count);
476
477 /* Enable the writing to the flash */
478 aducm360_set_write_enable(target, 1);
479
480 /* Clear any old status */
481 target_read_u32(target, ADUCM360_FLASH_BASE + ADUCM360_FLASH_FEESTA, &value);
482
483 for (i = 0; i < count; i += 4) {
484 a = offset+i;
485 for (j = 0; i < 4; i += 1)
486 *((uint8_t *)(&d) + j) = buffer[i+j];
487 target_write_u32(target, a, d);
488 do {
489 target_read_u32(target, ADUCM360_FLASH_BASE + ADUCM360_FLASH_FEESTA, &value);
490 } while (!(value & FEESTA_WRDONE));
491 }
492 aducm360_set_write_enable(target, 0);
493
494 return res;
495 }
496
497 /* ----------------------------------------------------------------------- */
498 static int aducm360_write(struct flash_bank *bank, const uint8_t *buffer, uint32_t offset, uint32_t count)
499 {
500 int retval;
501
502 /* try using a block write */
503 retval = aducm360_write_block(bank, buffer, offset, count);
504 if (retval != ERROR_OK) {
505 if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) {
506 /* if block write failed (no sufficient working area),
507 * use normal (slow) JTAG method */
508 LOG_WARNING("couldn't use block writes, falling back to single memory accesses");
509
510 retval = aducm360_write_modified(bank, buffer, offset, count);
511 if (retval != ERROR_OK) {
512 LOG_ERROR("slow write failed");
513 return ERROR_FLASH_OPERATION_FAILED;
514 }
515 }
516 }
517 return retval;
518 }
519
520 /* ----------------------------------------------------------------------- */
521 static int aducm360_probe(struct flash_bank *bank)
522 {
523 return ERROR_OK;
524 }
525
526 /* ----------------------------------------------------------------------- */
527 /* sets FEECON0 bit 2
528 * enable = 1 enables writes & erases, 0 disables them */
529 static int aducm360_set_write_enable(struct target *target, int enable)
530 {
531 /* don't bother to preserve int enable bit here */
532 uint32_t value;
533
534 target_read_u32(target, ADUCM360_FLASH_BASE + ADUCM360_FLASH_FEECON0, &value);
535 if (enable)
536 value |= 0x00000004;
537 else
538 value &= ~0x00000004;
539 target_write_u32(target, ADUCM360_FLASH_BASE + ADUCM360_FLASH_FEECON0, value);
540
541 return ERROR_OK;
542 }
543
544 /* ----------------------------------------------------------------------- */
545 /* wait up to timeout_ms for controller to not be busy,
546 * then check whether the command passed or failed.
547 *
548 * this function sleeps 1ms between checks (after the first one),
549 * so in some cases may slow things down without a usleep after the first read */
550 static int aducm360_check_flash_completion(struct target *target, unsigned int timeout_ms)
551 {
552 uint32_t v = 1;
553
554 int64_t endtime = timeval_ms() + timeout_ms;
555 while (1) {
556 target_read_u32(target, ADUCM360_FLASH_BASE+ADUCM360_FLASH_FEESTA, &v);
557 if ((v & 0x00000001) == 0)
558 break;
559 alive_sleep(1);
560 if (timeval_ms() >= endtime)
561 break;
562 }
563
564 if (!(v & 0x00000004)) /* b2 */
565 return ERROR_FAIL;
566
567 return ERROR_OK;
568 }
569
570 /* ----------------------------------------------------------------------- */
571 struct flash_driver aducm360_flash = {
572 .name = "aducm360",
573 .flash_bank_command = aducm360_flash_bank_command,
574 .erase = aducm360_erase,
575 .protect = aducm360_protect,
576 .write = aducm360_write,
577 .read = default_flash_read,
578 .probe = aducm360_probe,
579 .auto_probe = aducm360_probe,
580 .erase_check = default_flash_blank_check,
581 .protect_check = aducm360_protect_check,
582 };

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)