flash: cleanup stm32lx driver08/1008/5
authorSpencer Oliver <spen@spen-soft.co.uk>
Fri, 4 Jan 2013 21:15:59 +0000 (4 21:15 +0000)
committerSpencer Oliver <spen@spen-soft.co.uk>
Mon, 21 Jan 2013 16:45:20 +0000 (21 16:45 +0000)
Handle any leading bytes upto the next 128 byte page, enabling us to safely
use the faster page write.

Rather than use a separate word/byte write to program any trailing bytes
we use a combined write function.

Use memcpy for byte writes and change loader to using bytes.

Change-Id: Ie0164a30388f018dd00e752cf5ff87d4f96ced97
Signed-off-by: Spencer Oliver <spen@spen-soft.co.uk>
Reviewed-on: http://openocd.zylin.com/1008
Tested-by: jenkins
src/flash/nor/stm32lx.c

index ce41f94..fddd16d 100644 (file)
@@ -212,44 +212,34 @@ static int stm32lx_write_half_pages(struct flash_bank *bank, uint8_t *buffer,
                uint32_t offset, uint32_t count)
 {
        struct target *target = bank->target;
-       uint32_t buffer_size = 4096 * 4;
+       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];
+       struct reg_param reg_params[3];
        struct armv7m_algorithm armv7m_info;
 
        int retval = ERROR_OK;
-       uint32_t reg32;
-
-       /* see contib/loaders/flash/stm32lx.s for src */
 
-       static const uint16_t stm32lx_flash_write_code_16[] = {
-       /*      00000000 <write_word-0x4>: */
-                       0x2300, /* 0:   2300            movs    r3, #0 */
-                       0xe004, /* 2:   e004            b.n     e <test_done> */
+       /* see contib/loaders/flash/stm32lx.S for src */
 
-                       /*      00000004 <write_word>: */
-                       0xf851, 0xcb04, /* 4:   f851 cb04       ldr.w   ip, [r1], #4 */
-                       0xf840, 0xcb04, /* 8:   f840 cb04       str.w   ip, [r0], #4 */
-                       0x3301, /* c:   3301            adds    r3, #1 */
+       static const uint8_t stm32lx_flash_write_code[] = {
+               /* write_word: */
+               0x00, 0x23,             /* movs r3, #0 */
+               0x04, 0xe0,             /* b test_done */
 
-                       /*      0000000e <test_done>: */
-                       0x4293, /* e:   4293            cmp     r3, r2 */
-                       0xd3f8, /* 10:  d3f8            bcc.n   4 <write_word> */
-                       0xbe00, /* 12:  be00            bkpt    0x0000 */
+               /* write_word: */
+               0x51, 0xf8, 0x04, 0xcb, /* ldr ip, [r1], #4 */
+               0x40, 0xf8, 0x04, 0xcb, /* str ip, [r0], #4 */
+               0x01, 0x33,             /* adds r3, #1 */
 
-                       };
+               /* test_done: */
+               0x93, 0x42,             /* cmp r3, r2 */
+               0xf8, 0xd3,             /* bcc write_word */
+               0x00, 0xbe,             /* bkpt 0 */
+       };
 
