#include "general.h"
#include "target.h"
#include "target_internal.h"
#include "cortexm.h"
#define FLASHCALW_BASE UINT32_C(0x400a0000)
#define FLASHCALW_FCR FLASHCALW_BASE
#define FLASHALCW_FCR_WS1OPT (1U << 7U)
#define FLASHALCW_FCR_FWS (1U << 6U)
#define FLASHALCW_FCR_ECCE (1U << 4U)
#define FLASHALCW_FCR_PROGE (1U << 3U)
#define FLASHALCW_FCR_LOCKE (1U << 2U)
#define FLASHALCW_FCR_FRDY (1U << 0U)
#define FLASHCALW_FCMD (FLASHCALW_BASE + 0x04U)
#define FLASHCALW_FCMD_KEY_MASK 0xffU
#define FLASHCALW_FCMD_KEY_SHIFT 24U
#define FLASHCALW_FCMD_PAGEN_MASK 0xffffU
#define FLASHCALW_FCMD_PAGEN_SHIFT 8U
#define FLASHCALW_FCMD_CMD_MASK 0x3fU
#define FLASHCALW_FCMD_CMD_SHIFT 0U
#define FLASH_CMD_NOP 0U
#define FLASH_CMD_WP 1U
#define FLASH_CMD_EP 2U
#define FLASH_CMD_CPB 3U
#define FLASH_CMD_LP 4U
#define FLASH_CMD_UP 5U
#define FLASH_CMD_EA 6U
#define FLASH_CMD_WGPB 7U
#define FLASH_CMD_EGPB 8U
#define FLASH_CMD_SSB 9U
#define FLASH_CMD_PGPFB 10U
#define FLASH_CMD_EAGPF 11U
#define FLASH_CMD_QPR 12U
#define FLASH_CMD_WUP 13U
#define FLASH_CMD_EUP 14U
#define FLASH_CMD_QPRUP 15U
#define FLASH_CMD_HSEN 16U
#define FLASH_CMD_HSDIS 17U
#define FLASHCALW_FSR (FLASHCALW_BASE + 0x08U)
#define FLASHCALW_FSR_LOCK(x) (1U << (16U + (x)))
#define FLASHCALW_FSR_ECCERR (1U << 9U)
#define FLASHCALW_FSR_ECCERR2 (1U << 8U)
#define FLASHCALW_FSR_HSMODE (1U << 6U)
#define FLASHCALW_FSR_QPRR (1U << 5U)
#define FLASHCALW_FSR_SECURITY (1U << 4U)
#define FLASHCALW_FSR_PROGE (1U << 3U)
#define FLASHCALW_FSR_LOCKE (1U << 2U)
#define FLASHCALW_FSR_FRDY (1U << 0U)
#define FLASHCALW_FPR (FLASHCALW_BASE + 0x0aU)
#define FLASHCALW_FPR_PSZ_MASK 0x7U
#define FLASHCALW_FPR_PSZ_SHIFT 8U
#define FLASHCALW_FPR_FSZ_MASK 0xfU
#define FLASHCALW_FPR_FSZ_SHIFT 0U
#define FLASHCALW_FVR (FLASHCALW_BASE + 0x10U)
#define FLASHCALW_FVR_VARIANT_MASK 0xfU
#define FLASHCALW_FVR_VARIANT_SHIFT 16U
#define FLASHCALW_FVR_VERSION_MASK 0xfffU
#define FLASHCALW_FVR_VERSION_SHIFT 0U
#define FLASHCALW_FGPFRHI (FLASHCALW_BASE + 0x14U)
#define FLASHCALW_FGPFRLO (FLASHCALW_BASE + 0x18U)
#define SAM4L_PAGE_SIZE 512U
#define SAM4L_ARCH 0xb0U
#define SAM4L_CHIPID_CIDR 0x400e0740U
#define CHIPID_CIDR_ARCH_MASK 0xffU
#define CHIPID_CIDR_ARCH_SHIFT 20U
#define CHIPID_CIDR_SRAMSIZ_MASK 0xfU
#define CHIPID_CIDR_SRAMSIZ_SHIFT 16U
#define CHIPID_CIDR_NVPSIZ_MASK 0xfU
#define CHIPID_CIDR_NVPSIZ_SHIFT 8U
#define FLASH_TIMEOUT 1000U
#define SMAP_BASE UINT32_C(0x400a3000)
#define SMAP_CR (SMAP_BASE + 0x00U)
#define SMAP_SR (SMAP_BASE + 0x04U)
#define SMAP_SR_DONE (1U << 0U)
#define SMAP_SR_HCR (1U << 1U)
#define SMAP_SR_BERR (1U << 2U)
#define SMAP_SR_FAIL (1U << 3U)
#define SMAP_SR_LCK (1U << 4U)
#define SMAP_SR_EN (1U << 8U)
#define SMAP_SR_PROT (1U << 9U)
#define SMAP_SR_DBGP (1U << 10U)
#define SMAP_SCR (SMAP_BASE + 0x08U)
#define SMAP_ADDR (SMAP_BASE + 0x0cU)
#define SMAP_LEN (SMAP_BASE + 0x10U)
#define SMAP_DATA (SMAP_BASE + 0x14U)
#define SMAP_VERS (SMAP_BASE + 0x28U)
#define SMAP_CHIPID (SMAP_BASE + 0xf0U)
#define SMAP_EXTID (SMAP_BASE + 0xf4U)
#define SMAP_IDR (SMAP_BASE + 0xfcU)
static void sam4l_extended_reset(target_s *t);
static bool sam4l_flash_erase(target_flash_s *f, target_addr_t addr, size_t len);
static bool sam4l_flash_write(target_flash_s *f, target_addr_t dest, const void *src, size_t len);
static const uint32_t sam4l_ram_size[16] = {
48U * 1024U,
1U * 1024U,
2U * 1024U,
6U * 1024U,
24U * 1024U,
4U * 1024U,
80U * 1024U,
160U * 1024U,
8U * 1024U,
16U * 1024U,
32U * 1024U,
64U * 1024U,
128U * 1024U,
256U * 1024U,
96U * 1024U,
512U * 1024U
};
static const uint32_t sam4l_nvp_size[16] = {
0,
8U * 1024U,
16U * 1024U,
32U * 1024U,
0,
64U * 1024U,
0,
128U * 1024U,
0,
256U * 1024U,
512U * 1024U,
0,
1024U * 1024U,
0,
2048U * 1024U,
0
};
static void sam4l_add_flash(target_s *t, uint32_t addr, size_t length)
{
target_flash_s *f = calloc(1, sizeof(*f));
if (!f) {
DEBUG_ERROR("calloc: failed in %s\n", __func__);
return;
}
f->start = addr;
f->length = length;
f->blocksize = SAM4L_PAGE_SIZE;
f->erase = sam4l_flash_erase;
f->write = sam4l_flash_write;
f->writesize = SAM4L_PAGE_SIZE;
f->erased = 0xff;
target_add_flash(t, f);
}
bool sam4l_probe(target_s *t)
{
const uint32_t cidr = target_mem_read32(t, SAM4L_CHIPID_CIDR);
if (((cidr >> CHIPID_CIDR_ARCH_SHIFT) & CHIPID_CIDR_ARCH_MASK) != SAM4L_ARCH)
return false;
const uint32_t ram_size = sam4l_ram_size[(cidr >> CHIPID_CIDR_SRAMSIZ_SHIFT) & CHIPID_CIDR_SRAMSIZ_MASK];
const uint32_t flash_size = sam4l_nvp_size[(cidr >> CHIPID_CIDR_NVPSIZ_SHIFT) & CHIPID_CIDR_NVPSIZ_MASK];
t->driver = "Atmel SAM4L";
t->extended_reset = sam4l_extended_reset;
target_add_ram(t, 0x20000000, ram_size);
sam4l_add_flash(t, 0x0, flash_size);
DEBUG_INFO("SAM4L - RAM: 0x%" PRIx32 " (%" PRIu32 "kiB), FLASH: 0x%" PRIx32 " (%" PRIu32 "kiB)\n", ram_size,
ram_size / 1024U, flash_size, flash_size / 1024U);
sam4l_extended_reset(t);
if (target_check_error(t))
DEBUG_ERROR("SAM4L: target_check_error returned true\n");
return true;
}
static void sam4l_extended_reset(target_s *t)
{
DEBUG_INFO("SAM4L: Extended Reset\n");
target_mem_write32(t, SMAP_CR, 0x1);
uint32_t reg = target_mem_read32(t, SMAP_SR);
DEBUG_INFO("SMAP_SR has 0x%08" PRIx32 "\n", reg);
if ((reg & SMAP_SR_HCR) != 0) {
target_mem_write32(t, SMAP_SCR, SMAP_SR_HCR);
for (size_t i = 0; i < 250U; i++) {
reg = target_mem_read32(t, SMAP_SR);
if (!(reg & SMAP_SR_HCR))
break;
if (i == 249)
DEBUG_INFO("Reset failed. SMAP_SR has 0x%08" PRIx32 "\n", reg);
}
}
target_check_error(t);
}
static bool sam4l_flash_command(target_s *t, uint32_t page, uint32_t cmd)
{
DEBUG_INFO(
"%s: FSR: 0x%08" PRIx32 ", page = %" PRIu32 ", command = %" PRIu32 "\n", __func__, FLASHCALW_FSR, page, cmd);
platform_timeout_s timeout;
platform_timeout_set(&timeout, FLASH_TIMEOUT);
while (!(target_mem_read32(t, FLASHCALW_FSR) & FLASHCALW_FSR_FRDY)) {
if (platform_timeout_is_expired(&timeout)) {
DEBUG_WARN("%s: Not ready!\n", __func__);
return false;
}
}
const uint32_t cmd_reg = (cmd & FLASHCALW_FCMD_CMD_MASK) |
((page & FLASHCALW_FCMD_PAGEN_MASK) << FLASHCALW_FCMD_PAGEN_SHIFT) | (0xa5U << FLASHCALW_FCMD_KEY_SHIFT);
DEBUG_INFO("%s: Writing command word 0x%08" PRIx32 "\n", __func__, cmd_reg);
target_mem_write32(t, FLASHCALW_FCMD, cmd_reg);
return true;
}
static bool sam4l_flash_write(
target_flash_s *const f, const target_addr_t dest, const void *const src, const size_t len)
{
DEBUG_INFO("%s: dest = 0x%08" PRIx32 ", len %" PRIx32 "\n", __func__, dest, (uint32_t)len);
if (len != SAM4L_PAGE_SIZE)
return false;
target_s *t = f->t;
const uint16_t page = dest / SAM4L_PAGE_SIZE;
if (!sam4l_flash_command(t, 0, FLASH_CMD_CPB))
return false;
const uint32_t *const data = src;
for (size_t offset = 0; offset < SAM4L_PAGE_SIZE; offset += 4U) {
target_mem_write32(t, dest + offset, data[offset / 4U]);
}
return sam4l_flash_command(t, page, FLASH_CMD_WP);
}
static bool sam4l_flash_erase(target_flash_s *f, target_addr_t addr, size_t len)
{
DEBUG_INFO("SAM4L: flash erase address 0x%08" PRIx32 " for %" PRIu32 " bytes\n", addr, (uint32_t)len);
target_s *t = f->t;
for (size_t offset = 0; offset < len; offset += SAM4L_PAGE_SIZE) {
const size_t page = (addr + offset) / SAM4L_PAGE_SIZE;
if (!sam4l_flash_command(t, page, FLASH_CMD_EP))
return false;
}
return true;
}