-/***************************************************************************\r
- * Copyright (C) 2005 by Dominic Rath *\r
- * Dominic.Rath@gmx.de *\r
- * *\r
- * This program is free software; you can redistribute it and/or modify *\r
- * it under the terms of the GNU General Public License as published by *\r
- * the Free Software Foundation; either version 2 of the License, or *\r
- * (at your option) any later version. *\r
- * *\r
- * This program is distributed in the hope that it will be useful, *\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of *\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *\r
- * GNU General Public License for more details. *\r
- * *\r
- * You should have received a copy of the GNU General Public License *\r
- * along with this program; if not, write to the *\r
- * Free Software Foundation, Inc., *\r
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *\r
- ***************************************************************************/\r
-#ifdef HAVE_CONFIG_H\r
-#include "config.h"\r
-#endif\r
-\r
-#include "flash.h"\r
-#include "command.h"\r
-#include "target.h"\r
-#include "time_support.h"\r
-#include "fileio.h"\r
-#include "image.h"\r
-#include "log.h"\r
-\r
-#include <string.h>\r
-#include <unistd.h>\r
-#include <stdlib.h>\r
-#include <sys/types.h>\r
-#include <sys/stat.h>\r
-#include <errno.h>\r
-#include <inttypes.h>\r
-\r
-/* command handlers */\r
-int handle_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-int handle_flash_banks_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-int handle_flash_info_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-int handle_flash_probe_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-int handle_flash_erase_check_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-int handle_flash_erase_address_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-int handle_flash_protect_check_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-int handle_flash_erase_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-int handle_flash_write_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-int handle_flash_write_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-int handle_flash_write_image_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-int handle_flash_protect_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-int handle_flash_auto_erase_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-flash_bank_t *get_flash_bank_by_addr(target_t *target, u32 addr);\r
-\r
-/* flash drivers\r
- */\r
-extern flash_driver_t lpc2000_flash;\r
-extern flash_driver_t cfi_flash;\r
-extern flash_driver_t at91sam7_flash;\r
-extern flash_driver_t str7x_flash;\r
-extern flash_driver_t str9x_flash;\r
-extern flash_driver_t stellaris_flash;\r
-extern flash_driver_t str9xpec_flash;\r
-extern flash_driver_t stm32x_flash;\r
-extern flash_driver_t tms470_flash;\r
-\r
-flash_driver_t *flash_drivers[] =\r
-{\r
- &lpc2000_flash,\r
- &cfi_flash,\r
- &at91sam7_flash,\r
- &str7x_flash,\r
- &str9x_flash,\r
- &stellaris_flash,\r
- &str9xpec_flash,\r
- &stm32x_flash,\r
- &tms470_flash,\r
- NULL,\r
-};\r
-\r
-flash_bank_t *flash_banks;\r
-static command_t *flash_cmd;\r
-static int auto_erase = 0;\r
-\r
-/* wafer thin wrapper for invoking the flash driver */\r
-static int flash_driver_write(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count)\r
-{\r
- int retval=ERROR_OK;\r
- if (bank->target->state != TARGET_HALTED)\r
- {\r
- ERROR("target not halted - aborting flash write");\r
- retval=ERROR_TARGET_NOT_HALTED;\r
- } else\r
- {\r
- retval=bank->driver->write(bank, buffer, offset, count);\r
- }\r
- if (retval!=ERROR_OK)\r
- {\r
- ERROR("Writing to flash bank at address 0x%08x at offset 0x%8.8x", bank->base, offset);\r
- }\r
- return retval;\r
-}\r
-\r
-static int flash_driver_erase(struct flash_bank_s *bank, int first, int last)\r
-{\r
- int retval=ERROR_OK;\r
- if (bank->target->state != TARGET_HALTED)\r
- {\r
- ERROR("target not halted - aborting flash erase");\r
- retval=ERROR_TARGET_NOT_HALTED;\r
- } else if ((first < 0) || (last < first) || (last >= bank->num_sectors))\r
- {\r
- ERROR("invalid flash sector");\r
- retval=ERROR_FLASH_SECTOR_INVALID;\r
- } else \r
- {\r
- retval=bank->driver->erase(bank, first, last);\r
- }\r
- if (retval!=ERROR_OK)\r
- {\r
- ERROR("Failed erasing banks %d to %d", first, last);\r
- }\r
- return retval;\r
-}\r
-\r
-int flash_driver_protect(struct flash_bank_s *bank, int set, int first, int last)\r
-{\r
- int retval;\r
- if (bank->target->state != TARGET_HALTED)\r
- {\r
- ERROR("target not halted - aborting flash erase");\r
- retval=ERROR_TARGET_NOT_HALTED;\r
- } else if ((first < 0) || (last < first) || (last >= bank->num_sectors))\r
- {\r
- ERROR("invalid flash sector");\r
- retval=ERROR_FLASH_SECTOR_INVALID;\r
- } else \r
- {\r
- retval=bank->driver->protect(bank, set, first, last);\r
- }\r
- if (retval!=ERROR_OK)\r
- {\r
- ERROR("Failed protecting banks %d to %d", first, last);\r
- }\r
- return retval;\r
-}\r
-\r
-\r
-int flash_register_commands(struct command_context_s *cmd_ctx)\r
-{\r
- flash_cmd = register_command(cmd_ctx, NULL, "flash", NULL, COMMAND_ANY, NULL);\r
- \r
- register_command(cmd_ctx, flash_cmd, "bank", handle_flash_bank_command, COMMAND_CONFIG, "flash_bank <driver> <base> <size> <chip_width> <bus_width> <target> [driver_options ...]");\r
- register_command(cmd_ctx, flash_cmd, "auto_erase", handle_flash_auto_erase_command, COMMAND_ANY,\r
- "auto erase flash sectors <on|off>");\r
- return ERROR_OK;\r
-}\r
-\r
-int flash_init_drivers(struct command_context_s *cmd_ctx)\r
-{\r
- if (flash_banks)\r
- {\r
- register_command(cmd_ctx, flash_cmd, "banks", handle_flash_banks_command, COMMAND_EXEC,\r
- "list configured flash banks ");\r
- register_command(cmd_ctx, flash_cmd, "info", handle_flash_info_command, COMMAND_EXEC,\r
- "print info about flash bank <num>");\r
- register_command(cmd_ctx, flash_cmd, "probe", handle_flash_probe_command, COMMAND_EXEC,\r
- "identify flash bank <num>");\r
- register_command(cmd_ctx, flash_cmd, "erase_check", handle_flash_erase_check_command, COMMAND_EXEC,\r
- "check erase state of sectors in flash bank <num>");\r
- register_command(cmd_ctx, flash_cmd, "protect_check", handle_flash_protect_check_command, COMMAND_EXEC,\r
- "check protection state of sectors in flash bank <num>");\r
- register_command(cmd_ctx, flash_cmd, "erase_sector", handle_flash_erase_command, COMMAND_EXEC,\r
- "erase sectors at <bank> <first> <last>");\r
- register_command(cmd_ctx, flash_cmd, "erase_address", handle_flash_erase_address_command, COMMAND_EXEC,\r
- "erase address range <address> <length>");\r
- register_command(cmd_ctx, flash_cmd, "write_bank", handle_flash_write_bank_command, COMMAND_EXEC,\r
- "write binary data to <bank> <file> <offset>");\r
- register_command(cmd_ctx, flash_cmd, "write_image", handle_flash_write_image_command, COMMAND_EXEC,\r
- "write_image <file> [offset] [type]");\r
- register_command(cmd_ctx, flash_cmd, "protect", handle_flash_protect_command, COMMAND_EXEC,\r
- "set protection of sectors at <bank> <first> <last> <on|off>");\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-flash_bank_t *get_flash_bank_by_num_noprobe(int num)\r
-{\r
- flash_bank_t *p;\r
- int i = 0;\r
-\r
- for (p = flash_banks; p; p = p->next)\r
- {\r
- if (i++ == num)\r
- {\r
- return p;\r
- }\r
- }\r
- ERROR("Flash bank %d does not exist", num);\r
- return NULL;\r
-}\r
-\r
-flash_bank_t *get_flash_bank_by_num(int num)\r
-{\r
- flash_bank_t *p = get_flash_bank_by_num_noprobe(num);\r
- int retval;\r
- \r
- if (p == NULL)\r
- return NULL;\r
- \r
- retval = p->driver->auto_probe(p);\r
- \r
- if (retval != ERROR_OK)\r
- {\r
- ERROR("auto_probe failed %d\n", retval);\r
- return NULL;\r
- }\r
- return p;\r
-}\r
-\r
-int handle_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- int i;\r
- int found = 0;\r
- target_t *target;\r
- \r
- if (argc < 6)\r
- {\r
- return ERROR_COMMAND_SYNTAX_ERROR;\r
- }\r
- \r
- if ((target = get_target_by_num(strtoul(args[5], NULL, 0))) == NULL)\r
- {\r
- ERROR("target %lu not defined", strtoul(args[5], NULL, 0));\r
- return ERROR_OK;\r
- }\r
- \r
- for (i = 0; flash_drivers[i]; i++)\r
- {\r
- if (strcmp(args[0], flash_drivers[i]->name) == 0)\r
- {\r
- flash_bank_t *p, *c;\r
- \r
- /* register flash specific commands */\r
- if (flash_drivers[i]->register_commands(cmd_ctx) != ERROR_OK)\r
- {\r
- ERROR("couldn't register '%s' commands", args[0]);\r
- exit(-1);\r
- }\r
- \r
- c = malloc(sizeof(flash_bank_t));\r
- c->target = target;\r
- c->driver = flash_drivers[i];\r
- c->driver_priv = NULL;\r
- c->base = strtoul(args[1], NULL, 0);\r
- c->size = strtoul(args[2], NULL, 0);\r
- c->chip_width = strtoul(args[3], NULL, 0);\r
- c->bus_width = strtoul(args[4], NULL, 0);\r
- c->num_sectors = 0;\r
- c->sectors = NULL;\r
- c->next = NULL;\r
- \r
- if (flash_drivers[i]->flash_bank_command(cmd_ctx, cmd, args, argc, c) != ERROR_OK)\r
- {\r
- ERROR("'%s' driver rejected flash bank at 0x%8.8x", args[0], c->base);\r
- free(c);\r
- return ERROR_OK;\r
- }\r
- \r
- /* put flash bank in linked list */\r
- if (flash_banks)\r
- {\r
- /* find last flash bank */\r
- for (p = flash_banks; p && p->next; p = p->next);\r
- if (p)\r
- p->next = c;\r
- }\r
- else\r
- {\r
- flash_banks = c;\r
- }\r
- \r
- found = 1;\r
- }\r
- }\r
- \r
- /* no matching flash driver found */\r
- if (!found)\r
- {\r
- ERROR("flash driver '%s' not found", args[0]);\r
- exit(-1);\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int handle_flash_banks_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- flash_bank_t *p;\r
- int i = 0;\r
- \r
- if (!flash_banks)\r
- {\r
- command_print(cmd_ctx, "no flash banks configured");\r
- return ERROR_OK;\r
- }\r
- \r
- for (p = flash_banks; p; p = p->next)\r
- {\r
- command_print(cmd_ctx, "#%i: %s at 0x%8.8x, size 0x%8.8x, buswidth %i, chipwidth %i",\r
- i++, p->driver->name, p->base, p->size, p->bus_width, p->chip_width);\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int handle_flash_info_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- flash_bank_t *p;\r
- int i = 0;\r
- int j = 0;\r
- \r
- if (argc != 1)\r
- {\r
- return ERROR_COMMAND_SYNTAX_ERROR;\r
- }\r
- \r
- for (p = flash_banks; p; p = p->next, i++)\r
- {\r
- if (i == strtoul(args[0], NULL, 0))\r
- {\r
- char buf[1024];\r
- \r
- /* attempt auto probe */\r
- p->driver->auto_probe(p);\r
- \r
- command_print(cmd_ctx, "#%i: %s at 0x%8.8x, size 0x%8.8x, buswidth %i, chipwidth %i",\r
- i, p->driver->name, p->base, p->size, p->bus_width, p->chip_width);\r
- for (j = 0; j < p->num_sectors; j++)\r
- {\r
- char *erase_state, *protect_state;\r
- \r
- if (p->sectors[j].is_erased == 0)\r
- erase_state = "not erased";\r
- else if (p->sectors[j].is_erased == 1)\r
- erase_state = "erased";\r
- else\r
- erase_state = "erase state unknown";\r
- \r
- if (p->sectors[j].is_protected == 0)\r
- protect_state = "not protected";\r
- else if (p->sectors[j].is_protected == 1)\r
- protect_state = "protected";\r
- else\r
- protect_state = "protection state unknown";\r
-\r
- command_print(cmd_ctx, "\t#%i: 0x%8.8x (0x%x %ikB) %s, %s",\r
- j, p->sectors[j].offset, p->sectors[j].size, p->sectors[j].size>>10,\r
- erase_state, protect_state);\r
- }\r
- \r
- p->driver->info(p, buf, 1024);\r
- command_print(cmd_ctx, "%s", buf);\r
- }\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int handle_flash_probe_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- flash_bank_t *p;\r
- int retval;\r
- \r
- if (argc != 1)\r
- {\r
- return ERROR_COMMAND_SYNTAX_ERROR;\r
- }\r
- \r
- p = get_flash_bank_by_num_noprobe(strtoul(args[0], NULL, 0));\r
- if (p)\r
- {\r
- if ((retval = p->driver->probe(p)) == ERROR_OK)\r
- {\r
- command_print(cmd_ctx, "flash '%s' found at 0x%8.8x", p->driver->name, p->base);\r
- }\r
- else if (retval == ERROR_FLASH_BANK_INVALID)\r
- {\r
- command_print(cmd_ctx, "probing failed for flash bank '#%s' at 0x%8.8x",\r
- args[0], p->base);\r
- }\r
- else\r
- {\r
- command_print(cmd_ctx, "unknown error when probing flash bank '#%s' at 0x%8.8x",\r
- args[0], p->base);\r
- }\r
- }\r
- else\r
- {\r
- command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int handle_flash_erase_check_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- flash_bank_t *p;\r
- int retval;\r
- \r
- if (argc != 1)\r
- {\r
- return ERROR_COMMAND_SYNTAX_ERROR;\r
- }\r
- \r
- p = get_flash_bank_by_num(strtoul(args[0], NULL, 0));\r
- if (p)\r
- {\r
- if ((retval = p->driver->erase_check(p)) == ERROR_OK)\r
- {\r
- command_print(cmd_ctx, "successfully checked erase state", p->driver->name, p->base);\r
- }\r
- else\r
- {\r
- command_print(cmd_ctx, "unknown error when checking erase state of flash bank #%s at 0x%8.8x",\r
- args[0], p->base);\r
- }\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int handle_flash_erase_address_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- flash_bank_t *p;\r
- int retval;\r
- int address;\r
- int length;\r
- duration_t duration;\r
- char *duration_text;\r
- \r
- target_t *target = get_current_target(cmd_ctx);\r
-\r
- if (argc != 2)\r
- {\r
- return ERROR_COMMAND_SYNTAX_ERROR;\r
- }\r
- \r
- address = strtoul(args[0], NULL, 0);\r
- length = strtoul(args[1], NULL, 0);\r
- if (length <= 0)\r
- {\r
- command_print(cmd_ctx, "Length must be >0");\r
- return ERROR_COMMAND_SYNTAX_ERROR;\r
- }\r
-\r
- p = get_flash_bank_by_addr(target, address);\r
- if (p == NULL)\r
- {\r
- return ERROR_COMMAND_SYNTAX_ERROR;\r
- }\r
- \r
- /* We can't know if we did a resume + halt, in which case we no longer know the erased state */\r
- flash_set_dirty();\r
- \r
- duration_start_measure(&duration);\r
- \r
- if ((retval = flash_erase_address_range(target, address, length)) == ERROR_OK)\r
- {\r
- duration_stop_measure(&duration, &duration_text); \r
- command_print(cmd_ctx, "erased address 0x%8.8x length %i in %s", address, length, duration_text);\r
- free(duration_text);\r
- }\r
- \r
- return retval;\r
-}\r
-\r
-int handle_flash_protect_check_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- flash_bank_t *p;\r
- int retval;\r
- \r
- if (argc != 1)\r
- {\r
- return ERROR_COMMAND_SYNTAX_ERROR;\r
- }\r
- \r
- p = get_flash_bank_by_num(strtoul(args[0], NULL, 0));\r
- if (p)\r
- {\r
- if ((retval = p->driver->protect_check(p)) == ERROR_OK)\r
- {\r
- command_print(cmd_ctx, "successfully checked protect state");\r
- }\r
- else if (retval == ERROR_FLASH_OPERATION_FAILED)\r
- {\r
- command_print(cmd_ctx, "checking protection state failed (possibly unsupported) by flash #%s at 0x%8.8x", args[0], p->base);\r
- }\r
- else\r
- {\r
- command_print(cmd_ctx, "unknown error when checking protection state of flash bank '#%s' at 0x%8.8x", args[0], p->base);\r
- }\r
- }\r
- else\r
- {\r
- return ERROR_COMMAND_SYNTAX_ERROR;\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int handle_flash_erase_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- if (argc > 2)\r
- {\r
- int first = strtoul(args[1], NULL, 0);\r
- int last = strtoul(args[2], NULL, 0);\r
- int retval;\r
- flash_bank_t *p = get_flash_bank_by_num(strtoul(args[0], NULL, 0));\r
- duration_t duration;\r
- char *duration_text;\r
- \r
- duration_start_measure(&duration);\r
- \r
- if (!p)\r
- {\r
- return ERROR_COMMAND_SYNTAX_ERROR;\r
- }\r
- \r
- if ((retval = flash_driver_erase(p, first, last)) == ERROR_OK)\r
- {\r
- duration_stop_measure(&duration, &duration_text); \r
- \r
- command_print(cmd_ctx, "erased sectors %i through %i on flash bank %i in %s", first, last, strtoul(args[0], 0, 0), duration_text);\r
- free(duration_text);\r
- }\r
- }\r
- else\r
- {\r
- return ERROR_COMMAND_SYNTAX_ERROR;\r
- }\r
-\r
- return ERROR_OK;\r
-}\r
-\r
-int handle_flash_protect_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- if (argc > 3)\r
- {\r
- int first = strtoul(args[1], NULL, 0);\r
- int last = strtoul(args[2], NULL, 0);\r
- int set;\r
- int retval;\r
- flash_bank_t *p = get_flash_bank_by_num(strtoul(args[0], NULL, 0));\r
- if (!p)\r
- {\r
- command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);\r
- return ERROR_OK;\r
- }\r
- \r
- if (strcmp(args[3], "on") == 0)\r
- set = 1;\r
- else if (strcmp(args[3], "off") == 0)\r
- set = 0;\r
- else\r
- {\r
- return ERROR_COMMAND_SYNTAX_ERROR;\r
- }\r
- \r
- retval = flash_driver_protect(p, set, first, last);\r
- if (retval == ERROR_OK)\r
- {\r
- command_print(cmd_ctx, "%s protection for sectors %i through %i on flash bank %i", (set) ? "set" : "cleared", first, last, strtoul(args[0], 0, 0));\r
- }\r
- }\r
- else\r
- {\r
- return ERROR_COMMAND_SYNTAX_ERROR;\r
-\r
- }\r
-\r
- return ERROR_OK;\r
-}\r
-\r
-int handle_flash_write_image_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- target_t *target = get_current_target(cmd_ctx);\r
- \r
- image_t image;\r
- u32 written;\r
- \r
- duration_t duration;\r
- char *duration_text;\r
- \r
- int retval;\r
-\r
- if (argc < 1)\r
- {\r
- return ERROR_COMMAND_SYNTAX_ERROR;\r
-\r
- }\r
- \r
- if (!target)\r
- {\r
- ERROR("no target selected");\r
- return ERROR_OK;\r
- }\r
- \r
- duration_start_measure(&duration);\r
- \r
- if (argc >= 2)\r
- {\r
- image.base_address_set = 1;\r
- image.base_address = strtoul(args[1], NULL, 0);\r
- }\r
- else\r
- {\r
- image.base_address_set = 0;\r
- image.base_address = 0x0;\r
- }\r
- \r
- image.start_address_set = 0;\r
-\r
- retval = image_open(&image, args[0], (argc == 3) ? args[2] : NULL);\r
- if (retval != ERROR_OK)\r
- {\r
- command_print(cmd_ctx, "image_open error: %s", image.error_str);\r
- return retval;\r
- }\r
- \r
- retval = flash_write(target, &image, &written, auto_erase);\r
-\r
- if (retval != ERROR_OK)\r
- {\r
- image_close(&image);\r
- return retval;\r
- }\r
- \r
- duration_stop_measure(&duration, &duration_text);\r
- if (retval == ERROR_OK)\r
- {\r
- command_print(cmd_ctx, "wrote %u byte from file %s in %s (%f kb/s)",\r
- written, args[0], duration_text,\r
- (float)written / 1024.0 / ((float)duration.duration.tv_sec + ((float)duration.duration.tv_usec / 1000000.0)));\r
- }\r
- free(duration_text);\r
-\r
- image_close(&image);\r
- \r
- return retval;\r
-}\r
-\r
-int handle_flash_write_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- u32 offset;\r
- u8 *buffer;\r
- u32 buf_cnt;\r
-\r
- fileio_t fileio;\r
- \r
- duration_t duration;\r
- char *duration_text;\r
- \r
- int retval;\r
- flash_bank_t *p;\r
-\r
- if (argc != 3)\r
- {\r
- return ERROR_COMMAND_SYNTAX_ERROR;\r
- }\r
- \r
- duration_start_measure(&duration);\r
- \r
- offset = strtoul(args[2], NULL, 0);\r
- p = get_flash_bank_by_num(strtoul(args[0], NULL, 0));\r
- if (!p)\r
- {\r
- command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);\r
- return ERROR_OK;\r
- }\r
- \r
- if (fileio_open(&fileio, args[1], FILEIO_READ, FILEIO_BINARY) != ERROR_OK)\r
- {\r
- command_print(cmd_ctx, "flash write_binary error: %s", fileio.error_str);\r
- return ERROR_OK;\r
- }\r
- \r
- buffer = malloc(fileio.size);\r
- if (fileio_read(&fileio, fileio.size, buffer, &buf_cnt) != ERROR_OK)\r
- {\r
- command_print(cmd_ctx, "flash write_binary error: %s", fileio.error_str);\r
- return ERROR_OK;\r
- }\r
- \r
- retval = flash_driver_write(p, buffer, offset, buf_cnt);\r
- \r
- free(buffer);\r
- \r
- duration_stop_measure(&duration, &duration_text);\r
- if (retval!=ERROR_OK)\r
- {\r
- command_print(cmd_ctx, "wrote %"PRIi64" byte from file %s to flash bank %i at offset 0x%8.8x in %s (%f kb/s)",\r
- fileio.size, args[1], strtoul(args[0], NULL, 0), offset, duration_text,\r
- (float)fileio.size / 1024.0 / ((float)duration.duration.tv_sec + ((float)duration.duration.tv_usec / 1000000.0)));\r
- }\r
- free(duration_text);\r
-\r
- fileio_close(&fileio);\r
- \r
- return retval;\r
-}\r
-\r
-void flash_set_dirty(void)\r
-{\r
- flash_bank_t *c;\r
- int i;\r
- \r
- /* set all flash to require erasing */\r
- for (c = flash_banks; c; c = c->next)\r
- {\r
- for (i = 0; i < c->num_sectors; i++)\r
- {\r
- c->sectors[i].is_erased = 0; \r
- }\r
- }\r
-}\r
-\r
-/* lookup flash bank by address */\r
-flash_bank_t *get_flash_bank_by_addr(target_t *target, u32 addr)\r
-{\r
- flash_bank_t *c;\r
-\r
- /* cycle through bank list */\r
- for (c = flash_banks; c; c = c->next)\r
- {\r
- int retval;\r
- retval = c->driver->auto_probe(c);\r
- \r
- if (retval != ERROR_OK)\r
- {\r
- ERROR("auto_probe failed %d\n", retval);\r
- return NULL;\r
- }\r
- /* check whether address belongs to this flash bank */\r
- if ((addr >= c->base) && (addr < c->base + c->size) && target == c->target)\r
- return c;\r
- }\r
- ERROR("No flash at address 0x%08x\n", addr);\r
- return NULL;\r
-}\r
-\r
-/* erase given flash region, selects proper bank according to target and address */\r
-int flash_erase_address_range(target_t *target, u32 addr, u32 length)\r
-{\r
- flash_bank_t *c;\r
- int first = -1;\r
- int last = -1;\r
- int i;\r
- \r
- if ((c = get_flash_bank_by_addr(target, addr)) == NULL)\r
- return ERROR_FLASH_DST_OUT_OF_BANK; /* no corresponding bank found */\r
-\r
- if (c->size == 0 || c->num_sectors == 0)\r
- return ERROR_FLASH_BANK_INVALID;\r
- \r
- if (length == 0)\r
- {\r
- /* special case, erase whole bank when length is zero */\r
- if (addr != c->base)\r
- return ERROR_FLASH_DST_BREAKS_ALIGNMENT;\r
- \r
- return flash_driver_erase(c, 0, c->num_sectors - 1);\r
- }\r
-\r
- /* check whether it fits */\r
- if (addr + length > c->base + c->size)\r
- return ERROR_FLASH_DST_BREAKS_ALIGNMENT;\r
- \r
- addr -= c->base;\r
- \r
- for (i = 0; i < c->num_sectors; i++)\r
- { \r
- /* check whether sector overlaps with the given range and is not yet erased */\r
- if (addr < c->sectors[i].offset + c->sectors[i].size && addr + length > c->sectors[i].offset && c->sectors[i].is_erased != 1) {\r
- /* if first is not set yet then this is the first sector */\r
- if (first == -1)\r
- first = i;\r
- last = i; /* and it is the last one so far in any case */\r
- }\r
- }\r
- \r
- if( first == -1 || last == -1 )\r
- return ERROR_OK;\r
- \r
- return flash_driver_erase(c, first, last);\r
-}\r
-\r
-/* write (optional verify) an image to flash memory of the given target */\r
-int flash_write(target_t *target, image_t *image, u32 *written, int erase)\r
-{\r
- int retval;\r
-\r
- int section;\r
- u32 section_offset;\r
- flash_bank_t *c;\r
- \r
- section = 0;\r
- section_offset = 0;\r
-\r
- if (written)\r
- *written = 0;\r
- \r
- if (erase)\r
- {\r
- /* assume all sectors need erasing - stops any problems\r
- * when flash_write is called multiple times */\r
- \r
- flash_set_dirty();\r
- }\r
- \r
- /* loop until we reach end of the image */\r
- while (section < image->num_sections)\r
- {\r
- u32 buffer_size;\r
- u8 *buffer;\r
- int section_first;\r
- int section_last;\r
- u32 run_address = image->sections[section].base_address + section_offset;\r
- u32 run_size = image->sections[section].size - section_offset;\r
-\r
- if (image->sections[section].size == 0)\r
- {\r
- WARNING("empty section %d", section);\r
- section++;\r
- section_offset = 0;\r
- continue;\r
- }\r
-\r
- /* find the corresponding flash bank */\r
- if ((c = get_flash_bank_by_addr(target, run_address)) == NULL)\r
- {\r
- section++; /* and skip it */\r
- section_offset = 0;\r
- continue;\r
- }\r
-\r
- /* collect consecutive sections which fall into the same bank */\r
- section_first = section;\r
- section_last = section;\r
- while ((run_address + run_size < c->base + c->size)\r
- && (section_last + 1 < image->num_sections))\r
- {\r
- if (image->sections[section_last + 1].base_address < (run_address + run_size))\r
- {\r
- DEBUG("section %d out of order(very slightly surprising, but supported)", section_last + 1);\r
- break;\r
- }\r
- if (image->sections[section_last + 1].base_address != (run_address + run_size))\r
- break;\r
- run_size += image->sections[++section_last].size;\r
- }\r
-\r
- /* fit the run into bank constraints */\r
- if (run_address + run_size > c->base + c->size)\r
- run_size = c->base + c->size - run_address;\r
-\r
- /* allocate buffer */\r
- buffer = malloc(run_size);\r
- buffer_size = 0;\r
-\r
- /* read sections to the buffer */\r
- while (buffer_size < run_size)\r
- {\r
- u32 size_read;\r
- \r
- if (buffer_size - run_size <= image->sections[section].size - section_offset)\r
- size_read = buffer_size - run_size;\r
- else\r
- size_read = image->sections[section].size - section_offset;\r
- \r
- if ((retval = image_read_section(image, section, section_offset,\r
- size_read, buffer + buffer_size, &size_read)) != ERROR_OK || size_read == 0)\r
- {\r
- free(buffer);\r
- \r
- return retval;\r
- }\r
-\r
- buffer_size += size_read;\r
- section_offset += size_read;\r
-\r
- if (section_offset >= image->sections[section].size)\r
- {\r
- section++;\r
- section_offset = 0;\r
- }\r
- }\r
-\r
- retval = ERROR_OK;\r
- \r
- if (erase)\r
- {\r
- /* calculate and erase sectors */\r
- retval = flash_erase_address_range( target, run_address, run_size );\r
- }\r
- \r
- if (retval == ERROR_OK)\r
- {\r
- /* write flash sectors */\r
- retval = flash_driver_write(c, buffer, run_address - c->base, run_size);\r
- }\r
- \r
- free(buffer);\r
-\r
- if (retval != ERROR_OK)\r
- {\r
- return retval; /* abort operation */\r
- }\r
-\r
- if (written != NULL)\r
- *written += run_size; /* add run size to total written counter */\r
- }\r
-\r
- return ERROR_OK;\r
-}\r
-\r
-int handle_flash_auto_erase_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- if (argc != 1)\r
- {\r
- return ERROR_COMMAND_SYNTAX_ERROR;\r
-\r
- }\r
- \r
- if (strcmp(args[0], "on") == 0)\r
- auto_erase = 1;\r
- else if (strcmp(args[0], "off") == 0)\r
- auto_erase = 0;\r
- else \r
- return ERROR_COMMAND_SYNTAX_ERROR;\r
- \r
- return ERROR_OK;\r
-}\r
+/***************************************************************************
+ * Copyright (C) 2005 by Dominic Rath *
+ * Dominic.Rath@gmx.de *
+ * *
+ * Copyright (C) 2007,2008 Øyvind Harboe *
+ * oyvind.harboe@zylin.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "flash.h"
+#include "command.h"
+#include "target.h"
+#include "time_support.h"
+#include "fileio.h"
+#include "image.h"
+#include "log.h"
+#include "armv4_5.h"
+#include "algorithm.h"
+#include "binarybuffer.h"
+#include "armv7m.h"
+
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <inttypes.h>
+
+/* command handlers */
+int handle_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+int handle_flash_info_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+int handle_flash_probe_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+int handle_flash_erase_check_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+int handle_flash_erase_address_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+int handle_flash_protect_check_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+int handle_flash_erase_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+int handle_flash_write_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+int handle_flash_write_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+int handle_flash_write_image_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+int handle_flash_fill_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+int handle_flash_protect_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+flash_bank_t *get_flash_bank_by_addr(target_t *target, u32 addr);
+
+/* flash drivers
+ */
+extern flash_driver_t lpc2000_flash;
+extern flash_driver_t cfi_flash;
+extern flash_driver_t at91sam7_flash;
+extern flash_driver_t str7x_flash;
+extern flash_driver_t str9x_flash;
+extern flash_driver_t stellaris_flash;
+extern flash_driver_t str9xpec_flash;
+extern flash_driver_t stm32x_flash;
+extern flash_driver_t tms470_flash;
+extern flash_driver_t ecosflash_flash;
+extern flash_driver_t lpc288x_flash;
+extern flash_driver_t ocl_flash;
+
+flash_driver_t *flash_drivers[] =
+{
+ &lpc2000_flash,
+ &cfi_flash,
+ &at91sam7_flash,
+ &str7x_flash,
+ &str9x_flash,
+ &stellaris_flash,
+ &str9xpec_flash,
+ &stm32x_flash,
+ &tms470_flash,
+ &ecosflash_flash,
+ &lpc288x_flash,
+ &ocl_flash,
+ NULL,
+};
+
+flash_bank_t *flash_banks;
+static command_t *flash_cmd;
+
+/* wafer thin wrapper for invoking the flash driver */
+static int flash_driver_write(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count)
+{
+ int retval;
+
+ retval=bank->driver->write(bank, buffer, offset, count);
+ if (retval!=ERROR_OK)
+ {
+ LOG_ERROR("error writing to flash at address 0x%08x at offset 0x%8.8x (%d)", bank->base, offset, retval);
+ }
+
+ return retval;
+}
+
+static int flash_driver_erase(struct flash_bank_s *bank, int first, int last)
+{
+ int retval;
+
+ retval=bank->driver->erase(bank, first, last);
+ if (retval!=ERROR_OK)
+ {
+ LOG_ERROR("failed erasing sectors %d to %d (%d)", first, last, retval);
+ }
+
+ return retval;
+}
+
+int flash_driver_protect(struct flash_bank_s *bank, int set, int first, int last)
+{
+ int retval;
+
+ retval=bank->driver->protect(bank, set, first, last);
+ if (retval!=ERROR_OK)
+ {
+ LOG_ERROR("failed setting protection for areas %d to %d (%d)", first, last, retval);
+ }
+
+ return retval;
+}
+
+int flash_register_commands(struct command_context_s *cmd_ctx)
+{
+ flash_cmd = register_command(cmd_ctx, NULL, "flash", NULL, COMMAND_ANY, NULL);
+
+ register_command(cmd_ctx, flash_cmd, "bank", handle_flash_bank_command, COMMAND_CONFIG, "flash_bank <driver> <base> <size> <chip_width> <bus_width> <target> [driver_options ...]");
+ return ERROR_OK;
+}
+
+static int jim_flash_banks(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
+{
+ flash_bank_t *p;
+
+ if (argc != 1) {
+ Jim_WrongNumArgs(interp, 1, argv, "no arguments to flash_banks command");
+ return JIM_ERR;
+ }
+
+ if (!flash_banks)
+ {
+ return JIM_ERR;
+ }
+
+ Jim_Obj *list=Jim_NewListObj(interp, NULL, 0);
+ for (p = flash_banks; p; p = p->next)
+ {
+ Jim_Obj *elem=Jim_NewListObj(interp, NULL, 0);
+
+ Jim_ListAppendElement(interp, elem, Jim_NewStringObj(interp, "name", -1));
+ Jim_ListAppendElement(interp, elem, Jim_NewStringObj(interp, p->driver->name, -1));
+ Jim_ListAppendElement(interp, elem, Jim_NewStringObj(interp, "base", -1));
+ Jim_ListAppendElement(interp, elem, Jim_NewIntObj(interp, p->base));
+ Jim_ListAppendElement(interp, elem, Jim_NewStringObj(interp, "size", -1));
+ Jim_ListAppendElement(interp, elem, Jim_NewIntObj(interp, p->size));
+ Jim_ListAppendElement(interp, elem, Jim_NewStringObj(interp, "bus_width", -1));
+ Jim_ListAppendElement(interp, elem, Jim_NewIntObj(interp, p->bus_width));
+ Jim_ListAppendElement(interp, elem, Jim_NewStringObj(interp, "chip_width", -1));
+ Jim_ListAppendElement(interp, elem, Jim_NewIntObj(interp, p->chip_width));
+
+ Jim_ListAppendElement(interp, list, elem);
+ }
+
+ Jim_SetResult(interp, list);
+
+ return JIM_OK;
+}
+
+int flash_init_drivers(struct command_context_s *cmd_ctx)
+{
+ if (flash_banks)
+ {
+ register_jim(cmd_ctx, "ocd_flash_banks", jim_flash_banks, "return information about the flash banks");
+
+ register_command(cmd_ctx, flash_cmd, "info", handle_flash_info_command, COMMAND_EXEC,
+ "print info about flash bank <num>");
+ register_command(cmd_ctx, flash_cmd, "probe", handle_flash_probe_command, COMMAND_EXEC,
+ "identify flash bank <num>");
+ register_command(cmd_ctx, flash_cmd, "erase_check", handle_flash_erase_check_command, COMMAND_EXEC,
+ "check erase state of sectors in flash bank <num>");
+ register_command(cmd_ctx, flash_cmd, "protect_check", handle_flash_protect_check_command, COMMAND_EXEC,
+ "check protection state of sectors in flash bank <num>");
+ register_command(cmd_ctx, flash_cmd, "erase_sector", handle_flash_erase_command, COMMAND_EXEC,
+ "erase sectors at <bank> <first> <last>");
+ register_command(cmd_ctx, flash_cmd, "erase_address", handle_flash_erase_address_command, COMMAND_EXEC,
+ "erase address range <address> <length>");
+
+ register_command(cmd_ctx, flash_cmd, "fillw", handle_flash_fill_command, COMMAND_EXEC,
+ "fill with pattern <address> <word_pattern> <count>");
+ register_command(cmd_ctx, flash_cmd, "fillh", handle_flash_fill_command, COMMAND_EXEC,
+ "fill with pattern <address> <halfword_pattern> <count>");
+ register_command(cmd_ctx, flash_cmd, "fillb", handle_flash_fill_command, COMMAND_EXEC,
+ "fill with pattern <address> <byte_pattern> <count>");
+
+ register_command(cmd_ctx, flash_cmd, "write_bank", handle_flash_write_bank_command, COMMAND_EXEC,
+ "write binary data to <bank> <file> <offset>");
+ register_command(cmd_ctx, flash_cmd, "write_image", handle_flash_write_image_command, COMMAND_EXEC,
+ "write_image [erase] <file> [offset] [type]");
+ register_command(cmd_ctx, flash_cmd, "protect", handle_flash_protect_command, COMMAND_EXEC,
+ "set protection of sectors at <bank> <first> <last> <on|off>");
+ }
+
+ return ERROR_OK;
+}
+
+flash_bank_t *get_flash_bank_by_num_noprobe(int num)
+{
+ flash_bank_t *p;
+ int i = 0;
+
+ for (p = flash_banks; p; p = p->next)
+ {
+ if (i++ == num)
+ {
+ return p;
+ }
+ }
+ LOG_ERROR("flash bank %d does not exist", num);
+ return NULL;
+}
+
+int flash_get_bank_count(void)
+{
+ flash_bank_t *p;
+ int i = 0;
+ for (p = flash_banks; p; p = p->next)
+ {
+ i++;
+ }
+ return i;
+}
+
+flash_bank_t *get_flash_bank_by_num(int num)
+{
+ flash_bank_t *p = get_flash_bank_by_num_noprobe(num);
+ int retval;
+
+ if (p == NULL)
+ return NULL;
+
+ retval = p->driver->auto_probe(p);
+
+ if (retval != ERROR_OK)
+ {
+ LOG_ERROR("auto_probe failed %d\n", retval);
+ return NULL;
+ }
+ return p;
+}
+
+int handle_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ int i;
+ int found = 0;
+ target_t *target;
+
+ if (argc < 6)
+ {
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ if ((target = get_target_by_num(strtoul(args[5], NULL, 0))) == NULL)
+ {
+ LOG_ERROR("target %lu not defined", strtoul(args[5], NULL, 0));
+ return ERROR_OK;
+ }
+
+ for (i = 0; flash_drivers[i]; i++)
+ {
+ if (strcmp(args[0], flash_drivers[i]->name) == 0)
+ {
+ flash_bank_t *p, *c;
+
+ /* register flash specific commands */
+ if (flash_drivers[i]->register_commands(cmd_ctx) != ERROR_OK)
+ {
+ LOG_ERROR("couldn't register '%s' commands", args[0]);
+ exit(-1);
+ }
+
+ c = malloc(sizeof(flash_bank_t));
+ c->target = target;
+ c->driver = flash_drivers[i];
+ c->driver_priv = NULL;
+ c->base = strtoul(args[1], NULL, 0);
+ c->size = strtoul(args[2], NULL, 0);
+ c->chip_width = strtoul(args[3], NULL, 0);
+ c->bus_width = strtoul(args[4], NULL, 0);
+ c->num_sectors = 0;
+ c->sectors = NULL;
+ c->next = NULL;
+
+ if (flash_drivers[i]->flash_bank_command(cmd_ctx, cmd, args, argc, c) != ERROR_OK)
+ {
+ LOG_ERROR("'%s' driver rejected flash bank at 0x%8.8x", args[0], c->base);
+ free(c);
+ return ERROR_OK;
+ }
+
+ /* put flash bank in linked list */
+ if (flash_banks)
+ {
+ /* find last flash bank */
+ for (p = flash_banks; p && p->next; p = p->next);
+ if (p)
+ p->next = c;
+ }
+ else
+ {
+ flash_banks = c;
+ }
+
+ found = 1;
+ }
+ }
+
+ /* no matching flash driver found */
+ if (!found)
+ {
+ LOG_ERROR("flash driver '%s' not found", args[0]);
+ exit(-1);
+ }
+
+ return ERROR_OK;
+}
+
+int handle_flash_info_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ flash_bank_t *p;
+ int i = 0;
+ int j = 0;
+ int retval;
+
+ if (argc != 1)
+ {
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ for (p = flash_banks; p; p = p->next, i++)
+ {
+ if (i == strtoul(args[0], NULL, 0))
+ {
+ char buf[1024];
+
+ /* attempt auto probe */
+ if ((retval = p->driver->auto_probe(p)) != ERROR_OK)
+ return retval;
+
+ command_print(cmd_ctx, "#%i: %s at 0x%8.8x, size 0x%8.8x, buswidth %i, chipwidth %i",
+ i, p->driver->name, p->base, p->size, p->bus_width, p->chip_width);
+ for (j = 0; j < p->num_sectors; j++)
+ {
+ char *protect_state;
+
+ if (p->sectors[j].is_protected == 0)
+ protect_state = "not protected";
+ else if (p->sectors[j].is_protected == 1)
+ protect_state = "protected";
+ else
+ protect_state = "protection state unknown";
+
+ command_print(cmd_ctx, "\t#%i: 0x%8.8x (0x%x %ikB) %s",
+ j, p->sectors[j].offset, p->sectors[j].size, p->sectors[j].size>>10,
+ protect_state);
+ }
+
+ *buf = '\0'; /* initialize buffer, otherwise it migh contain garbage if driver function fails */
+ retval = p->driver->info(p, buf, sizeof(buf));
+ command_print(cmd_ctx, "%s", buf);
+ if (retval != ERROR_OK)
+ LOG_ERROR("error retrieving flash info (%d)", retval);
+ }
+ }
+
+ return ERROR_OK;
+}
+
+int handle_flash_probe_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ flash_bank_t *p;
+ int retval;
+
+ if (argc != 1)
+ {
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ p = get_flash_bank_by_num_noprobe(strtoul(args[0], NULL, 0));
+ if (p)
+ {
+ if ((retval = p->driver->probe(p)) == ERROR_OK)
+ {
+ command_print(cmd_ctx, "flash '%s' found at 0x%8.8x", p->driver->name, p->base);
+ }
+ else if (retval == ERROR_FLASH_BANK_INVALID)
+ {
+ command_print(cmd_ctx, "probing failed for flash bank '#%s' at 0x%8.8x",
+ args[0], p->base);
+ }
+ else
+ {
+ command_print(cmd_ctx, "unknown error when probing flash bank '#%s' at 0x%8.8x",
+ args[0], p->base);
+ }
+ }
+ else
+ {
+ command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);
+ }
+
+ return ERROR_OK;
+}
+
+int handle_flash_erase_check_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ flash_bank_t *p;
+ int retval;
+
+ if (argc != 1)
+ {
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ p = get_flash_bank_by_num(strtoul(args[0], NULL, 0));
+ if (p)
+ {
+ int j;
+ if ((retval = p->driver->erase_check(p)) == ERROR_OK)
+ {
+ command_print(cmd_ctx, "successfully checked erase state", p->driver->name, p->base);
+ }
+ else
+ {
+ command_print(cmd_ctx, "unknown error when checking erase state of flash bank #%s at 0x%8.8x",
+ args[0], p->base);
+ }
+
+ for (j = 0; j < p->num_sectors; j++)
+ {
+ char *erase_state;
+
+ if (p->sectors[j].is_erased == 0)
+ erase_state = "not erased";
+ else if (p->sectors[j].is_erased == 1)
+ erase_state = "erased";
+ else
+ erase_state = "erase state unknown";
+
+ command_print(cmd_ctx, "\t#%i: 0x%8.8x (0x%x %ikB) %s",
+ j, p->sectors[j].offset, p->sectors[j].size, p->sectors[j].size>>10,
+ erase_state);
+ }
+
+ }
+
+ return ERROR_OK;
+}
+
+int handle_flash_erase_address_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ flash_bank_t *p;
+ int retval;
+ int address;
+ int length;
+ duration_t duration;
+ char *duration_text;
+
+ target_t *target = get_current_target(cmd_ctx);
+
+ if (argc != 2)
+ {
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ address = strtoul(args[0], NULL, 0);
+ length = strtoul(args[1], NULL, 0);
+ if (length <= 0)
+ {
+ command_print(cmd_ctx, "Length must be >0");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ p = get_flash_bank_by_addr(target, address);
+ if (p == NULL)
+ {
+ return ERROR_FAIL;
+ }
+
+ /* We can't know if we did a resume + halt, in which case we no longer know the erased state */
+ flash_set_dirty();
+
+ duration_start_measure(&duration);
+
+ if ((retval = flash_erase_address_range(target, address, length)) == ERROR_OK)
+ {
+ duration_stop_measure(&duration, &duration_text);
+ command_print(cmd_ctx, "erased address 0x%8.8x length %i in %s", address, length, duration_text);
+ free(duration_text);
+ }
+
+ return retval;
+}
+
+int handle_flash_protect_check_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ flash_bank_t *p;
+ int retval;
+
+ if (argc != 1)
+ {
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ p = get_flash_bank_by_num(strtoul(args[0], NULL, 0));
+ if (p)
+ {
+ if ((retval = p->driver->protect_check(p)) == ERROR_OK)
+ {
+ command_print(cmd_ctx, "successfully checked protect state");
+ }
+ else if (retval == ERROR_FLASH_OPERATION_FAILED)
+ {
+ command_print(cmd_ctx, "checking protection state failed (possibly unsupported) by flash #%s at 0x%8.8x", args[0], p->base);
+ }
+ else
+ {
+ command_print(cmd_ctx, "unknown error when checking protection state of flash bank '#%s' at 0x%8.8x", args[0], p->base);
+ }
+ }
+ else
+ {
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ return ERROR_OK;
+}
+
+int handle_flash_erase_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ if (argc > 2)
+ {
+ int first = strtoul(args[1], NULL, 0);
+ int last = strtoul(args[2], NULL, 0);
+ int retval;
+ flash_bank_t *p = get_flash_bank_by_num(strtoul(args[0], NULL, 0));
+ duration_t duration;
+ char *duration_text;
+
+ duration_start_measure(&duration);
+
+ if (!p)
+ {
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ if ((retval = flash_driver_erase(p, first, last)) == ERROR_OK)
+ {
+ duration_stop_measure(&duration, &duration_text);
+
+ command_print(cmd_ctx, "erased sectors %i through %i on flash bank %i in %s", first, last, strtoul(args[0], 0, 0), duration_text);
+ free(duration_text);
+ }
+ }
+ else
+ {
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ return ERROR_OK;
+}
+
+int handle_flash_protect_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ if (argc > 3)
+ {
+ int first = strtoul(args[1], NULL, 0);
+ int last = strtoul(args[2], NULL, 0);
+ int set;
+ int retval;
+ flash_bank_t *p = get_flash_bank_by_num(strtoul(args[0], NULL, 0));
+ if (!p)
+ {
+ command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);
+ return ERROR_OK;
+ }
+
+ if (strcmp(args[3], "on") == 0)
+ set = 1;
+ else if (strcmp(args[3], "off") == 0)
+ set = 0;
+ else
+ {
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ retval = flash_driver_protect(p, set, first, last);
+ if (retval == ERROR_OK)
+ {
+ command_print(cmd_ctx, "%s protection for sectors %i through %i on flash bank %i", (set) ? "set" : "cleared", first, last, strtoul(args[0], 0, 0));
+ }
+ }
+ else
+ {
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ }
+
+ return ERROR_OK;
+}
+
+int handle_flash_write_image_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ target_t *target = get_current_target(cmd_ctx);
+
+ image_t image;
+ u32 written;
+
+ duration_t duration;
+ char *duration_text;
+
+ int retval;
+
+ if (argc < 1)
+ {
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ /* flash auto-erase is disabled by default*/
+ int auto_erase = 0;
+
+ if (strcmp(args[0], "erase")==0)
+ {
+ auto_erase = 1;
+ args++;
+ argc--;
+ command_print(cmd_ctx, "auto erase enabled");
+ }
+
+
+ if (argc < 1)
+ {
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ if (!target)
+ {
+ LOG_ERROR("no target selected");
+ return ERROR_FAIL;
+ }
+
+ duration_start_measure(&duration);
+
+ if (argc >= 2)
+ {
+ image.base_address_set = 1;
+ image.base_address = strtoul(args[1], NULL, 0);
+ }
+ else
+ {
+ image.base_address_set = 0;
+ image.base_address = 0x0;
+ }
+
+ image.start_address_set = 0;
+
+ retval = image_open(&image, args[0], (argc == 3) ? args[2] : NULL);
+ if (retval != ERROR_OK)
+ {
+ return retval;
+ }
+
+ retval = flash_write(target, &image, &written, auto_erase);
+ if (retval != ERROR_OK)
+ {
+ image_close(&image);
+ return retval;
+ }
+
+ duration_stop_measure(&duration, &duration_text);
+ if (retval == ERROR_OK)
+ {
+ command_print(cmd_ctx, "wrote %u byte from file %s in %s (%f kb/s)",
+ written, args[0], duration_text,
+ (float)written / 1024.0 / ((float)duration.duration.tv_sec + ((float)duration.duration.tv_usec / 1000000.0)));
+ }
+ free(duration_text);
+
+ image_close(&image);
+
+ return retval;
+}
+
+int handle_flash_fill_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ int err = ERROR_OK;
+ u32 address;
+ u32 pattern;
+ u32 count;
+ u8 chunk[1024];
+ u32 wrote = 0;
+ int chunk_count;
+ char *duration_text;
+ duration_t duration;
+ target_t *target = get_current_target(cmd_ctx);
+ u32 i;
+ int wordsize;
+
+ if (argc != 3)
+ {
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ address = strtoul(args[0], NULL, 0);
+ pattern = strtoul(args[1], NULL, 0);
+ count = strtoul(args[2], NULL, 0);
+
+ if(count == 0)
+ return ERROR_OK;
+
+
+ switch(cmd[4])
+ {
+ case 'w':
+ wordsize=4;
+ break;
+ case 'h':
+ wordsize=2;
+ break;
+ case 'b':
+ wordsize=1;
+ break;
+ default:
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ chunk_count = MIN(count, (1024 / wordsize));
+ switch(wordsize)
+ {
+ case 4:
+ for(i = 0; i < chunk_count; i++)
+ {
+ target_buffer_set_u32(target, chunk + i * wordsize, pattern);
+ }
+ break;
+ case 2:
+ for(i = 0; i < chunk_count; i++)
+ {
+ target_buffer_set_u16(target, chunk + i * wordsize, pattern);
+ }
+ break;
+ case 1:
+ memset(chunk, pattern, chunk_count);
+ break;
+ default:
+ LOG_ERROR("BUG: can't happen");
+ exit(-1);
+ }
+
+ duration_start_measure(&duration);
+
+ flash_set_dirty();
+ err = flash_erase_address_range( target, address, count*wordsize );
+ if (err == ERROR_OK)
+ {
+ for (wrote=0; wrote<(count*wordsize); wrote+=sizeof(chunk))
+ {
+ int cur_size = MIN( (count*wordsize - wrote) , 1024 );
+ if (err == ERROR_OK)
+ {
+ flash_bank_t *bank;
+ bank = get_flash_bank_by_addr(target, address);
+ if(bank == NULL)
+ {
+ err = ERROR_FAIL;
+ break;
+ }
+ err = flash_driver_write(bank, chunk, address - bank->base + wrote, cur_size);
+ wrote += cur_size;
+ }
+ if (err!=ERROR_OK)
+ break;
+ }
+ }
+
+ duration_stop_measure(&duration, &duration_text);
+
+ if(err == ERROR_OK)
+ {
+ float speed;
+ speed=wrote / 1024.0;
+ speed/=((float)duration.duration.tv_sec + ((float)duration.duration.tv_usec / 1000000.0));
+ command_print(cmd_ctx, "wrote %d bytes to 0x%8.8x in %s (%f kb/s)",
+ count*wordsize, address, duration_text,
+ speed);
+ }
+ free(duration_text);
+ return ERROR_OK;
+}
+
+int handle_flash_write_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ u32 offset;
+ u8 *buffer;
+ u32 buf_cnt;
+
+ fileio_t fileio;
+
+ duration_t duration;
+ char *duration_text;
+
+ int retval;
+ flash_bank_t *p;
+
+ if (argc != 3)
+ {
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ duration_start_measure(&duration);
+
+ offset = strtoul(args[2], NULL, 0);
+ p = get_flash_bank_by_num(strtoul(args[0], NULL, 0));
+ if (!p)
+ {
+ command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);
+ return ERROR_OK;
+ }
+
+ if (fileio_open(&fileio, args[1], FILEIO_READ, FILEIO_BINARY) != ERROR_OK)
+ {
+ return ERROR_OK;
+ }
+
+ buffer = malloc(fileio.size);
+ if (fileio_read(&fileio, fileio.size, buffer, &buf_cnt) != ERROR_OK)
+ {
+ free(buffer);
+ fileio_close(&fileio);
+ return ERROR_OK;
+ }
+
+ retval = flash_driver_write(p, buffer, offset, buf_cnt);
+
+ free(buffer);
+ buffer = NULL;
+
+ duration_stop_measure(&duration, &duration_text);
+ if (retval!=ERROR_OK)
+ {
+ command_print(cmd_ctx, "wrote %"PRIi64" byte from file %s to flash bank %i at offset 0x%8.8x in %s (%f kb/s)",
+ fileio.size, args[1], strtoul(args[0], NULL, 0), offset, duration_text,
+ (float)fileio.size / 1024.0 / ((float)duration.duration.tv_sec + ((float)duration.duration.tv_usec / 1000000.0)));
+ }
+ free(duration_text);
+
+ fileio_close(&fileio);
+
+ return retval;
+}
+
+void flash_set_dirty(void)
+{
+ flash_bank_t *c;
+ int i;
+
+ /* set all flash to require erasing */
+ for (c = flash_banks; c; c = c->next)
+ {
+ for (i = 0; i < c->num_sectors; i++)
+ {
+ c->sectors[i].is_erased = 0;
+ }
+ }
+}
+
+/* lookup flash bank by address */
+flash_bank_t *get_flash_bank_by_addr(target_t *target, u32 addr)
+{
+ flash_bank_t *c;
+
+ /* cycle through bank list */
+ for (c = flash_banks; c; c = c->next)
+ {
+ int retval;
+ retval = c->driver->auto_probe(c);
+
+ if (retval != ERROR_OK)
+ {
+ LOG_ERROR("auto_probe failed %d\n", retval);
+ return NULL;
+ }
+ /* check whether address belongs to this flash bank */
+ if ((addr >= c->base) && (addr < c->base + c->size) && target == c->target)
+ return c;
+ }
+ LOG_ERROR("No flash at address 0x%08x\n", addr);
+ return NULL;
+}
+
+/* erase given flash region, selects proper bank according to target and address */
+int flash_erase_address_range(target_t *target, u32 addr, u32 length)
+{
+ flash_bank_t *c;
+ int first = -1;
+ 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 */
+
+ if (c->size == 0 || c->num_sectors == 0)
+ {
+ LOG_ERROR("Bank is invalid");
+ return ERROR_FLASH_BANK_INVALID;
+ }
+
+ if (length == 0)
+ {
+ /* special case, erase whole bank when length is zero */
+ if (addr != c->base)
+ return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
+
+ return flash_driver_erase(c, 0, c->num_sectors - 1);
+ }
+
+ /* check whether it fits */
+ if (addr + length > c->base + c->size)
+ return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
+
+ addr -= c->base;
+
+ for (i = 0; i < c->num_sectors; i++)
+ {
+ /* check whether sector overlaps with the given range and is not yet erased */
+ if (addr < c->sectors[i].offset + c->sectors[i].size && addr + length > c->sectors[i].offset && c->sectors[i].is_erased != 1) {
+ /* if first is not set yet then this is the first sector */
+ if (first == -1)
+ first = i;
+ last = i; /* and it is the last one so far in any case */
+ }
+ }
+
+ if( first == -1 || last == -1 )
+ return ERROR_OK;
+
+ return flash_driver_erase(c, first, last);
+}
+
+/* write (optional verify) an image to flash memory of the given target */
+int flash_write(target_t *target, image_t *image, u32 *written, int erase)
+{
+ int retval=ERROR_OK;
+
+ int section;
+ u32 section_offset;
+ flash_bank_t *c;
+ int *padding;
+
+ section = 0;
+ section_offset = 0;
+
+ if (written)
+ *written = 0;
+
+ if (erase)
+ {
+ /* assume all sectors need erasing - stops any problems
+ * when flash_write is called multiple times */
+
+ flash_set_dirty();
+ }
+
+ /* allocate padding array */
+ padding = malloc(image->num_sections * sizeof(padding));
+
+ /* loop until we reach end of the image */
+ while (section < image->num_sections)
+ {
+ u32 buffer_size;
+ u8 *buffer;
+ int section_first;
+ int section_last;
+ u32 run_address = image->sections[section].base_address + section_offset;
+ u32 run_size = image->sections[section].size - section_offset;
+ int pad_bytes = 0;
+
+ if (image->sections[section].size == 0)
+ {
+ LOG_WARNING("empty section %d", section);
+ section++;
+ section_offset = 0;
+ continue;
+ }
+
+ /* find the corresponding flash bank */
+ if ((c = get_flash_bank_by_addr(target, run_address)) == NULL)
+ {
+ section++; /* and skip it */
+ section_offset = 0;
+ continue;
+ }
+
+ /* collect consecutive sections which fall into the same bank */
+ section_first = section;
+ section_last = section;
+ padding[section] = 0;
+ while ((run_address + run_size < c->base + c->size)
+ && (section_last + 1 < image->num_sections))
+ {
+ if (image->sections[section_last + 1].base_address < (run_address + run_size))
+ {
+ LOG_DEBUG("section %d out of order(very slightly surprising, but supported)", section_last + 1);
+ break;
+ }
+ /* if we have multiple sections within our image, flash programming could fail due to alignment issues
+ * attempt to rebuild a consecutive buffer for the flash loader */
+ pad_bytes = (image->sections[section_last + 1].base_address) - (run_address + run_size);
+ if ((run_address + run_size + pad_bytes) > (c->base + c->size))
+ break;
+ padding[section_last] = pad_bytes;
+ run_size += image->sections[++section_last].size;
+ run_size += pad_bytes;
+ padding[section_last] = 0;
+
+ LOG_INFO("Padding image section %d with %d bytes", section_last-1, pad_bytes );
+ }
+
+ /* fit the run into bank constraints */
+ if (run_address + run_size > c->base + c->size)
+ run_size = c->base + c->size - run_address;
+
+ /* allocate buffer */
+ buffer = malloc(run_size);
+ buffer_size = 0;
+
+ /* read sections to the buffer */
+ while (buffer_size < run_size)
+ {
+ u32 size_read;
+
+ if (buffer_size - run_size <= image->sections[section].size - section_offset)
+ size_read = buffer_size - run_size;
+ else
+ size_read = image->sections[section].size - section_offset;
+
+ if ((retval = image_read_section(image, section, section_offset,
+ size_read, buffer + buffer_size, &size_read)) != ERROR_OK || size_read == 0)
+ {
+ free(buffer);
+ free(padding);
+ return retval;
+ }
+
+ /* see if we need to pad the section */
+ while (padding[section]--)
+ buffer[size_read++] = 0xff;
+
+ buffer_size += size_read;
+ section_offset += size_read;
+
+ if (section_offset >= image->sections[section].size)
+ {
+ section++;
+ section_offset = 0;
+ }
+ }
+
+ retval = ERROR_OK;
+
+ if (erase)
+ {
+ /* calculate and erase sectors */
+ retval = flash_erase_address_range( target, run_address, run_size );
+ }
+
+ if (retval == ERROR_OK)
+ {
+ /* write flash sectors */
+ retval = flash_driver_write(c, buffer, run_address - c->base, run_size);
+ }
+
+ free(buffer);
+
+ if (retval != ERROR_OK)
+ {
+ free(padding);
+ return retval; /* abort operation */
+ }
+
+ if (written != NULL)
+ *written += run_size; /* add run size to total written counter */
+ }
+
+ free(padding);
+
+ return retval;
+}
+
+int default_flash_mem_blank_check(struct flash_bank_s *bank)
+{
+ target_t *target = bank->target;
+ u8 buffer[1024];
+ int buffer_size = sizeof(buffer);
+ int i;
+ int nBytes;
+
+ if (bank->target->state != TARGET_HALTED)
+ {
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ for (i = 0; i < bank->num_sectors; i++)
+ {
+ int j;
+ bank->sectors[i].is_erased = 1;
+
+ for (j = 0; j < bank->sectors[i].size; j += buffer_size)
+ {
+ int chunk;
+ int retval;
+ chunk = buffer_size;
+ if (chunk > (j - bank->sectors[i].size))
+ {
+ chunk = (j - bank->sectors[i].size);
+ }
+
+ retval = target->type->read_memory(target, bank->base + bank->sectors[i].offset + j, 4, chunk/4, buffer);
+ if (retval != ERROR_OK)
+ return retval;
+
+ for (nBytes = 0; nBytes < chunk; nBytes++)
+ {
+ if (buffer[nBytes] != 0xFF)
+ {
+ bank->sectors[i].is_erased = 0;
+ break;
+ }
+ }
+ }
+ }
+
+ return ERROR_OK;
+}
+
+int default_flash_blank_check(struct flash_bank_s *bank)
+{
+ target_t *target = bank->target;
+ int i;
+ int retval;
+ int fast_check = 0;
+ int blank;
+
+ if (bank->target->state != TARGET_HALTED)
+ {
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ for (i = 0; i < bank->num_sectors; i++)
+ {
+ u32 address = bank->base + bank->sectors[i].offset;
+ u32 size = bank->sectors[i].size;
+
+ if ((retval = target_blank_check_memory(target, address, size, &blank)) != ERROR_OK)
+ {
+ fast_check = 0;
+ break;
+ }
+ if (blank == 0xFF)
+ bank->sectors[i].is_erased = 1;
+ else
+ bank->sectors[i].is_erased = 0;
+ fast_check = 1;
+ }
+
+ if (!fast_check)
+ {
+ LOG_USER("Running slow fallback erase check - add working memory");
+ return default_flash_mem_blank_check(bank);
+ }
+
+ return ERROR_OK;
+}