From c89eb70a20230edfc79153c17c0c4c3f9dc64819 Mon Sep 17 00:00:00 2001 From: Andreas Fritiofson 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 Reviewed-on: http://openocd.zylin.com/756 Tested-by: jenkins Reviewed-by: Spencer Oliver Reviewed-by: Freddie Chopin --- 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 d06016a4..baf6b275 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; } -- 2.39.5