From 0aef6c677418772247eb830d5344a48860ecf2d7 Mon Sep 17 00:00:00 2001 From: Chuck McManis Date: Sun, 20 Nov 2016 14:15:58 -0800 Subject: [PATCH] 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. --- src/Makefile | 1 + src/gdb_main.c | 4 +- src/platforms/common/swdptap.c | 6 + src/target/cortexm.c | 6 + src/target/cortexm.h | 1 + src/target/sam4l.c | 407 +++++++++++++++++++++++++++++++++ src/target/target_internal.h | 2 + 7 files changed, 426 insertions(+), 1 deletion(-) create mode 100644 src/target/sam4l.c diff --git a/src/Makefile b/src/Makefile index 666c080..8bc2659 100644 --- a/src/Makefile +++ b/src/Makefile @@ -48,6 +48,7 @@ SRC = \ nrf51.c \ platform.c \ sam3x.c \ + sam4l.c \ samd.c \ stm32f1.c \ stm32f4.c \ diff --git a/src/gdb_main.c b/src/gdb_main.c index 6e9e958..d41d605 100644 --- a/src/gdb_main.c +++ b/src/gdb_main.c @@ -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); diff --git a/src/platforms/common/swdptap.c b/src/platforms/common/swdptap.c index bcc3ccb..c7c48ac 100644 --- a/src/platforms/common/swdptap.c +++ b/src/platforms/common/swdptap.c @@ -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); diff --git a/src/target/cortexm.c b/src/target/cortexm.c index 96c8fde..7e587a4 100644 --- a/src/target/cortexm.c +++ b/src/target/cortexm.c @@ -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); diff --git a/src/target/cortexm.h b/src/target/cortexm.h index bf1d821..7d42f19 100644 --- a/src/target/cortexm.h +++ b/src/target/cortexm.h @@ -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); diff --git a/src/target/sam4l.c b/src/target/sam4l.c new file mode 100644 index 0000000..1b1eb81 --- /dev/null +++ b/src/target/sam4l.c @@ -0,0 +1,407 @@ +/* + * vim: set noexpandtab ts=4: + * + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2016 Chuck McManis + * + * 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 . + */ + +/* 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; +} diff --git a/src/target/target_internal.h b/src/target/target_internal.h index 75bd743..33f1663 100644 --- a/src/target/target_internal.h +++ b/src/target/target_internal.h @@ -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);