From c89eb70a20230edfc79153c17c0c4c3f9dc64819 Mon Sep 17 00:00:00 2001
From: Andreas Fritiofson <andreas.fritiofson@gmail.com>
Date: Fri, 20 Jul 2012 14:44:22 +0200
Subject: [PATCH] flash: stm32f1x: Pad odd byte writes early to avoid 16-bit
 writes

For odd byte counts, stm32x_write() pads the last byte and writes it using
a discrete 16-bit access. The stlink debugger can't issue 16-bit writes so
it fails for odd byte writes.

This patch changes stm32x_write() to pad odd byte writes into a new buffer
and use the normal code path with a single block write. The fallback path,
when working area cannot be allocated, has to use 16-bit writes though
which means that sufficient working area is required for stlink and odd
byte writes.

Change-Id: I4c5dc456300b6e1056f76b0095be8aceee3e954f
Signed-off-by: Andreas Fritiofson <andreas.fritiofson@gmail.com>
Reviewed-on: http://openocd.zylin.com/756
Tested-by: jenkins
Reviewed-by: Spencer Oliver <spen@spen-soft.co.uk>
Reviewed-by: Freddie Chopin <freddie.chopin@gmail.com>
---
 src/flash/nor/stm32f1x.c | 103 +++++++++++++++++++--------------------
 1 file changed, 49 insertions(+), 54 deletions(-)

diff --git a/src/flash/nor/stm32f1x.c b/src/flash/nor/stm32f1x.c
index d06016a4d..baf6b2752 100644
--- a/src/flash/nor/stm32f1x.c
+++ b/src/flash/nor/stm32f1x.c
@@ -724,11 +724,7 @@ static int stm32x_write(struct flash_bank *bank, 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;
-	int retval;
+	uint8_t *new_buffer = NULL;
 
 	if (bank->target->state != TARGET_HALTED) {
 		LOG_ERROR("Target not halted");
@@ -736,76 +732,75 @@ static int stm32x_write(struct flash_bank *bank, uint8_t *buffer,
 	}
 
 	if (offset & 0x1) {
-		LOG_WARNING("offset 0x%" PRIx32 " breaks required 2-byte alignment", offset);
+		LOG_ERROR("offset 0x%" PRIx32 " breaks required 2-byte alignment", offset);
 		return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
 	}
 
+	/* If there's an odd number of bytes, the data has to be padded. Duplicate
+	 * the buffer and use the normal code path with a single block write since
+	 * it's probably cheaper than to special case the last odd write using
+	 * discrete accesses. */
+	if (count & 1) {
+		new_buffer = malloc(count + 1);
+		if (new_buffer == NULL) {
+			LOG_ERROR("odd number of bytes to write and no memory for padding buffer");
+			return ERROR_FAIL;
+		}
+		LOG_INFO("odd number of bytes to write, padding with 0xff");
+		buffer = memcpy(new_buffer, buffer, count);
+		buffer[count++] = 0xff;
+	}
+
+	uint32_t words_remaining = count / 2;
+	int retval, retval2;
+
 	/* unlock flash registers */
 	retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_KEYR), KEY1);
 	if (retval != ERROR_OK)
-		return retval;
+		goto cleanup;
 	retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_KEYR), KEY2);
 	if (retval != ERROR_OK)
-		return retval;
+		goto cleanup;
 
 	retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_PG);
 	if (retval != ERROR_OK)
-		return retval;
+		goto cleanup;
 
-	/* multiple half words (2-byte) to be programmed? */
-	if (words_remaining > 0) {
-		/* try using a block write */
-		retval = stm32x_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 {
-			buffer += words_remaining * 2;
-			address += words_remaining * 2;
-			words_remaining = 0;
-		}
-	}
+	/* try using a block write */
+	retval = stm32x_write_block(bank, buffer, offset, words_remaining);
 
-	if ((retval != ERROR_OK) && (retval != ERROR_TARGET_RESOURCE_NOT_AVAILABLE))
-		goto reset_pg_and_lock;
+	if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) {
+		/* if block write failed (no sufficient working area),
+		 * we use normal (slow) single halfword accesses */
+		LOG_WARNING("couldn't use block writes, falling back to single memory accesses");
 
-	while (words_remaining > 0) {
-		uint16_t value;
-		memcpy(&value, buffer + bytes_written, sizeof(uint16_t));
+		while (words_remaining > 0) {
+			uint16_t value;
+			memcpy(&value, buffer, sizeof(uint16_t));
 
-		retval = target_write_u16(target, address, value);
-		if (retval != ERROR_OK)
-			goto reset_pg_and_lock;
+			retval = target_write_u16(target, bank->base + offset, value);
+			if (retval != ERROR_OK)
+				goto reset_pg_and_lock;
 
-		retval = stm32x_wait_status_busy(bank, 5);
-		if (retval != ERROR_OK)
-			goto reset_pg_and_lock;
+			retval = stm32x_wait_status_busy(bank, 5);
+			if (retval != ERROR_OK)
+				goto reset_pg_and_lock;
 
-		bytes_written += 2;
-		words_remaining--;
-		address += 2;
+			words_remaining--;
+			buffer += 2;
+			offset += 2;
+		}
 	}
 
-	if (bytes_remaining) {
-		uint16_t value = 0xffff;
-		memcpy(&value, buffer + bytes_written, bytes_remaining);
-
-		retval = target_write_u16(target, address, value);
-		if (retval != ERROR_OK)
-			goto reset_pg_and_lock;
-
-		retval = stm32x_wait_status_busy(bank, 5);
-		if (retval != ERROR_OK)
-			goto reset_pg_and_lock;
-	}
+reset_pg_and_lock:
+	retval2 = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_LOCK);
+	if (retval == ERROR_OK)
+		retval = retval2;
 
-	return target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_LOCK);
+cleanup:
+	if (new_buffer)
+		free(new_buffer);
 
-reset_pg_and_lock:
-	target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_LOCK);
 	return retval;
 }
 
-- 
GitLab