You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by ad...@apache.org on 2016/06/15 22:03:57 UTC

[14/51] [partial] incubator-mynewt-site git commit: Fixed broken Quick Start link and added OpenOCD option for Arduino Primo debugging

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-site/blob/e302582d/docs/os/tutorials/downloads/openocd-code-89bf96ffe6ac66c80407af8383b9d5adc0dc35f4/src/flash/nor/str7x.c
----------------------------------------------------------------------
diff --git a/docs/os/tutorials/downloads/openocd-code-89bf96ffe6ac66c80407af8383b9d5adc0dc35f4/src/flash/nor/str7x.c b/docs/os/tutorials/downloads/openocd-code-89bf96ffe6ac66c80407af8383b9d5adc0dc35f4/src/flash/nor/str7x.c
new file mode 100755
index 0000000..d8a4cd4
--- /dev/null
+++ b/docs/os/tutorials/downloads/openocd-code-89bf96ffe6ac66c80407af8383b9d5adc0dc35f4/src/flash/nor/str7x.c
@@ -0,0 +1,817 @@
+/***************************************************************************
+ *   Copyright (C) 2005 by Dominic Rath                                    *
+ *   Dominic.Rath@gmx.de                                                   *
+ *                                                                         *
+ *   Copyright (C) 2008 by Spencer Oliver                                  *
+ *   spen@spen-soft.co.uk                                                  *
+ *                                                                         *
+ *   Copyright (C) 2010 �yvind Harboe                                      *
+ *   oyvind.harboe@zylin.com                                               *
+ *                                                                         *
+ *   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.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.           *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "imp.h"
+#include <target/arm.h>
+#include <helper/binarybuffer.h>
+#include <target/algorithm.h>
+
+/*  Flash registers */
+
+#define FLASH_CR0		0x00000000
+#define FLASH_CR1		0x00000004
+#define FLASH_DR0		0x00000008
+#define FLASH_DR1		0x0000000C
+#define FLASH_AR		0x00000010
+#define FLASH_ER		0x00000014
+#define FLASH_NVWPAR	0x0000DFB0
+#define FLASH_NVAPR0	0x0000DFB8
+#define FLASH_NVAPR1	0x0000DFBC
+
+/* FLASH_CR0 register bits */
+
+#define FLASH_WMS		0x80000000
+#define FLASH_SUSP		0x40000000
+#define FLASH_WPG		0x20000000
+#define FLASH_DWPG		0x10000000
+#define FLASH_SER		0x08000000
+#define FLASH_SPR		0x01000000
+#define FLASH_BER		0x04000000
+#define FLASH_MER		0x02000000
+#define FLASH_LOCK		0x00000010
+#define FLASH_BSYA1		0x00000004
+#define FLASH_BSYA0		0x00000002
+
+/* FLASH_CR1 register bits */
+
+#define FLASH_B1S		0x02000000
+#define FLASH_B0S		0x01000000
+#define FLASH_B1F1		0x00020000
+#define FLASH_B1F0		0x00010000
+#define FLASH_B0F7		0x00000080
+#define FLASH_B0F6		0x00000040
+#define FLASH_B0F5		0x00000020
+#define FLASH_B0F4		0x00000010
+#define FLASH_B0F3		0x00000008
+#define FLASH_B0F2		0x00000004
+#define FLASH_B0F1		0x00000002
+#define FLASH_B0F0		0x00000001
+
+/* FLASH_ER register bits */
+
+#define FLASH_WPF		0x00000100
+#define FLASH_RESER		0x00000080
+#define FLASH_SEQER		0x00000040
+#define FLASH_10ER		0x00000008
+#define FLASH_PGER		0x00000004
+#define FLASH_ERER		0x00000002
+#define FLASH_ERR		0x00000001
+
+
+struct str7x_flash_bank {
+	uint32_t *sector_bits;
+	uint32_t disable_bit;
+	uint32_t busy_bits;
+	uint32_t register_base;
+};
+
+struct str7x_mem_layout {
+	uint32_t sector_start;
+	uint32_t sector_size;
+	uint32_t sector_bit;
+};
+
+enum str7x_status_codes {
+	STR7X_CMD_SUCCESS = 0,
+	STR7X_INVALID_COMMAND = 1,
+	STR7X_SRC_ADDR_ERROR = 2,
+	STR7X_DST_ADDR_ERROR = 3,
+	STR7X_SRC_ADDR_NOT_MAPPED = 4,
+	STR7X_DST_ADDR_NOT_MAPPED = 5,
+	STR7X_COUNT_ERROR = 6,
+	STR7X_INVALID_SECTOR = 7,
+	STR7X_SECTOR_NOT_BLANK = 8,
+	STR7X_SECTOR_NOT_PREPARED = 9,
+	STR7X_COMPARE_ERROR = 10,
+	STR7X_BUSY = 11
+};
+
+static const struct str7x_mem_layout mem_layout_str7bank0[] = {
+	{0x00000000, 0x02000, 0x01},
+	{0x00002000, 0x02000, 0x02},
+	{0x00004000, 0x02000, 0x04},
+	{0x00006000, 0x02000, 0x08},
+	{0x00008000, 0x08000, 0x10},
+	{0x00010000, 0x10000, 0x20},
+	{0x00020000, 0x10000, 0x40},
+	{0x00030000, 0x10000, 0x80}
+};
+
+static const struct str7x_mem_layout mem_layout_str7bank1[] = {
+	{0x00000000, 0x02000, 0x10000},
+	{0x00002000, 0x02000, 0x20000}
+};
+
+static int str7x_get_flash_adr(struct flash_bank *bank, uint32_t reg)
+{
+	struct str7x_flash_bank *str7x_info = bank->driver_priv;
+	return str7x_info->register_base | reg;
+}
+
+static int str7x_build_block_list(struct flash_bank *bank)
+{
+	struct str7x_flash_bank *str7x_info = bank->driver_priv;
+
+	int i;
+	int num_sectors;
+	int b0_sectors = 0, b1_sectors = 0;
+
+	switch (bank->size) {
+		case 16 * 1024:
+			b1_sectors = 2;
+			break;
+		case 64 * 1024:
+			b0_sectors = 5;
+			break;
+		case 128 * 1024:
+			b0_sectors = 6;
+			break;
+		case 256 * 1024:
+			b0_sectors = 8;
+			break;
+		default:
+			LOG_ERROR("BUG: unknown bank->size encountered");
+			exit(-1);
+	}
+
+	num_sectors = b0_sectors + b1_sectors;
+
+	bank->num_sectors = num_sectors;
+	bank->sectors = malloc(sizeof(struct flash_sector) * num_sectors);
+	str7x_info->sector_bits = malloc(sizeof(uint32_t) * num_sectors);
+
+	num_sectors = 0;
+
+	for (i = 0; i < b0_sectors; i++) {
+		bank->sectors[num_sectors].offset = mem_layout_str7bank0[i].sector_start;
+		bank->sectors[num_sectors].size = mem_layout_str7bank0[i].sector_size;
+		bank->sectors[num_sectors].is_erased = -1;
+		/* the reset_init handler marks all the sectors unprotected,
+		 * matching hardware after reset; keep the driver in sync
+		 */
+		bank->sectors[num_sectors].is_protected = 0;
+		str7x_info->sector_bits[num_sectors++] = mem_layout_str7bank0[i].sector_bit;
+	}
+
+	for (i = 0; i < b1_sectors; i++) {
+		bank->sectors[num_sectors].offset = mem_layout_str7bank1[i].sector_start;
+		bank->sectors[num_sectors].size = mem_layout_str7bank1[i].sector_size;
+		bank->sectors[num_sectors].is_erased = -1;
+		/* the reset_init handler marks all the sectors unprotected,
+		 * matching hardware after reset; keep the driver in sync
+		 */
+		bank->sectors[num_sectors].is_protected = 0;
+		str7x_info->sector_bits[num_sectors++] = mem_layout_str7bank1[i].sector_bit;
+	}
+
+	return ERROR_OK;
+}
+
+/* flash bank str7x <base> <size> 0 0 <target#> <str71_variant>
+ */
+FLASH_BANK_COMMAND_HANDLER(str7x_flash_bank_command)
+{
+	struct str7x_flash_bank *str7x_info;
+
+	if (CMD_ARGC < 7)
+		return ERROR_COMMAND_SYNTAX_ERROR;
+
+	str7x_info = malloc(sizeof(struct str7x_flash_bank));
+	bank->driver_priv = str7x_info;
+
+	/* set default bits for str71x flash */
+	str7x_info->busy_bits = (FLASH_LOCK | FLASH_BSYA1 | FLASH_BSYA0);
+	str7x_info->disable_bit = (1 << 1);
+
+	if (strcmp(CMD_ARGV[6], "STR71x") == 0)
+		str7x_info->register_base = 0x40100000;
+	else if (strcmp(CMD_ARGV[6], "STR73x") == 0) {
+		str7x_info->register_base = 0x80100000;
+		str7x_info->busy_bits = (FLASH_LOCK | FLASH_BSYA0);
+	} else if (strcmp(CMD_ARGV[6], "STR75x") == 0) {
+		str7x_info->register_base = 0x20100000;
+		str7x_info->disable_bit = (1 << 0);
+	} else {
+		LOG_ERROR("unknown STR7x variant: '%s'", CMD_ARGV[6]);
+		free(str7x_info);
+		return ERROR_FLASH_BANK_INVALID;
+	}
+
+	str7x_build_block_list(bank);
+
+	return ERROR_OK;
+}
+
+/* wait for flash to become idle or report errors.
+
+   FIX!!! what's the maximum timeout??? The documentation doesn't
+   state any maximum time.... by inspection it seems > 1000ms is to be
+   expected.
+
+   10000ms is long enough that it should cover anything, yet not
+   quite be equivalent to an infinite loop.
+
+ */
+static int str7x_waitbusy(struct flash_bank *bank)
+{
+	int err;
+	int i;
+	struct target *target = bank->target;
+	struct str7x_flash_bank *str7x_info = bank->driver_priv;
+
+	for (i = 0 ; i < 10000; i++) {
+		uint32_t retval;
+		err = target_read_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), &retval);
+		if (err != ERROR_OK)
+			return err;
+
+		if ((retval & str7x_info->busy_bits) == 0)
+			return ERROR_OK;
+
+		alive_sleep(1);
+	}
+	LOG_ERROR("Timed out waiting for str7x flash");
+	return ERROR_FAIL;
+}
+
+
+static int str7x_result(struct flash_bank *bank)
+{
+	struct target *target = bank->target;
+	uint32_t flash_flags;
+
+	int retval;
+	retval = target_read_u32(target, str7x_get_flash_adr(bank, FLASH_ER), &flash_flags);
+	if (retval != ERROR_OK)
+		return retval;
+
+	if (flash_flags & FLASH_WPF) {
+		LOG_ERROR("str7x hw write protection set");
+		retval = ERROR_FAIL;
+	}
+	if (flash_flags & FLASH_RESER) {
+		LOG_ERROR("str7x suspended program erase not resumed");
+		retval = ERROR_FAIL;
+	}
+	if (flash_flags & FLASH_10ER) {
+		LOG_ERROR("str7x trying to set bit to 1 when it is already 0");
+		retval = ERROR_FAIL;
+	}
+	if (flash_flags & FLASH_PGER) {
+		LOG_ERROR("str7x program error");
+		retval = ERROR_FAIL;
+	}
+	if (flash_flags & FLASH_ERER) {
+		LOG_ERROR("str7x erase error");
+		retval = ERROR_FAIL;
+	}
+	if (retval == ERROR_OK) {
+		if (flash_flags & FLASH_ERR) {
+			/* this should always be set if one of the others are set... */
+			LOG_ERROR("str7x write operation failed / bad setup");
+			retval = ERROR_FAIL;
+		}
+	}
+
+	return retval;
+}
+
+static int str7x_protect_check(struct flash_bank *bank)
+{
+	struct str7x_flash_bank *str7x_info = bank->driver_priv;
+	struct target *target = bank->target;
+
+	int i;
+	uint32_t flash_flags;
+
+	if (bank->target->state != TARGET_HALTED) {
+		LOG_ERROR("Target not halted");
+		return ERROR_TARGET_NOT_HALTED;
+	}
+
+	int retval;
+	retval = target_read_u32(target, str7x_get_flash_adr(bank, FLASH_NVWPAR), &flash_flags);
+	if (retval != ERROR_OK)
+		return retval;
+
+	for (i = 0; i < bank->num_sectors; i++) {
+		if (flash_flags & str7x_info->sector_bits[i])
+			bank->sectors[i].is_protected = 0;
+		else
+			bank->sectors[i].is_protected = 1;
+	}
+
+	return ERROR_OK;
+}
+
+static int str7x_erase(struct flash_bank *bank, int first, int last)
+{
+	struct str7x_flash_bank *str7x_info = bank->driver_priv;
+	struct target *target = bank->target;
+
+	int i;
+	uint32_t cmd;
+	uint32_t sectors = 0;
+	int err;
+
+	if (bank->target->state != TARGET_HALTED) {
+		LOG_ERROR("Target not halted");
+		return ERROR_TARGET_NOT_HALTED;
+	}
+
+	for (i = first; i <= last; i++)
+		sectors |= str7x_info->sector_bits[i];
+
+	LOG_DEBUG("sectors: 0x%" PRIx32 "", sectors);
+
+	/* clear FLASH_ER register */
+	err = target_write_u32(target, str7x_get_flash_adr(bank, FLASH_ER), 0x0);
+	if (err != ERROR_OK)
+		return err;
+
+	cmd = FLASH_SER;
+	err = target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), cmd);
+	if (err != ERROR_OK)
+		return err;
+
+	cmd = sectors;
+	err = target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR1), cmd);
+	if (err != ERROR_OK)
+		return err;
+
+	cmd = FLASH_SER | FLASH_WMS;
+	err = target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), cmd);
+	if (err != ERROR_OK)
+		return err;
+
+	err = str7x_waitbusy(bank);
+	if (err != ERROR_OK)
+		return err;
+
+	err = str7x_result(bank);
+	if (err != ERROR_OK)
+		return err;
+
+	for (i = first; i <= last; i++)
+		bank->sectors[i].is_erased = 1;
+
+	return ERROR_OK;
+}
+
+static int str7x_protect(struct flash_bank *bank, int set, int first, int last)
+{
+	struct str7x_flash_bank *str7x_info = bank->driver_priv;
+	struct target *target = bank->target;
+	int i;
+	uint32_t cmd;
+	uint32_t protect_blocks;
+
+	if (bank->target->state != TARGET_HALTED) {
+		LOG_ERROR("Target not halted");
+		return ERROR_TARGET_NOT_HALTED;
+	}
+
+	protect_blocks = 0xFFFFFFFF;
+
+	if (set) {
+		for (i = first; i <= last; i++)
+			protect_blocks &= ~(str7x_info->sector_bits[i]);
+	}
+
+	/* clear FLASH_ER register */
+	int err;
+	err = target_write_u32(target, str7x_get_flash_adr(bank, FLASH_ER), 0x0);
+	if (err != ERROR_OK)
+		return err;
+
+	cmd = FLASH_SPR;
+	err = target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), cmd);
+	if (err != ERROR_OK)
+		return err;
+
+	cmd = str7x_get_flash_adr(bank, FLASH_NVWPAR);
+	err = target_write_u32(target, str7x_get_flash_adr(bank, FLASH_AR), cmd);
+	if (err != ERROR_OK)
+		return err;
+
+	cmd = protect_blocks;
+	err = target_write_u32(target, str7x_get_flash_adr(bank, FLASH_DR0), cmd);
+	if (err != ERROR_OK)
+		return err;
+
+	cmd = FLASH_SPR | FLASH_WMS;
+	err = target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), cmd);
+	if (err != ERROR_OK)
+		return err;
+
+	err = str7x_waitbusy(bank);
+	if (err != ERROR_OK)
+		return err;
+
+	err = str7x_result(bank);
+	if (err != ERROR_OK)
+		return err;
+
+	return ERROR_OK;
+}
+
+static int str7x_write_block(struct flash_bank *bank, const uint8_t *buffer,
+		uint32_t offset, uint32_t count)
+{
+	struct str7x_flash_bank *str7x_info = bank->driver_priv;
+	struct target *target = bank->target;
+	uint32_t buffer_size = 32768;
+	struct working_area *write_algorithm;
+	struct working_area *source;
+	uint32_t address = bank->base + offset;
+	struct reg_param reg_params[6];
+	struct arm_algorithm arm_algo;
+	int retval = ERROR_OK;
+
+	/* see contib/loaders/flash/str7x.s for src */
+
+	static const uint32_t str7x_flash_write_code[] = {
+					/* write:				*/
+		0xe3a04201, /*	mov r4, #0x10000000	*/
+		0xe5824000, /*	str r4, [r2, #0x0]	*/
+		0xe5821010, /*	str r1, [r2, #0x10]	*/
+		0xe4904004, /*	ldr r4, [r0], #4	*/
+		0xe5824008, /*	str r4, [r2, #0x8]	*/
+		0xe4904004, /*	ldr r4, [r0], #4	*/
+		0xe582400c, /*	str r4, [r2, #0xc]	*/
+		0xe3a04209, /*	mov r4, #0x90000000	*/
+		0xe5824000, /*	str r4, [r2, #0x0]	*/
+					/* busy:				*/
+		0xe5924000, /*	ldr r4, [r2, #0x0]	*/
+		0xe1140005,	/*	tst r4, r5			*/
+		0x1afffffc, /*	bne busy			*/
+		0xe5924014, /*	ldr r4, [r2, #0x14]	*/
+		0xe31400ff, /*	tst r4, #0xff		*/
+		0x03140c01, /*	tsteq r4, #0x100	*/
+		0x1a000002, /*	bne exit			*/
+		0xe2811008, /*	add r1, r1, #0x8	*/
+		0xe2533001, /*	subs r3, r3, #1		*/
+		0x1affffec, /*	bne write			*/
+					/* exit:				*/
+		0xeafffffe, /*	b exit				*/
+	};
+
+	/* flash write code */
+	if (target_alloc_working_area_try(target, sizeof(str7x_flash_write_code),
+			&write_algorithm) != ERROR_OK) {
+		return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+	}
+
+	uint8_t code[sizeof(str7x_flash_write_code)];
+	target_buffer_set_u32_array(target, code, ARRAY_SIZE(str7x_flash_write_code),
+			str7x_flash_write_code);
+	target_write_buffer(target, write_algorithm->address, sizeof(code), code);
+
+	/* memory buffer */
+	while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) {
+		buffer_size /= 2;
+		if (buffer_size <= 256) {
+			/* we already allocated the writing code, but failed to get a
+			 * buffer, free the algorithm */
+			target_free_working_area(target, write_algorithm);
+
+			LOG_WARNING("no large enough working area available, can't do block memory writes");
+			return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+		}
+	}
+
+	arm_algo.common_magic = ARM_COMMON_MAGIC;
+	arm_algo.core_mode = ARM_MODE_SVC;
+	arm_algo.core_state = ARM_STATE_ARM;
+
+	init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT);
+	init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
+	init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT);
+	init_reg_param(&reg_params[3], "r3", 32, PARAM_OUT);
+	init_reg_param(&reg_params[4], "r4", 32, PARAM_IN);
+	init_reg_param(&reg_params[5], "r5", 32, PARAM_OUT);
+
+	while (count > 0) {
+		uint32_t thisrun_count = (count > (buffer_size / 8)) ? (buffer_size / 8) : count;
+
+		target_write_buffer(target, source->address, thisrun_count * 8, buffer);
+
+		buf_set_u32(reg_params[0].value, 0, 32, source->address);
+		buf_set_u32(reg_params[1].value, 0, 32, address);
+		buf_set_u32(reg_params[2].value, 0, 32, str7x_get_flash_adr(bank, FLASH_CR0));
+		buf_set_u32(reg_params[3].value, 0, 32, thisrun_count);
+		buf_set_u32(reg_params[5].value, 0, 32, str7x_info->busy_bits);
+
+		retval = target_run_algorithm(target, 0, NULL, 6, reg_params,
+				write_algorithm->address,
+				write_algorithm->address + (sizeof(str7x_flash_write_code) - 4),
+				10000, &arm_algo);
+		if (retval != ERROR_OK)
+			break;
+
+		if (buf_get_u32(reg_params[4].value, 0, 32) != 0x00) {
+			retval = str7x_result(bank);
+			break;
+		}
+
+		buffer += thisrun_count * 8;
+		address += thisrun_count * 8;
+		count -= thisrun_count;
+	}
+
+	target_free_working_area(target, source);
+	target_free_working_area(target, write_algorithm);
+
+	destroy_reg_param(&reg_params[0]);
+	destroy_reg_param(&reg_params[1]);
+	destroy_reg_param(&reg_params[2]);
+	destroy_reg_param(&reg_params[3]);
+	destroy_reg_param(&reg_params[4]);
+	destroy_reg_param(&reg_params[5]);
+
+	return retval;
+}
+
+static int str7x_write(struct flash_bank *bank, const uint8_t *buffer,
+		uint32_t offset, uint32_t count)
+{
+	struct target *target = bank->target;
+	uint32_t dwords_remaining = (count / 8);
+	uint32_t bytes_remaining = (count & 0x00000007);
+	uint32_t address = bank->base + offset;
+	uint32_t bytes_written = 0;
+	uint32_t cmd;
+	int retval;
+	uint32_t check_address = offset;
+	int i;
+
+	if (bank->target->state != TARGET_HALTED) {
+		LOG_ERROR("Target not halted");
+		return ERROR_TARGET_NOT_HALTED;
+	}
+
+	if (offset & 0x7) {
+		LOG_WARNING("offset 0x%" PRIx32 " breaks required 8-byte alignment", offset);
+		return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
+	}
+
+	for (i = 0; i < bank->num_sectors; i++) {
+		uint32_t sec_start = bank->sectors[i].offset;
+		uint32_t sec_end = sec_start + bank->sectors[i].size;
+
+		/* check if destination falls within the current sector */
+		if ((check_address >= sec_start) && (check_address < sec_end)) {
+			/* check if destination ends in the current sector */
+			if (offset + count < sec_end)
+				check_address = offset + count;
+			else
+				check_address = sec_end;
+		}
+	}
+
+	if (check_address != offset + count)
+		return ERROR_FLASH_DST_OUT_OF_BANK;
+
+	/* clear FLASH_ER register */
+	target_write_u32(target, str7x_get_flash_adr(bank, FLASH_ER), 0x0);
+
+	/* multiple dwords (8-byte) to be programmed? */
+	if (dwords_remaining > 0) {
+		/* try using a block write */
+		retval = str7x_write_block(bank, buffer, offset, dwords_remaining);
+		if (retval != ERROR_OK) {
+			if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) {
+				/* if block write failed (no sufficient working area),
+				 * we use normal (slow) single dword accesses */
+				LOG_WARNING("couldn't use block writes, falling back to single memory accesses");
+			} else {
+				return retval;
+			}
+		} else {
+			buffer += dwords_remaining * 8;
+			address += dwords_remaining * 8;
+			dwords_remaining = 0;
+		}
+	}
+
+	while (dwords_remaining > 0) {
+		/* command */
+		cmd = FLASH_DWPG;
+		target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), cmd);
+
+		/* address */
+		target_write_u32(target, str7x_get_flash_adr(bank, FLASH_AR), address);
+
+		/* data word 1 */
+		target_write_memory(target, str7x_get_flash_adr(bank, FLASH_DR0),
+				4, 1, buffer + bytes_written);
+		bytes_written += 4;
+
+		/* data word 2 */
+		target_write_memory(target, str7x_get_flash_adr(bank, FLASH_DR1),
+				4, 1, buffer + bytes_written);
+		bytes_written += 4;
+
+		/* start programming cycle */
+		cmd = FLASH_DWPG | FLASH_WMS;
+		target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), cmd);
+
+		int err;
+		err = str7x_waitbusy(bank);
+		if (err != ERROR_OK)
+			return err;
+
+		err = str7x_result(bank);
+		if (err != ERROR_OK)
+			return err;
+
+		dwords_remaining--;
+		address += 8;
+	}
+
+	if (bytes_remaining) {
+		uint8_t last_dword[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+		/* copy the last remaining bytes into the write buffer */
+		memcpy(last_dword, buffer+bytes_written, bytes_remaining);
+
+		/* command */
+		cmd = FLASH_DWPG;
+		target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), cmd);
+
+		/* address */
+		target_write_u32(target, str7x_get_flash_adr(bank, FLASH_AR), address);
+
+		/* data word 1 */
+		target_write_memory(target, str7x_get_flash_adr(bank, FLASH_DR0),
+				4, 1, last_dword);
+
+		/* data word 2 */
+		target_write_memory(target, str7x_get_flash_adr(bank, FLASH_DR1),
+				4, 1, last_dword + 4);
+
+		/* start programming cycle */
+		cmd = FLASH_DWPG | FLASH_WMS;
+		target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), cmd);
+
+		int err;
+		err = str7x_waitbusy(bank);
+		if (err != ERROR_OK)
+			return err;
+
+		err = str7x_result(bank);
+		if (err != ERROR_OK)
+			return err;
+	}
+
+	return ERROR_OK;
+}
+
+static int str7x_probe(struct flash_bank *bank)
+{
+	return ERROR_OK;
+}
+
+#if 0
+COMMAND_HANDLER(str7x_handle_part_id_command)
+{
+	return ERROR_OK;
+}
+#endif
+
+static int get_str7x_info(struct flash_bank *bank, char *buf, int buf_size)
+{
+	/* Setting the write protection on a sector is a permanent change but it
+	 * can be disabled temporarily. FLASH_NVWPAR reflects the permanent
+	 * protection state of the sectors, not the temporary.
+	 */
+	snprintf(buf, buf_size, "STR7x flash protection info is only valid after a power cycle, "
+			"clearing the protection is only temporary and may not be reflected in the current "
+			"info returned.");
+	return ERROR_OK;
+}
+
+COMMAND_HANDLER(str7x_handle_disable_jtag_command)
+{
+	struct target *target = NULL;
+	struct str7x_flash_bank *str7x_info = NULL;
+
+	uint32_t flash_cmd;
+	uint16_t ProtectionLevel = 0;
+	uint16_t ProtectionRegs;
+
+	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 (ERROR_OK != retval)
+		return retval;
+
+	str7x_info = bank->driver_priv;
+
+	target = bank->target;
+
+	if (target->state != TARGET_HALTED) {
+		LOG_ERROR("Target not halted");
+		return ERROR_TARGET_NOT_HALTED;
+	}
+
+	/* first we get protection status */
+	uint32_t reg;
+	target_read_u32(target, str7x_get_flash_adr(bank, FLASH_NVAPR0), &reg);
+
+	if (!(reg & str7x_info->disable_bit))
+		ProtectionLevel = 1;
+
+	target_read_u32(target, str7x_get_flash_adr(bank, FLASH_NVAPR1), &reg);
+	ProtectionRegs = ~(reg >> 16);
+
+	while (((ProtectionRegs) != 0) && (ProtectionLevel < 16)) {
+		ProtectionRegs >>= 1;
+		ProtectionLevel++;
+	}
+
+	if (ProtectionLevel == 0) {
+		flash_cmd = FLASH_SPR;
+		target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), flash_cmd);
+		target_write_u32(target, str7x_get_flash_adr(bank, FLASH_AR), 0x4010DFB8);
+		target_write_u32(target, str7x_get_flash_adr(bank, FLASH_DR0), 0xFFFFFFFD);
+		flash_cmd = FLASH_SPR | FLASH_WMS;
+		target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), flash_cmd);
+	} else {
+		flash_cmd = FLASH_SPR;
+		target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), flash_cmd);
+		target_write_u32(target, str7x_get_flash_adr(bank, FLASH_AR), 0x4010DFBC);
+		target_write_u32(target, str7x_get_flash_adr(bank, FLASH_DR0),
+				~(1 << (15 + ProtectionLevel)));
+		flash_cmd = FLASH_SPR | FLASH_WMS;
+		target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), flash_cmd);
+	}
+
+	return ERROR_OK;
+}
+
+static const struct command_registration str7x_exec_command_handlers[] = {
+	{
+		.name = "disable_jtag",
+		.usage = "<bank>",
+		.handler = str7x_handle_disable_jtag_command,
+		.mode = COMMAND_EXEC,
+		.help = "disable jtag access",
+	},
+	COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration str7x_command_handlers[] = {
+	{
+		.name = "str7x",
+		.mode = COMMAND_ANY,
+		.help = "str7x flash command group",
+		.usage = "",
+		.chain = str7x_exec_command_handlers,
+	},
+	COMMAND_REGISTRATION_DONE
+};
+
+struct flash_driver str7x_flash = {
+	.name = "str7x",
+	.commands = str7x_command_handlers,
+	.flash_bank_command = str7x_flash_bank_command,
+	.erase = str7x_erase,
+	.protect = str7x_protect,
+	.write = str7x_write,
+	.read = default_flash_read,
+	.probe = str7x_probe,
+	.auto_probe = str7x_probe,
+	.erase_check = default_flash_blank_check,
+	.protect_check = str7x_protect_check,
+	.info = get_str7x_info,
+};

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-site/blob/e302582d/docs/os/tutorials/downloads/openocd-code-89bf96ffe6ac66c80407af8383b9d5adc0dc35f4/src/flash/nor/str9x.c
----------------------------------------------------------------------
diff --git a/docs/os/tutorials/downloads/openocd-code-89bf96ffe6ac66c80407af8383b9d5adc0dc35f4/src/flash/nor/str9x.c b/docs/os/tutorials/downloads/openocd-code-89bf96ffe6ac66c80407af8383b9d5adc0dc35f4/src/flash/nor/str9x.c
new file mode 100755
index 0000000..b3f08b0
--- /dev/null
+++ b/docs/os/tutorials/downloads/openocd-code-89bf96ffe6ac66c80407af8383b9d5adc0dc35f4/src/flash/nor/str9x.c
@@ -0,0 +1,684 @@
+/***************************************************************************
+ *   Copyright (C) 2005 by Dominic Rath                                    *
+ *   Dominic.Rath@gmx.de                                                   *
+ *                                                                         *
+ *   Copyright (C) 2008 by Spencer Oliver                                  *
+ *   spen@spen-soft.co.uk                                                  *
+ *
+ *   Copyright (C) 2008 by Oyvind Harboe                                   *
+ *   oyvind.harboe@zylin.com                                               *
+ *                                                                         *
+ *   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.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.           *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "imp.h"
+#include <target/arm966e.h>
+#include <target/algorithm.h>
+
+/* Flash registers */
+
+#define FLASH_BBSR		0x54000000		/* Boot Bank Size Register                */
+#define FLASH_NBBSR		0x54000004		/* Non-Boot Bank Size Register            */
+#define FLASH_BBADR		0x5400000C		/* Boot Bank Base Address Register        */
+#define FLASH_NBBADR	0x54000010		/* Non-Boot Bank Base Address Register    */
+#define FLASH_CR		0x54000018		/* Control Register                       */
+#define FLASH_SR		0x5400001C		/* Status Register                        */
+#define FLASH_BCE5ADDR	0x54000020		/* BC Fifth Entry Target Address Register */
+
+struct str9x_flash_bank {
+	uint32_t *sector_bits;
+	int variant;
+	int bank1;
+};
+
+enum str9x_status_codes {
+	STR9X_CMD_SUCCESS = 0,
+	STR9X_INVALID_COMMAND = 1,
+	STR9X_SRC_ADDR_ERROR = 2,
+	STR9X_DST_ADDR_ERROR = 3,
+	STR9X_SRC_ADDR_NOT_MAPPED = 4,
+	STR9X_DST_ADDR_NOT_MAPPED = 5,
+	STR9X_COUNT_ERROR = 6,
+	STR9X_INVALID_SECTOR = 7,
+	STR9X_SECTOR_NOT_BLANK = 8,
+	STR9X_SECTOR_NOT_PREPARED = 9,
+	STR9X_COMPARE_ERROR = 10,
+	STR9X_BUSY = 11
+};
+
+static uint32_t bank1start = 0x00080000;
+
+static int str9x_build_block_list(struct flash_bank *bank)
+{
+	struct str9x_flash_bank *str9x_info = bank->driver_priv;
+
+	int i;
+	int num_sectors;
+	int b0_sectors = 0, b1_sectors = 0;
+	uint32_t offset = 0;
+
+	/* set if we have large flash str9 */
+	str9x_info->variant = 0;
+	str9x_info->bank1 = 0;
+
+	switch (bank->size) {
+		case (256 * 1024):
+			b0_sectors = 4;
+			break;
+		case (512 * 1024):
+			b0_sectors = 8;
+			break;
+		case (1024 * 1024):
+			bank1start = 0x00100000;
+			str9x_info->variant = 1;
+			b0_sectors = 16;
+			break;
+		case (2048 * 1024):
+			bank1start = 0x00200000;
+			str9x_info->variant = 1;
+			b0_sectors = 32;
+			break;
+		case (128 * 1024):
+			str9x_info->variant = 1;
+			str9x_info->bank1 = 1;
+			b1_sectors = 8;
+			bank1start = bank->base;
+			break;
+		case (32 * 1024):
+			str9x_info->bank1 = 1;
+			b1_sectors = 4;
+			bank1start = bank->base;
+			break;
+		default:
+			LOG_ERROR("BUG: unknown bank->size encountered");
+			exit(-1);
+	}
+
+	num_sectors = b0_sectors + b1_sectors;
+
+	bank->num_sectors = num_sectors;
+	bank->sectors = malloc(sizeof(struct flash_sector) * num_sectors);
+	str9x_info->sector_bits = malloc(sizeof(uint32_t) * num_sectors);
+
+	num_sectors = 0;
+
+	for (i = 0; i < b0_sectors; i++) {
+		bank->sectors[num_sectors].offset = offset;
+		bank->sectors[num_sectors].size = 0x10000;
+		offset += bank->sectors[i].size;
+		bank->sectors[num_sectors].is_erased = -1;
+		bank->sectors[num_sectors].is_protected = 1;
+		str9x_info->sector_bits[num_sectors++] = (1 << i);
+	}
+
+	for (i = 0; i < b1_sectors; i++) {
+		bank->sectors[num_sectors].offset = offset;
+		bank->sectors[num_sectors].size = str9x_info->variant == 0 ? 0x2000 : 0x4000;
+		offset += bank->sectors[i].size;
+		bank->sectors[num_sectors].is_erased = -1;
+		bank->sectors[num_sectors].is_protected = 1;
+		if (str9x_info->variant)
+			str9x_info->sector_bits[num_sectors++] = (1 << i);
+		else
+			str9x_info->sector_bits[num_sectors++] = (1 << (i + 8));
+	}
+
+	return ERROR_OK;
+}
+
+/* flash bank str9x <base> <size> 0 0 <target#>
+ */
+FLASH_BANK_COMMAND_HANDLER(str9x_flash_bank_command)
+{
+	struct str9x_flash_bank *str9x_info;
+
+	if (CMD_ARGC < 6)
+		return ERROR_COMMAND_SYNTAX_ERROR;
+
+	str9x_info = malloc(sizeof(struct str9x_flash_bank));
+	bank->driver_priv = str9x_info;
+
+	str9x_build_block_list(bank);
+
+	return ERROR_OK;
+}
+
+static int str9x_protect_check(struct flash_bank *bank)
+{
+	int retval;
+	struct str9x_flash_bank *str9x_info = bank->driver_priv;
+	struct target *target = bank->target;
+
+	int i;
+	uint32_t adr;
+	uint32_t status = 0;
+	uint16_t hstatus = 0;
+
+	if (bank->target->state != TARGET_HALTED) {
+		LOG_ERROR("Target not halted");
+		return ERROR_TARGET_NOT_HALTED;
+	}
+
+	/* read level one protection */
+
+	if (str9x_info->variant) {
+		if (str9x_info->bank1) {
+			adr = bank1start + 0x18;
+			retval = target_write_u16(target, adr, 0x90);
+			if (retval != ERROR_OK)
+				return retval;
+			retval = target_read_u16(target, adr, &hstatus);
+			if (retval != ERROR_OK)
+				return retval;
+			status = hstatus;
+		} else {
+			adr = bank1start + 0x14;
+			retval = target_write_u16(target, adr, 0x90);
+			if (retval != ERROR_OK)
+				return retval;
+			retval = target_read_u32(target, adr, &status);
+			if (retval != ERROR_OK)
+				return retval;
+		}
+	} else {
+		adr = bank1start + 0x10;
+		retval = target_write_u16(target, adr, 0x90);
+		if (retval != ERROR_OK)
+			return retval;
+		retval = target_read_u16(target, adr, &hstatus);
+		if (retval != ERROR_OK)
+			return retval;
+		status = hstatus;
+	}
+
+	/* read array command */
+	retval = target_write_u16(target, adr, 0xFF);
+	if (retval != ERROR_OK)
+		return retval;
+
+	for (i = 0; i < bank->num_sectors; i++) {
+		if (status & str9x_info->sector_bits[i])
+			bank->sectors[i].is_protected = 1;
+		else
+			bank->sectors[i].is_protected = 0;
+	}
+
+	return ERROR_OK;
+}
+
+static int str9x_erase(struct flash_bank *bank, int first, int last)
+{
+	struct target *target = bank->target;
+	int i;
+	uint32_t adr;
+	uint8_t status;
+	uint8_t erase_cmd;
+	int total_timeout;
+
+	if (bank->target->state != TARGET_HALTED) {
+		LOG_ERROR("Target not halted");
+		return ERROR_TARGET_NOT_HALTED;
+	}
+
+	/* Check if we can erase whole bank */
+	if ((first == 0) && (last == (bank->num_sectors - 1))) {
+		/* Optimize to run erase bank command instead of sector */
+		erase_cmd = 0x80;
+		/* Add timeout duration since erase bank takes more time */
+		total_timeout = 1000 * bank->num_sectors;
+	} else {
+		/* Erase sector command */
+		erase_cmd = 0x20;
+		total_timeout = 1000;
+	}
+
+	/* this is so the compiler can *know* */
+	assert(total_timeout > 0);
+
+	for (i = first; i <= last; i++) {
+		int retval;
+		adr = bank->base + bank->sectors[i].offset;
+
+		/* erase sectors or block */
+		retval = target_write_u16(target, adr, erase_cmd);
+		if (retval != ERROR_OK)
+			return retval;
+		retval = target_write_u16(target, adr, 0xD0);
+		if (retval != ERROR_OK)
+			return retval;
+
+		/* get status */
+		retval = target_write_u16(target, adr, 0x70);
+		if (retval != ERROR_OK)
+			return retval;
+
+		int timeout;
+		for (timeout = 0; timeout < total_timeout; timeout++) {
+			retval = target_read_u8(target, adr, &status);
+			if (retval != ERROR_OK)
+				return retval;
+			if (status & 0x80)
+				break;
+			alive_sleep(1);
+		}
+		if (timeout == total_timeout) {
+			LOG_ERROR("erase timed out");
+			return ERROR_FAIL;
+		}
+
+		/* clear status, also clear read array */
+		retval = target_write_u16(target, adr, 0x50);
+		if (retval != ERROR_OK)
+			return retval;
+
+		/* read array command */
+		retval = target_write_u16(target, adr, 0xFF);
+		if (retval != ERROR_OK)
+			return retval;
+
+		if (status & 0x22) {
+			LOG_ERROR("error erasing flash bank, status: 0x%x", status);
+			return ERROR_FLASH_OPERATION_FAILED;
+		}
+
+		/* If we ran erase bank command, we are finished */
+		if (erase_cmd == 0x80)
+			break;
+	}
+
+	for (i = first; i <= last; i++)
+		bank->sectors[i].is_erased = 1;
+
+	return ERROR_OK;
+}
+
+static int str9x_protect(struct flash_bank *bank,
+		int set, int first, int last)
+{
+	struct target *target = bank->target;
+	int i;
+	uint32_t adr;
+	uint8_t status;
+
+	if (bank->target->state != TARGET_HALTED) {
+		LOG_ERROR("Target not halted");
+		return ERROR_TARGET_NOT_HALTED;
+	}
+
+	for (i = first; i <= last; i++) {
+		/* Level One Protection */
+
+		adr = bank->base + bank->sectors[i].offset;
+
+		target_write_u16(target, adr, 0x60);
+		if (set)
+			target_write_u16(target, adr, 0x01);
+		else
+			target_write_u16(target, adr, 0xD0);
+
+		/* query status */
+		target_read_u8(target, adr, &status);
+
+		/* clear status, also clear read array */
+		target_write_u16(target, adr, 0x50);
+
+		/* read array command */
+		target_write_u16(target, adr, 0xFF);
+	}
+
+	return ERROR_OK;
+}
+
+static int str9x_write_block(struct flash_bank *bank,
+		const uint8_t *buffer, uint32_t offset, uint32_t count)
+{
+	struct target *target = bank->target;
+	uint32_t buffer_size = 32768;
+	struct working_area *write_algorithm;
+	struct working_area *source;
+	uint32_t address = bank->base + offset;
+	struct reg_param reg_params[4];
+	struct arm_algorithm arm_algo;
+	int retval = ERROR_OK;
+
+	/* see contib/loaders/flash/str9x.s for src */
+
+	static const uint32_t str9x_flash_write_code[] = {
+					/* write:				*/
+		0xe3c14003,	/*	bic	r4, r1, #3		*/
+		0xe3a03040,	/*	mov	r3, #0x40		*/
+		0xe1c430b0,	/*	strh r3, [r4, #0]	*/
+		0xe0d030b2,	/*	ldrh r3, [r0], #2	*/
+		0xe0c130b2,	/*	strh r3, [r1], #2	*/
+		0xe3a03070,	/*	mov r3, #0x70		*/
+		0xe1c430b0,	/*	strh r3, [r4, #0]	*/
+					/* busy:				*/
+		0xe5d43000,	/*	ldrb r3, [r4, #0]	*/
+		0xe3130080,	/*	tst r3, #0x80		*/
+		0x0afffffc,	/*	beq busy			*/
+		0xe3a05050,	/*	mov	r5, #0x50		*/
+		0xe1c450b0,	/*	strh r5, [r4, #0]	*/
+		0xe3a050ff,	/*	mov	r5, #0xFF		*/
+		0xe1c450b0,	/*	strh r5, [r4, #0]	*/
+		0xe3130012,	/*	tst	r3, #0x12		*/
+		0x1a000001,	/*	bne exit			*/
+		0xe2522001,	/*	subs r2, r2, #1		*/
+		0x1affffed,	/*	bne write			*/
+					/* exit:				*/
+		0xe1200070,	/*	bkpt #0				*/
+	};
+
+	/* flash write code */
+	if (target_alloc_working_area(target, sizeof(str9x_flash_write_code),
+			&write_algorithm) != ERROR_OK) {
+		LOG_WARNING("no working area available, can't do block memory writes");
+		return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+	}
+
+	uint8_t code[sizeof(str9x_flash_write_code)];
+	target_buffer_set_u32_array(target, code, ARRAY_SIZE(str9x_flash_write_code),
+			str9x_flash_write_code);
+	target_write_buffer(target, write_algorithm->address, sizeof(code), code);
+
+	/* memory buffer */
+	while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) {
+		buffer_size /= 2;
+		if (buffer_size <= 256) {
+			/* we already allocated the writing code, but failed to get a
+			 * buffer, free the algorithm */
+			target_free_working_area(target, write_algorithm);
+
+			LOG_WARNING("no large enough working area available, can't do block memory writes");
+			return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+		}
+	}
+
+	arm_algo.common_magic = ARM_COMMON_MAGIC;
+	arm_algo.core_mode = ARM_MODE_SVC;
+	arm_algo.core_state = ARM_STATE_ARM;
+
+	init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT);
+	init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
+	init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT);
+	init_reg_param(&reg_params[3], "r3", 32, PARAM_IN);
+
+	while (count > 0) {
+		uint32_t thisrun_count = (count > (buffer_size / 2)) ? (buffer_size / 2) : count;
+
+		target_write_buffer(target, source->address, thisrun_count * 2, buffer);
+
+		buf_set_u32(reg_params[0].value, 0, 32, source->address);
+		buf_set_u32(reg_params[1].value, 0, 32, address);
+		buf_set_u32(reg_params[2].value, 0, 32, thisrun_count);
+
+		retval = target_run_algorithm(target, 0, NULL, 4, reg_params,
+				write_algorithm->address,
+				0, 10000, &arm_algo);
+		if (retval != ERROR_OK) {
+			LOG_ERROR("error executing str9x flash write algorithm");
+			retval = ERROR_FLASH_OPERATION_FAILED;
+			break;
+		}
+
+		if (buf_get_u32(reg_params[3].value, 0, 32) != 0x80) {
+			retval = ERROR_FLASH_OPERATION_FAILED;
+			break;
+		}
+
+		buffer += thisrun_count * 2;
+		address += thisrun_count * 2;
+		count -= thisrun_count;
+	}
+
+	target_free_working_area(target, source);
+	target_free_working_area(target, write_algorithm);
+
+	destroy_reg_param(&reg_params[0]);
+	destroy_reg_param(&reg_params[1]);
+	destroy_reg_param(&reg_params[2]);
+	destroy_reg_param(&reg_params[3]);
+
+	return retval;
+}
+
+static int str9x_write(struct flash_bank *bank,
+		const uint8_t *buffer, uint32_t offset, uint32_t count)
+{
+	struct target *target = bank->target;
+	uint32_t words_remaining = (count / 2);
+	uint32_t bytes_remaining = (count & 0x00000001);
+	uint32_t address = bank->base + offset;
+	uint32_t bytes_written = 0;
+	uint8_t status;
+	int retval;
+	uint32_t check_address = offset;
+	uint32_t bank_adr;
+	int i;
+
+	if (bank->target->state != TARGET_HALTED) {
+		LOG_ERROR("Target not halted");
+		return ERROR_TARGET_NOT_HALTED;
+	}
+
+	if (offset & 0x1) {
+		LOG_WARNING("offset 0x%" PRIx32 " breaks required 2-byte alignment", offset);
+		return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
+	}
+
+	for (i = 0; i < bank->num_sectors; i++) {
+		uint32_t sec_start = bank->sectors[i].offset;
+		uint32_t sec_end = sec_start + bank->sectors[i].size;
+
+		/* check if destination falls within the current sector */
+		if ((check_address >= sec_start) && (check_address < sec_end)) {
+			/* check if destination ends in the current sector */
+			if (offset + count < sec_end)
+				check_address = offset + count;
+			else
+				check_address = sec_end;
+		}
+	}
+
+	if (check_address != offset + count)
+		return ERROR_FLASH_DST_OUT_OF_BANK;
+
+	/* multiple half words (2-byte) to be programmed? */
+	if (words_remaining > 0) {
+		/* try using a block write */
+		retval = str9x_write_block(bank, buffer, offset, words_remaining);
+		if (retval != ERROR_OK) {
+			if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) {
+				/* if block write failed (no sufficient working area),
+				 * we use normal (slow) single dword accesses */
+				LOG_WARNING("couldn't use block writes, falling back to single memory accesses");
+			} else if (retval == ERROR_FLASH_OPERATION_FAILED) {
+				LOG_ERROR("flash writing failed");
+				return ERROR_FLASH_OPERATION_FAILED;
+			}
+		} else {
+			buffer += words_remaining * 2;
+			address += words_remaining * 2;
+			words_remaining = 0;
+		}
+	}
+
+	while (words_remaining > 0) {
+		bank_adr = address & ~0x03;
+
+		/* write data command */
+		target_write_u16(target, bank_adr, 0x40);
+		target_write_memory(target, address, 2, 1, buffer + bytes_written);
+
+		/* get status command */
+		target_write_u16(target, bank_adr, 0x70);
+
+		int timeout;
+		for (timeout = 0; timeout < 1000; timeout++) {
+			target_read_u8(target, bank_adr, &status);
+			if (status & 0x80)
+				break;
+			alive_sleep(1);
+		}
+		if (timeout == 1000) {
+			LOG_ERROR("write timed out");
+			return ERROR_FAIL;
+		}
+
+		/* clear status reg and read array */
+		target_write_u16(target, bank_adr, 0x50);
+		target_write_u16(target, bank_adr, 0xFF);
+
+		if (status & 0x10)
+			return ERROR_FLASH_OPERATION_FAILED;
+		else if (status & 0x02)
+			return ERROR_FLASH_OPERATION_FAILED;
+
+		bytes_written += 2;
+		words_remaining--;
+		address += 2;
+	}
+
+	if (bytes_remaining) {
+		uint8_t last_halfword[2] = {0xff, 0xff};
+
+		/* copy the last remaining bytes into the write buffer */
+		memcpy(last_halfword, buffer+bytes_written, bytes_remaining);
+
+		bank_adr = address & ~0x03;
+
+		/* write data command */
+		target_write_u16(target, bank_adr, 0x40);
+		target_write_memory(target, address, 2, 1, last_halfword);
+
+		/* query status command */
+		target_write_u16(target, bank_adr, 0x70);
+
+		int timeout;
+		for (timeout = 0; timeout < 1000; timeout++) {
+			target_read_u8(target, bank_adr, &status);
+			if (status & 0x80)
+				break;
+			alive_sleep(1);
+		}
+		if (timeout == 1000) {
+			LOG_ERROR("write timed out");
+			return ERROR_FAIL;
+		}
+
+		/* clear status reg and read array */
+		target_write_u16(target, bank_adr, 0x50);
+		target_write_u16(target, bank_adr, 0xFF);
+
+		if (status & 0x10)
+			return ERROR_FLASH_OPERATION_FAILED;
+		else if (status & 0x02)
+			return ERROR_FLASH_OPERATION_FAILED;
+	}
+
+	return ERROR_OK;
+}
+
+static int str9x_probe(struct flash_bank *bank)
+{
+	return ERROR_OK;
+}
+
+#if 0
+COMMAND_HANDLER(str9x_handle_part_id_command)
+{
+	return ERROR_OK;
+}
+#endif
+
+COMMAND_HANDLER(str9x_handle_flash_config_command)
+{
+	struct target *target = NULL;
+
+	if (CMD_ARGC < 5)
+		return ERROR_COMMAND_SYNTAX_ERROR;
+
+	struct flash_bank *bank;
+	int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
+	if (ERROR_OK != retval)
+		return retval;
+
+	uint32_t bbsr, nbbsr, bbadr, nbbadr;
+	COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], bbsr);
+	COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], nbbsr);
+	COMMAND_PARSE_NUMBER(u32, CMD_ARGV[3], bbadr);
+	COMMAND_PARSE_NUMBER(u32, CMD_ARGV[4], nbbadr);
+
+	target = bank->target;
+
+	if (bank->target->state != TARGET_HALTED) {
+		LOG_ERROR("Target not halted");
+		return ERROR_TARGET_NOT_HALTED;
+	}
+
+	/* config flash controller */
+	target_write_u32(target, FLASH_BBSR, bbsr);
+	target_write_u32(target, FLASH_NBBSR, nbbsr);
+	target_write_u32(target, FLASH_BBADR, bbadr >> 2);
+	target_write_u32(target, FLASH_NBBADR, nbbadr >> 2);
+
+	/* set bit 18 instruction TCM order as per flash programming manual */
+	arm966e_write_cp15(target, 62, 0x40000);
+
+	/* enable flash bank 1 */
+	target_write_u32(target, FLASH_CR, 0x18);
+	return ERROR_OK;
+}
+
+static const struct command_registration str9x_config_command_handlers[] = {
+	{
+		.name = "flash_config",
+		.handler = str9x_handle_flash_config_command,
+		.mode = COMMAND_EXEC,
+		.help = "Configure str9x flash controller, prior to "
+			"programming the flash.",
+		.usage = "bank_id BBSR NBBSR BBADR NBBADR",
+	},
+	COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration str9x_command_handlers[] = {
+	{
+		.name = "str9x",
+		.mode = COMMAND_ANY,
+		.help = "str9x flash command group",
+		.usage = "",
+		.chain = str9x_config_command_handlers,
+	},
+	COMMAND_REGISTRATION_DONE
+};
+
+struct flash_driver str9x_flash = {
+	.name = "str9x",
+	.commands = str9x_command_handlers,
+	.flash_bank_command = str9x_flash_bank_command,
+	.erase = str9x_erase,
+	.protect = str9x_protect,
+	.write = str9x_write,
+	.read = default_flash_read,
+	.probe = str9x_probe,
+	.auto_probe = str9x_probe,
+	.erase_check = default_flash_blank_check,
+	.protect_check = str9x_protect_check,
+};

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-site/blob/e302582d/docs/os/tutorials/downloads/openocd-code-89bf96ffe6ac66c80407af8383b9d5adc0dc35f4/src/flash/nor/str9xpec.c
----------------------------------------------------------------------
diff --git a/docs/os/tutorials/downloads/openocd-code-89bf96ffe6ac66c80407af8383b9d5adc0dc35f4/src/flash/nor/str9xpec.c b/docs/os/tutorials/downloads/openocd-code-89bf96ffe6ac66c80407af8383b9d5adc0dc35f4/src/flash/nor/str9xpec.c
new file mode 100755
index 0000000..f0af53a
--- /dev/null
+++ b/docs/os/tutorials/downloads/openocd-code-89bf96ffe6ac66c80407af8383b9d5adc0dc35f4/src/flash/nor/str9xpec.c
@@ -0,0 +1,1212 @@
+/***************************************************************************
+ *   Copyright (C) 2005 by Dominic Rath                                    *
+ *   Dominic.Rath@gmx.de                                                   *
+ *                                                                         *
+ *   Copyright (C) 2008 by Spencer Oliver                                  *
+ *   spen@spen-soft.co.uk                                                  *
+ *                                                                         *
+ *   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.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.           *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "imp.h"
+#include <target/arm7_9_common.h>
+
+/* ISC commands */
+
+#define ISC_IDCODE				0xFE
+#define ISC_MFG_READ			0x4C
+#define ISC_CONFIGURATION		0x07
+#define ISC_ENABLE				0x0C
+#define ISC_DISABLE				0x0F
+#define ISC_NOOP				0x10
+#define ISC_ADDRESS_SHIFT		0x11
+#define ISC_CLR_STATUS			0x13
+#define ISC_PROGRAM				0x20
+#define ISC_PROGRAM_SECURITY	0x22
+#define ISC_PROGRAM_UC			0x23
+#define ISC_ERASE				0x30
+#define ISC_READ				0x50
+#define ISC_BLANK_CHECK			0x60
+
+/* ISC_DEFAULT bit definitions */
+
+#define ISC_STATUS_SECURITY		0x40
+#define ISC_STATUS_INT_ERROR	0x30
+#define ISC_STATUS_MODE			0x08
+#define ISC_STATUS_BUSY			0x04
+#define ISC_STATUS_ERROR		0x03
+
+/* Option bytes definitions */
+
+#define STR9XPEC_OPT_CSMAPBIT		48
+#define STR9XPEC_OPT_LVDTHRESBIT	49
+#define STR9XPEC_OPT_LVDSELBIT		50
+#define STR9XPEC_OPT_LVDWARNBIT		51
+#define STR9XPEC_OPT_OTPBIT			63
+
+enum str9xpec_status_codes {
+	STR9XPEC_INVALID_COMMAND = 1,
+	STR9XPEC_ISC_SUCCESS = 2,
+	STR9XPEC_ISC_DISABLED = 3,
+	STR9XPEC_ISC_INTFAIL = 32,
+};
+
+struct str9xpec_flash_controller {
+	struct jtag_tap *tap;
+	uint32_t *sector_bits;
+	int chain_pos;
+	int isc_enable;
+	uint8_t options[8];
+};
+
+static int str9xpec_erase_area(struct flash_bank *bank, int first, int last);
+static int str9xpec_set_address(struct flash_bank *bank, uint8_t sector);
+static int str9xpec_write_options(struct flash_bank *bank);
+
+static int str9xpec_set_instr(struct jtag_tap *tap, uint32_t new_instr, tap_state_t end_state)
+{
+	if (tap == NULL)
+		return ERROR_TARGET_INVALID;
+
+	if (buf_get_u32(tap->cur_instr, 0, tap->ir_length) != new_instr) {
+		struct scan_field field;
+
+		field.num_bits = tap->ir_length;
+		void *t = calloc(DIV_ROUND_UP(field.num_bits, 8), 1);
+		field.out_value = t;
+		buf_set_u32(t, 0, field.num_bits, new_instr);
+		field.in_value = NULL;
+
+		jtag_add_ir_scan(tap, &field, end_state);
+
+		free(t);
+	}
+
+	return ERROR_OK;
+}
+
+static uint8_t str9xpec_isc_status(struct jtag_tap *tap)
+{
+	struct scan_field field;
+	uint8_t status;
+
+	if (str9xpec_set_instr(tap, ISC_NOOP, TAP_IRPAUSE) != ERROR_OK)
+		return ISC_STATUS_ERROR;
+
+	field.num_bits = 8;
+	field.out_value = NULL;
+	field.in_value = &status;
+
+
+	jtag_add_dr_scan(tap, 1, &field, TAP_IDLE);
+	jtag_execute_queue();
+
+	LOG_DEBUG("status: 0x%2.2x", status);
+
+	if (status & ISC_STATUS_SECURITY)
+		LOG_INFO("Device Security Bit Set");
+
+	return status;
+}
+
+static int str9xpec_isc_enable(struct flash_bank *bank)
+{
+	uint8_t status;
+	struct jtag_tap *tap;
+	struct str9xpec_flash_controller *str9xpec_info = bank->driver_priv;
+
+	tap = str9xpec_info->tap;
+
+	if (str9xpec_info->isc_enable)
+		return ERROR_OK;
+
+	/* enter isc mode */
+	if (str9xpec_set_instr(tap, ISC_ENABLE, TAP_IDLE) != ERROR_OK)
+		return ERROR_TARGET_INVALID;
+
+	/* check ISC status */
+	status = str9xpec_isc_status(tap);
+	if (status & ISC_STATUS_MODE) {
+		/* we have entered isc mode */
+		str9xpec_info->isc_enable = 1;
+		LOG_DEBUG("ISC_MODE Enabled");
+	}
+
+	return ERROR_OK;
+}
+
+static int str9xpec_isc_disable(struct flash_bank *bank)
+{
+	uint8_t status;
+	struct jtag_tap *tap;
+	struct str9xpec_flash_controller *str9xpec_info = bank->driver_priv;
+
+	tap = str9xpec_info->tap;
+
+	if (!str9xpec_info->isc_enable)
+		return ERROR_OK;
+
+	if (str9xpec_set_instr(tap, ISC_DISABLE, TAP_IDLE) != ERROR_OK)
+		return ERROR_TARGET_INVALID;
+
+	/* delay to handle aborts */
+	jtag_add_sleep(50);
+
+	/* check ISC status */
+	status = str9xpec_isc_status(tap);
+	if (!(status & ISC_STATUS_MODE)) {
+		/* we have left isc mode */
+		str9xpec_info->isc_enable = 0;
+		LOG_DEBUG("ISC_MODE Disabled");
+	}
+
+	return ERROR_OK;
+}
+
+static int str9xpec_read_config(struct flash_bank *bank)
+{
+	struct scan_field field;
+	uint8_t status;
+	struct jtag_tap *tap;
+
+	struct str9xpec_flash_controller *str9xpec_info = bank->driver_priv;
+
+	tap = str9xpec_info->tap;
+
+	LOG_DEBUG("ISC_CONFIGURATION");
+
+	/* execute ISC_CONFIGURATION command */
+	str9xpec_set_instr(tap, ISC_CONFIGURATION, TAP_IRPAUSE);
+
+	field.num_bits = 64;
+	field.out_value = NULL;
+	field.in_value = str9xpec_info->options;
+
+	jtag_add_dr_scan(tap, 1, &field, TAP_IDLE);
+	jtag_execute_queue();
+
+	status = str9xpec_isc_status(tap);
+
+	return status;
+}
+
+static int str9xpec_build_block_list(struct flash_bank *bank)
+{
+	struct str9xpec_flash_controller *str9xpec_info = bank->driver_priv;
+
+	int i;
+	int num_sectors;
+	int b0_sectors = 0, b1_sectors = 0;
+	uint32_t offset = 0;
+	int b1_size = 0x2000;
+
+	switch (bank->size) {
+		case (256 * 1024):
+			b0_sectors = 4;
+			break;
+		case (512 * 1024):
+			b0_sectors = 8;
+			break;
+		case (1024 * 1024):
+			b0_sectors = 16;
+			break;
+		case (2048 * 1024):
+			b0_sectors = 32;
+			break;
+		case (128 * 1024):
+			b1_size = 0x4000;
+			b1_sectors = 8;
+			break;
+		case (32 * 1024):
+			b1_sectors = 4;
+			break;
+		default:
+			LOG_ERROR("BUG: unknown bank->size encountered");
+			exit(-1);
+	}
+
+	num_sectors = b0_sectors + b1_sectors;
+
+	bank->num_sectors = num_sectors;
+	bank->sectors = malloc(sizeof(struct flash_sector) * num_sectors);
+	str9xpec_info->sector_bits = malloc(sizeof(uint32_t) * num_sectors);
+
+	num_sectors = 0;
+
+	for (i = 0; i < b0_sectors; i++) {
+		bank->sectors[num_sectors].offset = offset;
+		bank->sectors[num_sectors].size = 0x10000;
+		offset += bank->sectors[i].size;
+		bank->sectors[num_sectors].is_erased = -1;
+		bank->sectors[num_sectors].is_protected = 1;
+		str9xpec_info->sector_bits[num_sectors++] = i;
+	}
+
+	for (i = 0; i < b1_sectors; i++) {
+		bank->sectors[num_sectors].offset = offset;
+		bank->sectors[num_sectors].size = b1_size;
+		offset += bank->sectors[i].size;
+		bank->sectors[num_sectors].is_erased = -1;
+		bank->sectors[num_sectors].is_protected = 1;
+		str9xpec_info->sector_bits[num_sectors++] = i + 32;
+	}
+
+	return ERROR_OK;
+}
+
+/* flash bank str9x <base> <size> 0 0 <target#>
+ */
+FLASH_BANK_COMMAND_HANDLER(str9xpec_flash_bank_command)
+{
+	struct str9xpec_flash_controller *str9xpec_info;
+	struct arm *arm = NULL;
+	struct arm7_9_common *arm7_9 = NULL;
+	struct arm_jtag *jtag_info = NULL;
+
+	if (CMD_ARGC < 6)
+		return ERROR_COMMAND_SYNTAX_ERROR;
+
+	str9xpec_info = malloc(sizeof(struct str9xpec_flash_controller));
+	bank->driver_priv = str9xpec_info;
+
+	/* REVISIT verify that the jtag position of flash controller is
+	 * right after *THIS* core, which must be a STR9xx core ...
+	 */
+	arm = bank->target->arch_info;
+	arm7_9 = arm->arch_info;
+	jtag_info = &arm7_9->jtag_info;
+
+	/* The core is the next tap after the flash controller in the chain */
+	str9xpec_info->tap = jtag_tap_by_position(jtag_info->tap->abs_chain_position - 1);
+	str9xpec_info->isc_enable = 0;
+
+	str9xpec_build_block_list(bank);
+
+	/* clear option byte register */
+	buf_set_u32(str9xpec_info->options, 0, 64, 0);
+
+	return ERROR_OK;
+}
+
+static int str9xpec_blank_check(struct flash_bank *bank, int first, int last)
+{
+	struct scan_field field;
+	uint8_t status;
+	struct jtag_tap *tap;
+	int i;
+	uint8_t *buffer = NULL;
+
+	struct str9xpec_flash_controller *str9xpec_info = bank->driver_priv;
+
+	tap = str9xpec_info->tap;
+
+	if (!str9xpec_info->isc_enable)
+		str9xpec_isc_enable(bank);
+
+	if (!str9xpec_info->isc_enable)
+		return ERROR_FLASH_OPERATION_FAILED;
+
+	buffer = calloc(DIV_ROUND_UP(64, 8), 1);
+
+	LOG_DEBUG("blank check: first_bank: %i, last_bank: %i", first, last);
+
+	for (i = first; i <= last; i++)
+		buf_set_u32(buffer, str9xpec_info->sector_bits[i], 1, 1);
+
+	/* execute ISC_BLANK_CHECK command */
+	str9xpec_set_instr(tap, ISC_BLANK_CHECK, TAP_IRPAUSE);
+
+	field.num_bits = 64;
+	field.out_value = buffer;
+	field.in_value = NULL;
+
+	jtag_add_dr_scan(tap, 1, &field, TAP_IDLE);
+	jtag_add_sleep(40000);
+
+	/* read blank check result */
+	field.num_bits = 64;
+	field.out_value = NULL;
+	field.in_value = buffer;
+
+	jtag_add_dr_scan(tap, 1, &field, TAP_IRPAUSE);
+	jtag_execute_queue();
+
+	status = str9xpec_isc_status(tap);
+
+	for (i = first; i <= last; i++) {
+		if (buf_get_u32(buffer, str9xpec_info->sector_bits[i], 1))
+			bank->sectors[i].is_erased = 0;
+		else
+			bank->sectors[i].is_erased = 1;
+	}
+
+	free(buffer);
+
+	str9xpec_isc_disable(bank);
+
+	if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS)
+		return ERROR_FLASH_OPERATION_FAILED;
+	return ERROR_OK;
+}
+
+static int str9xpec_protect_check(struct flash_bank *bank)
+{
+	uint8_t status;
+	int i;
+
+	struct str9xpec_flash_controller *str9xpec_info = bank->driver_priv;
+
+	status = str9xpec_read_config(bank);
+
+	for (i = 0; i < bank->num_sectors; i++) {
+		if (buf_get_u32(str9xpec_info->options, str9xpec_info->sector_bits[i], 1))
+			bank->sectors[i].is_protected = 1;
+		else
+			bank->sectors[i].is_protected = 0;
+	}
+
+	if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS)
+		return ERROR_FLASH_OPERATION_FAILED;
+	return ERROR_OK;
+}
+
+static int str9xpec_erase_area(struct flash_bank *bank, int first, int last)
+{
+	struct scan_field field;
+	uint8_t status;
+	struct jtag_tap *tap;
+	int i;
+	uint8_t *buffer = NULL;
+
+	struct str9xpec_flash_controller *str9xpec_info = bank->driver_priv;
+
+	tap = str9xpec_info->tap;
+
+	if (!str9xpec_info->isc_enable)
+		str9xpec_isc_enable(bank);
+
+	if (!str9xpec_info->isc_enable)
+		return ISC_STATUS_ERROR;
+
+	buffer = calloc(DIV_ROUND_UP(64, 8), 1);
+
+	LOG_DEBUG("erase: first_bank: %i, last_bank: %i", first, last);
+
+	/* last bank: 0xFF signals a full erase (unlock complete device) */
+	/* last bank: 0xFE signals a option byte erase */
+	if (last == 0xFF) {
+		for (i = 0; i < 64; i++)
+			buf_set_u32(buffer, i, 1, 1);
+	} else if (last == 0xFE)
+		buf_set_u32(buffer, 49, 1, 1);
+	else {
+		for (i = first; i <= last; i++)
+			buf_set_u32(buffer, str9xpec_info->sector_bits[i], 1, 1);
+	}
+
+	LOG_DEBUG("ISC_ERASE");
+
+	/* execute ISC_ERASE command */
+	str9xpec_set_instr(tap, ISC_ERASE, TAP_IRPAUSE);
+
+	field.num_bits = 64;
+	field.out_value = buffer;
+	field.in_value = NULL;
+
+	jtag_add_dr_scan(tap, 1, &field, TAP_IDLE);
+	jtag_execute_queue();
+
+	jtag_add_sleep(10);
+
+	/* wait for erase completion */
+	while (!((status = str9xpec_isc_status(tap)) & ISC_STATUS_BUSY))
+		alive_sleep(1);
+
+	free(buffer);
+
+	str9xpec_isc_disable(bank);
+
+	return status;
+}
+
+static int str9xpec_erase(struct flash_bank *bank, int first, int last)
+{
+	int status;
+
+	status = str9xpec_erase_area(bank, first, last);
+
+	if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS)
+		return ERROR_FLASH_OPERATION_FAILED;
+
+	return ERROR_OK;
+}
+
+static int str9xpec_lock_device(struct flash_bank *bank)
+{
+	struct scan_field field;
+	uint8_t status;
+	struct jtag_tap *tap;
+	struct str9xpec_flash_controller *str9xpec_info = NULL;
+
+	str9xpec_info = bank->driver_priv;
+	tap = str9xpec_info->tap;
+
+	if (!str9xpec_info->isc_enable)
+		str9xpec_isc_enable(bank);
+
+	if (!str9xpec_info->isc_enable)
+		return ISC_STATUS_ERROR;
+
+	/* set security address */
+	str9xpec_set_address(bank, 0x80);
+
+	/* execute ISC_PROGRAM command */
+	str9xpec_set_instr(tap, ISC_PROGRAM_SECURITY, TAP_IDLE);
+
+	str9xpec_set_instr(tap, ISC_NOOP, TAP_IRPAUSE);
+
+	do {
+		field.num_bits = 8;
+		field.out_value = NULL;
+		field.in_value = &status;
+
+		jtag_add_dr_scan(tap, 1, &field, TAP_IDLE);
+		jtag_execute_queue();
+
+	} while (!(status & ISC_STATUS_BUSY));
+
+	str9xpec_isc_disable(bank);
+
+	return status;
+}
+
+static int str9xpec_unlock_device(struct flash_bank *bank)
+{
+	uint8_t status;
+
+	status = str9xpec_erase_area(bank, 0, 255);
+
+	return status;
+}
+
+static int str9xpec_protect(struct flash_bank *bank, int set, int first, int last)
+{
+	uint8_t status;
+	int i;
+
+	struct str9xpec_flash_controller *str9xpec_info = bank->driver_priv;
+
+	status = str9xpec_read_config(bank);
+
+	if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS)
+		return ERROR_FLASH_OPERATION_FAILED;
+
+	LOG_DEBUG("protect: first_bank: %i, last_bank: %i", first, last);
+
+	/* last bank: 0xFF signals a full device protect */
+	if (last == 0xFF) {
+		if (set)
+			status = str9xpec_lock_device(bank);
+		else {
+			/* perform full erase to unlock device */
+			status = str9xpec_unlock_device(bank);
+		}
+	} else {
+		for (i = first; i <= last; i++) {
+			if (set)
+				buf_set_u32(str9xpec_info->options, str9xpec_info->sector_bits[i], 1, 1);
+			else
+				buf_set_u32(str9xpec_info->options, str9xpec_info->sector_bits[i], 1, 0);
+		}
+
+		status = str9xpec_write_options(bank);
+	}
+
+	if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS)
+		return ERROR_FLASH_OPERATION_FAILED;
+
+	return ERROR_OK;
+}
+
+static int str9xpec_set_address(struct flash_bank *bank, uint8_t sector)
+{
+	struct jtag_tap *tap;
+	struct scan_field field;
+	struct str9xpec_flash_controller *str9xpec_info = bank->driver_priv;
+
+	tap = str9xpec_info->tap;
+
+	/* set flash controller address */
+	str9xpec_set_instr(tap, ISC_ADDRESS_SHIFT, TAP_IRPAUSE);
+
+	field.num_bits = 8;
+	field.out_value = &sector;
+	field.in_value = NULL;
+
+	jtag_add_dr_scan(tap, 1, &field, TAP_IRPAUSE);
+
+	return ERROR_OK;
+}
+
+static int str9xpec_write(struct flash_bank *bank, const uint8_t *buffer,
+		uint32_t offset, uint32_t count)
+{
+	struct str9xpec_flash_controller *str9xpec_info = bank->driver_priv;
+	uint32_t dwords_remaining = (count / 8);
+	uint32_t bytes_remaining = (count & 0x00000007);
+	uint32_t bytes_written = 0;
+	uint8_t status;
+	uint32_t check_address = offset;
+	struct jtag_tap *tap;
+	struct scan_field field;
+	uint8_t *scanbuf;
+	int i;
+	int first_sector = 0;
+	int last_sector = 0;
+
+	tap = str9xpec_info->tap;
+
+	if (!str9xpec_info->isc_enable)
+		str9xpec_isc_enable(bank);
+
+	if (!str9xpec_info->isc_enable)
+		return ERROR_FLASH_OPERATION_FAILED;
+
+	if (offset & 0x7) {
+		LOG_WARNING("offset 0x%" PRIx32 " breaks required 8-byte alignment", offset);
+		return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
+	}
+
+	for (i = 0; i < bank->num_sectors; i++) {
+		uint32_t sec_start = bank->sectors[i].offset;
+		uint32_t sec_end = sec_start + bank->sectors[i].size;
+
+		/* check if destination falls within the current sector */
+		if ((check_address >= sec_start) && (check_address < sec_end)) {
+			/* check if destination ends in the current sector */
+			if (offset + count < sec_end)
+				check_address = offset + count;
+			else
+				check_address = sec_end;
+		}
+
+		if ((offset >= sec_start) && (offset < sec_end))
+			first_sector = i;
+
+		if ((offset + count >= sec_start) && (offset + count < sec_end))
+			last_sector = i;
+	}
+
+	if (check_address != offset + count)
+		return ERROR_FLASH_DST_OUT_OF_BANK;
+
+	LOG_DEBUG("first_sector: %i, last_sector: %i", first_sector, last_sector);
+
+	scanbuf = calloc(DIV_ROUND_UP(64, 8), 1);
+
+	LOG_DEBUG("ISC_PROGRAM");
+
+	for (i = first_sector; i <= last_sector; i++) {
+		str9xpec_set_address(bank, str9xpec_info->sector_bits[i]);
+
+		dwords_remaining = dwords_remaining < (bank->sectors[i].size/8)
+				? dwords_remaining : (bank->sectors[i].size/8);
+
+		while (dwords_remaining > 0) {
+			str9xpec_set_instr(tap, ISC_PROGRAM, TAP_IRPAUSE);
+
+			field.num_bits = 64;
+			field.out_value = (buffer + bytes_written);
+			field.in_value = NULL;
+
+			jtag_add_dr_scan(tap, 1, &field, TAP_IDLE);
+
+			/* small delay before polling */
+			jtag_add_sleep(50);
+
+			str9xpec_set_instr(tap, ISC_NOOP, TAP_IRPAUSE);
+
+			do {
+				field.num_bits = 8;
+				field.out_value = NULL;
+				field.in_value = scanbuf;
+
+				jtag_add_dr_scan(tap, 1, &field, TAP_IRPAUSE);
+				jtag_execute_queue();
+
+				status = buf_get_u32(scanbuf, 0, 8);
+
+			} while (!(status & ISC_STATUS_BUSY));
+
+			if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS)
+				return ERROR_FLASH_OPERATION_FAILED;
+
+			/* if ((status & ISC_STATUS_INT_ERROR) != STR9XPEC_ISC_INTFAIL)
+				return ERROR_FLASH_OPERATION_FAILED; */
+
+			dwords_remaining--;
+			bytes_written += 8;
+		}
+	}
+
+	if (bytes_remaining) {
+		uint8_t last_dword[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+		/* copy the last remaining bytes into the write buffer */
+		memcpy(last_dword, buffer+bytes_written, bytes_remaining);
+
+		str9xpec_set_instr(tap, ISC_PROGRAM, TAP_IRPAUSE);
+
+		field.num_bits = 64;
+		field.out_value = last_dword;
+		field.in_value = NULL;
+
+		jtag_add_dr_scan(tap, 1, &field, TAP_IDLE);
+
+		/* small delay before polling */
+		jtag_add_sleep(50);
+
+		str9xpec_set_instr(tap, ISC_NOOP, TAP_IRPAUSE);
+
+		do {
+			field.num_bits = 8;
+			field.out_value = NULL;
+			field.in_value = scanbuf;
+
+			jtag_add_dr_scan(tap, 1, &field, TAP_IRPAUSE);
+			jtag_execute_queue();
+
+			status = buf_get_u32(scanbuf, 0, 8);
+
+		} while (!(status & ISC_STATUS_BUSY));
+
+		if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS)
+			return ERROR_FLASH_OPERATION_FAILED;
+
+		/* if ((status & ISC_STATUS_INT_ERROR) != STR9XPEC_ISC_INTFAIL)
+			return ERROR_FLASH_OPERATION_FAILED; */
+	}
+
+	free(scanbuf);
+
+	str9xpec_isc_disable(bank);
+
+	return ERROR_OK;
+}
+
+static int str9xpec_probe(struct flash_bank *bank)
+{
+	return ERROR_OK;
+}
+
+COMMAND_HANDLER(str9xpec_handle_part_id_command)
+{
+	struct scan_field field;
+	uint8_t *buffer = NULL;
+	struct jtag_tap *tap;
+	uint32_t idcode;
+	struct str9xpec_flash_controller *str9xpec_info = NULL;
+
+	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 (ERROR_OK != retval)
+		return retval;
+
+	str9xpec_info = bank->driver_priv;
+	tap = str9xpec_info->tap;
+
+	buffer = calloc(DIV_ROUND_UP(32, 8), 1);
+
+	str9xpec_set_instr(tap, ISC_IDCODE, TAP_IRPAUSE);
+
+	field.num_bits = 32;
+	field.out_value = NULL;
+	field.in_value = buffer;
+
+	jtag_add_dr_scan(tap, 1, &field, TAP_IDLE);
+	jtag_execute_queue();
+
+	idcode = buf_get_u32(buffer, 0, 32);
+
+	command_print(CMD_CTX, "str9xpec part id: 0x%8.8" PRIx32 "", idcode);
+
+	free(buffer);
+
+	return ERROR_OK;
+}
+
+static int str9xpec_erase_check(struct flash_bank *bank)
+{
+	return str9xpec_blank_check(bank, 0, bank->num_sectors - 1);
+}
+
+COMMAND_HANDLER(str9xpec_handle_flash_options_read_command)
+{
+	uint8_t status;
+	struct str9xpec_flash_controller *str9xpec_info = NULL;
+
+	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 (ERROR_OK != retval)
+		return retval;
+
+	str9xpec_info = bank->driver_priv;
+
+	status = str9xpec_read_config(bank);
+
+	if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS)
+		return ERROR_FLASH_OPERATION_FAILED;
+
+	/* boot bank */
+	if (buf_get_u32(str9xpec_info->options, STR9XPEC_OPT_CSMAPBIT, 1))
+		command_print(CMD_CTX, "CS Map: bank1");
+	else
+		command_print(CMD_CTX, "CS Map: bank0");
+
+	/* OTP lock */
+	if (buf_get_u32(str9xpec_info->options, STR9XPEC_OPT_OTPBIT, 1))
+		command_print(CMD_CTX, "OTP Lock: OTP Locked");
+	else
+		command_print(CMD_CTX, "OTP Lock: OTP Unlocked");
+
+	/* LVD Threshold */
+	if (buf_get_u32(str9xpec_info->options, STR9XPEC_OPT_LVDTHRESBIT, 1))
+		command_print(CMD_CTX, "LVD Threshold: 2.7v");
+	else
+		command_print(CMD_CTX, "LVD Threshold: 2.4v");
+
+	/* LVD reset warning */
+	if (buf_get_u32(str9xpec_info->options, STR9XPEC_OPT_LVDWARNBIT, 1))
+		command_print(CMD_CTX, "LVD Reset Warning: VDD or VDDQ Inputs");
+	else
+		command_print(CMD_CTX, "LVD Reset Warning: VDD Input Only");
+
+	/* LVD reset select */
+	if (buf_get_u32(str9xpec_info->options, STR9XPEC_OPT_LVDSELBIT, 1))
+		command_print(CMD_CTX, "LVD Reset Selection: VDD or VDDQ Inputs");
+	else
+		command_print(CMD_CTX, "LVD Reset Selection: VDD Input Only");
+
+	return ERROR_OK;
+}
+
+static int str9xpec_write_options(struct flash_bank *bank)
+{
+	struct scan_field field;
+	uint8_t status;
+	struct jtag_tap *tap;
+	struct str9xpec_flash_controller *str9xpec_info = NULL;
+
+	str9xpec_info = bank->driver_priv;
+	tap = str9xpec_info->tap;
+
+	/* erase config options first */
+	status = str9xpec_erase_area(bank, 0xFE, 0xFE);
+
+	if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS)
+		return status;
+
+	if (!str9xpec_info->isc_enable)
+		str9xpec_isc_enable(bank);
+
+	if (!str9xpec_info->isc_enable)
+		return ISC_STATUS_ERROR;
+
+	/* according to data 64th bit has to be set */
+	buf_set_u32(str9xpec_info->options, 63, 1, 1);
+
+	/* set option byte address */
+	str9xpec_set_address(bank, 0x50);
+
+	/* execute ISC_PROGRAM command */
+	str9xpec_set_instr(tap, ISC_PROGRAM, TAP_IRPAUSE);
+
+	field.num_bits = 64;
+	field.out_value = str9xpec_info->options;
+	field.in_value = NULL;
+
+	jtag_add_dr_scan(tap, 1, &field, TAP_IDLE);
+
+	/* small delay before polling */
+	jtag_add_sleep(50);
+
+	str9xpec_set_instr(tap, ISC_NOOP, TAP_IRPAUSE);
+
+	do {
+		field.num_bits = 8;
+		field.out_value = NULL;
+		field.in_value = &status;
+
+		jtag_add_dr_scan(tap, 1, &field, TAP_IRPAUSE);
+		jtag_execute_queue();
+
+	} while (!(status & ISC_STATUS_BUSY));
+
+	str9xpec_isc_disable(bank);
+
+	return status;
+}
+
+COMMAND_HANDLER(str9xpec_handle_flash_options_write_command)
+{
+	uint8_t status;
+
+	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 (ERROR_OK != retval)
+		return retval;
+
+	status = str9xpec_write_options(bank);
+
+	if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS)
+		return ERROR_FLASH_OPERATION_FAILED;
+
+	command_print(CMD_CTX, "str9xpec write options complete.\n"
+			"INFO: a reset or power cycle is required "
+			"for the new settings to take effect.");
+
+	return ERROR_OK;
+}
+
+COMMAND_HANDLER(str9xpec_handle_flash_options_cmap_command)
+{
+	struct str9xpec_flash_controller *str9xpec_info = NULL;
+
+	if (CMD_ARGC < 2)
+		return ERROR_COMMAND_SYNTAX_ERROR;
+
+	struct flash_bank *bank;
+	int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
+	if (ERROR_OK != retval)
+		return retval;
+
+	str9xpec_info = bank->driver_priv;
+
+	if (strcmp(CMD_ARGV[1], "bank1") == 0)
+		buf_set_u32(str9xpec_info->options, STR9XPEC_OPT_CSMAPBIT, 1, 1);
+	else
+		buf_set_u32(str9xpec_info->options, STR9XPEC_OPT_CSMAPBIT, 1, 0);
+
+	return ERROR_OK;
+}
+
+COMMAND_HANDLER(str9xpec_handle_flash_options_lvdthd_command)
+{
+	struct str9xpec_flash_controller *str9xpec_info = NULL;
+
+	if (CMD_ARGC < 2)
+		return ERROR_COMMAND_SYNTAX_ERROR;
+
+	struct flash_bank *bank;
+	int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
+	if (ERROR_OK != retval)
+		return retval;
+
+	str9xpec_info = bank->driver_priv;
+
+	if (strcmp(CMD_ARGV[1], "2.7v") == 0)
+		buf_set_u32(str9xpec_info->options, STR9XPEC_OPT_LVDTHRESBIT, 1, 1);
+	else
+		buf_set_u32(str9xpec_info->options, STR9XPEC_OPT_LVDTHRESBIT, 1, 0);
+
+	return ERROR_OK;
+}
+
+COMMAND_HANDLER(str9xpec_handle_flash_options_lvdsel_command)
+{
+	struct str9xpec_flash_controller *str9xpec_info = NULL;
+
+	if (CMD_ARGC < 2)
+		return ERROR_COMMAND_SYNTAX_ERROR;
+
+	struct flash_bank *bank;
+	int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
+	if (ERROR_OK != retval)
+		return retval;
+
+	str9xpec_info = bank->driver_priv;
+
+	if (strcmp(CMD_ARGV[1], "vdd_vddq") == 0)
+		buf_set_u32(str9xpec_info->options, STR9XPEC_OPT_LVDSELBIT, 1, 1);
+	else
+		buf_set_u32(str9xpec_info->options, STR9XPEC_OPT_LVDSELBIT, 1, 0);
+
+	return ERROR_OK;
+}
+
+COMMAND_HANDLER(str9xpec_handle_flash_options_lvdwarn_command)
+{
+	struct str9xpec_flash_controller *str9xpec_info = NULL;
+
+	if (CMD_ARGC < 2)
+		return ERROR_COMMAND_SYNTAX_ERROR;
+
+	struct flash_bank *bank;
+	int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
+	if (ERROR_OK != retval)
+		return retval;
+
+	str9xpec_info = bank->driver_priv;
+
+	if (strcmp(CMD_ARGV[1], "vdd_vddq") == 0)
+		buf_set_u32(str9xpec_info->options, STR9XPEC_OPT_LVDWARNBIT, 1, 1);
+	else
+		buf_set_u32(str9xpec_info->options, STR9XPEC_OPT_LVDWARNBIT, 1, 0);
+
+	return ERROR_OK;
+}
+
+COMMAND_HANDLER(str9xpec_handle_flash_lock_command)
+{
+	uint8_t status;
+
+	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 (ERROR_OK != retval)
+		return retval;
+
+	status = str9xpec_lock_device(bank);
+
+	if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS)
+		return ERROR_FLASH_OPERATION_FAILED;
+
+	return ERROR_OK;
+}
+
+COMMAND_HANDLER(str9xpec_handle_flash_unlock_command)
+{
+	uint8_t status;
+
+	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 (ERROR_OK != retval)
+		return retval;
+
+	status = str9xpec_unlock_device(bank);
+
+	if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS)
+		return ERROR_FLASH_OPERATION_FAILED;
+
+	command_print(CMD_CTX, "str9xpec unlocked.\n"
+			"INFO: a reset or power cycle is required "
+			"for the new settings to take effect.");
+
+	return ERROR_OK;
+}
+
+COMMAND_HANDLER(str9xpec_handle_flash_enable_turbo_command)
+{
+	struct jtag_tap *tap0;
+	struct jtag_tap *tap1;
+	struct jtag_tap *tap2;
+	struct str9xpec_flash_controller *str9xpec_info = NULL;
+
+	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 (ERROR_OK != retval)
+		return retval;
+
+	str9xpec_info = bank->driver_priv;
+
+	/* remove arm core from chain - enter turbo mode */
+	tap0 = str9xpec_info->tap;
+	if (tap0 == NULL) {
+		/* things are *WRONG* */
+		command_print(CMD_CTX, "**STR9FLASH** (tap0) invalid chain?");
+		return ERROR_FAIL;
+	}
+	tap1 = tap0->next_tap;
+	if (tap1 == NULL) {
+		/* things are *WRONG* */
+		command_print(CMD_CTX, "**STR9FLASH** (tap1) invalid chain?");
+		return ERROR_FAIL;
+	}
+	tap2 = tap1->next_tap;
+	if (tap2 == NULL) {
+		/* things are *WRONG* */
+		command_print(CMD_CTX, "**STR9FLASH** (tap2) invalid chain?");
+		return ERROR_FAIL;
+	}
+
+	/* enable turbo mode - TURBO-PROG-ENABLE */
+	str9xpec_set_instr(tap2, 0xD, TAP_IDLE);
+	retval = jtag_execute_queue();
+	if (retval != ERROR_OK)
+		return retval;
+
+	/* modify scan chain - str9 core has been removed */
+	tap1->enabled = 0;
+
+	return ERROR_OK;
+}
+
+COMMAND_HANDLER(str9xpec_handle_flash_disable_turbo_command)
+{
+	struct jtag_tap *tap;
+	struct str9xpec_flash_controller *str9xpec_info = NULL;
+
+	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 (ERROR_OK != retval)
+		return retval;
+
+	str9xpec_info = bank->driver_priv;
+	tap = str9xpec_info->tap;
+
+	if (tap == NULL)
+		return ERROR_FAIL;
+
+	/* exit turbo mode via RESET */
+	str9xpec_set_instr(tap, ISC_NOOP, TAP_IDLE);
+	jtag_add_tlr();
+	jtag_execute_queue();
+
+	/* restore previous scan chain */
+	if (tap->next_tap)
+		tap->next_tap->enabled = 1;
+
+	return ERROR_OK;
+}
+
+static const struct command_registration str9xpec_config_command_handlers[] = {
+	{
+		.name = "enable_turbo",
+		.usage = "<bank>",
+		.handler = str9xpec_handle_flash_enable_turbo_command,
+		.mode = COMMAND_EXEC,
+		.help = "enable str9xpec turbo mode",
+	},
+	{
+		.name = "disable_turbo",
+		.usage = "<bank>",
+		.handler = str9xpec_handle_flash_disable_turbo_command,
+		.mode = COMMAND_EXEC,
+		.help = "disable str9xpec turbo mode",
+	},
+	{
+		.name = "options_cmap",
+		.usage = "<bank> <bank0 | bank1>",
+		.handler = str9xpec_handle_flash_options_cmap_command,
+		.mode = COMMAND_EXEC,
+		.help = "configure str9xpec boot sector",
+	},
+	{
+		.name = "options_lvdthd",
+		.usage = "<bank> <2.4v | 2.7v>",
+		.handler = str9xpec_handle_flash_options_lvdthd_command,
+		.mode = COMMAND_EXEC,
+		.help = "configure str9xpec lvd threshold",
+	},
+	{
+		.name = "options_lvdsel",
+		.usage = "<bank> <vdd | vdd_vddq>",
+		.handler = str9xpec_handle_flash_options_lvdsel_command,
+		.mode = COMMAND_EXEC,
+		.help = "configure str9xpec lvd selection",
+	},
+	{
+		.name = "options_lvdwarn",
+		.usage = "<bank> <vdd | vdd_vddq>",
+		.handler = str9xpec_handle_flash_options_lvdwarn_command,
+		.mode = COMMAND_EXEC,
+		.help = "configure str9xpec lvd warning",
+	},
+	{
+		.name = "options_read",
+		.usage = "<bank>",
+		.handler = str9xpec_handle_flash_options_read_command,
+		.mode = COMMAND_EXEC,
+		.help = "read str9xpec options",
+	},
+	{
+		.name = "options_write",
+		.usage = "<bank>",
+		.handler = str9xpec_handle_flash_options_write_command,
+		.mode = COMMAND_EXEC,
+		.help = "write str9xpec options",
+	},
+	{
+		.name = "lock",
+		.usage = "<bank>",
+		.handler = str9xpec_handle_flash_lock_command,
+		.mode = COMMAND_EXEC,
+		.help = "lock str9xpec device",
+	},
+	{
+		.name = "unlock",
+		.usage = "<bank>",
+		.handler = str9xpec_handle_flash_unlock_command,
+		.mode = COMMAND_EXEC,
+		.help = "unlock str9xpec device",
+	},
+	{
+		.name = "part_id",
+		.handler = str9xpec_handle_part_id_command,
+		.mode = COMMAND_EXEC,
+		.help = "print part id of str9xpec flash bank <num>",
+	},
+	COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration str9xpec_command_handlers[] = {
+	{
+		.name = "str9xpec",
+		.mode = COMMAND_ANY,
+		.help = "str9xpec flash command group",
+		.usage = "",
+		.chain = str9xpec_config_command_handlers,
+	},
+	COMMAND_REGISTRATION_DONE
+};
+
+struct flash_driver str9xpec_flash = {
+	.name = "str9xpec",
+	.commands = str9xpec_command_handlers,
+	.flash_bank_command = str9xpec_flash_bank_command,
+	.erase = str9xpec_erase,
+	.protect = str9xpec_protect,
+	.write = str9xpec_write,
+	.read = default_flash_read,
+	.probe = str9xpec_probe,
+	.auto_probe = str9xpec_probe,
+	.erase_check = str9xpec_erase_check,
+	.protect_check = str9xpec_protect_check,
+};