SAM4L: Add extended reset to target interface

This adds a new function to the internal target interface
to allow the target to get control before reset is complete
so that it can do any additional work. On this target there
is a proprietary internal bit that has to be reset in some
cases to allow the core to continue operating.
This commit is contained in:
Chuck McManis 2016-11-20 14:15:58 -08:00
parent b745799156
commit 0aef6c6774
7 changed files with 426 additions and 1 deletions

View File

@ -48,6 +48,7 @@ SRC = \
nrf51.c \
platform.c \
sam3x.c \
sam4l.c \
samd.c \
stm32f1.c \
stm32f4.c \

View File

@ -96,7 +96,9 @@ int gdb_main_loop(struct target_controller *tc, bool in_syscall)
int size;
bool single_step = false;
DEBUG("Entring GDB protocol main loop\n");
/*
DEBUG("Entering GDB protocol main loop\n");
*/
/* GDB protocol main loop */
while(1) {
SET_IDLE_STATE(1);

View File

@ -36,7 +36,9 @@ static void swdptap_turnaround(uint8_t dir)
if(dir == olddir) return;
olddir = dir;
#if 0
DEBUG("%s", dir ? "\n-> ":"\n<- ");
#endif
if(dir)
SWDIO_MODE_FLOAT();
@ -56,14 +58,18 @@ bool swdptap_bit_in(void)
gpio_set(SWCLK_PORT, SWCLK_PIN);
gpio_clear(SWCLK_PORT, SWCLK_PIN);
#if 0
DEBUG("%d", ret?1:0);
#endif
return ret != 0;
}
void swdptap_bit_out(bool val)
{
#if 0
DEBUG("%d", val);
#endif
swdptap_turnaround(0);

View File

@ -262,6 +262,7 @@ bool cortexm_probe(ADIv5_AP_t *ap)
PROBE(lpc15xx_probe);
PROBE(lpc43xx_probe);
PROBE(sam3x_probe);
PROBE(sam4l_probe);
PROBE(nrf51_probe);
PROBE(samd_probe);
PROBE(lmi_probe);
@ -442,6 +443,11 @@ static void cortexm_reset(target *t)
target_mem_write32(t, CORTEXM_AIRCR,
CORTEXM_AIRCR_VECTKEY | CORTEXM_AIRCR_SYSRESETREQ);
/* If target needs to do something extra (see Atmel SAM4L for example) */
if ((t->target_options & CORTEXM_TOPT_EXTENDED_RESET) != 0) {
t->extended_reset(t);
}
/* Poll for release from reset */
while (target_mem_read32(t, CORTEXM_DHCSR) & CORTEXM_DHCSR_S_RESET_ST);

View File

@ -155,6 +155,7 @@
#define ARM_THUMB_BREAKPOINT 0xBE00
#define CORTEXM_TOPT_INHIBIT_SRST (1 << 2)
#define CORTEXM_TOPT_EXTENDED_RESET (1 << 3)
bool cortexm_probe(ADIv5_AP_t *ap);
ADIv5_AP_t *cortexm_ap(target *t);

407
src/target/sam4l.c Normal file
View File

@ -0,0 +1,407 @@
/*
* vim: set noexpandtab ts=4:
*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2016 Chuck McManis <cmcmanis@mcmanis.com>
*
* 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 SAM4 target specific functions for detecting
* the device, providing the XML memory map and Flash memory programming.
*
* Supported devices: SAM4L2, SAM4L4, SAM4L8
*/
#include "general.h"
#include "target.h"
#include "target_internal.h"
#include "cortexm.h"
/*
* Flash Controller defines
*/
#define FLASHCALW_BASE 0x400A0000
/* Flash Control Register */
#define FLASHCALW_FCR (FLASHCALW_BASE + 0x00)
#define FLASHALCW_FCR_WS1OPT (1 << 7)
#define FLASHALCW_FCR_FWS (1 << 6)
#define FLASHALCW_FCR_ECCE (1 << 4)
#define FLASHALCW_FCR_PROGE (1 << 3)
#define FLASHALCW_FCR_LOCKE (1 << 2)
#define FLASHALCW_FCR_FRDY (1 << 0)
/* Flash Command Register */
#define FLASHCALW_FCMD (FLASHCALW_BASE + 0x04)
#define FLASHCALW_FCMD_KEY_MASK 0xff
#define FLASHCALW_FCMD_KEY_SHIFT 24
#define FLASHCALW_FCMD_PAGEN_MASK 0xffff
#define FLASHCALW_FCMD_PAGEN_SHIFT 8
#define FLASHCALW_FCMD_CMD_MASK 0x3f
#define FLASHCALW_FCMD_CMD_SHIFT 0
#define FLASH_CMD_NOP 0
#define FLASH_CMD_WP 1 /* Write Page */
#define FLASH_CMD_EP 2 /* Erase Page */
#define FLASH_CMD_CPB 3 /* Clear Page Buffer */
#define FLASH_CMD_LP 4 /* Lock page region */
#define FLASH_CMD_UP 5 /* Unlock page region */
#define FLASH_CMD_EA 6 /* Erase All */
#define FLASH_CMD_WGPB 7 /* Write General Purpose Fuse Bit */
#define FLASH_CMD_EGPB 8 /* Erase General Purpose Fuse Bit */
#define FLASH_CMD_SSB 9 /* Set Security Fuses */
#define FLASH_CMD_PGPFB 10 /* Program General Purpose Fuse Byte */
#define FLASH_CMD_EAGPF 11 /* Erase All GP Fuses */
#define FLASH_CMD_QPR 12 /* Quick Page Read (erase check) */
#define FLASH_CMD_WUP 13 /* Write User Page */
#define FLASH_CMD_EUP 14 /* Erase User Page */
#define FLASH_CMD_QPRUP 15 /* Quick Page Read User Page */
#define FLASH_CMD_HSEN 16 /* High Speed Enable */
#define FLASH_CMD_HSDIS 17 /* High Speed Disable */
/* Flash Status Register */
#define FLASHCALW_FSR (FLASHCALW_BASE + 0x08)
#define FLASHCALW_FSR_LOCK(x) (1 << (16 + (x)))
#define FLASHCALW_FSR_ECCERR (1 << 9)
#define FLASHCALW_FSR_ECCERR2 (1 << 8)
#define FLASHCALW_FSR_HSMODE (1 << 6)
#define FLASHCALW_FSR_QPRR (1 << 5)
#define FLASHCALW_FSR_SECURITY (1 << 4)
#define FLASHCALW_FSR_PROGE (1 << 3)
#define FLASHCALW_FSR_LOCKE (1 << 2)
#define FLASHCALW_FSR_FRDY (1 << 0)
/* Flash Parameter Register */
#define FLASHCALW_FPR (FLASHCALW_BASE + 0x0a)
#define FLASHCALW_FPR_PSZ_MASK 0x7 /* page size */
#define FLASHCALW_FPR_PSZ_SHIFT 8
#define FLASHCALW_FPR_FSZ_MASK 0xf /* flash size */
#define FLASHCALW_FPR_FSZ_SHIFT 0
/* Flash Version Register */
#define FLASHCALW_FVR (FLASHCALW_BASE + 0x10)
#define FLASHCALW_FVR_VARIANT_MASK 0xf
#define FLASHCALW_FVR_VARIANT_SHIFT 16
#define FLASHCALW_FVR_VERSION_MASK 0xfff
#define FLASHCALW_FVR_VERSION_SHIFT 0
/* Flash General Purpose Registers (high) */
#define FLASHCALW_FGPFRHI (FLASHCALW_BASE + 0x14)
/* Flash General Purpose Registers (low) */
#define FLASHCALW_FGPFRLO (FLASHCALW_BASE + 0x18)
static int sam4_flash_erase(struct target_flash *f, target_addr addr, size_t len);
int sam4_flash_write(struct target_flash *f, target_addr dest,
const void *src, size_t len);
const struct command_s sam4l_cmds[] = {
{NULL, NULL, NULL}
};
/* why Atmel couldn't make it sequential ... */
static const size_t __ram_size[16] = {
48 * 1024, /* 0: 48K */
1 * 1024, /* 1: 1K */
2 * 1024, /* 2: 2K */
6 * 1024, /* 3: 6K */
24 * 1024, /* 4: 24K */
4 * 1024, /* 5: 4K */
80 * 1024, /* 6: 80K */
160 * 1024, /* 7: 160K */
8 * 1024, /* 8: 8K */
16 * 1024, /* 9: 16K */
32 * 1024, /* 10: 32K */
64 * 1024, /* 11: 64K */
128 * 1024, /* 12: 128K */
256 * 1024, /* 13: 256K */
96 * 1024, /* 14: 96K */
512 * 1024 /* 15: 512K */
};
static const size_t __nvp_size[16] = {
0, /* 0: none */
8 * 1024, /* 1: 8K */
16 * 1024, /* 2: 16K */
32 * 1024, /* 3: 32K */
0, /* 4: reserved */
64 * 1024, /* 5: 64K */
0, /* 6: reserved */
128 * 1024, /* 7: 128K */
0, /* 8: reserved */
256 * 1024, /* 9: 256K */
512 * 1024, /* 10: 512K */
0, /* 11: reserved */
1024 * 1024, /* 12: 1024K (1M) */
0, /* 13: reserved */
2048 * 1024, /* 14: 2048K (2M) */
0 /* 15: reserved */
};
#define SAM4L_PAGE_SIZE 512
#define SAM4L_ARCH 0xb0
#define SAM4L_CHIPID_CIDR 0x400E0740
#define CHIPID_CIDR_ARCH_MASK 0xff
#define CHIPID_CIDR_ARCH_SHIFT 20
#define CHIPID_CIDR_SRAMSIZ_MASK 0xf
#define CHIPID_CIDR_SRAMSIZ_SHIFT 16
#define CHIPID_CIDR_NVPSIZ_MASK 0xf
#define CHIPID_CIDR_NVPSIZ_SHIFT 8
/* All variants of 4L have a 512 byte page */
#define SAM4_PAGE_SIZE 512
/* Arbitrary time to wait for FLASH controller to be ready */
#define FLASH_TIMEOUT 10000
/*
* Populate a target_flash struct with the necessary function pointers
* and constants to describe our flash.
*/
static void sam4_add_flash(target *t, uint32_t addr, size_t length)
{
struct target_flash *f = calloc(1, sizeof(struct target_flash));
f->start = addr;
f->length = length;
f->blocksize = 512;
f->erase = sam4_flash_erase;
f->write = target_flash_write_buffered;
f->done = target_flash_done_buffered;
f->write_buf = sam4_flash_write;
f->buf_size = 512;;
f->align = 512;
f->erased = 0xff;
/* add it into the target structures flash chain */
target_add_flash(t, f);
}
/* Return size of RAM */
static size_t sam_ram_size(uint32_t idcode) {
return __ram_size[((idcode >> CHIPID_CIDR_SRAMSIZ_SHIFT) & CHIPID_CIDR_SRAMSIZ_MASK)];
}
/* Return size of FLASH */
static size_t sam_nvp_size(uint32_t idcode) {
return __nvp_size[((idcode >> CHIPID_CIDR_NVPSIZ_SHIFT) & CHIPID_CIDR_NVPSIZ_MASK)];
}
#define SMAP_BASE 0x400a3000
#define SMAP_CR (SMAP_BASE + 0x00)
#define SMAP_SR (SMAP_BASE + 0x04)
#define SMAP_SR_DONE (1 << 0)
#define SMAP_SR_HCR (1 << 1)
#define SMAP_SR_BERR (1 << 2)
#define SMAP_SR_FAIL (1 << 3)
#define SMAP_SR_LCK (1 << 4)
#define SMAP_SR_EN (1 << 8)
#define SMAP_SR_PROT (1 << 9)
#define SMAP_SR_DBGP (1 << 10)
#define SMAP_SCR (SMAP_BASE + 0x08)
#define SMAP_ADDR (SMAP_BASE + 0x0c)
#define SMAP_LEN (SMAP_BASE + 0x10)
#define SMAP_DATA (SMAP_BASE + 0x14)
#define SMAP_VERS (SMAP_BASE + 0x28)
#define SMAP_CHIPID (SMAP_BASE + 0xf0)
#define SMAP_EXTID (SMAP_BASE + 0xf4)
#define SMAP_IDR (SMAP_BASE + 0xfc)
static void sam4l_reset(target *t);
/*
* The probe function, look where the CIDR register should be, see if
* it matches the SAM4L architecture code.
*
* Figure out from the register how much RAM and FLASH this variant has.
*/
bool sam4l_probe(target *t)
{
uint32_t reg;
size_t ram_size, flash_size;
DEBUG("\nSAM4L: Probe function called\n");
t->idcode = target_mem_read32(t, SAM4L_CHIPID_CIDR);
if (((t->idcode >> CHIPID_CIDR_ARCH_SHIFT) & CHIPID_CIDR_ARCH_MASK) == SAM4L_ARCH) {
t->driver = "Atmel SAM4L";
/* Check to see if our extended reset will fix this */
t->target_options |= CORTEXM_TOPT_INHIBIT_SRST;
t->target_options |= CORTEXM_TOPT_EXTENDED_RESET;
t->extended_reset = sam4l_reset;
ram_size = sam_ram_size(t->idcode);
target_add_ram(t, 0x20000000, ram_size);
flash_size = sam_nvp_size(t->idcode);
sam4_add_flash(t, 0x0, flash_size);
DEBUG("\nSAM4L: RAM = 0x%x (%dK), FLASH = 0x%x (%dK)\n",
ram_size, ram_size / 1024, flash_size, flash_size / 1024);
target_mem_write32(t, SMAP_CR, 0x1); /* enable SMAP */
DEBUG("\nSAM4L: SMAP Enabled\n");
DEBUG("\nSAM4L: Orig CHIPID = 0x%08lx\n", t->idcode);
reg = target_mem_read32(t, SMAP_CHIPID);
DEBUG("\nSAM4L: SMAP CHIPID = 0x%08lx\n", reg);
reg = target_mem_read32(t, SMAP_SR);
DEBUG("\nSAM4L: SMAP is %s\n", (reg & SMAP_SR_EN) ? "Enabled" : "Disabled");
DEBUG("\nSAM4L: Debugger is %s\n", (reg & SMAP_SR_DBGP) ? "Present" : "Not Present");
DEBUG("\nSAM4L: Core is %s\n", (reg & SMAP_SR_HCR) ? "In Reset" : "Running");
sam4l_reset(t);
reg = target_mem_read32(t, SMAP_SR);
DEBUG("\nSAM4L: Core is %s\n", (reg & SMAP_SR_HCR) ? "In Reset" : "Running");
DEBUG("\nSAM4L: SAM4L Selected.\n");
if (target_check_error(t)) {
DEBUG("SAM4L: target_check_error returned true\n");
}
return true;
}
return false;
}
/*
* We've been reset, make sure we take the core out of reset
*/
void
sam4l_reset(target *t)
{
uint32_t reg;
int i;
DEBUG("SAM4L: Extended Reset\n");
reg = target_mem_read32(t, SMAP_SR);
DEBUG("\nSAM4L: SMAP_SR has 0x%08lx\n", reg);
if ((reg & SMAP_SR_HCR) != 0) {
target_mem_write32(t, SMAP_SCR, SMAP_SR_HCR);
i = 0;
do {
reg = target_mem_read32(t, SMAP_SR);
i++;
} while (((reg & SMAP_SR_HCR) != 0) && (i < 100));
if (i == 100) {
DEBUG("\nSAM4L: Reset failed. SMAP_SR has 0x%08lx\n", reg);
}
}
target_check_error(t);
}
/*
* sam4_flash_command
*
* Helper function, wait for the flash controller to be ready to receive a
* command. Then send it the command, page number, and the authorization
* key (always 0xA5) in the command register.
*
* Need the target struct to call the mem_read32 and mem_write32 function
* pointers.
*/
static int
sam4_flash_command(target *t, uint32_t page, uint32_t cmd)
{
uint32_t cmd_reg;
uint32_t status;
int timeout;
DEBUG("\nSAM4L: sam4_flash_command: FSR: 0x%08x, page = %d, command = %d\n",
(unsigned int)(FLASHCALW_FSR), (int) page, (int) cmd);
for (timeout = 0; timeout < FLASH_TIMEOUT; timeout++) {
status = target_mem_read32(t, FLASHCALW_FSR);
if (status & FLASHCALW_FSR_FRDY) {
break;
}
}
if (timeout == FLASH_TIMEOUT) {
DEBUG("\nSAM4L: sam4_flash_command: Not ready! Status = 0x%08x\n", (unsigned int) status);
return -1; /* Failed */
}
cmd_reg = (cmd & FLASHCALW_FCMD_CMD_MASK) |
((page & FLASHCALW_FCMD_PAGEN_MASK) << FLASHCALW_FCMD_PAGEN_SHIFT) |
(0xA5 << FLASHCALW_FCMD_KEY_SHIFT);
DEBUG("\nSAM4L: sam4_flash_command: Wrting command word 0x%08x\n", (unsigned int) cmd_reg);
target_mem_write32(t, FLASHCALW_FCMD, cmd_reg);
return 0;
}
/*
* Write data from 'src' into flash using the algorithim provided by
* Atmel in their data sheet.
*/
int
sam4_flash_write(struct target_flash *f, target_addr addr, const void *src, size_t len)
{
target *t = f->t;
uint32_t *src_data = (uint32_t *)src;
uint16_t page;
DEBUG("\nSAM4L: sam4_flash_write: addr = 0x%08lx, len %d\n", addr, len);
/* This will fail with unaligned writes */
while (len) {
page = addr / SAM4L_PAGE_SIZE;
/* clear the page buffer */
if (sam4_flash_command(t, 0, FLASH_CMD_CPB)) {
return -1;
}
/* Fill it with data to write */
target_mem_write(t, addr, src_data, SAM4L_PAGE_SIZE);
#if 0
for (ndx = 0; ndx < SAM4L_PAGE_SIZE; ndx += 4) {
/*
target_mem_write32(t, addr, *src_data);
addr += 4;
*/
/*
* the page buffer overlaps flash, its only 512 bytes long
* and no matter where you write it from it goes to the page
* you point it to. So we don't need the specific address here
* instead we just write 0 - pagelen (512) and that fills our
* buffer correctly.
*/
target_mem_write32(t, addr+ndx, *src_data);
src_data++;
}
#endif
/* write the page */
if (sam4_flash_command(t, page, FLASH_CMD_WP)) {
return -1;
}
len -= SAM4L_PAGE_SIZE;
addr += SAM4L_PAGE_SIZE;
}
return 0;
}
/*
* Erase flash across the addresses specified by addr and len
*/
static int
sam4_flash_erase(struct target_flash *f, target_addr addr, size_t len)
{
target *t = f->t;
uint16_t page;
DEBUG("SAM4L: flash erase address 0x%08x for %d bytes\n",
(unsigned int) addr, (unsigned int) len);
/* force len to be an integral number of pages */
/* len = (len + (SAM4_PAGE_SIZE - 1)) & ~SAM4_PAGE_SIZE; */
while (len) {
page = addr / SAM4_PAGE_SIZE;
if (sam4_flash_command(t, page, FLASH_CMD_EP)) {
return -1;
}
len -= SAM4_PAGE_SIZE;
addr += SAM4_PAGE_SIZE;
}
return 0;
}

View File

@ -99,6 +99,7 @@ struct target_s {
/* Halt/resume functions */
void (*reset)(target *t);
void (*extended_reset)(target *t);
void (*halt_request)(target *t);
enum target_halt_reason (*halt_poll)(target *t, target_addr *watch);
void (*halt_resume)(target *t, bool step);
@ -176,6 +177,7 @@ bool lpc11xx_probe(target *t);
bool lpc15xx_probe(target *t);
bool lpc43xx_probe(target *t);
bool sam3x_probe(target *t);
bool sam4l_probe(target *t);
bool nrf51_probe(target *t);
bool samd_probe(target *t);
bool kinetis_probe(target *t);