flash/nor: Add Ambiq Micro Apollo flash driver. 30/3230/12
authorRick Foos <rfoos@solengtech.com>
Mon, 8 Feb 2016 21:19:30 +0000 (15:19 -0600)
committerFreddie Chopin <freddie.chopin@gmail.com>
Sat, 14 May 2016 11:18:31 +0000 (12:18 +0100)
Initial release of Ambiq Micro Apollo flash driver
supporting our sub-threshold (low power) Cortex M4F part,
and Evaluation Kit.

We have been shipping openocd to our customers for about one year.

The EVK boards are SWD only using ftdi. We also use two of the
other COM instances to display debug information.

It takes about 15 seconds to flash 512K, and mass erase is
about 5 seconds.

Tested by internal verification group, FAE's, and customer sites.

Merged commit 'refs/changes/17/3417/1' as suggested. Makefile.am
and drivers.c follow the new format to avoid conflicts.

Removed unused fault_capture command.

Added documentation for flash driver.

Change-Id: Iae92d869369c6827244f0071f9cb522d8d91fed8
Signed-off-by: Rick Foos <rfoos@solengtech.com>
Reviewed-on: http://openocd.zylin.com/3230
Tested-by: jenkins
Reviewed-by: Freddie Chopin <freddie.chopin@gmail.com>
doc/openocd.texi
src/flash/nor/Makefile.am
src/flash/nor/ambiqmicro.c [new file with mode: 0644]
src/flash/nor/drivers.c

index ec0e926..3e7fc7f 100644 (file)
@@ -4960,6 +4960,52 @@ flash bank $_FLASHNAME aduc702x 0 0 0 0 $_TARGETNAME
 @end example
 @end deffn
 
+@deffn {Flash Driver} ambiqmicro
+@cindex ambiqmicro
+@cindex apollo
+All members of the Apollo microcontroller family from
+Ambiq Micro include internal flash and use ARM's Cortex-M4 core.
+The host connects over USB to an FTDI interface that communicates
+with the target using SWD.
+
+The @var{ambiqmicro} driver reads the Chip Information Register detect
+the device class of the MCU.
+The Flash and Sram sizes directly follow device class, and are used
+to set up the flash banks.
+If this fails, the driver will use default values set to the minimum
+sizes of an Apollo chip.
+
+All Apollo chips have two flash banks of the same size.
+In all cases the first flash bank starts at location 0,
+and the second bank starts after the first.
+
+@example
+# Flash bank 0
+flash bank $_FLASHNAME ambiqmicro 0 0x00040000 0 0 $_TARGETNAME
+# Flash bank 1 - same size as bank0, starts after bank 0.
+flash bank $_FLASHNAME ambiqmicro 0x00040000 0x00040000 0 0 $_TARGETNAME
+@end example
+
+Flash is programmed using custom entry points into the bootloader.
+This is the only way to program the flash as no flash control registers
+are available to the user.
+
+The @var{ambiqmicro} driver adds some additional commands:
+
+@deffn Command {ambiqmicro mass_erase} <bank>
+Erase entire bank.
+@end deffn
+@deffn Command {ambiqmicro page_erase} <bank> <first> <last>
+Erase device pages.
+@end deffn
+@deffn Command {ambiqmicro program_otp} <bank> <offset> <count>
+Program OTP is a one time operation to create write protected flash.
+The user writes sectors to sram starting at 0x10000010.
+Program OTP will write these sectors from sram to flash, and write protect
+the flash.
+@end deffn
+@end deffn
+
 @anchor{at91samd}
 @deffn {Flash Driver} at91samd
 @cindex at91samd
index 3386211..c167e8f 100644 (file)
@@ -10,6 +10,7 @@ libocdflashnor_la_SOURCES = \
 NOR_DRIVERS = \
        aduc702x.c \
        aducm360.c \
+       ambiqmicro.c \
        at91sam4.c \
        at91sam4l.c \
        at91samd.c \
