flash: iterating over an address range now handles multiple banks
[fw/openocd] / src / flash / nor / core.c
index 429bad6762dcbe606cb776f5f39e1eceefc4cfd0..ff467d3d0cc6766214c6dc4efa955bfa5b7b3779 100644 (file)
@@ -197,7 +197,7 @@ struct flash_bank *get_flash_bank_by_name_noprobe(const char *name)
        return NULL;
 }
 
-struct flash_bank *get_flash_bank_by_name(const char *name)
+int get_flash_bank_by_name(const char *name, struct flash_bank **bank_result)
 {
        struct flash_bank *bank;
        int retval;
@@ -210,11 +210,12 @@ struct flash_bank *get_flash_bank_by_name(const char *name)
                if (retval != ERROR_OK)
                {
                        LOG_ERROR("auto_probe failed %d\n", retval);
-                       return NULL;
+                       return retval;
                }
        }
 
-       return bank;
+       *bank_result = bank;
+       return ERROR_OK;
 }
 
 int get_flash_bank_by_num(int num, struct flash_bank **bank)
@@ -238,8 +239,9 @@ int get_flash_bank_by_num(int num, struct flash_bank **bank)
        return ERROR_OK;
 }
 
-/* lookup flash bank by address */
-struct flash_bank *get_flash_bank_by_addr(struct target *target, uint32_t addr)
+/* lookup flash bank by address, bank not found is success, but
+ * result_bank is set to NULL. */
+int get_flash_bank_by_addr(struct target *target, uint32_t addr, bool check, struct flash_bank **result_bank)
 {
        struct flash_bank *c;
 
@@ -252,14 +254,22 @@ struct flash_bank *get_flash_bank_by_addr(struct target *target, uint32_t addr)
                if (retval != ERROR_OK)
                {
                        LOG_ERROR("auto_probe failed %d\n", retval);
-                       return NULL;
+                       return retval;
                }
                /* check whether address belongs to this flash bank */
                if ((addr >= c->base) && (addr <= c->base + (c->size - 1)) && target == c->target)
-                       return c;
+               {
+                       *result_bank = c;
+                       return ERROR_OK;
+               }
        }
-       LOG_ERROR("No flash at address 0x%08" PRIx32 "\n", addr);
-       return NULL;
+       *result_bank = NULL;
+       if (check)
+       {
+               LOG_ERROR("No flash at address 0x%08" PRIx32 "\n", addr);
+               return ERROR_FAIL;
+       }
+       return ERROR_OK;
 }
 
 int default_flash_mem_blank_check(struct flash_bank *bank)
@@ -369,7 +379,7 @@ int default_flash_blank_check(struct flash_bank *bank)
  * sectors will be added to the range, and that reason string is used when
  * warning about those additions.
  */
