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
61
62
68
69
74
75
83
84
85
86
87
88
89
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
129
130
131
132
133
134
135
136
137
138
139
140
157
158
165
166
167
168
169
170
171
172
173
174
178
179
180
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
202
203
207
208
209
210
211
212
213
214
215
216
220
221
222
223
224
230
231
232
233
234
235
236
237
238
239
240
241
242
243
250
251
252
253
254
255
256
257
258
259
260
261
262
263
270
271
272
273
274
275
276
277
278
279
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
306
307
308
309
310
311
312
313
314
315
316
320
321
322
336
337
338
339
340
341
342
343
344
350
351
352
353
354
355
356
357
358
365
366
367
368
369
370
371
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
396
400
401
402
403
404
405
406
407
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
445
446
447
448
449
453
457
458
459
460
461
462
463
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
575
576
577
578
579
580
581
582
583
584
585
586
587
588
592
593
594
599
600
601
602
620
621
622
623
634
635
636
637
638
639
640
641
645
646
647
651
652
653
654
655
656
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
/* ... */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "imp.h"
#include <helper/time_support.h>
#define REG_NAME_WIDTH (12)
#define SAMV_EFC_FCMD_GETD (0x0)
#define SAMV_EFC_FCMD_WP (0x1)
#define SAMV_EFC_FCMD_WPL (0x2)
#define SAMV_EFC_FCMD_EWP (0x3)
#define SAMV_EFC_FCMD_EWPL (0x4)
#define SAMV_EFC_FCMD_EA (0x5)
#define SAMV_EFC_FCMD_EPA (0x7)
#define SAMV_EFC_FCMD_SLB (0x8)
#define SAMV_EFC_FCMD_CLB (0x9)
#define SAMV_EFC_FCMD_GLB (0xA)
#define SAMV_EFC_FCMD_SFB (0xB)
#define SAMV_EFC_FCMD_CFB (0xC)
#define SAMV_EFC_FCMD_GFB (0xD)
#define OFFSET_EFC_FMR 0
#define OFFSET_EFC_FCR 4
#define OFFSET_EFC_FSR 8
#define OFFSET_EFC_FRR 12
#define SAMV_CHIPID_CIDR (0x400E0940)
#define SAMV_NUM_GPNVM_BITS 9
#define SAMV_CONTROLLER_ADDR (0x400e0c00)
#define SAMV_SECTOR_SIZE 16384
#define SAMV_PAGE_SIZE 512
#define SAMV_FLASH_BASE 0x00400000
24 defines
struct samv_flash_bank {
bool probed;
unsigned size_bytes;
unsigned gpnvm[SAMV_NUM_GPNVM_BITS];
...};
/* ... */
static int samv_efc_get_status(struct target *target, uint32_t *v)
{
int r = target_read_u32(target, SAMV_CONTROLLER_ADDR + OFFSET_EFC_FSR, v);
return r;
}{ ... }
static int samv_efc_get_result(struct target *target, uint32_t *v)
{
uint32_t rv;
int r = target_read_u32(target, SAMV_CONTROLLER_ADDR + OFFSET_EFC_FRR, &rv);
if (v)
*v = rv;
return r;
}{ ... }
static int samv_efc_start_command(struct target *target,
unsigned command, unsigned argument)
{
uint32_t v;
samv_efc_get_status(target, &v);
if (!(v & 1)) {
LOG_ERROR("flash controller is not ready");
return ERROR_FAIL;
}if (!(v & 1)) { ... }
v = (0x5A << 24) | (argument << 8) | command;
LOG_DEBUG("starting flash command: 0x%08x", (unsigned int)(v));
int r = target_write_u32(target, SAMV_CONTROLLER_ADDR + OFFSET_EFC_FCR, v);
if (r != ERROR_OK)
LOG_DEBUG("write failed");
return r;
}{ ... }
static int samv_efc_perform_command(struct target *target,
unsigned command, unsigned argument, uint32_t *status)
{
int r;
uint32_t v;
int64_t ms_now, ms_end;
if (status)
*status = 0;
r = samv_efc_start_command(target, command, argument);
if (r != ERROR_OK)
return r;
ms_end = 10000 + timeval_ms();
do {
r = samv_efc_get_status(target, &v);
if (r != ERROR_OK)
return r;
ms_now = timeval_ms();
if (ms_now > ms_end) {
LOG_ERROR("Command timeout");
return ERROR_FAIL;
}if (ms_now > ms_end) { ... }
...} while ((v & 1) == 0);
if (status)
*status = (v & 0x6);
return ERROR_OK;
}{ ... }
static int samv_erase_pages(struct target *target,
int first_page, int num_pages, uint32_t *status)
{
uint8_t erase_pages;
switch (num_pages) {
case 4:
erase_pages = 0x00;
break;case 4:
case 8:
erase_pages = 0x01;
break;case 8:
case 16:
erase_pages = 0x02;
break;case 16:
case 32:
erase_pages = 0x03;
break;case 32:
default:
erase_pages = 0x00;
break;default
}switch (num_pages) { ... }
/* ... */
return samv_efc_perform_command(target, SAMV_EFC_FCMD_EPA,
first_page | erase_pages, status);
}{ ... }
static int samv_get_gpnvm(struct target *target, unsigned gpnvm, unsigned *out)
{
uint32_t v;
int r;
if (gpnvm >= SAMV_NUM_GPNVM_BITS) {
LOG_ERROR("invalid gpnvm %d, max: %d", gpnvm, SAMV_NUM_GPNVM_BITS);
return ERROR_FAIL;
}if (gpnvm >= SAMV_NUM_GPNVM_BITS) { ... }
r = samv_efc_perform_command(target, SAMV_EFC_FCMD_GFB, 0, NULL);
if (r != ERROR_OK) {
LOG_ERROR("samv_get_gpnvm failed");
return r;
}if (r != ERROR_OK) { ... }
r = samv_efc_get_result(target, &v);
if (out)
*out = (v >> gpnvm) & 1;
return r;
}{ ... }
static int samv_clear_gpnvm(struct target *target, unsigned gpnvm)
{
int r;
unsigned v;
if (gpnvm >= SAMV_NUM_GPNVM_BITS) {
LOG_ERROR("invalid gpnvm %d, max: %d", gpnvm, SAMV_NUM_GPNVM_BITS);
return ERROR_FAIL;
}if (gpnvm >= SAMV_NUM_GPNVM_BITS) { ... }
r = samv_get_gpnvm(target, gpnvm, &v);
if (r != ERROR_OK) {
LOG_DEBUG("get gpnvm failed: %d", r);
return r;
}if (r != ERROR_OK) { ... }
r = samv_efc_perform_command(target, SAMV_EFC_FCMD_CFB, gpnvm, NULL);
LOG_DEBUG("clear gpnvm result: %d", r);
return r;
}{ ... }
static int samv_set_gpnvm(struct target *target, unsigned gpnvm)
{
int r;
unsigned v;
if (gpnvm >= SAMV_NUM_GPNVM_BITS) {
LOG_ERROR("invalid gpnvm %d, max: %d", gpnvm, SAMV_NUM_GPNVM_BITS);
return ERROR_FAIL;
}if (gpnvm >= SAMV_NUM_GPNVM_BITS) { ... }
r = samv_get_gpnvm(target, gpnvm, &v);
if (r != ERROR_OK)
return r;
if (v) {
r = ERROR_OK;
}if (v) { ... } else {
r = samv_efc_perform_command(target, SAMV_EFC_FCMD_SFB, gpnvm, NULL);
}else { ... }
return r;
}{ ... }
static int samv_flash_unlock(struct target *target,
unsigned start_sector, unsigned end_sector)
{
int r;
uint32_t status;
uint32_t pg;
uint32_t pages_per_sector;
pages_per_sector = SAMV_SECTOR_SIZE / SAMV_PAGE_SIZE;
while (start_sector <= end_sector) {
pg = start_sector * pages_per_sector;
r = samv_efc_perform_command(target, SAMV_EFC_FCMD_CLB, pg, &status);
if (r != ERROR_OK)
return r;
start_sector++;
}while (start_sector <= end_sector) { ... }
return ERROR_OK;
}{ ... }
static int samv_flash_lock(struct target *target,
unsigned start_sector, unsigned end_sector)
{
uint32_t status;
uint32_t pg;
uint32_t pages_per_sector;
int r;
pages_per_sector = SAMV_SECTOR_SIZE / SAMV_PAGE_SIZE;
while (start_sector <= end_sector) {
pg = start_sector * pages_per_sector;
r = samv_efc_perform_command(target, SAMV_EFC_FCMD_SLB, pg, &status);
if (r != ERROR_OK)
return r;
start_sector++;
}while (start_sector <= end_sector) { ... }
return ERROR_OK;
}{ ... }
static int samv_protect_check(struct flash_bank *bank)
{
int r;
uint32_t v[4] = {0};
r = samv_efc_perform_command(bank->target, SAMV_EFC_FCMD_GLB, 0, NULL);
if (r == ERROR_OK) {
samv_efc_get_result(bank->target, &v[0]);
samv_efc_get_result(bank->target, &v[1]);
samv_efc_get_result(bank->target, &v[2]);
r = samv_efc_get_result(bank->target, &v[3]);
}if (r == ERROR_OK) { ... }
if (r != ERROR_OK)
return r;
for (unsigned int x = 0; x < bank->num_sectors; x++)
bank->sectors[x].is_protected = (!!(v[x >> 5] & (1 << (x % 32))));
return ERROR_OK;
}{ ... }
FLASH_BANK_COMMAND_HANDLER(samv_flash_bank_command)
{
LOG_INFO("flash bank command");
struct samv_flash_bank *samv_info;
samv_info = calloc(1, sizeof(struct samv_flash_bank));
bank->driver_priv = samv_info;
return ERROR_OK;
}{ ... }
static int samv_get_device_id(struct flash_bank *bank, uint32_t *device_id)
{
return target_read_u32(bank->target, SAMV_CHIPID_CIDR, device_id);
}{ ... }
static int samv_probe(struct flash_bank *bank)
{
uint32_t device_id;
int r = samv_get_device_id(bank, &device_id);
if (r != ERROR_OK)
return r;
LOG_INFO("device id = 0x%08" PRIx32 "", device_id);
uint8_t eproc = (device_id >> 5) & 0x7;
if (eproc != 0) {
LOG_ERROR("unexpected eproc code: %d was expecting 0 (Cortex-M7)", eproc);
return ERROR_FAIL;
}if (eproc != 0) { ... }
uint8_t nvm_size_code = (device_id >> 8) & 0xf;
switch (nvm_size_code) {
case 10:
bank->size = 512 * 1024;
break;case 10:
case 12:
bank->size = 1024 * 1024;
break;case 12:
case 14:
bank->size = 2048 * 1024;
break;case 14:
default:
LOG_ERROR("unrecognized flash size code: %d", nvm_size_code);
return ERROR_FAIL;default
}switch (nvm_size_code) { ... }
struct samv_flash_bank *samv_info = bank->driver_priv;
samv_info->size_bytes = bank->size;
samv_info->probed = true;
bank->base = SAMV_FLASH_BASE;
bank->num_sectors = bank->size / SAMV_SECTOR_SIZE;
bank->sectors = calloc(bank->num_sectors, sizeof(struct flash_sector));
for (unsigned int s = 0; s < bank->num_sectors; s++) {
bank->sectors[s].size = SAMV_SECTOR_SIZE;
bank->sectors[s].offset = s * SAMV_SECTOR_SIZE;
bank->sectors[s].is_erased = -1;
bank->sectors[s].is_protected = -1;
}for (unsigned int s = 0; s < bank->num_sectors; s++) { ... }
r = samv_protect_check(bank);
if (r != ERROR_OK)
return r;
return ERROR_OK;
}{ ... }
static int samv_auto_probe(struct flash_bank *bank)
{
struct samv_flash_bank *samv_info = bank->driver_priv;
if (samv_info->probed)
return ERROR_OK;
return samv_probe(bank);
}{ ... }
static int samv_erase(struct flash_bank *bank, unsigned int first,
unsigned int last)
{
const int page_count = 32;
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}if (bank->target->state != TARGET_HALTED) { ... }
int r = samv_auto_probe(bank);
if (r != ERROR_OK)
return r;
if ((first == 0) && ((last + 1) == bank->num_sectors))
return samv_efc_perform_command(bank->target, SAMV_EFC_FCMD_EA, 0, NULL);
LOG_INFO("erasing lock regions %u-%u...", first, last);
for (unsigned int i = first; i <= last; i++) {
uint32_t status;
r = samv_erase_pages(bank->target, (i * page_count), page_count, &status);
LOG_INFO("erasing lock region %u", i);
if (r != ERROR_OK)
LOG_ERROR("error performing erase page @ lock region number %u", i);
if (status & (1 << 2)) {
LOG_ERROR("lock region %u is locked", i);
return ERROR_FAIL;
}if (status & (1 << 2)) { ... }
if (status & (1 << 1)) {
LOG_ERROR("flash command error @lock region %u", i);
return ERROR_FAIL;
}if (status & (1 << 1)) { ... }
}for (unsigned int i = first; i <= last; i++) { ... }
return ERROR_OK;
}{ ... }
static int samv_protect(struct flash_bank *bank, int set, unsigned int first,
unsigned int last)
{
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}if (bank->target->state != TARGET_HALTED) { ... }
int r;
if (set)
r = samv_flash_lock(bank->target, first, last);
else
r = samv_flash_unlock(bank->target, first, last);
return r;
}{ ... }
static int samv_page_read(struct target *target,
unsigned page_num, uint8_t *buf)
{
uint32_t addr = SAMV_FLASH_BASE + page_num * SAMV_PAGE_SIZE;
int r = target_read_memory(target, addr, 4, SAMV_PAGE_SIZE / 4, buf);
if (r != ERROR_OK)
LOG_ERROR("flash program failed to read page @ 0x%08x",
(unsigned int)(addr));
return r;
}{ ... }
static int samv_page_write(struct target *target,
unsigned pagenum, const uint8_t *buf)
{
uint32_t status;
const uint32_t addr = SAMV_FLASH_BASE + pagenum * SAMV_PAGE_SIZE;
int r;
LOG_DEBUG("write page %u at address 0x%08x", pagenum, (unsigned int)addr);
r = target_write_memory(target, addr, 4, SAMV_PAGE_SIZE / 4, buf);
if (r != ERROR_OK) {
LOG_ERROR("failed to buffer page at 0x%08x", (unsigned int)addr);
return r;
}if (r != ERROR_OK) { ... }
r = samv_efc_perform_command(target, SAMV_EFC_FCMD_WP, pagenum, &status);
if (r != ERROR_OK)
LOG_ERROR("error performing write page at 0x%08x", (unsigned int)addr);
if (status & (1 << 2)) {
LOG_ERROR("page at 0x%08x is locked", (unsigned int)addr);
return ERROR_FAIL;
}if (status & (1 << 2)) { ... }
if (status & (1 << 1)) {
LOG_ERROR("flash command error at 0x%08x", (unsigned int)addr);
return ERROR_FAIL;
}if (status & (1 << 1)) { ... }
return ERROR_OK;
}{ ... }
static int samv_write(struct flash_bank *bank, const uint8_t *buffer,
uint32_t offset, uint32_t count)
{
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("target not halted");
return ERROR_TARGET_NOT_HALTED;
}if (bank->target->state != TARGET_HALTED) { ... }
if (count == 0)
return ERROR_OK;
if ((offset + count) > bank->size) {
LOG_ERROR("flash write error - past end of bank");
LOG_ERROR(" offset: 0x%08x, count 0x%08x, bank end: 0x%08x",
(unsigned int)(offset),
(unsigned int)(count),
(unsigned int)(bank->size));
return ERROR_FAIL;
}if ((offset + count) > bank->size) { ... }
uint8_t pagebuffer[SAMV_PAGE_SIZE] = {0};
uint32_t page_cur = offset / SAMV_PAGE_SIZE;
uint32_t page_end = (offset + count - 1) / SAMV_PAGE_SIZE;
LOG_DEBUG("offset: 0x%08x, count: 0x%08x",
(unsigned int)(offset), (unsigned int)(count));
LOG_DEBUG("page start: %d, page end: %d", (int)(page_cur), (int)(page_end));
int r;
uint32_t page_offset;
if (page_cur == page_end) {
LOG_DEBUG("special case, all in one page");
r = samv_page_read(bank->target, page_cur, pagebuffer);
if (r != ERROR_OK)
return r;
page_offset = offset & (SAMV_PAGE_SIZE-1);
memcpy(pagebuffer + page_offset, buffer, count);
r = samv_page_write(bank->target, page_cur, pagebuffer);
if (r != ERROR_OK)
return r;
return ERROR_OK;
}if (page_cur == page_end) { ... }
page_offset = offset & (SAMV_PAGE_SIZE - 1);
if (page_offset) {
LOG_DEBUG("non-aligned start");
r = samv_page_read(bank->target, page_cur, pagebuffer);
if (r != ERROR_OK)
return r;
uint32_t n = SAMV_PAGE_SIZE - page_offset;
memcpy(pagebuffer + page_offset, buffer, n);
r = samv_page_write(bank->target, page_cur, pagebuffer);
if (r != ERROR_OK)
return r;
count -= n;
offset += n;
buffer += n;
page_cur++;
}if (page_offset) { ... }
assert(offset % SAMV_PAGE_SIZE == 0);
LOG_DEBUG("full page loop: cur=%d, end=%d, count = 0x%08x",
(int)page_cur, (int)page_end, (unsigned int)(count));
while ((page_cur < page_end) && (count >= SAMV_PAGE_SIZE)) {
r = samv_page_write(bank->target, page_cur, buffer);
if (r != ERROR_OK)
return r;
count -= SAMV_PAGE_SIZE;
buffer += SAMV_PAGE_SIZE;
page_cur += 1;
}while ((page_cur < page_end) && (count >= SAMV_PAGE_SIZE)) { ... }
if (count) {
LOG_DEBUG("final partial page, count = 0x%08x", (unsigned int)(count));
r = samv_page_read(bank->target, page_cur, pagebuffer);
if (r != ERROR_OK)
return r;
memcpy(pagebuffer, buffer, count);
r = samv_page_write(bank->target, page_cur, pagebuffer);
if (r != ERROR_OK)
return r;
}if (count) { ... }
return ERROR_OK;
}{ ... }
static int samv_get_info(struct flash_bank *bank, struct command_invocation *cmd)
{
struct samv_flash_bank *samv_info = bank->driver_priv;
if (!samv_info->probed) {
int r = samv_probe(bank);
if (r != ERROR_OK)
return r;
}if (!samv_info->probed) { ... }
command_print_sameline(cmd, "Cortex-M7 detected with %" PRIu32 " kB flash\n",
bank->size / 1024);
return ERROR_OK;
}{ ... }
COMMAND_HANDLER(samv_handle_gpnvm_command)
{
struct flash_bank *bank = get_flash_bank_by_num_noprobe(0);
if (!bank)
return ERROR_FAIL;
struct samv_flash_bank *samv_info = bank->driver_priv;
struct target *target = bank->target;
if (target->state != TARGET_HALTED) {
LOG_ERROR("target not halted");
return ERROR_TARGET_NOT_HALTED;
}if (target->state != TARGET_HALTED) { ... }
int r;
if (!samv_info->probed) {
r = samv_auto_probe(bank);
if (r != ERROR_OK)
return r;
}if (!samv_info->probed) { ... }
int who = 0;
switch (CMD_ARGC) {
case 0:
goto showall;case 0:
case 1:
who = -1;
break;case 1:
case 2:
if (!strcmp(CMD_ARGV[0], "show") && !strcmp(CMD_ARGV[1], "all"))
who = -1;
else {
uint32_t v32;
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], v32);
who = v32;
}else { ... }
break;case 2:
default:
return ERROR_COMMAND_SYNTAX_ERROR;default
}switch (CMD_ARGC) { ... }
unsigned v = 0;
if (!strcmp("show", CMD_ARGV[0])) {
if (who == -1) {
showall:
r = ERROR_OK;
for (int x = 0; x < SAMV_NUM_GPNVM_BITS; x++) {
r = samv_get_gpnvm(target, x, &v);
if (r != ERROR_OK)
break;
command_print(CMD, "samv-gpnvm%u: %u", x, v);
}for (int x = 0; x < SAMV_NUM_GPNVM_BITS; x++) { ... }
return r;
}if (who == -1) { ... }
if ((who >= 0) && (((unsigned)who) < SAMV_NUM_GPNVM_BITS)) {
r = samv_get_gpnvm(target, who, &v);
if (r != ERROR_OK)
return r;
command_print(CMD, "samv-gpnvm%u: %u", who, v);
return r;
}if ((who >= 0) && (((unsigned)who) < SAMV_NUM_GPNVM_BITS)) { ... } else {
command_print(CMD, "invalid gpnvm: %u", who);
return ERROR_COMMAND_SYNTAX_ERROR;
}else { ... }
}if (!strcmp("show", CMD_ARGV[0])) { ... }
if (who == -1) {
command_print(CMD, "missing gpnvm number");
return ERROR_COMMAND_SYNTAX_ERROR;
}if (who == -1) { ... }
if (!strcmp("set", CMD_ARGV[0]))
r = samv_set_gpnvm(target, who);
else if (!strcmp("clr", CMD_ARGV[0]) || !strcmp("clear", CMD_ARGV[0]))
r = samv_clear_gpnvm(target, who);
else {
command_print(CMD, "unknown command: %s", CMD_ARGV[0]);
r = ERROR_COMMAND_SYNTAX_ERROR;
}else { ... }
return r;
}{ ... }
static const struct command_registration atsamv_exec_command_handlers[] = {
{
.name = "gpnvm",
.handler = samv_handle_gpnvm_command,
.mode = COMMAND_EXEC,
.usage = "[('clr'|'set'|'show') bitnum]",
.help = "Without arguments, shows all bits in the gpnvm "
"register. Otherwise, clears, sets, or shows one "
"General Purpose Non-Volatile Memory (gpnvm) bit.",
...},
COMMAND_REGISTRATION_DONE
...};
static const struct command_registration atsamv_command_handlers[] = {
{
.name = "atsamv",
.mode = COMMAND_ANY,
.help = "atsamv flash command group",
.usage = "",
.chain = atsamv_exec_command_handlers,
...},
COMMAND_REGISTRATION_DONE
...};
const struct flash_driver atsamv_flash = {
.name = "atsamv",
.commands = atsamv_command_handlers,
.flash_bank_command = samv_flash_bank_command,
.erase = samv_erase,
.protect = samv_protect,
.write = samv_write,
.read = default_flash_read,
.probe = samv_probe,
.auto_probe = samv_auto_probe,
.erase_check = default_flash_blank_check,
.protect_check = samv_protect_check,
.info = samv_get_info,
.free_driver_priv = default_flash_free_driver_priv,
...};