Files
openocd/src/flash/nor/ambiqmicro.c
Antonio Borneo 382148e4dd openocd: fix SPDX tag format for files .c
With the old checkpatch we cannot use the correct format for the
SPDX tags in the file .c, in fact the C99 comments are not allowed
and we had to use the block comment.

With the new checkpatch, let's switch to the correct SPDX format.

Change created automatically through the command:
	sed -i \
	's,^/\* *\(SPDX-License-Identifier: .*[^ ]\) *\*/$,// \1,' \
	$(find src/ contrib/ -name \*.c)

Change-Id: I6da16506baa7af718947562505dd49606d124171
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: https://review.openocd.org/c/openocd/+/7153
Tested-by: jenkins
2022-09-18 08:22:01 +00:00

868 lines
21 KiB
C

// SPDX-License-Identifier: BSD-3-Clause
/******************************************************************************
*
* @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.
*****************************************************************************/
#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 */
bool 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;
} ambiqmicro_parts[6] = {
{0xFF, 0x00, "Unknown"},
{0x01, 0x00, "Apollo"},
{0x02, 0x00, "Apollo2"},
{0x03, 0x00, "Unknown"},
{0x04, 0x00, "Unknown"},
{0x05, 0x00, "Apollo"},
};
static char *ambiqmicro_classname[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 = false;
return ERROR_OK;
}
static int get_ambiqmicro_info(struct flash_bank *bank, struct command_invocation *cmd)
{
struct ambiqmicro_flash_bank *ambiqmicro_info = bank->driver_priv;
char *classname;
if (!ambiqmicro_info->probed) {
LOG_ERROR("Target not probed");
return ERROR_FLASH_BANK_NOT_PROBED;
}
/* Check class name in range. */
if (ambiqmicro_info->target_class < sizeof(ambiqmicro_classname))
classname = ambiqmicro_classname[ambiqmicro_info->target_class];
else
classname = ambiqmicro_classname[0];
command_print_sameline(cmd, "\nAmbiq Micro information: Chip is "
"class %d (%s) %s\n",
ambiqmicro_info->target_class,
classname,
ambiqmicro_info->target_name);
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 part_num = 0;
int retval;
/*
* Read Part Number.
*/
retval = target_read_u32(target, 0x40020000, &part_num);
if (retval != ERROR_OK) {
LOG_ERROR("status(0x%x):Could not read part_num.\n", retval);
/* Set part_num to default device */
part_num = 0;
}
LOG_DEBUG("Part number: 0x%" PRIx32, part_num);
/*
* Determine device class.
*/
ambiqmicro_info->target_class = (part_num & 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[(part_num & 0x00F00000) >> 20];
ambiqmicro_info->sramsiz =
apollo_sram_size[(part_num & 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 < ARRAY_SIZE(ambiqmicro_parts))
ambiqmicro_info->target_name =
ambiqmicro_parts[ambiqmicro_info->target_class].partname;
else
ambiqmicro_info->target_name =
ambiqmicro_parts[0].partname;
LOG_DEBUG("num_pages: %" PRIu32 ", pagesize: %" PRIu32 ", flash: %" PRIu32 ", sram: %" PRIu32,
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) {
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%" PRIx32 ")", 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) {
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, unsigned int first,
unsigned int last)
{
struct ambiqmicro_flash_bank *ambiqmicro_info = bank->driver_priv;
struct target *target = bank->target;
int retval;
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
if (!ambiqmicro_info->probed) {
LOG_ERROR("Target not probed");
return ERROR_FLASH_BANK_NOT_PROBED;
}
/*
* Check pages.
* Fix num_pages for the device.
*/
if ((last < first) || (last >= 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 == (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 %u to %u on bank %u", 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("%u 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,
unsigned int first, unsigned 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%08" PRIx32, 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)
{
int 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;
int 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) {
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;
free(bank->sectors);
/* 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 (unsigned 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 = true;
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;
int retval;
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) {
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%08" PRIx32, 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)
{
if (CMD_ARGC < 1)
return ERROR_COMMAND_SYNTAX_ERROR;
struct flash_bank *bank;
int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
if (retval != ERROR_OK)
return retval;
if (ambiqmicro_mass_erase(bank) == ERROR_OK)
command_print(CMD, "ambiqmicro mass erase complete");
else
command_print(CMD, "ambiqmicro mass erase failed");
return ERROR_OK;
}
COMMAND_HANDLER(ambiqmicro_handle_page_erase_command)
{
struct flash_bank *bank;
uint32_t first, last;
int 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 (retval != ERROR_OK)
return retval;
if (ambiqmicro_erase(bank, first, last) == ERROR_OK)
command_print(CMD, "ambiqmicro page erase complete");
else
command_print(CMD, "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;
int 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, "offset=0x%08" PRIx32 " count=%" PRIu32, 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
};
const 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,
.free_driver_priv = default_flash_free_driver_priv,
};