-       /* Flip endian */
-       uint8_t stm32lx_flash_write_code[sizeof(stm32lx_flash_write_code_16)];
-       for (unsigned int i = 0; i < sizeof(stm32lx_flash_write_code_16) / 2; i++) {
-               stm32lx_flash_write_code[i * 2 + 0] = stm32lx_flash_write_code_16[i]
-                               & 0xff;
-               stm32lx_flash_write_code[i * 2 + 1] = (stm32lx_flash_write_code_16[i]
-                               >> 8) & 0xff;
-       }
        /* Check if there is an even number of half pages (128bytes) */
        if (count % 128) {
                LOG_ERROR("there should be an even number "
@@ -257,14 +247,12 @@ static int stm32lx_write_half_pages(struct flash_bank *bank, uint8_t *buffer,
                return ERROR_FAIL;
        }
 
-       /* Allocate working area */
-       reg32 = sizeof(stm32lx_flash_write_code);
-       /* Add bytes to make 4byte aligned */
-       reg32 += (4 - (reg32 % 4)) % 4;
-       retval = target_alloc_working_area(target, reg32,
-                       &write_algorithm);
-       if (retval != ERROR_OK)
-               return retval;
+       /* flash write code */
+       if (target_alloc_working_area(target, sizeof(stm32lx_flash_write_code),
+                       &write_algorithm) != ERROR_OK) {
+               LOG_DEBUG("no working area for block memory writes");
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+       };
 
        /* Write the flashing code */
        retval = target_write_buffer(target,
@@ -277,8 +265,7 @@ static int stm32lx_write_half_pages(struct flash_bank *bank, uint8_t *buffer,
        }
 
        /* Allocate half pages memory */
-       while (target_alloc_working_area_try(target, buffer_size, &source)
-                       != ERROR_OK) {
+       while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) {
                if (buffer_size > 1024)
                        buffer_size -= 1024;
                else
@@ -293,15 +280,12 @@ static int stm32lx_write_half_pages(struct flash_bank *bank, uint8_t *buffer,
                        return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
                }
        }
-       LOG_DEBUG("allocated working area for data (%" PRIx32 " bytes)", buffer_size);
 
        armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
        armv7m_info.core_mode = ARMV7M_MODE_ANY;
        init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT);
        init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
        init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT);
-       init_reg_param(&reg_params[3], "r3", 32, PARAM_IN_OUT);
-       init_reg_param(&reg_params[4], "r4", 32, PARAM_OUT);
 
        /* Enable half-page write */
        retval = stm32lx_enable_write_half_page(bank);
@@ -312,7 +296,6 @@ static int stm32lx_write_half_pages(struct flash_bank *bank, uint8_t *buffer,
                destroy_reg_param(&reg_params[0]);
                destroy_reg_param(&reg_params[1]);
                destroy_reg_param(&reg_params[2]);
-               destroy_reg_param(&reg_params[3]);
                return retval;
        }
 
@@ -322,8 +305,7 @@ static int stm32lx_write_half_pages(struct flash_bank *bank, uint8_t *buffer,
                this_count = (count > buffer_size) ? buffer_size : count;
 
                /* Write the next half pages */
-               retval = target_write_buffer(target, source->address, this_count,
-                               buffer);
+               retval = target_write_buffer(target, source->address, this_count, buffer);
                if (retval != ERROR_OK)
                        break;
 
@@ -361,103 +343,118 @@ static int stm32lx_write_half_pages(struct flash_bank *bank, uint8_t *buffer,
        destroy_reg_param(&reg_params[0]);
        destroy_reg_param(&reg_params[1]);
        destroy_reg_param(&reg_params[2]);
-       destroy_reg_param(&reg_params[3]);
 
        return retval;
 }
+
 static int stm32lx_write(struct flash_bank *bank, uint8_t *buffer,
                uint32_t offset, uint32_t count)
 {
        struct target *target = bank->target;
 
        uint32_t halfpages_number;
-       uint32_t words_remaining;
-       uint32_t bytes_remaining;
+       uint32_t bytes_remaining = 0;
        uint32_t address = bank->base + offset;
        uint32_t bytes_written = 0;
-       int retval;
+       int retval, retval2;
 
        if (bank->target->state != TARGET_HALTED) {
                LOG_ERROR("Target not halted");
                return ERROR_TARGET_NOT_HALTED;
        }
 
-       if (offset & 0x1) {
-               LOG_ERROR("offset 0x%" PRIx32 " breaks required 2-byte alignment", offset);
+       if (offset & 0x3) {
+               LOG_ERROR("offset 0x%" PRIx32 " breaks required 4-byte alignment", offset);
                return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
        }
 
-       /* Check if there are some full half pages */
-       if (((offset % 128) == 0) && (count >= 128)) {
-               halfpages_number = count / 128;
-               words_remaining = (count - 128 * halfpages_number) / 4;
-               bytes_remaining = (count & 0x3);
-       } else {
-               halfpages_number = 0;
-               words_remaining = (count / 4);
-               bytes_remaining = (count & 0x3);
+       retval = stm32lx_unlock_program_memory(bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* first we need to write any unaligned head bytes upto
+        * the next 128 byte page */
+
+       if (offset % 128)
+               bytes_remaining = MIN(count, 128 - (offset % 128));
+
+       while (bytes_remaining > 0) {
+               uint8_t value[4] = {0xff, 0xff, 0xff, 0xff};
+
+               /* copy remaining bytes into the write buffer */
+               uint32_t bytes_to_write = MIN(4, bytes_remaining);
+               memcpy(value, buffer + bytes_written, bytes_to_write);
+
+               retval = target_write_buffer(target, address, 4, value);
+               if (retval != ERROR_OK)
+                       goto reset_pg_and_lock;
+
+               bytes_written += bytes_to_write;
+               bytes_remaining -= bytes_to_write;
+               address += 4;
+
+               retval = stm32lx_wait_until_bsy_clear(bank);
+               if (retval != ERROR_OK)
+                       goto reset_pg_and_lock;
        }
 
+       offset += bytes_written;
+       count -= bytes_written;
+
+       /* this should always pass this check here */
+       assert((offset % 128) == 0);
+
+       /* calculate half pages */
+       halfpages_number = count / 128;
+
        if (halfpages_number) {
-               retval = stm32lx_write_half_pages(bank, buffer, offset, 128 * halfpages_number);
+               retval = stm32lx_write_half_pages(bank, buffer + bytes_written, offset, 128 * halfpages_number);
                if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) {
                        /* attempt slow memory writes */
                        LOG_WARNING("couldn't use block writes, falling back to single memory accesses");
                        halfpages_number = 0;
-                       words_remaining = (count / 4);
                } else {
                        if (retval != ERROR_OK)
                                return ERROR_FAIL;
                }
        }
 
-       bytes_written = 128 * halfpages_number;
-       address += bytes_written;
+       /* write any remaining bytes */
+       uint32_t page_bytes_written = 128 * halfpages_number;
+       bytes_written += page_bytes_written;
+       address += page_bytes_written;
+       bytes_remaining = count - page_bytes_written;
 
        retval = stm32lx_unlock_program_memory(bank);
        if (retval != ERROR_OK)
                return retval;
 
-       while (words_remaining > 0) {
-               uint32_t value;
-               uint8_t *p = buffer + bytes_written;
+       while (bytes_remaining > 0) {
+               uint8_t value[4] = {0xff, 0xff, 0xff, 0xff};
 
-               /* Prepare the word, Little endian conversion */
-               value = p[0] + (p[1] << 8) + (p[2] << 16) + (p[3] << 24);
+               /* copy remaining bytes into the write buffer */
+               uint32_t bytes_to_write = MIN(4, bytes_remaining);
+               memcpy(value, buffer + bytes_written, bytes_to_write);
 
-               retval = target_write_u32(target, address, value);
+               retval = target_write_buffer(target, address, 4, value);
                if (retval != ERROR_OK)
-                       return retval;
+                       goto reset_pg_and_lock;
 
-               bytes_written += 4;
-               words_remaining--;
+               bytes_written += bytes_to_write;
+               bytes_remaining -= bytes_to_write;
                address += 4;
 
                retval = stm32lx_wait_until_bsy_clear(bank);
                if (retval != ERROR_OK)
-                       return retval;
+                       goto reset_pg_and_lock;
        }
 
-       if (bytes_remaining) {
-               uint8_t last_word[4] = {0xff, 0xff, 0xff, 0xff};
-
-               /* copy the last remaining bytes into the write buffer */
-               memcpy(last_word, buffer+bytes_written, bytes_remaining);
-
-               retval = target_write_buffer(target, address, 4, last_word);
-               if (retval != ERROR_OK)
-                       return retval;
-
-               retval = stm32lx_wait_until_bsy_clear(bank);
-               if (retval != ERROR_OK)
-                       return retval;
-       }
-
-       retval = stm32lx_lock_program_memory(bank);
-       if (retval != ERROR_OK)
-               return retval;
+reset_pg_and_lock:
+       retval2 = stm32lx_lock_program_memory(bank);
+       if (retval == ERROR_OK)
+               retval = retval2;
 
-       return ERROR_OK;
+       return retval;
 }
 
 static int stm32lx_probe(struct flash_bank *bank)
@@ -710,6 +707,14 @@ static int stm32lx_unlock_program_memory(struct flash_bank *bank)
         * then by writing the 2 PRGKEY to the PRGKEYR register
         */
 
+       /* check flash is not already unlocked */
+       retval = target_read_u32(target, FLASH_PECR, &reg32);
+       if (retval != ERROR_OK)
+               return retval;
+
+       if ((reg32 & FLASH_PECR__PRGLOCK) == 0)
+               return ERROR_OK;
+
        /* To unlock the PECR write the 2 PEKEY to the PEKEYR register */
        retval = target_write_u32(target, FLASH_PEKEYR, PEKEY1);
        if (retval != ERROR_OK)
@@ -745,6 +750,7 @@ static int stm32lx_unlock_program_memory(struct flash_bank *bank)
                LOG_ERROR("PRGLOCK is not cleared :(");
                return ERROR_FLASH_OPERATION_FAILED;
        }
+
        return ERROR_OK;
 }