-static int flash_iterate_address_range(struct target *target,
+static int flash_iterate_address_range_inner(struct target *target,
                char *pad_reason, uint32_t addr, uint32_t length,
                int (*callback)(struct flash_bank *bank, int first, int last))
 {
@@ -379,8 +389,9 @@ static int flash_iterate_address_range(struct target *target,
        int last = -1;
        int i;
 
-       if ((c = get_flash_bank_by_addr(target, addr)) == NULL)
-               return ERROR_FLASH_DST_OUT_OF_BANK; /* no corresponding bank found */
+       int retval = get_flash_bank_by_addr(target, addr, true, &c);
+       if (retval != ERROR_OK)
+               return retval;
 
        if (c->size == 0 || c->num_sectors == 0)
        {
@@ -487,6 +498,43 @@ static int flash_iterate_address_range(struct target *target,
        return callback(c, first, last);
 }
 
+/* The inner fn only handles a single bank, we could be spanning
+ * multiple chips.
+ */
+static int flash_iterate_address_range(struct target *target,
+               char *pad_reason, uint32_t addr, uint32_t length,
+               int (*callback)(struct flash_bank *bank, int first, int last))
+{
+       struct flash_bank *c;
+       int retval = ERROR_OK;
+
+       /* Danger! zero-length iterations means entire bank! */
+       do
+       {
+               retval = get_flash_bank_by_addr(target, addr, true, &c);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               uint32_t cur_length = length;
+               /* check whether it all fits in this bank */
+               if (addr + length - 1 > c->base + c->size - 1)
+               {
+                       LOG_DEBUG("iterating over more than one flash bank.");
+                       cur_length = c->base + c->size - addr;
+               }
+               retval = flash_iterate_address_range_inner(target,
+                               pad_reason, addr, cur_length,
+                               callback);
+               if (retval != ERROR_OK)
+                       break;
+
+               length -= cur_length;
+               addr += cur_length;
+       } while (length > 0);
+
+       return retval;
+}
+
 int flash_erase_address_range(struct target *target,
                bool pad, uint32_t addr, uint32_t length)
 {
@@ -511,9 +559,9 @@ int flash_unlock_address_range(struct target *target, uint32_t addr, uint32_t le
 
 static int compare_section (const void * a, const void * b)
 {
-       struct imageection *b1, *b2;
-       b1=*((struct imageection **)a);
-       b2=*((struct imageection **)b);
+       struct imagesection *b1, *b2;
+       b1=*((struct imagesection **)a);
+       b2=*((struct imagesection **)b);
 
        if (b1->base_address == b2->base_address)
        {
@@ -557,7 +605,7 @@ int flash_write_unlock(struct target *target, struct image *image,
 
        /* This fn requires all sections to be in ascending order of addresses,
         * whereas an image can have sections out of order. */
-       struct imageection **sections = malloc(sizeof(struct imageection *) *
+       struct imagesection **sections = malloc(sizeof(struct imagesection *) *
                        image->num_sections);
        int i;
        for (i = 0; i < image->num_sections; i++)
@@ -565,7 +613,7 @@ int flash_write_unlock(struct target *target, struct image *image,
                sections[i] = &image->sections[i];
        }
 
-       qsort(sections, image->num_sections, sizeof(struct imageection *),
+       qsort(sections, image->num_sections, sizeof(struct imagesection *),
                        compare_section);
 
        /* loop until we reach end of the image */
@@ -588,7 +636,12 @@ int flash_write_unlock(struct target *target, struct image *image,
                }
 
                /* find the corresponding flash bank */
-               if ((c = get_flash_bank_by_addr(target, run_address)) == NULL)
+               retval = get_flash_bank_by_addr(target, run_address, false, &c);
+               if (retval != ERROR_OK)
+               {
+                       goto done;
+               }
+               if (c == NULL)
                {
                        section++; /* and skip it */
                        section_offset = 0;
@@ -638,8 +691,13 @@ int flash_write_unlock(struct target *target, struct image *image,
 
                if (run_address + run_size - 1 > c->base + c->size - 1)
                {
-                       LOG_ERROR("The image is too big for the flash");
-                       return ERROR_FAIL;
+                       /* If we have more than one flash chip back to back, then we limit
+                        * the current write operation to the current chip.
+                        */
+                       LOG_DEBUG("Truncate flash run size to the current flash chip.");
+
+                       run_size = c->base + c->size - run_address;
+                       assert(run_size > 0);
                }
 
                /* If we're applying any sector automagic, then pad this
@@ -665,6 +723,12 @@ int flash_write_unlock(struct target *target, struct image *image,
 
                /* allocate buffer */
                buffer = malloc(run_size);
+               if (buffer == NULL)
+               {
+                       LOG_ERROR("Out of memory for flash bank buffer");
+                       retval = ERROR_FAIL;
+                       goto done;
+               }
                buffer_size = 0;
 
                /* read sections to the buffer */
@@ -682,7 +746,7 @@ int flash_write_unlock(struct target *target, struct image *image,
                         * list of pointers to sections to invoke image_read_section()...
                         */
                        intptr_t diff = (intptr_t)sections[section] - (intptr_t)image->sections;
-                       int t_section_num = diff / sizeof(struct imageection);
+                       int t_section_num = diff / sizeof(struct imagesection);
 
                        LOG_DEBUG("image_read_section: section = %d, t_section_num = %d, section_offset = %d, buffer_size = %d, size_read = %d",
                                 (int)section,