SAM4L: Add target driver for SAM4L

This commit adds a target driver for SAM4L and it depends on the
extended reset change.
This commit is contained in:
Chuck McManis 2016-12-01 23:45:57 -08:00
parent 0aef6c6774
commit 469ecbf0f9
1 changed files with 60 additions and 57 deletions

View File

@ -103,8 +103,9 @@
/* Flash General Purpose Registers (low) */ /* Flash General Purpose Registers (low) */
#define FLASHCALW_FGPFRLO (FLASHCALW_BASE + 0x18) #define FLASHCALW_FGPFRLO (FLASHCALW_BASE + 0x18)
static int sam4_flash_erase(struct target_flash *f, target_addr addr, size_t len); static void sam4l_extended_reset(target *t);
int sam4_flash_write(struct target_flash *f, target_addr dest, static int sam4l_flash_erase(struct target_flash *f, target_addr addr, size_t len);
static int sam4l_flash_write_buf(struct target_flash *f, target_addr dest,
const void *src, size_t len); const void *src, size_t len);
const struct command_s sam4l_cmds[] = { const struct command_s sam4l_cmds[] = {
@ -171,18 +172,17 @@ static const size_t __nvp_size[16] = {
* Populate a target_flash struct with the necessary function pointers * Populate a target_flash struct with the necessary function pointers
* and constants to describe our flash. * and constants to describe our flash.
*/ */
static void sam4_add_flash(target *t, uint32_t addr, size_t length) static void sam4l_add_flash(target *t, uint32_t addr, size_t length)
{ {
struct target_flash *f = calloc(1, sizeof(struct target_flash)); struct target_flash *f = calloc(1, sizeof(struct target_flash));
f->start = addr; f->start = addr;
f->length = length; f->length = length;
f->blocksize = 512; f->blocksize = 512;
f->erase = sam4_flash_erase; f->erase = sam4l_flash_erase;
f->write = target_flash_write_buffered; f->write = target_flash_write_buffered;
f->done = target_flash_done_buffered; f->done = target_flash_done_buffered;
f->write_buf = sam4_flash_write; f->write_buf = sam4l_flash_write_buf;
f->buf_size = 512;; f->buf_size = 512;;
f->align = 512;
f->erased = 0xff; f->erased = 0xff;
/* add it into the target structures flash chain */ /* add it into the target structures flash chain */
target_add_flash(t, f); target_add_flash(t, f);
@ -220,7 +220,6 @@ static size_t sam_nvp_size(uint32_t idcode) {
#define SMAP_EXTID (SMAP_BASE + 0xf4) #define SMAP_EXTID (SMAP_BASE + 0xf4)
#define SMAP_IDR (SMAP_BASE + 0xfc) #define SMAP_IDR (SMAP_BASE + 0xfc)
static void sam4l_reset(target *t);
/* /*
* The probe function, look where the CIDR register should be, see if * The probe function, look where the CIDR register should be, see if
@ -230,37 +229,29 @@ static void sam4l_reset(target *t);
*/ */
bool sam4l_probe(target *t) bool sam4l_probe(target *t)
{ {
uint32_t reg;
size_t ram_size, flash_size; size_t ram_size, flash_size;
DEBUG("\nSAM4L: Probe function called\n"); DEBUG("\nSAM4L: Probe function called\n");
t->idcode = target_mem_read32(t, SAM4L_CHIPID_CIDR); t->idcode = target_mem_read32(t, SAM4L_CHIPID_CIDR);
if (((t->idcode >> CHIPID_CIDR_ARCH_SHIFT) & CHIPID_CIDR_ARCH_MASK) == SAM4L_ARCH) { if (((t->idcode >> CHIPID_CIDR_ARCH_SHIFT) & CHIPID_CIDR_ARCH_MASK) == SAM4L_ARCH) {
t->driver = "Atmel SAM4L"; t->driver = "Atmel SAM4L";
#ifdef SAM4_DISABLE_SRST
/* Check to see if our extended reset will fix this */ /* Check to see if our extended reset will fix this */
t->target_options |= CORTEXM_TOPT_INHIBIT_SRST; t->target_options |= CORTEXM_TOPT_INHIBIT_SRST;
#endif
/* this option says we need to do "extra" stuff after reset */
t->target_options |= CORTEXM_TOPT_EXTENDED_RESET; t->target_options |= CORTEXM_TOPT_EXTENDED_RESET;
t->extended_reset = sam4l_reset; t->extended_reset = sam4l_extended_reset;
ram_size = sam_ram_size(t->idcode); ram_size = sam_ram_size(t->idcode);
target_add_ram(t, 0x20000000, ram_size); target_add_ram(t, 0x20000000, ram_size);
flash_size = sam_nvp_size(t->idcode); flash_size = sam_nvp_size(t->idcode);
sam4_add_flash(t, 0x0, flash_size); sam4l_add_flash(t, 0x0, flash_size);
DEBUG("\nSAM4L: RAM = 0x%x (%dK), FLASH = 0x%x (%dK)\n", DEBUG("\nSAM4L: RAM = 0x%x (%dK), FLASH = 0x%x (%dK)\n",
ram_size, ram_size / 1024, flash_size, flash_size / 1024); (unsigned int) ram_size, (unsigned int) (ram_size / 1024),
(unsigned int) flash_size, (unsigned int)(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");
/* enable SMAP if not, check for HCR and reset if set */
sam4l_extended_reset(t);
DEBUG("\nSAM4L: SAM4L Selected.\n"); DEBUG("\nSAM4L: SAM4L Selected.\n");
if (target_check_error(t)) { if (target_check_error(t)) {
DEBUG("SAM4L: target_check_error returned true\n"); DEBUG("SAM4L: target_check_error returned true\n");
@ -274,30 +265,34 @@ bool sam4l_probe(target *t)
* We've been reset, make sure we take the core out of reset * We've been reset, make sure we take the core out of reset
*/ */
void void
sam4l_reset(target *t) sam4l_extended_reset(target *t)
{ {
uint32_t reg; uint32_t reg;
int i; int i;
DEBUG("SAM4L: Extended Reset\n"); DEBUG("SAM4L: Extended Reset\n");
/* enable SMAP in case we're dealing with a non-TCK SRST */
target_mem_write32(t, SMAP_CR, 0x1); /* enable SMAP */
reg = target_mem_read32(t, SMAP_SR); reg = target_mem_read32(t, SMAP_SR);
DEBUG("\nSAM4L: SMAP_SR has 0x%08lx\n", reg); DEBUG("\nSAM4L: SMAP_SR has 0x%08lx\n", (long unsigned int) reg);
if ((reg & SMAP_SR_HCR) != 0) { if ((reg & SMAP_SR_HCR) != 0) {
/* write '1' bit to the status clear register */
target_mem_write32(t, SMAP_SCR, SMAP_SR_HCR); target_mem_write32(t, SMAP_SCR, SMAP_SR_HCR);
i = 0; /* waiting 250 loops for it to reset is arbitrary, it should happen right away */
do { for (i = 0; i < 250; i++) {
reg = target_mem_read32(t, SMAP_SR); reg = target_mem_read32(t, SMAP_SR);
i++; }
} while (((reg & SMAP_SR_HCR) != 0) && (i < 100)); /* not sure what to do if we can't reset that bit */
if (i == 100) { if (i > 249) {
DEBUG("\nSAM4L: Reset failed. SMAP_SR has 0x%08lx\n", reg); DEBUG("\nSAM4L: Reset failed. SMAP_SR has 0x%08lx\n", (long unsigned int) reg);
} }
} }
/* reset bus error if for some reason SMAP was disabled */
target_check_error(t); target_check_error(t);
} }
/* /*
* sam4_flash_command * sam4l_flash_command
* *
* Helper function, wait for the flash controller to be ready to receive a * Helper function, wait for the flash controller to be ready to receive a
* command. Then send it the command, page number, and the authorization * command. Then send it the command, page number, and the authorization
@ -307,13 +302,14 @@ sam4l_reset(target *t)
* pointers. * pointers.
*/ */
static int static int
sam4_flash_command(target *t, uint32_t page, uint32_t cmd) sam4l_flash_command(target *t, uint32_t page, uint32_t cmd)
{ {
uint32_t cmd_reg; uint32_t cmd_reg;
uint32_t status; uint32_t status;
int timeout; int timeout;
DEBUG("\nSAM4L: sam4_flash_command: FSR: 0x%08x, page = %d, command = %d\n", DEBUG("\nSAM4L: sam4l_flash_command: FSR: 0x%08x, page = %d, command = %d\n",
(unsigned int)(FLASHCALW_FSR), (int) page, (int) cmd); (unsigned int)(FLASHCALW_FSR), (int) page, (int) cmd);
/* wait for Flash controller ready */
for (timeout = 0; timeout < FLASH_TIMEOUT; timeout++) { for (timeout = 0; timeout < FLASH_TIMEOUT; timeout++) {
status = target_mem_read32(t, FLASHCALW_FSR); status = target_mem_read32(t, FLASHCALW_FSR);
if (status & FLASHCALW_FSR_FRDY) { if (status & FLASHCALW_FSR_FRDY) {
@ -321,14 +317,17 @@ sam4_flash_command(target *t, uint32_t page, uint32_t cmd)
} }
} }
if (timeout == FLASH_TIMEOUT) { if (timeout == FLASH_TIMEOUT) {
DEBUG("\nSAM4L: sam4_flash_command: Not ready! Status = 0x%08x\n", (unsigned int) status); DEBUG("\nSAM4L: sam4l_flash_command: Not ready! Status = 0x%08x\n", (unsigned int) status);
return -1; /* Failed */ return -1; /* Failed */
} }
/* load up the new command */
cmd_reg = (cmd & FLASHCALW_FCMD_CMD_MASK) | cmd_reg = (cmd & FLASHCALW_FCMD_CMD_MASK) |
((page & FLASHCALW_FCMD_PAGEN_MASK) << FLASHCALW_FCMD_PAGEN_SHIFT) | ((page & FLASHCALW_FCMD_PAGEN_MASK) << FLASHCALW_FCMD_PAGEN_SHIFT) |
(0xA5 << FLASHCALW_FCMD_KEY_SHIFT); (0xA5 << FLASHCALW_FCMD_KEY_SHIFT);
DEBUG("\nSAM4L: sam4_flash_command: Wrting command word 0x%08x\n", (unsigned int) cmd_reg); DEBUG("\nSAM4L: sam4l_flash_command: Wrting command word 0x%08x\n", (unsigned int) cmd_reg);
/* and kick it off */
target_mem_write32(t, FLASHCALW_FCMD, cmd_reg); target_mem_write32(t, FLASHCALW_FCMD, cmd_reg);
/* don't actually wait for it to finish, the next command will stall if it is not done */
return 0; return 0;
} }
@ -336,30 +335,31 @@ sam4_flash_command(target *t, uint32_t page, uint32_t cmd)
* Write data from 'src' into flash using the algorithim provided by * Write data from 'src' into flash using the algorithim provided by
* Atmel in their data sheet. * Atmel in their data sheet.
*/ */
int static int
sam4_flash_write(struct target_flash *f, target_addr addr, const void *src, size_t len) sam4l_flash_write_buf(struct target_flash *f, target_addr addr, const void *src, size_t len)
{ {
target *t = f->t; target *t = f->t;
uint32_t *src_data = (uint32_t *)src; uint32_t *src_data = (uint32_t *)src;
uint32_t ndx;
uint16_t page; uint16_t page;
DEBUG("\nSAM4L: sam4_flash_write: addr = 0x%08lx, len %d\n", addr, len); DEBUG("\nSAM4L: sam4l_flash_write_buf: addr = 0x%08lx, len %d\n", (long unsigned int) addr, (int) len);
/* This will fail with unaligned writes */ /* This will fail with unaligned writes, the write_buf version */
while (len) { while (len) {
page = addr / SAM4L_PAGE_SIZE; page = addr / SAM4L_PAGE_SIZE;
/* clear the page buffer */ /* clear the page buffer */
if (sam4_flash_command(t, 0, FLASH_CMD_CPB)) { if (sam4l_flash_command(t, 0, FLASH_CMD_CPB)) {
return -1; return -1;
} }
/* Fill it with data to write */
target_mem_write(t, addr, src_data, SAM4L_PAGE_SIZE); /* Now fill page buffer with our 512 bytes of data */
#if 0
for (ndx = 0; ndx < SAM4L_PAGE_SIZE; ndx += 4) { /* I did try to use target_mem_write however that resulted in the
/* * last 64 bits (8 bytes) to be incorrect on even pages (0, 2, 4, ...)
target_mem_write32(t, addr, *src_data); * since it works this way I've not investigated further.
addr += 4;
*/ */
for (ndx = 0; ndx < SAM4L_PAGE_SIZE; ndx += 4) {
/* /*
* the page buffer overlaps flash, its only 512 bytes long * the page buffer overlaps flash, its only 512 bytes long
* and no matter where you write it from it goes to the page * and no matter where you write it from it goes to the page
@ -370,11 +370,11 @@ sam4_flash_write(struct target_flash *f, target_addr addr, const void *src, size
target_mem_write32(t, addr+ndx, *src_data); target_mem_write32(t, addr+ndx, *src_data);
src_data++; src_data++;
} }
#endif
/* write the page */ /* write the page */
if (sam4_flash_command(t, page, FLASH_CMD_WP)) { if (sam4l_flash_command(t, page, FLASH_CMD_WP)) {
return -1; return -1;
} }
/* it should be done after one page, but in case it isn't */
len -= SAM4L_PAGE_SIZE; len -= SAM4L_PAGE_SIZE;
addr += SAM4L_PAGE_SIZE; addr += SAM4L_PAGE_SIZE;
} }
@ -385,19 +385,22 @@ sam4_flash_write(struct target_flash *f, target_addr addr, const void *src, size
* Erase flash across the addresses specified by addr and len * Erase flash across the addresses specified by addr and len
*/ */
static int static int
sam4_flash_erase(struct target_flash *f, target_addr addr, size_t len) sam4l_flash_erase(struct target_flash *f, target_addr addr, size_t len)
{ {
target *t = f->t; target *t = f->t;
uint16_t page; uint16_t page;
DEBUG("SAM4L: flash erase address 0x%08x for %d bytes\n", DEBUG("SAM4L: flash erase address 0x%08x for %d bytes\n",
(unsigned int) addr, (unsigned int) len); (unsigned int) addr, (unsigned int) len);
/* force len to be an integral number of pages */ /*
/* len = (len + (SAM4_PAGE_SIZE - 1)) & ~SAM4_PAGE_SIZE; */ * NB: if addr isn't aligned to a page boundary, or length
* is not an even multiple of page sizes, we may end up
* erasing data we didn't intend to.
*/
while (len) { while (len) {
page = addr / SAM4_PAGE_SIZE; page = addr / SAM4_PAGE_SIZE;
if (sam4_flash_command(t, page, FLASH_CMD_EP)) { if (sam4l_flash_command(t, page, FLASH_CMD_EP)) {
return -1; return -1;
} }
len -= SAM4_PAGE_SIZE; len -= SAM4_PAGE_SIZE;