-/***************************************************************************\r
- * Copyright (C) 2006 by Magnus Lundin *\r
- * lundin@mlu.mine.nu *\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
-\r
-/***************************************************************************\r
-There are some things to notice\r
-\r
-* AT91SAM7S64 is tested\r
-* All AT91SAM7Sxx and AT91SAM7Xxx should work but is not tested\r
-* All parameters are identified from onchip configuartion registers \r
-*\r
-* The flash controller handles erases automatically on a page (128/265 byte) basis\r
-* Only an EraseAll command is supported by the controller\r
-* Partial erases can be implemented in software by writing one 0xFFFFFFFF word to \r
-* some location in every page in the region to be erased\r
-* \r
-* Lock regions (sectors) are 32 or 64 pages\r
-*\r
- ***************************************************************************/\r
-#ifdef HAVE_CONFIG_H\r
-#include "config.h"\r
-#endif\r
-\r
-#include "replacements.h"\r
-\r
-#include "at91sam7.h"\r
-\r
-#include "flash.h"\r
-#include "target.h"\r
-#include "log.h"\r
-#include "binarybuffer.h"\r
-#include "types.h"\r
-\r
-#include <stdlib.h>\r
-#include <string.h>\r
-#include <unistd.h>\r
-\r
-int at91sam7_register_commands(struct command_context_s *cmd_ctx);\r
-int at91sam7_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank);\r
-int at91sam7_erase(struct flash_bank_s *bank, int first, int last);\r
-int at91sam7_protect(struct flash_bank_s *bank, int set, int first, int last);\r
-int at91sam7_write(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count);\r
-int at91sam7_probe(struct flash_bank_s *bank);\r
-int at91sam7_auto_probe(struct flash_bank_s *bank);\r
-int at91sam7_erase_check(struct flash_bank_s *bank);\r
-int at91sam7_protect_check(struct flash_bank_s *bank);\r
-int at91sam7_info(struct flash_bank_s *bank, char *buf, int buf_size);\r
-\r
-u32 at91sam7_get_flash_status(flash_bank_t *bank, u8 flashplane);\r
-void at91sam7_set_flash_mode(flash_bank_t *bank, u8 flashplane, int mode);\r
-u32 at91sam7_wait_status_busy(flash_bank_t *bank, u8 flashplane, u32 waitbits, int timeout);\r
-int at91sam7_flash_command(struct flash_bank_s *bank, u8 flashplane, u8 cmd, u16 pagen); \r
-int at91sam7_handle_gpnvm_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-\r
-flash_driver_t at91sam7_flash =\r
-{\r
- .name = "at91sam7",\r
- .register_commands = at91sam7_register_commands,\r
- .flash_bank_command = at91sam7_flash_bank_command,\r
- .erase = at91sam7_erase,\r
- .protect = at91sam7_protect,\r
- .write = at91sam7_write,\r
- .probe = at91sam7_probe,\r
- .auto_probe = at91sam7_auto_probe,\r
- .erase_check = at91sam7_erase_check,\r
- .protect_check = at91sam7_protect_check,\r
- .info = at91sam7_info\r
-};\r
-\r
-u32 MC_FMR[4] = { 0xFFFFFF60, 0xFFFFFF70, 0xFFFFFF80, 0xFFFFFF90 };\r
-u32 MC_FCR[4] = { 0xFFFFFF64, 0xFFFFFF74, 0xFFFFFF84, 0xFFFFFF94 };\r
-u32 MC_FSR[4] = { 0xFFFFFF68, 0xFFFFFF78, 0xFFFFFF88, 0xFFFFFF98 };\r
-\r
-char * EPROC[8]= {"Unknown","ARM946-E","ARM7TDMI","Unknown","ARM920T","ARM926EJ-S","Unknown","Unknown"};\r
-long NVPSIZ[16] = {\r
- 0,\r
- 0x2000, /* 8K */\r
- 0x4000, /* 16K */ \r
- 0x8000, /* 32K */\r
- -1,\r
- 0x10000, /* 64K */\r
- -1,\r
- 0x20000, /* 128K */\r
- -1,\r
- 0x40000, /* 256K */\r
- 0x80000, /* 512K */\r
- -1,\r
- 0x100000, /* 1024K */\r
- -1,\r
- 0x200000, /* 2048K */\r
- -1\r
-};\r
-\r
-long SRAMSIZ[16] = {\r
- -1,\r
- 0x0400, /* 1K */\r
- 0x0800, /* 2K */ \r
- -1, \r
- 0x1c000, /* 112K */\r
- 0x1000, /* 4K */\r
- 0x14000, /* 80K */\r
- 0x28000, /* 160K */\r
- 0x2000, /* 8K */\r
- 0x4000, /* 16K */\r
- 0x8000, /* 32K */\r
- 0x10000, /* 64K */\r
- 0x20000, /* 128K */\r
- 0x40000, /* 256K */\r
- 0x18000, /* 96K */\r
- 0x80000, /* 512K */\r
-};\r
-\r
-int at91sam7_register_commands(struct command_context_s *cmd_ctx)\r
-{\r
- command_t *at91sam7_cmd = register_command(cmd_ctx, NULL, "at91sam7", NULL, COMMAND_ANY, NULL);\r
- register_command(cmd_ctx, at91sam7_cmd, "gpnvm", at91sam7_handle_gpnvm_command, COMMAND_EXEC,\r
- "at91sam7 gpnvm <num> <bit> set|clear, set or clear at91sam7 gpnvm bit");\r
-\r
- return ERROR_OK;\r
-}\r
-\r
-u32 at91sam7_get_flash_status(flash_bank_t *bank, u8 flashplane)\r
-{\r
- target_t *target = bank->target;\r
- u32 fsr;\r
- \r
- target_read_u32(target, MC_FSR[flashplane], &fsr);\r
- \r
- return fsr;\r
-}\r
-\r
-/** Read clock configuration and set at91sam7_info->usec_clocks*/ \r
-void at91sam7_read_clock_info(flash_bank_t *bank)\r
-{\r
- at91sam7_flash_bank_t *at91sam7_info = bank->driver_priv;\r
- target_t *target = bank->target;\r
- u32 mckr, mcfr, pllr;\r
- unsigned long tmp = 0, mainfreq;\r
- int flashplane;\r
-\r
- /* Read main clock freqency register */\r
- target_read_u32(target, CKGR_MCFR, &mcfr);\r
- /* Read master clock register */\r
- target_read_u32(target, PMC_MCKR, &mckr);\r
- /* Read Clock Generator PLL Register */\r
- target_read_u32(target, CKGR_PLLR, &pllr);\r
-\r
- at91sam7_info->mck_valid = 0;\r
- switch (mckr & PMC_MCKR_CSS) \r
- {\r
- case 0: /* Slow Clock */\r
- at91sam7_info->mck_valid = 1;\r
- mainfreq = RC_FREQ / 16ul * (mcfr & 0xffff);\r
- tmp = mainfreq;\r
- break;\r
- case 1: /* Main Clock */\r
- if (mcfr & CKGR_MCFR_MAINRDY) \r
- {\r
- at91sam7_info->mck_valid = 1;\r
- mainfreq = RC_FREQ / 16ul * (mcfr & 0xffff);\r
- tmp = mainfreq;\r
- }\r
- break;\r
-\r
- case 2: /* Reserved */\r
- break;\r
- case 3: /* PLL Clock */\r
- if (mcfr & CKGR_MCFR_MAINRDY) \r
- {\r
- target_read_u32(target, CKGR_PLLR, &pllr);\r
- if (!(pllr & CKGR_PLLR_DIV))\r
- break; /* 0 Hz */\r
- at91sam7_info->mck_valid = 1;\r
- mainfreq = RC_FREQ / 16ul * (mcfr & 0xffff);\r
- /* Integer arithmetic should have sufficient precision\r
- as long as PLL is properly configured. */\r
- tmp = mainfreq / (pllr & CKGR_PLLR_DIV) *\r
- (((pllr & CKGR_PLLR_MUL) >> 16) + 1);\r
- }\r
- break;\r
- }\r
- \r
- /* Prescaler adjust */\r
- if (((mckr & PMC_MCKR_PRES) >> 2) == 7)\r
- at91sam7_info->mck_valid = 0;\r
- else\r
- at91sam7_info->mck_freq = tmp >> ((mckr & PMC_MCKR_PRES) >> 2);\r
-\r
- /* Forget old flash timing */\r
- for (flashplane = 0; flashplane<at91sam7_info->num_planes; flashplane++)\r
- {\r
- at91sam7_set_flash_mode(bank, flashplane, FMR_TIMING_NONE);\r
- }\r
-}\r
-\r
-/* Setup the timimg registers for nvbits or normal flash */\r
-void at91sam7_set_flash_mode(flash_bank_t *bank, u8 flashplane, int mode)\r
-{\r
- u32 fmr, fmcn = 0, fws = 0;\r
- at91sam7_flash_bank_t *at91sam7_info = bank->driver_priv;\r
- target_t *target = bank->target;\r
- \r
- if (mode && (mode != at91sam7_info->flashmode[flashplane]))\r
- {\r
- /* Always round up (ceil) */\r
- if (mode==FMR_TIMING_NVBITS)\r
- {\r
- if (at91sam7_info->cidr_arch == 0x60)\r
- {\r
- /* AT91SAM7A3 uses master clocks in 100 ns */\r
- fmcn = (at91sam7_info->mck_freq/10000000ul)+1;\r
- }\r
- else\r
- {\r
- /* master clocks in 1uS for ARCH 0x7 types */\r
- fmcn = (at91sam7_info->mck_freq/1000000ul)+1;\r
- }\r
- }\r
- else if (mode==FMR_TIMING_FLASH)\r
- /* main clocks in 1.5uS */\r
- fmcn = (at91sam7_info->mck_freq/666666ul)+1;\r
-\r
- /* Only allow fmcn=0 if clock period is > 30 us = 33kHz. */\r
- if (at91sam7_info->mck_freq <= 33333ul)\r
- fmcn = 0;\r
- /* Only allow fws=0 if clock frequency is < 30 MHz. */\r
- if (at91sam7_info->mck_freq > 30000000ul)\r
- fws = 1;\r
-\r
- DEBUG("fmcn[%i]: %i", flashplane, fmcn); \r
- fmr = fmcn << 16 | fws << 8;\r
- target_write_u32(target, MC_FMR[flashplane], fmr);\r
- }\r
- \r
- at91sam7_info->flashmode[flashplane] = mode; \r
-}\r
-\r
-u32 at91sam7_wait_status_busy(flash_bank_t *bank, u8 flashplane, u32 waitbits, int timeout)\r
-{\r
- u32 status;\r
- \r
- while ((!((status = at91sam7_get_flash_status(bank,flashplane)) & waitbits)) && (timeout-- > 0))\r
- {\r
- DEBUG("status[%i]: 0x%x", flashplane, status);\r
- usleep(1000);\r
- }\r
- \r
- DEBUG("status[%i]: 0x%x", flashplane, status);\r
-\r
- if (status & 0x0C)\r
- {\r
- ERROR("status register: 0x%x", status);\r
- if (status & 0x4)\r
- ERROR("Lock Error Bit Detected, Operation Abort");\r
- if (status & 0x8)\r
- ERROR("Invalid command and/or bad keyword, Operation Abort");\r
- if (status & 0x10)\r
- ERROR("Security Bit Set, Operation Abort");\r
- }\r
- \r
- return status;\r
-}\r
-\r
-\r
-/* Send one command to the AT91SAM flash controller */\r
-int at91sam7_flash_command(struct flash_bank_s *bank, u8 flashplane, u8 cmd, u16 pagen) \r
-{\r
- u32 fcr;\r
- at91sam7_flash_bank_t *at91sam7_info = bank->driver_priv;\r
- target_t *target = bank->target;\r
-\r
- fcr = (0x5A<<24) | ((pagen&0x3FF)<<8) | cmd; \r
- target_write_u32(target, MC_FCR[flashplane], fcr);\r
- DEBUG("Flash command: 0x%x, flashplane: %i, pagenumber:%u", fcr, flashplane, pagen);\r
-\r
- if ((at91sam7_info->cidr_arch == 0x60)&&((cmd==SLB)|(cmd==CLB)))\r
- {\r
- /* Lock bit manipulation on AT91SAM7A3 waits for FC_FSR bit 1, EOL */\r
- if (at91sam7_wait_status_busy(bank, flashplane, MC_FSR_EOL, 10)&0x0C) \r
- {\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
- return ERROR_OK;\r
- }\r
-\r
- if (at91sam7_wait_status_busy(bank, flashplane, MC_FSR_FRDY, 10)&0x0C) \r
- {\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
- return ERROR_OK;\r
-}\r
-\r
-/* Read device id register, main clock frequency register and fill in driver info structure */\r
-int at91sam7_read_part_info(struct flash_bank_s *bank)\r
-{\r
- at91sam7_flash_bank_t *at91sam7_info = bank->driver_priv;\r
- target_t *target = bank->target;\r
- u32 cidr, status;\r
- int sectornum;\r
- \r
- if (bank->target->state != TARGET_HALTED)\r
- {\r
- return ERROR_TARGET_NOT_HALTED;\r
- }\r
- \r
- /* Read and parse chip identification register */\r
- target_read_u32(target, DBGU_CIDR, &cidr);\r
- \r
- if (cidr == 0)\r
- {\r
- WARNING("Cannot identify target as an AT91SAM");\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
- \r
- at91sam7_info->cidr = cidr;\r
- at91sam7_info->cidr_ext = (cidr>>31)&0x0001;\r
- at91sam7_info->cidr_nvptyp = (cidr>>28)&0x0007;\r
- at91sam7_info->cidr_arch = (cidr>>20)&0x00FF;\r
- at91sam7_info->cidr_sramsiz = (cidr>>16)&0x000F;\r
- at91sam7_info->cidr_nvpsiz2 = (cidr>>12)&0x000F;\r
- at91sam7_info->cidr_nvpsiz = (cidr>>8)&0x000F;\r
- at91sam7_info->cidr_eproc = (cidr>>5)&0x0007;\r
- at91sam7_info->cidr_version = cidr&0x001F;\r
- bank->size = NVPSIZ[at91sam7_info->cidr_nvpsiz];\r
- at91sam7_info->target_name = "Unknown";\r
-\r
- /* Support just for bulk erase of a single flash plane, whole device if flash size <= 256k */\r
- if (NVPSIZ[at91sam7_info->cidr_nvpsiz]<0x80000) /* Flash size less than 512K, one flash plane */\r
- {\r
- bank->num_sectors = 1;\r
- bank->sectors = malloc(sizeof(flash_sector_t));\r
- bank->sectors[0].offset = 0;\r
- bank->sectors[0].size = bank->size;\r
- bank->sectors[0].is_erased = -1;\r
- bank->sectors[0].is_protected = -1;\r
- }\r
- else /* Flash size 512K or larger, several flash planes */\r
- {\r
- bank->num_sectors = NVPSIZ[at91sam7_info->cidr_nvpsiz]/0x40000;\r
- bank->sectors = malloc(bank->num_sectors*sizeof(flash_sector_t));\r
- for (sectornum=0; sectornum<bank->num_sectors; sectornum++)\r
- {\r
- bank->sectors[sectornum].offset = sectornum*0x40000;\r
- bank->sectors[sectornum].size = 0x40000;\r
- bank->sectors[sectornum].is_erased = -1;\r
- bank->sectors[sectornum].is_protected = -1;\r
- }\r
- }\r
- \r
- \r
-\r
- DEBUG("nvptyp: 0x%3.3x, arch: 0x%4.4x", at91sam7_info->cidr_nvptyp, at91sam7_info->cidr_arch );\r
-\r
- /* Read main and master clock freqency register */\r
- at91sam7_read_clock_info(bank);\r
- \r
- at91sam7_info->num_planes = 1;\r
- status = at91sam7_get_flash_status(bank, 0);\r
- at91sam7_info->securitybit = (status>>4)&0x01;\r
- at91sam7_protect_check(bank); /* TODO Check the protect check */\r
- \r
- if (at91sam7_info->cidr_arch == 0x70 )\r
- {\r
- at91sam7_info->num_nvmbits = 2;\r
- at91sam7_info->nvmbits = (status>>8)&0x03;\r
- bank->base = 0x100000;\r
- bank->bus_width = 4;\r
- if (bank->size==0x80000) /* AT91SAM7S512 */\r
- {\r
- at91sam7_info->target_name = "AT91SAM7S512";\r
- at91sam7_info->num_planes = 2;\r
- if (at91sam7_info->num_planes != bank->num_sectors)\r
- WARNING("Internal error: Number of flash planes and erase sectors does not match, please report");;\r
- at91sam7_info->num_lockbits = 2*16;\r
- at91sam7_info->pagesize = 256;\r
- at91sam7_info->pages_in_lockregion = 64;\r
- at91sam7_info->num_pages = 2*16*64;\r
- }\r
- if (bank->size==0x40000) /* AT91SAM7S256 */\r
- {\r
- at91sam7_info->target_name = "AT91SAM7S256";\r
- at91sam7_info->num_lockbits = 16;\r
- at91sam7_info->pagesize = 256;\r
- at91sam7_info->pages_in_lockregion = 64;\r
- at91sam7_info->num_pages = 16*64;\r
- }\r
- if (bank->size==0x20000) /* AT91SAM7S128 */\r
- {\r
- at91sam7_info->target_name = "AT91SAM7S128";\r
- at91sam7_info->num_lockbits = 8;\r
- at91sam7_info->pagesize = 256;\r
- at91sam7_info->pages_in_lockregion = 64;\r
- at91sam7_info->num_pages = 8*64;\r
- }\r
- if (bank->size==0x10000) /* AT91SAM7S64 */\r
- {\r
- at91sam7_info->target_name = "AT91SAM7S64";\r
- at91sam7_info->num_lockbits = 16;\r
- at91sam7_info->pagesize = 128;\r
- at91sam7_info->pages_in_lockregion = 32;\r
- at91sam7_info->num_pages = 16*32;\r
- }\r
- if (bank->size==0x08000) /* AT91SAM7S321/32 */\r
- {\r
- at91sam7_info->target_name = "AT91SAM7S321/32";\r
- at91sam7_info->num_lockbits = 8;\r
- at91sam7_info->pagesize = 128;\r
- at91sam7_info->pages_in_lockregion = 32;\r
- at91sam7_info->num_pages = 8*32;\r
- }\r
- \r
- return ERROR_OK;\r
- }\r
-\r
- if (at91sam7_info->cidr_arch == 0x71 )\r
- {\r
- at91sam7_info->num_nvmbits = 3;\r
- at91sam7_info->nvmbits = (status>>8)&0x07;\r
- bank->base = 0x100000;\r
- bank->bus_width = 4;\r
- if (bank->size==0x80000) /* AT91SAM7XC512 */\r
- {\r
- at91sam7_info->target_name = "AT91SAM7XC512";\r
- at91sam7_info->num_planes = 2;\r
- if (at91sam7_info->num_planes != bank->num_sectors)\r
- WARNING("Internal error: Number of flash planes and erase sectors does not match, please report");;\r
- at91sam7_info->num_lockbits = 2*16;\r
- at91sam7_info->pagesize = 256;\r
- at91sam7_info->pages_in_lockregion = 64;\r
- at91sam7_info->num_pages = 2*16*64;\r
- }\r
- if (bank->size==0x40000) /* AT91SAM7XC256 */\r
- {\r
- at91sam7_info->target_name = "AT91SAM7XC256";\r
- at91sam7_info->num_lockbits = 16;\r
- at91sam7_info->pagesize = 256;\r
- at91sam7_info->pages_in_lockregion = 64;\r
- at91sam7_info->num_pages = 16*64;\r
- }\r
- if (bank->size==0x20000) /* AT91SAM7XC128 */\r
- {\r
- at91sam7_info->target_name = "AT91SAM7XC128";\r
- at91sam7_info->num_lockbits = 8;\r
- at91sam7_info->pagesize = 256;\r
- at91sam7_info->pages_in_lockregion = 64;\r
- at91sam7_info->num_pages = 8*64;\r
- }\r
- \r
- return ERROR_OK;\r
- }\r
- \r
- if (at91sam7_info->cidr_arch == 0x72 )\r
- {\r
- at91sam7_info->num_nvmbits = 3;\r
- at91sam7_info->nvmbits = (status>>8)&0x07;\r
- bank->base = 0x100000;\r
- bank->bus_width = 4;\r
- if (bank->size==0x80000) /* AT91SAM7SE512 */\r
- {\r
- at91sam7_info->target_name = "AT91SAM7SE512";\r
- at91sam7_info->num_planes = 2;\r
- if (at91sam7_info->num_planes != bank->num_sectors)\r
- WARNING("Internal error: Number of flash planes and erase sectors does not match, please report");;\r
- at91sam7_info->num_lockbits = 32;\r
- at91sam7_info->pagesize = 256;\r
- at91sam7_info->pages_in_lockregion = 64;\r
- at91sam7_info->num_pages = 32*64;\r
- }\r
- if (bank->size==0x40000)\r
- {\r
- at91sam7_info->target_name = "AT91SAM7SE256";\r
- at91sam7_info->num_lockbits = 16;\r
- at91sam7_info->pagesize = 256;\r
- at91sam7_info->pages_in_lockregion = 64;\r
- at91sam7_info->num_pages = 16*64;\r
- }\r
- if (bank->size==0x08000)\r
- {\r
- at91sam7_info->target_name = "AT91SAM7SE32";\r
- at91sam7_info->num_lockbits = 8;\r
- at91sam7_info->pagesize = 128;\r
- at91sam7_info->pages_in_lockregion = 32;\r
- at91sam7_info->num_pages = 8*32;\r
- }\r
- \r
- return ERROR_OK;\r
- }\r
- \r
- if (at91sam7_info->cidr_arch == 0x75 )\r
- {\r
- at91sam7_info->num_nvmbits = 3;\r
- at91sam7_info->nvmbits = (status>>8)&0x07;\r
- bank->base = 0x100000;\r
- bank->bus_width = 4;\r
- if (bank->size==0x80000) /* AT91SAM7X512 */\r
- {\r
- at91sam7_info->target_name = "AT91SAM7X512";\r
- at91sam7_info->num_planes = 2;\r
- if (at91sam7_info->num_planes != bank->num_sectors)\r
- WARNING("Internal error: Number of flash planes and erase sectors does not match, please report");;\r
- at91sam7_info->num_lockbits = 32;\r
- at91sam7_info->pagesize = 256;\r
- at91sam7_info->pages_in_lockregion = 64;\r
- at91sam7_info->num_pages = 2*16*64;\r
- DEBUG("Support for AT91SAM7X512 is experimental in this version!");\r
- }\r
- if (bank->size==0x40000) /* AT91SAM7X256 */\r
- {\r
- at91sam7_info->target_name = "AT91SAM7X256";\r
- at91sam7_info->num_lockbits = 16;\r
- at91sam7_info->pagesize = 256;\r
- at91sam7_info->pages_in_lockregion = 64;\r
- at91sam7_info->num_pages = 16*64;\r
- }\r
- if (bank->size==0x20000) /* AT91SAM7X128 */\r
- {\r
- at91sam7_info->target_name = "AT91SAM7X128";\r
- at91sam7_info->num_lockbits = 8;\r
- at91sam7_info->pagesize = 256;\r
- at91sam7_info->pages_in_lockregion = 64;\r
- at91sam7_info->num_pages = 8*64;\r
- }\r
- \r
- return ERROR_OK;\r
- }\r
- \r
- if (at91sam7_info->cidr_arch == 0x60 )\r
- {\r
- at91sam7_info->num_nvmbits = 3;\r
- at91sam7_info->nvmbits = (status>>8)&0x07;\r
- bank->base = 0x100000;\r
- bank->bus_width = 4;\r
- \r
- if (bank->size == 0x40000) /* AT91SAM7A3 */\r
- {\r
- at91sam7_info->target_name = "AT91SAM7A3";\r
- at91sam7_info->num_lockbits = 16;\r
- at91sam7_info->pagesize = 256;\r
- at91sam7_info->pages_in_lockregion = 16;\r
- at91sam7_info->num_pages = 16*64;\r
- }\r
- return ERROR_OK;\r
- }\r
- \r
- WARNING("at91sam7 flash only tested for AT91SAM7Sxx series");\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int at91sam7_erase_check(struct flash_bank_s *bank)\r
-{\r
- at91sam7_flash_bank_t *at91sam7_info = bank->driver_priv;\r
- \r
- if (!at91sam7_info->working_area_size)\r
- {\r
- }\r
- else\r
- { \r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int at91sam7_protect_check(struct flash_bank_s *bank)\r
-{\r
- u32 status;\r
- int flashplane;\r
- \r
- at91sam7_flash_bank_t *at91sam7_info = bank->driver_priv;\r
-\r
- if (at91sam7_info->cidr == 0)\r
- {\r
- at91sam7_read_part_info(bank);\r
- }\r
-\r
- if (at91sam7_info->cidr == 0)\r
- {\r
- WARNING("Cannot identify target as an AT91SAM");\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
-\r
- for (flashplane=0;flashplane<at91sam7_info->num_planes;flashplane++)\r
- {\r
- status = at91sam7_get_flash_status(bank, flashplane);\r
- at91sam7_info->lockbits[flashplane] = (status >> 16);\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-/* flash_bank at91sam7 0 0 0 0 <target#>\r
- */\r
-int at91sam7_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank)\r
-{\r
- at91sam7_flash_bank_t *at91sam7_info;\r
- int i;\r
- \r
- if (argc < 6)\r
- {\r
- WARNING("incomplete flash_bank at91sam7 configuration");\r
- return ERROR_FLASH_BANK_INVALID;\r
- }\r
- \r
- at91sam7_info = malloc(sizeof(at91sam7_flash_bank_t));\r
- bank->driver_priv = at91sam7_info;\r
- at91sam7_info->probed = 0;\r
- \r
- /* part wasn't probed for info yet */\r
- at91sam7_info->cidr = 0;\r
- for (i=0;i<4;i++)\r
- at91sam7_info->flashmode[i]=0;\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int at91sam7_erase(struct flash_bank_s *bank, int first, int last)\r
-{\r
- at91sam7_flash_bank_t *at91sam7_info = bank->driver_priv;\r
- u8 flashplane;\r
-\r
- if (bank->target->state != TARGET_HALTED)\r
- {\r
- return ERROR_TARGET_NOT_HALTED;\r
- }\r
- \r
- if (at91sam7_info->cidr == 0)\r
- {\r
- at91sam7_read_part_info(bank);\r
- }\r
-\r
- if (at91sam7_info->cidr == 0)\r
- {\r
- WARNING("Cannot identify target as an AT91SAM");\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- } \r
- \r
- if ((first < 0) || (last < first) || (last >= bank->num_sectors))\r
- {\r
- if ((first == 0) && (last == (at91sam7_info->num_lockbits-1)))\r
- {\r
- WARNING("Sector numbers based on lockbit count, probably a deprecated script");\r
- last = bank->num_sectors-1;\r
- }\r
- else return ERROR_FLASH_SECTOR_INVALID;\r
- }\r
-\r
- /* Configure the flash controller timing */\r
- at91sam7_read_clock_info(bank); \r
- for (flashplane = first; flashplane<=last; flashplane++)\r
- {\r
- /* Configure the flash controller timing */\r
- at91sam7_set_flash_mode(bank, flashplane, FMR_TIMING_FLASH);\r
- if (at91sam7_flash_command(bank, flashplane, EA, 0) != ERROR_OK) \r
- {\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- } \r
- }\r
- return ERROR_OK;\r
-\r
-}\r
-\r
-int at91sam7_protect(struct flash_bank_s *bank, int set, int first, int last)\r
-{\r
- u32 cmd, pagen;\r
- u8 flashplane;\r
- int lockregion;\r
- \r
- at91sam7_flash_bank_t *at91sam7_info = bank->driver_priv;\r
- \r
- if (bank->target->state != TARGET_HALTED)\r
- {\r
- return ERROR_TARGET_NOT_HALTED;\r
- }\r
- \r
- if ((first < 0) || (last < first) || (last >= at91sam7_info->num_lockbits))\r
- {\r
- return ERROR_FLASH_SECTOR_INVALID;\r
- }\r
- \r
- if (at91sam7_info->cidr == 0)\r
- {\r
- at91sam7_read_part_info(bank);\r
- }\r
-\r
- if (at91sam7_info->cidr == 0)\r
- {\r
- WARNING("Cannot identify target as an AT91SAM");\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
- \r
- at91sam7_read_clock_info(bank); \r
- \r
- for (lockregion=first;lockregion<=last;lockregion++) \r
- {\r
- pagen = lockregion*at91sam7_info->pages_in_lockregion;\r
- flashplane = (pagen>>10)&0x03;\r
- /* Configure the flash controller timing */\r
- at91sam7_set_flash_mode(bank, flashplane, FMR_TIMING_NVBITS);\r
- \r
- if (set)\r
- cmd = SLB; \r
- else\r
- cmd = CLB; \r
-\r
- if (at91sam7_flash_command(bank, flashplane, cmd, pagen) != ERROR_OK) \r
- {\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- } \r
- }\r
- \r
- at91sam7_protect_check(bank);\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-\r
-int at91sam7_write(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count)\r
-{\r
- at91sam7_flash_bank_t *at91sam7_info = bank->driver_priv;\r
- target_t *target = bank->target;\r
- u32 dst_min_alignment, wcount, bytes_remaining = count;\r
- u32 first_page, last_page, pagen, buffer_pos;\r
- u8 flashplane;\r
- \r
- if (at91sam7_info->cidr == 0)\r
- {\r
- at91sam7_read_part_info(bank);\r
- }\r
-\r
- if (at91sam7_info->cidr == 0)\r
- {\r
- WARNING("Cannot identify target as an AT91SAM");\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
- \r
- if (offset + count > bank->size)\r
- return ERROR_FLASH_DST_OUT_OF_BANK;\r
- \r
- dst_min_alignment = at91sam7_info->pagesize;\r
-\r
- if (offset % dst_min_alignment)\r
- {\r
- WARNING("offset 0x%x breaks required alignment 0x%x", offset, dst_min_alignment);\r
- return ERROR_FLASH_DST_BREAKS_ALIGNMENT;\r
- }\r
- \r
- if (at91sam7_info->cidr_arch == 0)\r
- return ERROR_FLASH_BANK_NOT_PROBED;\r
-\r
- first_page = offset/dst_min_alignment;\r
- last_page = CEIL(offset + count, dst_min_alignment);\r
- \r
- DEBUG("first_page: %i, last_page: %i, count %i", first_page, last_page, count);\r
- \r
- at91sam7_read_clock_info(bank); \r
-\r
- for (pagen=first_page; pagen<last_page; pagen++) \r
- {\r
- if (bytes_remaining<dst_min_alignment)\r
- count = bytes_remaining;\r
- else\r
- count = dst_min_alignment;\r
- bytes_remaining -= count;\r
- \r
- /* Write one block to the PageWriteBuffer */\r
- buffer_pos = (pagen-first_page)*dst_min_alignment;\r
- wcount = CEIL(count,4);\r
- target->type->write_memory(target, bank->base+pagen*dst_min_alignment, 4, wcount, buffer+buffer_pos);\r
- flashplane = (pagen>>10)&0x3;\r
- \r
- /* Configure the flash controller timing */ \r
- at91sam7_set_flash_mode(bank, flashplane, FMR_TIMING_FLASH);\r
- /* Send Write Page command to Flash Controller */\r
- if (at91sam7_flash_command(bank, flashplane, WP, pagen) != ERROR_OK) \r
- {\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
- DEBUG("Write flash plane:%i page number:%i", flashplane, pagen);\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-\r
-int at91sam7_probe(struct flash_bank_s *bank)\r
-{\r
- /* we can't probe on an at91sam7\r
- * if this is an at91sam7, it has the configured flash\r
- */\r
- at91sam7_flash_bank_t *at91sam7_info = bank->driver_priv;\r
- at91sam7_info->probed = 0;\r
- \r
- if (at91sam7_info->cidr == 0)\r
- {\r
- at91sam7_read_part_info(bank);\r
- }\r
-\r
- if (at91sam7_info->cidr == 0)\r
- {\r
- WARNING("Cannot identify target as an AT91SAM");\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
- \r
- at91sam7_info->probed = 1;\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-\r
-int at91sam7_auto_probe(struct flash_bank_s *bank)\r
-{\r
- at91sam7_flash_bank_t *at91sam7_info = bank->driver_priv;\r
- if (at91sam7_info->probed)\r
- return ERROR_OK;\r
- return at91sam7_probe(bank);\r
-}\r
-\r
-int at91sam7_info(struct flash_bank_s *bank, char *buf, int buf_size)\r
-{\r
- int printed, flashplane;\r
- at91sam7_flash_bank_t *at91sam7_info = bank->driver_priv;\r
- \r
- at91sam7_read_part_info(bank);\r
-\r
- if (at91sam7_info->cidr == 0)\r
- {\r
- printed = snprintf(buf, buf_size, "Cannot identify target as an AT91SAM\n");\r
- buf += printed;\r
- buf_size -= printed;\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
- \r
- printed = snprintf(buf, buf_size, "\nat91sam7 information: Chip is %s\n",at91sam7_info->target_name);\r
- buf += printed;\r
- buf_size -= printed;\r
- \r
- printed = snprintf(buf, buf_size, "cidr: 0x%8.8x, arch: 0x%4.4x, eproc: %s, version:0x%3.3x, flashsize: 0x%8.8x\n",\r
- at91sam7_info->cidr, at91sam7_info->cidr_arch, EPROC[at91sam7_info->cidr_eproc], at91sam7_info->cidr_version, bank->size);\r
- buf += printed;\r
- buf_size -= printed;\r
- \r
- printed = snprintf(buf, buf_size, "master clock(estimated): %ikHz \n", at91sam7_info->mck_freq / 1000);\r
- buf += printed;\r
- buf_size -= printed;\r
- \r
- if (at91sam7_info->num_planes>1) { \r
- printed = snprintf(buf, buf_size, "flashplanes: %i, pagesize: %i, lock regions: %i, pages in lock region: %i \n", \r
- at91sam7_info->num_planes, at91sam7_info->pagesize, at91sam7_info->num_lockbits, at91sam7_info->num_pages/at91sam7_info->num_lockbits);\r
- buf += printed;\r
- buf_size -= printed;\r
- for (flashplane=0; flashplane<at91sam7_info->num_planes; flashplane++)\r
- {\r
- printed = snprintf(buf, buf_size, "lockbits[%i]: 0x%4.4x, ", flashplane, at91sam7_info->lockbits[flashplane]);\r
- buf += printed;\r
- buf_size -= printed;\r
- }\r
- }\r
- else\r
- if (at91sam7_info->num_lockbits>0) { \r
- printed = snprintf(buf, buf_size, "pagesize: %i, lockbits: %i 0x%4.4x, pages in lock region: %i \n", \r
- at91sam7_info->pagesize, at91sam7_info->num_lockbits, at91sam7_info->lockbits[0], at91sam7_info->num_pages/at91sam7_info->num_lockbits);\r
- buf += printed;\r
- buf_size -= printed;\r
- }\r
- \r
- printed = snprintf(buf, buf_size, "securitybit: %i, nvmbits: 0x%1.1x\n", at91sam7_info->securitybit, at91sam7_info->nvmbits);\r
- buf += printed;\r
- buf_size -= printed;\r
-\r
- return ERROR_OK;\r
-}\r
-\r
-/* \r
-* On AT91SAM7S: When the gpnvm bits are set with \r
-* > at91sam7 gpnvm 0 bitnr set\r
-* the changes are not visible in the flash controller status register MC_FSR \r
-* until the processor has been reset.\r
-* On the Olimex board this requires a power cycle.\r
-* Note that the AT91SAM7S has the following errata (doc6175.pdf sec 14.1.3):\r
-* The maximum number of write/erase cycles for Non Volatile Memory bits is 100. This includes\r
-* Lock Bits (LOCKx), General Purpose NVM bits (GPNVMx) and the Security Bit.\r
-*/\r
-int at91sam7_handle_gpnvm_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- flash_bank_t *bank;\r
- int bit;\r
- u8 flashcmd;\r
- u32 status;\r
- char *value;\r
- at91sam7_flash_bank_t *at91sam7_info;\r
-\r
- if (argc < 3)\r
- {\r
- command_print(cmd_ctx, "at91sam7 gpnvm <num> <bit> <set|clear>");\r
- return ERROR_OK;\r
- }\r
- \r
- bank = get_flash_bank_by_num(strtoul(args[0], NULL, 0));\r
- bit = atoi(args[1]);\r
- value = args[2];\r
-\r
- if (!bank)\r
- {\r
- command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);\r
- return ERROR_OK;\r
- }\r
-\r
- at91sam7_info = bank->driver_priv;\r
-\r
- if (bank->target->state != TARGET_HALTED)\r
- {\r
- return ERROR_TARGET_NOT_HALTED;\r
- }\r
- \r
- if (at91sam7_info->cidr == 0)\r
- {\r
- at91sam7_read_part_info(bank);\r
- }\r
-\r
- if (at91sam7_info->cidr == 0)\r
- {\r
- WARNING("Cannot identify target as an AT91SAM");\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
-\r
- if ((bit<0) || (at91sam7_info->num_nvmbits <= bit))\r
- { \r
- command_print(cmd_ctx, "gpnvm bit '#%s' is out of bounds for target %s", args[1],at91sam7_info->target_name);\r
- return ERROR_OK;\r
- }\r
-\r
- if (strcmp(value, "set") == 0)\r
- {\r
- flashcmd = SGPB;\r
- }\r
- else if (strcmp(value, "clear") == 0)\r
- {\r
- flashcmd = CGPB;\r
- }\r
- else\r
- {\r
+/***************************************************************************
+ * Copyright (C) 2006 by Magnus Lundin *
+ * lundin@mlu.mine.nu *
+ * *
+ * 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. *
+ ***************************************************************************/
+
+/***************************************************************************
+There are some things to notice
+
+* AT91SAM7S64 is tested
+* All AT91SAM7Sxx and AT91SAM7Xxx should work but is not tested
+* All parameters are identified from onchip configuartion registers
+*
+* The flash controller handles erases automatically on a page (128/265 byte) basis
+* Only an EraseAll command is supported by the controller
+* Partial erases can be implemented in software by writing one 0xFFFFFFFF word to
+* some location in every page in the region to be erased
+*
+* Lock regions (sectors) are 32 or 64 pages
+*
+ ***************************************************************************/
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "replacements.h"
+
+#include "at91sam7.h"
+
+#include "flash.h"
+#include "target.h"
+#include "log.h"
+#include "binarybuffer.h"
+#include "types.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+int at91sam7_register_commands(struct command_context_s *cmd_ctx);
+int at91sam7_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank);
+int at91sam7_erase(struct flash_bank_s *bank, int first, int last);
+int at91sam7_protect(struct flash_bank_s *bank, int set, int first, int last);
+int at91sam7_write(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count);
+int at91sam7_probe(struct flash_bank_s *bank);
+int at91sam7_auto_probe(struct flash_bank_s *bank);
+int at91sam7_erase_check(struct flash_bank_s *bank);
+int at91sam7_protect_check(struct flash_bank_s *bank);
+int at91sam7_info(struct flash_bank_s *bank, char *buf, int buf_size);
+
+u32 at91sam7_get_flash_status(flash_bank_t *bank, u8 flashplane);
+void at91sam7_set_flash_mode(flash_bank_t *bank, u8 flashplane, int mode);
+u32 at91sam7_wait_status_busy(flash_bank_t *bank, u8 flashplane, u32 waitbits, int timeout);
+int at91sam7_flash_command(struct flash_bank_s *bank, u8 flashplane, u8 cmd, u16 pagen);
+int at91sam7_handle_gpnvm_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+
+flash_driver_t at91sam7_flash =
+{
+ .name = "at91sam7",
+ .register_commands = at91sam7_register_commands,
+ .flash_bank_command = at91sam7_flash_bank_command,
+ .erase = at91sam7_erase,
+ .protect = at91sam7_protect,
+ .write = at91sam7_write,
+ .probe = at91sam7_probe,
+ .auto_probe = at91sam7_auto_probe,
+ .erase_check = at91sam7_erase_check,
+ .protect_check = at91sam7_protect_check,
+ .info = at91sam7_info
+};
+
+u32 MC_FMR[4] = { 0xFFFFFF60, 0xFFFFFF70, 0xFFFFFF80, 0xFFFFFF90 };
+u32 MC_FCR[4] = { 0xFFFFFF64, 0xFFFFFF74, 0xFFFFFF84, 0xFFFFFF94 };
+u32 MC_FSR[4] = { 0xFFFFFF68, 0xFFFFFF78, 0xFFFFFF88, 0xFFFFFF98 };
+
+char * EPROC[8]= {"Unknown","ARM946-E","ARM7TDMI","Unknown","ARM920T","ARM926EJ-S","Unknown","Unknown"};
+long NVPSIZ[16] = {
+ 0,
+ 0x2000, /* 8K */
+ 0x4000, /* 16K */
+ 0x8000, /* 32K */
+ -1,
+ 0x10000, /* 64K */
+ -1,
+ 0x20000, /* 128K */
+ -1,
+ 0x40000, /* 256K */
+ 0x80000, /* 512K */
+ -1,
+ 0x100000, /* 1024K */
+ -1,
+ 0x200000, /* 2048K */
+ -1
+};
+
+long SRAMSIZ[16] = {
+ -1,
+ 0x0400, /* 1K */
+ 0x0800, /* 2K */
+ -1,
+ 0x1c000, /* 112K */
+ 0x1000, /* 4K */
+ 0x14000, /* 80K */
+ 0x28000, /* 160K */
+ 0x2000, /* 8K */
+ 0x4000, /* 16K */
+ 0x8000, /* 32K */
+ 0x10000, /* 64K */
+ 0x20000, /* 128K */
+ 0x40000, /* 256K */
+ 0x18000, /* 96K */
+ 0x80000, /* 512K */
+};
+
+int at91sam7_register_commands(struct command_context_s *cmd_ctx)
+{
+ command_t *at91sam7_cmd = register_command(cmd_ctx, NULL, "at91sam7", NULL, COMMAND_ANY, NULL);
+ register_command(cmd_ctx, at91sam7_cmd, "gpnvm", at91sam7_handle_gpnvm_command, COMMAND_EXEC,
+ "at91sam7 gpnvm <num> <bit> set|clear, set or clear at91sam7 gpnvm bit");
+
+ return ERROR_OK;
+}
+
+u32 at91sam7_get_flash_status(flash_bank_t *bank, u8 flashplane)
+{
+ target_t *target = bank->target;
+ u32 fsr;
+
+ target_read_u32(target, MC_FSR[flashplane], &fsr);
+
+ return fsr;
+}
+
+/** Read clock configuration and set at91sam7_info->usec_clocks*/
+void at91sam7_read_clock_info(flash_bank_t *bank)
+{
+ at91sam7_flash_bank_t *at91sam7_info = bank->driver_priv;
+ target_t *target = bank->target;
+ u32 mckr, mcfr, pllr;
+ unsigned long tmp = 0, mainfreq;
+ int flashplane;
+
+ /* Read main clock freqency register */
+ target_read_u32(target, CKGR_MCFR, &mcfr);
+ /* Read master clock register */
+ target_read_u32(target, PMC_MCKR, &mckr);
+ /* Read Clock Generator PLL Register */
+ target_read_u32(target, CKGR_PLLR, &pllr);
+
+ at91sam7_info->mck_valid = 0;
+ switch (mckr & PMC_MCKR_CSS)
+ {
+ case 0: /* Slow Clock */
+ at91sam7_info->mck_valid = 1;
+ mainfreq = RC_FREQ / 16ul * (mcfr & 0xffff);
+ tmp = mainfreq;
+ break;
+ case 1: /* Main Clock */
+ if (mcfr & CKGR_MCFR_MAINRDY)
+ {
+ at91sam7_info->mck_valid = 1;
+ mainfreq = RC_FREQ / 16ul * (mcfr & 0xffff);
+ tmp = mainfreq;
+ }
+ break;
+
+ case 2: /* Reserved */
+ break;
+ case 3: /* PLL Clock */
+ if (mcfr & CKGR_MCFR_MAINRDY)
+ {
+ target_read_u32(target, CKGR_PLLR, &pllr);
+ if (!(pllr & CKGR_PLLR_DIV))
+ break; /* 0 Hz */
+ at91sam7_info->mck_valid = 1;
+ mainfreq = RC_FREQ / 16ul * (mcfr & 0xffff);
+ /* Integer arithmetic should have sufficient precision
+ as long as PLL is properly configured. */
+ tmp = mainfreq / (pllr & CKGR_PLLR_DIV) *
+ (((pllr & CKGR_PLLR_MUL) >> 16) + 1);
+ }
+ break;
+ }
+
+ /* Prescaler adjust */
+ if (((mckr & PMC_MCKR_PRES) >> 2) == 7)
+ at91sam7_info->mck_valid = 0;
+ else
+ at91sam7_info->mck_freq = tmp >> ((mckr & PMC_MCKR_PRES) >> 2);
+
+ /* Forget old flash timing */
+ for (flashplane = 0; flashplane<at91sam7_info->num_planes; flashplane++)
+ {
+ at91sam7_set_flash_mode(bank, flashplane, FMR_TIMING_NONE);
+ }
+}
+
+/* Setup the timimg registers for nvbits or normal flash */
+void at91sam7_set_flash_mode(flash_bank_t *bank, u8 flashplane, int mode)
+{
+ u32 fmr, fmcn = 0, fws = 0;
+ at91sam7_flash_bank_t *at91sam7_info = bank->driver_priv;
+ target_t *target = bank->target;
+
+ if (mode && (mode != at91sam7_info->flashmode[flashplane]))
+ {
+ /* Always round up (ceil) */
+ if (mode==FMR_TIMING_NVBITS)
+ {
+ if (at91sam7_info->cidr_arch == 0x60)
+ {
+ /* AT91SAM7A3 uses master clocks in 100 ns */
+ fmcn = (at91sam7_info->mck_freq/10000000ul)+1;
+ }
+ else
+ {
+ /* master clocks in 1uS for ARCH 0x7 types */
+ fmcn = (at91sam7_info->mck_freq/1000000ul)+1;
+ }
+ }
+ else if (mode==FMR_TIMING_FLASH)
+ /* main clocks in 1.5uS */
+ fmcn = (at91sam7_info->mck_freq/666666ul)+1;
+
+ /* Only allow fmcn=0 if clock period is > 30 us = 33kHz. */
+ if (at91sam7_info->mck_freq <= 33333ul)
+ fmcn = 0;
+ /* Only allow fws=0 if clock frequency is < 30 MHz. */
+ if (at91sam7_info->mck_freq > 30000000ul)
+ fws = 1;
+
+ DEBUG("fmcn[%i]: %i", flashplane, fmcn);
+ fmr = fmcn << 16 | fws << 8;
+ target_write_u32(target, MC_FMR[flashplane], fmr);
+ }
+
+ at91sam7_info->flashmode[flashplane] = mode;
+}
+
+u32 at91sam7_wait_status_busy(flash_bank_t *bank, u8 flashplane, u32 waitbits, int timeout)
+{
+ u32 status;
+
+ while ((!((status = at91sam7_get_flash_status(bank,flashplane)) & waitbits)) && (timeout-- > 0))
+ {
+ DEBUG("status[%i]: 0x%x", flashplane, status);
+ usleep(1000);
+ }
+
+ DEBUG("status[%i]: 0x%x", flashplane, status);
+
+ if (status & 0x0C)
+ {
+ ERROR("status register: 0x%x", status);
+ if (status & 0x4)
+ ERROR("Lock Error Bit Detected, Operation Abort");
+ if (status & 0x8)
+ ERROR("Invalid command and/or bad keyword, Operation Abort");
+ if (status & 0x10)
+ ERROR("Security Bit Set, Operation Abort");
+ }
+
+ return status;
+}
+
+
+/* Send one command to the AT91SAM flash controller */
+int at91sam7_flash_command(struct flash_bank_s *bank, u8 flashplane, u8 cmd, u16 pagen)
+{
+ u32 fcr;
+ at91sam7_flash_bank_t *at91sam7_info = bank->driver_priv;
+ target_t *target = bank->target;
+
+ fcr = (0x5A<<24) | ((pagen&0x3FF)<<8) | cmd;
+ target_write_u32(target, MC_FCR[flashplane], fcr);
+ DEBUG("Flash command: 0x%x, flashplane: %i, pagenumber:%u", fcr, flashplane, pagen);
+
+ if ((at91sam7_info->cidr_arch == 0x60)&&((cmd==SLB)|(cmd==CLB)))
+ {
+ /* Lock bit manipulation on AT91SAM7A3 waits for FC_FSR bit 1, EOL */
+ if (at91sam7_wait_status_busy(bank, flashplane, MC_FSR_EOL, 10)&0x0C)
+ {
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+ return ERROR_OK;
+ }
+
+ if (at91sam7_wait_status_busy(bank, flashplane, MC_FSR_FRDY, 10)&0x0C)
+ {
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+ return ERROR_OK;
+}
+
+/* Read device id register, main clock frequency register and fill in driver info structure */
+int at91sam7_read_part_info(struct flash_bank_s *bank)
+{
+ at91sam7_flash_bank_t *at91sam7_info = bank->driver_priv;
+ target_t *target = bank->target;
+ u32 cidr, status;
+ int sectornum;
+
+ if (bank->target->state != TARGET_HALTED)
+ {
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ /* Read and parse chip identification register */
+ target_read_u32(target, DBGU_CIDR, &cidr);
+
+ if (cidr == 0)
+ {
+ WARNING("Cannot identify target as an AT91SAM");
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ at91sam7_info->cidr = cidr;
+ at91sam7_info->cidr_ext = (cidr>>31)&0x0001;
+ at91sam7_info->cidr_nvptyp = (cidr>>28)&0x0007;
+ at91sam7_info->cidr_arch = (cidr>>20)&0x00FF;
+ at91sam7_info->cidr_sramsiz = (cidr>>16)&0x000F;
+ at91sam7_info->cidr_nvpsiz2 = (cidr>>12)&0x000F;
+ at91sam7_info->cidr_nvpsiz = (cidr>>8)&0x000F;
+ at91sam7_info->cidr_eproc = (cidr>>5)&0x0007;
+ at91sam7_info->cidr_version = cidr&0x001F;
+ bank->size = NVPSIZ[at91sam7_info->cidr_nvpsiz];
+ at91sam7_info->target_name = "Unknown";
+
+ /* Support just for bulk erase of a single flash plane, whole device if flash size <= 256k */
+ if (NVPSIZ[at91sam7_info->cidr_nvpsiz]<0x80000) /* Flash size less than 512K, one flash plane */
+ {
+ bank->num_sectors = 1;
+ bank->sectors = malloc(sizeof(flash_sector_t));
+ bank->sectors[0].offset = 0;
+ bank->sectors[0].size = bank->size;
+ bank->sectors[0].is_erased = -1;
+ bank->sectors[0].is_protected = -1;
+ }
+ else /* Flash size 512K or larger, several flash planes */
+ {
+ bank->num_sectors = NVPSIZ[at91sam7_info->cidr_nvpsiz]/0x40000;
+ bank->sectors = malloc(bank->num_sectors*sizeof(flash_sector_t));
+ for (sectornum=0; sectornum<bank->num_sectors; sectornum++)
+ {
+ bank->sectors[sectornum].offset = sectornum*0x40000;
+ bank->sectors[sectornum].size = 0x40000;
+ bank->sectors[sectornum].is_erased = -1;
+ bank->sectors[sectornum].is_protected = -1;
+ }
+ }
+
+
+
+ DEBUG("nvptyp: 0x%3.3x, arch: 0x%4.4x", at91sam7_info->cidr_nvptyp, at91sam7_info->cidr_arch );
+
+ /* Read main and master clock freqency register */
+ at91sam7_read_clock_info(bank);
+
+ at91sam7_info->num_planes = 1;
+ status = at91sam7_get_flash_status(bank, 0);
+ at91sam7_info->securitybit = (status>>4)&0x01;
+ at91sam7_protect_check(bank); /* TODO Check the protect check */
+
+ if (at91sam7_info->cidr_arch == 0x70 )
+ {
+ at91sam7_info->num_nvmbits = 2;
+ at91sam7_info->nvmbits = (status>>8)&0x03;
+ bank->base = 0x100000;
+ bank->bus_width = 4;
+ if (bank->size==0x80000) /* AT91SAM7S512 */
+ {
+ at91sam7_info->target_name = "AT91SAM7S512";
+ at91sam7_info->num_planes = 2;
+ if (at91sam7_info->num_planes != bank->num_sectors)
+ WARNING("Internal error: Number of flash planes and erase sectors does not match, please report");;
+ at91sam7_info->num_lockbits = 2*16;
+ at91sam7_info->pagesize = 256;
+ at91sam7_info->pages_in_lockregion = 64;
+ at91sam7_info->num_pages = 2*16*64;
+ }
+ if (bank->size==0x40000) /* AT91SAM7S256 */
+ {
+ at91sam7_info->target_name = "AT91SAM7S256";
+ at91sam7_info->num_lockbits = 16;
+ at91sam7_info->pagesize = 256;
+ at91sam7_info->pages_in_lockregion = 64;
+ at91sam7_info->num_pages = 16*64;
+ }
+ if (bank->size==0x20000) /* AT91SAM7S128 */
+ {
+ at91sam7_info->target_name = "AT91SAM7S128";
+ at91sam7_info->num_lockbits = 8;
+ at91sam7_info->pagesize = 256;
+ at91sam7_info->pages_in_lockregion = 64;
+ at91sam7_info->num_pages = 8*64;
+ }
+ if (bank->size==0x10000) /* AT91SAM7S64 */
+ {
+ at91sam7_info->target_name = "AT91SAM7S64";
+ at91sam7_info->num_lockbits = 16;
+ at91sam7_info->pagesize = 128;
+ at91sam7_info->pages_in_lockregion = 32;
+ at91sam7_info->num_pages = 16*32;
+ }
+ if (bank->size==0x08000) /* AT91SAM7S321/32 */
+ {
+ at91sam7_info->target_name = "AT91SAM7S321/32";
+ at91sam7_info->num_lockbits = 8;
+ at91sam7_info->pagesize = 128;
+ at91sam7_info->pages_in_lockregion = 32;
+ at91sam7_info->num_pages = 8*32;
+ }
+
+ return ERROR_OK;
+ }
+
+ if (at91sam7_info->cidr_arch == 0x71 )
+ {
+ at91sam7_info->num_nvmbits = 3;
+ at91sam7_info->nvmbits = (status>>8)&0x07;
+ bank->base = 0x100000;
+ bank->bus_width = 4;
+ if (bank->size==0x80000) /* AT91SAM7XC512 */
+ {
+ at91sam7_info->target_name = "AT91SAM7XC512";
+ at91sam7_info->num_planes = 2;
+ if (at91sam7_info->num_planes != bank->num_sectors)
+ WARNING("Internal error: Number of flash planes and erase sectors does not match, please report");;
+ at91sam7_info->num_lockbits = 2*16;
+ at91sam7_info->pagesize = 256;
+ at91sam7_info->pages_in_lockregion = 64;
+ at91sam7_info->num_pages = 2*16*64;
+ }
+ if (bank->size==0x40000) /* AT91SAM7XC256 */
+ {
+ at91sam7_info->target_name = "AT91SAM7XC256";
+ at91sam7_info->num_lockbits = 16;
+ at91sam7_info->pagesize = 256;
+ at91sam7_info->pages_in_lockregion = 64;
+ at91sam7_info->num_pages = 16*64;
+ }
+ if (bank->size==0x20000) /* AT91SAM7XC128 */
+ {
+ at91sam7_info->target_name = "AT91SAM7XC128";
+ at91sam7_info->num_lockbits = 8;
+ at91sam7_info->pagesize = 256;
+ at91sam7_info->pages_in_lockregion = 64;
+ at91sam7_info->num_pages = 8*64;
+ }
+
+ return ERROR_OK;
+ }
+
+ if (at91sam7_info->cidr_arch == 0x72 )
+ {
+ at91sam7_info->num_nvmbits = 3;
+ at91sam7_info->nvmbits = (status>>8)&0x07;
+ bank->base = 0x100000;
+ bank->bus_width = 4;
+ if (bank->size==0x80000) /* AT91SAM7SE512 */
+ {
+ at91sam7_info->target_name = "AT91SAM7SE512";
+ at91sam7_info->num_planes = 2;
+ if (at91sam7_info->num_planes != bank->num_sectors)
+ WARNING("Internal error: Number of flash planes and erase sectors does not match, please report");;
+ at91sam7_info->num_lockbits = 32;
+ at91sam7_info->pagesize = 256;
+ at91sam7_info->pages_in_lockregion = 64;
+ at91sam7_info->num_pages = 32*64;
+ }
+ if (bank->size==0x40000)
+ {
+ at91sam7_info->target_name = "AT91SAM7SE256";
+ at91sam7_info->num_lockbits = 16;
+ at91sam7_info->pagesize = 256;
+ at91sam7_info->pages_in_lockregion = 64;
+ at91sam7_info->num_pages = 16*64;
+ }
+ if (bank->size==0x08000)
+ {
+ at91sam7_info->target_name = "AT91SAM7SE32";
+ at91sam7_info->num_lockbits = 8;
+ at91sam7_info->pagesize = 128;
+ at91sam7_info->pages_in_lockregion = 32;
+ at91sam7_info->num_pages = 8*32;
+ }
+
+ return ERROR_OK;
+ }
+
+ if (at91sam7_info->cidr_arch == 0x75 )
+ {
+ at91sam7_info->num_nvmbits = 3;
+ at91sam7_info->nvmbits = (status>>8)&0x07;
+ bank->base = 0x100000;
+ bank->bus_width = 4;
+ if (bank->size==0x80000) /* AT91SAM7X512 */
+ {
+ at91sam7_info->target_name = "AT91SAM7X512";
+ at91sam7_info->num_planes = 2;
+ if (at91sam7_info->num_planes != bank->num_sectors)
+ WARNING("Internal error: Number of flash planes and erase sectors does not match, please report");;
+ at91sam7_info->num_lockbits = 32;
+ at91sam7_info->pagesize = 256;
+ at91sam7_info->pages_in_lockregion = 64;
+ at91sam7_info->num_pages = 2*16*64;
+ DEBUG("Support for AT91SAM7X512 is experimental in this version!");
+ }
+ if (bank->size==0x40000) /* AT91SAM7X256 */
+ {
+ at91sam7_info->target_name = "AT91SAM7X256";
+ at91sam7_info->num_lockbits = 16;
+ at91sam7_info->pagesize = 256;
+ at91sam7_info->pages_in_lockregion = 64;
+ at91sam7_info->num_pages = 16*64;
+ }
+ if (bank->size==0x20000) /* AT91SAM7X128 */
+ {
+ at91sam7_info->target_name = "AT91SAM7X128";
+ at91sam7_info->num_lockbits = 8;
+ at91sam7_info->pagesize = 256;
+ at91sam7_info->pages_in_lockregion = 64;
+ at91sam7_info->num_pages = 8*64;
+ }
+
+ return ERROR_OK;
+ }
+
+ if (at91sam7_info->cidr_arch == 0x60 )
+ {
+ at91sam7_info->num_nvmbits = 3;
+ at91sam7_info->nvmbits = (status>>8)&0x07;
+ bank->base = 0x100000;
+ bank->bus_width = 4;
+
+ if (bank->size == 0x40000) /* AT91SAM7A3 */
+ {
+ at91sam7_info->target_name = "AT91SAM7A3";
+ at91sam7_info->num_lockbits = 16;
+ at91sam7_info->pagesize = 256;
+ at91sam7_info->pages_in_lockregion = 16;
+ at91sam7_info->num_pages = 16*64;
+ }
+ return ERROR_OK;
+ }
+
+ WARNING("at91sam7 flash only tested for AT91SAM7Sxx series");
+
+ return ERROR_OK;
+}
+
+int at91sam7_erase_check(struct flash_bank_s *bank)
+{
+ at91sam7_flash_bank_t *at91sam7_info = bank->driver_priv;
+
+ if (!at91sam7_info->working_area_size)
+ {
+ }
+ else
+ {
+ }
+
+ return ERROR_OK;
+}
+
+int at91sam7_protect_check(struct flash_bank_s *bank)
+{
+ u32 status;
+ int flashplane;
+
+ at91sam7_flash_bank_t *at91sam7_info = bank->driver_priv;
+
+ if (at91sam7_info->cidr == 0)
+ {
+ at91sam7_read_part_info(bank);
+ }
+
+ if (at91sam7_info->cidr == 0)
+ {
+ WARNING("Cannot identify target as an AT91SAM");
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ for (flashplane=0;flashplane<at91sam7_info->num_planes;flashplane++)
+ {
+ status = at91sam7_get_flash_status(bank, flashplane);
+ at91sam7_info->lockbits[flashplane] = (status >> 16);
+ }
+
+ return ERROR_OK;
+}
+
+/* flash_bank at91sam7 0 0 0 0 <target#>
+ */
+int at91sam7_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank)
+{
+ at91sam7_flash_bank_t *at91sam7_info;
+ int i;
+
+ if (argc < 6)
+ {
+ WARNING("incomplete flash_bank at91sam7 configuration");
+ return ERROR_FLASH_BANK_INVALID;
+ }
+
+ at91sam7_info = malloc(sizeof(at91sam7_flash_bank_t));
+ bank->driver_priv = at91sam7_info;
+ at91sam7_info->probed = 0;
+
+ /* part wasn't probed for info yet */
+ at91sam7_info->cidr = 0;
+ for (i=0;i<4;i++)
+ at91sam7_info->flashmode[i]=0;
+
+ return ERROR_OK;
+}
+
+int at91sam7_erase(struct flash_bank_s *bank, int first, int last)
+{
+ at91sam7_flash_bank_t *at91sam7_info = bank->driver_priv;
+ u8 flashplane;
+
+ if (bank->target->state != TARGET_HALTED)
+ {
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ if (at91sam7_info->cidr == 0)
+ {
+ at91sam7_read_part_info(bank);
+ }
+
+ if (at91sam7_info->cidr == 0)
+ {
+ WARNING("Cannot identify target as an AT91SAM");
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ if ((first < 0) || (last < first) || (last >= bank->num_sectors))
+ {
+ if ((first == 0) && (last == (at91sam7_info->num_lockbits-1)))
+ {
+ WARNING("Sector numbers based on lockbit count, probably a deprecated script");
+ last = bank->num_sectors-1;
+ }
+ else return ERROR_FLASH_SECTOR_INVALID;
+ }
+
+ /* Configure the flash controller timing */
+ at91sam7_read_clock_info(bank);
+ for (flashplane = first; flashplane<=last; flashplane++)
+ {
+ /* Configure the flash controller timing */
+ at91sam7_set_flash_mode(bank, flashplane, FMR_TIMING_FLASH);
+ if (at91sam7_flash_command(bank, flashplane, EA, 0) != ERROR_OK)
+ {
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+ }
+ return ERROR_OK;
+
+}
+
+int at91sam7_protect(struct flash_bank_s *bank, int set, int first, int last)
+{
+ u32 cmd, pagen;
+ u8 flashplane;
+ int lockregion;
+
+ at91sam7_flash_bank_t *at91sam7_info = bank->driver_priv;
+
+ if (bank->target->state != TARGET_HALTED)
+ {
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ if ((first < 0) || (last < first) || (last >= at91sam7_info->num_lockbits))
+ {
+ return ERROR_FLASH_SECTOR_INVALID;
+ }
+
+ if (at91sam7_info->cidr == 0)
+ {
+ at91sam7_read_part_info(bank);
+ }
+
+ if (at91sam7_info->cidr == 0)
+ {
+ WARNING("Cannot identify target as an AT91SAM");
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ at91sam7_read_clock_info(bank);
+
+ for (lockregion=first;lockregion<=last;lockregion++)
+ {
+ pagen = lockregion*at91sam7_info->pages_in_lockregion;
+ flashplane = (pagen>>10)&0x03;
+ /* Configure the flash controller timing */
+ at91sam7_set_flash_mode(bank, flashplane, FMR_TIMING_NVBITS);
+
+ if (set)
+ cmd = SLB;
+ else
+ cmd = CLB;
+
+ if (at91sam7_flash_command(bank, flashplane, cmd, pagen) != ERROR_OK)
+ {
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+ }
+
+ at91sam7_protect_check(bank);
+
+ return ERROR_OK;
+}
+
+
+int at91sam7_write(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count)
+{
+ at91sam7_flash_bank_t *at91sam7_info = bank->driver_priv;
+ target_t *target = bank->target;
+ u32 dst_min_alignment, wcount, bytes_remaining = count;
+ u32 first_page, last_page, pagen, buffer_pos;
+ u8 flashplane;
+
+ if (at91sam7_info->cidr == 0)
+ {
+ at91sam7_read_part_info(bank);
+ }
+
+ if (at91sam7_info->cidr == 0)
+ {
+ WARNING("Cannot identify target as an AT91SAM");
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ if (offset + count > bank->size)
+ return ERROR_FLASH_DST_OUT_OF_BANK;
+
+ dst_min_alignment = at91sam7_info->pagesize;
+
+ if (offset % dst_min_alignment)
+ {
+ WARNING("offset 0x%x breaks required alignment 0x%x", offset, dst_min_alignment);
+ return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
+ }
+
+ if (at91sam7_info->cidr_arch == 0)
+ return ERROR_FLASH_BANK_NOT_PROBED;
+
+ first_page = offset/dst_min_alignment;
+ last_page = CEIL(offset + count, dst_min_alignment);
+
+ DEBUG("first_page: %i, last_page: %i, count %i", first_page, last_page, count);
+
+ at91sam7_read_clock_info(bank);
+
+ for (pagen=first_page; pagen<last_page; pagen++)
+ {
+ if (bytes_remaining<dst_min_alignment)
+ count = bytes_remaining;
+ else
+ count = dst_min_alignment;
+ bytes_remaining -= count;
+
+ /* Write one block to the PageWriteBuffer */
+ buffer_pos = (pagen-first_page)*dst_min_alignment;
+ wcount = CEIL(count,4);
+ target->type->write_memory(target, bank->base+pagen*dst_min_alignment, 4, wcount, buffer+buffer_pos);
+ flashplane = (pagen>>10)&0x3;
+
+ /* Configure the flash controller timing */
+ at91sam7_set_flash_mode(bank, flashplane, FMR_TIMING_FLASH);
+ /* Send Write Page command to Flash Controller */
+ if (at91sam7_flash_command(bank, flashplane, WP, pagen) != ERROR_OK)
+ {
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+ DEBUG("Write flash plane:%i page number:%i", flashplane, pagen);
+ }
+
+ return ERROR_OK;
+}
+
+
+int at91sam7_probe(struct flash_bank_s *bank)
+{
+ /* we can't probe on an at91sam7
+ * if this is an at91sam7, it has the configured flash
+ */
+ at91sam7_flash_bank_t *at91sam7_info = bank->driver_priv;
+ at91sam7_info->probed = 0;
+
+ if (at91sam7_info->cidr == 0)
+ {
+ at91sam7_read_part_info(bank);
+ }
+
+ if (at91sam7_info->cidr == 0)
+ {
+ WARNING("Cannot identify target as an AT91SAM");
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ at91sam7_info->probed = 1;
+
+ return ERROR_OK;
+}
+
+
+int at91sam7_auto_probe(struct flash_bank_s *bank)
+{
+ at91sam7_flash_bank_t *at91sam7_info = bank->driver_priv;
+ if (at91sam7_info->probed)
+ return ERROR_OK;
+ return at91sam7_probe(bank);
+}
+
+int at91sam7_info(struct flash_bank_s *bank, char *buf, int buf_size)
+{
+ int printed, flashplane;
+ at91sam7_flash_bank_t *at91sam7_info = bank->driver_priv;
+
+ at91sam7_read_part_info(bank);
+
+ if (at91sam7_info->cidr == 0)
+ {
+ printed = snprintf(buf, buf_size, "Cannot identify target as an AT91SAM\n");
+ buf += printed;
+ buf_size -= printed;
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ printed = snprintf(buf, buf_size, "\nat91sam7 information: Chip is %s\n",at91sam7_info->target_name);
+ buf += printed;
+ buf_size -= printed;
+
+ printed = snprintf(buf, buf_size, "cidr: 0x%8.8x, arch: 0x%4.4x, eproc: %s, version:0x%3.3x, flashsize: 0x%8.8x\n",
+ at91sam7_info->cidr, at91sam7_info->cidr_arch, EPROC[at91sam7_info->cidr_eproc], at91sam7_info->cidr_version, bank->size);
+ buf += printed;
+ buf_size -= printed;
+
+ printed = snprintf(buf, buf_size, "master clock(estimated): %ikHz \n", at91sam7_info->mck_freq / 1000);
+ buf += printed;
+ buf_size -= printed;
+
+ if (at91sam7_info->num_planes>1) {
+ printed = snprintf(buf, buf_size, "flashplanes: %i, pagesize: %i, lock regions: %i, pages in lock region: %i \n",
+ at91sam7_info->num_planes, at91sam7_info->pagesize, at91sam7_info->num_lockbits, at91sam7_info->num_pages/at91sam7_info->num_lockbits);
+ buf += printed;
+ buf_size -= printed;
+ for (flashplane=0; flashplane<at91sam7_info->num_planes; flashplane++)
+ {
+ printed = snprintf(buf, buf_size, "lockbits[%i]: 0x%4.4x, ", flashplane, at91sam7_info->lockbits[flashplane]);
+ buf += printed;
+ buf_size -= printed;
+ }
+ }
+ else
+ if (at91sam7_info->num_lockbits>0) {
+ printed = snprintf(buf, buf_size, "pagesize: %i, lockbits: %i 0x%4.4x, pages in lock region: %i \n",
+ at91sam7_info->pagesize, at91sam7_info->num_lockbits, at91sam7_info->lockbits[0], at91sam7_info->num_pages/at91sam7_info->num_lockbits);
+ buf += printed;
+ buf_size -= printed;
+ }
+
+ printed = snprintf(buf, buf_size, "securitybit: %i, nvmbits: 0x%1.1x\n", at91sam7_info->securitybit, at91sam7_info->nvmbits);
+ buf += printed;
+ buf_size -= printed;
+
+ return ERROR_OK;
+}
+
+/*
+* On AT91SAM7S: When the gpnvm bits are set with
+* > at91sam7 gpnvm 0 bitnr set
+* the changes are not visible in the flash controller status register MC_FSR
+* until the processor has been reset.
+* On the Olimex board this requires a power cycle.
+* Note that the AT91SAM7S has the following errata (doc6175.pdf sec 14.1.3):
+* The maximum number of write/erase cycles for Non Volatile Memory bits is 100. This includes
+* Lock Bits (LOCKx), General Purpose NVM bits (GPNVMx) and the Security Bit.
+*/
+int at91sam7_handle_gpnvm_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ flash_bank_t *bank;
+ int bit;
+ u8 flashcmd;
+ u32 status;
+ char *value;
+ at91sam7_flash_bank_t *at91sam7_info;
+
+ if (argc < 3)
+ {
+ command_print(cmd_ctx, "at91sam7 gpnvm <num> <bit> <set|clear>");
+ return ERROR_OK;
+ }
+
+ bank = get_flash_bank_by_num(strtoul(args[0], NULL, 0));
+ bit = atoi(args[1]);
+ value = args[2];
+
+ if (!bank)
+ {
+ command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);
+ return ERROR_OK;
+ }
+
+ at91sam7_info = bank->driver_priv;
+
+ if (bank->target->state != TARGET_HALTED)
+ {
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ if (at91sam7_info->cidr == 0)
+ {
+ at91sam7_read_part_info(bank);
+ }
+
+ if (at91sam7_info->cidr == 0)
+ {
+ WARNING("Cannot identify target as an AT91SAM");
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ if ((bit<0) || (at91sam7_info->num_nvmbits <= bit))
+ {
+ command_print(cmd_ctx, "gpnvm bit '#%s' is out of bounds for target %s", args[1],at91sam7_info->target_name);
+ return ERROR_OK;
+ }
+
+ if (strcmp(value, "set") == 0)
+ {
+ flashcmd = SGPB;
+ }
+ else if (strcmp(value, "clear") == 0)
+ {
+ flashcmd = CGPB;
+ }
+ else
+ {
return ERROR_COMMAND_SYNTAX_ERROR;
- }\r
-\r
- /* Configure the flash controller timing */\r
- at91sam7_read_clock_info(bank); \r
- at91sam7_set_flash_mode(bank, 0, FMR_TIMING_NVBITS);\r
- \r
- if (at91sam7_flash_command(bank, 0, flashcmd, (u16)(bit)) != ERROR_OK) \r
- {\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- } \r
-\r
- status = at91sam7_get_flash_status(bank, 0);\r
- DEBUG("at91sam7_handle_gpnvm_command: cmd 0x%x, value 0x%x, status 0x%x \n",flashcmd,bit,status);\r
- at91sam7_info->nvmbits = (status>>8)&((1<<at91sam7_info->num_nvmbits)-1);\r
-\r
- return ERROR_OK;\r
-}\r
+ }
+
+ /* Configure the flash controller timing */
+ at91sam7_read_clock_info(bank);
+ at91sam7_set_flash_mode(bank, 0, FMR_TIMING_NVBITS);
+
+ if (at91sam7_flash_command(bank, 0, flashcmd, (u16)(bit)) != ERROR_OK)
+ {
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ status = at91sam7_get_flash_status(bank, 0);
+ DEBUG("at91sam7_handle_gpnvm_command: cmd 0x%x, value 0x%x, status 0x%x \n",flashcmd,bit,status);
+ at91sam7_info->nvmbits = (status>>8)&((1<<at91sam7_info->num_nvmbits)-1);
+
+ return ERROR_OK;
+}
-/***************************************************************************\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 *
+ * *
+ * 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 <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_banks_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_protect_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+int handle_flash_auto_erase_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;
+
+flash_driver_t *flash_drivers[] =
+{
+ &lpc2000_flash,
+ &cfi_flash,
+ &at91sam7_flash,
+ &str7x_flash,
+ &str9x_flash,
+ &stellaris_flash,
+ &str9xpec_flash,
+ &stm32x_flash,
+ &tms470_flash,
+ NULL,
+};
+
+flash_bank_t *flash_banks;
+static command_t *flash_cmd;
+static int auto_erase = 0;
+
+/* 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=ERROR_OK;
+ if (bank->target->state != TARGET_HALTED)
+ {
+ ERROR("target not halted - aborting flash write");
+ retval=ERROR_TARGET_NOT_HALTED;
+ } else
+ {
+ retval=bank->driver->write(bank, buffer, offset, count);
+ }
+ if (retval!=ERROR_OK)
+ {
+ ERROR("Writing to flash bank at address 0x%08x at offset 0x%8.8x", bank->base, offset);
+ }
+ return retval;
+}
+
+static int flash_driver_erase(struct flash_bank_s *bank, int first, int last)
+{
+ int retval=ERROR_OK;
+ if (bank->target->state != TARGET_HALTED)
+ {
+ ERROR("target not halted - aborting flash erase");
+ retval=ERROR_TARGET_NOT_HALTED;
+ } else if ((first < 0) || (last < first) || (last >= bank->num_sectors))
+ {
+ ERROR("invalid flash sector");
+ retval=ERROR_FLASH_SECTOR_INVALID;
+ } else
+ {
+ retval=bank->driver->erase(bank, first, last);
+ }
+ if (retval!=ERROR_OK)
+ {
+ ERROR("Failed erasing banks %d to %d", first, last);
+ }
+ return retval;
+}
+
+int flash_driver_protect(struct flash_bank_s *bank, int set, int first, int last)
+{
+ int retval;
+ if (bank->target->state != TARGET_HALTED)
+ {
+ ERROR("target not halted - aborting flash erase");
+ retval=ERROR_TARGET_NOT_HALTED;
+ } else if ((first < 0) || (last < first) || (last >= bank->num_sectors))
+ {
+ ERROR("invalid flash sector");
+ retval=ERROR_FLASH_SECTOR_INVALID;
+ } else
+ {
+ retval=bank->driver->protect(bank, set, first, last);
+ }
+ if (retval!=ERROR_OK)
+ {
+ ERROR("Failed protecting banks %d to %d", first, last);
+ }
+ 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 ...]");
+ register_command(cmd_ctx, flash_cmd, "auto_erase", handle_flash_auto_erase_command, COMMAND_ANY,
+ "auto erase flash sectors <on|off>");
+ return ERROR_OK;
+}
+
+int flash_init_drivers(struct command_context_s *cmd_ctx)
+{
+ if (flash_banks)
+ {
+ register_command(cmd_ctx, flash_cmd, "banks", handle_flash_banks_command, COMMAND_EXEC,
+ "list configured 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, "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 <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;
+ }
+ }
+ ERROR("Flash bank %d does not exist", num);
+ return NULL;
+}
+
+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)
+ {
+ 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)
+ {
+ 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)
+ {
+ 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)
+ {
+ 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)
+ {
+ ERROR("flash driver '%s' not found", args[0]);
+ exit(-1);
+ }
+
+ return ERROR_OK;
+}
+
+int handle_flash_banks_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ flash_bank_t *p;
+ int i = 0;
+
+ if (!flash_banks)
+ {
+ command_print(cmd_ctx, "no flash banks configured");
+ return ERROR_OK;
+ }
+
+ for (p = flash_banks; p; p = p->next)
+ {
+ 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);
+ }
+
+ 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;
+
+ 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 */
+ p->driver->auto_probe(p);
+
+ 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 *erase_state, *protect_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";
+
+ 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, %s",
+ j, p->sectors[j].offset, p->sectors[j].size, p->sectors[j].size>>10,
+ erase_state, protect_state);
+ }
+
+ p->driver->info(p, buf, 1024);
+ command_print(cmd_ctx, "%s", buf);
+ }
+ }
+
+ 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)
+ {
+ 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);
+ }
+ }
+
+ 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_COMMAND_SYNTAX_ERROR;
+ }
+
+ /* 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;
+
+ }
+
+ if (!target)
+ {
+ ERROR("no target selected");
+ return ERROR_OK;
+ }
+
+ 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)
+ {
+ command_print(cmd_ctx, "image_open error: %s", image.error_str);
+ 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_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)
+ {
+ command_print(cmd_ctx, "flash write_binary error: %s", fileio.error_str);
+ return ERROR_OK;
+ }
+
+ buffer = malloc(fileio.size);
+ if (fileio_read(&fileio, fileio.size, buffer, &buf_cnt) != ERROR_OK)
+ {
+ command_print(cmd_ctx, "flash write_binary error: %s", fileio.error_str);
+ return ERROR_OK;
+ }
+
+ retval = flash_driver_write(p, buffer, offset, buf_cnt);
+
+ free(buffer);
+
+ 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)
+ {
+ 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;
+ }
+ 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)
+ 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;
+
+ int section;
+ u32 section_offset;
+ flash_bank_t *c;
+
+ 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();
+ }
+
+ /* 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;
+
+ if (image->sections[section].size == 0)
+ {
+ 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;
+ 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))
+ {
+ DEBUG("section %d out of order(very slightly surprising, but supported)", section_last + 1);
+ break;
+ }
+ if (image->sections[section_last + 1].base_address != (run_address + run_size))
+ break;
+ run_size += image->sections[++section_last].size;
+ }
+
+ /* 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);
+
+ return retval;
+ }
+
+ 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)
+ {
+ return retval; /* abort operation */
+ }
+
+ if (written != NULL)
+ *written += run_size; /* add run size to total written counter */
+ }
+
+ return ERROR_OK;
+}
+
+int handle_flash_auto_erase_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ if (argc != 1)
+ {
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ }
+
+ if (strcmp(args[0], "on") == 0)
+ auto_erase = 1;
+ else if (strcmp(args[0], "off") == 0)
+ auto_erase = 0;
+ else
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ return ERROR_OK;
+}
-/***************************************************************************\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
-#ifndef FLASH_H\r
-#define FLASH_H\r
-\r
-#include "target.h"\r
-#include "image.h"\r
-\r
-#define FLASH_MAX_ERROR_STR (128)\r
-\r
-typedef struct flash_sector_s\r
-{\r
- u32 offset;\r
- u32 size;\r
- int is_erased;\r
- int is_protected;\r
-} flash_sector_t;\r
-\r
-struct flash_bank_s;\r
-\r
-typedef struct flash_driver_s\r
-{\r
- char *name;\r
- int (*register_commands)(struct command_context_s *cmd_ctx);\r
- int (*flash_bank_command)(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank);\r
- /* low level flash erase. Only invoke from flash_driver_erase()\r
- * \r
- * Will only be invoked when target is halted. \r
- */\r
- int (*erase)(struct flash_bank_s *bank, int first, int last);\r
- /* invoked only from flash_driver_protect().\r
- * \r
- * Only invoked if target is halted\r
- */\r
- int (*protect)(struct flash_bank_s *bank, int set, int first, int last);\r
- /* low level flash write. Will only be invoked if the target is halted.\r
- * use the flash_driver_write() wrapper to invoke.\r
- */\r
- int (*write)(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count);\r
- int (*probe)(struct flash_bank_s *bank);\r
- int (*erase_check)(struct flash_bank_s *bank);\r
- int (*protect_check)(struct flash_bank_s *bank);\r
- int (*info)(struct flash_bank_s *bank, char *buf, int buf_size);\r
- int (*auto_probe)(struct flash_bank_s *bank);\r
-} flash_driver_t;\r
-\r
-typedef struct flash_bank_s\r
-{\r
- target_t *target;\r
- flash_driver_t *driver;\r
- void *driver_priv;\r
- u32 base;\r
- u32 size;\r
- int chip_width;\r
- int bus_width;\r
- int num_sectors;\r
- flash_sector_t *sectors;\r
- struct flash_bank_s *next;\r
-} flash_bank_t;\r
-\r
-extern int flash_register_commands(struct command_context_s *cmd_ctx);\r
-extern int flash_init_drivers(struct command_context_s *cmd_ctx);\r
-\r
-extern int flash_erase_address_range(target_t *target, u32 addr, u32 length);\r
-extern int flash_write(target_t *target, image_t *image, u32 *written, int erase);\r
-extern void flash_set_dirty(void);\r
-\r
-extern flash_bank_t *get_flash_bank_by_num(int num);\r
-extern flash_bank_t *get_flash_bank_by_addr(target_t *target, u32 addr);\r
-\r
-#define ERROR_FLASH_BANK_INVALID (-900)\r
-#define ERROR_FLASH_SECTOR_INVALID (-901)\r
-#define ERROR_FLASH_OPERATION_FAILED (-902)\r
-#define ERROR_FLASH_DST_OUT_OF_BANK (-903)\r
-#define ERROR_FLASH_DST_BREAKS_ALIGNMENT (-904)\r
-#define ERROR_FLASH_BUSY (-905)\r
-#define ERROR_FLASH_SECTOR_NOT_ERASED (-906)\r
-#define ERROR_FLASH_BANK_NOT_PROBED (-907)\r
-\r
-#endif /* FLASH_H */\r
+/***************************************************************************
+ * Copyright (C) 2005 by Dominic Rath *
+ * Dominic.Rath@gmx.de *
+ * *
+ * 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. *
+ ***************************************************************************/
+#ifndef FLASH_H
+#define FLASH_H
+
+#include "target.h"
+#include "image.h"
+
+#define FLASH_MAX_ERROR_STR (128)
+
+typedef struct flash_sector_s
+{
+ u32 offset;
+ u32 size;
+ int is_erased;
+ int is_protected;
+} flash_sector_t;
+
+struct flash_bank_s;
+
+typedef struct flash_driver_s
+{
+ char *name;
+ int (*register_commands)(struct command_context_s *cmd_ctx);
+ int (*flash_bank_command)(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank);
+ /* low level flash erase. Only invoke from flash_driver_erase()
+ *
+ * Will only be invoked when target is halted.
+ */
+ int (*erase)(struct flash_bank_s *bank, int first, int last);
+ /* invoked only from flash_driver_protect().
+ *
+ * Only invoked if target is halted
+ */
+ int (*protect)(struct flash_bank_s *bank, int set, int first, int last);
+ /* low level flash write. Will only be invoked if the target is halted.
+ * use the flash_driver_write() wrapper to invoke.
+ */
+ int (*write)(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count);
+ int (*probe)(struct flash_bank_s *bank);
+ int (*erase_check)(struct flash_bank_s *bank);
+ int (*protect_check)(struct flash_bank_s *bank);
+ int (*info)(struct flash_bank_s *bank, char *buf, int buf_size);
+ int (*auto_probe)(struct flash_bank_s *bank);
+} flash_driver_t;
+
+typedef struct flash_bank_s
+{
+ target_t *target;
+ flash_driver_t *driver;
+ void *driver_priv;
+ u32 base;
+ u32 size;
+ int chip_width;
+ int bus_width;
+ int num_sectors;
+ flash_sector_t *sectors;
+ struct flash_bank_s *next;
+} flash_bank_t;
+
+extern int flash_register_commands(struct command_context_s *cmd_ctx);
+extern int flash_init_drivers(struct command_context_s *cmd_ctx);
+
+extern int flash_erase_address_range(target_t *target, u32 addr, u32 length);
+extern int flash_write(target_t *target, image_t *image, u32 *written, int erase);
+extern void flash_set_dirty(void);
+
+extern flash_bank_t *get_flash_bank_by_num(int num);
+extern flash_bank_t *get_flash_bank_by_addr(target_t *target, u32 addr);
+
+#define ERROR_FLASH_BANK_INVALID (-900)
+#define ERROR_FLASH_SECTOR_INVALID (-901)
+#define ERROR_FLASH_OPERATION_FAILED (-902)
+#define ERROR_FLASH_DST_OUT_OF_BANK (-903)
+#define ERROR_FLASH_DST_BREAKS_ALIGNMENT (-904)
+#define ERROR_FLASH_BUSY (-905)
+#define ERROR_FLASH_SECTOR_NOT_ERASED (-906)
+#define ERROR_FLASH_BANK_NOT_PROBED (-907)
+
+#endif /* FLASH_H */
-/***************************************************************************\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 "lpc2000.h"\r
-\r
-#include "flash.h"\r
-#include "target.h"\r
-#include "log.h"\r
-#include "armv4_5.h"\r
-#include "algorithm.h"\r
-#include "binarybuffer.h"\r
-\r
-#include <stdlib.h>\r
-#include <string.h>\r
-\r
-/* flash programming support for Philips LPC2xxx devices\r
- * currently supported devices:\r
- * variant 1 (lpc2000_v1):\r
- * - 2104|5|6\r
- * - 2114|9\r
- * - 2124|9\r
- * - 2194\r
- * - 2212|4\r
- * - 2292|4\r
- *\r
- * variant 2 (lpc2000_v2):\r
- * - 213x\r
- * - 214x\r
- * - 2101|2|3\r
- * - 2364|6|8\r
- * - 2378\r
- */\r
-\r
-int lpc2000_register_commands(struct command_context_s *cmd_ctx);\r
-int lpc2000_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank);\r
-int lpc2000_erase(struct flash_bank_s *bank, int first, int last);\r
-int lpc2000_protect(struct flash_bank_s *bank, int set, int first, int last);\r
-int lpc2000_write(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count);\r
-int lpc2000_probe(struct flash_bank_s *bank);\r
-int lpc2000_erase_check(struct flash_bank_s *bank);\r
-int lpc2000_protect_check(struct flash_bank_s *bank);\r
-int lpc2000_info(struct flash_bank_s *bank, char *buf, int buf_size);\r
- \r
-int lpc2000_handle_part_id_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-\r
-flash_driver_t lpc2000_flash =\r
-{\r
- .name = "lpc2000",\r
- .register_commands = lpc2000_register_commands,\r
- .flash_bank_command = lpc2000_flash_bank_command,\r
- .erase = lpc2000_erase,\r
- .protect = lpc2000_protect,\r
- .write = lpc2000_write,\r
- .probe = lpc2000_probe,\r
- .auto_probe = lpc2000_probe,\r
- .erase_check = lpc2000_erase_check,\r
- .protect_check = lpc2000_protect_check,\r
- .info = lpc2000_info\r
-};\r
-\r
-int lpc2000_register_commands(struct command_context_s *cmd_ctx)\r
-{\r
- command_t *lpc2000_cmd = register_command(cmd_ctx, NULL, "lpc2000", NULL, COMMAND_ANY, NULL);\r
- \r
- register_command(cmd_ctx, lpc2000_cmd, "part_id", lpc2000_handle_part_id_command, COMMAND_EXEC,\r
- "print part id of lpc2000 flash bank <num>");\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int lpc2000_build_sector_list(struct flash_bank_s *bank)\r
-{\r
- lpc2000_flash_bank_t *lpc2000_info = bank->driver_priv;\r
- \r
- /* default to a 4096 write buffer */\r
- lpc2000_info->cmd51_max_buffer = 4096;\r
- \r
- if (lpc2000_info->variant == 1)\r
- {\r
- int i = 0;\r
- u32 offset = 0;\r
- \r
- /* variant 1 has different layout for 128kb and 256kb flashes */\r
- if (bank->size == 128 * 1024)\r
- {\r
- bank->num_sectors = 16;\r
- bank->sectors = malloc(sizeof(flash_sector_t) * 16);\r
- for (i = 0; i < 16; i++)\r
- {\r
- bank->sectors[i].offset = offset;\r
- bank->sectors[i].size = 8 * 1024;\r
- offset += bank->sectors[i].size;\r
- bank->sectors[i].is_erased = -1;\r
- bank->sectors[i].is_protected = 1;\r
- }\r
- }\r
- else if (bank->size == 256 * 1024)\r
- {\r
- bank->num_sectors = 18;\r
- bank->sectors = malloc(sizeof(flash_sector_t) * 18);\r
- \r
- for (i = 0; i < 8; i++)\r
- {\r
- bank->sectors[i].offset = offset;\r
- bank->sectors[i].size = 8 * 1024;\r
- offset += bank->sectors[i].size;\r
- bank->sectors[i].is_erased = -1;\r
- bank->sectors[i].is_protected = 1;\r
- }\r
- for (i = 8; i < 10; i++)\r
- {\r
- bank->sectors[i].offset = offset;\r
- bank->sectors[i].size = 64 * 1024;\r
- offset += bank->sectors[i].size;\r
- bank->sectors[i].is_erased = -1;\r
- bank->sectors[i].is_protected = 1;\r
- }\r
- for (i = 10; i < 18; i++)\r
- {\r
- bank->sectors[i].offset = offset;\r
- bank->sectors[i].size = 8 * 1024;\r
- offset += bank->sectors[i].size;\r
- bank->sectors[i].is_erased = -1;\r
- bank->sectors[i].is_protected = 1;\r
- }\r
- }\r
- else\r
- {\r
- ERROR("BUG: unknown bank->size encountered");\r
- exit(-1);\r
- }\r
- }\r
- else if (lpc2000_info->variant == 2)\r
- {\r
- int num_sectors;\r
- int i;\r
- u32 offset = 0;\r
- \r
- /* variant 2 has a uniform layout, only number of sectors differs */\r
- switch (bank->size)\r
- {\r
- case 4 * 1024:\r
- lpc2000_info->cmd51_max_buffer = 1024;\r
- num_sectors = 1;\r
- break;\r
- case 8 * 1024:\r
- lpc2000_info->cmd51_max_buffer = 1024;\r
- num_sectors = 2;\r
- break;\r
- case 16 * 1024:\r
- num_sectors = 4;\r
- break;\r
- case 32 * 1024:\r
- num_sectors = 8;\r
- break;\r
- case 64 * 1024:\r
- num_sectors = 9;\r
- break;\r
- case 128 * 1024:\r
- num_sectors = 11;\r
- break;\r
- case 256 * 1024:\r
- num_sectors = 15;\r
- break;\r
- case 512 * 1024:\r
- case 500 * 1024:\r
- num_sectors = 27;\r
- break;\r
- default:\r
- ERROR("BUG: unknown bank->size encountered");\r
- exit(-1);\r
- break;\r
- }\r
- \r
- bank->num_sectors = num_sectors;\r
- bank->sectors = malloc(sizeof(flash_sector_t) * num_sectors);\r
- \r
- for (i = 0; i < num_sectors; i++)\r
- {\r
- if ((i >= 0) && (i < 8))\r
- {\r
- bank->sectors[i].offset = offset;\r
- bank->sectors[i].size = 4 * 1024;\r
- offset += bank->sectors[i].size;\r
- bank->sectors[i].is_erased = -1;\r
- bank->sectors[i].is_protected = 1;\r
- }\r
- if ((i >= 8) && (i < 22))\r
- {\r
- bank->sectors[i].offset = offset;\r
- bank->sectors[i].size = 32 * 1024;\r
- offset += bank->sectors[i].size;\r
- bank->sectors[i].is_erased = -1;\r
- bank->sectors[i].is_protected = 1;\r
- }\r
- if ((i >= 22) && (i < 27))\r
- {\r
- bank->sectors[i].offset = offset;\r
- bank->sectors[i].size = 4 * 1024;\r
- offset += bank->sectors[i].size;\r
- bank->sectors[i].is_erased = -1;\r
- bank->sectors[i].is_protected = 1;\r
- }\r
- }\r
- }\r
- else\r
- {\r
- ERROR("BUG: unknown lpc2000_info->variant encountered");\r
- exit(-1);\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-/* call LPC2000 IAP function\r
- * uses 172 bytes working area\r
- * 0x0 to 0x7: jump gate (BX to thumb state, b -2 to wait)\r
- * 0x8 to 0x1f: command parameter table\r
- * 0x20 to 0x2b: command result table\r
- * 0x2c to 0xac: stack (only 128b needed)\r
- */\r
-int lpc2000_iap_call(flash_bank_t *bank, int code, u32 param_table[5], u32 result_table[2])\r
-{\r
- lpc2000_flash_bank_t *lpc2000_info = bank->driver_priv;\r
- target_t *target = bank->target;\r
- mem_param_t mem_params[2];\r
- reg_param_t reg_params[5];\r
- armv4_5_algorithm_t armv4_5_info;\r
- u32 status_code;\r
- \r
- /* regrab previously allocated working_area, or allocate a new one */\r
- if (!lpc2000_info->iap_working_area)\r
- {\r
- u8 jump_gate[8];\r
- \r
- /* make sure we have a working area */\r
- if (target_alloc_working_area(target, 172, &lpc2000_info->iap_working_area) != ERROR_OK)\r
- {\r
- ERROR("no working area specified, can't write LPC2000 internal flash");\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
- \r
- /* write IAP code to working area */\r
- target_buffer_set_u32(target, jump_gate, ARMV4_5_BX(12));\r
- target_buffer_set_u32(target, jump_gate + 4, ARMV4_5_B(0xfffffe, 0));\r
- target->type->write_memory(target, lpc2000_info->iap_working_area->address, 4, 2, jump_gate);\r
- }\r
- \r
- armv4_5_info.common_magic = ARMV4_5_COMMON_MAGIC;\r
- armv4_5_info.core_mode = ARMV4_5_MODE_SVC;\r
- armv4_5_info.core_state = ARMV4_5_STATE_ARM;\r
- \r
- /* command parameter table */\r
- init_mem_param(&mem_params[0], lpc2000_info->iap_working_area->address + 8, 4 * 6, PARAM_OUT);\r
- target_buffer_set_u32(target, mem_params[0].value, code);\r
- target_buffer_set_u32(target, mem_params[0].value + 0x4, param_table[0]);\r
- target_buffer_set_u32(target, mem_params[0].value + 0x8, param_table[1]);\r
- target_buffer_set_u32(target, mem_params[0].value + 0xc, param_table[2]);\r
- target_buffer_set_u32(target, mem_params[0].value + 0x10, param_table[3]);\r
- target_buffer_set_u32(target, mem_params[0].value + 0x14, param_table[4]);\r
- \r
- init_reg_param(®_params[0], "r0", 32, PARAM_OUT);\r
- buf_set_u32(reg_params[0].value, 0, 32, lpc2000_info->iap_working_area->address + 0x8);\r
- \r
- /* command result table */\r
- init_mem_param(&mem_params[1], lpc2000_info->iap_working_area->address + 0x20, 4 * 3, PARAM_IN);\r
- \r
- init_reg_param(®_params[1], "r1", 32, PARAM_OUT);\r
- buf_set_u32(reg_params[1].value, 0, 32, lpc2000_info->iap_working_area->address + 0x20);\r
- \r
- /* IAP entry point */\r
- init_reg_param(®_params[2], "r12", 32, PARAM_OUT);\r
- buf_set_u32(reg_params[2].value, 0, 32, 0x7ffffff1);\r
- \r
- /* IAP stack */\r
- init_reg_param(®_params[3], "r13_svc", 32, PARAM_OUT);\r
- buf_set_u32(reg_params[3].value, 0, 32, lpc2000_info->iap_working_area->address + 0xac);\r
-\r
- /* return address */\r
- init_reg_param(®_params[4], "lr_svc", 32, PARAM_OUT);\r
- buf_set_u32(reg_params[4].value, 0, 32, lpc2000_info->iap_working_area->address + 0x4);\r
- \r
- target->type->run_algorithm(target, 2, mem_params, 5, reg_params, lpc2000_info->iap_working_area->address, lpc2000_info->iap_working_area->address + 0x4, 10000, &armv4_5_info);\r
- \r
- status_code = buf_get_u32(mem_params[1].value, 0, 32);\r
- result_table[0] = target_buffer_get_u32(target, mem_params[1].value);\r
- result_table[1] = target_buffer_get_u32(target, mem_params[1].value + 4);\r
- \r
- destroy_mem_param(&mem_params[0]);\r
- destroy_mem_param(&mem_params[1]);\r
- \r
- destroy_reg_param(®_params[0]);\r
- destroy_reg_param(®_params[1]);\r
- destroy_reg_param(®_params[2]);\r
- destroy_reg_param(®_params[3]);\r
- destroy_reg_param(®_params[4]);\r
- \r
- return status_code;\r
-}\r
-\r
-int lpc2000_iap_blank_check(struct flash_bank_s *bank, int first, int last)\r
-{\r
- u32 param_table[5];\r
- u32 result_table[2];\r
- int status_code;\r
- int i;\r
- \r
- if ((first < 0) || (last > bank->num_sectors))\r
- return ERROR_FLASH_SECTOR_INVALID;\r
- \r
- for (i = first; i <= last; i++)\r
- {\r
- /* check single sector */\r
- param_table[0] = param_table[1] = i;\r
- status_code = lpc2000_iap_call(bank, 53, param_table, result_table);\r
- \r
- switch (status_code)\r
- {\r
- case ERROR_FLASH_OPERATION_FAILED:\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- case LPC2000_CMD_SUCCESS:\r
- bank->sectors[i].is_erased = 1;\r
- break;\r
- case LPC2000_SECTOR_NOT_BLANK:\r
- bank->sectors[i].is_erased = 0;\r
- break;\r
- case LPC2000_INVALID_SECTOR:\r
- bank->sectors[i].is_erased = 0;\r
- break;\r
- case LPC2000_BUSY:\r
- return ERROR_FLASH_BUSY;\r
- break;\r
- default:\r
- ERROR("BUG: unknown LPC2000 status code");\r
- exit(-1);\r
- }\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-/* flash bank lpc2000 <base> <size> 0 0 <target#> <lpc_variant> <cclk> [calc_checksum]\r
- */\r
-int lpc2000_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank)\r
-{\r
- lpc2000_flash_bank_t *lpc2000_info;\r
- \r
- if (argc < 8)\r
- {\r
- WARNING("incomplete flash_bank lpc2000 configuration");\r
- return ERROR_FLASH_BANK_INVALID;\r
- }\r
- \r
- lpc2000_info = malloc(sizeof(lpc2000_flash_bank_t));\r
- bank->driver_priv = lpc2000_info;\r
- \r
- if (strcmp(args[6], "lpc2000_v1") == 0)\r
- {\r
- lpc2000_info->variant = 1;\r
- lpc2000_info->cmd51_dst_boundary = 512;\r
- lpc2000_info->cmd51_can_256b = 0;\r
- lpc2000_info->cmd51_can_8192b = 1;\r
- }\r
- else if (strcmp(args[6], "lpc2000_v2") == 0)\r
- {\r
- lpc2000_info->variant = 2;\r
- lpc2000_info->cmd51_dst_boundary = 256;\r
- lpc2000_info->cmd51_can_256b = 1;\r
- lpc2000_info->cmd51_can_8192b = 0;\r
- }\r
- else\r
- {\r
- ERROR("unknown LPC2000 variant");\r
- free(lpc2000_info);\r
- return ERROR_FLASH_BANK_INVALID;\r
- }\r
- \r
- lpc2000_info->iap_working_area = NULL;\r
- lpc2000_info->cclk = strtoul(args[7], NULL, 0);\r
- lpc2000_info->calc_checksum = 0;\r
- lpc2000_build_sector_list(bank);\r
- \r
- if (argc >= 9)\r
- {\r
- if (strcmp(args[8], "calc_checksum") == 0)\r
- lpc2000_info->calc_checksum = 1;\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int lpc2000_erase(struct flash_bank_s *bank, int first, int last)\r
-{\r
- lpc2000_flash_bank_t *lpc2000_info = bank->driver_priv;\r
- u32 param_table[5];\r
- u32 result_table[2];\r
- int status_code;\r
- \r
- param_table[0] = first;\r
- param_table[1] = last;\r
- param_table[2] = lpc2000_info->cclk;\r
- \r
- /* Prepare sectors */\r
- status_code = lpc2000_iap_call(bank, 50, param_table, result_table);\r
- switch (status_code)\r
- {\r
- case ERROR_FLASH_OPERATION_FAILED:\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- case LPC2000_CMD_SUCCESS:\r
- break;\r
- case LPC2000_INVALID_SECTOR:\r
- return ERROR_FLASH_SECTOR_INVALID;\r
- break;\r
- default:\r
- WARNING("lpc2000 prepare sectors returned %i", status_code);\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
- \r
- /* Erase sectors */\r
- status_code = lpc2000_iap_call(bank, 52, param_table, result_table);\r
- switch (status_code)\r
- {\r
- case ERROR_FLASH_OPERATION_FAILED:\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- case LPC2000_CMD_SUCCESS:\r
- break;\r
- case LPC2000_INVALID_SECTOR:\r
- return ERROR_FLASH_SECTOR_INVALID;\r
- break;\r
- default:\r
- WARNING("lpc2000 erase sectors returned %i", status_code);\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int lpc2000_protect(struct flash_bank_s *bank, int set, int first, int last)\r
-{\r
- /* can't protect/unprotect on the lpc2000 */\r
- return ERROR_OK;\r
-}\r
-\r
-int lpc2000_write(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count)\r
-{\r
- lpc2000_flash_bank_t *lpc2000_info = bank->driver_priv;\r
- target_t *target = bank->target;\r
- u32 dst_min_alignment;\r
- u32 bytes_remaining = count;\r
- u32 bytes_written = 0;\r
- int first_sector = 0;\r
- int last_sector = 0;\r
- u32 param_table[5];\r
- u32 result_table[2];\r
- int status_code;\r
- int i;\r
- working_area_t *download_area;\r
- \r
- /* allocate a working area */\r
- if (target_alloc_working_area(target, lpc2000_info->cmd51_max_buffer, &download_area) != ERROR_OK)\r
- {\r
- ERROR("no working area specified, can't write LPC2000 internal flash");\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
- \r
- if (offset + count > bank->size)\r
- return ERROR_FLASH_DST_OUT_OF_BANK;\r
- \r
- if (lpc2000_info->cmd51_can_256b)\r
- dst_min_alignment = 256;\r
- else\r
- dst_min_alignment = 512;\r
- \r
- if (offset % dst_min_alignment)\r
- {\r
- WARNING("offset 0x%x breaks required alignment 0x%x", offset, dst_min_alignment);\r
- return ERROR_FLASH_DST_BREAKS_ALIGNMENT;\r
- }\r
- \r
- for (i = 0; i < bank->num_sectors; i++)\r
- {\r
- if (offset >= bank->sectors[i].offset)\r
- first_sector = i;\r
- if (offset + CEIL(count, dst_min_alignment) * dst_min_alignment > bank->sectors[i].offset)\r
- last_sector = i;\r
- }\r
- \r
- DEBUG("first_sector: %i, last_sector: %i", first_sector, last_sector);\r
-\r
- /* check if exception vectors should be flashed */\r
- if ((offset == 0) && (count >= 0x20) && lpc2000_info->calc_checksum)\r
- {\r
- u32 checksum = 0;\r
- int i = 0;\r
- for (i = 0; i < 8; i++)\r
- {\r
- DEBUG("0x%2.2x: 0x%8.8x", i * 4, buf_get_u32(buffer + (i * 4), 0, 32));\r
- if (i != 5)\r
- checksum += buf_get_u32(buffer + (i * 4), 0, 32);\r
- }\r
- checksum = 0 - checksum;\r
- DEBUG("checksum: 0x%8.8x", checksum);\r
- buf_set_u32(buffer + 0x14, 0, 32, checksum);\r
- }\r
- \r
- while (bytes_remaining > 0)\r
- {\r
- u32 thisrun_bytes;\r
- if (bytes_remaining >= lpc2000_info->cmd51_max_buffer)\r
- thisrun_bytes = lpc2000_info->cmd51_max_buffer;\r
- else if (bytes_remaining >= 1024)\r
- thisrun_bytes = 1024;\r
- else if ((bytes_remaining >= 512) || (!lpc2000_info->cmd51_can_256b))\r
- thisrun_bytes = 512;\r
- else\r
- thisrun_bytes = 256;\r
- \r
- /* Prepare sectors */\r
- param_table[0] = first_sector;\r
- param_table[1] = last_sector;\r
- status_code = lpc2000_iap_call(bank, 50, param_table, result_table);\r
- switch (status_code)\r
- {\r
- case ERROR_FLASH_OPERATION_FAILED:\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- case LPC2000_CMD_SUCCESS:\r
- break;\r
- case LPC2000_INVALID_SECTOR:\r
- return ERROR_FLASH_SECTOR_INVALID;\r
- break;\r
- default:\r
- WARNING("lpc2000 prepare sectors returned %i", status_code);\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
- \r
- if (bytes_remaining >= thisrun_bytes)\r
- {\r
- if (target_write_buffer(bank->target, download_area->address, thisrun_bytes, buffer + bytes_written) != ERROR_OK)\r
- {\r
- target_free_working_area(target, download_area);\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
- }\r
- else\r
- {\r
- u8 *last_buffer = malloc(thisrun_bytes);\r
- int i;\r
- memcpy(last_buffer, buffer + bytes_written, bytes_remaining);\r
- for (i = bytes_remaining; i < thisrun_bytes; i++)\r
- last_buffer[i] = 0xff;\r
- target_write_buffer(bank->target, download_area->address, thisrun_bytes, last_buffer);\r
- free(last_buffer);\r
- }\r
- \r
- DEBUG("writing 0x%x bytes to address 0x%x", thisrun_bytes, bank->base + offset + bytes_written);\r
- \r
- /* Write data */\r
- param_table[0] = bank->base + offset + bytes_written;\r
- param_table[1] = download_area->address;\r
- param_table[2] = thisrun_bytes;\r
- param_table[3] = lpc2000_info->cclk;\r
- status_code = lpc2000_iap_call(bank, 51, param_table, result_table);\r
- switch (status_code)\r
- {\r
- case ERROR_FLASH_OPERATION_FAILED:\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- case LPC2000_CMD_SUCCESS:\r
- break;\r
- case LPC2000_INVALID_SECTOR:\r
- return ERROR_FLASH_SECTOR_INVALID;\r
- break;\r
- default:\r
- WARNING("lpc2000 returned %i", status_code);\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
- \r
- if (bytes_remaining > thisrun_bytes)\r
- bytes_remaining -= thisrun_bytes;\r
- else\r
- bytes_remaining = 0;\r
- bytes_written += thisrun_bytes;\r
- }\r
- \r
- target_free_working_area(target, download_area);\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int lpc2000_probe(struct flash_bank_s *bank)\r
-{\r
- /* we can't probe on an lpc2000 \r
- * if this is an lpc2xxx, it has the configured flash\r
- */\r
- return ERROR_OK;\r
-}\r
-\r
-int lpc2000_erase_check(struct flash_bank_s *bank)\r
-{\r
- if (bank->target->state != TARGET_HALTED)\r
- {\r
- return ERROR_TARGET_NOT_HALTED;\r
- }\r
- \r
- return lpc2000_iap_blank_check(bank, 0, bank->num_sectors - 1);\r
-}\r
-\r
-int lpc2000_protect_check(struct flash_bank_s *bank)\r
-{\r
- /* sectors are always protected */\r
- return ERROR_OK;\r
-}\r
-\r
-int lpc2000_info(struct flash_bank_s *bank, char *buf, int buf_size)\r
-{\r
- lpc2000_flash_bank_t *lpc2000_info = bank->driver_priv;\r
-\r
- snprintf(buf, buf_size, "lpc2000 flash driver variant: %i, clk: %i", lpc2000_info->variant, lpc2000_info->cclk);\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int lpc2000_handle_part_id_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- flash_bank_t *bank;\r
- u32 param_table[5];\r
- u32 result_table[2];\r
- int status_code;\r
- lpc2000_flash_bank_t *lpc2000_info;\r
-\r
- if (argc < 1)\r
- {\r
+/***************************************************************************
+ * Copyright (C) 2005 by Dominic Rath *
+ * Dominic.Rath@gmx.de *
+ * *
+ * 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 "lpc2000.h"
+
+#include "flash.h"
+#include "target.h"
+#include "log.h"
+#include "armv4_5.h"
+#include "algorithm.h"
+#include "binarybuffer.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/* flash programming support for Philips LPC2xxx devices
+ * currently supported devices:
+ * variant 1 (lpc2000_v1):
+ * - 2104|5|6
+ * - 2114|9
+ * - 2124|9
+ * - 2194
+ * - 2212|4
+ * - 2292|4
+ *
+ * variant 2 (lpc2000_v2):
+ * - 213x
+ * - 214x
+ * - 2101|2|3
+ * - 2364|6|8
+ * - 2378
+ */
+
+int lpc2000_register_commands(struct command_context_s *cmd_ctx);
+int lpc2000_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank);
+int lpc2000_erase(struct flash_bank_s *bank, int first, int last);
+int lpc2000_protect(struct flash_bank_s *bank, int set, int first, int last);
+int lpc2000_write(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count);
+int lpc2000_probe(struct flash_bank_s *bank);
+int lpc2000_erase_check(struct flash_bank_s *bank);
+int lpc2000_protect_check(struct flash_bank_s *bank);
+int lpc2000_info(struct flash_bank_s *bank, char *buf, int buf_size);
+
+int lpc2000_handle_part_id_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+
+flash_driver_t lpc2000_flash =
+{
+ .name = "lpc2000",
+ .register_commands = lpc2000_register_commands,
+ .flash_bank_command = lpc2000_flash_bank_command,
+ .erase = lpc2000_erase,
+ .protect = lpc2000_protect,
+ .write = lpc2000_write,
+ .probe = lpc2000_probe,
+ .auto_probe = lpc2000_probe,
+ .erase_check = lpc2000_erase_check,
+ .protect_check = lpc2000_protect_check,
+ .info = lpc2000_info
+};
+
+int lpc2000_register_commands(struct command_context_s *cmd_ctx)
+{
+ command_t *lpc2000_cmd = register_command(cmd_ctx, NULL, "lpc2000", NULL, COMMAND_ANY, NULL);
+
+ register_command(cmd_ctx, lpc2000_cmd, "part_id", lpc2000_handle_part_id_command, COMMAND_EXEC,
+ "print part id of lpc2000 flash bank <num>");
+
+ return ERROR_OK;
+}
+
+int lpc2000_build_sector_list(struct flash_bank_s *bank)
+{
+ lpc2000_flash_bank_t *lpc2000_info = bank->driver_priv;
+
+ /* default to a 4096 write buffer */
+ lpc2000_info->cmd51_max_buffer = 4096;
+
+ if (lpc2000_info->variant == 1)
+ {
+ int i = 0;
+ u32 offset = 0;
+
+ /* variant 1 has different layout for 128kb and 256kb flashes */
+ if (bank->size == 128 * 1024)
+ {
+ bank->num_sectors = 16;
+ bank->sectors = malloc(sizeof(flash_sector_t) * 16);
+ for (i = 0; i < 16; i++)
+ {
+ bank->sectors[i].offset = offset;
+ bank->sectors[i].size = 8 * 1024;
+ offset += bank->sectors[i].size;
+ bank->sectors[i].is_erased = -1;
+ bank->sectors[i].is_protected = 1;
+ }
+ }
+ else if (bank->size == 256 * 1024)
+ {
+ bank->num_sectors = 18;
+ bank->sectors = malloc(sizeof(flash_sector_t) * 18);
+
+ for (i = 0; i < 8; i++)
+ {
+ bank->sectors[i].offset = offset;
+ bank->sectors[i].size = 8 * 1024;
+ offset += bank->sectors[i].size;
+ bank->sectors[i].is_erased = -1;
+ bank->sectors[i].is_protected = 1;
+ }
+ for (i = 8; i < 10; i++)
+ {
+ bank->sectors[i].offset = offset;
+ bank->sectors[i].size = 64 * 1024;
+ offset += bank->sectors[i].size;
+ bank->sectors[i].is_erased = -1;
+ bank->sectors[i].is_protected = 1;
+ }
+ for (i = 10; i < 18; i++)
+ {
+ bank->sectors[i].offset = offset;
+ bank->sectors[i].size = 8 * 1024;
+ offset += bank->sectors[i].size;
+ bank->sectors[i].is_erased = -1;
+ bank->sectors[i].is_protected = 1;
+ }
+ }
+ else
+ {
+ ERROR("BUG: unknown bank->size encountered");
+ exit(-1);
+ }
+ }
+ else if (lpc2000_info->variant == 2)
+ {
+ int num_sectors;
+ int i;
+ u32 offset = 0;
+
+ /* variant 2 has a uniform layout, only number of sectors differs */
+ switch (bank->size)
+ {
+ case 4 * 1024:
+ lpc2000_info->cmd51_max_buffer = 1024;
+ num_sectors = 1;
+ break;
+ case 8 * 1024:
+ lpc2000_info->cmd51_max_buffer = 1024;
+ num_sectors = 2;
+ break;
+ case 16 * 1024:
+ num_sectors = 4;
+ break;
+ case 32 * 1024:
+ num_sectors = 8;
+ break;
+ case 64 * 1024:
+ num_sectors = 9;
+ break;
+ case 128 * 1024:
+ num_sectors = 11;
+ break;
+ case 256 * 1024:
+ num_sectors = 15;
+ break;
+ case 512 * 1024:
+ case 500 * 1024:
+ num_sectors = 27;
+ break;
+ default:
+ ERROR("BUG: unknown bank->size encountered");
+ exit(-1);
+ break;
+ }
+
+ bank->num_sectors = num_sectors;
+ bank->sectors = malloc(sizeof(flash_sector_t) * num_sectors);
+
+ for (i = 0; i < num_sectors; i++)
+ {
+ if ((i >= 0) && (i < 8))
+ {
+ bank->sectors[i].offset = offset;
+ bank->sectors[i].size = 4 * 1024;
+ offset += bank->sectors[i].size;
+ bank->sectors[i].is_erased = -1;
+ bank->sectors[i].is_protected = 1;
+ }
+ if ((i >= 8) && (i < 22))
+ {
+ bank->sectors[i].offset = offset;
+ bank->sectors[i].size = 32 * 1024;
+ offset += bank->sectors[i].size;
+ bank->sectors[i].is_erased = -1;
+ bank->sectors[i].is_protected = 1;
+ }
+ if ((i >= 22) && (i < 27))
+ {
+ bank->sectors[i].offset = offset;
+ bank->sectors[i].size = 4 * 1024;
+ offset += bank->sectors[i].size;
+ bank->sectors[i].is_erased = -1;
+ bank->sectors[i].is_protected = 1;
+ }
+ }
+ }
+ else
+ {
+ ERROR("BUG: unknown lpc2000_info->variant encountered");
+ exit(-1);
+ }
+
+ return ERROR_OK;
+}
+
+/* call LPC2000 IAP function
+ * uses 172 bytes working area
+ * 0x0 to 0x7: jump gate (BX to thumb state, b -2 to wait)
+ * 0x8 to 0x1f: command parameter table
+ * 0x20 to 0x2b: command result table
+ * 0x2c to 0xac: stack (only 128b needed)
+ */
+int lpc2000_iap_call(flash_bank_t *bank, int code, u32 param_table[5], u32 result_table[2])
+{
+ lpc2000_flash_bank_t *lpc2000_info = bank->driver_priv;
+ target_t *target = bank->target;
+ mem_param_t mem_params[2];
+ reg_param_t reg_params[5];
+ armv4_5_algorithm_t armv4_5_info;
+ u32 status_code;
+
+ /* regrab previously allocated working_area, or allocate a new one */
+ if (!lpc2000_info->iap_working_area)
+ {
+ u8 jump_gate[8];
+
+ /* make sure we have a working area */
+ if (target_alloc_working_area(target, 172, &lpc2000_info->iap_working_area) != ERROR_OK)
+ {
+ ERROR("no working area specified, can't write LPC2000 internal flash");
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ /* write IAP code to working area */
+ target_buffer_set_u32(target, jump_gate, ARMV4_5_BX(12));
+ target_buffer_set_u32(target, jump_gate + 4, ARMV4_5_B(0xfffffe, 0));
+ target->type->write_memory(target, lpc2000_info->iap_working_area->address, 4, 2, jump_gate);
+ }
+
+ armv4_5_info.common_magic = ARMV4_5_COMMON_MAGIC;
+ armv4_5_info.core_mode = ARMV4_5_MODE_SVC;
+ armv4_5_info.core_state = ARMV4_5_STATE_ARM;
+
+ /* command parameter table */
+ init_mem_param(&mem_params[0], lpc2000_info->iap_working_area->address + 8, 4 * 6, PARAM_OUT);
+ target_buffer_set_u32(target, mem_params[0].value, code);
+ target_buffer_set_u32(target, mem_params[0].value + 0x4, param_table[0]);
+ target_buffer_set_u32(target, mem_params[0].value + 0x8, param_table[1]);
+ target_buffer_set_u32(target, mem_params[0].value + 0xc, param_table[2]);
+ target_buffer_set_u32(target, mem_params[0].value + 0x10, param_table[3]);
+ target_buffer_set_u32(target, mem_params[0].value + 0x14, param_table[4]);
+
+ init_reg_param(®_params[0], "r0", 32, PARAM_OUT);
+ buf_set_u32(reg_params[0].value, 0, 32, lpc2000_info->iap_working_area->address + 0x8);
+
+ /* command result table */
+ init_mem_param(&mem_params[1], lpc2000_info->iap_working_area->address + 0x20, 4 * 3, PARAM_IN);
+
+ init_reg_param(®_params[1], "r1", 32, PARAM_OUT);
+ buf_set_u32(reg_params[1].value, 0, 32, lpc2000_info->iap_working_area->address + 0x20);
+
+ /* IAP entry point */
+ init_reg_param(®_params[2], "r12", 32, PARAM_OUT);
+ buf_set_u32(reg_params[2].value, 0, 32, 0x7ffffff1);
+
+ /* IAP stack */
+ init_reg_param(®_params[3], "r13_svc", 32, PARAM_OUT);
+ buf_set_u32(reg_params[3].value, 0, 32, lpc2000_info->iap_working_area->address + 0xac);
+
+ /* return address */
+ init_reg_param(®_params[4], "lr_svc", 32, PARAM_OUT);
+ buf_set_u32(reg_params[4].value, 0, 32, lpc2000_info->iap_working_area->address + 0x4);
+
+ target->type->run_algorithm(target, 2, mem_params, 5, reg_params, lpc2000_info->iap_working_area->address, lpc2000_info->iap_working_area->address + 0x4, 10000, &armv4_5_info);
+
+ status_code = buf_get_u32(mem_params[1].value, 0, 32);
+ result_table[0] = target_buffer_get_u32(target, mem_params[1].value);
+ result_table[1] = target_buffer_get_u32(target, mem_params[1].value + 4);
+
+ destroy_mem_param(&mem_params[0]);
+ destroy_mem_param(&mem_params[1]);
+
+ destroy_reg_param(®_params[0]);
+ destroy_reg_param(®_params[1]);
+ destroy_reg_param(®_params[2]);
+ destroy_reg_param(®_params[3]);
+ destroy_reg_param(®_params[4]);
+
+ return status_code;
+}
+
+int lpc2000_iap_blank_check(struct flash_bank_s *bank, int first, int last)
+{
+ u32 param_table[5];
+ u32 result_table[2];
+ int status_code;
+ int i;
+
+ if ((first < 0) || (last > bank->num_sectors))
+ return ERROR_FLASH_SECTOR_INVALID;
+
+ for (i = first; i <= last; i++)
+ {
+ /* check single sector */
+ param_table[0] = param_table[1] = i;
+ status_code = lpc2000_iap_call(bank, 53, param_table, result_table);
+
+ switch (status_code)
+ {
+ case ERROR_FLASH_OPERATION_FAILED:
+ return ERROR_FLASH_OPERATION_FAILED;
+ case LPC2000_CMD_SUCCESS:
+ bank->sectors[i].is_erased = 1;
+ break;
+ case LPC2000_SECTOR_NOT_BLANK:
+ bank->sectors[i].is_erased = 0;
+ break;
+ case LPC2000_INVALID_SECTOR:
+ bank->sectors[i].is_erased = 0;
+ break;
+ case LPC2000_BUSY:
+ return ERROR_FLASH_BUSY;
+ break;
+ default:
+ ERROR("BUG: unknown LPC2000 status code");
+ exit(-1);
+ }
+ }
+
+ return ERROR_OK;
+}
+
+/* flash bank lpc2000 <base> <size> 0 0 <target#> <lpc_variant> <cclk> [calc_checksum]
+ */
+int lpc2000_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank)
+{
+ lpc2000_flash_bank_t *lpc2000_info;
+
+ if (argc < 8)
+ {
+ WARNING("incomplete flash_bank lpc2000 configuration");
+ return ERROR_FLASH_BANK_INVALID;
+ }
+
+ lpc2000_info = malloc(sizeof(lpc2000_flash_bank_t));
+ bank->driver_priv = lpc2000_info;
+
+ if (strcmp(args[6], "lpc2000_v1") == 0)
+ {
+ lpc2000_info->variant = 1;
+ lpc2000_info->cmd51_dst_boundary = 512;
+ lpc2000_info->cmd51_can_256b = 0;
+ lpc2000_info->cmd51_can_8192b = 1;
+ }
+ else if (strcmp(args[6], "lpc2000_v2") == 0)
+ {
+ lpc2000_info->variant = 2;
+ lpc2000_info->cmd51_dst_boundary = 256;
+ lpc2000_info->cmd51_can_256b = 1;
+ lpc2000_info->cmd51_can_8192b = 0;
+ }
+ else
+ {
+ ERROR("unknown LPC2000 variant");
+ free(lpc2000_info);
+ return ERROR_FLASH_BANK_INVALID;
+ }
+
+ lpc2000_info->iap_working_area = NULL;
+ lpc2000_info->cclk = strtoul(args[7], NULL, 0);
+ lpc2000_info->calc_checksum = 0;
+ lpc2000_build_sector_list(bank);
+
+ if (argc >= 9)
+ {
+ if (strcmp(args[8], "calc_checksum") == 0)
+ lpc2000_info->calc_checksum = 1;
+ }
+
+ return ERROR_OK;
+}
+
+int lpc2000_erase(struct flash_bank_s *bank, int first, int last)
+{
+ lpc2000_flash_bank_t *lpc2000_info = bank->driver_priv;
+ u32 param_table[5];
+ u32 result_table[2];
+ int status_code;
+
+ param_table[0] = first;
+ param_table[1] = last;
+ param_table[2] = lpc2000_info->cclk;
+
+ /* Prepare sectors */
+ status_code = lpc2000_iap_call(bank, 50, param_table, result_table);
+ switch (status_code)
+ {
+ case ERROR_FLASH_OPERATION_FAILED:
+ return ERROR_FLASH_OPERATION_FAILED;
+ case LPC2000_CMD_SUCCESS:
+ break;
+ case LPC2000_INVALID_SECTOR:
+ return ERROR_FLASH_SECTOR_INVALID;
+ break;
+ default:
+ WARNING("lpc2000 prepare sectors returned %i", status_code);
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ /* Erase sectors */
+ status_code = lpc2000_iap_call(bank, 52, param_table, result_table);
+ switch (status_code)
+ {
+ case ERROR_FLASH_OPERATION_FAILED:
+ return ERROR_FLASH_OPERATION_FAILED;
+ case LPC2000_CMD_SUCCESS:
+ break;
+ case LPC2000_INVALID_SECTOR:
+ return ERROR_FLASH_SECTOR_INVALID;
+ break;
+ default:
+ WARNING("lpc2000 erase sectors returned %i", status_code);
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ return ERROR_OK;
+}
+
+int lpc2000_protect(struct flash_bank_s *bank, int set, int first, int last)
+{
+ /* can't protect/unprotect on the lpc2000 */
+ return ERROR_OK;
+}
+
+int lpc2000_write(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count)
+{
+ lpc2000_flash_bank_t *lpc2000_info = bank->driver_priv;
+ target_t *target = bank->target;
+ u32 dst_min_alignment;
+ u32 bytes_remaining = count;
+ u32 bytes_written = 0;
+ int first_sector = 0;
+ int last_sector = 0;
+ u32 param_table[5];
+ u32 result_table[2];
+ int status_code;
+ int i;
+ working_area_t *download_area;
+
+ /* allocate a working area */
+ if (target_alloc_working_area(target, lpc2000_info->cmd51_max_buffer, &download_area) != ERROR_OK)
+ {
+ ERROR("no working area specified, can't write LPC2000 internal flash");
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ if (offset + count > bank->size)
+ return ERROR_FLASH_DST_OUT_OF_BANK;
+
+ if (lpc2000_info->cmd51_can_256b)
+ dst_min_alignment = 256;
+ else
+ dst_min_alignment = 512;
+
+ if (offset % dst_min_alignment)
+ {
+ WARNING("offset 0x%x breaks required alignment 0x%x", offset, dst_min_alignment);
+ return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
+ }
+
+ for (i = 0; i < bank->num_sectors; i++)
+ {
+ if (offset >= bank->sectors[i].offset)
+ first_sector = i;
+ if (offset + CEIL(count, dst_min_alignment) * dst_min_alignment > bank->sectors[i].offset)
+ last_sector = i;
+ }
+
+ DEBUG("first_sector: %i, last_sector: %i", first_sector, last_sector);
+
+ /* check if exception vectors should be flashed */
+ if ((offset == 0) && (count >= 0x20) && lpc2000_info->calc_checksum)
+ {
+ u32 checksum = 0;
+ int i = 0;
+ for (i = 0; i < 8; i++)
+ {
+ DEBUG("0x%2.2x: 0x%8.8x", i * 4, buf_get_u32(buffer + (i * 4), 0, 32));
+ if (i != 5)
+ checksum += buf_get_u32(buffer + (i * 4), 0, 32);
+ }
+ checksum = 0 - checksum;
+ DEBUG("checksum: 0x%8.8x", checksum);
+ buf_set_u32(buffer + 0x14, 0, 32, checksum);
+ }
+
+ while (bytes_remaining > 0)
+ {
+ u32 thisrun_bytes;
+ if (bytes_remaining >= lpc2000_info->cmd51_max_buffer)
+ thisrun_bytes = lpc2000_info->cmd51_max_buffer;
+ else if (bytes_remaining >= 1024)
+ thisrun_bytes = 1024;
+ else if ((bytes_remaining >= 512) || (!lpc2000_info->cmd51_can_256b))
+ thisrun_bytes = 512;
+ else
+ thisrun_bytes = 256;
+
+ /* Prepare sectors */
+ param_table[0] = first_sector;
+ param_table[1] = last_sector;
+ status_code = lpc2000_iap_call(bank, 50, param_table, result_table);
+ switch (status_code)
+ {
+ case ERROR_FLASH_OPERATION_FAILED:
+ return ERROR_FLASH_OPERATION_FAILED;
+ case LPC2000_CMD_SUCCESS:
+ break;
+ case LPC2000_INVALID_SECTOR:
+ return ERROR_FLASH_SECTOR_INVALID;
+ break;
+ default:
+ WARNING("lpc2000 prepare sectors returned %i", status_code);
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ if (bytes_remaining >= thisrun_bytes)
+ {
+ if (target_write_buffer(bank->target, download_area->address, thisrun_bytes, buffer + bytes_written) != ERROR_OK)
+ {
+ target_free_working_area(target, download_area);
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+ }
+ else
+ {
+ u8 *last_buffer = malloc(thisrun_bytes);
+ int i;
+ memcpy(last_buffer, buffer + bytes_written, bytes_remaining);
+ for (i = bytes_remaining; i < thisrun_bytes; i++)
+ last_buffer[i] = 0xff;
+ target_write_buffer(bank->target, download_area->address, thisrun_bytes, last_buffer);
+ free(last_buffer);
+ }
+
+ DEBUG("writing 0x%x bytes to address 0x%x", thisrun_bytes, bank->base + offset + bytes_written);
+
+ /* Write data */
+ param_table[0] = bank->base + offset + bytes_written;
+ param_table[1] = download_area->address;
+ param_table[2] = thisrun_bytes;
+ param_table[3] = lpc2000_info->cclk;
+ status_code = lpc2000_iap_call(bank, 51, param_table, result_table);
+ switch (status_code)
+ {
+ case ERROR_FLASH_OPERATION_FAILED:
+ return ERROR_FLASH_OPERATION_FAILED;
+ case LPC2000_CMD_SUCCESS:
+ break;
+ case LPC2000_INVALID_SECTOR:
+ return ERROR_FLASH_SECTOR_INVALID;
+ break;
+ default:
+ WARNING("lpc2000 returned %i", status_code);
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ if (bytes_remaining > thisrun_bytes)
+ bytes_remaining -= thisrun_bytes;
+ else
+ bytes_remaining = 0;
+ bytes_written += thisrun_bytes;
+ }
+
+ target_free_working_area(target, download_area);
+
+ return ERROR_OK;
+}
+
+int lpc2000_probe(struct flash_bank_s *bank)
+{
+ /* we can't probe on an lpc2000
+ * if this is an lpc2xxx, it has the configured flash
+ */
+ return ERROR_OK;
+}
+
+int lpc2000_erase_check(struct flash_bank_s *bank)
+{
+ if (bank->target->state != TARGET_HALTED)
+ {
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ return lpc2000_iap_blank_check(bank, 0, bank->num_sectors - 1);
+}
+
+int lpc2000_protect_check(struct flash_bank_s *bank)
+{
+ /* sectors are always protected */
+ return ERROR_OK;
+}
+
+int lpc2000_info(struct flash_bank_s *bank, char *buf, int buf_size)
+{
+ lpc2000_flash_bank_t *lpc2000_info = bank->driver_priv;
+
+ snprintf(buf, buf_size, "lpc2000 flash driver variant: %i, clk: %i", lpc2000_info->variant, lpc2000_info->cclk);
+
+ return ERROR_OK;
+}
+
+int lpc2000_handle_part_id_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ flash_bank_t *bank;
+ u32 param_table[5];
+ u32 result_table[2];
+ int status_code;
+ lpc2000_flash_bank_t *lpc2000_info;
+
+ if (argc < 1)
+ {
return ERROR_COMMAND_SYNTAX_ERROR;
- }\r
- \r
- bank = get_flash_bank_by_num(strtoul(args[0], NULL, 0));\r
- if (!bank)\r
- {\r
- command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);\r
- return ERROR_OK;\r
- }\r
-\r
- lpc2000_info = bank->driver_priv;\r
- if (bank->target->state != TARGET_HALTED)\r
- {\r
- return ERROR_TARGET_NOT_HALTED;\r
- }\r
- \r
- if ((status_code = lpc2000_iap_call(bank, 54, param_table, result_table)) != 0x0)\r
- {\r
- if (status_code == ERROR_FLASH_OPERATION_FAILED)\r
- {\r
- command_print(cmd_ctx, "no sufficient working area specified, can't access LPC2000 IAP interface");\r
- return ERROR_OK;\r
- }\r
- command_print(cmd_ctx, "lpc2000 IAP returned status code %i", status_code);\r
- }\r
- else\r
- {\r
- command_print(cmd_ctx, "lpc2000 part id: 0x%8.8x", result_table[0]);\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
+ }
+
+ bank = get_flash_bank_by_num(strtoul(args[0], NULL, 0));
+ if (!bank)
+ {
+ command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);
+ return ERROR_OK;
+ }
+
+ lpc2000_info = bank->driver_priv;
+ if (bank->target->state != TARGET_HALTED)
+ {
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ if ((status_code = lpc2000_iap_call(bank, 54, param_table, result_table)) != 0x0)
+ {
+ if (status_code == ERROR_FLASH_OPERATION_FAILED)
+ {
+ command_print(cmd_ctx, "no sufficient working area specified, can't access LPC2000 IAP interface");
+ return ERROR_OK;
+ }
+ command_print(cmd_ctx, "lpc2000 IAP returned status code %i", status_code);
+ }
+ else
+ {
+ command_print(cmd_ctx, "lpc2000 part id: 0x%8.8x", result_table[0]);
+ }
+
+ return ERROR_OK;
+}
-/***************************************************************************\r
- * Copyright (C) 2007 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 "lpc3180_nand_controller.h"\r
-\r
-#include "replacements.h"\r
-#include "log.h"\r
-\r
-#include <stdlib.h>\r
-#include <string.h>\r
-\r
-#include "nand.h"\r
-#include "target.h"\r
-\r
-int lpc3180_nand_device_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct nand_device_s *device);\r
-int lpc3180_register_commands(struct command_context_s *cmd_ctx);\r
-int lpc3180_init(struct nand_device_s *device);\r
-int lpc3180_reset(struct nand_device_s *device);\r
-int lpc3180_command(struct nand_device_s *device, u8 command);\r
-int lpc3180_address(struct nand_device_s *device, u8 address);\r
-int lpc3180_write_data(struct nand_device_s *device, u16 data);\r
-int lpc3180_read_data(struct nand_device_s *device, void *data);\r
-int lpc3180_write_page(struct nand_device_s *device, u32 page, u8 *data, u32 data_size, u8 *oob, u32 oob_size);\r
-int lpc3180_read_page(struct nand_device_s *device, u32 page, u8 *data, u32 data_size, u8 *oob, u32 oob_size);\r
-int lpc3180_controller_ready(struct nand_device_s *device, int timeout);\r
-int lpc3180_nand_ready(struct nand_device_s *device, int timeout);\r
-\r
-int handle_lpc3180_select_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-\r
-nand_flash_controller_t lpc3180_nand_controller =\r
-{\r
- .name = "lpc3180",\r
- .nand_device_command = lpc3180_nand_device_command,\r
- .register_commands = lpc3180_register_commands,\r
- .init = lpc3180_init,\r
- .reset = lpc3180_reset,\r
- .command = lpc3180_command,\r
- .address = lpc3180_address,\r
- .write_data = lpc3180_write_data,\r
- .read_data = lpc3180_read_data,\r
- .write_page = lpc3180_write_page,\r
- .read_page = lpc3180_read_page,\r
- .controller_ready = lpc3180_controller_ready,\r
- .nand_ready = lpc3180_nand_ready,\r
-};\r
-\r
-/* nand device lpc3180 <target#> <oscillator_frequency>\r
- */\r
-int lpc3180_nand_device_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct nand_device_s *device)\r
-{\r
- lpc3180_nand_controller_t *lpc3180_info;\r
- \r
- if (argc < 3)\r
- {\r
- WARNING("incomplete 'lpc3180' nand flash configuration");\r
- return ERROR_FLASH_BANK_INVALID;\r
- }\r
- \r
- lpc3180_info = malloc(sizeof(lpc3180_nand_controller_t));\r
- device->controller_priv = lpc3180_info;\r
-\r
- lpc3180_info->target = get_target_by_num(strtoul(args[1], NULL, 0));\r
- if (!lpc3180_info->target)\r
- {\r
- ERROR("no target '%s' configured", args[1]);\r
- return ERROR_NAND_DEVICE_INVALID;\r
- }\r
-\r
- lpc3180_info->osc_freq = strtoul(args[2], NULL, 0);\r
- if ((lpc3180_info->osc_freq < 1000) || (lpc3180_info->osc_freq > 20000))\r
- {\r
- WARNING("LPC3180 oscillator frequency should be between 1000 and 20000 kHz, was %i", lpc3180_info->osc_freq); \r
- }\r
- lpc3180_info->selected_controller = LPC3180_NO_CONTROLLER;\r
- lpc3180_info->sw_write_protection = 0;\r
- lpc3180_info->sw_wp_lower_bound = 0x0;\r
- lpc3180_info->sw_wp_upper_bound = 0x0;\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int lpc3180_register_commands(struct command_context_s *cmd_ctx)\r
-{\r
- command_t *lpc3180_cmd = register_command(cmd_ctx, NULL, "lpc3180", NULL, COMMAND_ANY, "commands specific to the LPC3180 NAND flash controllers");\r
- \r
- register_command(cmd_ctx, lpc3180_cmd, "select", handle_lpc3180_select_command, COMMAND_EXEC, "select <'mlc'|'slc'> controller (default is mlc)");\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int lpc3180_pll(int fclkin, u32 pll_ctrl)\r
-{\r
- int bypass = (pll_ctrl & 0x8000) >> 15;\r
- int direct = (pll_ctrl & 0x4000) >> 14;\r
- int feedback = (pll_ctrl & 0x2000) >> 13;\r
- int p = (1 << ((pll_ctrl & 0x1800) >> 11) * 2);\r
- int n = ((pll_ctrl & 0x0600) >> 9) + 1;\r
- int m = ((pll_ctrl & 0x01fe) >> 1) + 1;\r
- int lock = (pll_ctrl & 0x1);\r
-\r
- if (!lock)\r
- WARNING("PLL is not locked");\r
- \r
- if (!bypass && direct) /* direct mode */\r
- return (m * fclkin) / n;\r
- \r
- if (bypass && !direct) /* bypass mode */\r
- return fclkin / (2 * p);\r
- \r
- if (bypass & direct) /* direct bypass mode */\r
- return fclkin;\r
- \r
- if (feedback) /* integer mode */\r
- return m * (fclkin / n);\r
- else /* non-integer mode */\r
- return (m / (2 * p)) * (fclkin / n); \r
-}\r
-\r
-float lpc3180_cycle_time(lpc3180_nand_controller_t *lpc3180_info)\r
-{\r
- target_t *target = lpc3180_info->target;\r
- u32 sysclk_ctrl, pwr_ctrl, hclkdiv_ctrl, hclkpll_ctrl;\r
- int sysclk;\r
- int hclk;\r
- int hclk_pll;\r
- float cycle;\r
- \r
- /* calculate timings */\r
- \r
- /* determine current SYSCLK (13'MHz or main oscillator) */ \r
- target_read_u32(target, 0x40004050, &sysclk_ctrl);\r
- \r
- if ((sysclk_ctrl & 1) == 0)\r
- sysclk = lpc3180_info->osc_freq;\r
- else\r
- sysclk = 13000;\r
- \r
- /* determine selected HCLK source */\r
- target_read_u32(target, 0x40004044, &pwr_ctrl);\r
- \r
- if ((pwr_ctrl & (1 << 2)) == 0) /* DIRECT RUN mode */\r
- {\r
- hclk = sysclk;\r
- }\r
- else\r
- {\r
- target_read_u32(target, 0x40004058, &hclkpll_ctrl);\r
- hclk_pll = lpc3180_pll(sysclk, hclkpll_ctrl);\r
-\r
- target_read_u32(target, 0x40004040, &hclkdiv_ctrl);\r
- \r
- if (pwr_ctrl & (1 << 10)) /* ARM_CLK and HCLK use PERIPH_CLK */\r
- {\r
- hclk = hclk_pll / (((hclkdiv_ctrl & 0x7c) >> 2) + 1);\r
- }\r
- else /* HCLK uses HCLK_PLL */\r
- {\r
- hclk = hclk_pll / (1 << (hclkdiv_ctrl & 0x3)); \r
- }\r
- }\r
- \r
- DEBUG("LPC3180 HCLK currently clocked at %i kHz", hclk);\r
- \r
- cycle = (1.0 / hclk) * 1000000.0;\r
- \r
- return cycle;\r
-}\r
-\r
-int lpc3180_init(struct nand_device_s *device)\r
-{\r
- lpc3180_nand_controller_t *lpc3180_info = device->controller_priv;\r
- target_t *target = lpc3180_info->target;\r
- int bus_width = (device->bus_width) ? (device->bus_width) : 8;\r
- int address_cycles = (device->address_cycles) ? (device->address_cycles) : 3;\r
- int page_size = (device->page_size) ? (device->page_size) : 512;\r
- \r
- if (target->state != TARGET_HALTED)\r
- {\r
- ERROR("target must be halted to use LPC3180 NAND flash controller");\r
- return ERROR_NAND_OPERATION_FAILED;\r
- }\r
- \r
- /* sanitize arguments */\r
- if ((bus_width != 8) && (bus_width != 16))\r
- {\r
- ERROR("LPC3180 only supports 8 or 16 bit bus width, not %i", bus_width);\r
- return ERROR_NAND_OPERATION_NOT_SUPPORTED;\r
- }\r
- \r
- /* The LPC3180 only brings out 8 bit NAND data bus, but the controller\r
- * would support 16 bit, too, so we just warn about this for now\r
- */\r
- if (bus_width == 16)\r
- {\r
- WARNING("LPC3180 only supports 8 bit bus width");\r
- }\r
- \r
- /* inform calling code about selected bus width */\r
- device->bus_width = bus_width;\r
- \r
- if ((address_cycles != 3) && (address_cycles != 4))\r
- {\r
- ERROR("LPC3180 only supports 3 or 4 address cycles, not %i", address_cycles);\r
- return ERROR_NAND_OPERATION_NOT_SUPPORTED;\r
- }\r
- \r
- if ((page_size != 512) && (page_size != 2048))\r
- {\r
- ERROR("LPC3180 only supports 512 or 2048 byte pages, not %i", page_size);\r
- return ERROR_NAND_OPERATION_NOT_SUPPORTED;\r
- }\r
- \r
- /* select MLC controller if none is currently selected */\r
- if (lpc3180_info->selected_controller == LPC3180_NO_CONTROLLER)\r
- {\r
- DEBUG("no LPC3180 NAND flash controller selected, using default 'mlc'");\r
- lpc3180_info->selected_controller = LPC3180_MLC_CONTROLLER;\r
- }\r
- \r
- if (lpc3180_info->selected_controller == LPC3180_MLC_CONTROLLER)\r
- {\r
- u32 mlc_icr_value = 0x0;\r
- float cycle;\r
- int twp, twh, trp, treh, trhz, trbwb, tcea;\r
- \r
- /* FLASHCLK_CTRL = 0x22 (enable clock for MLC flash controller) */\r
- target_write_u32(target, 0x400040c8, 0x22);\r
- \r
- /* MLC_CEH = 0x0 (Force nCE assert) */\r
- target_write_u32(target, 0x200b804c, 0x0);\r
- \r
- /* MLC_LOCK = 0xa25e (unlock protected registers) */\r
- target_write_u32(target, 0x200b8044, 0xa25e);\r
- \r
- /* MLC_ICR = configuration */\r
- if (lpc3180_info->sw_write_protection)\r
- mlc_icr_value |= 0x8;\r
- if (page_size == 2048)\r
- mlc_icr_value |= 0x4;\r
- if (address_cycles == 4)\r
- mlc_icr_value |= 0x2;\r
- if (bus_width == 16)\r
- mlc_icr_value |= 0x1;\r
- target_write_u32(target, 0x200b8030, mlc_icr_value);\r
- \r
- /* calculate NAND controller timings */\r
- cycle = lpc3180_cycle_time(lpc3180_info);\r
- \r
- twp = ((40 / cycle) + 1);\r
- twh = ((20 / cycle) + 1);\r
- trp = ((30 / cycle) + 1);\r
- treh = ((15 / cycle) + 1);\r
- trhz = ((30 / cycle) + 1);\r
- trbwb = ((100 / cycle) + 1);\r
- tcea = ((45 / cycle) + 1);\r
- \r
- /* MLC_LOCK = 0xa25e (unlock protected registers) */\r
- target_write_u32(target, 0x200b8044, 0xa25e);\r
- \r
- /* MLC_TIME_REG */\r
- target_write_u32(target, 0x200b8034, (twp & 0xf) | ((twh & 0xf) << 4) | \r
- ((trp & 0xf) << 8) | ((treh & 0xf) << 12) | ((trhz & 0x7) << 16) | \r
- ((trbwb & 0x1f) << 19) | ((tcea & 0x3) << 24)); \r
-\r
- lpc3180_reset(device);\r
- }\r
- else if (lpc3180_info->selected_controller == LPC3180_SLC_CONTROLLER)\r
- {\r
- float cycle;\r
- int r_setup, r_hold, r_width, r_rdy;\r
- int w_setup, w_hold, w_width, w_rdy;\r
- \r
- /* FLASHCLK_CTRL = 0x05 (enable clock for SLC flash controller) */\r
- target_write_u32(target, 0x400040c8, 0x05);\r
- \r
- /* SLC_CFG = 0x (Force nCE assert, ECC enabled, WIDTH = bus_width) */\r
- target_write_u32(target, 0x20020014, 0x28 | (bus_width == 16) ? 1 : 0);\r
- \r
- /* calculate NAND controller timings */\r
- cycle = lpc3180_cycle_time(lpc3180_info);\r
- \r
- r_setup = w_setup = 0;\r
- r_hold = w_hold = 10 / cycle;\r
- r_width = 30 / cycle;\r
- w_width = 40 / cycle;\r
- r_rdy = w_rdy = 100 / cycle;\r
- \r
- /* SLC_TAC: SLC timing arcs register */\r
- target_write_u32(target, 0x2002002c, (r_setup & 0xf) | ((r_hold & 0xf) << 4) |\r
- ((r_width & 0xf) << 8) | ((r_rdy & 0xf) << 12) | ((w_setup & 0xf) << 16) |\r
- ((w_hold & 0xf) << 20) | ((w_width & 0xf) << 24) | ((w_rdy & 0xf) << 28)); \r
- \r
- lpc3180_reset(device);\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int lpc3180_reset(struct nand_device_s *device)\r
-{\r
- lpc3180_nand_controller_t *lpc3180_info = device->controller_priv;\r
- target_t *target = lpc3180_info->target;\r
- \r
- if (target->state != TARGET_HALTED)\r
- {\r
- ERROR("target must be halted to use LPC3180 NAND flash controller");\r
- return ERROR_NAND_OPERATION_FAILED;\r
- }\r
- \r
- if (lpc3180_info->selected_controller == LPC3180_NO_CONTROLLER)\r
- {\r
- ERROR("BUG: no LPC3180 NAND flash controller selected");\r
- return ERROR_NAND_OPERATION_FAILED;\r
- }\r
- else if (lpc3180_info->selected_controller == LPC3180_MLC_CONTROLLER)\r
- {\r
- /* MLC_CMD = 0xff (reset controller and NAND device) */\r
- target_write_u32(target, 0x200b8000, 0xff);\r
-\r
- if (!lpc3180_controller_ready(device, 100))\r
- {\r
- ERROR("LPC3180 NAND controller timed out after reset");\r
- return ERROR_NAND_OPERATION_TIMEOUT;\r
- }\r
- }\r
- else if (lpc3180_info->selected_controller == LPC3180_SLC_CONTROLLER)\r
- {\r
- /* SLC_CTRL = 0x6 (ECC_CLEAR, SW_RESET) */\r
- target_write_u32(target, 0x20020010, 0x6);\r
- \r
- if (!lpc3180_controller_ready(device, 100))\r
- {\r
- ERROR("LPC3180 NAND controller timed out after reset");\r
- return ERROR_NAND_OPERATION_TIMEOUT;\r
- }\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int lpc3180_command(struct nand_device_s *device, u8 command)\r
-{\r
- lpc3180_nand_controller_t *lpc3180_info = device->controller_priv;\r
- target_t *target = lpc3180_info->target;\r
- \r
- if (target->state != TARGET_HALTED)\r
- {\r
- ERROR("target must be halted to use LPC3180 NAND flash controller");\r
- return ERROR_NAND_OPERATION_FAILED;\r
- }\r
- \r
- if (lpc3180_info->selected_controller == LPC3180_NO_CONTROLLER)\r
- {\r
- ERROR("BUG: no LPC3180 NAND flash controller selected");\r
- return ERROR_NAND_OPERATION_FAILED;\r
- }\r
- else if (lpc3180_info->selected_controller == LPC3180_MLC_CONTROLLER)\r
- {\r
- /* MLC_CMD = command */\r
- target_write_u32(target, 0x200b8000, command);\r
- }\r
- else if (lpc3180_info->selected_controller == LPC3180_SLC_CONTROLLER)\r
- {\r
- /* SLC_CMD = command */\r
- target_write_u32(target, 0x20020008, command);\r
- } \r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int lpc3180_address(struct nand_device_s *device, u8 address)\r
-{\r
- lpc3180_nand_controller_t *lpc3180_info = device->controller_priv;\r
- target_t *target = lpc3180_info->target;\r
- \r
- if (target->state != TARGET_HALTED)\r
- {\r
- ERROR("target must be halted to use LPC3180 NAND flash controller");\r
- return ERROR_NAND_OPERATION_FAILED;\r
- }\r
- \r
- if (lpc3180_info->selected_controller == LPC3180_NO_CONTROLLER)\r
- {\r
- ERROR("BUG: no LPC3180 NAND flash controller selected");\r
- return ERROR_NAND_OPERATION_FAILED;\r
- }\r
- else if (lpc3180_info->selected_controller == LPC3180_MLC_CONTROLLER)\r
- {\r
- /* MLC_ADDR = address */\r
- target_write_u32(target, 0x200b8004, address);\r
- }\r
- else if (lpc3180_info->selected_controller == LPC3180_SLC_CONTROLLER)\r
- {\r
- /* SLC_ADDR = address */\r
- target_write_u32(target, 0x20020004, address);\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int lpc3180_write_data(struct nand_device_s *device, u16 data)\r
-{\r
- lpc3180_nand_controller_t *lpc3180_info = device->controller_priv;\r
- target_t *target = lpc3180_info->target;\r
- \r
- if (target->state != TARGET_HALTED)\r
- {\r
- ERROR("target must be halted to use LPC3180 NAND flash controller");\r
- return ERROR_NAND_OPERATION_FAILED;\r
- }\r
- \r
- if (lpc3180_info->selected_controller == LPC3180_NO_CONTROLLER)\r
- {\r
- ERROR("BUG: no LPC3180 NAND flash controller selected");\r
- return ERROR_NAND_OPERATION_FAILED;\r
- }\r
- else if (lpc3180_info->selected_controller == LPC3180_MLC_CONTROLLER)\r
- {\r
- /* MLC_DATA = data */\r
- target_write_u32(target, 0x200b0000, data);\r
- }\r
- else if (lpc3180_info->selected_controller == LPC3180_SLC_CONTROLLER)\r
- {\r
- /* SLC_DATA = data */\r
- target_write_u32(target, 0x20020000, data);\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int lpc3180_read_data(struct nand_device_s *device, void *data)\r
-{\r
- lpc3180_nand_controller_t *lpc3180_info = device->controller_priv;\r
- target_t *target = lpc3180_info->target;\r
- \r
- if (target->state != TARGET_HALTED)\r
- {\r
- ERROR("target must be halted to use LPC3180 NAND flash controller");\r
- return ERROR_NAND_OPERATION_FAILED;\r
- }\r
- \r
- if (lpc3180_info->selected_controller == LPC3180_NO_CONTROLLER)\r
- {\r
- ERROR("BUG: no LPC3180 NAND flash controller selected");\r
- return ERROR_NAND_OPERATION_FAILED;\r
- }\r
- else if (lpc3180_info->selected_controller == LPC3180_MLC_CONTROLLER)\r
- {\r
- /* data = MLC_DATA, use sized access */\r
- if (device->bus_width == 8)\r
- {\r
- u8 *data8 = data;\r
- target_read_u8(target, 0x200b0000, data8);\r
- }\r
- else if (device->bus_width == 16)\r
- {\r
- u16 *data16 = data;\r
- target_read_u16(target, 0x200b0000, data16);\r
- }\r
- else\r
- {\r
- ERROR("BUG: bus_width neither 8 nor 16 bit");\r
- return ERROR_NAND_OPERATION_FAILED;\r
- }\r
- }\r
- else if (lpc3180_info->selected_controller == LPC3180_SLC_CONTROLLER)\r
- {\r
- u32 data32;\r
-\r
- /* data = SLC_DATA, must use 32-bit access */\r
- target_read_u32(target, 0x20020000, &data32);\r
- \r
- if (device->bus_width == 8)\r
- {\r
- u8 *data8 = data;\r
- *data8 = data32 & 0xff;\r
- }\r
- else if (device->bus_width == 16)\r
- {\r
- u16 *data16 = data;\r
- *data16 = data32 & 0xffff;\r
- }\r
- else\r
- {\r
- ERROR("BUG: bus_width neither 8 nor 16 bit");\r
- return ERROR_NAND_OPERATION_FAILED;\r
- }\r
- } \r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int lpc3180_write_page(struct nand_device_s *device, u32 page, u8 *data, u32 data_size, u8 *oob, u32 oob_size)\r
-{\r
- lpc3180_nand_controller_t *lpc3180_info = device->controller_priv;\r
- target_t *target = lpc3180_info->target;\r
- int retval;\r
- u8 status;\r
- \r
- if (target->state != TARGET_HALTED)\r
- {\r
- ERROR("target must be halted to use LPC3180 NAND flash controller");\r
- return ERROR_NAND_OPERATION_FAILED;\r
- }\r
- \r
- if (lpc3180_info->selected_controller == LPC3180_NO_CONTROLLER)\r
- {\r
- ERROR("BUG: no LPC3180 NAND flash controller selected");\r
- return ERROR_NAND_OPERATION_FAILED;\r
- }\r
- else if (lpc3180_info->selected_controller == LPC3180_MLC_CONTROLLER)\r
- {\r
- u8 *page_buffer;\r
- u8 *oob_buffer;\r
- int quarter, num_quarters;\r
- \r
- if (!data && oob)\r
- {\r
- ERROR("LPC3180 MLC controller can't write OOB data only");\r
- return ERROR_NAND_OPERATION_NOT_SUPPORTED;\r
- }\r
- \r
- if (oob && (oob_size > 6))\r
- {\r
- ERROR("LPC3180 MLC controller can't write more than 6 bytes of OOB data");\r
- return ERROR_NAND_OPERATION_NOT_SUPPORTED;\r
- }\r
- \r
- if (data_size > device->page_size)\r
- {\r
- ERROR("data size exceeds page size");\r
- return ERROR_NAND_OPERATION_NOT_SUPPORTED;\r
- }\r
- \r
- /* MLC_CMD = sequential input */\r
- target_write_u32(target, 0x200b8000, NAND_CMD_SEQIN);\r
-\r
- page_buffer = malloc(512);\r
- oob_buffer = malloc(6); \r
-\r
- if (device->page_size == 512)\r
- {\r
- /* MLC_ADDR = 0x0 (one column cycle) */\r
- target_write_u32(target, 0x200b8004, 0x0);\r
-\r
- /* MLC_ADDR = row */\r
- target_write_u32(target, 0x200b8004, page & 0xff);\r
- target_write_u32(target, 0x200b8004, (page >> 8) & 0xff);\r
- \r
- if (device->address_cycles == 4)\r
- target_write_u32(target, 0x200b8004, (page >> 16) & 0xff);\r
- }\r
- else\r
- {\r
- /* MLC_ADDR = 0x0 (two column cycles) */\r
- target_write_u32(target, 0x200b8004, 0x0);\r
- target_write_u32(target, 0x200b8004, 0x0);\r
-\r
- /* MLC_ADDR = row */\r
- target_write_u32(target, 0x200b8004, page & 0xff);\r
- target_write_u32(target, 0x200b8004, (page >> 8) & 0xff);\r
- }\r
- \r
- /* when using the MLC controller, we have to treat a large page device\r
- * as being made out of four quarters, each the size of a small page device\r
- */\r
- num_quarters = (device->page_size == 2048) ? 4 : 1;\r
- \r
- for (quarter = 0; quarter < num_quarters; quarter++)\r
- {\r
- int thisrun_data_size = (data_size > 512) ? 512 : data_size;\r
- int thisrun_oob_size = (oob_size > 6) ? 6 : oob_size;\r
- \r
- memset(page_buffer, 0xff, 512);\r
- if (data)\r
- {\r
- memcpy(page_buffer, data, thisrun_data_size);\r
- data_size -= thisrun_data_size;\r
- data += thisrun_data_size;\r
- }\r
- \r
- memset(oob_buffer, 0xff, (device->page_size == 512) ? 6 : 24);\r
- if (oob)\r
- {\r
- memcpy(page_buffer, oob, thisrun_oob_size);\r
- oob_size -= thisrun_oob_size;\r
- oob += thisrun_oob_size;\r
- }\r
- \r
- /* write MLC_ECC_ENC_REG to start encode cycle */\r
- target_write_u32(target, 0x200b8008, 0x0);\r
- \r
- target->type->write_memory(target, 0x200a8000, 4, 128, page_buffer + (quarter * 512));\r
- target->type->write_memory(target, 0x200a8000, 1, 6, oob_buffer + (quarter * 6));\r
- \r
- /* write MLC_ECC_AUTO_ENC_REG to start auto encode */\r
- target_write_u32(target, 0x200b8010, 0x0);\r
- \r
- if (!lpc3180_controller_ready(device, 1000))\r
- {\r
- ERROR("timeout while waiting for completion of auto encode cycle");\r
- return ERROR_NAND_OPERATION_FAILED;\r
- }\r
- }\r
- \r
- /* MLC_CMD = auto program command */\r
- target_write_u32(target, 0x200b8000, NAND_CMD_PAGEPROG);\r
- \r
- if ((retval = nand_read_status(device, &status)) != ERROR_OK)\r
- {\r
- ERROR("couldn't read status");\r
- return ERROR_NAND_OPERATION_FAILED;\r
- }\r
- \r
- if (status & NAND_STATUS_FAIL)\r
- {\r
- ERROR("write operation didn't pass, status: 0x%2.2x", status);\r
- return ERROR_NAND_OPERATION_FAILED;\r
- }\r
- \r
- free(page_buffer);\r
- free(oob_buffer);\r
- }\r
- else if (lpc3180_info->selected_controller == LPC3180_SLC_CONTROLLER)\r
- {\r
- return nand_write_page_raw(device, page, data, data_size, oob, oob_size);\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int lpc3180_read_page(struct nand_device_s *device, u32 page, u8 *data, u32 data_size, u8 *oob, u32 oob_size)\r
-{\r
- lpc3180_nand_controller_t *lpc3180_info = device->controller_priv;\r
- target_t *target = lpc3180_info->target;\r
- \r
- if (target->state != TARGET_HALTED)\r
- {\r
- ERROR("target must be halted to use LPC3180 NAND flash controller");\r
- return ERROR_NAND_OPERATION_FAILED;\r
- }\r
- \r
- if (lpc3180_info->selected_controller == LPC3180_NO_CONTROLLER)\r
- {\r
- ERROR("BUG: no LPC3180 NAND flash controller selected");\r
- return ERROR_NAND_OPERATION_FAILED;\r
- }\r
- else if (lpc3180_info->selected_controller == LPC3180_MLC_CONTROLLER)\r
- {\r
- u8 *page_buffer;\r
- u8 *oob_buffer;\r
- u32 page_bytes_done = 0;\r
- u32 oob_bytes_done = 0;\r
- u32 mlc_isr;\r
-\r
-#if 0\r
- if (oob && (oob_size > 6))\r
- {\r
- ERROR("LPC3180 MLC controller can't read more than 6 bytes of OOB data");\r
- return ERROR_NAND_OPERATION_NOT_SUPPORTED;\r
- }\r
-#endif\r
- \r
- if (data_size > device->page_size)\r
- {\r
- ERROR("data size exceeds page size");\r
- return ERROR_NAND_OPERATION_NOT_SUPPORTED;\r
- }\r
- \r
- if (device->page_size == 2048)\r
- {\r
- page_buffer = malloc(2048);\r
- oob_buffer = malloc(64);\r
- }\r
- else\r
- {\r
- page_buffer = malloc(512);\r
- oob_buffer = malloc(16);\r
- }\r
- \r
- if (!data && oob)\r
- {\r
- /* MLC_CMD = Read OOB \r
- * we can use the READOOB command on both small and large page devices,\r
- * as the controller translates the 0x50 command to a 0x0 with appropriate\r
- * positioning of the serial buffer read pointer\r
- */\r
- target_write_u32(target, 0x200b8000, NAND_CMD_READOOB);\r
- }\r
- else\r
- {\r
- /* MLC_CMD = Read0 */\r
- target_write_u32(target, 0x200b8000, NAND_CMD_READ0);\r
- }\r
- \r
- if (device->page_size == 512)\r
- {\r
- /* small page device */\r
- /* MLC_ADDR = 0x0 (one column cycle) */\r
- target_write_u32(target, 0x200b8004, 0x0);\r
-\r
- /* MLC_ADDR = row */\r
- target_write_u32(target, 0x200b8004, page & 0xff);\r
- target_write_u32(target, 0x200b8004, (page >> 8) & 0xff);\r
- \r
- if (device->address_cycles == 4)\r
- target_write_u32(target, 0x200b8004, (page >> 16) & 0xff);\r
- }\r
- else\r
- {\r
- /* large page device */\r
- /* MLC_ADDR = 0x0 (two column cycles) */\r
- target_write_u32(target, 0x200b8004, 0x0);\r
- target_write_u32(target, 0x200b8004, 0x0);\r
-\r
- /* MLC_ADDR = row */\r
- target_write_u32(target, 0x200b8004, page & 0xff);\r
- target_write_u32(target, 0x200b8004, (page >> 8) & 0xff);\r
- \r
- /* MLC_CMD = Read Start */\r
- target_write_u32(target, 0x200b8000, NAND_CMD_READSTART);\r
- }\r
- \r
- while (page_bytes_done < device->page_size)\r
- {\r
- /* MLC_ECC_AUTO_DEC_REG = dummy */\r
- target_write_u32(target, 0x200b8014, 0xaa55aa55);\r
- \r
- if (!lpc3180_controller_ready(device, 1000))\r
- {\r
- ERROR("timeout while waiting for completion of auto decode cycle");\r
- return ERROR_NAND_OPERATION_FAILED;\r
- }\r
- \r
- target_read_u32(target, 0x200b8048, &mlc_isr);\r
- \r
- if (mlc_isr & 0x8)\r
- {\r
- if (mlc_isr & 0x40)\r
- {\r
- ERROR("uncorrectable error detected: 0x%2.2x", mlc_isr);\r
- return ERROR_NAND_OPERATION_FAILED;\r
- }\r
- \r
- WARNING("%i symbol error detected and corrected", ((mlc_isr & 0x30) >> 4) + 1);\r
- }\r
- \r
- if (data)\r
- {\r
- target->type->read_memory(target, 0x200a8000, 4, 128, page_buffer + page_bytes_done);\r
- }\r
- \r
- if (oob)\r
- {\r
- target->type->read_memory(target, 0x200a8000, 4, 4, oob_buffer + oob_bytes_done);\r
- }\r
-\r
- page_bytes_done += 512;\r
- oob_bytes_done += 16;\r
- }\r
- \r
- if (data)\r
- memcpy(data, page_buffer, data_size);\r
- \r
- if (oob)\r
- memcpy(oob, oob_buffer, oob_size);\r
- \r
- free(page_buffer);\r
- free(oob_buffer);\r
- }\r
- else if (lpc3180_info->selected_controller == LPC3180_SLC_CONTROLLER)\r
- {\r
- return nand_read_page_raw(device, page, data, data_size, oob, oob_size);\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int lpc3180_controller_ready(struct nand_device_s *device, int timeout)\r
-{\r
- lpc3180_nand_controller_t *lpc3180_info = device->controller_priv;\r
- target_t *target = lpc3180_info->target;\r
- u8 status = 0x0;\r
- \r
- if (target->state != TARGET_HALTED)\r
- {\r
- ERROR("target must be halted to use LPC3180 NAND flash controller");\r
- return ERROR_NAND_OPERATION_FAILED;\r
- }\r
- \r
- do\r
- {\r
- if (lpc3180_info->selected_controller == LPC3180_MLC_CONTROLLER)\r
- {\r
- /* Read MLC_ISR, wait for controller to become ready */\r
- target_read_u8(target, 0x200b8048, &status);\r
- \r
- if (status & 2)\r
- return 1;\r
- }\r
- else if (lpc3180_info->selected_controller == LPC3180_SLC_CONTROLLER)\r
- {\r
- /* we pretend that the SLC controller is always ready */\r
- return 1;\r
- }\r
-\r
- usleep(1000);\r
- } while (timeout-- > 0);\r
- \r
- return 0;\r
-}\r
-\r
-int lpc3180_nand_ready(struct nand_device_s *device, int timeout)\r
-{\r
- lpc3180_nand_controller_t *lpc3180_info = device->controller_priv;\r
- target_t *target = lpc3180_info->target;\r
- \r
- if (target->state != TARGET_HALTED)\r
- {\r
- ERROR("target must be halted to use LPC3180 NAND flash controller");\r
- return ERROR_NAND_OPERATION_FAILED;\r
- }\r
- \r
- do\r
- {\r
- if (lpc3180_info->selected_controller == LPC3180_MLC_CONTROLLER)\r
- { \r
- u8 status = 0x0;\r
- \r
- /* Read MLC_ISR, wait for NAND flash device to become ready */\r
- target_read_u8(target, 0x200b8048, &status);\r
- \r
- if (status & 1)\r
- return 1;\r
- }\r
- else if (lpc3180_info->selected_controller == LPC3180_SLC_CONTROLLER)\r
- {\r
- u32 status = 0x0;\r
- \r
- /* Read SLC_STAT and check READY bit */\r
- target_read_u32(target, 0x20020018, &status);\r
- \r
- if (status & 1)\r
- return 1;\r
- }\r
- \r
- usleep(1000);\r
- } while (timeout-- > 0);\r
- \r
- return 0; \r
-}\r
-\r
-int handle_lpc3180_select_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- nand_device_t *device = NULL;\r
- lpc3180_nand_controller_t *lpc3180_info = NULL;\r
- char *selected[] = \r
- {\r
- "no", "mlc", "slc"\r
- };\r
- \r
- if ((argc < 1) || (argc > 2))\r
- {\r
- return ERROR_COMMAND_SYNTAX_ERROR;\r
- }\r
- \r
- device = get_nand_device_by_num(strtoul(args[0], NULL, 0));\r
- if (!device)\r
- {\r
- command_print(cmd_ctx, "nand device '#%s' is out of bounds", args[0]);\r
- return ERROR_OK;\r
- }\r
- \r
- lpc3180_info = device->controller_priv;\r
- \r
- if (argc == 2)\r
- {\r
- if (strcmp(args[1], "mlc") == 0)\r
- {\r
- lpc3180_info->selected_controller = LPC3180_MLC_CONTROLLER;\r
- }\r
- else if (strcmp(args[1], "slc") == 0)\r
- {\r
- lpc3180_info->selected_controller = LPC3180_SLC_CONTROLLER;\r
- }\r
- else\r
- {\r
- return ERROR_COMMAND_SYNTAX_ERROR;\r
- }\r
- }\r
- \r
- command_print(cmd_ctx, "%s controller selected", selected[lpc3180_info->selected_controller]);\r
- \r
- return ERROR_OK;\r
-}\r
+/***************************************************************************
+ * Copyright (C) 2007 by Dominic Rath *
+ * Dominic.Rath@gmx.de *
+ * *
+ * 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 "lpc3180_nand_controller.h"
+
+#include "replacements.h"
+#include "log.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "nand.h"
+#include "target.h"
+
+int lpc3180_nand_device_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct nand_device_s *device);
+int lpc3180_register_commands(struct command_context_s *cmd_ctx);
+int lpc3180_init(struct nand_device_s *device);
+int lpc3180_reset(struct nand_device_s *device);
+int lpc3180_command(struct nand_device_s *device, u8 command);
+int lpc3180_address(struct nand_device_s *device, u8 address);
+int lpc3180_write_data(struct nand_device_s *device, u16 data);
+int lpc3180_read_data(struct nand_device_s *device, void *data);
+int lpc3180_write_page(struct nand_device_s *device, u32 page, u8 *data, u32 data_size, u8 *oob, u32 oob_size);
+int lpc3180_read_page(struct nand_device_s *device, u32 page, u8 *data, u32 data_size, u8 *oob, u32 oob_size);
+int lpc3180_controller_ready(struct nand_device_s *device, int timeout);
+int lpc3180_nand_ready(struct nand_device_s *device, int timeout);
+
+int handle_lpc3180_select_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+
+nand_flash_controller_t lpc3180_nand_controller =
+{
+ .name = "lpc3180",
+ .nand_device_command = lpc3180_nand_device_command,
+ .register_commands = lpc3180_register_commands,
+ .init = lpc3180_init,
+ .reset = lpc3180_reset,
+ .command = lpc3180_command,
+ .address = lpc3180_address,
+ .write_data = lpc3180_write_data,
+ .read_data = lpc3180_read_data,
+ .write_page = lpc3180_write_page,
+ .read_page = lpc3180_read_page,
+ .controller_ready = lpc3180_controller_ready,
+ .nand_ready = lpc3180_nand_ready,
+};
+
+/* nand device lpc3180 <target#> <oscillator_frequency>
+ */
+int lpc3180_nand_device_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct nand_device_s *device)
+{
+ lpc3180_nand_controller_t *lpc3180_info;
+
+ if (argc < 3)
+ {
+ WARNING("incomplete 'lpc3180' nand flash configuration");
+ return ERROR_FLASH_BANK_INVALID;
+ }
+
+ lpc3180_info = malloc(sizeof(lpc3180_nand_controller_t));
+ device->controller_priv = lpc3180_info;
+
+ lpc3180_info->target = get_target_by_num(strtoul(args[1], NULL, 0));
+ if (!lpc3180_info->target)
+ {
+ ERROR("no target '%s' configured", args[1]);
+ return ERROR_NAND_DEVICE_INVALID;
+ }
+
+ lpc3180_info->osc_freq = strtoul(args[2], NULL, 0);
+ if ((lpc3180_info->osc_freq < 1000) || (lpc3180_info->osc_freq > 20000))
+ {
+ WARNING("LPC3180 oscillator frequency should be between 1000 and 20000 kHz, was %i", lpc3180_info->osc_freq);
+ }
+ lpc3180_info->selected_controller = LPC3180_NO_CONTROLLER;
+ lpc3180_info->sw_write_protection = 0;
+ lpc3180_info->sw_wp_lower_bound = 0x0;
+ lpc3180_info->sw_wp_upper_bound = 0x0;
+
+ return ERROR_OK;
+}
+
+int lpc3180_register_commands(struct command_context_s *cmd_ctx)
+{
+ command_t *lpc3180_cmd = register_command(cmd_ctx, NULL, "lpc3180", NULL, COMMAND_ANY, "commands specific to the LPC3180 NAND flash controllers");
+
+ register_command(cmd_ctx, lpc3180_cmd, "select", handle_lpc3180_select_command, COMMAND_EXEC, "select <'mlc'|'slc'> controller (default is mlc)");
+
+ return ERROR_OK;
+}
+
+int lpc3180_pll(int fclkin, u32 pll_ctrl)
+{
+ int bypass = (pll_ctrl & 0x8000) >> 15;
+ int direct = (pll_ctrl & 0x4000) >> 14;
+ int feedback = (pll_ctrl & 0x2000) >> 13;
+ int p = (1 << ((pll_ctrl & 0x1800) >> 11) * 2);
+ int n = ((pll_ctrl & 0x0600) >> 9) + 1;
+ int m = ((pll_ctrl & 0x01fe) >> 1) + 1;
+ int lock = (pll_ctrl & 0x1);
+
+ if (!lock)
+ WARNING("PLL is not locked");
+
+ if (!bypass && direct) /* direct mode */
+ return (m * fclkin) / n;
+
+ if (bypass && !direct) /* bypass mode */
+ return fclkin / (2 * p);
+
+ if (bypass & direct) /* direct bypass mode */
+ return fclkin;
+
+ if (feedback) /* integer mode */
+ return m * (fclkin / n);
+ else /* non-integer mode */
+ return (m / (2 * p)) * (fclkin / n);
+}
+
+float lpc3180_cycle_time(lpc3180_nand_controller_t *lpc3180_info)
+{
+ target_t *target = lpc3180_info->target;
+ u32 sysclk_ctrl, pwr_ctrl, hclkdiv_ctrl, hclkpll_ctrl;
+ int sysclk;
+ int hclk;
+ int hclk_pll;
+ float cycle;
+
+ /* calculate timings */
+
+ /* determine current SYSCLK (13'MHz or main oscillator) */
+ target_read_u32(target, 0x40004050, &sysclk_ctrl);
+
+ if ((sysclk_ctrl & 1) == 0)
+ sysclk = lpc3180_info->osc_freq;
+ else
+ sysclk = 13000;
+
+ /* determine selected HCLK source */
+ target_read_u32(target, 0x40004044, &pwr_ctrl);
+
+ if ((pwr_ctrl & (1 << 2)) == 0) /* DIRECT RUN mode */
+ {
+ hclk = sysclk;
+ }
+ else
+ {
+ target_read_u32(target, 0x40004058, &hclkpll_ctrl);
+ hclk_pll = lpc3180_pll(sysclk, hclkpll_ctrl);
+
+ target_read_u32(target, 0x40004040, &hclkdiv_ctrl);
+
+ if (pwr_ctrl & (1 << 10)) /* ARM_CLK and HCLK use PERIPH_CLK */
+ {
+ hclk = hclk_pll / (((hclkdiv_ctrl & 0x7c) >> 2) + 1);
+ }
+ else /* HCLK uses HCLK_PLL */
+ {
+ hclk = hclk_pll / (1 << (hclkdiv_ctrl & 0x3));
+ }
+ }
+
+ DEBUG("LPC3180 HCLK currently clocked at %i kHz", hclk);
+
+ cycle = (1.0 / hclk) * 1000000.0;
+
+ return cycle;
+}
+
+int lpc3180_init(struct nand_device_s *device)
+{
+ lpc3180_nand_controller_t *lpc3180_info = device->controller_priv;
+ target_t *target = lpc3180_info->target;
+ int bus_width = (device->bus_width) ? (device->bus_width) : 8;
+ int address_cycles = (device->address_cycles) ? (device->address_cycles) : 3;
+ int page_size = (device->page_size) ? (device->page_size) : 512;
+
+ if (target->state != TARGET_HALTED)
+ {
+ ERROR("target must be halted to use LPC3180 NAND flash controller");
+ return ERROR_NAND_OPERATION_FAILED;
+ }
+
+ /* sanitize arguments */
+ if ((bus_width != 8) && (bus_width != 16))
+ {
+ ERROR("LPC3180 only supports 8 or 16 bit bus width, not %i", bus_width);
+ return ERROR_NAND_OPERATION_NOT_SUPPORTED;
+ }
+
+ /* The LPC3180 only brings out 8 bit NAND data bus, but the controller
+ * would support 16 bit, too, so we just warn about this for now
+ */
+ if (bus_width == 16)
+ {
+ WARNING("LPC3180 only supports 8 bit bus width");
+ }
+
+ /* inform calling code about selected bus width */
+ device->bus_width = bus_width;
+
+ if ((address_cycles != 3) && (address_cycles != 4))
+ {
+ ERROR("LPC3180 only supports 3 or 4 address cycles, not %i", address_cycles);
+ return ERROR_NAND_OPERATION_NOT_SUPPORTED;
+ }
+
+ if ((page_size != 512) && (page_size != 2048))
+ {
+ ERROR("LPC3180 only supports 512 or 2048 byte pages, not %i", page_size);
+ return ERROR_NAND_OPERATION_NOT_SUPPORTED;
+ }
+
+ /* select MLC controller if none is currently selected */
+ if (lpc3180_info->selected_controller == LPC3180_NO_CONTROLLER)
+ {
+ DEBUG("no LPC3180 NAND flash controller selected, using default 'mlc'");
+ lpc3180_info->selected_controller = LPC3180_MLC_CONTROLLER;
+ }
+
+ if (lpc3180_info->selected_controller == LPC3180_MLC_CONTROLLER)
+ {
+ u32 mlc_icr_value = 0x0;
+ float cycle;
+ int twp, twh, trp, treh, trhz, trbwb, tcea;
+
+ /* FLASHCLK_CTRL = 0x22 (enable clock for MLC flash controller) */
+ target_write_u32(target, 0x400040c8, 0x22);
+
+ /* MLC_CEH = 0x0 (Force nCE assert) */
+ target_write_u32(target, 0x200b804c, 0x0);
+
+ /* MLC_LOCK = 0xa25e (unlock protected registers) */
+ target_write_u32(target, 0x200b8044, 0xa25e);
+
+ /* MLC_ICR = configuration */
+ if (lpc3180_info->sw_write_protection)
+ mlc_icr_value |= 0x8;
+ if (page_size == 2048)
+ mlc_icr_value |= 0x4;
+ if (address_cycles == 4)
+ mlc_icr_value |= 0x2;
+ if (bus_width == 16)
+ mlc_icr_value |= 0x1;
+ target_write_u32(target, 0x200b8030, mlc_icr_value);
+
+ /* calculate NAND controller timings */
+ cycle = lpc3180_cycle_time(lpc3180_info);
+
+ twp = ((40 / cycle) + 1);
+ twh = ((20 / cycle) + 1);
+ trp = ((30 / cycle) + 1);
+ treh = ((15 / cycle) + 1);
+ trhz = ((30 / cycle) + 1);
+ trbwb = ((100 / cycle) + 1);
+ tcea = ((45 / cycle) + 1);
+
+ /* MLC_LOCK = 0xa25e (unlock protected registers) */
+ target_write_u32(target, 0x200b8044, 0xa25e);
+
+ /* MLC_TIME_REG */
+ target_write_u32(target, 0x200b8034, (twp & 0xf) | ((twh & 0xf) << 4) |
+ ((trp & 0xf) << 8) | ((treh & 0xf) << 12) | ((trhz & 0x7) << 16) |
+ ((trbwb & 0x1f) << 19) | ((tcea & 0x3) << 24));
+
+ lpc3180_reset(device);
+ }
+ else if (lpc3180_info->selected_controller == LPC3180_SLC_CONTROLLER)
+ {
+ float cycle;
+ int r_setup, r_hold, r_width, r_rdy;
+ int w_setup, w_hold, w_width, w_rdy;
+
+ /* FLASHCLK_CTRL = 0x05 (enable clock for SLC flash controller) */
+ target_write_u32(target, 0x400040c8, 0x05);
+
+ /* SLC_CFG = 0x (Force nCE assert, ECC enabled, WIDTH = bus_width) */
+ target_write_u32(target, 0x20020014, 0x28 | (bus_width == 16) ? 1 : 0);
+
+ /* calculate NAND controller timings */
+ cycle = lpc3180_cycle_time(lpc3180_info);
+
+ r_setup = w_setup = 0;
+ r_hold = w_hold = 10 / cycle;
+ r_width = 30 / cycle;
+ w_width = 40 / cycle;
+ r_rdy = w_rdy = 100 / cycle;
+
+ /* SLC_TAC: SLC timing arcs register */
+ target_write_u32(target, 0x2002002c, (r_setup & 0xf) | ((r_hold & 0xf) << 4) |
+ ((r_width & 0xf) << 8) | ((r_rdy & 0xf) << 12) | ((w_setup & 0xf) << 16) |
+ ((w_hold & 0xf) << 20) | ((w_width & 0xf) << 24) | ((w_rdy & 0xf) << 28));
+
+ lpc3180_reset(device);
+ }
+
+ return ERROR_OK;
+}
+
+int lpc3180_reset(struct nand_device_s *device)
+{
+ lpc3180_nand_controller_t *lpc3180_info = device->controller_priv;
+ target_t *target = lpc3180_info->target;
+
+ if (target->state != TARGET_HALTED)
+ {
+ ERROR("target must be halted to use LPC3180 NAND flash controller");
+ return ERROR_NAND_OPERATION_FAILED;
+ }
+
+ if (lpc3180_info->selected_controller == LPC3180_NO_CONTROLLER)
+ {
+ ERROR("BUG: no LPC3180 NAND flash controller selected");
+ return ERROR_NAND_OPERATION_FAILED;
+ }
+ else if (lpc3180_info->selected_controller == LPC3180_MLC_CONTROLLER)
+ {
+ /* MLC_CMD = 0xff (reset controller and NAND device) */
+ target_write_u32(target, 0x200b8000, 0xff);
+
+ if (!lpc3180_controller_ready(device, 100))
+ {
+ ERROR("LPC3180 NAND controller timed out after reset");
+ return ERROR_NAND_OPERATION_TIMEOUT;
+ }
+ }
+ else if (lpc3180_info->selected_controller == LPC3180_SLC_CONTROLLER)
+ {
+ /* SLC_CTRL = 0x6 (ECC_CLEAR, SW_RESET) */
+ target_write_u32(target, 0x20020010, 0x6);
+
+ if (!lpc3180_controller_ready(device, 100))
+ {
+ ERROR("LPC3180 NAND controller timed out after reset");
+ return ERROR_NAND_OPERATION_TIMEOUT;
+ }
+ }
+
+ return ERROR_OK;
+}
+
+int lpc3180_command(struct nand_device_s *device, u8 command)
+{
+ lpc3180_nand_controller_t *lpc3180_info = device->controller_priv;
+ target_t *target = lpc3180_info->target;
+
+ if (target->state != TARGET_HALTED)
+ {
+ ERROR("target must be halted to use LPC3180 NAND flash controller");
+ return ERROR_NAND_OPERATION_FAILED;
+ }
+
+ if (lpc3180_info->selected_controller == LPC3180_NO_CONTROLLER)
+ {
+ ERROR("BUG: no LPC3180 NAND flash controller selected");
+ return ERROR_NAND_OPERATION_FAILED;
+ }
+ else if (lpc3180_info->selected_controller == LPC3180_MLC_CONTROLLER)
+ {
+ /* MLC_CMD = command */
+ target_write_u32(target, 0x200b8000, command);
+ }
+ else if (lpc3180_info->selected_controller == LPC3180_SLC_CONTROLLER)
+ {
+ /* SLC_CMD = command */
+ target_write_u32(target, 0x20020008, command);
+ }
+
+ return ERROR_OK;
+}
+
+int lpc3180_address(struct nand_device_s *device, u8 address)
+{
+ lpc3180_nand_controller_t *lpc3180_info = device->controller_priv;
+ target_t *target = lpc3180_info->target;
+
+ if (target->state != TARGET_HALTED)
+ {
+ ERROR("target must be halted to use LPC3180 NAND flash controller");
+ return ERROR_NAND_OPERATION_FAILED;
+ }
+
+ if (lpc3180_info->selected_controller == LPC3180_NO_CONTROLLER)
+ {
+ ERROR("BUG: no LPC3180 NAND flash controller selected");
+ return ERROR_NAND_OPERATION_FAILED;
+ }
+ else if (lpc3180_info->selected_controller == LPC3180_MLC_CONTROLLER)
+ {
+ /* MLC_ADDR = address */
+ target_write_u32(target, 0x200b8004, address);
+ }
+ else if (lpc3180_info->selected_controller == LPC3180_SLC_CONTROLLER)
+ {
+ /* SLC_ADDR = address */
+ target_write_u32(target, 0x20020004, address);
+ }
+
+ return ERROR_OK;
+}
+
+int lpc3180_write_data(struct nand_device_s *device, u16 data)
+{
+ lpc3180_nand_controller_t *lpc3180_info = device->controller_priv;
+ target_t *target = lpc3180_info->target;
+
+ if (target->state != TARGET_HALTED)
+ {
+ ERROR("target must be halted to use LPC3180 NAND flash controller");
+ return ERROR_NAND_OPERATION_FAILED;
+ }
+
+ if (lpc3180_info->selected_controller == LPC3180_NO_CONTROLLER)
+ {
+ ERROR("BUG: no LPC3180 NAND flash controller selected");
+ return ERROR_NAND_OPERATION_FAILED;
+ }
+ else if (lpc3180_info->selected_controller == LPC3180_MLC_CONTROLLER)
+ {
+ /* MLC_DATA = data */
+ target_write_u32(target, 0x200b0000, data);
+ }
+ else if (lpc3180_info->selected_controller == LPC3180_SLC_CONTROLLER)
+ {
+ /* SLC_DATA = data */
+ target_write_u32(target, 0x20020000, data);
+ }
+
+ return ERROR_OK;
+}
+
+int lpc3180_read_data(struct nand_device_s *device, void *data)
+{
+ lpc3180_nand_controller_t *lpc3180_info = device->controller_priv;
+ target_t *target = lpc3180_info->target;
+
+ if (target->state != TARGET_HALTED)
+ {
+ ERROR("target must be halted to use LPC3180 NAND flash controller");
+ return ERROR_NAND_OPERATION_FAILED;
+ }
+
+ if (lpc3180_info->selected_controller == LPC3180_NO_CONTROLLER)
+ {
+ ERROR("BUG: no LPC3180 NAND flash controller selected");
+ return ERROR_NAND_OPERATION_FAILED;
+ }
+ else if (lpc3180_info->selected_controller == LPC3180_MLC_CONTROLLER)
+ {
+ /* data = MLC_DATA, use sized access */
+ if (device->bus_width == 8)
+ {
+ u8 *data8 = data;
+ target_read_u8(target, 0x200b0000, data8);
+ }
+ else if (device->bus_width == 16)
+ {
+ u16 *data16 = data;
+ target_read_u16(target, 0x200b0000, data16);
+ }
+ else
+ {
+ ERROR("BUG: bus_width neither 8 nor 16 bit");
+ return ERROR_NAND_OPERATION_FAILED;
+ }
+ }
+ else if (lpc3180_info->selected_controller == LPC3180_SLC_CONTROLLER)
+ {
+ u32 data32;
+
+ /* data = SLC_DATA, must use 32-bit access */
+ target_read_u32(target, 0x20020000, &data32);
+
+ if (device->bus_width == 8)
+ {
+ u8 *data8 = data;
+ *data8 = data32 & 0xff;
+ }
+ else if (device->bus_width == 16)
+ {
+ u16 *data16 = data;
+ *data16 = data32 & 0xffff;
+ }
+ else
+ {
+ ERROR("BUG: bus_width neither 8 nor 16 bit");
+ return ERROR_NAND_OPERATION_FAILED;
+ }
+ }
+
+ return ERROR_OK;
+}
+
+int lpc3180_write_page(struct nand_device_s *device, u32 page, u8 *data, u32 data_size, u8 *oob, u32 oob_size)
+{
+ lpc3180_nand_controller_t *lpc3180_info = device->controller_priv;
+ target_t *target = lpc3180_info->target;
+ int retval;
+ u8 status;
+
+ if (target->state != TARGET_HALTED)
+ {
+ ERROR("target must be halted to use LPC3180 NAND flash controller");
+ return ERROR_NAND_OPERATION_FAILED;
+ }
+
+ if (lpc3180_info->selected_controller == LPC3180_NO_CONTROLLER)
+ {
+ ERROR("BUG: no LPC3180 NAND flash controller selected");
+ return ERROR_NAND_OPERATION_FAILED;
+ }
+ else if (lpc3180_info->selected_controller == LPC3180_MLC_CONTROLLER)
+ {
+ u8 *page_buffer;
+ u8 *oob_buffer;
+ int quarter, num_quarters;
+
+ if (!data && oob)
+ {
+ ERROR("LPC3180 MLC controller can't write OOB data only");
+ return ERROR_NAND_OPERATION_NOT_SUPPORTED;
+ }
+
+ if (oob && (oob_size > 6))
+ {
+ ERROR("LPC3180 MLC controller can't write more than 6 bytes of OOB data");
+ return ERROR_NAND_OPERATION_NOT_SUPPORTED;
+ }
+
+ if (data_size > device->page_size)
+ {
+ ERROR("data size exceeds page size");
+ return ERROR_NAND_OPERATION_NOT_SUPPORTED;
+ }
+
+ /* MLC_CMD = sequential input */
+ target_write_u32(target, 0x200b8000, NAND_CMD_SEQIN);
+
+ page_buffer = malloc(512);
+ oob_buffer = malloc(6);
+
+ if (device->page_size == 512)
+ {
+ /* MLC_ADDR = 0x0 (one column cycle) */
+ target_write_u32(target, 0x200b8004, 0x0);
+
+ /* MLC_ADDR = row */
+ target_write_u32(target, 0x200b8004, page & 0xff);
+ target_write_u32(target, 0x200b8004, (page >> 8) & 0xff);
+
+ if (device->address_cycles == 4)
+ target_write_u32(target, 0x200b8004, (page >> 16) & 0xff);
+ }
+ else
+ {
+ /* MLC_ADDR = 0x0 (two column cycles) */
+ target_write_u32(target, 0x200b8004, 0x0);
+ target_write_u32(target, 0x200b8004, 0x0);
+
+ /* MLC_ADDR = row */
+ target_write_u32(target, 0x200b8004, page & 0xff);
+ target_write_u32(target, 0x200b8004, (page >> 8) & 0xff);
+ }
+
+ /* when using the MLC controller, we have to treat a large page device
+ * as being made out of four quarters, each the size of a small page device
+ */
+ num_quarters = (device->page_size == 2048) ? 4 : 1;
+
+ for (quarter = 0; quarter < num_quarters; quarter++)
+ {
+ int thisrun_data_size = (data_size > 512) ? 512 : data_size;
+ int thisrun_oob_size = (oob_size > 6) ? 6 : oob_size;
+
+ memset(page_buffer, 0xff, 512);
+ if (data)
+ {
+ memcpy(page_buffer, data, thisrun_data_size);
+ data_size -= thisrun_data_size;
+ data += thisrun_data_size;
+ }
+
+ memset(oob_buffer, 0xff, (device->page_size == 512) ? 6 : 24);
+ if (oob)
+ {
+ memcpy(page_buffer, oob, thisrun_oob_size);
+ oob_size -= thisrun_oob_size;
+ oob += thisrun_oob_size;
+ }
+
+ /* write MLC_ECC_ENC_REG to start encode cycle */
+ target_write_u32(target, 0x200b8008, 0x0);
+
+ target->type->write_memory(target, 0x200a8000, 4, 128, page_buffer + (quarter * 512));
+ target->type->write_memory(target, 0x200a8000, 1, 6, oob_buffer + (quarter * 6));
+
+ /* write MLC_ECC_AUTO_ENC_REG to start auto encode */
+ target_write_u32(target, 0x200b8010, 0x0);
+
+ if (!lpc3180_controller_ready(device, 1000))
+ {
+ ERROR("timeout while waiting for completion of auto encode cycle");
+ return ERROR_NAND_OPERATION_FAILED;
+ }
+ }
+
+ /* MLC_CMD = auto program command */
+ target_write_u32(target, 0x200b8000, NAND_CMD_PAGEPROG);
+
+ if ((retval = nand_read_status(device, &status)) != ERROR_OK)
+ {
+ ERROR("couldn't read status");
+ return ERROR_NAND_OPERATION_FAILED;
+ }
+
+ if (status & NAND_STATUS_FAIL)
+ {
+ ERROR("write operation didn't pass, status: 0x%2.2x", status);
+ return ERROR_NAND_OPERATION_FAILED;
+ }
+
+ free(page_buffer);
+ free(oob_buffer);
+ }
+ else if (lpc3180_info->selected_controller == LPC3180_SLC_CONTROLLER)
+ {
+ return nand_write_page_raw(device, page, data, data_size, oob, oob_size);
+ }
+
+ return ERROR_OK;
+}
+
+int lpc3180_read_page(struct nand_device_s *device, u32 page, u8 *data, u32 data_size, u8 *oob, u32 oob_size)
+{
+ lpc3180_nand_controller_t *lpc3180_info = device->controller_priv;
+ target_t *target = lpc3180_info->target;
+
+ if (target->state != TARGET_HALTED)
+ {
+ ERROR("target must be halted to use LPC3180 NAND flash controller");
+ return ERROR_NAND_OPERATION_FAILED;
+ }
+
+ if (lpc3180_info->selected_controller == LPC3180_NO_CONTROLLER)
+ {
+ ERROR("BUG: no LPC3180 NAND flash controller selected");
+ return ERROR_NAND_OPERATION_FAILED;
+ }
+ else if (lpc3180_info->selected_controller == LPC3180_MLC_CONTROLLER)
+ {
+ u8 *page_buffer;
+ u8 *oob_buffer;
+ u32 page_bytes_done = 0;
+ u32 oob_bytes_done = 0;
+ u32 mlc_isr;
+
+#if 0
+ if (oob && (oob_size > 6))
+ {
+ ERROR("LPC3180 MLC controller can't read more than 6 bytes of OOB data");
+ return ERROR_NAND_OPERATION_NOT_SUPPORTED;
+ }
+#endif
+
+ if (data_size > device->page_size)
+ {
+ ERROR("data size exceeds page size");
+ return ERROR_NAND_OPERATION_NOT_SUPPORTED;
+ }
+
+ if (device->page_size == 2048)
+ {
+ page_buffer = malloc(2048);
+ oob_buffer = malloc(64);
+ }
+ else
+ {
+ page_buffer = malloc(512);
+ oob_buffer = malloc(16);
+ }
+
+ if (!data && oob)
+ {
+ /* MLC_CMD = Read OOB
+ * we can use the READOOB command on both small and large page devices,
+ * as the controller translates the 0x50 command to a 0x0 with appropriate
+ * positioning of the serial buffer read pointer
+ */
+ target_write_u32(target, 0x200b8000, NAND_CMD_READOOB);
+ }
+ else
+ {
+ /* MLC_CMD = Read0 */
+ target_write_u32(target, 0x200b8000, NAND_CMD_READ0);
+ }
+
+ if (device->page_size == 512)
+ {
+ /* small page device */
+ /* MLC_ADDR = 0x0 (one column cycle) */
+ target_write_u32(target, 0x200b8004, 0x0);
+
+ /* MLC_ADDR = row */
+ target_write_u32(target, 0x200b8004, page & 0xff);
+ target_write_u32(target, 0x200b8004, (page >> 8) & 0xff);
+
+ if (device->address_cycles == 4)
+ target_write_u32(target, 0x200b8004, (page >> 16) & 0xff);
+ }
+ else
+ {
+ /* large page device */
+ /* MLC_ADDR = 0x0 (two column cycles) */
+ target_write_u32(target, 0x200b8004, 0x0);
+ target_write_u32(target, 0x200b8004, 0x0);
+
+ /* MLC_ADDR = row */
+ target_write_u32(target, 0x200b8004, page & 0xff);
+ target_write_u32(target, 0x200b8004, (page >> 8) & 0xff);
+
+ /* MLC_CMD = Read Start */
+ target_write_u32(target, 0x200b8000, NAND_CMD_READSTART);
+ }
+
+ while (page_bytes_done < device->page_size)
+ {
+ /* MLC_ECC_AUTO_DEC_REG = dummy */
+ target_write_u32(target, 0x200b8014, 0xaa55aa55);
+
+ if (!lpc3180_controller_ready(device, 1000))
+ {
+ ERROR("timeout while waiting for completion of auto decode cycle");
+ return ERROR_NAND_OPERATION_FAILED;
+ }
+
+ target_read_u32(target, 0x200b8048, &mlc_isr);
+
+ if (mlc_isr & 0x8)
+ {
+ if (mlc_isr & 0x40)
+ {
+ ERROR("uncorrectable error detected: 0x%2.2x", mlc_isr);
+ return ERROR_NAND_OPERATION_FAILED;
+ }
+
+ WARNING("%i symbol error detected and corrected", ((mlc_isr & 0x30) >> 4) + 1);
+ }
+
+ if (data)
+ {
+ target->type->read_memory(target, 0x200a8000, 4, 128, page_buffer + page_bytes_done);
+ }
+
+ if (oob)
+ {
+ target->type->read_memory(target, 0x200a8000, 4, 4, oob_buffer + oob_bytes_done);
+ }
+
+ page_bytes_done += 512;
+ oob_bytes_done += 16;
+ }
+
+ if (data)
+ memcpy(data, page_buffer, data_size);
+
+ if (oob)
+ memcpy(oob, oob_buffer, oob_size);
+
+ free(page_buffer);
+ free(oob_buffer);
+ }
+ else if (lpc3180_info->selected_controller == LPC3180_SLC_CONTROLLER)
+ {
+ return nand_read_page_raw(device, page, data, data_size, oob, oob_size);
+ }
+
+ return ERROR_OK;
+}
+
+int lpc3180_controller_ready(struct nand_device_s *device, int timeout)
+{
+ lpc3180_nand_controller_t *lpc3180_info = device->controller_priv;
+ target_t *target = lpc3180_info->target;
+ u8 status = 0x0;
+
+ if (target->state != TARGET_HALTED)
+ {
+ ERROR("target must be halted to use LPC3180 NAND flash controller");
+ return ERROR_NAND_OPERATION_FAILED;
+ }
+
+ do
+ {
+ if (lpc3180_info->selected_controller == LPC3180_MLC_CONTROLLER)
+ {
+ /* Read MLC_ISR, wait for controller to become ready */
+ target_read_u8(target, 0x200b8048, &status);
+
+ if (status & 2)
+ return 1;
+ }
+ else if (lpc3180_info->selected_controller == LPC3180_SLC_CONTROLLER)
+ {
+ /* we pretend that the SLC controller is always ready */
+ return 1;
+ }
+
+ usleep(1000);
+ } while (timeout-- > 0);
+
+ return 0;
+}
+
+int lpc3180_nand_ready(struct nand_device_s *device, int timeout)
+{
+ lpc3180_nand_controller_t *lpc3180_info = device->controller_priv;
+ target_t *target = lpc3180_info->target;
+
+ if (target->state != TARGET_HALTED)
+ {
+ ERROR("target must be halted to use LPC3180 NAND flash controller");
+ return ERROR_NAND_OPERATION_FAILED;
+ }
+
+ do
+ {
+ if (lpc3180_info->selected_controller == LPC3180_MLC_CONTROLLER)
+ {
+ u8 status = 0x0;
+
+ /* Read MLC_ISR, wait for NAND flash device to become ready */
+ target_read_u8(target, 0x200b8048, &status);
+
+ if (status & 1)
+ return 1;
+ }
+ else if (lpc3180_info->selected_controller == LPC3180_SLC_CONTROLLER)
+ {
+ u32 status = 0x0;
+
+ /* Read SLC_STAT and check READY bit */
+ target_read_u32(target, 0x20020018, &status);
+
+ if (status & 1)
+ return 1;
+ }
+
+ usleep(1000);
+ } while (timeout-- > 0);
+
+ return 0;
+}
+
+int handle_lpc3180_select_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ nand_device_t *device = NULL;
+ lpc3180_nand_controller_t *lpc3180_info = NULL;
+ char *selected[] =
+ {
+ "no", "mlc", "slc"
+ };
+
+ if ((argc < 1) || (argc > 2))
+ {
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ device = get_nand_device_by_num(strtoul(args[0], NULL, 0));
+ if (!device)
+ {
+ command_print(cmd_ctx, "nand device '#%s' is out of bounds", args[0]);
+ return ERROR_OK;
+ }
+
+ lpc3180_info = device->controller_priv;
+
+ if (argc == 2)
+ {
+ if (strcmp(args[1], "mlc") == 0)
+ {
+ lpc3180_info->selected_controller = LPC3180_MLC_CONTROLLER;
+ }
+ else if (strcmp(args[1], "slc") == 0)
+ {
+ lpc3180_info->selected_controller = LPC3180_SLC_CONTROLLER;
+ }
+ else
+ {
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+ }
+
+ command_print(cmd_ctx, "%s controller selected", selected[lpc3180_info->selected_controller]);
+
+ return ERROR_OK;
+}
-/***************************************************************************\r
- * Copyright (C) 2007 by Dominic Rath *\r
- * Dominic.Rath@gmx.de *\r
- * *\r
- * partially based on *\r
- * drivers/mtd/nand_ids.c *\r
- * *\r
- * Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.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 "replacements.h"\r
-#include "log.h"\r
-\r
-#include <stdlib.h>\r
-#include <string.h>\r
-#include <inttypes.h>\r
-\r
-#include <errno.h>\r
-\r
-#include "nand.h"\r
-#include "flash.h"\r
-#include "time_support.h"\r
-#include "fileio.h"\r
-#include "image.h"\r
-\r
-int nand_register_commands(struct command_context_s *cmd_ctx);\r
-int handle_nand_list_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-int handle_nand_probe_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-int handle_nand_check_bad_blocks_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-int handle_nand_info_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-int handle_nand_copy_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-int handle_nand_write_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-int handle_nand_dump_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-int handle_nand_erase_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-\r
-int handle_nand_raw_access_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-\r
-int nand_read_page_raw(struct nand_device_s *device, u32 page, u8 *data, u32 data_size, u8 *oob, u32 oob_size);\r
-int nand_read_page(struct nand_device_s *device, u32 page, u8 *data, u32 data_size, u8 *oob, u32 oob_size);\r
-int nand_read_plain(struct nand_device_s *device, u32 address, u8 *data, u32 data_size);\r
-\r
-int nand_write_page_raw(struct nand_device_s *device, u32 page, u8 *data, u32 data_size, u8 *oob, u32 oob_size);\r
-int nand_write_page(struct nand_device_s *device, u32 page, u8 *data, u32 data_size, u8 *oob, u32 oob_size);\r
-\r
-/* NAND flash controller\r
- */\r
-extern nand_flash_controller_t lpc3180_nand_controller;\r
-extern nand_flash_controller_t s3c2410_nand_controller;\r
-extern nand_flash_controller_t s3c2412_nand_controller;\r
-extern nand_flash_controller_t s3c2440_nand_controller;\r
-extern nand_flash_controller_t s3c2443_nand_controller;\r
-\r
-/* extern nand_flash_controller_t boundary_scan_nand_controller; */\r
-\r
-nand_flash_controller_t *nand_flash_controllers[] =\r
-{\r
- &lpc3180_nand_controller,\r
- &s3c2410_nand_controller,\r
- &s3c2412_nand_controller,\r
- &s3c2440_nand_controller,\r
- &s3c2443_nand_controller,\r
-/* &boundary_scan_nand_controller, */\r
- NULL\r
-};\r
-\r
-/* configured NAND devices and NAND Flash command handler */\r
-nand_device_t *nand_devices = NULL;\r
-static command_t *nand_cmd;\r
-\r
-/* Chip ID list\r
- *\r
- * Name, ID code, pagesize, chipsize in MegaByte, eraseblock size,\r
- * options\r
- *\r
- * Pagesize; 0, 256, 512\r
- * 0 get this information from the extended chip ID\r
- * 256 256 Byte page size\r
- * 512 512 Byte page size\r
- */\r
-nand_info_t nand_flash_ids[] =\r
-{\r
- {"NAND 1MiB 5V 8-bit", 0x6e, 256, 1, 0x1000, 0},\r
- {"NAND 2MiB 5V 8-bit", 0x64, 256, 2, 0x1000, 0},\r
- {"NAND 4MiB 5V 8-bit", 0x6b, 512, 4, 0x2000, 0},\r
- {"NAND 1MiB 3,3V 8-bit", 0xe8, 256, 1, 0x1000, 0},\r
- {"NAND 1MiB 3,3V 8-bit", 0xec, 256, 1, 0x1000, 0},\r
- {"NAND 2MiB 3,3V 8-bit", 0xea, 256, 2, 0x1000, 0},\r
- {"NAND 4MiB 3,3V 8-bit", 0xd5, 512, 4, 0x2000, 0},\r
- {"NAND 4MiB 3,3V 8-bit", 0xe3, 512, 4, 0x2000, 0},\r
- {"NAND 4MiB 3,3V 8-bit", 0xe5, 512, 4, 0x2000, 0},\r
- {"NAND 8MiB 3,3V 8-bit", 0xd6, 512, 8, 0x2000, 0},\r
-\r
- {"NAND 8MiB 1,8V 8-bit", 0x39, 512, 8, 0x2000, 0},\r
- {"NAND 8MiB 3,3V 8-bit", 0xe6, 512, 8, 0x2000, 0},\r
- {"NAND 8MiB 1,8V 16-bit", 0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16},\r
- {"NAND 8MiB 3,3V 16-bit", 0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16},\r
-\r
- {"NAND 16MiB 1,8V 8-bit", 0x33, 512, 16, 0x4000, 0},\r
- {"NAND 16MiB 3,3V 8-bit", 0x73, 512, 16, 0x4000, 0},\r
- {"NAND 16MiB 1,8V 16-bit", 0x43, 512, 16, 0x4000, NAND_BUSWIDTH_16},\r
- {"NAND 16MiB 3,3V 16-bit", 0x53, 512, 16, 0x4000, NAND_BUSWIDTH_16},\r
-\r
- {"NAND 32MiB 1,8V 8-bit", 0x35, 512, 32, 0x4000, 0},\r
- {"NAND 32MiB 3,3V 8-bit", 0x75, 512, 32, 0x4000, 0},\r
- {"NAND 32MiB 1,8V 16-bit", 0x45, 512, 32, 0x4000, NAND_BUSWIDTH_16},\r
- {"NAND 32MiB 3,3V 16-bit", 0x55, 512, 32, 0x4000, NAND_BUSWIDTH_16},\r
-\r
- {"NAND 64MiB 1,8V 8-bit", 0x36, 512, 64, 0x4000, 0},\r
- {"NAND 64MiB 3,3V 8-bit", 0x76, 512, 64, 0x4000, 0},\r
- {"NAND 64MiB 1,8V 16-bit", 0x46, 512, 64, 0x4000, NAND_BUSWIDTH_16},\r
- {"NAND 64MiB 3,3V 16-bit", 0x56, 512, 64, 0x4000, NAND_BUSWIDTH_16},\r
-\r
- {"NAND 128MiB 1,8V 8-bit", 0x78, 512, 128, 0x4000, 0},\r
- {"NAND 128MiB 1,8V 8-bit", 0x39, 512, 128, 0x4000, 0},\r
- {"NAND 128MiB 3,3V 8-bit", 0x79, 512, 128, 0x4000, 0},\r
- {"NAND 128MiB 1,8V 16-bit", 0x72, 512, 128, 0x4000, NAND_BUSWIDTH_16},\r
- {"NAND 128MiB 1,8V 16-bit", 0x49, 512, 128, 0x4000, NAND_BUSWIDTH_16},\r
- {"NAND 128MiB 3,3V 16-bit", 0x74, 512, 128, 0x4000, NAND_BUSWIDTH_16},\r
- {"NAND 128MiB 3,3V 16-bit", 0x59, 512, 128, 0x4000, NAND_BUSWIDTH_16},\r
-\r
- {"NAND 256MiB 3,3V 8-bit", 0x71, 512, 256, 0x4000, 0},\r
-\r
- {"NAND 64MiB 1,8V 8-bit", 0xA2, 0, 64, 0, LP_OPTIONS},\r
- {"NAND 64MiB 3,3V 8-bit", 0xF2, 0, 64, 0, LP_OPTIONS},\r
- {"NAND 64MiB 1,8V 16-bit", 0xB2, 0, 64, 0, LP_OPTIONS16},\r
- {"NAND 64MiB 3,3V 16-bit", 0xC2, 0, 64, 0, LP_OPTIONS16},\r
-\r
- {"NAND 128MiB 1,8V 8-bit", 0xA1, 0, 128, 0, LP_OPTIONS},\r
- {"NAND 128MiB 3,3V 8-bit", 0xF1, 0, 128, 0, LP_OPTIONS},\r
- {"NAND 128MiB 1,8V 16-bit", 0xB1, 0, 128, 0, LP_OPTIONS16},\r
- {"NAND 128MiB 3,3V 16-bit", 0xC1, 0, 128, 0, LP_OPTIONS16},\r
-\r
- {"NAND 256MiB 1,8V 8-bit", 0xAA, 0, 256, 0, LP_OPTIONS},\r
- {"NAND 256MiB 3,3V 8-bit", 0xDA, 0, 256, 0, LP_OPTIONS},\r
- {"NAND 256MiB 1,8V 16-bit", 0xBA, 0, 256, 0, LP_OPTIONS16},\r
- {"NAND 256MiB 3,3V 16-bit", 0xCA, 0, 256, 0, LP_OPTIONS16},\r
-\r
- {"NAND 512MiB 1,8V 8-bit", 0xAC, 0, 512, 0, LP_OPTIONS},\r
- {"NAND 512MiB 3,3V 8-bit", 0xDC, 0, 512, 0, LP_OPTIONS},\r
- {"NAND 512MiB 1,8V 16-bit", 0xBC, 0, 512, 0, LP_OPTIONS16},\r
- {"NAND 512MiB 3,3V 16-bit", 0xCC, 0, 512, 0, LP_OPTIONS16},\r
-\r
- {"NAND 1GiB 1,8V 8-bit", 0xA3, 0, 1024, 0, LP_OPTIONS},\r
- {"NAND 1GiB 3,3V 8-bit", 0xD3, 0, 1024, 0, LP_OPTIONS},\r
- {"NAND 1GiB 1,8V 16-bit", 0xB3, 0, 1024, 0, LP_OPTIONS16},\r
- {"NAND 1GiB 3,3V 16-bit", 0xC3, 0, 1024, 0, LP_OPTIONS16},\r
-\r
- {"NAND 2GiB 1,8V 8-bit", 0xA5, 0, 2048, 0, LP_OPTIONS},\r
- {"NAND 2GiB 3,3V 8-bit", 0xD5, 0, 2048, 0, LP_OPTIONS},\r
- {"NAND 2GiB 1,8V 16-bit", 0xB5, 0, 2048, 0, LP_OPTIONS16},\r
- {"NAND 2GiB 3,3V 16-bit", 0xC5, 0, 2048, 0, LP_OPTIONS16},\r
-\r
- {NULL, 0,}\r
-};\r
-\r
-/* Manufacturer ID list\r
- */\r
-nand_manufacturer_t nand_manuf_ids[] =\r
-{\r
- {0x0, "unknown"},\r
- {NAND_MFR_TOSHIBA, "Toshiba"},\r
- {NAND_MFR_SAMSUNG, "Samsung"},\r
- {NAND_MFR_FUJITSU, "Fujitsu"},\r
- {NAND_MFR_NATIONAL, "National"},\r
- {NAND_MFR_RENESAS, "Renesas"},\r
- {NAND_MFR_STMICRO, "ST Micro"},\r
- {NAND_MFR_HYNIX, "Hynix"},\r
- {0x0, NULL},\r
-};\r
-\r
-/* nand device <nand_controller> [controller options]\r
- */\r
-int handle_nand_device_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- int i;\r
- int retval;\r
- \r
- if (argc < 1)\r
- {\r
- WARNING("incomplete flash device nand configuration");\r
- return ERROR_FLASH_BANK_INVALID;\r
- }\r
- \r
- for (i = 0; nand_flash_controllers[i]; i++)\r
- {\r
- nand_device_t *p, *c;\r
- \r
- if (strcmp(args[0], nand_flash_controllers[i]->name) == 0)\r
- {\r
- /* register flash specific commands */\r
- if (nand_flash_controllers[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(nand_device_t));\r
-\r
- c->controller = nand_flash_controllers[i];\r
- c->controller_priv = NULL;\r
- c->manufacturer = NULL;\r
- c->device = NULL;\r
- c->bus_width = 0;\r
- c->address_cycles = 0;\r
- c->page_size = 0;\r
- c->use_raw = 0;\r
- c->next = NULL;\r
-\r
- if ((retval = nand_flash_controllers[i]->nand_device_command(cmd_ctx, cmd, args, argc, c)) != ERROR_OK)\r
- {\r
- ERROR("'%s' driver rejected nand flash", c->controller->name);\r
- free(c);\r
- return ERROR_OK;\r
- }\r
- \r
- /* put NAND device in linked list */\r
- if (nand_devices)\r
- {\r
- /* find last flash device */\r
- for (p = nand_devices; p && p->next; p = p->next);\r
- if (p)\r
- p->next = c;\r
- }\r
- else\r
- {\r
- nand_devices = c;\r
- }\r
- \r
- return ERROR_OK;\r
- }\r
- }\r
-\r
- /* no valid NAND controller was found (i.e. the configuration option,\r
- * didn't match one of the compiled-in controllers)\r
- */\r
- ERROR("No valid NAND flash controller found (%s)", args[0]);\r
- ERROR("compiled-in NAND flash controllers:");\r
- for (i = 0; nand_flash_controllers[i]; i++)\r
- {\r
- ERROR("%i: %s", i, nand_flash_controllers[i]->name);\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int nand_register_commands(struct command_context_s *cmd_ctx)\r
-{\r
- nand_cmd = register_command(cmd_ctx, NULL, "nand", NULL, COMMAND_ANY, "NAND specific commands");\r
- \r
- register_command(cmd_ctx, nand_cmd, "device", handle_nand_device_command, COMMAND_CONFIG, NULL);\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int nand_init(struct command_context_s *cmd_ctx)\r
-{\r
- if (nand_devices)\r
- {\r
- register_command(cmd_ctx, nand_cmd, "list", handle_nand_list_command, COMMAND_EXEC,\r
- "list configured NAND flash devices");\r
- register_command(cmd_ctx, nand_cmd, "info", handle_nand_info_command, COMMAND_EXEC,\r
- "print info about NAND flash device <num>");\r
- register_command(cmd_ctx, nand_cmd, "probe", handle_nand_probe_command, COMMAND_EXEC,\r
- "identify NAND flash device <num>");\r
- register_command(cmd_ctx, nand_cmd, "check_bad_blocks", handle_nand_check_bad_blocks_command, COMMAND_EXEC,\r
- "check NAND flash device <num> for bad blocks [<first> <last>]");\r
- register_command(cmd_ctx, nand_cmd, "erase", handle_nand_erase_command, COMMAND_EXEC,\r
- "erase blocks on NAND flash device <num> <first> <last>");\r
- register_command(cmd_ctx, nand_cmd, "copy", handle_nand_copy_command, COMMAND_EXEC,\r
- "copy from NAND flash device <num> <offset> <length> <ram-address>");\r
- register_command(cmd_ctx, nand_cmd, "dump", handle_nand_dump_command, COMMAND_EXEC,\r
- "dump from NAND flash device <num> <filename> <offset> <size> [options]");\r
- register_command(cmd_ctx, nand_cmd, "write", handle_nand_write_command, COMMAND_EXEC,\r
- "write to NAND flash device <num> <filename> <offset> [options]");\r
- register_command(cmd_ctx, nand_cmd, "raw_access", handle_nand_raw_access_command, COMMAND_EXEC,\r
- "raw access to NAND flash device <num> ['enable'|'disable']");\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-nand_device_t *get_nand_device_by_num(int num)\r
-{\r
- nand_device_t *p;\r
- int i = 0;\r
-\r
- for (p = nand_devices; p; p = p->next)\r
- {\r
- if (i++ == num)\r
- {\r
- return p;\r
- }\r
- }\r
- \r
- return NULL;\r
-}\r
-\r
-int nand_build_bbt(struct nand_device_s *device, int first, int last)\r
-{\r
- u32 page = 0x0;\r
- int i;\r
- u8 *oob;\r
- \r
- oob = malloc(6);\r
- \r
- if ((first < 0) || (first >= device->num_blocks))\r
- first = 0;\r
- \r
- if ((last >= device->num_blocks) || (last == -1))\r
- last = device->num_blocks - 1;\r
- \r
- for (i = first; i < last; i++)\r
- {\r
- nand_read_page(device, page, NULL, 0, oob, 6);\r
- \r
- if (((device->device->options & NAND_BUSWIDTH_16) && ((oob[0] & oob[1]) != 0xff))\r
- || (((device->page_size == 512) && (oob[5] != 0xff)) ||\r
- ((device->page_size == 2048) && (oob[0] != 0xff))))\r
- {\r
- WARNING("invalid block: %i", i);\r
- device->blocks[i].is_bad = 1;\r
- }\r
- else\r
- {\r
- device->blocks[i].is_bad = 0;\r
- }\r
- \r
- page += (device->erase_size / device->page_size);\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int nand_read_status(struct nand_device_s *device, u8 *status)\r
-{\r
- if (!device->device)\r
- return ERROR_NAND_DEVICE_NOT_PROBED;\r
- \r
- /* Send read status command */\r
- device->controller->command(device, NAND_CMD_STATUS);\r
- \r
- usleep(1000);\r
- \r
- /* read status */\r
- if (device->device->options & NAND_BUSWIDTH_16)\r
- {\r
- u16 data;\r
- device->controller->read_data(device, &data);\r
- *status = data & 0xff;\r
- }\r
- else\r
- {\r
- device->controller->read_data(device, status);\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int nand_probe(struct nand_device_s *device)\r
-{\r
- u8 manufacturer_id, device_id;\r
- u8 id_buff[5];\r
- int retval;\r
- int i;\r
-\r
- /* clear device data */\r
- device->device = NULL;\r
- device->manufacturer = NULL;\r
- \r
- /* clear device parameters */\r
- device->bus_width = 0;\r
- device->address_cycles = 0;\r
- device->page_size = 0;\r
- device->erase_size = 0;\r
- \r
- /* initialize controller (device parameters are zero, use controller default) */\r
- if ((retval = device->controller->init(device) != ERROR_OK))\r
- {\r
- switch (retval)\r
- {\r
- case ERROR_NAND_OPERATION_FAILED:\r
- DEBUG("controller initialization failed");\r
- return ERROR_NAND_OPERATION_FAILED;\r
- case ERROR_NAND_OPERATION_NOT_SUPPORTED:\r
- ERROR("BUG: controller reported that it doesn't support default parameters");\r
- return ERROR_NAND_OPERATION_FAILED;\r
- default:\r
- ERROR("BUG: unknown controller initialization failure");\r
- return ERROR_NAND_OPERATION_FAILED;\r
- }\r
- }\r
- \r
- device->controller->command(device, NAND_CMD_RESET);\r
- device->controller->reset(device);\r
-\r
- device->controller->command(device, NAND_CMD_READID);\r
- device->controller->address(device, 0x0);\r
- \r
- if (device->bus_width == 8)\r
- {\r
- device->controller->read_data(device, &manufacturer_id);\r
- device->controller->read_data(device, &device_id);\r
- }\r
- else\r
- {\r
- u16 data_buf;\r
- device->controller->read_data(device, &data_buf);\r
- manufacturer_id = data_buf & 0xff;\r
- device->controller->read_data(device, &data_buf);\r
- device_id = data_buf & 0xff;\r
- }\r
- \r
- for (i = 0; nand_flash_ids[i].name; i++)\r
- {\r
- if (nand_flash_ids[i].id == device_id)\r
- {\r
- device->device = &nand_flash_ids[i];\r
- break;\r
- }\r
- }\r
- \r
- for (i = 0; nand_manuf_ids[i].name; i++)\r
- {\r
- if (nand_manuf_ids[i].id == manufacturer_id)\r
- {\r
- device->manufacturer = &nand_manuf_ids[i];\r
- break;\r
- }\r
- }\r
- \r
- if (!device->manufacturer)\r
- {\r
- device->manufacturer = &nand_manuf_ids[0];\r
- device->manufacturer->id = manufacturer_id;\r
- }\r
- \r
- if (!device->device)\r
- {\r
- ERROR("unknown NAND flash device found, manufacturer id: 0x%2.2x device id: 0x%2.2x",\r
- manufacturer_id, device_id);\r
- return ERROR_NAND_OPERATION_FAILED;\r
- }\r
- \r
- DEBUG("found %s (%s)", device->device->name, device->manufacturer->name);\r
- \r
- /* initialize device parameters */\r
- \r
- /* bus width */ \r
- if (device->device->options & NAND_BUSWIDTH_16)\r
- device->bus_width = 16;\r
- else\r
- device->bus_width = 8;\r
-\r
- /* Do we need extended device probe information? */\r
- if (device->device->page_size == 0 ||\r
- device->device->erase_size == 0)\r
- {\r
- if (device->bus_width == 8)\r
- {\r
- device->controller->read_data(device, id_buff+3);\r
- device->controller->read_data(device, id_buff+4);\r
- device->controller->read_data(device, id_buff+5);\r
- }\r
- else\r
- {\r
- u16 data_buf;\r
-\r
- device->controller->read_data(device, &data_buf);\r
- id_buff[3] = data_buf;\r
-\r
- device->controller->read_data(device, &data_buf);\r
- id_buff[4] = data_buf;\r
-\r
- device->controller->read_data(device, &data_buf);\r
- id_buff[5] = data_buf >> 8;\r
- }\r
- }\r
- \r
- /* page size */\r
- if (device->device->page_size == 0)\r
- {\r
- device->page_size = 1 << (10 + (id_buff[4] & 3));\r
- }\r
- else if (device->device->page_size == 256)\r
- {\r
- ERROR("NAND flashes with 256 byte pagesize are not supported");\r
- return ERROR_NAND_OPERATION_FAILED;\r
- }\r
- else\r
- {\r
- device->page_size = device->device->page_size;\r
- }\r
- \r
- /* number of address cycles */\r
- if (device->page_size <= 512)\r
- {\r
- /* small page devices */\r
- if (device->device->chip_size <= 32)\r
- device->address_cycles = 3;\r
- else if (device->device->chip_size <= 8*1024)\r
- device->address_cycles = 4;\r
- else\r
- {\r
- ERROR("BUG: small page NAND device with more than 8 GiB encountered");\r
- device->address_cycles = 5;\r
- }\r
- }\r
- else\r
- {\r
- /* large page devices */\r
- if (device->device->chip_size <= 128)\r
- device->address_cycles = 4;\r
- else if (device->device->chip_size <= 32*1024)\r
- device->address_cycles = 5;\r
- else\r
- {\r
- ERROR("BUG: small page NAND device with more than 32 GiB encountered");\r
- device->address_cycles = 6;\r
- }\r
- }\r
- \r
- /* erase size */\r
- if (device->device->erase_size == 0)\r
- {\r
- switch ((id_buff[4] >> 4) & 3) {\r
- case 0:\r
- device->erase_size = 64 << 10;\r
- break;\r
- case 1:\r
- device->erase_size = 128 << 10;\r
- break;\r
- case 2:\r
- device->erase_size = 256 << 10;\r
- break;\r
- case 3:\r
- device->erase_size =512 << 10;\r
- break;\r
- }\r
- }\r
- else\r
- {\r
- device->erase_size = device->device->erase_size;\r
- }\r
- \r
- /* initialize controller, but leave parameters at the controllers default */\r
- if ((retval = device->controller->init(device) != ERROR_OK))\r
- {\r
- switch (retval)\r
- {\r
- case ERROR_NAND_OPERATION_FAILED:\r
- DEBUG("controller initialization failed");\r
- return ERROR_NAND_OPERATION_FAILED;\r
- case ERROR_NAND_OPERATION_NOT_SUPPORTED:\r
- ERROR("controller doesn't support requested parameters (buswidth: %i, address cycles: %i, page size: %i)",\r
- device->bus_width, device->address_cycles, device->page_size);\r
- return ERROR_NAND_OPERATION_FAILED;\r
- default:\r
- ERROR("BUG: unknown controller initialization failure");\r
- return ERROR_NAND_OPERATION_FAILED;\r
- }\r
- }\r
- \r
- device->num_blocks = (device->device->chip_size * 1024) / (device->erase_size / 1024);\r
- device->blocks = malloc(sizeof(nand_block_t) * device->num_blocks);\r
- \r
- for (i = 0; i < device->num_blocks; i++)\r
- {\r
- device->blocks[i].size = device->erase_size;\r
- device->blocks[i].offset = i * device->erase_size;\r
- device->blocks[i].is_erased = -1;\r
- device->blocks[i].is_bad = -1;\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int nand_erase(struct nand_device_s *device, int first_block, int last_block)\r
-{\r
- int i;\r
- u32 page;\r
- u8 status;\r
- int retval;\r
- \r
- if (!device->device)\r
- return ERROR_NAND_DEVICE_NOT_PROBED;\r
- \r
- if ((first_block < 0) || (last_block > device->num_blocks))\r
- return ERROR_INVALID_ARGUMENTS;\r
- \r
- /* make sure we know if a block is bad before erasing it */\r
- for (i = first_block; i <= last_block; i++)\r
- {\r
- if (device->blocks[i].is_bad == -1)\r
- {\r
- nand_build_bbt(device, i, last_block);\r
- break;\r
- }\r
- }\r
- \r
- for (i = first_block; i <= last_block; i++)\r
- {\r
- /* Send erase setup command */\r
- device->controller->command(device, NAND_CMD_ERASE1);\r
- \r
- page = i * (device->erase_size / device->page_size);\r
- \r
- /* Send page address */\r
- if (device->page_size <= 512)\r
- {\r
- /* row */\r
- device->controller->address(device, page & 0xff);\r
- device->controller->address(device, (page >> 8) & 0xff);\r
- \r
- /* 3rd cycle only on devices with more than 32 MiB */\r
- if (device->address_cycles >= 4)\r
- device->controller->address(device, (page >> 16) & 0xff);\r
- \r
- /* 4th cycle only on devices with more than 8 GiB */\r
- if (device->address_cycles >= 5)\r
- device->controller->address(device, (page >> 24) & 0xff);\r
- }\r
- else\r
- {\r
- /* row */\r
- device->controller->address(device, page & 0xff);\r
- device->controller->address(device, (page >> 8) & 0xff);\r
- \r
- /* 3rd cycle only on devices with more than 128 MiB */\r
- if (device->address_cycles >= 5)\r
- device->controller->address(device, (page >> 16) & 0xff);\r
- }\r
- \r
- /* Send erase confirm command */\r
- device->controller->command(device, NAND_CMD_ERASE2);\r
- \r
- if (!device->controller->nand_ready(device, 1000))\r
- {\r
- ERROR("timeout waiting for NAND flash block erase to complete");\r
- return ERROR_NAND_OPERATION_TIMEOUT;\r
- }\r
- \r
- if ((retval = nand_read_status(device, &status)) != ERROR_OK)\r
- {\r
- ERROR("couldn't read status");\r
- return ERROR_NAND_OPERATION_FAILED;\r
- }\r
- \r
- if (status & 0x1)\r
- {\r
- ERROR("erase operation didn't pass, status: 0x%2.2x", status);\r
- return ERROR_NAND_OPERATION_FAILED;\r
- }\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int nand_read_plain(struct nand_device_s *device, u32 address, u8 *data, u32 data_size)\r
-{\r
- u8 *page;\r
- \r
- if (!device->device)\r
- return ERROR_NAND_DEVICE_NOT_PROBED;\r
- \r
- if (address % device->page_size)\r
- {\r
- ERROR("reads need to be page aligned");\r
- return ERROR_NAND_OPERATION_FAILED;\r
- }\r
- \r
- page = malloc(device->page_size);\r
- \r
- while (data_size > 0 )\r
- {\r
- u32 thisrun_size = (data_size > device->page_size) ? device->page_size : data_size;\r
- u32 page_address;\r
- \r
- \r
- page_address = address / device->page_size;\r
- \r
- nand_read_page(device, page_address, page, device->page_size, NULL, 0);\r
-\r
- memcpy(data, page, thisrun_size);\r
- \r
- address += thisrun_size;\r
- data += thisrun_size;\r
- data_size -= thisrun_size;\r
- }\r
- \r
- free(page);\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int nand_write_plain(struct nand_device_s *device, u32 address, u8 *data, u32 data_size)\r
-{\r
- u8 *page;\r
- \r
- if (!device->device)\r
- return ERROR_NAND_DEVICE_NOT_PROBED;\r
- \r
- if (address % device->page_size)\r
- {\r
- ERROR("writes need to be page aligned");\r
- return ERROR_NAND_OPERATION_FAILED;\r
- }\r
- \r
- page = malloc(device->page_size);\r
- \r
- while (data_size > 0 )\r
- {\r
- u32 thisrun_size = (data_size > device->page_size) ? device->page_size : data_size;\r
- u32 page_address;\r
- \r
- memset(page, 0xff, device->page_size);\r
- memcpy(page, data, thisrun_size);\r
- \r
- page_address = address / device->page_size;\r
- \r
- nand_write_page(device, page_address, page, device->page_size, NULL, 0);\r
- \r
- address += thisrun_size;\r
- data += thisrun_size;\r
- data_size -= thisrun_size;\r
- }\r
- \r
- free(page);\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int nand_write_page(struct nand_device_s *device, u32 page, u8 *data, u32 data_size, u8 *oob, u32 oob_size)\r
-{\r
- if (!device->device)\r
- return ERROR_NAND_DEVICE_NOT_PROBED;\r
- \r
- if (device->use_raw || device->controller->write_page == NULL)\r
- return nand_write_page_raw(device, page, data, data_size, oob, oob_size);\r
- else\r
- return device->controller->write_page(device, page, data, data_size, oob, oob_size);\r
-}\r
-\r
-int nand_read_page(struct nand_device_s *device, u32 page, u8 *data, u32 data_size, u8 *oob, u32 oob_size)\r
-{\r
- if (!device->device)\r
- return ERROR_NAND_DEVICE_NOT_PROBED;\r
- \r
- if (device->use_raw || device->controller->read_page == NULL)\r
- return nand_read_page_raw(device, page, data, data_size, oob, oob_size);\r
- else\r
- return device->controller->read_page(device, page, data, data_size, oob, oob_size);\r
-}\r
-\r
-int nand_read_page_raw(struct nand_device_s *device, u32 page, u8 *data, u32 data_size, u8 *oob, u32 oob_size)\r
-{\r
- int i;\r
- \r
- if (!device->device)\r
- return ERROR_NAND_DEVICE_NOT_PROBED;\r
-\r
- if (device->page_size <= 512)\r
- {\r
- /* small page device */\r
- if (data)\r
- device->controller->command(device, NAND_CMD_READ0);\r
- else\r
- device->controller->command(device, NAND_CMD_READOOB);\r
- \r
- /* column (always 0, we start at the beginning of a page/OOB area) */\r
- device->controller->address(device, 0x0);\r
- \r
- /* row */\r
- device->controller->address(device, page & 0xff);\r
- device->controller->address(device, (page >> 8) & 0xff);\r
- \r
- /* 4th cycle only on devices with more than 32 MiB */\r
- if (device->address_cycles >= 4)\r
- device->controller->address(device, (page >> 16) & 0xff);\r
-\r
- /* 5th cycle only on devices with more than 8 GiB */\r
- if (device->address_cycles >= 5)\r
- device->controller->address(device, (page >> 24) & 0xff);\r
- }\r
- else\r
- {\r
- /* large page device */\r
- device->controller->command(device, NAND_CMD_READ0);\r
- \r
- /* column (0 when we start at the beginning of a page,\r
- * or 2048 for the beginning of OOB area)\r
- */\r
- device->controller->address(device, 0x0);\r
- device->controller->address(device, 0x8);\r
- \r
- /* row */\r
- device->controller->address(device, page & 0xff);\r
- device->controller->address(device, (page >> 8) & 0xff);\r
-\r
- /* 5th cycle only on devices with more than 128 MiB */\r
- if (device->address_cycles >= 5)\r
- device->controller->address(device, (page >> 16) & 0xff);\r
-\r
- /* large page devices need a start command */\r
- device->controller->command(device, NAND_CMD_READSTART);\r
- }\r
- \r
- if (!device->controller->nand_ready(device, 100))\r
- return ERROR_NAND_OPERATION_TIMEOUT;\r
- \r
- if (data)\r
- {\r
- if (device->controller->read_block_data != NULL)\r
- (device->controller->read_block_data)(device, data, data_size);\r
- else\r
- {\r
- for (i = 0; i < data_size;)\r
- {\r
- if (device->device->options & NAND_BUSWIDTH_16)\r
- {\r
- device->controller->read_data(device, data);\r
- data += 2;\r
- i += 2;\r
- }\r
- else\r
- {\r
- device->controller->read_data(device, data);\r
- data += 1;\r
- i += 1;\r
- }\r
- }\r
- }\r
- }\r
- \r
- if (oob)\r
- {\r
- if (device->controller->read_block_data != NULL)\r
- (device->controller->read_block_data)(device, oob, oob_size);\r
- else\r
- {\r
- for (i = 0; i < oob_size;)\r
- {\r
- if (device->device->options & NAND_BUSWIDTH_16)\r
- {\r
- device->controller->read_data(device, oob);\r
- oob += 2;\r
- i += 2;\r
- }\r
- else\r
- {\r
- device->controller->read_data(device, oob);\r
- oob += 1;\r
- i += 1;\r
- }\r
- }\r
- }\r
- }\r
- \r
- return ERROR_OK; \r
-}\r
-\r
-int nand_write_page_raw(struct nand_device_s *device, u32 page, u8 *data, u32 data_size, u8 *oob, u32 oob_size)\r
-{\r
- int i;\r
- int retval;\r
- u8 status;\r
- \r
- if (!device->device)\r
- return ERROR_NAND_DEVICE_NOT_PROBED;\r
-\r
- device->controller->command(device, NAND_CMD_SEQIN);\r
- \r
- if (device->page_size <= 512)\r
- {\r
- /* column (always 0, we start at the beginning of a page/OOB area) */\r
- device->controller->address(device, 0x0);\r
- \r
- /* row */\r
- device->controller->address(device, page & 0xff);\r
- device->controller->address(device, (page >> 8) & 0xff);\r
- \r
- /* 4th cycle only on devices with more than 32 MiB */\r
- if (device->address_cycles >= 4)\r
- device->controller->address(device, (page >> 16) & 0xff);\r
-\r
- /* 5th cycle only on devices with more than 8 GiB */\r
- if (device->address_cycles >= 5)\r
- device->controller->address(device, (page >> 24) & 0xff);\r
- }\r
- else\r
- {\r
- /* column (0 when we start at the beginning of a page,\r
- * or 2048 for the beginning of OOB area)\r
- */\r
- device->controller->address(device, 0x0);\r
- device->controller->address(device, 0x8);\r
- \r
- /* row */\r
- device->controller->address(device, page & 0xff);\r
- device->controller->address(device, (page >> 8) & 0xff);\r
-\r
- /* 5th cycle only on devices with more than 128 MiB */\r
- if (device->address_cycles >= 5)\r
- device->controller->address(device, (page >> 16) & 0xff);\r
- }\r
- \r
- if (data)\r
- {\r
- if (device->controller->write_block_data != NULL)\r
- (device->controller->write_block_data)(device, data, data_size);\r
- else\r
- {\r
- for (i = 0; i < data_size;)\r
- {\r
- if (device->device->options & NAND_BUSWIDTH_16)\r
- {\r
- u16 data_buf = le_to_h_u16(data);\r
- device->controller->write_data(device, data_buf);\r
- data += 2;\r
- i += 2;\r
- }\r
- else\r
- {\r
- device->controller->write_data(device, *data);\r
- data += 1;\r
- i += 1;\r
- }\r
- }\r
- }\r
- }\r
- \r
- if (oob)\r
- {\r
- if (device->controller->write_block_data != NULL)\r
- (device->controller->write_block_data)(device, oob, oob_size);\r
- else\r
- {\r
- for (i = 0; i < oob_size;)\r
- {\r
- if (device->device->options & NAND_BUSWIDTH_16)\r
- {\r
- u16 oob_buf = le_to_h_u16(data);\r
- device->controller->write_data(device, oob_buf);\r
- oob += 2;\r
- i += 2;\r
- }\r
- else\r
- {\r
- device->controller->write_data(device, *oob);\r
- oob += 1;\r
- i += 1;\r
- }\r
- }\r
- }\r
- }\r
- \r
- device->controller->command(device, NAND_CMD_PAGEPROG);\r
- \r
- if (!device->controller->nand_ready(device, 100))\r
- return ERROR_NAND_OPERATION_TIMEOUT;\r
- \r
- if ((retval = nand_read_status(device, &status)) != ERROR_OK)\r
- {\r
- ERROR("couldn't read status");\r
- return ERROR_NAND_OPERATION_FAILED;\r
- }\r
- \r
- if (status & NAND_STATUS_FAIL)\r
- {\r
- ERROR("write operation didn't pass, status: 0x%2.2x", status);\r
- return ERROR_NAND_OPERATION_FAILED;\r
- }\r
- \r
- return ERROR_OK; \r
-}\r
-\r
-int handle_nand_list_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- nand_device_t *p;\r
- int i = 0;\r
- \r
- if (!nand_devices)\r
- {\r
- command_print(cmd_ctx, "no NAND flash devices configured");\r
- return ERROR_OK;\r
- }\r
- \r
- for (p = nand_devices; p; p = p->next)\r
- {\r
- if (p->device)\r
- command_print(cmd_ctx, "#%i: %s (%s) pagesize: %i, buswidth: %i, erasesize: %i",\r
- i++, p->device->name, p->manufacturer->name, p->page_size, p->bus_width, p->erase_size);\r
- else\r
- command_print(cmd_ctx, "#%i: not probed");\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int handle_nand_info_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- nand_device_t *p;\r
- int i = 0;\r
- int j = 0;\r
- int first = -1;\r
- int last = -1;\r
- \r
- if ((argc < 1) || (argc > 3))\r
- {\r
- return ERROR_COMMAND_SYNTAX_ERROR;\r
-\r
- }\r
- \r
- if (argc == 2)\r
- {\r
- first = last = strtoul(args[1], NULL, 0);\r
- }\r
- else if (argc == 3)\r
- {\r
- first = strtoul(args[1], NULL, 0);\r
- last = strtoul(args[2], NULL, 0);\r
- }\r
- \r
- p = get_nand_device_by_num(strtoul(args[0], NULL, 0));\r
- if (p)\r
- {\r
- if (p->device)\r
- {\r
- if (first >= p->num_blocks)\r
- first = p->num_blocks - 1;\r
- \r
- if (last >= p->num_blocks)\r
- last = p->num_blocks - 1;\r
- \r
- command_print(cmd_ctx, "#%i: %s (%s) pagesize: %i, buswidth: %i, erasesize: %i",\r
- i++, p->device->name, p->manufacturer->name, p->page_size, p->bus_width, p->erase_size);\r
- \r
- for (j = first; j <= last; j++)\r
- {\r
- char *erase_state, *bad_state;\r
- \r
- if (p->blocks[j].is_erased == 0)\r
- erase_state = "not erased";\r
- else if (p->blocks[j].is_erased == 1)\r
- erase_state = "erased";\r
- else\r
- erase_state = "erase state unknown";\r
- \r
- if (p->blocks[j].is_bad == 0)\r
- bad_state = "";\r
- else if (p->blocks[j].is_bad == 1)\r
- bad_state = " (marked bad)";\r
- else\r
- bad_state = " (block condition unknown)";\r
-\r
- command_print(cmd_ctx, "\t#%i: 0x%8.8x (0x%xkB) %s%s",\r
- j, p->blocks[j].offset, p->blocks[j].size / 1024,\r
- erase_state, bad_state);\r
- }\r
- }\r
- else\r
- {\r
- command_print(cmd_ctx, "#%i: not probed");\r
- }\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int handle_nand_probe_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- nand_device_t *p;\r
- int retval;\r
- \r
- if (argc != 1)\r
- {\r
- return ERROR_COMMAND_SYNTAX_ERROR;\r
- }\r
- \r
- p = get_nand_device_by_num(strtoul(args[0], NULL, 0));\r
- if (p)\r
- {\r
- if ((retval = nand_probe(p)) == ERROR_OK)\r
- {\r
- command_print(cmd_ctx, "NAND flash device '%s' found", p->device->name);\r
- }\r
- else if (retval == ERROR_NAND_OPERATION_FAILED)\r
- {\r
- command_print(cmd_ctx, "probing failed for NAND flash device");\r
- }\r
- else\r
- {\r
- command_print(cmd_ctx, "unknown error when probing NAND flash device");\r
- }\r
- }\r
- else\r
- {\r
- command_print(cmd_ctx, "NAND flash device '#%s' is out of bounds", args[0]);\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int handle_nand_erase_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- nand_device_t *p;\r
- int retval;\r
- \r
- if (argc != 3)\r
- {\r
- return ERROR_COMMAND_SYNTAX_ERROR;\r
-\r
- }\r
- \r
- p = get_nand_device_by_num(strtoul(args[0], NULL, 0));\r
- if (p)\r
- {\r
- int first = strtoul(args[1], NULL, 0);\r
- int last = strtoul(args[2], NULL, 0);\r
- \r
- if ((retval = nand_erase(p, first, last)) == ERROR_OK)\r
- {\r
- command_print(cmd_ctx, "successfully erased blocks %i to %i on NAND flash device '%s'", first, last, p->device->name);\r
- }\r
- else if (retval == ERROR_NAND_OPERATION_FAILED)\r
- {\r
- command_print(cmd_ctx, "erase failed");\r
- }\r
- else\r
- {\r
- command_print(cmd_ctx, "unknown error when erasing NAND flash device");\r
- }\r
- }\r
- else\r
- {\r
- command_print(cmd_ctx, "NAND flash device '#%s' is out of bounds", args[0]);\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int handle_nand_check_bad_blocks_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- nand_device_t *p;\r
- int retval;\r
- int first = -1;\r
- int last = -1;\r
- \r
- if ((argc < 1) || (argc > 3) || (argc == 2))\r
- {\r
- return ERROR_COMMAND_SYNTAX_ERROR;\r
-\r
- }\r
- \r
- if (argc == 3)\r
- {\r
- first = strtoul(args[1], NULL, 0);\r
- last = strtoul(args[2], NULL, 0);\r
- }\r
- \r
- p = get_nand_device_by_num(strtoul(args[0], NULL, 0));\r
- if (p)\r
- {\r
- if ((retval = nand_build_bbt(p, first, last)) == ERROR_OK)\r
- {\r
- command_print(cmd_ctx, "checked NAND flash device for bad blocks, use \"nand info\" command to list blocks", p->device->name);\r
- }\r
- else if (retval == ERROR_NAND_OPERATION_FAILED)\r
- {\r
- command_print(cmd_ctx, "error when checking for bad blocks on NAND flash device");\r
- }\r
- else\r
- {\r
- command_print(cmd_ctx, "unknown error when checking for bad blocks on NAND flash device");\r
- }\r
- }\r
- else\r
- {\r
- command_print(cmd_ctx, "NAND flash device '#%s' is out of bounds", args[0]);\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int handle_nand_copy_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- nand_device_t *p;\r
- \r
- if (argc != 4)\r
- {\r
- return ERROR_COMMAND_SYNTAX_ERROR;\r
-\r
- }\r
- \r
- p = get_nand_device_by_num(strtoul(args[0], NULL, 0));\r
- if (p)\r
- {\r
-\r
- }\r
- else\r
- {\r
- command_print(cmd_ctx, "NAND flash device '#%s' is out of bounds", args[0]);\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int handle_nand_write_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- u32 offset;\r
- u32 binary_size;\r
- u32 buf_cnt;\r
- enum oob_formats oob_format = NAND_OOB_NONE;\r
- \r
- fileio_t fileio;\r
- \r
- duration_t duration;\r
- char *duration_text;\r
- \r
- nand_device_t *p;\r
- \r
- if (argc < 3)\r
- {\r
- return ERROR_COMMAND_SYNTAX_ERROR;\r
-\r
- }\r
- \r
- p = get_nand_device_by_num(strtoul(args[0], NULL, 0));\r
- if (p)\r
- {\r
- u8 *page = NULL;\r
- u32 page_size = 0;\r
- u8 *oob = NULL;\r
- u32 oob_size = 0;\r
- \r
- duration_start_measure(&duration);\r
- offset = strtoul(args[2], NULL, 0);\r
- \r
- if (argc > 3)\r
- {\r
- int i;\r
- for (i = 3; i < argc; i++)\r
- {\r
- if (!strcmp(args[i], "oob_raw"))\r
- oob_format |= NAND_OOB_RAW;\r
- else if (!strcmp(args[i], "oob_only"))\r
- oob_format |= NAND_OOB_RAW | NAND_OOB_ONLY;\r
- else\r
- {\r
- command_print(cmd_ctx, "unknown option: %s", args[i]);\r
- }\r
- }\r
- }\r
- \r
- if (fileio_open(&fileio, args[1], FILEIO_READ, FILEIO_BINARY) != ERROR_OK)\r
- {\r
- command_print(cmd_ctx, "file open error: %s", fileio.error_str);\r
- return ERROR_OK;\r
- }\r
- \r
- buf_cnt = binary_size = fileio.size;\r
- \r
- if (!(oob_format & NAND_OOB_ONLY))\r
- {\r
- page_size = p->page_size;\r
- page = malloc(p->page_size);\r
- }\r
-\r
- if (oob_format & NAND_OOB_RAW)\r
- {\r
- if (p->page_size == 512)\r
- oob_size = 16;\r
- else if (p->page_size == 2048)\r
- oob_size = 64;\r
- oob = malloc(oob_size);\r
- }\r
- \r
- if (offset % p->page_size)\r
- {\r
- command_print(cmd_ctx, "only page size aligned offsets and sizes are supported");\r
- return ERROR_OK;\r
- }\r
- \r
- while (buf_cnt > 0)\r
- {\r
- u32 size_read;\r
- \r
- if (page)\r
- {\r
- fileio_read(&fileio, page_size, page, &size_read);\r
- buf_cnt -= size_read;\r
- if (size_read < page_size)\r
- {\r
- memset(page + size_read, 0xff, page_size - size_read);\r
- }\r
- }\r
- \r
- if (oob)\r
- {\r
- fileio_read(&fileio, oob_size, oob, &size_read);\r
- buf_cnt -= size_read;\r
- if (size_read < oob_size)\r
- {\r
- memset(oob + size_read, 0xff, oob_size - size_read);\r
- }\r
- }\r
- \r
- if (nand_write_page(p, offset / p->page_size, page, page_size, oob, oob_size) != ERROR_OK)\r
- {\r
- command_print(cmd_ctx, "failed writing file %s to NAND flash %s at offset 0x%8.8x",\r
- args[1], args[0], offset);\r
- return ERROR_OK;\r
- }\r
- offset += page_size;\r
- }\r
-\r
- fileio_close(&fileio);\r
- \r
- duration_stop_measure(&duration, &duration_text);\r
- command_print(cmd_ctx, "wrote file %s to NAND flash %s at offset 0x%8.8x in %s",\r
- args[1], args[0], offset, duration_text);\r
- free(duration_text);\r
- }\r
- else\r
- {\r
- command_print(cmd_ctx, "NAND flash device '#%s' is out of bounds", args[0]);\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int handle_nand_dump_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- nand_device_t *p;\r
- \r
- if (argc < 4)\r
- {\r
- return ERROR_COMMAND_SYNTAX_ERROR;\r
- }\r
- \r
- p = get_nand_device_by_num(strtoul(args[0], NULL, 0));\r
- if (p)\r
- {\r
- if (p->device)\r
- {\r
- fileio_t fileio;\r
- duration_t duration;\r
- char *duration_text;\r
- int retval;\r
- \r
- u8 *page = NULL;\r
- u32 page_size = 0;\r
- u8 *oob = NULL;\r
- u32 oob_size = 0;\r
- u32 address = strtoul(args[2], NULL, 0);\r
- u32 size = strtoul(args[3], NULL, 0);\r
- u32 bytes_done = 0;\r
- enum oob_formats oob_format = NAND_OOB_NONE;\r
- \r
- if (argc > 4)\r
- {\r
- int i;\r
- for (i = 4; i < argc; i++)\r
- {\r
- if (!strcmp(args[i], "oob_raw"))\r
- oob_format |= NAND_OOB_RAW;\r
- else if (!strcmp(args[i], "oob_only"))\r
- oob_format |= NAND_OOB_RAW | NAND_OOB_ONLY;\r
- else\r
- command_print(cmd_ctx, "unknown option: '%s'", args[i]); \r
- }\r
- }\r
- \r
- if ((address % p->page_size) || (size % p->page_size))\r
- {\r
- command_print(cmd_ctx, "only page size aligned addresses and sizes are supported");\r
- return ERROR_OK;\r
- }\r
- \r
- if (!(oob_format & NAND_OOB_ONLY))\r
- {\r
- page_size = p->page_size;\r
- page = malloc(p->page_size);\r
- }\r
-\r
- if (oob_format & NAND_OOB_RAW)\r
- {\r
- if (p->page_size == 512)\r
- oob_size = 16;\r
- else if (p->page_size == 2048)\r
- oob_size = 64;\r
- oob = malloc(oob_size);\r
- }\r
- \r
- if (fileio_open(&fileio, args[1], FILEIO_WRITE, FILEIO_BINARY) != ERROR_OK)\r
- {\r
- command_print(cmd_ctx, "dump_image error: %s", fileio.error_str);\r
- return ERROR_OK;\r
- }\r
- \r
- duration_start_measure(&duration);\r
- \r
- while (size > 0)\r
- {\r
- u32 size_written;\r
- if ((retval = nand_read_page(p, address / p->page_size, page, page_size, oob, oob_size)) != ERROR_OK)\r
- {\r
- command_print(cmd_ctx, "reading NAND flash page failed");\r
- return ERROR_OK;\r
- }\r
- \r
- if (page)\r
- {\r
- fileio_write(&fileio, page_size, page, &size_written);\r
- bytes_done += page_size;\r
- }\r
- \r
- if (oob)\r
- {\r
- fileio_write(&fileio, oob_size, oob, &size_written);\r
- bytes_done += oob_size;\r
- }\r
- \r
- size -= p->page_size;\r
- address += p->page_size;\r
- }\r
- \r
- if (page)\r
- free(page);\r
- \r
- if (oob)\r
- free(oob);\r
- \r
- fileio_close(&fileio);\r
-\r
- duration_stop_measure(&duration, &duration_text);\r
- command_print(cmd_ctx, "dumped %"PRIi64" byte in %s", fileio.size, duration_text);\r
- free(duration_text);\r
- }\r
- else\r
- {\r
- command_print(cmd_ctx, "#%i: not probed");\r
- }\r
- }\r
- else\r
- {\r
- command_print(cmd_ctx, "NAND flash device '#%s' is out of bounds", args[0]);\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int handle_nand_raw_access_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- nand_device_t *p;\r
- \r
- if ((argc < 1) || (argc > 2))\r
- {\r
- return ERROR_COMMAND_SYNTAX_ERROR;\r
- }\r
- \r
- p = get_nand_device_by_num(strtoul(args[0], NULL, 0));\r
- if (p)\r
- {\r
- if (p->device)\r
- {\r
- if (argc == 2)\r
- {\r
- if (strcmp("enable", args[1]) == 0)\r
- {\r
- p->use_raw = 1;\r
- }\r
- else if (strcmp("disable", args[1]) == 0)\r
- {\r
- p->use_raw = 0;\r
- }\r
- else\r
- {\r
- return ERROR_COMMAND_SYNTAX_ERROR;\r
- }\r
- }\r
- \r
- command_print(cmd_ctx, "raw access is %s", (p->use_raw) ? "enabled" : "disabled");\r
- }\r
- else\r
- {\r
- command_print(cmd_ctx, "#%i: not probed");\r
- }\r
- }\r
- else\r
- {\r
- command_print(cmd_ctx, "NAND flash device '#%s' is out of bounds", args[0]);\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
+/***************************************************************************
+ * Copyright (C) 2007 by Dominic Rath *
+ * Dominic.Rath@gmx.de *
+ * *
+ * partially based on *
+ * drivers/mtd/nand_ids.c *
+ * *
+ * Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de) *
+ * *
+ * 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 "replacements.h"
+#include "log.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include <errno.h>
+
+#include "nand.h"
+#include "flash.h"
+#include "time_support.h"
+#include "fileio.h"
+#include "image.h"
+
+int nand_register_commands(struct command_context_s *cmd_ctx);
+int handle_nand_list_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+int handle_nand_probe_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+int handle_nand_check_bad_blocks_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+int handle_nand_info_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+int handle_nand_copy_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+int handle_nand_write_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+int handle_nand_dump_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+int handle_nand_erase_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+
+int handle_nand_raw_access_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+
+int nand_read_page_raw(struct nand_device_s *device, u32 page, u8 *data, u32 data_size, u8 *oob, u32 oob_size);
+int nand_read_page(struct nand_device_s *device, u32 page, u8 *data, u32 data_size, u8 *oob, u32 oob_size);
+int nand_read_plain(struct nand_device_s *device, u32 address, u8 *data, u32 data_size);
+
+int nand_write_page_raw(struct nand_device_s *device, u32 page, u8 *data, u32 data_size, u8 *oob, u32 oob_size);
+int nand_write_page(struct nand_device_s *device, u32 page, u8 *data, u32 data_size, u8 *oob, u32 oob_size);
+
+/* NAND flash controller
+ */
+extern nand_flash_controller_t lpc3180_nand_controller;
+extern nand_flash_controller_t s3c2410_nand_controller;
+extern nand_flash_controller_t s3c2412_nand_controller;
+extern nand_flash_controller_t s3c2440_nand_controller;
+extern nand_flash_controller_t s3c2443_nand_controller;
+
+/* extern nand_flash_controller_t boundary_scan_nand_controller; */
+
+nand_flash_controller_t *nand_flash_controllers[] =
+{
+ &lpc3180_nand_controller,
+ &s3c2410_nand_controller,
+ &s3c2412_nand_controller,
+ &s3c2440_nand_controller,
+ &s3c2443_nand_controller,
+/* &boundary_scan_nand_controller, */
+ NULL
+};
+
+/* configured NAND devices and NAND Flash command handler */
+nand_device_t *nand_devices = NULL;
+static command_t *nand_cmd;
+
+/* Chip ID list
+ *
+ * Name, ID code, pagesize, chipsize in MegaByte, eraseblock size,
+ * options
+ *
+ * Pagesize; 0, 256, 512
+ * 0 get this information from the extended chip ID
+ * 256 256 Byte page size
+ * 512 512 Byte page size
+ */
+nand_info_t nand_flash_ids[] =
+{
+ {"NAND 1MiB 5V 8-bit", 0x6e, 256, 1, 0x1000, 0},
+ {"NAND 2MiB 5V 8-bit", 0x64, 256, 2, 0x1000, 0},
+ {"NAND 4MiB 5V 8-bit", 0x6b, 512, 4, 0x2000, 0},
+ {"NAND 1MiB 3,3V 8-bit", 0xe8, 256, 1, 0x1000, 0},
+ {"NAND 1MiB 3,3V 8-bit", 0xec, 256, 1, 0x1000, 0},
+ {"NAND 2MiB 3,3V 8-bit", 0xea, 256, 2, 0x1000, 0},
+ {"NAND 4MiB 3,3V 8-bit", 0xd5, 512, 4, 0x2000, 0},
+ {"NAND 4MiB 3,3V 8-bit", 0xe3, 512, 4, 0x2000, 0},
+ {"NAND 4MiB 3,3V 8-bit", 0xe5, 512, 4, 0x2000, 0},
+ {"NAND 8MiB 3,3V 8-bit", 0xd6, 512, 8, 0x2000, 0},
+
+ {"NAND 8MiB 1,8V 8-bit", 0x39, 512, 8, 0x2000, 0},
+ {"NAND 8MiB 3,3V 8-bit", 0xe6, 512, 8, 0x2000, 0},
+ {"NAND 8MiB 1,8V 16-bit", 0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16},
+ {"NAND 8MiB 3,3V 16-bit", 0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16},
+
+ {"NAND 16MiB 1,8V 8-bit", 0x33, 512, 16, 0x4000, 0},
+ {"NAND 16MiB 3,3V 8-bit", 0x73, 512, 16, 0x4000, 0},
+ {"NAND 16MiB 1,8V 16-bit", 0x43, 512, 16, 0x4000, NAND_BUSWIDTH_16},
+ {"NAND 16MiB 3,3V 16-bit", 0x53, 512, 16, 0x4000, NAND_BUSWIDTH_16},
+
+ {"NAND 32MiB 1,8V 8-bit", 0x35, 512, 32, 0x4000, 0},
+ {"NAND 32MiB 3,3V 8-bit", 0x75, 512, 32, 0x4000, 0},
+ {"NAND 32MiB 1,8V 16-bit", 0x45, 512, 32, 0x4000, NAND_BUSWIDTH_16},
+ {"NAND 32MiB 3,3V 16-bit", 0x55, 512, 32, 0x4000, NAND_BUSWIDTH_16},
+
+ {"NAND 64MiB 1,8V 8-bit", 0x36, 512, 64, 0x4000, 0},
+ {"NAND 64MiB 3,3V 8-bit", 0x76, 512, 64, 0x4000, 0},
+ {"NAND 64MiB 1,8V 16-bit", 0x46, 512, 64, 0x4000, NAND_BUSWIDTH_16},
+ {"NAND 64MiB 3,3V 16-bit", 0x56, 512, 64, 0x4000, NAND_BUSWIDTH_16},
+
+ {"NAND 128MiB 1,8V 8-bit", 0x78, 512, 128, 0x4000, 0},
+ {"NAND 128MiB 1,8V 8-bit", 0x39, 512, 128, 0x4000, 0},
+ {"NAND 128MiB 3,3V 8-bit", 0x79, 512, 128, 0x4000, 0},
+ {"NAND 128MiB 1,8V 16-bit", 0x72, 512, 128, 0x4000, NAND_BUSWIDTH_16},
+ {"NAND 128MiB 1,8V 16-bit", 0x49, 512, 128, 0x4000, NAND_BUSWIDTH_16},
+ {"NAND 128MiB 3,3V 16-bit", 0x74, 512, 128, 0x4000, NAND_BUSWIDTH_16},
+ {"NAND 128MiB 3,3V 16-bit", 0x59, 512, 128, 0x4000, NAND_BUSWIDTH_16},
+
+ {"NAND 256MiB 3,3V 8-bit", 0x71, 512, 256, 0x4000, 0},
+
+ {"NAND 64MiB 1,8V 8-bit", 0xA2, 0, 64, 0, LP_OPTIONS},
+ {"NAND 64MiB 3,3V 8-bit", 0xF2, 0, 64, 0, LP_OPTIONS},
+ {"NAND 64MiB 1,8V 16-bit", 0xB2, 0, 64, 0, LP_OPTIONS16},
+ {"NAND 64MiB 3,3V 16-bit", 0xC2, 0, 64, 0, LP_OPTIONS16},
+
+ {"NAND 128MiB 1,8V 8-bit", 0xA1, 0, 128, 0, LP_OPTIONS},
+ {"NAND 128MiB 3,3V 8-bit", 0xF1, 0, 128, 0, LP_OPTIONS},
+ {"NAND 128MiB 1,8V 16-bit", 0xB1, 0, 128, 0, LP_OPTIONS16},
+ {"NAND 128MiB 3,3V 16-bit", 0xC1, 0, 128, 0, LP_OPTIONS16},
+
+ {"NAND 256MiB 1,8V 8-bit", 0xAA, 0, 256, 0, LP_OPTIONS},
+ {"NAND 256MiB 3,3V 8-bit", 0xDA, 0, 256, 0, LP_OPTIONS},
+ {"NAND 256MiB 1,8V 16-bit", 0xBA, 0, 256, 0, LP_OPTIONS16},
+ {"NAND 256MiB 3,3V 16-bit", 0xCA, 0, 256, 0, LP_OPTIONS16},
+
+ {"NAND 512MiB 1,8V 8-bit", 0xAC, 0, 512, 0, LP_OPTIONS},
+ {"NAND 512MiB 3,3V 8-bit", 0xDC, 0, 512, 0, LP_OPTIONS},
+ {"NAND 512MiB 1,8V 16-bit", 0xBC, 0, 512, 0, LP_OPTIONS16},
+ {"NAND 512MiB 3,3V 16-bit", 0xCC, 0, 512, 0, LP_OPTIONS16},
+
+ {"NAND 1GiB 1,8V 8-bit", 0xA3, 0, 1024, 0, LP_OPTIONS},
+ {"NAND 1GiB 3,3V 8-bit", 0xD3, 0, 1024, 0, LP_OPTIONS},
+ {"NAND 1GiB 1,8V 16-bit", 0xB3, 0, 1024, 0, LP_OPTIONS16},
+ {"NAND 1GiB 3,3V 16-bit", 0xC3, 0, 1024, 0, LP_OPTIONS16},
+
+ {"NAND 2GiB 1,8V 8-bit", 0xA5, 0, 2048, 0, LP_OPTIONS},
+ {"NAND 2GiB 3,3V 8-bit", 0xD5, 0, 2048, 0, LP_OPTIONS},
+ {"NAND 2GiB 1,8V 16-bit", 0xB5, 0, 2048, 0, LP_OPTIONS16},
+ {"NAND 2GiB 3,3V 16-bit", 0xC5, 0, 2048, 0, LP_OPTIONS16},
+
+ {NULL, 0,}
+};
+
+/* Manufacturer ID list
+ */
+nand_manufacturer_t nand_manuf_ids[] =
+{
+ {0x0, "unknown"},
+ {NAND_MFR_TOSHIBA, "Toshiba"},
+ {NAND_MFR_SAMSUNG, "Samsung"},
+ {NAND_MFR_FUJITSU, "Fujitsu"},
+ {NAND_MFR_NATIONAL, "National"},
+ {NAND_MFR_RENESAS, "Renesas"},
+ {NAND_MFR_STMICRO, "ST Micro"},
+ {NAND_MFR_HYNIX, "Hynix"},
+ {0x0, NULL},
+};
+
+/* nand device <nand_controller> [controller options]
+ */
+int handle_nand_device_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ int i;
+ int retval;
+
+ if (argc < 1)
+ {
+ WARNING("incomplete flash device nand configuration");
+ return ERROR_FLASH_BANK_INVALID;
+ }
+
+ for (i = 0; nand_flash_controllers[i]; i++)
+ {
+ nand_device_t *p, *c;
+
+ if (strcmp(args[0], nand_flash_controllers[i]->name) == 0)
+ {
+ /* register flash specific commands */
+ if (nand_flash_controllers[i]->register_commands(cmd_ctx) != ERROR_OK)
+ {
+ ERROR("couldn't register '%s' commands", args[0]);
+ exit(-1);
+ }
+
+ c = malloc(sizeof(nand_device_t));
+
+ c->controller = nand_flash_controllers[i];
+ c->controller_priv = NULL;
+ c->manufacturer = NULL;
+ c->device = NULL;
+ c->bus_width = 0;
+ c->address_cycles = 0;
+ c->page_size = 0;
+ c->use_raw = 0;
+ c->next = NULL;
+
+ if ((retval = nand_flash_controllers[i]->nand_device_command(cmd_ctx, cmd, args, argc, c)) != ERROR_OK)
+ {
+ ERROR("'%s' driver rejected nand flash", c->controller->name);
+ free(c);
+ return ERROR_OK;
+ }
+
+ /* put NAND device in linked list */
+ if (nand_devices)
+ {
+ /* find last flash device */
+ for (p = nand_devices; p && p->next; p = p->next);
+ if (p)
+ p->next = c;
+ }
+ else
+ {
+ nand_devices = c;
+ }
+
+ return ERROR_OK;
+ }
+ }
+
+ /* no valid NAND controller was found (i.e. the configuration option,
+ * didn't match one of the compiled-in controllers)
+ */
+ ERROR("No valid NAND flash controller found (%s)", args[0]);
+ ERROR("compiled-in NAND flash controllers:");
+ for (i = 0; nand_flash_controllers[i]; i++)
+ {
+ ERROR("%i: %s", i, nand_flash_controllers[i]->name);
+ }
+
+ return ERROR_OK;
+}
+
+int nand_register_commands(struct command_context_s *cmd_ctx)
+{
+ nand_cmd = register_command(cmd_ctx, NULL, "nand", NULL, COMMAND_ANY, "NAND specific commands");
+
+ register_command(cmd_ctx, nand_cmd, "device", handle_nand_device_command, COMMAND_CONFIG, NULL);
+
+ return ERROR_OK;
+}
+
+int nand_init(struct command_context_s *cmd_ctx)
+{
+ if (nand_devices)
+ {
+ register_command(cmd_ctx, nand_cmd, "list", handle_nand_list_command, COMMAND_EXEC,
+ "list configured NAND flash devices");
+ register_command(cmd_ctx, nand_cmd, "info", handle_nand_info_command, COMMAND_EXEC,
+ "print info about NAND flash device <num>");
+ register_command(cmd_ctx, nand_cmd, "probe", handle_nand_probe_command, COMMAND_EXEC,
+ "identify NAND flash device <num>");
+ register_command(cmd_ctx, nand_cmd, "check_bad_blocks", handle_nand_check_bad_blocks_command, COMMAND_EXEC,
+ "check NAND flash device <num> for bad blocks [<first> <last>]");
+ register_command(cmd_ctx, nand_cmd, "erase", handle_nand_erase_command, COMMAND_EXEC,
+ "erase blocks on NAND flash device <num> <first> <last>");
+ register_command(cmd_ctx, nand_cmd, "copy", handle_nand_copy_command, COMMAND_EXEC,
+ "copy from NAND flash device <num> <offset> <length> <ram-address>");
+ register_command(cmd_ctx, nand_cmd, "dump", handle_nand_dump_command, COMMAND_EXEC,
+ "dump from NAND flash device <num> <filename> <offset> <size> [options]");
+ register_command(cmd_ctx, nand_cmd, "write", handle_nand_write_command, COMMAND_EXEC,
+ "write to NAND flash device <num> <filename> <offset> [options]");
+ register_command(cmd_ctx, nand_cmd, "raw_access", handle_nand_raw_access_command, COMMAND_EXEC,
+ "raw access to NAND flash device <num> ['enable'|'disable']");
+ }
+
+ return ERROR_OK;
+}
+
+nand_device_t *get_nand_device_by_num(int num)
+{
+ nand_device_t *p;
+ int i = 0;
+
+ for (p = nand_devices; p; p = p->next)
+ {
+ if (i++ == num)
+ {
+ return p;
+ }
+ }
+
+ return NULL;
+}
+
+int nand_build_bbt(struct nand_device_s *device, int first, int last)
+{
+ u32 page = 0x0;
+ int i;
+ u8 *oob;
+
+ oob = malloc(6);
+
+ if ((first < 0) || (first >= device->num_blocks))
+ first = 0;
+
+ if ((last >= device->num_blocks) || (last == -1))
+ last = device->num_blocks - 1;
+
+ for (i = first; i < last; i++)
+ {
+ nand_read_page(device, page, NULL, 0, oob, 6);
+
+ if (((device->device->options & NAND_BUSWIDTH_16) && ((oob[0] & oob[1]) != 0xff))
+ || (((device->page_size == 512) && (oob[5] != 0xff)) ||
+ ((device->page_size == 2048) && (oob[0] != 0xff))))
+ {
+ WARNING("invalid block: %i", i);
+ device->blocks[i].is_bad = 1;
+ }
+ else
+ {
+ device->blocks[i].is_bad = 0;
+ }
+
+ page += (device->erase_size / device->page_size);
+ }
+
+ return ERROR_OK;
+}
+
+int nand_read_status(struct nand_device_s *device, u8 *status)
+{
+ if (!device->device)
+ return ERROR_NAND_DEVICE_NOT_PROBED;
+
+ /* Send read status command */
+ device->controller->command(device, NAND_CMD_STATUS);
+
+ usleep(1000);
+
+ /* read status */
+ if (device->device->options & NAND_BUSWIDTH_16)
+ {
+ u16 data;
+ device->controller->read_data(device, &data);
+ *status = data & 0xff;
+ }
+ else
+ {
+ device->controller->read_data(device, status);
+ }
+
+ return ERROR_OK;
+}
+
+int nand_probe(struct nand_device_s *device)
+{
+ u8 manufacturer_id, device_id;
+ u8 id_buff[5];
+ int retval;
+ int i;
+
+ /* clear device data */
+ device->device = NULL;
+ device->manufacturer = NULL;
+
+ /* clear device parameters */
+ device->bus_width = 0;
+ device->address_cycles = 0;
+ device->page_size = 0;
+ device->erase_size = 0;
+
+ /* initialize controller (device parameters are zero, use controller default) */
+ if ((retval = device->controller->init(device) != ERROR_OK))
+ {
+ switch (retval)
+ {
+ case ERROR_NAND_OPERATION_FAILED:
+ DEBUG("controller initialization failed");
+ return ERROR_NAND_OPERATION_FAILED;
+ case ERROR_NAND_OPERATION_NOT_SUPPORTED:
+ ERROR("BUG: controller reported that it doesn't support default parameters");
+ return ERROR_NAND_OPERATION_FAILED;
+ default:
+ ERROR("BUG: unknown controller initialization failure");
+ return ERROR_NAND_OPERATION_FAILED;
+ }
+ }
+
+ device->controller->command(device, NAND_CMD_RESET);
+ device->controller->reset(device);
+
+ device->controller->command(device, NAND_CMD_READID);
+ device->controller->address(device, 0x0);
+
+ if (device->bus_width == 8)
+ {
+ device->controller->read_data(device, &manufacturer_id);
+ device->controller->read_data(device, &device_id);
+ }
+ else
+ {
+ u16 data_buf;
+ device->controller->read_data(device, &data_buf);
+ manufacturer_id = data_buf & 0xff;
+ device->controller->read_data(device, &data_buf);
+ device_id = data_buf & 0xff;
+ }
+
+ for (i = 0; nand_flash_ids[i].name; i++)
+ {
+ if (nand_flash_ids[i].id == device_id)
+ {
+ device->device = &nand_flash_ids[i];
+ break;
+ }
+ }
+
+ for (i = 0; nand_manuf_ids[i].name; i++)
+ {
+ if (nand_manuf_ids[i].id == manufacturer_id)
+ {
+ device->manufacturer = &nand_manuf_ids[i];
+ break;
+ }
+ }
+
+ if (!device->manufacturer)
+ {
+ device->manufacturer = &nand_manuf_ids[0];
+ device->manufacturer->id = manufacturer_id;
+ }
+
+ if (!device->device)
+ {
+ ERROR("unknown NAND flash device found, manufacturer id: 0x%2.2x device id: 0x%2.2x",
+ manufacturer_id, device_id);
+ return ERROR_NAND_OPERATION_FAILED;
+ }
+
+ DEBUG("found %s (%s)", device->device->name, device->manufacturer->name);
+
+ /* initialize device parameters */
+
+ /* bus width */
+ if (device->device->options & NAND_BUSWIDTH_16)
+ device->bus_width = 16;
+ else
+ device->bus_width = 8;
+
+ /* Do we need extended device probe information? */
+ if (device->device->page_size == 0 ||
+ device->device->erase_size == 0)
+ {
+ if (device->bus_width == 8)
+ {
+ device->controller->read_data(device, id_buff+3);
+ device->controller->read_data(device, id_buff+4);
+ device->controller->read_data(device, id_buff+5);
+ }
+ else
+ {
+ u16 data_buf;
+
+ device->controller->read_data(device, &data_buf);
+ id_buff[3] = data_buf;
+
+ device->controller->read_data(device, &data_buf);
+ id_buff[4] = data_buf;
+
+ device->controller->read_data(device, &data_buf);
+ id_buff[5] = data_buf >> 8;
+ }
+ }
+
+ /* page size */
+ if (device->device->page_size == 0)
+ {
+ device->page_size = 1 << (10 + (id_buff[4] & 3));
+ }
+ else if (device->device->page_size == 256)
+ {
+ ERROR("NAND flashes with 256 byte pagesize are not supported");
+ return ERROR_NAND_OPERATION_FAILED;
+ }
+ else
+ {
+ device->page_size = device->device->page_size;
+ }
+
+ /* number of address cycles */
+ if (device->page_size <= 512)
+ {
+ /* small page devices */
+ if (device->device->chip_size <= 32)
+ device->address_cycles = 3;
+ else if (device->device->chip_size <= 8*1024)
+ device->address_cycles = 4;
+ else
+ {
+ ERROR("BUG: small page NAND device with more than 8 GiB encountered");
+ device->address_cycles = 5;
+ }
+ }
+ else
+ {
+ /* large page devices */
+ if (device->device->chip_size <= 128)
+ device->address_cycles = 4;
+ else if (device->device->chip_size <= 32*1024)
+ device->address_cycles = 5;
+ else
+ {
+ ERROR("BUG: small page NAND device with more than 32 GiB encountered");
+ device->address_cycles = 6;
+ }
+ }
+
+ /* erase size */
+ if (device->device->erase_size == 0)
+ {
+ switch ((id_buff[4] >> 4) & 3) {
+ case 0:
+ device->erase_size = 64 << 10;
+ break;
+ case 1:
+ device->erase_size = 128 << 10;
+ break;
+ case 2:
+ device->erase_size = 256 << 10;
+ break;
+ case 3:
+ device->erase_size =512 << 10;
+ break;
+ }
+ }
+ else
+ {
+ device->erase_size = device->device->erase_size;
+ }
+
+ /* initialize controller, but leave parameters at the controllers default */
+ if ((retval = device->controller->init(device) != ERROR_OK))
+ {
+ switch (retval)
+ {
+ case ERROR_NAND_OPERATION_FAILED:
+ DEBUG("controller initialization failed");
+ return ERROR_NAND_OPERATION_FAILED;
+ case ERROR_NAND_OPERATION_NOT_SUPPORTED:
+ ERROR("controller doesn't support requested parameters (buswidth: %i, address cycles: %i, page size: %i)",
+ device->bus_width, device->address_cycles, device->page_size);
+ return ERROR_NAND_OPERATION_FAILED;
+ default:
+ ERROR("BUG: unknown controller initialization failure");
+ return ERROR_NAND_OPERATION_FAILED;
+ }
+ }
+
+ device->num_blocks = (device->device->chip_size * 1024) / (device->erase_size / 1024);
+ device->blocks = malloc(sizeof(nand_block_t) * device->num_blocks);
+
+ for (i = 0; i < device->num_blocks; i++)
+ {
+ device->blocks[i].size = device->erase_size;
+ device->blocks[i].offset = i * device->erase_size;
+ device->blocks[i].is_erased = -1;
+ device->blocks[i].is_bad = -1;
+ }
+
+ return ERROR_OK;
+}
+
+int nand_erase(struct nand_device_s *device, int first_block, int last_block)
+{
+ int i;
+ u32 page;
+ u8 status;
+ int retval;
+
+ if (!device->device)
+ return ERROR_NAND_DEVICE_NOT_PROBED;
+
+ if ((first_block < 0) || (last_block > device->num_blocks))
+ return ERROR_INVALID_ARGUMENTS;
+
+ /* make sure we know if a block is bad before erasing it */
+ for (i = first_block; i <= last_block; i++)
+ {
+ if (device->blocks[i].is_bad == -1)
+ {
+ nand_build_bbt(device, i, last_block);
+ break;
+ }
+ }
+
+ for (i = first_block; i <= last_block; i++)
+ {
+ /* Send erase setup command */
+ device->controller->command(device, NAND_CMD_ERASE1);
+
+ page = i * (device->erase_size / device->page_size);
+
+ /* Send page address */
+ if (device->page_size <= 512)
+ {
+ /* row */
+ device->controller->address(device, page & 0xff);
+ device->controller->address(device, (page >> 8) & 0xff);
+
+ /* 3rd cycle only on devices with more than 32 MiB */
+ if (device->address_cycles >= 4)
+ device->controller->address(device, (page >> 16) & 0xff);
+
+ /* 4th cycle only on devices with more than 8 GiB */
+ if (device->address_cycles >= 5)
+ device->controller->address(device, (page >> 24) & 0xff);
+ }
+ else
+ {
+ /* row */
+ device->controller->address(device, page & 0xff);
+ device->controller->address(device, (page >> 8) & 0xff);
+
+ /* 3rd cycle only on devices with more than 128 MiB */
+ if (device->address_cycles >= 5)
+ device->controller->address(device, (page >> 16) & 0xff);
+ }
+
+ /* Send erase confirm command */
+ device->controller->command(device, NAND_CMD_ERASE2);
+
+ if (!device->controller->nand_ready(device, 1000))
+ {
+ ERROR("timeout waiting for NAND flash block erase to complete");
+ return ERROR_NAND_OPERATION_TIMEOUT;
+ }
+
+ if ((retval = nand_read_status(device, &status)) != ERROR_OK)
+ {
+ ERROR("couldn't read status");
+ return ERROR_NAND_OPERATION_FAILED;
+ }
+
+ if (status & 0x1)
+ {
+ ERROR("erase operation didn't pass, status: 0x%2.2x", status);
+ return ERROR_NAND_OPERATION_FAILED;
+ }
+ }
+
+ return ERROR_OK;
+}
+
+int nand_read_plain(struct nand_device_s *device, u32 address, u8 *data, u32 data_size)
+{
+ u8 *page;
+
+ if (!device->device)
+ return ERROR_NAND_DEVICE_NOT_PROBED;
+
+ if (address % device->page_size)
+ {
+ ERROR("reads need to be page aligned");
+ return ERROR_NAND_OPERATION_FAILED;
+ }
+
+ page = malloc(device->page_size);
+
+ while (data_size > 0 )
+ {
+ u32 thisrun_size = (data_size > device->page_size) ? device->page_size : data_size;
+ u32 page_address;
+
+
+ page_address = address / device->page_size;
+
+ nand_read_page(device, page_address, page, device->page_size, NULL, 0);
+
+ memcpy(data, page, thisrun_size);
+
+ address += thisrun_size;
+ data += thisrun_size;
+ data_size -= thisrun_size;
+ }
+
+ free(page);
+
+ return ERROR_OK;
+}
+
+int nand_write_plain(struct nand_device_s *device, u32 address, u8 *data, u32 data_size)
+{
+ u8 *page;
+
+ if (!device->device)
+ return ERROR_NAND_DEVICE_NOT_PROBED;
+
+ if (address % device->page_size)
+ {
+ ERROR("writes need to be page aligned");
+ return ERROR_NAND_OPERATION_FAILED;
+ }
+
+ page = malloc(device->page_size);
+
+ while (data_size > 0 )
+ {
+ u32 thisrun_size = (data_size > device->page_size) ? device->page_size : data_size;
+ u32 page_address;
+
+ memset(page, 0xff, device->page_size);
+ memcpy(page, data, thisrun_size);
+
+ page_address = address / device->page_size;
+
+ nand_write_page(device, page_address, page, device->page_size, NULL, 0);
+
+ address += thisrun_size;
+ data += thisrun_size;
+ data_size -= thisrun_size;
+ }
+
+ free(page);
+
+ return ERROR_OK;
+}
+
+int nand_write_page(struct nand_device_s *device, u32 page, u8 *data, u32 data_size, u8 *oob, u32 oob_size)
+{
+ if (!device->device)
+ return ERROR_NAND_DEVICE_NOT_PROBED;
+
+ if (device->use_raw || device->controller->write_page == NULL)
+ return nand_write_page_raw(device, page, data, data_size, oob, oob_size);
+ else
+ return device->controller->write_page(device, page, data, data_size, oob, oob_size);
+}
+
+int nand_read_page(struct nand_device_s *device, u32 page, u8 *data, u32 data_size, u8 *oob, u32 oob_size)
+{
+ if (!device->device)
+ return ERROR_NAND_DEVICE_NOT_PROBED;
+
+ if (device->use_raw || device->controller->read_page == NULL)
+ return nand_read_page_raw(device, page, data, data_size, oob, oob_size);
+ else
+ return device->controller->read_page(device, page, data, data_size, oob, oob_size);
+}
+
+int nand_read_page_raw(struct nand_device_s *device, u32 page, u8 *data, u32 data_size, u8 *oob, u32 oob_size)
+{
+ int i;
+
+ if (!device->device)
+ return ERROR_NAND_DEVICE_NOT_PROBED;
+
+ if (device->page_size <= 512)
+ {
+ /* small page device */
+ if (data)
+ device->controller->command(device, NAND_CMD_READ0);
+ else
+ device->controller->command(device, NAND_CMD_READOOB);
+
+ /* column (always 0, we start at the beginning of a page/OOB area) */
+ device->controller->address(device, 0x0);
+
+ /* row */
+ device->controller->address(device, page & 0xff);
+ device->controller->address(device, (page >> 8) & 0xff);
+
+ /* 4th cycle only on devices with more than 32 MiB */
+ if (device->address_cycles >= 4)
+ device->controller->address(device, (page >> 16) & 0xff);
+
+ /* 5th cycle only on devices with more than 8 GiB */
+ if (device->address_cycles >= 5)
+ device->controller->address(device, (page >> 24) & 0xff);
+ }
+ else
+ {
+ /* large page device */
+ device->controller->command(device, NAND_CMD_READ0);
+
+ /* column (0 when we start at the beginning of a page,
+ * or 2048 for the beginning of OOB area)
+ */
+ device->controller->address(device, 0x0);
+ device->controller->address(device, 0x8);
+
+ /* row */
+ device->controller->address(device, page & 0xff);
+ device->controller->address(device, (page >> 8) & 0xff);
+
+ /* 5th cycle only on devices with more than 128 MiB */
+ if (device->address_cycles >= 5)
+ device->controller->address(device, (page >> 16) & 0xff);
+
+ /* large page devices need a start command */
+ device->controller->command(device, NAND_CMD_READSTART);
+ }
+
+ if (!device->controller->nand_ready(device, 100))
+ return ERROR_NAND_OPERATION_TIMEOUT;
+
+ if (data)
+ {
+ if (device->controller->read_block_data != NULL)
+ (device->controller->read_block_data)(device, data, data_size);
+ else
+ {
+ for (i = 0; i < data_size;)
+ {
+ if (device->device->options & NAND_BUSWIDTH_16)
+ {
+ device->controller->read_data(device, data);
+ data += 2;
+ i += 2;
+ }
+ else
+ {
+ device->controller->read_data(device, data);
+ data += 1;
+ i += 1;
+ }
+ }
+ }
+ }
+
+ if (oob)
+ {
+ if (device->controller->read_block_data != NULL)
+ (device->controller->read_block_data)(device, oob, oob_size);
+ else
+ {
+ for (i = 0; i < oob_size;)
+ {
+ if (device->device->options & NAND_BUSWIDTH_16)
+ {
+ device->controller->read_data(device, oob);
+ oob += 2;
+ i += 2;
+ }
+ else
+ {
+ device->controller->read_data(device, oob);
+ oob += 1;
+ i += 1;
+ }
+ }
+ }
+ }
+
+ return ERROR_OK;
+}
+
+int nand_write_page_raw(struct nand_device_s *device, u32 page, u8 *data, u32 data_size, u8 *oob, u32 oob_size)
+{
+ int i;
+ int retval;
+ u8 status;
+
+ if (!device->device)
+ return ERROR_NAND_DEVICE_NOT_PROBED;
+
+ device->controller->command(device, NAND_CMD_SEQIN);
+
+ if (device->page_size <= 512)
+ {
+ /* column (always 0, we start at the beginning of a page/OOB area) */
+ device->controller->address(device, 0x0);
+
+ /* row */
+ device->controller->address(device, page & 0xff);
+ device->controller->address(device, (page >> 8) & 0xff);
+
+ /* 4th cycle only on devices with more than 32 MiB */
+ if (device->address_cycles >= 4)
+ device->controller->address(device, (page >> 16) & 0xff);
+
+ /* 5th cycle only on devices with more than 8 GiB */
+ if (device->address_cycles >= 5)
+ device->controller->address(device, (page >> 24) & 0xff);
+ }
+ else
+ {
+ /* column (0 when we start at the beginning of a page,
+ * or 2048 for the beginning of OOB area)
+ */
+ device->controller->address(device, 0x0);
+ device->controller->address(device, 0x8);
+
+ /* row */
+ device->controller->address(device, page & 0xff);
+ device->controller->address(device, (page >> 8) & 0xff);
+
+ /* 5th cycle only on devices with more than 128 MiB */
+ if (device->address_cycles >= 5)
+ device->controller->address(device, (page >> 16) & 0xff);
+ }
+
+ if (data)
+ {
+ if (device->controller->write_block_data != NULL)
+ (device->controller->write_block_data)(device, data, data_size);
+ else
+ {
+ for (i = 0; i < data_size;)
+ {
+ if (device->device->options & NAND_BUSWIDTH_16)
+ {
+ u16 data_buf = le_to_h_u16(data);
+ device->controller->write_data(device, data_buf);
+ data += 2;
+ i += 2;
+ }
+ else
+ {
+ device->controller->write_data(device, *data);
+ data += 1;
+ i += 1;
+ }
+ }
+ }
+ }
+
+ if (oob)
+ {
+ if (device->controller->write_block_data != NULL)
+ (device->controller->write_block_data)(device, oob, oob_size);
+ else
+ {
+ for (i = 0; i < oob_size;)
+ {
+ if (device->device->options & NAND_BUSWIDTH_16)
+ {
+ u16 oob_buf = le_to_h_u16(data);
+ device->controller->write_data(device, oob_buf);
+ oob += 2;
+ i += 2;
+ }
+ else
+ {
+ device->controller->write_data(device, *oob);
+ oob += 1;
+ i += 1;
+ }
+ }
+ }
+ }
+
+ device->controller->command(device, NAND_CMD_PAGEPROG);
+
+ if (!device->controller->nand_ready(device, 100))
+ return ERROR_NAND_OPERATION_TIMEOUT;
+
+ if ((retval = nand_read_status(device, &status)) != ERROR_OK)
+ {
+ ERROR("couldn't read status");
+ return ERROR_NAND_OPERATION_FAILED;
+ }
+
+ if (status & NAND_STATUS_FAIL)
+ {
+ ERROR("write operation didn't pass, status: 0x%2.2x", status);
+ return ERROR_NAND_OPERATION_FAILED;
+ }
+
+ return ERROR_OK;
+}
+
+int handle_nand_list_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ nand_device_t *p;
+ int i = 0;
+
+ if (!nand_devices)
+ {
+ command_print(cmd_ctx, "no NAND flash devices configured");
+ return ERROR_OK;
+ }
+
+ for (p = nand_devices; p; p = p->next)
+ {
+ if (p->device)
+ command_print(cmd_ctx, "#%i: %s (%s) pagesize: %i, buswidth: %i, erasesize: %i",
+ i++, p->device->name, p->manufacturer->name, p->page_size, p->bus_width, p->erase_size);
+ else
+ command_print(cmd_ctx, "#%i: not probed");
+ }
+
+ return ERROR_OK;
+}
+
+int handle_nand_info_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ nand_device_t *p;
+ int i = 0;
+ int j = 0;
+ int first = -1;
+ int last = -1;
+
+ if ((argc < 1) || (argc > 3))
+ {
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ }
+
+ if (argc == 2)
+ {
+ first = last = strtoul(args[1], NULL, 0);
+ }
+ else if (argc == 3)
+ {
+ first = strtoul(args[1], NULL, 0);
+ last = strtoul(args[2], NULL, 0);
+ }
+
+ p = get_nand_device_by_num(strtoul(args[0], NULL, 0));
+ if (p)
+ {
+ if (p->device)
+ {
+ if (first >= p->num_blocks)
+ first = p->num_blocks - 1;
+
+ if (last >= p->num_blocks)
+ last = p->num_blocks - 1;
+
+ command_print(cmd_ctx, "#%i: %s (%s) pagesize: %i, buswidth: %i, erasesize: %i",
+ i++, p->device->name, p->manufacturer->name, p->page_size, p->bus_width, p->erase_size);
+
+ for (j = first; j <= last; j++)
+ {
+ char *erase_state, *bad_state;
+
+ if (p->blocks[j].is_erased == 0)
+ erase_state = "not erased";
+ else if (p->blocks[j].is_erased == 1)
+ erase_state = "erased";
+ else
+ erase_state = "erase state unknown";
+
+ if (p->blocks[j].is_bad == 0)
+ bad_state = "";
+ else if (p->blocks[j].is_bad == 1)
+ bad_state = " (marked bad)";
+ else
+ bad_state = " (block condition unknown)";
+
+ command_print(cmd_ctx, "\t#%i: 0x%8.8x (0x%xkB) %s%s",
+ j, p->blocks[j].offset, p->blocks[j].size / 1024,
+ erase_state, bad_state);
+ }
+ }
+ else
+ {
+ command_print(cmd_ctx, "#%i: not probed");
+ }
+ }
+
+ return ERROR_OK;
+}
+
+int handle_nand_probe_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ nand_device_t *p;
+ int retval;
+
+ if (argc != 1)
+ {
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ p = get_nand_device_by_num(strtoul(args[0], NULL, 0));
+ if (p)
+ {
+ if ((retval = nand_probe(p)) == ERROR_OK)
+ {
+ command_print(cmd_ctx, "NAND flash device '%s' found", p->device->name);
+ }
+ else if (retval == ERROR_NAND_OPERATION_FAILED)
+ {
+ command_print(cmd_ctx, "probing failed for NAND flash device");
+ }
+ else
+ {
+ command_print(cmd_ctx, "unknown error when probing NAND flash device");
+ }
+ }
+ else
+ {
+ command_print(cmd_ctx, "NAND flash device '#%s' is out of bounds", args[0]);
+ }
+
+ return ERROR_OK;
+}
+
+int handle_nand_erase_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ nand_device_t *p;
+ int retval;
+
+ if (argc != 3)
+ {
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ }
+
+ p = get_nand_device_by_num(strtoul(args[0], NULL, 0));
+ if (p)
+ {
+ int first = strtoul(args[1], NULL, 0);
+ int last = strtoul(args[2], NULL, 0);
+
+ if ((retval = nand_erase(p, first, last)) == ERROR_OK)
+ {
+ command_print(cmd_ctx, "successfully erased blocks %i to %i on NAND flash device '%s'", first, last, p->device->name);
+ }
+ else if (retval == ERROR_NAND_OPERATION_FAILED)
+ {
+ command_print(cmd_ctx, "erase failed");
+ }
+ else
+ {
+ command_print(cmd_ctx, "unknown error when erasing NAND flash device");
+ }
+ }
+ else
+ {
+ command_print(cmd_ctx, "NAND flash device '#%s' is out of bounds", args[0]);
+ }
+
+ return ERROR_OK;
+}
+
+int handle_nand_check_bad_blocks_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ nand_device_t *p;
+ int retval;
+ int first = -1;
+ int last = -1;
+
+ if ((argc < 1) || (argc > 3) || (argc == 2))
+ {
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ }
+
+ if (argc == 3)
+ {
+ first = strtoul(args[1], NULL, 0);
+ last = strtoul(args[2], NULL, 0);
+ }
+
+ p = get_nand_device_by_num(strtoul(args[0], NULL, 0));
+ if (p)
+ {
+ if ((retval = nand_build_bbt(p, first, last)) == ERROR_OK)
+ {
+ command_print(cmd_ctx, "checked NAND flash device for bad blocks, use \"nand info\" command to list blocks", p->device->name);
+ }
+ else if (retval == ERROR_NAND_OPERATION_FAILED)
+ {
+ command_print(cmd_ctx, "error when checking for bad blocks on NAND flash device");
+ }
+ else
+ {
+ command_print(cmd_ctx, "unknown error when checking for bad blocks on NAND flash device");
+ }
+ }
+ else
+ {
+ command_print(cmd_ctx, "NAND flash device '#%s' is out of bounds", args[0]);
+ }
+
+ return ERROR_OK;
+}
+
+int handle_nand_copy_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ nand_device_t *p;
+
+ if (argc != 4)
+ {
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ }
+
+ p = get_nand_device_by_num(strtoul(args[0], NULL, 0));
+ if (p)
+ {
+
+ }
+ else
+ {
+ command_print(cmd_ctx, "NAND flash device '#%s' is out of bounds", args[0]);
+ }
+
+ return ERROR_OK;
+}
+
+int handle_nand_write_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ u32 offset;
+ u32 binary_size;
+ u32 buf_cnt;
+ enum oob_formats oob_format = NAND_OOB_NONE;
+
+ fileio_t fileio;
+
+ duration_t duration;
+ char *duration_text;
+
+ nand_device_t *p;
+
+ if (argc < 3)
+ {
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ }
+
+ p = get_nand_device_by_num(strtoul(args[0], NULL, 0));
+ if (p)
+ {
+ u8 *page = NULL;
+ u32 page_size = 0;
+ u8 *oob = NULL;
+ u32 oob_size = 0;
+
+ duration_start_measure(&duration);
+ offset = strtoul(args[2], NULL, 0);
+
+ if (argc > 3)
+ {
+ int i;
+ for (i = 3; i < argc; i++)
+ {
+ if (!strcmp(args[i], "oob_raw"))
+ oob_format |= NAND_OOB_RAW;
+ else if (!strcmp(args[i], "oob_only"))
+ oob_format |= NAND_OOB_RAW | NAND_OOB_ONLY;
+ else
+ {
+ command_print(cmd_ctx, "unknown option: %s", args[i]);
+ }
+ }
+ }
+
+ if (fileio_open(&fileio, args[1], FILEIO_READ, FILEIO_BINARY) != ERROR_OK)
+ {
+ command_print(cmd_ctx, "file open error: %s", fileio.error_str);
+ return ERROR_OK;
+ }
+
+ buf_cnt = binary_size = fileio.size;
+
+ if (!(oob_format & NAND_OOB_ONLY))
+ {
+ page_size = p->page_size;
+ page = malloc(p->page_size);
+ }
+
+ if (oob_format & NAND_OOB_RAW)
+ {
+ if (p->page_size == 512)
+ oob_size = 16;
+ else if (p->page_size == 2048)
+ oob_size = 64;
+ oob = malloc(oob_size);
+ }
+
+ if (offset % p->page_size)
+ {
+ command_print(cmd_ctx, "only page size aligned offsets and sizes are supported");
+ return ERROR_OK;
+ }
+
+ while (buf_cnt > 0)
+ {
+ u32 size_read;
+
+ if (page)
+ {
+ fileio_read(&fileio, page_size, page, &size_read);
+ buf_cnt -= size_read;
+ if (size_read < page_size)
+ {
+ memset(page + size_read, 0xff, page_size - size_read);
+ }
+ }
+
+ if (oob)
+ {
+ fileio_read(&fileio, oob_size, oob, &size_read);
+ buf_cnt -= size_read;
+ if (size_read < oob_size)
+ {
+ memset(oob + size_read, 0xff, oob_size - size_read);
+ }
+ }
+
+ if (nand_write_page(p, offset / p->page_size, page, page_size, oob, oob_size) != ERROR_OK)
+ {
+ command_print(cmd_ctx, "failed writing file %s to NAND flash %s at offset 0x%8.8x",
+ args[1], args[0], offset);
+ return ERROR_OK;
+ }
+ offset += page_size;
+ }
+
+ fileio_close(&fileio);
+
+ duration_stop_measure(&duration, &duration_text);
+ command_print(cmd_ctx, "wrote file %s to NAND flash %s at offset 0x%8.8x in %s",
+ args[1], args[0], offset, duration_text);
+ free(duration_text);
+ }
+ else
+ {
+ command_print(cmd_ctx, "NAND flash device '#%s' is out of bounds", args[0]);
+ }
+
+ return ERROR_OK;
+}
+
+int handle_nand_dump_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ nand_device_t *p;
+
+ if (argc < 4)
+ {
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ p = get_nand_device_by_num(strtoul(args[0], NULL, 0));
+ if (p)
+ {
+ if (p->device)
+ {
+ fileio_t fileio;
+ duration_t duration;
+ char *duration_text;
+ int retval;
+
+ u8 *page = NULL;
+ u32 page_size = 0;
+ u8 *oob = NULL;
+ u32 oob_size = 0;
+ u32 address = strtoul(args[2], NULL, 0);
+ u32 size = strtoul(args[3], NULL, 0);
+ u32 bytes_done = 0;
+ enum oob_formats oob_format = NAND_OOB_NONE;
+
+ if (argc > 4)
+ {
+ int i;
+ for (i = 4; i < argc; i++)
+ {
+ if (!strcmp(args[i], "oob_raw"))
+ oob_format |= NAND_OOB_RAW;
+ else if (!strcmp(args[i], "oob_only"))
+ oob_format |= NAND_OOB_RAW | NAND_OOB_ONLY;
+ else
+ command_print(cmd_ctx, "unknown option: '%s'", args[i]);
+ }
+ }
+
+ if ((address % p->page_size) || (size % p->page_size))
+ {
+ command_print(cmd_ctx, "only page size aligned addresses and sizes are supported");
+ return ERROR_OK;
+ }
+
+ if (!(oob_format & NAND_OOB_ONLY))
+ {
+ page_size = p->page_size;
+ page = malloc(p->page_size);
+ }
+
+ if (oob_format & NAND_OOB_RAW)
+ {
+ if (p->page_size == 512)
+ oob_size = 16;
+ else if (p->page_size == 2048)
+ oob_size = 64;
+ oob = malloc(oob_size);
+ }
+
+ if (fileio_open(&fileio, args[1], FILEIO_WRITE, FILEIO_BINARY) != ERROR_OK)
+ {
+ command_print(cmd_ctx, "dump_image error: %s", fileio.error_str);
+ return ERROR_OK;
+ }
+
+ duration_start_measure(&duration);
+
+ while (size > 0)
+ {
+ u32 size_written;
+ if ((retval = nand_read_page(p, address / p->page_size, page, page_size, oob, oob_size)) != ERROR_OK)
+ {
+ command_print(cmd_ctx, "reading NAND flash page failed");
+ return ERROR_OK;
+ }
+
+ if (page)
+ {
+ fileio_write(&fileio, page_size, page, &size_written);
+ bytes_done += page_size;
+ }
+
+ if (oob)
+ {
+ fileio_write(&fileio, oob_size, oob, &size_written);
+ bytes_done += oob_size;
+ }
+
+ size -= p->page_size;
+ address += p->page_size;
+ }
+
+ if (page)
+ free(page);
+
+ if (oob)
+ free(oob);
+
+ fileio_close(&fileio);
+
+ duration_stop_measure(&duration, &duration_text);
+ command_print(cmd_ctx, "dumped %"PRIi64" byte in %s", fileio.size, duration_text);
+ free(duration_text);
+ }
+ else
+ {
+ command_print(cmd_ctx, "#%i: not probed");
+ }
+ }
+ else
+ {
+ command_print(cmd_ctx, "NAND flash device '#%s' is out of bounds", args[0]);
+ }
+
+ return ERROR_OK;
+}
+
+int handle_nand_raw_access_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ nand_device_t *p;
+
+ if ((argc < 1) || (argc > 2))
+ {
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ p = get_nand_device_by_num(strtoul(args[0], NULL, 0));
+ if (p)
+ {
+ if (p->device)
+ {
+ if (argc == 2)
+ {
+ if (strcmp("enable", args[1]) == 0)
+ {
+ p->use_raw = 1;
+ }
+ else if (strcmp("disable", args[1]) == 0)
+ {
+ p->use_raw = 0;
+ }
+ else
+ {
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+ }
+
+ command_print(cmd_ctx, "raw access is %s", (p->use_raw) ? "enabled" : "disabled");
+ }
+ else
+ {
+ command_print(cmd_ctx, "#%i: not probed");
+ }
+ }
+ else
+ {
+ command_print(cmd_ctx, "NAND flash device '#%s' is out of bounds", args[0]);
+ }
+
+ return ERROR_OK;
+}
+
-/***************************************************************************\r
- * Copyright (C) 2006 by Magnus Lundin *\r
- * lundin@mlu.mine.nu *\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
-\r
-/***************************************************************************\r
-* STELLARIS is tested on LM3S811\r
-* \r
-*\r
-*\r
- ***************************************************************************/\r
-#ifdef HAVE_CONFIG_H\r
-#include "config.h"\r
-#endif\r
-\r
-#include "replacements.h"\r
-\r
-#include "stellaris.h"\r
-#include "cortex_m3.h"\r
-\r
-#include "flash.h"\r
-#include "target.h"\r
-#include "log.h"\r
-#include "binarybuffer.h"\r
-#include "types.h"\r
-\r
-#include <stdlib.h>\r
-#include <string.h>\r
-#include <unistd.h>\r
-\r
-#define DID0_VER(did0) ((did0>>28)&0x07)\r
-int stellaris_register_commands(struct command_context_s *cmd_ctx);\r
-int stellaris_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank);\r
-int stellaris_erase(struct flash_bank_s *bank, int first, int last);\r
-int stellaris_protect(struct flash_bank_s *bank, int set, int first, int last);\r
-int stellaris_write(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count);\r
-int stellaris_auto_probe(struct flash_bank_s *bank);\r
-int stellaris_probe(struct flash_bank_s *bank);\r
-int stellaris_erase_check(struct flash_bank_s *bank);\r
-int stellaris_protect_check(struct flash_bank_s *bank);\r
-int stellaris_info(struct flash_bank_s *bank, char *buf, int buf_size);\r
-\r
-int stellaris_read_part_info(struct flash_bank_s *bank);\r
-u32 stellaris_get_flash_status(flash_bank_t *bank);\r
-void stellaris_set_flash_mode(flash_bank_t *bank,int mode);\r
-u32 stellaris_wait_status_busy(flash_bank_t *bank, u32 waitbits, int timeout);\r
-\r
-int stellaris_read_part_info(struct flash_bank_s *bank);\r
-\r
-flash_driver_t stellaris_flash =\r
-{\r
- .name = "stellaris",\r
- .register_commands = stellaris_register_commands,\r
- .flash_bank_command = stellaris_flash_bank_command,\r
- .erase = stellaris_erase,\r
- .protect = stellaris_protect,\r
- .write = stellaris_write,\r
- .probe = stellaris_probe,\r
- .auto_probe = stellaris_auto_probe,\r
- .erase_check = stellaris_erase_check,\r
- .protect_check = stellaris_protect_check,\r
- .info = stellaris_info\r
-};\r
-\r
-\r
-struct {\r
- u32 partno;\r
- char *partname;\r
-} StellarisParts[] =\r
-{\r
- {0x01,"LM3S101"},\r
- {0x02,"LM3S102"},\r
- {0x19,"LM3S300"},\r
- {0x11,"LM3S301"},\r
- {0x12,"LM3S310"},\r
- {0x1A,"LM3S308"},\r
- {0x13,"LM3S315"},\r
- {0x14,"LM3S316"},\r
- {0x17,"LM3S317"},\r
- {0x18,"LM3S318"},\r
- {0x15,"LM3S328"},\r
- {0x2A,"LM3S600"},\r
- {0x21,"LM3S601"},\r
- {0x2B,"LM3S608"},\r
- {0x22,"LM3S610"},\r
- {0x23,"LM3S611"},\r
- {0x24,"LM3S612"},\r
- {0x25,"LM3S613"},\r
- {0x26,"LM3S615"},\r
- {0x28,"LM3S617"},\r
- {0x29,"LM3S618"},\r
- {0x27,"LM3S628"},\r
- {0x38,"LM3S800"},\r
- {0x31,"LM3S801"},\r
- {0x39,"LM3S808"},\r
- {0x32,"LM3S811"},\r
- {0x33,"LM3S812"},\r
- {0x34,"LM3S815"},\r
- {0x36,"LM3S817"},\r
- {0x37,"LM3S818"},\r
- {0x35,"LM3S828"},\r
- {0x51,"LM3S2110"},\r
- {0x52,"LM3S2739"},\r
- {0x53,"LM3S2651"},\r
- {0x54,"LM3S2939"},\r
- {0x55,"LM3S2965"},\r
- {0x56,"LM3S2432"},\r
- {0x57,"LM3S2620"},\r
- {0x58,"LM3S2950"},\r
- {0x59,"LM3S2412"},\r
- {0x5A,"LM3S2533"},\r
- {0x61,"LM3S8630"},\r
- {0x62,"LM3S8970"},\r
- {0x63,"LM3S8730"},\r
- {0x64,"LM3S8530"},\r
- {0x65,"LM3S8930"},\r
- {0x71,"LM3S6610"},\r
- {0x72,"LM3S6950"},\r
- {0x73,"LM3S6965"},\r
- {0x74,"LM3S6110"},\r
- {0x75,"LM3S6432"},\r
- {0x76,"LM3S6537"},\r
- {0x77,"LM3S6753"},\r
- {0x78,"LM3S6952"},\r
- {0x82,"LM3S6422"},\r
- {0x83,"LM3S6633"},\r
- {0x84,"LM3S2139"},\r
- {0x85,"LM3S2637"},\r
- {0x86,"LM3S8738"},\r
- {0x88,"LM3S8938"},\r
- {0x89,"LM3S6938"},\r
- {0x8B,"LM3S6637"},\r
- {0x8C,"LM3S8933"},\r
- {0x8D,"LM3S8733"},\r
- {0x8E,"LM3S8538"},\r
- {0x8F,"LM3S2948"},\r
- {0xA1,"LM3S6100"},\r
- {0xA2,"LM3S2410"},\r
- {0xA3,"LM3S6730"},\r
- {0xA4,"LM3S2730"},\r
- {0xA5,"LM3S6420"},\r
- {0xA6,"LM3S8962"},\r
- {0xB3,"LM3S1635"},\r
- {0xB4,"LM3S1850"},\r
- {0xB5,"LM3S1960"},\r
- {0xB7,"LM3S1937"},\r
- {0xB8,"LM3S1968"},\r
- {0xB9,"LM3S1751"},\r
- {0xBA,"LM3S1439"},\r
- {0xBB,"LM3S1512"},\r
- {0xBC,"LM3S1435"},\r
- {0xBD,"LM3S1637"},\r
- {0xBE,"LM3S1958"},\r
- {0xBF,"LM3S1110"},\r
- {0xC0,"LM3S1620"},\r
- {0xC1,"LM3S1150"},\r
- {0xC2,"LM3S1165"},\r
- {0xC3,"LM3S1133"},\r
- {0xC4,"LM3S1162"},\r
- {0xC5,"LM3S1138"},\r
- {0xC6,"LM3S1332"},\r
- {0xC7,"LM3S1538"},\r
- {0xD0,"LM3S6815"},\r
- {0xD1,"LM3S6816"},\r
- {0xD2,"LM3S6915"},\r
- {0xD3,"LM3S6916"},\r
- {0xD4,"LM3S2016"},\r
- {0xD5,"LM3S1615"},\r
- {0xD6,"LM3S1616"},\r
- {0xD7,"LM3S8971"},\r
- {0xD8,"LM3S1108"},\r
- {0xD9,"LM3S1101"},\r
- {0xDA,"LM3S1608"},\r
- {0xDB,"LM3S1601"},\r
- {0xDC,"LM3S1918"},\r
- {0xDD,"LM3S1911"},\r
- {0xDE,"LM3S2108"},\r
- {0xDF,"LM3S2101"},\r
- {0xE0,"LM3S2608"},\r
- {0xE1,"LM3S2601"},\r
- {0xE2,"LM3S2918"},\r
- {0xE3,"LM3S2911"},\r
- {0xE4,"LM3S6118"},\r
- {0xE5,"LM3S6111"},\r
- {0xE6,"LM3S6618"},\r
- {0xE7,"LM3S6611"},\r
- {0xE8,"LM3S6918"},\r
- {0xE9,"LM3S6911"},\r
- {0,"Unknown part"}\r
-};\r
-\r
-char * StellarisClassname[2] =\r
-{\r
- "Sandstorm",\r
- "Fury"\r
-};\r
-\r
-/***************************************************************************\r
-* openocd command interface *\r
-***************************************************************************/\r
-\r
-/* flash_bank stellaris <base> <size> 0 0 <target#>\r
- */\r
-int stellaris_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank)\r
-{\r
- stellaris_flash_bank_t *stellaris_info;\r
- \r
- if (argc < 6)\r
- {\r
- WARNING("incomplete flash_bank stellaris configuration");\r
- return ERROR_FLASH_BANK_INVALID;\r
- }\r
- \r
- stellaris_info = calloc(sizeof(stellaris_flash_bank_t),1);\r
- bank->base = 0x0;\r
- bank->driver_priv = stellaris_info;\r
- \r
- stellaris_info->target_name = "Unknown target";\r
- \r
- /* part wasn't probed for info yet */\r
- stellaris_info->did1 = 0;\r
- \r
- /* TODO Use an optional main oscillator clock rate in kHz from arg[6] */ \r
- return ERROR_OK;\r
-}\r
-\r
-int stellaris_register_commands(struct command_context_s *cmd_ctx)\r
-{\r
-/*\r
- command_t *stellaris_cmd = register_command(cmd_ctx, NULL, "stellaris", NULL, COMMAND_ANY, NULL);\r
- register_command(cmd_ctx, stellaris_cmd, "gpnvm", stellaris_handle_gpnvm_command, COMMAND_EXEC,\r
- "stellaris gpnvm <num> <bit> set|clear, set or clear stellaris gpnvm bit");\r
-*/\r
- return ERROR_OK;\r
-}\r
-\r
-int stellaris_info(struct flash_bank_s *bank, char *buf, int buf_size)\r
-{\r
- int printed, device_class;\r
- stellaris_flash_bank_t *stellaris_info = bank->driver_priv;\r
- \r
- stellaris_read_part_info(bank);\r
-\r
- if (stellaris_info->did1 == 0)\r
- {\r
- printed = snprintf(buf, buf_size, "Cannot identify target as a Stellaris\n");\r
- buf += printed;\r
- buf_size -= printed;\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
- \r
- if (DID0_VER(stellaris_info->did0)>0)\r
- {\r
- device_class = (stellaris_info->did0>>16)&0xFF;\r
- }\r
- else\r
- {\r
- device_class = 0;\r
- } \r
- printed = snprintf(buf, buf_size, "\nLMI Stellaris information: Chip is class %i(%s) %s v%c.%i\n",\r
- device_class, StellarisClassname[device_class], stellaris_info->target_name,\r
- 'A' + (stellaris_info->did0>>8)&0xFF, (stellaris_info->did0)&0xFF);\r
- buf += printed;\r
- buf_size -= printed;\r
-\r
- printed = snprintf(buf, buf_size, "did1: 0x%8.8x, arch: 0x%4.4x, eproc: %s, ramsize:%ik, flashsize: %ik\n", \r
- stellaris_info->did1, stellaris_info->did1, "ARMV7M", (1+(stellaris_info->dc0>>16)&0xFFFF)/4, (1+stellaris_info->dc0&0xFFFF)*2);\r
- buf += printed;\r
- buf_size -= printed;\r
-\r
- printed = snprintf(buf, buf_size, "master clock(estimated): %ikHz, rcc is 0x%x \n", stellaris_info->mck_freq / 1000, stellaris_info->rcc);\r
- buf += printed;\r
- buf_size -= printed;\r
-\r
- if (stellaris_info->num_lockbits>0) { \r
- printed = snprintf(buf, buf_size, "pagesize: %i, lockbits: %i 0x%4.4x, pages in lock region: %i \n", stellaris_info->pagesize, stellaris_info->num_lockbits, stellaris_info->lockbits,stellaris_info->num_pages/stellaris_info->num_lockbits);\r
- buf += printed;\r
- buf_size -= printed;\r
- }\r
- return ERROR_OK;\r
-}\r
-\r
-/***************************************************************************\r
-* chip identification and status *\r
-***************************************************************************/\r
-\r
-u32 stellaris_get_flash_status(flash_bank_t *bank)\r
-{\r
- target_t *target = bank->target;\r
- u32 fmc;\r
- \r
- target_read_u32(target, FLASH_CONTROL_BASE|FLASH_FMC, &fmc);\r
- \r
- return fmc;\r
-}\r
-\r
-/** Read clock configuration and set stellaris_info->usec_clocks*/\r
- \r
-void stellaris_read_clock_info(flash_bank_t *bank)\r
-{\r
- stellaris_flash_bank_t *stellaris_info = bank->driver_priv;\r
- target_t *target = bank->target;\r
- u32 rcc, pllcfg, sysdiv, usesysdiv, bypass, oscsrc;\r
- unsigned long mainfreq;\r
-\r
- target_read_u32(target, SCB_BASE|RCC, &rcc);\r
- DEBUG("Stellaris RCC %x",rcc);\r
- target_read_u32(target, SCB_BASE|PLLCFG, &pllcfg);\r
- DEBUG("Stellaris PLLCFG %x",pllcfg);\r
- stellaris_info->rcc = rcc;\r
- \r
- sysdiv = (rcc>>23)&0xF;\r
- usesysdiv = (rcc>>22)&0x1;\r
- bypass = (rcc>>11)&0x1;\r
- oscsrc = (rcc>>4)&0x3;\r
- /* xtal = (rcc>>6)&0xF; */\r
- switch (oscsrc)\r
- {\r
- case 0:\r
- mainfreq = 6000000; /* Default xtal */\r
- break;\r
- case 1:\r
- mainfreq = 22500000; /* Internal osc. 15 MHz +- 50% */\r
- break;\r
- case 2:\r
- mainfreq = 5625000; /* Internal osc. / 4 */\r
- break;\r
- case 3:\r
- WARNING("Invalid oscsrc (3) in rcc register");\r
- mainfreq = 6000000;\r
- break;\r
- }\r
- \r
- if (!bypass)\r
- mainfreq = 200000000; /* PLL out frec */\r
- \r
- if (usesysdiv)\r
- stellaris_info->mck_freq = mainfreq/(1+sysdiv);\r
- else\r
- stellaris_info->mck_freq = mainfreq;\r
- \r
- /* Forget old flash timing */\r
- stellaris_set_flash_mode(bank,0);\r
-}\r
-\r
-/* Setup the timimg registers */\r
-void stellaris_set_flash_mode(flash_bank_t *bank,int mode)\r
-{\r
- stellaris_flash_bank_t *stellaris_info = bank->driver_priv;\r
- target_t *target = bank->target;\r
-\r
- u32 usecrl = (stellaris_info->mck_freq/1000000ul-1);\r
- DEBUG("usecrl = %i",usecrl); \r
- target_write_u32(target, SCB_BASE|USECRL , usecrl);\r
- \r
-}\r
-\r
-u32 stellaris_wait_status_busy(flash_bank_t *bank, u32 waitbits, int timeout)\r
-{\r
- u32 status;\r
- \r
- /* Stellaris waits for cmdbit to clear */\r
- while (((status = stellaris_get_flash_status(bank)) & waitbits) && (timeout-- > 0))\r
- {\r
- DEBUG("status: 0x%x", status);\r
- usleep(1000);\r
- }\r
- \r
- /* Flash errors are reflected in the FLASH_CRIS register */\r
-\r
- return status;\r
-}\r
-\r
-\r
-/* Send one command to the flash controller */\r
-int stellaris_flash_command(struct flash_bank_s *bank,u8 cmd,u16 pagen) \r
-{\r
- u32 fmc;\r
-// stellaris_flash_bank_t *stellaris_info = bank->driver_priv;\r
- target_t *target = bank->target;\r
-\r
- fmc = FMC_WRKEY | cmd; \r
- target_write_u32(target, FLASH_CONTROL_BASE|FLASH_FMC, fmc);\r
- DEBUG("Flash command: 0x%x", fmc);\r
-\r
- if (stellaris_wait_status_busy(bank, cmd, 100)) \r
- {\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- } \r
-\r
- return ERROR_OK;\r
-}\r
-\r
-/* Read device id register, main clock frequency register and fill in driver info structure */\r
-int stellaris_read_part_info(struct flash_bank_s *bank)\r
-{\r
- stellaris_flash_bank_t *stellaris_info = bank->driver_priv;\r
- target_t *target = bank->target;\r
- u32 did0,did1, ver, fam, status;\r
- int i;\r
- \r
- /* Read and parse chip identification register */\r
- target_read_u32(target, SCB_BASE|DID0, &did0);\r
- target_read_u32(target, SCB_BASE|DID1, &did1);\r
- target_read_u32(target, SCB_BASE|DC0, &stellaris_info->dc0);\r
- target_read_u32(target, SCB_BASE|DC1, &stellaris_info->dc1);\r
- DEBUG("did0 0x%x, did1 0x%x, dc0 0x%x, dc1 0x%x",did0, did1, stellaris_info->dc0,stellaris_info->dc1);\r
-\r
- ver = did0 >> 28;\r
- if((ver != 0) && (ver != 1))\r
- {\r
- WARNING("Unknown did0 version, cannot identify target");\r
- return ERROR_FLASH_OPERATION_FAILED; \r
- }\r
-\r
- ver = did1 >> 28;\r
- fam = (did1 >> 24) & 0xF;\r
- if(((ver != 0) && (ver != 1)) || (fam != 0))\r
- {\r
- WARNING("Unknown did1 version/family, cannot positively identify target as a Stellaris");\r
- }\r
-\r
- if (did1 == 0)\r
- {\r
- WARNING("Cannot identify target as a Stellaris");\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
- \r
- for (i=0;StellarisParts[i].partno;i++)\r
- {\r
- if (StellarisParts[i].partno==((did1>>16)&0xFF))\r
- break;\r
- }\r
- \r
- stellaris_info->target_name = StellarisParts[i].partname;\r
- \r
- stellaris_info->did0 = did0;\r
- stellaris_info->did1 = did1;\r
-\r
- stellaris_info->num_lockbits = 1+stellaris_info->dc0&0xFFFF;\r
- stellaris_info->num_pages = 2*(1+stellaris_info->dc0&0xFFFF);\r
- stellaris_info->pagesize = 1024;\r
- bank->size = 1024*stellaris_info->num_pages;\r
- stellaris_info->pages_in_lockregion = 2;\r
- target_read_u32(target, SCB_BASE|FMPPE, &stellaris_info->lockbits);\r
-\r
- // Read main and master clock freqency register \r
- stellaris_read_clock_info(bank);\r
- \r
- status = stellaris_get_flash_status(bank);\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-/***************************************************************************\r
-* flash operations *\r
-***************************************************************************/\r
-\r
-int stellaris_erase_check(struct flash_bank_s *bank)\r
-{\r
- /* \r
- \r
- stellaris_flash_bank_t *stellaris_info = bank->driver_priv;\r
- target_t *target = bank->target;\r
- int i;\r
- \r
- */\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int stellaris_protect_check(struct flash_bank_s *bank)\r
-{\r
- u32 status;\r
- \r
- stellaris_flash_bank_t *stellaris_info = bank->driver_priv;\r
-\r
- if (stellaris_info->did1 == 0)\r
- {\r
- stellaris_read_part_info(bank);\r
- }\r
-\r
- if (stellaris_info->did1 == 0)\r
- {\r
- WARNING("Cannot identify target as an AT91SAM");\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
- \r
- status = stellaris_get_flash_status(bank);\r
- stellaris_info->lockbits = status >> 16;\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int stellaris_erase(struct flash_bank_s *bank, int first, int last)\r
-{\r
- int banknr;\r
- u32 flash_fmc, flash_cris;\r
- stellaris_flash_bank_t *stellaris_info = bank->driver_priv;\r
- target_t *target = bank->target;\r
- \r
- if (stellaris_info->did1 == 0)\r
- {\r
- stellaris_read_part_info(bank);\r
- }\r
-\r
- if (stellaris_info->did1 == 0)\r
- {\r
- WARNING("Cannot identify target as Stellaris");\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- } \r
- \r
- if ((first < 0) || (last < first) || (last >= stellaris_info->num_pages))\r
- {\r
- return ERROR_FLASH_SECTOR_INVALID;\r
- }\r
-\r
- /* Configure the flash controller timing */\r
- stellaris_read_clock_info(bank); \r
- stellaris_set_flash_mode(bank,0);\r
-\r
- /* Clear and disable flash programming interrupts */\r
- target_write_u32(target, FLASH_CIM, 0);\r
- target_write_u32(target, FLASH_MISC, PMISC|AMISC);\r
-\r
- if ((first == 0) && (last == (stellaris_info->num_pages-1)))\r
- {\r
- target_write_u32(target, FLASH_FMA, 0);\r
- target_write_u32(target, FLASH_FMC, FMC_WRKEY | FMC_MERASE);\r
- /* Wait until erase complete */\r
- do\r
- {\r
- target_read_u32(target, FLASH_FMC, &flash_fmc);\r
- }\r
- while(flash_fmc & FMC_MERASE);\r
- \r
- /* if device has > 128k, then second erase cycle is needed */\r
- if(stellaris_info->num_pages * stellaris_info->pagesize > 0x20000)\r
- {\r
- target_write_u32(target, FLASH_FMA, 0x20000);\r
- target_write_u32(target, FLASH_FMC, FMC_WRKEY | FMC_MERASE);\r
- /* Wait until erase complete */\r
- do\r
- {\r
- target_read_u32(target, FLASH_FMC, &flash_fmc);\r
- }\r
- while(flash_fmc & FMC_MERASE);\r
- }\r
-\r
- return ERROR_OK;\r
- }\r
-\r
- for (banknr=first;banknr<=last;banknr++)\r
- {\r
- /* Address is first word in page */\r
- target_write_u32(target, FLASH_FMA, banknr*stellaris_info->pagesize);\r
- /* Write erase command */\r
- target_write_u32(target, FLASH_FMC, FMC_WRKEY | FMC_ERASE);\r
- /* Wait until erase complete */\r
- do\r
- {\r
- target_read_u32(target, FLASH_FMC, &flash_fmc);\r
- }\r
- while(flash_fmc & FMC_ERASE);\r
-\r
- /* Check acess violations */\r
- target_read_u32(target, FLASH_CRIS, &flash_cris);\r
- if(flash_cris & (AMASK))\r
- {\r
- WARNING("Error erasing flash page %i, flash_cris 0x%x", banknr, flash_cris);\r
- target_write_u32(target, FLASH_CRIS, 0);\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
- }\r
-\r
- return ERROR_OK;\r
-}\r
-\r
-int stellaris_protect(struct flash_bank_s *bank, int set, int first, int last)\r
-{\r
- u32 fmppe, flash_fmc, flash_cris;\r
- int lockregion;\r
- \r
- stellaris_flash_bank_t *stellaris_info = bank->driver_priv;\r
- target_t *target = bank->target;\r
- \r
- if (bank->target->state != TARGET_HALTED)\r
- {\r
- return ERROR_TARGET_NOT_HALTED;\r
- }\r
- \r
- if ((first < 0) || (last < first) || (last >= stellaris_info->num_lockbits))\r
- {\r
- return ERROR_FLASH_SECTOR_INVALID;\r
- }\r
- \r
- if (stellaris_info->did1 == 0)\r
- {\r
- stellaris_read_part_info(bank);\r
- }\r
-\r
- if (stellaris_info->did1 == 0)\r
- {\r
- WARNING("Cannot identify target as an Stellaris MCU");\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
- \r
- /* Configure the flash controller timing */\r
- stellaris_read_clock_info(bank); \r
- stellaris_set_flash_mode(bank,0);\r
-\r
- fmppe = stellaris_info->lockbits; \r
- for (lockregion=first;lockregion<=last;lockregion++) \r
- {\r
- if (set)\r
- fmppe &= ~(1<<lockregion); \r
- else\r
- fmppe |= (1<<lockregion); \r
- }\r
-\r
- /* Clear and disable flash programming interrupts */\r
- target_write_u32(target, FLASH_CIM, 0);\r
- target_write_u32(target, FLASH_MISC, PMISC|AMISC);\r
- \r
- DEBUG("fmppe 0x%x",fmppe);\r
- target_write_u32(target, SCB_BASE|FMPPE, fmppe);\r
- /* Commit FMPPE */\r
- target_write_u32(target, FLASH_FMA, 1);\r
- /* Write commit command */\r
- /* TODO safety check, sice this cannot be undone */\r
- WARNING("Flash protection cannot be removed once commited, commit is NOT executed !");\r
- /* target_write_u32(target, FLASH_FMC, FMC_WRKEY | FMC_COMT); */\r
- /* Wait until erase complete */\r
- do\r
- {\r
- target_read_u32(target, FLASH_FMC, &flash_fmc);\r
- }\r
- while(flash_fmc & FMC_COMT);\r
-\r
- /* Check acess violations */\r
- target_read_u32(target, FLASH_CRIS, &flash_cris);\r
- if(flash_cris & (AMASK))\r
- {\r
- WARNING("Error setting flash page protection, flash_cris 0x%x", flash_cris);\r
- target_write_u32(target, FLASH_CRIS, 0);\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
- \r
- target_read_u32(target, SCB_BASE|FMPPE, &stellaris_info->lockbits);\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-u8 stellaris_write_code[] = \r
-{\r
-/* \r
- Call with : \r
- r0 = buffer address\r
- r1 = destination address\r
- r2 = bytecount (in) - endaddr (work) \r
- \r
- Used registers: \r
- r3 = pFLASH_CTRL_BASE\r
- r4 = FLASHWRITECMD\r
- r5 = #1\r
- r6 = bytes written\r
- r7 = temp reg\r
-*/\r
- 0x07,0x4B, /* ldr r3,pFLASH_CTRL_BASE */\r
- 0x08,0x4C, /* ldr r4,FLASHWRITECMD */\r
- 0x01,0x25, /* movs r5, 1 */\r
- 0x00,0x26, /* movs r6, #0 */\r
-/* mainloop: */\r
- 0x19,0x60, /* str r1, [r3, #0] */\r
- 0x87,0x59, /* ldr r7, [r0, r6] */\r
- 0x5F,0x60, /* str r7, [r3, #4] */\r
- 0x9C,0x60, /* str r4, [r3, #8] */\r
-/* waitloop: */\r
- 0x9F,0x68, /* ldr r7, [r3, #8] */\r
- 0x2F,0x42, /* tst r7, r5 */\r
- 0xFC,0xD1, /* bne waitloop */\r
- 0x04,0x31, /* adds r1, r1, #4 */\r
- 0x04,0x36, /* adds r6, r6, #4 */\r
- 0x96,0x42, /* cmp r6, r2 */\r
- 0xF4,0xD1, /* bne mainloop */\r
- 0x00,0xBE, /* bkpt #0 */\r
-/* pFLASH_CTRL_BASE: */\r
- 0x00,0xD0,0x0F,0x40, /* .word 0x400FD000 */\r
-/* FLASHWRITECMD: */\r
- 0x01,0x00,0x42,0xA4 /* .word 0xA4420001 */\r
-};\r
-\r
-int stellaris_write_block(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 wcount)\r
-{\r
-// stellaris_flash_bank_t *stellaris_info = bank->driver_priv;\r
- target_t *target = bank->target;\r
- u32 buffer_size = 8192;\r
- working_area_t *source;\r
- working_area_t *write_algorithm;\r
- u32 address = bank->base + offset;\r
- reg_param_t reg_params[8];\r
- armv7m_algorithm_t armv7m_info;\r
- int retval;\r
- \r
- DEBUG("(bank=%08X buffer=%08X offset=%08X wcount=%08X)",\r
- (unsigned int)bank, (unsigned int)buffer, offset, wcount);\r
-\r
- /* flash write code */\r
- if (target_alloc_working_area(target, sizeof(stellaris_write_code), &write_algorithm) != ERROR_OK)\r
- {\r
- WARNING("no working area available, can't do block memory writes");\r
- return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;\r
- };\r
-\r
- target_write_buffer(target, write_algorithm->address, sizeof(stellaris_write_code), stellaris_write_code);\r
-\r
- /* memory buffer */\r
- while (target_alloc_working_area(target, buffer_size, &source) != ERROR_OK)\r
- {\r
- DEBUG("called target_alloc_working_area(target=%08X buffer_size=%08X source=%08X)",\r
- (unsigned int)target, buffer_size, (unsigned int)source); \r
- buffer_size /= 2;\r
- if (buffer_size <= 256)\r
- {\r
- /* if we already allocated the writing code, but failed to get a buffer, free the algorithm */\r
- if (write_algorithm)\r
- target_free_working_area(target, write_algorithm);\r
- \r
- WARNING("no large enough working area available, can't do block memory writes");\r
- return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;\r
- }\r
- };\r
- \r
- armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;\r
- armv7m_info.core_mode = ARMV7M_MODE_ANY;\r
- armv7m_info.core_state = ARMV7M_STATE_THUMB;\r
- \r
- init_reg_param(®_params[0], "r0", 32, PARAM_OUT);\r
- init_reg_param(®_params[1], "r1", 32, PARAM_OUT);\r
- init_reg_param(®_params[2], "r2", 32, PARAM_OUT);\r
- init_reg_param(®_params[3], "r3", 32, PARAM_OUT);\r
- init_reg_param(®_params[4], "r4", 32, PARAM_OUT);\r
- init_reg_param(®_params[5], "r5", 32, PARAM_OUT);\r
- init_reg_param(®_params[6], "r6", 32, PARAM_OUT);\r
- init_reg_param(®_params[7], "r7", 32, PARAM_OUT);\r
-\r
- while (wcount > 0)\r
- {\r
- u32 thisrun_count = (wcount > (buffer_size / 4)) ? (buffer_size / 4) : wcount;\r
- \r
- target_write_buffer(target, source->address, thisrun_count * 4, buffer);\r
- \r
- buf_set_u32(reg_params[0].value, 0, 32, source->address);\r
- buf_set_u32(reg_params[1].value, 0, 32, address);\r
- buf_set_u32(reg_params[2].value, 0, 32, 4*thisrun_count);\r
- WARNING("Algorithm flash write %i words to 0x%x, %i remaining",thisrun_count,address, wcount);\r
- DEBUG("Algorithm flash write %i words to 0x%x, %i remaining",thisrun_count,address, wcount);\r
- if ((retval = target->type->run_algorithm(target, 0, NULL, 3, reg_params, write_algorithm->address, write_algorithm->address + sizeof(stellaris_write_code)-10, 10000, &armv7m_info)) != ERROR_OK)\r
- {\r
- ERROR("error executing stellaris flash write algorithm");\r
- target_free_working_area(target, source);\r
- destroy_reg_param(®_params[0]);\r
- destroy_reg_param(®_params[1]);\r
- destroy_reg_param(®_params[2]);\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
- \r
- buffer += thisrun_count * 4;\r
- address += thisrun_count * 4;\r
- wcount -= thisrun_count;\r
- }\r
- \r
-\r
- target_free_working_area(target, write_algorithm);\r
- target_free_working_area(target, source);\r
- \r
- destroy_reg_param(®_params[0]);\r
- destroy_reg_param(®_params[1]);\r
- destroy_reg_param(®_params[2]);\r
- destroy_reg_param(®_params[3]);\r
- destroy_reg_param(®_params[4]);\r
- destroy_reg_param(®_params[5]);\r
- destroy_reg_param(®_params[6]);\r
- destroy_reg_param(®_params[7]);\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int stellaris_write(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count)\r
-{\r
- stellaris_flash_bank_t *stellaris_info = bank->driver_priv;\r
- target_t *target = bank->target;\r
- u32 address = offset;\r
- u32 flash_cris,flash_fmc;\r
- u32 retval;\r
- \r
- DEBUG("(bank=%08X buffer=%08X offset=%08X count=%08X)",\r
- (unsigned int)bank, (unsigned int)buffer, offset, count);\r
-\r
- if (stellaris_info->did1 == 0)\r
- {\r
- stellaris_read_part_info(bank);\r
- }\r
-\r
- if (stellaris_info->did1 == 0)\r
- {\r
- WARNING("Cannot identify target as a Stellaris processor");\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
- \r
- if((offset & 3) || (count & 3))\r
- {\r
- WARNING("offset size must be word aligned");\r
- return ERROR_FLASH_DST_BREAKS_ALIGNMENT;\r
- }\r
- \r
- if (offset + count > bank->size)\r
- return ERROR_FLASH_DST_OUT_OF_BANK;\r
-\r
- /* Configure the flash controller timing */ \r
- stellaris_read_clock_info(bank); \r
- stellaris_set_flash_mode(bank,0);\r
-\r
- \r
- /* Clear and disable flash programming interrupts */\r
- target_write_u32(target, FLASH_CIM, 0);\r
- target_write_u32(target, FLASH_MISC, PMISC|AMISC);\r
-\r
- /* multiple words to be programmed? */\r
- if (count > 0) \r
- {\r
- /* try using a block write */\r
- if ((retval = stellaris_write_block(bank, buffer, offset, count/4)) != ERROR_OK)\r
- {\r
- if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE)\r
- {\r
- /* if block write failed (no sufficient working area),\r
- * we use normal (slow) single dword accesses */ \r
- WARNING("couldn't use block writes, falling back to single memory accesses");\r
- }\r
- else if (retval == ERROR_FLASH_OPERATION_FAILED)\r
- {\r
- /* if an error occured, we examine the reason, and quit */\r
- target_read_u32(target, FLASH_CRIS, &flash_cris);\r
- \r
- ERROR("flash writing failed with CRIS: 0x%x", flash_cris);\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
- }\r
- else\r
- {\r
- buffer += count * 4;\r
- address += count * 4;\r
- count = 0;\r
- }\r
- }\r
-\r
-\r
-\r
- while(count>0)\r
- {\r
- if (!(address&0xff)) DEBUG("0x%x",address);\r
- /* Program one word */\r
- target_write_u32(target, FLASH_FMA, address);\r
- target_write_buffer(target, FLASH_FMD, 4, buffer);\r
- target_write_u32(target, FLASH_FMC, FMC_WRKEY | FMC_WRITE);\r
- //DEBUG("0x%x 0x%x 0x%x",address,buf_get_u32(buffer, 0, 32),FMC_WRKEY | FMC_WRITE);\r
- /* Wait until write complete */\r
- do\r
- {\r
- target_read_u32(target, FLASH_FMC, &flash_fmc);\r
- }\r
- while(flash_fmc & FMC_WRITE);\r
- buffer += 4;\r
- address += 4;\r
- count -= 4;\r
- }\r
- /* Check acess violations */\r
- target_read_u32(target, FLASH_CRIS, &flash_cris);\r
- if(flash_cris & (AMASK))\r
- {\r
- DEBUG("flash_cris 0x%x", flash_cris);\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
- return ERROR_OK;\r
-}\r
-\r
-\r
-int stellaris_probe(struct flash_bank_s *bank)\r
-{\r
- /* we can't probe on an stellaris\r
- * if this is an stellaris, it has the configured flash\r
- */\r
- stellaris_flash_bank_t *stellaris_info = bank->driver_priv;\r
- \r
- stellaris_info->probed = 0;\r
- \r
- if (stellaris_info->did1 == 0)\r
- {\r
- stellaris_read_part_info(bank);\r
- }\r
-\r
- if (stellaris_info->did1 == 0)\r
- {\r
- WARNING("Cannot identify target as a LMI Stellaris");\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
- \r
- stellaris_info->probed = 1;\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int stellaris_auto_probe(struct flash_bank_s *bank)\r
-{\r
- stellaris_flash_bank_t *stellaris_info = bank->driver_priv;\r
- if (stellaris_info->probed)\r
- return ERROR_OK;\r
- return stellaris_probe(bank);\r
-}\r
+/***************************************************************************
+ * Copyright (C) 2006 by Magnus Lundin *
+ * lundin@mlu.mine.nu *
+ * *
+ * 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. *
+ ***************************************************************************/
+
+/***************************************************************************
+* STELLARIS is tested on LM3S811
+*
+*
+*
+ ***************************************************************************/
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "replacements.h"
+
+#include "stellaris.h"
+#include "cortex_m3.h"
+
+#include "flash.h"
+#include "target.h"
+#include "log.h"
+#include "binarybuffer.h"
+#include "types.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define DID0_VER(did0) ((did0>>28)&0x07)
+int stellaris_register_commands(struct command_context_s *cmd_ctx);
+int stellaris_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank);
+int stellaris_erase(struct flash_bank_s *bank, int first, int last);
+int stellaris_protect(struct flash_bank_s *bank, int set, int first, int last);
+int stellaris_write(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count);
+int stellaris_auto_probe(struct flash_bank_s *bank);
+int stellaris_probe(struct flash_bank_s *bank);
+int stellaris_erase_check(struct flash_bank_s *bank);
+int stellaris_protect_check(struct flash_bank_s *bank);
+int stellaris_info(struct flash_bank_s *bank, char *buf, int buf_size);
+
+int stellaris_read_part_info(struct flash_bank_s *bank);
+u32 stellaris_get_flash_status(flash_bank_t *bank);
+void stellaris_set_flash_mode(flash_bank_t *bank,int mode);
+u32 stellaris_wait_status_busy(flash_bank_t *bank, u32 waitbits, int timeout);
+
+int stellaris_read_part_info(struct flash_bank_s *bank);
+
+flash_driver_t stellaris_flash =
+{
+ .name = "stellaris",
+ .register_commands = stellaris_register_commands,
+ .flash_bank_command = stellaris_flash_bank_command,
+ .erase = stellaris_erase,
+ .protect = stellaris_protect,
+ .write = stellaris_write,
+ .probe = stellaris_probe,
+ .auto_probe = stellaris_auto_probe,
+ .erase_check = stellaris_erase_check,
+ .protect_check = stellaris_protect_check,
+ .info = stellaris_info
+};
+
+
+struct {
+ u32 partno;
+ char *partname;
+} StellarisParts[] =
+{
+ {0x01,"LM3S101"},
+ {0x02,"LM3S102"},
+ {0x19,"LM3S300"},
+ {0x11,"LM3S301"},
+ {0x12,"LM3S310"},
+ {0x1A,"LM3S308"},
+ {0x13,"LM3S315"},
+ {0x14,"LM3S316"},
+ {0x17,"LM3S317"},
+ {0x18,"LM3S318"},
+ {0x15,"LM3S328"},
+ {0x2A,"LM3S600"},
+ {0x21,"LM3S601"},
+ {0x2B,"LM3S608"},
+ {0x22,"LM3S610"},
+ {0x23,"LM3S611"},
+ {0x24,"LM3S612"},
+ {0x25,"LM3S613"},
+ {0x26,"LM3S615"},
+ {0x28,"LM3S617"},
+ {0x29,"LM3S618"},
+ {0x27,"LM3S628"},
+ {0x38,"LM3S800"},
+ {0x31,"LM3S801"},
+ {0x39,"LM3S808"},
+ {0x32,"LM3S811"},
+ {0x33,"LM3S812"},
+ {0x34,"LM3S815"},
+ {0x36,"LM3S817"},
+ {0x37,"LM3S818"},
+ {0x35,"LM3S828"},
+ {0x51,"LM3S2110"},
+ {0x52,"LM3S2739"},
+ {0x53,"LM3S2651"},
+ {0x54,"LM3S2939"},
+ {0x55,"LM3S2965"},
+ {0x56,"LM3S2432"},
+ {0x57,"LM3S2620"},
+ {0x58,"LM3S2950"},
+ {0x59,"LM3S2412"},
+ {0x5A,"LM3S2533"},
+ {0x61,"LM3S8630"},
+ {0x62,"LM3S8970"},
+ {0x63,"LM3S8730"},
+ {0x64,"LM3S8530"},
+ {0x65,"LM3S8930"},
+ {0x71,"LM3S6610"},
+ {0x72,"LM3S6950"},
+ {0x73,"LM3S6965"},
+ {0x74,"LM3S6110"},
+ {0x75,"LM3S6432"},
+ {0x76,"LM3S6537"},
+ {0x77,"LM3S6753"},
+ {0x78,"LM3S6952"},
+ {0x82,"LM3S6422"},
+ {0x83,"LM3S6633"},
+ {0x84,"LM3S2139"},
+ {0x85,"LM3S2637"},
+ {0x86,"LM3S8738"},
+ {0x88,"LM3S8938"},
+ {0x89,"LM3S6938"},
+ {0x8B,"LM3S6637"},
+ {0x8C,"LM3S8933"},
+ {0x8D,"LM3S8733"},
+ {0x8E,"LM3S8538"},
+ {0x8F,"LM3S2948"},
+ {0xA1,"LM3S6100"},
+ {0xA2,"LM3S2410"},
+ {0xA3,"LM3S6730"},
+ {0xA4,"LM3S2730"},
+ {0xA5,"LM3S6420"},
+ {0xA6,"LM3S8962"},
+ {0xB3,"LM3S1635"},
+ {0xB4,"LM3S1850"},
+ {0xB5,"LM3S1960"},
+ {0xB7,"LM3S1937"},
+ {0xB8,"LM3S1968"},
+ {0xB9,"LM3S1751"},
+ {0xBA,"LM3S1439"},
+ {0xBB,"LM3S1512"},
+ {0xBC,"LM3S1435"},
+ {0xBD,"LM3S1637"},
+ {0xBE,"LM3S1958"},
+ {0xBF,"LM3S1110"},
+ {0xC0,"LM3S1620"},
+ {0xC1,"LM3S1150"},
+ {0xC2,"LM3S1165"},
+ {0xC3,"LM3S1133"},
+ {0xC4,"LM3S1162"},
+ {0xC5,"LM3S1138"},
+ {0xC6,"LM3S1332"},
+ {0xC7,"LM3S1538"},
+ {0xD0,"LM3S6815"},
+ {0xD1,"LM3S6816"},
+ {0xD2,"LM3S6915"},
+ {0xD3,"LM3S6916"},
+ {0xD4,"LM3S2016"},
+ {0xD5,"LM3S1615"},
+ {0xD6,"LM3S1616"},
+ {0xD7,"LM3S8971"},
+ {0xD8,"LM3S1108"},
+ {0xD9,"LM3S1101"},
+ {0xDA,"LM3S1608"},
+ {0xDB,"LM3S1601"},
+ {0xDC,"LM3S1918"},
+ {0xDD,"LM3S1911"},
+ {0xDE,"LM3S2108"},
+ {0xDF,"LM3S2101"},
+ {0xE0,"LM3S2608"},
+ {0xE1,"LM3S2601"},
+ {0xE2,"LM3S2918"},
+ {0xE3,"LM3S2911"},
+ {0xE4,"LM3S6118"},
+ {0xE5,"LM3S6111"},
+ {0xE6,"LM3S6618"},
+ {0xE7,"LM3S6611"},
+ {0xE8,"LM3S6918"},
+ {0xE9,"LM3S6911"},
+ {0,"Unknown part"}
+};
+
+char * StellarisClassname[2] =
+{
+ "Sandstorm",
+ "Fury"
+};
+
+/***************************************************************************
+* openocd command interface *
+***************************************************************************/
+
+/* flash_bank stellaris <base> <size> 0 0 <target#>
+ */
+int stellaris_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank)
+{
+ stellaris_flash_bank_t *stellaris_info;
+
+ if (argc < 6)
+ {
+ WARNING("incomplete flash_bank stellaris configuration");
+ return ERROR_FLASH_BANK_INVALID;
+ }
+
+ stellaris_info = calloc(sizeof(stellaris_flash_bank_t),1);
+ bank->base = 0x0;
+ bank->driver_priv = stellaris_info;
+
+ stellaris_info->target_name = "Unknown target";
+
+ /* part wasn't probed for info yet */
+ stellaris_info->did1 = 0;
+
+ /* TODO Use an optional main oscillator clock rate in kHz from arg[6] */
+ return ERROR_OK;
+}
+
+int stellaris_register_commands(struct command_context_s *cmd_ctx)
+{
+/*
+ command_t *stellaris_cmd = register_command(cmd_ctx, NULL, "stellaris", NULL, COMMAND_ANY, NULL);
+ register_command(cmd_ctx, stellaris_cmd, "gpnvm", stellaris_handle_gpnvm_command, COMMAND_EXEC,
+ "stellaris gpnvm <num> <bit> set|clear, set or clear stellaris gpnvm bit");
+*/
+ return ERROR_OK;
+}
+
+int stellaris_info(struct flash_bank_s *bank, char *buf, int buf_size)
+{
+ int printed, device_class;
+ stellaris_flash_bank_t *stellaris_info = bank->driver_priv;
+
+ stellaris_read_part_info(bank);
+
+ if (stellaris_info->did1 == 0)
+ {
+ printed = snprintf(buf, buf_size, "Cannot identify target as a Stellaris\n");
+ buf += printed;
+ buf_size -= printed;
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ if (DID0_VER(stellaris_info->did0)>0)
+ {
+ device_class = (stellaris_info->did0>>16)&0xFF;
+ }
+ else
+ {
+ device_class = 0;
+ }
+ printed = snprintf(buf, buf_size, "\nLMI Stellaris information: Chip is class %i(%s) %s v%c.%i\n",
+ device_class, StellarisClassname[device_class], stellaris_info->target_name,
+ 'A' + (stellaris_info->did0>>8)&0xFF, (stellaris_info->did0)&0xFF);
+ buf += printed;
+ buf_size -= printed;
+
+ printed = snprintf(buf, buf_size, "did1: 0x%8.8x, arch: 0x%4.4x, eproc: %s, ramsize:%ik, flashsize: %ik\n",
+ stellaris_info->did1, stellaris_info->did1, "ARMV7M", (1+(stellaris_info->dc0>>16)&0xFFFF)/4, (1+stellaris_info->dc0&0xFFFF)*2);
+ buf += printed;
+ buf_size -= printed;
+
+ printed = snprintf(buf, buf_size, "master clock(estimated): %ikHz, rcc is 0x%x \n", stellaris_info->mck_freq / 1000, stellaris_info->rcc);
+ buf += printed;
+ buf_size -= printed;
+
+ if (stellaris_info->num_lockbits>0) {
+ printed = snprintf(buf, buf_size, "pagesize: %i, lockbits: %i 0x%4.4x, pages in lock region: %i \n", stellaris_info->pagesize, stellaris_info->num_lockbits, stellaris_info->lockbits,stellaris_info->num_pages/stellaris_info->num_lockbits);
+ buf += printed;
+ buf_size -= printed;
+ }
+ return ERROR_OK;
+}
+
+/***************************************************************************
+* chip identification and status *
+***************************************************************************/
+
+u32 stellaris_get_flash_status(flash_bank_t *bank)
+{
+ target_t *target = bank->target;
+ u32 fmc;
+
+ target_read_u32(target, FLASH_CONTROL_BASE|FLASH_FMC, &fmc);
+
+ return fmc;
+}
+
+/** Read clock configuration and set stellaris_info->usec_clocks*/
+
+void stellaris_read_clock_info(flash_bank_t *bank)
+{
+ stellaris_flash_bank_t *stellaris_info = bank->driver_priv;
+ target_t *target = bank->target;
+ u32 rcc, pllcfg, sysdiv, usesysdiv, bypass, oscsrc;
+ unsigned long mainfreq;
+
+ target_read_u32(target, SCB_BASE|RCC, &rcc);
+ DEBUG("Stellaris RCC %x",rcc);
+ target_read_u32(target, SCB_BASE|PLLCFG, &pllcfg);
+ DEBUG("Stellaris PLLCFG %x",pllcfg);
+ stellaris_info->rcc = rcc;
+
+ sysdiv = (rcc>>23)&0xF;
+ usesysdiv = (rcc>>22)&0x1;
+ bypass = (rcc>>11)&0x1;
+ oscsrc = (rcc>>4)&0x3;
+ /* xtal = (rcc>>6)&0xF; */
+ switch (oscsrc)
+ {
+ case 0:
+ mainfreq = 6000000; /* Default xtal */
+ break;
+ case 1:
+ mainfreq = 22500000; /* Internal osc. 15 MHz +- 50% */
+ break;
+ case 2:
+ mainfreq = 5625000; /* Internal osc. / 4 */
+ break;
+ case 3:
+ WARNING("Invalid oscsrc (3) in rcc register");
+ mainfreq = 6000000;
+ break;
+ }
+
+ if (!bypass)
+ mainfreq = 200000000; /* PLL out frec */
+
+ if (usesysdiv)
+ stellaris_info->mck_freq = mainfreq/(1+sysdiv);
+ else
+ stellaris_info->mck_freq = mainfreq;
+
+ /* Forget old flash timing */
+ stellaris_set_flash_mode(bank,0);
+}
+
+/* Setup the timimg registers */
+void stellaris_set_flash_mode(flash_bank_t *bank,int mode)
+{
+ stellaris_flash_bank_t *stellaris_info = bank->driver_priv;
+ target_t *target = bank->target;
+
+ u32 usecrl = (stellaris_info->mck_freq/1000000ul-1);
+ DEBUG("usecrl = %i",usecrl);
+ target_write_u32(target, SCB_BASE|USECRL , usecrl);
+
+}
+
+u32 stellaris_wait_status_busy(flash_bank_t *bank, u32 waitbits, int timeout)
+{
+ u32 status;
+
+ /* Stellaris waits for cmdbit to clear */
+ while (((status = stellaris_get_flash_status(bank)) & waitbits) && (timeout-- > 0))
+ {
+ DEBUG("status: 0x%x", status);
+ usleep(1000);
+ }
+
+ /* Flash errors are reflected in the FLASH_CRIS register */
+
+ return status;
+}
+
+
+/* Send one command to the flash controller */
+int stellaris_flash_command(struct flash_bank_s *bank,u8 cmd,u16 pagen)
+{
+ u32 fmc;
+// stellaris_flash_bank_t *stellaris_info = bank->driver_priv;
+ target_t *target = bank->target;
+
+ fmc = FMC_WRKEY | cmd;
+ target_write_u32(target, FLASH_CONTROL_BASE|FLASH_FMC, fmc);
+ DEBUG("Flash command: 0x%x", fmc);
+
+ if (stellaris_wait_status_busy(bank, cmd, 100))
+ {
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ return ERROR_OK;
+}
+
+/* Read device id register, main clock frequency register and fill in driver info structure */
+int stellaris_read_part_info(struct flash_bank_s *bank)
+{
+ stellaris_flash_bank_t *stellaris_info = bank->driver_priv;
+ target_t *target = bank->target;
+ u32 did0,did1, ver, fam, status;
+ int i;
+
+ /* Read and parse chip identification register */
+ target_read_u32(target, SCB_BASE|DID0, &did0);
+ target_read_u32(target, SCB_BASE|DID1, &did1);
+ target_read_u32(target, SCB_BASE|DC0, &stellaris_info->dc0);
+ target_read_u32(target, SCB_BASE|DC1, &stellaris_info->dc1);
+ DEBUG("did0 0x%x, did1 0x%x, dc0 0x%x, dc1 0x%x",did0, did1, stellaris_info->dc0,stellaris_info->dc1);
+
+ ver = did0 >> 28;
+ if((ver != 0) && (ver != 1))
+ {
+ WARNING("Unknown did0 version, cannot identify target");
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ ver = did1 >> 28;
+ fam = (did1 >> 24) & 0xF;
+ if(((ver != 0) && (ver != 1)) || (fam != 0))
+ {
+ WARNING("Unknown did1 version/family, cannot positively identify target as a Stellaris");
+ }
+
+ if (did1 == 0)
+ {
+ WARNING("Cannot identify target as a Stellaris");
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ for (i=0;StellarisParts[i].partno;i++)
+ {
+ if (StellarisParts[i].partno==((did1>>16)&0xFF))
+ break;
+ }
+
+ stellaris_info->target_name = StellarisParts[i].partname;
+
+ stellaris_info->did0 = did0;
+ stellaris_info->did1 = did1;
+
+ stellaris_info->num_lockbits = 1+stellaris_info->dc0&0xFFFF;
+ stellaris_info->num_pages = 2*(1+stellaris_info->dc0&0xFFFF);
+ stellaris_info->pagesize = 1024;
+ bank->size = 1024*stellaris_info->num_pages;
+ stellaris_info->pages_in_lockregion = 2;
+ target_read_u32(target, SCB_BASE|FMPPE, &stellaris_info->lockbits);
+
+ // Read main and master clock freqency register
+ stellaris_read_clock_info(bank);
+
+ status = stellaris_get_flash_status(bank);
+
+ return ERROR_OK;
+}
+
+/***************************************************************************
+* flash operations *
+***************************************************************************/
+
+int stellaris_erase_check(struct flash_bank_s *bank)
+{
+ /*
+
+ stellaris_flash_bank_t *stellaris_info = bank->driver_priv;
+ target_t *target = bank->target;
+ int i;
+
+ */
+
+ return ERROR_OK;
+}
+
+int stellaris_protect_check(struct flash_bank_s *bank)
+{
+ u32 status;
+
+ stellaris_flash_bank_t *stellaris_info = bank->driver_priv;
+
+ if (stellaris_info->did1 == 0)
+ {
+ stellaris_read_part_info(bank);
+ }
+
+ if (stellaris_info->did1 == 0)
+ {
+ WARNING("Cannot identify target as an AT91SAM");
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ status = stellaris_get_flash_status(bank);
+ stellaris_info->lockbits = status >> 16;
+
+ return ERROR_OK;
+}
+
+int stellaris_erase(struct flash_bank_s *bank, int first, int last)
+{
+ int banknr;
+ u32 flash_fmc, flash_cris;
+ stellaris_flash_bank_t *stellaris_info = bank->driver_priv;
+ target_t *target = bank->target;
+
+ if (stellaris_info->did1 == 0)
+ {
+ stellaris_read_part_info(bank);
+ }
+
+ if (stellaris_info->did1 == 0)
+ {
+ WARNING("Cannot identify target as Stellaris");
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ if ((first < 0) || (last < first) || (last >= stellaris_info->num_pages))
+ {
+ return ERROR_FLASH_SECTOR_INVALID;
+ }
+
+ /* Configure the flash controller timing */
+ stellaris_read_clock_info(bank);
+ stellaris_set_flash_mode(bank,0);
+
+ /* Clear and disable flash programming interrupts */
+ target_write_u32(target, FLASH_CIM, 0);
+ target_write_u32(target, FLASH_MISC, PMISC|AMISC);
+
+ if ((first == 0) && (last == (stellaris_info->num_pages-1)))
+ {
+ target_write_u32(target, FLASH_FMA, 0);
+ target_write_u32(target, FLASH_FMC, FMC_WRKEY | FMC_MERASE);
+ /* Wait until erase complete */
+ do
+ {
+ target_read_u32(target, FLASH_FMC, &flash_fmc);
+ }
+ while(flash_fmc & FMC_MERASE);
+
+ /* if device has > 128k, then second erase cycle is needed */
+ if(stellaris_info->num_pages * stellaris_info->pagesize > 0x20000)
+ {
+ target_write_u32(target, FLASH_FMA, 0x20000);
+ target_write_u32(target, FLASH_FMC, FMC_WRKEY | FMC_MERASE);
+ /* Wait until erase complete */
+ do
+ {
+ target_read_u32(target, FLASH_FMC, &flash_fmc);
+ }
+ while(flash_fmc & FMC_MERASE);
+ }
+
+ return ERROR_OK;
+ }
+
+ for (banknr=first;banknr<=last;banknr++)
+ {
+ /* Address is first word in page */
+ target_write_u32(target, FLASH_FMA, banknr*stellaris_info->pagesize);
+ /* Write erase command */
+ target_write_u32(target, FLASH_FMC, FMC_WRKEY | FMC_ERASE);
+ /* Wait until erase complete */
+ do
+ {
+ target_read_u32(target, FLASH_FMC, &flash_fmc);
+ }
+ while(flash_fmc & FMC_ERASE);
+
+ /* Check acess violations */
+ target_read_u32(target, FLASH_CRIS, &flash_cris);
+ if(flash_cris & (AMASK))
+ {
+ WARNING("Error erasing flash page %i, flash_cris 0x%x", banknr, flash_cris);
+ target_write_u32(target, FLASH_CRIS, 0);
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+ }
+
+ return ERROR_OK;
+}
+
+int stellaris_protect(struct flash_bank_s *bank, int set, int first, int last)
+{
+ u32 fmppe, flash_fmc, flash_cris;
+ int lockregion;
+
+ stellaris_flash_bank_t *stellaris_info = bank->driver_priv;
+ target_t *target = bank->target;
+
+ if (bank->target->state != TARGET_HALTED)
+ {
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ if ((first < 0) || (last < first) || (last >= stellaris_info->num_lockbits))
+ {
+ return ERROR_FLASH_SECTOR_INVALID;
+ }
+
+ if (stellaris_info->did1 == 0)
+ {
+ stellaris_read_part_info(bank);
+ }
+
+ if (stellaris_info->did1 == 0)
+ {
+ WARNING("Cannot identify target as an Stellaris MCU");
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ /* Configure the flash controller timing */
+ stellaris_read_clock_info(bank);
+ stellaris_set_flash_mode(bank,0);
+
+ fmppe = stellaris_info->lockbits;
+ for (lockregion=first;lockregion<=last;lockregion++)
+ {
+ if (set)
+ fmppe &= ~(1<<lockregion);
+ else
+ fmppe |= (1<<lockregion);
+ }
+
+ /* Clear and disable flash programming interrupts */
+ target_write_u32(target, FLASH_CIM, 0);
+ target_write_u32(target, FLASH_MISC, PMISC|AMISC);
+
+ DEBUG("fmppe 0x%x",fmppe);
+ target_write_u32(target, SCB_BASE|FMPPE, fmppe);
+ /* Commit FMPPE */
+ target_write_u32(target, FLASH_FMA, 1);
+ /* Write commit command */
+ /* TODO safety check, sice this cannot be undone */
+ WARNING("Flash protection cannot be removed once commited, commit is NOT executed !");
+ /* target_write_u32(target, FLASH_FMC, FMC_WRKEY | FMC_COMT); */
+ /* Wait until erase complete */
+ do
+ {
+ target_read_u32(target, FLASH_FMC, &flash_fmc);
+ }
+ while(flash_fmc & FMC_COMT);
+
+ /* Check acess violations */
+ target_read_u32(target, FLASH_CRIS, &flash_cris);
+ if(flash_cris & (AMASK))
+ {
+ WARNING("Error setting flash page protection, flash_cris 0x%x", flash_cris);
+ target_write_u32(target, FLASH_CRIS, 0);
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ target_read_u32(target, SCB_BASE|FMPPE, &stellaris_info->lockbits);
+
+ return ERROR_OK;
+}
+
+u8 stellaris_write_code[] =
+{
+/*
+ Call with :
+ r0 = buffer address
+ r1 = destination address
+ r2 = bytecount (in) - endaddr (work)
+
+ Used registers:
+ r3 = pFLASH_CTRL_BASE
+ r4 = FLASHWRITECMD
+ r5 = #1
+ r6 = bytes written
+ r7 = temp reg
+*/
+ 0x07,0x4B, /* ldr r3,pFLASH_CTRL_BASE */
+ 0x08,0x4C, /* ldr r4,FLASHWRITECMD */
+ 0x01,0x25, /* movs r5, 1 */
+ 0x00,0x26, /* movs r6, #0 */
+/* mainloop: */
+ 0x19,0x60, /* str r1, [r3, #0] */
+ 0x87,0x59, /* ldr r7, [r0, r6] */
+ 0x5F,0x60, /* str r7, [r3, #4] */
+ 0x9C,0x60, /* str r4, [r3, #8] */
+/* waitloop: */
+ 0x9F,0x68, /* ldr r7, [r3, #8] */
+ 0x2F,0x42, /* tst r7, r5 */
+ 0xFC,0xD1, /* bne waitloop */
+ 0x04,0x31, /* adds r1, r1, #4 */
+ 0x04,0x36, /* adds r6, r6, #4 */
+ 0x96,0x42, /* cmp r6, r2 */
+ 0xF4,0xD1, /* bne mainloop */
+ 0x00,0xBE, /* bkpt #0 */
+/* pFLASH_CTRL_BASE: */
+ 0x00,0xD0,0x0F,0x40, /* .word 0x400FD000 */
+/* FLASHWRITECMD: */
+ 0x01,0x00,0x42,0xA4 /* .word 0xA4420001 */
+};
+
+int stellaris_write_block(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 wcount)
+{
+// stellaris_flash_bank_t *stellaris_info = bank->driver_priv;
+ target_t *target = bank->target;
+ u32 buffer_size = 8192;
+ working_area_t *source;
+ working_area_t *write_algorithm;
+ u32 address = bank->base + offset;
+ reg_param_t reg_params[8];
+ armv7m_algorithm_t armv7m_info;
+ int retval;
+
+ DEBUG("(bank=%08X buffer=%08X offset=%08X wcount=%08X)",
+ (unsigned int)bank, (unsigned int)buffer, offset, wcount);
+
+ /* flash write code */
+ if (target_alloc_working_area(target, sizeof(stellaris_write_code), &write_algorithm) != ERROR_OK)
+ {
+ WARNING("no working area available, can't do block memory writes");
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ };
+
+ target_write_buffer(target, write_algorithm->address, sizeof(stellaris_write_code), stellaris_write_code);
+
+ /* memory buffer */
+ while (target_alloc_working_area(target, buffer_size, &source) != ERROR_OK)
+ {
+ DEBUG("called target_alloc_working_area(target=%08X buffer_size=%08X source=%08X)",
+ (unsigned int)target, buffer_size, (unsigned int)source);
+ buffer_size /= 2;
+ if (buffer_size <= 256)
+ {
+ /* if we already allocated the writing code, but failed to get a buffer, free the algorithm */
+ if (write_algorithm)
+ target_free_working_area(target, write_algorithm);
+
+ WARNING("no large enough working area available, can't do block memory writes");
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+ };
+
+ armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
+ armv7m_info.core_mode = ARMV7M_MODE_ANY;
+ armv7m_info.core_state = ARMV7M_STATE_THUMB;
+
+ init_reg_param(®_params[0], "r0", 32, PARAM_OUT);
+ init_reg_param(®_params[1], "r1", 32, PARAM_OUT);
+ init_reg_param(®_params[2], "r2", 32, PARAM_OUT);
+ init_reg_param(®_params[3], "r3", 32, PARAM_OUT);
+ init_reg_param(®_params[4], "r4", 32, PARAM_OUT);
+ init_reg_param(®_params[5], "r5", 32, PARAM_OUT);
+ init_reg_param(®_params[6], "r6", 32, PARAM_OUT);
+ init_reg_param(®_params[7], "r7", 32, PARAM_OUT);
+
+ while (wcount > 0)
+ {
+ u32 thisrun_count = (wcount > (buffer_size / 4)) ? (buffer_size / 4) : wcount;
+
+ target_write_buffer(target, source->address, thisrun_count * 4, buffer);
+
+ buf_set_u32(reg_params[0].value, 0, 32, source->address);
+ buf_set_u32(reg_params[1].value, 0, 32, address);
+ buf_set_u32(reg_params[2].value, 0, 32, 4*thisrun_count);
+ WARNING("Algorithm flash write %i words to 0x%x, %i remaining",thisrun_count,address, wcount);
+ DEBUG("Algorithm flash write %i words to 0x%x, %i remaining",thisrun_count,address, wcount);
+ if ((retval = target->type->run_algorithm(target, 0, NULL, 3, reg_params, write_algorithm->address, write_algorithm->address + sizeof(stellaris_write_code)-10, 10000, &armv7m_info)) != ERROR_OK)
+ {
+ ERROR("error executing stellaris flash write algorithm");
+ target_free_working_area(target, source);
+ destroy_reg_param(®_params[0]);
+ destroy_reg_param(®_params[1]);
+ destroy_reg_param(®_params[2]);
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ buffer += thisrun_count * 4;
+ address += thisrun_count * 4;
+ wcount -= thisrun_count;
+ }
+
+
+ target_free_working_area(target, write_algorithm);
+ target_free_working_area(target, source);
+
+ destroy_reg_param(®_params[0]);
+ destroy_reg_param(®_params[1]);
+ destroy_reg_param(®_params[2]);
+ destroy_reg_param(®_params[3]);
+ destroy_reg_param(®_params[4]);
+ destroy_reg_param(®_params[5]);
+ destroy_reg_param(®_params[6]);
+ destroy_reg_param(®_params[7]);
+
+ return ERROR_OK;
+}
+
+int stellaris_write(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count)
+{
+ stellaris_flash_bank_t *stellaris_info = bank->driver_priv;
+ target_t *target = bank->target;
+ u32 address = offset;
+ u32 flash_cris,flash_fmc;
+ u32 retval;
+
+ DEBUG("(bank=%08X buffer=%08X offset=%08X count=%08X)",
+ (unsigned int)bank, (unsigned int)buffer, offset, count);
+
+ if (stellaris_info->did1 == 0)
+ {
+ stellaris_read_part_info(bank);
+ }
+
+ if (stellaris_info->did1 == 0)
+ {
+ WARNING("Cannot identify target as a Stellaris processor");
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ if((offset & 3) || (count & 3))
+ {
+ WARNING("offset size must be word aligned");
+ return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
+ }
+
+ if (offset + count > bank->size)
+ return ERROR_FLASH_DST_OUT_OF_BANK;
+
+ /* Configure the flash controller timing */
+ stellaris_read_clock_info(bank);
+ stellaris_set_flash_mode(bank,0);
+
+
+ /* Clear and disable flash programming interrupts */
+ target_write_u32(target, FLASH_CIM, 0);
+ target_write_u32(target, FLASH_MISC, PMISC|AMISC);
+
+ /* multiple words to be programmed? */
+ if (count > 0)
+ {
+ /* try using a block write */
+ if ((retval = stellaris_write_block(bank, buffer, offset, count/4)) != ERROR_OK)
+ {
+ if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE)
+ {
+ /* if block write failed (no sufficient working area),
+ * we use normal (slow) single dword accesses */
+ WARNING("couldn't use block writes, falling back to single memory accesses");
+ }
+ else if (retval == ERROR_FLASH_OPERATION_FAILED)
+ {
+ /* if an error occured, we examine the reason, and quit */
+ target_read_u32(target, FLASH_CRIS, &flash_cris);
+
+ ERROR("flash writing failed with CRIS: 0x%x", flash_cris);
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+ }
+ else
+ {
+ buffer += count * 4;
+ address += count * 4;
+ count = 0;
+ }
+ }
+
+
+
+ while(count>0)
+ {
+ if (!(address&0xff)) DEBUG("0x%x",address);
+ /* Program one word */
+ target_write_u32(target, FLASH_FMA, address);
+ target_write_buffer(target, FLASH_FMD, 4, buffer);
+ target_write_u32(target, FLASH_FMC, FMC_WRKEY | FMC_WRITE);
+ //DEBUG("0x%x 0x%x 0x%x",address,buf_get_u32(buffer, 0, 32),FMC_WRKEY | FMC_WRITE);
+ /* Wait until write complete */
+ do
+ {
+ target_read_u32(target, FLASH_FMC, &flash_fmc);
+ }
+ while(flash_fmc & FMC_WRITE);
+ buffer += 4;
+ address += 4;
+ count -= 4;
+ }
+ /* Check acess violations */
+ target_read_u32(target, FLASH_CRIS, &flash_cris);
+ if(flash_cris & (AMASK))
+ {
+ DEBUG("flash_cris 0x%x", flash_cris);
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+ return ERROR_OK;
+}
+
+
+int stellaris_probe(struct flash_bank_s *bank)
+{
+ /* we can't probe on an stellaris
+ * if this is an stellaris, it has the configured flash
+ */
+ stellaris_flash_bank_t *stellaris_info = bank->driver_priv;
+
+ stellaris_info->probed = 0;
+
+ if (stellaris_info->did1 == 0)
+ {
+ stellaris_read_part_info(bank);
+ }
+
+ if (stellaris_info->did1 == 0)
+ {
+ WARNING("Cannot identify target as a LMI Stellaris");
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ stellaris_info->probed = 1;
+
+ return ERROR_OK;
+}
+
+int stellaris_auto_probe(struct flash_bank_s *bank)
+{
+ stellaris_flash_bank_t *stellaris_info = bank->driver_priv;
+ if (stellaris_info->probed)
+ return ERROR_OK;
+ return stellaris_probe(bank);
+}
-/***************************************************************************\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 "replacements.h"\r
-\r
-#include "stm32x.h"\r
-#include "flash.h"\r
-#include "target.h"\r
-#include "log.h"\r
-#include "armv7m.h"\r
-#include "algorithm.h"\r
-#include "binarybuffer.h"\r
-\r
-#include <stdlib.h>\r
-#include <string.h>\r
-\r
-int stm32x_register_commands(struct command_context_s *cmd_ctx);\r
-int stm32x_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank);\r
-int stm32x_erase(struct flash_bank_s *bank, int first, int last);\r
-int stm32x_protect(struct flash_bank_s *bank, int set, int first, int last);\r
-int stm32x_write(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count);\r
-int stm32x_probe(struct flash_bank_s *bank);\r
-int stm32x_auto_probe(struct flash_bank_s *bank);\r
-int stm32x_handle_part_id_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-int stm32x_protect_check(struct flash_bank_s *bank);\r
-int stm32x_erase_check(struct flash_bank_s *bank);\r
-int stm32x_info(struct flash_bank_s *bank, char *buf, int buf_size);\r
-\r
-int stm32x_handle_lock_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-int stm32x_handle_unlock_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-int stm32x_handle_options_read_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-int stm32x_handle_options_write_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-int stm32x_handle_mass_erase_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-\r
-flash_driver_t stm32x_flash =\r
-{\r
- .name = "stm32x",\r
- .register_commands = stm32x_register_commands,\r
- .flash_bank_command = stm32x_flash_bank_command,\r
- .erase = stm32x_erase,\r
- .protect = stm32x_protect,\r
- .write = stm32x_write,\r
- .probe = stm32x_probe,\r
- .auto_probe = stm32x_auto_probe,\r
- .erase_check = stm32x_erase_check,\r
- .protect_check = stm32x_protect_check,\r
- .info = stm32x_info\r
-};\r
-\r
-int stm32x_register_commands(struct command_context_s *cmd_ctx)\r
-{\r
- command_t *stm32x_cmd = register_command(cmd_ctx, NULL, "stm32x", NULL, COMMAND_ANY, "stm32x flash specific commands");\r
- \r
- register_command(cmd_ctx, stm32x_cmd, "lock", stm32x_handle_lock_command, COMMAND_EXEC,\r
- "lock device");\r
- register_command(cmd_ctx, stm32x_cmd, "unlock", stm32x_handle_unlock_command, COMMAND_EXEC,\r
- "unlock protected device");\r
- register_command(cmd_ctx, stm32x_cmd, "mass_erase", stm32x_handle_mass_erase_command, COMMAND_EXEC,\r
- "mass erase device");\r
- register_command(cmd_ctx, stm32x_cmd, "options_read", stm32x_handle_options_read_command, COMMAND_EXEC,\r
- "read device option bytes");\r
- register_command(cmd_ctx, stm32x_cmd, "options_write", stm32x_handle_options_write_command, COMMAND_EXEC,\r
- "write device option bytes");\r
- return ERROR_OK;\r
-}\r
-\r
-/* flash bank stm32x <base> <size> 0 0 <target#>\r
- */\r
-int stm32x_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank)\r
-{\r
- stm32x_flash_bank_t *stm32x_info;\r
- \r
- if (argc < 6)\r
- {\r
- WARNING("incomplete flash_bank stm32x configuration");\r
- return ERROR_FLASH_BANK_INVALID;\r
- }\r
- \r
- stm32x_info = malloc(sizeof(stm32x_flash_bank_t));\r
- bank->driver_priv = stm32x_info;\r
- \r
- stm32x_info->write_algorithm = NULL;\r
- stm32x_info->probed = 0;\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-u32 stm32x_get_flash_status(flash_bank_t *bank)\r
-{\r
- target_t *target = bank->target;\r
- u32 status;\r
- \r
- target_read_u32(target, STM32_FLASH_SR, &status);\r
- \r
- return status;\r
-}\r
-\r
-u32 stm32x_wait_status_busy(flash_bank_t *bank, int timeout)\r
-{\r
- u32 status;\r
- \r
- /* wait for busy to clear */\r
- while (((status = stm32x_get_flash_status(bank)) & FLASH_BSY) && (timeout-- > 0))\r
- {\r
- DEBUG("status: 0x%x", status);\r
- usleep(1000);\r
- }\r
- \r
- return status;\r
-}\r
-\r
-int stm32x_read_options(struct flash_bank_s *bank)\r
-{\r
- u32 optiondata;\r
- stm32x_flash_bank_t *stm32x_info = NULL;\r
- target_t *target = bank->target;\r
- \r
- stm32x_info = bank->driver_priv;\r
- \r
- /* read current option bytes */\r
- target_read_u32(target, STM32_FLASH_OBR, &optiondata);\r
- \r
- stm32x_info->option_bytes.user_options = (u16)0xFFF8|((optiondata >> 2) & 0x07);\r
- stm32x_info->option_bytes.RDP = (optiondata & (1 << OPT_READOUT)) ? 0xFFFF : 0x5AA5;\r
- \r
- if (optiondata & (1 << OPT_READOUT))\r
- INFO("Device Security Bit Set");\r
- \r
- /* each bit refers to a 4bank protection */\r
- target_read_u32(target, STM32_FLASH_WRPR, &optiondata);\r
- \r
- stm32x_info->option_bytes.protection[0] = (u16)optiondata;\r
- stm32x_info->option_bytes.protection[1] = (u16)(optiondata >> 8);\r
- stm32x_info->option_bytes.protection[2] = (u16)(optiondata >> 16);\r
- stm32x_info->option_bytes.protection[3] = (u16)(optiondata >> 24);\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int stm32x_erase_options(struct flash_bank_s *bank)\r
-{\r
- stm32x_flash_bank_t *stm32x_info = NULL;\r
- target_t *target = bank->target;\r
- u32 status;\r
- \r
- stm32x_info = bank->driver_priv;\r
- \r
- /* read current options */\r
- stm32x_read_options(bank);\r
- \r
- /* unlock flash registers */\r
- target_write_u32(target, STM32_FLASH_KEYR, KEY1);\r
- target_write_u32(target, STM32_FLASH_KEYR, KEY2);\r
- \r
- /* unlock option flash registers */\r
- target_write_u32(target, STM32_FLASH_OPTKEYR, KEY1);\r
- target_write_u32(target, STM32_FLASH_OPTKEYR, KEY2);\r
- \r
- /* erase option bytes */\r
- target_write_u32(target, STM32_FLASH_CR, FLASH_OPTER|FLASH_OPTWRE);\r
- target_write_u32(target, STM32_FLASH_CR, FLASH_OPTER|FLASH_STRT|FLASH_OPTWRE);\r
- \r
- status = stm32x_wait_status_busy(bank, 10);\r
- \r
- if( status & FLASH_WRPRTERR )\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- if( status & FLASH_PGERR )\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- \r
- /* clear readout protection and complementary option bytes\r
- * this will also force a device unlock if set */\r
- stm32x_info->option_bytes.RDP = 0x5AA5;\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int stm32x_write_options(struct flash_bank_s *bank)\r
-{\r
- stm32x_flash_bank_t *stm32x_info = NULL;\r
- target_t *target = bank->target;\r
- u32 status;\r
- \r
- stm32x_info = bank->driver_priv;\r
- \r
- /* unlock flash registers */\r
- target_write_u32(target, STM32_FLASH_KEYR, KEY1);\r
- target_write_u32(target, STM32_FLASH_KEYR, KEY2);\r
- \r
- /* unlock option flash registers */\r
- target_write_u32(target, STM32_FLASH_OPTKEYR, KEY1);\r
- target_write_u32(target, STM32_FLASH_OPTKEYR, KEY2);\r
- \r
- /* program option bytes */\r
- target_write_u32(target, STM32_FLASH_CR, FLASH_OPTPG|FLASH_OPTWRE);\r
- \r
- /* write user option byte */\r
- target_write_u16(target, STM32_OB_USER, stm32x_info->option_bytes.user_options);\r
- \r
- status = stm32x_wait_status_busy(bank, 10);\r
- \r
- if( status & FLASH_WRPRTERR )\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- if( status & FLASH_PGERR )\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- \r
- /* write protection byte 1 */\r
- target_write_u16(target, STM32_OB_WRP0, stm32x_info->option_bytes.protection[0]);\r
- \r
- status = stm32x_wait_status_busy(bank, 10);\r
- \r
- if( status & FLASH_WRPRTERR )\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- if( status & FLASH_PGERR )\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- \r
- /* write protection byte 2 */\r
- target_write_u16(target, STM32_OB_WRP1, stm32x_info->option_bytes.protection[1]);\r
- \r
- status = stm32x_wait_status_busy(bank, 10);\r
- \r
- if( status & FLASH_WRPRTERR )\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- if( status & FLASH_PGERR )\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- \r
- /* write protection byte 3 */\r
- target_write_u16(target, STM32_OB_WRP2, stm32x_info->option_bytes.protection[2]);\r
- \r
- status = stm32x_wait_status_busy(bank, 10);\r
- \r
- if( status & FLASH_WRPRTERR )\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- if( status & FLASH_PGERR )\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- \r
- /* write protection byte 4 */\r
- target_write_u16(target, STM32_OB_WRP3, stm32x_info->option_bytes.protection[3]);\r
- \r
- status = stm32x_wait_status_busy(bank, 10);\r
- \r
- if( status & FLASH_WRPRTERR )\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- if( status & FLASH_PGERR )\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- \r
- /* write readout protection bit */\r
- target_write_u16(target, STM32_OB_RDP, stm32x_info->option_bytes.RDP);\r
- \r
- status = stm32x_wait_status_busy(bank, 10);\r
- \r
- if( status & FLASH_WRPRTERR )\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- if( status & FLASH_PGERR )\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- \r
- target_write_u32(target, STM32_FLASH_CR, FLASH_LOCK);\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int stm32x_blank_check(struct flash_bank_s *bank, int first, int last)\r
-{\r
- target_t *target = bank->target;\r
- u8 *buffer;\r
- int i;\r
- int nBytes;\r
- \r
- if ((first < 0) || (last > bank->num_sectors))\r
- return ERROR_FLASH_SECTOR_INVALID;\r
-\r
- if (target->state != TARGET_HALTED)\r
- {\r
- return ERROR_TARGET_NOT_HALTED;\r
- }\r
- \r
- buffer = malloc(256);\r
- \r
- for (i = first; i <= last; i++)\r
- {\r
- bank->sectors[i].is_erased = 1;\r
-\r
- target->type->read_memory(target, bank->base + bank->sectors[i].offset, 4, 256/4, buffer);\r
- \r
- for (nBytes = 0; nBytes < 256; nBytes++)\r
- {\r
- if (buffer[nBytes] != 0xFF)\r
- {\r
- bank->sectors[i].is_erased = 0;\r
- break;\r
- }\r
- } \r
- }\r
- \r
- free(buffer);\r
-\r
- return ERROR_OK;\r
-}\r
-\r
-int stm32x_protect_check(struct flash_bank_s *bank)\r
-{\r
- target_t *target = bank->target;\r
- \r
- u32 protection;\r
- int i, s;\r
- int num_bits;\r
-\r
- if (target->state != TARGET_HALTED)\r
- {\r
- return ERROR_TARGET_NOT_HALTED;\r
- }\r
-\r
- /* each bit refers to a 4bank protection */\r
- target_read_u32(target, STM32_FLASH_WRPR, &protection);\r
- \r
- /* each protection bit is for 4 1K pages */\r
- num_bits = (bank->num_sectors / 4);\r
- \r
- for (i = 0; i < num_bits; i++)\r
- {\r
- int set = 1;\r
- \r
- if( protection & (1 << i))\r
- set = 0;\r
- \r
- for (s = 0; s < 4; s++)\r
- bank->sectors[(i * 4) + s].is_protected = set;\r
- }\r
-\r
- return ERROR_OK;\r
-}\r
-\r
-int stm32x_erase(struct flash_bank_s *bank, int first, int last)\r
-{\r
- target_t *target = bank->target;\r
- \r
- int i;\r
- u32 status;\r
- \r
- /* unlock flash registers */\r
- target_write_u32(target, STM32_FLASH_KEYR, KEY1);\r
- target_write_u32(target, STM32_FLASH_KEYR, KEY2);\r
- \r
- for (i = first; i <= last; i++)\r
- { \r
- target_write_u32(target, STM32_FLASH_CR, FLASH_PER);\r
- target_write_u32(target, STM32_FLASH_AR, bank->base + bank->sectors[i].offset);\r
- target_write_u32(target, STM32_FLASH_CR, FLASH_PER|FLASH_STRT);\r
- \r
- status = stm32x_wait_status_busy(bank, 10);\r
- \r
- if( status & FLASH_WRPRTERR )\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- if( status & FLASH_PGERR )\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- bank->sectors[i].is_erased = 1;\r
- }\r
-\r
- target_write_u32(target, STM32_FLASH_CR, FLASH_LOCK);\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int stm32x_protect(struct flash_bank_s *bank, int set, int first, int last)\r
-{\r
- stm32x_flash_bank_t *stm32x_info = NULL;\r
- target_t *target = bank->target;\r
- u16 prot_reg[4] = {0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF};\r
- int i, reg, bit;\r
- int status;\r
- u32 protection;\r
- \r
- stm32x_info = bank->driver_priv;\r
- \r
- if (target->state != TARGET_HALTED)\r
- {\r
- return ERROR_TARGET_NOT_HALTED;\r
- }\r
- \r
- if ((first && (first % 4)) || ((last + 1) && (last + 1) % 4))\r
- {\r
- WARNING("sector start/end incorrect - stm32 has 4K sector protection");\r
- return ERROR_FLASH_SECTOR_INVALID;\r
- }\r
- \r
- /* each bit refers to a 4bank protection */\r
- target_read_u32(target, STM32_FLASH_WRPR, &protection);\r
- \r
- prot_reg[0] = (u16)protection;\r
- prot_reg[1] = (u16)(protection >> 8);\r
- prot_reg[2] = (u16)(protection >> 16);\r
- prot_reg[3] = (u16)(protection >> 24);\r
- \r
- for (i = first; i <= last; i++)\r
- {\r
- reg = (i / 4) / 8;\r
- bit = (i / 4) - (reg * 8);\r
- \r
- if( set )\r
- prot_reg[reg] &= ~(1 << bit);\r
- else\r
- prot_reg[reg] |= (1 << bit);\r
- }\r
- \r
- if ((status = stm32x_erase_options(bank)) != ERROR_OK)\r
- return status;\r
- \r
- stm32x_info->option_bytes.protection[0] = prot_reg[0];\r
- stm32x_info->option_bytes.protection[1] = prot_reg[1];\r
- stm32x_info->option_bytes.protection[2] = prot_reg[2];\r
- stm32x_info->option_bytes.protection[3] = prot_reg[3];\r
- \r
- return stm32x_write_options(bank);\r
-}\r
-\r
-int stm32x_write_block(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count)\r
-{\r
- stm32x_flash_bank_t *stm32x_info = bank->driver_priv;\r
- target_t *target = bank->target;\r
- u32 buffer_size = 8192;\r
- working_area_t *source;\r
- u32 address = bank->base + offset;\r
- reg_param_t reg_params[4];\r
- armv7m_algorithm_t armv7m_info;\r
- int retval = ERROR_OK;\r
- \r
- u8 stm32x_flash_write_code[] = {\r
- /* write: */\r
- 0xDF, 0xF8, 0x24, 0x40, /* ldr r4, STM32_FLASH_CR */\r
- 0x09, 0x4D, /* ldr r5, STM32_FLASH_SR */\r
- 0x4F, 0xF0, 0x01, 0x03, /* mov r3, #1 */\r
- 0x23, 0x60, /* str r3, [r4, #0] */\r
- 0x30, 0xF8, 0x02, 0x3B, /* ldrh r3, [r0], #2 */\r
- 0x21, 0xF8, 0x02, 0x3B, /* strh r3, [r1], #2 */\r
- /* busy: */\r
- 0x2B, 0x68, /* ldr r3, [r5, #0] */\r
- 0x13, 0xF0, 0x01, 0x0F, /* tst r3, #0x01 */\r
- 0xFB, 0xD0, /* beq busy */\r
- 0x13, 0xF0, 0x14, 0x0F, /* tst r3, #0x14 */\r
- 0x01, 0xD1, /* bne exit */\r
- 0x01, 0x3A, /* subs r2, r2, #1 */\r
- 0xED, 0xD1, /* bne write */\r
- /* exit: */\r
- 0xFE, 0xE7, /* b exit */ \r
- 0x10, 0x20, 0x02, 0x40, /* STM32_FLASH_CR: .word 0x40022010 */\r
- 0x0C, 0x20, 0x02, 0x40 /* STM32_FLASH_SR: .word 0x4002200C */\r
- };\r
- \r
- /* flash write code */\r
- if (target_alloc_working_area(target, sizeof(stm32x_flash_write_code), &stm32x_info->write_algorithm) != ERROR_OK)\r
- {\r
- WARNING("no working area available, can't do block memory writes");\r
- return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;\r
- };\r
- \r
- target_write_buffer(target, stm32x_info->write_algorithm->address, sizeof(stm32x_flash_write_code), stm32x_flash_write_code);\r
-\r
- /* memory buffer */\r
- while (target_alloc_working_area(target, buffer_size, &source) != ERROR_OK)\r
- {\r
- buffer_size /= 2;\r
- if (buffer_size <= 256)\r
- {\r
- /* if we already allocated the writing code, but failed to get a buffer, free the algorithm */\r
- if (stm32x_info->write_algorithm)\r
- target_free_working_area(target, stm32x_info->write_algorithm);\r
- \r
- WARNING("no large enough working area available, can't do block memory writes");\r
- return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;\r
- }\r
- };\r
- \r
- armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;\r
- armv7m_info.core_mode = ARMV7M_MODE_ANY;\r
- armv7m_info.core_state = ARMV7M_STATE_THUMB;\r
- \r
- init_reg_param(®_params[0], "r0", 32, PARAM_OUT);\r
- init_reg_param(®_params[1], "r1", 32, PARAM_OUT);\r
- init_reg_param(®_params[2], "r2", 32, PARAM_OUT);\r
- init_reg_param(®_params[3], "r3", 32, PARAM_IN);\r
- \r
- while (count > 0)\r
- {\r
- u32 thisrun_count = (count > (buffer_size / 2)) ? (buffer_size / 2) : count;\r
- \r
- target_write_buffer(target, source->address, thisrun_count * 2, buffer);\r
- \r
- buf_set_u32(reg_params[0].value, 0, 32, source->address);\r
- buf_set_u32(reg_params[1].value, 0, 32, address);\r
- buf_set_u32(reg_params[2].value, 0, 32, thisrun_count);\r
- \r
- if ((retval = target->type->run_algorithm(target, 0, NULL, 4, reg_params, stm32x_info->write_algorithm->address, \\r
- stm32x_info->write_algorithm->address + (sizeof(stm32x_flash_write_code) - 10), 10000, &armv7m_info)) != ERROR_OK)\r
- {\r
- ERROR("error executing str7x flash write algorithm");\r
- break;\r
- }\r
- \r
- if (buf_get_u32(reg_params[3].value, 0, 32) & 0x14)\r
- {\r
- retval = ERROR_FLASH_OPERATION_FAILED;\r
- break;\r
- }\r
- \r
- buffer += thisrun_count * 2;\r
- address += thisrun_count * 2;\r
- count -= thisrun_count;\r
- }\r
- \r
- target_free_working_area(target, source);\r
- target_free_working_area(target, stm32x_info->write_algorithm);\r
- \r
- destroy_reg_param(®_params[0]);\r
- destroy_reg_param(®_params[1]);\r
- destroy_reg_param(®_params[2]);\r
- destroy_reg_param(®_params[3]);\r
- \r
- return retval;\r
-}\r
-\r
-int stm32x_write(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count)\r
-{\r
- target_t *target = bank->target;\r
- u32 words_remaining = (count / 2);\r
- u32 bytes_remaining = (count & 0x00000001);\r
- u32 address = bank->base + offset;\r
- u32 bytes_written = 0;\r
- u8 status;\r
- u32 retval;\r
- \r
- if (offset & 0x1)\r
- {\r
- WARNING("offset 0x%x breaks required 2-byte alignment", offset);\r
- return ERROR_FLASH_DST_BREAKS_ALIGNMENT;\r
- }\r
- \r
- /* unlock flash registers */\r
- target_write_u32(target, STM32_FLASH_KEYR, KEY1);\r
- target_write_u32(target, STM32_FLASH_KEYR, KEY2);\r
- \r
- /* multiple half words (2-byte) to be programmed? */\r
- if (words_remaining > 0) \r
- {\r
- /* try using a block write */\r
- if ((retval = stm32x_write_block(bank, buffer, offset, words_remaining)) != ERROR_OK)\r
- {\r
- if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE)\r
- {\r
- /* if block write failed (no sufficient working area),\r
- * we use normal (slow) single dword accesses */ \r
- WARNING("couldn't use block writes, falling back to single memory accesses");\r
- }\r
- else if (retval == ERROR_FLASH_OPERATION_FAILED)\r
- {\r
- ERROR("flash writing failed with error code: 0x%x", retval);\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
- }\r
- else\r
- {\r
- buffer += words_remaining * 2;\r
- address += words_remaining * 2;\r
- words_remaining = 0;\r
- }\r
- }\r
-\r
- while (words_remaining > 0)\r
- {\r
- target_write_u32(target, STM32_FLASH_CR, FLASH_PG);\r
- target_write_u16(target, address, *(u16*)(buffer + bytes_written));\r
- \r
- status = stm32x_wait_status_busy(bank, 5);\r
- \r
- if( status & FLASH_WRPRTERR )\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- if( status & FLASH_PGERR )\r
- return ERROR_FLASH_OPERATION_FAILED;\r
-\r
- bytes_written += 2;\r
- words_remaining--;\r
- address += 2;\r
- }\r
- \r
- if (bytes_remaining)\r
- {\r
- u8 last_halfword[2] = {0xff, 0xff};\r
- int i = 0;\r
- \r
- while(bytes_remaining > 0)\r
- {\r
- last_halfword[i++] = *(buffer + bytes_written); \r
- bytes_remaining--;\r
- bytes_written++;\r
- }\r
- \r
- target_write_u32(target, STM32_FLASH_CR, FLASH_PG);\r
- target_write_u16(target, address, *(u16*)last_halfword);\r
- \r
- status = stm32x_wait_status_busy(bank, 5);\r
- \r
- if( status & FLASH_WRPRTERR )\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- if( status & FLASH_PGERR )\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
- \r
- target_write_u32(target, STM32_FLASH_CR, FLASH_LOCK);\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int stm32x_probe(struct flash_bank_s *bank)\r
-{\r
- target_t *target = bank->target;\r
- stm32x_flash_bank_t *stm32x_info = bank->driver_priv;\r
- int i;\r
- u16 num_sectors;\r
- u32 device_id;\r
- \r
- stm32x_info->probed = 0;\r
- \r
- /* read stm32 device id register */\r
- target_read_u32(target, 0xE0042000, &device_id);\r
- INFO( "device id = 0x%08x", device_id );\r
- \r
- if (!(device_id & 0x410))\r
- {\r
- WARNING( "Cannot identify target as a STM32 family." );\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
- \r
- /* get flash size from target */\r
- target_read_u16(target, 0x1FFFF7E0, &num_sectors);\r
- \r
- /* check for early silicon rev A */\r
- if ((device_id >> 16) == 0 )\r
- {\r
- /* number of sectors incorrect on revA */\r
- WARNING( "STM32 Rev A Silicon detected, probe inaccurate - assuming 128k flash" );\r
- num_sectors = 128;\r
- }\r
- \r
- INFO( "flash size = %dkbytes", num_sectors );\r
- \r
- bank->base = 0x08000000;\r
- bank->size = num_sectors * 1024;\r
- bank->num_sectors = num_sectors;\r
- bank->sectors = malloc(sizeof(flash_sector_t) * num_sectors);\r
- \r
- for (i = 0; i < num_sectors; i++)\r
- {\r
- bank->sectors[i].offset = i * 1024;\r
- bank->sectors[i].size = 1024;\r
- bank->sectors[i].is_erased = -1;\r
- bank->sectors[i].is_protected = 1;\r
- }\r
- \r
- stm32x_info->probed = 1;\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int stm32x_auto_probe(struct flash_bank_s *bank)\r
-{\r
- stm32x_flash_bank_t *stm32x_info = bank->driver_priv;\r
- if (stm32x_info->probed)\r
- return ERROR_OK;\r
- return stm32x_probe(bank);\r
-}\r
-\r
-int stm32x_handle_part_id_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- return ERROR_OK;\r
-}\r
-\r
-int stm32x_erase_check(struct flash_bank_s *bank)\r
-{\r
- return stm32x_blank_check(bank, 0, bank->num_sectors - 1);\r
-}\r
-\r
-int stm32x_info(struct flash_bank_s *bank, char *buf, int buf_size)\r
-{\r
- snprintf(buf, buf_size, "stm32x flash driver info" );\r
- return ERROR_OK;\r
-}\r
-\r
-int stm32x_handle_lock_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- flash_bank_t *bank;\r
- target_t *target = NULL;\r
- stm32x_flash_bank_t *stm32x_info = NULL;\r
- \r
- if (argc < 1)\r
- {\r
- command_print(cmd_ctx, "stm32x lock <bank>");\r
- return ERROR_OK; \r
- }\r
- \r
- bank = get_flash_bank_by_num(strtoul(args[0], NULL, 0));\r
- if (!bank)\r
- {\r
- command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);\r
- return ERROR_OK;\r
- }\r
- \r
- stm32x_info = bank->driver_priv;\r
- \r
- target = bank->target;\r
- \r
- if (target->state != TARGET_HALTED)\r
- {\r
- return ERROR_TARGET_NOT_HALTED;\r
- }\r
- \r
- if (stm32x_erase_options(bank) != ERROR_OK)\r
- {\r
- command_print(cmd_ctx, "stm32x failed to erase options");\r
- return ERROR_OK;\r
- }\r
- \r
- /* set readout protection */ \r
- stm32x_info->option_bytes.RDP = 0;\r
- \r
- if (stm32x_write_options(bank) != ERROR_OK)\r
- {\r
- command_print(cmd_ctx, "stm32x failed to lock device");\r
- return ERROR_OK;\r
- }\r
- \r
- command_print(cmd_ctx, "stm32x locked");\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int stm32x_handle_unlock_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- flash_bank_t *bank;\r
- target_t *target = NULL;\r
- stm32x_flash_bank_t *stm32x_info = NULL;\r
- \r
- if (argc < 1)\r
- {\r
- command_print(cmd_ctx, "stm32x unlock <bank>");\r
- return ERROR_OK; \r
- }\r
- \r
- bank = get_flash_bank_by_num(strtoul(args[0], NULL, 0));\r
- if (!bank)\r
- {\r
- command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);\r
- return ERROR_OK;\r
- }\r
- \r
- stm32x_info = bank->driver_priv;\r
- \r
- target = bank->target;\r
- \r
- if (target->state != TARGET_HALTED)\r
- {\r
- return ERROR_TARGET_NOT_HALTED;\r
- }\r
- \r
- if (stm32x_erase_options(bank) != ERROR_OK)\r
- {\r
- command_print(cmd_ctx, "stm32x failed to unlock device");\r
- return ERROR_OK;\r
- }\r
- \r
- if (stm32x_write_options(bank) != ERROR_OK)\r
- {\r
- command_print(cmd_ctx, "stm32x failed to lock device");\r
- return ERROR_OK;\r
- }\r
- \r
- command_print(cmd_ctx, "stm32x unlocked");\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int stm32x_handle_options_read_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- flash_bank_t *bank;\r
- u32 optionbyte;\r
- target_t *target = NULL;\r
- stm32x_flash_bank_t *stm32x_info = NULL;\r
- \r
- if (argc < 1)\r
- {\r
- command_print(cmd_ctx, "stm32x options_read <bank>");\r
- return ERROR_OK; \r
- }\r
- \r
- bank = get_flash_bank_by_num(strtoul(args[0], NULL, 0));\r
- if (!bank)\r
- {\r
- command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);\r
- return ERROR_OK;\r
- }\r
- \r
- stm32x_info = bank->driver_priv;\r
- \r
- target = bank->target;\r
- \r
- if (target->state != TARGET_HALTED)\r
- {\r
- return ERROR_TARGET_NOT_HALTED;\r
- }\r
- \r
- target_read_u32(target, STM32_FLASH_OBR, &optionbyte);\r
- command_print(cmd_ctx, "Option Byte: 0x%x", optionbyte);\r
- \r
- if (buf_get_u32((u8*)&optionbyte, OPT_ERROR, 1))\r
- command_print(cmd_ctx, "Option Byte Complement Error");\r
- \r
- if (buf_get_u32((u8*)&optionbyte, OPT_READOUT, 1))\r
- command_print(cmd_ctx, "Readout Protection On");\r
- else\r
- command_print(cmd_ctx, "Readout Protection Off");\r
- \r
- if (buf_get_u32((u8*)&optionbyte, OPT_RDWDGSW, 1))\r
- command_print(cmd_ctx, "Software Watchdog");\r
- else\r
- command_print(cmd_ctx, "Hardware Watchdog");\r
- \r
- if (buf_get_u32((u8*)&optionbyte, OPT_RDRSTSTOP, 1))\r
- command_print(cmd_ctx, "Stop: No reset generated");\r
- else\r
- command_print(cmd_ctx, "Stop: Reset generated");\r
- \r
- if (buf_get_u32((u8*)&optionbyte, OPT_RDRSTSTDBY, 1))\r
- command_print(cmd_ctx, "Standby: No reset generated");\r
- else\r
- command_print(cmd_ctx, "Standby: Reset generated");\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int stm32x_handle_options_write_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- flash_bank_t *bank;\r
- target_t *target = NULL;\r
- stm32x_flash_bank_t *stm32x_info = NULL;\r
- u16 optionbyte = 0xF8;\r
- \r
- if (argc < 4)\r
- {\r
- command_print(cmd_ctx, "stm32x options_write <bank> <SWWDG|HWWDG> <RSTSTNDBY|NORSTSTNDBY> <RSTSTOP|NORSTSTOP>");\r
- return ERROR_OK; \r
- }\r
- \r
- bank = get_flash_bank_by_num(strtoul(args[0], NULL, 0));\r
- if (!bank)\r
- {\r
- command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);\r
- return ERROR_OK;\r
- }\r
- \r
- stm32x_info = bank->driver_priv;\r
- \r
- target = bank->target;\r
- \r
- if (target->state != TARGET_HALTED)\r
- {\r
- return ERROR_TARGET_NOT_HALTED;\r
- }\r
- \r
- if (strcmp(args[1], "SWWDG") == 0)\r
- {\r
- optionbyte |= (1<<0);\r
- }\r
- else\r
- {\r
- optionbyte &= ~(1<<0);\r
- }\r
- \r
- if (strcmp(args[2], "NORSTSTNDBY") == 0)\r
- {\r
- optionbyte |= (1<<1);\r
- }\r
- else\r
- {\r
- optionbyte &= ~(1<<1);\r
- }\r
- \r
- if (strcmp(args[3], "NORSTSTOP") == 0)\r
- {\r
- optionbyte |= (1<<2);\r
- }\r
- else\r
- {\r
- optionbyte &= ~(1<<2);\r
- }\r
- \r
- if (stm32x_erase_options(bank) != ERROR_OK)\r
- {\r
- command_print(cmd_ctx, "stm32x failed to erase options");\r
- return ERROR_OK;\r
- }\r
- \r
- stm32x_info->option_bytes.user_options = optionbyte;\r
- \r
- if (stm32x_write_options(bank) != ERROR_OK)\r
- {\r
- command_print(cmd_ctx, "stm32x failed to write options");\r
- return ERROR_OK;\r
- }\r
- \r
- command_print(cmd_ctx, "stm32x write options complete");\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int stm32x_handle_mass_erase_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- target_t *target = NULL;\r
- stm32x_flash_bank_t *stm32x_info = NULL;\r
- flash_bank_t *bank;\r
- u32 status;\r
- \r
- if (argc < 1)\r
- {\r
- command_print(cmd_ctx, "stm32x mass_erase <bank>");\r
- return ERROR_OK; \r
- }\r
- \r
- bank = get_flash_bank_by_num(strtoul(args[0], NULL, 0));\r
- if (!bank)\r
- {\r
- command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);\r
- return ERROR_OK;\r
- }\r
- \r
- stm32x_info = bank->driver_priv;\r
- \r
- target = bank->target;\r
- \r
- if (target->state != TARGET_HALTED)\r
- {\r
- return ERROR_TARGET_NOT_HALTED;\r
- }\r
- \r
- /* unlock option flash registers */\r
- target_write_u32(target, STM32_FLASH_KEYR, KEY1);\r
- target_write_u32(target, STM32_FLASH_KEYR, KEY2);\r
- \r
- /* mass erase flash memory */\r
- target_write_u32(target, STM32_FLASH_CR, FLASH_MER);\r
- target_write_u32(target, STM32_FLASH_CR, FLASH_MER|FLASH_STRT);\r
- \r
- status = stm32x_wait_status_busy(bank, 10);\r
- \r
- target_write_u32(target, STM32_FLASH_CR, FLASH_LOCK);\r
- \r
- if( status & FLASH_WRPRTERR )\r
- {\r
- command_print(cmd_ctx, "stm32x device protected");\r
- return ERROR_OK;\r
- }\r
- \r
- if( status & FLASH_PGERR )\r
- {\r
- command_print(cmd_ctx, "stm32x device programming failed");\r
- return ERROR_OK;\r
- }\r
- \r
- command_print(cmd_ctx, "stm32x mass erase complete");\r
- \r
- return ERROR_OK;\r
-}\r
+/***************************************************************************
+ * Copyright (C) 2005 by Dominic Rath *
+ * Dominic.Rath@gmx.de *
+ * *
+ * 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 "replacements.h"
+
+#include "stm32x.h"
+#include "flash.h"
+#include "target.h"
+#include "log.h"
+#include "armv7m.h"
+#include "algorithm.h"
+#include "binarybuffer.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+int stm32x_register_commands(struct command_context_s *cmd_ctx);
+int stm32x_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank);
+int stm32x_erase(struct flash_bank_s *bank, int first, int last);
+int stm32x_protect(struct flash_bank_s *bank, int set, int first, int last);
+int stm32x_write(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count);
+int stm32x_probe(struct flash_bank_s *bank);
+int stm32x_auto_probe(struct flash_bank_s *bank);
+int stm32x_handle_part_id_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+int stm32x_protect_check(struct flash_bank_s *bank);
+int stm32x_erase_check(struct flash_bank_s *bank);
+int stm32x_info(struct flash_bank_s *bank, char *buf, int buf_size);
+
+int stm32x_handle_lock_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+int stm32x_handle_unlock_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+int stm32x_handle_options_read_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+int stm32x_handle_options_write_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+int stm32x_handle_mass_erase_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+
+flash_driver_t stm32x_flash =
+{
+ .name = "stm32x",
+ .register_commands = stm32x_register_commands,
+ .flash_bank_command = stm32x_flash_bank_command,
+ .erase = stm32x_erase,
+ .protect = stm32x_protect,
+ .write = stm32x_write,
+ .probe = stm32x_probe,
+ .auto_probe = stm32x_auto_probe,
+ .erase_check = stm32x_erase_check,
+ .protect_check = stm32x_protect_check,
+ .info = stm32x_info
+};
+
+int stm32x_register_commands(struct command_context_s *cmd_ctx)
+{
+ command_t *stm32x_cmd = register_command(cmd_ctx, NULL, "stm32x", NULL, COMMAND_ANY, "stm32x flash specific commands");
+
+ register_command(cmd_ctx, stm32x_cmd, "lock", stm32x_handle_lock_command, COMMAND_EXEC,
+ "lock device");
+ register_command(cmd_ctx, stm32x_cmd, "unlock", stm32x_handle_unlock_command, COMMAND_EXEC,
+ "unlock protected device");
+ register_command(cmd_ctx, stm32x_cmd, "mass_erase", stm32x_handle_mass_erase_command, COMMAND_EXEC,
+ "mass erase device");
+ register_command(cmd_ctx, stm32x_cmd, "options_read", stm32x_handle_options_read_command, COMMAND_EXEC,
+ "read device option bytes");
+ register_command(cmd_ctx, stm32x_cmd, "options_write", stm32x_handle_options_write_command, COMMAND_EXEC,
+ "write device option bytes");
+ return ERROR_OK;
+}
+
+/* flash bank stm32x <base> <size> 0 0 <target#>
+ */
+int stm32x_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank)
+{
+ stm32x_flash_bank_t *stm32x_info;
+
+ if (argc < 6)
+ {
+ WARNING("incomplete flash_bank stm32x configuration");
+ return ERROR_FLASH_BANK_INVALID;
+ }
+
+ stm32x_info = malloc(sizeof(stm32x_flash_bank_t));
+ bank->driver_priv = stm32x_info;
+
+ stm32x_info->write_algorithm = NULL;
+ stm32x_info->probed = 0;
+
+ return ERROR_OK;
+}
+
+u32 stm32x_get_flash_status(flash_bank_t *bank)
+{
+ target_t *target = bank->target;
+ u32 status;
+
+ target_read_u32(target, STM32_FLASH_SR, &status);
+
+ return status;
+}
+
+u32 stm32x_wait_status_busy(flash_bank_t *bank, int timeout)
+{
+ u32 status;
+
+ /* wait for busy to clear */
+ while (((status = stm32x_get_flash_status(bank)) & FLASH_BSY) && (timeout-- > 0))
+ {
+ DEBUG("status: 0x%x", status);
+ usleep(1000);
+ }
+
+ return status;
+}
+
+int stm32x_read_options(struct flash_bank_s *bank)
+{
+ u32 optiondata;
+ stm32x_flash_bank_t *stm32x_info = NULL;
+ target_t *target = bank->target;
+
+ stm32x_info = bank->driver_priv;
+
+ /* read current option bytes */
+ target_read_u32(target, STM32_FLASH_OBR, &optiondata);
+
+ stm32x_info->option_bytes.user_options = (u16)0xFFF8|((optiondata >> 2) & 0x07);
+ stm32x_info->option_bytes.RDP = (optiondata & (1 << OPT_READOUT)) ? 0xFFFF : 0x5AA5;
+
+ if (optiondata & (1 << OPT_READOUT))
+ INFO("Device Security Bit Set");
+
+ /* each bit refers to a 4bank protection */
+ target_read_u32(target, STM32_FLASH_WRPR, &optiondata);
+
+ stm32x_info->option_bytes.protection[0] = (u16)optiondata;
+ stm32x_info->option_bytes.protection[1] = (u16)(optiondata >> 8);
+ stm32x_info->option_bytes.protection[2] = (u16)(optiondata >> 16);
+ stm32x_info->option_bytes.protection[3] = (u16)(optiondata >> 24);
+
+ return ERROR_OK;
+}
+
+int stm32x_erase_options(struct flash_bank_s *bank)
+{
+ stm32x_flash_bank_t *stm32x_info = NULL;
+ target_t *target = bank->target;
+ u32 status;
+
+ stm32x_info = bank->driver_priv;
+
+ /* read current options */
+ stm32x_read_options(bank);
+
+ /* unlock flash registers */
+ target_write_u32(target, STM32_FLASH_KEYR, KEY1);
+ target_write_u32(target, STM32_FLASH_KEYR, KEY2);
+
+ /* unlock option flash registers */
+ target_write_u32(target, STM32_FLASH_OPTKEYR, KEY1);
+ target_write_u32(target, STM32_FLASH_OPTKEYR, KEY2);
+
+ /* erase option bytes */
+ target_write_u32(target, STM32_FLASH_CR, FLASH_OPTER|FLASH_OPTWRE);
+ target_write_u32(target, STM32_FLASH_CR, FLASH_OPTER|FLASH_STRT|FLASH_OPTWRE);
+
+ status = stm32x_wait_status_busy(bank, 10);
+
+ if( status & FLASH_WRPRTERR )
+ return ERROR_FLASH_OPERATION_FAILED;
+ if( status & FLASH_PGERR )
+ return ERROR_FLASH_OPERATION_FAILED;
+
+ /* clear readout protection and complementary option bytes
+ * this will also force a device unlock if set */
+ stm32x_info->option_bytes.RDP = 0x5AA5;
+
+ return ERROR_OK;
+}
+
+int stm32x_write_options(struct flash_bank_s *bank)
+{
+ stm32x_flash_bank_t *stm32x_info = NULL;
+ target_t *target = bank->target;
+ u32 status;
+
+ stm32x_info = bank->driver_priv;
+
+ /* unlock flash registers */
+ target_write_u32(target, STM32_FLASH_KEYR, KEY1);
+ target_write_u32(target, STM32_FLASH_KEYR, KEY2);
+
+ /* unlock option flash registers */
+ target_write_u32(target, STM32_FLASH_OPTKEYR, KEY1);
+ target_write_u32(target, STM32_FLASH_OPTKEYR, KEY2);
+
+ /* program option bytes */
+ target_write_u32(target, STM32_FLASH_CR, FLASH_OPTPG|FLASH_OPTWRE);
+
+ /* write user option byte */
+ target_write_u16(target, STM32_OB_USER, stm32x_info->option_bytes.user_options);
+
+ status = stm32x_wait_status_busy(bank, 10);
+
+ if( status & FLASH_WRPRTERR )
+ return ERROR_FLASH_OPERATION_FAILED;
+ if( status & FLASH_PGERR )
+ return ERROR_FLASH_OPERATION_FAILED;
+
+ /* write protection byte 1 */
+ target_write_u16(target, STM32_OB_WRP0, stm32x_info->option_bytes.protection[0]);
+
+ status = stm32x_wait_status_busy(bank, 10);
+
+ if( status & FLASH_WRPRTERR )
+ return ERROR_FLASH_OPERATION_FAILED;
+ if( status & FLASH_PGERR )
+ return ERROR_FLASH_OPERATION_FAILED;
+
+ /* write protection byte 2 */
+ target_write_u16(target, STM32_OB_WRP1, stm32x_info->option_bytes.protection[1]);
+
+ status = stm32x_wait_status_busy(bank, 10);
+
+ if( status & FLASH_WRPRTERR )
+ return ERROR_FLASH_OPERATION_FAILED;
+ if( status & FLASH_PGERR )
+ return ERROR_FLASH_OPERATION_FAILED;
+
+ /* write protection byte 3 */
+ target_write_u16(target, STM32_OB_WRP2, stm32x_info->option_bytes.protection[2]);
+
+ status = stm32x_wait_status_busy(bank, 10);
+
+ if( status & FLASH_WRPRTERR )
+ return ERROR_FLASH_OPERATION_FAILED;
+ if( status & FLASH_PGERR )
+ return ERROR_FLASH_OPERATION_FAILED;
+
+ /* write protection byte 4 */
+ target_write_u16(target, STM32_OB_WRP3, stm32x_info->option_bytes.protection[3]);
+
+ status = stm32x_wait_status_busy(bank, 10);
+
+ if( status & FLASH_WRPRTERR )
+ return ERROR_FLASH_OPERATION_FAILED;
+ if( status & FLASH_PGERR )
+ return ERROR_FLASH_OPERATION_FAILED;
+
+ /* write readout protection bit */
+ target_write_u16(target, STM32_OB_RDP, stm32x_info->option_bytes.RDP);
+
+ status = stm32x_wait_status_busy(bank, 10);
+
+ if( status & FLASH_WRPRTERR )
+ return ERROR_FLASH_OPERATION_FAILED;
+ if( status & FLASH_PGERR )
+ return ERROR_FLASH_OPERATION_FAILED;
+
+ target_write_u32(target, STM32_FLASH_CR, FLASH_LOCK);
+
+ return ERROR_OK;
+}
+
+int stm32x_blank_check(struct flash_bank_s *bank, int first, int last)
+{
+ target_t *target = bank->target;
+ u8 *buffer;
+ int i;
+ int nBytes;
+
+ if ((first < 0) || (last > bank->num_sectors))
+ return ERROR_FLASH_SECTOR_INVALID;
+
+ if (target->state != TARGET_HALTED)
+ {
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ buffer = malloc(256);
+
+ for (i = first; i <= last; i++)
+ {
+ bank->sectors[i].is_erased = 1;
+
+ target->type->read_memory(target, bank->base + bank->sectors[i].offset, 4, 256/4, buffer);
+
+ for (nBytes = 0; nBytes < 256; nBytes++)
+ {
+ if (buffer[nBytes] != 0xFF)
+ {
+ bank->sectors[i].is_erased = 0;
+ break;
+ }
+ }
+ }
+
+ free(buffer);
+
+ return ERROR_OK;
+}
+
+int stm32x_protect_check(struct flash_bank_s *bank)
+{
+ target_t *target = bank->target;
+
+ u32 protection;
+ int i, s;
+ int num_bits;
+
+ if (target->state != TARGET_HALTED)
+ {
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ /* each bit refers to a 4bank protection */
+ target_read_u32(target, STM32_FLASH_WRPR, &protection);
+
+ /* each protection bit is for 4 1K pages */
+ num_bits = (bank->num_sectors / 4);
+
+ for (i = 0; i < num_bits; i++)
+ {
+ int set = 1;
+
+ if( protection & (1 << i))
+ set = 0;
+
+ for (s = 0; s < 4; s++)
+ bank->sectors[(i * 4) + s].is_protected = set;
+ }
+
+ return ERROR_OK;
+}
+
+int stm32x_erase(struct flash_bank_s *bank, int first, int last)
+{
+ target_t *target = bank->target;
+
+ int i;
+ u32 status;
+
+ /* unlock flash registers */
+ target_write_u32(target, STM32_FLASH_KEYR, KEY1);
+ target_write_u32(target, STM32_FLASH_KEYR, KEY2);
+
+ for (i = first; i <= last; i++)
+ {
+ target_write_u32(target, STM32_FLASH_CR, FLASH_PER);
+ target_write_u32(target, STM32_FLASH_AR, bank->base + bank->sectors[i].offset);
+ target_write_u32(target, STM32_FLASH_CR, FLASH_PER|FLASH_STRT);
+
+ status = stm32x_wait_status_busy(bank, 10);
+
+ if( status & FLASH_WRPRTERR )
+ return ERROR_FLASH_OPERATION_FAILED;
+ if( status & FLASH_PGERR )
+ return ERROR_FLASH_OPERATION_FAILED;
+ bank->sectors[i].is_erased = 1;
+ }
+
+ target_write_u32(target, STM32_FLASH_CR, FLASH_LOCK);
+
+ return ERROR_OK;
+}
+
+int stm32x_protect(struct flash_bank_s *bank, int set, int first, int last)
+{
+ stm32x_flash_bank_t *stm32x_info = NULL;
+ target_t *target = bank->target;
+ u16 prot_reg[4] = {0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF};
+ int i, reg, bit;
+ int status;
+ u32 protection;
+
+ stm32x_info = bank->driver_priv;
+
+ if (target->state != TARGET_HALTED)
+ {
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ if ((first && (first % 4)) || ((last + 1) && (last + 1) % 4))
+ {
+ WARNING("sector start/end incorrect - stm32 has 4K sector protection");
+ return ERROR_FLASH_SECTOR_INVALID;
+ }
+
+ /* each bit refers to a 4bank protection */
+ target_read_u32(target, STM32_FLASH_WRPR, &protection);
+
+ prot_reg[0] = (u16)protection;
+ prot_reg[1] = (u16)(protection >> 8);
+ prot_reg[2] = (u16)(protection >> 16);
+ prot_reg[3] = (u16)(protection >> 24);
+
+ for (i = first; i <= last; i++)
+ {
+ reg = (i / 4) / 8;
+ bit = (i / 4) - (reg * 8);
+
+ if( set )
+ prot_reg[reg] &= ~(1 << bit);
+ else
+ prot_reg[reg] |= (1 << bit);
+ }
+
+ if ((status = stm32x_erase_options(bank)) != ERROR_OK)
+ return status;
+
+ stm32x_info->option_bytes.protection[0] = prot_reg[0];
+ stm32x_info->option_bytes.protection[1] = prot_reg[1];
+ stm32x_info->option_bytes.protection[2] = prot_reg[2];
+ stm32x_info->option_bytes.protection[3] = prot_reg[3];
+
+ return stm32x_write_options(bank);
+}
+
+int stm32x_write_block(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count)
+{
+ stm32x_flash_bank_t *stm32x_info = bank->driver_priv;
+ target_t *target = bank->target;
+ u32 buffer_size = 8192;
+ working_area_t *source;
+ u32 address = bank->base + offset;
+ reg_param_t reg_params[4];
+ armv7m_algorithm_t armv7m_info;
+ int retval = ERROR_OK;
+
+ u8 stm32x_flash_write_code[] = {
+ /* write: */
+ 0xDF, 0xF8, 0x24, 0x40, /* ldr r4, STM32_FLASH_CR */
+ 0x09, 0x4D, /* ldr r5, STM32_FLASH_SR */
+ 0x4F, 0xF0, 0x01, 0x03, /* mov r3, #1 */
+ 0x23, 0x60, /* str r3, [r4, #0] */
+ 0x30, 0xF8, 0x02, 0x3B, /* ldrh r3, [r0], #2 */
+ 0x21, 0xF8, 0x02, 0x3B, /* strh r3, [r1], #2 */
+ /* busy: */
+ 0x2B, 0x68, /* ldr r3, [r5, #0] */
+ 0x13, 0xF0, 0x01, 0x0F, /* tst r3, #0x01 */
+ 0xFB, 0xD0, /* beq busy */
+ 0x13, 0xF0, 0x14, 0x0F, /* tst r3, #0x14 */
+ 0x01, 0xD1, /* bne exit */
+ 0x01, 0x3A, /* subs r2, r2, #1 */
+ 0xED, 0xD1, /* bne write */
+ /* exit: */
+ 0xFE, 0xE7, /* b exit */
+ 0x10, 0x20, 0x02, 0x40, /* STM32_FLASH_CR: .word 0x40022010 */
+ 0x0C, 0x20, 0x02, 0x40 /* STM32_FLASH_SR: .word 0x4002200C */
+ };
+
+ /* flash write code */
+ if (target_alloc_working_area(target, sizeof(stm32x_flash_write_code), &stm32x_info->write_algorithm) != ERROR_OK)
+ {
+ WARNING("no working area available, can't do block memory writes");
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ };
+
+ target_write_buffer(target, stm32x_info->write_algorithm->address, sizeof(stm32x_flash_write_code), stm32x_flash_write_code);
+
+ /* memory buffer */
+ while (target_alloc_working_area(target, buffer_size, &source) != ERROR_OK)
+ {
+ buffer_size /= 2;
+ if (buffer_size <= 256)
+ {
+ /* if 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);
+
+ WARNING("no large enough working area available, can't do block memory writes");
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+ };
+
+ armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
+ armv7m_info.core_mode = ARMV7M_MODE_ANY;
+ armv7m_info.core_state = ARMV7M_STATE_THUMB;
+
+ init_reg_param(®_params[0], "r0", 32, PARAM_OUT);
+ init_reg_param(®_params[1], "r1", 32, PARAM_OUT);
+ init_reg_param(®_params[2], "r2", 32, PARAM_OUT);
+ init_reg_param(®_params[3], "r3", 32, PARAM_IN);
+
+ while (count > 0)
+ {
+ u32 thisrun_count = (count > (buffer_size / 2)) ? (buffer_size / 2) : count;
+
+ target_write_buffer(target, source->address, thisrun_count * 2, buffer);
+
+ buf_set_u32(reg_params[0].value, 0, 32, source->address);
+ buf_set_u32(reg_params[1].value, 0, 32, address);
+ buf_set_u32(reg_params[2].value, 0, 32, thisrun_count);
+
+ if ((retval = target->type->run_algorithm(target, 0, NULL, 4, reg_params, stm32x_info->write_algorithm->address, \
+ stm32x_info->write_algorithm->address + (sizeof(stm32x_flash_write_code) - 10), 10000, &armv7m_info)) != ERROR_OK)
+ {
+ ERROR("error executing str7x flash write algorithm");
+ break;
+ }
+
+ if (buf_get_u32(reg_params[3].value, 0, 32) & 0x14)
+ {
+ retval = ERROR_FLASH_OPERATION_FAILED;
+ break;
+ }
+
+ buffer += thisrun_count * 2;
+ address += thisrun_count * 2;
+ count -= thisrun_count;
+ }
+
+ target_free_working_area(target, source);
+ target_free_working_area(target, stm32x_info->write_algorithm);
+
+ destroy_reg_param(®_params[0]);
+ destroy_reg_param(®_params[1]);
+ destroy_reg_param(®_params[2]);
+ destroy_reg_param(®_params[3]);
+
+ return retval;
+}
+
+int stm32x_write(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count)
+{
+ target_t *target = bank->target;
+ u32 words_remaining = (count / 2);
+ u32 bytes_remaining = (count & 0x00000001);
+ u32 address = bank->base + offset;
+ u32 bytes_written = 0;
+ u8 status;
+ u32 retval;
+
+ if (offset & 0x1)
+ {
+ WARNING("offset 0x%x breaks required 2-byte alignment", offset);
+ return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
+ }
+
+ /* unlock flash registers */
+ target_write_u32(target, STM32_FLASH_KEYR, KEY1);
+ target_write_u32(target, STM32_FLASH_KEYR, KEY2);
+
+ /* multiple half words (2-byte) to be programmed? */
+ if (words_remaining > 0)
+ {
+ /* try using a block write */
+ if ((retval = stm32x_write_block(bank, buffer, offset, words_remaining)) != ERROR_OK)
+ {
+ if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE)
+ {
+ /* if block write failed (no sufficient working area),
+ * we use normal (slow) single dword accesses */
+ WARNING("couldn't use block writes, falling back to single memory accesses");
+ }
+ else if (retval == ERROR_FLASH_OPERATION_FAILED)
+ {
+ ERROR("flash writing failed with error code: 0x%x", retval);
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+ }
+ else
+ {
+ buffer += words_remaining * 2;
+ address += words_remaining * 2;
+ words_remaining = 0;
+ }
+ }
+
+ while (words_remaining > 0)
+ {
+ target_write_u32(target, STM32_FLASH_CR, FLASH_PG);
+ target_write_u16(target, address, *(u16*)(buffer + bytes_written));
+
+ status = stm32x_wait_status_busy(bank, 5);
+
+ if( status & FLASH_WRPRTERR )
+ return ERROR_FLASH_OPERATION_FAILED;
+ if( status & FLASH_PGERR )
+ return ERROR_FLASH_OPERATION_FAILED;
+
+ bytes_written += 2;
+ words_remaining--;
+ address += 2;
+ }
+
+ if (bytes_remaining)
+ {
+ u8 last_halfword[2] = {0xff, 0xff};
+ int i = 0;
+
+ while(bytes_remaining > 0)
+ {
+ last_halfword[i++] = *(buffer + bytes_written);
+ bytes_remaining--;
+ bytes_written++;
+ }
+
+ target_write_u32(target, STM32_FLASH_CR, FLASH_PG);
+ target_write_u16(target, address, *(u16*)last_halfword);
+
+ status = stm32x_wait_status_busy(bank, 5);
+
+ if( status & FLASH_WRPRTERR )
+ return ERROR_FLASH_OPERATION_FAILED;
+ if( status & FLASH_PGERR )
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ target_write_u32(target, STM32_FLASH_CR, FLASH_LOCK);
+
+ return ERROR_OK;
+}
+
+int stm32x_probe(struct flash_bank_s *bank)
+{
+ target_t *target = bank->target;
+ stm32x_flash_bank_t *stm32x_info = bank->driver_priv;
+ int i;
+ u16 num_sectors;
+ u32 device_id;
+
+ stm32x_info->probed = 0;
+
+ /* read stm32 device id register */
+ target_read_u32(target, 0xE0042000, &device_id);
+ INFO( "device id = 0x%08x", device_id );
+
+ if (!(device_id & 0x410))
+ {
+ WARNING( "Cannot identify target as a STM32 family." );
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ /* get flash size from target */
+ target_read_u16(target, 0x1FFFF7E0, &num_sectors);
+
+ /* check for early silicon rev A */
+ if ((device_id >> 16) == 0 )
+ {
+ /* number of sectors incorrect on revA */
+ WARNING( "STM32 Rev A Silicon detected, probe inaccurate - assuming 128k flash" );
+ num_sectors = 128;
+ }
+
+ INFO( "flash size = %dkbytes", num_sectors );
+
+ bank->base = 0x08000000;
+ bank->size = num_sectors * 1024;
+ bank->num_sectors = num_sectors;
+ bank->sectors = malloc(sizeof(flash_sector_t) * num_sectors);
+
+ for (i = 0; i < num_sectors; i++)
+ {
+ bank->sectors[i].offset = i * 1024;
+ bank->sectors[i].size = 1024;
+ bank->sectors[i].is_erased = -1;
+ bank->sectors[i].is_protected = 1;
+ }
+
+ stm32x_info->probed = 1;
+
+ return ERROR_OK;
+}
+
+int stm32x_auto_probe(struct flash_bank_s *bank)
+{
+ stm32x_flash_bank_t *stm32x_info = bank->driver_priv;
+ if (stm32x_info->probed)
+ return ERROR_OK;
+ return stm32x_probe(bank);
+}
+
+int stm32x_handle_part_id_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ return ERROR_OK;
+}
+
+int stm32x_erase_check(struct flash_bank_s *bank)
+{
+ return stm32x_blank_check(bank, 0, bank->num_sectors - 1);
+}
+
+int stm32x_info(struct flash_bank_s *bank, char *buf, int buf_size)
+{
+ snprintf(buf, buf_size, "stm32x flash driver info" );
+ return ERROR_OK;
+}
+
+int stm32x_handle_lock_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ flash_bank_t *bank;
+ target_t *target = NULL;
+ stm32x_flash_bank_t *stm32x_info = NULL;
+
+ if (argc < 1)
+ {
+ command_print(cmd_ctx, "stm32x lock <bank>");
+ return ERROR_OK;
+ }
+
+ bank = get_flash_bank_by_num(strtoul(args[0], NULL, 0));
+ if (!bank)
+ {
+ command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);
+ return ERROR_OK;
+ }
+
+ stm32x_info = bank->driver_priv;
+
+ target = bank->target;
+
+ if (target->state != TARGET_HALTED)
+ {
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ if (stm32x_erase_options(bank) != ERROR_OK)
+ {
+ command_print(cmd_ctx, "stm32x failed to erase options");
+ return ERROR_OK;
+ }
+
+ /* set readout protection */
+ stm32x_info->option_bytes.RDP = 0;
+
+ if (stm32x_write_options(bank) != ERROR_OK)
+ {
+ command_print(cmd_ctx, "stm32x failed to lock device");
+ return ERROR_OK;
+ }
+
+ command_print(cmd_ctx, "stm32x locked");
+
+ return ERROR_OK;
+}
+
+int stm32x_handle_unlock_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ flash_bank_t *bank;
+ target_t *target = NULL;
+ stm32x_flash_bank_t *stm32x_info = NULL;
+
+ if (argc < 1)
+ {
+ command_print(cmd_ctx, "stm32x unlock <bank>");
+ return ERROR_OK;
+ }
+
+ bank = get_flash_bank_by_num(strtoul(args[0], NULL, 0));
+ if (!bank)
+ {
+ command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);
+ return ERROR_OK;
+ }
+
+ stm32x_info = bank->driver_priv;
+
+ target = bank->target;
+
+ if (target->state != TARGET_HALTED)
+ {
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ if (stm32x_erase_options(bank) != ERROR_OK)
+ {
+ command_print(cmd_ctx, "stm32x failed to unlock device");
+ return ERROR_OK;
+ }
+
+ if (stm32x_write_options(bank) != ERROR_OK)
+ {
+ command_print(cmd_ctx, "stm32x failed to lock device");
+ return ERROR_OK;
+ }
+
+ command_print(cmd_ctx, "stm32x unlocked");
+
+ return ERROR_OK;
+}
+
+int stm32x_handle_options_read_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ flash_bank_t *bank;
+ u32 optionbyte;
+ target_t *target = NULL;
+ stm32x_flash_bank_t *stm32x_info = NULL;
+
+ if (argc < 1)
+ {
+ command_print(cmd_ctx, "stm32x options_read <bank>");
+ return ERROR_OK;
+ }
+
+ bank = get_flash_bank_by_num(strtoul(args[0], NULL, 0));
+ if (!bank)
+ {
+ command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);
+ return ERROR_OK;
+ }
+
+ stm32x_info = bank->driver_priv;
+
+ target = bank->target;
+
+ if (target->state != TARGET_HALTED)
+ {
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ target_read_u32(target, STM32_FLASH_OBR, &optionbyte);
+ command_print(cmd_ctx, "Option Byte: 0x%x", optionbyte);
+
+ if (buf_get_u32((u8*)&optionbyte, OPT_ERROR, 1))
+ command_print(cmd_ctx, "Option Byte Complement Error");
+
+ if (buf_get_u32((u8*)&optionbyte, OPT_READOUT, 1))
+ command_print(cmd_ctx, "Readout Protection On");
+ else
+ command_print(cmd_ctx, "Readout Protection Off");
+
+ if (buf_get_u32((u8*)&optionbyte, OPT_RDWDGSW, 1))
+ command_print(cmd_ctx, "Software Watchdog");
+ else
+ command_print(cmd_ctx, "Hardware Watchdog");
+
+ if (buf_get_u32((u8*)&optionbyte, OPT_RDRSTSTOP, 1))
+ command_print(cmd_ctx, "Stop: No reset generated");
+ else
+ command_print(cmd_ctx, "Stop: Reset generated");
+
+ if (buf_get_u32((u8*)&optionbyte, OPT_RDRSTSTDBY, 1))
+ command_print(cmd_ctx, "Standby: No reset generated");
+ else
+ command_print(cmd_ctx, "Standby: Reset generated");
+
+ return ERROR_OK;
+}
+
+int stm32x_handle_options_write_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ flash_bank_t *bank;
+ target_t *target = NULL;
+ stm32x_flash_bank_t *stm32x_info = NULL;
+ u16 optionbyte = 0xF8;
+
+ if (argc < 4)
+ {
+ command_print(cmd_ctx, "stm32x options_write <bank> <SWWDG|HWWDG> <RSTSTNDBY|NORSTSTNDBY> <RSTSTOP|NORSTSTOP>");
+ return ERROR_OK;
+ }
+
+ bank = get_flash_bank_by_num(strtoul(args[0], NULL, 0));
+ if (!bank)
+ {
+ command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);
+ return ERROR_OK;
+ }
+
+ stm32x_info = bank->driver_priv;
+
+ target = bank->target;
+
+ if (target->state != TARGET_HALTED)
+ {
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ if (strcmp(args[1], "SWWDG") == 0)
+ {
+ optionbyte |= (1<<0);
+ }
+ else
+ {
+ optionbyte &= ~(1<<0);
+ }
+
+ if (strcmp(args[2], "NORSTSTNDBY") == 0)
+ {
+ optionbyte |= (1<<1);
+ }
+ else
+ {
+ optionbyte &= ~(1<<1);
+ }
+
+ if (strcmp(args[3], "NORSTSTOP") == 0)
+ {
+ optionbyte |= (1<<2);
+ }
+ else
+ {
+ optionbyte &= ~(1<<2);
+ }
+
+ if (stm32x_erase_options(bank) != ERROR_OK)
+ {
+ command_print(cmd_ctx, "stm32x failed to erase options");
+ return ERROR_OK;
+ }
+
+ stm32x_info->option_bytes.user_options = optionbyte;
+
+ if (stm32x_write_options(bank) != ERROR_OK)
+ {
+ command_print(cmd_ctx, "stm32x failed to write options");
+ return ERROR_OK;
+ }
+
+ command_print(cmd_ctx, "stm32x write options complete");
+
+ return ERROR_OK;
+}
+
+int stm32x_handle_mass_erase_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ target_t *target = NULL;
+ stm32x_flash_bank_t *stm32x_info = NULL;
+ flash_bank_t *bank;
+ u32 status;
+
+ if (argc < 1)
+ {
+ command_print(cmd_ctx, "stm32x mass_erase <bank>");
+ return ERROR_OK;
+ }
+
+ bank = get_flash_bank_by_num(strtoul(args[0], NULL, 0));
+ if (!bank)
+ {
+ command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);
+ return ERROR_OK;
+ }
+
+ stm32x_info = bank->driver_priv;
+
+ target = bank->target;
+
+ if (target->state != TARGET_HALTED)
+ {
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ /* unlock option flash registers */
+ target_write_u32(target, STM32_FLASH_KEYR, KEY1);
+ target_write_u32(target, STM32_FLASH_KEYR, KEY2);
+
+ /* mass erase flash memory */
+ target_write_u32(target, STM32_FLASH_CR, FLASH_MER);
+ target_write_u32(target, STM32_FLASH_CR, FLASH_MER|FLASH_STRT);
+
+ status = stm32x_wait_status_busy(bank, 10);
+
+ target_write_u32(target, STM32_FLASH_CR, FLASH_LOCK);
+
+ if( status & FLASH_WRPRTERR )
+ {
+ command_print(cmd_ctx, "stm32x device protected");
+ return ERROR_OK;
+ }
+
+ if( status & FLASH_PGERR )
+ {
+ command_print(cmd_ctx, "stm32x device programming failed");
+ return ERROR_OK;
+ }
+
+ command_print(cmd_ctx, "stm32x mass erase complete");
+
+ return ERROR_OK;
+}
-/***************************************************************************\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 "replacements.h"\r
-\r
-#include "str7x.h"\r
-#include "flash.h"\r
-#include "target.h"\r
-#include "log.h"\r
-#include "armv4_5.h"\r
-#include "algorithm.h"\r
-#include "binarybuffer.h"\r
-\r
-#include <stdlib.h>\r
-#include <string.h>\r
-#include <unistd.h>\r
-\r
-str7x_mem_layout_t mem_layout[] = {\r
- {0x00000000, 0x02000, 0x01},\r
- {0x00002000, 0x02000, 0x02},\r
- {0x00004000, 0x02000, 0x04},\r
- {0x00006000, 0x02000, 0x08},\r
- {0x00008000, 0x08000, 0x10},\r
- {0x00010000, 0x10000, 0x20},\r
- {0x00020000, 0x10000, 0x40},\r
- {0x00030000, 0x10000, 0x80},\r
- {0x000C0000, 0x02000, 0x10000},\r
- {0x000C2000, 0x02000, 0x20000},\r
-};\r
-\r
-int str7x_register_commands(struct command_context_s *cmd_ctx);\r
-int str7x_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank);\r
-int str7x_erase(struct flash_bank_s *bank, int first, int last);\r
-int str7x_protect(struct flash_bank_s *bank, int set, int first, int last);\r
-int str7x_write(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count);\r
-int str7x_probe(struct flash_bank_s *bank);\r
-int str7x_handle_part_id_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-int str7x_protect_check(struct flash_bank_s *bank);\r
-int str7x_erase_check(struct flash_bank_s *bank);\r
-int str7x_info(struct flash_bank_s *bank, char *buf, int buf_size);\r
-\r
-int str7x_handle_disable_jtag_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-\r
-flash_driver_t str7x_flash =\r
-{\r
- .name = "str7x",\r
- .register_commands = str7x_register_commands,\r
- .flash_bank_command = str7x_flash_bank_command,\r
- .erase = str7x_erase,\r
- .protect = str7x_protect,\r
- .write = str7x_write,\r
- .probe = str7x_probe,\r
- .auto_probe = str7x_probe,\r
- .erase_check = str7x_erase_check,\r
- .protect_check = str7x_protect_check,\r
- .info = str7x_info\r
-};\r
-\r
-int str7x_register_commands(struct command_context_s *cmd_ctx)\r
-{\r
- command_t *str7x_cmd = register_command(cmd_ctx, NULL, "str7x", NULL, COMMAND_ANY, NULL);\r
- \r
- register_command(cmd_ctx, str7x_cmd, "disable_jtag", str7x_handle_disable_jtag_command, COMMAND_EXEC,\r
- "disable jtag access");\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int str7x_get_flash_adr(struct flash_bank_s *bank, u32 reg)\r
-{\r
- return (bank->base | reg);\r
-}\r
-\r
-int str7x_build_block_list(struct flash_bank_s *bank)\r
-{\r
- str7x_flash_bank_t *str7x_info = bank->driver_priv;\r
-\r
- int i;\r
- int num_sectors = 0, b0_sectors = 0, b1_sectors = 0;\r
- \r
- switch (bank->size)\r
- {\r
- case 16 * 1024:\r
- b0_sectors = 2;\r
- break;\r
- case 64 * 1024:\r
- b0_sectors = 5;\r
- break;\r
- case 128 * 1024:\r
- b0_sectors = 6;\r
- break;\r
- case 256 * 1024:\r
- b0_sectors = 8;\r
- break;\r
- default:\r
- ERROR("BUG: unknown bank->size encountered");\r
- exit(-1);\r
- }\r
- \r
- if( str7x_info->bank1 == 1 )\r
- {\r
- b1_sectors += 2;\r
- }\r
- \r
- num_sectors = b0_sectors + b1_sectors;\r
- \r
- bank->num_sectors = num_sectors;\r
- bank->sectors = malloc(sizeof(flash_sector_t) * num_sectors);\r
- str7x_info->sector_bits = malloc(sizeof(u32) * num_sectors);\r
- str7x_info->sector_bank = malloc(sizeof(u32) * num_sectors);\r
- \r
- num_sectors = 0;\r
- \r
- for (i = 0; i < b0_sectors; i++)\r
- {\r
- bank->sectors[num_sectors].offset = mem_layout[i].sector_start;\r
- bank->sectors[num_sectors].size = mem_layout[i].sector_size;\r
- bank->sectors[num_sectors].is_erased = -1;\r
- bank->sectors[num_sectors].is_protected = 1;\r
- str7x_info->sector_bank[num_sectors] = 0;\r
- str7x_info->sector_bits[num_sectors++] = mem_layout[i].sector_bit;\r
- }\r
- \r
- if (b1_sectors)\r
- {\r
- for (i = 8; i < 10; i++)\r
- {\r
- bank->sectors[num_sectors].offset = mem_layout[i].sector_start;\r
- bank->sectors[num_sectors].size = mem_layout[i].sector_size;\r
- bank->sectors[num_sectors].is_erased = -1;\r
- bank->sectors[num_sectors].is_protected = 1;\r
- str7x_info->sector_bank[num_sectors] = 1;\r
- str7x_info->sector_bits[num_sectors++] = mem_layout[i].sector_bit;\r
- }\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-/* flash bank str7x <base> <size> 0 0 <target#> <str71_variant>\r
- */\r
-int str7x_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank)\r
-{\r
- str7x_flash_bank_t *str7x_info;\r
- \r
- if (argc < 7)\r
- {\r
- WARNING("incomplete flash_bank str7x configuration");\r
- return ERROR_FLASH_BANK_INVALID;\r
- }\r
- \r
- str7x_info = malloc(sizeof(str7x_flash_bank_t));\r
- bank->driver_priv = str7x_info;\r
- \r
- /* set default bits for str71x flash */\r
- str7x_info->bank1 = 1;\r
- str7x_info->busy_bits = (FLASH_LOCK|FLASH_BSYA1|FLASH_BSYA0);\r
- str7x_info->disable_bit = (1<<1);\r
- \r
- if (strcmp(args[6], "STR71x") == 0)\r
- {\r
- if (bank->base != 0x40000000)\r
- {\r
- WARNING("overriding flash base address for STR71x device with 0x40000000");\r
- bank->base = 0x40000000;\r
- }\r
- }\r
- else if (strcmp(args[6], "STR73x") == 0)\r
- {\r
- str7x_info->bank1 = 0;\r
- str7x_info->busy_bits = (FLASH_LOCK|FLASH_BSYA0);\r
- \r
- if (bank->base != 0x80000000)\r
- {\r
- WARNING("overriding flash base address for STR73x device with 0x80000000");\r
- bank->base = 0x80000000;\r
- }\r
- }\r
- else if (strcmp(args[6], "STR75x") == 0)\r
- {\r
- str7x_info->disable_bit = (1<<0);\r
- \r
- if (bank->base != 0x20000000)\r
- {\r
- WARNING("overriding flash base address for STR75x device with 0x20000000");\r
- bank->base = 0x20000000;\r
- }\r
- }\r
- else\r
- {\r
- ERROR("unknown STR7x variant: '%s'", args[6]);\r
- free(str7x_info);\r
- return ERROR_FLASH_BANK_INVALID;\r
- }\r
-\r
- str7x_build_block_list(bank);\r
- \r
- str7x_info->write_algorithm = NULL;\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-u32 str7x_status(struct flash_bank_s *bank)\r
-{\r
- target_t *target = bank->target;\r
- u32 retval;\r
-\r
- target_read_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), &retval);\r
-\r
- return retval;\r
-}\r
-\r
-u32 str7x_result(struct flash_bank_s *bank)\r
-{\r
- target_t *target = bank->target;\r
- u32 retval;\r
-\r
- target_read_u32(target, str7x_get_flash_adr(bank, FLASH_ER), &retval);\r
- \r
- return retval;\r
-}\r
-\r
-int str7x_blank_check(struct flash_bank_s *bank, int first, int last)\r
-{\r
- target_t *target = bank->target;\r
- u8 *buffer;\r
- int i;\r
- int nBytes;\r
- \r
- if ((first < 0) || (last > bank->num_sectors))\r
- return ERROR_FLASH_SECTOR_INVALID;\r
-\r
- if (bank->target->state != TARGET_HALTED)\r
- {\r
- return ERROR_TARGET_NOT_HALTED;\r
- }\r
- \r
- buffer = malloc(256);\r
- \r
- for (i = first; i <= last; i++)\r
- {\r
- bank->sectors[i].is_erased = 1;\r
-\r
- target->type->read_memory(target, bank->base + bank->sectors[i].offset, 4, 256/4, buffer);\r
- \r
- for (nBytes = 0; nBytes < 256; nBytes++)\r
- {\r
- if (buffer[nBytes] != 0xFF)\r
- {\r
- bank->sectors[i].is_erased = 0;\r
- break;\r
- }\r
- } \r
- }\r
- \r
- free(buffer);\r
-\r
- return ERROR_OK;\r
-}\r
-\r
-int str7x_protect_check(struct flash_bank_s *bank)\r
-{\r
- str7x_flash_bank_t *str7x_info = bank->driver_priv;\r
- target_t *target = bank->target;\r
- \r
- int i;\r
- u32 retval;\r
-\r
- if (bank->target->state != TARGET_HALTED)\r
- {\r
- return ERROR_TARGET_NOT_HALTED;\r
- }\r
-\r
- target_read_u32(target, str7x_get_flash_adr(bank, FLASH_NVWPAR), &retval);\r
-\r
- for (i = 0; i < bank->num_sectors; i++)\r
- {\r
- if (retval & str7x_info->sector_bits[i])\r
- bank->sectors[i].is_protected = 0;\r
- else\r
- bank->sectors[i].is_protected = 1;\r
- }\r
-\r
- return ERROR_OK;\r
-}\r
-\r
-int str7x_erase(struct flash_bank_s *bank, int first, int last)\r
-{\r
- str7x_flash_bank_t *str7x_info = bank->driver_priv;\r
- target_t *target = bank->target;\r
- \r
- int i;\r
- u32 cmd;\r
- u32 retval;\r
- u32 b0_sectors = 0, b1_sectors = 0;\r
- \r
- for (i = first; i <= last; i++)\r
- {\r
- if (str7x_info->sector_bank[i] == 0)\r
- b0_sectors |= str7x_info->sector_bits[i];\r
- else if (str7x_info->sector_bank[i] == 1)\r
- b1_sectors |= str7x_info->sector_bits[i];\r
- else\r
- ERROR("BUG: str7x_info->sector_bank[i] neither 0 nor 1 (%i)", str7x_info->sector_bank[i]);\r
- }\r
- \r
- if (b0_sectors)\r
- {\r
- DEBUG("b0_sectors: 0x%x", b0_sectors);\r
- \r
- /* clear FLASH_ER register */ \r
- target_write_u32(target, str7x_get_flash_adr(bank, FLASH_ER), 0x0);\r
- \r
- cmd = FLASH_SER;\r
- target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), cmd);\r
- \r
- cmd = b0_sectors;\r
- target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR1), cmd);\r
- \r
- cmd = FLASH_SER|FLASH_WMS;\r
- target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), cmd);\r
- \r
- while (((retval = str7x_status(bank)) & str7x_info->busy_bits)){\r
- usleep(1000);\r
- }\r
- \r
- retval = str7x_result(bank);\r
- \r
- if (retval)\r
- {\r
- ERROR("error erasing flash bank, FLASH_ER: 0x%x", retval);\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
- }\r
- \r
- if (b1_sectors)\r
- {\r
- DEBUG("b1_sectors: 0x%x", b1_sectors);\r
- \r
- /* clear FLASH_ER register */ \r
- target_write_u32(target, str7x_get_flash_adr(bank, FLASH_ER), 0x0);\r
- \r
- cmd = FLASH_SER;\r
- target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), cmd);\r
- \r
- cmd = b1_sectors;\r
- target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR1), cmd);\r
- \r
- cmd = FLASH_SER|FLASH_WMS;\r
- target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), cmd);\r
- \r
- while (((retval = str7x_status(bank)) & str7x_info->busy_bits)){\r
- usleep(1000);\r
- }\r
- \r
- retval = str7x_result(bank);\r
- \r
- if (retval)\r
- {\r
- ERROR("error erasing flash bank, FLASH_ER: 0x%x", retval);\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
- }\r
- \r
- for (i = first; i <= last; i++)\r
- bank->sectors[i].is_erased = 1;\r
-\r
- return ERROR_OK;\r
-}\r
-\r
-int str7x_protect(struct flash_bank_s *bank, int set, int first, int last)\r
-{\r
- str7x_flash_bank_t *str7x_info = bank->driver_priv;\r
- target_t *target = bank->target;\r
- int i;\r
- u32 cmd;\r
- u32 retval;\r
- u32 protect_blocks;\r
- \r
- if (bank->target->state != TARGET_HALTED)\r
- {\r
- return ERROR_TARGET_NOT_HALTED;\r
- }\r
- \r
- protect_blocks = 0xFFFFFFFF;\r
-\r
- if (set)\r
- {\r
- for (i = first; i <= last; i++)\r
- protect_blocks &= ~(str7x_info->sector_bits[i]);\r
- }\r
- \r
- /* clear FLASH_ER register */ \r
- target_write_u32(target, str7x_get_flash_adr(bank, FLASH_ER), 0x0);\r
-\r
- cmd = FLASH_SPR;\r
- target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), cmd);\r
- \r
- cmd = str7x_get_flash_adr(bank, FLASH_NVWPAR);\r
- target_write_u32(target, str7x_get_flash_adr(bank, FLASH_AR), cmd);\r
- \r
- cmd = protect_blocks;\r
- target_write_u32(target, str7x_get_flash_adr(bank, FLASH_DR0), cmd);\r
- \r
- cmd = FLASH_SPR|FLASH_WMS;\r
- target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), cmd);\r
- \r
- while (((retval = str7x_status(bank)) & str7x_info->busy_bits)){\r
- usleep(1000);\r
- }\r
- \r
- retval = str7x_result(bank);\r
- \r
- DEBUG("retval: 0x%8.8x", retval);\r
- \r
- if (retval & FLASH_ERER)\r
- return ERROR_FLASH_SECTOR_NOT_ERASED;\r
- else if (retval & FLASH_WPF)\r
- return ERROR_FLASH_OPERATION_FAILED;\r
-\r
- return ERROR_OK;\r
-}\r
-\r
-int str7x_write_block(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count)\r
-{\r
- str7x_flash_bank_t *str7x_info = bank->driver_priv;\r
- target_t *target = bank->target;\r
- u32 buffer_size = 8192;\r
- working_area_t *source;\r
- u32 address = bank->base + offset;\r
- reg_param_t reg_params[6];\r
- armv4_5_algorithm_t armv4_5_info;\r
- int retval = ERROR_OK;\r
- \r
- u32 str7x_flash_write_code[] = {\r
- /* write: */\r
- 0xe3a04201, /* mov r4, #0x10000000 */\r
- 0xe5824000, /* str r4, [r2, #0x0] */\r
- 0xe5821010, /* str r1, [r2, #0x10] */\r
- 0xe4904004, /* ldr r4, [r0], #4 */\r
- 0xe5824008, /* str r4, [r2, #0x8] */\r
- 0xe4904004, /* ldr r4, [r0], #4 */\r
- 0xe582400c, /* str r4, [r2, #0xc] */\r
- 0xe3a04209, /* mov r4, #0x90000000 */\r
- 0xe5824000, /* str r4, [r2, #0x0] */\r
- /* busy: */\r
- 0xe5924000, /* ldr r4, [r2, #0x0] */\r
- 0xe1140005, /* tst r4, r5 */\r
- 0x1afffffc, /* bne busy */\r
- 0xe5924014, /* ldr r4, [r2, #0x14] */\r
- 0xe31400ff, /* tst r4, #0xff */\r
- 0x03140c01, /* tsteq r4, #0x100 */\r
- 0x1a000002, /* bne exit */\r
- 0xe2811008, /* add r1, r1, #0x8 */\r
- 0xe2533001, /* subs r3, r3, #1 */\r
- 0x1affffec, /* bne write */\r
- /* exit: */\r
- 0xeafffffe, /* b exit */\r
- };\r
- \r
- /* flash write code */\r
- if (target_alloc_working_area(target, 4 * 20, &str7x_info->write_algorithm) != ERROR_OK)\r
- {\r
- WARNING("no working area available, can't do block memory writes");\r
- return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;\r
- };\r
- \r
- target_write_buffer(target, str7x_info->write_algorithm->address, 20 * 4, (u8*)str7x_flash_write_code);\r
-\r
- /* memory buffer */\r
- while (target_alloc_working_area(target, buffer_size, &source) != ERROR_OK)\r
- {\r
- buffer_size /= 2;\r
- if (buffer_size <= 256)\r
- {\r
- /* if we already allocated the writing code, but failed to get a buffer, free the algorithm */\r
- if (str7x_info->write_algorithm)\r
- target_free_working_area(target, str7x_info->write_algorithm);\r
- \r
- WARNING("no large enough working area available, can't do block memory writes");\r
- return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;\r
- }\r
- }\r
- \r
- armv4_5_info.common_magic = ARMV4_5_COMMON_MAGIC;\r
- armv4_5_info.core_mode = ARMV4_5_MODE_SVC;\r
- armv4_5_info.core_state = ARMV4_5_STATE_ARM;\r
- \r
- init_reg_param(®_params[0], "r0", 32, PARAM_OUT);\r
- init_reg_param(®_params[1], "r1", 32, PARAM_OUT);\r
- init_reg_param(®_params[2], "r2", 32, PARAM_OUT);\r
- init_reg_param(®_params[3], "r3", 32, PARAM_OUT);\r
- init_reg_param(®_params[4], "r4", 32, PARAM_IN);\r
- init_reg_param(®_params[5], "r5", 32, PARAM_OUT);\r
- \r
- while (count > 0)\r
- {\r
- u32 thisrun_count = (count > (buffer_size / 8)) ? (buffer_size / 8) : count;\r
- \r
- target_write_buffer(target, source->address, thisrun_count * 8, buffer);\r
- \r
- buf_set_u32(reg_params[0].value, 0, 32, source->address);\r
- buf_set_u32(reg_params[1].value, 0, 32, address);\r
- buf_set_u32(reg_params[2].value, 0, 32, str7x_get_flash_adr(bank, FLASH_CR0));\r
- buf_set_u32(reg_params[3].value, 0, 32, thisrun_count);\r
- buf_set_u32(reg_params[5].value, 0, 32, str7x_info->busy_bits);\r
- \r
- if ((retval = target->type->run_algorithm(target, 0, NULL, 6, reg_params, str7x_info->write_algorithm->address, str7x_info->write_algorithm->address + (19 * 4), 10000, &armv4_5_info)) != ERROR_OK)\r
- {\r
- ERROR("error executing str7x flash write algorithm");\r
- break;\r
- }\r
- \r
- if (buf_get_u32(reg_params[4].value, 0, 32) != 0x00)\r
- {\r
- retval = ERROR_FLASH_OPERATION_FAILED;\r
- break;\r
- }\r
- \r
- buffer += thisrun_count * 8;\r
- address += thisrun_count * 8;\r
- count -= thisrun_count;\r
- }\r
- \r
- target_free_working_area(target, source);\r
- target_free_working_area(target, str7x_info->write_algorithm);\r
- \r
- destroy_reg_param(®_params[0]);\r
- destroy_reg_param(®_params[1]);\r
- destroy_reg_param(®_params[2]);\r
- destroy_reg_param(®_params[3]);\r
- destroy_reg_param(®_params[4]);\r
- destroy_reg_param(®_params[5]);\r
- \r
- return retval;\r
-}\r
-\r
-int str7x_write(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count)\r
-{\r
- target_t *target = bank->target;\r
- str7x_flash_bank_t *str7x_info = bank->driver_priv;\r
- u32 dwords_remaining = (count / 8);\r
- u32 bytes_remaining = (count & 0x00000007);\r
- u32 address = bank->base + offset;\r
- u32 bytes_written = 0;\r
- u32 cmd;\r
- u32 retval;\r
- u32 check_address = offset;\r
- int i;\r
- \r
- if (offset & 0x7)\r
- {\r
- WARNING("offset 0x%x breaks required 8-byte alignment", offset);\r
- return ERROR_FLASH_DST_BREAKS_ALIGNMENT;\r
- }\r
- \r
- for (i = 0; i < bank->num_sectors; i++)\r
- {\r
- u32 sec_start = bank->sectors[i].offset;\r
- u32 sec_end = sec_start + bank->sectors[i].size;\r
- \r
- /* check if destination falls within the current sector */\r
- if ((check_address >= sec_start) && (check_address < sec_end))\r
- {\r
- /* check if destination ends in the current sector */\r
- if (offset + count < sec_end)\r
- check_address = offset + count;\r
- else\r
- check_address = sec_end;\r
- }\r
- }\r
- \r
- if (check_address != offset + count)\r
- return ERROR_FLASH_DST_OUT_OF_BANK;\r
- \r
- /* clear FLASH_ER register */ \r
- target_write_u32(target, str7x_get_flash_adr(bank, FLASH_ER), 0x0);\r
-\r
- /* multiple dwords (8-byte) to be programmed? */\r
- if (dwords_remaining > 0) \r
- {\r
- /* try using a block write */\r
- if ((retval = str7x_write_block(bank, buffer, offset, dwords_remaining)) != ERROR_OK)\r
- {\r
- if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE)\r
- {\r
- /* if block write failed (no sufficient working area),\r
- * we use normal (slow) single dword accesses */ \r
- WARNING("couldn't use block writes, falling back to single memory accesses");\r
- }\r
- else if (retval == ERROR_FLASH_OPERATION_FAILED)\r
- {\r
- /* if an error occured, we examine the reason, and quit */\r
- retval = str7x_result(bank);\r
- \r
- ERROR("flash writing failed with error code: 0x%x", retval);\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
- }\r
- else\r
- {\r
- buffer += dwords_remaining * 8;\r
- address += dwords_remaining * 8;\r
- dwords_remaining = 0;\r
- }\r
- }\r
-\r
- while (dwords_remaining > 0)\r
- {\r
- // command\r
- cmd = FLASH_DWPG;\r
- target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), cmd);\r
- \r
- // address\r
- target_write_u32(target, str7x_get_flash_adr(bank, FLASH_AR), address);\r
- \r
- // data word 1\r
- target->type->write_memory(target, str7x_get_flash_adr(bank, FLASH_DR0), 4, 1, buffer + bytes_written);\r
- bytes_written += 4;\r
- \r
- // data word 2\r
- target->type->write_memory(target, str7x_get_flash_adr(bank, FLASH_DR1), 4, 1, buffer + bytes_written);\r
- bytes_written += 4;\r
- \r
- /* start programming cycle */\r
- cmd = FLASH_DWPG | FLASH_WMS;\r
- target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), cmd);\r
- \r
- while (((retval = str7x_status(bank)) & str7x_info->busy_bits))\r
- {\r
- usleep(1000);\r
- }\r
- \r
- retval = str7x_result(bank);\r
- \r
- if (retval & FLASH_PGER)\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- else if (retval & FLASH_WPF)\r
- return ERROR_FLASH_OPERATION_FAILED;\r
-\r
- dwords_remaining--;\r
- address += 8;\r
- }\r
- \r
- if (bytes_remaining)\r
- {\r
- u8 last_dword[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};\r
- int i = 0;\r
- \r
- while(bytes_remaining > 0)\r
- {\r
- last_dword[i++] = *(buffer + bytes_written); \r
- bytes_remaining--;\r
- bytes_written++;\r
- }\r
- \r
- // command\r
- cmd = FLASH_DWPG;\r
- target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), cmd);\r
- \r
- // address\r
- target_write_u32(target, str7x_get_flash_adr(bank, FLASH_AR), address);\r
- \r
- // data word 1\r
- target->type->write_memory(target, str7x_get_flash_adr(bank, FLASH_DR0), 4, 1, last_dword);\r
- bytes_written += 4;\r
- \r
- // data word 2\r
- target->type->write_memory(target, str7x_get_flash_adr(bank, FLASH_DR1), 4, 1, last_dword + 4);\r
- bytes_written += 4;\r
- \r
- /* start programming cycle */\r
- cmd = FLASH_DWPG | FLASH_WMS;\r
- target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), cmd);\r
- \r
- while (((retval = str7x_status(bank)) & str7x_info->busy_bits))\r
- {\r
- usleep(1000);\r
- }\r
- \r
- retval = str7x_result(bank);\r
- \r
- if (retval & FLASH_PGER)\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- else if (retval & FLASH_WPF)\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int str7x_probe(struct flash_bank_s *bank)\r
-{\r
- return ERROR_OK;\r
-}\r
-\r
-int str7x_handle_part_id_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- return ERROR_OK;\r
-}\r
-\r
-int str7x_erase_check(struct flash_bank_s *bank)\r
-{\r
- return str7x_blank_check(bank, 0, bank->num_sectors - 1);\r
-}\r
-\r
-int str7x_info(struct flash_bank_s *bank, char *buf, int buf_size)\r
-{\r
- snprintf(buf, buf_size, "str7x flash driver info" );\r
- return ERROR_OK;\r
-}\r
-\r
-int str7x_handle_disable_jtag_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- flash_bank_t *bank;\r
- target_t *target = NULL;\r
- str7x_flash_bank_t *str7x_info = NULL;\r
- \r
- u32 flash_cmd;\r
- u32 retval;\r
- u16 ProtectionLevel = 0;\r
- u16 ProtectionRegs;\r
- \r
- if (argc < 1)\r
- {\r
- command_print(cmd_ctx, "str7x disable_jtag <bank>");\r
- return ERROR_OK; \r
- }\r
- \r
- bank = get_flash_bank_by_num(strtoul(args[0], NULL, 0));\r
- if (!bank)\r
- {\r
- command_print(cmd_ctx, "str7x disable_jtag <bank> ok");\r
- return ERROR_OK;\r
- }\r
- \r
- str7x_info = bank->driver_priv;\r
- \r
- target = bank->target;\r
- \r
- if (target->state != TARGET_HALTED)\r
- {\r
- return ERROR_TARGET_NOT_HALTED;\r
- }\r
- \r
- /* first we get protection status */\r
- target_read_u32(target, str7x_get_flash_adr(bank, FLASH_NVAPR0), &retval);\r
-\r
- if (!(retval & str7x_info->disable_bit))\r
- {\r
- ProtectionLevel = 1;\r
- }\r
- \r
- target_read_u32(target, str7x_get_flash_adr(bank, FLASH_NVAPR1), &retval);\r
- ProtectionRegs = ~(retval >> 16);\r
-\r
- while (((ProtectionRegs) != 0) && (ProtectionLevel < 16))\r
- {\r
- ProtectionRegs >>= 1;\r
- ProtectionLevel++;\r
- }\r
- \r
- if (ProtectionLevel == 0)\r
- {\r
- flash_cmd = FLASH_SPR;\r
- target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), flash_cmd);\r
- target_write_u32(target, str7x_get_flash_adr(bank, FLASH_AR), 0x4010DFB8);\r
- target_write_u32(target, str7x_get_flash_adr(bank, FLASH_DR0), 0xFFFFFFFD);\r
- flash_cmd = FLASH_SPR | FLASH_WMS;\r
- target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), flash_cmd);\r
- }\r
- else\r
- {\r
- flash_cmd = FLASH_SPR;\r
- target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), flash_cmd);\r
- target_write_u32(target, str7x_get_flash_adr(bank, FLASH_AR), 0x4010DFBC);\r
- target_write_u32(target, str7x_get_flash_adr(bank, FLASH_DR0), ~(1<<(15+ProtectionLevel)));\r
- flash_cmd = FLASH_SPR | FLASH_WMS;\r
- target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), flash_cmd);\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
+/***************************************************************************
+ * Copyright (C) 2005 by Dominic Rath *
+ * Dominic.Rath@gmx.de *
+ * *
+ * 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 "replacements.h"
+
+#include "str7x.h"
+#include "flash.h"
+#include "target.h"
+#include "log.h"
+#include "armv4_5.h"
+#include "algorithm.h"
+#include "binarybuffer.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+str7x_mem_layout_t mem_layout[] = {
+ {0x00000000, 0x02000, 0x01},
+ {0x00002000, 0x02000, 0x02},
+ {0x00004000, 0x02000, 0x04},
+ {0x00006000, 0x02000, 0x08},
+ {0x00008000, 0x08000, 0x10},
+ {0x00010000, 0x10000, 0x20},
+ {0x00020000, 0x10000, 0x40},
+ {0x00030000, 0x10000, 0x80},
+ {0x000C0000, 0x02000, 0x10000},
+ {0x000C2000, 0x02000, 0x20000},
+};
+
+int str7x_register_commands(struct command_context_s *cmd_ctx);
+int str7x_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank);
+int str7x_erase(struct flash_bank_s *bank, int first, int last);
+int str7x_protect(struct flash_bank_s *bank, int set, int first, int last);
+int str7x_write(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count);
+int str7x_probe(struct flash_bank_s *bank);
+int str7x_handle_part_id_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+int str7x_protect_check(struct flash_bank_s *bank);
+int str7x_erase_check(struct flash_bank_s *bank);
+int str7x_info(struct flash_bank_s *bank, char *buf, int buf_size);
+
+int str7x_handle_disable_jtag_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+
+flash_driver_t str7x_flash =
+{
+ .name = "str7x",
+ .register_commands = str7x_register_commands,
+ .flash_bank_command = str7x_flash_bank_command,
+ .erase = str7x_erase,
+ .protect = str7x_protect,
+ .write = str7x_write,
+ .probe = str7x_probe,
+ .auto_probe = str7x_probe,
+ .erase_check = str7x_erase_check,
+ .protect_check = str7x_protect_check,
+ .info = str7x_info
+};
+
+int str7x_register_commands(struct command_context_s *cmd_ctx)
+{
+ command_t *str7x_cmd = register_command(cmd_ctx, NULL, "str7x", NULL, COMMAND_ANY, NULL);
+
+ register_command(cmd_ctx, str7x_cmd, "disable_jtag", str7x_handle_disable_jtag_command, COMMAND_EXEC,
+ "disable jtag access");
+
+ return ERROR_OK;
+}
+
+int str7x_get_flash_adr(struct flash_bank_s *bank, u32 reg)
+{
+ return (bank->base | reg);
+}
+
+int str7x_build_block_list(struct flash_bank_s *bank)
+{
+ str7x_flash_bank_t *str7x_info = bank->driver_priv;
+
+ int i;
+ int num_sectors = 0, b0_sectors = 0, b1_sectors = 0;
+
+ switch (bank->size)
+ {
+ case 16 * 1024:
+ b0_sectors = 2;
+ break;
+ case 64 * 1024:
+ b0_sectors = 5;
+ break;
+ case 128 * 1024:
+ b0_sectors = 6;
+ break;
+ case 256 * 1024:
+ b0_sectors = 8;
+ break;
+ default:
+ ERROR("BUG: unknown bank->size encountered");
+ exit(-1);
+ }
+
+ if( str7x_info->bank1 == 1 )
+ {
+ b1_sectors += 2;
+ }
+
+ num_sectors = b0_sectors + b1_sectors;
+
+ bank->num_sectors = num_sectors;
+ bank->sectors = malloc(sizeof(flash_sector_t) * num_sectors);
+ str7x_info->sector_bits = malloc(sizeof(u32) * num_sectors);
+ str7x_info->sector_bank = malloc(sizeof(u32) * num_sectors);
+
+ num_sectors = 0;
+
+ for (i = 0; i < b0_sectors; i++)
+ {
+ bank->sectors[num_sectors].offset = mem_layout[i].sector_start;
+ bank->sectors[num_sectors].size = mem_layout[i].sector_size;
+ bank->sectors[num_sectors].is_erased = -1;
+ bank->sectors[num_sectors].is_protected = 1;
+ str7x_info->sector_bank[num_sectors] = 0;
+ str7x_info->sector_bits[num_sectors++] = mem_layout[i].sector_bit;
+ }
+
+ if (b1_sectors)
+ {
+ for (i = 8; i < 10; i++)
+ {
+ bank->sectors[num_sectors].offset = mem_layout[i].sector_start;
+ bank->sectors[num_sectors].size = mem_layout[i].sector_size;
+ bank->sectors[num_sectors].is_erased = -1;
+ bank->sectors[num_sectors].is_protected = 1;
+ str7x_info->sector_bank[num_sectors] = 1;
+ str7x_info->sector_bits[num_sectors++] = mem_layout[i].sector_bit;
+ }
+ }
+
+ return ERROR_OK;
+}
+
+/* flash bank str7x <base> <size> 0 0 <target#> <str71_variant>
+ */
+int str7x_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank)
+{
+ str7x_flash_bank_t *str7x_info;
+
+ if (argc < 7)
+ {
+ WARNING("incomplete flash_bank str7x configuration");
+ return ERROR_FLASH_BANK_INVALID;
+ }
+
+ str7x_info = malloc(sizeof(str7x_flash_bank_t));
+ bank->driver_priv = str7x_info;
+
+ /* set default bits for str71x flash */
+ str7x_info->bank1 = 1;
+ str7x_info->busy_bits = (FLASH_LOCK|FLASH_BSYA1|FLASH_BSYA0);
+ str7x_info->disable_bit = (1<<1);
+
+ if (strcmp(args[6], "STR71x") == 0)
+ {
+ if (bank->base != 0x40000000)
+ {
+ WARNING("overriding flash base address for STR71x device with 0x40000000");
+ bank->base = 0x40000000;
+ }
+ }
+ else if (strcmp(args[6], "STR73x") == 0)
+ {
+ str7x_info->bank1 = 0;
+ str7x_info->busy_bits = (FLASH_LOCK|FLASH_BSYA0);
+
+ if (bank->base != 0x80000000)
+ {
+ WARNING("overriding flash base address for STR73x device with 0x80000000");
+ bank->base = 0x80000000;
+ }
+ }
+ else if (strcmp(args[6], "STR75x") == 0)
+ {
+ str7x_info->disable_bit = (1<<0);
+
+ if (bank->base != 0x20000000)
+ {
+ WARNING("overriding flash base address for STR75x device with 0x20000000");
+ bank->base = 0x20000000;
+ }
+ }
+ else
+ {
+ ERROR("unknown STR7x variant: '%s'", args[6]);
+ free(str7x_info);
+ return ERROR_FLASH_BANK_INVALID;
+ }
+
+ str7x_build_block_list(bank);
+
+ str7x_info->write_algorithm = NULL;
+
+ return ERROR_OK;
+}
+
+u32 str7x_status(struct flash_bank_s *bank)
+{
+ target_t *target = bank->target;
+ u32 retval;
+
+ target_read_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), &retval);
+
+ return retval;
+}
+
+u32 str7x_result(struct flash_bank_s *bank)
+{
+ target_t *target = bank->target;
+ u32 retval;
+
+ target_read_u32(target, str7x_get_flash_adr(bank, FLASH_ER), &retval);
+
+ return retval;
+}
+
+int str7x_blank_check(struct flash_bank_s *bank, int first, int last)
+{
+ target_t *target = bank->target;
+ u8 *buffer;
+ int i;
+ int nBytes;
+
+ if ((first < 0) || (last > bank->num_sectors))
+ return ERROR_FLASH_SECTOR_INVALID;
+
+ if (bank->target->state != TARGET_HALTED)
+ {
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ buffer = malloc(256);
+
+ for (i = first; i <= last; i++)
+ {
+ bank->sectors[i].is_erased = 1;
+
+ target->type->read_memory(target, bank->base + bank->sectors[i].offset, 4, 256/4, buffer);
+
+ for (nBytes = 0; nBytes < 256; nBytes++)
+ {
+ if (buffer[nBytes] != 0xFF)
+ {
+ bank->sectors[i].is_erased = 0;
+ break;
+ }
+ }
+ }
+
+ free(buffer);
+
+ return ERROR_OK;
+}
+
+int str7x_protect_check(struct flash_bank_s *bank)
+{
+ str7x_flash_bank_t *str7x_info = bank->driver_priv;
+ target_t *target = bank->target;
+
+ int i;
+ u32 retval;
+
+ if (bank->target->state != TARGET_HALTED)
+ {
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ target_read_u32(target, str7x_get_flash_adr(bank, FLASH_NVWPAR), &retval);
+
+ for (i = 0; i < bank->num_sectors; i++)
+ {
+ if (retval & str7x_info->sector_bits[i])
+ bank->sectors[i].is_protected = 0;
+ else
+ bank->sectors[i].is_protected = 1;
+ }
+
+ return ERROR_OK;
+}
+
+int str7x_erase(struct flash_bank_s *bank, int first, int last)
+{
+ str7x_flash_bank_t *str7x_info = bank->driver_priv;
+ target_t *target = bank->target;
+
+ int i;
+ u32 cmd;
+ u32 retval;
+ u32 b0_sectors = 0, b1_sectors = 0;
+
+ for (i = first; i <= last; i++)
+ {
+ if (str7x_info->sector_bank[i] == 0)
+ b0_sectors |= str7x_info->sector_bits[i];
+ else if (str7x_info->sector_bank[i] == 1)
+ b1_sectors |= str7x_info->sector_bits[i];
+ else
+ ERROR("BUG: str7x_info->sector_bank[i] neither 0 nor 1 (%i)", str7x_info->sector_bank[i]);
+ }
+
+ if (b0_sectors)
+ {
+ DEBUG("b0_sectors: 0x%x", b0_sectors);
+
+ /* clear FLASH_ER register */
+ target_write_u32(target, str7x_get_flash_adr(bank, FLASH_ER), 0x0);
+
+ cmd = FLASH_SER;
+ target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), cmd);
+
+ cmd = b0_sectors;
+ target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR1), cmd);
+
+ cmd = FLASH_SER|FLASH_WMS;
+ target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), cmd);
+
+ while (((retval = str7x_status(bank)) & str7x_info->busy_bits)){
+ usleep(1000);
+ }
+
+ retval = str7x_result(bank);
+
+ if (retval)
+ {
+ ERROR("error erasing flash bank, FLASH_ER: 0x%x", retval);
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+ }
+
+ if (b1_sectors)
+ {
+ DEBUG("b1_sectors: 0x%x", b1_sectors);
+
+ /* clear FLASH_ER register */
+ target_write_u32(target, str7x_get_flash_adr(bank, FLASH_ER), 0x0);
+
+ cmd = FLASH_SER;
+ target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), cmd);
+
+ cmd = b1_sectors;
+ target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR1), cmd);
+
+ cmd = FLASH_SER|FLASH_WMS;
+ target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), cmd);
+
+ while (((retval = str7x_status(bank)) & str7x_info->busy_bits)){
+ usleep(1000);
+ }
+
+ retval = str7x_result(bank);
+
+ if (retval)
+ {
+ ERROR("error erasing flash bank, FLASH_ER: 0x%x", retval);
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+ }
+
+ for (i = first; i <= last; i++)
+ bank->sectors[i].is_erased = 1;
+
+ return ERROR_OK;
+}
+
+int str7x_protect(struct flash_bank_s *bank, int set, int first, int last)
+{
+ str7x_flash_bank_t *str7x_info = bank->driver_priv;
+ target_t *target = bank->target;
+ int i;
+ u32 cmd;
+ u32 retval;
+ u32 protect_blocks;
+
+ if (bank->target->state != TARGET_HALTED)
+ {
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ protect_blocks = 0xFFFFFFFF;
+
+ if (set)
+ {
+ for (i = first; i <= last; i++)
+ protect_blocks &= ~(str7x_info->sector_bits[i]);
+ }
+
+ /* clear FLASH_ER register */
+ target_write_u32(target, str7x_get_flash_adr(bank, FLASH_ER), 0x0);
+
+ cmd = FLASH_SPR;
+ target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), cmd);
+
+ cmd = str7x_get_flash_adr(bank, FLASH_NVWPAR);
+ target_write_u32(target, str7x_get_flash_adr(bank, FLASH_AR), cmd);
+
+ cmd = protect_blocks;
+ target_write_u32(target, str7x_get_flash_adr(bank, FLASH_DR0), cmd);
+
+ cmd = FLASH_SPR|FLASH_WMS;
+ target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), cmd);
+
+ while (((retval = str7x_status(bank)) & str7x_info->busy_bits)){
+ usleep(1000);
+ }
+
+ retval = str7x_result(bank);
+
+ DEBUG("retval: 0x%8.8x", retval);
+
+ if (retval & FLASH_ERER)
+ return ERROR_FLASH_SECTOR_NOT_ERASED;
+ else if (retval & FLASH_WPF)
+ return ERROR_FLASH_OPERATION_FAILED;
+
+ return ERROR_OK;
+}
+
+int str7x_write_block(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count)
+{
+ str7x_flash_bank_t *str7x_info = bank->driver_priv;
+ target_t *target = bank->target;
+ u32 buffer_size = 8192;
+ working_area_t *source;
+ u32 address = bank->base + offset;
+ reg_param_t reg_params[6];
+ armv4_5_algorithm_t armv4_5_info;
+ int retval = ERROR_OK;
+
+ u32 str7x_flash_write_code[] = {
+ /* write: */
+ 0xe3a04201, /* mov r4, #0x10000000 */
+ 0xe5824000, /* str r4, [r2, #0x0] */
+ 0xe5821010, /* str r1, [r2, #0x10] */
+ 0xe4904004, /* ldr r4, [r0], #4 */
+ 0xe5824008, /* str r4, [r2, #0x8] */
+ 0xe4904004, /* ldr r4, [r0], #4 */
+ 0xe582400c, /* str r4, [r2, #0xc] */
+ 0xe3a04209, /* mov r4, #0x90000000 */
+ 0xe5824000, /* str r4, [r2, #0x0] */
+ /* busy: */
+ 0xe5924000, /* ldr r4, [r2, #0x0] */
+ 0xe1140005, /* tst r4, r5 */
+ 0x1afffffc, /* bne busy */
+ 0xe5924014, /* ldr r4, [r2, #0x14] */
+ 0xe31400ff, /* tst r4, #0xff */
+ 0x03140c01, /* tsteq r4, #0x100 */
+ 0x1a000002, /* bne exit */
+ 0xe2811008, /* add r1, r1, #0x8 */
+ 0xe2533001, /* subs r3, r3, #1 */
+ 0x1affffec, /* bne write */
+ /* exit: */
+ 0xeafffffe, /* b exit */
+ };
+
+ /* flash write code */
+ if (target_alloc_working_area(target, 4 * 20, &str7x_info->write_algorithm) != ERROR_OK)
+ {
+ WARNING("no working area available, can't do block memory writes");
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ };
+
+ target_write_buffer(target, str7x_info->write_algorithm->address, 20 * 4, (u8*)str7x_flash_write_code);
+
+ /* memory buffer */
+ while (target_alloc_working_area(target, buffer_size, &source) != ERROR_OK)
+ {
+ buffer_size /= 2;
+ if (buffer_size <= 256)
+ {
+ /* if we already allocated the writing code, but failed to get a buffer, free the algorithm */
+ if (str7x_info->write_algorithm)
+ target_free_working_area(target, str7x_info->write_algorithm);
+
+ WARNING("no large enough working area available, can't do block memory writes");
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+ }
+
+ armv4_5_info.common_magic = ARMV4_5_COMMON_MAGIC;
+ armv4_5_info.core_mode = ARMV4_5_MODE_SVC;
+ armv4_5_info.core_state = ARMV4_5_STATE_ARM;
+
+ init_reg_param(®_params[0], "r0", 32, PARAM_OUT);
+ init_reg_param(®_params[1], "r1", 32, PARAM_OUT);
+ init_reg_param(®_params[2], "r2", 32, PARAM_OUT);
+ init_reg_param(®_params[3], "r3", 32, PARAM_OUT);
+ init_reg_param(®_params[4], "r4", 32, PARAM_IN);
+ init_reg_param(®_params[5], "r5", 32, PARAM_OUT);
+
+ while (count > 0)
+ {
+ u32 thisrun_count = (count > (buffer_size / 8)) ? (buffer_size / 8) : count;
+
+ target_write_buffer(target, source->address, thisrun_count * 8, buffer);
+
+ buf_set_u32(reg_params[0].value, 0, 32, source->address);
+ buf_set_u32(reg_params[1].value, 0, 32, address);
+ buf_set_u32(reg_params[2].value, 0, 32, str7x_get_flash_adr(bank, FLASH_CR0));
+ buf_set_u32(reg_params[3].value, 0, 32, thisrun_count);
+ buf_set_u32(reg_params[5].value, 0, 32, str7x_info->busy_bits);
+
+ if ((retval = target->type->run_algorithm(target, 0, NULL, 6, reg_params, str7x_info->write_algorithm->address, str7x_info->write_algorithm->address + (19 * 4), 10000, &armv4_5_info)) != ERROR_OK)
+ {
+ ERROR("error executing str7x flash write algorithm");
+ break;
+ }
+
+ if (buf_get_u32(reg_params[4].value, 0, 32) != 0x00)
+ {
+ retval = ERROR_FLASH_OPERATION_FAILED;
+ break;
+ }
+
+ buffer += thisrun_count * 8;
+ address += thisrun_count * 8;
+ count -= thisrun_count;
+ }
+
+ target_free_working_area(target, source);
+ target_free_working_area(target, str7x_info->write_algorithm);
+
+ destroy_reg_param(®_params[0]);
+ destroy_reg_param(®_params[1]);
+ destroy_reg_param(®_params[2]);
+ destroy_reg_param(®_params[3]);
+ destroy_reg_param(®_params[4]);
+ destroy_reg_param(®_params[5]);
+
+ return retval;
+}
+
+int str7x_write(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count)
+{
+ target_t *target = bank->target;
+ str7x_flash_bank_t *str7x_info = bank->driver_priv;
+ u32 dwords_remaining = (count / 8);
+ u32 bytes_remaining = (count & 0x00000007);
+ u32 address = bank->base + offset;
+ u32 bytes_written = 0;
+ u32 cmd;
+ u32 retval;
+ u32 check_address = offset;
+ int i;
+
+ if (offset & 0x7)
+ {
+ WARNING("offset 0x%x breaks required 8-byte alignment", offset);
+ return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
+ }
+
+ for (i = 0; i < bank->num_sectors; i++)
+ {
+ u32 sec_start = bank->sectors[i].offset;
+ u32 sec_end = sec_start + bank->sectors[i].size;
+
+ /* check if destination falls within the current sector */
+ if ((check_address >= sec_start) && (check_address < sec_end))
+ {
+ /* check if destination ends in the current sector */
+ if (offset + count < sec_end)
+ check_address = offset + count;
+ else
+ check_address = sec_end;
+ }
+ }
+
+ if (check_address != offset + count)
+ return ERROR_FLASH_DST_OUT_OF_BANK;
+
+ /* clear FLASH_ER register */
+ target_write_u32(target, str7x_get_flash_adr(bank, FLASH_ER), 0x0);
+
+ /* multiple dwords (8-byte) to be programmed? */
+ if (dwords_remaining > 0)
+ {
+ /* try using a block write */
+ if ((retval = str7x_write_block(bank, buffer, offset, dwords_remaining)) != ERROR_OK)
+ {
+ if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE)
+ {
+ /* if block write failed (no sufficient working area),
+ * we use normal (slow) single dword accesses */
+ WARNING("couldn't use block writes, falling back to single memory accesses");
+ }
+ else if (retval == ERROR_FLASH_OPERATION_FAILED)
+ {
+ /* if an error occured, we examine the reason, and quit */
+ retval = str7x_result(bank);
+
+ ERROR("flash writing failed with error code: 0x%x", retval);
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+ }
+ else
+ {
+ buffer += dwords_remaining * 8;
+ address += dwords_remaining * 8;
+ dwords_remaining = 0;
+ }
+ }
+
+ while (dwords_remaining > 0)
+ {
+ // command
+ cmd = FLASH_DWPG;
+ target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), cmd);
+
+ // address
+ target_write_u32(target, str7x_get_flash_adr(bank, FLASH_AR), address);
+
+ // data word 1
+ target->type->write_memory(target, str7x_get_flash_adr(bank, FLASH_DR0), 4, 1, buffer + bytes_written);
+ bytes_written += 4;
+
+ // data word 2
+ target->type->write_memory(target, str7x_get_flash_adr(bank, FLASH_DR1), 4, 1, buffer + bytes_written);
+ bytes_written += 4;
+
+ /* start programming cycle */
+ cmd = FLASH_DWPG | FLASH_WMS;
+ target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), cmd);
+
+ while (((retval = str7x_status(bank)) & str7x_info->busy_bits))
+ {
+ usleep(1000);
+ }
+
+ retval = str7x_result(bank);
+
+ if (retval & FLASH_PGER)
+ return ERROR_FLASH_OPERATION_FAILED;
+ else if (retval & FLASH_WPF)
+ return ERROR_FLASH_OPERATION_FAILED;
+
+ dwords_remaining--;
+ address += 8;
+ }
+
+ if (bytes_remaining)
+ {
+ u8 last_dword[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ int i = 0;
+
+ while(bytes_remaining > 0)
+ {
+ last_dword[i++] = *(buffer + bytes_written);
+ bytes_remaining--;
+ bytes_written++;
+ }
+
+ // command
+ cmd = FLASH_DWPG;
+ target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), cmd);
+
+ // address
+ target_write_u32(target, str7x_get_flash_adr(bank, FLASH_AR), address);
+
+ // data word 1
+ target->type->write_memory(target, str7x_get_flash_adr(bank, FLASH_DR0), 4, 1, last_dword);
+ bytes_written += 4;
+
+ // data word 2
+ target->type->write_memory(target, str7x_get_flash_adr(bank, FLASH_DR1), 4, 1, last_dword + 4);
+ bytes_written += 4;
+
+ /* start programming cycle */
+ cmd = FLASH_DWPG | FLASH_WMS;
+ target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), cmd);
+
+ while (((retval = str7x_status(bank)) & str7x_info->busy_bits))
+ {
+ usleep(1000);
+ }
+
+ retval = str7x_result(bank);
+
+ if (retval & FLASH_PGER)
+ return ERROR_FLASH_OPERATION_FAILED;
+ else if (retval & FLASH_WPF)
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ return ERROR_OK;
+}
+
+int str7x_probe(struct flash_bank_s *bank)
+{
+ return ERROR_OK;
+}
+
+int str7x_handle_part_id_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ return ERROR_OK;
+}
+
+int str7x_erase_check(struct flash_bank_s *bank)
+{
+ return str7x_blank_check(bank, 0, bank->num_sectors - 1);
+}
+
+int str7x_info(struct flash_bank_s *bank, char *buf, int buf_size)
+{
+ snprintf(buf, buf_size, "str7x flash driver info" );
+ return ERROR_OK;
+}
+
+int str7x_handle_disable_jtag_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ flash_bank_t *bank;
+ target_t *target = NULL;
+ str7x_flash_bank_t *str7x_info = NULL;
+
+ u32 flash_cmd;
+ u32 retval;
+ u16 ProtectionLevel = 0;
+ u16 ProtectionRegs;
+
+ if (argc < 1)
+ {
+ command_print(cmd_ctx, "str7x disable_jtag <bank>");
+ return ERROR_OK;
+ }
+
+ bank = get_flash_bank_by_num(strtoul(args[0], NULL, 0));
+ if (!bank)
+ {
+ command_print(cmd_ctx, "str7x disable_jtag <bank> ok");
+ return ERROR_OK;
+ }
+
+ str7x_info = bank->driver_priv;
+
+ target = bank->target;
+
+ if (target->state != TARGET_HALTED)
+ {
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ /* first we get protection status */
+ target_read_u32(target, str7x_get_flash_adr(bank, FLASH_NVAPR0), &retval);
+
+ if (!(retval & str7x_info->disable_bit))
+ {
+ ProtectionLevel = 1;
+ }
+
+ target_read_u32(target, str7x_get_flash_adr(bank, FLASH_NVAPR1), &retval);
+ ProtectionRegs = ~(retval >> 16);
+
+ while (((ProtectionRegs) != 0) && (ProtectionLevel < 16))
+ {
+ ProtectionRegs >>= 1;
+ ProtectionLevel++;
+ }
+
+ if (ProtectionLevel == 0)
+ {
+ flash_cmd = FLASH_SPR;
+ target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), flash_cmd);
+ target_write_u32(target, str7x_get_flash_adr(bank, FLASH_AR), 0x4010DFB8);
+ target_write_u32(target, str7x_get_flash_adr(bank, FLASH_DR0), 0xFFFFFFFD);
+ flash_cmd = FLASH_SPR | FLASH_WMS;
+ target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), flash_cmd);
+ }
+ else
+ {
+ flash_cmd = FLASH_SPR;
+ target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), flash_cmd);
+ target_write_u32(target, str7x_get_flash_adr(bank, FLASH_AR), 0x4010DFBC);
+ target_write_u32(target, str7x_get_flash_adr(bank, FLASH_DR0), ~(1<<(15+ProtectionLevel)));
+ flash_cmd = FLASH_SPR | FLASH_WMS;
+ target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), flash_cmd);
+ }
+
+ return ERROR_OK;
+}
+
-/***************************************************************************\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 "replacements.h"\r
-\r
-#include "str9x.h"\r
-#include "flash.h"\r
-#include "target.h"\r
-#include "log.h"\r
-#include "armv4_5.h"\r
-#include "arm966e.h"\r
-#include "algorithm.h"\r
-#include "binarybuffer.h"\r
-\r
-#include <stdlib.h>\r
-#include <string.h>\r
-#include <unistd.h>\r
-\r
-str9x_mem_layout_t mem_layout_str9bank0[] = {\r
- {0x00000000, 0x10000, 0x01},\r
- {0x00010000, 0x10000, 0x02},\r
- {0x00020000, 0x10000, 0x04},\r
- {0x00030000, 0x10000, 0x08},\r
- {0x00040000, 0x10000, 0x10},\r
- {0x00050000, 0x10000, 0x20},\r
- {0x00060000, 0x10000, 0x40},\r
- {0x00070000, 0x10000, 0x80},\r
-};\r
-\r
-str9x_mem_layout_t mem_layout_str9bank1[] = {\r
- {0x00000000, 0x02000, 0x100},\r
- {0x00002000, 0x02000, 0x200},\r
- {0x00004000, 0x02000, 0x400},\r
- {0x00006000, 0x02000, 0x800}\r
-};\r
-\r
-static u32 bank1start = 0x00080000;\r
-\r
-int str9x_register_commands(struct command_context_s *cmd_ctx);\r
-int str9x_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank);\r
-int str9x_erase(struct flash_bank_s *bank, int first, int last);\r
-int str9x_protect(struct flash_bank_s *bank, int set, int first, int last);\r
-int str9x_write(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count);\r
-int str9x_probe(struct flash_bank_s *bank);\r
-int str9x_handle_part_id_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-int str9x_protect_check(struct flash_bank_s *bank);\r
-int str9x_erase_check(struct flash_bank_s *bank);\r
-int str9x_info(struct flash_bank_s *bank, char *buf, int buf_size);\r
-\r
-int str9x_handle_flash_config_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-\r
-flash_driver_t str9x_flash =\r
-{\r
- .name = "str9x",\r
- .register_commands = str9x_register_commands,\r
- .flash_bank_command = str9x_flash_bank_command,\r
- .erase = str9x_erase,\r
- .protect = str9x_protect,\r
- .write = str9x_write,\r
- .probe = str9x_probe,\r
- .auto_probe = str9x_probe,\r
- .erase_check = str9x_erase_check,\r
- .protect_check = str9x_protect_check,\r
- .info = str9x_info\r
-};\r
-\r
-int str9x_register_commands(struct command_context_s *cmd_ctx)\r
-{\r
- command_t *str9x_cmd = register_command(cmd_ctx, NULL, "str9x", NULL, COMMAND_ANY, NULL);\r
- \r
- register_command(cmd_ctx, str9x_cmd, "flash_config", str9x_handle_flash_config_command, COMMAND_EXEC,\r
- "configure str9 flash controller");\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int str9x_build_block_list(struct flash_bank_s *bank)\r
-{\r
- str9x_flash_bank_t *str9x_info = bank->driver_priv;\r
- \r
- int i;\r
- int num_sectors = 0;\r
- int b0_sectors = 0, b1_sectors = 0;\r
- \r
- switch (bank->size)\r
- {\r
- case (256 * 1024):\r
- b0_sectors = 4;\r
- break;\r
- case (512 * 1024):\r
- b0_sectors = 8;\r
- break;\r
- case (32 * 1024):\r
- b1_sectors = 4;\r
- bank1start = bank->base;\r
- break;\r
- default:\r
- ERROR("BUG: unknown bank->size encountered");\r
- exit(-1);\r
- }\r
- \r
- num_sectors = b0_sectors + b1_sectors;\r
- \r
- bank->num_sectors = num_sectors;\r
- bank->sectors = malloc(sizeof(flash_sector_t) * num_sectors);\r
- str9x_info->sector_bits = malloc(sizeof(u32) * num_sectors);\r
- \r
- num_sectors = 0;\r
- \r
- for (i = 0; i < b0_sectors; i++)\r
- {\r
- bank->sectors[num_sectors].offset = mem_layout_str9bank0[i].sector_start;\r
- bank->sectors[num_sectors].size = mem_layout_str9bank0[i].sector_size;\r
- bank->sectors[num_sectors].is_erased = -1;\r
- bank->sectors[num_sectors].is_protected = 1;\r
- str9x_info->sector_bits[num_sectors++] = mem_layout_str9bank0[i].sector_bit;\r
- }\r
-\r
- for (i = 0; i < b1_sectors; i++)\r
- {\r
- bank->sectors[num_sectors].offset = mem_layout_str9bank1[i].sector_start;\r
- bank->sectors[num_sectors].size = mem_layout_str9bank1[i].sector_size;\r
- bank->sectors[num_sectors].is_erased = -1;\r
- bank->sectors[num_sectors].is_protected = 1;\r
- str9x_info->sector_bits[num_sectors++] = mem_layout_str9bank1[i].sector_bit;\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-/* flash bank str9x <base> <size> 0 0 <target#>\r
- */\r
-int str9x_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank)\r
-{\r
- str9x_flash_bank_t *str9x_info;\r
- \r
- if (argc < 6)\r
- {\r
- WARNING("incomplete flash_bank str9x configuration");\r
- return ERROR_FLASH_BANK_INVALID;\r
- }\r
- \r
- str9x_info = malloc(sizeof(str9x_flash_bank_t));\r
- bank->driver_priv = str9x_info;\r
- \r
- str9x_build_block_list(bank);\r
- \r
- str9x_info->write_algorithm = NULL;\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int str9x_blank_check(struct flash_bank_s *bank, int first, int last)\r
-{\r
- target_t *target = bank->target;\r
- u8 *buffer;\r
- int i;\r
- int nBytes;\r
- \r
- if ((first < 0) || (last > bank->num_sectors))\r
- return ERROR_FLASH_SECTOR_INVALID;\r
-\r
- if (bank->target->state != TARGET_HALTED)\r
- {\r
- return ERROR_TARGET_NOT_HALTED;\r
- }\r
- \r
- buffer = malloc(256);\r
- \r
- for (i = first; i <= last; i++)\r
- {\r
- bank->sectors[i].is_erased = 1;\r
-\r
- target->type->read_memory(target, bank->base + bank->sectors[i].offset, 4, 256/4, buffer);\r
- \r
- for (nBytes = 0; nBytes < 256; nBytes++)\r
- {\r
- if (buffer[nBytes] != 0xFF)\r
- {\r
- bank->sectors[i].is_erased = 0;\r
- break;\r
- }\r
- } \r
- }\r
- \r
- free(buffer);\r
-\r
- return ERROR_OK;\r
-}\r
-\r
-int str9x_protect_check(struct flash_bank_s *bank)\r
-{\r
- str9x_flash_bank_t *str9x_info = bank->driver_priv;\r
- target_t *target = bank->target;\r
- \r
- int i;\r
- u32 adr;\r
- u16 status;\r
-\r
- if (bank->target->state != TARGET_HALTED)\r
- {\r
- return ERROR_TARGET_NOT_HALTED;\r
- }\r
-\r
- /* read level one protection */\r
- \r
- adr = bank1start + 0x10;\r
- \r
- target_write_u16(target, adr, 0x90);\r
- target_read_u16(target, adr, &status);\r
- target_write_u16(target, adr, 0xFF);\r
- \r
- for (i = 0; i < bank->num_sectors; i++)\r
- {\r
- if (status & str9x_info->sector_bits[i])\r
- bank->sectors[i].is_protected = 1;\r
- else\r
- bank->sectors[i].is_protected = 0;\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int str9x_erase(struct flash_bank_s *bank, int first, int last)\r
-{\r
- target_t *target = bank->target;\r
- int i;\r
- u32 adr;\r
- u8 status;\r
- \r
- for (i = first; i <= last; i++)\r
- {\r
- adr = bank->base + bank->sectors[i].offset;\r
- \r
- /* erase sectors */\r
- target_write_u16(target, adr, 0x20);\r
- target_write_u16(target, adr, 0xD0);\r
- \r
- /* get status */\r
- target_write_u16(target, adr, 0x70);\r
- \r
- while (1) {\r
- target_read_u8(target, adr, &status);\r
- if( status & 0x80 )\r
- break;\r
- usleep(1000);\r
- }\r
- \r
- /* clear status, also clear read array */\r
- target_write_u16(target, adr, 0x50);\r
- \r
- /* read array command */\r
- target_write_u16(target, adr, 0xFF);\r
- \r
- if( status & 0x22 )\r
- {\r
- ERROR("error erasing flash bank, status: 0x%x", status);\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
- }\r
- \r
- for (i = first; i <= last; i++)\r
- bank->sectors[i].is_erased = 1;\r
-\r
- return ERROR_OK;\r
-}\r
-\r
-int str9x_protect(struct flash_bank_s *bank, int set, int first, int last)\r
-{\r
- target_t *target = bank->target;\r
- int i;\r
- u32 adr;\r
- u8 status;\r
- \r
- if (bank->target->state != TARGET_HALTED)\r
- {\r
- return ERROR_TARGET_NOT_HALTED;\r
- }\r
- \r
- for (i = first; i <= last; i++)\r
- {\r
- /* Level One Protection */\r
- \r
- adr = bank->base + bank->sectors[i].offset;\r
- \r
- target_write_u16(target, adr, 0x60);\r
- if( set )\r
- target_write_u16(target, adr, 0x01);\r
- else\r
- target_write_u16(target, adr, 0xD0);\r
- \r
- /* query status */\r
- target_read_u8(target, adr, &status);\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int str9x_write_block(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count)\r
-{\r
- str9x_flash_bank_t *str9x_info = bank->driver_priv;\r
- target_t *target = bank->target;\r
- u32 buffer_size = 8192;\r
- working_area_t *source;\r
- u32 address = bank->base + offset;\r
- reg_param_t reg_params[4];\r
- armv4_5_algorithm_t armv4_5_info;\r
- int retval;\r
- \r
- u32 str9x_flash_write_code[] = {\r
- /* write: */\r
- 0xe3c14003, /* bic r4, r1, #3 */\r
- 0xe3a03040, /* mov r3, #0x40 */\r
- 0xe1c430b0, /* strh r3, [r4, #0] */\r
- 0xe0d030b2, /* ldrh r3, [r0], #2 */\r
- 0xe0c130b2, /* strh r3, [r1], #2 */\r
- 0xe3a03070, /* mov r3, #0x70 */\r
- 0xe1c430b0, /* strh r3, [r4, #0] */\r
- /* busy: */\r
- 0xe5d43000, /* ldrb r3, [r4, #0] */\r
- 0xe3130080, /* tst r3, #0x80 */\r
- 0x0afffffc, /* beq busy */\r
- 0xe3a05050, /* mov r5, #0x50 */\r
- 0xe1c450b0, /* strh r5, [r4, #0] */\r
- 0xe3a050ff, /* mov r5, #0xFF */\r
- 0xe1c450b0, /* strh r5, [r4, #0] */\r
- 0xe3130012, /* tst r3, #0x12 */\r
- 0x1a000001, /* bne exit */\r
- 0xe2522001, /* subs r2, r2, #1 */\r
- 0x1affffed, /* bne write */\r
- /* exit: */\r
- 0xeafffffe, /* b exit */\r
- };\r
- \r
- /* flash write code */\r
- if (target_alloc_working_area(target, 4 * 19, &str9x_info->write_algorithm) != ERROR_OK)\r
- {\r
- WARNING("no working area available, can't do block memory writes");\r
- return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;\r
- };\r
- \r
- target_write_buffer(target, str9x_info->write_algorithm->address, 19 * 4, (u8*)str9x_flash_write_code);\r
-\r
- /* memory buffer */\r
- while (target_alloc_working_area(target, buffer_size, &source) != ERROR_OK)\r
- {\r
- buffer_size /= 2;\r
- if (buffer_size <= 256)\r
- {\r
- /* if we already allocated the writing code, but failed to get a buffer, free the algorithm */\r
- if (str9x_info->write_algorithm)\r
- target_free_working_area(target, str9x_info->write_algorithm);\r
- \r
- WARNING("no large enough working area available, can't do block memory writes");\r
- return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;\r
- }\r
- }\r
- \r
- armv4_5_info.common_magic = ARMV4_5_COMMON_MAGIC;\r
- armv4_5_info.core_mode = ARMV4_5_MODE_SVC;\r
- armv4_5_info.core_state = ARMV4_5_STATE_ARM;\r
- \r
- init_reg_param(®_params[0], "r0", 32, PARAM_OUT);\r
- init_reg_param(®_params[1], "r1", 32, PARAM_OUT);\r
- init_reg_param(®_params[2], "r2", 32, PARAM_OUT);\r
- init_reg_param(®_params[3], "r3", 32, PARAM_IN);\r
- \r
- while (count > 0)\r
- {\r
- u32 thisrun_count = (count > (buffer_size / 2)) ? (buffer_size / 2) : count;\r
- \r
- target_write_buffer(target, source->address, thisrun_count * 2, buffer);\r
- \r
- buf_set_u32(reg_params[0].value, 0, 32, source->address);\r
- buf_set_u32(reg_params[1].value, 0, 32, address);\r
- buf_set_u32(reg_params[2].value, 0, 32, thisrun_count);\r
-\r
- if ((retval = target->type->run_algorithm(target, 0, NULL, 4, reg_params, str9x_info->write_algorithm->address, str9x_info->write_algorithm->address + (18 * 4), 10000, &armv4_5_info)) != ERROR_OK)\r
- {\r
- target_free_working_area(target, source);\r
- target_free_working_area(target, str9x_info->write_algorithm);\r
- ERROR("error executing str9x flash write algorithm");\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
- \r
- if (buf_get_u32(reg_params[3].value, 0, 32) != 0x80)\r
- {\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
- \r
- buffer += thisrun_count * 2;\r
- address += thisrun_count * 2;\r
- count -= thisrun_count;\r
- }\r
- \r
- target_free_working_area(target, source);\r
- target_free_working_area(target, str9x_info->write_algorithm);\r
- \r
- destroy_reg_param(®_params[0]);\r
- destroy_reg_param(®_params[1]);\r
- destroy_reg_param(®_params[2]);\r
- destroy_reg_param(®_params[3]);\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int str9x_write(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count)\r
-{\r
- target_t *target = bank->target;\r
- u32 words_remaining = (count / 2);\r
- u32 bytes_remaining = (count & 0x00000001);\r
- u32 address = bank->base + offset;\r
- u32 bytes_written = 0;\r
- u8 status;\r
- u32 retval;\r
- u32 check_address = offset;\r
- u32 bank_adr;\r
- int i;\r
- \r
- if (offset & 0x1)\r
- {\r
- WARNING("offset 0x%x breaks required 2-byte alignment", offset);\r
- return ERROR_FLASH_DST_BREAKS_ALIGNMENT;\r
- }\r
- \r
- for (i = 0; i < bank->num_sectors; i++)\r
- {\r
- u32 sec_start = bank->sectors[i].offset;\r
- u32 sec_end = sec_start + bank->sectors[i].size;\r
- \r
- /* check if destination falls within the current sector */\r
- if ((check_address >= sec_start) && (check_address < sec_end))\r
- {\r
- /* check if destination ends in the current sector */\r
- if (offset + count < sec_end)\r
- check_address = offset + count;\r
- else\r
- check_address = sec_end;\r
- }\r
- }\r
- \r
- if (check_address != offset + count)\r
- return ERROR_FLASH_DST_OUT_OF_BANK;\r
- \r
- /* multiple half words (2-byte) to be programmed? */\r
- if (words_remaining > 0) \r
- {\r
- /* try using a block write */\r
- if ((retval = str9x_write_block(bank, buffer, offset, words_remaining)) != ERROR_OK)\r
- {\r
- if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE)\r
- {\r
- /* if block write failed (no sufficient working area),\r
- * we use normal (slow) single dword accesses */ \r
- WARNING("couldn't use block writes, falling back to single memory accesses");\r
- }\r
- else if (retval == ERROR_FLASH_OPERATION_FAILED)\r
- {\r
- ERROR("flash writing failed with error code: 0x%x", retval);\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
- }\r
- else\r
- {\r
- buffer += words_remaining * 2;\r
- address += words_remaining * 2;\r
- words_remaining = 0;\r
- }\r
- }\r
-\r
- while (words_remaining > 0)\r
- {\r
- bank_adr = address & ~0x03;\r
- \r
- /* write data command */\r
- target_write_u16(target, bank_adr, 0x40);\r
- target->type->write_memory(target, address, 2, 1, buffer + bytes_written);\r
- \r
- /* get status command */\r
- target_write_u16(target, bank_adr, 0x70);\r
- \r
- while (1) {\r
- target_read_u8(target, bank_adr, &status);\r
- if( status & 0x80 )\r
- break;\r
- usleep(1000);\r
- }\r
- \r
- /* clear status reg and read array */\r
- target_write_u16(target, bank_adr, 0x50);\r
- target_write_u16(target, bank_adr, 0xFF);\r
- \r
- if (status & 0x10)\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- else if (status & 0x02)\r
- return ERROR_FLASH_OPERATION_FAILED;\r
-\r
- bytes_written += 2;\r
- words_remaining--;\r
- address += 2;\r
- }\r
- \r
- if (bytes_remaining)\r
- {\r
- u8 last_halfword[2] = {0xff, 0xff};\r
- int i = 0;\r
- \r
- while(bytes_remaining > 0)\r
- {\r
- last_halfword[i++] = *(buffer + bytes_written); \r
- bytes_remaining--;\r
- bytes_written++;\r
- }\r
- \r
- bank_adr = address & ~0x03;\r
- \r
- /* write data comamnd */\r
- target_write_u16(target, bank_adr, 0x40);\r
- target->type->write_memory(target, address, 2, 1, last_halfword);\r
- \r
- /* query status command */\r
- target_write_u16(target, bank_adr, 0x70);\r
- \r
- while (1) {\r
- target_read_u8(target, bank_adr, &status);\r
- if( status & 0x80 )\r
- break;\r
- usleep(1000);\r
- }\r
- \r
- /* clear status reg and read array */\r
- target_write_u16(target, bank_adr, 0x50);\r
- target_write_u16(target, bank_adr, 0xFF);\r
- \r
- if (status & 0x10)\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- else if (status & 0x02)\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int str9x_probe(struct flash_bank_s *bank)\r
-{\r
- return ERROR_OK;\r
-}\r
-\r
-int str9x_handle_part_id_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- return ERROR_OK;\r
-}\r
-\r
-int str9x_erase_check(struct flash_bank_s *bank)\r
-{\r
- return str9x_blank_check(bank, 0, bank->num_sectors - 1);\r
-}\r
-\r
-int str9x_info(struct flash_bank_s *bank, char *buf, int buf_size)\r
-{\r
- snprintf(buf, buf_size, "str9x flash driver info" );\r
- return ERROR_OK;\r
-}\r
-\r
-int str9x_handle_flash_config_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- str9x_flash_bank_t *str9x_info;\r
- flash_bank_t *bank;\r
- target_t *target = NULL;\r
- \r
- if (argc < 5)\r
- {\r
- return ERROR_COMMAND_SYNTAX_ERROR;\r
- }\r
- \r
- bank = get_flash_bank_by_num(strtoul(args[0], NULL, 0));\r
- if (!bank)\r
- {\r
- command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);\r
- return ERROR_OK;\r
- }\r
- \r
- str9x_info = bank->driver_priv;\r
- \r
- target = bank->target;\r
- \r
- if (bank->target->state != TARGET_HALTED)\r
- {\r
- return ERROR_TARGET_NOT_HALTED;\r
- }\r
- \r
- /* config flash controller */\r
- target_write_u32(target, FLASH_BBSR, strtoul(args[1], NULL, 0));\r
- target_write_u32(target, FLASH_NBBSR, strtoul(args[2], NULL, 0));\r
- target_write_u32(target, FLASH_BBADR, (strtoul(args[3], NULL, 0) >> 2));\r
- target_write_u32(target, FLASH_NBBADR, (strtoul(args[4], NULL, 0) >> 2));\r
-\r
- /* set bit 18 instruction TCM order as per flash programming manual */\r
- arm966e_write_cp15(target, 62, 0x40000);\r
- \r
- /* enable flash bank 1 */\r
- target_write_u32(target, FLASH_CR, 0x18);\r
- return ERROR_OK;\r
-}\r
+/***************************************************************************
+ * Copyright (C) 2005 by Dominic Rath *
+ * Dominic.Rath@gmx.de *
+ * *
+ * 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 "replacements.h"
+
+#include "str9x.h"
+#include "flash.h"
+#include "target.h"
+#include "log.h"
+#include "armv4_5.h"
+#include "arm966e.h"
+#include "algorithm.h"
+#include "binarybuffer.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+str9x_mem_layout_t mem_layout_str9bank0[] = {
+ {0x00000000, 0x10000, 0x01},
+ {0x00010000, 0x10000, 0x02},
+ {0x00020000, 0x10000, 0x04},
+ {0x00030000, 0x10000, 0x08},
+ {0x00040000, 0x10000, 0x10},
+ {0x00050000, 0x10000, 0x20},
+ {0x00060000, 0x10000, 0x40},
+ {0x00070000, 0x10000, 0x80},
+};
+
+str9x_mem_layout_t mem_layout_str9bank1[] = {
+ {0x00000000, 0x02000, 0x100},
+ {0x00002000, 0x02000, 0x200},
+ {0x00004000, 0x02000, 0x400},
+ {0x00006000, 0x02000, 0x800}
+};
+
+static u32 bank1start = 0x00080000;
+
+int str9x_register_commands(struct command_context_s *cmd_ctx);
+int str9x_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank);
+int str9x_erase(struct flash_bank_s *bank, int first, int last);
+int str9x_protect(struct flash_bank_s *bank, int set, int first, int last);
+int str9x_write(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count);
+int str9x_probe(struct flash_bank_s *bank);
+int str9x_handle_part_id_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+int str9x_protect_check(struct flash_bank_s *bank);
+int str9x_erase_check(struct flash_bank_s *bank);
+int str9x_info(struct flash_bank_s *bank, char *buf, int buf_size);
+
+int str9x_handle_flash_config_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+
+flash_driver_t str9x_flash =
+{
+ .name = "str9x",
+ .register_commands = str9x_register_commands,
+ .flash_bank_command = str9x_flash_bank_command,
+ .erase = str9x_erase,
+ .protect = str9x_protect,
+ .write = str9x_write,
+ .probe = str9x_probe,
+ .auto_probe = str9x_probe,
+ .erase_check = str9x_erase_check,
+ .protect_check = str9x_protect_check,
+ .info = str9x_info
+};
+
+int str9x_register_commands(struct command_context_s *cmd_ctx)
+{
+ command_t *str9x_cmd = register_command(cmd_ctx, NULL, "str9x", NULL, COMMAND_ANY, NULL);
+
+ register_command(cmd_ctx, str9x_cmd, "flash_config", str9x_handle_flash_config_command, COMMAND_EXEC,
+ "configure str9 flash controller");
+
+ return ERROR_OK;
+}
+
+int str9x_build_block_list(struct flash_bank_s *bank)
+{
+ str9x_flash_bank_t *str9x_info = bank->driver_priv;
+
+ int i;
+ int num_sectors = 0;
+ int b0_sectors = 0, b1_sectors = 0;
+
+ switch (bank->size)
+ {
+ case (256 * 1024):
+ b0_sectors = 4;
+ break;
+ case (512 * 1024):
+ b0_sectors = 8;
+ break;
+ case (32 * 1024):
+ b1_sectors = 4;
+ bank1start = bank->base;
+ break;
+ default:
+ ERROR("BUG: unknown bank->size encountered");
+ exit(-1);
+ }
+
+ num_sectors = b0_sectors + b1_sectors;
+
+ bank->num_sectors = num_sectors;
+ bank->sectors = malloc(sizeof(flash_sector_t) * num_sectors);
+ str9x_info->sector_bits = malloc(sizeof(u32) * num_sectors);
+
+ num_sectors = 0;
+
+ for (i = 0; i < b0_sectors; i++)
+ {
+ bank->sectors[num_sectors].offset = mem_layout_str9bank0[i].sector_start;
+ bank->sectors[num_sectors].size = mem_layout_str9bank0[i].sector_size;
+ bank->sectors[num_sectors].is_erased = -1;
+ bank->sectors[num_sectors].is_protected = 1;
+ str9x_info->sector_bits[num_sectors++] = mem_layout_str9bank0[i].sector_bit;
+ }
+
+ for (i = 0; i < b1_sectors; i++)
+ {
+ bank->sectors[num_sectors].offset = mem_layout_str9bank1[i].sector_start;
+ bank->sectors[num_sectors].size = mem_layout_str9bank1[i].sector_size;
+ bank->sectors[num_sectors].is_erased = -1;
+ bank->sectors[num_sectors].is_protected = 1;
+ str9x_info->sector_bits[num_sectors++] = mem_layout_str9bank1[i].sector_bit;
+ }
+
+ return ERROR_OK;
+}
+
+/* flash bank str9x <base> <size> 0 0 <target#>
+ */
+int str9x_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank)
+{
+ str9x_flash_bank_t *str9x_info;
+
+ if (argc < 6)
+ {
+ WARNING("incomplete flash_bank str9x configuration");
+ return ERROR_FLASH_BANK_INVALID;
+ }
+
+ str9x_info = malloc(sizeof(str9x_flash_bank_t));
+ bank->driver_priv = str9x_info;
+
+ str9x_build_block_list(bank);
+
+ str9x_info->write_algorithm = NULL;
+
+ return ERROR_OK;
+}
+
+int str9x_blank_check(struct flash_bank_s *bank, int first, int last)
+{
+ target_t *target = bank->target;
+ u8 *buffer;
+ int i;
+ int nBytes;
+
+ if ((first < 0) || (last > bank->num_sectors))
+ return ERROR_FLASH_SECTOR_INVALID;
+
+ if (bank->target->state != TARGET_HALTED)
+ {
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ buffer = malloc(256);
+
+ for (i = first; i <= last; i++)
+ {
+ bank->sectors[i].is_erased = 1;
+
+ target->type->read_memory(target, bank->base + bank->sectors[i].offset, 4, 256/4, buffer);
+
+ for (nBytes = 0; nBytes < 256; nBytes++)
+ {
+ if (buffer[nBytes] != 0xFF)
+ {
+ bank->sectors[i].is_erased = 0;
+ break;
+ }
+ }
+ }
+
+ free(buffer);
+
+ return ERROR_OK;
+}
+
+int str9x_protect_check(struct flash_bank_s *bank)
+{
+ str9x_flash_bank_t *str9x_info = bank->driver_priv;
+ target_t *target = bank->target;
+
+ int i;
+ u32 adr;
+ u16 status;
+
+ if (bank->target->state != TARGET_HALTED)
+ {
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ /* read level one protection */
+
+ adr = bank1start + 0x10;
+
+ target_write_u16(target, adr, 0x90);
+ target_read_u16(target, adr, &status);
+ target_write_u16(target, adr, 0xFF);
+
+ for (i = 0; i < bank->num_sectors; i++)
+ {
+ if (status & str9x_info->sector_bits[i])
+ bank->sectors[i].is_protected = 1;
+ else
+ bank->sectors[i].is_protected = 0;
+ }
+
+ return ERROR_OK;
+}
+
+int str9x_erase(struct flash_bank_s *bank, int first, int last)
+{
+ target_t *target = bank->target;
+ int i;
+ u32 adr;
+ u8 status;
+
+ for (i = first; i <= last; i++)
+ {
+ adr = bank->base + bank->sectors[i].offset;
+
+ /* erase sectors */
+ target_write_u16(target, adr, 0x20);
+ target_write_u16(target, adr, 0xD0);
+
+ /* get status */
+ target_write_u16(target, adr, 0x70);
+
+ while (1) {
+ target_read_u8(target, adr, &status);
+ if( status & 0x80 )
+ break;
+ usleep(1000);
+ }
+
+ /* clear status, also clear read array */
+ target_write_u16(target, adr, 0x50);
+
+ /* read array command */
+ target_write_u16(target, adr, 0xFF);
+
+ if( status & 0x22 )
+ {
+ ERROR("error erasing flash bank, status: 0x%x", status);
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+ }
+
+ for (i = first; i <= last; i++)
+ bank->sectors[i].is_erased = 1;
+
+ return ERROR_OK;
+}
+
+int str9x_protect(struct flash_bank_s *bank, int set, int first, int last)
+{
+ target_t *target = bank->target;
+ int i;
+ u32 adr;
+ u8 status;
+
+ if (bank->target->state != TARGET_HALTED)
+ {
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ for (i = first; i <= last; i++)
+ {
+ /* Level One Protection */
+
+ adr = bank->base + bank->sectors[i].offset;
+
+ target_write_u16(target, adr, 0x60);
+ if( set )
+ target_write_u16(target, adr, 0x01);
+ else
+ target_write_u16(target, adr, 0xD0);
+
+ /* query status */
+ target_read_u8(target, adr, &status);
+ }
+
+ return ERROR_OK;
+}
+
+int str9x_write_block(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count)
+{
+ str9x_flash_bank_t *str9x_info = bank->driver_priv;
+ target_t *target = bank->target;
+ u32 buffer_size = 8192;
+ working_area_t *source;
+ u32 address = bank->base + offset;
+ reg_param_t reg_params[4];
+ armv4_5_algorithm_t armv4_5_info;
+ int retval;
+
+ u32 str9x_flash_write_code[] = {
+ /* write: */
+ 0xe3c14003, /* bic r4, r1, #3 */
+ 0xe3a03040, /* mov r3, #0x40 */
+ 0xe1c430b0, /* strh r3, [r4, #0] */
+ 0xe0d030b2, /* ldrh r3, [r0], #2 */
+ 0xe0c130b2, /* strh r3, [r1], #2 */
+ 0xe3a03070, /* mov r3, #0x70 */
+ 0xe1c430b0, /* strh r3, [r4, #0] */
+ /* busy: */
+ 0xe5d43000, /* ldrb r3, [r4, #0] */
+ 0xe3130080, /* tst r3, #0x80 */
+ 0x0afffffc, /* beq busy */
+ 0xe3a05050, /* mov r5, #0x50 */
+ 0xe1c450b0, /* strh r5, [r4, #0] */
+ 0xe3a050ff, /* mov r5, #0xFF */
+ 0xe1c450b0, /* strh r5, [r4, #0] */
+ 0xe3130012, /* tst r3, #0x12 */
+ 0x1a000001, /* bne exit */
+ 0xe2522001, /* subs r2, r2, #1 */
+ 0x1affffed, /* bne write */
+ /* exit: */
+ 0xeafffffe, /* b exit */
+ };
+
+ /* flash write code */
+ if (target_alloc_working_area(target, 4 * 19, &str9x_info->write_algorithm) != ERROR_OK)
+ {
+ WARNING("no working area available, can't do block memory writes");
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ };
+
+ target_write_buffer(target, str9x_info->write_algorithm->address, 19 * 4, (u8*)str9x_flash_write_code);
+
+ /* memory buffer */
+ while (target_alloc_working_area(target, buffer_size, &source) != ERROR_OK)
+ {
+ buffer_size /= 2;
+ if (buffer_size <= 256)
+ {
+ /* if we already allocated the writing code, but failed to get a buffer, free the algorithm */
+ if (str9x_info->write_algorithm)
+ target_free_working_area(target, str9x_info->write_algorithm);
+
+ WARNING("no large enough working area available, can't do block memory writes");
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+ }
+
+ armv4_5_info.common_magic = ARMV4_5_COMMON_MAGIC;
+ armv4_5_info.core_mode = ARMV4_5_MODE_SVC;
+ armv4_5_info.core_state = ARMV4_5_STATE_ARM;
+
+ init_reg_param(®_params[0], "r0", 32, PARAM_OUT);
+ init_reg_param(®_params[1], "r1", 32, PARAM_OUT);
+ init_reg_param(®_params[2], "r2", 32, PARAM_OUT);
+ init_reg_param(®_params[3], "r3", 32, PARAM_IN);
+
+ while (count > 0)
+ {
+ u32 thisrun_count = (count > (buffer_size / 2)) ? (buffer_size / 2) : count;
+
+ target_write_buffer(target, source->address, thisrun_count * 2, buffer);
+
+ buf_set_u32(reg_params[0].value, 0, 32, source->address);
+ buf_set_u32(reg_params[1].value, 0, 32, address);
+ buf_set_u32(reg_params[2].value, 0, 32, thisrun_count);
+
+ if ((retval = target->type->run_algorithm(target, 0, NULL, 4, reg_params, str9x_info->write_algorithm->address, str9x_info->write_algorithm->address + (18 * 4), 10000, &armv4_5_info)) != ERROR_OK)
+ {
+ target_free_working_area(target, source);
+ target_free_working_area(target, str9x_info->write_algorithm);
+ ERROR("error executing str9x flash write algorithm");
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ if (buf_get_u32(reg_params[3].value, 0, 32) != 0x80)
+ {
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ buffer += thisrun_count * 2;
+ address += thisrun_count * 2;
+ count -= thisrun_count;
+ }
+
+ target_free_working_area(target, source);
+ target_free_working_area(target, str9x_info->write_algorithm);
+
+ destroy_reg_param(®_params[0]);
+ destroy_reg_param(®_params[1]);
+ destroy_reg_param(®_params[2]);
+ destroy_reg_param(®_params[3]);
+
+ return ERROR_OK;
+}
+
+int str9x_write(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count)
+{
+ target_t *target = bank->target;
+ u32 words_remaining = (count / 2);
+ u32 bytes_remaining = (count & 0x00000001);
+ u32 address = bank->base + offset;
+ u32 bytes_written = 0;
+ u8 status;
+ u32 retval;
+ u32 check_address = offset;
+ u32 bank_adr;
+ int i;
+
+ if (offset & 0x1)
+ {
+ WARNING("offset 0x%x breaks required 2-byte alignment", offset);
+ return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
+ }
+
+ for (i = 0; i < bank->num_sectors; i++)
+ {
+ u32 sec_start = bank->sectors[i].offset;
+ u32 sec_end = sec_start + bank->sectors[i].size;
+
+ /* check if destination falls within the current sector */
+ if ((check_address >= sec_start) && (check_address < sec_end))
+ {
+ /* check if destination ends in the current sector */
+ if (offset + count < sec_end)
+ check_address = offset + count;
+ else
+ check_address = sec_end;
+ }
+ }
+
+ if (check_address != offset + count)
+ return ERROR_FLASH_DST_OUT_OF_BANK;
+
+ /* multiple half words (2-byte) to be programmed? */
+ if (words_remaining > 0)
+ {
+ /* try using a block write */
+ if ((retval = str9x_write_block(bank, buffer, offset, words_remaining)) != ERROR_OK)
+ {
+ if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE)
+ {
+ /* if block write failed (no sufficient working area),
+ * we use normal (slow) single dword accesses */
+ WARNING("couldn't use block writes, falling back to single memory accesses");
+ }
+ else if (retval == ERROR_FLASH_OPERATION_FAILED)
+ {
+ ERROR("flash writing failed with error code: 0x%x", retval);
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+ }
+ else
+ {
+ buffer += words_remaining * 2;
+ address += words_remaining * 2;
+ words_remaining = 0;
+ }
+ }
+
+ while (words_remaining > 0)
+ {
+ bank_adr = address & ~0x03;
+
+ /* write data command */
+ target_write_u16(target, bank_adr, 0x40);
+ target->type->write_memory(target, address, 2, 1, buffer + bytes_written);
+
+ /* get status command */
+ target_write_u16(target, bank_adr, 0x70);
+
+ while (1) {
+ target_read_u8(target, bank_adr, &status);
+ if( status & 0x80 )
+ break;
+ usleep(1000);
+ }
+
+ /* clear status reg and read array */
+ target_write_u16(target, bank_adr, 0x50);
+ target_write_u16(target, bank_adr, 0xFF);
+
+ if (status & 0x10)
+ return ERROR_FLASH_OPERATION_FAILED;
+ else if (status & 0x02)
+ return ERROR_FLASH_OPERATION_FAILED;
+
+ bytes_written += 2;
+ words_remaining--;
+ address += 2;
+ }
+
+ if (bytes_remaining)
+ {
+ u8 last_halfword[2] = {0xff, 0xff};
+ int i = 0;
+
+ while(bytes_remaining > 0)
+ {
+ last_halfword[i++] = *(buffer + bytes_written);
+ bytes_remaining--;
+ bytes_written++;
+ }
+
+ bank_adr = address & ~0x03;
+
+ /* write data comamnd */
+ target_write_u16(target, bank_adr, 0x40);
+ target->type->write_memory(target, address, 2, 1, last_halfword);
+
+ /* query status command */
+ target_write_u16(target, bank_adr, 0x70);
+
+ while (1) {
+ target_read_u8(target, bank_adr, &status);
+ if( status & 0x80 )
+ break;
+ usleep(1000);
+ }
+
+ /* clear status reg and read array */
+ target_write_u16(target, bank_adr, 0x50);
+ target_write_u16(target, bank_adr, 0xFF);
+
+ if (status & 0x10)
+ return ERROR_FLASH_OPERATION_FAILED;
+ else if (status & 0x02)
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ return ERROR_OK;
+}
+
+int str9x_probe(struct flash_bank_s *bank)
+{
+ return ERROR_OK;
+}
+
+int str9x_handle_part_id_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ return ERROR_OK;
+}
+
+int str9x_erase_check(struct flash_bank_s *bank)
+{
+ return str9x_blank_check(bank, 0, bank->num_sectors - 1);
+}
+
+int str9x_info(struct flash_bank_s *bank, char *buf, int buf_size)
+{
+ snprintf(buf, buf_size, "str9x flash driver info" );
+ return ERROR_OK;
+}
+
+int str9x_handle_flash_config_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ str9x_flash_bank_t *str9x_info;
+ flash_bank_t *bank;
+ target_t *target = NULL;
+
+ if (argc < 5)
+ {
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ bank = get_flash_bank_by_num(strtoul(args[0], NULL, 0));
+ if (!bank)
+ {
+ command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);
+ return ERROR_OK;
+ }
+
+ str9x_info = bank->driver_priv;
+
+ target = bank->target;
+
+ if (bank->target->state != TARGET_HALTED)
+ {
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ /* config flash controller */
+ target_write_u32(target, FLASH_BBSR, strtoul(args[1], NULL, 0));
+ target_write_u32(target, FLASH_NBBSR, strtoul(args[2], NULL, 0));
+ target_write_u32(target, FLASH_BBADR, (strtoul(args[3], NULL, 0) >> 2));
+ target_write_u32(target, FLASH_NBBADR, (strtoul(args[4], NULL, 0) >> 2));
+
+ /* set bit 18 instruction TCM order as per flash programming manual */
+ arm966e_write_cp15(target, 62, 0x40000);
+
+ /* enable flash bank 1 */
+ target_write_u32(target, FLASH_CR, 0x18);
+ return ERROR_OK;
+}
-/***************************************************************************\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 "replacements.h"\r
-\r
-#include "str9xpec.h"\r
-#include "flash.h"\r
-#include "target.h"\r
-#include "log.h"\r
-#include "armv4_5.h"\r
-#include "arm7_9_common.h"\r
-#include "jtag.h"\r
-#include "binarybuffer.h"\r
-\r
-#include <stdlib.h>\r
-#include <string.h>\r
-#include <unistd.h>\r
-#include <getopt.h>\r
-\r
-str9xpec_mem_layout_t mem_layout_str9pec[] = {\r
- {0x00000000, 0x10000, 0},\r
- {0x00010000, 0x10000, 1},\r
- {0x00020000, 0x10000, 2},\r
- {0x00030000, 0x10000, 3},\r
- {0x00040000, 0x10000, 4},\r
- {0x00050000, 0x10000, 5},\r
- {0x00060000, 0x10000, 6},\r
- {0x00070000, 0x10000, 7},\r
- {0x00080000, 0x02000, 32},\r
- {0x00082000, 0x02000, 33},\r
- {0x00084000, 0x02000, 34},\r
- {0x00086000, 0x02000, 35}\r
-};\r
-\r
-int str9xpec_register_commands(struct command_context_s *cmd_ctx);\r
-int str9xpec_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank);\r
-int str9xpec_erase(struct flash_bank_s *bank, int first, int last);\r
-int str9xpec_protect(struct flash_bank_s *bank, int set, int first, int last);\r
-int str9xpec_write(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count);\r
-int str9xpec_probe(struct flash_bank_s *bank);\r
-int str9xpec_handle_part_id_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-int str9xpec_protect_check(struct flash_bank_s *bank);\r
-int str9xpec_erase_check(struct flash_bank_s *bank);\r
-int str9xpec_info(struct flash_bank_s *bank, char *buf, int buf_size);\r
-\r
-int str9xpec_erase_area(struct flash_bank_s *bank, int first, int last);\r
-int str9xpec_set_address(struct flash_bank_s *bank, u8 sector);\r
-int str9xpec_write_options(struct flash_bank_s *bank);\r
-\r
-int str9xpec_handle_flash_options_cmap_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-int str9xpec_handle_flash_options_lvdthd_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-int str9xpec_handle_flash_options_lvdsel_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-int str9xpec_handle_flash_options_lvdwarn_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-int str9xpec_handle_flash_options_read_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-int str9xpec_handle_flash_options_write_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-int str9xpec_handle_flash_lock_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-int str9xpec_handle_flash_unlock_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-int str9xpec_handle_flash_enable_turbo_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-int str9xpec_handle_flash_disable_turbo_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-\r
-flash_driver_t str9xpec_flash =\r
-{\r
- .name = "str9xpec",\r
- .register_commands = str9xpec_register_commands,\r
- .flash_bank_command = str9xpec_flash_bank_command,\r
- .erase = str9xpec_erase,\r
- .protect = str9xpec_protect,\r
- .write = str9xpec_write,\r
- .probe = str9xpec_probe,\r
- .auto_probe = str9xpec_probe,\r
- .erase_check = str9xpec_erase_check,\r
- .protect_check = str9xpec_protect_check,\r
- .info = str9xpec_info\r
-};\r
-\r
-int str9xpec_register_commands(struct command_context_s *cmd_ctx)\r
-{\r
- command_t *str9xpec_cmd = register_command(cmd_ctx, NULL, "str9xpec", NULL, COMMAND_ANY, "str9xpec flash specific commands");\r
- \r
- register_command(cmd_ctx, str9xpec_cmd, "enable_turbo", str9xpec_handle_flash_enable_turbo_command, COMMAND_EXEC,\r
- "enable str9xpec turbo mode");\r
- register_command(cmd_ctx, str9xpec_cmd, "disable_turbo", str9xpec_handle_flash_disable_turbo_command, COMMAND_EXEC,\r
- "disable str9xpec turbo mode");\r
- register_command(cmd_ctx, str9xpec_cmd, "options_cmap", str9xpec_handle_flash_options_cmap_command, COMMAND_EXEC,\r
- "configure str9xpec boot sector");\r
- register_command(cmd_ctx, str9xpec_cmd, "options_lvdthd", str9xpec_handle_flash_options_lvdthd_command, COMMAND_EXEC,\r
- "configure str9xpec lvd threshold");\r
- register_command(cmd_ctx, str9xpec_cmd, "options_lvdsel", str9xpec_handle_flash_options_lvdsel_command, COMMAND_EXEC,\r
- "configure str9xpec lvd selection");\r
- register_command(cmd_ctx, str9xpec_cmd, "options_lvdwarn", str9xpec_handle_flash_options_lvdwarn_command, COMMAND_EXEC,\r
- "configure str9xpec lvd warning");\r
- register_command(cmd_ctx, str9xpec_cmd, "options_read", str9xpec_handle_flash_options_read_command, COMMAND_EXEC,\r
- "read str9xpec options");\r
- register_command(cmd_ctx, str9xpec_cmd, "options_write", str9xpec_handle_flash_options_write_command, COMMAND_EXEC,\r
- "write str9xpec options");\r
- register_command(cmd_ctx, str9xpec_cmd, "lock", str9xpec_handle_flash_lock_command, COMMAND_EXEC,\r
- "lock str9xpec device");\r
- register_command(cmd_ctx, str9xpec_cmd, "unlock", str9xpec_handle_flash_unlock_command, COMMAND_EXEC,\r
- "unlock str9xpec device");\r
- register_command(cmd_ctx, str9xpec_cmd, "part_id", str9xpec_handle_part_id_command, COMMAND_EXEC,\r
- "print part id of str9xpec flash bank <num>");\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int str9xpec_set_instr(int chain_pos, u32 new_instr, enum tap_state end_state)\r
-{\r
- jtag_device_t *device = jtag_get_device(chain_pos);\r
- \r
- if (device == NULL)\r
- {\r
- DEBUG("Invalid Target");\r
- return ERROR_TARGET_INVALID;\r
- }\r
- \r
- if (buf_get_u32(device->cur_instr, 0, device->ir_length) != new_instr)\r
- {\r
- scan_field_t field;\r
- \r
- field.device = chain_pos;\r
- field.num_bits = device->ir_length;\r
- field.out_value = calloc(CEIL(field.num_bits, 8), 1);\r
- buf_set_u32(field.out_value, 0, field.num_bits, new_instr);\r
- field.out_mask = NULL;\r
- field.in_value = NULL;\r
- field.in_check_value = NULL;\r
- field.in_check_mask = NULL;\r
- field.in_handler = NULL;\r
- field.in_handler_priv = NULL;\r
- \r
- jtag_add_ir_scan(1, &field, end_state, NULL);\r
- \r
- free(field.out_value);\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-u8 str9xpec_isc_status(int chain_pos)\r
-{\r
- scan_field_t field;\r
- u8 status;\r
- \r
- if (str9xpec_set_instr(chain_pos, ISC_NOOP, TAP_PI) != ERROR_OK)\r
- return ISC_STATUS_ERROR;\r
- \r
- field.device = chain_pos;\r
- field.num_bits = 8;\r
- field.out_value = NULL;\r
- field.out_mask = NULL;\r
- field.in_value = &status;\r
- field.in_check_value = NULL;\r
- field.in_check_mask = NULL;\r
- field.in_handler = NULL;\r
- field.in_handler_priv = NULL;\r
- \r
- jtag_add_dr_scan(1, &field, TAP_RTI, NULL);\r
- jtag_execute_queue();\r
- \r
- DEBUG("status: 0x%2.2x", status);\r
- \r
- if (status & ISC_STATUS_SECURITY)\r
- INFO("Device Security Bit Set");\r
- \r
- return status;\r
-}\r
-\r
-int str9xpec_isc_enable(struct flash_bank_s *bank)\r
-{\r
- u8 status;\r
- u32 chain_pos;\r
- str9xpec_flash_controller_t *str9xpec_info = bank->driver_priv;\r
- \r
- chain_pos = str9xpec_info->chain_pos;\r
- \r
- if (str9xpec_info->isc_enable)\r
- return ERROR_OK;\r
- \r
- /* enter isc mode */\r
- if (str9xpec_set_instr(chain_pos, ISC_ENABLE, TAP_RTI) != ERROR_OK)\r
- return ERROR_TARGET_INVALID;\r
- \r
- /* check ISC status */\r
- status = str9xpec_isc_status(chain_pos);\r
- if (status & ISC_STATUS_MODE)\r
- {\r
- /* we have entered isc mode */\r
- str9xpec_info->isc_enable = 1;\r
- DEBUG("ISC_MODE Enabled");\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int str9xpec_isc_disable(struct flash_bank_s *bank)\r
-{\r
- u8 status;\r
- u32 chain_pos;\r
- str9xpec_flash_controller_t *str9xpec_info = bank->driver_priv;\r
- \r
- chain_pos = str9xpec_info->chain_pos;\r
- \r
- if (!str9xpec_info->isc_enable)\r
- return ERROR_OK;\r
- \r
- if (str9xpec_set_instr(chain_pos, ISC_DISABLE, TAP_RTI) != ERROR_OK)\r
- return ERROR_TARGET_INVALID;\r
- \r
- /* delay to handle aborts */\r
- jtag_add_sleep(50);\r
- \r
- /* check ISC status */\r
- status = str9xpec_isc_status(chain_pos);\r
- if (!(status & ISC_STATUS_MODE))\r
- {\r
- /* we have left isc mode */\r
- str9xpec_info->isc_enable = 0;\r
- DEBUG("ISC_MODE Disabled");\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int str9xpec_read_config(struct flash_bank_s *bank)\r
-{\r
- scan_field_t field;\r
- u8 status;\r
- u32 chain_pos;\r
- \r
- str9xpec_flash_controller_t *str9xpec_info = bank->driver_priv;\r
- \r
- chain_pos = str9xpec_info->chain_pos;\r
- \r
- DEBUG("ISC_CONFIGURATION");\r
- \r
- /* execute ISC_CONFIGURATION command */\r
- str9xpec_set_instr(chain_pos, ISC_CONFIGURATION, TAP_PI);\r
- \r
- field.device = chain_pos;\r
- field.num_bits = 64;\r
- field.out_value = NULL;\r
- field.out_mask = NULL;\r
- field.in_value = str9xpec_info->options;\r
- field.in_check_value = NULL;\r
- field.in_check_mask = NULL;\r
- field.in_handler = NULL;\r
- field.in_handler_priv = NULL;\r
- \r
- jtag_add_dr_scan(1, &field, TAP_RTI, NULL);\r
- jtag_execute_queue();\r
- \r
- status = str9xpec_isc_status(chain_pos);\r
- \r
- return status;\r
-}\r
-\r
-int str9xpec_build_block_list(struct flash_bank_s *bank)\r
-{\r
- str9xpec_flash_controller_t *str9xpec_info = bank->driver_priv;\r
- \r
- int i;\r
- int num_sectors = 0, b0_sectors = 0;\r
- \r
- switch (bank->size)\r
- {\r
- case (256 * 1024):\r
- b0_sectors = 4;\r
- break;\r
- case (512 * 1024):\r
- b0_sectors = 8;\r
- break;\r
- default:\r
- ERROR("BUG: unknown bank->size encountered");\r
- exit(-1);\r
- }\r
- \r
- /* include bank 1 sectors */\r
- num_sectors = b0_sectors + 4;\r
- bank->size += (32 * 1024);\r
- \r
- bank->num_sectors = num_sectors;\r
- bank->sectors = malloc(sizeof(flash_sector_t) * num_sectors);\r
- str9xpec_info->sector_bits = malloc(sizeof(u32) * num_sectors);\r
- \r
- num_sectors = 0;\r
- \r
- for (i = 0; i < b0_sectors; i++)\r
- {\r
- bank->sectors[num_sectors].offset = mem_layout_str9pec[i].sector_start;\r
- bank->sectors[num_sectors].size = mem_layout_str9pec[i].sector_size;\r
- bank->sectors[num_sectors].is_erased = -1;\r
- bank->sectors[num_sectors].is_protected = 1;\r
- str9xpec_info->sector_bits[num_sectors++] = mem_layout_str9pec[i].sector_bit;\r
- }\r
- \r
- for (i = 8; i < 12; i++)\r
- {\r
- bank->sectors[num_sectors].offset = mem_layout_str9pec[i].sector_start;\r
- bank->sectors[num_sectors].size = mem_layout_str9pec[i].sector_size;\r
- bank->sectors[num_sectors].is_erased = -1;\r
- bank->sectors[num_sectors].is_protected = 1;\r
- str9xpec_info->sector_bits[num_sectors++] = mem_layout_str9pec[i].sector_bit;\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-/* flash bank str9x <base> <size> 0 0 <target#>\r
- */\r
-int str9xpec_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank)\r
-{\r
- str9xpec_flash_controller_t *str9xpec_info;\r
- armv4_5_common_t *armv4_5 = NULL;\r
- arm7_9_common_t *arm7_9 = NULL;\r
- arm_jtag_t *jtag_info = NULL;\r
- \r
- if (argc < 6)\r
- {\r
- WARNING("incomplete flash_bank str9x configuration");\r
- return ERROR_FLASH_BANK_INVALID;\r
- }\r
- \r
- str9xpec_info = malloc(sizeof(str9xpec_flash_controller_t));\r
- bank->driver_priv = str9xpec_info;\r
- \r
- if (bank->base != 0x00000000)\r
- {\r
- WARNING("overriding flash base address for STR91x device with 0x00000000");\r
- bank->base = 0x00000000;\r
- }\r
-\r
- /* find out jtag position of flash controller\r
- * it is always after the arm966 core */\r
- \r
- armv4_5 = bank->target->arch_info;\r
- arm7_9 = armv4_5->arch_info;\r
- jtag_info = &arm7_9->jtag_info;\r
- \r
- str9xpec_info->chain_pos = (jtag_info->chain_pos - 1);\r
- str9xpec_info->isc_enable = 0;\r
- str9xpec_info->devarm = NULL;\r
- \r
- str9xpec_build_block_list(bank);\r
- \r
- /* clear option byte register */\r
- buf_set_u32(str9xpec_info->options, 0, 64, 0);\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int str9xpec_blank_check(struct flash_bank_s *bank, int first, int last)\r
-{\r
- scan_field_t field;\r
- u8 status;\r
- u32 chain_pos;\r
- int i;\r
- u8 *buffer = NULL;\r
- \r
- str9xpec_flash_controller_t *str9xpec_info = bank->driver_priv;\r
- \r
- chain_pos = str9xpec_info->chain_pos;\r
- \r
- if (!str9xpec_info->isc_enable) {\r
- str9xpec_isc_enable( bank );\r
- }\r
- \r
- if (!str9xpec_info->isc_enable) {\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
- \r
- buffer = calloc(CEIL(64, 8), 1);\r
-\r
- DEBUG("blank check: first_bank: %i, last_bank: %i", first, last);\r
- \r
- for (i = first; i <= last; i++) {\r
- buf_set_u32(buffer, str9xpec_info->sector_bits[i], 1, 1);\r
- }\r
- \r
- /* execute ISC_BLANK_CHECK command */\r
- str9xpec_set_instr(chain_pos, ISC_BLANK_CHECK, TAP_PI);\r
- \r
- field.device = chain_pos;\r
- field.num_bits = 64;\r
- field.out_value = buffer;\r
- field.out_mask = NULL;\r
- field.in_value = NULL;\r
- field.in_check_value = NULL;\r
- field.in_check_mask = NULL;\r
- field.in_handler = NULL;\r
- field.in_handler_priv = NULL;\r
- \r
- jtag_add_dr_scan(1, &field, TAP_RTI, NULL);\r
- jtag_add_sleep(40000);\r
- \r
- /* read blank check result */\r
- field.device = chain_pos;\r
- field.num_bits = 64;\r
- field.out_value = NULL;\r
- field.out_mask = NULL;\r
- field.in_value = buffer;\r
- field.in_check_value = NULL;\r
- field.in_check_mask = NULL;\r
- field.in_handler = NULL;\r
- field.in_handler_priv = NULL;\r
- \r
- jtag_add_dr_scan(1, &field, TAP_PI, NULL);\r
- jtag_execute_queue();\r
- \r
- status = str9xpec_isc_status(chain_pos);\r
- \r
- for (i = first; i <= last; i++)\r
- {\r
- if (buf_get_u32(buffer, str9xpec_info->sector_bits[i], 1))\r
- bank->sectors[i].is_erased = 0;\r
- else\r
- bank->sectors[i].is_erased = 1;\r
- }\r
- \r
- free(buffer);\r
- \r
- str9xpec_isc_disable(bank);\r
- \r
- if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS)\r
- return ERROR_FLASH_OPERATION_FAILED; \r
- return ERROR_OK;\r
-}\r
-\r
-int str9xpec_protect_check(struct flash_bank_s *bank)\r
-{\r
- u8 status;\r
- int i;\r
- \r
- str9xpec_flash_controller_t *str9xpec_info = bank->driver_priv;\r
- \r
- status = str9xpec_read_config(bank);\r
- \r
- for (i = 0; i < bank->num_sectors; i++)\r
- {\r
- if (buf_get_u32(str9xpec_info->options, str9xpec_info->sector_bits[i], 1))\r
- bank->sectors[i].is_protected = 1;\r
- else\r
- bank->sectors[i].is_protected = 0;\r
- }\r
- \r
- if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS)\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- return ERROR_OK;\r
-}\r
-\r
-int str9xpec_erase_area(struct flash_bank_s *bank, int first, int last)\r
-{\r
- scan_field_t field;\r
- u8 status;\r
- u32 chain_pos;\r
- int i;\r
- u8 *buffer = NULL;\r
- \r
- str9xpec_flash_controller_t *str9xpec_info = bank->driver_priv;\r
- \r
- chain_pos = str9xpec_info->chain_pos;\r
- \r
- if (!str9xpec_info->isc_enable) {\r
- str9xpec_isc_enable( bank );\r
- }\r
- \r
- if (!str9xpec_info->isc_enable) {\r
- return ISC_STATUS_ERROR;\r
- }\r
- \r
- buffer = calloc(CEIL(64, 8), 1);\r
- \r
- DEBUG("erase: first_bank: %i, last_bank: %i", first, last);\r
- \r
- /* last bank: 0xFF signals a full erase (unlock complete device) */\r
- /* last bank: 0xFE signals a option byte erase */\r
- if (last == 0xFF)\r
- {\r
- for (i = 0; i < 64; i++) {\r
- buf_set_u32(buffer, i, 1, 1);\r
- } \r
- }\r
- else if (last == 0xFE)\r
- {\r
- buf_set_u32(buffer, 49, 1, 1);\r
- }\r
- else\r
- { \r
- for (i = first; i <= last; i++) {\r
- buf_set_u32(buffer, str9xpec_info->sector_bits[i], 1, 1);\r
- }\r
- }\r
- \r
- DEBUG("ISC_ERASE");\r
- \r
- /* execute ISC_ERASE command */\r
- str9xpec_set_instr(chain_pos, ISC_ERASE, TAP_PI);\r
- \r
- field.device = chain_pos;\r
- field.num_bits = 64;\r
- field.out_value = buffer;\r
- field.out_mask = NULL;\r
- field.in_value = NULL;\r
- field.in_check_value = NULL;\r
- field.in_check_mask = NULL;\r
- field.in_handler = NULL;\r
- field.in_handler_priv = NULL;\r
- \r
- jtag_add_dr_scan(1, &field, TAP_RTI, NULL);\r
- jtag_execute_queue();\r
- \r
- jtag_add_sleep(10);\r
- \r
- /* wait for erase completion */\r
- while (!((status = str9xpec_isc_status(chain_pos)) & ISC_STATUS_BUSY)) {\r
- usleep(1000);\r
- }\r
- \r
- free(buffer);\r
- \r
- str9xpec_isc_disable(bank);\r
- \r
- return status;\r
-}\r
-\r
-int str9xpec_erase(struct flash_bank_s *bank, int first, int last)\r
-{\r
- int status;\r
- \r
- status = str9xpec_erase_area(bank, first, last);\r
- \r
- if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS)\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int str9xpec_lock_device(struct flash_bank_s *bank)\r
-{\r
- scan_field_t field;\r
- u8 status;\r
- u32 chain_pos;\r
- str9xpec_flash_controller_t *str9xpec_info = NULL;\r
- \r
- str9xpec_info = bank->driver_priv;\r
- chain_pos = str9xpec_info->chain_pos;\r
- \r
- if (!str9xpec_info->isc_enable) {\r
- str9xpec_isc_enable( bank );\r
- }\r
- \r
- if (!str9xpec_info->isc_enable) {\r
- return ISC_STATUS_ERROR;\r
- }\r
- \r
- /* set security address */\r
- str9xpec_set_address(bank, 0x80);\r
- \r
- /* execute ISC_PROGRAM command */\r
- str9xpec_set_instr(chain_pos, ISC_PROGRAM_SECURITY, TAP_RTI);\r
- \r
- str9xpec_set_instr(chain_pos, ISC_NOOP, TAP_PI);\r
- \r
- do {\r
- field.device = chain_pos;\r
- field.num_bits = 8;\r
- field.out_value = NULL;\r
- field.out_mask = NULL;\r
- field.in_value = &status;\r
- field.in_check_value = NULL;\r
- field.in_check_mask = NULL;\r
- field.in_handler = NULL;\r
- field.in_handler_priv = NULL;\r
- \r
- jtag_add_dr_scan(1, &field, -1, NULL);\r
- jtag_execute_queue();\r
- \r
- } while(!(status & ISC_STATUS_BUSY));\r
- \r
- str9xpec_isc_disable(bank);\r
- \r
- return status;\r
-}\r
-\r
-int str9xpec_unlock_device(struct flash_bank_s *bank)\r
-{\r
- u8 status;\r
- \r
- status = str9xpec_erase_area(bank, 0, 255);\r
- \r
- return status;\r
-}\r
-\r
-int str9xpec_protect(struct flash_bank_s *bank, int set, int first, int last)\r
-{\r
- u8 status;\r
- int i;\r
- \r
- str9xpec_flash_controller_t *str9xpec_info = bank->driver_priv;\r
- \r
- status = str9xpec_read_config(bank);\r
- \r
- if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS)\r
- return ERROR_FLASH_OPERATION_FAILED;\r
-\r
- DEBUG("protect: first_bank: %i, last_bank: %i", first, last);\r
- \r
- /* last bank: 0xFF signals a full device protect */\r
- if (last == 0xFF)\r
- {\r
- if( set )\r
- {\r
- status = str9xpec_lock_device(bank);\r
- }\r
- else\r
- {\r
- /* perform full erase to unlock device */\r
- status = str9xpec_unlock_device(bank);\r
- }\r
- }\r
- else\r
- { \r
- for (i = first; i <= last; i++)\r
- {\r
- if( set )\r
- buf_set_u32(str9xpec_info->options, str9xpec_info->sector_bits[i], 1, 1);\r
- else\r
- buf_set_u32(str9xpec_info->options, str9xpec_info->sector_bits[i], 1, 0);\r
- }\r
- \r
- status = str9xpec_write_options(bank);\r
- }\r
- \r
- if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS)\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int str9xpec_set_address(struct flash_bank_s *bank, u8 sector)\r
-{\r
- u32 chain_pos;\r
- scan_field_t field;\r
- str9xpec_flash_controller_t *str9xpec_info = bank->driver_priv;\r
- \r
- chain_pos = str9xpec_info->chain_pos;\r
- \r
- /* set flash controller address */\r
- str9xpec_set_instr(chain_pos, ISC_ADDRESS_SHIFT, TAP_PI);\r
- \r
- field.device = chain_pos;\r
- field.num_bits = 8;\r
- field.out_value = §or;\r
- field.out_mask = NULL;\r
- field.in_value = NULL;\r
- field.in_check_value = NULL;\r
- field.in_check_mask = NULL;\r
- field.in_handler = NULL;\r
- field.in_handler_priv = NULL;\r
- \r
- jtag_add_dr_scan(1, &field, -1, NULL);\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int str9xpec_write(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count)\r
-{\r
- str9xpec_flash_controller_t *str9xpec_info = bank->driver_priv;\r
- u32 dwords_remaining = (count / 8);\r
- u32 bytes_remaining = (count & 0x00000007);\r
- u32 bytes_written = 0;\r
- u8 status;\r
- u32 check_address = offset;\r
- u32 chain_pos;\r
- scan_field_t field;\r
- u8 *scanbuf;\r
- int i;\r
- u32 first_sector = 0;\r
- u32 last_sector = 0;\r
- \r
- chain_pos = str9xpec_info->chain_pos;\r
- \r
- if (!str9xpec_info->isc_enable) {\r
- str9xpec_isc_enable(bank);\r
- }\r
- \r
- if (!str9xpec_info->isc_enable) {\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
- \r
- if (offset & 0x7)\r
- {\r
- WARNING("offset 0x%x breaks required 8-byte alignment", offset);\r
- return ERROR_FLASH_DST_BREAKS_ALIGNMENT;\r
- }\r
- \r
- for (i = 0; i < bank->num_sectors; i++)\r
- {\r
- u32 sec_start = bank->sectors[i].offset;\r
- u32 sec_end = sec_start + bank->sectors[i].size;\r
- \r
- /* check if destination falls within the current sector */\r
- if ((check_address >= sec_start) && (check_address < sec_end))\r
- {\r
- /* check if destination ends in the current sector */\r
- if (offset + count < sec_end)\r
- check_address = offset + count;\r
- else\r
- check_address = sec_end;\r
- }\r
- \r
- if ((offset >= sec_start) && (offset < sec_end)){\r
- first_sector = i;\r
- }\r
- \r
- if ((offset + count >= sec_start) && (offset + count < sec_end)){\r
- last_sector = i;\r
- }\r
- }\r
- \r
- if (check_address != offset + count)\r
- return ERROR_FLASH_DST_OUT_OF_BANK;\r
-\r
- DEBUG("first_sector: %i, last_sector: %i", first_sector, last_sector);\r
- \r
- scanbuf = calloc(CEIL(64, 8), 1);\r
- \r
- DEBUG("ISC_PROGRAM");\r
- \r
- for (i = first_sector; i <= last_sector; i++)\r
- {\r
- str9xpec_set_address(bank, str9xpec_info->sector_bits[i]);\r
- \r
- dwords_remaining = dwords_remaining < (bank->sectors[i].size/8) ? dwords_remaining : (bank->sectors[i].size/8);\r
-\r
- while (dwords_remaining > 0)\r
- { \r
- str9xpec_set_instr(chain_pos, ISC_PROGRAM, TAP_PI);\r
- \r
- field.device = chain_pos;\r
- field.num_bits = 64;\r
- field.out_value = (buffer + bytes_written);\r
- field.out_mask = NULL;\r
- field.in_value = NULL;\r
- field.in_check_value = NULL;\r
- field.in_check_mask = NULL;\r
- field.in_handler = NULL;\r
- field.in_handler_priv = NULL;\r
- \r
- jtag_add_dr_scan(1, &field, TAP_RTI, NULL);\r
- \r
- /* small delay before polling */\r
- jtag_add_sleep(50);\r
- \r
- str9xpec_set_instr(chain_pos, ISC_NOOP, TAP_PI);\r
- \r
- do {\r
- field.device = chain_pos;\r
- field.num_bits = 8;\r
- field.out_value = NULL;\r
- field.out_mask = NULL;\r
- field.in_value = scanbuf;\r
- field.in_check_value = NULL;\r
- field.in_check_mask = NULL;\r
- field.in_handler = NULL;\r
- field.in_handler_priv = NULL;\r
- \r
- jtag_add_dr_scan(1, &field, -1, NULL);\r
- jtag_execute_queue();\r
- \r
- status = buf_get_u32(scanbuf, 0, 8);\r
- \r
- } while(!(status & ISC_STATUS_BUSY));\r
- \r
- if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS)\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- \r
- //if ((status & ISC_STATUS_INT_ERROR) != STR9XPEC_ISC_INTFAIL)\r
- // return ERROR_FLASH_OPERATION_FAILED;\r
- \r
- dwords_remaining--;\r
- bytes_written += 8;\r
- }\r
- }\r
- \r
- if (bytes_remaining)\r
- {\r
- u8 last_dword[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};\r
- int i = 0;\r
- \r
- while(bytes_remaining > 0)\r
- {\r
- last_dword[i++] = *(buffer + bytes_written); \r
- bytes_remaining--;\r
- bytes_written++;\r
- }\r
- \r
- str9xpec_set_instr(chain_pos, ISC_PROGRAM, TAP_PI);\r
- \r
- field.device = chain_pos;\r
- field.num_bits = 64;\r
- field.out_value = last_dword;\r
- field.out_mask = NULL;\r
- field.in_value = NULL;\r
- field.in_check_value = NULL;\r
- field.in_check_mask = NULL;\r
- field.in_handler = NULL;\r
- field.in_handler_priv = NULL;\r
- \r
- jtag_add_dr_scan(1, &field, TAP_RTI, NULL);\r
- \r
- /* small delay before polling */\r
- jtag_add_sleep(50);\r
- \r
- str9xpec_set_instr(chain_pos, ISC_NOOP, TAP_PI);\r
- \r
- do {\r
- field.device = chain_pos;\r
- field.num_bits = 8;\r
- field.out_value = NULL;\r
- field.out_mask = NULL;\r
- field.in_value = scanbuf;\r
- field.in_check_value = NULL;\r
- field.in_check_mask = NULL;\r
- field.in_handler = NULL;\r
- field.in_handler_priv = NULL;\r
- \r
- jtag_add_dr_scan(1, &field, -1, NULL);\r
- jtag_execute_queue();\r
- \r
- status = buf_get_u32(scanbuf, 0, 8);\r
- \r
- } while(!(status & ISC_STATUS_BUSY));\r
- \r
- if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS)\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- \r
- //if ((status & ISC_STATUS_INT_ERROR) != STR9XPEC_ISC_INTFAIL)\r
- // return ERROR_FLASH_OPERATION_FAILED;\r
- }\r
-\r
- free(scanbuf);\r
-\r
- str9xpec_isc_disable(bank);\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int str9xpec_probe(struct flash_bank_s *bank)\r
-{\r
- return ERROR_OK;\r
-}\r
-\r
-int str9xpec_handle_part_id_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- flash_bank_t *bank;\r
- scan_field_t field;\r
- u8 *buffer = NULL;\r
- u32 chain_pos;\r
- u32 idcode;\r
- str9xpec_flash_controller_t *str9xpec_info = NULL;\r
-\r
- if (argc < 1)\r
- {\r
- return ERROR_COMMAND_SYNTAX_ERROR;\r
- }\r
- \r
- bank = get_flash_bank_by_num(strtoul(args[0], NULL, 0));\r
- if (!bank)\r
- {\r
- command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);\r
- return ERROR_OK;\r
- }\r
- \r
- str9xpec_info = bank->driver_priv;\r
- chain_pos = str9xpec_info->chain_pos;\r
- \r
- buffer = calloc(CEIL(32, 8), 1);\r
- \r
- str9xpec_set_instr(chain_pos, ISC_IDCODE, TAP_PI);\r
- \r
- field.device = chain_pos;\r
- field.num_bits = 32;\r
- field.out_value = NULL;\r
- field.out_mask = NULL;\r
- field.in_value = buffer;\r
- field.in_check_value = NULL;\r
- field.in_check_mask = NULL;\r
- field.in_handler = NULL;\r
- field.in_handler_priv = NULL;\r
- \r
- jtag_add_dr_scan(1, &field, TAP_RTI, NULL);\r
- jtag_execute_queue();\r
- \r
- idcode = buf_get_u32(buffer, 0, 32);\r
- \r
- command_print(cmd_ctx, "str9xpec part id: 0x%8.8x", idcode);\r
- \r
- free(buffer);\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int str9xpec_erase_check(struct flash_bank_s *bank)\r
-{\r
- return str9xpec_blank_check(bank, 0, bank->num_sectors - 1);\r
-}\r
-\r
-int str9xpec_info(struct flash_bank_s *bank, char *buf, int buf_size)\r
-{\r
- snprintf(buf, buf_size, "str9xpec flash driver info" );\r
- return ERROR_OK;\r
-}\r
-\r
-int str9xpec_handle_flash_options_read_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- flash_bank_t *bank;\r
- u8 status;\r
- str9xpec_flash_controller_t *str9xpec_info = NULL;\r
- \r
- if (argc < 1)\r
- {\r
- command_print(cmd_ctx, "str9xpec options_read <bank>");\r
- return ERROR_OK; \r
- }\r
- \r
- bank = get_flash_bank_by_num(strtoul(args[0], NULL, 0));\r
- if (!bank)\r
- {\r
- command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);\r
- return ERROR_OK;\r
- }\r
- \r
- str9xpec_info = bank->driver_priv;\r
- \r
- status = str9xpec_read_config(bank);\r
- \r
- if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS)\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- \r
- /* boot bank */\r
- if (buf_get_u32(str9xpec_info->options, STR9XPEC_OPT_CSMAPBIT, 1))\r
- command_print(cmd_ctx, "CS Map: bank1");\r
- else\r
- command_print(cmd_ctx, "CS Map: bank0");\r
- \r
- /* OTP lock */\r
- if (buf_get_u32(str9xpec_info->options, STR9XPEC_OPT_OTPBIT, 1))\r
- command_print(cmd_ctx, "OTP Lock: OTP Locked");\r
- else\r
- command_print(cmd_ctx, "OTP Lock: OTP Unlocked");\r
- \r
- /* LVD Threshold */\r
- if (buf_get_u32(str9xpec_info->options, STR9XPEC_OPT_LVDTHRESBIT, 1))\r
- command_print(cmd_ctx, "LVD Threshold: 2.7v");\r
- else\r
- command_print(cmd_ctx, "LVD Threshold: 2.4v");\r
- \r
- /* LVD reset warning */\r
- if (buf_get_u32(str9xpec_info->options, STR9XPEC_OPT_LVDWARNBIT, 1))\r
- command_print(cmd_ctx, "LVD Reset Warning: VDD or VDDQ Inputs");\r
- else\r
- command_print(cmd_ctx, "LVD Reset Warning: VDD Input Only");\r
- \r
- /* LVD reset select */\r
- if (buf_get_u32(str9xpec_info->options, STR9XPEC_OPT_LVDSELBIT, 1))\r
- command_print(cmd_ctx, "LVD Reset Selection: VDD or VDDQ Inputs");\r
- else\r
- command_print(cmd_ctx, "LVD Reset Selection: VDD Input Only");\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int str9xpec_write_options(struct flash_bank_s *bank)\r
-{\r
- scan_field_t field;\r
- u8 status;\r
- u32 chain_pos;\r
- str9xpec_flash_controller_t *str9xpec_info = NULL;\r
- \r
- str9xpec_info = bank->driver_priv;\r
- chain_pos = str9xpec_info->chain_pos;\r
- \r
- /* erase config options first */\r
- status = str9xpec_erase_area( bank, 0xFE, 0xFE );\r
- \r
- if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS)\r
- return status; \r
- \r
- if (!str9xpec_info->isc_enable) {\r
- str9xpec_isc_enable( bank );\r
- }\r
- \r
- if (!str9xpec_info->isc_enable) {\r
- return ISC_STATUS_ERROR;\r
- }\r
- \r
- /* according to data 64th bit has to be set */\r
- buf_set_u32(str9xpec_info->options, 63, 1, 1);\r
- \r
- /* set option byte address */\r
- str9xpec_set_address(bank, 0x50);\r
- \r
- /* execute ISC_PROGRAM command */\r
- str9xpec_set_instr(chain_pos, ISC_PROGRAM, TAP_PI);\r
- \r
- field.device = chain_pos;\r
- field.num_bits = 64;\r
- field.out_value = str9xpec_info->options;\r
- field.out_mask = NULL;\r
- field.in_value = NULL;\r
- field.in_check_value = NULL;\r
- field.in_check_mask = NULL;\r
- field.in_handler = NULL;\r
- field.in_handler_priv = NULL;\r
- \r
- jtag_add_dr_scan(1, &field, TAP_RTI, NULL);\r
- \r
- /* small delay before polling */\r
- jtag_add_sleep(50);\r
- \r
- str9xpec_set_instr(chain_pos, ISC_NOOP, TAP_PI);\r
- \r
- do {\r
- field.device = chain_pos;\r
- field.num_bits = 8;\r
- field.out_value = NULL;\r
- field.out_mask = NULL;\r
- field.in_value = &status;\r
- field.in_check_value = NULL;\r
- field.in_check_mask = NULL;\r
- field.in_handler = NULL;\r
- field.in_handler_priv = NULL;\r
- \r
- jtag_add_dr_scan(1, &field, -1, NULL);\r
- jtag_execute_queue();\r
- \r
- } while(!(status & ISC_STATUS_BUSY));\r
- \r
- str9xpec_isc_disable(bank);\r
- \r
- return status;\r
-}\r
-\r
-int str9xpec_handle_flash_options_write_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- flash_bank_t *bank;\r
- u8 status;\r
- \r
- if (argc < 1)\r
- {\r
- command_print(cmd_ctx, "str9xpec options_write <bank>");\r
- return ERROR_OK; \r
- }\r
- \r
- bank = get_flash_bank_by_num(strtoul(args[0], NULL, 0));\r
- if (!bank)\r
- {\r
- command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);\r
- return ERROR_OK;\r
- }\r
- \r
- status = str9xpec_write_options(bank);\r
- \r
- if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS)\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int str9xpec_handle_flash_options_cmap_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- flash_bank_t *bank;\r
- str9xpec_flash_controller_t *str9xpec_info = NULL;\r
- \r
- if (argc < 2)\r
- {\r
- command_print(cmd_ctx, "str9xpec options_cmap <bank> <bank0|bank1>");\r
- return ERROR_OK; \r
- }\r
- \r
- bank = get_flash_bank_by_num(strtoul(args[0], NULL, 0));\r
- if (!bank)\r
- {\r
- command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);\r
- return ERROR_OK;\r
- }\r
- \r
- str9xpec_info = bank->driver_priv;\r
- \r
- if (strcmp(args[1], "bank1") == 0)\r
- {\r
- buf_set_u32(str9xpec_info->options, STR9XPEC_OPT_CSMAPBIT, 1, 1);\r
- }\r
- else\r
- {\r
- buf_set_u32(str9xpec_info->options, STR9XPEC_OPT_CSMAPBIT, 1, 0);\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int str9xpec_handle_flash_options_lvdthd_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- flash_bank_t *bank;\r
- str9xpec_flash_controller_t *str9xpec_info = NULL;\r
- \r
- if (argc < 2)\r
- {\r
- command_print(cmd_ctx, "str9xpec options_lvdthd <bank> <2.4v|2.7v>");\r
- return ERROR_OK; \r
- }\r
- \r
- bank = get_flash_bank_by_num(strtoul(args[0], NULL, 0));\r
- if (!bank)\r
- {\r
- command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);\r
- return ERROR_OK;\r
- }\r
- \r
- str9xpec_info = bank->driver_priv;\r
- \r
- if (strcmp(args[1], "2.7v") == 0)\r
- {\r
- buf_set_u32(str9xpec_info->options, STR9XPEC_OPT_LVDTHRESBIT, 1, 1);\r
- }\r
- else\r
- {\r
- buf_set_u32(str9xpec_info->options, STR9XPEC_OPT_LVDTHRESBIT, 1, 0);\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int str9xpec_handle_flash_options_lvdsel_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- flash_bank_t *bank;\r
- str9xpec_flash_controller_t *str9xpec_info = NULL;\r
- \r
- if (argc < 2)\r
- {\r
- command_print(cmd_ctx, "str9xpec options_lvdsel <bank> <vdd|vdd_vddq>");\r
- return ERROR_OK; \r
- }\r
- \r
- bank = get_flash_bank_by_num(strtoul(args[0], NULL, 0));\r
- if (!bank)\r
- {\r
- command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);\r
- return ERROR_OK;\r
- }\r
- \r
- str9xpec_info = bank->driver_priv;\r
- \r
- if (strcmp(args[1], "vdd_vddq") == 0)\r
- {\r
- buf_set_u32(str9xpec_info->options, STR9XPEC_OPT_LVDSELBIT, 1, 1);\r
- }\r
- else\r
- {\r
- buf_set_u32(str9xpec_info->options, STR9XPEC_OPT_LVDSELBIT, 1, 0);\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int str9xpec_handle_flash_options_lvdwarn_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- flash_bank_t *bank;\r
- str9xpec_flash_controller_t *str9xpec_info = NULL;\r
- \r
- if (argc < 2)\r
- {\r
- command_print(cmd_ctx, "str9xpec options_lvdwarn <bank> <vdd|vdd_vddq>");\r
- return ERROR_OK; \r
- }\r
- \r
- bank = get_flash_bank_by_num(strtoul(args[0], NULL, 0));\r
- if (!bank)\r
- {\r
- command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);\r
- return ERROR_OK;\r
- }\r
- \r
- str9xpec_info = bank->driver_priv;\r
- \r
- if (strcmp(args[1], "vdd_vddq") == 0)\r
- {\r
- buf_set_u32(str9xpec_info->options, STR9XPEC_OPT_LVDWARNBIT, 1, 1);\r
- }\r
- else\r
- {\r
- buf_set_u32(str9xpec_info->options, STR9XPEC_OPT_LVDWARNBIT, 1, 0);\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int str9xpec_handle_flash_lock_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- u8 status;\r
- flash_bank_t *bank;\r
- \r
- if (argc < 1)\r
- {\r
- command_print(cmd_ctx, "str9xpec lock <bank>");\r
- return ERROR_OK; \r
- }\r
- \r
- bank = get_flash_bank_by_num(strtoul(args[0], NULL, 0));\r
- if (!bank)\r
- {\r
- command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);\r
- return ERROR_OK;\r
- }\r
- \r
- status = str9xpec_lock_device(bank);\r
- \r
- if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS)\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int str9xpec_handle_flash_unlock_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- u8 status;\r
- flash_bank_t *bank;\r
- \r
- if (argc < 1)\r
- {\r
- command_print(cmd_ctx, "str9xpec unlock <bank>");\r
- return ERROR_OK; \r
- }\r
- \r
- bank = get_flash_bank_by_num(strtoul(args[0], NULL, 0));\r
- if (!bank)\r
- {\r
- command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);\r
- return ERROR_OK;\r
- }\r
- \r
- status = str9xpec_unlock_device(bank);\r
- \r
- if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS)\r
- return ERROR_FLASH_OPERATION_FAILED;\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int str9xpec_handle_flash_enable_turbo_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- flash_bank_t *bank;\r
- u32 chain_pos;\r
- jtag_device_t* dev0;\r
- jtag_device_t* dev2;\r
- str9xpec_flash_controller_t *str9xpec_info = NULL;\r
- \r
- if (argc < 1)\r
- {\r
- command_print(cmd_ctx, "str9xpec enable_turbo <bank>");\r
- return ERROR_OK; \r
- }\r
- \r
- bank = get_flash_bank_by_num(strtoul(args[0], NULL, 0));\r
- if (!bank)\r
- {\r
- command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);\r
- return ERROR_OK;\r
- }\r
- \r
- str9xpec_info = bank->driver_priv;\r
- \r
- chain_pos = str9xpec_info->chain_pos;\r
- \r
- /* remove arm core from chain - enter turbo mode */\r
- \r
- str9xpec_set_instr(chain_pos+2, 0xD, TAP_RTI);\r
- jtag_execute_queue();\r
- \r
- /* modify scan chain - str9 core has been removed */\r
- dev0 = jtag_get_device(chain_pos);\r
- str9xpec_info->devarm = jtag_get_device(chain_pos+1);\r
- dev2 = jtag_get_device(chain_pos+2);\r
- dev0->next = dev2;\r
- jtag_num_devices--;\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int str9xpec_handle_flash_disable_turbo_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- flash_bank_t *bank;\r
- u32 chain_pos;\r
- jtag_device_t* dev0;\r
- str9xpec_flash_controller_t *str9xpec_info = NULL;\r
- \r
- if (argc < 1)\r
- {\r
- command_print(cmd_ctx, "str9xpec disable_turbo <bank>");\r
- return ERROR_OK; \r
- }\r
- \r
- bank = get_flash_bank_by_num(strtoul(args[0], NULL, 0));\r
- if (!bank)\r
- {\r
- command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);\r
- return ERROR_OK;\r
- }\r
- \r
- str9xpec_info = bank->driver_priv;\r
- \r
- chain_pos = str9xpec_info->chain_pos;\r
- \r
- dev0 = jtag_get_device(chain_pos);\r
- \r
- /* exit turbo mode via TLR */\r
- str9xpec_set_instr(chain_pos, ISC_NOOP, TAP_TLR);\r
- jtag_execute_queue();\r
- \r
- /* restore previous scan chain */\r
- if( str9xpec_info->devarm ) {\r
- dev0->next = str9xpec_info->devarm;\r
- jtag_num_devices++;\r
- str9xpec_info->devarm = NULL;\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
+/***************************************************************************
+ * Copyright (C) 2005 by Dominic Rath *
+ * Dominic.Rath@gmx.de *
+ * *
+ * 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 "replacements.h"
+
+#include "str9xpec.h"
+#include "flash.h"
+#include "target.h"
+#include "log.h"
+#include "armv4_5.h"
+#include "arm7_9_common.h"
+#include "jtag.h"
+#include "binarybuffer.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+
+str9xpec_mem_layout_t mem_layout_str9pec[] = {
+ {0x00000000, 0x10000, 0},
+ {0x00010000, 0x10000, 1},
+ {0x00020000, 0x10000, 2},
+ {0x00030000, 0x10000, 3},
+ {0x00040000, 0x10000, 4},
+ {0x00050000, 0x10000, 5},
+ {0x00060000, 0x10000, 6},
+ {0x00070000, 0x10000, 7},
+ {0x00080000, 0x02000, 32},
+ {0x00082000, 0x02000, 33},
+ {0x00084000, 0x02000, 34},
+ {0x00086000, 0x02000, 35}
+};
+
+int str9xpec_register_commands(struct command_context_s *cmd_ctx);
+int str9xpec_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank);
+int str9xpec_erase(struct flash_bank_s *bank, int first, int last);
+int str9xpec_protect(struct flash_bank_s *bank, int set, int first, int last);
+int str9xpec_write(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count);
+int str9xpec_probe(struct flash_bank_s *bank);
+int str9xpec_handle_part_id_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+int str9xpec_protect_check(struct flash_bank_s *bank);
+int str9xpec_erase_check(struct flash_bank_s *bank);
+int str9xpec_info(struct flash_bank_s *bank, char *buf, int buf_size);
+
+int str9xpec_erase_area(struct flash_bank_s *bank, int first, int last);
+int str9xpec_set_address(struct flash_bank_s *bank, u8 sector);
+int str9xpec_write_options(struct flash_bank_s *bank);
+
+int str9xpec_handle_flash_options_cmap_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+int str9xpec_handle_flash_options_lvdthd_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+int str9xpec_handle_flash_options_lvdsel_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+int str9xpec_handle_flash_options_lvdwarn_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+int str9xpec_handle_flash_options_read_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+int str9xpec_handle_flash_options_write_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+int str9xpec_handle_flash_lock_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+int str9xpec_handle_flash_unlock_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+int str9xpec_handle_flash_enable_turbo_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+int str9xpec_handle_flash_disable_turbo_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+
+flash_driver_t str9xpec_flash =
+{
+ .name = "str9xpec",
+ .register_commands = str9xpec_register_commands,
+ .flash_bank_command = str9xpec_flash_bank_command,
+ .erase = str9xpec_erase,
+ .protect = str9xpec_protect,
+ .write = str9xpec_write,
+ .probe = str9xpec_probe,
+ .auto_probe = str9xpec_probe,
+ .erase_check = str9xpec_erase_check,
+ .protect_check = str9xpec_protect_check,
+ .info = str9xpec_info
+};
+
+int str9xpec_register_commands(struct command_context_s *cmd_ctx)
+{
+ command_t *str9xpec_cmd = register_command(cmd_ctx, NULL, "str9xpec", NULL, COMMAND_ANY, "str9xpec flash specific commands");
+
+ register_command(cmd_ctx, str9xpec_cmd, "enable_turbo", str9xpec_handle_flash_enable_turbo_command, COMMAND_EXEC,
+ "enable str9xpec turbo mode");
+ register_command(cmd_ctx, str9xpec_cmd, "disable_turbo", str9xpec_handle_flash_disable_turbo_command, COMMAND_EXEC,
+ "disable str9xpec turbo mode");
+ register_command(cmd_ctx, str9xpec_cmd, "options_cmap", str9xpec_handle_flash_options_cmap_command, COMMAND_EXEC,
+ "configure str9xpec boot sector");
+ register_command(cmd_ctx, str9xpec_cmd, "options_lvdthd", str9xpec_handle_flash_options_lvdthd_command, COMMAND_EXEC,
+ "configure str9xpec lvd threshold");
+ register_command(cmd_ctx, str9xpec_cmd, "options_lvdsel", str9xpec_handle_flash_options_lvdsel_command, COMMAND_EXEC,
+ "configure str9xpec lvd selection");
+ register_command(cmd_ctx, str9xpec_cmd, "options_lvdwarn", str9xpec_handle_flash_options_lvdwarn_command, COMMAND_EXEC,
+ "configure str9xpec lvd warning");
+ register_command(cmd_ctx, str9xpec_cmd, "options_read", str9xpec_handle_flash_options_read_command, COMMAND_EXEC,
+ "read str9xpec options");
+ register_command(cmd_ctx, str9xpec_cmd, "options_write", str9xpec_handle_flash_options_write_command, COMMAND_EXEC,
+ "write str9xpec options");
+ register_command(cmd_ctx, str9xpec_cmd, "lock", str9xpec_handle_flash_lock_command, COMMAND_EXEC,
+ "lock str9xpec device");
+ register_command(cmd_ctx, str9xpec_cmd, "unlock", str9xpec_handle_flash_unlock_command, COMMAND_EXEC,
+ "unlock str9xpec device");
+ register_command(cmd_ctx, str9xpec_cmd, "part_id", str9xpec_handle_part_id_command, COMMAND_EXEC,
+ "print part id of str9xpec flash bank <num>");
+
+ return ERROR_OK;
+}
+
+int str9xpec_set_instr(int chain_pos, u32 new_instr, enum tap_state end_state)
+{
+ jtag_device_t *device = jtag_get_device(chain_pos);
+
+ if (device == NULL)
+ {
+ DEBUG("Invalid Target");
+ return ERROR_TARGET_INVALID;
+ }
+
+ if (buf_get_u32(device->cur_instr, 0, device->ir_length) != new_instr)
+ {
+ scan_field_t field;
+
+ field.device = chain_pos;
+ field.num_bits = device->ir_length;
+ field.out_value = calloc(CEIL(field.num_bits, 8), 1);
+ buf_set_u32(field.out_value, 0, field.num_bits, new_instr);
+ field.out_mask = NULL;
+ field.in_value = NULL;
+ field.in_check_value = NULL;
+ field.in_check_mask = NULL;
+ field.in_handler = NULL;
+ field.in_handler_priv = NULL;
+
+ jtag_add_ir_scan(1, &field, end_state, NULL);
+
+ free(field.out_value);
+ }
+
+ return ERROR_OK;
+}
+
+u8 str9xpec_isc_status(int chain_pos)
+{
+ scan_field_t field;
+ u8 status;
+
+ if (str9xpec_set_instr(chain_pos, ISC_NOOP, TAP_PI) != ERROR_OK)
+ return ISC_STATUS_ERROR;
+
+ field.device = chain_pos;
+ field.num_bits = 8;
+ field.out_value = NULL;
+ field.out_mask = NULL;
+ field.in_value = &status;
+ field.in_check_value = NULL;
+ field.in_check_mask = NULL;
+ field.in_handler = NULL;
+ field.in_handler_priv = NULL;
+
+ jtag_add_dr_scan(1, &field, TAP_RTI, NULL);
+ jtag_execute_queue();
+
+ DEBUG("status: 0x%2.2x", status);
+
+ if (status & ISC_STATUS_SECURITY)
+ INFO("Device Security Bit Set");
+
+ return status;
+}
+
+int str9xpec_isc_enable(struct flash_bank_s *bank)
+{
+ u8 status;
+ u32 chain_pos;
+ str9xpec_flash_controller_t *str9xpec_info = bank->driver_priv;
+
+ chain_pos = str9xpec_info->chain_pos;
+
+ if (str9xpec_info->isc_enable)
+ return ERROR_OK;
+
+ /* enter isc mode */
+ if (str9xpec_set_instr(chain_pos, ISC_ENABLE, TAP_RTI) != ERROR_OK)
+ return ERROR_TARGET_INVALID;
+
+ /* check ISC status */
+ status = str9xpec_isc_status(chain_pos);
+ if (status & ISC_STATUS_MODE)
+ {
+ /* we have entered isc mode */
+ str9xpec_info->isc_enable = 1;
+ DEBUG("ISC_MODE Enabled");
+ }
+
+ return ERROR_OK;
+}
+
+int str9xpec_isc_disable(struct flash_bank_s *bank)
+{
+ u8 status;
+ u32 chain_pos;
+ str9xpec_flash_controller_t *str9xpec_info = bank->driver_priv;
+
+ chain_pos = str9xpec_info->chain_pos;
+
+ if (!str9xpec_info->isc_enable)
+ return ERROR_OK;
+
+ if (str9xpec_set_instr(chain_pos, ISC_DISABLE, TAP_RTI) != ERROR_OK)
+ return ERROR_TARGET_INVALID;
+
+ /* delay to handle aborts */
+ jtag_add_sleep(50);
+
+ /* check ISC status */
+ status = str9xpec_isc_status(chain_pos);
+ if (!(status & ISC_STATUS_MODE))
+ {
+ /* we have left isc mode */
+ str9xpec_info->isc_enable = 0;
+ DEBUG("ISC_MODE Disabled");
+ }
+
+ return ERROR_OK;
+}
+
+int str9xpec_read_config(struct flash_bank_s *bank)
+{
+ scan_field_t field;
+ u8 status;
+ u32 chain_pos;
+
+ str9xpec_flash_controller_t *str9xpec_info = bank->driver_priv;
+
+ chain_pos = str9xpec_info->chain_pos;
+
+ DEBUG("ISC_CONFIGURATION");
+
+ /* execute ISC_CONFIGURATION command */
+ str9xpec_set_instr(chain_pos, ISC_CONFIGURATION, TAP_PI);
+
+ field.device = chain_pos;
+ field.num_bits = 64;
+ field.out_value = NULL;
+ field.out_mask = NULL;
+ field.in_value = str9xpec_info->options;
+ field.in_check_value = NULL;
+ field.in_check_mask = NULL;
+ field.in_handler = NULL;
+ field.in_handler_priv = NULL;
+
+ jtag_add_dr_scan(1, &field, TAP_RTI, NULL);
+ jtag_execute_queue();
+
+ status = str9xpec_isc_status(chain_pos);
+
+ return status;
+}
+
+int str9xpec_build_block_list(struct flash_bank_s *bank)
+{
+ str9xpec_flash_controller_t *str9xpec_info = bank->driver_priv;
+
+ int i;
+ int num_sectors = 0, b0_sectors = 0;
+
+ switch (bank->size)
+ {
+ case (256 * 1024):
+ b0_sectors = 4;
+ break;
+ case (512 * 1024):
+ b0_sectors = 8;
+ break;
+ default:
+ ERROR("BUG: unknown bank->size encountered");
+ exit(-1);
+ }
+
+ /* include bank 1 sectors */
+ num_sectors = b0_sectors + 4;
+ bank->size += (32 * 1024);
+
+ bank->num_sectors = num_sectors;
+ bank->sectors = malloc(sizeof(flash_sector_t) * num_sectors);
+ str9xpec_info->sector_bits = malloc(sizeof(u32) * num_sectors);
+
+ num_sectors = 0;
+
+ for (i = 0; i < b0_sectors; i++)
+ {
+ bank->sectors[num_sectors].offset = mem_layout_str9pec[i].sector_start;
+ bank->sectors[num_sectors].size = mem_layout_str9pec[i].sector_size;
+ bank->sectors[num_sectors].is_erased = -1;
+ bank->sectors[num_sectors].is_protected = 1;
+ str9xpec_info->sector_bits[num_sectors++] = mem_layout_str9pec[i].sector_bit;
+ }
+
+ for (i = 8; i < 12; i++)
+ {
+ bank->sectors[num_sectors].offset = mem_layout_str9pec[i].sector_start;
+ bank->sectors[num_sectors].size = mem_layout_str9pec[i].sector_size;
+ bank->sectors[num_sectors].is_erased = -1;
+ bank->sectors[num_sectors].is_protected = 1;
+ str9xpec_info->sector_bits[num_sectors++] = mem_layout_str9pec[i].sector_bit;
+ }
+
+ return ERROR_OK;
+}
+
+/* flash bank str9x <base> <size> 0 0 <target#>
+ */
+int str9xpec_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank)
+{
+ str9xpec_flash_controller_t *str9xpec_info;
+ armv4_5_common_t *armv4_5 = NULL;
+ arm7_9_common_t *arm7_9 = NULL;
+ arm_jtag_t *jtag_info = NULL;
+
+ if (argc < 6)
+ {
+ WARNING("incomplete flash_bank str9x configuration");
+ return ERROR_FLASH_BANK_INVALID;
+ }
+
+ str9xpec_info = malloc(sizeof(str9xpec_flash_controller_t));
+ bank->driver_priv = str9xpec_info;
+
+ if (bank->base != 0x00000000)
+ {
+ WARNING("overriding flash base address for STR91x device with 0x00000000");
+ bank->base = 0x00000000;
+ }
+
+ /* find out jtag position of flash controller
+ * it is always after the arm966 core */
+
+ armv4_5 = bank->target->arch_info;
+ arm7_9 = armv4_5->arch_info;
+ jtag_info = &arm7_9->jtag_info;
+
+ str9xpec_info->chain_pos = (jtag_info->chain_pos - 1);
+ str9xpec_info->isc_enable = 0;
+ str9xpec_info->devarm = NULL;
+
+ str9xpec_build_block_list(bank);
+
+ /* clear option byte register */
+ buf_set_u32(str9xpec_info->options, 0, 64, 0);
+
+ return ERROR_OK;
+}
+
+int str9xpec_blank_check(struct flash_bank_s *bank, int first, int last)
+{
+ scan_field_t field;
+ u8 status;
+ u32 chain_pos;
+ int i;
+ u8 *buffer = NULL;
+
+ str9xpec_flash_controller_t *str9xpec_info = bank->driver_priv;
+
+ chain_pos = str9xpec_info->chain_pos;
+
+ if (!str9xpec_info->isc_enable) {
+ str9xpec_isc_enable( bank );
+ }
+
+ if (!str9xpec_info->isc_enable) {
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ buffer = calloc(CEIL(64, 8), 1);
+
+ DEBUG("blank check: first_bank: %i, last_bank: %i", first, last);
+
+ for (i = first; i <= last; i++) {
+ buf_set_u32(buffer, str9xpec_info->sector_bits[i], 1, 1);
+ }
+
+ /* execute ISC_BLANK_CHECK command */
+ str9xpec_set_instr(chain_pos, ISC_BLANK_CHECK, TAP_PI);
+
+ field.device = chain_pos;
+ field.num_bits = 64;
+ field.out_value = buffer;
+ field.out_mask = NULL;
+ field.in_value = NULL;
+ field.in_check_value = NULL;
+ field.in_check_mask = NULL;
+ field.in_handler = NULL;
+ field.in_handler_priv = NULL;
+
+ jtag_add_dr_scan(1, &field, TAP_RTI, NULL);
+ jtag_add_sleep(40000);
+
+ /* read blank check result */
+ field.device = chain_pos;
+ field.num_bits = 64;
+ field.out_value = NULL;
+ field.out_mask = NULL;
+ field.in_value = buffer;
+ field.in_check_value = NULL;
+ field.in_check_mask = NULL;
+ field.in_handler = NULL;
+ field.in_handler_priv = NULL;
+
+ jtag_add_dr_scan(1, &field, TAP_PI, NULL);
+ jtag_execute_queue();
+
+ status = str9xpec_isc_status(chain_pos);
+
+ for (i = first; i <= last; i++)
+ {
+ if (buf_get_u32(buffer, str9xpec_info->sector_bits[i], 1))
+ bank->sectors[i].is_erased = 0;
+ else
+ bank->sectors[i].is_erased = 1;
+ }
+
+ free(buffer);
+
+ str9xpec_isc_disable(bank);
+
+ if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS)
+ return ERROR_FLASH_OPERATION_FAILED;
+ return ERROR_OK;
+}
+
+int str9xpec_protect_check(struct flash_bank_s *bank)
+{
+ u8 status;
+ int i;
+
+ str9xpec_flash_controller_t *str9xpec_info = bank->driver_priv;
+
+ status = str9xpec_read_config(bank);
+
+ for (i = 0; i < bank->num_sectors; i++)
+ {
+ if (buf_get_u32(str9xpec_info->options, str9xpec_info->sector_bits[i], 1))
+ bank->sectors[i].is_protected = 1;
+ else
+ bank->sectors[i].is_protected = 0;
+ }
+
+ if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS)
+ return ERROR_FLASH_OPERATION_FAILED;
+ return ERROR_OK;
+}
+
+int str9xpec_erase_area(struct flash_bank_s *bank, int first, int last)
+{
+ scan_field_t field;
+ u8 status;
+ u32 chain_pos;
+ int i;
+ u8 *buffer = NULL;
+
+ str9xpec_flash_controller_t *str9xpec_info = bank->driver_priv;
+
+ chain_pos = str9xpec_info->chain_pos;
+
+ if (!str9xpec_info->isc_enable) {
+ str9xpec_isc_enable( bank );
+ }
+
+ if (!str9xpec_info->isc_enable) {
+ return ISC_STATUS_ERROR;
+ }
+
+ buffer = calloc(CEIL(64, 8), 1);
+
+ DEBUG("erase: first_bank: %i, last_bank: %i", first, last);
+
+ /* last bank: 0xFF signals a full erase (unlock complete device) */
+ /* last bank: 0xFE signals a option byte erase */
+ if (last == 0xFF)
+ {
+ for (i = 0; i < 64; i++) {
+ buf_set_u32(buffer, i, 1, 1);
+ }
+ }
+ else if (last == 0xFE)
+ {
+ buf_set_u32(buffer, 49, 1, 1);
+ }
+ else
+ {
+ for (i = first; i <= last; i++) {
+ buf_set_u32(buffer, str9xpec_info->sector_bits[i], 1, 1);
+ }
+ }
+
+ DEBUG("ISC_ERASE");
+
+ /* execute ISC_ERASE command */
+ str9xpec_set_instr(chain_pos, ISC_ERASE, TAP_PI);
+
+ field.device = chain_pos;
+ field.num_bits = 64;
+ field.out_value = buffer;
+ field.out_mask = NULL;
+ field.in_value = NULL;
+ field.in_check_value = NULL;
+ field.in_check_mask = NULL;
+ field.in_handler = NULL;
+ field.in_handler_priv = NULL;
+
+ jtag_add_dr_scan(1, &field, TAP_RTI, NULL);
+ jtag_execute_queue();
+
+ jtag_add_sleep(10);
+
+ /* wait for erase completion */
+ while (!((status = str9xpec_isc_status(chain_pos)) & ISC_STATUS_BUSY)) {
+ usleep(1000);
+ }
+
+ free(buffer);
+
+ str9xpec_isc_disable(bank);
+
+ return status;
+}
+
+int str9xpec_erase(struct flash_bank_s *bank, int first, int last)
+{
+ int status;
+
+ status = str9xpec_erase_area(bank, first, last);
+
+ if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS)
+ return ERROR_FLASH_OPERATION_FAILED;
+
+ return ERROR_OK;
+}
+
+int str9xpec_lock_device(struct flash_bank_s *bank)
+{
+ scan_field_t field;
+ u8 status;
+ u32 chain_pos;
+ str9xpec_flash_controller_t *str9xpec_info = NULL;
+
+ str9xpec_info = bank->driver_priv;
+ chain_pos = str9xpec_info->chain_pos;
+
+ if (!str9xpec_info->isc_enable) {
+ str9xpec_isc_enable( bank );
+ }
+
+ if (!str9xpec_info->isc_enable) {
+ return ISC_STATUS_ERROR;
+ }
+
+ /* set security address */
+ str9xpec_set_address(bank, 0x80);
+
+ /* execute ISC_PROGRAM command */
+ str9xpec_set_instr(chain_pos, ISC_PROGRAM_SECURITY, TAP_RTI);
+
+ str9xpec_set_instr(chain_pos, ISC_NOOP, TAP_PI);
+
+ do {
+ field.device = chain_pos;
+ field.num_bits = 8;
+ field.out_value = NULL;
+ field.out_mask = NULL;
+ field.in_value = &status;
+ field.in_check_value = NULL;
+ field.in_check_mask = NULL;
+ field.in_handler = NULL;
+ field.in_handler_priv = NULL;
+
+ jtag_add_dr_scan(1, &field, -1, NULL);
+ jtag_execute_queue();
+
+ } while(!(status & ISC_STATUS_BUSY));
+
+ str9xpec_isc_disable(bank);
+
+ return status;
+}
+
+int str9xpec_unlock_device(struct flash_bank_s *bank)
+{
+ u8 status;
+
+ status = str9xpec_erase_area(bank, 0, 255);
+
+ return status;
+}
+
+int str9xpec_protect(struct flash_bank_s *bank, int set, int first, int last)
+{
+ u8 status;
+ int i;
+
+ str9xpec_flash_controller_t *str9xpec_info = bank->driver_priv;
+
+ status = str9xpec_read_config(bank);
+
+ if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS)
+ return ERROR_FLASH_OPERATION_FAILED;
+
+ DEBUG("protect: first_bank: %i, last_bank: %i", first, last);
+
+ /* last bank: 0xFF signals a full device protect */
+ if (last == 0xFF)
+ {
+ if( set )
+ {
+ status = str9xpec_lock_device(bank);
+ }
+ else
+ {
+ /* perform full erase to unlock device */
+ status = str9xpec_unlock_device(bank);
+ }
+ }
+ else
+ {
+ for (i = first; i <= last; i++)
+ {
+ if( set )
+ buf_set_u32(str9xpec_info->options, str9xpec_info->sector_bits[i], 1, 1);
+ else
+ buf_set_u32(str9xpec_info->options, str9xpec_info->sector_bits[i], 1, 0);
+ }
+
+ status = str9xpec_write_options(bank);
+ }
+
+ if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS)
+ return ERROR_FLASH_OPERATION_FAILED;
+
+ return ERROR_OK;
+}
+
+int str9xpec_set_address(struct flash_bank_s *bank, u8 sector)
+{
+ u32 chain_pos;
+ scan_field_t field;
+ str9xpec_flash_controller_t *str9xpec_info = bank->driver_priv;
+
+ chain_pos = str9xpec_info->chain_pos;
+
+ /* set flash controller address */
+ str9xpec_set_instr(chain_pos, ISC_ADDRESS_SHIFT, TAP_PI);
+
+ field.device = chain_pos;
+ field.num_bits = 8;
+ field.out_value = §or;
+ field.out_mask = NULL;
+ field.in_value = NULL;
+ field.in_check_value = NULL;
+ field.in_check_mask = NULL;
+ field.in_handler = NULL;
+ field.in_handler_priv = NULL;
+
+ jtag_add_dr_scan(1, &field, -1, NULL);
+
+ return ERROR_OK;
+}
+
+int str9xpec_write(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count)
+{
+ str9xpec_flash_controller_t *str9xpec_info = bank->driver_priv;
+ u32 dwords_remaining = (count / 8);
+ u32 bytes_remaining = (count & 0x00000007);
+ u32 bytes_written = 0;
+ u8 status;
+ u32 check_address = offset;
+ u32 chain_pos;
+ scan_field_t field;
+ u8 *scanbuf;
+ int i;
+ u32 first_sector = 0;
+ u32 last_sector = 0;
+
+ chain_pos = str9xpec_info->chain_pos;
+
+ if (!str9xpec_info->isc_enable) {
+ str9xpec_isc_enable(bank);
+ }
+
+ if (!str9xpec_info->isc_enable) {
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ if (offset & 0x7)
+ {
+ WARNING("offset 0x%x breaks required 8-byte alignment", offset);
+ return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
+ }
+
+ for (i = 0; i < bank->num_sectors; i++)
+ {
+ u32 sec_start = bank->sectors[i].offset;
+ u32 sec_end = sec_start + bank->sectors[i].size;
+
+ /* check if destination falls within the current sector */
+ if ((check_address >= sec_start) && (check_address < sec_end))
+ {
+ /* check if destination ends in the current sector */
+ if (offset + count < sec_end)
+ check_address = offset + count;
+ else
+ check_address = sec_end;
+ }
+
+ if ((offset >= sec_start) && (offset < sec_end)){
+ first_sector = i;
+ }
+
+ if ((offset + count >= sec_start) && (offset + count < sec_end)){
+ last_sector = i;
+ }
+ }
+
+ if (check_address != offset + count)
+ return ERROR_FLASH_DST_OUT_OF_BANK;
+
+ DEBUG("first_sector: %i, last_sector: %i", first_sector, last_sector);
+
+ scanbuf = calloc(CEIL(64, 8), 1);
+
+ DEBUG("ISC_PROGRAM");
+
+ for (i = first_sector; i <= last_sector; i++)
+ {
+ str9xpec_set_address(bank, str9xpec_info->sector_bits[i]);
+
+ dwords_remaining = dwords_remaining < (bank->sectors[i].size/8) ? dwords_remaining : (bank->sectors[i].size/8);
+
+ while (dwords_remaining > 0)
+ {
+ str9xpec_set_instr(chain_pos, ISC_PROGRAM, TAP_PI);
+
+ field.device = chain_pos;
+ field.num_bits = 64;
+ field.out_value = (buffer + bytes_written);
+ field.out_mask = NULL;
+ field.in_value = NULL;
+ field.in_check_value = NULL;
+ field.in_check_mask = NULL;
+ field.in_handler = NULL;
+ field.in_handler_priv = NULL;
+
+ jtag_add_dr_scan(1, &field, TAP_RTI, NULL);
+
+ /* small delay before polling */
+ jtag_add_sleep(50);
+
+ str9xpec_set_instr(chain_pos, ISC_NOOP, TAP_PI);
+
+ do {
+ field.device = chain_pos;
+ field.num_bits = 8;
+ field.out_value = NULL;
+ field.out_mask = NULL;
+ field.in_value = scanbuf;
+ field.in_check_value = NULL;
+ field.in_check_mask = NULL;
+ field.in_handler = NULL;
+ field.in_handler_priv = NULL;
+
+ jtag_add_dr_scan(1, &field, -1, NULL);
+ jtag_execute_queue();
+
+ status = buf_get_u32(scanbuf, 0, 8);
+
+ } while(!(status & ISC_STATUS_BUSY));
+
+ if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS)
+ return ERROR_FLASH_OPERATION_FAILED;
+
+ //if ((status & ISC_STATUS_INT_ERROR) != STR9XPEC_ISC_INTFAIL)
+ // return ERROR_FLASH_OPERATION_FAILED;
+
+ dwords_remaining--;
+ bytes_written += 8;
+ }
+ }
+
+ if (bytes_remaining)
+ {
+ u8 last_dword[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ int i = 0;
+
+ while(bytes_remaining > 0)
+ {
+ last_dword[i++] = *(buffer + bytes_written);
+ bytes_remaining--;
+ bytes_written++;
+ }
+
+ str9xpec_set_instr(chain_pos, ISC_PROGRAM, TAP_PI);
+
+ field.device = chain_pos;
+ field.num_bits = 64;
+ field.out_value = last_dword;
+ field.out_mask = NULL;
+ field.in_value = NULL;
+ field.in_check_value = NULL;
+ field.in_check_mask = NULL;
+ field.in_handler = NULL;
+ field.in_handler_priv = NULL;
+
+ jtag_add_dr_scan(1, &field, TAP_RTI, NULL);
+
+ /* small delay before polling */
+ jtag_add_sleep(50);
+
+ str9xpec_set_instr(chain_pos, ISC_NOOP, TAP_PI);
+
+ do {
+ field.device = chain_pos;
+ field.num_bits = 8;
+ field.out_value = NULL;
+ field.out_mask = NULL;
+ field.in_value = scanbuf;
+ field.in_check_value = NULL;
+ field.in_check_mask = NULL;
+ field.in_handler = NULL;
+ field.in_handler_priv = NULL;
+
+ jtag_add_dr_scan(1, &field, -1, NULL);
+ jtag_execute_queue();
+
+ status = buf_get_u32(scanbuf, 0, 8);
+
+ } while(!(status & ISC_STATUS_BUSY));
+
+ if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS)
+ return ERROR_FLASH_OPERATION_FAILED;
+
+ //if ((status & ISC_STATUS_INT_ERROR) != STR9XPEC_ISC_INTFAIL)
+ // return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ free(scanbuf);
+
+ str9xpec_isc_disable(bank);
+
+ return ERROR_OK;
+}
+
+int str9xpec_probe(struct flash_bank_s *bank)
+{
+ return ERROR_OK;
+}
+
+int str9xpec_handle_part_id_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ flash_bank_t *bank;
+ scan_field_t field;
+ u8 *buffer = NULL;
+ u32 chain_pos;
+ u32 idcode;
+ str9xpec_flash_controller_t *str9xpec_info = NULL;
+
+ if (argc < 1)
+ {
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ bank = get_flash_bank_by_num(strtoul(args[0], NULL, 0));
+ if (!bank)
+ {
+ command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);
+ return ERROR_OK;
+ }
+
+ str9xpec_info = bank->driver_priv;
+ chain_pos = str9xpec_info->chain_pos;
+
+ buffer = calloc(CEIL(32, 8), 1);
+
+ str9xpec_set_instr(chain_pos, ISC_IDCODE, TAP_PI);
+
+ field.device = chain_pos;
+ field.num_bits = 32;
+ field.out_value = NULL;
+ field.out_mask = NULL;
+ field.in_value = buffer;
+ field.in_check_value = NULL;
+ field.in_check_mask = NULL;
+ field.in_handler = NULL;
+ field.in_handler_priv = NULL;
+
+ jtag_add_dr_scan(1, &field, TAP_RTI, NULL);
+ jtag_execute_queue();
+
+ idcode = buf_get_u32(buffer, 0, 32);
+
+ command_print(cmd_ctx, "str9xpec part id: 0x%8.8x", idcode);
+
+ free(buffer);
+
+ return ERROR_OK;
+}
+
+int str9xpec_erase_check(struct flash_bank_s *bank)
+{
+ return str9xpec_blank_check(bank, 0, bank->num_sectors - 1);
+}
+
+int str9xpec_info(struct flash_bank_s *bank, char *buf, int buf_size)
+{
+ snprintf(buf, buf_size, "str9xpec flash driver info" );
+ return ERROR_OK;
+}
+
+int str9xpec_handle_flash_options_read_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ flash_bank_t *bank;
+ u8 status;
+ str9xpec_flash_controller_t *str9xpec_info = NULL;
+
+ if (argc < 1)
+ {
+ command_print(cmd_ctx, "str9xpec options_read <bank>");
+ return ERROR_OK;
+ }
+
+ bank = get_flash_bank_by_num(strtoul(args[0], NULL, 0));
+ if (!bank)
+ {
+ command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);
+ return ERROR_OK;
+ }
+
+ str9xpec_info = bank->driver_priv;
+
+ status = str9xpec_read_config(bank);
+
+ if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS)
+ return ERROR_FLASH_OPERATION_FAILED;
+
+ /* boot bank */
+ if (buf_get_u32(str9xpec_info->options, STR9XPEC_OPT_CSMAPBIT, 1))
+ command_print(cmd_ctx, "CS Map: bank1");
+ else
+ command_print(cmd_ctx, "CS Map: bank0");
+
+ /* OTP lock */
+ if (buf_get_u32(str9xpec_info->options, STR9XPEC_OPT_OTPBIT, 1))
+ command_print(cmd_ctx, "OTP Lock: OTP Locked");
+ else
+ command_print(cmd_ctx, "OTP Lock: OTP Unlocked");
+
+ /* LVD Threshold */
+ if (buf_get_u32(str9xpec_info->options, STR9XPEC_OPT_LVDTHRESBIT, 1))
+ command_print(cmd_ctx, "LVD Threshold: 2.7v");
+ else
+ command_print(cmd_ctx, "LVD Threshold: 2.4v");
+
+ /* LVD reset warning */
+ if (buf_get_u32(str9xpec_info->options, STR9XPEC_OPT_LVDWARNBIT, 1))
+ command_print(cmd_ctx, "LVD Reset Warning: VDD or VDDQ Inputs");
+ else
+ command_print(cmd_ctx, "LVD Reset Warning: VDD Input Only");
+
+ /* LVD reset select */
+ if (buf_get_u32(str9xpec_info->options, STR9XPEC_OPT_LVDSELBIT, 1))
+ command_print(cmd_ctx, "LVD Reset Selection: VDD or VDDQ Inputs");
+ else
+ command_print(cmd_ctx, "LVD Reset Selection: VDD Input Only");
+
+ return ERROR_OK;
+}
+
+int str9xpec_write_options(struct flash_bank_s *bank)
+{
+ scan_field_t field;
+ u8 status;
+ u32 chain_pos;
+ str9xpec_flash_controller_t *str9xpec_info = NULL;
+
+ str9xpec_info = bank->driver_priv;
+ chain_pos = str9xpec_info->chain_pos;
+
+ /* erase config options first */
+ status = str9xpec_erase_area( bank, 0xFE, 0xFE );
+
+ if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS)
+ return status;
+
+ if (!str9xpec_info->isc_enable) {
+ str9xpec_isc_enable( bank );
+ }
+
+ if (!str9xpec_info->isc_enable) {
+ return ISC_STATUS_ERROR;
+ }
+
+ /* according to data 64th bit has to be set */
+ buf_set_u32(str9xpec_info->options, 63, 1, 1);
+
+ /* set option byte address */
+ str9xpec_set_address(bank, 0x50);
+
+ /* execute ISC_PROGRAM command */
+ str9xpec_set_instr(chain_pos, ISC_PROGRAM, TAP_PI);
+
+ field.device = chain_pos;
+ field.num_bits = 64;
+ field.out_value = str9xpec_info->options;
+ field.out_mask = NULL;
+ field.in_value = NULL;
+ field.in_check_value = NULL;
+ field.in_check_mask = NULL;
+ field.in_handler = NULL;
+ field.in_handler_priv = NULL;
+
+ jtag_add_dr_scan(1, &field, TAP_RTI, NULL);
+
+ /* small delay before polling */
+ jtag_add_sleep(50);
+
+ str9xpec_set_instr(chain_pos, ISC_NOOP, TAP_PI);
+
+ do {
+ field.device = chain_pos;
+ field.num_bits = 8;
+ field.out_value = NULL;
+ field.out_mask = NULL;
+ field.in_value = &status;
+ field.in_check_value = NULL;
+ field.in_check_mask = NULL;
+ field.in_handler = NULL;
+ field.in_handler_priv = NULL;
+
+ jtag_add_dr_scan(1, &field, -1, NULL);
+ jtag_execute_queue();
+
+ } while(!(status & ISC_STATUS_BUSY));
+
+ str9xpec_isc_disable(bank);
+
+ return status;
+}
+
+int str9xpec_handle_flash_options_write_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ flash_bank_t *bank;
+ u8 status;
+
+ if (argc < 1)
+ {
+ command_print(cmd_ctx, "str9xpec options_write <bank>");
+ return ERROR_OK;
+ }
+
+ bank = get_flash_bank_by_num(strtoul(args[0], NULL, 0));
+ if (!bank)
+ {
+ command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);
+ return ERROR_OK;
+ }
+
+ status = str9xpec_write_options(bank);
+
+ if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS)
+ return ERROR_FLASH_OPERATION_FAILED;
+
+ return ERROR_OK;
+}
+
+int str9xpec_handle_flash_options_cmap_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ flash_bank_t *bank;
+ str9xpec_flash_controller_t *str9xpec_info = NULL;
+
+ if (argc < 2)
+ {
+ command_print(cmd_ctx, "str9xpec options_cmap <bank> <bank0|bank1>");
+ return ERROR_OK;
+ }
+
+ bank = get_flash_bank_by_num(strtoul(args[0], NULL, 0));
+ if (!bank)
+ {
+ command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);
+ return ERROR_OK;
+ }
+
+ str9xpec_info = bank->driver_priv;
+
+ if (strcmp(args[1], "bank1") == 0)
+ {
+ buf_set_u32(str9xpec_info->options, STR9XPEC_OPT_CSMAPBIT, 1, 1);
+ }
+ else
+ {
+ buf_set_u32(str9xpec_info->options, STR9XPEC_OPT_CSMAPBIT, 1, 0);
+ }
+
+ return ERROR_OK;
+}
+
+int str9xpec_handle_flash_options_lvdthd_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ flash_bank_t *bank;
+ str9xpec_flash_controller_t *str9xpec_info = NULL;
+
+ if (argc < 2)
+ {
+ command_print(cmd_ctx, "str9xpec options_lvdthd <bank> <2.4v|2.7v>");
+ return ERROR_OK;
+ }
+
+ bank = get_flash_bank_by_num(strtoul(args[0], NULL, 0));
+ if (!bank)
+ {
+ command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);
+ return ERROR_OK;
+ }
+
+ str9xpec_info = bank->driver_priv;
+
+ if (strcmp(args[1], "2.7v") == 0)
+ {
+ buf_set_u32(str9xpec_info->options, STR9XPEC_OPT_LVDTHRESBIT, 1, 1);
+ }
+ else
+ {
+ buf_set_u32(str9xpec_info->options, STR9XPEC_OPT_LVDTHRESBIT, 1, 0);
+ }
+
+ return ERROR_OK;
+}
+
+int str9xpec_handle_flash_options_lvdsel_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ flash_bank_t *bank;
+ str9xpec_flash_controller_t *str9xpec_info = NULL;
+
+ if (argc < 2)
+ {
+ command_print(cmd_ctx, "str9xpec options_lvdsel <bank> <vdd|vdd_vddq>");
+ return ERROR_OK;
+ }
+
+ bank = get_flash_bank_by_num(strtoul(args[0], NULL, 0));
+ if (!bank)
+ {
+ command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);
+ return ERROR_OK;
+ }
+
+ str9xpec_info = bank->driver_priv;
+
+ if (strcmp(args[1], "vdd_vddq") == 0)
+ {
+ buf_set_u32(str9xpec_info->options, STR9XPEC_OPT_LVDSELBIT, 1, 1);
+ }
+ else
+ {
+ buf_set_u32(str9xpec_info->options, STR9XPEC_OPT_LVDSELBIT, 1, 0);
+ }
+
+ return ERROR_OK;
+}
+
+int str9xpec_handle_flash_options_lvdwarn_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ flash_bank_t *bank;
+ str9xpec_flash_controller_t *str9xpec_info = NULL;
+
+ if (argc < 2)
+ {
+ command_print(cmd_ctx, "str9xpec options_lvdwarn <bank> <vdd|vdd_vddq>");
+ return ERROR_OK;
+ }
+
+ bank = get_flash_bank_by_num(strtoul(args[0], NULL, 0));
+ if (!bank)
+ {
+ command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);
+ return ERROR_OK;
+ }
+
+ str9xpec_info = bank->driver_priv;
+
+ if (strcmp(args[1], "vdd_vddq") == 0)
+ {
+ buf_set_u32(str9xpec_info->options, STR9XPEC_OPT_LVDWARNBIT, 1, 1);
+ }
+ else
+ {
+ buf_set_u32(str9xpec_info->options, STR9XPEC_OPT_LVDWARNBIT, 1, 0);
+ }
+
+ return ERROR_OK;
+}
+
+int str9xpec_handle_flash_lock_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ u8 status;
+ flash_bank_t *bank;
+
+ if (argc < 1)
+ {
+ command_print(cmd_ctx, "str9xpec lock <bank>");
+ return ERROR_OK;
+ }
+
+ bank = get_flash_bank_by_num(strtoul(args[0], NULL, 0));
+ if (!bank)
+ {
+ command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);
+ return ERROR_OK;
+ }
+
+ status = str9xpec_lock_device(bank);
+
+ if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS)
+ return ERROR_FLASH_OPERATION_FAILED;
+
+ return ERROR_OK;
+}
+
+int str9xpec_handle_flash_unlock_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ u8 status;
+ flash_bank_t *bank;
+
+ if (argc < 1)
+ {
+ command_print(cmd_ctx, "str9xpec unlock <bank>");
+ return ERROR_OK;
+ }
+
+ bank = get_flash_bank_by_num(strtoul(args[0], NULL, 0));
+ if (!bank)
+ {
+ command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);
+ return ERROR_OK;
+ }
+
+ status = str9xpec_unlock_device(bank);
+
+ if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS)
+ return ERROR_FLASH_OPERATION_FAILED;
+
+ return ERROR_OK;
+}
+
+int str9xpec_handle_flash_enable_turbo_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ flash_bank_t *bank;
+ u32 chain_pos;
+ jtag_device_t* dev0;
+ jtag_device_t* dev2;
+ str9xpec_flash_controller_t *str9xpec_info = NULL;
+
+ if (argc < 1)
+ {
+ command_print(cmd_ctx, "str9xpec enable_turbo <bank>");
+ return ERROR_OK;
+ }
+
+ bank = get_flash_bank_by_num(strtoul(args[0], NULL, 0));
+ if (!bank)
+ {
+ command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);
+ return ERROR_OK;
+ }
+
+ str9xpec_info = bank->driver_priv;
+
+ chain_pos = str9xpec_info->chain_pos;
+
+ /* remove arm core from chain - enter turbo mode */
+
+ str9xpec_set_instr(chain_pos+2, 0xD, TAP_RTI);
+ jtag_execute_queue();
+
+ /* modify scan chain - str9 core has been removed */
+ dev0 = jtag_get_device(chain_pos);
+ str9xpec_info->devarm = jtag_get_device(chain_pos+1);
+ dev2 = jtag_get_device(chain_pos+2);
+ dev0->next = dev2;
+ jtag_num_devices--;
+
+ return ERROR_OK;
+}
+
+int str9xpec_handle_flash_disable_turbo_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ flash_bank_t *bank;
+ u32 chain_pos;
+ jtag_device_t* dev0;
+ str9xpec_flash_controller_t *str9xpec_info = NULL;
+
+ if (argc < 1)
+ {
+ command_print(cmd_ctx, "str9xpec disable_turbo <bank>");
+ return ERROR_OK;
+ }
+
+ bank = get_flash_bank_by_num(strtoul(args[0], NULL, 0));
+ if (!bank)
+ {
+ command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);
+ return ERROR_OK;
+ }
+
+ str9xpec_info = bank->driver_priv;
+
+ chain_pos = str9xpec_info->chain_pos;
+
+ dev0 = jtag_get_device(chain_pos);
+
+ /* exit turbo mode via TLR */
+ str9xpec_set_instr(chain_pos, ISC_NOOP, TAP_TLR);
+ jtag_execute_queue();
+
+ /* restore previous scan chain */
+ if( str9xpec_info->devarm ) {
+ dev0->next = str9xpec_info->devarm;
+ jtag_num_devices++;
+ str9xpec_info->devarm = NULL;
+ }
+
+ return ERROR_OK;
+}
-/***************************************************************************\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 "interpreter.h"\r
-#include "configuration.h"\r
-\r
-#include "binarybuffer.h"\r
-#include <stdlib.h>\r
-#include <string.h>\r
-\r
-var_t *variables = NULL;\r
-\r
-int handle_var_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-int handle_field_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-int handle_script_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
-\r
-int interpreter_register_commands(struct command_context_s *cmd_ctx)\r
-{\r
- register_command(cmd_ctx, NULL, "var", handle_var_command,\r
- COMMAND_ANY, "allocate, display or delete variable <name> [num_fields|'del'] [size1] ...");\r
- register_command(cmd_ctx, NULL, "field", handle_field_command,\r
- COMMAND_ANY, "display/modify variable field <var> <field> [value|'flip']");\r
- register_command(cmd_ctx, NULL, "script", handle_script_command,\r
- COMMAND_ANY, "execute commands from <file>");\r
-\r
- return ERROR_OK;\r
-}\r
-\r
-var_t* get_var_by_num(int num)\r
-{\r
- int count = 0;\r
- var_t *var = variables;\r
-\r
- if (var) \r
- {\r
- if (num == count)\r
- return var;\r
- while (var->next)\r
- {\r
- var = var->next;\r
- count++;\r
- if (num == count)\r
- return var;\r
- }\r
- }\r
- return NULL;\r
-}\r
-\r
-var_t* get_var_by_name(char *name)\r
-{\r
- var_t *var = variables;\r
-\r
- if (var) \r
- {\r
- if (strcmp(var->name, name) == 0)\r
- return var;\r
- while (var->next)\r
- {\r
- var = var->next;\r
- if (strcmp(var->name, name) == 0)\r
- return var;\r
- }\r
- }\r
- return NULL;\r
-}\r
-\r
-var_t* get_var_by_namenum(char *namenum)\r
-{\r
- if ((namenum[0] >= '0') && (namenum[0] <= '9'))\r
- return get_var_by_num(strtol(namenum, NULL, 0));\r
- else\r
- return get_var_by_name(namenum);\r
- \r
-}\r
-\r
-int field_le_to_host(u8 *buffer, void *priv, struct scan_field_s *dummy)\r
-{\r
- var_field_t *field = priv;\r
- field->value = buf_get_u32(buffer, 0, field->num_bits);\r
-\r
- return ERROR_OK;\r
-}\r
-\r
-int handle_var_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- var_t **last_var_p = &variables;\r
- int i;\r
-\r
- if (argc >= 2)\r
- {\r
- while (*last_var_p)\r
- {\r
- if (strcmp((*last_var_p)->name, args[0]) == 0)\r
- {\r
- if (strcmp(args[1], "del") == 0)\r
- {\r
- var_t *next = (*last_var_p)->next;\r
- free ((*last_var_p)->fields);\r
- free (*last_var_p);\r
- *last_var_p = next;\r
- command_print(cmd_ctx, "variable %s deleted", args[0]);\r
- }\r
- else\r
- command_print(cmd_ctx, "variable of that name already exists");\r
- return ERROR_OK;\r
- }\r
- last_var_p = &((*last_var_p)->next);\r
- }\r
-\r
- if ((args[0][0] >= '0') && (args[0][0] <= '9'))\r
- {\r
- command_print(cmd_ctx, "invalid name specified (first character may not be a number)");\r
- return ERROR_OK;\r
- }\r
-\r
- *last_var_p = malloc(sizeof(var_t));\r
- (*last_var_p)->name = strdup(args[0]);\r
- (*last_var_p)->num_fields = argc - 1;\r
- (*last_var_p)->next = NULL;\r
-\r
- (*last_var_p)->fields = malloc(sizeof(var_field_t) * (*last_var_p)->num_fields);\r
- for (i = 0; i < (*last_var_p)->num_fields; i++)\r
- {\r
- (*last_var_p)->fields[i].num_bits = strtol(args[1+i], NULL, 0);\r
- (*last_var_p)->fields[i].value = 0x0;\r
- }\r
- return ERROR_OK;\r
- }\r
-\r
- if (argc == 1)\r
- {\r
- var_t *var = get_var_by_namenum(args[0]);\r
- if (var)\r
- {\r
- int i;\r
- command_print(cmd_ctx, "%s (%i fields):", var->name, var->num_fields);\r
- for (i = 0; i < (var->num_fields); i++)\r
- {\r
- command_print(cmd_ctx, "0x%x (/%i)", var->fields[i].value, var->fields[i].num_bits);\r
- }\r
- }\r
- else\r
- {\r
- command_print(cmd_ctx, "variable %s doesn't exist", args[0]);\r
- }\r
- }\r
-\r
- if (argc == 0)\r
- {\r
- var_t *var = variables;\r
- int count = 0;\r
- while (var)\r
- {\r
- command_print(cmd_ctx, "%i: %s (%i fields)", count, var->name, var->num_fields);\r
- var = var->next;\r
- count++;\r
- }\r
- }\r
-\r
- return ERROR_OK;\r
-}\r
-\r
-int handle_field_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
-\r
- if (argc < 2)\r
- return ERROR_COMMAND_SYNTAX_ERROR;\r
-\r
- if (argc >= 2)\r
- {\r
- var_t *var = get_var_by_namenum(args[0]);\r
- int field_num = strtol(args[1], NULL, 0);\r
- if (!var)\r
- {\r
- command_print(cmd_ctx, "variable %s doesn't exist", args[0]);\r
- return ERROR_OK;\r
- }\r
- if (field_num >= var->num_fields)\r
- command_print(cmd_ctx, "variable field %i is out of bounds (max. %i)", field_num, var->num_fields - 1);\r
- if ((var) && (field_num < var->num_fields))\r
- {\r
- if (argc > 2)\r
- {\r
- if (strcmp(args[2], "flip") == 0)\r
- var->fields[field_num].value = flip_u32(var->fields[field_num].value, var->fields[field_num].num_bits);\r
- else\r
- var->fields[field_num].value = strtoul(args[2], NULL, 0);\r
- }\r
-\r
- command_print(cmd_ctx, "%s(%i): 0x%x (/%i)", var->name, field_num, var->fields[field_num].value, var->fields[field_num].num_bits);\r
- }\r
- }\r
-\r
- return ERROR_OK;\r
-}\r
-\r
-int handle_script_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- FILE *script_file;\r
- int echo;\r
-\r
- if (argc != 1)\r
- return ERROR_COMMAND_SYNTAX_ERROR;\r
-\r
- script_file = open_file_from_path(cmd_ctx, args[0], "r");\r
-\r
- if (!script_file)\r
- {\r
- command_print(cmd_ctx, "couldn't open script file %s", args[0]);\r
- return ERROR_OK;\r
- }\r
-\r
- echo = cmd_ctx->echo;\r
- cmd_ctx->echo = 1;\r
- \r
- command_run_file(cmd_ctx, script_file, cmd_ctx->mode);\r
- \r
- cmd_ctx->echo = echo;\r
- \r
- fclose(script_file);\r
-\r
- return ERROR_OK;\r
-}\r
+/***************************************************************************
+ * Copyright (C) 2005 by Dominic Rath *
+ * Dominic.Rath@gmx.de *
+ * *
+ * 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 "interpreter.h"
+#include "configuration.h"
+
+#include "binarybuffer.h"
+#include <stdlib.h>
+#include <string.h>
+
+var_t *variables = NULL;
+
+int handle_var_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+int handle_field_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+int handle_script_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+
+int interpreter_register_commands(struct command_context_s *cmd_ctx)
+{
+ register_command(cmd_ctx, NULL, "var", handle_var_command,
+ COMMAND_ANY, "allocate, display or delete variable <name> [num_fields|'del'] [size1] ...");
+ register_command(cmd_ctx, NULL, "field", handle_field_command,
+ COMMAND_ANY, "display/modify variable field <var> <field> [value|'flip']");
+ register_command(cmd_ctx, NULL, "script", handle_script_command,
+ COMMAND_ANY, "execute commands from <file>");
+
+ return ERROR_OK;
+}
+
+var_t* get_var_by_num(int num)
+{
+ int count = 0;
+ var_t *var = variables;
+
+ if (var)
+ {
+ if (num == count)
+ return var;
+ while (var->next)
+ {
+ var = var->next;
+ count++;
+ if (num == count)
+ return var;
+ }
+ }
+ return NULL;
+}
+
+var_t* get_var_by_name(char *name)
+{
+ var_t *var = variables;
+
+ if (var)
+ {
+ if (strcmp(var->name, name) == 0)
+ return var;
+ while (var->next)
+ {
+ var = var->next;
+ if (strcmp(var->name, name) == 0)
+ return var;
+ }
+ }
+ return NULL;
+}
+
+var_t* get_var_by_namenum(char *namenum)
+{
+ if ((namenum[0] >= '0') && (namenum[0] <= '9'))
+ return get_var_by_num(strtol(namenum, NULL, 0));
+ else
+ return get_var_by_name(namenum);
+
+}
+
+int field_le_to_host(u8 *buffer, void *priv, struct scan_field_s *dummy)
+{
+ var_field_t *field = priv;
+ field->value = buf_get_u32(buffer, 0, field->num_bits);
+
+ return ERROR_OK;
+}
+
+int handle_var_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ var_t **last_var_p = &variables;
+ int i;
+
+ if (argc >= 2)
+ {
+ while (*last_var_p)
+ {
+ if (strcmp((*last_var_p)->name, args[0]) == 0)
+ {
+ if (strcmp(args[1], "del") == 0)
+ {
+ var_t *next = (*last_var_p)->next;
+ free ((*last_var_p)->fields);
+ free (*last_var_p);
+ *last_var_p = next;
+ command_print(cmd_ctx, "variable %s deleted", args[0]);
+ }
+ else
+ command_print(cmd_ctx, "variable of that name already exists");
+ return ERROR_OK;
+ }
+ last_var_p = &((*last_var_p)->next);
+ }
+
+ if ((args[0][0] >= '0') && (args[0][0] <= '9'))
+ {
+ command_print(cmd_ctx, "invalid name specified (first character may not be a number)");
+ return ERROR_OK;
+ }
+
+ *last_var_p = malloc(sizeof(var_t));
+ (*last_var_p)->name = strdup(args[0]);
+ (*last_var_p)->num_fields = argc - 1;
+ (*last_var_p)->next = NULL;
+
+ (*last_var_p)->fields = malloc(sizeof(var_field_t) * (*last_var_p)->num_fields);
+ for (i = 0; i < (*last_var_p)->num_fields; i++)
+ {
+ (*last_var_p)->fields[i].num_bits = strtol(args[1+i], NULL, 0);
+ (*last_var_p)->fields[i].value = 0x0;
+ }
+ return ERROR_OK;
+ }
+
+ if (argc == 1)
+ {
+ var_t *var = get_var_by_namenum(args[0]);
+ if (var)
+ {
+ int i;
+ command_print(cmd_ctx, "%s (%i fields):", var->name, var->num_fields);
+ for (i = 0; i < (var->num_fields); i++)
+ {
+ command_print(cmd_ctx, "0x%x (/%i)", var->fields[i].value, var->fields[i].num_bits);
+ }
+ }
+ else
+ {
+ command_print(cmd_ctx, "variable %s doesn't exist", args[0]);
+ }
+ }
+
+ if (argc == 0)
+ {
+ var_t *var = variables;
+ int count = 0;
+ while (var)
+ {
+ command_print(cmd_ctx, "%i: %s (%i fields)", count, var->name, var->num_fields);
+ var = var->next;
+ count++;
+ }
+ }
+
+ return ERROR_OK;
+}
+
+int handle_field_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+
+ if (argc < 2)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ if (argc >= 2)
+ {
+ var_t *var = get_var_by_namenum(args[0]);
+ int field_num = strtol(args[1], NULL, 0);
+ if (!var)
+ {
+ command_print(cmd_ctx, "variable %s doesn't exist", args[0]);
+ return ERROR_OK;
+ }
+ if (field_num >= var->num_fields)
+ command_print(cmd_ctx, "variable field %i is out of bounds (max. %i)", field_num, var->num_fields - 1);
+ if ((var) && (field_num < var->num_fields))
+ {
+ if (argc > 2)
+ {
+ if (strcmp(args[2], "flip") == 0)
+ var->fields[field_num].value = flip_u32(var->fields[field_num].value, var->fields[field_num].num_bits);
+ else
+ var->fields[field_num].value = strtoul(args[2], NULL, 0);
+ }
+
+ command_print(cmd_ctx, "%s(%i): 0x%x (/%i)", var->name, field_num, var->fields[field_num].value, var->fields[field_num].num_bits);
+ }
+ }
+
+ return ERROR_OK;
+}
+
+int handle_script_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ FILE *script_file;
+ int echo;
+
+ if (argc != 1)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ script_file = open_file_from_path(cmd_ctx, args[0], "r");
+
+ if (!script_file)
+ {
+ command_print(cmd_ctx, "couldn't open script file %s", args[0]);
+ return ERROR_OK;
+ }
+
+ echo = cmd_ctx->echo;
+ cmd_ctx->echo = 1;
+
+ command_run_file(cmd_ctx, script_file, cmd_ctx->mode);
+
+ cmd_ctx->echo = echo;
+
+ fclose(script_file);
+
+ return ERROR_OK;
+}
-/***************************************************************************\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 "replacements.h"\r
-\r
-#include "gdb_server.h"\r
-\r
-#include "server.h"\r
-#include "log.h"\r
-#include "binarybuffer.h"\r
-#include "jtag.h"\r
-#include "breakpoints.h"\r
-#include "flash.h"\r
-#include "target_request.h"\r
-#include "configuration.h"\r
-\r
-#include <string.h>\r
-#include <errno.h>\r
-#include <unistd.h>\r
-#include <stdlib.h>\r
-\r
-#if 0\r
-#define _DEBUG_GDB_IO_\r
-#endif\r
-\r
-static unsigned short gdb_port;\r
-static const char *DIGITS = "0123456789abcdef";\r
-\r
-static void gdb_log_callback(void *priv, const char *file, int line, \r
- const char *function, const char *format, va_list args);\r
-\r
-enum gdb_detach_mode\r
-{\r
- GDB_DETACH_RESUME,\r
- GDB_DETACH_RESET,\r
- GDB_DETACH_HALT,\r
- GDB_DETACH_NOTHING\r
-};\r
-\r
-/* target behaviour on gdb detach */\r
-enum gdb_detach_mode detach_mode = GDB_DETACH_RESUME;\r
-\r
-/* set if we are sending a memory map to gdb\r
- * via qXfer:memory-map:read packet */\r
-int gdb_use_memory_map = 0;\r
-int gdb_flash_program = 0;\r
-\r
-/* if set, data aborts cause an error to be reported in memory read packets\r
- * see the code in gdb_read_memory_packet() for further explanations */\r
-int gdb_report_data_abort = 0;\r
-\r
-int gdb_last_signal(target_t *target)\r
-{\r
- switch (target->debug_reason)\r
- {\r
- case DBG_REASON_DBGRQ:\r
- return 0x2; /* SIGINT */\r
- case DBG_REASON_BREAKPOINT:\r
- case DBG_REASON_WATCHPOINT:\r
- case DBG_REASON_WPTANDBKPT:\r
- return 0x05; /* SIGTRAP */\r
- case DBG_REASON_SINGLESTEP:\r
- return 0x05; /* SIGTRAP */\r
- case DBG_REASON_NOTHALTED:\r
- return 0x0; /* no signal... shouldn't happen */\r
- default:\r
- ERROR("BUG: undefined debug reason");\r
- exit(-1);\r
- }\r
-}\r
-\r
-int gdb_get_char(connection_t *connection, int* next_char)\r
-{\r
- gdb_connection_t *gdb_con = connection->priv;\r
-\r
-#ifdef _DEBUG_GDB_IO_\r
- char *debug_buffer;\r
-#endif\r
-\r
- if (gdb_con->buf_cnt-- > 0)\r
- {\r
- *next_char = *(gdb_con->buf_p++);\r
- if (gdb_con->buf_cnt > 0)\r
- connection->input_pending = 1;\r
- else\r
- connection->input_pending = 0;\r
-\r
-#ifdef _DEBUG_GDB_IO_\r
- DEBUG("returned char '%c' (0x%2.2x)", *next_char, *next_char);\r
-#endif\r
-\r
- return ERROR_OK;\r
- }\r
-\r
- for (;;)\r
- {\r
-#ifndef _WIN32\r
- /* a non-blocking socket will block if there is 0 bytes available on the socket,\r
- * but return with as many bytes as are available immediately\r
- */\r
- struct timeval tv;\r
- fd_set read_fds;\r
- \r
- FD_ZERO(&read_fds);\r
- FD_SET(connection->fd, &read_fds);\r
- \r
- tv.tv_sec = 1;\r
- tv.tv_usec = 0;\r
- if (select(connection->fd + 1, &read_fds, NULL, NULL, &tv) == 0)\r
- {\r
- /* This can typically be because a "monitor" command took too long\r
- * before printing any progress messages\r
- */\r
- return ERROR_GDB_TIMEOUT; \r
- }\r
-#endif\r
- gdb_con->buf_cnt = read_socket(connection->fd, gdb_con->buffer, GDB_BUFFER_SIZE);\r
- if (gdb_con->buf_cnt > 0)\r
- {\r
- break;\r
- }\r
- if (gdb_con->buf_cnt == 0)\r
- {\r
- gdb_con->closed = 1;\r
- return ERROR_SERVER_REMOTE_CLOSED;\r
- }\r
-\r
-#ifdef _WIN32\r
- errno = WSAGetLastError();\r
-\r
- switch(errno)\r
- {\r
- case WSAEWOULDBLOCK:\r
- usleep(1000);\r
- break;\r
- case WSAECONNABORTED:\r
- return ERROR_SERVER_REMOTE_CLOSED;\r
- case WSAECONNRESET:\r
- return ERROR_SERVER_REMOTE_CLOSED;\r
- default:\r
- ERROR("read: %d", errno);\r
- exit(-1);\r
- }\r
-#else\r
- switch(errno)\r
- {\r
- case EAGAIN:\r
- usleep(1000);\r
- break;\r
- case ECONNABORTED:\r
- return ERROR_SERVER_REMOTE_CLOSED;\r
- case ECONNRESET:\r
- return ERROR_SERVER_REMOTE_CLOSED;\r
- default:\r
- ERROR("read: %s", strerror(errno));\r
- return ERROR_SERVER_REMOTE_CLOSED;\r
- }\r
-#endif\r
- }\r
-\r
-#ifdef _DEBUG_GDB_IO_\r
- debug_buffer = malloc(gdb_con->buf_cnt + 1);\r
- memcpy(debug_buffer, gdb_con->buffer, gdb_con->buf_cnt);\r
- debug_buffer[gdb_con->buf_cnt] = 0;\r
- DEBUG("received '%s'", debug_buffer);\r
- free(debug_buffer);\r
-#endif\r
-\r
- gdb_con->buf_p = gdb_con->buffer;\r
- gdb_con->buf_cnt--;\r
- *next_char = *(gdb_con->buf_p++);\r
- if (gdb_con->buf_cnt > 0)\r
- connection->input_pending = 1;\r
- else\r
- connection->input_pending = 0; \r
-#ifdef _DEBUG_GDB_IO_\r
- DEBUG("returned char '%c' (0x%2.2x)", *next_char, *next_char);\r
-#endif\r
-\r
- return ERROR_OK;\r
-}\r
-\r
-int gdb_putback_char(connection_t *connection, int last_char)\r
-{\r
- gdb_connection_t *gdb_con = connection->priv;\r
-\r
- if (gdb_con->buf_p > gdb_con->buffer)\r
- {\r
- *(--gdb_con->buf_p) = last_char;\r
- gdb_con->buf_cnt++;\r
- }\r
- else\r
- {\r
- ERROR("BUG: couldn't put character back"); \r
- }\r
-\r
- return ERROR_OK;\r
-}\r
-\r
-/* The only way we can detect that the socket is closed is the first time\r
- * we write to it, we will fail. Subsequent write operations will\r
- * succeed. Shudder! */\r
-int gdb_write(connection_t *connection, void *data, int len)\r
-{\r
- gdb_connection_t *gdb_con = connection->priv;\r
- if (gdb_con->closed)\r
- return ERROR_SERVER_REMOTE_CLOSED;\r
-\r
- if (write_socket(connection->fd, data, len) == len)\r
- {\r
- return ERROR_OK;\r
- }\r
- gdb_con->closed = 1;\r
- return ERROR_SERVER_REMOTE_CLOSED;\r
-}\r
-\r
-int gdb_put_packet_inner(connection_t *connection, char *buffer, int len)\r
-{\r
- int i;\r
- unsigned char my_checksum = 0;\r
-#ifdef _DEBUG_GDB_IO_\r
- char *debug_buffer;\r
-#endif\r
- int reply;\r
- int retval;\r
- gdb_connection_t *gdb_con = connection->priv;\r
-\r
- for (i = 0; i < len; i++)\r
- my_checksum += buffer[i];\r
-\r
- while (1)\r
- {\r
-#ifdef _DEBUG_GDB_IO_\r
- debug_buffer = malloc(len + 1);\r
- memcpy(debug_buffer, buffer, len);\r
- debug_buffer[len] = 0;\r
- DEBUG("sending packet '$%s#%2.2x'", debug_buffer, my_checksum);\r
- free(debug_buffer);\r
-#endif\r
-#if 0\r
- char checksum[3];\r
- gdb_write(connection, "$", 1);\r
- if (len > 0)\r
- gdb_write(connection, buffer, len);\r
- gdb_write(connection, "#", 1);\r
- \r
- snprintf(checksum, 3, "%2.2x", my_checksum);\r
- \r
- gdb_write(connection, checksum, 2);\r
-#else\r
- void *allocated = NULL;\r
- char stackAlloc[1024];\r
- char *t = stackAlloc;\r
- int totalLen = 1 + len + 1 + 2;\r
- if (totalLen > sizeof(stackAlloc))\r
- {\r
- allocated = malloc(totalLen);\r
- t = allocated;\r
- if (allocated == NULL)\r
- {\r
- ERROR("Ran out of memory trying to reply packet %d\n", totalLen);\r
- exit(-1);\r
- }\r
- }\r
- t[0] = '$';\r
- memcpy(t + 1, buffer, len);\r
- t[1 + len] = '#';\r
- t[1 + len + 1] = DIGITS[(my_checksum >> 4) & 0xf];\r
- t[1 + len + 2] = DIGITS[my_checksum & 0xf];\r
- \r
- gdb_write(connection, t, totalLen);\r
- \r
- if (allocated)\r
- {\r
- free(allocated);\r
- }\r
-#endif\r
- if ((retval = gdb_get_char(connection, &reply)) != ERROR_OK)\r
- return retval;\r
-\r
- if (reply == '+')\r
- break;\r
- else if (reply == '-')\r
- {\r
- /* Stop sending output packets for now */\r
- log_setCallback(NULL, NULL);\r
- WARNING("negative reply, retrying");\r
- }\r
- else if (reply == 0x3)\r
- {\r
- gdb_con->ctrl_c = 1;\r
- if ((retval = gdb_get_char(connection, &reply)) != ERROR_OK)\r
- return retval;\r
- if (reply == '+')\r
- break;\r
- else if (reply == '-')\r
- {\r
- /* Stop sending output packets for now */\r
- log_setCallback(NULL, NULL);\r
- WARNING("negative reply, retrying");\r
- }\r
- else\r
- {\r
- ERROR("unknown character 0x%2.2x in reply, dropping connection", reply);\r
- return ERROR_SERVER_REMOTE_CLOSED;\r
- }\r
- }\r
- else\r
- {\r
- ERROR("unknown character 0x%2.2x in reply, dropping connection", reply);\r
- return ERROR_SERVER_REMOTE_CLOSED;\r
- }\r
- }\r
- if (gdb_con->closed)\r
- return ERROR_SERVER_REMOTE_CLOSED;\r
-\r
- return ERROR_OK;\r
-}\r
-\r
-int gdb_put_packet(connection_t *connection, char *buffer, int len)\r
-{\r
- gdb_connection_t *gdb_con = connection->priv;\r
- gdb_con->busy = 1;\r
- int retval = gdb_put_packet_inner(connection, buffer, len);\r
- gdb_con->busy = 0;\r
- return retval;\r
-}\r
-\r
-int gdb_get_packet_inner(connection_t *connection, char *buffer, int *len)\r
-{\r
- int character;\r
- int count = 0;\r
- int retval;\r
- char checksum[3];\r
- unsigned char my_checksum = 0;\r
- gdb_connection_t *gdb_con = connection->priv;\r
-\r
- while (1)\r
- {\r
- do\r
- {\r
- if ((retval = gdb_get_char(connection, &character)) != ERROR_OK)\r
- return retval;\r
-\r
-#ifdef _DEBUG_GDB_IO_\r
- DEBUG("character: '%c'", character);\r
-#endif\r
-\r
- switch (character)\r
- {\r
- case '$':\r
- break;\r
- case '+':\r
- WARNING("acknowledgment received, but no packet pending");\r
- break;\r
- case '-':\r
- WARNING("negative acknowledgment, but no packet pending");\r
- break;\r
- case 0x3:\r
- gdb_con->ctrl_c = 1;\r
- *len = 0;\r
- return ERROR_OK;\r
- default:\r
- WARNING("ignoring character 0x%x", character);\r
- break;\r
- }\r
- } while (character != '$');\r
-\r
- my_checksum = 0;\r
- \r
- count = 0;\r
- gdb_connection_t *gdb_con = connection->priv;\r
- for (;;)\r
- {\r
- /* The common case is that we have an entire packet with no escape chars.\r
- * We need to leave at least 2 bytes in the buffer to have\r
- * gdb_get_char() update various bits and bobs correctly. \r
- */\r
- if ((gdb_con->buf_cnt > 2) && ((gdb_con->buf_cnt+count) < *len))\r
- {\r
- /* The compiler will struggle a bit with constant propagation and\r
- * aliasing, so we help it by showing that these values do not\r
- * change inside the loop \r
- */ \r
- int i;\r
- char *buf = gdb_con->buf_p;\r
- int run = gdb_con->buf_cnt - 2;\r
- i = 0;\r
- int done = 0;\r
- while (i < run)\r
- {\r
- character = *buf++;\r
- i++;\r
- if (character == '#')\r
- {\r
- /* Danger! character can be '#' when esc is \r
- * used so we need an explicit boolean for done here.\r
- */\r
- done = 1;\r
- break;\r
- }\r
- \r
- if (character == '}')\r
- {\r
- /* data transmitted in binary mode (X packet)\r
- * uses 0x7d as escape character */\r
- my_checksum += character & 0xff;\r
- character = *buf++;\r
- i++;\r
- my_checksum += character & 0xff;\r
- buffer[count++] = (character ^ 0x20) & 0xff;\r
- } else\r
- {\r
- my_checksum += character & 0xff;\r
- buffer[count++] = character & 0xff;\r
- }\r
- }\r
- gdb_con->buf_p += i;\r
- gdb_con->buf_cnt -= i;\r
- if (done) \r
- break;\r
- } \r
- if (count > *len)\r
- {\r
- ERROR("packet buffer too small");\r
- return ERROR_GDB_BUFFER_TOO_SMALL;\r
- }\r
- \r
- if ((retval = gdb_get_char(connection, &character)) != ERROR_OK)\r
- return retval;\r
-\r
- if (character == '#')\r
- break;\r
-\r
- if (character == '}')\r
- {\r
- /* data transmitted in binary mode (X packet)\r
- * uses 0x7d as escape character */\r
- my_checksum += character & 0xff;\r
- if ((retval = gdb_get_char(connection, &character)) != ERROR_OK)\r
- return retval;\r
- my_checksum += character & 0xff;\r
- buffer[count++] = (character ^ 0x20) & 0xff;\r
- }\r
- else\r
- {\r
- my_checksum += character & 0xff;\r
- buffer[count++] = character & 0xff;\r
- }\r
-\r
- }\r
-\r
- *len = count;\r
-\r
- if ((retval = gdb_get_char(connection, &character)) != ERROR_OK)\r
- return retval;\r
- checksum[0] = character;\r
- if ((retval = gdb_get_char(connection, &character)) != ERROR_OK)\r
- return retval;\r
- checksum[1] = character;\r
- checksum[2] = 0;\r
-\r
- if (my_checksum == strtoul(checksum, NULL, 16))\r
- {\r
- gdb_write(connection, "+", 1);\r
- break;\r
- }\r
-\r
- WARNING("checksum error, requesting retransmission");\r
- gdb_write(connection, "-", 1);\r
- }\r
- if (gdb_con->closed)\r
- return ERROR_SERVER_REMOTE_CLOSED;\r
-\r
- return ERROR_OK;\r
-}\r
-\r
-int gdb_get_packet(connection_t *connection, char *buffer, int *len)\r
-{\r
- gdb_connection_t *gdb_con = connection->priv;\r
- gdb_con->busy = 1;\r
- int retval = gdb_get_packet_inner(connection, buffer, len);\r
- gdb_con->busy = 0;\r
- return retval;\r
-}\r
- \r
-int gdb_output_con(connection_t *connection, char* line)\r
-{\r
- char *hex_buffer;\r
- int i, bin_size;\r
-\r
- bin_size = strlen(line);\r
-\r
- hex_buffer = malloc(bin_size*2 + 4);\r
-\r
- hex_buffer[0] = 'O';\r
- for (i=0; i<bin_size; i++)\r
- snprintf(hex_buffer + 1 + i*2, 3, "%2.2x", line[i]);\r
- hex_buffer[bin_size*2+1] = '0';\r
- hex_buffer[bin_size*2+2] = 'a';\r
- hex_buffer[bin_size*2+3] = 0x0;\r
-\r
- gdb_put_packet(connection, hex_buffer, bin_size*2 + 3);\r
-\r
- free(hex_buffer);\r
- return ERROR_OK;\r
-}\r
-\r
-int gdb_output(struct command_context_s *context, char* line)\r
-{\r
- /* this will be dumped to the log and also sent as an O packet if possible */\r
- USER(line); \r
- return ERROR_OK;\r
-}\r
-\r
-int gdb_program_handler(struct target_s *target, enum target_event event, void *priv)\r
-{\r
- FILE *script;\r
- struct command_context_s *cmd_ctx = priv;\r
- \r
- if (target->gdb_program_script)\r
- {\r
- script = open_file_from_path(cmd_ctx, target->gdb_program_script, "r");\r
- if (!script)\r
- {\r
- ERROR("couldn't open script file %s", target->gdb_program_script);\r
- return ERROR_OK;\r
- }\r
-\r
- INFO("executing gdb_program script '%s'", target->gdb_program_script);\r
- command_run_file(cmd_ctx, script, COMMAND_EXEC);\r
- fclose(script);\r
- \r
- jtag_execute_queue();\r
- }\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-int gdb_target_callback_event_handler(struct target_s *target, enum target_event event, void *priv)\r
-{\r
- connection_t *connection = priv;\r
- gdb_connection_t *gdb_connection = connection->priv;\r
- char sig_reply[4];\r
- int signal;\r
-\r
- switch (event)\r
- {\r
- case TARGET_EVENT_HALTED:\r
- /* In the GDB protocol when we are stepping or coninuing execution,\r
- * we have a lingering reply. Upon receiving a halted event \r
- * when we have that lingering packet, we reply to the original\r
- * step or continue packet.\r
- * \r
- * Executing monitor commands can bring the target in and\r
- * out of the running state so we'll see lots of TARGET_EVENT_XXX\r
- * that are to be ignored.\r
- */\r
- if (gdb_connection->frontend_state == TARGET_RUNNING)\r
- {\r
- /* stop forwarding log packets! */\r
- log_setCallback(NULL, NULL);\r
- \r
- if (gdb_connection->ctrl_c)\r
- {\r
- signal = 0x2;\r
- gdb_connection->ctrl_c = 0;\r
- }\r
- else\r
- {\r
- signal = gdb_last_signal(target);\r
- }\r
-\r
- snprintf(sig_reply, 4, "T%2.2x", signal);\r
- gdb_put_packet(connection, sig_reply, 3);\r
- gdb_connection->frontend_state = TARGET_HALTED;\r
- }\r
- break;\r
- case TARGET_EVENT_GDB_PROGRAM:\r
- gdb_program_handler(target, event, connection->cmd_ctx);\r
- break;\r
- default:\r
- break;\r
- }\r
-\r
- return ERROR_OK;\r
-}\r
-\r
-int gdb_new_connection(connection_t *connection)\r
-{\r
- gdb_connection_t *gdb_connection = malloc(sizeof(gdb_connection_t));\r
- gdb_service_t *gdb_service = connection->service->priv;\r
- int retval;\r
- int initial_ack;\r
-\r
- connection->priv = gdb_connection;\r
-\r
- /* initialize gdb connection information */\r
- gdb_connection->buf_p = gdb_connection->buffer;\r
- gdb_connection->buf_cnt = 0;\r
- gdb_connection->ctrl_c = 0;\r
- gdb_connection->frontend_state = TARGET_HALTED;\r
- gdb_connection->vflash_image = NULL;\r
- gdb_connection->closed = 0;\r
- gdb_connection->busy = 0;\r
- \r
- /* output goes through gdb connection */\r
- command_set_output_handler(connection->cmd_ctx, gdb_output, connection);\r
-\r
- /* register callback to be informed about target events */\r
- target_register_event_callback(gdb_target_callback_event_handler, connection); \r
-\r
- /* a gdb session just attached, put the target in halt mode */\r
- if (((retval = gdb_service->target->type->halt(gdb_service->target)) != ERROR_OK) &&\r
- (retval != ERROR_TARGET_ALREADY_HALTED))\r
- {\r
- ERROR("error(%d) when trying to halt target, falling back to \"reset halt\"", retval);\r
- command_run_line(connection->cmd_ctx, "reset halt");\r
- }\r
-\r
- /* This will time out after 1 second */\r
- command_run_line(connection->cmd_ctx, "wait_halt 1");\r
-\r
- /* remove the initial ACK from the incoming buffer */\r
- if ((retval = gdb_get_char(connection, &initial_ack)) != ERROR_OK)\r
- return retval;\r
-\r
- if (initial_ack != '+')\r
- gdb_putback_char(connection, initial_ack);\r
-\r
- return ERROR_OK;\r
-}\r
-\r
-int gdb_connection_closed(connection_t *connection)\r
-{\r
- gdb_service_t *gdb_service = connection->service->priv;\r
- gdb_connection_t *gdb_connection = connection->priv;\r
-\r
- /* see if an image built with vFlash commands is left */\r
- if (gdb_connection->vflash_image)\r
- {\r
- image_close(gdb_connection->vflash_image);\r
- free(gdb_connection->vflash_image);\r
- gdb_connection->vflash_image = NULL;\r
- }\r
-\r
- /* if this connection registered a debug-message receiver delete it */\r
- delete_debug_msg_receiver(connection->cmd_ctx, gdb_service->target);\r
- \r
- if (connection->priv)\r
- {\r
- free(connection->priv);\r
- connection->priv = NULL;\r
- }\r
- else\r
- {\r
- ERROR("BUG: connection->priv == NULL");\r
- }\r
-\r
- target_unregister_event_callback(gdb_target_callback_event_handler, connection);\r
- log_setCallback(NULL, NULL);\r
-\r
- return ERROR_OK;\r
-}\r
-\r
-void gdb_send_error(connection_t *connection, u8 the_error)\r
-{\r
- char err[4];\r
- snprintf(err, 4, "E%2.2X", the_error );\r
- gdb_put_packet(connection, err, 3);\r
-}\r
-\r
-int gdb_last_signal_packet(connection_t *connection, target_t *target, char* packet, int packet_size)\r
-{\r
- char sig_reply[4];\r
- int signal;\r
-\r
- signal = gdb_last_signal(target);\r
-\r
- snprintf(sig_reply, 4, "S%2.2x", signal);\r
- gdb_put_packet(connection, sig_reply, 3);\r
-\r
- return ERROR_OK;\r
-}\r
-\r
-/* Convert register to string of bits. NB! The # of bits in the\r
- * register might be non-divisible by 8(a byte), in which\r
- * case an entire byte is shown. */\r
-void gdb_str_to_target(target_t *target, char *tstr, reg_t *reg)\r
-{\r
- int i;\r
-\r
- u8 *buf;\r
- int buf_len;\r
- buf = reg->value;\r
- buf_len = CEIL(reg->size, 8); \r
-\r
- if (target->endianness == TARGET_LITTLE_ENDIAN)\r
- {\r
- for (i = 0; i < buf_len; i++)\r
- {\r
- tstr[i*2] = DIGITS[(buf[i]>>4) & 0xf];\r
- tstr[i*2+1] = DIGITS[buf[i]&0xf];\r
- }\r
- }\r
- else\r
- {\r
- for (i = 0; i < buf_len; i++)\r
- {\r
- tstr[(buf_len-1-i)*2] = DIGITS[(buf[i]>>4)&0xf];\r
- tstr[(buf_len-1-i)*2+1] = DIGITS[buf[i]&0xf];\r
- }\r
- } \r
-}\r
-\r
-void gdb_target_to_str(target_t *target, char *tstr, char *str)\r
-{\r
- int str_len = strlen(tstr);\r
- int i;\r
-\r
- if (str_len % 2)\r
- {\r
- ERROR("BUG: gdb value with uneven number of characters encountered");\r
- exit(-1);\r
- }\r
-\r
- if (target->endianness == TARGET_LITTLE_ENDIAN)\r
- {\r
- for (i = 0; i < str_len; i+=2)\r
- {\r
- str[str_len - i - 1] = tstr[i + 1];\r
- str[str_len - i - 2] = tstr[i];\r
- }\r
- }\r
- else\r
- {\r
- for (i = 0; i < str_len; i++)\r
- {\r
- str[i] = tstr[i];\r
- }\r
- } \r
-}\r
-\r
-int gdb_get_registers_packet(connection_t *connection, target_t *target, char* packet, int packet_size)\r
-{\r
- reg_t **reg_list;\r
- int reg_list_size;\r
- int retval;\r
- int reg_packet_size = 0;\r
- char *reg_packet;\r
- char *reg_packet_p;\r
- int i;\r
-\r
-#ifdef _DEBUG_GDB_IO_\r
- DEBUG("-");\r
-#endif\r
-\r
- if ((retval = target->type->get_gdb_reg_list(target, ®_list, ®_list_size)) != ERROR_OK)\r
- {\r
- switch (retval)\r
- {\r
- case ERROR_TARGET_NOT_HALTED:\r
- ERROR("gdb requested registers but we're not halted, dropping connection");\r
- return ERROR_SERVER_REMOTE_CLOSED;\r
- default:\r
- /* this is a bug condition - get_gdb_reg_list() may not return any other error */\r
- ERROR("BUG: unexpected error returned by get_gdb_reg_list()");\r
- exit(-1);\r
- }\r
- }\r
-\r
- for (i = 0; i < reg_list_size; i++)\r
- {\r
- reg_packet_size += reg_list[i]->size;\r
- }\r
-\r
- reg_packet = malloc(CEIL(reg_packet_size, 8) * 2);\r
- reg_packet_p = reg_packet;\r
-\r
- for (i = 0; i < reg_list_size; i++)\r
- {\r
- gdb_str_to_target(target, reg_packet_p, reg_list[i]);\r
- reg_packet_p += CEIL(reg_list[i]->size, 8) * 2;\r
- }\r
-\r
-#ifdef _DEBUG_GDB_IO_\r
- {\r
- char *reg_packet_p;\r
- reg_packet_p = strndup(reg_packet, CEIL(reg_packet_size, 8) * 2);\r
- DEBUG("reg_packet: %s", reg_packet_p);\r
- free(reg_packet_p);\r
- }\r
-#endif\r
-\r
- gdb_put_packet(connection, reg_packet, CEIL(reg_packet_size, 8) * 2);\r
- free(reg_packet);\r
-\r
- free(reg_list);\r
-\r
- return ERROR_OK;\r
-}\r
-\r
-int gdb_set_registers_packet(connection_t *connection, target_t *target, char *packet, int packet_size)\r
-{\r
- int i;\r
- reg_t **reg_list;\r
- int reg_list_size;\r
- int retval;\r
- char *packet_p;\r
-\r
-#ifdef _DEBUG_GDB_IO_\r
- DEBUG("-");\r
-#endif\r
-\r
- /* skip command character */\r
- packet++;\r
- packet_size--;\r
-\r
- if (packet_size % 2)\r
- {\r
- WARNING("GDB set_registers packet with uneven characters received, dropping connection");\r
- return ERROR_SERVER_REMOTE_CLOSED;\r
- }\r
-\r
- if ((retval = target->type->get_gdb_reg_list(target, ®_list, ®_list_size)) != ERROR_OK)\r
- {\r
- switch (retval)\r
- {\r
- case ERROR_TARGET_NOT_HALTED:\r
- ERROR("gdb tried to registers but we're not halted, dropping connection");\r
- return ERROR_SERVER_REMOTE_CLOSED;\r
- default:\r
- /* this is a bug condition - get_gdb_reg_list() may not return any other error */\r
- ERROR("BUG: unexpected error returned by get_gdb_reg_list()");\r
- exit(-1);\r
- }\r
- }\r
-\r
- packet_p = packet;\r
- for (i = 0; i < reg_list_size; i++)\r
- {\r
- u8 *bin_buf;\r
- char *hex_buf;\r
- reg_arch_type_t *arch_type;\r
-\r
- /* convert from GDB-string (target-endian) to hex-string (big-endian) */\r
- hex_buf = malloc(CEIL(reg_list[i]->size, 8) * 2);\r
- gdb_target_to_str(target, packet_p, hex_buf);\r
-\r
- /* convert hex-string to binary buffer */\r
- bin_buf = malloc(CEIL(reg_list[i]->size, 8));\r
- str_to_buf(hex_buf, CEIL(reg_list[i]->size, 8) * 2, bin_buf, reg_list[i]->size, 16);\r
-\r
- /* get register arch_type, and call set method */ \r
- arch_type = register_get_arch_type(reg_list[i]->arch_type);\r
- if (arch_type == NULL)\r
- {\r
- ERROR("BUG: encountered unregistered arch type");\r
- exit(-1);\r
- }\r
- arch_type->set(reg_list[i], bin_buf);\r
-\r
- /* advance packet pointer */ \r
- packet_p += (CEIL(reg_list[i]->size, 8) * 2);\r
-\r
- free(bin_buf);\r
- free(hex_buf);\r
- }\r
-\r
- /* free reg_t *reg_list[] array allocated by get_gdb_reg_list */ \r
- free(reg_list);\r
-\r
- gdb_put_packet(connection, "OK", 2);\r
-\r
- return ERROR_OK;\r
-}\r
-\r
-int gdb_get_register_packet(connection_t *connection, target_t *target, char *packet, int packet_size)\r
-{\r
- char *reg_packet;\r
- int reg_num = strtoul(packet + 1, NULL, 16);\r
- reg_t **reg_list;\r
- int reg_list_size;\r
- int retval;\r
-\r
-#ifdef _DEBUG_GDB_IO_\r
- DEBUG("-");\r
-#endif\r
-\r
- if ((retval = target->type->get_gdb_reg_list(target, ®_list, ®_list_size)) != ERROR_OK)\r
- {\r
- switch (retval)\r
- {\r
- case ERROR_TARGET_NOT_HALTED:\r
- ERROR("gdb requested registers but we're not halted, dropping connection");\r
- return ERROR_SERVER_REMOTE_CLOSED;\r
- default:\r
- /* this is a bug condition - get_gdb_reg_list() may not return any other error */\r
- ERROR("BUG: unexpected error returned by get_gdb_reg_list()");\r
- exit(-1);\r
- }\r
- }\r
-\r
- if (reg_list_size <= reg_num)\r
- {\r
- ERROR("gdb requested a non-existing register");\r
- exit(-1);\r
- }\r
-\r
- reg_packet = malloc(CEIL(reg_list[reg_num]->size, 8) * 2);\r
-\r
- gdb_str_to_target(target, reg_packet, reg_list[reg_num]);\r
-\r
- gdb_put_packet(connection, reg_packet, CEIL(reg_list[reg_num]->size, 8) * 2);\r
-\r
- free(reg_list);\r
- free(reg_packet);\r
-\r
- return ERROR_OK;\r
-}\r
-\r
-int gdb_set_register_packet(connection_t *connection, target_t *target, char *packet, int packet_size)\r
-{\r
- char *separator;\r
- char *hex_buf;\r
- u8 *bin_buf;\r
- int reg_num = strtoul(packet + 1, &separator, 16);\r
- reg_t **reg_list;\r
- int reg_list_size;\r
- int retval;\r
- reg_arch_type_t *arch_type;\r
-\r
- DEBUG("-");\r
-\r
- if ((retval = target->type->get_gdb_reg_list(target, ®_list, ®_list_size)) != ERROR_OK)\r
- {\r
- switch (retval)\r
- {\r
- case ERROR_TARGET_NOT_HALTED:\r
- ERROR("gdb tried to set a register but we're not halted, dropping connection");\r
- return ERROR_SERVER_REMOTE_CLOSED;\r
- default:\r
- /* this is a bug condition - get_gdb_reg_list() may not return any other error */\r
- ERROR("BUG: unexpected error returned by get_gdb_reg_list()");\r
- exit(-1);\r
- }\r
- }\r
-\r
- if (reg_list_size < reg_num)\r
- {\r
- ERROR("gdb requested a non-existing register");\r
- return ERROR_SERVER_REMOTE_CLOSED;\r
- }\r
-\r
- if (*separator != '=')\r
- {\r
- ERROR("GDB 'set register packet', but no '=' following the register number");\r
- return ERROR_SERVER_REMOTE_CLOSED;\r
- }\r
-\r
- /* convert from GDB-string (target-endian) to hex-string (big-endian) */\r
- hex_buf = malloc(CEIL(reg_list[reg_num]->size, 8) * 2);\r
- gdb_target_to_str(target, separator + 1, hex_buf);\r
-\r
- /* convert hex-string to binary buffer */\r
- bin_buf = malloc(CEIL(reg_list[reg_num]->size, 8));\r
- str_to_buf(hex_buf, CEIL(reg_list[reg_num]->size, 8) * 2, bin_buf, reg_list[reg_num]->size, 16);\r
-\r
- /* get register arch_type, and call set method */ \r
- arch_type = register_get_arch_type(reg_list[reg_num]->arch_type);\r
- if (arch_type == NULL)\r
- {\r
- ERROR("BUG: encountered unregistered arch type");\r
- exit(-1);\r
- }\r
- arch_type->set(reg_list[reg_num], bin_buf);\r
-\r
- gdb_put_packet(connection, "OK", 2);\r
-\r
- free(bin_buf);\r
- free(hex_buf);\r
- free(reg_list);\r
-\r
- return ERROR_OK;\r
-}\r
-\r
-int gdb_memory_packet_error(connection_t *connection, int retval)\r
-{\r
- switch (retval)\r
- {\r
- case ERROR_TARGET_NOT_HALTED:\r
- ERROR("gdb tried to read memory but we're not halted, dropping connection");\r
- return ERROR_SERVER_REMOTE_CLOSED;\r
- case ERROR_TARGET_DATA_ABORT:\r
- gdb_send_error(connection, EIO);\r
- break;\r
- case ERROR_TARGET_TRANSLATION_FAULT:\r
- gdb_send_error(connection, EFAULT);\r
- break;\r
- case ERROR_TARGET_UNALIGNED_ACCESS:\r
- gdb_send_error(connection, EFAULT);\r
- break;\r
- default:\r
- /* This could be that the target reset itself. */\r
- ERROR("unexpected error %i. Dropping connection.", retval);\r
- return ERROR_SERVER_REMOTE_CLOSED;\r
- }\r
-\r
- return ERROR_OK;\r
-}\r
-\r
-/* We don't have to worry about the default 2 second timeout for GDB packets,\r
- * because GDB breaks up large memory reads into smaller reads.\r
- * \r
- * 8191 bytes by the looks of it. Why 8191 bytes instead of 8192?????\r
- */\r
-int gdb_read_memory_packet(connection_t *connection, target_t *target, char *packet, int packet_size)\r
-{\r
- char *separator;\r
- u32 addr = 0;\r
- u32 len = 0;\r
-\r
- u8 *buffer;\r
- char *hex_buffer;\r
-\r
- int retval = ERROR_OK;\r
-\r
- /* skip command character */\r
- packet++;\r
-\r
- addr = strtoul(packet, &separator, 16);\r
-\r
- if (*separator != ',')\r
- {\r
- ERROR("incomplete read memory packet received, dropping connection");\r
- return ERROR_SERVER_REMOTE_CLOSED;\r
- }\r
-\r
- len = strtoul(separator+1, NULL, 16);\r
-\r
- buffer = malloc(len);\r
-\r
- DEBUG("addr: 0x%8.8x, len: 0x%8.8x", addr, len);\r
-\r
- retval = target_read_buffer(target, addr, len, buffer);\r
-\r
- if ((retval == ERROR_TARGET_DATA_ABORT) && (!gdb_report_data_abort))\r
- {\r
- /* TODO : Here we have to lie and send back all zero's lest stack traces won't work.\r
- * At some point this might be fixed in GDB, in which case this code can be removed.\r
- * \r
- * OpenOCD developers are acutely aware of this problem, but there is nothing\r
- * gained by involving the user in this problem that hopefully will get resolved\r
- * eventually\r
- * \r
- * http://sourceware.org/cgi-bin/gnatsweb.pl?cmd=view%20audit-trail&database=gdb&pr=2395\r
- *\r
- * For now, the default is to fix up things to make current GDB versions work.\r
- * This can be overwritten using the gdb_report_data_abort <'enable'|'disable'> command.\r
- */\r
- memset(buffer, 0, len);\r
- retval = ERROR_OK;\r
- }\r
-\r
- if (retval == ERROR_OK)\r
- {\r
- hex_buffer = malloc(len * 2 + 1);\r
-\r
- int i;\r
- for (i = 0; i < len; i++)\r
- {\r
- u8 t = buffer[i];\r
- hex_buffer[2 * i] = DIGITS[(t >> 4) & 0xf];\r
- hex_buffer[2 * i + 1] = DIGITS[t & 0xf];\r
- }\r
-\r
- gdb_put_packet(connection, hex_buffer, len * 2);\r
-\r
- free(hex_buffer);\r
- }\r
- else\r
- {\r
- retval = gdb_memory_packet_error(connection, retval);\r
- }\r
-\r
- free(buffer);\r
-\r
- return retval;\r
-}\r
-\r
-int gdb_write_memory_packet(connection_t *connection, target_t *target, char *packet, int packet_size)\r
-{\r
- char *separator;\r
- u32 addr = 0;\r
- u32 len = 0;\r
-\r
- u8 *buffer;\r
-\r
- int i;\r
- int retval;\r
-\r
- /* skip command character */\r
- packet++;\r
-\r
- addr = strtoul(packet, &separator, 16);\r
-\r
- if (*separator != ',')\r
- {\r
- ERROR("incomplete write memory packet received, dropping connection");\r
- return ERROR_SERVER_REMOTE_CLOSED;\r
- }\r
-\r
- len = strtoul(separator+1, &separator, 16);\r
-\r
- if (*(separator++) != ':')\r
- {\r
- ERROR("incomplete write memory packet received, dropping connection");\r
- return ERROR_SERVER_REMOTE_CLOSED;\r
- }\r
-\r
- buffer = malloc(len);\r
-\r
- DEBUG("addr: 0x%8.8x, len: 0x%8.8x", addr, len);\r
-\r
- for (i=0; i<len; i++)\r
- {\r
- u32 tmp;\r
- sscanf(separator + 2*i, "%2x", &tmp);\r
- buffer[i] = tmp;\r
- }\r
-\r
- retval = target_write_buffer(target, addr, len, buffer);\r
-\r
- if (retval == ERROR_OK)\r
- {\r
- gdb_put_packet(connection, "OK", 2);\r
- }\r
- else\r
- {\r
- if ((retval = gdb_memory_packet_error(connection, retval)) != ERROR_OK)\r
- return retval; \r
- }\r
-\r
- free(buffer);\r
-\r
- return ERROR_OK;\r
-}\r
-\r
-int gdb_write_memory_binary_packet(connection_t *connection, target_t *target, char *packet, int packet_size)\r
-{\r
- char *separator;\r
- u32 addr = 0;\r
- u32 len = 0;\r
-\r
- int retval;\r
-\r
- /* skip command character */\r
- packet++;\r
-\r
- addr = strtoul(packet, &separator, 16);\r
-\r
- if (*separator != ',')\r
- {\r
- ERROR("incomplete write memory binary packet received, dropping connection");\r
- return ERROR_SERVER_REMOTE_CLOSED;\r
- }\r
-\r
- len = strtoul(separator+1, &separator, 16);\r
-\r
- if (*(separator++) != ':')\r
- {\r
- ERROR("incomplete write memory binary packet received, dropping connection");\r
- return ERROR_SERVER_REMOTE_CLOSED;\r
- }\r
-\r
- retval = ERROR_OK;\r
- if (len)\r
- {\r
- DEBUG("addr: 0x%8.8x, len: 0x%8.8x", addr, len);\r
-\r
- retval = target_write_buffer(target, addr, len, (u8*)separator);\r
- }\r
-\r
- if (retval == ERROR_OK)\r
- {\r
- gdb_put_packet(connection, "OK", 2);\r
- }\r
- else\r
- {\r
- if ((retval = gdb_memory_packet_error(connection, retval)) != ERROR_OK)\r
- return retval; \r
- }\r
-\r
- return ERROR_OK;\r
-}\r
-\r
-void gdb_step_continue_packet(connection_t *connection, target_t *target, char *packet, int packet_size)\r
-{\r
- int current = 0;\r
- u32 address = 0x0;\r
-\r
- DEBUG("-");\r
-\r
- if (packet_size > 1)\r
- {\r
- packet[packet_size] = 0;\r
- address = strtoul(packet + 1, NULL, 16);\r
- }\r
- else\r
- {\r
- current = 1;\r
- }\r
-\r
- if (packet[0] == 'c')\r
- {\r
- DEBUG("continue");\r
- target->type->resume(target, current, address, 0, 0); /* resume at current address, don't handle breakpoints, not debugging */\r
- }\r
- else if (packet[0] == 's')\r
- {\r
- DEBUG("step");\r
- target->type->step(target, current, address, 0); /* step at current or address, don't handle breakpoints */\r
- }\r
-}\r
-\r
-int gdb_bp_wp_packet_error(connection_t *connection, int retval)\r
-{\r
- switch (retval)\r
- {\r
- case ERROR_TARGET_NOT_HALTED:\r
- ERROR("gdb tried to set a breakpoint but we're not halted, dropping connection");\r
- return ERROR_SERVER_REMOTE_CLOSED;\r
- break;\r
- case ERROR_TARGET_RESOURCE_NOT_AVAILABLE:\r
- gdb_send_error(connection, EBUSY);\r
- break;\r
- default:\r
- ERROR("BUG: unexpected error %i", retval);\r
- exit(-1);\r
- }\r
-\r
- return ERROR_OK;\r
-}\r
-\r
-int gdb_breakpoint_watchpoint_packet(connection_t *connection, target_t *target, char *packet, int packet_size)\r
-{\r
- int type;\r
- enum breakpoint_type bp_type = BKPT_SOFT /* dummy init to avoid warning */;\r
- enum watchpoint_rw wp_type;\r
- u32 address;\r
- u32 size;\r
- char *separator;\r
- int retval;\r
-\r
- DEBUG("-");\r
-\r
- type = strtoul(packet + 1, &separator, 16);\r
-\r
- if (type == 0) /* memory breakpoint */\r
- bp_type = BKPT_SOFT;\r
- else if (type == 1) /* hardware breakpoint */\r
- bp_type = BKPT_HARD;\r
- else if (type == 2) /* write watchpoint */\r
- wp_type = WPT_WRITE;\r
- else if (type == 3) /* read watchpoint */\r
- wp_type = WPT_READ;\r
- else if (type == 4) /* access watchpoint */\r
- wp_type = WPT_ACCESS;\r
-\r
- if (*separator != ',')\r
- {\r
- ERROR("incomplete breakpoint/watchpoint packet received, dropping connection");\r
- return ERROR_SERVER_REMOTE_CLOSED;\r
- }\r
-\r
- address = strtoul(separator+1, &separator, 16);\r
-\r
- if (*separator != ',')\r
- {\r
- ERROR("incomplete breakpoint/watchpoint packet received, dropping connection");\r
- return ERROR_SERVER_REMOTE_CLOSED;\r
- }\r
-\r
- size = strtoul(separator+1, &separator, 16);\r
-\r
- switch (type)\r
- {\r
- case 0:\r
- case 1:\r
- if (packet[0] == 'Z')\r
- {\r
- if ((retval = breakpoint_add(target, address, size, bp_type)) != ERROR_OK)\r
- {\r
- if ((retval = gdb_bp_wp_packet_error(connection, retval)) != ERROR_OK)\r
- return retval;\r
- }\r
- else\r
- {\r
- gdb_put_packet(connection, "OK", 2);\r
- }\r
- }\r
- else\r
- {\r
- breakpoint_remove(target, address);\r
- gdb_put_packet(connection, "OK", 2);\r
- }\r
- break;\r
- case 2:\r
- case 3:\r
- case 4:\r
- {\r
- if (packet[0] == 'Z')\r
- {\r
- if ((retval = watchpoint_add(target, address, size, type-2, 0, 0xffffffffu)) != ERROR_OK)\r
- {\r
- if ((retval = gdb_bp_wp_packet_error(connection, retval)) != ERROR_OK)\r
- return retval;\r
- }\r
- else\r
- {\r
- gdb_put_packet(connection, "OK", 2);\r
- }\r
- }\r
- else\r
- {\r
- watchpoint_remove(target, address);\r
- gdb_put_packet(connection, "OK", 2);\r
- }\r
- break;\r
- }\r
- default:\r
- break;\r
- }\r
-\r
- return ERROR_OK;\r
-}\r
-\r
-/* print out a string and allocate more space as needed, mainly used for XML at this point */\r
-void xml_printf(int *retval, char **xml, int *pos, int *size, const char *fmt, ...)\r
-{\r
- if (*retval != ERROR_OK)\r
- {\r
- return;\r
- }\r
- int first = 1;\r
- \r
- for (;;)\r
- {\r
- if ((*xml == NULL) || (!first))\r
- {\r
- /* start by 0 to exercise all the code paths.\r
- * Need minimum 2 bytes to fit 1 char and 0 terminator. */\r
- \r
- *size = *size * 2 + 2;\r
- char *t = *xml;\r
- *xml = realloc(*xml, *size);\r
- if (*xml == NULL)\r
- {\r
- if (t)\r
- free(t);\r
- *retval = ERROR_SERVER_REMOTE_CLOSED;\r
- return;\r
- }\r
- }\r
- \r
- va_list ap;\r
- int ret;\r
- va_start(ap, fmt);\r
- ret = vsnprintf(*xml + *pos, *size - *pos, fmt, ap);\r
- va_end(ap);\r
- if ((ret > 0) && ((ret + 1) < *size - *pos))\r
- {\r
- *pos += ret;\r
- return;\r
- }\r
- /* there was just enough or not enough space, allocate more. */\r
- first = 0;\r
- }\r
-}\r
-\r
-static int decode_xfer_read(char *buf, char **annex, int *ofs, unsigned int *len)\r
-{\r
- char *separator;\r
- \r
- /* Extract and NUL-terminate the annex. */\r
- *annex = buf;\r
- while (*buf && *buf != ':')\r
- buf++;\r
- if (*buf == '\0')\r
- return -1;\r
- *buf++ = 0;\r
- \r
- /* After the read marker and annex, qXfer looks like a\r
- * traditional 'm' packet. */\r
- \r
- *ofs = strtoul(buf, &separator, 16);\r
-\r
- if (*separator != ',')\r
- return -1;\r
-\r
- *len = strtoul(separator+1, NULL, 16);\r
- \r
- return 0;\r
-}\r
-\r
-int gdb_calc_blocksize(flash_bank_t *bank)\r
-{\r
- int i;\r
- int block_size = 0xffffffff;\r
- \r
- /* loop through all sectors and return smallest sector size */\r
- \r
- for (i = 0; i < bank->num_sectors; i++)\r
- {\r
- if (bank->sectors[i].size < block_size)\r
- block_size = bank->sectors[i].size;\r
- }\r
- \r
- return block_size;\r
-}\r
-\r
-int gdb_query_packet(connection_t *connection, target_t *target, char *packet, int packet_size)\r
-{\r
- command_context_t *cmd_ctx = connection->cmd_ctx;\r
- \r
- if (strstr(packet, "qRcmd,"))\r
- {\r
- if (packet_size > 6)\r
- {\r
- char *cmd;\r
- int i;\r
- cmd = malloc((packet_size - 6)/2 + 1);\r
- for (i=0; i < (packet_size - 6)/2; i++)\r
- {\r
- u32 tmp;\r
- sscanf(packet + 6 + 2*i, "%2x", &tmp);\r
- cmd[i] = tmp;\r
- }\r
- cmd[(packet_size - 6)/2] = 0x0;\r
- \r
- /* We want to print all debug output to GDB connection */\r
- log_setCallback(gdb_log_callback, connection);\r
- target_call_timer_callbacks();\r
- command_run_line(cmd_ctx, cmd);\r
- free(cmd);\r
- }\r
- gdb_put_packet(connection, "OK", 2);\r
- return ERROR_OK;\r
- }\r
- else if (strstr(packet, "qCRC:"))\r
- {\r
- if (packet_size > 5)\r
- {\r
- int retval;\r
- char gdb_reply[10];\r
- char *separator;\r
- u32 checksum;\r
- u32 addr = 0;\r
- u32 len = 0;\r
- \r
- /* skip command character */\r
- packet += 5;\r
- \r
- addr = strtoul(packet, &separator, 16);\r
- \r
- if (*separator != ',')\r
- {\r
- ERROR("incomplete read memory packet received, dropping connection");\r
- return ERROR_SERVER_REMOTE_CLOSED;\r
- }\r
- \r
- len = strtoul(separator + 1, NULL, 16);\r
- \r
- retval = target_checksum_memory(target, addr, len, &checksum);\r
- \r
- if (retval == ERROR_OK)\r
- {\r
- snprintf(gdb_reply, 10, "C%8.8x", checksum);\r
- gdb_put_packet(connection, gdb_reply, 9);\r
- }\r
- else\r
- {\r
- if ((retval = gdb_memory_packet_error(connection, retval)) != ERROR_OK)\r
- return retval; \r
- }\r
- \r
- return ERROR_OK;\r
- }\r
- }\r
- else if (strstr(packet, "qSupported"))\r
- {\r
- /* we currently support packet size and qXfer:memory-map:read (if enabled)\r
- * disable qXfer:features:read for the moment */\r
- int retval = ERROR_OK;\r
- char *buffer = NULL;\r
- int pos = 0;\r
- int size = 0;\r
-\r
- xml_printf(&retval, &buffer, &pos, &size, \r
- "PacketSize=%x;qXfer:memory-map:read%c;qXfer:features:read-",\r
- (GDB_BUFFER_SIZE - 1), gdb_use_memory_map == 1 ? '+' : '-');\r
- \r
- if (retval != ERROR_OK)\r
- {\r
- gdb_send_error(connection, 01);\r
- return ERROR_OK;\r
- }\r
- \r
- gdb_put_packet(connection, buffer, strlen(buffer));\r
- free(buffer);\r
- \r
- return ERROR_OK;\r
- }\r
- else if (strstr(packet, "qXfer:memory-map:read::"))\r
- {\r
- /* We get away with only specifying flash here. Regions that are not\r
- * specified are treated as if we provided no memory map(if not we \r
- * could detect the holes and mark them as RAM).\r
- * Normally we only execute this code once, but no big deal if we\r
- * have to regenerate it a couple of times. */\r
- \r
- flash_bank_t *p;\r
- char *xml = NULL;\r
- int size = 0;\r
- int pos = 0;\r
- int retval = ERROR_OK;\r
- \r
- int offset;\r
- int length;\r
- char *separator;\r
- int blocksize;\r
- \r
- /* skip command character */\r
- packet += 23;\r
- \r
- offset = strtoul(packet, &separator, 16);\r
- length = strtoul(separator + 1, &separator, 16);\r
- \r
- xml_printf(&retval, &xml, &pos, &size, "<memory-map>\n");\r
- \r
- int i = 0;\r
- for (;;)\r
- {\r
- p = get_flash_bank_by_num(i);\r
- if (p == NULL)\r
- break;\r
- \r
- /* if device has uneven sector sizes, eg. str7, lpc\r
- * we pass the smallest sector size to gdb memory map */\r
- blocksize = gdb_calc_blocksize(p);\r
- \r
- xml_printf(&retval, &xml, &pos, &size, "<memory type=\"flash\" start=\"0x%x\" length=\"0x%x\">\n" \\r
- "<property name=\"blocksize\">0x%x</property>\n" \\r
- "</memory>\n", \\r
- p->base, p->size, blocksize);\r
- i++;\r
- }\r
- \r
- xml_printf(&retval, &xml, &pos, &size, "</memory-map>\n");\r
-\r
- if (retval != ERROR_OK)\r
- {\r
- gdb_send_error(connection, retval);\r
- return retval;\r
- }\r
- \r
- if (offset + length > pos)\r
- {\r
- length = pos - offset;\r
- }\r
-\r
- char *t = malloc(length + 1);\r
- t[0] = 'l';\r
- memcpy(t + 1, xml + offset, length);\r
- gdb_put_packet(connection, t, length + 1);\r
- \r
- free(t);\r
- free(xml);\r
- return ERROR_OK;\r
- }\r
- else if (strstr(packet, "qXfer:features:read:"))\r
- { \r
- char *xml = NULL;\r
- int size = 0;\r
- int pos = 0;\r
- int retval = ERROR_OK;\r
- \r
- int offset;\r
- unsigned int length;\r
- char *annex;\r
- \r
- /* skip command character */\r
- packet += 20;\r
- \r
- if (decode_xfer_read(packet, &annex, &offset, &length) < 0)\r
- {\r
- gdb_send_error(connection, 01);\r
- return ERROR_OK;\r
- }\r
- \r
- if (strcmp(annex, "target.xml") != 0)\r
- {\r
- gdb_send_error(connection, 01);\r
- return ERROR_OK;\r
- }\r
- \r
- xml_printf(&retval, &xml, &pos, &size, \\r
- "l<target version=\"1.0\">\n<architecture>arm</architecture>\n</target>\n");\r
- \r
- if (retval != ERROR_OK)\r
- {\r
- gdb_send_error(connection, retval);\r
- return retval;\r
- }\r
- \r
- gdb_put_packet(connection, xml, strlen(xml) + 1);\r
- \r
- free(xml);\r
- return ERROR_OK;\r
- }\r
- \r
- gdb_put_packet(connection, "", 0);\r
- return ERROR_OK;\r
-}\r
-\r
-int gdb_v_packet(connection_t *connection, target_t *target, char *packet, int packet_size)\r
-{\r
- gdb_connection_t *gdb_connection = connection->priv;\r
- gdb_service_t *gdb_service = connection->service->priv;\r
- int result;\r
-\r
- /* if flash programming disabled - send a empty reply */\r
- \r
- if (gdb_flash_program == 0)\r
- {\r
- gdb_put_packet(connection, "", 0);\r
- return ERROR_OK;\r
- }\r
- \r
- if (strstr(packet, "vFlashErase:"))\r
- {\r
- unsigned long addr;\r
- unsigned long length;\r
- \r
- char *parse = packet + 12;\r
- if (*parse == '\0')\r
- {\r
- ERROR("incomplete vFlashErase packet received, dropping connection");\r
- return ERROR_SERVER_REMOTE_CLOSED;\r
- }\r
-\r
- addr = strtoul(parse, &parse, 16);\r
-\r
- if (*(parse++) != ',' || *parse == '\0')\r
- {\r
- ERROR("incomplete vFlashErase packet received, dropping connection");\r
- return ERROR_SERVER_REMOTE_CLOSED;\r
- }\r
-\r
- length = strtoul(parse, &parse, 16);\r
-\r
- if (*parse != '\0')\r
- {\r
- ERROR("incomplete vFlashErase packet received, dropping connection");\r
- return ERROR_SERVER_REMOTE_CLOSED;\r
- }\r
- \r
- /* assume all sectors need erasing - stops any problems\r
- * when flash_write is called multiple times */\r
- flash_set_dirty();\r
- \r
- /* perform any target specific operations before the erase */\r
- target_call_event_callbacks(gdb_service->target, TARGET_EVENT_GDB_PROGRAM);\r
- \r
- /* perform erase */\r
- if ((result = flash_erase_address_range(gdb_service->target, addr, length)) != ERROR_OK)\r
- {\r
- /* GDB doesn't evaluate the actual error number returned,\r
- * treat a failed erase as an I/O error\r
- */\r
- gdb_send_error(connection, EIO);\r
- ERROR("flash_erase returned %i", result);\r
- }\r
- else\r
- gdb_put_packet(connection, "OK", 2);\r
- \r
- return ERROR_OK;\r
- }\r
-\r
- if (strstr(packet, "vFlashWrite:"))\r
- {\r
- unsigned long addr;\r
- unsigned long length;\r
- char *parse = packet + 12;\r
-\r
- if (*parse == '\0')\r
- {\r
- ERROR("incomplete vFlashErase packet received, dropping connection");\r
- return ERROR_SERVER_REMOTE_CLOSED;\r
- }\r
- addr = strtoul(parse, &parse, 16);\r
- if (*(parse++) != ':')\r
- {\r
- ERROR("incomplete vFlashErase packet received, dropping connection");\r
- return ERROR_SERVER_REMOTE_CLOSED;\r
- }\r
- length = packet_size - (parse - packet);\r
- \r
- /* create a new image if there isn't already one */\r
- if (gdb_connection->vflash_image == NULL)\r
- {\r
- gdb_connection->vflash_image = malloc(sizeof(image_t));\r
- image_open(gdb_connection->vflash_image, "", "build");\r
- }\r
-\r
- /* create new section with content from packet buffer */\r
- image_add_section(gdb_connection->vflash_image, addr, length, 0x0, (u8*)parse);\r
-\r
- gdb_put_packet(connection, "OK", 2);\r
-\r
- return ERROR_OK;\r
- }\r
-\r
- if (!strcmp(packet, "vFlashDone"))\r
- {\r
- u32 written;\r
-\r
- /* process the flashing buffer. No need to erase as GDB\r
- * always issues a vFlashErase first. */\r
- if ((result = flash_write(gdb_service->target, gdb_connection->vflash_image, &written, 0)) != ERROR_OK)\r
- {\r
- if (result == ERROR_FLASH_DST_OUT_OF_BANK)\r
- gdb_put_packet(connection, "E.memtype", 9);\r
- else\r
- gdb_send_error(connection, EIO);\r
- }\r
- else\r
- {\r
- DEBUG("wrote %u bytes from vFlash image to flash", written);\r
- gdb_put_packet(connection, "OK", 2);\r
- }\r
- \r
- image_close(gdb_connection->vflash_image);\r
- free(gdb_connection->vflash_image);\r
- gdb_connection->vflash_image = NULL;\r
- \r
- return ERROR_OK;\r
- }\r
-\r
- gdb_put_packet(connection, "", 0);\r
- return ERROR_OK;\r
-}\r
-\r
-int gdb_detach(connection_t *connection, target_t *target)\r
-{\r
- switch( detach_mode )\r
- {\r
- case GDB_DETACH_RESUME:\r
- target->type->resume(target, 1, 0, 1, 0);\r
- break;\r
- \r
- case GDB_DETACH_RESET:\r
- target_process_reset(connection->cmd_ctx);\r
- break;\r
- \r
- case GDB_DETACH_HALT:\r
- target->type->halt(target);\r
- break;\r
- \r
- case GDB_DETACH_NOTHING:\r
- break;\r
- }\r
- \r
- gdb_put_packet(connection, "OK", 2);\r
- \r
- return ERROR_OK;\r
-}\r
-\r
-static void gdb_log_callback(void *priv, const char *file, int line, \r
- const char *function, const char *format, va_list args)\r
-{\r
- connection_t *connection = priv;\r
- gdb_connection_t *gdb_con = connection->priv;\r
- \r
- if (gdb_con->busy)\r
- {\r
- /* do not reply this using the O packet */\r
- return;\r
- }\r
-\r
- char *t = allocPrintf(format, args);\r
- if (t == NULL)\r
- return;\r
- \r
- gdb_output_con(connection, t); \r
- \r
- free(t);\r
-}\r
-\r
-int gdb_input_inner(connection_t *connection)\r
-{\r
- gdb_service_t *gdb_service = connection->service->priv;\r
- target_t *target = gdb_service->target;\r
- char packet[GDB_BUFFER_SIZE];\r
- int packet_size;\r
- int retval;\r
- gdb_connection_t *gdb_con = connection->priv;\r
- static int extended_protocol = 0;\r
-\r
- /* drain input buffer */\r
- do\r
- {\r
- packet_size = GDB_BUFFER_SIZE-1;\r
- if ((retval = gdb_get_packet(connection, packet, &packet_size)) != ERROR_OK)\r
- {\r
- return retval;\r
- }\r
-\r
- /* terminate with zero */\r
- packet[packet_size] = 0;\r
-\r
- DEBUG("received packet: '%s'", packet);\r
-\r
- if (packet_size > 0)\r
- {\r
- retval = ERROR_OK;\r
- switch (packet[0])\r
- {\r
- case 'H':\r
- /* Hct... -- set thread \r
- * we don't have threads, send empty reply */\r
- gdb_put_packet(connection, NULL, 0);\r
- break;\r
- case 'q':\r
- retval = gdb_query_packet(connection, target, packet, packet_size);\r
- break;\r
- case 'g':\r
- retval = gdb_get_registers_packet(connection, target, packet, packet_size);\r
- break;\r
- case 'G':\r
- retval = gdb_set_registers_packet(connection, target, packet, packet_size);\r
- break;\r
- case 'p':\r
- retval = gdb_get_register_packet(connection, target, packet, packet_size);\r
- break;\r
- case 'P':\r
- retval = gdb_set_register_packet(connection, target, packet, packet_size);\r
- break;\r
- case 'm':\r
- retval = gdb_read_memory_packet(connection, target, packet, packet_size);\r
- break;\r
- case 'M':\r
- retval = gdb_write_memory_packet(connection, target, packet, packet_size);\r
- break;\r
- case 'z':\r
- case 'Z':\r
- retval = gdb_breakpoint_watchpoint_packet(connection, target, packet, packet_size);\r
- break;\r
- case '?':\r
- gdb_last_signal_packet(connection, target, packet, packet_size);\r
- break;\r
- case 'c':\r
- case 's':\r
- {\r
- /* We're running/stepping, in which case we can \r
- * forward log output until the target is halted */\r
- gdb_connection_t *gdb_con = connection->priv;\r
- gdb_con->frontend_state = TARGET_RUNNING;\r
- log_setCallback(gdb_log_callback, connection);\r
- gdb_step_continue_packet(connection, target, packet, packet_size);\r
- }\r
- break;\r
- case 'v':\r
- retval = gdb_v_packet(connection, target, packet, packet_size);\r
- break;\r
- case 'D':\r
- retval = gdb_detach(connection, target);\r
- extended_protocol = 0;\r
- break;\r
- case 'X':\r
- if ((retval = gdb_write_memory_binary_packet(connection, target, packet, packet_size)) != ERROR_OK)\r
- return retval;\r
- break;\r
- case 'k':\r
- if (extended_protocol != 0)\r
- break;\r
- gdb_put_packet(connection, "OK", 2);\r
- return ERROR_SERVER_REMOTE_CLOSED;\r
- case '!':\r
- /* handle extended remote protocol */\r
- extended_protocol = 1;\r
- gdb_put_packet(connection, "OK", 2);\r
- break;\r
- case 'R':\r
- /* handle extended restart packet */\r
- target_process_reset(connection->cmd_ctx);\r
- break;\r
- default:\r
- /* ignore unkown packets */\r
- DEBUG("ignoring 0x%2.2x packet", packet[0]);\r
- gdb_put_packet(connection, NULL, 0);\r
- break;\r
- }\r
-\r
- /* if a packet handler returned an error, exit input loop */\r
- if (retval != ERROR_OK)\r
- return retval;\r
- }\r
-\r
- if (gdb_con->ctrl_c)\r
- {\r
- if (target->state == TARGET_RUNNING)\r
- {\r
- target->type->halt(target);\r
- gdb_con->ctrl_c = 0;\r
- }\r
- }\r
-\r
- } while (gdb_con->buf_cnt > 0);\r
-\r
- return ERROR_OK;\r
-}\r
-\r
-int gdb_input(connection_t *connection)\r
-{\r
- int retval = gdb_input_inner(connection);\r
- if (retval == ERROR_SERVER_REMOTE_CLOSED)\r
- return retval;\r
- /* we'll recover from any other errors(e.g. temporary timeouts, etc.) */\r
- return ERROR_OK;\r
-}\r
-\r
-int gdb_init()\r
-{\r
- gdb_service_t *gdb_service;\r
- target_t *target = targets;\r
- int i = 0;\r
-\r
- if (!target)\r
- {\r
- WARNING("no gdb ports allocated as no target has been specified");\r
- return ERROR_OK;\r
- }\r
-\r
- if (gdb_port == 0)\r
- {\r
- WARNING("no gdb port specified, using default port 3333");\r
- gdb_port = 3333;\r
- }\r
-\r
- while (target)\r
- {\r
- char service_name[8];\r
-\r
- snprintf(service_name, 8, "gdb-%2.2i", i);\r
-\r
- gdb_service = malloc(sizeof(gdb_service_t));\r
- gdb_service->target = target;\r
-\r
- add_service("gdb", CONNECTION_GDB, gdb_port + i, 1, gdb_new_connection, gdb_input, gdb_connection_closed, gdb_service);\r
-\r
- DEBUG("gdb service for target %s at port %i", target->type->name, gdb_port + i);\r
-\r
- i++;\r
- target = target->next;\r
- }\r
-\r
- return ERROR_OK;\r
-}\r
-\r
-/* daemon configuration command gdb_port */\r
-int handle_gdb_port_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- if (argc == 0)\r
- return ERROR_OK;\r
-\r
- /* only if the port wasn't overwritten by cmdline */\r
- if (gdb_port == 0)\r
- gdb_port = strtoul(args[0], NULL, 0);\r
-\r
- return ERROR_OK;\r
-}\r
-\r
-int handle_gdb_detach_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- if (argc == 1)\r
- {\r
- if (strcmp(args[0], "resume") == 0)\r
- {\r
- detach_mode = GDB_DETACH_RESUME;\r
- return ERROR_OK;\r
- }\r
- else if (strcmp(args[0], "reset") == 0)\r
- {\r
- detach_mode = GDB_DETACH_RESET;\r
- return ERROR_OK;\r
- }\r
- else if (strcmp(args[0], "halt") == 0)\r
- {\r
- detach_mode = GDB_DETACH_HALT;\r
- return ERROR_OK;\r
- }\r
- else if (strcmp(args[0], "nothing") == 0)\r
- {\r
- detach_mode = GDB_DETACH_NOTHING;\r
- return ERROR_OK;\r
- }\r
- }\r
- \r
- WARNING("invalid gdb_detach configuration directive: %s", args[0]);\r
- return ERROR_OK;\r
-}\r
-\r
-int handle_gdb_memory_map_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- if (argc == 1)\r
- {\r
- if (strcmp(args[0], "enable") == 0)\r
- {\r
- gdb_use_memory_map = 1;\r
- return ERROR_OK;\r
- }\r
- else if (strcmp(args[0], "disable") == 0)\r
- {\r
- gdb_use_memory_map = 0;\r
- return ERROR_OK;\r
- }\r
- }\r
- \r
- WARNING("invalid gdb_memory_map configuration directive: %s", args[0]);\r
- return ERROR_OK;\r
-}\r
-\r
-int handle_gdb_flash_program_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- if (argc == 1)\r
- {\r
- if (strcmp(args[0], "enable") == 0)\r
- {\r
- gdb_flash_program = 1;\r
- return ERROR_OK;\r
- }\r
- else if (strcmp(args[0], "disable") == 0)\r
- {\r
- gdb_flash_program = 0;\r
- return ERROR_OK;\r
- }\r
- }\r
- \r
- WARNING("invalid gdb_memory_map configuration directive: %s", args[0]);\r
- return ERROR_OK;\r
-}\r
-\r
-int handle_gdb_report_data_abort_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
-{\r
- if (argc == 1)\r
- {\r
- if (strcmp(args[0], "enable") == 0)\r
- {\r
- gdb_report_data_abort = 1;\r
- return ERROR_OK;\r
- }\r
- else if (strcmp(args[0], "disable") == 0)\r
- {\r
- gdb_report_data_abort = 0;\r
- return ERROR_OK;\r
- }\r
- }\r
- \r
- WARNING("invalid gdb_report_data_abort configuration directive: %s", args[0]);\r
- return ERROR_OK;\r
-}\r
-\r
-int gdb_register_commands(command_context_t *command_context)\r
-{\r
- register_command(command_context, NULL, "gdb_port", handle_gdb_port_command,\r
- COMMAND_CONFIG, "");\r
- register_command(command_context, NULL, "gdb_detach", handle_gdb_detach_command,\r
- COMMAND_CONFIG, "");\r
- register_command(command_context, NULL, "gdb_memory_map", handle_gdb_memory_map_command,\r
- COMMAND_CONFIG, "");\r
- register_command(command_context, NULL, "gdb_flash_program", handle_gdb_flash_program_command,\r
- COMMAND_CONFIG, "");\r
- register_command(command_context, NULL, "gdb_report_data_abort", handle_gdb_report_data_abort_command,\r
- COMMAND_CONFIG, "");\r
- return ERROR_OK;\r
-}\r
+/***************************************************************************
+ * Copyright (C) 2005 by Dominic Rath *
+ * Dominic.Rath@gmx.de *
+ * *
+ * 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 "replacements.h"
+
+#include "gdb_server.h"
+
+#include "server.h"
+#include "log.h"
+#include "binarybuffer.h"
+#include "jtag.h"
+#include "breakpoints.h"
+#include "flash.h"
+#include "target_request.h"
+#include "configuration.h"
+
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#if 0
+#define _DEBUG_GDB_IO_
+#endif
+
+static unsigned short gdb_port;
+static const char *DIGITS = "0123456789abcdef";
+
+static void gdb_log_callback(void *priv, const char *file, int line,
+ const char *function, const char *format, va_list args);
+
+enum gdb_detach_mode
+{
+ GDB_DETACH_RESUME,
+ GDB_DETACH_RESET,
+ GDB_DETACH_HALT,
+ GDB_DETACH_NOTHING
+};
+
+/* target behaviour on gdb detach */
+enum gdb_detach_mode detach_mode = GDB_DETACH_RESUME;
+
+/* set if we are sending a memory map to gdb
+ * via qXfer:memory-map:read packet */
+int gdb_use_memory_map = 0;
+int gdb_flash_program = 0;
+
+/* if set, data aborts cause an error to be reported in memory read packets
+ * see the code in gdb_read_memory_packet() for further explanations */
+int gdb_report_data_abort = 0;
+
+int gdb_last_signal(target_t *target)
+{
+ switch (target->debug_reason)
+ {
+ case DBG_REASON_DBGRQ:
+ return 0x2; /* SIGINT */
+ case DBG_REASON_BREAKPOINT:
+ case DBG_REASON_WATCHPOINT:
+ case DBG_REASON_WPTANDBKPT:
+ return 0x05; /* SIGTRAP */
+ case DBG_REASON_SINGLESTEP:
+ return 0x05; /* SIGTRAP */
+ case DBG_REASON_NOTHALTED:
+ return 0x0; /* no signal... shouldn't happen */
+ default:
+ ERROR("BUG: undefined debug reason");
+ exit(-1);
+ }
+}
+
+int gdb_get_char(connection_t *connection, int* next_char)
+{
+ gdb_connection_t *gdb_con = connection->priv;
+
+#ifdef _DEBUG_GDB_IO_
+ char *debug_buffer;
+#endif
+
+ if (gdb_con->buf_cnt-- > 0)
+ {
+ *next_char = *(gdb_con->buf_p++);
+ if (gdb_con->buf_cnt > 0)
+ connection->input_pending = 1;
+ else
+ connection->input_pending = 0;
+
+#ifdef _DEBUG_GDB_IO_
+ DEBUG("returned char '%c' (0x%2.2x)", *next_char, *next_char);
+#endif
+
+ return ERROR_OK;
+ }
+
+ for (;;)
+ {
+#ifndef _WIN32
+ /* a non-blocking socket will block if there is 0 bytes available on the socket,
+ * but return with as many bytes as are available immediately
+ */
+ struct timeval tv;
+ fd_set read_fds;
+
+ FD_ZERO(&read_fds);
+ FD_SET(connection->fd, &read_fds);
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ if (select(connection->fd + 1, &read_fds, NULL, NULL, &tv) == 0)
+ {
+ /* This can typically be because a "monitor" command took too long
+ * before printing any progress messages
+ */
+ return ERROR_GDB_TIMEOUT;
+ }
+#endif
+ gdb_con->buf_cnt = read_socket(connection->fd, gdb_con->buffer, GDB_BUFFER_SIZE);
+ if (gdb_con->buf_cnt > 0)
+ {
+ break;
+ }
+ if (gdb_con->buf_cnt == 0)
+ {
+ gdb_con->closed = 1;
+ return ERROR_SERVER_REMOTE_CLOSED;
+ }
+
+#ifdef _WIN32
+ errno = WSAGetLastError();
+
+ switch(errno)
+ {
+ case WSAEWOULDBLOCK:
+ usleep(1000);
+ break;
+ case WSAECONNABORTED:
+ return ERROR_SERVER_REMOTE_CLOSED;
+ case WSAECONNRESET:
+ return ERROR_SERVER_REMOTE_CLOSED;
+ default:
+ ERROR("read: %d", errno);
+ exit(-1);
+ }
+#else
+ switch(errno)
+ {
+ case EAGAIN:
+ usleep(1000);
+ break;
+ case ECONNABORTED:
+ return ERROR_SERVER_REMOTE_CLOSED;
+ case ECONNRESET:
+ return ERROR_SERVER_REMOTE_CLOSED;
+ default:
+ ERROR("read: %s", strerror(errno));
+ return ERROR_SERVER_REMOTE_CLOSED;
+ }
+#endif
+ }
+
+#ifdef _DEBUG_GDB_IO_
+ debug_buffer = malloc(gdb_con->buf_cnt + 1);
+ memcpy(debug_buffer, gdb_con->buffer, gdb_con->buf_cnt);
+ debug_buffer[gdb_con->buf_cnt] = 0;
+ DEBUG("received '%s'", debug_buffer);
+ free(debug_buffer);
+#endif
+
+ gdb_con->buf_p = gdb_con->buffer;
+ gdb_con->buf_cnt--;
+ *next_char = *(gdb_con->buf_p++);
+ if (gdb_con->buf_cnt > 0)
+ connection->input_pending = 1;
+ else
+ connection->input_pending = 0;
+#ifdef _DEBUG_GDB_IO_
+ DEBUG("returned char '%c' (0x%2.2x)", *next_char, *next_char);
+#endif
+
+ return ERROR_OK;
+}
+
+int gdb_putback_char(connection_t *connection, int last_char)
+{
+ gdb_connection_t *gdb_con = connection->priv;
+
+ if (gdb_con->buf_p > gdb_con->buffer)
+ {
+ *(--gdb_con->buf_p) = last_char;
+ gdb_con->buf_cnt++;
+ }
+ else
+ {
+ ERROR("BUG: couldn't put character back");
+ }
+
+ return ERROR_OK;
+}
+
+/* The only way we can detect that the socket is closed is the first time
+ * we write to it, we will fail. Subsequent write operations will
+ * succeed. Shudder! */
+int gdb_write(connection_t *connection, void *data, int len)
+{
+ gdb_connection_t *gdb_con = connection->priv;
+ if (gdb_con->closed)
+ return ERROR_SERVER_REMOTE_CLOSED;
+
+ if (write_socket(connection->fd, data, len) == len)
+ {
+ return ERROR_OK;
+ }
+ gdb_con->closed = 1;
+ return ERROR_SERVER_REMOTE_CLOSED;
+}
+
+int gdb_put_packet_inner(connection_t *connection, char *buffer, int len)
+{
+ int i;
+ unsigned char my_checksum = 0;
+#ifdef _DEBUG_GDB_IO_
+ char *debug_buffer;
+#endif
+ int reply;
+ int retval;
+ gdb_connection_t *gdb_con = connection->priv;
+
+ for (i = 0; i < len; i++)
+ my_checksum += buffer[i];
+
+ while (1)
+ {
+#ifdef _DEBUG_GDB_IO_
+ debug_buffer = malloc(len + 1);
+ memcpy(debug_buffer, buffer, len);
+ debug_buffer[len] = 0;
+ DEBUG("sending packet '$%s#%2.2x'", debug_buffer, my_checksum);
+ free(debug_buffer);
+#endif
+#if 0
+ char checksum[3];
+ gdb_write(connection, "$", 1);
+ if (len > 0)
+ gdb_write(connection, buffer, len);
+ gdb_write(connection, "#", 1);
+
+ snprintf(checksum, 3, "%2.2x", my_checksum);
+
+ gdb_write(connection, checksum, 2);
+#else
+ void *allocated = NULL;
+ char stackAlloc[1024];
+ char *t = stackAlloc;
+ int totalLen = 1 + len + 1 + 2;
+ if (totalLen > sizeof(stackAlloc))
+ {
+ allocated = malloc(totalLen);
+ t = allocated;
+ if (allocated == NULL)
+ {
+ ERROR("Ran out of memory trying to reply packet %d\n", totalLen);
+ exit(-1);
+ }
+ }
+ t[0] = '$';
+ memcpy(t + 1, buffer, len);
+ t[1 + len] = '#';
+ t[1 + len + 1] = DIGITS[(my_checksum >> 4) & 0xf];
+ t[1 + len + 2] = DIGITS[my_checksum & 0xf];
+
+ gdb_write(connection, t, totalLen);
+
+ if (allocated)
+ {
+ free(allocated);
+ }
+#endif
+ if ((retval = gdb_get_char(connection, &reply)) != ERROR_OK)
+ return retval;
+
+ if (reply == '+')
+ break;
+ else if (reply == '-')
+ {
+ /* Stop sending output packets for now */
+ log_setCallback(NULL, NULL);
+ WARNING("negative reply, retrying");
+ }
+ else if (reply == 0x3)
+ {
+ gdb_con->ctrl_c = 1;
+ if ((retval = gdb_get_char(connection, &reply)) != ERROR_OK)
+ return retval;
+ if (reply == '+')
+ break;
+ else if (reply == '-')
+ {
+ /* Stop sending output packets for now */
+ log_setCallback(NULL, NULL);
+ WARNING("negative reply, retrying");
+ }
+ else
+ {
+ ERROR("unknown character 0x%2.2x in reply, dropping connection", reply);
+ return ERROR_SERVER_REMOTE_CLOSED;
+ }
+ }
+ else
+ {
+ ERROR("unknown character 0x%2.2x in reply, dropping connection", reply);
+ return ERROR_SERVER_REMOTE_CLOSED;
+ }
+ }
+ if (gdb_con->closed)
+ return ERROR_SERVER_REMOTE_CLOSED;
+
+ return ERROR_OK;
+}
+
+int gdb_put_packet(connection_t *connection, char *buffer, int len)
+{
+ gdb_connection_t *gdb_con = connection->priv;
+ gdb_con->busy = 1;
+ int retval = gdb_put_packet_inner(connection, buffer, len);
+ gdb_con->busy = 0;
+ return retval;
+}
+
+int gdb_get_packet_inner(connection_t *connection, char *buffer, int *len)
+{
+ int character;
+ int count = 0;
+ int retval;
+ char checksum[3];
+ unsigned char my_checksum = 0;
+ gdb_connection_t *gdb_con = connection->priv;
+
+ while (1)
+ {
+ do
+ {
+ if ((retval = gdb_get_char(connection, &character)) != ERROR_OK)
+ return retval;
+
+#ifdef _DEBUG_GDB_IO_
+ DEBUG("character: '%c'", character);
+#endif
+
+ switch (character)
+ {
+ case '$':
+ break;
+ case '+':
+ WARNING("acknowledgment received, but no packet pending");
+ break;
+ case '-':
+ WARNING("negative acknowledgment, but no packet pending");
+ break;
+ case 0x3:
+ gdb_con->ctrl_c = 1;
+ *len = 0;
+ return ERROR_OK;
+ default:
+ WARNING("ignoring character 0x%x", character);
+ break;
+ }
+ } while (character != '$');
+
+ my_checksum = 0;
+
+ count = 0;
+ gdb_connection_t *gdb_con = connection->priv;
+ for (;;)
+ {
+ /* The common case is that we have an entire packet with no escape chars.
+ * We need to leave at least 2 bytes in the buffer to have
+ * gdb_get_char() update various bits and bobs correctly.
+ */
+ if ((gdb_con->buf_cnt > 2) && ((gdb_con->buf_cnt+count) < *len))
+ {
+ /* The compiler will struggle a bit with constant propagation and
+ * aliasing, so we help it by showing that these values do not
+ * change inside the loop
+ */
+ int i;
+ char *buf = gdb_con->buf_p;
+ int run = gdb_con->buf_cnt - 2;
+ i = 0;
+ int done = 0;
+ while (i < run)
+ {
+ character = *buf++;
+ i++;
+ if (character == '#')
+ {
+ /* Danger! character can be '#' when esc is
+ * used so we need an explicit boolean for done here.
+ */
+ done = 1;
+ break;
+ }
+
+ if (character == '}')
+ {
+ /* data transmitted in binary mode (X packet)
+ * uses 0x7d as escape character */
+ my_checksum += character & 0xff;
+ character = *buf++;
+ i++;
+ my_checksum += character & 0xff;
+ buffer[count++] = (character ^ 0x20) & 0xff;
+ } else
+ {
+ my_checksum += character & 0xff;
+ buffer[count++] = character & 0xff;
+ }
+ }
+ gdb_con->buf_p += i;
+ gdb_con->buf_cnt -= i;
+ if (done)
+ break;
+ }
+ if (count > *len)
+ {
+ ERROR("packet buffer too small");
+ return ERROR_GDB_BUFFER_TOO_SMALL;
+ }
+
+ if ((retval = gdb_get_char(connection, &character)) != ERROR_OK)
+ return retval;
+
+ if (character == '#')
+ break;
+
+ if (character == '}')
+ {
+ /* data transmitted in binary mode (X packet)
+ * uses 0x7d as escape character */
+ my_checksum += character & 0xff;
+ if ((retval = gdb_get_char(connection, &character)) != ERROR_OK)
+ return retval;
+ my_checksum += character & 0xff;
+ buffer[count++] = (character ^ 0x20) & 0xff;
+ }
+ else
+ {
+ my_checksum += character & 0xff;
+ buffer[count++] = character & 0xff;
+ }
+
+ }
+
+ *len = count;
+
+ if ((retval = gdb_get_char(connection, &character)) != ERROR_OK)
+ return retval;
+ checksum[0] = character;
+ if ((retval = gdb_get_char(connection, &character)) != ERROR_OK)
+ return retval;
+ checksum[1] = character;
+ checksum[2] = 0;
+
+ if (my_checksum == strtoul(checksum, NULL, 16))
+ {
+ gdb_write(connection, "+", 1);
+ break;
+ }
+
+ WARNING("checksum error, requesting retransmission");
+ gdb_write(connection, "-", 1);
+ }
+ if (gdb_con->closed)
+ return ERROR_SERVER_REMOTE_CLOSED;
+
+ return ERROR_OK;
+}
+
+int gdb_get_packet(connection_t *connection, char *buffer, int *len)
+{
+ gdb_connection_t *gdb_con = connection->priv;
+ gdb_con->busy = 1;
+ int retval = gdb_get_packet_inner(connection, buffer, len);
+ gdb_con->busy = 0;
+ return retval;
+}
+
+int gdb_output_con(connection_t *connection, char* line)
+{
+ char *hex_buffer;
+ int i, bin_size;
+
+ bin_size = strlen(line);
+
+ hex_buffer = malloc(bin_size*2 + 4);
+
+ hex_buffer[0] = 'O';
+ for (i=0; i<bin_size; i++)
+ snprintf(hex_buffer + 1 + i*2, 3, "%2.2x", line[i]);
+ hex_buffer[bin_size*2+1] = '0';
+ hex_buffer[bin_size*2+2] = 'a';
+ hex_buffer[bin_size*2+3] = 0x0;
+
+ gdb_put_packet(connection, hex_buffer, bin_size*2 + 3);
+
+ free(hex_buffer);
+ return ERROR_OK;
+}
+
+int gdb_output(struct command_context_s *context, char* line)
+{
+ /* this will be dumped to the log and also sent as an O packet if possible */
+ USER(line);
+ return ERROR_OK;
+}
+
+int gdb_program_handler(struct target_s *target, enum target_event event, void *priv)
+{
+ FILE *script;
+ struct command_context_s *cmd_ctx = priv;
+
+ if (target->gdb_program_script)
+ {
+ script = open_file_from_path(cmd_ctx, target->gdb_program_script, "r");
+ if (!script)
+ {
+ ERROR("couldn't open script file %s", target->gdb_program_script);
+ return ERROR_OK;
+ }
+
+ INFO("executing gdb_program script '%s'", target->gdb_program_script);
+ command_run_file(cmd_ctx, script, COMMAND_EXEC);
+ fclose(script);
+
+ jtag_execute_queue();
+ }
+
+ return ERROR_OK;
+}
+
+int gdb_target_callback_event_handler(struct target_s *target, enum target_event event, void *priv)
+{
+ connection_t *connection = priv;
+ gdb_connection_t *gdb_connection = connection->priv;
+ char sig_reply[4];
+ int signal;
+
+ switch (event)
+ {
+ case TARGET_EVENT_HALTED:
+ /* In the GDB protocol when we are stepping or coninuing execution,
+ * we have a lingering reply. Upon receiving a halted event
+ * when we have that lingering packet, we reply to the original
+ * step or continue packet.
+ *
+ * Executing monitor commands can bring the target in and
+ * out of the running state so we'll see lots of TARGET_EVENT_XXX
+ * that are to be ignored.
+ */
+ if (gdb_connection->frontend_state == TARGET_RUNNING)
+ {
+ /* stop forwarding log packets! */
+ log_setCallback(NULL, NULL);
+
+ if (gdb_connection->ctrl_c)
+ {
+ signal = 0x2;
+ gdb_connection->ctrl_c = 0;
+ }
+ else
+ {
+ signal = gdb_last_signal(target);
+ }
+
+ snprintf(sig_reply, 4, "T%2.2x", signal);
+ gdb_put_packet(connection, sig_reply, 3);
+ gdb_connection->frontend_state = TARGET_HALTED;
+ }
+ break;
+ case TARGET_EVENT_GDB_PROGRAM:
+ gdb_program_handler(target, event, connection->cmd_ctx);
+ break;
+ default:
+ break;
+ }
+
+ return ERROR_OK;
+}
+
+int gdb_new_connection(connection_t *connection)
+{
+ gdb_connection_t *gdb_connection = malloc(sizeof(gdb_connection_t));
+ gdb_service_t *gdb_service = connection->service->priv;
+ int retval;
+ int initial_ack;
+
+ connection->priv = gdb_connection;
+
+ /* initialize gdb connection information */
+ gdb_connection->buf_p = gdb_connection->buffer;
+ gdb_connection->buf_cnt = 0;
+ gdb_connection->ctrl_c = 0;
+ gdb_connection->frontend_state = TARGET_HALTED;
+ gdb_connection->vflash_image = NULL;
+ gdb_connection->closed = 0;
+ gdb_connection->busy = 0;
+
+ /* output goes through gdb connection */
+ command_set_output_handler(connection->cmd_ctx, gdb_output, connection);
+
+ /* register callback to be informed about target events */
+ target_register_event_callback(gdb_target_callback_event_handler, connection);
+
+ /* a gdb session just attached, put the target in halt mode */
+ if (((retval = gdb_service->target->type->halt(gdb_service->target)) != ERROR_OK) &&
+ (retval != ERROR_TARGET_ALREADY_HALTED))
+ {
+ ERROR("error(%d) when trying to halt target, falling back to \"reset halt\"", retval);
+ command_run_line(connection->cmd_ctx, "reset halt");
+ }
+
+ /* This will time out after 1 second */
+ command_run_line(connection->cmd_ctx, "wait_halt 1");
+
+ /* remove the initial ACK from the incoming buffer */
+ if ((retval = gdb_get_char(connection, &initial_ack)) != ERROR_OK)
+ return retval;
+
+ if (initial_ack != '+')
+ gdb_putback_char(connection, initial_ack);
+
+ return ERROR_OK;
+}
+
+int gdb_connection_closed(connection_t *connection)
+{
+ gdb_service_t *gdb_service = connection->service->priv;
+ gdb_connection_t *gdb_connection = connection->priv;
+
+ /* see if an image built with vFlash commands is left */
+ if (gdb_connection->vflash_image)
+ {
+ image_close(gdb_connection->vflash_image);
+ free(gdb_connection->vflash_image);
+ gdb_connection->vflash_image = NULL;
+ }
+
+ /* if this connection registered a debug-message receiver delete it */
+ delete_debug_msg_receiver(connection->cmd_ctx, gdb_service->target);
+
+ if (connection->priv)
+ {
+ free(connection->priv);
+ connection->priv = NULL;
+ }
+ else
+ {
+ ERROR("BUG: connection->priv == NULL");
+ }
+
+ target_unregister_event_callback(gdb_target_callback_event_handler, connection);
+ log_setCallback(NULL, NULL);
+
+ return ERROR_OK;
+}
+
+void gdb_send_error(connection_t *connection, u8 the_error)
+{
+ char err[4];
+ snprintf(err, 4, "E%2.2X", the_error );
+ gdb_put_packet(connection, err, 3);
+}
+
+int gdb_last_signal_packet(connection_t *connection, target_t *target, char* packet, int packet_size)
+{
+ char sig_reply[4];
+ int signal;
+
+ signal = gdb_last_signal(target);
+
+ snprintf(sig_reply, 4, "S%2.2x", signal);
+ gdb_put_packet(connection, sig_reply, 3);
+
+ return ERROR_OK;
+}
+
+/* Convert register to string of bits. NB! The # of bits in the
+ * register might be non-divisible by 8(a byte), in which
+ * case an entire byte is shown. */
+void gdb_str_to_target(target_t *target, char *tstr, reg_t *reg)
+{
+ int i;
+
+ u8 *buf;
+ int buf_len;
+ buf = reg->value;
+ buf_len = CEIL(reg->size, 8);
+
+ if (target->endianness == TARGET_LITTLE_ENDIAN)
+ {
+ for (i = 0; i < buf_len; i++)
+ {
+ tstr[i*2] = DIGITS[(buf[i]>>4) & 0xf];
+ tstr[i*2+1] = DIGITS[buf[i]&0xf];
+ }
+ }
+ else
+ {
+ for (i = 0; i < buf_len; i++)
+ {
+ tstr[(buf_len-1-i)*2] = DIGITS[(buf[i]>>4)&0xf];
+ tstr[(buf_len-1-i)*2+1] = DIGITS[buf[i]&0xf];
+ }
+ }
+}
+
+void gdb_target_to_str(target_t *target, char *tstr, char *str)
+{
+ int str_len = strlen(tstr);
+ int i;
+
+ if (str_len % 2)
+ {
+ ERROR("BUG: gdb value with uneven number of characters encountered");
+ exit(-1);
+ }
+
+ if (target->endianness == TARGET_LITTLE_ENDIAN)
+ {
+ for (i = 0; i < str_len; i+=2)
+ {
+ str[str_len - i - 1] = tstr[i + 1];
+ str[str_len - i - 2] = tstr[i];
+ }
+ }
+ else
+ {
+ for (i = 0; i < str_len; i++)
+ {
+ str[i] = tstr[i];
+ }
+ }
+}
+
+int gdb_get_registers_packet(connection_t *connection, target_t *target, char* packet, int packet_size)
+{
+ reg_t **reg_list;
+ int reg_list_size;
+ int retval;
+ int reg_packet_size = 0;
+ char *reg_packet;
+ char *reg_packet_p;
+ int i;
+
+#ifdef _DEBUG_GDB_IO_
+ DEBUG("-");
+#endif
+
+ if ((retval = target->type->get_gdb_reg_list(target, ®_list, ®_list_size)) != ERROR_OK)
+ {
+ switch (retval)
+ {
+ case ERROR_TARGET_NOT_HALTED:
+ ERROR("gdb requested registers but we're not halted, dropping connection");
+ return ERROR_SERVER_REMOTE_CLOSED;
+ default:
+ /* this is a bug condition - get_gdb_reg_list() may not return any other error */
+ ERROR("BUG: unexpected error returned by get_gdb_reg_list()");
+ exit(-1);
+ }
+ }
+
+ for (i = 0; i < reg_list_size; i++)
+ {
+ reg_packet_size += reg_list[i]->size;
+ }
+
+ reg_packet = malloc(CEIL(reg_packet_size, 8) * 2);
+ reg_packet_p = reg_packet;
+
+ for (i = 0; i < reg_list_size; i++)
+ {
+ gdb_str_to_target(target, reg_packet_p, reg_list[i]);
+ reg_packet_p += CEIL(reg_list[i]->size, 8) * 2;
+ }
+
+#ifdef _DEBUG_GDB_IO_
+ {
+ char *reg_packet_p;
+ reg_packet_p = strndup(reg_packet, CEIL(reg_packet_size, 8) * 2);
+ DEBUG("reg_packet: %s", reg_packet_p);
+ free(reg_packet_p);
+ }
+#endif
+
+ gdb_put_packet(connection, reg_packet, CEIL(reg_packet_size, 8) * 2);
+ free(reg_packet);
+
+ free(reg_list);
+
+ return ERROR_OK;
+}
+
+int gdb_set_registers_packet(connection_t *connection, target_t *target, char *packet, int packet_size)
+{
+ int i;
+ reg_t **reg_list;
+ int reg_list_size;
+ int retval;
+ char *packet_p;
+
+#ifdef _DEBUG_GDB_IO_
+ DEBUG("-");
+#endif
+
+ /* skip command character */
+ packet++;
+ packet_size--;
+
+ if (packet_size % 2)
+ {
+ WARNING("GDB set_registers packet with uneven characters received, dropping connection");
+ return ERROR_SERVER_REMOTE_CLOSED;
+ }
+
+ if ((retval = target->type->get_gdb_reg_list(target, ®_list, ®_list_size)) != ERROR_OK)
+ {
+ switch (retval)
+ {
+ case ERROR_TARGET_NOT_HALTED:
+ ERROR("gdb tried to registers but we're not halted, dropping connection");
+ return ERROR_SERVER_REMOTE_CLOSED;
+ default:
+ /* this is a bug condition - get_gdb_reg_list() may not return any other error */
+ ERROR("BUG: unexpected error returned by get_gdb_reg_list()");
+ exit(-1);
+ }
+ }
+
+ packet_p = packet;
+ for (i = 0; i < reg_list_size; i++)
+ {
+ u8 *bin_buf;
+ char *hex_buf;
+ reg_arch_type_t *arch_type;
+
+ /* convert from GDB-string (target-endian) to hex-string (big-endian) */
+ hex_buf = malloc(CEIL(reg_list[i]->size, 8) * 2);
+ gdb_target_to_str(target, packet_p, hex_buf);
+
+ /* convert hex-string to binary buffer */
+ bin_buf = malloc(CEIL(reg_list[i]->size, 8));
+ str_to_buf(hex_buf, CEIL(reg_list[i]->size, 8) * 2, bin_buf, reg_list[i]->size, 16);
+
+ /* get register arch_type, and call set method */
+ arch_type = register_get_arch_type(reg_list[i]->arch_type);
+ if (arch_type == NULL)
+ {
+ ERROR("BUG: encountered unregistered arch type");
+ exit(-1);
+ }
+ arch_type->set(reg_list[i], bin_buf);
+
+ /* advance packet pointer */
+ packet_p += (CEIL(reg_list[i]->size, 8) * 2);
+
+ free(bin_buf);
+ free(hex_buf);
+ }
+
+ /* free reg_t *reg_list[] array allocated by get_gdb_reg_list */
+ free(reg_list);
+
+ gdb_put_packet(connection, "OK", 2);
+
+ return ERROR_OK;
+}
+
+int gdb_get_register_packet(connection_t *connection, target_t *target, char *packet, int packet_size)
+{
+ char *reg_packet;
+ int reg_num = strtoul(packet + 1, NULL, 16);
+ reg_t **reg_list;
+ int reg_list_size;
+ int retval;
+
+#ifdef _DEBUG_GDB_IO_
+ DEBUG("-");
+#endif
+
+ if ((retval = target->type->get_gdb_reg_list(target, ®_list, ®_list_size)) != ERROR_OK)
+ {
+ switch (retval)
+ {
+ case ERROR_TARGET_NOT_HALTED:
+ ERROR("gdb requested registers but we're not halted, dropping connection");
+ return ERROR_SERVER_REMOTE_CLOSED;
+ default:
+ /* this is a bug condition - get_gdb_reg_list() may not return any other error */
+ ERROR("BUG: unexpected error returned by get_gdb_reg_list()");
+ exit(-1);
+ }
+ }
+
+ if (reg_list_size <= reg_num)
+ {
+ ERROR("gdb requested a non-existing register");
+ exit(-1);
+ }
+
+ reg_packet = malloc(CEIL(reg_list[reg_num]->size, 8) * 2);
+
+ gdb_str_to_target(target, reg_packet, reg_list[reg_num]);
+
+ gdb_put_packet(connection, reg_packet, CEIL(reg_list[reg_num]->size, 8) * 2);
+
+ free(reg_list);
+ free(reg_packet);
+
+ return ERROR_OK;
+}
+
+int gdb_set_register_packet(connection_t *connection, target_t *target, char *packet, int packet_size)
+{
+ char *separator;
+ char *hex_buf;
+ u8 *bin_buf;
+ int reg_num = strtoul(packet + 1, &separator, 16);
+ reg_t **reg_list;
+ int reg_list_size;
+ int retval;
+ reg_arch_type_t *arch_type;
+
+ DEBUG("-");
+
+ if ((retval = target->type->get_gdb_reg_list(target, ®_list, ®_list_size)) != ERROR_OK)
+ {
+ switch (retval)
+ {
+ case ERROR_TARGET_NOT_HALTED:
+ ERROR("gdb tried to set a register but we're not halted, dropping connection");
+ return ERROR_SERVER_REMOTE_CLOSED;
+ default:
+ /* this is a bug condition - get_gdb_reg_list() may not return any other error */
+ ERROR("BUG: unexpected error returned by get_gdb_reg_list()");
+ exit(-1);
+ }
+ }
+
+ if (reg_list_size < reg_num)
+ {
+ ERROR("gdb requested a non-existing register");
+ return ERROR_SERVER_REMOTE_CLOSED;
+ }
+
+ if (*separator != '=')
+ {
+ ERROR("GDB 'set register packet', but no '=' following the register number");
+ return ERROR_SERVER_REMOTE_CLOSED;
+ }
+
+ /* convert from GDB-string (target-endian) to hex-string (big-endian) */
+ hex_buf = malloc(CEIL(reg_list[reg_num]->size, 8) * 2);
+ gdb_target_to_str(target, separator + 1, hex_buf);
+
+ /* convert hex-string to binary buffer */
+ bin_buf = malloc(CEIL(reg_list[reg_num]->size, 8));
+ str_to_buf(hex_buf, CEIL(reg_list[reg_num]->size, 8) * 2, bin_buf, reg_list[reg_num]->size, 16);
+
+ /* get register arch_type, and call set method */
+ arch_type = register_get_arch_type(reg_list[reg_num]->arch_type);
+ if (arch_type == NULL)
+ {
+ ERROR("BUG: encountered unregistered arch type");
+ exit(-1);
+ }
+ arch_type->set(reg_list[reg_num], bin_buf);
+
+ gdb_put_packet(connection, "OK", 2);
+
+ free(bin_buf);
+ free(hex_buf);
+ free(reg_list);
+
+ return ERROR_OK;
+}
+
+int gdb_memory_packet_error(connection_t *connection, int retval)
+{
+ switch (retval)
+ {
+ case ERROR_TARGET_NOT_HALTED:
+ ERROR("gdb tried to read memory but we're not halted, dropping connection");
+ return ERROR_SERVER_REMOTE_CLOSED;
+ case ERROR_TARGET_DATA_ABORT:
+ gdb_send_error(connection, EIO);
+ break;
+ case ERROR_TARGET_TRANSLATION_FAULT:
+ gdb_send_error(connection, EFAULT);
+ break;
+ case ERROR_TARGET_UNALIGNED_ACCESS:
+ gdb_send_error(connection, EFAULT);
+ break;
+ default:
+ /* This could be that the target reset itself. */
+ ERROR("unexpected error %i. Dropping connection.", retval);
+ return ERROR_SERVER_REMOTE_CLOSED;
+ }
+
+ return ERROR_OK;
+}
+
+/* We don't have to worry about the default 2 second timeout for GDB packets,
+ * because GDB breaks up large memory reads into smaller reads.
+ *
+ * 8191 bytes by the looks of it. Why 8191 bytes instead of 8192?????
+ */
+int gdb_read_memory_packet(connection_t *connection, target_t *target, char *packet, int packet_size)
+{
+ char *separator;
+ u32 addr = 0;
+ u32 len = 0;
+
+ u8 *buffer;
+ char *hex_buffer;
+
+ int retval = ERROR_OK;
+
+ /* skip command character */
+ packet++;
+
+ addr = strtoul(packet, &separator, 16);
+
+ if (*separator != ',')
+ {
+ ERROR("incomplete read memory packet received, dropping connection");
+ return ERROR_SERVER_REMOTE_CLOSED;
+ }
+
+ len = strtoul(separator+1, NULL, 16);
+
+ buffer = malloc(len);
+
+ DEBUG("addr: 0x%8.8x, len: 0x%8.8x", addr, len);
+
+ retval = target_read_buffer(target, addr, len, buffer);
+
+ if ((retval == ERROR_TARGET_DATA_ABORT) && (!gdb_report_data_abort))
+ {
+ /* TODO : Here we have to lie and send back all zero's lest stack traces won't work.
+ * At some point this might be fixed in GDB, in which case this code can be removed.
+ *
+ * OpenOCD developers are acutely aware of this problem, but there is nothing
+ * gained by involving the user in this problem that hopefully will get resolved
+ * eventually
+ *
+ * http://sourceware.org/cgi-bin/gnatsweb.pl?cmd=view%20audit-trail&database=gdb&pr=2395
+ *
+ * For now, the default is to fix up things to make current GDB versions work.
+ * This can be overwritten using the gdb_report_data_abort <'enable'|'disable'> command.
+ */
+ memset(buffer, 0, len);
+ retval = ERROR_OK;
+ }
+
+ if (retval == ERROR_OK)
+ {
+ hex_buffer = malloc(len * 2 + 1);
+
+ int i;
+ for (i = 0; i < len; i++)
+ {
+ u8 t = buffer[i];
+ hex_buffer[2 * i] = DIGITS[(t >> 4) & 0xf];
+ hex_buffer[2 * i + 1] = DIGITS[t & 0xf];
+ }
+
+ gdb_put_packet(connection, hex_buffer, len * 2);
+
+ free(hex_buffer);
+ }
+ else
+ {
+ retval = gdb_memory_packet_error(connection, retval);
+ }
+
+ free(buffer);
+
+ return retval;
+}
+
+int gdb_write_memory_packet(connection_t *connection, target_t *target, char *packet, int packet_size)
+{
+ char *separator;
+ u32 addr = 0;
+ u32 len = 0;
+
+ u8 *buffer;
+
+ int i;
+ int retval;
+
+ /* skip command character */
+ packet++;
+
+ addr = strtoul(packet, &separator, 16);
+
+ if (*separator != ',')
+ {
+ ERROR("incomplete write memory packet received, dropping connection");
+ return ERROR_SERVER_REMOTE_CLOSED;
+ }
+
+ len = strtoul(separator+1, &separator, 16);
+
+ if (*(separator++) != ':')
+ {
+ ERROR("incomplete write memory packet received, dropping connection");
+ return ERROR_SERVER_REMOTE_CLOSED;
+ }
+
+ buffer = malloc(len);
+
+ DEBUG("addr: 0x%8.8x, len: 0x%8.8x", addr, len);
+
+ for (i=0; i<len; i++)
+ {
+ u32 tmp;
+ sscanf(separator + 2*i, "%2x", &tmp);
+ buffer[i] = tmp;
+ }
+
+ retval = target_write_buffer(target, addr, len, buffer);
+
+ if (retval == ERROR_OK)
+ {
+ gdb_put_packet(connection, "OK", 2);
+ }
+ else
+ {
+ if ((retval = gdb_memory_packet_error(connection, retval)) != ERROR_OK)
+ return retval;
+ }
+
+ free(buffer);
+
+ return ERROR_OK;
+}
+
+int gdb_write_memory_binary_packet(connection_t *connection, target_t *target, char *packet, int packet_size)
+{
+ char *separator;
+ u32 addr = 0;
+ u32 len = 0;
+
+ int retval;
+
+ /* skip command character */
+ packet++;
+
+ addr = strtoul(packet, &separator, 16);
+
+ if (*separator != ',')
+ {
+ ERROR("incomplete write memory binary packet received, dropping connection");
+ return ERROR_SERVER_REMOTE_CLOSED;
+ }
+
+ len = strtoul(separator+1, &separator, 16);
+
+ if (*(separator++) != ':')
+ {
+ ERROR("incomplete write memory binary packet received, dropping connection");
+ return ERROR_SERVER_REMOTE_CLOSED;
+ }
+
+ retval = ERROR_OK;
+ if (len)
+ {
+ DEBUG("addr: 0x%8.8x, len: 0x%8.8x", addr, len);
+
+ retval = target_write_buffer(target, addr, len, (u8*)separator);
+ }
+
+ if (retval == ERROR_OK)
+ {
+ gdb_put_packet(connection, "OK", 2);
+ }
+ else
+ {
+ if ((retval = gdb_memory_packet_error(connection, retval)) != ERROR_OK)
+ return retval;
+ }
+
+ return ERROR_OK;
+}
+
+void gdb_step_continue_packet(connection_t *connection, target_t *target, char *packet, int packet_size)
+{
+ int current = 0;
+ u32 address = 0x0;
+
+ DEBUG("-");
+
+ if (packet_size > 1)
+ {
+ packet[packet_size] = 0;
+ address = strtoul(packet + 1, NULL, 16);
+ }
+ else
+ {
+ current = 1;
+ }
+
+ if (packet[0] == 'c')
+ {
+ DEBUG("continue");
+ target->type->resume(target, current, address, 0, 0); /* resume at current address, don't handle breakpoints, not debugging */
+ }
+ else if (packet[0] == 's')
+ {
+ DEBUG("step");
+ target->type->step(target, current, address, 0); /* step at current or address, don't handle breakpoints */
+ }
+}
+
+int gdb_bp_wp_packet_error(connection_t *connection, int retval)
+{
+ switch (retval)
+ {
+ case ERROR_TARGET_NOT_HALTED:
+ ERROR("gdb tried to set a breakpoint but we're not halted, dropping connection");
+ return ERROR_SERVER_REMOTE_CLOSED;
+ break;
+ case ERROR_TARGET_RESOURCE_NOT_AVAILABLE:
+ gdb_send_error(connection, EBUSY);
+ break;
+ default:
+ ERROR("BUG: unexpected error %i", retval);
+ exit(-1);
+ }
+
+ return ERROR_OK;
+}
+
+int gdb_breakpoint_watchpoint_packet(connection_t *connection, target_t *target, char *packet, int packet_size)
+{
+ int type;
+ enum breakpoint_type bp_type = BKPT_SOFT /* dummy init to avoid warning */;
+ enum watchpoint_rw wp_type;
+ u32 address;
+ u32 size;
+ char *separator;
+ int retval;
+
+ DEBUG("-");
+
+ type = strtoul(packet + 1, &separator, 16);
+
+ if (type == 0) /* memory breakpoint */
+ bp_type = BKPT_SOFT;
+ else if (type == 1) /* hardware breakpoint */
+ bp_type = BKPT_HARD;
+ else if (type == 2) /* write watchpoint */
+ wp_type = WPT_WRITE;
+ else if (type == 3) /* read watchpoint */
+ wp_type = WPT_READ;
+ else if (type == 4) /* access watchpoint */
+ wp_type = WPT_ACCESS;
+
+ if (*separator != ',')
+ {
+ ERROR("incomplete breakpoint/watchpoint packet received, dropping connection");
+ return ERROR_SERVER_REMOTE_CLOSED;
+ }
+
+ address = strtoul(separator+1, &separator, 16);
+
+ if (*separator != ',')
+ {
+ ERROR("incomplete breakpoint/watchpoint packet received, dropping connection");
+ return ERROR_SERVER_REMOTE_CLOSED;
+ }
+
+ size = strtoul(separator+1, &separator, 16);
+
+ switch (type)
+ {
+ case 0:
+ case 1:
+ if (packet[0] == 'Z')
+ {
+ if ((retval = breakpoint_add(target, address, size, bp_type)) != ERROR_OK)
+ {
+ if ((retval = gdb_bp_wp_packet_error(connection, retval)) != ERROR_OK)
+ return retval;
+ }
+ else
+ {
+ gdb_put_packet(connection, "OK", 2);
+ }
+ }
+ else
+ {
+ breakpoint_remove(target, address);
+ gdb_put_packet(connection, "OK", 2);
+ }
+ break;
+ case 2:
+ case 3:
+ case 4:
+ {
+ if (packet[0] == 'Z')
+ {
+ if ((retval = watchpoint_add(target, address, size, type-2, 0, 0xffffffffu)) != ERROR_OK)
+ {
+ if ((retval = gdb_bp_wp_packet_error(connection, retval)) != ERROR_OK)
+ return retval;
+ }
+ else
+ {
+ gdb_put_packet(connection, "OK", 2);
+ }
+ }
+ else
+ {
+ watchpoint_remove(target, address);
+ gdb_put_packet(connection, "OK", 2);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ return ERROR_OK;
+}
+
+/* print out a string and allocate more space as needed, mainly used for XML at this point */
+void xml_printf(int *retval, char **xml, int *pos, int *size, const char *fmt, ...)
+{
+ if (*retval != ERROR_OK)
+ {
+ return;
+ }
+ int first = 1;
+
+ for (;;)
+ {
+ if ((*xml == NULL) || (!first))
+ {
+ /* start by 0 to exercise all the code paths.
+ * Need minimum 2 bytes to fit 1 char and 0 terminator. */
+
+ *size = *size * 2 + 2;
+ char *t = *xml;
+ *xml = realloc(*xml, *size);
+ if (*xml == NULL)
+ {
+ if (t)
+ free(t);
+ *retval = ERROR_SERVER_REMOTE_CLOSED;
+ return;
+ }
+ }
+
+ va_list ap;
+ int ret;
+ va_start(ap, fmt);
+ ret = vsnprintf(*xml + *pos, *size - *pos, fmt, ap);
+ va_end(ap);
+ if ((ret > 0) && ((ret + 1) < *size - *pos))
+ {
+ *pos += ret;
+ return;
+ }
+ /* there was just enough or not enough space, allocate more. */
+ first = 0;
+ }
+}
+
+static int decode_xfer_read(char *buf, char **annex, int *ofs, unsigned int *len)
+{
+ char *separator;
+
+ /* Extract and NUL-terminate the annex. */
+ *annex = buf;
+ while (*buf && *buf != ':')
+ buf++;
+ if (*buf == '\0')
+ return -1;
+ *buf++ = 0;
+
+ /* After the read marker and annex, qXfer looks like a
+ * traditional 'm' packet. */
+
+ *ofs = strtoul(buf, &separator, 16);
+
+ if (*separator != ',')
+ return -1;
+
+ *len = strtoul(separator+1, NULL, 16);
+
+ return 0;
+}
+
+int gdb_calc_blocksize(flash_bank_t *bank)
+{
+ int i;
+ int block_size = 0xffffffff;
+
+ /* loop through all sectors and return smallest sector size */
+
+ for (i = 0; i < bank->num_sectors; i++)
+ {
+ if (bank->sectors[i].size < block_size)
+ block_size = bank->sectors[i].size;
+ }
+
+ return block_size;
+}
+
+int gdb_query_packet(connection_t *connection, target_t *target, char *packet, int packet_size)
+{
+ command_context_t *cmd_ctx = connection->cmd_ctx;
+
+ if (strstr(packet, "qRcmd,"))
+ {
+ if (packet_size > 6)
+ {
+ char *cmd;
+ int i;
+ cmd = malloc((packet_size - 6)/2 + 1);
+ for (i=0; i < (packet_size - 6)/2; i++)
+ {
+ u32 tmp;
+ sscanf(packet + 6 + 2*i, "%2x", &tmp);
+ cmd[i] = tmp;
+ }
+ cmd[(packet_size - 6)/2] = 0x0;
+
+ /* We want to print all debug output to GDB connection */
+ log_setCallback(gdb_log_callback, connection);
+ target_call_timer_callbacks();
+ command_run_line(cmd_ctx, cmd);
+ free(cmd);
+ }
+ gdb_put_packet(connection, "OK", 2);
+ return ERROR_OK;
+ }
+ else if (strstr(packet, "qCRC:"))
+ {
+ if (packet_size > 5)
+ {
+ int retval;
+ char gdb_reply[10];
+ char *separator;
+ u32 checksum;
+ u32 addr = 0;
+ u32 len = 0;
+
+ /* skip command character */
+ packet += 5;
+
+ addr = strtoul(packet, &separator, 16);
+
+ if (*separator != ',')
+ {
+ ERROR("incomplete read memory packet received, dropping connection");
+ return ERROR_SERVER_REMOTE_CLOSED;
+ }
+
+ len = strtoul(separator + 1, NULL, 16);
+
+ retval = target_checksum_memory(target, addr, len, &checksum);
+
+ if (retval == ERROR_OK)
+ {
+ snprintf(gdb_reply, 10, "C%8.8x", checksum);
+ gdb_put_packet(connection, gdb_reply, 9);
+ }
+ else
+ {
+ if ((retval = gdb_memory_packet_error(connection, retval)) != ERROR_OK)
+ return retval;
+ }
+
+ return ERROR_OK;
+ }
+ }
+ else if (strstr(packet, "qSupported"))
+ {
+ /* we currently support packet size and qXfer:memory-map:read (if enabled)
+ * disable qXfer:features:read for the moment */
+ int retval = ERROR_OK;
+ char *buffer = NULL;
+ int pos = 0;
+ int size = 0;
+
+ xml_printf(&retval, &buffer, &pos, &size,
+ "PacketSize=%x;qXfer:memory-map:read%c;qXfer:features:read-",
+ (GDB_BUFFER_SIZE - 1), gdb_use_memory_map == 1 ? '+' : '-');
+
+ if (retval != ERROR_OK)
+ {
+ gdb_send_error(connection, 01);
+ return ERROR_OK;
+ }
+
+ gdb_put_packet(connection, buffer, strlen(buffer));
+ free(buffer);
+
+ return ERROR_OK;
+ }
+ else if (strstr(packet, "qXfer:memory-map:read::"))
+ {
+ /* We get away with only specifying flash here. Regions that are not
+ * specified are treated as if we provided no memory map(if not we
+ * could detect the holes and mark them as RAM).
+ * Normally we only execute this code once, but no big deal if we
+ * have to regenerate it a couple of times. */
+
+ flash_bank_t *p;
+ char *xml = NULL;
+ int size = 0;
+ int pos = 0;
+ int retval = ERROR_OK;
+
+ int offset;
+ int length;
+ char *separator;
+ int blocksize;
+
+ /* skip command character */
+ packet += 23;
+
+ offset = strtoul(packet, &separator, 16);
+ length = strtoul(separator + 1, &separator, 16);
+
+ xml_printf(&retval, &xml, &pos, &size, "<memory-map>\n");
+
+ int i = 0;
+ for (;;)
+ {
+ p = get_flash_bank_by_num(i);
+ if (p == NULL)
+ break;
+
+ /* if device has uneven sector sizes, eg. str7, lpc
+ * we pass the smallest sector size to gdb memory map */
+ blocksize = gdb_calc_blocksize(p);
+
+ xml_printf(&retval, &xml, &pos, &size, "<memory type=\"flash\" start=\"0x%x\" length=\"0x%x\">\n" \
+ "<property name=\"blocksize\">0x%x</property>\n" \
+ "</memory>\n", \
+ p->base, p->size, blocksize);
+ i++;
+ }
+
+ xml_printf(&retval, &xml, &pos, &size, "</memory-map>\n");
+
+ if (retval != ERROR_OK)
+ {
+ gdb_send_error(connection, retval);
+ return retval;
+ }
+
+ if (offset + length > pos)
+ {
+ length = pos - offset;
+ }
+
+ char *t = malloc(length + 1);
+ t[0] = 'l';
+ memcpy(t + 1, xml + offset, length);
+ gdb_put_packet(connection, t, length + 1);
+
+ free(t);
+ free(xml);
+ return ERROR_OK;
+ }
+ else if (strstr(packet, "qXfer:features:read:"))
+ {
+ char *xml = NULL;
+ int size = 0;
+ int pos = 0;
+ int retval = ERROR_OK;
+
+ int offset;
+ unsigned int length;
+ char *annex;
+
+ /* skip command character */
+ packet += 20;
+
+ if (decode_xfer_read(packet, &annex, &offset, &length) < 0)
+ {
+ gdb_send_error(connection, 01);
+ return ERROR_OK;
+ }
+
+ if (strcmp(annex, "target.xml") != 0)
+ {
+ gdb_send_error(connection, 01);
+ return ERROR_OK;
+ }
+
+ xml_printf(&retval, &xml, &pos, &size, \
+ "l<target version=\"1.0\">\n<architecture>arm</architecture>\n</target>\n");
+
+ if (retval != ERROR_OK)
+ {
+ gdb_send_error(connection, retval);
+ return retval;
+ }
+
+ gdb_put_packet(connection, xml, strlen(xml) + 1);
+
+ free(xml);
+ return ERROR_OK;
+ }
+
+ gdb_put_packet(connection, "", 0);
+ return ERROR_OK;
+}
+
+int gdb_v_packet(connection_t *connection, target_t *target, char *packet, int packet_size)
+{
+ gdb_connection_t *gdb_connection = connection->priv;
+ gdb_service_t *gdb_service = connection->service->priv;
+ int result;
+
+ /* if flash programming disabled - send a empty reply */
+
+ if (gdb_flash_program == 0)
+ {
+ gdb_put_packet(connection, "", 0);
+ return ERROR_OK;
+ }
+
+ if (strstr(packet, "vFlashErase:"))
+ {
+ unsigned long addr;
+ unsigned long length;
+
+ char *parse = packet + 12;
+ if (*parse == '\0')
+ {
+ ERROR("incomplete vFlashErase packet received, dropping connection");
+ return ERROR_SERVER_REMOTE_CLOSED;
+ }
+
+ addr = strtoul(parse, &parse, 16);
+
+ if (*(parse++) != ',' || *parse == '\0')
+ {
+ ERROR("incomplete vFlashErase packet received, dropping connection");
+ return ERROR_SERVER_REMOTE_CLOSED;
+ }
+
+ length = strtoul(parse, &parse, 16);
+
+ if (*parse != '\0')
+ {
+ ERROR("incomplete vFlashErase packet received, dropping connection");
+ return ERROR_SERVER_REMOTE_CLOSED;
+ }
+
+ /* assume all sectors need erasing - stops any problems
+ * when flash_write is called multiple times */
+ flash_set_dirty();
+
+ /* perform any target specific operations before the erase */
+ target_call_event_callbacks(gdb_service->target, TARGET_EVENT_GDB_PROGRAM);
+
+ /* perform erase */
+ if ((result = flash_erase_address_range(gdb_service->target, addr, length)) != ERROR_OK)
+ {
+ /* GDB doesn't evaluate the actual error number returned,
+ * treat a failed erase as an I/O error
+ */
+ gdb_send_error(connection, EIO);
+ ERROR("flash_erase returned %i", result);
+ }
+ else
+ gdb_put_packet(connection, "OK", 2);
+
+ return ERROR_OK;
+ }
+
+ if (strstr(packet, "vFlashWrite:"))
+ {
+ unsigned long addr;
+ unsigned long length;
+ char *parse = packet + 12;
+
+ if (*parse == '\0')
+ {
+ ERROR("incomplete vFlashErase packet received, dropping connection");
+ return ERROR_SERVER_REMOTE_CLOSED;
+ }
+ addr = strtoul(parse, &parse, 16);
+ if (*(parse++) != ':')
+ {
+ ERROR("incomplete vFlashErase packet received, dropping connection");
+ return ERROR_SERVER_REMOTE_CLOSED;
+ }
+ length = packet_size - (parse - packet);
+
+ /* create a new image if there isn't already one */
+ if (gdb_connection->vflash_image == NULL)
+ {
+ gdb_connection->vflash_image = malloc(sizeof(image_t));
+ image_open(gdb_connection->vflash_image, "", "build");
+ }
+
+ /* create new section with content from packet buffer */
+ image_add_section(gdb_connection->vflash_image, addr, length, 0x0, (u8*)parse);
+
+ gdb_put_packet(connection, "OK", 2);
+
+ return ERROR_OK;
+ }
+
+ if (!strcmp(packet, "vFlashDone"))
+ {
+ u32 written;
+
+ /* process the flashing buffer. No need to erase as GDB
+ * always issues a vFlashErase first. */
+ if ((result = flash_write(gdb_service->target, gdb_connection->vflash_image, &written, 0)) != ERROR_OK)
+ {
+ if (result == ERROR_FLASH_DST_OUT_OF_BANK)
+ gdb_put_packet(connection, "E.memtype", 9);
+ else
+ gdb_send_error(connection, EIO);
+ }
+ else
+ {
+ DEBUG("wrote %u bytes from vFlash image to flash", written);
+ gdb_put_packet(connection, "OK", 2);
+ }
+
+ image_close(gdb_connection->vflash_image);
+ free(gdb_connection->vflash_image);
+ gdb_connection->vflash_image = NULL;
+
+ return ERROR_OK;
+ }
+
+ gdb_put_packet(connection, "", 0);
+ return ERROR_OK;
+}
+
+int gdb_detach(connection_t *connection, target_t *target)
+{
+ switch( detach_mode )
+ {
+ case GDB_DETACH_RESUME:
+ target->type->resume(target, 1, 0, 1, 0);
+ break;
+
+ case GDB_DETACH_RESET:
+ target_process_reset(connection->cmd_ctx);
+ break;
+
+ case GDB_DETACH_HALT:
+ target->type->halt(target);
+ break;
+
+ case GDB_DETACH_NOTHING:
+ break;
+ }
+
+ gdb_put_packet(connection, "OK", 2);
+
+ return ERROR_OK;
+}
+
+static void gdb_log_callback(void *priv, const char *file, int line,
+ const char *function, const char *format, va_list args)
+{
+ connection_t *connection = priv;
+ gdb_connection_t *gdb_con = connection->priv;
+
+ if (gdb_con->busy)
+ {
+ /* do not reply this using the O packet */
+ return;
+ }
+
+ char *t = allocPrintf(format, args);
+ if (t == NULL)
+ return;
+
+ gdb_output_con(connection, t);
+
+ free(t);
+}
+
+int gdb_input_inner(connection_t *connection)
+{
+ gdb_service_t *gdb_service = connection->service->priv;
+ target_t *target = gdb_service->target;
+ char packet[GDB_BUFFER_SIZE];
+ int packet_size;
+ int retval;
+ gdb_connection_t *gdb_con = connection->priv;
+ static int extended_protocol = 0;
+
+ /* drain input buffer */
+ do
+ {
+ packet_size = GDB_BUFFER_SIZE-1;
+ if ((retval = gdb_get_packet(connection, packet, &packet_size)) != ERROR_OK)
+ {
+ return retval;
+ }
+
+ /* terminate with zero */
+ packet[packet_size] = 0;
+
+ DEBUG("received packet: '%s'", packet);
+
+ if (packet_size > 0)
+ {
+ retval = ERROR_OK;
+ switch (packet[0])
+ {
+ case 'H':
+ /* Hct... -- set thread
+ * we don't have threads, send empty reply */
+ gdb_put_packet(connection, NULL, 0);
+ break;
+ case 'q':
+ retval = gdb_query_packet(connection, target, packet, packet_size);
+ break;
+ case 'g':
+ retval = gdb_get_registers_packet(connection, target, packet, packet_size);
+ break;
+ case 'G':
+ retval = gdb_set_registers_packet(connection, target, packet, packet_size);
+ break;
+ case 'p':
+ retval = gdb_get_register_packet(connection, target, packet, packet_size);
+ break;
+ case 'P':
+ retval = gdb_set_register_packet(connection, target, packet, packet_size);
+ break;
+ case 'm':
+ retval = gdb_read_memory_packet(connection, target, packet, packet_size);
+ break;
+ case 'M':
+ retval = gdb_write_memory_packet(connection, target, packet, packet_size);
+ break;
+ case 'z':
+ case 'Z':
+ retval = gdb_breakpoint_watchpoint_packet(connection, target, packet, packet_size);
+ break;
+ case '?':
+ gdb_last_signal_packet(connection, target, packet, packet_size);
+ break;
+ case 'c':
+ case 's':
+ {
+ /* We're running/stepping, in which case we can
+ * forward log output until the target is halted */
+ gdb_connection_t *gdb_con = connection->priv;
+ gdb_con->frontend_state = TARGET_RUNNING;
+ log_setCallback(gdb_log_callback, connection);
+ gdb_step_continue_packet(connection, target, packet, packet_size);
+ }
+ break;
+ case 'v':
+ retval = gdb_v_packet(connection, target, packet, packet_size);
+ break;
+ case 'D':
+ retval = gdb_detach(connection, target);
+ extended_protocol = 0;
+ break;
+ case 'X':
+ if ((retval = gdb_write_memory_binary_packet(connection, target, packet, packet_size)) != ERROR_OK)
+ return retval;
+ break;
+ case 'k':
+ if (extended_protocol != 0)
+ break;
+ gdb_put_packet(connection, "OK", 2);
+ return ERROR_SERVER_REMOTE_CLOSED;
+ case '!':
+ /* handle extended remote protocol */
+ extended_protocol = 1;
+ gdb_put_packet(connection, "OK", 2);
+ break;
+ case 'R':
+ /* handle extended restart packet */
+ target_process_reset(connection->cmd_ctx);
+ break;
+ default:
+ /* ignore unkown packets */
+ DEBUG("ignoring 0x%2.2x packet", packet[0]);
+ gdb_put_packet(connection, NULL, 0);
+ break;
+ }
+
+ /* if a packet handler returned an error, exit input loop */
+ if (retval != ERROR_OK)
+ return retval;
+ }
+
+ if (gdb_con->ctrl_c)
+ {
+ if (target->state == TARGET_RUNNING)
+ {
+ target->type->halt(target);
+ gdb_con->ctrl_c = 0;
+ }
+ }
+
+ } while (gdb_con->buf_cnt > 0);
+
+ return ERROR_OK;
+}
+
+int gdb_input(connection_t *connection)
+{
+ int retval = gdb_input_inner(connection);
+ if (retval == ERROR_SERVER_REMOTE_CLOSED)
+ return retval;
+ /* we'll recover from any other errors(e.g. temporary timeouts, etc.) */
+ return ERROR_OK;
+}
+
+int gdb_init()
+{
+ gdb_service_t *gdb_service;
+ target_t *target = targets;
+ int i = 0;
+
+ if (!target)
+ {
+ WARNING("no gdb ports allocated as no target has been specified");
+ return ERROR_OK;
+ }
+
+ if (gdb_port == 0)
+ {
+ WARNING("no gdb port specified, using default port 3333");
+ gdb_port = 3333;
+ }
+
+ while (target)
+ {
+ char service_name[8];
+
+ snprintf(service_name, 8, "gdb-%2.2i", i);
+
+ gdb_service = malloc(sizeof(gdb_service_t));
+ gdb_service->target = target;
+
+ add_service("gdb", CONNECTION_GDB, gdb_port + i, 1, gdb_new_connection, gdb_input, gdb_connection_closed, gdb_service);
+
+ DEBUG("gdb service for target %s at port %i", target->type->name, gdb_port + i);
+
+ i++;
+ target = target->next;
+ }
+
+ return ERROR_OK;
+}
+
+/* daemon configuration command gdb_port */
+int handle_gdb_port_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ if (argc == 0)
+ return ERROR_OK;
+
+ /* only if the port wasn't overwritten by cmdline */
+ if (gdb_port == 0)
+ gdb_port = strtoul(args[0], NULL, 0);
+
+ return ERROR_OK;
+}
+
+int handle_gdb_detach_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ if (argc == 1)
+ {
+ if (strcmp(args[0], "resume") == 0)
+ {
+ detach_mode = GDB_DETACH_RESUME;
+ return ERROR_OK;
+ }
+ else if (strcmp(args[0], "reset") == 0)
+ {
+ detach_mode = GDB_DETACH_RESET;
+ return ERROR_OK;
+ }
+ else if (strcmp(args[0], "halt") == 0)
+ {
+ detach_mode = GDB_DETACH_HALT;
+ return ERROR_OK;
+ }
+ else if (strcmp(args[0], "nothing") == 0)
+ {
+ detach_mode = GDB_DETACH_NOTHING;
+ return ERROR_OK;
+ }
+ }
+
+ WARNING("invalid gdb_detach configuration directive: %s", args[0]);
+ return ERROR_OK;
+}
+
+int handle_gdb_memory_map_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ if (argc == 1)
+ {
+ if (strcmp(args[0], "enable") == 0)
+ {
+ gdb_use_memory_map = 1;
+ return ERROR_OK;
+ }
+ else if (strcmp(args[0], "disable") == 0)
+ {
+ gdb_use_memory_map = 0;
+ return ERROR_OK;
+ }
+ }
+
+ WARNING("invalid gdb_memory_map configuration directive: %s", args[0]);
+ return ERROR_OK;
+}
+
+int handle_gdb_flash_program_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ if (argc == 1)
+ {
+ if (strcmp(args[0], "enable") == 0)
+ {
+ gdb_flash_program = 1;
+ return ERROR_OK;
+ }
+ else if (strcmp(args[0], "disable") == 0)
+ {
+ gdb_flash_program = 0;
+ return ERROR_OK;
+ }
+ }
+
+ WARNING("invalid gdb_memory_map configuration directive: %s", args[0]);
+ return ERROR_OK;
+}
+
+int handle_gdb_report_data_abort_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ if (argc == 1)
+ {
+ if (strcmp(args[0], "enable") == 0)
+ {
+ gdb_report_data_abort = 1;
+ return ERROR_OK;
+ }
+ else if (strcmp(args[0], "disable") == 0)
+ {
+ gdb_report_data_abort = 0;
+ return ERROR_OK;
+ }
+ }
+
+ WARNING("invalid gdb_report_data_abort configuration directive: %s", args[0]);
+ return ERROR_OK;
+}
+
+int gdb_register_commands(command_context_t *command_context)
+{
+ register_command(command_context, NULL, "gdb_port", handle_gdb_port_command,
+ COMMAND_CONFIG, "");
+ register_command(command_context, NULL, "gdb_detach", handle_gdb_detach_command,
+ COMMAND_CONFIG, "");
+ register_command(command_context, NULL, "gdb_memory_map", handle_gdb_memory_map_command,
+ COMMAND_CONFIG, "");
+ register_command(command_context, NULL, "gdb_flash_program", handle_gdb_flash_program_command,
+ COMMAND_CONFIG, "");
+ register_command(command_context, NULL, "gdb_report_data_abort", handle_gdb_report_data_abort_command,
+ COMMAND_CONFIG, "");
+ return ERROR_OK;
+}