From 42c24acebd23d758c543c9bf2c2b97bdbd083c35 Mon Sep 17 00:00:00 2001 From: Morgan Quigley Date: Sun, 6 Sep 2015 11:31:09 -0700 Subject: [PATCH] flash: driver for Atmel SAMV, SAMS, and SAME This is a driver for the Atmel Cortex-M7 SAMV, SAMS, and SAME. I started with the at91sam4.c driver and then restructured it significantly to try to simplify it and limit the functionality to just a flash driver, as well as to comply with the style guide. Change-Id: I5340bf61f067265b8ebabd3adad45be45324b707 Signed-off-by: Morgan Quigley Reviewed-on: http://openocd.zylin.com/2952 Tested-by: jenkins Reviewed-by: Tomas Vanek --- doc/openocd.texi | 7 + src/flash/nor/Makefile.am | 1 + src/flash/nor/atsamv.c | 737 ++++++++++++++++++++++++++++++++++++++ src/flash/nor/drivers.c | 2 + tcl/target/atsamv.cfg | 3 + 5 files changed, 750 insertions(+) create mode 100644 src/flash/nor/atsamv.c diff --git a/doc/openocd.texi b/doc/openocd.texi index 76c9766c15..655a3537be 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -5068,6 +5068,13 @@ Command is used internally in event event reset-deassert-post. @end deffn @end deffn +@deffn {Flash Driver} atsamv +@cindex atsamv +All members of the ATSAMV, ATSAMS, and ATSAME families from +Atmel include internal flash and use ARM's Cortex-M7 core. +This driver uses the same cmd names/syntax as @xref{at91sam3}. +@end deffn + @deffn {Flash Driver} at91sam7 All members of the AT91SAM7 microcontroller family from Atmel include internal flash and use ARM7TDMI cores. The driver automatically diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am index 5aa4f6b0a9..b065bb076f 100644 --- a/src/flash/nor/Makefile.am +++ b/src/flash/nor/Makefile.am @@ -15,6 +15,7 @@ NOR_DRIVERS = \ at91samd.c \ at91sam3.c \ at91sam7.c \ + atsamv.c \ avrf.c \ cfi.c \ efm32.c \ diff --git a/src/flash/nor/atsamv.c b/src/flash/nor/atsamv.c new file mode 100644 index 0000000000..08f8bb8ba7 --- /dev/null +++ b/src/flash/nor/atsamv.c @@ -0,0 +1,737 @@ +/*************************************************************************** + * Copyright (C) 2009 by Duane Ellis * + * openocd@duaneellis.com * + * * + * Copyright (C) 2010 by Olaf Lüke (at91sam3s* support) * + * olaf@uni-paderborn.de * + * * + * Copyright (C) 2011 by Olivier Schonken, Jim Norris * + * (at91sam3x* & at91sam4 support)* * + * * + * Copyright (C) 2015 Morgan Quigley * + * (atsamv, atsams, and atsame support) * + * * + * 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 2 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. * + * * + ***************************************************************************/ + +/* Some of the the lower level code was based on code supplied by + * ATMEL under this copyright. */ + +/* BEGIN ATMEL COPYRIGHT */ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2009, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ +/* END ATMEL COPYRIGHT */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "imp.h" +#include + +#define REG_NAME_WIDTH (12) + +#define SAMV_EFC_FCMD_GETD (0x0) /* (EFC) Get Flash Descriptor */ +#define SAMV_EFC_FCMD_WP (0x1) /* (EFC) Write Page */ +#define SAMV_EFC_FCMD_WPL (0x2) /* (EFC) Write Page and Lock */ +#define SAMV_EFC_FCMD_EWP (0x3) /* (EFC) Erase Page and Write Page */ +#define SAMV_EFC_FCMD_EWPL (0x4) /* (EFC) Erase Page, Write Page then Lock*/ +#define SAMV_EFC_FCMD_EA (0x5) /* (EFC) Erase All */ +#define SAMV_EFC_FCMD_EPA (0x7) /* (EFC) Erase pages */ +#define SAMV_EFC_FCMD_SLB (0x8) /* (EFC) Set Lock Bit */ +#define SAMV_EFC_FCMD_CLB (0x9) /* (EFC) Clear Lock Bit */ +#define SAMV_EFC_FCMD_GLB (0xA) /* (EFC) Get Lock Bit */ +#define SAMV_EFC_FCMD_SFB (0xB) /* (EFC) Set Fuse Bit */ +#define SAMV_EFC_FCMD_CFB (0xC) /* (EFC) Clear Fuse Bit */ +#define SAMV_EFC_FCMD_GFB (0xD) /* (EFC) Get Fuse Bit */ + +#define OFFSET_EFC_FMR 0 +#define OFFSET_EFC_FCR 4 +#define OFFSET_EFC_FSR 8 +#define OFFSET_EFC_FRR 12 + +#define SAMV_CHIPID_CIDR (0x400E0940) +#define SAMV_NUM_GPNVM_BITS 9 +#define SAMV_CONTROLLER_ADDR (0x400e0c00) +#define SAMV_SECTOR_SIZE 16384 +#define SAMV_PAGE_SIZE 512 +#define SAMV_FLASH_BASE 0x00400000 + +extern struct flash_driver atsamv_flash; + +struct samv_flash_bank { + int probed; + unsigned size_bytes; + unsigned gpnvm[SAMV_NUM_GPNVM_BITS]; +}; + +/* The actual sector size of the SAMV7 flash memory is 128K bytes. + * 16 sectors for a 2048KB device. The lock regions are 16KB per lock + * region, with a 2048KB device having 128 lock regions. + * For the best results, num_sectors is thus set to the number of lock + * regions, and the sector_size set to the lock region size. Page + * erases are used to erase 16KB sections when programming */ + +static int samv_efc_get_status(struct target *target, uint32_t *v) +{ + int r = target_read_u32(target, SAMV_CONTROLLER_ADDR + OFFSET_EFC_FSR, v); + return r; +} + +static int samv_efc_get_result(struct target *target, uint32_t *v) +{ + uint32_t rv; + int r = target_read_u32(target, SAMV_CONTROLLER_ADDR + OFFSET_EFC_FRR, &rv); + if (v) + *v = rv; + return r; +} + +static int samv_efc_start_command(struct target *target, + unsigned command, unsigned argument) +{ + uint32_t v; + samv_efc_get_status(target, &v); + if (!(v & 1)) { + LOG_ERROR("flash controller is not ready"); + return ERROR_FAIL; + } + + v = (0x5A << 24) | (argument << 8) | command; + LOG_DEBUG("starting flash command: 0x%08x", (unsigned int)(v)); + int r = target_write_u32(target, SAMV_CONTROLLER_ADDR + OFFSET_EFC_FCR, v); + if (r != ERROR_OK) + LOG_DEBUG("write failed"); + return r; +} + +static int samv_efc_perform_command(struct target *target, + unsigned command, unsigned argument, uint32_t *status) +{ + int r; + uint32_t v; + long long ms_now, ms_end; + + if (status) + *status = 0; + + r = samv_efc_start_command(target, command, argument); + if (r != ERROR_OK) + return r; + + ms_end = 10000 + timeval_ms(); + + do { + r = samv_efc_get_status(target, &v); + if (r != ERROR_OK) + return r; + ms_now = timeval_ms(); + if (ms_now > ms_end) { + /* error */ + LOG_ERROR("Command timeout"); + return ERROR_FAIL; + } + } while ((v & 1) == 0); + + /* if requested, copy the flash controller error bits back to the caller */ + if (status) + *status = (v & 0x6); + return ERROR_OK; +} + +static int samv_erase_pages(struct target *target, + int first_page, int num_pages, uint32_t *status) +{ + uint8_t erase_pages; + switch (num_pages) { + case 4: + erase_pages = 0x00; + break; + case 8: + erase_pages = 0x01; + break; + case 16: + erase_pages = 0x02; + break; + case 32: + erase_pages = 0x03; + break; + default: + erase_pages = 0x00; + break; + } + + /* SAMV_EFC_FCMD_EPA + * According to the datasheet FARG[15:2] defines the page from which + * the erase will start.This page must be modulo 4, 8, 16 or 32 + * according to the number of pages to erase. FARG[1:0] defines the + * number of pages to be erased. Previously (firstpage << 2) was used + * to conform to this, seems it should not be shifted... + */ + return samv_efc_perform_command(target, SAMV_EFC_FCMD_EPA, + first_page | erase_pages, status); +} + +static int samv_get_gpnvm(struct target *target, unsigned gpnvm, unsigned *out) +{ + uint32_t v; + int r; + + if (gpnvm >= SAMV_NUM_GPNVM_BITS) { + LOG_ERROR("invalid gpnvm %d, max: %d", gpnvm, SAMV_NUM_GPNVM_BITS); + return ERROR_FAIL; + } + + r = samv_efc_perform_command(target, SAMV_EFC_FCMD_GFB, 0, NULL); + if (r != ERROR_OK) { + LOG_ERROR("samv_get_gpnvm failed"); + return r; + } + + r = samv_efc_get_result(target, &v); + + if (out) + *out = (v >> gpnvm) & 1; + + return r; +} + +static int samv_clear_gpnvm(struct target *target, unsigned gpnvm) +{ + int r; + unsigned v; + + if (gpnvm >= SAMV_NUM_GPNVM_BITS) { + LOG_ERROR("invalid gpnvm %d, max: %d", gpnvm, SAMV_NUM_GPNVM_BITS); + return ERROR_FAIL; + } + r = samv_get_gpnvm(target, gpnvm, &v); + if (r != ERROR_OK) { + LOG_DEBUG("get gpnvm failed: %d", r); + return r; + } + r = samv_efc_perform_command(target, SAMV_EFC_FCMD_CFB, gpnvm, NULL); + LOG_DEBUG("clear gpnvm result: %d", r); + return r; +} + +static int samv_set_gpnvm(struct target *target, unsigned gpnvm) +{ + int r; + unsigned v; + if (gpnvm >= SAMV_NUM_GPNVM_BITS) { + LOG_ERROR("invalid gpnvm %d, max: %d", gpnvm, SAMV_NUM_GPNVM_BITS); + return ERROR_FAIL; + } + + r = samv_get_gpnvm(target, gpnvm, &v); + if (r != ERROR_OK) + return r; + if (v) { + r = ERROR_OK; /* the gpnvm bit is already set */ + } else { + /* we need to set it */ + r = samv_efc_perform_command(target, SAMV_EFC_FCMD_SFB, gpnvm, NULL); + } + return r; +} + +static int samv_flash_unlock(struct target *target, + unsigned start_sector, unsigned end_sector) +{ + int r; + uint32_t status; + uint32_t pg; + uint32_t pages_per_sector; + + /* todo: look into this... i think this should be done on lock regions */ + pages_per_sector = SAMV_SECTOR_SIZE / SAMV_PAGE_SIZE; + while (start_sector <= end_sector) { + pg = start_sector * pages_per_sector; + r = samv_efc_perform_command(target, SAMV_EFC_FCMD_CLB, pg, &status); + if (r != ERROR_OK) + return r; + start_sector++; + } + return ERROR_OK; +} + +static int samv_flash_lock(struct target *target, + unsigned start_sector, unsigned end_sector) +{ + uint32_t status; + uint32_t pg; + uint32_t pages_per_sector; + int r; + + /* todo: look into this... i think this should be done on lock regions */ + pages_per_sector = SAMV_SECTOR_SIZE / SAMV_PAGE_SIZE; + while (start_sector <= end_sector) { + pg = start_sector * pages_per_sector; + r = samv_efc_perform_command(target, SAMV_EFC_FCMD_SLB, pg, &status); + if (r != ERROR_OK) + return r; + start_sector++; + } + return ERROR_OK; +} + +static int samv_protect_check(struct flash_bank *bank) +{ + int r; + uint32_t v[4] = {0}; + + r = samv_efc_perform_command(bank->target, SAMV_EFC_FCMD_GLB, 0, NULL); + if (r == ERROR_OK) { + samv_efc_get_result(bank->target, &v[0]); + samv_efc_get_result(bank->target, &v[1]); + samv_efc_get_result(bank->target, &v[2]); + r = samv_efc_get_result(bank->target, &v[3]); + } + if (r != ERROR_OK) + return r; + + for (int x = 0; x < bank->num_sectors; x++) + bank->sectors[x].is_protected = (!!(v[x >> 5] & (1 << (x % 32)))); + return ERROR_OK; +} + +FLASH_BANK_COMMAND_HANDLER(samv_flash_bank_command) +{ + LOG_INFO("flash bank command"); + struct samv_flash_bank *samv_info; + samv_info = calloc(1, sizeof(struct samv_flash_bank)); + bank->driver_priv = samv_info; + return ERROR_OK; +} + +static int samv_get_device_id(struct flash_bank *bank, uint32_t *device_id) +{ + return target_read_u32(bank->target, SAMV_CHIPID_CIDR, device_id); +} + +static int samv_probe(struct flash_bank *bank) +{ + uint32_t device_id; + int r = samv_get_device_id(bank, &device_id); + if (r != ERROR_OK) + return r; + LOG_INFO("device id = 0x%08" PRIx32 "", device_id); + + uint8_t eproc = (device_id >> 5) & 0x7; + if (eproc != 0) { + LOG_ERROR("unexpected eproc code: %d was expecting 0 (cortex-m7)", eproc); + return ERROR_FAIL; + } + + uint8_t nvm_size_code = (device_id >> 8) & 0xf; + switch (nvm_size_code) { + case 12: + bank->size = 1024 * 1024; + break; + case 14: + bank->size = 2048 * 1024; + break; + default: + LOG_ERROR("unrecognized flash size code: %d", nvm_size_code); + return ERROR_FAIL; + break; + } + + struct samv_flash_bank *samv_info = bank->driver_priv; + samv_info->size_bytes = bank->size; + samv_info->probed = 1; + + bank->base = SAMV_FLASH_BASE; + bank->num_sectors = bank->size / SAMV_SECTOR_SIZE; + bank->sectors = calloc(bank->num_sectors, sizeof(struct flash_sector)); + for (int s = 0; s < (int)bank->num_sectors; s++) { + bank->sectors[s].size = SAMV_SECTOR_SIZE; + bank->sectors[s].offset = s * SAMV_SECTOR_SIZE; + bank->sectors[s].is_erased = -1; + bank->sectors[s].is_protected = -1; + } + + r = samv_protect_check(bank); + if (r != ERROR_OK) + return r; + + return ERROR_OK; +} + +static int samv_auto_probe(struct flash_bank *bank) +{ + struct samv_flash_bank *samv_info = bank->driver_priv; + if (samv_info->probed) + return ERROR_OK; + return samv_probe(bank); +} + +static int samv_erase(struct flash_bank *bank, int first, int last) +{ + const int page_count = 32; /* 32 pages equals 16 KB lock region */ + + if (bank->target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + int r = samv_auto_probe(bank); + if (r != ERROR_OK) + return r; + + /* easy case: we've been requested to erase the entire flash */ + if ((first == 0) && ((last + 1) == (int)(bank->num_sectors))) + return samv_efc_perform_command(bank->target, SAMV_EFC_FCMD_EA, 0, NULL); + + LOG_INFO("erasing lock regions %d-%d...", first, last); + + for (int i = first; i <= last; i++) { + uint32_t status; + r = samv_erase_pages(bank->target, (i * page_count), page_count, &status); + LOG_INFO("erasing lock region %d", i); + if (r != ERROR_OK) + LOG_ERROR("error performing erase page @ lock region number %d", + (unsigned int)(i)); + if (status & (1 << 2)) { + LOG_ERROR("lock region %d is locked", (unsigned int)(i)); + return ERROR_FAIL; + } + if (status & (1 << 1)) { + LOG_ERROR("flash command error @lock region %d", (unsigned int)(i)); + return ERROR_FAIL; + } + } + return ERROR_OK; +} + +static int samv_protect(struct flash_bank *bank, int set, int first, int last) +{ + if (bank->target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + int r; + if (set) + r = samv_flash_lock(bank->target, (unsigned)(first), (unsigned)(last)); + else + r = samv_flash_unlock(bank->target, (unsigned)(first), (unsigned)(last)); + + return r; +} + +static int samv_page_read(struct target *target, + unsigned page_num, uint8_t *buf) +{ + uint32_t addr = SAMV_FLASH_BASE + page_num * SAMV_PAGE_SIZE; + int r = target_read_memory(target, addr, 4, SAMV_PAGE_SIZE / 4, buf); + if (r != ERROR_OK) + LOG_ERROR("flash program failed to read page @ 0x%08x", + (unsigned int)(addr)); + return r; +} + +static int samv_page_write(struct target *target, + unsigned pagenum, const uint8_t *buf) +{ + uint32_t status; + const uint32_t addr = SAMV_FLASH_BASE + pagenum * SAMV_PAGE_SIZE; + int r; + + LOG_DEBUG("write page %u at address 0x%08x", pagenum, (unsigned int)addr); + r = target_write_memory(target, addr, 4, SAMV_PAGE_SIZE / 4, buf); + if (r != ERROR_OK) { + LOG_ERROR("failed to buffer page at 0x%08x", (unsigned int)addr); + return r; + } + + r = samv_efc_perform_command(target, SAMV_EFC_FCMD_WP, pagenum, &status); + if (r != ERROR_OK) + LOG_ERROR("error performing write page at 0x%08x", (unsigned int)addr); + if (status & (1 << 2)) { + LOG_ERROR("page at 0x%08x is locked", (unsigned int)addr); + return ERROR_FAIL; + } + if (status & (1 << 1)) { + LOG_ERROR("flash command error at 0x%08x", (unsigned int)addr); + return ERROR_FAIL; + } + return ERROR_OK; +} + +static int samv_write(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + if (bank->target->state != TARGET_HALTED) { + LOG_ERROR("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (count == 0) + return ERROR_OK; + + if ((offset + count) > bank->size) { + LOG_ERROR("flash write error - past end of bank"); + LOG_ERROR(" offset: 0x%08x, count 0x%08x, bank end: 0x%08x", + (unsigned int)(offset), + (unsigned int)(count), + (unsigned int)(bank->size)); + return ERROR_FAIL; + } + + uint8_t pagebuffer[SAMV_PAGE_SIZE] = {0}; + uint32_t page_cur = offset / SAMV_PAGE_SIZE; + uint32_t page_end = (offset + count - 1) / SAMV_PAGE_SIZE; + + LOG_DEBUG("offset: 0x%08x, count: 0x%08x", + (unsigned int)(offset), (unsigned int)(count)); + LOG_DEBUG("page start: %d, page end: %d", (int)(page_cur), (int)(page_end)); + + /* Special case: all one page */ + /* Otherwise: */ + /* (1) non-aligned start */ + /* (2) body pages */ + /* (3) non-aligned end. */ + + int r; + uint32_t page_offset; + + /* handle special case - all one page. */ + if (page_cur == page_end) { + LOG_DEBUG("special case, all in one page"); + r = samv_page_read(bank->target, page_cur, pagebuffer); + if (r != ERROR_OK) + return r; + + page_offset = offset & (SAMV_PAGE_SIZE-1); + memcpy(pagebuffer + page_offset, buffer, count); + + r = samv_page_write(bank->target, page_cur, pagebuffer); + if (r != ERROR_OK) + return r; + return ERROR_OK; + } + + /* step 1) handle the non-aligned starting address */ + page_offset = offset & (SAMV_PAGE_SIZE - 1); + if (page_offset) { + LOG_DEBUG("non-aligned start"); + /* read the partial page */ + r = samv_page_read(bank->target, page_cur, pagebuffer); + if (r != ERROR_OK) + return r; + + /* over-write with new data */ + uint32_t n = SAMV_PAGE_SIZE - page_offset; + memcpy(pagebuffer + page_offset, buffer, n); + + r = samv_page_write(bank->target, page_cur, pagebuffer); + if (r != ERROR_OK) + return r; + + count -= n; + offset += n; + buffer += n; + page_cur++; + } + + /* By checking that offset is correct here, we also fix a clang warning */ + assert(offset % SAMV_PAGE_SIZE == 0); + + /* step 2) handle the full pages */ + LOG_DEBUG("full page loop: cur=%d, end=%d, count = 0x%08x", + (int)page_cur, (int)page_end, (unsigned int)(count)); + + while ((page_cur < page_end) && (count >= SAMV_PAGE_SIZE)) { + r = samv_page_write(bank->target, page_cur, buffer); + if (r != ERROR_OK) + return r; + count -= SAMV_PAGE_SIZE; + buffer += SAMV_PAGE_SIZE; + page_cur += 1; + } + + /* step 3) write final page, if it's partial (otherwise it's already done) */ + if (count) { + LOG_DEBUG("final partial page, count = 0x%08x", (unsigned int)(count)); + /* we have a partial page */ + r = samv_page_read(bank->target, page_cur, pagebuffer); + if (r != ERROR_OK) + return r; + memcpy(pagebuffer, buffer, count); /* data goes at start of page */ + r = samv_page_write(bank->target, page_cur, pagebuffer); + if (r != ERROR_OK) + return r; + } + return ERROR_OK; +} + +static int samv_get_info(struct flash_bank *bank, char *buf, int buf_size) +{ + struct samv_flash_bank *samv_info = bank->driver_priv; + if (!samv_info->probed) { + int r = samv_probe(bank); + if (ERROR_OK != r) + return r; + } + snprintf(buf, buf_size, "Cortex-M7 detected with %d kB flash", + bank->size / 1024); + return ERROR_OK; +} + +COMMAND_HANDLER(samv_handle_gpnvm_command) +{ + struct flash_bank *bank = get_flash_bank_by_num_noprobe(0); + if (!bank) + return ERROR_FAIL; + struct samv_flash_bank *samv_info = bank->driver_priv; + struct target *target = bank->target; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + int r; + if (!samv_info->probed) { + r = samv_auto_probe(bank); + if (r != ERROR_OK) + return r; + } + + int who = 0; + + switch (CMD_ARGC) { + case 0: + goto showall; + break; + case 1: + who = -1; + break; + case 2: + if (!strcmp(CMD_ARGV[0], "show") && !strcmp(CMD_ARGV[1], "all")) + who = -1; + else { + uint32_t v32; + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], v32); + who = v32; + } + break; + default: + return ERROR_COMMAND_SYNTAX_ERROR; + break; + } + + uint32_t v; + if (!strcmp("show", CMD_ARGV[0])) { + if (who == -1) { +showall: + r = ERROR_OK; + for (int x = 0; x < SAMV_NUM_GPNVM_BITS; x++) { + r = samv_get_gpnvm(target, x, &v); + if (r != ERROR_OK) + break; + command_print(CMD_CTX, "samv-gpnvm%u: %u", x, v); + } + return r; + } + if ((who >= 0) && (((unsigned)who) < SAMV_NUM_GPNVM_BITS)) { + r = samv_get_gpnvm(target, who, &v); + command_print(CMD_CTX, "samv-gpnvm%u: %u", who, v); + return r; + } else { + command_print(CMD_CTX, "invalid gpnvm: %u", who); + return ERROR_COMMAND_SYNTAX_ERROR; + } + } + + if (who == -1) { + command_print(CMD_CTX, "missing gpnvm number"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + if (!strcmp("set", CMD_ARGV[0])) + r = samv_set_gpnvm(target, who); + else if (!strcmp("clr", CMD_ARGV[0]) || !strcmp("clear", CMD_ARGV[0])) + r = samv_clear_gpnvm(target, who); + else { + command_print(CMD_CTX, "unknown command: %s", CMD_ARGV[0]); + r = ERROR_COMMAND_SYNTAX_ERROR; + } + return r; +} + +static const struct command_registration atsamv_exec_command_handlers[] = { + { + .name = "gpnvm", + .handler = samv_handle_gpnvm_command, + .mode = COMMAND_EXEC, + .usage = "[('clr'|'set'|'show') bitnum]", + .help = "Without arguments, shows all bits in the gpnvm " + "register. Otherwise, clears, sets, or shows one " + "General Purpose Non-Volatile Memory (gpnvm) bit.", + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration atsamv_command_handlers[] = { + { + .name = "atsamv", + .mode = COMMAND_ANY, + .help = "atsamv flash command group", + .usage = "", + .chain = atsamv_exec_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +struct flash_driver atsamv_flash = { + .name = "atsamv", + .commands = atsamv_command_handlers, + .flash_bank_command = samv_flash_bank_command, + .erase = samv_erase, + .protect = samv_protect, + .write = samv_write, + .read = default_flash_read, + .probe = samv_probe, + .auto_probe = samv_auto_probe, + .erase_check = default_flash_blank_check, + .protect_check = samv_protect_check, + .info = samv_get_info, +}; diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c index 8c761cdc1d..4723cffc11 100644 --- a/src/flash/nor/drivers.c +++ b/src/flash/nor/drivers.c @@ -32,6 +32,7 @@ extern struct flash_driver at91sam4_flash; extern struct flash_driver at91sam4l_flash; extern struct flash_driver at91samd_flash; extern struct flash_driver at91sam7_flash; +extern struct flash_driver atsamv_flash; extern struct flash_driver str7x_flash; extern struct flash_driver str9x_flash; extern struct flash_driver aduc702x_flash; @@ -77,6 +78,7 @@ static struct flash_driver *flash_drivers[] = { &at91sam4_flash, &at91sam4l_flash, &at91samd_flash, + &atsamv_flash, &str7x_flash, &str9x_flash, &aduc702x_flash, diff --git a/tcl/target/atsamv.cfg b/tcl/target/atsamv.cfg index 4587fc1728..b6c4842373 100644 --- a/tcl/target/atsamv.cfg +++ b/tcl/target/atsamv.cfg @@ -46,3 +46,6 @@ if {![using_hla]} { cortex_m reset_config sysresetreq } +set _FLASHNAME $_CHIPNAME.flash +flash bank $_FLASHNAME atsamv 0x00400000 0 0 0 $_TARGETNAME + -- 2.30.2