stm32f1x: Increase options erase timeout
[fw/openocd] / src / flash / nor / stm32f1x.c
index d06016a4d849ff3ae46fd88115616cacd642e591..d021bbfb3835a4a81f06ebc4de97d426197cdc70 100644 (file)
 #define KEY1                   0x45670123
 #define KEY2                   0xCDEF89AB
 
+/* timeout values */
+
+#define FLASH_WRITE_TIMEOUT 10
+#define FLASH_ERASE_TIMEOUT 100
+
 struct stm32x_options {
        uint16_t RDP;
        uint16_t user_options;
@@ -110,7 +115,6 @@ struct stm32x_options {
 
 struct stm32x_flash_bank {
        struct stm32x_options option_bytes;
-       struct working_area *write_algorithm;
        int ppage_size;
        int probed;
 
@@ -134,7 +138,6 @@ FLASH_BANK_COMMAND_HANDLER(stm32x_flash_bank_command)
        stm32x_info = malloc(sizeof(struct stm32x_flash_bank));
 
        bank->driver_priv = stm32x_info;
-       stm32x_info->write_algorithm = NULL;
        stm32x_info->probed = 0;
        stm32x_info->has_dual_banks = false;
        stm32x_info->register_base = FLASH_REG_BASE_B0;
@@ -249,6 +252,14 @@ static int stm32x_erase_options(struct flash_bank *bank)
 
        stm32x_info = bank->driver_priv;
 
+       /* stlink is currently does not support 16bit
+        * read/writes. so we cannot write option bytes */
+       struct armv7m_common *armv7m = target_to_armv7m(target);
+       if (armv7m && armv7m->stlink) {
+               LOG_ERROR("Option bytes currently unsupported for stlink");
+               return ERROR_FAIL;
+       }
+
        /* read current options */
        stm32x_read_options(bank);
 
@@ -277,7 +288,7 @@ static int stm32x_erase_options(struct flash_bank *bank)
        if (retval != ERROR_OK)
                return retval;
 
-       retval = stm32x_wait_status_busy(bank, 10);
+       retval = stm32x_wait_status_busy(bank, FLASH_ERASE_TIMEOUT);
        if (retval != ERROR_OK)
                return retval;
 
@@ -321,7 +332,7 @@ static int stm32x_write_options(struct flash_bank *bank)
        if (retval != ERROR_OK)
                return retval;
 
-       retval = stm32x_wait_status_busy(bank, 10);
+       retval = stm32x_wait_status_busy(bank, FLASH_WRITE_TIMEOUT);
        if (retval != ERROR_OK)
                return retval;
 
@@ -330,7 +341,7 @@ static int stm32x_write_options(struct flash_bank *bank)
        if (retval != ERROR_OK)
                return retval;
 
-       retval = stm32x_wait_status_busy(bank, 10);
+       retval = stm32x_wait_status_busy(bank, FLASH_WRITE_TIMEOUT);
        if (retval != ERROR_OK)
                return retval;
 
@@ -339,7 +350,7 @@ static int stm32x_write_options(struct flash_bank *bank)
        if (retval != ERROR_OK)
                return retval;
 
-       retval = stm32x_wait_status_busy(bank, 10);
+       retval = stm32x_wait_status_busy(bank, FLASH_WRITE_TIMEOUT);
        if (retval != ERROR_OK)
                return retval;
 
@@ -348,7 +359,7 @@ static int stm32x_write_options(struct flash_bank *bank)
        if (retval != ERROR_OK)
                return retval;
 
-       retval = stm32x_wait_status_busy(bank, 10);
+       retval = stm32x_wait_status_busy(bank, FLASH_WRITE_TIMEOUT);
        if (retval != ERROR_OK)
                return retval;
 
@@ -357,7 +368,7 @@ static int stm32x_write_options(struct flash_bank *bank)
        if (retval != ERROR_OK)
                return retval;
 
-       retval = stm32x_wait_status_busy(bank, 10);
+       retval = stm32x_wait_status_busy(bank, FLASH_WRITE_TIMEOUT);
        if (retval != ERROR_OK)
                return retval;
 
@@ -366,7 +377,7 @@ static int stm32x_write_options(struct flash_bank *bank)
        if (retval != ERROR_OK)
                return retval;
 
-       retval = stm32x_wait_status_busy(bank, 10);
+       retval = stm32x_wait_status_busy(bank, FLASH_WRITE_TIMEOUT);
        if (retval != ERROR_OK)
                return retval;
 
@@ -481,7 +492,7 @@ static int stm32x_erase(struct flash_bank *bank, int first, int last)
                if (retval != ERROR_OK)
                        return retval;
 
-               retval = stm32x_wait_status_busy(bank, 100);
+               retval = stm32x_wait_status_busy(bank, FLASH_ERASE_TIMEOUT);
                if (retval != ERROR_OK)
                        return retval;
 
@@ -595,6 +606,7 @@ static int stm32x_write_block(struct flash_bank *bank, uint8_t *buffer,
        struct stm32x_flash_bank *stm32x_info = bank->driver_priv;
        struct target *target = bank->target;
        uint32_t buffer_size = 16384;
+       struct working_area *write_algorithm;
        struct working_area *source;
        uint32_t address = bank->base + offset;
        struct reg_param reg_params[5];
@@ -644,12 +656,12 @@ static int stm32x_write_block(struct flash_bank *bank, uint8_t *buffer,
 
        /* flash write code */
        if (target_alloc_working_area(target, sizeof(stm32x_flash_write_code),
-                       &stm32x_info->write_algorithm) != ERROR_OK) {
+                       &write_algorithm) != ERROR_OK) {
                LOG_WARNING("no working area available, can't do block memory writes");
                return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
        };
 
-       retval = target_write_buffer(target, stm32x_info->write_algorithm->address,
+       retval = target_write_buffer(target, write_algorithm->address,
                        sizeof(stm32x_flash_write_code), (uint8_t *)stm32x_flash_write_code);
        if (retval != ERROR_OK)
                return retval;
@@ -659,10 +671,9 @@ static int stm32x_write_block(struct flash_bank *bank, uint8_t *buffer,
                buffer_size /= 2;
                buffer_size &= ~3UL; /* Make sure it's 4 byte aligned */
                if (buffer_size <= 256) {
-                       /* if we already allocated the writing code, but failed to get a
+                       /* we already allocated the writing code, but failed to get a
                         * buffer, free the algorithm */
-                       if (stm32x_info->write_algorithm)
-                               target_free_working_area(target, stm32x_info->write_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;
@@ -688,7 +699,7 @@ static int stm32x_write_block(struct flash_bank *bank, uint8_t *buffer,
                        0, NULL,
                        5, reg_params,
                        source->address, source->size,
-                       stm32x_info->write_algorithm->address, 0,
+                       write_algorithm->address, 0,
                        &armv7m_info);
 
        if (retval == ERROR_FLASH_OPERATION_FAILED) {
@@ -709,7 +720,7 @@ static int stm32x_write_block(struct flash_bank *bank, uint8_t *buffer,
        }
 
        target_free_working_area(target, source);
-       target_free_working_area(target, stm32x_info->write_algorithm);
+       target_free_working_area(target, write_algorithm);
 
        destroy_reg_param(&reg_params[0]);
        destroy_reg_param(&reg_params[1]);
@@ -724,11 +735,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 +743,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;
 }
 
@@ -1139,6 +1145,10 @@ static int get_stm32x_info(struct flash_bank *bank, char *buf, int buf_size)
                                snprintf(buf, buf_size, "1.0");
                                break;
 
+                       case 0x2000:
+                               snprintf(buf, buf_size, "2.0");
+                               break;
+
                        default:
                                snprintf(buf, buf_size, "unknown");
                                break;
@@ -1185,6 +1195,10 @@ static int get_stm32x_info(struct flash_bank *bank, char *buf, int buf_size)
                                snprintf(buf, buf_size, "1.0");
                                break;
 
+                       case 0x2000:
+                               snprintf(buf, buf_size, "2.0");
+                               break;
+
                        default:
                                snprintf(buf, buf_size, "unknown");
                                break;
@@ -1466,7 +1480,7 @@ static int stm32x_mass_erase(struct flash_bank *bank)
        if (retval != ERROR_OK)
                return retval;
 
-       retval = stm32x_wait_status_busy(bank, 100);
+       retval = stm32x_wait_status_busy(bank, FLASH_ERASE_TIMEOUT);
        if (retval != ERROR_OK)
                return retval;