From 041611f785d7351dc38e1d5df2a2ccf38eef6c59 Mon Sep 17 00:00:00 2001
From: Laurent Charpentier <laurent_pubs@yahoo.com>
Date: Tue, 24 Jul 2012 10:40:03 +0200
Subject: [PATCH] Added OTP (One-Time Programmable) memory support for
 STM32F2x. OTP is part of the Flash module organization. It
 has 512 bytes (16 sectors of 32 bytes).

---
 doc/openocd.texi         |   16 +++
 src/flash/nor/stm32f2x.c |  264 +++++++++++++++++++++++++++++++++++-----------
 tcl/target/stm32f2x.cfg  |    6 +-
 3 files changed, 225 insertions(+), 61 deletions(-)

diff --git a/doc/openocd.texi b/doc/openocd.texi
index 14772e1..ec01f98 100644
--- a/doc/openocd.texi
+++ b/doc/openocd.texi
@@ -5091,6 +5091,22 @@ All members of the STM32f2x microcontroller family from ST Microelectronics
 include internal flash and use ARM Cortex M3 cores.
 The driver automatically recognizes a number of these chips using
 the chip identification register, and autoconfigures itself.
+
+@example
+flash bank $_FLASHNAME stm32f2x 0x08000000 0 0 0 $_TARGETNAME
+@end example
+
+If you use OTP (One-Time Programmable) memory define it as a second bank 
+as per the following example.
+@example
+flash bank $_FLASHNAME stm32f2x 0x1FFF7800 0 0 0 $_TARGETNAME
+@end example
+
+@deffn Command {stm32f2x otp } num (@option{enable}|@option{disable}|@option{show})
+Enables or disables OTP write commands for bank @var{num}.
+The @var{num} parameter is a value shown by @command{flash banks}.
+@end deffn
+
 @end deffn
 
 @deffn {Flash Driver} str7x
diff --git a/src/flash/nor/stm32f2x.c b/src/flash/nor/stm32f2x.c
index c7e050d..d5ef53d 100644
--- a/src/flash/nor/stm32f2x.c
+++ b/src/flash/nor/stm32f2x.c
@@ -91,6 +91,9 @@
 #define FLASH_ERASE_TIMEOUT 10000
 #define FLASH_WRITE_TIMEOUT 5
 
+#define FLASH_BANK_BASE         0x08000000
+#define OTP_BANK_BASE           0x1FFF7800
+
 #define STM32_FLASH_BASE	0x40023c00
 #define STM32_FLASH_ACR		0x40023c00
 #define STM32_FLASH_KEYR	0x40023c04
@@ -152,9 +155,41 @@
 struct stm32x_flash_bank {
 	struct working_area *write_algorithm;
 	int probed;
+	int otp_unlocked;
 };
 
 
+static int stm32x_is_otp(struct flash_bank *bank)
+{
+	return (bank->base == OTP_BANK_BASE);
+}
+
+static int stm32x_is_otp_unlocked(struct flash_bank *bank)
+{
+	struct stm32x_flash_bank *stm32x_info = bank->driver_priv;
+	return stm32x_info->otp_unlocked;
+}
+
+static int stm32x_otp_disable(struct flash_bank *bank)
+{
+	struct stm32x_flash_bank *stm32x_info = bank->driver_priv;
+	LOG_INFO("OTP memory bank #%d is disabled for write commands.", bank->bank_number);
+	stm32x_info->otp_unlocked = 0;
+	return ERROR_OK;
+}
+
+static int stm32x_otp_enable(struct flash_bank *bank)
+{
+	struct stm32x_flash_bank *stm32x_info = bank->driver_priv;
+	if (stm32x_info->otp_unlocked != 1) {
+		LOG_INFO("OTP memory bank #%d is enabled for write commands.", bank->bank_number);
+		stm32x_info->otp_unlocked = 1;
+	} else {
+		LOG_WARNING("OTP memory bank #%d is already enabled for write commands.", bank->bank_number);
+	}
+	return ERROR_OK;
+}
+
 /* flash bank stm32x <base> <size> 0 0 <target#>
  */
 FLASH_BANK_COMMAND_HANDLER(stm32x_flash_bank_command)
@@ -169,6 +204,25 @@ FLASH_BANK_COMMAND_HANDLER(stm32x_flash_bank_command)
 
 	stm32x_info->write_algorithm = NULL;
 	stm32x_info->probed = 0;
