685 lines
20 KiB
C
685 lines
20 KiB
C
/*
|
|
* This file is part of the Black Magic Debug project.
|
|
*
|
|
* Copyright (C) 2015 Black Sphere Technologies Ltd.
|
|
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/* This file implements Atmel SAM3/4 target specific functions for detecting
|
|
* the device, providing the XML memory map and Flash memory programming.
|
|
*
|
|
* Supported devices: SAM3N, SAM3S, SAM3U, SAM3X, SAM4S, SAME70, SAMS70, SAMV71, SAMV70
|
|
*/
|
|
|
|
#include "general.h"
|
|
#include "target.h"
|
|
#include "target_internal.h"
|
|
|
|
static int sam_flash_erase(target_flash_s *f, target_addr_t addr, size_t len);
|
|
static int sam3_flash_erase(target_flash_s *f, target_addr_t addr, size_t len);
|
|
static int sam_flash_write(target_flash_s *f, target_addr_t dest, const void *src, size_t len);
|
|
|
|
static int sam_gpnvm_get(target *t, uint32_t base, uint32_t *gpnvm);
|
|
|
|
static bool sam_cmd_gpnvm(target *t, int argc, const char **argv);
|
|
|
|
const struct command_s sam_cmd_list[] = {
|
|
{"gpnvm", (cmd_handler)sam_cmd_gpnvm, "Set/Get GPVNM bits"},
|
|
{NULL, NULL, NULL}
|
|
};
|
|
|
|
/* Enhanced Embedded Flash Controller (EEFC) Register Map */
|
|
#define SAMX7X_EEFC_BASE 0x400E0C00
|
|
#define SAM3N_EEFC_BASE 0x400E0A00
|
|
#define SAM3X_EEFC_BASE(x) (0x400E0A00+((x)*0x200))
|
|
#define SAM3U_EEFC_BASE(x) (0x400E0800+((x)*0x200))
|
|
#define SAM4S_EEFC_BASE(x) (0x400E0A00+((x)*0x200))
|
|
#define EEFC_FMR(base) ((base)+0x00)
|
|
#define EEFC_FCR(base) ((base)+0x04)
|
|
#define EEFC_FSR(base) ((base)+0x08)
|
|
#define EEFC_FRR(base) ((base)+0x0C)
|
|
|
|
#define EEFC_FCR_FKEY (0x5A << 24)
|
|
#define EEFC_FCR_FCMD_GETD 0x00
|
|
#define EEFC_FCR_FCMD_WP 0x01
|
|
#define EEFC_FCR_FCMD_WPL 0x02
|
|
#define EEFC_FCR_FCMD_EWP 0x03
|
|
#define EEFC_FCR_FCMD_EWPL 0x04
|
|
#define EEFC_FCR_FCMD_EA 0x05
|
|
#define EEFC_FCR_FCMD_EPA 0x07
|
|
#define EEFC_FCR_FCMD_SLB 0x08
|
|
#define EEFC_FCR_FCMD_CLB 0x09
|
|
#define EEFC_FCR_FCMD_GLB 0x0A
|
|
#define EEFC_FCR_FCMD_SGPB 0x0B
|
|
#define EEFC_FCR_FCMD_CGPB 0x0C
|
|
#define EEFC_FCR_FCMD_GGPB 0x0D
|
|
#define EEFC_FCR_FCMD_STUI 0x0E
|
|
#define EEFC_FCR_FCMD_SPUI 0x0F
|
|
|
|
#define EEFC_FSR_FRDY (1 << 0)
|
|
#define EEFC_FSR_FCMDE (1 << 1)
|
|
#define EEFC_FSR_FLOCKE (1 << 2)
|
|
#define EEFC_FSR_ERROR (EEFC_FSR_FCMDE | EEFC_FSR_FLOCKE)
|
|
|
|
#define SAM_SMALL_PAGE_SIZE 256
|
|
#define SAM_LARGE_PAGE_SIZE 512
|
|
|
|
/* CHIPID Register Map */
|
|
#define SAM_CHIPID_CIDR 0x400E0940
|
|
#define SAM34NSU_CHIPID_CIDR 0x400E0740
|
|
|
|
#define SAM_CHIPID_EXID (SAM_CHIPID_CIDR + 0x4)
|
|
|
|
#define CHIPID_CIDR_VERSION_MASK (0x1F << 0)
|
|
|
|
#define CHIPID_CIDR_EPROC_OFFSET (5)
|
|
#define CHIPID_CIDR_EPROC_MASK (0x7 << CHIPID_CIDR_EPROC_OFFSET)
|
|
#define CHIPID_CIDR_EPROC_CM7 (0x0 << CHIPID_CIDR_EPROC_OFFSET)
|
|
#define CHIPID_CIDR_EPROC_CM3 (0x3 << CHIPID_CIDR_EPROC_OFFSET)
|
|
#define CHIPID_CIDR_EPROC_CM4 (0x7 << CHIPID_CIDR_EPROC_OFFSET)
|
|
|
|
#define CHIPID_CIDR_NVPSIZ_OFFSET (8)
|
|
#define CHIPID_CIDR_NVPSIZ_MASK (0xF << CHIPID_CIDR_NVPSIZ_OFFSET)
|
|
#define CHIPID_CIDR_NVPSIZ_8K (0x1 << CHIPID_CIDR_NVPSIZ_OFFSET)
|
|
#define CHIPID_CIDR_NVPSIZ_16K (0x2 << CHIPID_CIDR_NVPSIZ_OFFSET)
|
|
#define CHIPID_CIDR_NVPSIZ_32K (0x3 << CHIPID_CIDR_NVPSIZ_OFFSET)
|
|
#define CHIPID_CIDR_NVPSIZ_64K (0x5 << CHIPID_CIDR_NVPSIZ_OFFSET)
|
|
#define CHIPID_CIDR_NVPSIZ_128K (0x7 << CHIPID_CIDR_NVPSIZ_OFFSET)
|
|
#define CHIPID_CIDR_NVPSIZ_256K (0x9 << CHIPID_CIDR_NVPSIZ_OFFSET)
|
|
#define CHIPID_CIDR_NVPSIZ_512K (0xA << CHIPID_CIDR_NVPSIZ_OFFSET)
|
|
#define CHIPID_CIDR_NVPSIZ_1024K (0xC << CHIPID_CIDR_NVPSIZ_OFFSET)
|
|
#define CHIPID_CIDR_NVPSIZ_2048K (0xE << CHIPID_CIDR_NVPSIZ_OFFSET)
|
|
|
|
#define CHIPID_CIDR_NVPSIZ2_OFFSET (12)
|
|
#define CHIPID_CIDR_NVPSIZ2_MASK (0xF << CHIPID_CIDR_NVPSIZ2_OFFSET)
|
|
|
|
#define CHIPID_CIDR_SRAMSIZ_OFFSET (16)
|
|
#define CHIPID_CIDR_SRAMSIZ_MASK (0xF << CHIPID_CIDR_SRAMSIZ_OFFSET)
|
|
#define CHIPID_CIDR_SRAMSIZ_384K (0x2 << CHIPID_CIDR_SRAMSIZ_OFFSET)
|
|
#define CHIPID_CIDR_SRAMSIZ_256K (0xD << CHIPID_CIDR_SRAMSIZ_OFFSET)
|
|
|
|
#define CHIPID_CIDR_ARCH_OFFSET (20)
|
|
#define CHIPID_CIDR_ARCH_MASK (0xFF << CHIPID_CIDR_ARCH_OFFSET)
|
|
#define CHIPID_CIDR_ARCH_SAME70 (0x10 << CHIPID_CIDR_ARCH_OFFSET)
|
|
#define CHIPID_CIDR_ARCH_SAMS70 (0x11 << CHIPID_CIDR_ARCH_OFFSET)
|
|
#define CHIPID_CIDR_ARCH_SAMV71 (0x12 << CHIPID_CIDR_ARCH_OFFSET)
|
|
#define CHIPID_CIDR_ARCH_SAMV70 (0x13 << CHIPID_CIDR_ARCH_OFFSET)
|
|
#define CHIPID_CIDR_ARCH_SAM3UxC (0x80 << CHIPID_CIDR_ARCH_OFFSET)
|
|
#define CHIPID_CIDR_ARCH_SAM3UxE (0x81 << CHIPID_CIDR_ARCH_OFFSET)
|
|
#define CHIPID_CIDR_ARCH_SAM3XxC (0x84 << CHIPID_CIDR_ARCH_OFFSET)
|
|
#define CHIPID_CIDR_ARCH_SAM3XxE (0x85 << CHIPID_CIDR_ARCH_OFFSET)
|
|
#define CHIPID_CIDR_ARCH_SAM3XxG (0x86 << CHIPID_CIDR_ARCH_OFFSET)
|
|
#define CHIPID_CIDR_ARCH_SAM3NxA (0x93 << CHIPID_CIDR_ARCH_OFFSET)
|
|
#define CHIPID_CIDR_ARCH_SAM3NxB (0x94 << CHIPID_CIDR_ARCH_OFFSET)
|
|
#define CHIPID_CIDR_ARCH_SAM3NxC (0x95 << CHIPID_CIDR_ARCH_OFFSET)
|
|
#define CHIPID_CIDR_ARCH_SAM3SxA (0x88 << CHIPID_CIDR_ARCH_OFFSET)
|
|
#define CHIPID_CIDR_ARCH_SAM3SxB (0x89 << CHIPID_CIDR_ARCH_OFFSET)
|
|
#define CHIPID_CIDR_ARCH_SAM3SxC (0x8A << CHIPID_CIDR_ARCH_OFFSET)
|
|
#define CHIPID_CIDR_ARCH_SAM4SxA (0x88 << CHIPID_CIDR_ARCH_OFFSET)
|
|
#define CHIPID_CIDR_ARCH_SAM4SxB (0x89 << CHIPID_CIDR_ARCH_OFFSET)
|
|
#define CHIPID_CIDR_ARCH_SAM4SxC (0x8A << CHIPID_CIDR_ARCH_OFFSET)
|
|
#define CHIPID_CIDR_ARCH_SAM4SDB (0x99 << CHIPID_CIDR_ARCH_OFFSET)
|
|
#define CHIPID_CIDR_ARCH_SAM4SDC (0x9A << CHIPID_CIDR_ARCH_OFFSET)
|
|
|
|
#define CHIPID_CIDR_NVPTYP_OFFSET (28)
|
|
#define CHIPID_CIDR_NVPTYP_MASK (0x7 << CHIPID_CIDR_NVPTYP_OFFSET)
|
|
#define CHIPID_CIDR_NVPTYP_FLASH (0x2 << CHIPID_CIDR_NVPTYP_OFFSET)
|
|
#define CHIPID_CIDR_NVPTYP_ROM_FLASH (0x3 << CHIPID_CIDR_NVPTYP_OFFSET)
|
|
|
|
#define CHIPID_CIDR_EXT (0x01 << 31)
|
|
|
|
#define CHIPID_EXID_SAMX7X_PINS_OFFSET (0)
|
|
#define CHIPID_EXID_SAMX7X_PINS_MASK (0x3 << CHIPID_EXID_SAMX7X_PINS_OFFSET)
|
|
#define CHIPID_EXID_SAMX7X_PINS_Q (0x2 << CHIPID_EXID_SAMX7X_PINS_OFFSET)
|
|
#define CHIPID_EXID_SAMX7X_PINS_N (0x1 << CHIPID_EXID_SAMX7X_PINS_OFFSET)
|
|
#define CHIPID_EXID_SAMX7X_PINS_J (0x0 << CHIPID_EXID_SAMX7X_PINS_OFFSET)
|
|
|
|
/* GPNVM */
|
|
#define GPNVM_SAMX7X_SECURITY_BIT_OFFSET (0)
|
|
#define GPNVM_SAMX7X_SECURITY_BIT_MASK (0x1 << GPNVM_SAMX7X_SECURITY_BIT_OFFSET)
|
|
|
|
#define GPNVM_SAMX7X_BOOT_BIT_OFFSET (1)
|
|
#define GPNVM_SAMX7X_BOOT_BIT_MASK (0x1 << GPNVM_SAMX7X_BOOT_BIT_OFFSET)
|
|
#define GPNVM_SAMX7X_BOOT_ROM (0x0 << GPNVM_SAMX7X_BOOT_BIT_OFFSET)
|
|
#define GPNVM_SAMX7X_BOOT_FLASH (0x1 << GPNVM_SAMX7X_BOOT_BIT_OFFSET)
|
|
|
|
#define GPNVM_SAMX7X_TCM_BIT_OFFSET (7)
|
|
#define GPNVM_SAMX7X_TCM_BIT_MASK (0x3 << GPNVM_SAMX7X_TCM_BIT_OFFSET)
|
|
#define GPNVM_SAMX7X_TCM_0K (0x0 << GPNVM_SAMX7X_TCM_BIT_OFFSET)
|
|
#define GPNVM_SAMX7X_TCM_32K (0x1 << GPNVM_SAMX7X_TCM_BIT_OFFSET)
|
|
#define GPNVM_SAMX7X_TCM_64K (0x2 << GPNVM_SAMX7X_TCM_BIT_OFFSET)
|
|
#define GPNVM_SAMX7X_TCM_128K (0x3 << GPNVM_SAMX7X_TCM_BIT_OFFSET)
|
|
|
|
enum sam_driver {
|
|
DRIVER_SAM3X,
|
|
DRIVER_SAM3U,
|
|
DRIVER_SAM4S,
|
|
DRIVER_SAM3NS,
|
|
DRIVER_SAMX7X,
|
|
};
|
|
|
|
struct sam_flash {
|
|
target_flash_s f;
|
|
uint32_t eefc_base;
|
|
uint8_t write_cmd;
|
|
};
|
|
|
|
struct samx7x_descr {
|
|
char product_code;
|
|
uint8_t product_id;
|
|
char pins;
|
|
uint32_t ram_size;
|
|
uint32_t flash_size;
|
|
uint8_t density;
|
|
char revision;
|
|
};
|
|
struct sam_priv_s {
|
|
struct samx7x_descr descr;
|
|
char sam_variant_string[16];
|
|
};
|
|
|
|
static void sam3_add_flash(target *t, uint32_t eefc_base, uint32_t addr, size_t length)
|
|
{
|
|
struct sam_flash *sf = calloc(1, sizeof(*sf));
|
|
target_flash_s *f;
|
|
|
|
if (!sf) { /* calloc failed: heap exhaustion */
|
|
DEBUG_WARN("calloc: failed in %s\n", __func__);
|
|
return;
|
|
}
|
|
|
|
f = &sf->f;
|
|
f->start = addr;
|
|
f->length = length;
|
|
f->blocksize = SAM_SMALL_PAGE_SIZE;
|
|
f->erase = sam3_flash_erase;
|
|
f->write = sam_flash_write;
|
|
f->writesize = SAM_SMALL_PAGE_SIZE;
|
|
sf->eefc_base = eefc_base;
|
|
sf->write_cmd = EEFC_FCR_FCMD_EWP;
|
|
target_add_flash(t, f);
|
|
}
|
|
|
|
static void sam_add_flash(target *t, uint32_t eefc_base, uint32_t addr, size_t length)
|
|
{
|
|
struct sam_flash *sf = calloc(1, sizeof(*sf));
|
|
target_flash_s *f;
|
|
|
|
if (!sf) { /* calloc failed: heap exhaustion */
|
|
DEBUG_WARN("calloc: failed in %s\n", __func__);
|
|
return;
|
|
}
|
|
|
|
f = &sf->f;
|
|
f->start = addr;
|
|
f->length = length;
|
|
f->blocksize = SAM_LARGE_PAGE_SIZE * 8;
|
|
f->erase = sam_flash_erase;
|
|
f->write = sam_flash_write;
|
|
f->writesize = SAM_LARGE_PAGE_SIZE;
|
|
sf->eefc_base = eefc_base;
|
|
sf->write_cmd = EEFC_FCR_FCMD_WP;
|
|
target_add_flash(t, f);
|
|
}
|
|
|
|
static void samx7x_add_ram(target* t, uint32_t tcm_config, uint32_t ram_size) {
|
|
uint32_t itcm_size = 0, dtcm_size = 0;
|
|
|
|
switch (tcm_config)
|
|
{
|
|
case GPNVM_SAMX7X_TCM_32K:
|
|
itcm_size = dtcm_size = 0x8000;
|
|
break;
|
|
case GPNVM_SAMX7X_TCM_64K:
|
|
itcm_size = dtcm_size = 0x10000;
|
|
break;
|
|
case GPNVM_SAMX7X_TCM_128K:
|
|
itcm_size = dtcm_size = 0x20000;
|
|
break;
|
|
}
|
|
|
|
if (dtcm_size > 0) {
|
|
target_add_ram(t, 0x20000000, dtcm_size);
|
|
}
|
|
if (itcm_size > 0) {
|
|
target_add_ram(t, 0x00000000, itcm_size);
|
|
}
|
|
|
|
uint32_t sram_size = ram_size - (itcm_size + dtcm_size);
|
|
if (sram_size > 0) {
|
|
target_add_ram(t, 0x20400000, sram_size);
|
|
}
|
|
}
|
|
|
|
static size_t sam_flash_size(uint32_t cidr)
|
|
{
|
|
switch (cidr & CHIPID_CIDR_NVPSIZ_MASK) {
|
|
case CHIPID_CIDR_NVPSIZ_8K:
|
|
return 0x2000;
|
|
case CHIPID_CIDR_NVPSIZ_16K:
|
|
return 0x4000;
|
|
case CHIPID_CIDR_NVPSIZ_32K:
|
|
return 0x8000;
|
|
case CHIPID_CIDR_NVPSIZ_64K:
|
|
return 0x10000;
|
|
case CHIPID_CIDR_NVPSIZ_128K:
|
|
return 0x20000;
|
|
case CHIPID_CIDR_NVPSIZ_256K:
|
|
return 0x40000;
|
|
case CHIPID_CIDR_NVPSIZ_512K:
|
|
return 0x80000;
|
|
case CHIPID_CIDR_NVPSIZ_1024K:
|
|
return 0x100000;
|
|
case CHIPID_CIDR_NVPSIZ_2048K:
|
|
return 0x200000;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static size_t sam_sram_size(uint32_t cidr)
|
|
{
|
|
switch (cidr & CHIPID_CIDR_SRAMSIZ_MASK) {
|
|
case CHIPID_CIDR_SRAMSIZ_256K:
|
|
return 0x40000;
|
|
case CHIPID_CIDR_SRAMSIZ_384K:
|
|
return 0x60000;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
struct samx7x_descr samx7x_parse_id(uint32_t cidr, uint32_t exid) {
|
|
|
|
struct samx7x_descr descr = {0};
|
|
|
|
switch (cidr & CHIPID_CIDR_ARCH_MASK) {
|
|
case CHIPID_CIDR_ARCH_SAME70:
|
|
descr.product_code = 'E';
|
|
descr.product_id = 70;
|
|
break;
|
|
case CHIPID_CIDR_ARCH_SAMS70:
|
|
descr.product_code = 'S';
|
|
descr.product_id = 70;
|
|
break;
|
|
case CHIPID_CIDR_ARCH_SAMV71:
|
|
descr.product_code = 'V';
|
|
descr.product_id = 71;
|
|
break;
|
|
case CHIPID_CIDR_ARCH_SAMV70:
|
|
descr.product_code = 'V';
|
|
descr.product_id = 70;
|
|
break;
|
|
}
|
|
|
|
// A = Revision A, legacy version
|
|
// B = Revision B, current variant
|
|
switch (exid & CHIPID_CIDR_VERSION_MASK) {
|
|
case 0:
|
|
descr.revision = 'A';
|
|
break;
|
|
case 1:
|
|
descr.revision = 'B';
|
|
break;
|
|
default:
|
|
descr.revision = '_';
|
|
break;
|
|
}
|
|
|
|
// Q = 144 pins
|
|
// N = 100 pins
|
|
// J = 64 pins
|
|
switch (exid & CHIPID_EXID_SAMX7X_PINS_MASK) {
|
|
case CHIPID_EXID_SAMX7X_PINS_Q:
|
|
descr.pins = 'Q';
|
|
break;
|
|
case CHIPID_EXID_SAMX7X_PINS_N:
|
|
descr.pins = 'N';
|
|
break;
|
|
case CHIPID_EXID_SAMX7X_PINS_J:
|
|
descr.pins = 'J';
|
|
break;
|
|
}
|
|
|
|
descr.ram_size = sam_sram_size(cidr);
|
|
descr.flash_size = sam_flash_size(cidr);
|
|
|
|
// 21 = 2048 KB
|
|
// 20 = 1024 KB
|
|
// 19 = 512 KB
|
|
switch (descr.flash_size)
|
|
{
|
|
case 0x200000:
|
|
descr.density = 21;
|
|
break;
|
|
case 0x100000:
|
|
descr.density = 20;
|
|
break;
|
|
case 0x80000:
|
|
descr.density = 19;
|
|
break;
|
|
default:
|
|
descr.density = 0;
|
|
break;
|
|
}
|
|
|
|
return descr;
|
|
}
|
|
|
|
bool samx7x_probe(target *t)
|
|
{
|
|
uint32_t cidr = target_mem_read32(t, SAM_CHIPID_CIDR);
|
|
uint32_t exid = 0;
|
|
if (cidr & CHIPID_CIDR_EXT) {
|
|
exid = target_mem_read32(t, SAM_CHIPID_EXID);
|
|
}
|
|
|
|
switch (cidr & CHIPID_CIDR_ARCH_MASK) {
|
|
case CHIPID_CIDR_ARCH_SAME70:
|
|
case CHIPID_CIDR_ARCH_SAMS70:
|
|
case CHIPID_CIDR_ARCH_SAMV71:
|
|
case CHIPID_CIDR_ARCH_SAMV70:
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
struct sam_priv_s *priv_storage = calloc(1, sizeof(*priv_storage));
|
|
if (!priv_storage) { /* calloc failed: heap exhaustion */
|
|
DEBUG_WARN("calloc: failed in %s\n", __func__);
|
|
return false;
|
|
}
|
|
t->target_storage = (void*)priv_storage;
|
|
|
|
priv_storage->descr = samx7x_parse_id(cidr, exid);
|
|
|
|
uint32_t tcm_config = 0;
|
|
sam_gpnvm_get(t, SAMX7X_EEFC_BASE, &tcm_config);
|
|
tcm_config &= GPNVM_SAMX7X_TCM_BIT_MASK;
|
|
|
|
samx7x_add_ram(t, tcm_config, priv_storage->descr.ram_size);
|
|
|
|
sam_add_flash(t, SAMX7X_EEFC_BASE, 0x00400000, priv_storage->descr.flash_size);
|
|
|
|
target_add_commands(t, sam_cmd_list, "SAMX7X");
|
|
|
|
sprintf(priv_storage->sam_variant_string,
|
|
"SAM%c%02d%c%d%c",
|
|
priv_storage->descr.product_code,
|
|
priv_storage->descr.product_id,
|
|
priv_storage->descr.pins,
|
|
priv_storage->descr.density,
|
|
priv_storage->descr.revision);
|
|
|
|
t->driver = priv_storage->sam_variant_string;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool sam3x_probe(target *t)
|
|
{
|
|
uint32_t cidr = target_mem_read32(t, SAM_CHIPID_CIDR);
|
|
size_t size = sam_flash_size(cidr);
|
|
switch (cidr & (CHIPID_CIDR_ARCH_MASK | CHIPID_CIDR_EPROC_MASK)) {
|
|
case CHIPID_CIDR_ARCH_SAM3XxC | CHIPID_CIDR_EPROC_CM3:
|
|
case CHIPID_CIDR_ARCH_SAM3XxE | CHIPID_CIDR_EPROC_CM3:
|
|
case CHIPID_CIDR_ARCH_SAM3XxG | CHIPID_CIDR_EPROC_CM3:
|
|
t->driver = "Atmel SAM3X";
|
|
target_add_ram(t, 0x20000000, 0x200000);
|
|
/* 2 Flash memories back-to-back starting at 0x80000 */
|
|
sam3_add_flash(t, SAM3X_EEFC_BASE(0), 0x80000, size/2);
|
|
sam3_add_flash(t, SAM3X_EEFC_BASE(1), 0x80000 + size/2, size/2);
|
|
target_add_commands(t, sam_cmd_list, "SAM3X");
|
|
return true;
|
|
}
|
|
|
|
cidr = target_mem_read32(t, SAM34NSU_CHIPID_CIDR);
|
|
size = sam_flash_size(cidr);
|
|
switch (cidr & (CHIPID_CIDR_ARCH_MASK | CHIPID_CIDR_EPROC_MASK)) {
|
|
case CHIPID_CIDR_ARCH_SAM3NxA | CHIPID_CIDR_EPROC_CM3:
|
|
case CHIPID_CIDR_ARCH_SAM3NxB | CHIPID_CIDR_EPROC_CM3:
|
|
case CHIPID_CIDR_ARCH_SAM3NxC | CHIPID_CIDR_EPROC_CM3:
|
|
case CHIPID_CIDR_ARCH_SAM3SxA | CHIPID_CIDR_EPROC_CM3:
|
|
case CHIPID_CIDR_ARCH_SAM3SxB | CHIPID_CIDR_EPROC_CM3:
|
|
case CHIPID_CIDR_ARCH_SAM3SxC | CHIPID_CIDR_EPROC_CM3:
|
|
t->driver = "Atmel SAM3N/S";
|
|
target_add_ram(t, 0x20000000, 0x200000);
|
|
/* These devices only have a single bank */
|
|
size = sam_flash_size(cidr);
|
|
sam3_add_flash(t, SAM3N_EEFC_BASE, 0x400000, size);
|
|
target_add_commands(t, sam_cmd_list, "SAM3N/S");
|
|
return true;
|
|
case CHIPID_CIDR_ARCH_SAM3UxC | CHIPID_CIDR_EPROC_CM3:
|
|
case CHIPID_CIDR_ARCH_SAM3UxE | CHIPID_CIDR_EPROC_CM3:
|
|
t->driver = "Atmel SAM3U";
|
|
target_add_ram(t, 0x20000000, 0x200000);
|
|
/* One flash up to 512K at 0x80000 */
|
|
sam3_add_flash(t, SAM3U_EEFC_BASE(0), 0x80000, MIN(size, 0x80000));
|
|
if (size >= 0x80000) {
|
|
/* Larger devices have a second bank at 0x100000 */
|
|
sam3_add_flash(t, SAM3U_EEFC_BASE(1),
|
|
0x100000, 0x80000);
|
|
}
|
|
target_add_commands(t, sam_cmd_list, "SAM3U");
|
|
return true;
|
|
case CHIPID_CIDR_ARCH_SAM4SxA | CHIPID_CIDR_EPROC_CM4:
|
|
case CHIPID_CIDR_ARCH_SAM4SxB | CHIPID_CIDR_EPROC_CM4:
|
|
case CHIPID_CIDR_ARCH_SAM4SxC | CHIPID_CIDR_EPROC_CM4:
|
|
case CHIPID_CIDR_ARCH_SAM4SDB | CHIPID_CIDR_EPROC_CM4:
|
|
case CHIPID_CIDR_ARCH_SAM4SDC | CHIPID_CIDR_EPROC_CM4:
|
|
t->driver = "Atmel SAM4S";
|
|
target_add_ram(t, 0x20000000, 0x400000);
|
|
size = sam_flash_size(cidr);
|
|
if (size <= 0x80000) {
|
|
/* Smaller devices have a single bank */
|
|
sam_add_flash(t, SAM4S_EEFC_BASE(0), 0x400000, size);
|
|
} else {
|
|
/* Larger devices are split evenly between 2 */
|
|
sam_add_flash(t, SAM4S_EEFC_BASE(0), 0x400000, size/2);
|
|
sam_add_flash(t, SAM4S_EEFC_BASE(1),
|
|
0x400000 + size/2, size/2);
|
|
}
|
|
target_add_commands(t, sam_cmd_list, "SAM4S");
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static int
|
|
sam_flash_cmd(target *t, uint32_t base, uint8_t cmd, uint16_t arg)
|
|
{
|
|
DEBUG_INFO("%s: base = 0x%08"PRIx32" cmd = 0x%02X, arg = 0x%06X\n",
|
|
__func__, base, cmd, arg);
|
|
|
|
if(base == 0)
|
|
return -1;
|
|
|
|
target_mem_write32(t, EEFC_FCR(base),
|
|
EEFC_FCR_FKEY | cmd | ((uint32_t)arg << 8));
|
|
|
|
while (!(target_mem_read32(t, EEFC_FSR(base)) & EEFC_FSR_FRDY))
|
|
if(target_check_error(t))
|
|
return -1;
|
|
|
|
uint32_t sr = target_mem_read32(t, EEFC_FSR(base));
|
|
return sr & EEFC_FSR_ERROR;
|
|
}
|
|
|
|
static enum sam_driver sam_driver(target *t)
|
|
{
|
|
if (strcmp(t->driver, "Atmel SAM3X") == 0) {
|
|
return DRIVER_SAM3X;
|
|
}
|
|
if (strcmp(t->driver, "Atmel SAM3U") == 0) {
|
|
return DRIVER_SAM3U;
|
|
}
|
|
if (strcmp(t->driver, "Atmel SAM4S") == 0) {
|
|
return DRIVER_SAM4S;
|
|
}
|
|
if (strcmp(t->driver, "Atmel SAM3N/S") == 0) {
|
|
return DRIVER_SAM3NS;
|
|
}
|
|
return DRIVER_SAMX7X;
|
|
}
|
|
|
|
static int sam_flash_erase(target_flash_s *f, target_addr_t addr, size_t len)
|
|
{
|
|
target *t = f->t;
|
|
uint32_t base = ((struct sam_flash *)f)->eefc_base;
|
|
uint32_t offset = addr - f->start;
|
|
|
|
/* The SAM4S is the only supported device with a page erase command.
|
|
* Erasing is done in 8-page chunks. arg[15:2] contains the page
|
|
* number and arg[1:0] contains 0x1, indicating 8-page chunks.
|
|
*/
|
|
unsigned chunk = offset / SAM_LARGE_PAGE_SIZE;
|
|
|
|
while (len) {
|
|
int16_t arg = chunk | 0x1;
|
|
if(sam_flash_cmd(t, base, EEFC_FCR_FCMD_EPA, arg))
|
|
return -1;
|
|
|
|
if (len > f->blocksize)
|
|
len -= f->blocksize;
|
|
else
|
|
len = 0;
|
|
chunk += 8;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int sam3_flash_erase(target_flash_s *f, target_addr_t addr, size_t len)
|
|
{
|
|
/* The SAM3X/SAM3N don't really have a page erase function.
|
|
* We do nothing here and use Erase/Write page in flash_write.
|
|
*/
|
|
(void)f; (void)addr; (void)len;
|
|
return 0;
|
|
}
|
|
|
|
static int sam_flash_write(target_flash_s *f, target_addr_t dest, const void *src, size_t len)
|
|
{
|
|
target *t = f->t;
|
|
struct sam_flash *sf = (struct sam_flash *)f;
|
|
uint32_t base = sf->eefc_base;
|
|
unsigned chunk = (dest - f->start) / f->writesize;
|
|
|
|
target_mem_write(t, dest, src, len);
|
|
if(sam_flash_cmd(t, base, sf->write_cmd, chunk))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sam_gpnvm_get(target *t, uint32_t base, uint32_t *gpnvm)
|
|
{
|
|
if(!gpnvm || sam_flash_cmd(t, base, EEFC_FCR_FCMD_GGPB, 0))
|
|
return -1;
|
|
|
|
*gpnvm = target_mem_read32(t, EEFC_FRR(base));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool sam_cmd_gpnvm(target *t, int argc, const char **argv)
|
|
{
|
|
if (argc != 2 && argc != 4) {
|
|
goto bad_usage;
|
|
}
|
|
|
|
uint8_t arglen = strlen(argv[1]);
|
|
if (arglen == 0) {
|
|
goto bad_usage;
|
|
}
|
|
|
|
uint32_t base = 0, gpnvm_mask = 0;
|
|
enum sam_driver drv = sam_driver(t);
|
|
switch(drv) {
|
|
case DRIVER_SAM3X:
|
|
gpnvm_mask = 0x7;
|
|
base = SAM3X_EEFC_BASE(0);
|
|
break;
|
|
case DRIVER_SAM3U:
|
|
gpnvm_mask = 0x7;
|
|
base = SAM3U_EEFC_BASE(0);
|
|
break;
|
|
case DRIVER_SAM4S:
|
|
gpnvm_mask = 0x7;
|
|
base = SAM4S_EEFC_BASE(0);
|
|
break;
|
|
case DRIVER_SAM3NS:
|
|
gpnvm_mask = 0x3;
|
|
base = SAM3N_EEFC_BASE;
|
|
break;
|
|
case DRIVER_SAMX7X:
|
|
gpnvm_mask = 0x1BF;
|
|
base = SAMX7X_EEFC_BASE;
|
|
break;
|
|
default:
|
|
/* unknown / invalid driver*/
|
|
return false;
|
|
}
|
|
|
|
uint32_t mask = 0, values = 0;
|
|
if (strncmp(argv[1], "get", arglen) == 0) {
|
|
/* nothing to do */
|
|
} else if (strncmp(argv[1], "set", arglen) == 0) {
|
|
char *eos;
|
|
mask = strtoul(argv[2], &eos, 0);
|
|
values = strtoul(argv[3], &eos, 0);
|
|
|
|
if (mask == 0 || mask & ~gpnvm_mask) {
|
|
/* trying to write invalid bits */
|
|
goto bad_usage;
|
|
}
|
|
|
|
uint32_t work_mask = mask;
|
|
uint32_t work_values = values;
|
|
for (uint16_t bit = 0; work_mask > 0; bit++)
|
|
{
|
|
if (work_mask & 1) {
|
|
uint8_t cmd = (work_values & 1) ? EEFC_FCR_FCMD_SGPB : EEFC_FCR_FCMD_CGPB;
|
|
if(sam_flash_cmd(t, base, cmd, bit))
|
|
return false;
|
|
}
|
|
work_mask >>= 1;
|
|
work_values >>= 1;
|
|
}
|
|
} else {
|
|
goto bad_usage;
|
|
}
|
|
|
|
uint32_t gpnvm = 0;
|
|
if (sam_gpnvm_get(t, base, &gpnvm))
|
|
return false;
|
|
tc_printf(t, "GPNVM: 0x%08X\n", gpnvm);
|
|
|
|
if ((drv == DRIVER_SAMX7X) && (mask & GPNVM_SAMX7X_TCM_BIT_MASK)) {
|
|
struct sam_priv_s * storage = (struct sam_priv_s *)t->target_storage;
|
|
|
|
target_ram_map_free(t);
|
|
samx7x_add_ram(t, gpnvm & GPNVM_SAMX7X_TCM_BIT_MASK, storage->descr.ram_size);
|
|
target_reset(t);
|
|
}
|
|
|
|
return true;
|
|
|
|
bad_usage:
|
|
tc_printf(t, "usage: monitor gpnvm get\n");
|
|
tc_printf(t, "usage: monitor gpnvm set <mask> <val>\n");
|
|
return false;
|
|
}
|