flash: driver for Atmel SAMV, SAMS, and SAME 52/2952/14
authorMorgan Quigley <morgan@osrfoundation.org>
Sun, 6 Sep 2015 18:31:09 +0000 (11:31 -0700)
committerSpencer Oliver <spen@spen-soft.co.uk>
Tue, 10 Nov 2015 20:27:09 +0000 (20:27 +0000)
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 <morgan@osrfoundation.org>
Reviewed-on: http://openocd.zylin.com/2952
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
doc/openocd.texi
src/flash/nor/Makefile.am
src/flash/nor/atsamv.c [new file with mode: 0644]
src/flash/nor/drivers.c
tcl/target/atsamv.cfg

index 76c9766c15bd350949b429d972638d243b0f6d3b..655a3537be1e66522432ff85ce30d975b8307b51 100644 (file)
@@ -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
index 5aa4f6b0a9e3e5f8e861d15a3d01c299641185bd..b065bb076f40fcb4a6eb6a96027a06dd27f8285b 100644 (file)
@@ -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 (file)
index 0000000..08f8bb8
--- /dev/null
@@ -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 <helper/time_support.h>
+
+#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,
+};
index 8c761cdc1dc765e24d6fb41348d1753ef2fcdba6..4723cffc111df9824c20fec6b318653042e4ca92 100644 (file)
@@ -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,
index 4587fc1728ec0e172bd20d9457399614500d6615..b6c48423739f914eedddbc42bb7f35dc5e833ef6 100644 (file)
@@ -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
+

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)