diff options
-rw-r--r-- | src/flash/at91sam7.c | 1948 | ||||
-rw-r--r-- | src/flash/flash.c | 1890 | ||||
-rw-r--r-- | src/flash/flash.h | 194 | ||||
-rw-r--r-- | src/flash/lpc2000.c | 1364 | ||||
-rw-r--r-- | src/flash/lpc3180_nand_controller.c | 1830 | ||||
-rw-r--r-- | src/flash/nand.c | 3028 | ||||
-rw-r--r-- | src/flash/stellaris.c | 1870 | ||||
-rw-r--r-- | src/flash/stm32x.c | 1976 | ||||
-rw-r--r-- | src/flash/str7x.c | 1608 | ||||
-rw-r--r-- | src/flash/str9x.c | 1248 | ||||
-rw-r--r-- | src/flash/str9xpec.c | 2696 | ||||
-rw-r--r-- | src/helper/interpreter.c | 486 | ||||
-rw-r--r-- | src/server/gdb_server.c | 4214 |
13 files changed, 12176 insertions, 12176 deletions
diff --git a/src/flash/at91sam7.c b/src/flash/at91sam7.c index a9776d4..a1f055f 100644 --- a/src/flash/at91sam7.c +++ b/src/flash/at91sam7.c @@ -1,975 +1,975 @@ -/***************************************************************************
- * 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
- {
+/*************************************************************************** + * 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; - }
-
- /* 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;
-}
+ } + + /* 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; +} diff --git a/src/flash/flash.c b/src/flash/flash.c index 416cac4..2fefb4c 100644 --- a/src/flash/flash.c +++ b/src/flash/flash.c @@ -1,945 +1,945 @@ -/***************************************************************************
- * 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;
-}
+/*************************************************************************** + * 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; +} diff --git a/src/flash/flash.h b/src/flash/flash.h index e8262ef..b707b51 100644 --- a/src/flash/flash.h +++ b/src/flash/flash.h @@ -1,97 +1,97 @@ -/***************************************************************************
- * 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 */
+/*************************************************************************** + * 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 */ diff --git a/src/flash/lpc2000.c b/src/flash/lpc2000.c index 009c0c0..396f910 100644 --- a/src/flash/lpc2000.c +++ b/src/flash/lpc2000.c @@ -1,683 +1,683 @@ -/***************************************************************************
- * 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)
- {
+/*************************************************************************** + * 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; - }
-
- 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;
-}
+ } + + 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; +} diff --git a/src/flash/lpc3180_nand_controller.c b/src/flash/lpc3180_nand_controller.c index 4e1fd2e..2874f62 100644 --- a/src/flash/lpc3180_nand_controller.c +++ b/src/flash/lpc3180_nand_controller.c @@ -1,915 +1,915 @@ -/***************************************************************************
- * 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;
-}
+/*************************************************************************** + * 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; +} diff --git a/src/flash/nand.c b/src/flash/nand.c index b339442..8ce7cf0 100644 --- a/src/flash/nand.c +++ b/src/flash/nand.c @@ -1,1514 +1,1514 @@ -/***************************************************************************
- * 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;
-}
-
+/*************************************************************************** + * 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; +} + diff --git a/src/flash/stellaris.c b/src/flash/stellaris.c index 196730b..d8bd14e 100644 --- a/src/flash/stellaris.c +++ b/src/flash/stellaris.c @@ -1,935 +1,935 @@ -/***************************************************************************
- * 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);
-}
+/*************************************************************************** + * 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); +} diff --git a/src/flash/stm32x.c b/src/flash/stm32x.c index 0935c2c..f7dc103 100644 --- a/src/flash/stm32x.c +++ b/src/flash/stm32x.c @@ -1,988 +1,988 @@ -/***************************************************************************
- * 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;
-}
+/*************************************************************************** + * 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; +} diff --git a/src/flash/str7x.c b/src/flash/str7x.c index 21122d0..3574a89 100644 --- a/src/flash/str7x.c +++ b/src/flash/str7x.c @@ -1,804 +1,804 @@ -/***************************************************************************
- * 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;
-}
-
+/*************************************************************************** + * 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; +} + diff --git a/src/flash/str9x.c b/src/flash/str9x.c index f5fe2e9..a34d40a 100644 --- a/src/flash/str9x.c +++ b/src/flash/str9x.c @@ -1,624 +1,624 @@ -/***************************************************************************
- * 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;
-}
+/*************************************************************************** + * 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; +} diff --git a/src/flash/str9xpec.c b/src/flash/str9xpec.c index 4d3093e..4959df9 100644 --- a/src/flash/str9xpec.c +++ b/src/flash/str9xpec.c @@ -1,1348 +1,1348 @@ -/***************************************************************************
- * 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;
-}
+/*************************************************************************** + * 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; +} diff --git a/src/helper/interpreter.c b/src/helper/interpreter.c index cfda856..aa331fd 100644 --- a/src/helper/interpreter.c +++ b/src/helper/interpreter.c @@ -1,243 +1,243 @@ -/***************************************************************************
- * 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;
-}
+/*************************************************************************** + * 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; +} diff --git a/src/server/gdb_server.c b/src/server/gdb_server.c index b4a7982..4963057 100644 --- a/src/server/gdb_server.c +++ b/src/server/gdb_server.c @@ -1,2107 +1,2107 @@ -/***************************************************************************
- * 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;
-}
+/*************************************************************************** + * 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; +} |