diff --git a/src/flash/nor/ambiqmicro.c b/src/flash/nor/ambiqmicro.c
new file mode 100644 (file)
index 0000000..b2c30e6
--- /dev/null
@@ -0,0 +1,904 @@
+/******************************************************************************
+ *
+ * @file ambiqmicro.c
+ *
+ * @brief Ambiq Micro flash driver.
+ *
+ *****************************************************************************/
+
+/******************************************************************************
+ * Copyright (c) 2015, David Racine <dracine at ambiqmicro.com>
+ *
+ * Copyright (c) 2016, Rick Foos <rfoos at solengtech.com>
+ *
+ * Copyright (c) 2015-2016, Ambiq Micro, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
+ *
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "jtag/interface.h"
+#include "imp.h"
+#include "target/algorithm.h"
+#include "target/armv7m.h"
+#include "target/cortex_m.h"
+
+/** Check error, log error. */
+#define CHECK_STATUS(rc, msg) {        \
+               if (rc != ERROR_OK) { \
+                       LOG_ERROR("status(%d):%s\n", rc, msg); } }
+
+/*
+ * Address and Key defines.
+ */
+#define PROGRAM_KEY      (0x12344321)
+#define OTP_PROGRAM_KEY  (0x87655678)
+
+#define FLASH_PROGRAM_MAIN_FROM_SRAM                0x0800005d
+#define FLASH_PROGRAM_OTP_FROM_SRAM                 0x08000061
+#define FLASH_ERASE_LIST_MAIN_PAGES_FROM_SRAM       0x08000065
+#define FLASH_MASS_ERASE_MAIN_PAGES_FROM_SRAM       0x08000069
+
+
+static const uint32_t apollo_flash_size[] = {
+       1 << 15,
+       1 << 16,
+       1 << 17,
+       1 << 18,
+       1 << 19,
+       1 << 20,
+       1 << 21
+};
+
+static const uint32_t apollo_sram_size[] = {
+       1 << 15,
+       1 << 16,
+       1 << 17,
+       1 << 18,
+       1 << 19,
+       1 << 20,
+       1 << 21
+};
+
+struct ambiqmicro_flash_bank {
+       /* chip id register */
+
+       uint32_t probed;
+
+       const char *target_name;
+       uint8_t target_class;
+
+       uint32_t sramsiz;
+       uint32_t flshsiz;
+
+       /* flash geometry */
+       uint32_t num_pages;
+       uint32_t pagesize;
+       uint32_t pages_in_lockregion;
+
+       /* nv memory bits */
+       uint16_t num_lockbits;
+
+       /* main clock status */
+       uint32_t rcc;
+       uint32_t rcc2;
+       uint8_t mck_valid;
+       uint8_t xtal_mask;
+       uint32_t iosc_freq;
+       uint32_t mck_freq;
+       const char *iosc_desc;
+       const char *mck_desc;
+};
+
+static struct {
+       uint8_t class;
+       uint8_t partno;
+       const char *partname;
+} ambiqmicroParts[6] = {
+       {0xFF, 0x00, "Unknown"},
+       {0x01, 0x00, "Apollo"},
+       {0x02, 0x00, "Apollo2"},
+       {0x03, 0x00, "Unknown"},
+       {0x04, 0x00, "Unknown"},
+       {0x05, 0x00, "Apollo"},
+};
+
+static char *ambiqmicroClassname[6] = {
+       "Unknown", "Apollo", "Apollo2", "Unknown", "Unknown", "Apollo"
+};
+
+/***************************************************************************
+*      openocd command interface                                              *
+***************************************************************************/
+
+/* flash_bank ambiqmicro <base> <size> 0 0 <target#>
+ */
+FLASH_BANK_COMMAND_HANDLER(ambiqmicro_flash_bank_command)
+{
+       struct ambiqmicro_flash_bank *ambiqmicro_info;
+
+       if (CMD_ARGC < 6)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       ambiqmicro_info = calloc(sizeof(struct ambiqmicro_flash_bank), 1);
+
+       bank->driver_priv = ambiqmicro_info;
+
+       ambiqmicro_info->target_name = "Unknown target";
+
+       /* part wasn't probed yet */
+       ambiqmicro_info->probed = 0;
+
+       return ERROR_OK;
+}
+
+static int get_ambiqmicro_info(struct flash_bank *bank, char *buf, int buf_size)
+{
+       struct ambiqmicro_flash_bank *ambiqmicro_info = bank->driver_priv;
+       int printed;
+       char *classname;
+
+       if (ambiqmicro_info->probed == 0) {
+               LOG_ERROR("Target not probed");
+               return ERROR_FLASH_BANK_NOT_PROBED;
+       }
+
+       /* Check class name in range. */
+       if (ambiqmicro_info->target_class < sizeof(ambiqmicroClassname))
+               classname = ambiqmicroClassname[ambiqmicro_info->target_class];
+       else
+               classname = ambiqmicroClassname[0];
+
+       printed = snprintf(buf,
+               buf_size,
+               "\nAmbiq Micro information: Chip is "
+               "class %d (%s) %s\n",
+               ambiqmicro_info->target_class,
+               classname,
+               ambiqmicro_info->target_name);
+
+       if ((printed < 0))
+               return ERROR_BUF_TOO_SMALL;
+       return ERROR_OK;
+}
+
+/***************************************************************************
+*      chip identification and status                                         *
+***************************************************************************/
+
+/* Fill in driver info structure */
+static int ambiqmicro_read_part_info(struct flash_bank *bank)
+{
+       struct ambiqmicro_flash_bank *ambiqmicro_info = bank->driver_priv;
+       struct target *target = bank->target;
+       uint32_t PartNum = 0;
+       int retval;
+
+       /*
+        * Read Part Number.
+        */
+       retval = target_read_u32(target, 0x40020000, &PartNum);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("status(0x%x):Could not read PartNum.\n", retval);
+               /* Set PartNum to default device */
+               PartNum = 0;
+       }
+       LOG_DEBUG("Part number: 0x%x", PartNum);
+
+       /*
+        * Determine device class.
+        */
+       ambiqmicro_info->target_class = (PartNum & 0xFF000000) >> 24;
+
+       switch (ambiqmicro_info->target_class) {
+               case 1:         /* 1 - Apollo */
+               case 5:         /* 5 - Apollo Bootloader */
+                       bank->base = bank->bank_number * 0x40000;
+                       ambiqmicro_info->pagesize = 2048;
+                       ambiqmicro_info->flshsiz =
+                       apollo_flash_size[(PartNum & 0x00F00000) >> 20];
+                       ambiqmicro_info->sramsiz =
+                       apollo_sram_size[(PartNum & 0x000F0000) >> 16];
+                       ambiqmicro_info->num_pages = ambiqmicro_info->flshsiz /
+                       ambiqmicro_info->pagesize;
+                       if (ambiqmicro_info->num_pages > 128) {
+                               ambiqmicro_info->num_pages = 128;
+                               ambiqmicro_info->flshsiz = 1024 * 256;
+                       }
+                       break;
+
+               default:
+                       LOG_INFO("Unknown Class. Using Apollo-64 as default.");
+
+                       bank->base = bank->bank_number * 0x40000;
+                       ambiqmicro_info->pagesize = 2048;
+                       ambiqmicro_info->flshsiz = apollo_flash_size[1];
+                       ambiqmicro_info->sramsiz = apollo_sram_size[0];
+                       ambiqmicro_info->num_pages = ambiqmicro_info->flshsiz /
+                       ambiqmicro_info->pagesize;
+                       if (ambiqmicro_info->num_pages > 128) {
+                               ambiqmicro_info->num_pages = 128;
+                               ambiqmicro_info->flshsiz = 1024 * 256;
+                       }
+                       break;
+
+       }
+
+       if (ambiqmicro_info->target_class <
+               (sizeof(ambiqmicroParts)/sizeof(ambiqmicroParts[0])))
+               ambiqmicro_info->target_name =
+                       ambiqmicroParts[ambiqmicro_info->target_class].partname;
+       else
+               ambiqmicro_info->target_name =
+                       ambiqmicroParts[0].partname;
+
+       LOG_DEBUG("num_pages: %d, pagesize: %d, flash: %d, sram: %d",
+               ambiqmicro_info->num_pages,
+               ambiqmicro_info->pagesize,
+               ambiqmicro_info->flshsiz,
+               ambiqmicro_info->sramsiz);
+
+       return ERROR_OK;
+}
+
+/***************************************************************************
+*      flash operations                                                       *
+***************************************************************************/
+
+static int ambiqmicro_protect_check(struct flash_bank *bank)
+{
+       struct ambiqmicro_flash_bank *ambiqmicro = bank->driver_priv;
+       int status = ERROR_OK;
+       uint32_t i;
+
+
+       if (ambiqmicro->probed == 0) {
+               LOG_ERROR("Target not probed");
+               return ERROR_FLASH_BANK_NOT_PROBED;
+       }
+
+       for (i = 0; i < (unsigned) bank->num_sectors; i++)
+               bank->sectors[i].is_protected = -1;
+
+       return status;
+}
+/** Read flash status from bootloader. */
+static int check_flash_status(struct target *target, uint32_t address)
+{
+       uint32_t retflash;
+       int rc;
+       rc = target_read_u32(target, address, &retflash);
+       /* target connection failed. */
+       if (rc != ERROR_OK) {
+               LOG_DEBUG("%s:%d:%s(): status(0x%x)\n",
+                       __FILE__, __LINE__, __func__, rc);
+               return rc;
+       }
+       /* target flash failed, unknown cause. */
+       if (retflash != 0) {
+               LOG_ERROR("Flash not happy: status(0x%x)", retflash);
+               return ERROR_FLASH_OPERATION_FAILED;
+       }
+       return ERROR_OK;
+}
+
+static int ambiqmicro_exec_command(struct target *target,
+       uint32_t command,
+       uint32_t flash_return_address)
+{
+       int retval, retflash;
+
+       retval = target_resume(
+               target,
+               false,
+               command,
+               true,
+               true);
+
+       CHECK_STATUS(retval, "error executing ambiqmicro command");
+
+       /*
+        * Wait for halt.
+        */
+       for (;; ) {
+               target_poll(target);
+               if (target->state == TARGET_HALTED)
+                       break;
+               else if (target->state == TARGET_RUNNING ||
+                       target->state == TARGET_DEBUG_RUNNING) {
+                       /*
+                        * Keep polling until target halts.
+                        */
+                       target_poll(target);
+                       alive_sleep(100);
+                       LOG_DEBUG("state = %d", target->state);
+               } else {
+                       LOG_ERROR("Target not halted or running %d", target->state);
+                       break;
+               }
+       }
+
+       /*
+        * Read return value, flash error takes precedence.
+        */
+       retflash = check_flash_status(target, flash_return_address);
+       if (retflash != ERROR_OK)
+               retval = retflash;
+
+       /* Return code from target_resume OR flash. */
+       return retval;
+}
+
+static int ambiqmicro_mass_erase(struct flash_bank *bank)
+{
+       struct target *target = NULL;
+       struct ambiqmicro_flash_bank *ambiqmicro_info = NULL;
+       int retval = ERROR_OK;
+
+       ambiqmicro_info = bank->driver_priv;
+       target = bank->target;
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       if (ambiqmicro_info->probed == 0) {
+               LOG_ERROR("Target not probed");
+               return ERROR_FLASH_BANK_NOT_PROBED;
+       }
+
+       /*
+        * Clear Bootloader bit.
+        */
+       retval = target_write_u32(target, 0x400201a0, 0x0);
+       CHECK_STATUS(retval, "error clearing bootloader bit.");
+
+       /*
+        * Set up the SRAM.
+        */
+
+       /*
+        * Bank.
+        */
+       retval = target_write_u32(target, 0x10000000, bank->bank_number);
+       CHECK_STATUS(retval, "error writing target SRAM parameters.");
+
+       /*
+        * Write Key.
+        */
+       retval = target_write_u32(target, 0x10000004, PROGRAM_KEY);
+       CHECK_STATUS(retval, "error writing target SRAM parameters.");
+
+       /*
+        * Breakpoint.
+        */
+       retval = target_write_u32(target, 0x10000008, 0xfffffffe);
+       CHECK_STATUS(retval, "error writing target SRAM parameters.");
+
+       /*
+        * Erase the main array.
+        */
+       LOG_INFO("Mass erase on bank %d.", bank->bank_number);
+
+       /*
+        * passed pc, addr = ROM function, handle breakpoints, not debugging.
+        */
+       retval = ambiqmicro_exec_command(target, FLASH_MASS_ERASE_MAIN_PAGES_FROM_SRAM, 0x10000008);
+       CHECK_STATUS(retval, "error executing ambiqmicro flash mass erase.");
+       if (retval != ERROR_OK)
+               return retval;
+
+       /*
+        * Set Bootloader bit, regardless of command execution.
+        */
+       retval = target_write_u32(target, 0x400201a0, 0x1);
+       CHECK_STATUS(retval, "error setting bootloader bit.");
+
+       return retval;
+}
+
+
+static int ambiqmicro_erase(struct flash_bank *bank, int first, int last)
+{
+       struct ambiqmicro_flash_bank *ambiqmicro_info = bank->driver_priv;
+       struct target *target = bank->target;
+       uint32_t retval = ERROR_OK;
+
+       if (bank->target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       if (ambiqmicro_info->probed == 0) {
+               LOG_ERROR("Target not probed");
+               return ERROR_FLASH_BANK_NOT_PROBED;
+       }
+
+       /*
+        * Check pages.
+        * Fix num_pages for the device.
+        */
+       if ((first < 0) || (last < first) || (last >= (int)ambiqmicro_info->num_pages))
+               return ERROR_FLASH_SECTOR_INVALID;
+
+       /*
+        * Just Mass Erase if all pages are given.
+        * TODO: Fix num_pages for the device
+        */
+       if ((first == 0) && (last == ((int)ambiqmicro_info->num_pages-1)))
+               return ambiqmicro_mass_erase(bank);
+
+       /*
+        * Clear Bootloader bit.
+        */
+       retval = target_write_u32(target, 0x400201a0, 0x0);
+       CHECK_STATUS(retval, "error clearing bootloader bit.");
+
+       /*
+        * Set up the SRAM.
+        */
+
+       /*
+        * Bank.
+        */
+       retval = target_write_u32(target, 0x10000000, bank->bank_number);
+       CHECK_STATUS(retval, "error writing target SRAM parameters.");
+
+       /*
+        * Number of pages to erase.
+        */
+       retval = target_write_u32(target, 0x10000004, 1 + (last-first));
+       CHECK_STATUS(retval, "error writing target SRAM parameters.");
+
+       /*
+        * Write Key.
+        */
+       retval = target_write_u32(target, 0x10000008, PROGRAM_KEY);
+       CHECK_STATUS(retval, "error writing target SRAM parameters.");
+
+       /*
+        * Breakpoint.
+        */
+       retval = target_write_u32(target, 0x1000000c, 0xfffffffe);
+       CHECK_STATUS(retval, "error writing target SRAM parameters.");
+
+       /*
+        * Pointer to flash address.
+        */
+       retval = target_write_u32(target, 0x10000010, first);
+       CHECK_STATUS(retval, "error writing target SRAM parameters.");
+       if (retval != ERROR_OK)
+               return retval;
+
+       /*
+        * Erase the pages.
+        */
+       LOG_INFO("Erasing pages %d to %d on bank %d", first, last, bank->bank_number);
+
+       /*
+        * passed pc, addr = ROM function, handle breakpoints, not debugging.
+        */
+       retval = ambiqmicro_exec_command(target, FLASH_ERASE_LIST_MAIN_PAGES_FROM_SRAM, 0x1000000C);
+       CHECK_STATUS(retval, "error executing flash page erase");
+       if (retval != ERROR_OK)
+               return retval;
+
+       LOG_INFO("%d pages erased!", 1+(last-first));
+
+       if (first == 0) {
+               /*
+                * Set Bootloader bit.
+                */
+               retval = target_write_u32(target, 0x400201a0, 0x1);
+               CHECK_STATUS(retval, "error setting bootloader bit.");
+               if (retval != ERROR_OK)
+                       return retval;
+       }
+
+       return retval;
+}
+
+static int ambiqmicro_protect(struct flash_bank *bank, int set, int first, int last)
+{
+       /* struct ambiqmicro_flash_bank *ambiqmicro_info = bank->driver_priv;
+        * struct target *target = bank->target; */
+
+       /*
+        * TODO
+        */
+       LOG_INFO("Not yet implemented");
+
+       if (bank->target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       return ERROR_OK;
+}
+
+static int ambiqmicro_write_block(struct flash_bank *bank,
+       const uint8_t *buffer, uint32_t offset, uint32_t count)
+{
+       /* struct ambiqmicro_flash_bank *ambiqmicro_info = bank->driver_priv; */
+       struct target *target = bank->target;
+       uint32_t address = bank->base + offset;
+       uint32_t buffer_pointer = 0x10000010;
+       uint32_t maxbuffer;
+       uint32_t thisrun_count;
+       int retval = ERROR_OK;
+
+       if (((count%4) != 0) || ((offset%4) != 0)) {
+               LOG_ERROR("write block must be multiple of 4 bytes in offset & length");
+               return ERROR_FAIL;
+       }
+
+       /*
+        * Max buffer size for this device.
+        * Hard code 6kB for the buffer.
+        */
+       maxbuffer = 0x1800;
+
+       LOG_INFO("Flashing main array");
+
+       while (count > 0) {
+               if (count > maxbuffer)
+                       thisrun_count = maxbuffer;
+               else
+                       thisrun_count = count;
+
+               /*
+                * Set up the SRAM.
+                */
+
+               /*
+                * Pointer to flash.
+                */
+               retval = target_write_u32(target, 0x10000000, address);
+               CHECK_STATUS(retval, "error writing target SRAM parameters.");
+
+               /*
+                * Number of 32-bit words to program.
+                */
+               retval = target_write_u32(target, 0x10000004, thisrun_count/4);
+               CHECK_STATUS(retval, "error writing target SRAM parameters.");
+
+               /*
+                * Write Key.
+                */
+               retval = target_write_u32(target, 0x10000008, PROGRAM_KEY);
+               CHECK_STATUS(retval, "error writing target SRAM parameters.");
+
+               /*
+                * Breakpoint.
+                */
+               retval = target_write_u32(target, 0x1000000c, 0xfffffffe);
+               CHECK_STATUS(retval, "error writing target SRAM parameters.");
+
+               /*
+                * Write Buffer.
+                */
+               retval = target_write_buffer(target, buffer_pointer, thisrun_count, buffer);
+
+               if (retval != ERROR_OK) {
+                       CHECK_STATUS(retval, "error writing target SRAM parameters.");
+                       break;
+               }
+
+               LOG_DEBUG("address = 0x%08x", address);
+
+               retval = ambiqmicro_exec_command(target, FLASH_PROGRAM_MAIN_FROM_SRAM, 0x1000000c);
+               CHECK_STATUS(retval, "error executing ambiqmicro flash write algorithm");
+               if (retval != ERROR_OK)
+                       break;
+               buffer += thisrun_count;
+               address += thisrun_count;
+               count -= thisrun_count;
+       }
+
+
+       LOG_INFO("Main array flashed");
+
+       /*
+        * Clear Bootloader bit.
+        */
+       retval = target_write_u32(target, 0x400201a0, 0x0);
+       CHECK_STATUS(retval, "error clearing bootloader bit");
+
+       return retval;
+}
+
+static int ambiqmicro_write(struct flash_bank *bank, const uint8_t *buffer,
+       uint32_t offset, uint32_t count)
+{
+       uint32_t retval;
+
+       /* try using a block write */
+       retval = ambiqmicro_write_block(bank, buffer, offset, count);
+       if (retval != ERROR_OK)
+               LOG_ERROR("write failed");
+
+       return retval;
+}
+
+static int ambiqmicro_probe(struct flash_bank *bank)
+{
+       struct ambiqmicro_flash_bank *ambiqmicro_info = bank->driver_priv;
+       uint32_t retval;
+
+       /* If this is a ambiqmicro chip, it has flash; probe() is just
+        * to figure out how much is present.  Only do it once.
+        */
+       if (ambiqmicro_info->probed == 1) {
+               LOG_INFO("Target already probed");
+               return ERROR_OK;
+       }
+
+       /* ambiqmicro_read_part_info() already handled error checking and
+        * reporting.  Note that it doesn't write, so we don't care about
+        * whether the target is halted or not.
+        */
+       retval = ambiqmicro_read_part_info(bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       if (bank->sectors) {
+               free(bank->sectors);
+               bank->sectors = NULL;
+       }
+
+       /* provide this for the benefit of the NOR flash framework */
+       bank->size = ambiqmicro_info->pagesize * ambiqmicro_info->num_pages;
+       bank->num_sectors = ambiqmicro_info->num_pages;
+       bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors);
+       for (int i = 0; i < bank->num_sectors; i++) {
+               bank->sectors[i].offset = i * ambiqmicro_info->pagesize;
+               bank->sectors[i].size = ambiqmicro_info->pagesize;
+               bank->sectors[i].is_erased = -1;
+               bank->sectors[i].is_protected = -1;
+       }
+
+       /*
+        * Part has been probed.
+        */
+       ambiqmicro_info->probed = 1;
+
+       return retval;
+}
+
+static int ambiqmicro_otp_program(struct flash_bank *bank,
+       uint32_t offset, uint32_t count)
+{
+       struct target *target = NULL;
+       struct ambiqmicro_flash_bank *ambiqmicro_info = NULL;
+       uint32_t retval = ERROR_OK;
+
+       ambiqmicro_info = bank->driver_priv;
+       target = bank->target;
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       if (ambiqmicro_info->probed == 0) {
+               LOG_ERROR("Target not probed");
+               return ERROR_FLASH_BANK_NOT_PROBED;
+       }
+
+       if (count > 256) {
+               LOG_ERROR("Count must be < 256");
+               return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
+       }
+
+       /*
+        * Clear Bootloader bit.
+        */
+       retval = target_write_u32(target, 0x400201a0, 0x0);
+       CHECK_STATUS(retval, "error clearing bootloader bit.");
+
+       /*
+        * Set up the SRAM.
+        */
+
+       /*
+        * Bank.
+        */
+       retval = target_write_u32(target, 0x10000000, offset);
+       CHECK_STATUS(retval, "error setting target SRAM parameters.");
+
+       /*
+        * Num of words to program.
+        */
+       retval = target_write_u32(target, 0x10000004, count);
+       CHECK_STATUS(retval, "error setting target SRAM parameters.");
+
+       /*
+        * Write Key.
+        */
+       retval = target_write_u32(target, 0x10000008, OTP_PROGRAM_KEY);
+       CHECK_STATUS(retval, "error setting target SRAM parameters.");
+
+       /*
+        * Breakpoint.
+        */
+       retval = target_write_u32(target, 0x1000000c, 0xfffffffe);
+       CHECK_STATUS(retval, "error setting target SRAM parameters.");
+       if (retval != ERROR_OK)
+               return retval;
+
+       /*
+        * Program OTP.
+        */
+       LOG_INFO("Programming OTP offset 0x%08x", offset);
+
+       /*
+        * passed pc, addr = ROM function, handle breakpoints, not debugging.
+        */
+       retval = ambiqmicro_exec_command(target, FLASH_PROGRAM_OTP_FROM_SRAM, 0x1000000C);
+       CHECK_STATUS(retval, "error executing ambiqmicro otp program algorithm");
+
+       LOG_INFO("Programming OTP finished.");
+
+       return retval;
+}
+
+
+
+COMMAND_HANDLER(ambiqmicro_handle_mass_erase_command)
+{
+       int i;
+
+       if (CMD_ARGC < 1)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       struct flash_bank *bank;
+       uint32_t retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
+       if (ERROR_OK != retval)
+               return retval;
+
+       if (ambiqmicro_mass_erase(bank) == ERROR_OK) {
+               /* set all sectors as erased */
+               for (i = 0; i < bank->num_sectors; i++)
+                       bank->sectors[i].is_erased = 1;
+
+               command_print(CMD_CTX, "ambiqmicro mass erase complete");
+       } else
+               command_print(CMD_CTX, "ambiqmicro mass erase failed");
+
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(ambiqmicro_handle_page_erase_command)
+{
+       struct flash_bank *bank;
+       uint32_t first, last;
+       uint32_t retval;
+
+       if (CMD_ARGC < 3)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], first);
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], last);
+
+       retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
+       if (ERROR_OK != retval)
+               return retval;
+
+       if (ambiqmicro_erase(bank, first, last) == ERROR_OK)
+               command_print(CMD_CTX, "ambiqmicro page erase complete");
+       else
+               command_print(CMD_CTX, "ambiqmicro page erase failed");
+
+       return ERROR_OK;
+}
+
+
+/**
+ * Program the otp block.
+ */
+COMMAND_HANDLER(ambiqmicro_handle_program_otp_command)
+{
+       struct flash_bank *bank;
+       uint32_t offset, count;
+       uint32_t retval;
+
+       if (CMD_ARGC < 3)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], offset);
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], count);
+
+       command_print(CMD_CTX, "offset=0x%08x count=%d", offset, count);
+
+       CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
+
+       retval = ambiqmicro_otp_program(bank, offset, count);
+
+       if (retval != ERROR_OK)
+               LOG_ERROR("error check log");
+
+       return ERROR_OK;
+}
+
+
+
+static const struct command_registration ambiqmicro_exec_command_handlers[] = {
+       {
+               .name = "mass_erase",
+               .usage = "<bank>",
+               .handler = ambiqmicro_handle_mass_erase_command,
+               .mode = COMMAND_EXEC,
+               .help = "Erase entire device",
+       },
+       {
+               .name = "page_erase",
+               .usage = "<bank> <first> <last>",
+               .handler = ambiqmicro_handle_page_erase_command,
+               .mode = COMMAND_EXEC,
+               .help = "Erase device pages",
+       },
+       {
+               .name = "program_otp",
+               .handler = ambiqmicro_handle_program_otp_command,
+               .mode = COMMAND_EXEC,
+               .usage = "<bank> <offset> <count>",
+               .help =
+                       "Program OTP (assumes you have already written array starting at 0x10000010)",
+       },
+       COMMAND_REGISTRATION_DONE
+};
+static const struct command_registration ambiqmicro_command_handlers[] = {
+       {
+               .name = "ambiqmicro",
+               .mode = COMMAND_EXEC,
+               .help = "ambiqmicro flash command group",
+               .usage = "Support for Ambiq Micro parts.",
+               .chain = ambiqmicro_exec_command_handlers,
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+struct flash_driver ambiqmicro_flash = {
+       .name = "ambiqmicro",
+       .commands = ambiqmicro_command_handlers,
+       .flash_bank_command = ambiqmicro_flash_bank_command,
+       .erase = ambiqmicro_erase,
+       .protect = ambiqmicro_protect,
+       .write = ambiqmicro_write,
+       .read = default_flash_read,
+       .probe = ambiqmicro_probe,
+       .auto_probe = ambiqmicro_probe,
+       .erase_check = default_flash_blank_check,
+       .protect_check = ambiqmicro_protect_check,
+       .info = get_ambiqmicro_info,
+};
index e39b37e..57079b4 100644 (file)
@@ -24,6 +24,7 @@
 
 extern struct flash_driver aduc702x_flash;
 extern struct flash_driver aducm360_flash;
+extern struct flash_driver ambiqmicro_flash;
 extern struct flash_driver at91sam3_flash;
 extern struct flash_driver at91sam4_flash;
 extern struct flash_driver at91sam4l_flash;
@@ -75,6 +76,7 @@ extern struct flash_driver xmc4xxx_flash;
 static struct flash_driver *flash_drivers[] = {
        &aduc702x_flash,
        &aducm360_flash,
+       &ambiqmicro_flash,
        &at91sam3_flash,
        &at91sam4_flash,
        &at91sam4l_flash,