+	stm32x_info->otp_unlocked = 0;
+
+	switch (bank->base) {
+		default:
+			LOG_ERROR("Address 0x%08x invalid bank address (try 0x%08x or 0x%08x)",
+			((unsigned int)(bank->base)),
+			((unsigned int)(FLASH_BANK_BASE)),
+			((unsigned int)(OTP_BANK_BASE)));
+			return ERROR_FAIL;
+			break;
+
+		/* stm32f2 has one flash bank and one OTP bank */
+		case FLASH_BANK_BASE:
+			bank->bank_number = 0;
+			break;
+		case OTP_BANK_BASE:
+			bank->bank_number = 1;
+			break;
+	}
 
 	return ERROR_OK;
 }
@@ -267,6 +321,11 @@ static int stm32x_erase(struct flash_bank *bank, int first, int last)
 	struct target *target = bank->target;
 	int i;
 
+	if (stm32x_is_otp(bank)) {
+		LOG_ERROR("Can not erase OTP memory");
+		return ERROR_FAIL;
+	}
+
 	if (bank->target->state != TARGET_HALTED) {
 		LOG_ERROR("Target not halted");
 		return ERROR_TARGET_NOT_HALTED;
@@ -365,6 +424,11 @@ static int stm32x_write_block(struct flash_bank *bank, uint8_t *buffer,
 		0x01, 0x01, 0x00, 0x00,		/* .word	0x00000101 */
 	};
 
+	if ( stm32x_is_otp(bank)  && !stm32x_is_otp_unlocked(bank) ) {
+		LOG_ERROR("OTP memory bank is disabled for write commands.");
+		return ERROR_FAIL;
+	}
+	
 	if (target_alloc_working_area(target, sizeof(stm32x_flash_write_code),
 			&stm32x_info->write_algorithm) != ERROR_OK) {
 		LOG_WARNING("no working area available, can't do block memory writes");
@@ -586,75 +650,105 @@ static int stm32x_probe(struct flash_bank *bank)
 	struct stm32x_flash_bank *stm32x_info = bank->driver_priv;
 	int i;
 	uint16_t flash_size_in_kb;
+	uint16_t flash_size_in_b;
 	uint32_t device_id;
-	uint32_t base_address = 0x08000000;
+	int retval;
 
 	stm32x_info->probed = 0;
+	stm32x_info->otp_unlocked = 0;
 
-	/* read stm32 device id register */
-	int retval = stm32x_get_device_id(bank, &device_id);
-	if (retval != ERROR_OK)
-		return retval;
-	LOG_INFO("device id = 0x%08" PRIx32 "", device_id);
-
-	/* get flash size from target. */
-	retval = target_read_u16(target, 0x1FFF7A22, &flash_size_in_kb);
-	if (retval != ERROR_OK) {
-		LOG_WARNING("failed reading flash size, default to max target family");
-		/* failed reading flash size, default to max target family */
-		flash_size_in_kb = 0xffff;
-	}
+	if ( bank->base == FLASH_BANK_BASE ) {
+		/* read stm32 device id register */
+		retval = stm32x_get_device_id(bank, &device_id);
+		if (retval != ERROR_OK)
+			return retval;
+		LOG_INFO("device id = 0x%08" PRIx32 "", device_id);
 
-	if ((device_id & 0xfff) == 0x411) {
-		/* check for early silicon */
-		if (flash_size_in_kb == 0xffff) {
-			/* number of sectors may be incorrrect on early silicon */
-			LOG_WARNING("STM32 flash size failed, probe inaccurate - assuming 1024k flash");
-			flash_size_in_kb = 1024;
+		/* get flash size from target. */
+		retval = target_read_u16(target, 0x1FFF7A22, &flash_size_in_kb);
+		if (retval != ERROR_OK) {
+			LOG_WARNING("failed reading flash size, default to max target family");
+			/* failed reading flash size, default to max target family */
+			flash_size_in_kb = 0xffff;
 		}
-	} else if ((device_id & 0xfff) == 0x413) {
-		/* check for early silicon */
-		if (flash_size_in_kb == 0xffff) {
-			/* number of sectors may be incorrrect on early silicon */
-			LOG_WARNING("STM32 flash size failed, probe inaccurate - assuming 1024k flash");
-			flash_size_in_kb = 1024;
+
+		if ((device_id & 0xfff) == 0x411) {
+			/* check for early silicon */
+			if (flash_size_in_kb == 0xffff) {
+				/* number of sectors may be incorrrect on early silicon */
+				LOG_WARNING("STM32 flash size failed, probe inaccurate - assuming 1024k flash");
+				flash_size_in_kb = 1024;
+			}
+		} else if ((device_id & 0xfff) == 0x413) {
+			/* check for early silicon */
+			if (flash_size_in_kb == 0xffff) {
+				/* number of sectors may be incorrrect on early silicon */
+				LOG_WARNING("STM32 flash size failed, probe inaccurate - assuming 1024k flash");
+				flash_size_in_kb = 1024;
+			}
+		} else {
+			LOG_WARNING("Cannot identify target as a STM32 family.");
+			return ERROR_FAIL;
 		}
-	} else {
-		LOG_WARNING("Cannot identify target as a STM32 family.");
-		return ERROR_FAIL;
-	}
 
-	LOG_INFO("flash size = %dkbytes", flash_size_in_kb);
+		LOG_INFO("flash size = %dkbytes", flash_size_in_kb);
 
-	/* did we assign flash size? */
-	assert(flash_size_in_kb != 0xffff);
+		/* did we assign flash size? */
+		assert(flash_size_in_kb != 0xffff);
 
-	/* calculate numbers of pages */
-	int num_pages = (flash_size_in_kb / 128) + 4;
+		/* calculate numbers of pages */
+		int num_pages = (flash_size_in_kb / 128) + 4;
 
-	/* check that calculation result makes sense */
-	assert(num_pages > 0);
+		/* check that calculation result makes sense */
+		assert(num_pages > 0);
 
-	if (bank->sectors) {
-		free(bank->sectors);
-		bank->sectors = NULL;
+		if (bank->sectors) {
+			free(bank->sectors);
+			bank->sectors = NULL;
+		}
+
+		bank->num_sectors = num_pages;
+		bank->sectors = malloc(sizeof(struct flash_sector) * num_pages);
+		bank->size = 0;
+
+		/* fixed memory */
+		setup_sector(bank, 0, 4, 16 * 1024);
+		setup_sector(bank, 4, 1, 64 * 1024);
+
+		/* dynamic memory */
+		setup_sector(bank, 4 + 1, num_pages - 5, 128 * 1024);
+
+		for (i = 0; i < num_pages; i++) {
+			bank->sectors[i].is_erased = -1;
+			bank->sectors[i].is_protected = 0;
+		}
 	}
+	else if (bank->base == OTP_BANK_BASE) {
+		flash_size_in_b = 512;
+		LOG_INFO("flash size = %dbytes", flash_size_in_b);
 
-	bank->base = base_address;
-	bank->num_sectors = num_pages;
-	bank->sectors = malloc(sizeof(struct flash_sector) * num_pages);
-	bank->size = 0;
+		/* calculate numbers of sectors */
+		int num_sectors = flash_size_in_b / 32; /* OTP sector size is 32 bytes */
 
-	/* fixed memory */
-	setup_sector(bank, 0, 4, 16 * 1024);
-	setup_sector(bank, 4, 1, 64 * 1024);
+		/* check that calculation result makes sense */
+		assert(num_sectors > 0);
 
-	/* dynamic memory */
-	setup_sector(bank, 4 + 1, num_pages - 5, 128 * 1024);
+		if (bank->sectors) {
+			free(bank->sectors);
+			bank->sectors = NULL;
+		}
+
+		bank->num_sectors = num_sectors;
+		bank->sectors = malloc(sizeof(struct flash_sector) * num_sectors);
+		bank->size = 512;
 
-	for (i = 0; i < num_pages; i++) {
-		bank->sectors[i].is_erased = -1;
-		bank->sectors[i].is_protected = 0;
+		/* OTP memory */
+		for (i = 0; i < num_sectors; i++) {
+			bank->sectors[i].offset = i*32;
+			bank->sectors[i].size = 32;
+			bank->sectors[i].is_erased = 1;
+			bank->sectors[i].is_protected = 0;
+		}
 	}
 
 	stm32x_info->probed = 1;
@@ -687,23 +781,23 @@ static int get_stm32x_info(struct flash_bank *bank, char *buf, int buf_size)
 
 		switch (device_id >> 16) {
 			case 0x1000:
-				snprintf(buf, buf_size, "A");
+				printed = snprintf(buf, buf_size, "A");
 				break;
 
 			case 0x2000:
-				snprintf(buf, buf_size, "B");
+				printed = snprintf(buf, buf_size, "B");
 				break;
 
 			case 0x1001:
-				snprintf(buf, buf_size, "Z");
+				printed = snprintf(buf, buf_size, "Z");
 				break;
 
 			case 0x2001:
-				snprintf(buf, buf_size, "Y");
+				printed = snprintf(buf, buf_size, "Y");
 				break;
 
 			default:
-				snprintf(buf, buf_size, "unknown");
+				printed = snprintf(buf, buf_size, "unknown");
 				break;
 		}
 	} else if ((device_id & 0xfff) == 0x413) {
@@ -713,15 +807,15 @@ static int get_stm32x_info(struct flash_bank *bank, char *buf, int buf_size)
 
 		switch (device_id >> 16) {
 			case 0x1000:
-				snprintf(buf, buf_size, "A");
+				printed = snprintf(buf, buf_size, "A");
 				break;
 
 			case 0x1001:
-				snprintf(buf, buf_size, "Z");
+				printed = snprintf(buf, buf_size, "Z");
 				break;
 
 			default:
-				snprintf(buf, buf_size, "unknown");
+				printed = snprintf(buf, buf_size, "unknown");
 				break;
 		}
 	} else {
@@ -729,6 +823,12 @@ static int get_stm32x_info(struct flash_bank *bank, char *buf, int buf_size)
 		return ERROR_FAIL;
 	}
 
+	if ( stm32x_is_otp(bank) ) {
+		buf += printed;
+		buf_size -= printed;
+		snprintf(buf, buf_size, "\nOTP memory bank is %s for write commands.", ( (stm32x_is_otp_unlocked(bank) == 1) ? "enabled" : "disabled") );
+	}
+
 	return ERROR_OK;
 }
 
@@ -737,6 +837,11 @@ static int stm32x_mass_erase(struct flash_bank *bank)
 	int retval;
 	struct target *target = bank->target;
 
+	if (stm32x_is_otp(bank)) {
+		LOG_ERROR("Can not erase OTP memory");
+		return ERROR_FAIL;
+	}
+
 	if (target->state != TARGET_HALTED) {
 		LOG_ERROR("Target not halted");
 		return ERROR_TARGET_NOT_HALTED;
@@ -766,6 +871,8 @@ static int stm32x_mass_erase(struct flash_bank *bank)
 	return ERROR_OK;
 }
 
+/* stm32f2x mass_erase <bank#>
+ */
 COMMAND_HANDLER(stm32x_handle_mass_erase_command)
 {
 	int i;
@@ -794,6 +901,36 @@ COMMAND_HANDLER(stm32x_handle_mass_erase_command)
 	return retval;
 }
 
+/* stm32f2x otp <bank#> enable|disable|show
+ */
+COMMAND_HANDLER(stm32x_handle_otp_command)
+{
+	if (CMD_ARGC < 2) {
+		command_print(CMD_CTX, "stm32x otp <bank> (enable|disable|show)");
+		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;
+	if ( stm32x_is_otp(bank) ) {
+		if ( strcmp(CMD_ARGV[1], "enable") == 0 ) {
+			stm32x_otp_enable(bank);
+		} else if ( strcmp(CMD_ARGV[1], "disable") == 0 ) {
+			stm32x_otp_disable(bank);
+		} else if ( strcmp(CMD_ARGV[1], "show") == 0 ) {
+			command_print(CMD_CTX, "OTP memory bank #%d is %s for write commands.", bank->bank_number, (stm32x_is_otp_unlocked(bank) == 1 ? "enabled" : "disabled"));
+		} else {
+			return ERROR_COMMAND_SYNTAX_ERROR;
+		}
+	} else {
+		command_print(CMD_CTX, "Failed: not an OTP bank.");
+	}
+
+	return retval;
+}
+
 static const struct command_registration stm32x_exec_command_handlers[] = {
 	{
 		.name = "mass_erase",
@@ -802,6 +939,13 @@ static const struct command_registration stm32x_exec_command_handlers[] = {
 		.usage = "bank_id",
 		.help = "Erase entire flash device.",
 	},
+	{
+		.name = "otp",
+		.handler = stm32x_handle_otp_command,
+		.mode = COMMAND_EXEC,
+		.usage = "bank_id",
+		.help = "OTP (One Time Programmable) memory write enable/disable.",
+	},
 	COMMAND_REGISTRATION_DONE
 };
 
diff --git a/tcl/target/stm32f2x.cfg b/tcl/target/stm32f2x.cfg
index cfd6274..777fd48 100644
--- a/tcl/target/stm32f2x.cfg
+++ b/tcl/target/stm32f2x.cfg
@@ -56,8 +56,12 @@ target create $_TARGETNAME cortex_m3 -endian $_ENDIAN -chain-position $_TARGETNA
 
 $_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0
 
+# size is automatically "calculated" by probing
 set _FLASHNAME $_CHIPNAME.flash
-flash bank $_FLASHNAME stm32f2x 0 0 0 0 $_TARGETNAME
+flash bank $_FLASHNAME stm32f2x 0x08000000 0 0 0 $_TARGETNAME
+# STM32F2 has an OTP (One-Time Programmable) memory
+set _FLASHNAME $_CHIPNAME.otp
+flash bank $_FLASHNAME stm32f2x 0x1FFF7800 0 0 0 $_TARGETNAME
 
 # if srst is not fitted use SYSRESETREQ to
 # perform a soft reset
-- 
1.7.4.4

