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>__archive__
parent
b8862229d0
commit
c89eb70a20
|
@ -724,11 +724,7 @@ static int stm32x_write(struct flash_bank *bank, uint8_t *buffer,
|
||||||
uint32_t offset, uint32_t count)
|
uint32_t offset, uint32_t count)
|
||||||
{
|
{
|
||||||
struct target *target = bank->target;
|
struct target *target = bank->target;
|
||||||
uint32_t words_remaining = (count / 2);
|
uint8_t *new_buffer = NULL;
|
||||||
uint32_t bytes_remaining = (count & 0x00000001);
|
|
||||||
uint32_t address = bank->base + offset;
|
|
||||||
uint32_t bytes_written = 0;
|
|
||||||
int retval;
|
|
||||||
|
|
||||||
if (bank->target->state != TARGET_HALTED) {
|
if (bank->target->state != TARGET_HALTED) {
|
||||||
LOG_ERROR("Target not halted");
|
LOG_ERROR("Target not halted");
|
||||||
|
@ -736,76 +732,75 @@ static int stm32x_write(struct flash_bank *bank, uint8_t *buffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (offset & 0x1) {
|
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;
|
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 */
|
/* unlock flash registers */
|
||||||
retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_KEYR), KEY1);
|
retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_KEYR), KEY1);
|
||||||
if (retval != ERROR_OK)
|
if (retval != ERROR_OK)
|
||||||
return retval;
|
goto cleanup;
|
||||||
retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_KEYR), KEY2);
|
retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_KEYR), KEY2);
|
||||||
if (retval != ERROR_OK)
|
if (retval != ERROR_OK)
|
||||||
return retval;
|
goto cleanup;
|
||||||
|
|
||||||
retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_PG);
|
retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_PG);
|
||||||
if (retval != ERROR_OK)
|
if (retval != ERROR_OK)
|
||||||
return retval;
|
goto cleanup;
|
||||||
|
|
||||||
/* multiple half words (2-byte) to be programmed? */
|
/* try using a block write */
|
||||||
if (words_remaining > 0) {
|
retval = stm32x_write_block(bank, buffer, offset, words_remaining);
|
||||||
/* try using a block write */
|
|
||||||
retval = stm32x_write_block(bank, buffer, offset, words_remaining);
|
if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) {
|
||||||
if (retval != ERROR_OK) {
|
/* if block write failed (no sufficient working area),
|
||||||
if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) {
|
* we use normal (slow) single halfword accesses */
|
||||||
/* if block write failed (no sufficient working area),
|
LOG_WARNING("couldn't use block writes, falling back to single memory accesses");
|
||||||
* we use normal (slow) single dword accesses */
|
|
||||||
LOG_WARNING("couldn't use block writes, falling back to single memory accesses");
|
while (words_remaining > 0) {
|
||||||
}
|
uint16_t value;
|
||||||
} else {
|
memcpy(&value, buffer, sizeof(uint16_t));
|
||||||
buffer += words_remaining * 2;
|
|
||||||
address += words_remaining * 2;
|
retval = target_write_u16(target, bank->base + offset, value);
|
||||||
words_remaining = 0;
|
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;
|
||||||
|
|
||||||
|
words_remaining--;
|
||||||
|
buffer += 2;
|
||||||
|
offset += 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((retval != ERROR_OK) && (retval != ERROR_TARGET_RESOURCE_NOT_AVAILABLE))
|
|
||||||
goto reset_pg_and_lock;
|
|
||||||
|
|
||||||
while (words_remaining > 0) {
|
|
||||||
uint16_t value;
|
|
||||||
memcpy(&value, buffer + bytes_written, sizeof(uint16_t));
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
bytes_written += 2;
|
|
||||||
words_remaining--;
|
|
||||||
address += 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
return target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_LOCK);
|
|
||||||
|
|
||||||
reset_pg_and_lock:
|
reset_pg_and_lock:
|
||||||
target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_LOCK);
|
retval2 = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_LOCK);
|
||||||
|
if (retval == ERROR_OK)
|
||||||
|
retval = retval2;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
if (new_buffer)
|
||||||
|
free(new_buffer);
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue