flash: driver for Atmel SAMV, SAMS, and SAME
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
+