From fe9228a32db930be297d4d3b35ffb94d6de1b9ab Mon Sep 17 00:00:00 2001 From: Zachary T Welch Date: Wed, 2 Dec 2009 15:54:15 -0800 Subject: move nor drivers to src/flash/nor Moves NOR flash drivers to 'src/flash/nor/'. Adds 'src/flash/nor/Makefile.am'. Builds 'libocdflashnor.la'. --- configure.in | 1 + src/flash/Makefile.am | 59 +- src/flash/aduc702x.c | 425 -------- src/flash/at91sam3.c | 2516 ------------------------------------------- src/flash/at91sam3.h | 23 - src/flash/at91sam7.c | 1213 --------------------- src/flash/at91sam7.h | 118 -- src/flash/avrf.c | 483 --------- src/flash/avrf.h | 41 - src/flash/cfi.c | 2630 --------------------------------------------- src/flash/cfi.h | 164 --- src/flash/ecos.c | 444 -------- src/flash/faux.c | 149 --- src/flash/lpc2000.c | 812 -------------- src/flash/lpc2000.h | 73 -- src/flash/lpc288x.c | 485 --------- src/flash/lpc288x.h | 39 - src/flash/lpc2900.c | 1834 ------------------------------- src/flash/non_cfi.c | 491 --------- src/flash/non_cfi.h | 40 - src/flash/nor/Makefile.am | 46 + src/flash/nor/aduc702x.c | 425 ++++++++ src/flash/nor/at91sam3.c | 2516 +++++++++++++++++++++++++++++++++++++++++++ src/flash/nor/at91sam3.h | 23 + src/flash/nor/at91sam7.c | 1213 +++++++++++++++++++++ src/flash/nor/at91sam7.h | 118 ++ src/flash/nor/avrf.c | 483 +++++++++ src/flash/nor/avrf.h | 41 + src/flash/nor/cfi.c | 2630 +++++++++++++++++++++++++++++++++++++++++++++ src/flash/nor/cfi.h | 164 +++ src/flash/nor/ecos.c | 444 ++++++++ src/flash/nor/faux.c | 149 +++ src/flash/nor/lpc2000.c | 812 ++++++++++++++ src/flash/nor/lpc2000.h | 73 ++ src/flash/nor/lpc288x.c | 485 +++++++++ src/flash/nor/lpc288x.h | 39 + src/flash/nor/lpc2900.c | 1834 +++++++++++++++++++++++++++++++ src/flash/nor/non_cfi.c | 491 +++++++++ src/flash/nor/non_cfi.h | 40 + src/flash/nor/ocl.c | 361 +++++++ src/flash/nor/ocl.h | 40 + src/flash/nor/pic32mx.c | 922 ++++++++++++++++ src/flash/nor/pic32mx.h | 113 ++ src/flash/nor/stellaris.c | 1195 ++++++++++++++++++++ src/flash/nor/stellaris.h | 99 ++ src/flash/nor/stm32x.c | 1240 +++++++++++++++++++++ src/flash/nor/stm32x.h | 101 ++ src/flash/nor/str7x.c | 706 ++++++++++++ src/flash/nor/str7x.h | 110 ++ src/flash/nor/str9x.c | 711 ++++++++++++ src/flash/nor/str9x.h | 62 ++ src/flash/nor/str9xpec.c | 1257 ++++++++++++++++++++++ src/flash/nor/str9xpec.h | 79 ++ src/flash/nor/tms470.c | 1271 ++++++++++++++++++++++ src/flash/nor/tms470.h | 39 + src/flash/ocl.c | 361 ------- src/flash/ocl.h | 40 - src/flash/pic32mx.c | 922 ---------------- src/flash/pic32mx.h | 113 -- src/flash/stellaris.c | 1195 -------------------- src/flash/stellaris.h | 99 -- src/flash/stm32x.c | 1240 --------------------- src/flash/stm32x.h | 101 -- src/flash/str7x.c | 706 ------------ src/flash/str7x.h | 110 -- src/flash/str9x.c | 711 ------------ src/flash/str9x.h | 62 -- src/flash/str9xpec.c | 1257 ---------------------- src/flash/str9xpec.h | 79 -- src/flash/tms470.c | 1271 ---------------------- src/flash/tms470.h | 39 - 71 files changed, 20342 insertions(+), 20336 deletions(-) delete mode 100644 src/flash/aduc702x.c delete mode 100644 src/flash/at91sam3.c delete mode 100644 src/flash/at91sam3.h delete mode 100644 src/flash/at91sam7.c delete mode 100644 src/flash/at91sam7.h delete mode 100644 src/flash/avrf.c delete mode 100644 src/flash/avrf.h delete mode 100644 src/flash/cfi.c delete mode 100644 src/flash/cfi.h delete mode 100644 src/flash/ecos.c delete mode 100644 src/flash/faux.c delete mode 100644 src/flash/lpc2000.c delete mode 100644 src/flash/lpc2000.h delete mode 100644 src/flash/lpc288x.c delete mode 100644 src/flash/lpc288x.h delete mode 100644 src/flash/lpc2900.c delete mode 100644 src/flash/non_cfi.c delete mode 100644 src/flash/non_cfi.h create mode 100644 src/flash/nor/Makefile.am create mode 100644 src/flash/nor/aduc702x.c create mode 100644 src/flash/nor/at91sam3.c create mode 100644 src/flash/nor/at91sam3.h create mode 100644 src/flash/nor/at91sam7.c create mode 100644 src/flash/nor/at91sam7.h create mode 100644 src/flash/nor/avrf.c create mode 100644 src/flash/nor/avrf.h create mode 100644 src/flash/nor/cfi.c create mode 100644 src/flash/nor/cfi.h create mode 100644 src/flash/nor/ecos.c create mode 100644 src/flash/nor/faux.c create mode 100644 src/flash/nor/lpc2000.c create mode 100644 src/flash/nor/lpc2000.h create mode 100644 src/flash/nor/lpc288x.c create mode 100644 src/flash/nor/lpc288x.h create mode 100644 src/flash/nor/lpc2900.c create mode 100644 src/flash/nor/non_cfi.c create mode 100644 src/flash/nor/non_cfi.h create mode 100644 src/flash/nor/ocl.c create mode 100644 src/flash/nor/ocl.h create mode 100644 src/flash/nor/pic32mx.c create mode 100644 src/flash/nor/pic32mx.h create mode 100644 src/flash/nor/stellaris.c create mode 100644 src/flash/nor/stellaris.h create mode 100644 src/flash/nor/stm32x.c create mode 100644 src/flash/nor/stm32x.h create mode 100644 src/flash/nor/str7x.c create mode 100644 src/flash/nor/str7x.h create mode 100644 src/flash/nor/str9x.c create mode 100644 src/flash/nor/str9x.h create mode 100644 src/flash/nor/str9xpec.c create mode 100644 src/flash/nor/str9xpec.h create mode 100644 src/flash/nor/tms470.c create mode 100644 src/flash/nor/tms470.h delete mode 100644 src/flash/ocl.c delete mode 100644 src/flash/ocl.h delete mode 100644 src/flash/pic32mx.c delete mode 100644 src/flash/pic32mx.h delete mode 100644 src/flash/stellaris.c delete mode 100644 src/flash/stellaris.h delete mode 100644 src/flash/stm32x.c delete mode 100644 src/flash/stm32x.h delete mode 100644 src/flash/str7x.c delete mode 100644 src/flash/str7x.h delete mode 100644 src/flash/str9x.c delete mode 100644 src/flash/str9x.h delete mode 100644 src/flash/str9xpec.c delete mode 100644 src/flash/str9xpec.h delete mode 100644 src/flash/tms470.c delete mode 100644 src/flash/tms470.h diff --git a/configure.in b/configure.in index 3680cac5..7b2b4af3 100644 --- a/configure.in +++ b/configure.in @@ -1122,6 +1122,7 @@ AC_OUTPUT(dnl src/target/Makefile dnl src/server/Makefile dnl src/flash/Makefile dnl + src/flash/nor/Makefile dnl src/flash/nand/Makefile dnl src/pld/Makefile dnl doc/Makefile dnl diff --git a/src/flash/Makefile.am b/src/flash/Makefile.am index 353fcf16..54a5116e 100644 --- a/src/flash/Makefile.am +++ b/src/flash/Makefile.am @@ -1,4 +1,5 @@ SUBDIRS = \ + nor \ nand AM_CPPFLAGS = \ @@ -9,66 +10,24 @@ AM_CPPFLAGS = \ METASOURCES = AUTO noinst_LTLIBRARIES = libflash.la libflash_la_SOURCES = \ - $(FLASH_SRCS) \ - $(NAND_SRCS) \ - mflash.c - -libflash_la_LIBADD = \ - $(top_builddir)/src/flash/nand/libocdflashnand.la - -FLASH_SRCS = \ common.c \ - cfi.c \ - non_cfi.c \ - faux.c \ - $(FLASH_DEVICES_SRCS) \ - flash.c - -FLASH_DEVICES_SRCS = \ - aduc702x.c \ - at91sam3.c \ - at91sam7.c \ - avrf.c \ - ecos.c \ - lpc2000.c \ - lpc288x.c \ - lpc2900.c \ - ocl.c \ - pic32mx.c \ - stellaris.c \ - stm32x.c \ - str7x.c \ - str9x.c \ - str9xpec.c \ - tms470.c - -NAND_SRCS = \ + flash.c \ arm_nandio.c \ nand_ecc.c \ nand_ecc_kw.c \ - nand.c + nand.c \ + mflash.c + +libflash_la_LIBADD = \ + $(top_builddir)/src/flash/nor/libocdflashnor.la \ + $(top_builddir)/src/flash/nand/libocdflashnand.la noinst_HEADERS = \ arm_nandio.h \ - at91sam7.h \ - at91sam3.h \ - avrf.h \ - cfi.h \ common.h \ flash.h \ - lpc2000.h \ - lpc288x.h \ mflash.h \ - non_cfi.h \ - nand.h \ - ocl.h \ - pic32mx.h \ - stellaris.h \ - stm32x.h \ - str7x.h \ - str9x.h \ - str9xpec.h \ - tms470.h + nand.h EXTRA_DIST = startup.tcl diff --git a/src/flash/aduc702x.c b/src/flash/aduc702x.c deleted file mode 100644 index 643705ca..00000000 --- a/src/flash/aduc702x.c +++ /dev/null @@ -1,425 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2008 by Kevin McGuire * - * Copyright (C) 2008 by Marcel Wijlaars * - * Copyright (C) 2009 by Michael Ashton * - * * - * 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 "armv4_5.h" -#include "binarybuffer.h" -#include "time_support.h" -#include "algorithm.h" - - -static int aduc702x_build_sector_list(struct flash_bank *bank); -static int aduc702x_check_flash_completion(struct target* target, unsigned int timeout_ms); -static int aduc702x_set_write_enable(struct target *target, int enable); - -#define ADUC702x_FLASH 0xfffff800 -#define ADUC702x_FLASH_FEESTA (0*4) -#define ADUC702x_FLASH_FEEMOD (1*4) -#define ADUC702x_FLASH_FEECON (2*4) -#define ADUC702x_FLASH_FEEDAT (3*4) -#define ADUC702x_FLASH_FEEADR (4*4) -#define ADUC702x_FLASH_FEESIGN (5*4) -#define ADUC702x_FLASH_FEEPRO (6*4) -#define ADUC702x_FLASH_FEEHIDE (7*4) - -struct aduc702x_flash_bank { - struct working_area *write_algorithm; -}; - -/* flash bank aduc702x 0 0 0 0 - * The ADC7019-28 devices all have the same flash layout */ -FLASH_BANK_COMMAND_HANDLER(aduc702x_flash_bank_command) -{ - struct aduc702x_flash_bank *nbank; - - nbank = malloc(sizeof(struct aduc702x_flash_bank)); - - bank->base = 0x80000; - bank->size = 0xF800; // top 4k not accessible - bank->driver_priv = nbank; - - aduc702x_build_sector_list(bank); - - return ERROR_OK; -} - -static int aduc702x_build_sector_list(struct flash_bank *bank) -{ - //aduc7026_struct flash_bank *aduc7026_info = bank->driver_priv; - - int i = 0; - uint32_t offset = 0; - - // sector size is 512 - bank->num_sectors = bank->size / 512; - bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors); - for (i = 0; i < bank->num_sectors; ++i) - { - bank->sectors[i].offset = offset; - bank->sectors[i].size = 512; - offset += bank->sectors[i].size; - bank->sectors[i].is_erased = -1; - bank->sectors[i].is_protected = 0; - } - - return ERROR_OK; -} - -static int aduc702x_protect_check(struct flash_bank *bank) -{ - printf("aduc702x_protect_check not implemented yet.\n"); - return ERROR_OK; -} - -static int aduc702x_erase(struct flash_bank *bank, int first, int last) -{ - //int res; - int x; - int count; - //uint32_t v; - struct target *target = bank->target; - - aduc702x_set_write_enable(target, 1); - - /* mass erase */ - if (((first | last) == 0) || ((first == 0) && (last >= bank->num_sectors))) { - LOG_DEBUG("performing mass erase.\n"); - target_write_u16(target, ADUC702x_FLASH + ADUC702x_FLASH_FEEDAT, 0x3cff); - target_write_u16(target, ADUC702x_FLASH + ADUC702x_FLASH_FEEADR, 0xffc3); - target_write_u8(target, ADUC702x_FLASH + ADUC702x_FLASH_FEECON, 0x06); - - if (aduc702x_check_flash_completion(target, 3500) != ERROR_OK) - { - LOG_ERROR("mass erase failed\n"); - aduc702x_set_write_enable(target, 0); - return ERROR_FLASH_OPERATION_FAILED; - } - - LOG_DEBUG("mass erase successful.\n"); - return ERROR_OK; - } else { - unsigned long adr; - - count = last - first + 1; - for (x = 0; x < count; ++x) - { - adr = bank->base + ((first + x) * 512); - - target_write_u16(target, ADUC702x_FLASH + ADUC702x_FLASH_FEEADR, adr); - target_write_u8(target, ADUC702x_FLASH + ADUC702x_FLASH_FEECON, 0x05); - - if (aduc702x_check_flash_completion(target, 50) != ERROR_OK) - { - LOG_ERROR("failed to erase sector at address 0x%08lX\n", adr); - aduc702x_set_write_enable(target, 0); - return ERROR_FLASH_SECTOR_NOT_ERASED; - } - - LOG_DEBUG("erased sector at address 0x%08lX\n", adr); - } - } - - aduc702x_set_write_enable(target, 0); - - return ERROR_OK; -} - -static int aduc702x_protect(struct flash_bank *bank, int set, int first, int last) -{ - printf("aduc702x_protect not implemented yet.\n"); - return ERROR_FLASH_OPERATION_FAILED; -} - -/* If this fn returns ERROR_TARGET_RESOURCE_NOT_AVAILABLE, then the caller can fall - * back to another mechanism that does not require onboard RAM - * - * Caller should not check for other return values specifically - */ -static int aduc702x_write_block(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) -{ - struct aduc702x_flash_bank *aduc702x_info = bank->driver_priv; - struct target *target = bank->target; - uint32_t buffer_size = 7000; - struct working_area *source; - uint32_t address = bank->base + offset; - struct reg_param reg_params[6]; - struct armv4_5_algorithm armv4_5_info; - int retval = ERROR_OK; - - if (((count%2)!=0)||((offset%2)!=0)) - { - LOG_ERROR("write block must be multiple of two bytes in offset & length"); - return ERROR_FAIL; - } - - /* parameters: - - r0 - address of source data (absolute) - r1 - number of halfwords to be copied - r2 - start address in flash (offset from beginning of flash memory) - r3 - exit code - r4 - base address of flash controller (0xFFFFF800) - - registers: - - r5 - scratch - r6 - set to 2, used to write flash command - - */ - uint32_t aduc702x_flash_write_code[] = { - //<_start>: - 0xe3a05008, // mov r5, #8 ; 0x8 - 0xe5845004, // str r5, [r4, #4] - 0xe3a06002, // mov r6, #2 ; 0x2 - //: - 0xe1c421b0, // strh r2, [r4, #16] - 0xe0d050b2, // ldrh r5, [r0], #2 - 0xe1c450bc, // strh r5, [r4, #12] - 0xe5c46008, // strb r6, [r4, #8] - //: - 0xe1d430b0, // ldrh r3, [r4] - 0xe3130004, // tst r3, #4 ; 0x4 - 0x1afffffc, // bne 1001c - 0xe2822002, // add r2, r2, #2 ; 0x2 - 0xe2511001, // subs r1, r1, #1 ; 0x1 - 0x0a000001, // beq 1003c - 0xe3130001, // tst r3, #1 ; 0x1 - 0x1afffff3, // bne 1000c - //: - 0xeafffffe // b 1003c - }; - - /* flash write code */ - if (target_alloc_working_area(target, sizeof(aduc702x_flash_write_code), - &aduc702x_info->write_algorithm) != ERROR_OK) - { - LOG_WARNING("no working area available, can't do block memory writes"); - return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; - }; - - retval=target_write_buffer(target, aduc702x_info->write_algorithm->address, - sizeof(aduc702x_flash_write_code), (uint8_t*)aduc702x_flash_write_code); - if (retval!=ERROR_OK) - { - return retval; - } - - /* 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 (aduc702x_info->write_algorithm) - target_free_working_area(target, aduc702x_info->write_algorithm); - - LOG_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); - init_reg_param(®_params[4], "r4", 32, PARAM_OUT); - - while (count > 0) - { - uint32_t thisrun_count = (count > buffer_size) ? buffer_size : count; - - retval=target_write_buffer(target, source->address, thisrun_count, buffer); - if (retval!=ERROR_OK) - { - break; - } - - buf_set_u32(reg_params[0].value, 0, 32, source->address); - buf_set_u32(reg_params[1].value, 0, 32, thisrun_count/2); - buf_set_u32(reg_params[2].value, 0, 32, address); - buf_set_u32(reg_params[4].value, 0, 32, 0xFFFFF800); - - if ((retval = target_run_algorithm(target, 0, NULL, 5, - reg_params, aduc702x_info->write_algorithm->address, - aduc702x_info->write_algorithm->address + sizeof(aduc702x_flash_write_code) - 4, - 10000, &armv4_5_info)) != ERROR_OK) - { - LOG_ERROR("error executing aduc702x flash write algorithm"); - break; - } - - if ((buf_get_u32(reg_params[3].value, 0, 32) & 1) != 1) - { - /* FIX!!!! what does this mean??? replace w/sensible error message */ - LOG_ERROR("aduc702x detected error writing flash"); - retval = ERROR_FAIL; - break; - } - - buffer += thisrun_count; - address += thisrun_count; - count -= thisrun_count; - } - - target_free_working_area(target, source); - target_free_working_area(target, aduc702x_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]); - - return retval; -} - -/* All-JTAG, single-access method. Very slow. Used only if there is no - * working area available. */ -static int aduc702x_write_single(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) -{ - uint32_t x; - uint8_t b; - struct target *target = bank->target; - - aduc702x_set_write_enable(target, 1); - - for (x = 0; x < count; x += 2) { - // FEEADR = address - target_write_u16(target, ADUC702x_FLASH + ADUC702x_FLASH_FEEADR, offset + x); - - // set up data - if ((x + 1) == count) - { - // last byte - target_read_u8(target, offset + x + 1, &b); - } - else - b = buffer[x + 1]; - - target_write_u16(target, ADUC702x_FLASH + ADUC702x_FLASH_FEEDAT, buffer[x] | (b << 8)); - - // do single-write command - target_write_u8(target, ADUC702x_FLASH + ADUC702x_FLASH_FEECON, 0x02); - - if (aduc702x_check_flash_completion(target, 1) != ERROR_OK) - { - LOG_ERROR("single write failed for address 0x%08lX\n", (unsigned long)(offset + x)); - aduc702x_set_write_enable(target, 0); - return ERROR_FLASH_OPERATION_FAILED; - } - - } - LOG_DEBUG("wrote %d bytes at address 0x%08lX\n", (int)count, (unsigned long)(offset + x)); - - aduc702x_set_write_enable(target, 0); - - return ERROR_OK; -} - -int aduc702x_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) -{ - int retval; - - /* try using a block write */ - if ((retval = aduc702x_write_block(bank, buffer, offset, count)) != ERROR_OK) - { - if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) - { - /* if block write failed (no sufficient working area), - * use normal (slow) JTAG method */ - LOG_WARNING("couldn't use block writes, falling back to single memory accesses"); - - if ((retval = aduc702x_write_single(bank, buffer, offset, count)) != ERROR_OK) - { - LOG_ERROR("slow write failed"); - return ERROR_FLASH_OPERATION_FAILED; - } - } - } - - return retval; -} - -static int aduc702x_probe(struct flash_bank *bank) -{ - return ERROR_OK; -} - -static int aduc702x_info(struct flash_bank *bank, char *buf, int buf_size) -{ - snprintf(buf, buf_size, "aduc702x flash driver info"); - return ERROR_OK; -} - -/* sets FEEMOD bit 3 - * enable = 1 enables writes & erases, 0 disables them */ -static int aduc702x_set_write_enable(struct target *target, int enable) -{ - // don't bother to preserve int enable bit here - target_write_u16(target, ADUC702x_FLASH + ADUC702x_FLASH_FEEMOD, enable ? 8 : 0); - - return ERROR_OK; -} - -/* wait up to timeout_ms for controller to not be busy, - * then check whether the command passed or failed. - * - * this function sleeps 1ms between checks (after the first one), - * so in some cases may slow things down without a usleep after the first read */ -static int aduc702x_check_flash_completion(struct target* target, unsigned int timeout_ms) -{ - uint8_t v = 4; - - long long endtime = timeval_ms() + timeout_ms; - while (1) { - target_read_u8(target, ADUC702x_FLASH + ADUC702x_FLASH_FEESTA, &v); - if ((v & 4) == 0) break; - alive_sleep(1); - if (timeval_ms() >= endtime) break; - } - - if (v & 2) return ERROR_FAIL; - // if a command is ignored, both the success and fail bits may be 0 - else if ((v & 3) == 0) return ERROR_FAIL; - else return ERROR_OK; -} - -struct flash_driver aduc702x_flash = { - .name = "aduc702x", - .flash_bank_command = &aduc702x_flash_bank_command, - .erase = &aduc702x_erase, - .protect = &aduc702x_protect, - .write = &aduc702x_write, - .probe = &aduc702x_probe, - .auto_probe = &aduc702x_probe, - .erase_check = &default_flash_blank_check, - .protect_check = &aduc702x_protect_check, - .info = &aduc702x_info - }; diff --git a/src/flash/at91sam3.c b/src/flash/at91sam3.c deleted file mode 100644 index be17a5f8..00000000 --- a/src/flash/at91sam3.c +++ /dev/null @@ -1,2516 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2009 by Duane Ellis * - * openocd@duaneellis.com * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS for A PARTICULAR PURPOSE. See the * - * GNU General public License for more details. * - * * - * You should have received a copy of the GNU General public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * -****************************************************************************/ - -/* Some of the the lower level code was based on code supplied by - * ATMEL under this copyright. */ - -/* BEGIN ATMEL COPYRIGHT */ -/* ---------------------------------------------------------------------------- - * ATMEL Microcontroller Software Support - * ---------------------------------------------------------------------------- - * Copyright (c) 2009, Atmel Corporation - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * - Redistributions of source code must retain the above copyright notice, - * this list of conditions and the disclaimer below. - * - * Atmel's name may not be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE - * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, - * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * ---------------------------------------------------------------------------- - */ -/* END ATMEL COPYRIGHT */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - - -#include -#include -#include -#include "types.h" -#include "flash.h" -#include "membuf.h" -#include "at91sam3.h" -#include "time_support.h" - -#define REG_NAME_WIDTH (12) - - -#define FLASH_BANK0_BASE 0x00080000 -#define FLASH_BANK1_BASE 0x00100000 - -#define AT91C_EFC_FCMD_GETD (0x0) // (EFC) Get Flash Descriptor -#define AT91C_EFC_FCMD_WP (0x1) // (EFC) Write Page -#define AT91C_EFC_FCMD_WPL (0x2) // (EFC) Write Page and Lock -#define AT91C_EFC_FCMD_EWP (0x3) // (EFC) Erase Page and Write Page -#define AT91C_EFC_FCMD_EWPL (0x4) // (EFC) Erase Page and Write Page then Lock -#define AT91C_EFC_FCMD_EA (0x5) // (EFC) Erase All -// cmd6 is not present int he at91sam3u4/2/1 data sheet table 17-2 -// #define AT91C_EFC_FCMD_EPL (0x6) // (EFC) Erase plane? -// cmd7 is not present int he at91sam3u4/2/1 data sheet table 17-2 -// #define AT91C_EFC_FCMD_EPA (0x7) // (EFC) Erase pages? -#define AT91C_EFC_FCMD_SLB (0x8) // (EFC) Set Lock Bit -#define AT91C_EFC_FCMD_CLB (0x9) // (EFC) Clear Lock Bit -#define AT91C_EFC_FCMD_GLB (0xA) // (EFC) Get Lock Bit -#define AT91C_EFC_FCMD_SFB (0xB) // (EFC) Set Fuse Bit -#define AT91C_EFC_FCMD_CFB (0xC) // (EFC) Clear Fuse Bit -#define AT91C_EFC_FCMD_GFB (0xD) // (EFC) Get Fuse Bit -#define AT91C_EFC_FCMD_STUI (0xE) // (EFC) Start Read Unique ID -#define AT91C_EFC_FCMD_SPUI (0xF) // (EFC) Stop Read Unique ID - -#define offset_EFC_FMR 0 -#define offset_EFC_FCR 4 -#define offset_EFC_FSR 8 -#define offset_EFC_FRR 12 - - -static float -_tomhz(uint32_t freq_hz) -{ - float f; - - f = ((float)(freq_hz)) / 1000000.0; - return f; -} - -// How the chip is configured. -struct sam3_cfg { - uint32_t unique_id[4]; - - uint32_t slow_freq; - uint32_t rc_freq; - uint32_t mainosc_freq; - uint32_t plla_freq; - uint32_t mclk_freq; - uint32_t cpu_freq; - uint32_t fclk_freq; - uint32_t pclk0_freq; - uint32_t pclk1_freq; - uint32_t pclk2_freq; - - -#define SAM3_CHIPID_CIDR (0x400E0740) - uint32_t CHIPID_CIDR; -#define SAM3_CHIPID_EXID (0x400E0744) - uint32_t CHIPID_EXID; - -#define SAM3_SUPC_CR (0x400E1210) - uint32_t SUPC_CR; - -#define SAM3_PMC_BASE (0x400E0400) -#define SAM3_PMC_SCSR (SAM3_PMC_BASE + 0x0008) - uint32_t PMC_SCSR; -#define SAM3_PMC_PCSR (SAM3_PMC_BASE + 0x0018) - uint32_t PMC_PCSR; -#define SAM3_CKGR_UCKR (SAM3_PMC_BASE + 0x001c) - uint32_t CKGR_UCKR; -#define SAM3_CKGR_MOR (SAM3_PMC_BASE + 0x0020) - uint32_t CKGR_MOR; -#define SAM3_CKGR_MCFR (SAM3_PMC_BASE + 0x0024) - uint32_t CKGR_MCFR; -#define SAM3_CKGR_PLLAR (SAM3_PMC_BASE + 0x0028) - uint32_t CKGR_PLLAR; -#define SAM3_PMC_MCKR (SAM3_PMC_BASE + 0x0030) - uint32_t PMC_MCKR; -#define SAM3_PMC_PCK0 (SAM3_PMC_BASE + 0x0040) - uint32_t PMC_PCK0; -#define SAM3_PMC_PCK1 (SAM3_PMC_BASE + 0x0044) - uint32_t PMC_PCK1; -#define SAM3_PMC_PCK2 (SAM3_PMC_BASE + 0x0048) - uint32_t PMC_PCK2; -#define SAM3_PMC_SR (SAM3_PMC_BASE + 0x0068) - uint32_t PMC_SR; -#define SAM3_PMC_IMR (SAM3_PMC_BASE + 0x006c) - uint32_t PMC_IMR; -#define SAM3_PMC_FSMR (SAM3_PMC_BASE + 0x0070) - uint32_t PMC_FSMR; -#define SAM3_PMC_FSPR (SAM3_PMC_BASE + 0x0074) - uint32_t PMC_FSPR; -}; - - -struct sam3_bank_private { - int probed; - // DANGER: THERE ARE DRAGONS HERE.. - // NOTE: If you add more 'ghost' pointers - // be aware that you must *manually* update - // these pointers in the function sam3_GetDetails() - // See the comment "Here there be dragons" - - // so we can find the chip we belong to - struct sam3_chip *pChip; - // so we can find the orginal bank pointer - struct flash_bank *pBank; - unsigned bank_number; - uint32_t controller_address; - uint32_t base_address; - bool present; - unsigned size_bytes; - unsigned nsectors; - unsigned sector_size; - unsigned page_size; -}; - -struct sam3_chip_details { - // THERE ARE DRAGONS HERE.. - // note: If you add pointers here - // becareful about them as they - // may need to be updated inside - // the function: "sam3_GetDetails() - // which copy/overwrites the - // 'runtime' copy of this structure - uint32_t chipid_cidr; - const char *name; - - unsigned n_gpnvms; -#define SAM3_N_NVM_BITS 3 - unsigned gpnvm[SAM3_N_NVM_BITS]; - unsigned total_flash_size; - unsigned total_sram_size; - unsigned n_banks; -#define SAM3_MAX_FLASH_BANKS 2 - // these are "initialized" from the global const data - struct sam3_bank_private bank[SAM3_MAX_FLASH_BANKS]; -}; - - -struct sam3_chip { - struct sam3_chip *next; - int probed; - - // this is "initialized" from the global const structure - struct sam3_chip_details details; - struct target *target; - struct sam3_cfg cfg; - - struct membuf *mbuf; -}; - - -struct sam3_reg_list { - uint32_t address; size_t struct_offset; const char *name; - void (*explain_func)(struct sam3_chip *pInfo); -}; - - -static struct sam3_chip *all_sam3_chips; - -static struct sam3_chip * -get_current_sam3(struct command_context *cmd_ctx) -{ - struct target *t; - static struct sam3_chip *p; - - t = get_current_target(cmd_ctx); - if (!t) { - command_print(cmd_ctx, "No current target?"); - return NULL; - } - - p = all_sam3_chips; - if (!p) { - // this should not happen - // the command is not registered until the chip is created? - command_print(cmd_ctx, "No SAM3 chips exist?"); - return NULL; - } - - while (p) { - if (p->target == t) { - return p; - } - p = p->next; - } - command_print(cmd_ctx, "Cannot find SAM3 chip?"); - return NULL; -} - - -// these are used to *initialize* the "pChip->details" structure. -static const struct sam3_chip_details all_sam3_details[] = { - { - .chipid_cidr = 0x28100960, - .name = "at91sam3u4e", - .total_flash_size = 256 * 1024, - .total_sram_size = 52 * 1024, - .n_gpnvms = 3, - .n_banks = 2, - - // System boots at address 0x0 - // gpnvm[1] = selects boot code - // if gpnvm[1] == 0 - // boot is via "SAMBA" (rom) - // else - // boot is via FLASH - // Selection is via gpnvm[2] - // endif - // - // NOTE: banks 0 & 1 switch places - // if gpnvm[2] == 0 - // Bank0 is the boot rom - // else - // Bank1 is the boot rom - // endif -// .bank[0] = { - { - { - .probed = 0, - .pChip = NULL, - .pBank = NULL, - .bank_number = 0, - .base_address = FLASH_BANK0_BASE, - .controller_address = 0x400e0800, - .present = 1, - .size_bytes = 128 * 1024, - .nsectors = 16, - .sector_size = 8192, - .page_size = 256, - }, - -// .bank[1] = { - { - .probed = 0, - .pChip = NULL, - .pBank = NULL, - .bank_number = 1, - .base_address = FLASH_BANK1_BASE, - .controller_address = 0x400e0a00, - .present = 1, - .size_bytes = 128 * 1024, - .nsectors = 16, - .sector_size = 8192, - .page_size = 256, - }, - }, - }, - - { - .chipid_cidr = 0x281a0760, - .name = "at91sam3u2e", - .total_flash_size = 128 * 1024, - .total_sram_size = 36 * 1024, - .n_gpnvms = 2, - .n_banks = 1, - - // System boots at address 0x0 - // gpnvm[1] = selects boot code - // if gpnvm[1] == 0 - // boot is via "SAMBA" (rom) - // else - // boot is via FLASH - // Selection is via gpnvm[2] - // endif -// .bank[0] = { - { - { - .probed = 0, - .pChip = NULL, - .pBank = NULL, - .bank_number = 0, - .base_address = FLASH_BANK0_BASE, - .controller_address = 0x400e0800, - .present = 1, - .size_bytes = 128 * 1024, - .nsectors = 16, - .sector_size = 8192, - .page_size = 256, - }, -// .bank[1] = { - { - .present = 0, - .probed = 0, - .bank_number = 1, - }, - }, - }, - { - .chipid_cidr = 0x28190560, - .name = "at91sam3u1e", - .total_flash_size = 64 * 1024, - .total_sram_size = 20 * 1024, - .n_gpnvms = 2, - .n_banks = 1, - - // System boots at address 0x0 - // gpnvm[1] = selects boot code - // if gpnvm[1] == 0 - // boot is via "SAMBA" (rom) - // else - // boot is via FLASH - // Selection is via gpnvm[2] - // endif - // - -// .bank[0] = { - { - { - .probed = 0, - .pChip = NULL, - .pBank = NULL, - .bank_number = 0, - .base_address = FLASH_BANK0_BASE, - .controller_address = 0x400e0800, - .present = 1, - .size_bytes = 64 * 1024, - .nsectors = 8, - .sector_size = 8192, - .page_size = 256, - }, - -// .bank[1] = { - { - .present = 0, - .probed = 0, - .bank_number = 1, - }, - }, - }, - - { - .chipid_cidr = 0x28000960, - .name = "at91sam3u4c", - .total_flash_size = 256 * 1024, - .total_sram_size = 52 * 1024, - .n_gpnvms = 3, - .n_banks = 2, - - // System boots at address 0x0 - // gpnvm[1] = selects boot code - // if gpnvm[1] == 0 - // boot is via "SAMBA" (rom) - // else - // boot is via FLASH - // Selection is via gpnvm[2] - // endif - // - // NOTE: banks 0 & 1 switch places - // if gpnvm[2] == 0 - // Bank0 is the boot rom - // else - // Bank1 is the boot rom - // endif - { - { -// .bank[0] = { - .probed = 0, - .pChip = NULL, - .pBank = NULL, - .bank_number = 0, - .base_address = FLASH_BANK0_BASE, - .controller_address = 0x400e0800, - .present = 1, - .size_bytes = 128 * 1024, - .nsectors = 16, - .sector_size = 8192, - .page_size = 256, - }, -// .bank[1] = { - { - .probed = 0, - .pChip = NULL, - .pBank = NULL, - .bank_number = 1, - .base_address = FLASH_BANK1_BASE, - .controller_address = 0x400e0a00, - .present = 1, - .size_bytes = 128 * 1024, - .nsectors = 16, - .sector_size = 8192, - .page_size = 256, - }, - }, - }, - - { - .chipid_cidr = 0x280a0760, - .name = "at91sam3u2c", - .total_flash_size = 128 * 1024, - .total_sram_size = 36 * 1024, - .n_gpnvms = 2, - .n_banks = 1, - - // System boots at address 0x0 - // gpnvm[1] = selects boot code - // if gpnvm[1] == 0 - // boot is via "SAMBA" (rom) - // else - // boot is via FLASH - // Selection is via gpnvm[2] - // endif - { -// .bank[0] = { - { - .probed = 0, - .pChip = NULL, - .pBank = NULL, - .bank_number = 0, - .base_address = FLASH_BANK0_BASE, - .controller_address = 0x400e0800, - .present = 1, - .size_bytes = 128 * 1024, - .nsectors = 16, - .sector_size = 8192, - .page_size = 256, - }, -// .bank[1] = { - { - .present = 0, - .probed = 0, - .bank_number = 1, - }, - }, - }, - { - .chipid_cidr = 0x28090560, - .name = "at91sam3u1c", - .total_flash_size = 64 * 1024, - .total_sram_size = 20 * 1024, - .n_gpnvms = 2, - .n_banks = 1, - - // System boots at address 0x0 - // gpnvm[1] = selects boot code - // if gpnvm[1] == 0 - // boot is via "SAMBA" (rom) - // else - // boot is via FLASH - // Selection is via gpnvm[2] - // endif - // - - { -// .bank[0] = { - { - .probed = 0, - .pChip = NULL, - .pBank = NULL, - .bank_number = 0, - .base_address = FLASH_BANK0_BASE, - .controller_address = 0x400e0800, - .present = 1, - .size_bytes = 64 * 1024, - .nsectors = 8, - .sector_size = 8192, - .page_size = 256, - }, -// .bank[1] = { - { - .present = 0, - .probed = 0, - .bank_number = 1, - - }, - }, - }, - - // terminate - { - .chipid_cidr = 0, - .name = NULL, - } -}; - -/* Globals above */ -/*********************************************************************** - ********************************************************************** - ********************************************************************** - ********************************************************************** - ********************************************************************** - **********************************************************************/ -/* *ATMEL* style code - from the SAM3 driver code */ - -/** - * Get the current status of the EEFC and - * the value of some status bits (LOCKE, PROGE). - * @param pPrivate - info about the bank - * @param v - result goes here - */ -static int -EFC_GetStatus(struct sam3_bank_private *pPrivate, uint32_t *v) -{ - int r; - r = target_read_u32(pPrivate->pChip->target, pPrivate->controller_address + offset_EFC_FSR, v); - LOG_DEBUG("Status: 0x%08x (lockerror: %d, cmderror: %d, ready: %d)", - (unsigned int)(*v), - ((unsigned int)((*v >> 2) & 1)), - ((unsigned int)((*v >> 1) & 1)), - ((unsigned int)((*v >> 0) & 1))); - - return r; -} - -/** - * Get the result of the last executed command. - * @param pPrivate - info about the bank - * @param v - result goes here - */ -static int -EFC_GetResult(struct sam3_bank_private *pPrivate, uint32_t *v) -{ - int r; - uint32_t rv; - r = target_read_u32(pPrivate->pChip->target, pPrivate->controller_address + offset_EFC_FRR, &rv); - if (v) { - *v = rv; - } - LOG_DEBUG("Result: 0x%08x", ((unsigned int)(rv))); - return r; -} - -static int -EFC_StartCommand(struct sam3_bank_private *pPrivate, - unsigned command, unsigned argument) -{ - uint32_t n,v; - int r; - int retry; - - retry = 0; - do_retry: - - // Check command & argument - switch (command) { - - case AT91C_EFC_FCMD_WP: - case AT91C_EFC_FCMD_WPL: - case AT91C_EFC_FCMD_EWP: - case AT91C_EFC_FCMD_EWPL: - // case AT91C_EFC_FCMD_EPL: - // case AT91C_EFC_FCMD_EPA: - case AT91C_EFC_FCMD_SLB: - case AT91C_EFC_FCMD_CLB: - n = (pPrivate->size_bytes / pPrivate->page_size); - if (argument >= n) { - LOG_ERROR("*BUG*: Embedded flash has only %u pages", (unsigned)(n)); - } - break; - - case AT91C_EFC_FCMD_SFB: - case AT91C_EFC_FCMD_CFB: - if (argument >= pPrivate->pChip->details.n_gpnvms) { - LOG_ERROR("*BUG*: Embedded flash has only %d GPNVMs", - pPrivate->pChip->details.n_gpnvms); - } - break; - - case AT91C_EFC_FCMD_GETD: - case AT91C_EFC_FCMD_EA: - case AT91C_EFC_FCMD_GLB: - case AT91C_EFC_FCMD_GFB: - case AT91C_EFC_FCMD_STUI: - case AT91C_EFC_FCMD_SPUI: - if (argument != 0) { - LOG_ERROR("Argument is meaningless for cmd: %d", command); - } - break; - default: - LOG_ERROR("Unknown command %d", command); - break; - } - - if (command == AT91C_EFC_FCMD_SPUI) { - // this is a very special situation. - // Situation (1) - error/retry - see below - // And we are being called recursively - // Situation (2) - normal, finished reading unique id - } else { - // it should be "ready" - EFC_GetStatus(pPrivate, &v); - if (v & 1) { - // then it is ready - // we go on - } else { - if (retry) { - // we have done this before - // the controller is not responding. - LOG_ERROR("flash controller(%d) is not ready! Error", pPrivate->bank_number); - return ERROR_FAIL; - } else { - retry++; - LOG_ERROR("Flash controller(%d) is not ready, attempting reset", - pPrivate->bank_number); - // we do that by issuing the *STOP* command - EFC_StartCommand(pPrivate, AT91C_EFC_FCMD_SPUI, 0); - // above is recursive, and further recursion is blocked by - // if (command == AT91C_EFC_FCMD_SPUI) above - goto do_retry; - } - } - } - - v = (0x5A << 24) | (argument << 8) | command; - LOG_DEBUG("Command: 0x%08x", ((unsigned int)(v))); - r = target_write_u32(pPrivate->pBank->target, - pPrivate->controller_address + offset_EFC_FCR, - v); - if (r != ERROR_OK) { - LOG_DEBUG("Error Write failed"); - } - return r; -} - -/** - * Performs the given command and wait until its completion (or an error). - * @param pPrivate - info about the bank - * @param command - Command to perform. - * @param argument - Optional command argument. - * @param status - put command status bits here - */ -static int -EFC_PerformCommand(struct sam3_bank_private *pPrivate, - unsigned command, - unsigned argument, - uint32_t *status) -{ - - int r; - uint32_t v; - long long ms_now, ms_end; - - // default - if (status) { - *status = 0; - } - - r = EFC_StartCommand(pPrivate, command, argument); - if (r != ERROR_OK) { - return r; - } - - ms_end = 500 + timeval_ms(); - - - do { - r = EFC_GetStatus(pPrivate, &v); - if (r != ERROR_OK) { - return r; - } - ms_now = timeval_ms(); - if (ms_now > ms_end) { - // error - LOG_ERROR("Command timeout"); - return ERROR_FAIL; - } - } - while ((v & 1) == 0) - ; - - // error bits.. - if (status) { - *status = (v & 0x6); - } - return ERROR_OK; - -} - - - - - -/** - * Read the unique ID. - * @param pPrivate - info about the bank - * The unique ID is stored in the 'pPrivate' structure. - */ -static int -FLASHD_ReadUniqueID (struct sam3_bank_private *pPrivate) -{ - int r; - uint32_t v; - int x; - // assume 0 - pPrivate->pChip->cfg.unique_id[0] = 0; - pPrivate->pChip->cfg.unique_id[1] = 0; - pPrivate->pChip->cfg.unique_id[2] = 0; - pPrivate->pChip->cfg.unique_id[3] = 0; - - LOG_DEBUG("Begin"); - r = EFC_StartCommand(pPrivate, AT91C_EFC_FCMD_STUI, 0); - if (r < 0) { - return r; - } - - for (x = 0 ; x < 4 ; x++) { - r = target_read_u32(pPrivate->pChip->target, - pPrivate->pBank->base + (x * 4), - &v); - if (r < 0) { - return r; - } - pPrivate->pChip->cfg.unique_id[x] = v; - } - - r = EFC_PerformCommand(pPrivate, AT91C_EFC_FCMD_SPUI, 0, NULL); - LOG_DEBUG("End: R=%d, id = 0x%08x, 0x%08x, 0x%08x, 0x%08x", - r, - (unsigned int)(pPrivate->pChip->cfg.unique_id[0]), - (unsigned int)(pPrivate->pChip->cfg.unique_id[1]), - (unsigned int)(pPrivate->pChip->cfg.unique_id[2]), - (unsigned int)(pPrivate->pChip->cfg.unique_id[3])); - return r; - -} - -/** - * Erases the entire flash. - * @param pPrivate - the info about the bank. - */ -static int -FLASHD_EraseEntireBank(struct sam3_bank_private *pPrivate) -{ - LOG_DEBUG("Here"); - return EFC_PerformCommand(pPrivate, AT91C_EFC_FCMD_EA, 0, NULL); -} - - - -/** - * Gets current GPNVM state. - * @param pPrivate - info about the bank. - * @param gpnvm - GPNVM bit index. - * @param puthere - result stored here. - */ -//------------------------------------------------------------------------------ -static int -FLASHD_GetGPNVM(struct sam3_bank_private *pPrivate, unsigned gpnvm, unsigned *puthere) -{ - uint32_t v; - int r; - - LOG_DEBUG("Here"); - if (pPrivate->bank_number != 0) { - LOG_ERROR("GPNVM only works with Bank0"); - return ERROR_FAIL; - } - - if (gpnvm >= pPrivate->pChip->details.n_gpnvms) { - LOG_ERROR("Invalid GPNVM %d, max: %d, ignored", - gpnvm,pPrivate->pChip->details.n_gpnvms); - return ERROR_FAIL; - } - - // Get GPNVMs status - r = EFC_PerformCommand(pPrivate, AT91C_EFC_FCMD_GFB, 0, NULL); - if (r != ERROR_OK) { - LOG_ERROR("Failed"); - return r; - } - - r = EFC_GetResult(pPrivate, &v); - - if (puthere) { - // Check if GPNVM is set - // get the bit and make it a 0/1 - *puthere = (v >> gpnvm) & 1; - } - - return r; -} - - - - -/** - * Clears the selected GPNVM bit. - * @param pPrivate info about the bank - * @param gpnvm GPNVM index. - * @returns 0 if successful; otherwise returns an error code. - */ -static int -FLASHD_ClrGPNVM(struct sam3_bank_private *pPrivate, unsigned gpnvm) -{ - int r; - unsigned v; - - LOG_DEBUG("Here"); - if (pPrivate->bank_number != 0) { - LOG_ERROR("GPNVM only works with Bank0"); - return ERROR_FAIL; - } - - if (gpnvm >= pPrivate->pChip->details.n_gpnvms) { - LOG_ERROR("Invalid GPNVM %d, max: %d, ignored", - gpnvm,pPrivate->pChip->details.n_gpnvms); - return ERROR_FAIL; - } - - r = FLASHD_GetGPNVM(pPrivate, gpnvm, &v); - if (r != ERROR_OK) { - LOG_DEBUG("Failed: %d",r); - return r; - } - r = EFC_PerformCommand(pPrivate, AT91C_EFC_FCMD_CFB, gpnvm, NULL); - LOG_DEBUG("End: %d",r); - return r; -} - - - -/** - * Sets the selected GPNVM bit. - * @param pPrivate info about the bank - * @param gpnvm GPNVM index. - */ -static int -FLASHD_SetGPNVM(struct sam3_bank_private *pPrivate, unsigned gpnvm) -{ - int r; - unsigned v; - - if (pPrivate->bank_number != 0) { - LOG_ERROR("GPNVM only works with Bank0"); - return ERROR_FAIL; - } - - if (gpnvm >= pPrivate->pChip->details.n_gpnvms) { - LOG_ERROR("Invalid GPNVM %d, max: %d, ignored", - gpnvm,pPrivate->pChip->details.n_gpnvms); - return ERROR_FAIL; - } - - r = FLASHD_GetGPNVM(pPrivate, gpnvm, &v); - if (r != ERROR_OK) { - return r; - } - if (v) { - // already set - r = ERROR_OK; - } else { - // set it - r = EFC_PerformCommand(pPrivate, AT91C_EFC_FCMD_SFB, gpnvm, NULL); - } - return r; -} - - -/** - * Returns a bit field (at most 64) of locked regions within a page. - * @param pPrivate info about the bank - * @param v where to store locked bits - */ -static int -FLASHD_GetLockBits(struct sam3_bank_private *pPrivate, uint32_t *v) -{ - int r; - LOG_DEBUG("Here"); - r = EFC_PerformCommand(pPrivate, AT91C_EFC_FCMD_GLB, 0, NULL); - if (r == ERROR_OK) { - r = EFC_GetResult(pPrivate, v); - } - LOG_DEBUG("End: %d",r); - return r; -} - - -/** - * Unlocks all the regions in the given address range. - * @param pPrivate info about the bank - * @param start_sector first sector to unlock - * @param end_sector last (inclusive) to unlock - */ - -static int -FLASHD_Unlock(struct sam3_bank_private *pPrivate, - unsigned start_sector, - unsigned end_sector) -{ - int r; - uint32_t status; - uint32_t pg; - uint32_t pages_per_sector; - - pages_per_sector = pPrivate->sector_size / pPrivate->page_size; - - /* Unlock all pages */ - while (start_sector <= end_sector) { - pg = start_sector * pages_per_sector; - - r = EFC_PerformCommand(pPrivate, AT91C_EFC_FCMD_CLB, pg, &status); - if (r != ERROR_OK) { - return r; - } - start_sector++; - } - - return ERROR_OK; -} - - -/** - * Locks regions - * @param pPrivate - info about the bank - * @param start_sector - first sector to lock - * @param end_sector - last sector (inclusive) to lock - */ -static int -FLASHD_Lock(struct sam3_bank_private *pPrivate, - unsigned start_sector, - unsigned end_sector) -{ - uint32_t status; - uint32_t pg; - uint32_t pages_per_sector; - int r; - - pages_per_sector = pPrivate->sector_size / pPrivate->page_size; - - /* Lock all pages */ - while (start_sector <= end_sector) { - pg = start_sector * pages_per_sector; - - r = EFC_PerformCommand(pPrivate, AT91C_EFC_FCMD_SLB, pg, &status); - if (r != ERROR_OK) { - return r; - } - start_sector++; - } - return ERROR_OK; -} - - -/****** END SAM3 CODE ********/ - -/* begin helpful debug code */ - -static void -sam3_sprintf(struct sam3_chip *pChip , const char *fmt, ...) -{ - va_list ap; - va_start(ap,fmt); - if (pChip->mbuf == NULL) { - return; - } - - membuf_vsprintf(pChip->mbuf, fmt, ap); - va_end(ap); -} - -// print the fieldname, the field value, in dec & hex, and return field value -static uint32_t -sam3_reg_fieldname(struct sam3_chip *pChip, - const char *regname, - uint32_t value, - unsigned shift, - unsigned width) -{ - uint32_t v; - int hwidth, dwidth; - - - // extract the field - v = value >> shift; - v = v & ((1 << width)-1); - if (width <= 16) { - hwidth = 4; - dwidth = 5; - } else { - hwidth = 8; - dwidth = 12; - } - - // show the basics - sam3_sprintf(pChip, "\t%*s: %*d [0x%0*x] ", - REG_NAME_WIDTH, regname, - dwidth, v, - hwidth, v); - return v; -} - - -static const char _unknown[] = "unknown"; -static const char * const eproc_names[] = { - _unknown, // 0 - "arm946es", // 1 - "arm7tdmi", // 2 - "cortex-m3", // 3 - "arm920t", // 4 - "arm926ejs", // 5 - _unknown, // 6 - _unknown, // 7 - _unknown, // 8 - _unknown, // 9 - _unknown, // 10 - _unknown, // 11 - _unknown, // 12 - _unknown, // 13 - _unknown, // 14 - _unknown, // 15 -}; - -#define nvpsize2 nvpsize // these two tables are identical -static const char * const nvpsize[] = { - "none", // 0 - "8K bytes", // 1 - "16K bytes", // 2 - "32K bytes", // 3 - _unknown, // 4 - "64K bytes", // 5 - _unknown, // 6 - "128K bytes", // 7 - _unknown, // 8 - "256K bytes", // 9 - "512K bytes", // 10 - _unknown, // 11 - "1024K bytes", // 12 - _unknown, // 13 - "2048K bytes", // 14 - _unknown, // 15 -}; - - -static const char * const sramsize[] = { - "48K Bytes", // 0 - "1K Bytes", // 1 - "2K Bytes", // 2 - "6K Bytes", // 3 - "112K Bytes", // 4 - "4K Bytes", // 5 - "80K Bytes", // 6 - "160K Bytes", // 7 - "8K Bytes", // 8 - "16K Bytes", // 9 - "32K Bytes", // 10 - "64K Bytes", // 11 - "128K Bytes", // 12 - "256K Bytes", // 13 - "96K Bytes", // 14 - "512K Bytes", // 15 - -}; - -static const struct archnames { unsigned value; const char *name; } archnames[] = { - { 0x19, "AT91SAM9xx Series" }, - { 0x29, "AT91SAM9XExx Series" }, - { 0x34, "AT91x34 Series" }, - { 0x37, "CAP7 Series" }, - { 0x39, "CAP9 Series" }, - { 0x3B, "CAP11 Series" }, - { 0x40, "AT91x40 Series" }, - { 0x42, "AT91x42 Series" }, - { 0x55, "AT91x55 Series" }, - { 0x60, "AT91SAM7Axx Series" }, - { 0x61, "AT91SAM7AQxx Series" }, - { 0x63, "AT91x63 Series" }, - { 0x70, "AT91SAM7Sxx Series" }, - { 0x71, "AT91SAM7XCxx Series" }, - { 0x72, "AT91SAM7SExx Series" }, - { 0x73, "AT91SAM7Lxx Series" }, - { 0x75, "AT91SAM7Xxx Series" }, - { 0x76, "AT91SAM7SLxx Series" }, - { 0x80, "ATSAM3UxC Series (100-pin version)" }, - { 0x81, "ATSAM3UxE Series (144-pin version)" }, - { 0x83, "ATSAM3AxC Series (100-pin version)" }, - { 0x84, "ATSAM3XxC Series (100-pin version)" }, - { 0x85, "ATSAM3XxE Series (144-pin version)" }, - { 0x86, "ATSAM3XxG Series (208/217-pin version)" }, - { 0x88, "ATSAM3SxA Series (48-pin version)" }, - { 0x89, "ATSAM3SxB Series (64-pin version)" }, - { 0x8A, "ATSAM3SxC Series (100-pin version)" }, - { 0x92, "AT91x92 Series" }, - { 0xF0, "AT75Cxx Series" }, - { -1, NULL }, - -}; - -static const char * const nvptype[] = { - "rom", // 0 - "romless or onchip flash", // 1 - "embedded flash memory", // 2 - "rom(nvpsiz) + embedded flash (nvpsiz2)", //3 - "sram emulating flash", // 4 - _unknown, // 5 - _unknown, // 6 - _unknown, // 7 - -}; - -static const char *_yes_or_no(uint32_t v) -{ - if (v) { - return "YES"; - } else { - return "NO"; - } -} - -static const char * const _rc_freq[] = { - "4 MHz", "8 MHz", "12 MHz", "reserved" -}; - -static void -sam3_explain_ckgr_mor(struct sam3_chip *pChip) -{ - uint32_t v; - uint32_t rcen; - - v = sam3_reg_fieldname(pChip, "MOSCXTEN", pChip->cfg.CKGR_MOR, 0, 1); - sam3_sprintf(pChip, "(main xtal enabled: %s)\n", - _yes_or_no(v)); - v = sam3_reg_fieldname(pChip, "MOSCXTBY", pChip->cfg.CKGR_MOR, 1, 1); - sam3_sprintf(pChip, "(main osc bypass: %s)\n", - _yes_or_no(v)); - rcen = sam3_reg_fieldname(pChip, "MOSCRCEN", pChip->cfg.CKGR_MOR, 2, 1); - sam3_sprintf(pChip, "(onchip RC-OSC enabled: %s)\n", - _yes_or_no(rcen)); - v = sam3_reg_fieldname(pChip, "MOSCRCF", pChip->cfg.CKGR_MOR, 4, 3); - sam3_sprintf(pChip, "(onchip RC-OSC freq: %s)\n", - _rc_freq[v]); - - pChip->cfg.rc_freq = 0; - if (rcen) { - switch (v) { - default: - pChip->cfg.rc_freq = 0; - case 0: - pChip->cfg.rc_freq = 4 * 1000 * 1000; - break; - case 1: - pChip->cfg.rc_freq = 8 * 1000 * 1000; - break; - case 2: - pChip->cfg.rc_freq = 12* 1000 * 1000; - break; - } - } - - v = sam3_reg_fieldname(pChip,"MOSCXTST", pChip->cfg.CKGR_MOR, 8, 8); - sam3_sprintf(pChip, "(startup clks, time= %f uSecs)\n", - ((float)(v * 1000000)) / ((float)(pChip->cfg.slow_freq))); - v = sam3_reg_fieldname(pChip, "MOSCSEL", pChip->cfg.CKGR_MOR, 24, 1); - sam3_sprintf(pChip, "(mainosc source: %s)\n", - v ? "external xtal" : "internal RC"); - - v = sam3_reg_fieldname(pChip,"CFDEN", pChip->cfg.CKGR_MOR, 25, 1); - sam3_sprintf(pChip, "(clock failure enabled: %s)\n", - _yes_or_no(v)); -} - - - -static void -sam3_explain_chipid_cidr(struct sam3_chip *pChip) -{ - int x; - uint32_t v; - const char *cp; - - sam3_reg_fieldname(pChip, "Version", pChip->cfg.CHIPID_CIDR, 0, 5); - sam3_sprintf(pChip,"\n"); - - v = sam3_reg_fieldname(pChip, "EPROC", pChip->cfg.CHIPID_CIDR, 5, 3); - sam3_sprintf(pChip, "%s\n", eproc_names[v]); - - v = sam3_reg_fieldname(pChip, "NVPSIZE", pChip->cfg.CHIPID_CIDR, 8, 4); - sam3_sprintf(pChip, "%s\n", nvpsize[v]); - - v = sam3_reg_fieldname(pChip, "NVPSIZE2", pChip->cfg.CHIPID_CIDR, 12, 4); - sam3_sprintf(pChip, "%s\n", nvpsize2[v]); - - v = sam3_reg_fieldname(pChip, "SRAMSIZE", pChip->cfg.CHIPID_CIDR, 16,4); - sam3_sprintf(pChip, "%s\n", sramsize[ v ]); - - v = sam3_reg_fieldname(pChip, "ARCH", pChip->cfg.CHIPID_CIDR, 20, 8); - cp = _unknown; - for (x = 0 ; archnames[x].name ; x++) { - if (v == archnames[x].value) { - cp = archnames[x].name; - break; - } - } - - sam3_sprintf(pChip, "%s\n", cp); - - v = sam3_reg_fieldname(pChip, "NVPTYP", pChip->cfg.CHIPID_CIDR, 28, 3); - sam3_sprintf(pChip, "%s\n", nvptype[ v ]); - - v = sam3_reg_fieldname(pChip, "EXTID", pChip->cfg.CHIPID_CIDR, 31, 1); - sam3_sprintf(pChip, "(exists: %s)\n", _yes_or_no(v)); -} - -static void -sam3_explain_ckgr_mcfr(struct sam3_chip *pChip) -{ - uint32_t v; - - - v = sam3_reg_fieldname(pChip, "MAINFRDY", pChip->cfg.CKGR_MCFR, 16, 1); - sam3_sprintf(pChip, "(main ready: %s)\n", _yes_or_no(v)); - - v = sam3_reg_fieldname(pChip, "MAINF", pChip->cfg.CKGR_MCFR, 0, 16); - - v = (v * pChip->cfg.slow_freq) / 16; - pChip->cfg.mainosc_freq = v; - - sam3_sprintf(pChip, "(%3.03f Mhz (%d.%03dkhz slowclk)\n", - _tomhz(v), - pChip->cfg.slow_freq / 1000, - pChip->cfg.slow_freq % 1000); - -} - -static void -sam3_explain_ckgr_plla(struct sam3_chip *pChip) -{ - uint32_t mula,diva; - - diva = sam3_reg_fieldname(pChip, "DIVA", pChip->cfg.CKGR_PLLAR, 0, 8); - sam3_sprintf(pChip,"\n"); - mula = sam3_reg_fieldname(pChip, "MULA", pChip->cfg.CKGR_PLLAR, 16, 11); - sam3_sprintf(pChip,"\n"); - pChip->cfg.plla_freq = 0; - if (mula == 0) { - sam3_sprintf(pChip,"\tPLLA Freq: (Disabled,mula = 0)\n"); - } else if (diva == 0) { - sam3_sprintf(pChip,"\tPLLA Freq: (Disabled,diva = 0)\n"); - } else if (diva == 1) { - pChip->cfg.plla_freq = (pChip->cfg.mainosc_freq * (mula + 1)); - sam3_sprintf(pChip,"\tPLLA Freq: %3.03f MHz\n", - _tomhz(pChip->cfg.plla_freq)); - } -} - - -static void -sam3_explain_mckr(struct sam3_chip *pChip) -{ - uint32_t css, pres, fin = 0; - int pdiv = 0; - const char *cp = NULL; - - css = sam3_reg_fieldname(pChip, "CSS", pChip->cfg.PMC_MCKR, 0, 2); - switch (css & 3) { - case 0: - fin = pChip->cfg.slow_freq; - cp = "slowclk"; - break; - case 1: - fin = pChip->cfg.mainosc_freq; - cp = "mainosc"; - break; - case 2: - fin = pChip->cfg.plla_freq; - cp = "plla"; - break; - case 3: - if (pChip->cfg.CKGR_UCKR & (1 << 16)) { - fin = 480 * 1000 * 1000; - cp = "upll"; - } else { - fin = 0; - cp = "upll (*ERROR* UPLL is disabled)"; - } - break; - default: - assert(0); - break; - } - - sam3_sprintf(pChip, "%s (%3.03f Mhz)\n", - cp, - _tomhz(fin)); - pres = sam3_reg_fieldname(pChip, "PRES", pChip->cfg.PMC_MCKR, 4, 3); - switch (pres & 0x07) { - case 0: - pdiv = 1; - cp = "selected clock"; - case 1: - pdiv = 2; - cp = "clock/2"; - break; - case 2: - pdiv = 4; - cp = "clock/4"; - break; - case 3: - pdiv = 8; - cp = "clock/8"; - break; - case 4: - pdiv = 16; - cp = "clock/16"; - break; - case 5: - pdiv = 32; - cp = "clock/32"; - break; - case 6: - pdiv = 64; - cp = "clock/64"; - break; - case 7: - pdiv = 6; - cp = "clock/6"; - break; - default: - assert(0); - break; - } - sam3_sprintf(pChip, "(%s)\n", cp); - fin = fin / pdiv; - // sam3 has a *SINGLE* clock - - // other at91 series parts have divisors for these. - pChip->cfg.cpu_freq = fin; - pChip->cfg.mclk_freq = fin; - pChip->cfg.fclk_freq = fin; - sam3_sprintf(pChip, "\t\tResult CPU Freq: %3.03f\n", - _tomhz(fin)); -} - -#if 0 -static struct sam3_chip * -target2sam3(struct target *pTarget) -{ - struct sam3_chip *pChip; - - if (pTarget == NULL) { - return NULL; - } - - pChip = all_sam3_chips; - while (pChip) { - if (pChip->target == pTarget) { - break; // return below - } else { - pChip = pChip->next; - } - } - return pChip; -} -#endif - -static uint32_t * -sam3_get_reg_ptr(struct sam3_cfg *pCfg, const struct sam3_reg_list *pList) -{ - // this function exists to help - // keep funky offsetof() errors - // and casting from causing bugs - - // By using prototypes - we can detect what would - // be casting errors. - - return ((uint32_t *)(((char *)(pCfg)) + pList->struct_offset)); -} - - -#define SAM3_ENTRY(NAME, FUNC) { .address = SAM3_ ## NAME, .struct_offset = offsetof(struct sam3_cfg, NAME), #NAME, FUNC } -static const struct sam3_reg_list sam3_all_regs[] = { - SAM3_ENTRY(CKGR_MOR , sam3_explain_ckgr_mor), - SAM3_ENTRY(CKGR_MCFR , sam3_explain_ckgr_mcfr), - SAM3_ENTRY(CKGR_PLLAR , sam3_explain_ckgr_plla), - SAM3_ENTRY(CKGR_UCKR , NULL), - SAM3_ENTRY(PMC_FSMR , NULL), - SAM3_ENTRY(PMC_FSPR , NULL), - SAM3_ENTRY(PMC_IMR , NULL), - SAM3_ENTRY(PMC_MCKR , sam3_explain_mckr), - SAM3_ENTRY(PMC_PCK0 , NULL), - SAM3_ENTRY(PMC_PCK1 , NULL), - SAM3_ENTRY(PMC_PCK2 , NULL), - SAM3_ENTRY(PMC_PCSR , NULL), - SAM3_ENTRY(PMC_SCSR , NULL), - SAM3_ENTRY(PMC_SR , NULL), - SAM3_ENTRY(CHIPID_CIDR , sam3_explain_chipid_cidr), - SAM3_ENTRY(CHIPID_EXID , NULL), - SAM3_ENTRY(SUPC_CR, NULL), - - // TERMINATE THE LIST - { .name = NULL } -}; -#undef SAM3_ENTRY - - - - -static struct sam3_bank_private * -get_sam3_bank_private(struct flash_bank *bank) -{ - return (struct sam3_bank_private *)(bank->driver_priv); -} - -/** - * Given a pointer to where it goes in the structure, - * determine the register name, address from the all registers table. - */ -static const struct sam3_reg_list * -sam3_GetReg(struct sam3_chip *pChip, uint32_t *goes_here) -{ - const struct sam3_reg_list *pReg; - - pReg = &(sam3_all_regs[0]); - while (pReg->name) { - uint32_t *pPossible; - - // calculate where this one go.. - // it is "possibly" this register. - - pPossible = ((uint32_t *)(((char *)(&(pChip->cfg))) + pReg->struct_offset)); - - // well? Is it this register - if (pPossible == goes_here) { - // Jump for joy! - return pReg; - } - - // next... - pReg++; - } - // This is *TOTAL*PANIC* - we are totally screwed. - LOG_ERROR("INVALID SAM3 REGISTER"); - return NULL; -} - - -static int -sam3_ReadThisReg(struct sam3_chip *pChip, uint32_t *goes_here) -{ - const struct sam3_reg_list *pReg; - int r; - - pReg = sam3_GetReg(pChip, goes_here); - if (!pReg) { - return ERROR_FAIL; - } - - r = target_read_u32(pChip->target, pReg->address, goes_here); - if (r != ERROR_OK) { - LOG_ERROR("Cannot read SAM3 register: %s @ 0x%08x, Err: %d\n", - pReg->name, (unsigned)(pReg->address), r); - } - return r; -} - - - -static int -sam3_ReadAllRegs(struct sam3_chip *pChip) -{ - int r; - const struct sam3_reg_list *pReg; - - pReg = &(sam3_all_regs[0]); - while (pReg->name) { - r = sam3_ReadThisReg(pChip, - sam3_get_reg_ptr(&(pChip->cfg), pReg)); - if (r != ERROR_OK) { - LOG_ERROR("Cannot read SAM3 registere: %s @ 0x%08x, Error: %d\n", - pReg->name, ((unsigned)(pReg->address)), r); - return r; - } - - pReg++; - } - - return ERROR_OK; -} - - -static int -sam3_GetInfo(struct sam3_chip *pChip) -{ - const struct sam3_reg_list *pReg; - uint32_t regval; - - membuf_reset(pChip->mbuf); - - - pReg = &(sam3_all_regs[0]); - while (pReg->name) { - // display all regs - LOG_DEBUG("Start: %s", pReg->name); - regval = *sam3_get_reg_ptr(&(pChip->cfg), pReg); - sam3_sprintf(pChip, "%*s: [0x%08x] -> 0x%08x\n", - REG_NAME_WIDTH, - pReg->name, - pReg->address, - regval); - if (pReg->explain_func) { - (*(pReg->explain_func))(pChip); - } - LOG_DEBUG("End: %s", pReg->name); - pReg++; - } - sam3_sprintf(pChip," rc-osc: %3.03f MHz\n", _tomhz(pChip->cfg.rc_freq)); - sam3_sprintf(pChip," mainosc: %3.03f MHz\n", _tomhz(pChip->cfg.mainosc_freq)); - sam3_sprintf(pChip," plla: %3.03f MHz\n", _tomhz(pChip->cfg.plla_freq)); - sam3_sprintf(pChip," cpu-freq: %3.03f MHz\n", _tomhz(pChip->cfg.cpu_freq)); - sam3_sprintf(pChip,"mclk-freq: %3.03f MHz\n", _tomhz(pChip->cfg.mclk_freq)); - - - sam3_sprintf(pChip, " UniqueId: 0x%08x 0x%08x 0x%08x 0x%08x\n", - pChip->cfg.unique_id[0], - pChip->cfg.unique_id[1], - pChip->cfg.unique_id[2], - pChip->cfg.unique_id[3]); - - - return ERROR_OK; -} - - -static int -sam3_erase_check(struct flash_bank *bank) -{ - int x; - - LOG_DEBUG("Here"); - if (bank->target->state != TARGET_HALTED) { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - if (0 == bank->num_sectors) { - LOG_ERROR("Target: not supported/not probed\n"); - return ERROR_FAIL; - } - - LOG_INFO("sam3 - supports auto-erase, erase_check ignored"); - for (x = 0 ; x < bank->num_sectors ; x++) { - bank->sectors[x].is_erased = 1; - } - - LOG_DEBUG("Done"); - return ERROR_OK; -} - -static int -sam3_protect_check(struct flash_bank *bank) -{ - int r; - uint32_t v=0; - unsigned x; - struct sam3_bank_private *pPrivate; - - LOG_DEBUG("Begin"); - if (bank->target->state != TARGET_HALTED) { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - pPrivate = get_sam3_bank_private(bank); - if (!pPrivate) { - LOG_ERROR("no private for this bank?"); - return ERROR_FAIL; - } - if (!(pPrivate->probed)) { - return ERROR_FLASH_BANK_NOT_PROBED; - } - - r = FLASHD_GetLockBits(pPrivate , &v); - if (r != ERROR_OK) { - LOG_DEBUG("Failed: %d",r); - return r; - } - - for (x = 0 ; x < pPrivate->nsectors ; x++) { - bank->sectors[x].is_protected = (!!(v & (1 << x))); - } - LOG_DEBUG("Done"); - return ERROR_OK; -} - -FLASH_BANK_COMMAND_HANDLER(sam3_flash_bank_command) -{ - struct sam3_chip *pChip; - - pChip = all_sam3_chips; - - // is this an existing chip? - while (pChip) { - if (pChip->target == bank->target) { - break; - } - pChip = pChip->next; - } - - if (!pChip) { - // this is a *NEW* chip - pChip = calloc(1, sizeof(struct sam3_chip)); - if (!pChip) { - LOG_ERROR("NO RAM!"); - return ERROR_FAIL; - } - pChip->target = bank->target; - // insert at head - pChip->next = all_sam3_chips; - all_sam3_chips = pChip; - pChip->target = bank->target; - // assumption is this runs at 32khz - pChip->cfg.slow_freq = 32768; - pChip->probed = 0; - pChip->mbuf = membuf_new(); - if (!(pChip->mbuf)) { - LOG_ERROR("no memory"); - return ERROR_FAIL; - } - } - - switch (bank->base) { - default: - LOG_ERROR("Address 0x%08x invalid bank address (try 0x%08x or 0x%08x)", - ((unsigned int)(bank->base)), - ((unsigned int)(FLASH_BANK0_BASE)), - ((unsigned int)(FLASH_BANK1_BASE))); - return ERROR_FAIL; - break; - case FLASH_BANK0_BASE: - bank->driver_priv = &(pChip->details.bank[0]); - bank->bank_number = 0; - pChip->details.bank[0].pChip = pChip; - pChip->details.bank[0].pBank = bank; - break; - case FLASH_BANK1_BASE: - bank->driver_priv = &(pChip->details.bank[1]); - bank->bank_number = 1; - pChip->details.bank[1].pChip = pChip; - pChip->details.bank[1].pBank = bank; - break; - } - - // we initialize after probing. - return ERROR_OK; -} - -static int -sam3_GetDetails(struct sam3_bank_private *pPrivate) -{ - const struct sam3_chip_details *pDetails; - struct sam3_chip *pChip; - void *vp; - struct flash_bank *saved_banks[SAM3_MAX_FLASH_BANKS]; - - unsigned x; - const char *cp; - - LOG_DEBUG("Begin"); - pDetails = all_sam3_details; - while (pDetails->name) { - if (pDetails->chipid_cidr == pPrivate->pChip->cfg.CHIPID_CIDR) { - break; - } else { - pDetails++; - } - } - if (pDetails->name == NULL) { - LOG_ERROR("SAM3 ChipID 0x%08x not found in table (perhaps you can this chip?)", - (unsigned int)(pPrivate->pChip->cfg.CHIPID_CIDR)); - // Help the victim, print details about the chip - membuf_reset(pPrivate->pChip->mbuf); - membuf_sprintf(pPrivate->pChip->mbuf, - "SAM3 CHIPID_CIDR: 0x%08x decodes as follows\n", - pPrivate->pChip->cfg.CHIPID_CIDR); - sam3_explain_chipid_cidr(pPrivate->pChip); - cp = membuf_strtok(pPrivate->pChip->mbuf, "\n", &vp); - while (cp) { - LOG_INFO("%s", cp); - cp = membuf_strtok(NULL, "\n", &vp); - } - return ERROR_FAIL; - } - - // DANGER: THERE ARE DRAGONS HERE - - // get our pChip - it is going - // to be over-written shortly - pChip = pPrivate->pChip; - - // Note that, in reality: - // - // pPrivate = &(pChip->details.bank[0]) - // or pPrivate = &(pChip->details.bank[1]) - // - - // save the "bank" pointers - for (x = 0 ; x < SAM3_MAX_FLASH_BANKS ; x++) { - saved_banks[ x ] = pChip->details.bank[x].pBank; - } - - // Overwrite the "details" structure. - memcpy(&(pPrivate->pChip->details), - pDetails, - sizeof(pPrivate->pChip->details)); - - // now fix the ghosted pointers - for (x = 0 ; x < SAM3_MAX_FLASH_BANKS ; x++) { - pChip->details.bank[x].pChip = pChip; - pChip->details.bank[x].pBank = saved_banks[x]; - } - - // update the *BANK*SIZE* - - LOG_DEBUG("End"); - return ERROR_OK; -} - - - -static int -_sam3_probe(struct flash_bank *bank, int noise) -{ - unsigned x; - int r; - struct sam3_bank_private *pPrivate; - - - LOG_DEBUG("Begin: Bank: %d, Noise: %d", bank->bank_number, noise); - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - pPrivate = get_sam3_bank_private(bank); - if (!pPrivate) { - LOG_ERROR("Invalid/unknown bank number\n"); - return ERROR_FAIL; - } - - r = sam3_ReadAllRegs(pPrivate->pChip); - if (r != ERROR_OK) { - return r; - } - - - LOG_DEBUG("Here"); - r = sam3_GetInfo(pPrivate->pChip); - if (r != ERROR_OK) { - return r; - } - if (!(pPrivate->pChip->probed)) { - pPrivate->pChip->probed = 1; - LOG_DEBUG("Here"); - r = sam3_GetDetails(pPrivate); - if (r != ERROR_OK) { - return r; - } - } - - // update the flash bank size - for (x = 0 ; x < SAM3_MAX_FLASH_BANKS ; x++) { - if (bank->base == pPrivate->pChip->details.bank[0].base_address) { - bank->size = pPrivate->pChip->details.bank[0].size_bytes; - break; - } - } - - if (bank->sectors == NULL) { - bank->sectors = calloc(pPrivate->nsectors, (sizeof((bank->sectors)[0]))); - if (bank->sectors == NULL) { - LOG_ERROR("No memory!"); - return ERROR_FAIL; - } - bank->num_sectors = pPrivate->nsectors; - - for (x = 0 ; ((int)(x)) < bank->num_sectors ; x++) { - bank->sectors[x].size = pPrivate->sector_size; - bank->sectors[x].offset = x * (pPrivate->sector_size); - // mark as unknown - bank->sectors[x].is_erased = -1; - bank->sectors[x].is_protected = -1; - } - } - - pPrivate->probed = 1; - - r = sam3_protect_check(bank); - if (r != ERROR_OK) { - return r; - } - - LOG_DEBUG("Bank = %d, nbanks = %d", - pPrivate->bank_number , pPrivate->pChip->details.n_banks); - if ((pPrivate->bank_number + 1) == pPrivate->pChip->details.n_banks) { - // read unique id, - // it appears to be associated with the *last* flash bank. - FLASHD_ReadUniqueID(pPrivate); - } - - return r; -} - -static int -sam3_probe(struct flash_bank *bank) -{ - return _sam3_probe(bank, 1); -} - -static int -sam3_auto_probe(struct flash_bank *bank) -{ - return _sam3_probe(bank, 0); -} - - - -static int -sam3_erase(struct flash_bank *bank, int first, int last) -{ - struct sam3_bank_private *pPrivate; - int r; - - LOG_DEBUG("Here"); - if (bank->target->state != TARGET_HALTED) { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - r = sam3_auto_probe(bank); - if (r != ERROR_OK) { - LOG_DEBUG("Here,r=%d",r); - return r; - } - - pPrivate = get_sam3_bank_private(bank); - if (!(pPrivate->probed)) { - return ERROR_FLASH_BANK_NOT_PROBED; - } - - if ((first == 0) && ((last + 1)== ((int)(pPrivate->nsectors)))) { - // whole chip - LOG_DEBUG("Here"); - return FLASHD_EraseEntireBank(pPrivate); - } - LOG_INFO("sam3 auto-erases while programing (request ignored)"); - return ERROR_OK; -} - -static int -sam3_protect(struct flash_bank *bank, int set, int first, int last) -{ - struct sam3_bank_private *pPrivate; - int r; - - LOG_DEBUG("Here"); - if (bank->target->state != TARGET_HALTED) { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - pPrivate = get_sam3_bank_private(bank); - if (!(pPrivate->probed)) { - return ERROR_FLASH_BANK_NOT_PROBED; - } - - if (set) { - r = FLASHD_Lock(pPrivate, (unsigned)(first), (unsigned)(last)); - } else { - r = FLASHD_Unlock(pPrivate, (unsigned)(first), (unsigned)(last)); - } - LOG_DEBUG("End: r=%d",r); - - return r; - -} - - -static int -sam3_info(struct flash_bank *bank, char *buf, int buf_size) -{ - if (bank->target->state != TARGET_HALTED) { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - buf[ 0 ] = 0; - return ERROR_OK; -} - -static int -sam3_page_read(struct sam3_bank_private *pPrivate, unsigned pagenum, uint8_t *buf) -{ - uint32_t adr; - int r; - - adr = pagenum * pPrivate->page_size; - adr += adr + pPrivate->base_address; - - r = target_read_memory(pPrivate->pChip->target, - adr, - 4, /* THIS*MUST*BE* in 32bit values */ - pPrivate->page_size / 4, - buf); - if (r != ERROR_OK) { - LOG_ERROR("SAM3: Flash program failed to read page phys address: 0x%08x", (unsigned int)(adr)); - } - return r; -} - -// The code below is basically this: -// compiled with -// arm-none-eabi-gcc -mthumb -mcpu = cortex-m3 -O9 -S ./foobar.c -o foobar.s -// -// Only the *CPU* can write to the flash buffer. -// the DAP cannot... so - we download this 28byte thing -// Run the algorithm - (below) -// to program the device -// -// ======================================== -// #include -// -// struct foo { -// uint32_t *dst; -// const uint32_t *src; -// int n; -// volatile uint32_t *base; -// uint32_t cmd; -// }; -// -// -// uint32_t sam3_function(struct foo *p) -// { -// volatile uint32_t *v; -// uint32_t *d; -// const uint32_t *s; -// int n; -// uint32_t r; -// -// d = p->dst; -// s = p->src; -// n = p->n; -// -// do { -// *d++ = *s++; -// } while (--n) -// ; -// -// v = p->base; -// -// v[ 1 ] = p->cmd; -// do { -// r = v[8/4]; -// } while (!(r&1)) -// ; -// return r; -// } -// ======================================== - - - -static const uint8_t -sam3_page_write_opcodes[] = { - // 24 0000 0446 mov r4, r0 - 0x04,0x46, - // 25 0002 6168 ldr r1, [r4, #4] - 0x61,0x68, - // 26 0004 0068 ldr r0, [r0, #0] - 0x00,0x68, - // 27 0006 A268 ldr r2, [r4, #8] - 0xa2,0x68, - // 28 @ lr needed for prologue - // 29 .L2: - // 30 0008 51F8043B ldr r3, [r1], #4 - 0x51,0xf8,0x04,0x3b, - // 31 000c 12F1FF32 adds r2, r2, #-1 - 0x12,0xf1,0xff,0x32, - // 32 0010 40F8043B str r3, [r0], #4 - 0x40,0xf8,0x04,0x3b, - // 33 0014 F8D1 bne .L2 - 0xf8,0xd1, - // 34 0016 E268 ldr r2, [r4, #12] - 0xe2,0x68, - // 35 0018 2369 ldr r3, [r4, #16] - 0x23,0x69, - // 36 001a 5360 str r3, [r2, #4] - 0x53,0x60, - // 37 001c 0832 adds r2, r2, #8 - 0x08,0x32, - // 38 .L4: - // 39 001e 1068 ldr r0, [r2, #0] - 0x10,0x68, - // 40 0020 10F0010F tst r0, #1 - 0x10,0xf0,0x01,0x0f, - // 41 0024 FBD0 beq .L4 - 0xfb,0xd0, - // 42 .done: - // 43 0026 FEE7 b .done - 0xfe,0xe7 -}; - - -static int -sam3_page_write(struct sam3_bank_private *pPrivate, unsigned pagenum, uint8_t *buf) -{ - uint32_t adr; - uint32_t status; - int r; - - adr = pagenum * pPrivate->page_size; - adr += (adr + pPrivate->base_address); - - LOG_DEBUG("Wr Page %u @ phys address: 0x%08x", pagenum, (unsigned int)(adr)); - r = target_write_memory(pPrivate->pChip->target, - adr, - 4, /* THIS*MUST*BE* in 32bit values */ - pPrivate->page_size / 4, - buf); - if (r != ERROR_OK) { - LOG_ERROR("SAM3: Failed to write (buffer) page at phys address 0x%08x", (unsigned int)(adr)); - return r; - } - - r = EFC_PerformCommand(pPrivate, - // send Erase & Write Page - AT91C_EFC_FCMD_EWP, - pagenum, - &status); - - if (r != ERROR_OK) { - LOG_ERROR("SAM3: Error performing Erase & Write page @ phys address 0x%08x", (unsigned int)(adr)); - } - if (status & (1 << 2)) { - LOG_ERROR("SAM3: Page @ Phys address 0x%08x is locked", (unsigned int)(adr)); - return ERROR_FAIL; - } - if (status & (1 << 1)) { - LOG_ERROR("SAM3: Flash Command error @phys address 0x%08x", (unsigned int)(adr)); - return ERROR_FAIL; - } - return ERROR_OK; -} - - - - - -static int -sam3_write(struct flash_bank *bank, - uint8_t *buffer, - uint32_t offset, - uint32_t count) -{ - int n; - unsigned page_cur; - unsigned page_end; - int r; - unsigned page_offset; - struct sam3_bank_private *pPrivate; - uint8_t *pagebuffer; - - // incase we bail further below, set this to null - pagebuffer = NULL; - - // ignore dumb requests - if (count == 0) { - r = ERROR_OK; - goto done; - } - - if (bank->target->state != TARGET_HALTED) { - LOG_ERROR("Target not halted"); - r = ERROR_TARGET_NOT_HALTED; - goto done; - } - - pPrivate = get_sam3_bank_private(bank); - if (!(pPrivate->probed)) { - r = ERROR_FLASH_BANK_NOT_PROBED; - goto done; - } - - - if ((offset + count) > pPrivate->size_bytes) { - LOG_ERROR("Flash write error - past end of bank"); - LOG_ERROR(" offset: 0x%08x, count 0x%08x, BankEnd: 0x%08x", - (unsigned int)(offset), - (unsigned int)(count), - (unsigned int)(pPrivate->size_bytes)); - r = ERROR_FAIL; - goto done; - } - - pagebuffer = malloc(pPrivate->page_size); - if( !pagebuffer ){ - LOG_ERROR("No memory for %d Byte page buffer", (int)(pPrivate->page_size)); - r = ERROR_FAIL; - goto done; - } - - // what page do we start & end in? - page_cur = offset / pPrivate->page_size; - page_end = (offset + count - 1) / pPrivate->page_size; - - LOG_DEBUG("Offset: 0x%08x, Count: 0x%08x", (unsigned int)(offset), (unsigned int)(count)); - LOG_DEBUG("Page start: %d, Page End: %d", (int)(page_cur), (int)(page_end)); - - // Special case: all one page - // - // Otherwise: - // (1) non-aligned start - // (2) body pages - // (3) non-aligned end. - - // Handle special case - all one page. - if (page_cur == page_end) { - LOG_DEBUG("Special case, all in one page"); - r = sam3_page_read(pPrivate, page_cur, pagebuffer); - if (r != ERROR_OK) { - goto done; - } - - page_offset = (offset & (pPrivate->page_size-1)); - memcpy(pagebuffer + page_offset, - buffer, - count); - - r = sam3_page_write(pPrivate, page_cur, pagebuffer); - if (r != ERROR_OK) { - goto done; - } - r = ERROR_OK; - goto done; - } - - // non-aligned start - page_offset = offset & (pPrivate->page_size - 1); - if (page_offset) { - LOG_DEBUG("Not-Aligned start"); - // read the partial - r = sam3_page_read(pPrivate, page_cur, pagebuffer); - if (r != ERROR_OK) { - goto done; - } - - // over-write with new data - n = (pPrivate->page_size - page_offset); - memcpy(pagebuffer + page_offset, - buffer, - n); - - r = sam3_page_write(pPrivate, page_cur, pagebuffer); - if (r != ERROR_OK) { - goto done; - } - - count -= n; - offset += n; - buffer += n; - page_cur++; - } - - // intermediate large pages - // also - the final *terminal* - // if that terminal page is a full page - LOG_DEBUG("Full Page Loop: cur=%d, end=%d, count = 0x%08x", - (int)page_cur, (int)page_end, (unsigned int)(count)); - - while ((page_cur < page_end) && - (count >= pPrivate->page_size)) { - r = sam3_page_write(pPrivate, page_cur, buffer); - if (r != ERROR_OK) { - goto done; - } - count -= pPrivate->page_size; - buffer += pPrivate->page_size; - page_cur += 1; - } - - // terminal partial page? - if (count) { - LOG_DEBUG("Terminal partial page, count = 0x%08x", (unsigned int)(count)); - // we have a partial page - r = sam3_page_read(pPrivate, page_cur, pagebuffer); - if (r != ERROR_OK) { - goto done; - } - // data goes at start - memcpy(pagebuffer, buffer, count); - r = sam3_page_write(pPrivate, page_cur, pagebuffer); - if (r != ERROR_OK) { - goto done; - } - buffer += count; - count -= count; - } - LOG_DEBUG("Done!"); - r = ERROR_OK; - done: - if( pagebuffer ){ - free(pagebuffer); - } - return r; -} - -COMMAND_HANDLER(sam3_handle_info_command) -{ - struct sam3_chip *pChip; - void *vp; - const char *cp; - unsigned x; - int r; - - pChip = get_current_sam3(CMD_CTX); - if (!pChip) { - return ERROR_OK; - } - - r = 0; - - // bank0 must exist before we can do anything - if (pChip->details.bank[0].pBank == NULL) { - x = 0; - need_define: - command_print(CMD_CTX, - "Please define bank %d via command: flash bank %s ... ", - x, - at91sam3_flash.name); - return ERROR_FAIL; - } - - // if bank 0 is not probed, then probe it - if (!(pChip->details.bank[0].probed)) { - r = sam3_auto_probe(pChip->details.bank[0].pBank); - if (r != ERROR_OK) { - return ERROR_FAIL; - } - } - // above garentees the "chip details" structure is valid - // and thus, bank private areas are valid - // and we have a SAM3 chip, what a concept! - - - // auto-probe other banks, 0 done above - for (x = 1 ; x < SAM3_MAX_FLASH_BANKS ; x++) { - // skip banks not present - if (!(pChip->details.bank[x].present)) { - continue; - } - - if (pChip->details.bank[x].pBank == NULL) { - goto need_define; - } - - if (pChip->details.bank[x].probed) { - continue; - } - - r = sam3_auto_probe(pChip->details.bank[x].pBank); - if (r != ERROR_OK) { - return r; - } - } - - - r = sam3_GetInfo(pChip); - if (r != ERROR_OK) { - LOG_DEBUG("Sam3Info, Failed %d\n",r); - return r; - } - - - // print results - cp = membuf_strtok(pChip->mbuf, "\n", &vp); - while (cp) { - command_print(CMD_CTX,"%s", cp); - cp = membuf_strtok(NULL, "\n", &vp); - } - return ERROR_OK; -} - -COMMAND_HANDLER(sam3_handle_gpnvm_command) -{ - unsigned x,v; - int r,who; - struct sam3_chip *pChip; - - pChip = get_current_sam3(CMD_CTX); - if (!pChip) { - return ERROR_OK; - } - - if (pChip->target->state != TARGET_HALTED) { - LOG_ERROR("sam3 - target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - - if (pChip->details.bank[0].pBank == NULL) { - command_print(CMD_CTX, "Bank0 must be defined first via: flash bank %s ...", - at91sam3_flash.name); - return ERROR_FAIL; - } - if (!pChip->details.bank[0].probed) { - r = sam3_auto_probe(pChip->details.bank[0].pBank); - if (r != ERROR_OK) { - return r; - } - } - - - switch (CMD_ARGC) { - default: - command_print(CMD_CTX,"Too many parameters\n"); - return ERROR_COMMAND_SYNTAX_ERROR; - break; - case 0: - who = -1; - goto showall; - break; - case 1: - who = -1; - break; - case 2: - if ((0 == strcmp(CMD_ARGV[0], "show")) && (0 == strcmp(CMD_ARGV[1], "all"))) { - who = -1; - } else { - uint32_t v32; - COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], v32); - who = v32; - } - break; - } - - if (0 == strcmp("show", CMD_ARGV[0])) { - if (who == -1) { - showall: - r = ERROR_OK; - for (x = 0 ; x < pChip->details.n_gpnvms ; x++) { - r = FLASHD_GetGPNVM(&(pChip->details.bank[0]), x, &v); - if (r != ERROR_OK) { - break; - } - command_print(CMD_CTX, "sam3-gpnvm%u: %u", x, v); - } - return r; - } - if ((who >= 0) && (((unsigned)(who)) < pChip->details.n_gpnvms)) { - r = FLASHD_GetGPNVM(&(pChip->details.bank[0]), who, &v); - command_print(CMD_CTX, "sam3-gpnvm%u: %u", who, v); - return r; - } else { - command_print(CMD_CTX, "sam3-gpnvm invalid GPNVM: %u", who); - return ERROR_COMMAND_SYNTAX_ERROR; - } - } - - if (who == -1) { - command_print(CMD_CTX, "Missing GPNVM number"); - return ERROR_COMMAND_SYNTAX_ERROR; - } - - if (0 == strcmp("set", CMD_ARGV[0])) { - r = FLASHD_SetGPNVM(&(pChip->details.bank[0]), who); - } else if ((0 == strcmp("clr", CMD_ARGV[0])) || - (0 == strcmp("clear", CMD_ARGV[0]))) { // quietly accept both - r = FLASHD_ClrGPNVM(&(pChip->details.bank[0]), who); - } else { - command_print(CMD_CTX, "Unkown command: %s", CMD_ARGV[0]); - r = ERROR_COMMAND_SYNTAX_ERROR; - } - return r; -} - -COMMAND_HANDLER(sam3_handle_slowclk_command) -{ - struct sam3_chip *pChip; - - pChip = get_current_sam3(CMD_CTX); - if (!pChip) { - return ERROR_OK; - } - - - switch (CMD_ARGC) { - case 0: - // show - break; - case 1: - { - // set - uint32_t v; - COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], v); - if (v > 200000) { - // absurd slow clock of 200Khz? - command_print(CMD_CTX,"Absurd/illegal slow clock freq: %d\n", (int)(v)); - return ERROR_COMMAND_SYNTAX_ERROR; - } - pChip->cfg.slow_freq = v; - break; - } - default: - // error - command_print(CMD_CTX,"Too many parameters"); - return ERROR_COMMAND_SYNTAX_ERROR; - break; - } - command_print(CMD_CTX, "Slowclk freq: %d.%03dkhz", - (int)(pChip->cfg.slow_freq/ 1000), - (int)(pChip->cfg.slow_freq% 1000)); - return ERROR_OK; -} - -static const struct command_registration at91sam3_exec_command_handlers[] = { - { - .name = "gpnvm", - .handler = &sam3_handle_gpnvm_command, - .mode = COMMAND_EXEC, - .usage = "[(set|clear) []]", - .help = "Without arguments, shows the gpnvm register; " - "otherwise, sets or clear the specified bit.", - }, - { - .name = "info", - .handler = &sam3_handle_info_command, - .mode = COMMAND_EXEC, - .help = "print information about the current sam3 chip", - }, - { - .name = "slowclk", - .handler = &sam3_handle_slowclk_command, - .mode = COMMAND_EXEC, - .usage = "", - .help = "set the slowclock frequency (default 32768hz)", - }, - COMMAND_REGISTRATION_DONE -}; -static const struct command_registration at91sam3_command_handlers[] = { - { - .name = "at91sam3", - .mode = COMMAND_ANY, - .help = "at91sam3 flash command group", - .chain = at91sam3_exec_command_handlers, - }, - COMMAND_REGISTRATION_DONE -}; - -struct flash_driver at91sam3_flash = { - .name = "at91sam3", - .commands = at91sam3_command_handlers, - .flash_bank_command = &sam3_flash_bank_command, - .erase = &sam3_erase, - .protect = &sam3_protect, - .write = &sam3_write, - .probe = &sam3_probe, - .auto_probe = &sam3_auto_probe, - .erase_check = &sam3_erase_check, - .protect_check = &sam3_protect_check, - .info = &sam3_info, - }; diff --git a/src/flash/at91sam3.h b/src/flash/at91sam3.h deleted file mode 100644 index 4fa7f467..00000000 --- a/src/flash/at91sam3.h +++ /dev/null @@ -1,23 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2009 by Duane Ellis * - * openocd@duaneellis.com * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ - - -// nothing to do here other then export this. -extern struct flash_driver at91sam3_flash; diff --git a/src/flash/at91sam7.c b/src/flash/at91sam7.c deleted file mode 100644 index f9b87bab..00000000 --- a/src/flash/at91sam7.c +++ /dev/null @@ -1,1213 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2006 by Magnus Lundin * - * lundin@mlu.mine.nu * - * * - * Copyright (C) 2008 by Gheorghe Guran (atlas) * - * * - * 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. * -****************************************************************************/ - -/*************************************************************************** -* -* New flash setup command: -* -* flash bank -* [ -* -* -* ] -* -* - MUST be used if clock is from external source, -* CAN be used if main oscillator frequency is known (recommended) -* Examples: -* ==== RECOMMENDED (covers clock speed) ============ -* flash bank at91sam7 0x00100000 0 0 4 $_TARGETNAME AT91SAM7XC256 1 16 64 256 3 25000 -* (if auto-detect fails; provides clock spec) -* flash bank at91sam7 0 0 0 0 $_TARGETNAME 0 0 0 0 0 0 25000 -* (auto-detect everything except the clock) -* ==== NOT RECOMMENDED !!! (clock speed is not configured) ==== -* flash bank at91sam7 0x00100000 0 0 4 $_TARGETNAME AT91SAM7XC256 1 16 64 256 3 0 -* (if auto-detect fails) -* flash bank at91sam7 0 0 0 0 $_TARGETNAME -* (old style, auto-detect everything) -****************************************************************************/ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "at91sam7.h" -#include "binarybuffer.h" - -static int at91sam7_protect_check(struct flash_bank *bank); -static int at91sam7_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count); - -static uint32_t at91sam7_get_flash_status(struct target *target, int bank_number); -static void at91sam7_set_flash_mode(struct flash_bank *bank, int mode); -static uint32_t at91sam7_wait_status_busy(struct flash_bank *bank, uint32_t waitbits, int timeout); -static int at91sam7_flash_command(struct flash_bank *bank, uint8_t cmd, uint16_t pagen); - -static uint32_t MC_FMR[4] = { 0xFFFFFF60, 0xFFFFFF70, 0xFFFFFF80, 0xFFFFFF90 }; -static uint32_t MC_FCR[4] = { 0xFFFFFF64, 0xFFFFFF74, 0xFFFFFF84, 0xFFFFFF94 }; -static uint32_t MC_FSR[4] = { 0xFFFFFF68, 0xFFFFFF78, 0xFFFFFF88, 0xFFFFFF98 }; - -static char * EPROC[8]= {"Unknown","ARM946-E","ARM7TDMI","Unknown","ARM920T","ARM926EJ-S","Unknown","Unknown"}; - -#if 0 -static 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 */ -}; -#endif - - -static uint32_t at91sam7_get_flash_status(struct target *target, int bank_number) -{ - uint32_t fsr; - target_read_u32(target, MC_FSR[bank_number], &fsr); - - return fsr; -} - -/* Read clock configuration and set at91sam7_info->mck_freq */ -static void at91sam7_read_clock_info(struct flash_bank *bank) -{ - struct at91sam7_flash_bank *at91sam7_info = bank->driver_priv; - struct target *target = bank->target; - uint32_t mckr, mcfr, pllr, mor; - unsigned long tmp = 0, mainfreq; - - /* Read Clock Generator Main Oscillator Register */ - target_read_u32(target, CKGR_MOR, &mor); - /* Read Clock Generator Main Clock Frequency 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; - at91sam7_info->mck_freq = 0; - switch (mckr & PMC_MCKR_CSS) - { - case 0: /* Slow Clock */ - at91sam7_info->mck_valid = 1; - tmp = RC_FREQ; - break; - - case 1: /* Main Clock */ - if ((mcfr & CKGR_MCFR_MAINRDY) && - (at91sam7_info->ext_freq == 0)) - { - at91sam7_info->mck_valid = 1; - tmp = RC_FREQ / 16ul * (mcfr & 0xffff); - } - else if (at91sam7_info->ext_freq != 0) - { - at91sam7_info->mck_valid = 1; - tmp = at91sam7_info->ext_freq; - } - break; - - case 2: /* Reserved */ - break; - - case 3: /* PLL Clock */ - if ((mcfr & CKGR_MCFR_MAINRDY) && - (at91sam7_info->ext_freq == 0)) - { - 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); - } - else if ((at91sam7_info->ext_freq != 0) && - ((pllr&CKGR_PLLR_DIV) != 0)) - { - at91sam7_info->mck_valid = 1; - tmp = at91sam7_info->ext_freq / (pllr&CKGR_PLLR_DIV)* - (((pllr & CKGR_PLLR_MUL) >> 16) + 1); - } - break; - } - - /* Prescaler adjust */ - if ((((mckr & PMC_MCKR_PRES) >> 2) == 7) || (tmp == 0)) - { - at91sam7_info->mck_valid = 0; - at91sam7_info->mck_freq = 0; - } - else if (((mckr & PMC_MCKR_PRES) >> 2) != 0) - at91sam7_info->mck_freq = tmp >> ((mckr & PMC_MCKR_PRES) >> 2); - else - at91sam7_info->mck_freq = tmp; -} - -/* Setup the timimg registers for nvbits or normal flash */ -static void at91sam7_set_flash_mode(struct flash_bank *bank, int mode) -{ - uint32_t fmr, fmcn = 0, fws = 0; - struct at91sam7_flash_bank *at91sam7_info = bank->driver_priv; - struct target *target = bank->target; - - if (mode && (mode != at91sam7_info->flashmode)) - { - /* 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/1000000ul)+ - (at91sam7_info->mck_freq/2000000ul) + 1; - } - - /* hard overclocking */ - if (fmcn > 0xFF) - fmcn = 0xFF; - - /* 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; - - LOG_DEBUG("fmcn[%i]: %i", bank->bank_number, (int)(fmcn)); - fmr = fmcn << 16 | fws << 8; - target_write_u32(target, MC_FMR[bank->bank_number], fmr); - } - - at91sam7_info->flashmode = mode; -} - -static uint32_t at91sam7_wait_status_busy(struct flash_bank *bank, uint32_t waitbits, int timeout) -{ - uint32_t status; - - while ((!((status = at91sam7_get_flash_status(bank->target, bank->bank_number)) & waitbits)) && (timeout-- > 0)) - { - LOG_DEBUG("status[%i]: 0x%" PRIx32 "", (int)bank->bank_number, status); - alive_sleep(1); - } - - LOG_DEBUG("status[%i]: 0x%" PRIx32 "", bank->bank_number, status); - - if (status & 0x0C) - { - LOG_ERROR("status register: 0x%" PRIx32 "", status); - if (status & 0x4) - LOG_ERROR("Lock Error Bit Detected, Operation Abort"); - if (status & 0x8) - LOG_ERROR("Invalid command and/or bad keyword, Operation Abort"); - if (status & 0x10) - LOG_ERROR("Security Bit Set, Operation Abort"); - } - - return status; -} - -/* Send one command to the AT91SAM flash controller */ -static int at91sam7_flash_command(struct flash_bank *bank, uint8_t cmd, uint16_t pagen) -{ - uint32_t fcr; - struct at91sam7_flash_bank *at91sam7_info = bank->driver_priv; - struct target *target = bank->target; - - fcr = (0x5A << 24) | ((pagen&0x3FF) << 8) | cmd; - target_write_u32(target, MC_FCR[bank->bank_number], fcr); - LOG_DEBUG("Flash command: 0x%" PRIx32 ", flash bank: %i, page number: %u", fcr, bank->bank_number + 1, 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, MC_FSR_EOL, 10)&0x0C) - { - return ERROR_FLASH_OPERATION_FAILED; - } - return ERROR_OK; - } - - if (at91sam7_wait_status_busy(bank, 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 */ -static int at91sam7_read_part_info(struct flash_bank *bank) -{ - struct flash_bank *t_bank = bank; - struct at91sam7_flash_bank *at91sam7_info; - struct target *target = t_bank->target; - - uint16_t bnk, sec; - uint16_t arch; - uint32_t cidr; - uint8_t banks_num = 0; - uint16_t num_nvmbits = 0; - uint16_t sectors_num = 0; - uint16_t pages_per_sector = 0; - uint16_t page_size = 0; - uint32_t ext_freq; - uint32_t bank_size; - uint32_t base_address = 0; - char *target_name = "Unknown"; - - at91sam7_info = t_bank->driver_priv; - - if (at91sam7_info->cidr != 0) - { - /* flash already configured, update clock and check for protected sectors */ - struct flash_bank *fb = bank; - t_bank = fb; - - while (t_bank) - { - /* re-calculate master clock frequency */ - at91sam7_read_clock_info(t_bank); - - /* no timming */ - at91sam7_set_flash_mode(t_bank, FMR_TIMING_NONE); - - /* check protect state */ - at91sam7_protect_check(t_bank); - - t_bank = fb->next; - fb = t_bank; - } - - return ERROR_OK; - } - - /* Read and parse chip identification register */ - target_read_u32(target, DBGU_CIDR, &cidr); - if (cidr == 0) - { - LOG_WARNING("Cannot identify target as an AT91SAM"); - return ERROR_FLASH_OPERATION_FAILED; - } - - if (at91sam7_info->flash_autodetection == 0) - { - /* banks and sectors are already created, based on data from input file */ - struct flash_bank *fb = bank; - t_bank = fb; - while (t_bank) - { - at91sam7_info = t_bank->driver_priv; - - 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; - - /* calculate master clock frequency */ - at91sam7_read_clock_info(t_bank); - - /* no timming */ - at91sam7_set_flash_mode(t_bank, FMR_TIMING_NONE); - - /* check protect state */ - at91sam7_protect_check(t_bank); - - t_bank = fb->next; - fb = t_bank; - } - - return ERROR_OK; - } - - arch = (cidr >> 20)&0x00FF; - - /* check flash size */ - switch ((cidr >> 8)&0x000F) - { - case FLASH_SIZE_8KB: - break; - - case FLASH_SIZE_16KB: - banks_num = 1; - sectors_num = 8; - pages_per_sector = 32; - page_size = 64; - base_address = 0x00100000; - if (arch == 0x70) - { - num_nvmbits = 2; - target_name = "AT91SAM7S161/16"; - } - break; - - case FLASH_SIZE_32KB: - banks_num = 1; - sectors_num = 8; - pages_per_sector = 32; - page_size = 128; - base_address = 0x00100000; - if (arch == 0x70) - { - num_nvmbits = 2; - target_name = "AT91SAM7S321/32"; - } - if (arch == 0x72) - { - num_nvmbits = 3; - target_name = "AT91SAM7SE32"; - } - break; - - case FLASH_SIZE_64KB: - banks_num = 1; - sectors_num = 16; - pages_per_sector = 32; - page_size = 128; - base_address = 0x00100000; - if (arch == 0x70) - { - num_nvmbits = 2; - target_name = "AT91SAM7S64"; - } - break; - - case FLASH_SIZE_128KB: - banks_num = 1; - sectors_num = 8; - pages_per_sector = 64; - page_size = 256; - base_address = 0x00100000; - if (arch == 0x70) - { - num_nvmbits = 2; - target_name = "AT91SAM7S128"; - } - if (arch == 0x71) - { - num_nvmbits = 3; - target_name = "AT91SAM7XC128"; - } - if (arch == 0x72) - { - num_nvmbits = 3; - target_name = "AT91SAM7SE128"; - } - if (arch == 0x75) - { - num_nvmbits = 3; - target_name = "AT91SAM7X128"; - } - break; - - case FLASH_SIZE_256KB: - banks_num = 1; - sectors_num = 16; - pages_per_sector = 64; - page_size = 256; - base_address = 0x00100000; - if (arch == 0x60) - { - num_nvmbits = 3; - target_name = "AT91SAM7A3"; - } - if (arch == 0x70) - { - num_nvmbits = 2; - target_name = "AT91SAM7S256"; - } - if (arch == 0x71) - { - num_nvmbits = 3; - target_name = "AT91SAM7XC256"; - } - if (arch == 0x72) - { - num_nvmbits = 3; - target_name = "AT91SAM7SE256"; - } - if (arch == 0x75) - { - num_nvmbits = 3; - target_name = "AT91SAM7X256"; - } - break; - - case FLASH_SIZE_512KB: - banks_num = 2; - sectors_num = 16; - pages_per_sector = 64; - page_size = 256; - base_address = 0x00100000; - if (arch == 0x70) - { - num_nvmbits = 2; - target_name = "AT91SAM7S512"; - } - if (arch == 0x71) - { - num_nvmbits = 3; - target_name = "AT91SAM7XC512"; - } - if (arch == 0x72) - { - num_nvmbits = 3; - target_name = "AT91SAM7SE512"; - } - if (arch == 0x75) - { - num_nvmbits = 3; - target_name = "AT91SAM7X512"; - } - break; - - case FLASH_SIZE_1024KB: - break; - - case FLASH_SIZE_2048KB: - break; - } - - if (strcmp(target_name, "Unknown") == 0) - { - LOG_ERROR("Target autodetection failed! Please specify target parameters in configuration file"); - return ERROR_FLASH_OPERATION_FAILED; - } - - ext_freq = at91sam7_info->ext_freq; - - /* calculate bank size */ - bank_size = sectors_num * pages_per_sector * page_size; - - for (bnk = 0; bnk < banks_num; bnk++) - { - if (bnk > 0) - { - /* create a new flash bank element */ - struct flash_bank *fb = malloc(sizeof(struct flash_bank)); - fb->target = target; - fb->driver = bank->driver; - fb->driver_priv = malloc(sizeof(struct at91sam7_flash_bank)); - fb->next = NULL; - - /* link created bank in 'flash_banks' list and redirect t_bank */ - t_bank->next = fb; - t_bank = fb; - } - - t_bank->bank_number = bnk; - t_bank->base = base_address + bnk * bank_size; - t_bank->size = bank_size; - t_bank->chip_width = 0; - t_bank->bus_width = 4; - t_bank->num_sectors = sectors_num; - - /* allocate sectors */ - t_bank->sectors = malloc(sectors_num * sizeof(struct flash_sector)); - for (sec = 0; sec < sectors_num; sec++) - { - t_bank->sectors[sec].offset = sec * pages_per_sector * page_size; - t_bank->sectors[sec].size = pages_per_sector * page_size; - t_bank->sectors[sec].is_erased = -1; - t_bank->sectors[sec].is_protected = -1; - } - - at91sam7_info = t_bank->driver_priv; - - 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; - - at91sam7_info->target_name = target_name; - at91sam7_info->flashmode = 0; - at91sam7_info->ext_freq = ext_freq; - at91sam7_info->num_nvmbits = num_nvmbits; - at91sam7_info->num_nvmbits_on = 0; - at91sam7_info->pagesize = page_size; - at91sam7_info->pages_per_sector = pages_per_sector; - - /* calculate master clock frequency */ - at91sam7_read_clock_info(t_bank); - - /* no timming */ - at91sam7_set_flash_mode(t_bank, FMR_TIMING_NONE); - - /* check protect state */ - at91sam7_protect_check(t_bank); - } - - LOG_DEBUG("nvptyp: 0x%3.3x, arch: 0x%4.4x", at91sam7_info->cidr_nvptyp, at91sam7_info->cidr_arch); - - return ERROR_OK; -} - -static int at91sam7_erase_check(struct flash_bank *bank) -{ - struct target *target = bank->target; - uint16_t retval; - uint32_t blank; - uint16_t fast_check; - uint8_t *buffer; - uint16_t nSector; - uint16_t nByte; - - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - /* Configure the flash controller timing */ - at91sam7_read_clock_info(bank); - at91sam7_set_flash_mode(bank, FMR_TIMING_FLASH); - - fast_check = 1; - for (nSector = 0; nSector < bank->num_sectors; nSector++) - { - retval = target_blank_check_memory(target, bank->base + bank->sectors[nSector].offset, - bank->sectors[nSector].size, &blank); - if (retval != ERROR_OK) - { - fast_check = 0; - break; - } - if (blank == 0xFF) - bank->sectors[nSector].is_erased = 1; - else - bank->sectors[nSector].is_erased = 0; - } - - if (fast_check) - { - return ERROR_OK; - } - - LOG_USER("Running slow fallback erase check - add working memory"); - - buffer = malloc(bank->sectors[0].size); - for (nSector = 0; nSector < bank->num_sectors; nSector++) - { - bank->sectors[nSector].is_erased = 1; - retval = target_read_memory(target, bank->base + bank->sectors[nSector].offset, 4, - bank->sectors[nSector].size/4, buffer); - if (retval != ERROR_OK) - return retval; - - for (nByte = 0; nByte < bank->sectors[nSector].size; nByte++) - { - if (buffer[nByte] != 0xFF) - { - bank->sectors[nSector].is_erased = 0; - break; - } - } - } - free(buffer); - - return ERROR_OK; -} - -static int at91sam7_protect_check(struct flash_bank *bank) -{ - uint8_t lock_pos, gpnvm_pos; - uint32_t status; - - struct at91sam7_flash_bank *at91sam7_info = bank->driver_priv; - - if (at91sam7_info->cidr == 0) - { - return ERROR_FLASH_BANK_NOT_PROBED; - } - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - status = at91sam7_get_flash_status(bank->target, bank->bank_number); - at91sam7_info->lockbits = (status >> 16); - - at91sam7_info->num_lockbits_on = 0; - for (lock_pos = 0; lock_pos < bank->num_sectors; lock_pos++) - { - if (((status >> (16 + lock_pos))&(0x0001)) == 1) - { - at91sam7_info->num_lockbits_on++; - bank->sectors[lock_pos].is_protected = 1; - } - else - bank->sectors[lock_pos].is_protected = 0; - } - - /* GPNVM and SECURITY bits apply only for MC_FSR of EFC0 */ - status = at91sam7_get_flash_status(bank->target, 0); - - at91sam7_info->securitybit = (status >> 4)&0x01; - at91sam7_info->nvmbits = (status >> 8)&0xFF; - - at91sam7_info->num_nvmbits_on = 0; - for (gpnvm_pos = 0; gpnvm_pos < at91sam7_info->num_nvmbits; gpnvm_pos++) - { - if (((status >> (8 + gpnvm_pos))&(0x01)) == 1) - { - at91sam7_info->num_nvmbits_on++; - } - } - - return ERROR_OK; -} - -FLASH_BANK_COMMAND_HANDLER(at91sam7_flash_bank_command) -{ - struct flash_bank *t_bank = bank; - struct at91sam7_flash_bank *at91sam7_info; - struct target *target = t_bank->target; - - uint32_t base_address; - uint32_t bank_size; - uint32_t ext_freq = 0; - - int chip_width; - int bus_width; - int banks_num; - int num_sectors; - - uint16_t pages_per_sector; - uint16_t page_size; - uint16_t num_nvmbits; - - char *target_name; - - int bnk, sec; - - at91sam7_info = malloc(sizeof(struct at91sam7_flash_bank)); - t_bank->driver_priv = at91sam7_info; - - /* part wasn't probed for info yet */ - at91sam7_info->cidr = 0; - at91sam7_info->flashmode = 0; - at91sam7_info->ext_freq = 0; - at91sam7_info->flash_autodetection = 0; - - if (CMD_ARGC < 13) - { - at91sam7_info->flash_autodetection = 1; - return ERROR_OK; - } - - COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], base_address); - - COMMAND_PARSE_NUMBER(int, CMD_ARGV[3], chip_width); - COMMAND_PARSE_NUMBER(int, CMD_ARGV[4], bus_width); - - COMMAND_PARSE_NUMBER(int, CMD_ARGV[8], banks_num); - COMMAND_PARSE_NUMBER(int, CMD_ARGV[9], num_sectors); - COMMAND_PARSE_NUMBER(u16, CMD_ARGV[10], pages_per_sector); - COMMAND_PARSE_NUMBER(u16, CMD_ARGV[11], page_size); - COMMAND_PARSE_NUMBER(u16, CMD_ARGV[12], num_nvmbits); - - if (CMD_ARGC == 14) { - unsigned long freq; - COMMAND_PARSE_NUMBER(ulong, CMD_ARGV[13], freq); - ext_freq = freq * 1000; - at91sam7_info->ext_freq = ext_freq; - } - - if ((bus_width == 0) || (banks_num == 0) || (num_sectors == 0) || - (pages_per_sector == 0) || (page_size == 0) || (num_nvmbits == 0)) - { - at91sam7_info->flash_autodetection = 1; - return ERROR_OK; - } - - target_name = calloc(strlen(CMD_ARGV[7]) + 1, sizeof(char)); - strcpy(target_name, CMD_ARGV[7]); - - /* calculate bank size */ - bank_size = num_sectors * pages_per_sector * page_size; - - for (bnk = 0; bnk < banks_num; bnk++) - { - if (bnk > 0) - { - /* create a new bank element */ - struct flash_bank *fb = malloc(sizeof(struct flash_bank)); - fb->target = target; - fb->driver = bank->driver; - fb->driver_priv = malloc(sizeof(struct at91sam7_flash_bank)); - fb->next = NULL; - - /* link created bank in 'flash_banks' list and redirect t_bank */ - t_bank->next = fb; - t_bank = fb; - } - - t_bank->bank_number = bnk; - t_bank->base = base_address + bnk * bank_size; - t_bank->size = bank_size; - t_bank->chip_width = chip_width; - t_bank->bus_width = bus_width; - t_bank->num_sectors = num_sectors; - - /* allocate sectors */ - t_bank->sectors = malloc(num_sectors * sizeof(struct flash_sector)); - for (sec = 0; sec < num_sectors; sec++) - { - t_bank->sectors[sec].offset = sec * pages_per_sector * page_size; - t_bank->sectors[sec].size = pages_per_sector * page_size; - t_bank->sectors[sec].is_erased = -1; - t_bank->sectors[sec].is_protected = -1; - } - - at91sam7_info = t_bank->driver_priv; - - at91sam7_info->target_name = target_name; - at91sam7_info->flashmode = 0; - at91sam7_info->ext_freq = ext_freq; - at91sam7_info->num_nvmbits = num_nvmbits; - at91sam7_info->num_nvmbits_on = 0; - at91sam7_info->pagesize = page_size; - at91sam7_info->pages_per_sector = pages_per_sector; - } - - return ERROR_OK; -} - -static int at91sam7_erase(struct flash_bank *bank, int first, int last) -{ - struct at91sam7_flash_bank *at91sam7_info = bank->driver_priv; - int sec; - uint32_t nbytes, pos; - uint8_t *buffer; - uint8_t erase_all; - - if (at91sam7_info->cidr == 0) - { - return ERROR_FLASH_BANK_NOT_PROBED; - } - - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - if ((first < 0) || (last < first) || (last >= bank->num_sectors)) - { - return ERROR_FLASH_SECTOR_INVALID; - } - - erase_all = 0; - if ((first == 0) && (last == (bank->num_sectors-1))) - { - erase_all = 1; - } - - /* Configure the flash controller timing */ - at91sam7_read_clock_info(bank); - at91sam7_set_flash_mode(bank, FMR_TIMING_FLASH); - - if (erase_all) - { - if (at91sam7_flash_command(bank, EA, 0) != ERROR_OK) - { - return ERROR_FLASH_OPERATION_FAILED; - } - } - else - { - /* allocate and clean buffer */ - nbytes = (last - first + 1) * bank->sectors[first].size; - buffer = malloc(nbytes * sizeof(uint8_t)); - for (pos = 0; pos < nbytes; pos++) - { - buffer[pos] = 0xFF; - } - - if (at91sam7_write(bank, buffer, bank->sectors[first].offset, nbytes) != ERROR_OK) - { - return ERROR_FLASH_OPERATION_FAILED; - } - - free(buffer); - } - - /* mark erased sectors */ - for (sec = first; sec <= last; sec++) - { - bank->sectors[sec].is_erased = 1; - } - - return ERROR_OK; -} - -static int at91sam7_protect(struct flash_bank *bank, int set, int first, int last) -{ - uint32_t cmd; - int sector; - uint32_t pagen; - - struct at91sam7_flash_bank *at91sam7_info = bank->driver_priv; - - if (at91sam7_info->cidr == 0) - { - return ERROR_FLASH_BANK_NOT_PROBED; - } - - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - if ((first < 0) || (last < first) || (last >= bank->num_sectors)) - { - return ERROR_FLASH_SECTOR_INVALID; - } - - /* Configure the flash controller timing */ - at91sam7_read_clock_info(bank); - at91sam7_set_flash_mode(bank, FMR_TIMING_NVBITS); - - for (sector = first; sector <= last; sector++) - { - if (set) - cmd = SLB; - else - cmd = CLB; - - /* if we lock a page from one sector then entire sector will be locked, also, - * if we unlock a page from a locked sector, entire sector will be unlocked */ - pagen = sector * at91sam7_info->pages_per_sector; - - if (at91sam7_flash_command(bank, cmd, pagen) != ERROR_OK) - { - return ERROR_FLASH_OPERATION_FAILED; - } - } - - at91sam7_protect_check(bank); - - return ERROR_OK; -} - -static int at91sam7_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) -{ - int retval; - struct at91sam7_flash_bank *at91sam7_info = bank->driver_priv; - struct target *target = bank->target; - uint32_t dst_min_alignment, wcount, bytes_remaining = count; - uint32_t first_page, last_page, pagen, buffer_pos; - - if (at91sam7_info->cidr == 0) - { - return ERROR_FLASH_BANK_NOT_PROBED; - } - - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - if (offset + count > bank->size) - return ERROR_FLASH_DST_OUT_OF_BANK; - - dst_min_alignment = at91sam7_info->pagesize; - - if (offset % dst_min_alignment) - { - LOG_WARNING("offset 0x%" PRIx32 " breaks required alignment 0x%" PRIx32 "", 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 = DIV_ROUND_UP(offset + count, dst_min_alignment); - - LOG_DEBUG("first_page: %i, last_page: %i, count %i", (int)first_page, (int)last_page, (int)count); - - /* Configure the flash controller timing */ - at91sam7_read_clock_info(bank); - at91sam7_set_flash_mode(bank, FMR_TIMING_FLASH); - - 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 = DIV_ROUND_UP(count,4); - if ((retval = target_write_memory(target, bank->base + pagen*dst_min_alignment, 4, wcount, buffer + buffer_pos)) != ERROR_OK) - { - return retval; - } - - /* Send Write Page command to Flash Controller */ - if (at91sam7_flash_command(bank, WP, pagen) != ERROR_OK) - { - return ERROR_FLASH_OPERATION_FAILED; - } - LOG_DEBUG("Write flash bank:%i page number:%" PRIi32 "", bank->bank_number, pagen); - } - - return ERROR_OK; -} - -static int at91sam7_probe(struct flash_bank *bank) -{ - /* we can't probe on an at91sam7 - * if this is an at91sam7, it has the configured flash */ - int retval; - - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - retval = at91sam7_read_part_info(bank); - if (retval != ERROR_OK) - return retval; - - return ERROR_OK; -} - -static int at91sam7_info(struct flash_bank *bank, char *buf, int buf_size) -{ - int printed; - struct at91sam7_flash_bank *at91sam7_info = bank->driver_priv; - - if (at91sam7_info->cidr == 0) - { - return ERROR_FLASH_BANK_NOT_PROBED; - } - - printed = snprintf(buf, buf_size, - "\n at91sam7 driver information: Chip is %s\n", - at91sam7_info->target_name); - - buf += printed; - buf_size -= printed; - - printed = snprintf(buf, - buf_size, - " Cidr: 0x%8.8" PRIx32 " | Arch: 0x%4.4x | Eproc: %s | Version: 0x%3.3x | Flashsize: 0x%8.8" PRIx32 "\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): %u KHz | External clock: %u KHz\n", - (unsigned)(at91sam7_info->mck_freq / 1000), (unsigned)(at91sam7_info->ext_freq / 1000)); - - buf += printed; - buf_size -= printed; - - printed = snprintf(buf, buf_size, - " Pagesize: %i bytes | Lockbits(%i): %i 0x%4.4x | Pages in lock region: %i \n", - at91sam7_info->pagesize, bank->num_sectors, at91sam7_info->num_lockbits_on, - at91sam7_info->lockbits, at91sam7_info->pages_per_sector*at91sam7_info->num_lockbits_on); - - buf += printed; - buf_size -= printed; - - printed = snprintf(buf, buf_size, - " Securitybit: %i | Nvmbits(%i): %i 0x%1.1x\n", - at91sam7_info->securitybit, at91sam7_info->num_nvmbits, - at91sam7_info->num_nvmbits_on, at91sam7_info->nvmbits); - - buf += printed; - buf_size -= printed; - - return ERROR_OK; -} - -/* -* On AT91SAM7S: When the gpnvm bits are set with -* > at91sam7 gpnvm 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. -*/ -COMMAND_HANDLER(at91sam7_handle_gpnvm_command) -{ - struct flash_bank *bank; - int bit; - uint8_t flashcmd; - uint32_t status; - struct at91sam7_flash_bank *at91sam7_info; - int retval; - - if (CMD_ARGC != 2) - { - command_print(CMD_CTX, "at91sam7 gpnvm "); - return ERROR_OK; - } - - bank = get_flash_bank_by_num_noprobe(0); - if (bank == NULL) - { - return ERROR_FLASH_BANK_INVALID; - } - if (strcmp(bank->driver->name, "at91sam7")) - { - command_print(CMD_CTX, "not an at91sam7 flash bank '%s'", CMD_ARGV[0]); - return ERROR_FLASH_BANK_INVALID; - } - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("target has to be halted to perform flash operation"); - return ERROR_TARGET_NOT_HALTED; - } - - if (strcmp(CMD_ARGV[1], "set") == 0) - { - flashcmd = SGPB; - } - else if (strcmp(CMD_ARGV[1], "clear") == 0) - { - flashcmd = CGPB; - } - else - { - return ERROR_COMMAND_SYNTAX_ERROR; - } - - at91sam7_info = bank->driver_priv; - if (at91sam7_info->cidr == 0) - { - retval = at91sam7_read_part_info(bank); - if (retval != ERROR_OK) - { - return retval; - } - } - - COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], bit); - if ((bit < 0) || (bit >= at91sam7_info->num_nvmbits)) - { - command_print(CMD_CTX, "gpnvm bit '#%s' is out of bounds for target %s", CMD_ARGV[0], at91sam7_info->target_name); - return ERROR_OK; - } - - /* Configure the flash controller timing */ - at91sam7_read_clock_info(bank); - at91sam7_set_flash_mode(bank, FMR_TIMING_NVBITS); - - if (at91sam7_flash_command(bank, flashcmd, bit) != ERROR_OK) - { - return ERROR_FLASH_OPERATION_FAILED; - } - - /* GPNVM and SECURITY bits apply only for MC_FSR of EFC0 */ - status = at91sam7_get_flash_status(bank->target, 0); - LOG_DEBUG("at91sam7_handle_gpnvm_command: cmd 0x%x, value %d, status 0x%" PRIx32 " \n", flashcmd, bit, status); - - /* check protect state */ - at91sam7_protect_check(bank); - - return ERROR_OK; -} - -static const struct command_registration at91sam7_exec_command_handlers[] = { - { - .name = "gpnvm", - .handler = &at91sam7_handle_gpnvm_command, - .mode = COMMAND_EXEC, - .usage = "gpnvm set | clear, " - "set or clear one gpnvm bit", - }, - COMMAND_REGISTRATION_DONE -}; -static const struct command_registration at91sam7_command_handlers[] = { - { - .name = "at91sam7", - .mode = COMMAND_ANY, - .help = "at91sam7 flash command group", - .chain = at91sam7_exec_command_handlers, - }, - COMMAND_REGISTRATION_DONE -}; - -struct flash_driver at91sam7_flash = { - .name = "at91sam7", - .commands = at91sam7_command_handlers, - .flash_bank_command = &at91sam7_flash_bank_command, - .erase = &at91sam7_erase, - .protect = &at91sam7_protect, - .write = &at91sam7_write, - .probe = &at91sam7_probe, - .auto_probe = &at91sam7_probe, - .erase_check = &at91sam7_erase_check, - .protect_check = &at91sam7_protect_check, - .info = &at91sam7_info, - }; diff --git a/src/flash/at91sam7.h b/src/flash/at91sam7.h deleted file mode 100644 index 45106869..00000000 --- a/src/flash/at91sam7.h +++ /dev/null @@ -1,118 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2006 by Magnus Lundin * - * lundin@mlu.mine.nu * - * * - * Copyright (C) 2006 by Gheorghe Guran (atlas) * - * * - * 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 AT91SAM7_H -#define AT91SAM7_H - -#include "flash.h" - -struct at91sam7_flash_bank -{ - /* chip id register */ - uint32_t cidr; - uint16_t cidr_ext; - uint16_t cidr_nvptyp; - uint16_t cidr_arch; - uint16_t cidr_sramsiz; - uint16_t cidr_nvpsiz; - uint16_t cidr_nvpsiz2; - uint16_t cidr_eproc; - uint16_t cidr_version; - char *target_name; - - /* flash auto-detection */ - uint8_t flash_autodetection; - - /* flash geometry */ - uint16_t pages_per_sector; - uint16_t pagesize; - uint16_t pages_in_lockregion; - - /* nv memory bits */ - uint16_t num_lockbits_on; - uint16_t lockbits; - uint16_t num_nvmbits; - uint16_t num_nvmbits_on; - uint16_t nvmbits; - uint8_t securitybit; - - /* 0: not init - * 1: fmcn for nvbits (1uS) - * 2: fmcn for flash (1.5uS) */ - uint8_t flashmode; - - /* main clock status */ - uint8_t mck_valid; - uint32_t mck_freq; - - /* external clock frequency */ - uint32_t ext_freq; - -}; - - -/* AT91SAM7 control registers */ -#define DBGU_CIDR 0xFFFFF240 -#define CKGR_MCFR 0xFFFFFC24 -#define CKGR_MOR 0xFFFFFC20 -#define CKGR_MCFR_MAINRDY 0x10000 -#define CKGR_PLLR 0xFFFFFC2c -#define CKGR_PLLR_DIV 0xff -#define CKGR_PLLR_MUL 0x07ff0000 -#define PMC_MCKR 0xFFFFFC30 -#define PMC_MCKR_CSS 0x03 -#define PMC_MCKR_PRES 0x1c - -/* Flash Controller Commands */ -#define WP 0x01 -#define SLB 0x02 -#define WPL 0x03 -#define CLB 0x04 -#define EA 0x08 -#define SGPB 0x0B -#define CGPB 0x0D -#define SSB 0x0F - -/* MC_FSR bit definitions */ -#define MC_FSR_FRDY 1 -#define MC_FSR_EOL 2 - -/* AT91SAM7 constants */ -#define RC_FREQ 32000 - -/* Flash timing modes */ -#define FMR_TIMING_NONE 0 -#define FMR_TIMING_NVBITS 1 -#define FMR_TIMING_FLASH 2 - -/* Flash size constants */ -#define FLASH_SIZE_8KB 1 -#define FLASH_SIZE_16KB 2 -#define FLASH_SIZE_32KB 3 -#define FLASH_SIZE_64KB 5 -#define FLASH_SIZE_128KB 7 -#define FLASH_SIZE_256KB 9 -#define FLASH_SIZE_512KB 10 -#define FLASH_SIZE_1024KB 12 -#define FLASH_SIZE_2048KB 14 - -#endif /* AT91SAM7_H */ diff --git a/src/flash/avrf.c b/src/flash/avrf.c deleted file mode 100644 index 687dd4b5..00000000 --- a/src/flash/avrf.c +++ /dev/null @@ -1,483 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2009 by Simon Qian * - * SimonQian@SimonQian.com * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "avrf.h" -#include "avrt.h" -#include "flash.h" - - -/* AVR_JTAG_Instructions */ -#define AVR_JTAG_INS_LEN 4 -// Public Instructions: -#define AVR_JTAG_INS_EXTEST 0x00 -#define AVR_JTAG_INS_IDCODE 0x01 -#define AVR_JTAG_INS_SAMPLE_PRELOAD 0x02 -#define AVR_JTAG_INS_BYPASS 0x0F -// AVR Specified Public Instructions: -#define AVR_JTAG_INS_AVR_RESET 0x0C -#define AVR_JTAG_INS_PROG_ENABLE 0x04 -#define AVR_JTAG_INS_PROG_COMMANDS 0x05 -#define AVR_JTAG_INS_PROG_PAGELOAD 0x06 -#define AVR_JTAG_INS_PROG_PAGEREAD 0x07 - -// Data Registers: -#define AVR_JTAG_REG_Bypass_Len 1 -#define AVR_JTAG_REG_DeviceID_Len 32 - -#define AVR_JTAG_REG_Reset_Len 1 -#define AVR_JTAG_REG_JTAGID_Len 32 -#define AVR_JTAG_REG_ProgrammingEnable_Len 16 -#define AVR_JTAG_REG_ProgrammingCommand_Len 15 -#define AVR_JTAG_REG_FlashDataByte_Len 16 - -struct avrf_type avft_chips_info[] = -{ -// name, chip_id, flash_page_size, flash_page_num, eeprom_page_size, eeprom_page_num - {"atmega128", 0x9702, 256, 512, 8, 512}, -}; - -int avr_jtag_sendinstr(struct jtag_tap *tap, uint8_t *ir_in, uint8_t ir_out); -int avr_jtag_senddat(struct jtag_tap *tap, uint32_t *dr_in, uint32_t dr_out, int len); - -int mcu_write_ir(struct jtag_tap *tap, uint8_t *ir_in, uint8_t *ir_out, int ir_len, int rti); -int mcu_write_dr(struct jtag_tap *tap, uint8_t *ir_in, uint8_t *ir_out, int dr_len, int rti); -int mcu_write_ir_u8(struct jtag_tap *tap, uint8_t *ir_in, uint8_t ir_out, int ir_len, int rti); -int mcu_write_dr_u8(struct jtag_tap *tap, uint8_t *ir_in, uint8_t ir_out, int dr_len, int rti); -int mcu_write_ir_u16(struct jtag_tap *tap, uint16_t *ir_in, uint16_t ir_out, int ir_len, int rti); -int mcu_write_dr_u16(struct jtag_tap *tap, uint16_t *ir_in, uint16_t ir_out, int dr_len, int rti); -int mcu_write_ir_u32(struct jtag_tap *tap, uint32_t *ir_in, uint32_t ir_out, int ir_len, int rti); -int mcu_write_dr_u32(struct jtag_tap *tap, uint32_t *ir_in, uint32_t ir_out, int dr_len, int rti); -int mcu_execute_queue(void); - -/* avr program functions */ -static int avr_jtag_reset(struct avr_common *avr, uint32_t reset) -{ - avr_jtag_sendinstr(avr->jtag_info.tap, NULL, AVR_JTAG_INS_AVR_RESET); - avr_jtag_senddat(avr->jtag_info.tap, NULL, reset ,AVR_JTAG_REG_Reset_Len); - - return ERROR_OK; -} - -static int avr_jtag_read_jtagid(struct avr_common *avr, uint32_t *id) -{ - avr_jtag_sendinstr(avr->jtag_info.tap, NULL, AVR_JTAG_INS_IDCODE); - avr_jtag_senddat(avr->jtag_info.tap, id, 0, AVR_JTAG_REG_JTAGID_Len); - - return ERROR_OK; -} - -static int avr_jtagprg_enterprogmode(struct avr_common *avr) -{ - avr_jtag_reset(avr, 1); - - avr_jtag_sendinstr(avr->jtag_info.tap, NULL, AVR_JTAG_INS_PROG_ENABLE); - avr_jtag_senddat(avr->jtag_info.tap, NULL, 0xA370, AVR_JTAG_REG_ProgrammingEnable_Len); - - return ERROR_OK; -} - -static int avr_jtagprg_leaveprogmode(struct avr_common *avr) -{ - avr_jtag_sendinstr(avr->jtag_info.tap, NULL, AVR_JTAG_INS_PROG_COMMANDS); - avr_jtag_senddat(avr->jtag_info.tap, NULL, 0x2300, AVR_JTAG_REG_ProgrammingCommand_Len); - avr_jtag_senddat(avr->jtag_info.tap, NULL, 0x3300, AVR_JTAG_REG_ProgrammingCommand_Len); - - avr_jtag_sendinstr(avr->jtag_info.tap, NULL, AVR_JTAG_INS_PROG_ENABLE); - avr_jtag_senddat(avr->jtag_info.tap, NULL, 0, AVR_JTAG_REG_ProgrammingEnable_Len); - - avr_jtag_reset(avr, 0); - - return ERROR_OK; -} - -static int avr_jtagprg_chiperase(struct avr_common *avr) -{ - uint32_t poll_value; - - avr_jtag_sendinstr(avr->jtag_info.tap, NULL, AVR_JTAG_INS_PROG_COMMANDS); - avr_jtag_senddat(avr->jtag_info.tap, NULL, 0x2380, AVR_JTAG_REG_ProgrammingCommand_Len); - avr_jtag_senddat(avr->jtag_info.tap, NULL, 0x3180, AVR_JTAG_REG_ProgrammingCommand_Len); - avr_jtag_senddat(avr->jtag_info.tap, NULL, 0x3380, AVR_JTAG_REG_ProgrammingCommand_Len); - avr_jtag_senddat(avr->jtag_info.tap, NULL, 0x3380, AVR_JTAG_REG_ProgrammingCommand_Len); - - do { - poll_value = 0; - avr_jtag_senddat(avr->jtag_info.tap, &poll_value, 0x3380, AVR_JTAG_REG_ProgrammingCommand_Len); - if (ERROR_OK != mcu_execute_queue()) - { - return ERROR_FAIL; - } - LOG_DEBUG("poll_value = 0x%04" PRIx32 "", poll_value); - } while (!(poll_value & 0x0200)); - - return ERROR_OK; -} - -static int avr_jtagprg_writeflashpage(struct avr_common *avr, uint8_t *page_buf, uint32_t buf_size, uint32_t addr, uint32_t page_size) -{ - uint32_t i, poll_value; - - avr_jtag_sendinstr(avr->jtag_info.tap, NULL, AVR_JTAG_INS_PROG_COMMANDS); - avr_jtag_senddat(avr->jtag_info.tap, NULL, 0x2310, AVR_JTAG_REG_ProgrammingCommand_Len); - - // load addr high byte - avr_jtag_senddat(avr->jtag_info.tap, NULL, 0x0700 | ((addr >> 9) & 0xFF), AVR_JTAG_REG_ProgrammingCommand_Len); - - // load addr low byte - avr_jtag_senddat(avr->jtag_info.tap, NULL, 0x0300 | ((addr >> 1) & 0xFF), AVR_JTAG_REG_ProgrammingCommand_Len); - - avr_jtag_sendinstr(avr->jtag_info.tap, NULL, AVR_JTAG_INS_PROG_PAGELOAD); - - for (i = 0; i < page_size; i++) - { - if (i < buf_size) - { - avr_jtag_senddat(avr->jtag_info.tap, NULL, page_buf[i], 8); - } - else - { - avr_jtag_senddat(avr->jtag_info.tap, NULL, 0xFF, 8); - } - } - - avr_jtag_sendinstr(avr->jtag_info.tap, NULL, AVR_JTAG_INS_PROG_COMMANDS); - - avr_jtag_senddat(avr->jtag_info.tap, NULL, 0x3700, AVR_JTAG_REG_ProgrammingCommand_Len); - avr_jtag_senddat(avr->jtag_info.tap, NULL, 0x3500, AVR_JTAG_REG_ProgrammingCommand_Len); - avr_jtag_senddat(avr->jtag_info.tap, NULL, 0x3700, AVR_JTAG_REG_ProgrammingCommand_Len); - avr_jtag_senddat(avr->jtag_info.tap, NULL, 0x3700, AVR_JTAG_REG_ProgrammingCommand_Len); - - do { - poll_value = 0; - avr_jtag_senddat(avr->jtag_info.tap, &poll_value, 0x3700, AVR_JTAG_REG_ProgrammingCommand_Len); - if (ERROR_OK != mcu_execute_queue()) - { - return ERROR_FAIL; - } - LOG_DEBUG("poll_value = 0x%04" PRIx32 "", poll_value); - } while (!(poll_value & 0x0200)); - - return ERROR_OK; -} - -FLASH_BANK_COMMAND_HANDLER(avrf_flash_bank_command) -{ - struct avrf_flash_bank *avrf_info; - - if (CMD_ARGC < 6) - { - LOG_WARNING("incomplete flash_bank avr configuration"); - return ERROR_FLASH_BANK_INVALID; - } - - avrf_info = malloc(sizeof(struct avrf_flash_bank)); - bank->driver_priv = avrf_info; - - avrf_info->probed = 0; - - return ERROR_OK; -} - -static int avrf_erase(struct flash_bank *bank, int first, int last) -{ - LOG_INFO("%s", __FUNCTION__); - return ERROR_OK; -} - -static int avrf_protect(struct flash_bank *bank, int set, int first, int last) -{ - LOG_INFO("%s", __FUNCTION__); - return ERROR_OK; -} - -static int avrf_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) -{ - struct target *target = bank->target; - struct avr_common *avr = target->arch_info; - uint32_t cur_size, cur_buffer_size, page_size; - - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - page_size = bank->sectors[0].size; - if ((offset % page_size) != 0) - { - LOG_WARNING("offset 0x%" PRIx32 " breaks required %" PRIu32 "-byte alignment", offset, page_size); - return ERROR_FLASH_DST_BREAKS_ALIGNMENT; - } - - LOG_DEBUG("offset is 0x%08" PRIx32 "", offset); - LOG_DEBUG("count is %" PRId32 "", count); - - if (ERROR_OK != avr_jtagprg_enterprogmode(avr)) - { - return ERROR_FAIL; - } - - cur_size = 0; - while (count > 0) - { - if (count > page_size) - { - cur_buffer_size = page_size; - } - else - { - cur_buffer_size = count; - } - avr_jtagprg_writeflashpage(avr, buffer + cur_size, cur_buffer_size, offset + cur_size, page_size); - count -= cur_buffer_size; - cur_size += cur_buffer_size; - - keep_alive(); - } - - return avr_jtagprg_leaveprogmode(avr); -} - -#define EXTRACT_MFG(X) (((X) & 0xffe) >> 1) -#define EXTRACT_PART(X) (((X) & 0xffff000) >> 12) -#define EXTRACT_VER(X) (((X) & 0xf0000000) >> 28) -static int avrf_probe(struct flash_bank *bank) -{ - struct target *target = bank->target; - struct avrf_flash_bank *avrf_info = bank->driver_priv; - struct avr_common *avr = target->arch_info; - struct avrf_type *avr_info = NULL; - int i; - uint32_t device_id; - - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - avrf_info->probed = 0; - - avr_jtag_read_jtagid(avr, &device_id); - if (ERROR_OK != mcu_execute_queue()) - { - return ERROR_FAIL; - } - - LOG_INFO("device id = 0x%08" PRIx32 "", device_id); - if (EXTRACT_MFG(device_id) != 0x1F) - { - LOG_ERROR("0x%" PRIx32 " is invalid Manufacturer for avr, 0x%X is expected", EXTRACT_MFG(device_id), 0x1F); - } - - for (i = 0; i < (int)ARRAY_SIZE(avft_chips_info); i++) - { - if (avft_chips_info[i].chip_id == EXTRACT_PART(device_id)) - { - avr_info = &avft_chips_info[i]; - LOG_INFO("target device is %s", avr_info->name); - break; - } - } - - if (avr_info != NULL) - { - // chip found - bank->base = 0x00000000; - bank->size = (avr_info->flash_page_size * avr_info->flash_page_num); - bank->num_sectors = avr_info->flash_page_num; - bank->sectors = malloc(sizeof(struct flash_sector) * avr_info->flash_page_num); - - for (i = 0; i < avr_info->flash_page_num; i++) - { - bank->sectors[i].offset = i * avr_info->flash_page_size; - bank->sectors[i].size = avr_info->flash_page_size; - bank->sectors[i].is_erased = -1; - bank->sectors[i].is_protected = 1; - } - - avrf_info->probed = 1; - return ERROR_OK; - } - else - { - // chip not supported - LOG_ERROR("0x%" PRIx32 " is not support for avr", EXTRACT_PART(device_id)); - - avrf_info->probed = 1; - return ERROR_FAIL; - } -} - -static int avrf_auto_probe(struct flash_bank *bank) -{ - struct avrf_flash_bank *avrf_info = bank->driver_priv; - if (avrf_info->probed) - return ERROR_OK; - return avrf_probe(bank); -} - -static int avrf_protect_check(struct flash_bank *bank) -{ - LOG_INFO("%s", __FUNCTION__); - return ERROR_OK; -} - -static int avrf_info(struct flash_bank *bank, char *buf, int buf_size) -{ - struct target *target = bank->target; - struct avr_common *avr = target->arch_info; - struct avrf_type *avr_info = NULL; - int i; - uint32_t device_id; - - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - avr_jtag_read_jtagid(avr, &device_id); - if (ERROR_OK != mcu_execute_queue()) - { - return ERROR_FAIL; - } - - LOG_INFO("device id = 0x%08" PRIx32 "", device_id); - if (EXTRACT_MFG(device_id) != 0x1F) - { - LOG_ERROR("0x%" PRIx32 " is invalid Manufacturer for avr, 0x%X is expected", EXTRACT_MFG(device_id), 0x1F); - } - - for (i = 0; i < (int)ARRAY_SIZE(avft_chips_info); i++) - { - if (avft_chips_info[i].chip_id == EXTRACT_PART(device_id)) - { - avr_info = &avft_chips_info[i]; - LOG_INFO("target device is %s", avr_info->name); - - break; - } - } - - if (avr_info != NULL) - { - // chip found - snprintf(buf, buf_size, "%s - Rev: 0x%" PRIx32 "", avr_info->name, EXTRACT_VER(device_id)); - return ERROR_OK; - } - else - { - // chip not supported - snprintf(buf, buf_size, "Cannot identify target as a avr\n"); - return ERROR_FLASH_OPERATION_FAILED; - } -} - -static int avrf_mass_erase(struct flash_bank *bank) -{ - struct target *target = bank->target; - struct avr_common *avr = target->arch_info; - - if (target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - if ((ERROR_OK != avr_jtagprg_enterprogmode(avr)) - || (ERROR_OK != avr_jtagprg_chiperase(avr)) - || (ERROR_OK != avr_jtagprg_leaveprogmode(avr))) - { - return ERROR_FAIL; - } - - return ERROR_OK; -} - -COMMAND_HANDLER(avrf_handle_mass_erase_command) -{ - int i; - - if (CMD_ARGC < 1) - { - command_print(CMD_CTX, "avr mass_erase "); - return ERROR_OK; - } - - struct flash_bank *bank; - int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) - return retval; - - if (avrf_mass_erase(bank) == ERROR_OK) - { - /* set all sectors as erased */ - for (i = 0; i < bank->num_sectors; i++) - { - bank->sectors[i].is_erased = 1; - } - - command_print(CMD_CTX, "avr mass erase complete"); - } - else - { - command_print(CMD_CTX, "avr mass erase failed"); - } - - LOG_DEBUG("%s", __FUNCTION__); - return ERROR_OK; -} - -static const struct command_registration avrf_exec_command_handlers[] = { - { - .name = "mass_erase", - .handler = &avrf_handle_mass_erase_command, - .mode = COMMAND_EXEC, - .help = "erase entire device", - }, - COMMAND_REGISTRATION_DONE -}; -static const struct command_registration avrf_command_handlers[] = { - { - .name = "avrf", - .mode = COMMAND_ANY, - .help = "AVR flash command group", - .chain = avrf_exec_command_handlers, - }, - COMMAND_REGISTRATION_DONE -}; - -struct flash_driver avr_flash = { - .name = "avr", - .commands = avrf_command_handlers, - .flash_bank_command = &avrf_flash_bank_command, - .erase = &avrf_erase, - .protect = &avrf_protect, - .write = &avrf_write, - .probe = &avrf_probe, - .auto_probe = &avrf_auto_probe, - .erase_check = &default_flash_mem_blank_check, - .protect_check = &avrf_protect_check, - .info = &avrf_info, - }; diff --git a/src/flash/avrf.h b/src/flash/avrf.h deleted file mode 100644 index e75d9d7e..00000000 --- a/src/flash/avrf.h +++ /dev/null @@ -1,41 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2009 by Simon Qian * - * SimonQian@SimonQian.com * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ -#ifndef AVRF_H -#define AVRF_H - -#include "types.h" - -struct avrf_type -{ - char name[15]; - uint16_t chip_id; - int flash_page_size; - int flash_page_num; - int eeprom_page_size; - int eeprom_page_num; -}; - -struct avrf_flash_bank -{ - int ppage_size; - int probed; -}; - -#endif /* AVRF_H */ diff --git a/src/flash/cfi.c b/src/flash/cfi.c deleted file mode 100644 index 6dbffb9e..00000000 --- a/src/flash/cfi.c +++ /dev/null @@ -1,2630 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2005, 2007 by Dominic Rath * - * Dominic.Rath@gmx.de * - * Copyright (C) 2009 Michael Schwingen * - * michael@schwingen.org * - * * - * 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 "cfi.h" -#include "non_cfi.h" -#include "armv4_5.h" -#include "binarybuffer.h" -#include "algorithm.h" - - -#define CFI_MAX_BUS_WIDTH 4 -#define CFI_MAX_CHIP_WIDTH 4 - -/* defines internal maximum size for code fragment in cfi_intel_write_block() */ -#define CFI_MAX_INTEL_CODESIZE 256 - -static struct cfi_unlock_addresses cfi_unlock_addresses[] = -{ - [CFI_UNLOCK_555_2AA] = { .unlock1 = 0x555, .unlock2 = 0x2aa }, - [CFI_UNLOCK_5555_2AAA] = { .unlock1 = 0x5555, .unlock2 = 0x2aaa }, -}; - -/* CFI fixups foward declarations */ -static void cfi_fixup_0002_erase_regions(struct flash_bank *flash, void *param); -static void cfi_fixup_0002_unlock_addresses(struct flash_bank *flash, void *param); -static void cfi_fixup_atmel_reversed_erase_regions(struct flash_bank *flash, void *param); - -/* fixup after reading cmdset 0002 primary query table */ -static const struct cfi_fixup cfi_0002_fixups[] = { - {CFI_MFR_SST, 0x00D4, cfi_fixup_0002_unlock_addresses, &cfi_unlock_addresses[CFI_UNLOCK_5555_2AAA]}, - {CFI_MFR_SST, 0x00D5, cfi_fixup_0002_unlock_addresses, &cfi_unlock_addresses[CFI_UNLOCK_5555_2AAA]}, - {CFI_MFR_SST, 0x00D6, cfi_fixup_0002_unlock_addresses, &cfi_unlock_addresses[CFI_UNLOCK_5555_2AAA]}, - {CFI_MFR_SST, 0x00D7, cfi_fixup_0002_unlock_addresses, &cfi_unlock_addresses[CFI_UNLOCK_5555_2AAA]}, - {CFI_MFR_SST, 0x2780, cfi_fixup_0002_unlock_addresses, &cfi_unlock_addresses[CFI_UNLOCK_5555_2AAA]}, - {CFI_MFR_ATMEL, 0x00C8, cfi_fixup_atmel_reversed_erase_regions, NULL}, - {CFI_MFR_FUJITSU, 0x226b, cfi_fixup_0002_unlock_addresses, &cfi_unlock_addresses[CFI_UNLOCK_5555_2AAA]}, - {CFI_MFR_AMIC, 0xb31a, cfi_fixup_0002_unlock_addresses, &cfi_unlock_addresses[CFI_UNLOCK_555_2AA]}, - {CFI_MFR_MX, 0x225b, cfi_fixup_0002_unlock_addresses, &cfi_unlock_addresses[CFI_UNLOCK_555_2AA]}, - {CFI_MFR_AMD, 0x225b, cfi_fixup_0002_unlock_addresses, &cfi_unlock_addresses[CFI_UNLOCK_555_2AA]}, - {CFI_MFR_ANY, CFI_ID_ANY, cfi_fixup_0002_erase_regions, NULL}, - {0, 0, NULL, NULL} -}; - -/* fixup after reading cmdset 0001 primary query table */ -static const struct cfi_fixup cfi_0001_fixups[] = { - {0, 0, NULL, NULL} -}; - -static void cfi_fixup(struct flash_bank *bank, const struct cfi_fixup *fixups) -{ - struct cfi_flash_bank *cfi_info = bank->driver_priv; - const struct cfi_fixup *f; - - for (f = fixups; f->fixup; f++) - { - if (((f->mfr == CFI_MFR_ANY) || (f->mfr == cfi_info->manufacturer)) && - ((f->id == CFI_ID_ANY) || (f->id == cfi_info->device_id))) - { - f->fixup(bank, f->param); - } - } -} - -/* inline uint32_t flash_address(struct flash_bank *bank, int sector, uint32_t offset) */ -static __inline__ uint32_t flash_address(struct flash_bank *bank, int sector, uint32_t offset) -{ - struct cfi_flash_bank *cfi_info = bank->driver_priv; - - if (cfi_info->x16_as_x8) offset *= 2; - - /* while the sector list isn't built, only accesses to sector 0 work */ - if (sector == 0) - return bank->base + offset * bank->bus_width; - else - { - if (!bank->sectors) - { - LOG_ERROR("BUG: sector list not yet built"); - exit(-1); - } - return bank->base + bank->sectors[sector].offset + offset * bank->bus_width; - } - -} - -static void cfi_command(struct flash_bank *bank, uint8_t cmd, uint8_t *cmd_buf) -{ - int i; - - /* clear whole buffer, to ensure bits that exceed the bus_width - * are set to zero - */ - for (i = 0; i < CFI_MAX_BUS_WIDTH; i++) - cmd_buf[i] = 0; - - if (bank->target->endianness == TARGET_LITTLE_ENDIAN) - { - for (i = bank->bus_width; i > 0; i--) - { - *cmd_buf++ = (i & (bank->chip_width - 1)) ? 0x0 : cmd; - } - } - else - { - for (i = 1; i <= bank->bus_width; i++) - { - *cmd_buf++ = (i & (bank->chip_width - 1)) ? 0x0 : cmd; - } - } -} - -/* read unsigned 8-bit value from the bank - * flash banks are expected to be made of similar chips - * the query result should be the same for all - */ -static uint8_t cfi_query_u8(struct flash_bank *bank, int sector, uint32_t offset) -{ - struct target *target = bank->target; - uint8_t data[CFI_MAX_BUS_WIDTH]; - - target_read_memory(target, flash_address(bank, sector, offset), bank->bus_width, 1, data); - - if (bank->target->endianness == TARGET_LITTLE_ENDIAN) - return data[0]; - else - return data[bank->bus_width - 1]; -} - -/* read unsigned 8-bit value from the bank - * in case of a bank made of multiple chips, - * the individual values are ORed - */ -static uint8_t cfi_get_u8(struct flash_bank *bank, int sector, uint32_t offset) -{ - struct target *target = bank->target; - uint8_t data[CFI_MAX_BUS_WIDTH]; - int i; - - target_read_memory(target, flash_address(bank, sector, offset), bank->bus_width, 1, data); - - if (bank->target->endianness == TARGET_LITTLE_ENDIAN) - { - for (i = 0; i < bank->bus_width / bank->chip_width; i++) - data[0] |= data[i]; - - return data[0]; - } - else - { - uint8_t value = 0; - for (i = 0; i < bank->bus_width / bank->chip_width; i++) - value |= data[bank->bus_width - 1 - i]; - - return value; - } -} - -static uint16_t cfi_query_u16(struct flash_bank *bank, int sector, uint32_t offset) -{ - struct target *target = bank->target; - struct cfi_flash_bank *cfi_info = bank->driver_priv; - uint8_t data[CFI_MAX_BUS_WIDTH * 2]; - - if (cfi_info->x16_as_x8) - { - uint8_t i; - for (i = 0;i < 2;i++) - target_read_memory(target, flash_address(bank, sector, offset + i), bank->bus_width, 1, - &data[i*bank->bus_width]); - } - else - target_read_memory(target, flash_address(bank, sector, offset), bank->bus_width, 2, data); - - if (bank->target->endianness == TARGET_LITTLE_ENDIAN) - return data[0] | data[bank->bus_width] << 8; - else - return data[bank->bus_width - 1] | data[(2 * bank->bus_width) - 1] << 8; -} - -static uint32_t cfi_query_u32(struct flash_bank *bank, int sector, uint32_t offset) -{ - struct target *target = bank->target; - struct cfi_flash_bank *cfi_info = bank->driver_priv; - uint8_t data[CFI_MAX_BUS_WIDTH * 4]; - - if (cfi_info->x16_as_x8) - { - uint8_t i; - for (i = 0;i < 4;i++) - target_read_memory(target, flash_address(bank, sector, offset + i), bank->bus_width, 1, - &data[i*bank->bus_width]); - } - else - target_read_memory(target, flash_address(bank, sector, offset), bank->bus_width, 4, data); - - if (bank->target->endianness == TARGET_LITTLE_ENDIAN) - return data[0] | data[bank->bus_width] << 8 | data[bank->bus_width * 2] << 16 | data[bank->bus_width * 3] << 24; - else - return data[bank->bus_width - 1] | data[(2* bank->bus_width) - 1] << 8 | - data[(3 * bank->bus_width) - 1] << 16 | data[(4 * bank->bus_width) - 1] << 24; -} - -static void cfi_intel_clear_status_register(struct flash_bank *bank) -{ - struct target *target = bank->target; - uint8_t command[8]; - - if (target->state != TARGET_HALTED) - { - LOG_ERROR("BUG: attempted to clear status register while target wasn't halted"); - exit(-1); - } - - cfi_command(bank, 0x50, command); - target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command); -} - -uint8_t cfi_intel_wait_status_busy(struct flash_bank *bank, int timeout) -{ - uint8_t status; - - while ((!((status = cfi_get_u8(bank, 0, 0x0)) & 0x80)) && (timeout-- > 0)) - { - LOG_DEBUG("status: 0x%x", status); - alive_sleep(1); - } - - /* mask out bit 0 (reserved) */ - status = status & 0xfe; - - LOG_DEBUG("status: 0x%x", status); - - if ((status & 0x80) != 0x80) - { - LOG_ERROR("timeout while waiting for WSM to become ready"); - } - else if (status != 0x80) - { - LOG_ERROR("status register: 0x%x", status); - if (status & 0x2) - LOG_ERROR("Block Lock-Bit Detected, Operation Abort"); - if (status & 0x4) - LOG_ERROR("Program suspended"); - if (status & 0x8) - LOG_ERROR("Low Programming Voltage Detected, Operation Aborted"); - if (status & 0x10) - LOG_ERROR("Program Error / Error in Setting Lock-Bit"); - if (status & 0x20) - LOG_ERROR("Error in Block Erasure or Clear Lock-Bits"); - if (status & 0x40) - LOG_ERROR("Block Erase Suspended"); - - cfi_intel_clear_status_register(bank); - } - - return status; -} - -int cfi_spansion_wait_status_busy(struct flash_bank *bank, int timeout) -{ - uint8_t status, oldstatus; - struct cfi_flash_bank *cfi_info = bank->driver_priv; - - oldstatus = cfi_get_u8(bank, 0, 0x0); - - do { - status = cfi_get_u8(bank, 0, 0x0); - if ((status ^ oldstatus) & 0x40) { - if (status & cfi_info->status_poll_mask & 0x20) { - oldstatus = cfi_get_u8(bank, 0, 0x0); - status = cfi_get_u8(bank, 0, 0x0); - if ((status ^ oldstatus) & 0x40) { - LOG_ERROR("dq5 timeout, status: 0x%x", status); - return(ERROR_FLASH_OPERATION_FAILED); - } else { - LOG_DEBUG("status: 0x%x", status); - return(ERROR_OK); - } - } - } else { /* no toggle: finished, OK */ - LOG_DEBUG("status: 0x%x", status); - return(ERROR_OK); - } - - oldstatus = status; - alive_sleep(1); - } while (timeout-- > 0); - - LOG_ERROR("timeout, status: 0x%x", status); - - return(ERROR_FLASH_BUSY); -} - -static int cfi_read_intel_pri_ext(struct flash_bank *bank) -{ - int retval; - struct cfi_flash_bank *cfi_info = bank->driver_priv; - struct cfi_intel_pri_ext *pri_ext = malloc(sizeof(struct cfi_intel_pri_ext)); - struct target *target = bank->target; - uint8_t command[8]; - - cfi_info->pri_ext = pri_ext; - - pri_ext->pri[0] = cfi_query_u8(bank, 0, cfi_info->pri_addr + 0); - pri_ext->pri[1] = cfi_query_u8(bank, 0, cfi_info->pri_addr + 1); - pri_ext->pri[2] = cfi_query_u8(bank, 0, cfi_info->pri_addr + 2); - - if ((pri_ext->pri[0] != 'P') || (pri_ext->pri[1] != 'R') || (pri_ext->pri[2] != 'I')) - { - cfi_command(bank, 0xf0, command); - if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - cfi_command(bank, 0xff, command); - if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - LOG_ERROR("Could not read bank flash bank information"); - return ERROR_FLASH_BANK_INVALID; - } - - pri_ext->major_version = cfi_query_u8(bank, 0, cfi_info->pri_addr + 3); - pri_ext->minor_version = cfi_query_u8(bank, 0, cfi_info->pri_addr + 4); - - LOG_DEBUG("pri: '%c%c%c', version: %c.%c", pri_ext->pri[0], pri_ext->pri[1], pri_ext->pri[2], pri_ext->major_version, pri_ext->minor_version); - - pri_ext->feature_support = cfi_query_u32(bank, 0, cfi_info->pri_addr + 5); - pri_ext->suspend_cmd_support = cfi_query_u8(bank, 0, cfi_info->pri_addr + 9); - pri_ext->blk_status_reg_mask = cfi_query_u16(bank, 0, cfi_info->pri_addr + 0xa); - - LOG_DEBUG("feature_support: 0x%" PRIx32 ", suspend_cmd_support: 0x%x, blk_status_reg_mask: 0x%x", - pri_ext->feature_support, - pri_ext->suspend_cmd_support, - pri_ext->blk_status_reg_mask); - - pri_ext->vcc_optimal = cfi_query_u8(bank, 0, cfi_info->pri_addr + 0xc); - pri_ext->vpp_optimal = cfi_query_u8(bank, 0, cfi_info->pri_addr + 0xd); - - LOG_DEBUG("Vcc opt: %1.1x.%1.1x, Vpp opt: %1.1x.%1.1x", - (pri_ext->vcc_optimal & 0xf0) >> 4, pri_ext->vcc_optimal & 0x0f, - (pri_ext->vpp_optimal & 0xf0) >> 4, pri_ext->vpp_optimal & 0x0f); - - pri_ext->num_protection_fields = cfi_query_u8(bank, 0, cfi_info->pri_addr + 0xe); - if (pri_ext->num_protection_fields != 1) - { - LOG_WARNING("expected one protection register field, but found %i", pri_ext->num_protection_fields); - } - - pri_ext->prot_reg_addr = cfi_query_u16(bank, 0, cfi_info->pri_addr + 0xf); - pri_ext->fact_prot_reg_size = cfi_query_u8(bank, 0, cfi_info->pri_addr + 0x11); - pri_ext->user_prot_reg_size = cfi_query_u8(bank, 0, cfi_info->pri_addr + 0x12); - - LOG_DEBUG("protection_fields: %i, prot_reg_addr: 0x%x, factory pre-programmed: %i, user programmable: %i", pri_ext->num_protection_fields, pri_ext->prot_reg_addr, 1 << pri_ext->fact_prot_reg_size, 1 << pri_ext->user_prot_reg_size); - - return ERROR_OK; -} - -static int cfi_read_spansion_pri_ext(struct flash_bank *bank) -{ - int retval; - struct cfi_flash_bank *cfi_info = bank->driver_priv; - struct cfi_spansion_pri_ext *pri_ext = malloc(sizeof(struct cfi_spansion_pri_ext)); - struct target *target = bank->target; - uint8_t command[8]; - - cfi_info->pri_ext = pri_ext; - - pri_ext->pri[0] = cfi_query_u8(bank, 0, cfi_info->pri_addr + 0); - pri_ext->pri[1] = cfi_query_u8(bank, 0, cfi_info->pri_addr + 1); - pri_ext->pri[2] = cfi_query_u8(bank, 0, cfi_info->pri_addr + 2); - - if ((pri_ext->pri[0] != 'P') || (pri_ext->pri[1] != 'R') || (pri_ext->pri[2] != 'I')) - { - cfi_command(bank, 0xf0, command); - if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - LOG_ERROR("Could not read spansion bank information"); - return ERROR_FLASH_BANK_INVALID; - } - - pri_ext->major_version = cfi_query_u8(bank, 0, cfi_info->pri_addr + 3); - pri_ext->minor_version = cfi_query_u8(bank, 0, cfi_info->pri_addr + 4); - - LOG_DEBUG("pri: '%c%c%c', version: %c.%c", pri_ext->pri[0], pri_ext->pri[1], pri_ext->pri[2], pri_ext->major_version, pri_ext->minor_version); - - pri_ext->SiliconRevision = cfi_query_u8(bank, 0, cfi_info->pri_addr + 5); - pri_ext->EraseSuspend = cfi_query_u8(bank, 0, cfi_info->pri_addr + 6); - pri_ext->BlkProt = cfi_query_u8(bank, 0, cfi_info->pri_addr + 7); - pri_ext->TmpBlkUnprotect = cfi_query_u8(bank, 0, cfi_info->pri_addr + 8); - pri_ext->BlkProtUnprot = cfi_query_u8(bank, 0, cfi_info->pri_addr + 9); - pri_ext->SimultaneousOps = cfi_query_u8(bank, 0, cfi_info->pri_addr + 10); - pri_ext->BurstMode = cfi_query_u8(bank, 0, cfi_info->pri_addr + 11); - pri_ext->PageMode = cfi_query_u8(bank, 0, cfi_info->pri_addr + 12); - pri_ext->VppMin = cfi_query_u8(bank, 0, cfi_info->pri_addr + 13); - pri_ext->VppMax = cfi_query_u8(bank, 0, cfi_info->pri_addr + 14); - pri_ext->TopBottom = cfi_query_u8(bank, 0, cfi_info->pri_addr + 15); - - LOG_DEBUG("Silicon Revision: 0x%x, Erase Suspend: 0x%x, Block protect: 0x%x", pri_ext->SiliconRevision, - pri_ext->EraseSuspend, pri_ext->BlkProt); - - LOG_DEBUG("Temporary Unprotect: 0x%x, Block Protect Scheme: 0x%x, Simultaneous Ops: 0x%x", pri_ext->TmpBlkUnprotect, - pri_ext->BlkProtUnprot, pri_ext->SimultaneousOps); - - LOG_DEBUG("Burst Mode: 0x%x, Page Mode: 0x%x, ", pri_ext->BurstMode, pri_ext->PageMode); - - - LOG_DEBUG("Vpp min: %2.2d.%1.1d, Vpp max: %2.2d.%1.1x", - (pri_ext->VppMin & 0xf0) >> 4, pri_ext->VppMin & 0x0f, - (pri_ext->VppMax & 0xf0) >> 4, pri_ext->VppMax & 0x0f); - - LOG_DEBUG("WP# protection 0x%x", pri_ext->TopBottom); - - /* default values for implementation specific workarounds */ - pri_ext->_unlock1 = cfi_unlock_addresses[CFI_UNLOCK_555_2AA].unlock1; - pri_ext->_unlock2 = cfi_unlock_addresses[CFI_UNLOCK_555_2AA].unlock2; - pri_ext->_reversed_geometry = 0; - - return ERROR_OK; -} - -static int cfi_read_atmel_pri_ext(struct flash_bank *bank) -{ - int retval; - struct cfi_atmel_pri_ext atmel_pri_ext; - struct cfi_flash_bank *cfi_info = bank->driver_priv; - struct cfi_spansion_pri_ext *pri_ext = malloc(sizeof(struct cfi_spansion_pri_ext)); - struct target *target = bank->target; - uint8_t command[8]; - - /* ATMEL devices use the same CFI primary command set (0x2) as AMD/Spansion, - * but a different primary extended query table. - * We read the atmel table, and prepare a valid AMD/Spansion query table. - */ - - memset(pri_ext, 0, sizeof(struct cfi_spansion_pri_ext)); - - cfi_info->pri_ext = pri_ext; - - atmel_pri_ext.pri[0] = cfi_query_u8(bank, 0, cfi_info->pri_addr + 0); - atmel_pri_ext.pri[1] = cfi_query_u8(bank, 0, cfi_info->pri_addr + 1); - atmel_pri_ext.pri[2] = cfi_query_u8(bank, 0, cfi_info->pri_addr + 2); - - if ((atmel_pri_ext.pri[0] != 'P') || (atmel_pri_ext.pri[1] != 'R') || (atmel_pri_ext.pri[2] != 'I')) - { - cfi_command(bank, 0xf0, command); - if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - LOG_ERROR("Could not read atmel bank information"); - return ERROR_FLASH_BANK_INVALID; - } - - pri_ext->pri[0] = atmel_pri_ext.pri[0]; - pri_ext->pri[1] = atmel_pri_ext.pri[1]; - pri_ext->pri[2] = atmel_pri_ext.pri[2]; - - atmel_pri_ext.major_version = cfi_query_u8(bank, 0, cfi_info->pri_addr + 3); - atmel_pri_ext.minor_version = cfi_query_u8(bank, 0, cfi_info->pri_addr + 4); - - LOG_DEBUG("pri: '%c%c%c', version: %c.%c", atmel_pri_ext.pri[0], atmel_pri_ext.pri[1], atmel_pri_ext.pri[2], atmel_pri_ext.major_version, atmel_pri_ext.minor_version); - - pri_ext->major_version = atmel_pri_ext.major_version; - pri_ext->minor_version = atmel_pri_ext.minor_version; - - atmel_pri_ext.features = cfi_query_u8(bank, 0, cfi_info->pri_addr + 5); - atmel_pri_ext.bottom_boot = cfi_query_u8(bank, 0, cfi_info->pri_addr + 6); - atmel_pri_ext.burst_mode = cfi_query_u8(bank, 0, cfi_info->pri_addr + 7); - atmel_pri_ext.page_mode = cfi_query_u8(bank, 0, cfi_info->pri_addr + 8); - - LOG_DEBUG("features: 0x%2.2x, bottom_boot: 0x%2.2x, burst_mode: 0x%2.2x, page_mode: 0x%2.2x", - atmel_pri_ext.features, atmel_pri_ext.bottom_boot, atmel_pri_ext.burst_mode, atmel_pri_ext.page_mode); - - if (atmel_pri_ext.features & 0x02) - pri_ext->EraseSuspend = 2; - - if (atmel_pri_ext.bottom_boot) - pri_ext->TopBottom = 2; - else - pri_ext->TopBottom = 3; - - pri_ext->_unlock1 = cfi_unlock_addresses[CFI_UNLOCK_555_2AA].unlock1; - pri_ext->_unlock2 = cfi_unlock_addresses[CFI_UNLOCK_555_2AA].unlock2; - - return ERROR_OK; -} - -static int cfi_read_0002_pri_ext(struct flash_bank *bank) -{ - struct cfi_flash_bank *cfi_info = bank->driver_priv; - - if (cfi_info->manufacturer == CFI_MFR_ATMEL) - { - return cfi_read_atmel_pri_ext(bank); - } - else - { - return cfi_read_spansion_pri_ext(bank); - } -} - -static int cfi_spansion_info(struct flash_bank *bank, char *buf, int buf_size) -{ - int printed; - struct cfi_flash_bank *cfi_info = bank->driver_priv; - struct cfi_spansion_pri_ext *pri_ext = cfi_info->pri_ext; - - printed = snprintf(buf, buf_size, "\nSpansion primary algorithm extend information:\n"); - buf += printed; - buf_size -= printed; - - printed = snprintf(buf, buf_size, "pri: '%c%c%c', version: %c.%c\n", pri_ext->pri[0], - pri_ext->pri[1], pri_ext->pri[2], - pri_ext->major_version, pri_ext->minor_version); - buf += printed; - buf_size -= printed; - - printed = snprintf(buf, buf_size, "Silicon Rev.: 0x%x, Address Sensitive unlock: 0x%x\n", - (pri_ext->SiliconRevision) >> 2, - (pri_ext->SiliconRevision) & 0x03); - buf += printed; - buf_size -= printed; - - printed = snprintf(buf, buf_size, "Erase Suspend: 0x%x, Sector Protect: 0x%x\n", - pri_ext->EraseSuspend, - pri_ext->BlkProt); - buf += printed; - buf_size -= printed; - - printed = snprintf(buf, buf_size, "VppMin: %2.2d.%1.1x, VppMax: %2.2d.%1.1x\n", - (pri_ext->VppMin & 0xf0) >> 4, pri_ext->VppMin & 0x0f, - (pri_ext->VppMax & 0xf0) >> 4, pri_ext->VppMax & 0x0f); - - return ERROR_OK; -} - -static int cfi_intel_info(struct flash_bank *bank, char *buf, int buf_size) -{ - int printed; - struct cfi_flash_bank *cfi_info = bank->driver_priv; - struct cfi_intel_pri_ext *pri_ext = cfi_info->pri_ext; - - printed = snprintf(buf, buf_size, "\nintel primary algorithm extend information:\n"); - buf += printed; - buf_size -= printed; - - printed = snprintf(buf, buf_size, "pri: '%c%c%c', version: %c.%c\n", pri_ext->pri[0], pri_ext->pri[1], pri_ext->pri[2], pri_ext->major_version, pri_ext->minor_version); - buf += printed; - buf_size -= printed; - - printed = snprintf(buf, buf_size, "feature_support: 0x%" PRIx32 ", suspend_cmd_support: 0x%x, blk_status_reg_mask: 0x%x\n", pri_ext->feature_support, pri_ext->suspend_cmd_support, pri_ext->blk_status_reg_mask); - buf += printed; - buf_size -= printed; - - printed = snprintf(buf, buf_size, "Vcc opt: %1.1x.%1.1x, Vpp opt: %1.1x.%1.1x\n", - (pri_ext->vcc_optimal & 0xf0) >> 4, pri_ext->vcc_optimal & 0x0f, - (pri_ext->vpp_optimal & 0xf0) >> 4, pri_ext->vpp_optimal & 0x0f); - buf += printed; - buf_size -= printed; - - printed = snprintf(buf, buf_size, "protection_fields: %i, prot_reg_addr: 0x%x, factory pre-programmed: %i, user programmable: %i\n", pri_ext->num_protection_fields, pri_ext->prot_reg_addr, 1 << pri_ext->fact_prot_reg_size, 1 << pri_ext->user_prot_reg_size); - - return ERROR_OK; -} - -/* flash_bank cfi [options] - */ -FLASH_BANK_COMMAND_HANDLER(cfi_flash_bank_command) -{ - struct cfi_flash_bank *cfi_info; - - if (CMD_ARGC < 6) - { - LOG_WARNING("incomplete flash_bank cfi configuration"); - return ERROR_FLASH_BANK_INVALID; - } - - uint16_t chip_width, bus_width; - COMMAND_PARSE_NUMBER(u16, CMD_ARGV[3], bus_width); - COMMAND_PARSE_NUMBER(u16, CMD_ARGV[4], chip_width); - - if ((chip_width > CFI_MAX_CHIP_WIDTH) - || (bus_width > CFI_MAX_BUS_WIDTH)) - { - LOG_ERROR("chip and bus width have to specified in bytes"); - return ERROR_FLASH_BANK_INVALID; - } - - cfi_info = malloc(sizeof(struct cfi_flash_bank)); - cfi_info->probed = 0; - bank->driver_priv = cfi_info; - - cfi_info->write_algorithm = NULL; - - cfi_info->x16_as_x8 = 0; - cfi_info->jedec_probe = 0; - cfi_info->not_cfi = 0; - - for (unsigned i = 6; i < CMD_ARGC; i++) - { - if (strcmp(CMD_ARGV[i], "x16_as_x8") == 0) - { - cfi_info->x16_as_x8 = 1; - } - else if (strcmp(CMD_ARGV[i], "jedec_probe") == 0) - { - cfi_info->jedec_probe = 1; - } - } - - cfi_info->write_algorithm = NULL; - - /* bank wasn't probed yet */ - cfi_info->qry[0] = -1; - - return ERROR_OK; -} - -static int cfi_intel_erase(struct flash_bank *bank, int first, int last) -{ - int retval; - struct cfi_flash_bank *cfi_info = bank->driver_priv; - struct target *target = bank->target; - uint8_t command[8]; - int i; - - cfi_intel_clear_status_register(bank); - - for (i = first; i <= last; i++) - { - cfi_command(bank, 0x20, command); - if ((retval = target_write_memory(target, flash_address(bank, i, 0x0), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - - cfi_command(bank, 0xd0, command); - if ((retval = target_write_memory(target, flash_address(bank, i, 0x0), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - - if (cfi_intel_wait_status_busy(bank, 1000 * (1 << cfi_info->block_erase_timeout_typ)) == 0x80) - bank->sectors[i].is_erased = 1; - else - { - cfi_command(bank, 0xff, command); - if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - - LOG_ERROR("couldn't erase block %i of flash bank at base 0x%" PRIx32 , i, bank->base); - return ERROR_FLASH_OPERATION_FAILED; - } - } - - cfi_command(bank, 0xff, command); - return target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command); - -} - -static int cfi_spansion_erase(struct flash_bank *bank, int first, int last) -{ - int retval; - struct cfi_flash_bank *cfi_info = bank->driver_priv; - struct cfi_spansion_pri_ext *pri_ext = cfi_info->pri_ext; - struct target *target = bank->target; - uint8_t command[8]; - int i; - - for (i = first; i <= last; i++) - { - cfi_command(bank, 0xaa, command); - if ((retval = target_write_memory(target, flash_address(bank, 0, pri_ext->_unlock1), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - - cfi_command(bank, 0x55, command); - if ((retval = target_write_memory(target, flash_address(bank, 0, pri_ext->_unlock2), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - - cfi_command(bank, 0x80, command); - if ((retval = target_write_memory(target, flash_address(bank, 0, pri_ext->_unlock1), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - - cfi_command(bank, 0xaa, command); - if ((retval = target_write_memory(target, flash_address(bank, 0, pri_ext->_unlock1), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - - cfi_command(bank, 0x55, command); - if ((retval = target_write_memory(target, flash_address(bank, 0, pri_ext->_unlock2), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - - cfi_command(bank, 0x30, command); - if ((retval = target_write_memory(target, flash_address(bank, i, 0x0), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - - if (cfi_spansion_wait_status_busy(bank, 1000 * (1 << cfi_info->block_erase_timeout_typ)) == ERROR_OK) - bank->sectors[i].is_erased = 1; - else - { - cfi_command(bank, 0xf0, command); - if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - - LOG_ERROR("couldn't erase block %i of flash bank at base 0x%" PRIx32, i, bank->base); - return ERROR_FLASH_OPERATION_FAILED; - } - } - - cfi_command(bank, 0xf0, command); - return target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command); -} - -static int cfi_erase(struct flash_bank *bank, int first, int last) -{ - struct cfi_flash_bank *cfi_info = bank->driver_priv; - - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - if ((first < 0) || (last < first) || (last >= bank->num_sectors)) - { - return ERROR_FLASH_SECTOR_INVALID; - } - - if (cfi_info->qry[0] != 'Q') - return ERROR_FLASH_BANK_NOT_PROBED; - - switch (cfi_info->pri_id) - { - case 1: - case 3: - return cfi_intel_erase(bank, first, last); - break; - case 2: - return cfi_spansion_erase(bank, first, last); - break; - default: - LOG_ERROR("cfi primary command set %i unsupported", cfi_info->pri_id); - break; - } - - return ERROR_OK; -} - -static int cfi_intel_protect(struct flash_bank *bank, int set, int first, int last) -{ - int retval; - struct cfi_flash_bank *cfi_info = bank->driver_priv; - struct cfi_intel_pri_ext *pri_ext = cfi_info->pri_ext; - struct target *target = bank->target; - uint8_t command[8]; - int retry = 0; - int i; - - /* if the device supports neither legacy lock/unlock (bit 3) nor - * instant individual block locking (bit 5). - */ - if (!(pri_ext->feature_support & 0x28)) - return ERROR_FLASH_OPERATION_FAILED; - - cfi_intel_clear_status_register(bank); - - for (i = first; i <= last; i++) - { - cfi_command(bank, 0x60, command); - LOG_DEBUG("address: 0x%4.4" PRIx32 ", command: 0x%4.4" PRIx32, flash_address(bank, i, 0x0), target_buffer_get_u32(target, command)); - if ((retval = target_write_memory(target, flash_address(bank, i, 0x0), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - if (set) - { - cfi_command(bank, 0x01, command); - LOG_DEBUG("address: 0x%4.4" PRIx32 ", command: 0x%4.4" PRIx32 , flash_address(bank, i, 0x0), target_buffer_get_u32(target, command)); - if ((retval = target_write_memory(target, flash_address(bank, i, 0x0), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - bank->sectors[i].is_protected = 1; - } - else - { - cfi_command(bank, 0xd0, command); - LOG_DEBUG("address: 0x%4.4" PRIx32 ", command: 0x%4.4" PRIx32, flash_address(bank, i, 0x0), target_buffer_get_u32(target, command)); - if ((retval = target_write_memory(target, flash_address(bank, i, 0x0), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - bank->sectors[i].is_protected = 0; - } - - /* instant individual block locking doesn't require reading of the status register */ - if (!(pri_ext->feature_support & 0x20)) - { - /* Clear lock bits operation may take up to 1.4s */ - cfi_intel_wait_status_busy(bank, 1400); - } - else - { - uint8_t block_status; - /* read block lock bit, to verify status */ - cfi_command(bank, 0x90, command); - if ((retval = target_write_memory(target, flash_address(bank, 0, 0x55), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - block_status = cfi_get_u8(bank, i, 0x2); - - if ((block_status & 0x1) != set) - { - LOG_ERROR("couldn't change block lock status (set = %i, block_status = 0x%2.2x)", set, block_status); - cfi_command(bank, 0x70, command); - if ((retval = target_write_memory(target, flash_address(bank, 0, 0x55), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - cfi_intel_wait_status_busy(bank, 10); - - if (retry > 10) - return ERROR_FLASH_OPERATION_FAILED; - else - { - i--; - retry++; - } - } - } - } - - /* if the device doesn't support individual block lock bits set/clear, - * all blocks have been unlocked in parallel, so we set those that should be protected - */ - if ((!set) && (!(pri_ext->feature_support & 0x20))) - { - for (i = 0; i < bank->num_sectors; i++) - { - if (bank->sectors[i].is_protected == 1) - { - cfi_intel_clear_status_register(bank); - - cfi_command(bank, 0x60, command); - if ((retval = target_write_memory(target, flash_address(bank, i, 0x0), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - - cfi_command(bank, 0x01, command); - if ((retval = target_write_memory(target, flash_address(bank, i, 0x0), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - - cfi_intel_wait_status_busy(bank, 100); - } - } - } - - cfi_command(bank, 0xff, command); - return target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command); -} - -static int cfi_protect(struct flash_bank *bank, int set, int first, int last) -{ - struct cfi_flash_bank *cfi_info = bank->driver_priv; - - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - if ((first < 0) || (last < first) || (last >= bank->num_sectors)) - { - return ERROR_FLASH_SECTOR_INVALID; - } - - if (cfi_info->qry[0] != 'Q') - return ERROR_FLASH_BANK_NOT_PROBED; - - switch (cfi_info->pri_id) - { - case 1: - case 3: - cfi_intel_protect(bank, set, first, last); - break; - default: - LOG_ERROR("protect: cfi primary command set %i unsupported", cfi_info->pri_id); - break; - } - - return ERROR_OK; -} - -/* FIXME Replace this by a simple memcpy() - still unsure about sideeffects */ -static void cfi_add_byte(struct flash_bank *bank, uint8_t *word, uint8_t byte) -{ - /* struct target *target = bank->target; */ - - int i; - - /* NOTE: - * The data to flash must not be changed in endian! We write a bytestrem in - * target byte order already. Only the control and status byte lane of the flash - * WSM is interpreted by the CPU in different ways, when read a uint16_t or uint32_t - * word (data seems to be in the upper or lower byte lane for uint16_t accesses). - */ - -#if 0 - if (target->endianness == TARGET_LITTLE_ENDIAN) - { -#endif - /* shift bytes */ - for (i = 0; i < bank->bus_width - 1; i++) - word[i] = word[i + 1]; - word[bank->bus_width - 1] = byte; -#if 0 - } - else - { - /* shift bytes */ - for (i = bank->bus_width - 1; i > 0; i--) - word[i] = word[i - 1]; - word[0] = byte; - } -#endif -} - -/* Convert code image to target endian */ -/* FIXME create general block conversion fcts in target.c?) */ -static void cfi_fix_code_endian(struct target *target, uint8_t *dest, const uint32_t *src, uint32_t count) -{ - uint32_t i; - for (i = 0; i< count; i++) - { - target_buffer_set_u32(target, dest, *src); - dest += 4; - src++; - } -} - -static uint32_t cfi_command_val(struct flash_bank *bank, uint8_t cmd) -{ - struct target *target = bank->target; - - uint8_t buf[CFI_MAX_BUS_WIDTH]; - cfi_command(bank, cmd, buf); - switch (bank->bus_width) - { - case 1 : - return buf[0]; - break; - case 2 : - return target_buffer_get_u16(target, buf); - break; - case 4 : - return target_buffer_get_u32(target, buf); - break; - default : - LOG_ERROR("Unsupported bank buswidth %d, can't do block memory writes", bank->bus_width); - return 0; - } -} - -static int cfi_intel_write_block(struct flash_bank *bank, uint8_t *buffer, uint32_t address, uint32_t count) -{ - struct cfi_flash_bank *cfi_info = bank->driver_priv; - struct target *target = bank->target; - struct reg_param reg_params[7]; - struct armv4_5_algorithm armv4_5_info; - struct working_area *source; - uint32_t buffer_size = 32768; - uint32_t write_command_val, busy_pattern_val, error_pattern_val; - - /* algorithm register usage: - * r0: source address (in RAM) - * r1: target address (in Flash) - * r2: count - * r3: flash write command - * r4: status byte (returned to host) - * r5: busy test pattern - * r6: error test pattern - */ - - static const uint32_t word_32_code[] = { - 0xe4904004, /* loop: ldr r4, [r0], #4 */ - 0xe5813000, /* str r3, [r1] */ - 0xe5814000, /* str r4, [r1] */ - 0xe5914000, /* busy: ldr r4, [r1] */ - 0xe0047005, /* and r7, r4, r5 */ - 0xe1570005, /* cmp r7, r5 */ - 0x1afffffb, /* bne busy */ - 0xe1140006, /* tst r4, r6 */ - 0x1a000003, /* bne done */ - 0xe2522001, /* subs r2, r2, #1 */ - 0x0a000001, /* beq done */ - 0xe2811004, /* add r1, r1 #4 */ - 0xeafffff2, /* b loop */ - 0xeafffffe /* done: b -2 */ - }; - - static const uint32_t word_16_code[] = { - 0xe0d040b2, /* loop: ldrh r4, [r0], #2 */ - 0xe1c130b0, /* strh r3, [r1] */ - 0xe1c140b0, /* strh r4, [r1] */ - 0xe1d140b0, /* busy ldrh r4, [r1] */ - 0xe0047005, /* and r7, r4, r5 */ - 0xe1570005, /* cmp r7, r5 */ - 0x1afffffb, /* bne busy */ - 0xe1140006, /* tst r4, r6 */ - 0x1a000003, /* bne done */ - 0xe2522001, /* subs r2, r2, #1 */ - 0x0a000001, /* beq done */ - 0xe2811002, /* add r1, r1 #2 */ - 0xeafffff2, /* b loop */ - 0xeafffffe /* done: b -2 */ - }; - - static const uint32_t word_8_code[] = { - 0xe4d04001, /* loop: ldrb r4, [r0], #1 */ - 0xe5c13000, /* strb r3, [r1] */ - 0xe5c14000, /* strb r4, [r1] */ - 0xe5d14000, /* busy ldrb r4, [r1] */ - 0xe0047005, /* and r7, r4, r5 */ - 0xe1570005, /* cmp r7, r5 */ - 0x1afffffb, /* bne busy */ - 0xe1140006, /* tst r4, r6 */ - 0x1a000003, /* bne done */ - 0xe2522001, /* subs r2, r2, #1 */ - 0x0a000001, /* beq done */ - 0xe2811001, /* add r1, r1 #1 */ - 0xeafffff2, /* b loop */ - 0xeafffffe /* done: b -2 */ - }; - uint8_t target_code[4*CFI_MAX_INTEL_CODESIZE]; - const uint32_t *target_code_src; - uint32_t target_code_size; - int retval = ERROR_OK; - - - cfi_intel_clear_status_register(bank); - - 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; - - /* If we are setting up the write_algorith, we need target_code_src */ - /* if not we only need target_code_size. */ - - /* However, we don't want to create multiple code paths, so we */ - /* do the unecessary evaluation of target_code_src, which the */ - /* compiler will probably nicely optimize away if not needed */ - - /* prepare algorithm code for target endian */ - switch (bank->bus_width) - { - case 1 : - target_code_src = word_8_code; - target_code_size = sizeof(word_8_code); - break; - case 2 : - target_code_src = word_16_code; - target_code_size = sizeof(word_16_code); - break; - case 4 : - target_code_src = word_32_code; - target_code_size = sizeof(word_32_code); - break; - default: - LOG_ERROR("Unsupported bank buswidth %d, can't do block memory writes", bank->bus_width); - return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; - } - - /* flash write code */ - if (!cfi_info->write_algorithm) - { - if (target_code_size > sizeof(target_code)) - { - LOG_WARNING("Internal error - target code buffer to small. Increase CFI_MAX_INTEL_CODESIZE and recompile."); - return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; - } - cfi_fix_code_endian(target, target_code, target_code_src, target_code_size / 4); - - /* Get memory for block write handler */ - retval = target_alloc_working_area(target, target_code_size, &cfi_info->write_algorithm); - if (retval != ERROR_OK) - { - LOG_WARNING("No working area available, can't do block memory writes"); - return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; - }; - - /* write algorithm code to working area */ - retval = target_write_buffer(target, cfi_info->write_algorithm->address, target_code_size, target_code); - if (retval != ERROR_OK) - { - LOG_ERROR("Unable to write block write code to target"); - goto cleanup; - } - } - - /* Get a workspace buffer for the data to flash starting with 32k size. - Half size until buffer would be smaller 256 Bytem then fail back */ - /* FIXME Why 256 bytes, why not 32 bytes (smallest flash write page */ - while (target_alloc_working_area(target, buffer_size, &source) != ERROR_OK) - { - buffer_size /= 2; - if (buffer_size <= 256) - { - LOG_WARNING("no large enough working area available, can't do block memory writes"); - retval = ERROR_TARGET_RESOURCE_NOT_AVAILABLE; - goto cleanup; - } - }; - - /* setup algo registers */ - 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); - init_reg_param(®_params[6], "r6", 32, PARAM_OUT); - - /* prepare command and status register patterns */ - write_command_val = cfi_command_val(bank, 0x40); - busy_pattern_val = cfi_command_val(bank, 0x80); - error_pattern_val = cfi_command_val(bank, 0x7e); - - LOG_INFO("Using target buffer at 0x%08" PRIx32 " and of size 0x%04" PRIx32, source->address, buffer_size); - - /* Programming main loop */ - while (count > 0) - { - uint32_t thisrun_count = (count > buffer_size) ? buffer_size : count; - uint32_t wsm_error; - - if ((retval = target_write_buffer(target, source->address, thisrun_count, buffer)) != ERROR_OK) - { - goto cleanup; - } - - 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 / bank->bus_width); - - buf_set_u32(reg_params[3].value, 0, 32, write_command_val); - buf_set_u32(reg_params[5].value, 0, 32, busy_pattern_val); - buf_set_u32(reg_params[6].value, 0, 32, error_pattern_val); - - LOG_INFO("Write 0x%04" PRIx32 " bytes to flash at 0x%08" PRIx32 , thisrun_count, address); - - /* Execute algorithm, assume breakpoint for last instruction */ - retval = target_run_algorithm(target, 0, NULL, 7, reg_params, - cfi_info->write_algorithm->address, - cfi_info->write_algorithm->address + target_code_size - sizeof(uint32_t), - 10000, /* 10s should be enough for max. 32k of data */ - &armv4_5_info); - - /* On failure try a fall back to direct word writes */ - if (retval != ERROR_OK) - { - cfi_intel_clear_status_register(bank); - LOG_ERROR("Execution of flash algorythm failed. Can't fall back. Please report."); - retval = ERROR_FLASH_OPERATION_FAILED; - /* retval = ERROR_TARGET_RESOURCE_NOT_AVAILABLE; */ - /* FIXME To allow fall back or recovery, we must save the actual status - somewhere, so that a higher level code can start recovery. */ - goto cleanup; - } - - /* Check return value from algo code */ - wsm_error = buf_get_u32(reg_params[4].value, 0, 32) & error_pattern_val; - if (wsm_error) - { - /* read status register (outputs debug inforation) */ - cfi_intel_wait_status_busy(bank, 100); - cfi_intel_clear_status_register(bank); - retval = ERROR_FLASH_OPERATION_FAILED; - goto cleanup; - } - - buffer += thisrun_count; - address += thisrun_count; - count -= thisrun_count; - } - - /* free up resources */ -cleanup: - if (source) - target_free_working_area(target, source); - - if (cfi_info->write_algorithm) - { - target_free_working_area(target, cfi_info->write_algorithm); - cfi_info->write_algorithm = NULL; - } - - 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]); - - return retval; -} - -static int cfi_spansion_write_block(struct flash_bank *bank, uint8_t *buffer, uint32_t address, uint32_t count) -{ - struct cfi_flash_bank *cfi_info = bank->driver_priv; - struct cfi_spansion_pri_ext *pri_ext = cfi_info->pri_ext; - struct target *target = bank->target; - struct reg_param reg_params[10]; - struct armv4_5_algorithm armv4_5_info; - struct working_area *source; - uint32_t buffer_size = 32768; - uint32_t status; - int retval, retvaltemp; - int exit_code = ERROR_OK; - - /* input parameters - */ - /* R0 = source address */ - /* R1 = destination address */ - /* R2 = number of writes */ - /* R3 = flash write command */ - /* R4 = constant to mask DQ7 bits (also used for Dq5 with shift) */ - /* output parameters - */ - /* R5 = 0x80 ok 0x00 bad */ - /* temp registers - */ - /* R6 = value read from flash to test status */ - /* R7 = holding register */ - /* unlock registers - */ - /* R8 = unlock1_addr */ - /* R9 = unlock1_cmd */ - /* R10 = unlock2_addr */ - /* R11 = unlock2_cmd */ - - static const uint32_t word_32_code[] = { - /* 00008100 : */ - 0xe4905004, /* ldr r5, [r0], #4 */ - 0xe5889000, /* str r9, [r8] */ - 0xe58ab000, /* str r11, [r10] */ - 0xe5883000, /* str r3, [r8] */ - 0xe5815000, /* str r5, [r1] */ - 0xe1a00000, /* nop */ - /* */ - /* 00008110 : */ - 0xe5916000, /* ldr r6, [r1] */ - 0xe0257006, /* eor r7, r5, r6 */ - 0xe0147007, /* ands r7, r4, r7 */ - 0x0a000007, /* beq 8140 ; b if DQ7 == Data7 */ - 0xe0166124, /* ands r6, r6, r4, lsr #2 */ - 0x0afffff9, /* beq 8110 ; b if DQ5 low */ - 0xe5916000, /* ldr r6, [r1] */ - 0xe0257006, /* eor r7, r5, r6 */ - 0xe0147007, /* ands r7, r4, r7 */ - 0x0a000001, /* beq 8140 ; b if DQ7 == Data7 */ - 0xe3a05000, /* mov r5, #0 ; 0x0 - return 0x00, error */ - 0x1a000004, /* bne 8154 */ - /* */ - /* 00008140 : */ - 0xe2522001, /* subs r2, r2, #1 ; 0x1 */ - 0x03a05080, /* moveq r5, #128 ; 0x80 */ - 0x0a000001, /* beq 8154 */ - 0xe2811004, /* add r1, r1, #4 ; 0x4 */ - 0xeaffffe8, /* b 8100 */ - /* */ - /* 00008154 : */ - 0xeafffffe /* b 8154 */ - }; - - static const uint32_t word_16_code[] = { - /* 00008158 : */ - 0xe0d050b2, /* ldrh r5, [r0], #2 */ - 0xe1c890b0, /* strh r9, [r8] */ - 0xe1cab0b0, /* strh r11, [r10] */ - 0xe1c830b0, /* strh r3, [r8] */ - 0xe1c150b0, /* strh r5, [r1] */ - 0xe1a00000, /* nop (mov r0,r0) */ - /* */ - /* 00008168 : */ - 0xe1d160b0, /* ldrh r6, [r1] */ - 0xe0257006, /* eor r7, r5, r6 */ - 0xe0147007, /* ands r7, r4, r7 */ - 0x0a000007, /* beq 8198 */ - 0xe0166124, /* ands r6, r6, r4, lsr #2 */ - 0x0afffff9, /* beq 8168 */ - 0xe1d160b0, /* ldrh r6, [r1] */ - 0xe0257006, /* eor r7, r5, r6 */ - 0xe0147007, /* ands r7, r4, r7 */ - 0x0a000001, /* beq 8198 */ - 0xe3a05000, /* mov r5, #0 ; 0x0 */ - 0x1a000004, /* bne 81ac */ - /* */ - /* 00008198 : */ - 0xe2522001, /* subs r2, r2, #1 ; 0x1 */ - 0x03a05080, /* moveq r5, #128 ; 0x80 */ - 0x0a000001, /* beq 81ac */ - 0xe2811002, /* add r1, r1, #2 ; 0x2 */ - 0xeaffffe8, /* b 8158 */ - /* */ - /* 000081ac : */ - 0xeafffffe /* b 81ac */ - }; - - static const uint32_t word_16_code_dq7only[] = { - /* : */ - 0xe0d050b2, /* ldrh r5, [r0], #2 */ - 0xe1c890b0, /* strh r9, [r8] */ - 0xe1cab0b0, /* strh r11, [r10] */ - 0xe1c830b0, /* strh r3, [r8] */ - 0xe1c150b0, /* strh r5, [r1] */ - 0xe1a00000, /* nop (mov r0,r0) */ - /* */ - /* : */ - 0xe1d160b0, /* ldrh r6, [r1] */ - 0xe0257006, /* eor r7, r5, r6 */ - 0xe2177080, /* ands r7, #0x80 */ - 0x1afffffb, /* bne 8168 */ - /* */ - 0xe2522001, /* subs r2, r2, #1 ; 0x1 */ - 0x03a05080, /* moveq r5, #128 ; 0x80 */ - 0x0a000001, /* beq 81ac */ - 0xe2811002, /* add r1, r1, #2 ; 0x2 */ - 0xeafffff0, /* b 8158 */ - /* */ - /* 000081ac : */ - 0xeafffffe /* b 81ac */ - }; - - static const uint32_t word_8_code[] = { - /* 000081b0 : */ - 0xe4d05001, /* ldrb r5, [r0], #1 */ - 0xe5c89000, /* strb r9, [r8] */ - 0xe5cab000, /* strb r11, [r10] */ - 0xe5c83000, /* strb r3, [r8] */ - 0xe5c15000, /* strb r5, [r1] */ - 0xe1a00000, /* nop (mov r0,r0) */ - /* */ - /* 000081c0 : */ - 0xe5d16000, /* ldrb r6, [r1] */ - 0xe0257006, /* eor r7, r5, r6 */ - 0xe0147007, /* ands r7, r4, r7 */ - 0x0a000007, /* beq 81f0 */ - 0xe0166124, /* ands r6, r6, r4, lsr #2 */ - 0x0afffff9, /* beq 81c0 */ - 0xe5d16000, /* ldrb r6, [r1] */ - 0xe0257006, /* eor r7, r5, r6 */ - 0xe0147007, /* ands r7, r4, r7 */ - 0x0a000001, /* beq 81f0 */ - 0xe3a05000, /* mov r5, #0 ; 0x0 */ - 0x1a000004, /* bne 8204 */ - /* */ - /* 000081f0 : */ - 0xe2522001, /* subs r2, r2, #1 ; 0x1 */ - 0x03a05080, /* moveq r5, #128 ; 0x80 */ - 0x0a000001, /* beq 8204 */ - 0xe2811001, /* add r1, r1, #1 ; 0x1 */ - 0xeaffffe8, /* b 81b0 */ - /* */ - /* 00008204 : */ - 0xeafffffe /* b 8204 */ - }; - - 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; - - int target_code_size; - const uint32_t *target_code_src; - - switch (bank->bus_width) - { - case 1 : - target_code_src = word_8_code; - target_code_size = sizeof(word_8_code); - break; - case 2 : - /* Check for DQ5 support */ - if( cfi_info->status_poll_mask & (1 << 5) ) - { - target_code_src = word_16_code; - target_code_size = sizeof(word_16_code); - } - else - { - /* No DQ5 support. Use DQ7 DATA# polling only. */ - target_code_src = word_16_code_dq7only; - target_code_size = sizeof(word_16_code_dq7only); - } - break; - case 4 : - target_code_src = word_32_code; - target_code_size = sizeof(word_32_code); - break; - default: - LOG_ERROR("Unsupported bank buswidth %d, can't do block memory writes", bank->bus_width); - return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; - } - - /* flash write code */ - if (!cfi_info->write_algorithm) - { - uint8_t *target_code; - - /* convert bus-width dependent algorithm code to correct endiannes */ - target_code = malloc(target_code_size); - cfi_fix_code_endian(target, target_code, target_code_src, target_code_size / 4); - - /* allocate working area */ - retval = target_alloc_working_area(target, target_code_size, - &cfi_info->write_algorithm); - if (retval != ERROR_OK) - { - free(target_code); - return retval; - } - - /* write algorithm code to working area */ - if ((retval = target_write_buffer(target, cfi_info->write_algorithm->address, - target_code_size, target_code)) != ERROR_OK) - { - free(target_code); - return retval; - } - - free(target_code); - } - /* the following code still assumes target code is fixed 24*4 bytes */ - - 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 (cfi_info->write_algorithm) - target_free_working_area(target, cfi_info->write_algorithm); - - LOG_WARNING("not enough working area available, can't do block memory writes"); - return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; - } - }; - - 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_IN); - init_reg_param(®_params[6], "r8", 32, PARAM_OUT); - init_reg_param(®_params[7], "r9", 32, PARAM_OUT); - init_reg_param(®_params[8], "r10", 32, PARAM_OUT); - init_reg_param(®_params[9], "r11", 32, PARAM_OUT); - - while (count > 0) - { - uint32_t thisrun_count = (count > buffer_size) ? buffer_size : count; - - retvaltemp = target_write_buffer(target, source->address, thisrun_count, 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 / bank->bus_width); - buf_set_u32(reg_params[3].value, 0, 32, cfi_command_val(bank, 0xA0)); - buf_set_u32(reg_params[4].value, 0, 32, cfi_command_val(bank, 0x80)); - buf_set_u32(reg_params[6].value, 0, 32, flash_address(bank, 0, pri_ext->_unlock1)); - buf_set_u32(reg_params[7].value, 0, 32, 0xaaaaaaaa); - buf_set_u32(reg_params[8].value, 0, 32, flash_address(bank, 0, pri_ext->_unlock2)); - buf_set_u32(reg_params[9].value, 0, 32, 0x55555555); - - retval = target_run_algorithm(target, 0, NULL, 10, reg_params, - cfi_info->write_algorithm->address, - cfi_info->write_algorithm->address + ((target_code_size) - 4), - 10000, &armv4_5_info); - - status = buf_get_u32(reg_params[5].value, 0, 32); - - if ((retval != ERROR_OK) || (retvaltemp != ERROR_OK) || status != 0x80) - { - LOG_DEBUG("status: 0x%" PRIx32 , status); - exit_code = ERROR_FLASH_OPERATION_FAILED; - break; - } - - buffer += thisrun_count; - address += thisrun_count; - count -= thisrun_count; - } - - target_free_all_working_areas(target); - - 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]); - destroy_reg_param(®_params[8]); - destroy_reg_param(®_params[9]); - - return exit_code; -} - -static int cfi_intel_write_word(struct flash_bank *bank, uint8_t *word, uint32_t address) -{ - int retval; - struct cfi_flash_bank *cfi_info = bank->driver_priv; - struct target *target = bank->target; - uint8_t command[8]; - - cfi_intel_clear_status_register(bank); - cfi_command(bank, 0x40, command); - if ((retval = target_write_memory(target, address, bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - - if ((retval = target_write_memory(target, address, bank->bus_width, 1, word)) != ERROR_OK) - { - return retval; - } - - if (cfi_intel_wait_status_busy(bank, 1000 * (1 << cfi_info->word_write_timeout_max)) != 0x80) - { - cfi_command(bank, 0xff, command); - if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - - LOG_ERROR("couldn't write word at base 0x%" PRIx32 ", address %" PRIx32 , bank->base, address); - return ERROR_FLASH_OPERATION_FAILED; - } - - return ERROR_OK; -} - -static int cfi_intel_write_words(struct flash_bank *bank, uint8_t *word, uint32_t wordcount, uint32_t address) -{ - int retval; - struct cfi_flash_bank *cfi_info = bank->driver_priv; - struct target *target = bank->target; - uint8_t command[8]; - - /* Calculate buffer size and boundary mask */ - uint32_t buffersize = (1UL << cfi_info->max_buf_write_size) * (bank->bus_width / bank->chip_width); - uint32_t buffermask = buffersize-1; - uint32_t bufferwsize; - - /* Check for valid range */ - if (address & buffermask) - { - LOG_ERROR("Write address at base 0x%" PRIx32 ", address %" PRIx32 " not aligned to 2^%d boundary", - bank->base, address, cfi_info->max_buf_write_size); - return ERROR_FLASH_OPERATION_FAILED; - } - switch (bank->chip_width) - { - case 4 : bufferwsize = buffersize / 4; break; - case 2 : bufferwsize = buffersize / 2; break; - case 1 : bufferwsize = buffersize; break; - default: - LOG_ERROR("Unsupported chip width %d", bank->chip_width); - return ERROR_FLASH_OPERATION_FAILED; - } - - bufferwsize/=(bank->bus_width / bank->chip_width); - - - /* Check for valid size */ - if (wordcount > bufferwsize) - { - LOG_ERROR("Number of data words %" PRId32 " exceeds available buffersize %" PRId32 , wordcount, buffersize); - return ERROR_FLASH_OPERATION_FAILED; - } - - /* Write to flash buffer */ - cfi_intel_clear_status_register(bank); - - /* Initiate buffer operation _*/ - cfi_command(bank, 0xE8, command); - if ((retval = target_write_memory(target, address, bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - if (cfi_intel_wait_status_busy(bank, 1000 * (1 << cfi_info->buf_write_timeout_max)) != 0x80) - { - cfi_command(bank, 0xff, command); - if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - - LOG_ERROR("couldn't start buffer write operation at base 0x%" PRIx32 ", address %" PRIx32 , bank->base, address); - return ERROR_FLASH_OPERATION_FAILED; - } - - /* Write buffer wordcount-1 and data words */ - cfi_command(bank, bufferwsize-1, command); - if ((retval = target_write_memory(target, address, bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - - if ((retval = target_write_memory(target, address, bank->bus_width, bufferwsize, word)) != ERROR_OK) - { - return retval; - } - - /* Commit write operation */ - cfi_command(bank, 0xd0, command); - if ((retval = target_write_memory(target, address, bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - if (cfi_intel_wait_status_busy(bank, 1000 * (1 << cfi_info->buf_write_timeout_max)) != 0x80) - { - cfi_command(bank, 0xff, command); - if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - - LOG_ERROR("Buffer write at base 0x%" PRIx32 ", address %" PRIx32 " failed.", bank->base, address); - return ERROR_FLASH_OPERATION_FAILED; - } - - return ERROR_OK; -} - -static int cfi_spansion_write_word(struct flash_bank *bank, uint8_t *word, uint32_t address) -{ - int retval; - struct cfi_flash_bank *cfi_info = bank->driver_priv; - struct cfi_spansion_pri_ext *pri_ext = cfi_info->pri_ext; - struct target *target = bank->target; - uint8_t command[8]; - - cfi_command(bank, 0xaa, command); - if ((retval = target_write_memory(target, flash_address(bank, 0, pri_ext->_unlock1), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - - cfi_command(bank, 0x55, command); - if ((retval = target_write_memory(target, flash_address(bank, 0, pri_ext->_unlock2), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - - cfi_command(bank, 0xa0, command); - if ((retval = target_write_memory(target, flash_address(bank, 0, pri_ext->_unlock1), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - - if ((retval = target_write_memory(target, address, bank->bus_width, 1, word)) != ERROR_OK) - { - return retval; - } - - if (cfi_spansion_wait_status_busy(bank, 1000 * (1 << cfi_info->word_write_timeout_max)) != ERROR_OK) - { - cfi_command(bank, 0xf0, command); - if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - - LOG_ERROR("couldn't write word at base 0x%" PRIx32 ", address %" PRIx32 , bank->base, address); - return ERROR_FLASH_OPERATION_FAILED; - } - - return ERROR_OK; -} - -static int cfi_spansion_write_words(struct flash_bank *bank, uint8_t *word, uint32_t wordcount, uint32_t address) -{ - int retval; - struct cfi_flash_bank *cfi_info = bank->driver_priv; - struct target *target = bank->target; - uint8_t command[8]; - struct cfi_spansion_pri_ext *pri_ext = cfi_info->pri_ext; - - /* Calculate buffer size and boundary mask */ - uint32_t buffersize = (1UL << cfi_info->max_buf_write_size) * (bank->bus_width / bank->chip_width); - uint32_t buffermask = buffersize-1; - uint32_t bufferwsize; - - /* Check for valid range */ - if (address & buffermask) - { - LOG_ERROR("Write address at base 0x%" PRIx32 ", address %" PRIx32 " not aligned to 2^%d boundary", bank->base, address, cfi_info->max_buf_write_size); - return ERROR_FLASH_OPERATION_FAILED; - } - switch (bank->chip_width) - { - case 4 : bufferwsize = buffersize / 4; break; - case 2 : bufferwsize = buffersize / 2; break; - case 1 : bufferwsize = buffersize; break; - default: - LOG_ERROR("Unsupported chip width %d", bank->chip_width); - return ERROR_FLASH_OPERATION_FAILED; - } - - bufferwsize/=(bank->bus_width / bank->chip_width); - - /* Check for valid size */ - if (wordcount > bufferwsize) - { - LOG_ERROR("Number of data words %" PRId32 " exceeds available buffersize %" PRId32, wordcount, buffersize); - return ERROR_FLASH_OPERATION_FAILED; - } - - // Unlock - cfi_command(bank, 0xaa, command); - if ((retval = target_write_memory(target, flash_address(bank, 0, pri_ext->_unlock1), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - - cfi_command(bank, 0x55, command); - if ((retval = target_write_memory(target, flash_address(bank, 0, pri_ext->_unlock2), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - - // Buffer load command - cfi_command(bank, 0x25, command); - if ((retval = target_write_memory(target, address, bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - - /* Write buffer wordcount-1 and data words */ - cfi_command(bank, bufferwsize-1, command); - if ((retval = target_write_memory(target, address, bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - - if ((retval = target_write_memory(target, address, bank->bus_width, bufferwsize, word)) != ERROR_OK) - { - return retval; - } - - /* Commit write operation */ - cfi_command(bank, 0x29, command); - if ((retval = target_write_memory(target, address, bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - - if (cfi_spansion_wait_status_busy(bank, 1000 * (1 << cfi_info->word_write_timeout_max)) != ERROR_OK) - { - cfi_command(bank, 0xf0, command); - if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - - LOG_ERROR("couldn't write block at base 0x%" PRIx32 ", address %" PRIx32 ", size %" PRIx32 , bank->base, address, bufferwsize); - return ERROR_FLASH_OPERATION_FAILED; - } - - return ERROR_OK; -} - -static int cfi_write_word(struct flash_bank *bank, uint8_t *word, uint32_t address) -{ - struct cfi_flash_bank *cfi_info = bank->driver_priv; - - switch (cfi_info->pri_id) - { - case 1: - case 3: - return cfi_intel_write_word(bank, word, address); - break; - case 2: - return cfi_spansion_write_word(bank, word, address); - break; - default: - LOG_ERROR("cfi primary command set %i unsupported", cfi_info->pri_id); - break; - } - - return ERROR_FLASH_OPERATION_FAILED; -} - -static int cfi_write_words(struct flash_bank *bank, uint8_t *word, uint32_t wordcount, uint32_t address) -{ - struct cfi_flash_bank *cfi_info = bank->driver_priv; - - switch (cfi_info->pri_id) - { - case 1: - case 3: - return cfi_intel_write_words(bank, word, wordcount, address); - break; - case 2: - return cfi_spansion_write_words(bank, word, wordcount, address); - break; - default: - LOG_ERROR("cfi primary command set %i unsupported", cfi_info->pri_id); - break; - } - - return ERROR_FLASH_OPERATION_FAILED; -} - -int cfi_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) -{ - struct cfi_flash_bank *cfi_info = bank->driver_priv; - struct target *target = bank->target; - uint32_t address = bank->base + offset; /* address of first byte to be programmed */ - uint32_t write_p, copy_p; - int align; /* number of unaligned bytes */ - int blk_count; /* number of bus_width bytes for block copy */ - uint8_t current_word[CFI_MAX_BUS_WIDTH * 4]; /* word (bus_width size) currently being programmed */ - int i; - int retval; - - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - if (offset + count > bank->size) - return ERROR_FLASH_DST_OUT_OF_BANK; - - if (cfi_info->qry[0] != 'Q') - return ERROR_FLASH_BANK_NOT_PROBED; - - /* start at the first byte of the first word (bus_width size) */ - write_p = address & ~(bank->bus_width - 1); - if ((align = address - write_p) != 0) - { - LOG_INFO("Fixup %d unaligned head bytes", align); - - for (i = 0; i < bank->bus_width; i++) - current_word[i] = 0; - copy_p = write_p; - - /* copy bytes before the first write address */ - for (i = 0; i < align; ++i, ++copy_p) - { - uint8_t byte; - if ((retval = target_read_memory(target, copy_p, 1, 1, &byte)) != ERROR_OK) - { - return retval; - } - cfi_add_byte(bank, current_word, byte); - } - - /* add bytes from the buffer */ - for (; (i < bank->bus_width) && (count > 0); i++) - { - cfi_add_byte(bank, current_word, *buffer++); - count--; - copy_p++; - } - - /* if the buffer is already finished, copy bytes after the last write address */ - for (; (count == 0) && (i < bank->bus_width); ++i, ++copy_p) - { - uint8_t byte; - if ((retval = target_read_memory(target, copy_p, 1, 1, &byte)) != ERROR_OK) - { - return retval; - } - cfi_add_byte(bank, current_word, byte); - } - - retval = cfi_write_word(bank, current_word, write_p); - if (retval != ERROR_OK) - return retval; - write_p = copy_p; - } - - /* handle blocks of bus_size aligned bytes */ - blk_count = count & ~(bank->bus_width - 1); /* round down, leave tail bytes */ - switch (cfi_info->pri_id) - { - /* try block writes (fails without working area) */ - case 1: - case 3: - retval = cfi_intel_write_block(bank, buffer, write_p, blk_count); - break; - case 2: - retval = cfi_spansion_write_block(bank, buffer, write_p, blk_count); - break; - default: - LOG_ERROR("cfi primary command set %i unsupported", cfi_info->pri_id); - retval = ERROR_FLASH_OPERATION_FAILED; - break; - } - if (retval == ERROR_OK) - { - /* Increment pointers and decrease count on succesful block write */ - buffer += blk_count; - write_p += blk_count; - count -= blk_count; - } - else - { - if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) - { - //adjust buffersize for chip width - uint32_t buffersize = (1UL << cfi_info->max_buf_write_size) * (bank->bus_width / bank->chip_width); - uint32_t buffermask = buffersize-1; - uint32_t bufferwsize; - - switch (bank->chip_width) - { - case 4 : bufferwsize = buffersize / 4; break; - case 2 : bufferwsize = buffersize / 2; break; - case 1 : bufferwsize = buffersize; break; - default: - LOG_ERROR("Unsupported chip width %d", bank->chip_width); - return ERROR_FLASH_OPERATION_FAILED; - } - - bufferwsize/=(bank->bus_width / bank->chip_width); - - /* fall back to memory writes */ - while (count >= (uint32_t)bank->bus_width) - { - int fallback; - if ((write_p & 0xff) == 0) - { - LOG_INFO("Programming at %08" PRIx32 ", count %08" PRIx32 " bytes remaining", write_p, count); - } - fallback = 1; - if ((bufferwsize > 0) && (count >= buffersize) && !(write_p & buffermask)) - { - retval = cfi_write_words(bank, buffer, bufferwsize, write_p); - if (retval == ERROR_OK) - { - buffer += buffersize; - write_p += buffersize; - count -= buffersize; - fallback = 0; - } - } - /* try the slow way? */ - if (fallback) - { - for (i = 0; i < bank->bus_width; i++) - current_word[i] = 0; - - for (i = 0; i < bank->bus_width; i++) - { - cfi_add_byte(bank, current_word, *buffer++); - } - - retval = cfi_write_word(bank, current_word, write_p); - if (retval != ERROR_OK) - return retval; - - write_p += bank->bus_width; - count -= bank->bus_width; - } - } - } - else - return retval; - } - - /* return to read array mode, so we can read from flash again for padding */ - cfi_command(bank, 0xf0, current_word); - if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, current_word)) != ERROR_OK) - { - return retval; - } - cfi_command(bank, 0xff, current_word); - if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, current_word)) != ERROR_OK) - { - return retval; - } - - /* handle unaligned tail bytes */ - if (count > 0) - { - LOG_INFO("Fixup %" PRId32 " unaligned tail bytes", count); - - copy_p = write_p; - for (i = 0; i < bank->bus_width; i++) - current_word[i] = 0; - - for (i = 0; (i < bank->bus_width) && (count > 0); ++i, ++copy_p) - { - cfi_add_byte(bank, current_word, *buffer++); - count--; - } - for (; i < bank->bus_width; ++i, ++copy_p) - { - uint8_t byte; - if ((retval = target_read_memory(target, copy_p, 1, 1, &byte)) != ERROR_OK) - { - return retval; - } - cfi_add_byte(bank, current_word, byte); - } - retval = cfi_write_word(bank, current_word, write_p); - if (retval != ERROR_OK) - return retval; - } - - /* return to read array mode */ - cfi_command(bank, 0xf0, current_word); - if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, current_word)) != ERROR_OK) - { - return retval; - } - cfi_command(bank, 0xff, current_word); - return target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, current_word); -} - -static void cfi_fixup_atmel_reversed_erase_regions(struct flash_bank *bank, void *param) -{ - (void) param; - struct cfi_flash_bank *cfi_info = bank->driver_priv; - struct cfi_spansion_pri_ext *pri_ext = cfi_info->pri_ext; - - pri_ext->_reversed_geometry = 1; -} - -static void cfi_fixup_0002_erase_regions(struct flash_bank *bank, void *param) -{ - int i; - struct cfi_flash_bank *cfi_info = bank->driver_priv; - struct cfi_spansion_pri_ext *pri_ext = cfi_info->pri_ext; - (void) param; - - if ((pri_ext->_reversed_geometry) || (pri_ext->TopBottom == 3)) - { - LOG_DEBUG("swapping reversed erase region information on cmdset 0002 device"); - - for (i = 0; i < cfi_info->num_erase_regions / 2; i++) - { - int j = (cfi_info->num_erase_regions - 1) - i; - uint32_t swap; - - swap = cfi_info->erase_region_info[i]; - cfi_info->erase_region_info[i] = cfi_info->erase_region_info[j]; - cfi_info->erase_region_info[j] = swap; - } - } -} - -static void cfi_fixup_0002_unlock_addresses(struct flash_bank *bank, void *param) -{ - struct cfi_flash_bank *cfi_info = bank->driver_priv; - struct cfi_spansion_pri_ext *pri_ext = cfi_info->pri_ext; - struct cfi_unlock_addresses *unlock_addresses = param; - - pri_ext->_unlock1 = unlock_addresses->unlock1; - pri_ext->_unlock2 = unlock_addresses->unlock2; -} - - -static int cfi_query_string(struct flash_bank *bank, int address) -{ - struct cfi_flash_bank *cfi_info = bank->driver_priv; - struct target *target = bank->target; - int retval; - uint8_t command[8]; - - cfi_command(bank, 0x98, command); - if ((retval = target_write_memory(target, flash_address(bank, 0, address), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - - cfi_info->qry[0] = cfi_query_u8(bank, 0, 0x10); - cfi_info->qry[1] = cfi_query_u8(bank, 0, 0x11); - cfi_info->qry[2] = cfi_query_u8(bank, 0, 0x12); - - LOG_DEBUG("CFI qry returned: 0x%2.2x 0x%2.2x 0x%2.2x", cfi_info->qry[0], cfi_info->qry[1], cfi_info->qry[2]); - - if ((cfi_info->qry[0] != 'Q') || (cfi_info->qry[1] != 'R') || (cfi_info->qry[2] != 'Y')) - { - cfi_command(bank, 0xf0, command); - if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - cfi_command(bank, 0xff, command); - if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - LOG_ERROR("Could not probe bank: no QRY"); - return ERROR_FLASH_BANK_INVALID; - } - - return ERROR_OK; -} - -static int cfi_probe(struct flash_bank *bank) -{ - struct cfi_flash_bank *cfi_info = bank->driver_priv; - struct target *target = bank->target; - uint8_t command[8]; - int num_sectors = 0; - int i; - int sector = 0; - uint32_t unlock1 = 0x555; - uint32_t unlock2 = 0x2aa; - int retval; - - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - cfi_info->probed = 0; - - /* JEDEC standard JESD21C uses 0x5555 and 0x2aaa as unlock addresses, - * while CFI compatible AMD/Spansion flashes use 0x555 and 0x2aa - */ - if (cfi_info->jedec_probe) - { - unlock1 = 0x5555; - unlock2 = 0x2aaa; - } - - /* switch to read identifier codes mode ("AUTOSELECT") */ - cfi_command(bank, 0xaa, command); - if ((retval = target_write_memory(target, flash_address(bank, 0, unlock1), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - cfi_command(bank, 0x55, command); - if ((retval = target_write_memory(target, flash_address(bank, 0, unlock2), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - cfi_command(bank, 0x90, command); - if ((retval = target_write_memory(target, flash_address(bank, 0, unlock1), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - - if (bank->chip_width == 1) - { - uint8_t manufacturer, device_id; - if ((retval = target_read_u8(target, flash_address(bank, 0, 0x00), &manufacturer)) != ERROR_OK) - { - return retval; - } - if ((retval = target_read_u8(target, flash_address(bank, 0, 0x01), &device_id)) != ERROR_OK) - { - return retval; - } - cfi_info->manufacturer = manufacturer; - cfi_info->device_id = device_id; - } - else if (bank->chip_width == 2) - { - if ((retval = target_read_u16(target, flash_address(bank, 0, 0x00), &cfi_info->manufacturer)) != ERROR_OK) - { - return retval; - } - if ((retval = target_read_u16(target, flash_address(bank, 0, 0x01), &cfi_info->device_id)) != ERROR_OK) - { - return retval; - } - } - - LOG_INFO("Flash Manufacturer/Device: 0x%04x 0x%04x", cfi_info->manufacturer, cfi_info->device_id); - /* switch back to read array mode */ - cfi_command(bank, 0xf0, command); - if ((retval = target_write_memory(target, flash_address(bank, 0, 0x00), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - cfi_command(bank, 0xff, command); - if ((retval = target_write_memory(target, flash_address(bank, 0, 0x00), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - - /* check device/manufacturer ID for known non-CFI flashes. */ - cfi_fixup_non_cfi(bank); - - /* query only if this is a CFI compatible flash, - * otherwise the relevant info has already been filled in - */ - if (cfi_info->not_cfi == 0) - { - int retval; - - /* enter CFI query mode - * according to JEDEC Standard No. 68.01, - * a single bus sequence with address = 0x55, data = 0x98 should put - * the device into CFI query mode. - * - * SST flashes clearly violate this, and we will consider them incompatbile for now - */ - - retval = cfi_query_string(bank, 0x55); - if (retval != ERROR_OK) - { - /* - * Spansion S29WS-N CFI query fix is to try 0x555 if 0x55 fails. Should - * be harmless enough: - * - * http://www.infradead.org/pipermail/linux-mtd/2005-September/013618.html - */ - LOG_USER("Try workaround w/0x555 instead of 0x55 to get QRY."); - retval = cfi_query_string(bank, 0x555); - } - if (retval != ERROR_OK) - return retval; - - cfi_info->pri_id = cfi_query_u16(bank, 0, 0x13); - cfi_info->pri_addr = cfi_query_u16(bank, 0, 0x15); - cfi_info->alt_id = cfi_query_u16(bank, 0, 0x17); - cfi_info->alt_addr = cfi_query_u16(bank, 0, 0x19); - - LOG_DEBUG("qry: '%c%c%c', pri_id: 0x%4.4x, pri_addr: 0x%4.4x, alt_id: 0x%4.4x, alt_addr: 0x%4.4x", cfi_info->qry[0], cfi_info->qry[1], cfi_info->qry[2], cfi_info->pri_id, cfi_info->pri_addr, cfi_info->alt_id, cfi_info->alt_addr); - - cfi_info->vcc_min = cfi_query_u8(bank, 0, 0x1b); - cfi_info->vcc_max = cfi_query_u8(bank, 0, 0x1c); - cfi_info->vpp_min = cfi_query_u8(bank, 0, 0x1d); - cfi_info->vpp_max = cfi_query_u8(bank, 0, 0x1e); - cfi_info->word_write_timeout_typ = cfi_query_u8(bank, 0, 0x1f); - cfi_info->buf_write_timeout_typ = cfi_query_u8(bank, 0, 0x20); - cfi_info->block_erase_timeout_typ = cfi_query_u8(bank, 0, 0x21); - cfi_info->chip_erase_timeout_typ = cfi_query_u8(bank, 0, 0x22); - cfi_info->word_write_timeout_max = cfi_query_u8(bank, 0, 0x23); - cfi_info->buf_write_timeout_max = cfi_query_u8(bank, 0, 0x24); - cfi_info->block_erase_timeout_max = cfi_query_u8(bank, 0, 0x25); - cfi_info->chip_erase_timeout_max = cfi_query_u8(bank, 0, 0x26); - - LOG_DEBUG("Vcc min: %1.1x.%1.1x, Vcc max: %1.1x.%1.1x, Vpp min: %1.1x.%1.1x, Vpp max: %1.1x.%1.1x", - (cfi_info->vcc_min & 0xf0) >> 4, cfi_info->vcc_min & 0x0f, - (cfi_info->vcc_max & 0xf0) >> 4, cfi_info->vcc_max & 0x0f, - (cfi_info->vpp_min & 0xf0) >> 4, cfi_info->vpp_min & 0x0f, - (cfi_info->vpp_max & 0xf0) >> 4, cfi_info->vpp_max & 0x0f); - LOG_DEBUG("typ. word write timeout: %u, typ. buf write timeout: %u, typ. block erase timeout: %u, typ. chip erase timeout: %u", 1 << cfi_info->word_write_timeout_typ, 1 << cfi_info->buf_write_timeout_typ, - 1 << cfi_info->block_erase_timeout_typ, 1 << cfi_info->chip_erase_timeout_typ); - LOG_DEBUG("max. word write timeout: %u, max. buf write timeout: %u, max. block erase timeout: %u, max. chip erase timeout: %u", (1 << cfi_info->word_write_timeout_max) * (1 << cfi_info->word_write_timeout_typ), - (1 << cfi_info->buf_write_timeout_max) * (1 << cfi_info->buf_write_timeout_typ), - (1 << cfi_info->block_erase_timeout_max) * (1 << cfi_info->block_erase_timeout_typ), - (1 << cfi_info->chip_erase_timeout_max) * (1 << cfi_info->chip_erase_timeout_typ)); - - cfi_info->dev_size = 1 << cfi_query_u8(bank, 0, 0x27); - cfi_info->interface_desc = cfi_query_u16(bank, 0, 0x28); - cfi_info->max_buf_write_size = cfi_query_u16(bank, 0, 0x2a); - cfi_info->num_erase_regions = cfi_query_u8(bank, 0, 0x2c); - - LOG_DEBUG("size: 0x%" PRIx32 ", interface desc: %i, max buffer write size: %x", cfi_info->dev_size, cfi_info->interface_desc, (1 << cfi_info->max_buf_write_size)); - - if (cfi_info->num_erase_regions) - { - cfi_info->erase_region_info = malloc(4 * cfi_info->num_erase_regions); - for (i = 0; i < cfi_info->num_erase_regions; i++) - { - cfi_info->erase_region_info[i] = cfi_query_u32(bank, 0, 0x2d + (4 * i)); - LOG_DEBUG("erase region[%i]: %" PRIu32 " blocks of size 0x%" PRIx32 "", - i, - (cfi_info->erase_region_info[i] & 0xffff) + 1, - (cfi_info->erase_region_info[i] >> 16) * 256); - } - } - else - { - cfi_info->erase_region_info = NULL; - } - - /* We need to read the primary algorithm extended query table before calculating - * the sector layout to be able to apply fixups - */ - switch (cfi_info->pri_id) - { - /* Intel command set (standard and extended) */ - case 0x0001: - case 0x0003: - cfi_read_intel_pri_ext(bank); - break; - /* AMD/Spansion, Atmel, ... command set */ - case 0x0002: - cfi_info->status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7; /* default for all CFI flashs */ - cfi_read_0002_pri_ext(bank); - break; - default: - LOG_ERROR("cfi primary command set %i unsupported", cfi_info->pri_id); - break; - } - - /* return to read array mode - * we use both reset commands, as some Intel flashes fail to recognize the 0xF0 command - */ - cfi_command(bank, 0xf0, command); - if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - cfi_command(bank, 0xff, command); - if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - } /* end CFI case */ - - /* apply fixups depending on the primary command set */ - switch (cfi_info->pri_id) - { - /* Intel command set (standard and extended) */ - case 0x0001: - case 0x0003: - cfi_fixup(bank, cfi_0001_fixups); - break; - /* AMD/Spansion, Atmel, ... command set */ - case 0x0002: - cfi_fixup(bank, cfi_0002_fixups); - break; - default: - LOG_ERROR("cfi primary command set %i unsupported", cfi_info->pri_id); - break; - } - - if ((cfi_info->dev_size * bank->bus_width / bank->chip_width) != bank->size) - { - LOG_WARNING("configuration specifies 0x%" PRIx32 " size, but a 0x%" PRIx32 " size flash was found", bank->size, cfi_info->dev_size); - } - - if (cfi_info->num_erase_regions == 0) - { - /* a device might have only one erase block, spanning the whole device */ - bank->num_sectors = 1; - bank->sectors = malloc(sizeof(struct flash_sector)); - - bank->sectors[sector].offset = 0x0; - bank->sectors[sector].size = bank->size; - bank->sectors[sector].is_erased = -1; - bank->sectors[sector].is_protected = -1; - } - else - { - uint32_t offset = 0; - - for (i = 0; i < cfi_info->num_erase_regions; i++) - { - num_sectors += (cfi_info->erase_region_info[i] & 0xffff) + 1; - } - - bank->num_sectors = num_sectors; - bank->sectors = malloc(sizeof(struct flash_sector) * num_sectors); - - for (i = 0; i < cfi_info->num_erase_regions; i++) - { - uint32_t j; - for (j = 0; j < (cfi_info->erase_region_info[i] & 0xffff) + 1; j++) - { - bank->sectors[sector].offset = offset; - bank->sectors[sector].size = ((cfi_info->erase_region_info[i] >> 16) * 256) * bank->bus_width / bank->chip_width; - offset += bank->sectors[sector].size; - bank->sectors[sector].is_erased = -1; - bank->sectors[sector].is_protected = -1; - sector++; - } - } - if (offset != (cfi_info->dev_size * bank->bus_width / bank->chip_width)) - { - LOG_WARNING("CFI size is 0x%" PRIx32 ", but total sector size is 0x%" PRIx32 "", \ - (cfi_info->dev_size * bank->bus_width / bank->chip_width), offset); - } - } - - cfi_info->probed = 1; - - return ERROR_OK; -} - -static int cfi_auto_probe(struct flash_bank *bank) -{ - struct cfi_flash_bank *cfi_info = bank->driver_priv; - if (cfi_info->probed) - return ERROR_OK; - return cfi_probe(bank); -} - - -static int cfi_intel_protect_check(struct flash_bank *bank) -{ - int retval; - struct cfi_flash_bank *cfi_info = bank->driver_priv; - struct cfi_intel_pri_ext *pri_ext = cfi_info->pri_ext; - struct target *target = bank->target; - uint8_t command[CFI_MAX_BUS_WIDTH]; - int i; - - /* check if block lock bits are supported on this device */ - if (!(pri_ext->blk_status_reg_mask & 0x1)) - return ERROR_FLASH_OPERATION_FAILED; - - cfi_command(bank, 0x90, command); - if ((retval = target_write_memory(target, flash_address(bank, 0, 0x55), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - - for (i = 0; i < bank->num_sectors; i++) - { - uint8_t block_status = cfi_get_u8(bank, i, 0x2); - - if (block_status & 1) - bank->sectors[i].is_protected = 1; - else - bank->sectors[i].is_protected = 0; - } - - cfi_command(bank, 0xff, command); - return target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command); -} - -static int cfi_spansion_protect_check(struct flash_bank *bank) -{ - int retval; - struct cfi_flash_bank *cfi_info = bank->driver_priv; - struct cfi_spansion_pri_ext *pri_ext = cfi_info->pri_ext; - struct target *target = bank->target; - uint8_t command[8]; - int i; - - cfi_command(bank, 0xaa, command); - if ((retval = target_write_memory(target, flash_address(bank, 0, pri_ext->_unlock1), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - - cfi_command(bank, 0x55, command); - if ((retval = target_write_memory(target, flash_address(bank, 0, pri_ext->_unlock2), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - - cfi_command(bank, 0x90, command); - if ((retval = target_write_memory(target, flash_address(bank, 0, pri_ext->_unlock1), bank->bus_width, 1, command)) != ERROR_OK) - { - return retval; - } - - for (i = 0; i < bank->num_sectors; i++) - { - uint8_t block_status = cfi_get_u8(bank, i, 0x2); - - if (block_status & 1) - bank->sectors[i].is_protected = 1; - else - bank->sectors[i].is_protected = 0; - } - - cfi_command(bank, 0xf0, command); - return target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command); -} - -static int cfi_protect_check(struct flash_bank *bank) -{ - struct cfi_flash_bank *cfi_info = bank->driver_priv; - - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - if (cfi_info->qry[0] != 'Q') - return ERROR_FLASH_BANK_NOT_PROBED; - - switch (cfi_info->pri_id) - { - case 1: - case 3: - return cfi_intel_protect_check(bank); - break; - case 2: - return cfi_spansion_protect_check(bank); - break; - default: - LOG_ERROR("cfi primary command set %i unsupported", cfi_info->pri_id); - break; - } - - return ERROR_OK; -} - -static int cfi_info(struct flash_bank *bank, char *buf, int buf_size) -{ - int printed; - struct cfi_flash_bank *cfi_info = bank->driver_priv; - - if (cfi_info->qry[0] == (char)-1) - { - printed = snprintf(buf, buf_size, "\ncfi flash bank not probed yet\n"); - return ERROR_OK; - } - - if (cfi_info->not_cfi == 0) - printed = snprintf(buf, buf_size, "\ncfi information:\n"); - else - printed = snprintf(buf, buf_size, "\nnon-cfi flash:\n"); - buf += printed; - buf_size -= printed; - - printed = snprintf(buf, buf_size, "\nmfr: 0x%4.4x, id:0x%4.4x\n", - cfi_info->manufacturer, cfi_info->device_id); - buf += printed; - buf_size -= printed; - - if (cfi_info->not_cfi == 0) - { - printed = snprintf(buf, buf_size, "qry: '%c%c%c', pri_id: 0x%4.4x, pri_addr: 0x%4.4x, alt_id: 0x%4.4x, alt_addr: 0x%4.4x\n", cfi_info->qry[0], cfi_info->qry[1], cfi_info->qry[2], cfi_info->pri_id, cfi_info->pri_addr, cfi_info->alt_id, cfi_info->alt_addr); - buf += printed; - buf_size -= printed; - - printed = snprintf(buf, buf_size, "Vcc min: %1.1x.%1.1x, Vcc max: %1.1x.%1.1x, Vpp min: %1.1x.%1.1x, Vpp max: %1.1x.%1.1x\n", - (cfi_info->vcc_min & 0xf0) >> 4, cfi_info->vcc_min & 0x0f, - (cfi_info->vcc_max & 0xf0) >> 4, cfi_info->vcc_max & 0x0f, - (cfi_info->vpp_min & 0xf0) >> 4, cfi_info->vpp_min & 0x0f, - (cfi_info->vpp_max & 0xf0) >> 4, cfi_info->vpp_max & 0x0f); - buf += printed; - buf_size -= printed; - - printed = snprintf(buf, buf_size, "typ. word write timeout: %u, typ. buf write timeout: %u, typ. block erase timeout: %u, typ. chip erase timeout: %u\n", - 1 << cfi_info->word_write_timeout_typ, - 1 << cfi_info->buf_write_timeout_typ, - 1 << cfi_info->block_erase_timeout_typ, - 1 << cfi_info->chip_erase_timeout_typ); - buf += printed; - buf_size -= printed; - - printed = snprintf(buf, buf_size, "max. word write timeout: %u, max. buf write timeout: %u, max. block erase timeout: %u, max. chip erase timeout: %u\n", - (1 << cfi_info->word_write_timeout_max) * (1 << cfi_info->word_write_timeout_typ), - (1 << cfi_info->buf_write_timeout_max) * (1 << cfi_info->buf_write_timeout_typ), - (1 << cfi_info->block_erase_timeout_max) * (1 << cfi_info->block_erase_timeout_typ), - (1 << cfi_info->chip_erase_timeout_max) * (1 << cfi_info->chip_erase_timeout_typ)); - buf += printed; - buf_size -= printed; - - printed = snprintf(buf, buf_size, "size: 0x%" PRIx32 ", interface desc: %i, max buffer write size: %x\n", - cfi_info->dev_size, - cfi_info->interface_desc, - 1 << cfi_info->max_buf_write_size); - buf += printed; - buf_size -= printed; - - switch (cfi_info->pri_id) - { - case 1: - case 3: - cfi_intel_info(bank, buf, buf_size); - break; - case 2: - cfi_spansion_info(bank, buf, buf_size); - break; - default: - LOG_ERROR("cfi primary command set %i unsupported", cfi_info->pri_id); - break; - } - } - - return ERROR_OK; -} - -struct flash_driver cfi_flash = { - .name = "cfi", - .flash_bank_command = &cfi_flash_bank_command, - .erase = &cfi_erase, - .protect = &cfi_protect, - .write = &cfi_write, - .probe = &cfi_probe, - .auto_probe = &cfi_auto_probe, - .erase_check = &default_flash_blank_check, - .protect_check = &cfi_protect_check, - .info = &cfi_info, - }; diff --git a/src/flash/cfi.h b/src/flash/cfi.h deleted file mode 100644 index d55fd34e..00000000 --- a/src/flash/cfi.h +++ /dev/null @@ -1,164 +0,0 @@ -/*************************************************************************** - * 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 CFI_H -#define CFI_H - -#include "flash.h" - -#define CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7 0xE0 /* DQ5..DQ7 */ -#define CFI_STATUS_POLL_MASK_DQ6_DQ7 0xC0 /* DQ6..DQ7 */ - -struct cfi_flash_bank -{ - struct working_area *write_algorithm; - - int x16_as_x8; - int jedec_probe; - int not_cfi; - int probed; - - uint16_t manufacturer; - uint16_t device_id; - - char qry[3]; - - /* identification string */ - uint16_t pri_id; - uint16_t pri_addr; - uint16_t alt_id; - uint16_t alt_addr; - - /* device-system interface */ - uint8_t vcc_min; - uint8_t vcc_max; - uint8_t vpp_min; - uint8_t vpp_max; - uint8_t word_write_timeout_typ; - uint8_t buf_write_timeout_typ; - uint8_t block_erase_timeout_typ; - uint8_t chip_erase_timeout_typ; - uint8_t word_write_timeout_max; - uint8_t buf_write_timeout_max; - uint8_t block_erase_timeout_max; - uint8_t chip_erase_timeout_max; - - uint8_t status_poll_mask; - - /* flash geometry */ - uint32_t dev_size; - uint16_t interface_desc; - uint16_t max_buf_write_size; - uint8_t num_erase_regions; - uint32_t *erase_region_info; - - void *pri_ext; - void *alt_ext; -}; - -/* Intel primary extended query table - * as defined for the Advanced+ Boot Block Flash Memory (C3) - * and used by the linux kernel cfi driver (as of 2.6.14) - */ -struct cfi_intel_pri_ext -{ - char pri[3]; - uint8_t major_version; - uint8_t minor_version; - uint32_t feature_support; - uint8_t suspend_cmd_support; - uint16_t blk_status_reg_mask; - uint8_t vcc_optimal; - uint8_t vpp_optimal; - uint8_t num_protection_fields; - uint16_t prot_reg_addr; - uint8_t fact_prot_reg_size; - uint8_t user_prot_reg_size; - uint8_t extra[0]; -}; - -/* Spansion primary extended query table as defined for and used by - * the linux kernel cfi driver (as of 2.6.15) - */ -struct cfi_spansion_pri_ext -{ - uint8_t pri[3]; - uint8_t major_version; - uint8_t minor_version; - uint8_t SiliconRevision; /* bits 1-0: Address Sensitive Unlock */ - uint8_t EraseSuspend; - uint8_t BlkProt; - uint8_t TmpBlkUnprotect; - uint8_t BlkProtUnprot; - uint8_t SimultaneousOps; - uint8_t BurstMode; - uint8_t PageMode; - uint8_t VppMin; - uint8_t VppMax; - uint8_t TopBottom; - int _reversed_geometry; - uint32_t _unlock1; - uint32_t _unlock2; -}; - -/* Atmel primary extended query table as defined for and used by - * the linux kernel cfi driver (as of 2.6.20+) - */ -struct cfi_atmel_pri_ext -{ - uint8_t pri[3]; - uint8_t major_version; - uint8_t minor_version; - uint8_t features; - uint8_t bottom_boot; - uint8_t burst_mode; - uint8_t page_mode; -}; - -enum { - CFI_UNLOCK_555_2AA, - CFI_UNLOCK_5555_2AAA, -}; - -struct cfi_unlock_addresses -{ - uint32_t unlock1; - uint32_t unlock2; -}; - -struct cfi_fixup -{ - uint16_t mfr; - uint16_t id; - void (*fixup)(struct flash_bank *flash, void *param); - void *param; -}; - -#define CFI_MFR_AMD 0x0001 -#define CFI_MFR_FUJITSU 0x0004 -#define CFI_MFR_ATMEL 0x001F -#define CFI_MFR_ST 0x0020 /* STMicroelectronics */ -#define CFI_MFR_AMIC 0x0037 -#define CFI_MFR_SST 0x00BF -#define CFI_MFR_MX 0x00C2 - -#define CFI_MFR_ANY 0xffff -#define CFI_ID_ANY 0xffff - -#endif /* CFI_H */ diff --git a/src/flash/ecos.c b/src/flash/ecos.c deleted file mode 100644 index 7a0b26f3..00000000 --- a/src/flash/ecos.c +++ /dev/null @@ -1,444 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2007,2008 Øyvind Harboe * - * oyvind.harboe@zylin.com * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "flash.h" -#include "embeddedice.h" -#include "image.h" -#include "algorithm.h" - - -#if 0 -static uint32_t ecosflash_get_flash_status(struct flash_bank *bank); -static void ecosflash_set_flash_mode(struct flash_bank *bank,int mode); -static uint32_t ecosflash_wait_status_busy(struct flash_bank *bank, uint32_t waitbits, int timeout); -static int ecosflash_handle_gpnvm_command(struct command_context *cmd_ctx, char *cmd, char **args, int argc); -#endif - -struct ecosflash_flash_bank -{ - struct target *target; - struct working_area *write_algorithm; - struct working_area *erase_check_algorithm; - char *driverPath; - uint32_t start_address; -}; - -static const int sectorSize = 0x10000; - -char * -flash_errmsg(int err); - -#ifndef __ECOS -#define FLASH_ERR_OK 0x00 /* No error - operation complete */ -#define FLASH_ERR_INVALID 0x01 /* Invalid FLASH address */ -#define FLASH_ERR_ERASE 0x02 /* Error trying to erase */ -#define FLASH_ERR_LOCK 0x03 /* Error trying to lock/unlock */ -#define FLASH_ERR_PROGRAM 0x04 /* Error trying to program */ -#define FLASH_ERR_PROTOCOL 0x05 /* Generic error */ -#define FLASH_ERR_PROTECT 0x06 /* Device/region is write-protected */ -#define FLASH_ERR_NOT_INIT 0x07 /* FLASH info not yet initialized */ -#define FLASH_ERR_HWR 0x08 /* Hardware (configuration?) problem */ -#define FLASH_ERR_ERASE_SUSPEND 0x09 /* Device is in erase suspend mode */ -#define FLASH_ERR_PROGRAM_SUSPEND 0x0a /* Device is in in program suspend mode */ -#define FLASH_ERR_DRV_VERIFY 0x0b /* Driver failed to verify data */ -#define FLASH_ERR_DRV_TIMEOUT 0x0c /* Driver timed out waiting for device */ -#define FLASH_ERR_DRV_WRONG_PART 0x0d /* Driver does not support device */ -#define FLASH_ERR_LOW_VOLTAGE 0x0e /* Not enough juice to complete job */ - -char * -flash_errmsg(int err) -{ - switch (err) { - case FLASH_ERR_OK: - return "No error - operation complete"; - case FLASH_ERR_ERASE_SUSPEND: - return "Device is in erase suspend state"; - case FLASH_ERR_PROGRAM_SUSPEND: - return "Device is in program suspend state"; - case FLASH_ERR_INVALID: - return "Invalid FLASH address"; - case FLASH_ERR_ERASE: - return "Error trying to erase"; - case FLASH_ERR_LOCK: - return "Error trying to lock/unlock"; - case FLASH_ERR_PROGRAM: - return "Error trying to program"; - case FLASH_ERR_PROTOCOL: - return "Generic error"; - case FLASH_ERR_PROTECT: - return "Device/region is write-protected"; - case FLASH_ERR_NOT_INIT: - return "FLASH sub-system not initialized"; - case FLASH_ERR_DRV_VERIFY: - return "Data verify failed after operation"; - case FLASH_ERR_DRV_TIMEOUT: - return "Driver timed out waiting for device"; - case FLASH_ERR_DRV_WRONG_PART: - return "Driver does not support device"; - case FLASH_ERR_LOW_VOLTAGE: - return "Device reports low voltage"; - default: - return "Unknown error"; - } -} -#endif - -/* flash bank ecosflash - */ -FLASH_BANK_COMMAND_HANDLER(ecosflash_flash_bank_command) -{ - struct ecosflash_flash_bank *info; - - if (CMD_ARGC < 7) - { - LOG_WARNING("incomplete flash_bank ecosflash configuration"); - return ERROR_FLASH_BANK_INVALID; - } - - info = malloc(sizeof(struct ecosflash_flash_bank)); - if (info == NULL) - { - LOG_ERROR("no memory for flash bank info"); - exit(-1); - } - bank->driver_priv = info; - info->driverPath = strdup(CMD_ARGV[6]); - - /* eCos flash sector sizes are not exposed to OpenOCD, use 0x10000 as - * a way to improve impedance match between OpenOCD and eCos flash - * driver. - */ - int i = 0; - uint32_t offset = 0; - bank->num_sectors = bank->size/sectorSize; - bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors); - for (i = 0; i < bank->num_sectors; i++) - { - bank->sectors[i].offset = offset; - bank->sectors[i].size = sectorSize; - offset += bank->sectors[i].size; - bank->sectors[i].is_erased = -1; - bank->sectors[i].is_protected = 0; - } - - info->target = get_target(CMD_ARGV[5]); - if (info->target == NULL) - { - LOG_ERROR("target '%s' not defined", CMD_ARGV[5]); - return ERROR_FAIL; - } - return ERROR_OK; -} - -static int loadDriver(struct ecosflash_flash_bank *info) -{ - size_t buf_cnt; - size_t image_size; - struct image image; - - image.base_address_set = 0; - image.start_address_set = 0; - struct target *target = info->target; - int retval; - - if ((retval = image_open(&image, info->driverPath, NULL)) != ERROR_OK) - { - return retval; - } - - info->start_address = image.start_address; - - image_size = 0x0; - int i; - for (i = 0; i < image.num_sections; i++) - { - void *buffer = malloc(image.sections[i].size); - int retval; - if ((retval = image_read_section(&image, i, 0x0, image.sections[i].size, buffer, &buf_cnt)) != ERROR_OK) - { - free(buffer); - image_close(&image); - return retval; - } - target_write_buffer(target, image.sections[i].base_address, buf_cnt, buffer); - image_size += buf_cnt; - LOG_DEBUG("%zu bytes written at address 0x%8.8" PRIx32 "", - buf_cnt, image.sections[i].base_address); - - free(buffer); - } - - image_close(&image); - - return ERROR_OK; -} - -static int const OFFSET_ERASE = 0x0; -static int const OFFSET_ERASE_SIZE = 0x8; -static int const OFFSET_FLASH = 0xc; -static int const OFFSET_FLASH_SIZE = 0x8; -static int const OFFSET_GET_WORKAREA = 0x18; -static int const OFFSET_GET_WORKAREA_SIZE = 0x4; - -static int runCode(struct ecosflash_flash_bank *info, - uint32_t codeStart, uint32_t codeStop, uint32_t r0, uint32_t r1, uint32_t r2, - uint32_t *result, - /* timeout in ms */ - int timeout) -{ - struct target *target = info->target; - - struct reg_param reg_params[3]; - struct armv4_5_algorithm armv4_5_info; - 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_IN_OUT); - init_reg_param(®_params[1], "r1", 32, PARAM_OUT); - init_reg_param(®_params[2], "r2", 32, PARAM_OUT); - - buf_set_u32(reg_params[0].value, 0, 32, r0); - buf_set_u32(reg_params[1].value, 0, 32, r1); - buf_set_u32(reg_params[2].value, 0, 32, r2); - - int retval; - if ((retval = target_run_algorithm(target, 0, NULL, 3, reg_params, - codeStart, - codeStop, timeout, - &armv4_5_info)) != ERROR_OK) - { - LOG_ERROR("error executing eCos flash algorithm"); - return retval; - } - - *result = buf_get_u32(reg_params[0].value, 0, 32); - - destroy_reg_param(®_params[0]); - destroy_reg_param(®_params[1]); - destroy_reg_param(®_params[2]); - - return ERROR_OK; -} - -static int eCosBoard_erase(struct ecosflash_flash_bank *info, uint32_t address, uint32_t len) -{ - int retval; - int timeout = (len / 20480 + 1) * 1000; /*asume 20 KB/s*/ - - retval = loadDriver(info); - if (retval != ERROR_OK) - return retval; - - uint32_t flashErr; - retval = runCode(info, - info->start_address + OFFSET_ERASE, - info->start_address + OFFSET_ERASE + OFFSET_ERASE_SIZE, - address, - len, - 0, - &flashErr, - timeout -); - if (retval != ERROR_OK) - return retval; - - if (flashErr != 0x0) - { - LOG_ERROR("Flash erase failed with %d (%s)\n", (int)flashErr, flash_errmsg(flashErr)); - return ERROR_FAIL; - } - - return ERROR_OK; -} - -static int eCosBoard_flash(struct ecosflash_flash_bank *info, void *data, uint32_t address, uint32_t len) -{ - struct target *target = info->target; - const int chunk = 8192; - int retval = ERROR_OK; - int timeout = (chunk / 20480 + 1) * 1000; /*asume 20 KB/s + 1 second*/ - - retval = loadDriver(info); - if (retval != ERROR_OK) - return retval; - - uint32_t buffer; - retval = runCode(info, - info->start_address + OFFSET_GET_WORKAREA, - info->start_address + OFFSET_GET_WORKAREA + OFFSET_GET_WORKAREA_SIZE, - 0, - 0, - 0, - &buffer, - 1000); - if (retval != ERROR_OK) - return retval; - - - uint32_t i; - for (i = 0; i < len; i += chunk) - { - int t = len-i; - if (t > chunk) - { - t = chunk; - } - - int retval; - retval = target_write_buffer(target, buffer, t, ((uint8_t *)data) + i); - if (retval != ERROR_OK) - return retval; - - uint32_t flashErr; - retval = runCode(info, - info->start_address + OFFSET_FLASH, - info->start_address + OFFSET_FLASH + OFFSET_FLASH_SIZE, - buffer, - address + i, - t, - &flashErr, - timeout); - if (retval != ERROR_OK) - return retval; - - if (flashErr != 0x0) - { - LOG_ERROR("Flash prog failed with %d (%s)\n", (int)flashErr, flash_errmsg(flashErr)); - return ERROR_FAIL; - } - } - return ERROR_OK; -} - -static int ecosflash_probe(struct flash_bank *bank) -{ - return ERROR_OK; -} - -#if 0 -static void command(struct flash_bank *bank, uint8_t cmd, uint8_t *cmd_buf) -{ - struct ecosflash_flash_bank *info = bank->driver_priv; - int i; - - if (info->target->endianness == TARGET_LITTLE_ENDIAN) - { - for (i = bank->bus_width; i > 0; i--) - { - *cmd_buf++ = (i & (bank->chip_width - 1)) ? 0x0 : cmd; - } - } - else - { - for (i = 1; i <= bank->bus_width; i++) - { - *cmd_buf++ = (i & (bank->chip_width - 1)) ? 0x0 : cmd; - } - } -} -#endif - -#if 0 -static uint32_t ecosflash_address(struct flash_bank *bank, uint32_t address) -{ - uint32_t retval = 0; - switch (bank->bus_width) - { - case 4: - retval = address & 0xfffffffc; - case 2: - retval = address & 0xfffffffe; - case 1: - retval = address; - } - - return retval + bank->base; -} -#endif - -static int ecosflash_erase(struct flash_bank *bank, int first, int last) -{ - struct flash_bank *c = bank; - struct ecosflash_flash_bank *info = bank->driver_priv; - return eCosBoard_erase(info, c->base + first*sectorSize, sectorSize*(last-first + 1)); -} - -static int ecosflash_protect(struct flash_bank *bank, int set, int first, int last) -{ - return ERROR_OK; -} - -static int ecosflash_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) -{ - struct ecosflash_flash_bank *info = bank->driver_priv; - struct flash_bank *c = bank; - return eCosBoard_flash(info, buffer, c->base + offset, count); -} - -static int ecosflash_protect_check(struct flash_bank *bank) -{ - return ERROR_OK; -} - -static int ecosflash_info(struct flash_bank *bank, char *buf, int buf_size) -{ - struct ecosflash_flash_bank *info = bank->driver_priv; - snprintf(buf, buf_size, "eCos flash driver: %s", info->driverPath); - return ERROR_OK; -} - -#if 0 -static uint32_t ecosflash_get_flash_status(struct flash_bank *bank) -{ - return ERROR_OK; -} - -static void ecosflash_set_flash_mode(struct flash_bank *bank,int mode) -{ - -} - -static uint32_t ecosflash_wait_status_busy(struct flash_bank *bank, uint32_t waitbits, int timeout) -{ - return ERROR_OK; -} - -static int ecosflash_handle_gpnvm_command(struct command_context *cmd_ctx, char *cmd, char **args, int argc) -{ - return ERROR_OK; -} -#endif - -struct flash_driver ecosflash_flash = { - .name = "ecosflash", - .flash_bank_command = &ecosflash_flash_bank_command, - .erase = &ecosflash_erase, - .protect = &ecosflash_protect, - .write = &ecosflash_write, - .probe = &ecosflash_probe, - .auto_probe = &ecosflash_probe, - .erase_check = &default_flash_blank_check, - .protect_check = &ecosflash_protect_check, - .info = &ecosflash_info - }; diff --git a/src/flash/faux.c b/src/flash/faux.c deleted file mode 100644 index caec2c79..00000000 --- a/src/flash/faux.c +++ /dev/null @@ -1,149 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2009 Øyvind Harboe * - * oyvind.harboe@zylin.com * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "flash.h" -#include "image.h" -#include "../hello.h" - - -struct faux_flash_bank -{ - struct target *target; - uint8_t *memory; - uint32_t start_address; -}; - -static const int sectorSize = 0x10000; - - -/* flash bank faux - */ -FLASH_BANK_COMMAND_HANDLER(faux_flash_bank_command) -{ - struct faux_flash_bank *info; - - if (CMD_ARGC < 6) - { - LOG_WARNING("incomplete flash_bank faux configuration"); - return ERROR_FLASH_BANK_INVALID; - } - - info = malloc(sizeof(struct faux_flash_bank)); - if (info == NULL) - { - LOG_ERROR("no memory for flash bank info"); - return ERROR_FAIL; - } - info->memory = malloc(bank->size); - if (info == NULL) - { - free(info); - LOG_ERROR("no memory for flash bank info"); - return ERROR_FAIL; - } - bank->driver_priv = info; - - /* Use 0x10000 as a fixed sector size. */ - int i = 0; - uint32_t offset = 0; - bank->num_sectors = bank->size/sectorSize; - bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors); - for (i = 0; i < bank->num_sectors; i++) - { - bank->sectors[i].offset = offset; - bank->sectors[i].size = sectorSize; - offset += bank->sectors[i].size; - bank->sectors[i].is_erased = -1; - bank->sectors[i].is_protected = 0; - } - - info->target = get_target(CMD_ARGV[5]); - if (info->target == NULL) - { - LOG_ERROR("target '%s' not defined", CMD_ARGV[5]); - free(info->memory); - free(info); - return ERROR_FAIL; - } - return ERROR_OK; -} - -static int faux_erase(struct flash_bank *bank, int first, int last) -{ - struct faux_flash_bank *info = bank->driver_priv; - memset(info->memory + first*sectorSize, 0xff, sectorSize*(last-first + 1)); - return ERROR_OK; -} - -static int faux_protect(struct flash_bank *bank, int set, int first, int last) -{ - LOG_USER("set protection sector %d to %d to %s", first, last, set?"on":"off"); - return ERROR_OK; -} - -static int faux_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) -{ - struct faux_flash_bank *info = bank->driver_priv; - memcpy(info->memory + offset, buffer, count); - return ERROR_OK; -} - -static int faux_protect_check(struct flash_bank *bank) -{ - return ERROR_OK; -} - -static int faux_info(struct flash_bank *bank, char *buf, int buf_size) -{ - snprintf(buf, buf_size, "faux flash driver"); - return ERROR_OK; -} - -static int faux_probe(struct flash_bank *bank) -{ - return ERROR_OK; -} - -static const struct command_registration faux_command_handlers[] = { - { - .name = "faux", - .mode = COMMAND_ANY, - .help = "faux flash command group", - .chain = hello_command_handlers, - }, - COMMAND_REGISTRATION_DONE -}; - -struct flash_driver faux_flash = { - .name = "faux", - .commands = faux_command_handlers, - .flash_bank_command = &faux_flash_bank_command, - .erase = &faux_erase, - .protect = &faux_protect, - .write = &faux_write, - .probe = &faux_probe, - .auto_probe = &faux_probe, - .erase_check = &default_flash_blank_check, - .protect_check = &faux_protect_check, - .info = &faux_info - }; diff --git a/src/flash/lpc2000.c b/src/flash/lpc2000.c deleted file mode 100644 index 418b5b03..00000000 --- a/src/flash/lpc2000.c +++ /dev/null @@ -1,812 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2005 by Dominic Rath * - * Dominic.Rath@gmx.de * - * * - * LPC1700 support Copyright (C) 2009 by Audrius Urmanavicius * - * didele.deze@gmail.com * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "lpc2000.h" -#include "armv7m.h" -#include "binarybuffer.h" -#include "algorithm.h" - - -/* flash programming support for NXP LPC17xx and 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 - * - * lpc1700: - * - 175x - * - 176x (tested with LPC1768) - */ - -static int lpc2000_build_sector_list(struct flash_bank *bank) -{ - struct lpc2000_flash_bank *lpc2000_info = bank->driver_priv; - int i; - uint32_t offset = 0; - - /* default to a 4096 write buffer */ - lpc2000_info->cmd51_max_buffer = 4096; - - if (lpc2000_info->variant == lpc2000_v1) - { - /* variant 1 has different layout for 128kb and 256kb flashes */ - if (bank->size == 128 * 1024) - { - bank->num_sectors = 16; - bank->sectors = malloc(sizeof(struct flash_sector) * 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(struct flash_sector) * 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 - { - LOG_ERROR("BUG: unknown bank->size encountered"); - exit(-1); - } - } - else if (lpc2000_info->variant == lpc2000_v2) - { - /* variant 2 has a uniform layout, only number of sectors differs */ - switch (bank->size) - { - case 4 * 1024: - lpc2000_info->cmd51_max_buffer = 1024; - bank->num_sectors = 1; - break; - case 8 * 1024: - lpc2000_info->cmd51_max_buffer = 1024; - bank->num_sectors = 2; - break; - case 16 * 1024: - bank->num_sectors = 4; - break; - case 32 * 1024: - bank->num_sectors = 8; - break; - case 64 * 1024: - bank->num_sectors = 9; - break; - case 128 * 1024: - bank->num_sectors = 11; - break; - case 256 * 1024: - bank->num_sectors = 15; - break; - case 512 * 1024: - case 500 * 1024: - bank->num_sectors = 27; - break; - default: - LOG_ERROR("BUG: unknown bank->size encountered"); - exit(-1); - break; - } - - bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors); - - for (i = 0; i < bank->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 if (lpc2000_info->variant == lpc1700) - { - switch(bank->size) - { - case 32 * 1024: - bank->num_sectors = 8; - break; - case 64 * 1024: - bank->num_sectors = 16; - break; - case 128 * 1024: - bank->num_sectors = 18; - break; - case 256 * 1024: - bank->num_sectors = 22; - break; - case 512 * 1024: - bank->num_sectors = 30; - break; - default: - LOG_ERROR("BUG: unknown bank->size encountered"); - exit(-1); - } - - bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors); - - for(i = 0; i < bank->num_sectors; i++) - { - bank->sectors[i].offset = offset; - /* sectors 0-15 are 4kB-sized, 16 and above are 32kB-sized for LPC17xx devices */ - bank->sectors[i].size = (i < 16)? 4 * 1024 : 32 * 1024; - offset += bank->sectors[i].size; - bank->sectors[i].is_erased = -1; - bank->sectors[i].is_protected = 1; - } - } - else - { - LOG_ERROR("BUG: unknown lpc2000_info->variant encountered"); - exit(-1); - } - - return ERROR_OK; -} - -/* call LPC1700/LPC2000 IAP function - * uses 180 bytes working area - * 0x0 to 0x7: jump gate (BX to thumb state, b -2 to wait) - * 0x8 to 0x1f: command parameter table (1+5 words) - * 0x20 to 0x33: command result table (1+4 words) - * 0x34 to 0xb3: stack (only 128b needed) - */ -static int lpc2000_iap_call(struct flash_bank *bank, int code, uint32_t param_table[5], uint32_t result_table[4]) -{ - int retval; - struct lpc2000_flash_bank *lpc2000_info = bank->driver_priv; - struct target *target = bank->target; - struct mem_param mem_params[2]; - struct reg_param reg_params[5]; - struct armv4_5_algorithm armv4_5_info; /* for LPC2000 */ - struct armv7m_algorithm armv7m_info; /* for LPC1700 */ - uint32_t status_code; - uint32_t iap_entry_point = 0; /* to make compiler happier */ - - /* regrab previously allocated working_area, or allocate a new one */ - if (!lpc2000_info->iap_working_area) - { - uint8_t jump_gate[8]; - - /* make sure we have a working area */ - if (target_alloc_working_area(target, 180, &lpc2000_info->iap_working_area) != ERROR_OK) - { - LOG_ERROR("no working area specified, can't write LPC2000 internal flash"); - return ERROR_FLASH_OPERATION_FAILED; - } - - /* write IAP code to working area */ - switch(lpc2000_info->variant) - { - case lpc1700: - target_buffer_set_u32(target, jump_gate, ARMV7M_T_BX(12)); - target_buffer_set_u32(target, jump_gate + 4, ARMV7M_T_B(0xfffffe)); - break; - case lpc2000_v1: - case lpc2000_v2: - target_buffer_set_u32(target, jump_gate, ARMV4_5_BX(12)); - target_buffer_set_u32(target, jump_gate + 4, ARMV4_5_B(0xfffffe, 0)); - break; - default: - LOG_ERROR("BUG: unknown bank->size encountered"); - exit(-1); - } - - if ((retval = target_write_memory(target, lpc2000_info->iap_working_area->address, 4, 2, jump_gate)) != ERROR_OK) - { - LOG_ERROR("Write memory at address 0x%8.8" PRIx32 " failed (check work_area definition)", lpc2000_info->iap_working_area->address); - return retval; - } - } - - switch(lpc2000_info->variant) - { - case lpc1700: - armv7m_info.common_magic = ARMV7M_COMMON_MAGIC; - armv7m_info.core_mode = ARMV7M_MODE_ANY; - iap_entry_point = 0x1fff1ff1; - break; - case lpc2000_v1: - case lpc2000_v2: - 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; - iap_entry_point = 0x7ffffff1; - break; - default: - LOG_ERROR("BUG: unknown lpc2000->variant encountered"); - exit(-1); - } - - /* command parameter table */ - init_mem_param(&mem_params[0], lpc2000_info->iap_working_area->address + 8, 6 * 4, PARAM_OUT); - target_buffer_set_u32(target, mem_params[0].value, code); - target_buffer_set_u32(target, mem_params[0].value + 0x04, param_table[0]); - target_buffer_set_u32(target, mem_params[0].value + 0x08, param_table[1]); - target_buffer_set_u32(target, mem_params[0].value + 0x0c, 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 + 0x08); - - /* command result table */ - init_mem_param(&mem_params[1], lpc2000_info->iap_working_area->address + 0x20, 5 * 4, 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, iap_entry_point); - - switch(lpc2000_info->variant) - { - case lpc1700: - /* IAP stack */ - init_reg_param(®_params[3], "sp", 32, PARAM_OUT); - buf_set_u32(reg_params[3].value, 0, 32, lpc2000_info->iap_working_area->address + 0xb4); - - /* return address */ - init_reg_param(®_params[4], "lr", 32, PARAM_OUT); - buf_set_u32(reg_params[4].value, 0, 32, (lpc2000_info->iap_working_area->address + 0x04) | 1); /* bit0 of LR = 1 to return in Thumb mode */ - - target_run_algorithm(target, 2, mem_params, 5, reg_params, lpc2000_info->iap_working_area->address, lpc2000_info->iap_working_area->address + 0x4, 10000, &armv7m_info); - break; - case lpc2000_v1: - case lpc2000_v2: - /* 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 + 0xb4); - - /* 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 + 0x04); - - target_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); - break; - default: - LOG_ERROR("BUG: unknown lpc2000->variant encountered"); - exit(-1); - } - - - status_code = target_buffer_get_u32(target, mem_params[1].value); - result_table[0] = target_buffer_get_u32(target, mem_params[1].value + 0x04); - result_table[1] = target_buffer_get_u32(target, mem_params[1].value + 0x08); - result_table[2] = target_buffer_get_u32(target, mem_params[1].value + 0x0c); - result_table[3] = target_buffer_get_u32(target, mem_params[1].value + 0x10); - - LOG_DEBUG("IAP command = %i (0x%8.8" PRIx32", 0x%8.8" PRIx32", 0x%8.8" PRIx32", 0x%8.8" PRIx32", 0x%8.8" PRIx32") completed with result = %8.8" PRIx32, - code, param_table[0], param_table[1], param_table[2], param_table[3], param_table[4], status_code); - - 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; -} - -static int lpc2000_iap_blank_check(struct flash_bank *bank, int first, int last) -{ - uint32_t param_table[5]; - uint32_t result_table[4]; - 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: - LOG_ERROR("BUG: unknown LPC2000 status code %i", status_code); - exit(-1); - } - } - - return ERROR_OK; -} - -/* - * flash bank lpc2000 0 0 [calc_checksum] - */ -FLASH_BANK_COMMAND_HANDLER(lpc2000_flash_bank_command) -{ - struct lpc2000_flash_bank *lpc2000_info; - - if (CMD_ARGC < 8) - { - LOG_WARNING("incomplete flash_bank lpc2000 configuration"); - return ERROR_FLASH_BANK_INVALID; - } - - lpc2000_info = malloc(sizeof(struct lpc2000_flash_bank)); - bank->driver_priv = lpc2000_info; - - if (strcmp(CMD_ARGV[6], "lpc2000_v1") == 0) - { - lpc2000_info->variant = lpc2000_v1; - lpc2000_info->cmd51_dst_boundary = 512; - lpc2000_info->cmd51_can_256b = 0; - lpc2000_info->cmd51_can_8192b = 1; - lpc2000_info->checksum_vector = 5; - } - else if (strcmp(CMD_ARGV[6], "lpc2000_v2") == 0) - { - lpc2000_info->variant = lpc2000_v2; - lpc2000_info->cmd51_dst_boundary = 256; - lpc2000_info->cmd51_can_256b = 1; - lpc2000_info->cmd51_can_8192b = 0; - lpc2000_info->checksum_vector = 5; - } - else if (strcmp(CMD_ARGV[6], "lpc1700") == 0) - { - lpc2000_info->variant = lpc1700; - lpc2000_info->cmd51_dst_boundary = 256; - lpc2000_info->cmd51_can_256b = 1; - lpc2000_info->cmd51_can_8192b = 0; - lpc2000_info->checksum_vector = 7; - } - else - { - LOG_ERROR("unknown LPC2000 variant: %s", CMD_ARGV[6]); - free(lpc2000_info); - return ERROR_FLASH_BANK_INVALID; - } - - lpc2000_info->iap_working_area = NULL; - COMMAND_PARSE_NUMBER(u32, CMD_ARGV[7], lpc2000_info->cclk); - lpc2000_info->calc_checksum = 0; - lpc2000_build_sector_list(bank); - - if (CMD_ARGC >= 9) - { - if (strcmp(CMD_ARGV[8], "calc_checksum") == 0) - lpc2000_info->calc_checksum = 1; - } - - return ERROR_OK; -} - -static int lpc2000_erase(struct flash_bank *bank, int first, int last) -{ - struct lpc2000_flash_bank *lpc2000_info = bank->driver_priv; - uint32_t param_table[5]; - uint32_t result_table[4]; - int status_code; - - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - 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: - LOG_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: - LOG_WARNING("lpc2000 erase sectors returned %i", status_code); - return ERROR_FLASH_OPERATION_FAILED; - } - - return ERROR_OK; -} - -static int lpc2000_protect(struct flash_bank *bank, int set, int first, int last) -{ - /* can't protect/unprotect on the lpc2000 */ - return ERROR_OK; -} - -static int lpc2000_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) -{ - struct lpc2000_flash_bank *lpc2000_info = bank->driver_priv; - struct target *target = bank->target; - uint32_t dst_min_alignment; - uint32_t bytes_remaining = count; - uint32_t bytes_written = 0; - int first_sector = 0; - int last_sector = 0; - uint32_t param_table[5]; - uint32_t result_table[4]; - int status_code; - int i; - struct working_area *download_area; - int retval = ERROR_OK; - - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - if (offset + count > bank->size) - return ERROR_FLASH_DST_OUT_OF_BANK; - - dst_min_alignment = lpc2000_info->cmd51_dst_boundary; - - if (offset % dst_min_alignment) - { - LOG_WARNING("offset 0x%" PRIx32 " breaks required alignment 0x%" PRIx32, 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 + DIV_ROUND_UP(count, dst_min_alignment) * dst_min_alignment > bank->sectors[i].offset) - last_sector = i; - } - - LOG_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) - { - uint32_t checksum = 0; - int i; - for (i = 0; i < 8; i++) - { - LOG_DEBUG("Vector 0x%2.2x: 0x%8.8" PRIx32, i * 4, buf_get_u32(buffer + (i * 4), 0, 32)); - if (i != lpc2000_info->checksum_vector) - checksum += buf_get_u32(buffer + (i * 4), 0, 32); - } - checksum = 0 - checksum; - LOG_DEBUG("checksum: 0x%8.8" PRIx32, checksum); - - uint32_t original_value = buf_get_u32(buffer + (lpc2000_info->checksum_vector * 4), 0, 32); - if (original_value != checksum) - { - LOG_WARNING("Verification will fail since checksum in image (0x%8.8" PRIx32 ") to be written to flash is different from calculated vector checksum (0x%8.8" PRIx32 ").", - original_value, checksum); - LOG_WARNING("To remove this warning modify build tools on developer PC to inject correct LPC vector checksum."); - } - - buf_set_u32(buffer + (lpc2000_info->checksum_vector * 4), 0, 32, checksum); - } - - /* allocate a working area */ - if (target_alloc_working_area(target, lpc2000_info->cmd51_max_buffer, &download_area) != ERROR_OK) - { - LOG_ERROR("no working area specified, can't write LPC2000 internal flash"); - return ERROR_FLASH_OPERATION_FAILED; - } - - while (bytes_remaining > 0) - { - uint32_t 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: - retval = ERROR_FLASH_OPERATION_FAILED; - break; - case LPC2000_CMD_SUCCESS: - break; - case LPC2000_INVALID_SECTOR: - retval = ERROR_FLASH_SECTOR_INVALID; - break; - default: - LOG_WARNING("lpc2000 prepare sectors returned %i", status_code); - retval = ERROR_FLASH_OPERATION_FAILED; - break; - } - - /* Exit if error occured */ - if (retval != ERROR_OK) - break; - - if (bytes_remaining >= thisrun_bytes) - { - if ((retval = target_write_buffer(bank->target, download_area->address, thisrun_bytes, buffer + bytes_written)) != ERROR_OK) - { - retval = ERROR_FLASH_OPERATION_FAILED; - break; - } - } - else - { - uint8_t *last_buffer = malloc(thisrun_bytes); - memcpy(last_buffer, buffer + bytes_written, bytes_remaining); - memset(last_buffer + bytes_remaining, 0xff, thisrun_bytes - bytes_remaining); - target_write_buffer(bank->target, download_area->address, thisrun_bytes, last_buffer); - free(last_buffer); - } - - LOG_DEBUG("writing 0x%" PRIx32 " bytes to address 0x%" PRIx32 , 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: - retval = ERROR_FLASH_OPERATION_FAILED; - break; - case LPC2000_CMD_SUCCESS: - break; - case LPC2000_INVALID_SECTOR: - retval = ERROR_FLASH_SECTOR_INVALID; - break; - default: - LOG_WARNING("lpc2000 returned %i", status_code); - retval = ERROR_FLASH_OPERATION_FAILED; - break; - } - - /* Exit if error occured */ - if (retval != ERROR_OK) - break; - - 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 retval; -} - -static int lpc2000_probe(struct flash_bank *bank) -{ - /* we can't probe on an lpc2000 - * if this is an lpc2xxx, it has the configured flash - */ - return ERROR_OK; -} - -static int lpc2000_erase_check(struct flash_bank *bank) -{ - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - return lpc2000_iap_blank_check(bank, 0, bank->num_sectors - 1); -} - -static int lpc2000_protect_check(struct flash_bank *bank) -{ - /* sectors are always protected */ - return ERROR_OK; -} - -static int lpc2000_info(struct flash_bank *bank, char *buf, int buf_size) -{ - struct lpc2000_flash_bank *lpc2000_info = bank->driver_priv; - - snprintf(buf, buf_size, "lpc2000 flash driver variant: %i, clk: %" PRIi32 "kHz" , lpc2000_info->variant, lpc2000_info->cclk); - - return ERROR_OK; -} - -COMMAND_HANDLER(lpc2000_handle_part_id_command) -{ - uint32_t param_table[5]; - uint32_t result_table[4]; - int status_code; - - if (CMD_ARGC < 1) - { - return ERROR_COMMAND_SYNTAX_ERROR; - } - - struct flash_bank *bank; - int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) - return retval; - - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("Target not 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.8" PRIx32 , result_table[0]); - } - - return ERROR_OK; -} - -static const struct command_registration lpc2000_exec_command_handlers[] = { - { - .name = "part_id", - .handler = &lpc2000_handle_part_id_command, - .mode = COMMAND_EXEC, - .help = "print part id of lpc2000 flash bank ", - }, - COMMAND_REGISTRATION_DONE -}; -static const struct command_registration lpc2000_command_handlers[] = { - { - .name = "lpc2000", - .mode = COMMAND_ANY, - .help = "lpc2000 flash command group", - .chain = lpc2000_exec_command_handlers, - }, - COMMAND_REGISTRATION_DONE -}; - -struct flash_driver lpc2000_flash = { - .name = "lpc2000", - .commands = lpc2000_command_handlers, - .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, - }; - - diff --git a/src/flash/lpc2000.h b/src/flash/lpc2000.h deleted file mode 100644 index 08e278a3..00000000 --- a/src/flash/lpc2000.h +++ /dev/null @@ -1,73 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2005 by Dominic Rath * - * Dominic.Rath@gmx.de * - * * - * LPC1700 support Copyright (C) 2009 by Audrius Urmanavicius * - * didele.deze@gmail.com * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ -#ifndef LPC2000_H -#define LPC2000_H - -#include "flash.h" - -typedef enum -{ - lpc2000_v1, - lpc2000_v2, - lpc1700 -} lpc2000_variant; - -struct lpc2000_flash_bank -{ - lpc2000_variant variant; - struct working_area *iap_working_area; - uint32_t cclk; - int cmd51_dst_boundary; - int cmd51_can_256b; - int cmd51_can_8192b; - int calc_checksum; - uint32_t cmd51_max_buffer; - int checksum_vector; -}; - -enum lpc2000_status_codes -{ - LPC2000_CMD_SUCCESS = 0, - LPC2000_INVALID_COMMAND = 1, - LPC2000_SRC_ADDR_ERROR = 2, - LPC2000_DST_ADDR_ERROR = 3, - LPC2000_SRC_ADDR_NOT_MAPPED = 4, - LPC2000_DST_ADDR_NOT_MAPPED = 5, - LPC2000_COUNT_ERROR = 6, - LPC2000_INVALID_SECTOR = 7, - LPC2000_SECTOR_NOT_BLANK = 8, - LPC2000_SECTOR_NOT_PREPARED = 9, - LPC2000_COMPARE_ERROR = 10, - LPC2000_BUSY = 11, - LPC2000_PARAM_ERROR = 12, - LPC2000_ADDR_ERROR = 13, - LPC2000_ADDR_NOT_MAPPED = 14, - LPC2000_CMD_NOT_LOCKED = 15, - LPC2000_INVALID_CODE = 16, - LPC2000_INVALID_BAUD_RATE = 17, - LPC2000_INVALID_STOP_BIT = 18, - LPC2000_CRP_ENABLED = 19 - -}; - -#endif /* LPC2000_H */ diff --git a/src/flash/lpc288x.c b/src/flash/lpc288x.c deleted file mode 100644 index 446fc9da..00000000 --- a/src/flash/lpc288x.c +++ /dev/null @@ -1,485 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2008 by * - * Karl RobinSod * - * * - * 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 -* -* You need to unprotect flash sectors each time you connect the OpenOCD -* Dumping 1MB takes about 60 Seconds -* Full erase (sectors 0-22 inclusive) takes 2-4 seconds -* Writing 1MB takes 88 seconds -* - ***************************************************************************/ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "lpc288x.h" -#include "binarybuffer.h" - - -#define LOAD_TIMER_ERASE 0 -#define LOAD_TIMER_WRITE 1 - -#define FLASH_PAGE_SIZE 512 - -/* LPC288X control registers */ -#define DBGU_CIDR 0x8000507C -/* LPC288X flash registers */ -#define F_CTRL 0x80102000 /* Flash control register R/W 0x5 */ -#define F_STAT 0x80102004 /* Flash status register RO 0x45 */ -#define F_PROG_TIME 0x80102008 /* Flash program time register R/W 0 */ -#define F_WAIT 0x80102010 /* Flash read wait state register R/W 0xC004 */ -#define F_CLK_TIME 0x8010201C /* Flash clock divider for 66 kHz generation R/W 0 */ -#define F_INTEN_CLR 0x80102FD8 /* Clear interrupt enable bits WO - */ -#define F_INTEN_SET 0x80102FDC /* Set interrupt enable bits WO - */ -#define F_INT_STAT 0x80102FE0 /* Interrupt status bits RO 0 */ -#define F_INTEN 0x80102FE4 /* Interrupt enable bits RO 0 */ -#define F_INT_CLR 0x80102FE8 /* Clear interrupt status bits WO */ -#define F_INT_SET 0x80102FEC /* Set interrupt status bits WO - */ -#define FLASH_PD 0x80005030 /* Allows turning off the Flash memory for power savings. R/W 1*/ -#define FLASH_INIT 0x80005034 /* Monitors Flash readiness, such as recovery from Power Down mode. R/W -*/ - -/* F_CTRL bits */ -#define FC_CS 0x0001 -#define FC_FUNC 0x0002 -#define FC_WEN 0x0004 -#define FC_RD_LATCH 0x0020 -#define FC_PROTECT 0x0080 -#define FC_SET_DATA 0x0400 -#define FC_RSSL 0x0800 -#define FC_PROG_REQ 0x1000 -#define FC_CLR_BUF 0x4000 -#define FC_LOAD_REQ 0x8000 -/* F_STAT bits */ -#define FS_DONE 0x0001 -#define FS_PROGGNT 0x0002 -#define FS_RDY 0x0004 -#define FS_ERR 0x0020 -/* F_PROG_TIME */ -#define FPT_TIME_MASK 0x7FFF - -#define FPT_ENABLE 0x8000 -/* F_WAIT */ -#define FW_WAIT_STATES_MASK 0x00FF -#define FW_SET_MASK 0xC000 - -/* F_CLK_TIME */ -#define FCT_CLK_DIV_MASK 0x0FFF - -static uint32_t lpc288x_wait_status_busy(struct flash_bank *bank, int timeout); -static void lpc288x_load_timer(int erase, struct target *target); -static void lpc288x_set_flash_clk(struct flash_bank *bank); -static uint32_t lpc288x_system_ready(struct flash_bank *bank); - -static uint32_t lpc288x_wait_status_busy(struct flash_bank *bank, int timeout) -{ - uint32_t status; - struct target *target = bank->target; - do - { - alive_sleep(1); - timeout--; - target_read_u32(target, F_STAT, &status); - } while (((status & FS_DONE) == 0) && timeout); - - if (timeout == 0) - { - LOG_DEBUG("Timedout!"); - return ERROR_FLASH_OPERATION_FAILED; - } - return ERROR_OK; -} - -/* Read device id register and fill in driver info structure */ -static int lpc288x_read_part_info(struct flash_bank *bank) -{ - struct lpc288x_flash_bank *lpc288x_info = bank->driver_priv; - struct target *target = bank->target; - uint32_t cidr; - - int i = 0; - uint32_t offset; - - if (lpc288x_info->cidr == 0x0102100A) - return ERROR_OK; /* already probed, multiple probes may cause memory leak, not allowed */ - - /* Read and parse chip identification register */ - target_read_u32(target, DBGU_CIDR, &cidr); - - if (cidr != 0x0102100A) - { - LOG_WARNING("Cannot identify target as an LPC288X (%08" PRIx32 ")",cidr); - return ERROR_FLASH_OPERATION_FAILED; - } - - lpc288x_info->cidr = cidr; - lpc288x_info->sector_size_break = 0x000F0000; - lpc288x_info->target_name = "LPC288x"; - - /* setup the sector info... */ - offset = bank->base; - bank->num_sectors = 23; - bank->sectors = malloc(sizeof(struct flash_sector) * 23); - - for (i = 0; i < 15; 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 = 15; i < 23; 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; - } - - return ERROR_OK; -} - -static int lpc288x_protect_check(struct flash_bank *bank) -{ - return ERROR_OK; -} - -/* flash_bank LPC288x 0 0 0 0 */ -FLASH_BANK_COMMAND_HANDLER(lpc288x_flash_bank_command) -{ - struct lpc288x_flash_bank *lpc288x_info; - - if (CMD_ARGC < 6) - { - LOG_WARNING("incomplete flash_bank LPC288x configuration"); - return ERROR_FLASH_BANK_INVALID; - } - - lpc288x_info = malloc(sizeof(struct lpc288x_flash_bank)); - bank->driver_priv = lpc288x_info; - - /* part wasn't probed for info yet */ - lpc288x_info->cidr = 0; - COMMAND_PARSE_NUMBER(u32, CMD_ARGV[6], lpc288x_info->cclk); - - return ERROR_OK; -} - -/* The frequency is the AHB clock frequency divided by (CLK_DIV ×3) + 1. - * This must be programmed such that the Flash Programming clock frequency is 66 kHz ± 20%. - * AHB = 12 MHz ? - * 12000000/66000 = 182 - * CLK_DIV = 60 ? */ -static void lpc288x_set_flash_clk(struct flash_bank *bank) -{ - uint32_t clk_time; - struct lpc288x_flash_bank *lpc288x_info = bank->driver_priv; - clk_time = (lpc288x_info->cclk / 66000) / 3; - target_write_u32(bank->target, F_CTRL, FC_CS | FC_WEN); - target_write_u32(bank->target, F_CLK_TIME, clk_time); -} - -/* AHB tcyc (in ns) 83 ns - * LOAD_TIMER_ERASE FPT_TIME = ((400,000,000 / AHB tcyc (in ns)) - 2) / 512 - * = 9412 (9500) (AN10548 9375) - * LOAD_TIMER_WRITE FPT_TIME = ((1,000,000 / AHB tcyc (in ns)) - 2) / 512 - * = 23 (75) (AN10548 72 - is this wrong?) - * TODO: Sort out timing calcs ;) */ -static void lpc288x_load_timer(int erase, struct target *target) -{ - if (erase == LOAD_TIMER_ERASE) - { - target_write_u32(target, F_PROG_TIME, FPT_ENABLE | 9500); - } - else - { - target_write_u32(target, F_PROG_TIME, FPT_ENABLE | 75); - } -} - -static uint32_t lpc288x_system_ready(struct flash_bank *bank) -{ - struct lpc288x_flash_bank *lpc288x_info = bank->driver_priv; - if (lpc288x_info->cidr == 0) - { - return ERROR_FLASH_BANK_NOT_PROBED; - } - - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - return ERROR_OK; -} - -static int lpc288x_erase_check(struct flash_bank *bank) -{ - uint32_t status = lpc288x_system_ready(bank); /* probed? halted? */ - if (status != ERROR_OK) - { - LOG_INFO("Processor not halted/not probed"); - return status; - } - - return ERROR_OK; -} - -static int lpc288x_erase(struct flash_bank *bank, int first, int last) -{ - uint32_t status; - int sector; - struct target *target = bank->target; - - status = lpc288x_system_ready(bank); /* probed? halted? */ - if (status != ERROR_OK) - { - return status; - } - - if ((first < 0) || (last < first) || (last >= bank->num_sectors)) - { - LOG_INFO("Bad sector range"); - return ERROR_FLASH_SECTOR_INVALID; - } - - /* Configure the flash controller timing */ - lpc288x_set_flash_clk(bank); - - for (sector = first; sector <= last; sector++) - { - if (lpc288x_wait_status_busy(bank, 1000) != ERROR_OK) - { - return ERROR_FLASH_OPERATION_FAILED; - } - - lpc288x_load_timer(LOAD_TIMER_ERASE,target); - - target_write_u32(target, bank->sectors[sector].offset, 0x00); - - target_write_u32(target, F_CTRL, FC_PROG_REQ | FC_PROTECT | FC_CS); - } - if (lpc288x_wait_status_busy(bank, 1000) != ERROR_OK) - { - return ERROR_FLASH_OPERATION_FAILED; - } - return ERROR_OK; -} - -static int lpc288x_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) -{ - uint8_t page_buffer[FLASH_PAGE_SIZE]; - uint32_t status, source_offset,dest_offset; - struct target *target = bank->target; - uint32_t bytes_remaining = count; - uint32_t first_sector, last_sector, sector, page; - int i; - - /* probed? halted? */ - status = lpc288x_system_ready(bank); - if (status != ERROR_OK) - { - return status; - } - - /* Initialise search indices */ - first_sector = last_sector = 0xffffffff; - - /* validate the write range... */ - for (i = 0; i < bank->num_sectors; i++) - { - if ((offset >= bank->sectors[i].offset) && - (offset < (bank->sectors[i].offset + bank->sectors[i].size)) && - (first_sector == 0xffffffff)) - { - first_sector = i; - /* all writes must start on a sector boundary... */ - if (offset % bank->sectors[i].size) - { - LOG_INFO("offset 0x%" PRIx32 " breaks required alignment 0x%" PRIx32 "", offset, bank->sectors[i].size); - return ERROR_FLASH_DST_BREAKS_ALIGNMENT; - } - } - if (((offset + count) > bank->sectors[i].offset) && - ((offset + count) <= (bank->sectors[i].offset + bank->sectors[i].size)) && - (last_sector == 0xffffffff)) - { - last_sector = i; - } - } - - /* Range check... */ - if (first_sector == 0xffffffff || last_sector == 0xffffffff) - { - LOG_INFO("Range check failed %" PRIx32 " %" PRIx32 "", offset, count); - return ERROR_FLASH_DST_OUT_OF_BANK; - } - - /* Configure the flash controller timing */ - lpc288x_set_flash_clk(bank); - - /* initialise the offsets */ - source_offset = 0; - dest_offset = 0; - - for (sector = first_sector; sector <= last_sector; sector++) - { - for (page = 0; page < bank->sectors[sector].size / FLASH_PAGE_SIZE; page++) - { - if (bytes_remaining == 0) - { - count = 0; - memset(page_buffer, 0xFF, FLASH_PAGE_SIZE); - } - else if (bytes_remaining < FLASH_PAGE_SIZE) - { - count = bytes_remaining; - memset(page_buffer, 0xFF, FLASH_PAGE_SIZE); - memcpy(page_buffer, &buffer[source_offset], count); - } - else - { - count = FLASH_PAGE_SIZE; - memcpy(page_buffer, &buffer[source_offset], count); - } - - /* Wait for flash to become ready */ - if (lpc288x_wait_status_busy(bank, 1000) != ERROR_OK) - { - return ERROR_FLASH_OPERATION_FAILED; - } - - /* fill flash data latches with 1's */ - target_write_u32(target, F_CTRL, FC_CS | FC_SET_DATA | FC_WEN | FC_FUNC); - - target_write_u32(target, F_CTRL, FC_CS | FC_WEN | FC_FUNC); - /*would be better to use the clean target_write_buffer() interface but - * it seems not to be a LOT slower.... - * bulk_write_memory() is no quicker :(*/ -#if 1 - if (target_write_memory(target, offset + dest_offset, 4, 128, page_buffer) != ERROR_OK) - { - LOG_ERROR("Write failed s %" PRIx32 " p %" PRIx32 "", sector, page); - return ERROR_FLASH_OPERATION_FAILED; - } -#else - if (target_write_buffer(target, offset + dest_offset, FLASH_PAGE_SIZE, page_buffer) != ERROR_OK) - { - LOG_INFO("Write to flash buffer failed"); - return ERROR_FLASH_OPERATION_FAILED; - } -#endif - dest_offset += FLASH_PAGE_SIZE; - source_offset += count; - bytes_remaining -= count; - - lpc288x_load_timer(LOAD_TIMER_WRITE, target); - - target_write_u32(target, F_CTRL, FC_PROG_REQ | FC_PROTECT | FC_FUNC | FC_CS); - } - } - - return ERROR_OK; -} - -static int lpc288x_probe(struct flash_bank *bank) -{ - /* we only deal with LPC2888 so flash config is fixed */ - struct lpc288x_flash_bank *lpc288x_info = bank->driver_priv; - int retval; - - if (lpc288x_info->cidr != 0) - { - return ERROR_OK; /* already probed */ - } - - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - retval = lpc288x_read_part_info(bank); - if (retval != ERROR_OK) - return retval; - return ERROR_OK; -} - -static int lpc288x_info(struct flash_bank *bank, char *buf, int buf_size) -{ - snprintf(buf, buf_size, "lpc288x flash driver"); - return ERROR_OK; -} - -static int lpc288x_protect(struct flash_bank *bank, int set, int first, int last) -{ - int lockregion, status; - uint32_t value; - struct target *target = bank->target; - - /* probed? halted? */ - status = lpc288x_system_ready(bank); - if (status != ERROR_OK) - { - return status; - } - - if ((first < 0) || (last < first) || (last >= bank->num_sectors)) - { - return ERROR_FLASH_SECTOR_INVALID; - } - - /* Configure the flash controller timing */ - lpc288x_set_flash_clk(bank); - - for (lockregion = first; lockregion <= last; lockregion++) - { - if (set) - { - /* write an odd value to base addy to protect... */ - value = 0x01; - } - else - { - /* write an even value to base addy to unprotect... */ - value = 0x00; - } - target_write_u32(target, bank->sectors[lockregion].offset, value); - target_write_u32(target, F_CTRL, FC_LOAD_REQ | FC_PROTECT | FC_WEN | FC_FUNC | FC_CS); - } - - return ERROR_OK; -} - -struct flash_driver lpc288x_flash = { - .name = "lpc288x", - .flash_bank_command = &lpc288x_flash_bank_command, - .erase = &lpc288x_erase, - .protect = &lpc288x_protect, - .write = &lpc288x_write, - .probe = &lpc288x_probe, - .auto_probe = &lpc288x_probe, - .erase_check = &lpc288x_erase_check, - .protect_check = &lpc288x_protect_check, - .info = &lpc288x_info, - }; diff --git a/src/flash/lpc288x.h b/src/flash/lpc288x.h deleted file mode 100644 index 5a71ee08..00000000 --- a/src/flash/lpc288x.h +++ /dev/null @@ -1,39 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2008 by * - * Karl RobinSod * - * * - * 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 lpc288x_H -#define lpc288x_H - -#include "flash.h" - -struct lpc288x_flash_bank -{ - uint32_t working_area; - uint32_t working_area_size; - - /* chip id register */ - uint32_t cidr; - char * target_name; - uint32_t cclk; - - uint32_t sector_size_break; -}; - -#endif /* lpc288x_H */ diff --git a/src/flash/lpc2900.c b/src/flash/lpc2900.c deleted file mode 100644 index 81e2def4..00000000 --- a/src/flash/lpc2900.c +++ /dev/null @@ -1,1834 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2009 by * - * Rolf Meeser * - * * - * 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 "image.h" -#include "flash.h" -#include "binarybuffer.h" -#include "armv4_5.h" -#include "algorithm.h" - - -/* 1024 bytes */ -#define KiB 1024 - -/* Some flash constants */ -#define FLASH_PAGE_SIZE 512 /* bytes */ -#define FLASH_ERASE_TIME 100000 /* microseconds */ -#define FLASH_PROGRAM_TIME 1000 /* microseconds */ - -/* Chip ID / Feature Registers */ -#define CHIPID 0xE0000000 /* Chip ID */ -#define FEAT0 0xE0000100 /* Chip feature 0 */ -#define FEAT1 0xE0000104 /* Chip feature 1 */ -#define FEAT2 0xE0000108 /* Chip feature 2 (contains flash size indicator) */ -#define FEAT3 0xE000010C /* Chip feature 3 */ - -#define EXPECTED_CHIPID 0x209CE02B /* Chip ID of all LPC2900 devices */ - -/* Flash/EEPROM Control Registers */ -#define FCTR 0x20200000 /* Flash control */ -#define FPTR 0x20200008 /* Flash program-time */ -#define FTCTR 0x2020000C /* Flash test control */ -#define FBWST 0x20200010 /* Flash bridge wait-state */ -#define FCRA 0x2020001C /* Flash clock divider */ -#define FMSSTART 0x20200020 /* Flash Built-In Selft Test start address */ -#define FMSSTOP 0x20200024 /* Flash Built-In Selft Test stop address */ -#define FMS16 0x20200028 /* Flash 16-bit signature */ -#define FMSW0 0x2020002C /* Flash 128-bit signature Word 0 */ -#define FMSW1 0x20200030 /* Flash 128-bit signature Word 1 */ -#define FMSW2 0x20200034 /* Flash 128-bit signature Word 2 */ -#define FMSW3 0x20200038 /* Flash 128-bit signature Word 3 */ - -#define EECMD 0x20200080 /* EEPROM command */ -#define EEADDR 0x20200084 /* EEPROM address */ -#define EEWDATA 0x20200088 /* EEPROM write data */ -#define EERDATA 0x2020008C /* EEPROM read data */ -#define EEWSTATE 0x20200090 /* EEPROM wait state */ -#define EECLKDIV 0x20200094 /* EEPROM clock divider */ -#define EEPWRDWN 0x20200098 /* EEPROM power-down/start */ -#define EEMSSTART 0x2020009C /* EEPROM BIST start address */ -#define EEMSSTOP 0x202000A0 /* EEPROM BIST stop address */ -#define EEMSSIG 0x202000A4 /* EEPROM 24-bit BIST signature */ - -#define INT_CLR_ENABLE 0x20200FD8 /* Flash/EEPROM interrupt clear enable */ -#define INT_SET_ENABLE 0x20200FDC /* Flash/EEPROM interrupt set enable */ -#define INT_STATUS 0x20200FE0 /* Flash/EEPROM interrupt status */ -#define INT_ENABLE 0x20200FE4 /* Flash/EEPROM interrupt enable */ -#define INT_CLR_STATUS 0x20200FE8 /* Flash/EEPROM interrupt clear status */ -#define INT_SET_STATUS 0x20200FEC /* Flash/EEPROM interrupt set status */ - -/* Interrupt sources */ -#define INTSRC_END_OF_PROG (1 << 28) -#define INTSRC_END_OF_BIST (1 << 27) -#define INTSRC_END_OF_RDWR (1 << 26) -#define INTSRC_END_OF_MISR (1 << 2) -#define INTSRC_END_OF_BURN (1 << 1) -#define INTSRC_END_OF_ERASE (1 << 0) - - -/* FCTR bits */ -#define FCTR_FS_LOADREQ (1 << 15) -#define FCTR_FS_CACHECLR (1 << 14) -#define FCTR_FS_CACHEBYP (1 << 13) -#define FCTR_FS_PROGREQ (1 << 12) -#define FCTR_FS_RLS (1 << 11) -#define FCTR_FS_PDL (1 << 10) -#define FCTR_FS_PD (1 << 9) -#define FCTR_FS_WPB (1 << 7) -#define FCTR_FS_ISS (1 << 6) -#define FCTR_FS_RLD (1 << 5) -#define FCTR_FS_DCR (1 << 4) -#define FCTR_FS_WEB (1 << 2) -#define FCTR_FS_WRE (1 << 1) -#define FCTR_FS_CS (1 << 0) -/* FPTR bits */ -#define FPTR_EN_T (1 << 15) -/* FTCTR bits */ -#define FTCTR_FS_BYPASS_R (1 << 29) -#define FTCTR_FS_BYPASS_W (1 << 28) -/* FMSSTOP bits */ -#define FMSSTOP_MISR_START (1 << 17) -/* EEMSSTOP bits */ -#define EEMSSTOP_STRTBIST (1 << 31) - -/* Index sector */ -#define ISS_CUSTOMER_START1 (0x830) -#define ISS_CUSTOMER_END1 (0xA00) -#define ISS_CUSTOMER_SIZE1 (ISS_CUSTOMER_END1 - ISS_CUSTOMER_START1) -#define ISS_CUSTOMER_NWORDS1 (ISS_CUSTOMER_SIZE1 / 4) -#define ISS_CUSTOMER_START2 (0xA40) -#define ISS_CUSTOMER_END2 (0xC00) -#define ISS_CUSTOMER_SIZE2 (ISS_CUSTOMER_END2 - ISS_CUSTOMER_START2) -#define ISS_CUSTOMER_NWORDS2 (ISS_CUSTOMER_SIZE2 / 4) -#define ISS_CUSTOMER_SIZE (ISS_CUSTOMER_SIZE1 + ISS_CUSTOMER_SIZE2) - - - -/** - * Private data for \c lpc2900 flash driver. - */ -struct lpc2900_flash_bank -{ - /** - * Holds the value read from CHIPID register. - * The driver will not load if the chipid doesn't match the expected - * value of 0x209CE02B of the LPC2900 family. A probe will only be done - * if the chipid does not yet contain the expected value. - */ - uint32_t chipid; - - /** - * String holding device name. - * This string is set by the probe function to the type number of the - * device. It takes the form "LPC29xx". - */ - char * target_name; - - /** - * System clock frequency. - * Holds the clock frequency in Hz, as passed by the configuration file - * to the flash bank command. - */ - uint32_t clk_sys_fmc; - - /** - * Flag to indicate that dangerous operations are possible. - * This flag can be set by passing the correct password to the - * lpc2900 password command. If set, other dangerous commands, - * which operate on the index sector, can be executed. - */ - uint32_t risky; - - /** - * Maximum contiguous block of internal SRAM (bytes). - * Autodetected by the driver. Not the total amount of SRAM, only the - * the largest \em contiguous block! - */ - uint32_t max_ram_block; - -}; - - -static uint32_t lpc2900_wait_status(struct flash_bank *bank, uint32_t mask, int timeout); -static void lpc2900_setup(struct flash_bank *bank); -static uint32_t lpc2900_is_ready(struct flash_bank *bank); -static uint32_t lpc2900_read_security_status(struct flash_bank *bank); -static uint32_t lpc2900_run_bist128(struct flash_bank *bank, - uint32_t addr_from, uint32_t addr_to, - uint32_t (*signature)[4] ); -static uint32_t lpc2900_address2sector(struct flash_bank *bank, uint32_t offset); -static uint32_t lpc2900_calc_tr( uint32_t clock, uint32_t time ); - - -/*********************** Helper functions **************************/ - - -/** - * Wait for an event in mask to occur in INT_STATUS. - * - * Return when an event occurs, or after a timeout. - * - * @param[in] bank Pointer to the flash bank descriptor - * @param[in] mask Mask to be used for INT_STATUS - * @param[in] timeout Timeout in ms - */ -static uint32_t lpc2900_wait_status( struct flash_bank *bank, - uint32_t mask, - int timeout ) -{ - uint32_t int_status; - struct target *target = bank->target; - - - do - { - alive_sleep(1); - timeout--; - target_read_u32(target, INT_STATUS, &int_status); - } - while( ((int_status & mask) == 0) && (timeout != 0) ); - - if (timeout == 0) - { - LOG_DEBUG("Timeout!"); - return ERROR_FLASH_OPERATION_FAILED; - } - - return ERROR_OK; -} - - - -/** - * Set up the flash for erase/program operations. - * - * Enable the flash, and set the correct CRA clock of 66 kHz. - * - * @param bank Pointer to the flash bank descriptor - */ -static void lpc2900_setup( struct flash_bank *bank ) -{ - uint32_t fcra; - struct lpc2900_flash_bank *lpc2900_info = bank->driver_priv; - - - /* Power up the flash block */ - target_write_u32( bank->target, FCTR, FCTR_FS_WEB | FCTR_FS_CS ); - - - fcra = (lpc2900_info->clk_sys_fmc / (3 * 66000)) - 1; - target_write_u32( bank->target, FCRA, fcra ); -} - - - -/** - * Check if device is ready. - * - * Check if device is ready for flash operation: - * Must have been successfully probed. - * Must be halted. - */ -static uint32_t lpc2900_is_ready( struct flash_bank *bank ) -{ - struct lpc2900_flash_bank *lpc2900_info = bank->driver_priv; - - if( lpc2900_info->chipid != EXPECTED_CHIPID ) - { - return ERROR_FLASH_BANK_NOT_PROBED; - } - - if( bank->target->state != TARGET_HALTED ) - { - LOG_ERROR( "Target not halted" ); - return ERROR_TARGET_NOT_HALTED; - } - - return ERROR_OK; -} - - -/** - * Read the status of sector security from the index sector. - * - * @param bank Pointer to the flash bank descriptor - */ -static uint32_t lpc2900_read_security_status( struct flash_bank *bank ) -{ - uint32_t status; - if( (status = lpc2900_is_ready( bank )) != ERROR_OK ) - { - return status; - } - - struct target *target = bank->target; - - /* Enable ISS access */ - target_write_u32(target, FCTR, FCTR_FS_CS | FCTR_FS_WEB | FCTR_FS_ISS); - - /* Read the relevant block of memory from the ISS sector */ - uint32_t iss_secured_field[ 0x230/16 ][ 4 ]; - target_read_memory(target, bank->base + 0xC00, 4, 0x230/4, - (uint8_t *)iss_secured_field); - - /* Disable ISS access */ - target_write_u32(target, FCTR, FCTR_FS_CS | FCTR_FS_WEB); - - /* Check status of each sector. Note that the sector numbering in the LPC2900 - * is different from the logical sector numbers used in OpenOCD! - * Refer to the user manual for details. - * - * All zeros (16x 0x00) are treated as a secured sector (is_protected = 1) - * All ones (16x 0xFF) are treated as a non-secured sector (is_protected = 0) - * Anything else is undefined (is_protected = -1). This is treated as - * a protected sector! - */ - int sector; - int index; - for( sector = 0; sector < bank->num_sectors; sector++ ) - { - /* Convert logical sector number to physical sector number */ - if( sector <= 4 ) - { - index = sector + 11; - } - else if( sector <= 7 ) - { - index = sector + 27; - } - else - { - index = sector - 8; - } - - bank->sectors[sector].is_protected = -1; - - if ( - (iss_secured_field[index][0] == 0x00000000) && - (iss_secured_field[index][1] == 0x00000000) && - (iss_secured_field[index][2] == 0x00000000) && - (iss_secured_field[index][3] == 0x00000000) ) - { - bank->sectors[sector].is_protected = 1; - } - - if ( - (iss_secured_field[index][0] == 0xFFFFFFFF) && - (iss_secured_field[index][1] == 0xFFFFFFFF) && - (iss_secured_field[index][2] == 0xFFFFFFFF) && - (iss_secured_field[index][3] == 0xFFFFFFFF) ) - { - bank->sectors[sector].is_protected = 0; - } - } - - return ERROR_OK; -} - - -/** - * Use BIST to calculate a 128-bit hash value over a range of flash. - * - * @param bank Pointer to the flash bank descriptor - * @param addr_from - * @param addr_to - * @param signature - */ -static uint32_t lpc2900_run_bist128(struct flash_bank *bank, - uint32_t addr_from, - uint32_t addr_to, - uint32_t (*signature)[4] ) -{ - struct target *target = bank->target; - - /* Clear END_OF_MISR interrupt status */ - target_write_u32( target, INT_CLR_STATUS, INTSRC_END_OF_MISR ); - - /* Start address */ - target_write_u32( target, FMSSTART, addr_from >> 4); - /* End address, and issue start command */ - target_write_u32( target, FMSSTOP, (addr_to >> 4) | FMSSTOP_MISR_START ); - - /* Poll for end of operation. Calculate a reasonable timeout. */ - if( lpc2900_wait_status( bank, INTSRC_END_OF_MISR, 1000 ) != ERROR_OK ) - { - return ERROR_FLASH_OPERATION_FAILED; - } - - /* Return the signature */ - target_read_memory( target, FMSW0, 4, 4, (uint8_t *)signature ); - - return ERROR_OK; -} - - -/** - * Return sector number for given address. - * - * Return the (logical) sector number for a given relative address. - * No sanity check is done. It assumed that the address is valid. - * - * @param bank Pointer to the flash bank descriptor - * @param offset Offset address relative to bank start - */ -static uint32_t lpc2900_address2sector( struct flash_bank *bank, - uint32_t offset ) -{ - uint32_t address = bank->base + offset; - - - /* Run through all sectors of this bank */ - int sector; - for( sector = 0; sector < bank->num_sectors; sector++ ) - { - /* Return immediately if address is within the current sector */ - if( address < (bank->sectors[sector].offset + bank->sectors[sector].size) ) - { - return sector; - } - } - - /* We should never come here. If we do, return an arbitrary sector number. */ - return 0; -} - - - - -/** - * Write one page to the index sector. - * - * @param bank Pointer to the flash bank descriptor - * @param pagenum Page number (0...7) - * @param page Page array (FLASH_PAGE_SIZE bytes) - */ -static int lpc2900_write_index_page( struct flash_bank *bank, - int pagenum, - uint8_t (*page)[FLASH_PAGE_SIZE] ) -{ - /* Only pages 4...7 are user writable */ - if ((pagenum < 4) || (pagenum > 7)) - { - LOG_ERROR("Refuse to burn index sector page %d", pagenum); - return ERROR_COMMAND_ARGUMENT_INVALID; - } - - /* Get target, and check if it's halted */ - struct target *target = bank->target; - if( target->state != TARGET_HALTED ) - { - LOG_ERROR( "Target not halted" ); - return ERROR_TARGET_NOT_HALTED; - } - - /* Private info */ - struct lpc2900_flash_bank *lpc2900_info = bank->driver_priv; - - /* Enable flash block and set the correct CRA clock of 66 kHz */ - lpc2900_setup( bank ); - - /* Un-protect the index sector */ - target_write_u32( target, bank->base, 0 ); - target_write_u32( target, FCTR, - FCTR_FS_LOADREQ | FCTR_FS_WPB | FCTR_FS_ISS | - FCTR_FS_WEB | FCTR_FS_WRE | FCTR_FS_CS ); - - /* Set latch load mode */ - target_write_u32( target, FCTR, - FCTR_FS_ISS | FCTR_FS_WEB | FCTR_FS_WRE | FCTR_FS_CS ); - - /* Write whole page to flash data latches */ - if( target_write_memory( target, - bank->base + pagenum * FLASH_PAGE_SIZE, - 4, FLASH_PAGE_SIZE / 4, (uint8_t *)page) != ERROR_OK ) - { - LOG_ERROR("Index sector write failed @ page %d", pagenum); - target_write_u32( target, FCTR, FCTR_FS_CS | FCTR_FS_WEB ); - - return ERROR_FLASH_OPERATION_FAILED; - } - - /* Clear END_OF_BURN interrupt status */ - target_write_u32( target, INT_CLR_STATUS, INTSRC_END_OF_BURN ); - - /* Set the program/erase time to FLASH_PROGRAM_TIME */ - target_write_u32(target, FPTR, - FPTR_EN_T | lpc2900_calc_tr( lpc2900_info->clk_sys_fmc, - FLASH_PROGRAM_TIME )); - - /* Trigger flash write */ - target_write_u32( target, FCTR, - FCTR_FS_PROGREQ | FCTR_FS_ISS | - FCTR_FS_WPB | FCTR_FS_WRE | FCTR_FS_CS ); - - /* Wait for the end of the write operation. If it's not over after one - * second, something went dreadfully wrong... :-( - */ - if (lpc2900_wait_status(bank, INTSRC_END_OF_BURN, 1000) != ERROR_OK) - { - LOG_ERROR("Index sector write failed @ page %d", pagenum); - target_write_u32(target, FCTR, FCTR_FS_CS | FCTR_FS_WEB); - - return ERROR_FLASH_OPERATION_FAILED; - } - - target_write_u32( target, FCTR, FCTR_FS_CS | FCTR_FS_WEB ); - - return ERROR_OK; -} - - - -/** - * Calculate FPTR.TR register value for desired program/erase time. - * - * @param clock System clock in Hz - * @param time Program/erase time in µs - */ -static uint32_t lpc2900_calc_tr( uint32_t clock, uint32_t time ) -{ - /* ((time[µs]/1e6) * f[Hz]) + 511 - * FPTR.TR = ------------------------------- - * 512 - * - * The result is the - */ - - uint32_t tr_val = (uint32_t)((((time / 1e6) * clock) + 511.0) / 512.0); - - return tr_val; -} - - -/*********************** Private flash commands **************************/ - - -/** - * Command to determine the signature of the whole flash. - * - * Uses the Built-In-Self-Test (BIST) to generate a 128-bit hash value - * of the flash content. - */ -COMMAND_HANDLER(lpc2900_handle_signature_command) -{ - uint32_t status; - uint32_t signature[4]; - - - if( CMD_ARGC < 1 ) - { - LOG_WARNING( "Too few arguments. Call: lpc2900 signature " ); - return ERROR_FLASH_BANK_INVALID; - } - - struct flash_bank *bank; - int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) - return retval; - - if( bank->target->state != TARGET_HALTED ) - { - LOG_ERROR( "Target not halted" ); - return ERROR_TARGET_NOT_HALTED; - } - - /* Run BIST over whole flash range */ - if( (status = lpc2900_run_bist128( bank, - bank->base, - bank->base + (bank->size - 1), - &signature) - ) != ERROR_OK ) - { - return status; - } - - command_print( CMD_CTX, "signature: 0x%8.8" PRIx32 - ":0x%8.8" PRIx32 - ":0x%8.8" PRIx32 - ":0x%8.8" PRIx32, - signature[3], signature[2], signature[1], signature[0] ); - - return ERROR_OK; -} - - - -/** - * Store customer info in file. - * - * Read customer info from index sector, and store that block of data into - * a disk file. The format is binary. - */ -COMMAND_HANDLER(lpc2900_handle_read_custom_command) -{ - if( CMD_ARGC < 2 ) - { - return ERROR_COMMAND_SYNTAX_ERROR; - } - - struct flash_bank *bank; - int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) - return retval; - - struct lpc2900_flash_bank *lpc2900_info = bank->driver_priv; - lpc2900_info->risky = 0; - - /* Get target, and check if it's halted */ - struct target *target = bank->target; - if( target->state != TARGET_HALTED ) - { - LOG_ERROR( "Target not halted" ); - return ERROR_TARGET_NOT_HALTED; - } - - /* Storage for customer info. Read in two parts */ - uint32_t customer[ ISS_CUSTOMER_NWORDS1 + ISS_CUSTOMER_NWORDS2 ]; - - /* Enable access to index sector */ - target_write_u32( target, FCTR, FCTR_FS_CS | FCTR_FS_WEB | FCTR_FS_ISS ); - - /* Read two parts */ - target_read_memory( target, bank->base+ISS_CUSTOMER_START1, 4, - ISS_CUSTOMER_NWORDS1, - (uint8_t *)&customer[0] ); - target_read_memory( target, bank->base+ISS_CUSTOMER_START2, 4, - ISS_CUSTOMER_NWORDS2, - (uint8_t *)&customer[ISS_CUSTOMER_NWORDS1] ); - - /* Deactivate access to index sector */ - target_write_u32( target, FCTR, FCTR_FS_CS | FCTR_FS_WEB ); - - /* Try and open the file */ - struct fileio fileio; - const char *filename = CMD_ARGV[1]; - int ret = fileio_open( &fileio, filename, FILEIO_WRITE, FILEIO_BINARY ); - if( ret != ERROR_OK ) - { - LOG_WARNING( "Could not open file %s", filename ); - return ret; - } - - size_t nwritten; - ret = fileio_write( &fileio, sizeof(customer), - (const uint8_t *)customer, &nwritten ); - if( ret != ERROR_OK ) - { - LOG_ERROR( "Write operation to file %s failed", filename ); - fileio_close( &fileio ); - return ret; - } - - fileio_close( &fileio ); - - return ERROR_OK; -} - - - - -/** - * Enter password to enable potentially dangerous options. - */ -COMMAND_HANDLER(lpc2900_handle_password_command) -{ - if (CMD_ARGC < 2) - { - return ERROR_COMMAND_SYNTAX_ERROR; - } - - struct flash_bank *bank; - int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) - return retval; - - struct lpc2900_flash_bank *lpc2900_info = bank->driver_priv; - -#define ISS_PASSWORD "I_know_what_I_am_doing" - - lpc2900_info->risky = !strcmp( CMD_ARGV[1], ISS_PASSWORD ); - - if( !lpc2900_info->risky ) - { - command_print(CMD_CTX, "Wrong password (use '%s')", ISS_PASSWORD); - return ERROR_COMMAND_ARGUMENT_INVALID; - } - - command_print(CMD_CTX, - "Potentially dangerous operation allowed in next command!"); - - return ERROR_OK; -} - - - -/** - * Write customer info from file to the index sector. - */ -COMMAND_HANDLER(lpc2900_handle_write_custom_command) -{ - if (CMD_ARGC < 2) - { - return ERROR_COMMAND_SYNTAX_ERROR; - } - - struct flash_bank *bank; - int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) - return retval; - - struct lpc2900_flash_bank *lpc2900_info = bank->driver_priv; - - /* Check if command execution is allowed. */ - if( !lpc2900_info->risky ) - { - command_print( CMD_CTX, "Command execution not allowed!" ); - return ERROR_COMMAND_ARGUMENT_INVALID; - } - lpc2900_info->risky = 0; - - /* Get target, and check if it's halted */ - struct target *target = bank->target; - if (target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - /* The image will always start at offset 0 */ - struct image image; - image.base_address_set = 1; - image.base_address = 0; - image.start_address_set = 0; - - const char *filename = CMD_ARGV[1]; - const char *type = (CMD_ARGC >= 3) ? CMD_ARGV[2] : NULL; - retval = image_open(&image, filename, type); - if (retval != ERROR_OK) - { - return retval; - } - - /* Do a sanity check: The image must be exactly the size of the customer - programmable area. Any other size is rejected. */ - if( image.num_sections != 1 ) - { - LOG_ERROR("Only one section allowed in image file."); - return ERROR_COMMAND_SYNTAX_ERROR; - } - if( (image.sections[0].base_address != 0) || - (image.sections[0].size != ISS_CUSTOMER_SIZE) ) - { - LOG_ERROR("Incorrect image file size. Expected %d, " - "got %" PRIu32, - ISS_CUSTOMER_SIZE, image.sections[0].size); - return ERROR_COMMAND_SYNTAX_ERROR; - } - - /* Well boys, I reckon this is it... */ - - /* Customer info is split into two blocks in pages 4 and 5. */ - uint8_t page[FLASH_PAGE_SIZE]; - - /* Page 4 */ - uint32_t offset = ISS_CUSTOMER_START1 % FLASH_PAGE_SIZE; - memset( page, 0xff, FLASH_PAGE_SIZE ); - size_t size_read; - retval = image_read_section( &image, 0, 0, - ISS_CUSTOMER_SIZE1, &page[offset], &size_read); - if( retval != ERROR_OK ) - { - LOG_ERROR("couldn't read from file '%s'", filename); - image_close(&image); - return retval; - } - if( (retval = lpc2900_write_index_page( bank, 4, &page )) != ERROR_OK ) - { - image_close(&image); - return retval; - } - - /* Page 5 */ - offset = ISS_CUSTOMER_START2 % FLASH_PAGE_SIZE; - memset( page, 0xff, FLASH_PAGE_SIZE ); - retval = image_read_section( &image, 0, ISS_CUSTOMER_SIZE1, - ISS_CUSTOMER_SIZE2, &page[offset], &size_read); - if( retval != ERROR_OK ) - { - LOG_ERROR("couldn't read from file '%s'", filename); - image_close(&image); - return retval; - } - if( (retval = lpc2900_write_index_page( bank, 5, &page )) != ERROR_OK ) - { - image_close(&image); - return retval; - } - - image_close(&image); - - return ERROR_OK; -} - - - -/** - * Activate 'sector security' for a range of sectors. - */ -COMMAND_HANDLER(lpc2900_handle_secure_sector_command) -{ - if (CMD_ARGC < 3) - { - return ERROR_COMMAND_SYNTAX_ERROR; - } - - /* Get the bank descriptor */ - struct flash_bank *bank; - int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) - return retval; - - struct lpc2900_flash_bank *lpc2900_info = bank->driver_priv; - - /* Check if command execution is allowed. */ - if( !lpc2900_info->risky ) - { - command_print( CMD_CTX, "Command execution not allowed! " - "(use 'password' command first)"); - return ERROR_COMMAND_ARGUMENT_INVALID; - } - lpc2900_info->risky = 0; - - /* Read sector range, and do a sanity check. */ - int first, last; - COMMAND_PARSE_NUMBER(int, CMD_ARGV[1], first); - COMMAND_PARSE_NUMBER(int, CMD_ARGV[2], last); - if( (first >= bank->num_sectors) || - (last >= bank->num_sectors) || - (first > last) ) - { - command_print( CMD_CTX, "Illegal sector range" ); - return ERROR_COMMAND_ARGUMENT_INVALID; - } - - uint8_t page[FLASH_PAGE_SIZE]; - int sector; - - /* Sectors in page 6 */ - if( (first <= 4) || (last >= 8) ) - { - memset( &page, 0xff, FLASH_PAGE_SIZE ); - for( sector = first; sector <= last; sector++ ) - { - if( sector <= 4 ) - { - memset( &page[0xB0 + 16*sector], 0, 16 ); - } - else if( sector >= 8 ) - { - memset( &page[0x00 + 16*(sector - 8)], 0, 16 ); - } - } - - if( (retval = lpc2900_write_index_page( bank, 6, &page )) != ERROR_OK ) - { - LOG_ERROR("failed to update index sector page 6"); - return retval; - } - } - - /* Sectors in page 7 */ - if( (first <= 7) && (last >= 5) ) - { - memset( &page, 0xff, FLASH_PAGE_SIZE ); - for( sector = first; sector <= last; sector++ ) - { - if( (sector >= 5) && (sector <= 7) ) - { - memset( &page[0x00 + 16*(sector - 5)], 0, 16 ); - } - } - - if( (retval = lpc2900_write_index_page( bank, 7, &page )) != ERROR_OK ) - { - LOG_ERROR("failed to update index sector page 7"); - return retval; - } - } - - command_print( CMD_CTX, - "Sectors security will become effective after next power cycle"); - - /* Update the sector security status */ - if ( lpc2900_read_security_status(bank) != ERROR_OK ) - { - LOG_ERROR( "Cannot determine sector security status" ); - return ERROR_FLASH_OPERATION_FAILED; - } - - return ERROR_OK; -} - - - -/** - * Activate JTAG protection. - */ -COMMAND_HANDLER(lpc2900_handle_secure_jtag_command) -{ - if (CMD_ARGC < 1) - { - return ERROR_COMMAND_SYNTAX_ERROR; - } - - /* Get the bank descriptor */ - struct flash_bank *bank; - int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) - return retval; - - struct lpc2900_flash_bank *lpc2900_info = bank->driver_priv; - - /* Check if command execution is allowed. */ - if( !lpc2900_info->risky ) - { - command_print( CMD_CTX, "Command execution not allowed! " - "(use 'password' command first)"); - return ERROR_COMMAND_ARGUMENT_INVALID; - } - lpc2900_info->risky = 0; - - /* Prepare page */ - uint8_t page[FLASH_PAGE_SIZE]; - memset( &page, 0xff, FLASH_PAGE_SIZE ); - - - /* Insert "soft" protection word */ - page[0x30 + 15] = 0x7F; - page[0x30 + 11] = 0x7F; - page[0x30 + 7] = 0x7F; - page[0x30 + 3] = 0x7F; - - /* Write to page 5 */ - if( (retval = lpc2900_write_index_page( bank, 5, &page )) - != ERROR_OK ) - { - LOG_ERROR("failed to update index sector page 5"); - return retval; - } - - LOG_INFO("JTAG security set. Good bye!"); - - return ERROR_OK; -} - - - -/*********************** Flash interface functions **************************/ - -static const struct command_registration lpc2900_exec_command_handlers[] = { - { - .name = "signature", - .handler = &lpc2900_handle_signature_command, - .mode = COMMAND_EXEC, - .usage = "", - .help = "print device signature of flash bank", - }, - { - .name = "read_custom", - .handler = &lpc2900_handle_read_custom_command, - .mode = COMMAND_EXEC, - .usage = " ", - .help = "read customer information from index sector to file", - }, - { - .name = "password", - .handler = &lpc2900_handle_password_command, - .mode = COMMAND_EXEC, - .usage = " ", - .help = "enter password to enable 'dangerous' options", - }, - { - .name = "write_custom", - .handler = &lpc2900_handle_write_custom_command, - .mode = COMMAND_EXEC, - .usage = " []", - .help = "write customer info from file to index sector", - }, - { - .name = "secure_sector", - .handler = &lpc2900_handle_secure_sector_command, - .mode = COMMAND_EXEC, - .usage = " ", - .help = "activate sector security for a range of sectors", - }, - { - .name = "secure_jtag", - .handler = &lpc2900_handle_secure_jtag_command, - .mode = COMMAND_EXEC, - .usage = " ", - .help = "activate JTAG security", - }, - COMMAND_REGISTRATION_DONE -}; -static const struct command_registration lpc2900_command_handlers[] = { - { - .name = "lpc2900", - .mode = COMMAND_ANY, - .help = "LPC2900 flash command group", - .chain = lpc2900_exec_command_handlers, - }, - COMMAND_REGISTRATION_DONE -}; - -/// Evaluate flash bank command. -FLASH_BANK_COMMAND_HANDLER(lpc2900_flash_bank_command) -{ - struct lpc2900_flash_bank *lpc2900_info; - - if (CMD_ARGC < 6) - { - LOG_WARNING("incomplete flash_bank LPC2900 configuration"); - return ERROR_FLASH_BANK_INVALID; - } - - lpc2900_info = malloc(sizeof(struct lpc2900_flash_bank)); - bank->driver_priv = lpc2900_info; - - /* Get flash clock. - * Reject it if we can't meet the requirements for program time - * (if clock too slow), or for erase time (clock too fast). - */ - uint32_t clk_sys_fmc; - COMMAND_PARSE_NUMBER(u32, CMD_ARGV[6], clk_sys_fmc); - lpc2900_info->clk_sys_fmc = clk_sys_fmc * 1000; - - uint32_t clock_limit; - /* Check program time limit */ - clock_limit = 512000000l / FLASH_PROGRAM_TIME; - if (lpc2900_info->clk_sys_fmc < clock_limit) - { - LOG_WARNING("flash clock must be at least %" PRIu32 " kHz", - (clock_limit / 1000)); - return ERROR_FLASH_BANK_INVALID; - } - - /* Check erase time limit */ - clock_limit = (uint32_t)((32767.0 * 512.0 * 1e6) / FLASH_ERASE_TIME); - if (lpc2900_info->clk_sys_fmc > clock_limit) - { - LOG_WARNING("flash clock must be a maximum of %" PRIu32" kHz", - (clock_limit / 1000)); - return ERROR_FLASH_BANK_INVALID; - } - - /* Chip ID will be obtained by probing the device later */ - lpc2900_info->chipid = 0; - - return ERROR_OK; -} - - -/** - * Erase sector(s). - * - * @param bank Pointer to the flash bank descriptor - * @param first First sector to be erased - * @param last Last sector (including) to be erased - */ -static int lpc2900_erase(struct flash_bank *bank, int first, int last) -{ - uint32_t status; - int sector; - int last_unsecured_sector; - struct target *target = bank->target; - struct lpc2900_flash_bank *lpc2900_info = bank->driver_priv; - - - status = lpc2900_is_ready(bank); - if (status != ERROR_OK) - { - return status; - } - - /* Sanity check on sector range */ - if ((first < 0) || (last < first) || (last >= bank->num_sectors)) - { - LOG_INFO("Bad sector range"); - return ERROR_FLASH_SECTOR_INVALID; - } - - /* Update the info about secured sectors */ - lpc2900_read_security_status( bank ); - - /* The selected sector range might include secured sectors. An attempt - * to erase such a sector will cause the erase to fail also for unsecured - * sectors. It is necessary to determine the last unsecured sector now, - * because we have to treat the last relevant sector in the list in - * a special way. - */ - last_unsecured_sector = -1; - for (sector = first; sector <= last; sector++) - { - if ( !bank->sectors[sector].is_protected ) - { - last_unsecured_sector = sector; - } - } - - /* Exit now, in case of the rare constellation where all sectors in range - * are secured. This is regarded a success, since erasing/programming of - * secured sectors shall be handled transparently. - */ - if ( last_unsecured_sector == -1 ) - { - return ERROR_OK; - } - - /* Enable flash block and set the correct CRA clock of 66 kHz */ - lpc2900_setup(bank); - - /* Clear END_OF_ERASE interrupt status */ - target_write_u32(target, INT_CLR_STATUS, INTSRC_END_OF_ERASE); - - /* Set the program/erase timer to FLASH_ERASE_TIME */ - target_write_u32(target, FPTR, - FPTR_EN_T | lpc2900_calc_tr( lpc2900_info->clk_sys_fmc, - FLASH_ERASE_TIME )); - - /* Sectors are marked for erasure, then erased all together */ - for (sector = first; sector <= last_unsecured_sector; sector++) - { - /* Only mark sectors that aren't secured. Any attempt to erase a group - * of sectors will fail if any single one of them is secured! - */ - if ( !bank->sectors[sector].is_protected ) - { - /* Unprotect the sector */ - target_write_u32(target, bank->sectors[sector].offset, 0); - target_write_u32(target, FCTR, - FCTR_FS_LOADREQ | FCTR_FS_WPB | - FCTR_FS_WEB | FCTR_FS_WRE | FCTR_FS_CS); - - /* Mark the sector for erasure. The last sector in the list - triggers the erasure. */ - target_write_u32(target, bank->sectors[sector].offset, 0); - if ( sector == last_unsecured_sector ) - { - target_write_u32(target, FCTR, - FCTR_FS_PROGREQ | FCTR_FS_WPB | FCTR_FS_CS); - } - else - { - target_write_u32(target, FCTR, - FCTR_FS_LOADREQ | FCTR_FS_WPB | - FCTR_FS_WEB | FCTR_FS_CS); - } - } - } - - /* Wait for the end of the erase operation. If it's not over after two seconds, - * something went dreadfully wrong... :-( - */ - if( lpc2900_wait_status(bank, INTSRC_END_OF_ERASE, 2000) != ERROR_OK ) - { - return ERROR_FLASH_OPERATION_FAILED; - } - - /* Normal flash operating mode */ - target_write_u32(target, FCTR, FCTR_FS_CS | FCTR_FS_WEB); - - return ERROR_OK; -} - - - -static int lpc2900_protect(struct flash_bank *bank, int set, int first, int last) -{ - /* This command is not supported. - * "Protection" in LPC2900 terms is handled transparently. Sectors will - * automatically be unprotected as needed. - * Instead we use the concept of sector security. A secured sector is shown - * as "protected" in OpenOCD. Sector security is a permanent feature, and - * cannot be disabled once activated. - */ - - return ERROR_OK; -} - - -/** - * Write data to flash. - * - * @param bank Pointer to the flash bank descriptor - * @param buffer Buffer with data - * @param offset Start address (relative to bank start) - * @param count Number of bytes to be programmed - */ -static int lpc2900_write(struct flash_bank *bank, uint8_t *buffer, - uint32_t offset, uint32_t count) -{ - uint8_t page[FLASH_PAGE_SIZE]; - uint32_t status; - uint32_t num_bytes; - struct target *target = bank->target; - struct lpc2900_flash_bank *lpc2900_info = bank->driver_priv; - int sector; - int retval; - - static const uint32_t write_target_code[] = { - /* Set auto latch mode: FCTR=CS|WRE|WEB */ - 0xe3a0a007, /* loop mov r10, #0x007 */ - 0xe583a000, /* str r10,[r3,#0] */ - - /* Load complete page into latches */ - 0xe3a06020, /* mov r6,#(512/16) */ - 0xe8b00f00, /* next ldmia r0!,{r8-r11} */ - 0xe8a10f00, /* stmia r1!,{r8-r11} */ - 0xe2566001, /* subs r6,#1 */ - 0x1afffffb, /* bne next */ - - /* Clear END_OF_BURN interrupt status */ - 0xe3a0a002, /* mov r10,#(1 << 1) */ - 0xe583afe8, /* str r10,[r3,#0xfe8] */ - - /* Set the erase time to FLASH_PROGRAM_TIME */ - 0xe5834008, /* str r4,[r3,#8] */ - - /* Trigger flash write - FCTR = CS | WRE | WPB | PROGREQ */ - 0xe3a0a083, /* mov r10,#0x83 */ - 0xe38aaa01, /* orr r10,#0x1000 */ - 0xe583a000, /* str r10,[r3,#0] */ - - /* Wait for end of burn */ - 0xe593afe0, /* wait ldr r10,[r3,#0xfe0] */ - 0xe21aa002, /* ands r10,#(1 << 1) */ - 0x0afffffc, /* beq wait */ - - /* End? */ - 0xe2522001, /* subs r2,#1 */ - 0x1affffed, /* bne loop */ - - 0xeafffffe /* done b done */ - }; - - - status = lpc2900_is_ready(bank); - if (status != ERROR_OK) - { - return status; - } - - /* Enable flash block and set the correct CRA clock of 66 kHz */ - lpc2900_setup(bank); - - /* Update the info about secured sectors */ - lpc2900_read_security_status( bank ); - - /* Unprotect all involved sectors */ - for (sector = 0; sector < bank->num_sectors; sector++) - { - /* Start address in or before this sector? */ - /* End address in or behind this sector? */ - if ( ((bank->base + offset) < - (bank->sectors[sector].offset + bank->sectors[sector].size)) && - ((bank->base + (offset + count - 1)) >= bank->sectors[sector].offset) ) - { - /* This sector is involved and needs to be unprotected. - * Don't do it for secured sectors. - */ - if ( !bank->sectors[sector].is_protected ) - { - target_write_u32(target, bank->sectors[sector].offset, 0); - target_write_u32(target, FCTR, - FCTR_FS_LOADREQ | FCTR_FS_WPB | - FCTR_FS_WEB | FCTR_FS_WRE | FCTR_FS_CS); - } - } - } - - /* Set the program/erase time to FLASH_PROGRAM_TIME */ - uint32_t prog_time = FPTR_EN_T | lpc2900_calc_tr( lpc2900_info->clk_sys_fmc, - FLASH_PROGRAM_TIME ); - - /* If there is a working area of reasonable size, use it to program via - a target algorithm. If not, fall back to host programming. */ - - /* We need some room for target code. */ - uint32_t target_code_size = sizeof(write_target_code); - - /* Try working area allocation. Start with a large buffer, and try with - reduced size if that fails. */ - struct working_area *warea; - uint32_t buffer_size = lpc2900_info->max_ram_block - 1 * KiB; - while( (retval = target_alloc_working_area(target, - buffer_size + target_code_size, - &warea)) != ERROR_OK ) - { - /* Try a smaller buffer now, and stop if it's too small. */ - buffer_size -= 1 * KiB; - if (buffer_size < 2 * KiB) - { - LOG_INFO( "no (large enough) working area" - ", falling back to host mode" ); - warea = NULL; - break; - } - }; - - if( warea ) - { - struct reg_param reg_params[5]; - struct armv4_5_algorithm armv4_5_info; - - /* We can use target mode. Download the algorithm. */ - retval = target_write_buffer( target, - (warea->address)+buffer_size, - target_code_size, - (uint8_t *)write_target_code); - if (retval != ERROR_OK) - { - LOG_ERROR("Unable to write block write code to target"); - target_free_all_working_areas(target); - return ERROR_FLASH_OPERATION_FAILED; - } - - 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); - - /* Write to flash in large blocks */ - while ( count != 0 ) - { - uint32_t this_npages; - uint8_t *this_buffer; - int start_sector = lpc2900_address2sector( bank, offset ); - - /* First page / last page / rest */ - if( offset % FLASH_PAGE_SIZE ) - { - /* Block doesn't start on page boundary. - Burn first partial page separately. */ - memset( &page, 0xff, sizeof(page) ); - memcpy( &page[offset % FLASH_PAGE_SIZE], - buffer, - FLASH_PAGE_SIZE - (offset % FLASH_PAGE_SIZE) ); - this_npages = 1; - this_buffer = &page[0]; - count = count + (offset % FLASH_PAGE_SIZE); - offset = offset - (offset % FLASH_PAGE_SIZE); - } - else if( count < FLASH_PAGE_SIZE ) - { - /* Download last incomplete page separately. */ - memset( &page, 0xff, sizeof(page) ); - memcpy( &page, buffer, count ); - this_npages = 1; - this_buffer = &page[0]; - count = FLASH_PAGE_SIZE; - } - else - { - /* Download as many full pages as possible */ - this_npages = (count < buffer_size) ? - count / FLASH_PAGE_SIZE : - buffer_size / FLASH_PAGE_SIZE; - this_buffer = buffer; - - /* Make sure we stop at the next secured sector */ - int sector = start_sector + 1; - while( sector < bank->num_sectors ) - { - /* Secured? */ - if( bank->sectors[sector].is_protected ) - { - /* Is that next sector within the current block? */ - if( (bank->sectors[sector].offset - bank->base) < - (offset + (this_npages * FLASH_PAGE_SIZE)) ) - { - /* Yes! Split the block */ - this_npages = - (bank->sectors[sector].offset - bank->base - offset) - / FLASH_PAGE_SIZE; - break; - } - } - - sector++; - } - } - - /* Skip the current sector if it is secured */ - if (bank->sectors[start_sector].is_protected) - { - LOG_DEBUG("Skip secured sector %d", - start_sector); - - /* Stop if this is the last sector */ - if (start_sector == bank->num_sectors - 1) - { - break; - } - - /* Skip */ - uint32_t nskip = bank->sectors[start_sector].size - - (offset % bank->sectors[start_sector].size); - offset += nskip; - buffer += nskip; - count = (count >= nskip) ? (count - nskip) : 0; - continue; - } - - /* Execute buffer download */ - if ((retval = target_write_buffer(target, - warea->address, - this_npages * FLASH_PAGE_SIZE, - this_buffer)) != ERROR_OK) - { - LOG_ERROR("Unable to write data to target"); - target_free_all_working_areas(target); - return ERROR_FLASH_OPERATION_FAILED; - } - - /* Prepare registers */ - buf_set_u32(reg_params[0].value, 0, 32, warea->address); - buf_set_u32(reg_params[1].value, 0, 32, offset); - buf_set_u32(reg_params[2].value, 0, 32, this_npages); - buf_set_u32(reg_params[3].value, 0, 32, FCTR); - buf_set_u32(reg_params[4].value, 0, 32, FPTR_EN_T | prog_time); - - /* Execute algorithm, assume breakpoint for last instruction */ - 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; - - retval = target_run_algorithm(target, 0, NULL, 5, reg_params, - (warea->address) + buffer_size, - (warea->address) + buffer_size + target_code_size - 4, - 10000, /* 10s should be enough for max. 16 KiB of data */ - &armv4_5_info); - - if (retval != ERROR_OK) - { - LOG_ERROR("Execution of flash algorithm failed."); - target_free_all_working_areas(target); - retval = ERROR_FLASH_OPERATION_FAILED; - break; - } - - count -= this_npages * FLASH_PAGE_SIZE; - buffer += this_npages * FLASH_PAGE_SIZE; - offset += this_npages * FLASH_PAGE_SIZE; - } - - /* Free all resources */ - 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]); - target_free_all_working_areas(target); - } - else - { - /* Write to flash memory page-wise */ - while ( count != 0 ) - { - /* How many bytes do we copy this time? */ - num_bytes = (count >= FLASH_PAGE_SIZE) ? - FLASH_PAGE_SIZE - (offset % FLASH_PAGE_SIZE) : - count; - - /* Don't do anything with it if the page is in a secured sector. */ - if ( !bank->sectors[lpc2900_address2sector(bank, offset)].is_protected ) - { - /* Set latch load mode */ - target_write_u32(target, FCTR, - FCTR_FS_CS | FCTR_FS_WRE | FCTR_FS_WEB); - - /* Always clear the buffer (a little overhead, but who cares) */ - memset(page, 0xFF, FLASH_PAGE_SIZE); - - /* Copy them to the buffer */ - memcpy( &page[offset % FLASH_PAGE_SIZE], - &buffer[offset % FLASH_PAGE_SIZE], - num_bytes ); - - /* Write whole page to flash data latches */ - if (target_write_memory( - target, - bank->base + (offset - (offset % FLASH_PAGE_SIZE)), - 4, FLASH_PAGE_SIZE / 4, page) != ERROR_OK) - { - LOG_ERROR("Write failed @ 0x%8.8" PRIx32, offset); - target_write_u32(target, FCTR, FCTR_FS_CS | FCTR_FS_WEB); - - return ERROR_FLASH_OPERATION_FAILED; - } - - /* Clear END_OF_BURN interrupt status */ - target_write_u32(target, INT_CLR_STATUS, INTSRC_END_OF_BURN); - - /* Set the programming time */ - target_write_u32(target, FPTR, FPTR_EN_T | prog_time); - - /* Trigger flash write */ - target_write_u32(target, FCTR, - FCTR_FS_CS | FCTR_FS_WRE | FCTR_FS_WPB | FCTR_FS_PROGREQ); - - /* Wait for the end of the write operation. If it's not over - * after one second, something went dreadfully wrong... :-( - */ - if (lpc2900_wait_status(bank, INTSRC_END_OF_BURN, 1000) != ERROR_OK) - { - LOG_ERROR("Write failed @ 0x%8.8" PRIx32, offset); - target_write_u32(target, FCTR, FCTR_FS_CS | FCTR_FS_WEB); - - return ERROR_FLASH_OPERATION_FAILED; - } - } - - /* Update pointers and counters */ - offset += num_bytes; - buffer += num_bytes; - count -= num_bytes; - } - - retval = ERROR_OK; - } - - /* Normal flash operating mode */ - target_write_u32(target, FCTR, FCTR_FS_CS | FCTR_FS_WEB); - - return retval; -} - - -/** - * Try and identify the device. - * - * Determine type number and its memory layout. - * - * @param bank Pointer to the flash bank descriptor - */ -static int lpc2900_probe(struct flash_bank *bank) -{ - struct lpc2900_flash_bank *lpc2900_info = bank->driver_priv; - struct target *target = bank->target; - int i = 0; - uint32_t offset; - - - if (target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - /* We want to do this only once. Check if we already have a valid CHIPID, - * because then we will have already successfully probed the device. - */ - if (lpc2900_info->chipid == EXPECTED_CHIPID) - { - return ERROR_OK; - } - - /* Probing starts with reading the CHIPID register. We will continue only - * if this identifies as an LPC2900 device. - */ - target_read_u32(target, CHIPID, &lpc2900_info->chipid); - - if (lpc2900_info->chipid != EXPECTED_CHIPID) - { - LOG_WARNING("Device is not an LPC29xx"); - return ERROR_FLASH_OPERATION_FAILED; - } - - /* It's an LPC29xx device. Now read the feature register FEAT0...FEAT3. */ - uint32_t feat0, feat1, feat2, feat3; - target_read_u32(target, FEAT0, &feat0); - target_read_u32(target, FEAT1, &feat1); - target_read_u32(target, FEAT2, &feat2); - target_read_u32(target, FEAT3, &feat3); - - /* Base address */ - bank->base = 0x20000000; - - /* Determine flash layout from FEAT2 register */ - uint32_t num_64k_sectors = (feat2 >> 16) & 0xFF; - uint32_t num_8k_sectors = (feat2 >> 0) & 0xFF; - bank->num_sectors = num_64k_sectors + num_8k_sectors; - bank->size = KiB * (64 * num_64k_sectors + 8 * num_8k_sectors); - - /* Determine maximum contiguous RAM block */ - lpc2900_info->max_ram_block = 16 * KiB; - if( (feat1 & 0x30) == 0x30 ) - { - lpc2900_info->max_ram_block = 32 * KiB; - if( (feat1 & 0x0C) == 0x0C ) - { - lpc2900_info->max_ram_block = 48 * KiB; - } - } - - /* Determine package code and ITCM size */ - uint32_t package_code = feat0 & 0x0F; - uint32_t itcm_code = (feat1 >> 16) & 0x1F; - - /* Determine the exact type number. */ - uint32_t found = 1; - if ( (package_code == 4) && (itcm_code == 5) ) - { - /* Old LPC2917 or LPC2919 (non-/01 devices) */ - lpc2900_info->target_name = (bank->size == 768*KiB) ? "LPC2919" : "LPC2917"; - } - else - { - if ( package_code == 2 ) - { - /* 100-pin package */ - if ( bank->size == 128*KiB ) - { - lpc2900_info->target_name = "LPC2921"; - } - else if ( bank->size == 256*KiB ) - { - lpc2900_info->target_name = "LPC2923"; - } - else if ( bank->size == 512*KiB ) - { - lpc2900_info->target_name = "LPC2925"; - } - else - { - found = 0; - } - } - else if ( package_code == 4 ) - { - /* 144-pin package */ - if ( (bank->size == 512*KiB) && (feat3 == 0xFFFFFCF0) ) - { - lpc2900_info->target_name = "LPC2917/01"; - } - else if ( (bank->size == 512*KiB) && (feat3 == 0xFFFFFFF1) ) - { - lpc2900_info->target_name = "LPC2927"; - } - else if ( (bank->size == 768*KiB) && (feat3 == 0xFFFFFCF8) ) - { - lpc2900_info->target_name = "LPC2919/01"; - } - else if ( (bank->size == 768*KiB) && (feat3 == 0xFFFFFFF9) ) - { - lpc2900_info->target_name = "LPC2929"; - } - else - { - found = 0; - } - } - else if ( package_code == 5 ) - { - /* 208-pin package */ - lpc2900_info->target_name = (bank->size == 0) ? "LPC2930" : "LPC2939"; - } - else - { - found = 0; - } - } - - if ( !found ) - { - LOG_WARNING("Unknown LPC29xx derivative"); - return ERROR_FLASH_OPERATION_FAILED; - } - - /* Show detected device */ - LOG_INFO("Flash bank %d" - ": Device %s, %" PRIu32 - " KiB in %d sectors", - bank->bank_number, - lpc2900_info->target_name, bank->size / KiB, - bank->num_sectors); - - /* Flashless devices cannot be handled */ - if ( bank->num_sectors == 0 ) - { - LOG_WARNING("Flashless device cannot be handled"); - return ERROR_FLASH_OPERATION_FAILED; - } - - /* Sector layout. - * These are logical sector numbers. When doing real flash operations, - * the logical flash number are translated into the physical flash numbers - * of the device. - */ - bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors); - - offset = 0; - for (i = 0; i < bank->num_sectors; i++) - { - bank->sectors[i].offset = offset; - bank->sectors[i].is_erased = -1; - bank->sectors[i].is_protected = -1; - - if ( i <= 7 ) - { - bank->sectors[i].size = 8 * KiB; - } - else if ( i <= 18 ) - { - bank->sectors[i].size = 64 * KiB; - } - else - { - /* We shouldn't come here. But there might be a new part out there - * that has more than 19 sectors. Politely ask for a fix then. - */ - bank->sectors[i].size = 0; - LOG_ERROR("Never heard about sector %d", i); - } - - offset += bank->sectors[i].size; - } - - /* Read sector security status */ - if ( lpc2900_read_security_status(bank) != ERROR_OK ) - { - LOG_ERROR("Cannot determine sector security status"); - return ERROR_FLASH_OPERATION_FAILED; - } - - return ERROR_OK; -} - - -/** - * Run a blank check for each sector. - * - * For speed reasons, the device isn't read word by word. - * A hash value is calculated by the hardware ("BIST") for each sector. - * This value is then compared against the known hash of an empty sector. - * - * @param bank Pointer to the flash bank descriptor - */ -static int lpc2900_erase_check(struct flash_bank *bank) -{ - uint32_t status = lpc2900_is_ready(bank); - if (status != ERROR_OK) - { - LOG_INFO("Processor not halted/not probed"); - return status; - } - - /* Use the BIST (Built-In Selft Test) to generate a signature of each flash - * sector. Compare against the expected signature of an empty sector. - */ - int sector; - for ( sector = 0; sector < bank->num_sectors; sector++ ) - { - uint32_t signature[4]; - if ( (status = lpc2900_run_bist128( bank, - bank->sectors[sector].offset, - bank->sectors[sector].offset + - (bank->sectors[sector].size - 1), - &signature)) != ERROR_OK ) - { - return status; - } - - /* The expected signatures for an empty sector are different - * for 8 KiB and 64 KiB sectors. - */ - if ( bank->sectors[sector].size == 8*KiB ) - { - bank->sectors[sector].is_erased = - (signature[3] == 0x01ABAAAA) && - (signature[2] == 0xAAAAAAAA) && - (signature[1] == 0xAAAAAAAA) && - (signature[0] == 0xAAA00AAA); - } - if ( bank->sectors[sector].size == 64*KiB ) - { - bank->sectors[sector].is_erased = - (signature[3] == 0x11801222) && - (signature[2] == 0xB88844FF) && - (signature[1] == 0x11A22008) && - (signature[0] == 0x2B1BFE44); - } - } - - return ERROR_OK; -} - - -/** - * Get protection (sector security) status. - * - * Determine the status of "sector security" for each sector. - * A secured sector is one that can never be erased/programmed again. - * - * @param bank Pointer to the flash bank descriptor - */ -static int lpc2900_protect_check(struct flash_bank *bank) -{ - return lpc2900_read_security_status(bank); -} - - -/** - * Print info about the driver (not the device). - * - * @param bank Pointer to the flash bank descriptor - * @param buf Buffer to take the string - * @param buf_size Maximum number of characters that the buffer can take - */ -static int lpc2900_info(struct flash_bank *bank, char *buf, int buf_size) -{ - snprintf(buf, buf_size, "lpc2900 flash driver"); - - return ERROR_OK; -} - - -struct flash_driver lpc2900_flash = -{ - .name = "lpc2900", - .commands = lpc2900_command_handlers, - .flash_bank_command = lpc2900_flash_bank_command, - .erase = lpc2900_erase, - .protect = lpc2900_protect, - .write = lpc2900_write, - .probe = lpc2900_probe, - .auto_probe = lpc2900_probe, - .erase_check = lpc2900_erase_check, - .protect_check = lpc2900_protect_check, - .info = lpc2900_info -}; diff --git a/src/flash/non_cfi.c b/src/flash/non_cfi.c deleted file mode 100644 index f98b1080..00000000 --- a/src/flash/non_cfi.c +++ /dev/null @@ -1,491 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2007 by Dominic Rath * - * Dominic.Rath@gmx.de * - * Copyright (C) 2009 Michael Schwingen * - * michael@schwingen.org * - * * - * 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 "non_cfi.h" -#include "cfi.h" - - -#define KB 1024 -#define MB (1024*1024) -#define ERASE_REGION(num, size) (((size/256) << 16) | (num-1)) - -/* non-CFI compatible flashes */ -static struct non_cfi non_cfi_flashes[] = { - { - .mfr = CFI_MFR_SST, - .id = 0xd4, - .pri_id = 0x02, - .dev_size = 64*KB, - .interface_desc = 0x0, /* x8 only device */ - .max_buf_write_size = 0x0, - .status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7, - .num_erase_regions = 1, - .erase_region_info = - { - ERASE_REGION(16, 4*KB) - } - }, - { - .mfr = CFI_MFR_SST, - .id = 0xd5, - .pri_id = 0x02, - .dev_size = 128*KB, - .interface_desc = 0x0, /* x8 only device */ - .max_buf_write_size = 0x0, - .status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7, - .num_erase_regions = 1, - .erase_region_info = - { - ERASE_REGION(32, 4*KB) - } - }, - { - .mfr = CFI_MFR_SST, - .id = 0xd6, - .pri_id = 0x02, - .dev_size = 256*KB, - .interface_desc = 0x0, /* x8 only device */ - .max_buf_write_size = 0x0, - .status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7, - .num_erase_regions = 1, - .erase_region_info = - { - ERASE_REGION(64, 4*KB) - } - }, - { - .mfr = CFI_MFR_SST, - .id = 0xd7, - .pri_id = 0x02, - .dev_size = 512*KB, - .interface_desc = 0x0, /* x8 only device */ - .max_buf_write_size = 0x0, - .status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7, - .num_erase_regions = 1, - .erase_region_info = - { - ERASE_REGION(128, 4*KB) - } - }, - { - .mfr = CFI_MFR_SST, - .id = 0x2780, - .pri_id = 0x02, - .dev_size = 512*KB, - .interface_desc = 0x2, /* x8 or x16 device */ - .max_buf_write_size = 0x0, - .status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7, - .num_erase_regions = 1, - .erase_region_info = - { - ERASE_REGION(128, 4*KB) - } - }, - { - .mfr = CFI_MFR_ST, - .id = 0xd6, /* ST29F400BB */ - .pri_id = 0x02, - .dev_size = 512*KB, - .interface_desc = 0x2, /* x8 or x16 device with nBYTE */ - .max_buf_write_size = 0x0, - .status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7, - .num_erase_regions = 4, - .erase_region_info = - { - ERASE_REGION(1, 16*KB), - ERASE_REGION(2, 8*KB), - ERASE_REGION(1, 32*KB), - ERASE_REGION(7, 64*KB) - } - }, - { - .mfr = CFI_MFR_ST, - .id = 0xd5, /* ST29F400BT */ - .pri_id = 0x02, - .dev_size = 512*KB, - .interface_desc = 0x2, /* x8 or x16 device with nBYTE */ - .max_buf_write_size = 0x0, - .status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7, - .num_erase_regions = 4, - .erase_region_info = - { - ERASE_REGION(7, 64*KB), - ERASE_REGION(1, 32*KB), - ERASE_REGION(2, 8*KB), - ERASE_REGION(1, 16*KB) - } - }, - - /* SST 39VF* do not support DQ5 status polling - this currently is - only supported by the host algorithm, not by the target code using - the work area. - Only true for 8-bit and 32-bit wide memories. 16-bit wide memories - without DQ5 status polling are supported by the target code. - */ - { - .mfr = CFI_MFR_SST, - .id = 0x2782, /* SST39xF160 */ - .pri_id = 0x02, - .dev_size = 2*MB, - .interface_desc = 0x2, /* x8 or x16 device with nBYTE */ - .max_buf_write_size = 0x0, - .status_poll_mask = CFI_STATUS_POLL_MASK_DQ6_DQ7, - .num_erase_regions = 1, - .erase_region_info = - { - ERASE_REGION(512, 4*KB) - } - }, - { - .mfr = CFI_MFR_SST, - .id = 0x2783, /* SST39VF320 */ - .pri_id = 0x02, - .dev_size = 4*MB, - .interface_desc = 0x2, /* x8 or x16 device with nBYTE */ - .max_buf_write_size = 0x0, - .status_poll_mask = CFI_STATUS_POLL_MASK_DQ6_DQ7, - .num_erase_regions = 1, - .erase_region_info = - { - ERASE_REGION(1024, 4*KB) - } - }, - { - .mfr = CFI_MFR_SST, - .id = 0x234b, /* SST39VF1601 */ - .pri_id = 0x02, - .dev_size = 2*MB, - .interface_desc = 0x2, /* x8 or x16 device with nBYTE */ - .max_buf_write_size = 0x0, - .status_poll_mask = CFI_STATUS_POLL_MASK_DQ6_DQ7, - .num_erase_regions = 1, - .erase_region_info = - { - ERASE_REGION(512, 4*KB) - } - }, - { - .mfr = CFI_MFR_SST, - .id = 0x234a, /* SST39VF1602 */ - .pri_id = 0x02, - .dev_size = 2*MB, - .interface_desc = 0x2, /* x8 or x16 device with nBYTE */ - .max_buf_write_size = 0x0, - .status_poll_mask = CFI_STATUS_POLL_MASK_DQ6_DQ7, - .num_erase_regions = 1, - .erase_region_info = - { - ERASE_REGION(512, 4*KB) - } - }, - { - .mfr = CFI_MFR_SST, - .id = 0x235b, /* SST39VF3201 */ - .pri_id = 0x02, - .dev_size = 4*MB, - .interface_desc = 0x2, /* x8 or x16 device with nBYTE */ - .max_buf_write_size = 0x0, - .status_poll_mask = CFI_STATUS_POLL_MASK_DQ6_DQ7, - .num_erase_regions = 1, - .erase_region_info = - { - ERASE_REGION(1024, 4*KB) - } - }, - { - .mfr = CFI_MFR_SST, - .id = 0x235a, /* SST39VF3202 */ - .pri_id = 0x02, - .dev_size = 4*MB, - .interface_desc = 0x2, /* x8 or x16 device with nBYTE */ - .max_buf_write_size = 0x0, - .status_poll_mask = CFI_STATUS_POLL_MASK_DQ6_DQ7, - .num_erase_regions = 1, - .erase_region_info = - { - ERASE_REGION(1024, 4*KB) - } - }, - { - .mfr = CFI_MFR_AMD, - .id = 0x22ab, /* AM29F400BB */ - .pri_id = 0x02, - .dev_size = 512*KB, - .interface_desc = 0x2, /* x8 or x16 device with nBYTE */ - .max_buf_write_size = 0x0, - .status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7, - .num_erase_regions = 4, - .erase_region_info = - { - ERASE_REGION(1, 16*KB), - ERASE_REGION(2, 8*KB), - ERASE_REGION(1, 32*KB), - ERASE_REGION(7, 64*KB) - } - }, - { - .mfr = CFI_MFR_AMD, - .id = 0x2223, /* AM29F400BT */ - .pri_id = 0x02, - .dev_size = 512*KB, - .interface_desc = 0x2, /* x8 or x16 device with nBYTE */ - .max_buf_write_size = 0x0, - .status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7, - .num_erase_regions = 4, - .erase_region_info = - { - ERASE_REGION(7, 64*KB), - ERASE_REGION(1, 32*KB), - ERASE_REGION(2, 8*KB), - ERASE_REGION(1, 16*KB) - } - }, - { - .mfr = CFI_MFR_FUJITSU, - .id = 0x226b, /* AM29SL800DB */ - .pri_id = 0x02, - .dev_size = 1*MB, - .interface_desc = 0x2, /* x8 or x16 device with nBYTE */ - .max_buf_write_size = 0x0, - .status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7, - .num_erase_regions = 4, - .erase_region_info = - { - ERASE_REGION(1, 16*KB), - ERASE_REGION(2, 8*KB), - ERASE_REGION(1, 32*KB), - ERASE_REGION(15, 64*KB) - } - }, - { - .mfr = CFI_MFR_AMIC, - .id = 0xb31a, /* A29L800A */ - .pri_id = 0x02, - .dev_size = 1*MB, - .interface_desc = 0x2, - .max_buf_write_size = 0x0, - .status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7, - .num_erase_regions = 4, - .erase_region_info = - { - ERASE_REGION(1, 16*KB), - ERASE_REGION(2, 8*KB), - ERASE_REGION(1, 32*KB), - ERASE_REGION(15, 64*KB) - } - }, - { - .mfr = CFI_MFR_MX, - .id = 0x225b, /* MX29LV800B */ - .pri_id = 0x02, - .dev_size = 1*MB, - .interface_desc = 0x2, /* x8 or x16 device with nBYTE */ - .max_buf_write_size = 0x0, - .status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7, - .num_erase_regions = 4, - .erase_region_info = - { - ERASE_REGION(1, 16*KB), - ERASE_REGION(2, 8*KB), - ERASE_REGION(1, 32*KB), - ERASE_REGION(15, 64*KB) - } - }, - - { - .mfr = CFI_MFR_MX, - .id = 0x2249, /* MX29LV160AB: 2MB */ - .pri_id = 0x02, - .dev_size = 2*MB, - .interface_desc = 0x2, /* x8 or x16 device with nBYTE */ - .max_buf_write_size = 0x0, - .status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7, - .num_erase_regions = 4, - .erase_region_info = - { - ERASE_REGION(1, 16*KB), - ERASE_REGION(2, 8*KB), - ERASE_REGION(1, 32*KB), - ERASE_REGION(31, 64*KB) - } - }, - { - .mfr = CFI_MFR_MX, - .id = 0x22C4, /* MX29LV160AT: 2MB */ - .pri_id = 0x02, - .dev_size = 2*MB, - .interface_desc = 0x2, /* x8 or x16 device with nBYTE */ - .max_buf_write_size = 0x0, - .status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7, - .num_erase_regions = 4, - .erase_region_info = - { - ERASE_REGION(31, 64*KB), - ERASE_REGION(1, 32*KB), - ERASE_REGION(2, 8*KB), - ERASE_REGION(1, 16*KB) - } - }, - { - .mfr = CFI_MFR_ATMEL, - .id = 0x00c0, /* Atmel 49BV1614 */ - .pri_id = 0x02, - .dev_size = 2*MB, - .interface_desc = 0x2, /* x8 or x16 device with nBYTE */ - .max_buf_write_size = 0x0, - .status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7, - .num_erase_regions = 3, - .erase_region_info = - { - ERASE_REGION(8, 8*KB), - ERASE_REGION(2, 32*KB), - ERASE_REGION(30, 64*KB) - } - }, - { - .mfr = CFI_MFR_ATMEL, - .id = 0xC2, /* Atmel 49BV1614T */ - .pri_id = 0x02, - .dev_size = 2*MB, - .interface_desc = 0x2, /* x8 or x16 device with nBYTE */ - .max_buf_write_size = 0x0, - .status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7, - .num_erase_regions = 3, - .erase_region_info = - { - ERASE_REGION(30, 64*KB), - ERASE_REGION(2, 32*KB), - ERASE_REGION(8, 8*KB) - } - }, - { - .mfr = CFI_MFR_AMD, - .id = 0x225b, /* S29AL008D */ - .pri_id = 0x02, - .dev_size = 1*MB, - .interface_desc = 0x2, /* x8 or x16 device with nBYTE */ - .max_buf_write_size = 0x0, - .status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7, - .num_erase_regions = 4, - .erase_region_info = - { - ERASE_REGION(1, 16*KB), - ERASE_REGION(2, 8*KB), - ERASE_REGION(1, 32*KB), - ERASE_REGION(15, 64*KB) - } - }, - { - .mfr = 0, - .id = 0, - } -}; - -void cfi_fixup_non_cfi(struct flash_bank *bank) -{ - struct cfi_flash_bank *cfi_info = bank->driver_priv; - struct non_cfi *non_cfi = non_cfi_flashes; - - for (non_cfi = non_cfi_flashes; non_cfi->mfr; non_cfi++) - { - if ((cfi_info->manufacturer == non_cfi->mfr) - && (cfi_info->device_id == non_cfi->id)) - { - break; - } - } - - /* only fixup jedec flashs found in table */ - if (!non_cfi->mfr) - return; - - cfi_info->not_cfi = 1; - - /* fill in defaults for non-critical data */ - cfi_info->vcc_min = 0x0; - cfi_info->vcc_max = 0x0; - cfi_info->vpp_min = 0x0; - cfi_info->vpp_max = 0x0; - cfi_info->word_write_timeout_typ = 0x0; - cfi_info->buf_write_timeout_typ = 0x0; - cfi_info->block_erase_timeout_typ = 0x0; - cfi_info->chip_erase_timeout_typ = 0x0; - cfi_info->word_write_timeout_max = 0x0; - cfi_info->buf_write_timeout_max = 0x0; - cfi_info->block_erase_timeout_max = 0x0; - cfi_info->chip_erase_timeout_max = 0x0; - - cfi_info->qry[0] = 'Q'; - cfi_info->qry[1] = 'R'; - cfi_info->qry[2] = 'Y'; - - cfi_info->pri_id = non_cfi->pri_id; - cfi_info->pri_addr = 0x0; - cfi_info->alt_id = 0x0; - cfi_info->alt_addr = 0x0; - cfi_info->alt_ext = NULL; - - cfi_info->interface_desc = non_cfi->interface_desc; - cfi_info->max_buf_write_size = non_cfi->max_buf_write_size; - cfi_info->status_poll_mask = non_cfi->status_poll_mask; - cfi_info->num_erase_regions = non_cfi->num_erase_regions; - cfi_info->erase_region_info = non_cfi->erase_region_info; - cfi_info->dev_size = non_cfi->dev_size; - - if (cfi_info->pri_id == 0x2) - { - struct cfi_spansion_pri_ext *pri_ext = malloc(sizeof(struct cfi_spansion_pri_ext)); - - pri_ext->pri[0] = 'P'; - pri_ext->pri[1] = 'R'; - pri_ext->pri[2] = 'I'; - - pri_ext->major_version = '1'; - pri_ext->minor_version = '0'; - - pri_ext->SiliconRevision = 0x0; - pri_ext->EraseSuspend = 0x0; - pri_ext->EraseSuspend = 0x0; - pri_ext->BlkProt = 0x0; - pri_ext->TmpBlkUnprotect = 0x0; - pri_ext->BlkProtUnprot = 0x0; - pri_ext->SimultaneousOps = 0x0; - pri_ext->BurstMode = 0x0; - pri_ext->PageMode = 0x0; - pri_ext->VppMin = 0x0; - pri_ext->VppMax = 0x0; - pri_ext->TopBottom = 0x0; - - pri_ext->_unlock1 = 0x5555; - pri_ext->_unlock2 = 0x2AAA; - pri_ext->_reversed_geometry = 0; - - cfi_info->pri_ext = pri_ext; - } else if ((cfi_info->pri_id == 0x1) || (cfi_info->pri_id == 0x3)) - { - LOG_ERROR("BUG: non-CFI flashes using the Intel commandset are not yet supported"); - exit(-1); - } -} diff --git a/src/flash/non_cfi.h b/src/flash/non_cfi.h deleted file mode 100644 index 44c92db4..00000000 --- a/src/flash/non_cfi.h +++ /dev/null @@ -1,40 +0,0 @@ -/*************************************************************************** - * 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. * - ***************************************************************************/ -#ifndef NON_CFI_H -#define NON_CFI_H - -#include "flash.h" - -struct non_cfi -{ - uint16_t mfr; - uint16_t id; - uint16_t pri_id; - uint32_t dev_size; - uint16_t interface_desc; - uint16_t max_buf_write_size; - uint8_t num_erase_regions; - uint32_t erase_region_info[6]; - uint8_t status_poll_mask; -}; - -void cfi_fixup_non_cfi(struct flash_bank *bank); - -#endif /* NON_CFI_H */ diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am new file mode 100644 index 00000000..d2d99981 --- /dev/null +++ b/src/flash/nor/Makefile.am @@ -0,0 +1,46 @@ +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/helper \ + -I$(top_srcdir)/src/jtag \ + -I$(top_srcdir)/src/flash \ + -I$(top_srcdir)/src/target + +noinst_LTLIBRARIES = libocdflashnor.la +libocdflashnor_la_SOURCES = \ + aduc702x.c \ + at91sam3.c \ + at91sam7.c \ + avrf.c \ + cfi.c \ + ecos.c \ + faux.c \ + lpc2000.c \ + lpc288x.c \ + lpc2900.c \ + non_cfi.c \ + ocl.c \ + pic32mx.c \ + stellaris.c \ + stm32x.c \ + str7x.c \ + str9x.c \ + str9xpec.c \ + tms470.c + +noinst_HEADERS = \ + at91sam7.h \ + at91sam3.h \ + avrf.h \ + cfi.h \ + lpc2000.h \ + lpc288x.h \ + non_cfi.h \ + ocl.h \ + pic32mx.h \ + stellaris.h \ + stm32x.h \ + str7x.h \ + str9x.h \ + str9xpec.h \ + tms470.h + +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in diff --git a/src/flash/nor/aduc702x.c b/src/flash/nor/aduc702x.c new file mode 100644 index 00000000..643705ca --- /dev/null +++ b/src/flash/nor/aduc702x.c @@ -0,0 +1,425 @@ +/*************************************************************************** + * Copyright (C) 2008 by Kevin McGuire * + * Copyright (C) 2008 by Marcel Wijlaars * + * Copyright (C) 2009 by Michael Ashton * + * * + * 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 "armv4_5.h" +#include "binarybuffer.h" +#include "time_support.h" +#include "algorithm.h" + + +static int aduc702x_build_sector_list(struct flash_bank *bank); +static int aduc702x_check_flash_completion(struct target* target, unsigned int timeout_ms); +static int aduc702x_set_write_enable(struct target *target, int enable); + +#define ADUC702x_FLASH 0xfffff800 +#define ADUC702x_FLASH_FEESTA (0*4) +#define ADUC702x_FLASH_FEEMOD (1*4) +#define ADUC702x_FLASH_FEECON (2*4) +#define ADUC702x_FLASH_FEEDAT (3*4) +#define ADUC702x_FLASH_FEEADR (4*4) +#define ADUC702x_FLASH_FEESIGN (5*4) +#define ADUC702x_FLASH_FEEPRO (6*4) +#define ADUC702x_FLASH_FEEHIDE (7*4) + +struct aduc702x_flash_bank { + struct working_area *write_algorithm; +}; + +/* flash bank aduc702x 0 0 0 0 + * The ADC7019-28 devices all have the same flash layout */ +FLASH_BANK_COMMAND_HANDLER(aduc702x_flash_bank_command) +{ + struct aduc702x_flash_bank *nbank; + + nbank = malloc(sizeof(struct aduc702x_flash_bank)); + + bank->base = 0x80000; + bank->size = 0xF800; // top 4k not accessible + bank->driver_priv = nbank; + + aduc702x_build_sector_list(bank); + + return ERROR_OK; +} + +static int aduc702x_build_sector_list(struct flash_bank *bank) +{ + //aduc7026_struct flash_bank *aduc7026_info = bank->driver_priv; + + int i = 0; + uint32_t offset = 0; + + // sector size is 512 + bank->num_sectors = bank->size / 512; + bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors); + for (i = 0; i < bank->num_sectors; ++i) + { + bank->sectors[i].offset = offset; + bank->sectors[i].size = 512; + offset += bank->sectors[i].size; + bank->sectors[i].is_erased = -1; + bank->sectors[i].is_protected = 0; + } + + return ERROR_OK; +} + +static int aduc702x_protect_check(struct flash_bank *bank) +{ + printf("aduc702x_protect_check not implemented yet.\n"); + return ERROR_OK; +} + +static int aduc702x_erase(struct flash_bank *bank, int first, int last) +{ + //int res; + int x; + int count; + //uint32_t v; + struct target *target = bank->target; + + aduc702x_set_write_enable(target, 1); + + /* mass erase */ + if (((first | last) == 0) || ((first == 0) && (last >= bank->num_sectors))) { + LOG_DEBUG("performing mass erase.\n"); + target_write_u16(target, ADUC702x_FLASH + ADUC702x_FLASH_FEEDAT, 0x3cff); + target_write_u16(target, ADUC702x_FLASH + ADUC702x_FLASH_FEEADR, 0xffc3); + target_write_u8(target, ADUC702x_FLASH + ADUC702x_FLASH_FEECON, 0x06); + + if (aduc702x_check_flash_completion(target, 3500) != ERROR_OK) + { + LOG_ERROR("mass erase failed\n"); + aduc702x_set_write_enable(target, 0); + return ERROR_FLASH_OPERATION_FAILED; + } + + LOG_DEBUG("mass erase successful.\n"); + return ERROR_OK; + } else { + unsigned long adr; + + count = last - first + 1; + for (x = 0; x < count; ++x) + { + adr = bank->base + ((first + x) * 512); + + target_write_u16(target, ADUC702x_FLASH + ADUC702x_FLASH_FEEADR, adr); + target_write_u8(target, ADUC702x_FLASH + ADUC702x_FLASH_FEECON, 0x05); + + if (aduc702x_check_flash_completion(target, 50) != ERROR_OK) + { + LOG_ERROR("failed to erase sector at address 0x%08lX\n", adr); + aduc702x_set_write_enable(target, 0); + return ERROR_FLASH_SECTOR_NOT_ERASED; + } + + LOG_DEBUG("erased sector at address 0x%08lX\n", adr); + } + } + + aduc702x_set_write_enable(target, 0); + + return ERROR_OK; +} + +static int aduc702x_protect(struct flash_bank *bank, int set, int first, int last) +{ + printf("aduc702x_protect not implemented yet.\n"); + return ERROR_FLASH_OPERATION_FAILED; +} + +/* If this fn returns ERROR_TARGET_RESOURCE_NOT_AVAILABLE, then the caller can fall + * back to another mechanism that does not require onboard RAM + * + * Caller should not check for other return values specifically + */ +static int aduc702x_write_block(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) +{ + struct aduc702x_flash_bank *aduc702x_info = bank->driver_priv; + struct target *target = bank->target; + uint32_t buffer_size = 7000; + struct working_area *source; + uint32_t address = bank->base + offset; + struct reg_param reg_params[6]; + struct armv4_5_algorithm armv4_5_info; + int retval = ERROR_OK; + + if (((count%2)!=0)||((offset%2)!=0)) + { + LOG_ERROR("write block must be multiple of two bytes in offset & length"); + return ERROR_FAIL; + } + + /* parameters: + + r0 - address of source data (absolute) + r1 - number of halfwords to be copied + r2 - start address in flash (offset from beginning of flash memory) + r3 - exit code + r4 - base address of flash controller (0xFFFFF800) + + registers: + + r5 - scratch + r6 - set to 2, used to write flash command + + */ + uint32_t aduc702x_flash_write_code[] = { + //<_start>: + 0xe3a05008, // mov r5, #8 ; 0x8 + 0xe5845004, // str r5, [r4, #4] + 0xe3a06002, // mov r6, #2 ; 0x2 + //: + 0xe1c421b0, // strh r2, [r4, #16] + 0xe0d050b2, // ldrh r5, [r0], #2 + 0xe1c450bc, // strh r5, [r4, #12] + 0xe5c46008, // strb r6, [r4, #8] + //: + 0xe1d430b0, // ldrh r3, [r4] + 0xe3130004, // tst r3, #4 ; 0x4 + 0x1afffffc, // bne 1001c + 0xe2822002, // add r2, r2, #2 ; 0x2 + 0xe2511001, // subs r1, r1, #1 ; 0x1 + 0x0a000001, // beq 1003c + 0xe3130001, // tst r3, #1 ; 0x1 + 0x1afffff3, // bne 1000c + //: + 0xeafffffe // b 1003c + }; + + /* flash write code */ + if (target_alloc_working_area(target, sizeof(aduc702x_flash_write_code), + &aduc702x_info->write_algorithm) != ERROR_OK) + { + LOG_WARNING("no working area available, can't do block memory writes"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + }; + + retval=target_write_buffer(target, aduc702x_info->write_algorithm->address, + sizeof(aduc702x_flash_write_code), (uint8_t*)aduc702x_flash_write_code); + if (retval!=ERROR_OK) + { + return retval; + } + + /* 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 (aduc702x_info->write_algorithm) + target_free_working_area(target, aduc702x_info->write_algorithm); + + LOG_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); + init_reg_param(®_params[4], "r4", 32, PARAM_OUT); + + while (count > 0) + { + uint32_t thisrun_count = (count > buffer_size) ? buffer_size : count; + + retval=target_write_buffer(target, source->address, thisrun_count, buffer); + if (retval!=ERROR_OK) + { + break; + } + + buf_set_u32(reg_params[0].value, 0, 32, source->address); + buf_set_u32(reg_params[1].value, 0, 32, thisrun_count/2); + buf_set_u32(reg_params[2].value, 0, 32, address); + buf_set_u32(reg_params[4].value, 0, 32, 0xFFFFF800); + + if ((retval = target_run_algorithm(target, 0, NULL, 5, + reg_params, aduc702x_info->write_algorithm->address, + aduc702x_info->write_algorithm->address + sizeof(aduc702x_flash_write_code) - 4, + 10000, &armv4_5_info)) != ERROR_OK) + { + LOG_ERROR("error executing aduc702x flash write algorithm"); + break; + } + + if ((buf_get_u32(reg_params[3].value, 0, 32) & 1) != 1) + { + /* FIX!!!! what does this mean??? replace w/sensible error message */ + LOG_ERROR("aduc702x detected error writing flash"); + retval = ERROR_FAIL; + break; + } + + buffer += thisrun_count; + address += thisrun_count; + count -= thisrun_count; + } + + target_free_working_area(target, source); + target_free_working_area(target, aduc702x_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]); + + return retval; +} + +/* All-JTAG, single-access method. Very slow. Used only if there is no + * working area available. */ +static int aduc702x_write_single(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) +{ + uint32_t x; + uint8_t b; + struct target *target = bank->target; + + aduc702x_set_write_enable(target, 1); + + for (x = 0; x < count; x += 2) { + // FEEADR = address + target_write_u16(target, ADUC702x_FLASH + ADUC702x_FLASH_FEEADR, offset + x); + + // set up data + if ((x + 1) == count) + { + // last byte + target_read_u8(target, offset + x + 1, &b); + } + else + b = buffer[x + 1]; + + target_write_u16(target, ADUC702x_FLASH + ADUC702x_FLASH_FEEDAT, buffer[x] | (b << 8)); + + // do single-write command + target_write_u8(target, ADUC702x_FLASH + ADUC702x_FLASH_FEECON, 0x02); + + if (aduc702x_check_flash_completion(target, 1) != ERROR_OK) + { + LOG_ERROR("single write failed for address 0x%08lX\n", (unsigned long)(offset + x)); + aduc702x_set_write_enable(target, 0); + return ERROR_FLASH_OPERATION_FAILED; + } + + } + LOG_DEBUG("wrote %d bytes at address 0x%08lX\n", (int)count, (unsigned long)(offset + x)); + + aduc702x_set_write_enable(target, 0); + + return ERROR_OK; +} + +int aduc702x_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) +{ + int retval; + + /* try using a block write */ + if ((retval = aduc702x_write_block(bank, buffer, offset, count)) != ERROR_OK) + { + if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) + { + /* if block write failed (no sufficient working area), + * use normal (slow) JTAG method */ + LOG_WARNING("couldn't use block writes, falling back to single memory accesses"); + + if ((retval = aduc702x_write_single(bank, buffer, offset, count)) != ERROR_OK) + { + LOG_ERROR("slow write failed"); + return ERROR_FLASH_OPERATION_FAILED; + } + } + } + + return retval; +} + +static int aduc702x_probe(struct flash_bank *bank) +{ + return ERROR_OK; +} + +static int aduc702x_info(struct flash_bank *bank, char *buf, int buf_size) +{ + snprintf(buf, buf_size, "aduc702x flash driver info"); + return ERROR_OK; +} + +/* sets FEEMOD bit 3 + * enable = 1 enables writes & erases, 0 disables them */ +static int aduc702x_set_write_enable(struct target *target, int enable) +{ + // don't bother to preserve int enable bit here + target_write_u16(target, ADUC702x_FLASH + ADUC702x_FLASH_FEEMOD, enable ? 8 : 0); + + return ERROR_OK; +} + +/* wait up to timeout_ms for controller to not be busy, + * then check whether the command passed or failed. + * + * this function sleeps 1ms between checks (after the first one), + * so in some cases may slow things down without a usleep after the first read */ +static int aduc702x_check_flash_completion(struct target* target, unsigned int timeout_ms) +{ + uint8_t v = 4; + + long long endtime = timeval_ms() + timeout_ms; + while (1) { + target_read_u8(target, ADUC702x_FLASH + ADUC702x_FLASH_FEESTA, &v); + if ((v & 4) == 0) break; + alive_sleep(1); + if (timeval_ms() >= endtime) break; + } + + if (v & 2) return ERROR_FAIL; + // if a command is ignored, both the success and fail bits may be 0 + else if ((v & 3) == 0) return ERROR_FAIL; + else return ERROR_OK; +} + +struct flash_driver aduc702x_flash = { + .name = "aduc702x", + .flash_bank_command = &aduc702x_flash_bank_command, + .erase = &aduc702x_erase, + .protect = &aduc702x_protect, + .write = &aduc702x_write, + .probe = &aduc702x_probe, + .auto_probe = &aduc702x_probe, + .erase_check = &default_flash_blank_check, + .protect_check = &aduc702x_protect_check, + .info = &aduc702x_info + }; diff --git a/src/flash/nor/at91sam3.c b/src/flash/nor/at91sam3.c new file mode 100644 index 00000000..be17a5f8 --- /dev/null +++ b/src/flash/nor/at91sam3.c @@ -0,0 +1,2516 @@ +/*************************************************************************** + * Copyright (C) 2009 by Duane Ellis * + * openocd@duaneellis.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS for A PARTICULAR PURPOSE. See the * + * GNU General public License for more details. * + * * + * You should have received a copy of the GNU General public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +****************************************************************************/ + +/* Some of the the lower level code was based on code supplied by + * ATMEL under this copyright. */ + +/* BEGIN ATMEL COPYRIGHT */ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2009, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ +/* END ATMEL COPYRIGHT */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + + +#include +#include +#include +#include "types.h" +#include "flash.h" +#include "membuf.h" +#include "at91sam3.h" +#include "time_support.h" + +#define REG_NAME_WIDTH (12) + + +#define FLASH_BANK0_BASE 0x00080000 +#define FLASH_BANK1_BASE 0x00100000 + +#define AT91C_EFC_FCMD_GETD (0x0) // (EFC) Get Flash Descriptor +#define AT91C_EFC_FCMD_WP (0x1) // (EFC) Write Page +#define AT91C_EFC_FCMD_WPL (0x2) // (EFC) Write Page and Lock +#define AT91C_EFC_FCMD_EWP (0x3) // (EFC) Erase Page and Write Page +#define AT91C_EFC_FCMD_EWPL (0x4) // (EFC) Erase Page and Write Page then Lock +#define AT91C_EFC_FCMD_EA (0x5) // (EFC) Erase All +// cmd6 is not present int he at91sam3u4/2/1 data sheet table 17-2 +// #define AT91C_EFC_FCMD_EPL (0x6) // (EFC) Erase plane? +// cmd7 is not present int he at91sam3u4/2/1 data sheet table 17-2 +// #define AT91C_EFC_FCMD_EPA (0x7) // (EFC) Erase pages? +#define AT91C_EFC_FCMD_SLB (0x8) // (EFC) Set Lock Bit +#define AT91C_EFC_FCMD_CLB (0x9) // (EFC) Clear Lock Bit +#define AT91C_EFC_FCMD_GLB (0xA) // (EFC) Get Lock Bit +#define AT91C_EFC_FCMD_SFB (0xB) // (EFC) Set Fuse Bit +#define AT91C_EFC_FCMD_CFB (0xC) // (EFC) Clear Fuse Bit +#define AT91C_EFC_FCMD_GFB (0xD) // (EFC) Get Fuse Bit +#define AT91C_EFC_FCMD_STUI (0xE) // (EFC) Start Read Unique ID +#define AT91C_EFC_FCMD_SPUI (0xF) // (EFC) Stop Read Unique ID + +#define offset_EFC_FMR 0 +#define offset_EFC_FCR 4 +#define offset_EFC_FSR 8 +#define offset_EFC_FRR 12 + + +static float +_tomhz(uint32_t freq_hz) +{ + float f; + + f = ((float)(freq_hz)) / 1000000.0; + return f; +} + +// How the chip is configured. +struct sam3_cfg { + uint32_t unique_id[4]; + + uint32_t slow_freq; + uint32_t rc_freq; + uint32_t mainosc_freq; + uint32_t plla_freq; + uint32_t mclk_freq; + uint32_t cpu_freq; + uint32_t fclk_freq; + uint32_t pclk0_freq; + uint32_t pclk1_freq; + uint32_t pclk2_freq; + + +#define SAM3_CHIPID_CIDR (0x400E0740) + uint32_t CHIPID_CIDR; +#define SAM3_CHIPID_EXID (0x400E0744) + uint32_t CHIPID_EXID; + +#define SAM3_SUPC_CR (0x400E1210) + uint32_t SUPC_CR; + +#define SAM3_PMC_BASE (0x400E0400) +#define SAM3_PMC_SCSR (SAM3_PMC_BASE + 0x0008) + uint32_t PMC_SCSR; +#define SAM3_PMC_PCSR (SAM3_PMC_BASE + 0x0018) + uint32_t PMC_PCSR; +#define SAM3_CKGR_UCKR (SAM3_PMC_BASE + 0x001c) + uint32_t CKGR_UCKR; +#define SAM3_CKGR_MOR (SAM3_PMC_BASE + 0x0020) + uint32_t CKGR_MOR; +#define SAM3_CKGR_MCFR (SAM3_PMC_BASE + 0x0024) + uint32_t CKGR_MCFR; +#define SAM3_CKGR_PLLAR (SAM3_PMC_BASE + 0x0028) + uint32_t CKGR_PLLAR; +#define SAM3_PMC_MCKR (SAM3_PMC_BASE + 0x0030) + uint32_t PMC_MCKR; +#define SAM3_PMC_PCK0 (SAM3_PMC_BASE + 0x0040) + uint32_t PMC_PCK0; +#define SAM3_PMC_PCK1 (SAM3_PMC_BASE + 0x0044) + uint32_t PMC_PCK1; +#define SAM3_PMC_PCK2 (SAM3_PMC_BASE + 0x0048) + uint32_t PMC_PCK2; +#define SAM3_PMC_SR (SAM3_PMC_BASE + 0x0068) + uint32_t PMC_SR; +#define SAM3_PMC_IMR (SAM3_PMC_BASE + 0x006c) + uint32_t PMC_IMR; +#define SAM3_PMC_FSMR (SAM3_PMC_BASE + 0x0070) + uint32_t PMC_FSMR; +#define SAM3_PMC_FSPR (SAM3_PMC_BASE + 0x0074) + uint32_t PMC_FSPR; +}; + + +struct sam3_bank_private { + int probed; + // DANGER: THERE ARE DRAGONS HERE.. + // NOTE: If you add more 'ghost' pointers + // be aware that you must *manually* update + // these pointers in the function sam3_GetDetails() + // See the comment "Here there be dragons" + + // so we can find the chip we belong to + struct sam3_chip *pChip; + // so we can find the orginal bank pointer + struct flash_bank *pBank; + unsigned bank_number; + uint32_t controller_address; + uint32_t base_address; + bool present; + unsigned size_bytes; + unsigned nsectors; + unsigned sector_size; + unsigned page_size; +}; + +struct sam3_chip_details { + // THERE ARE DRAGONS HERE.. + // note: If you add pointers here + // becareful about them as they + // may need to be updated inside + // the function: "sam3_GetDetails() + // which copy/overwrites the + // 'runtime' copy of this structure + uint32_t chipid_cidr; + const char *name; + + unsigned n_gpnvms; +#define SAM3_N_NVM_BITS 3 + unsigned gpnvm[SAM3_N_NVM_BITS]; + unsigned total_flash_size; + unsigned total_sram_size; + unsigned n_banks; +#define SAM3_MAX_FLASH_BANKS 2 + // these are "initialized" from the global const data + struct sam3_bank_private bank[SAM3_MAX_FLASH_BANKS]; +}; + + +struct sam3_chip { + struct sam3_chip *next; + int probed; + + // this is "initialized" from the global const structure + struct sam3_chip_details details; + struct target *target; + struct sam3_cfg cfg; + + struct membuf *mbuf; +}; + + +struct sam3_reg_list { + uint32_t address; size_t struct_offset; const char *name; + void (*explain_func)(struct sam3_chip *pInfo); +}; + + +static struct sam3_chip *all_sam3_chips; + +static struct sam3_chip * +get_current_sam3(struct command_context *cmd_ctx) +{ + struct target *t; + static struct sam3_chip *p; + + t = get_current_target(cmd_ctx); + if (!t) { + command_print(cmd_ctx, "No current target?"); + return NULL; + } + + p = all_sam3_chips; + if (!p) { + // this should not happen + // the command is not registered until the chip is created? + command_print(cmd_ctx, "No SAM3 chips exist?"); + return NULL; + } + + while (p) { + if (p->target == t) { + return p; + } + p = p->next; + } + command_print(cmd_ctx, "Cannot find SAM3 chip?"); + return NULL; +} + + +// these are used to *initialize* the "pChip->details" structure. +static const struct sam3_chip_details all_sam3_details[] = { + { + .chipid_cidr = 0x28100960, + .name = "at91sam3u4e", + .total_flash_size = 256 * 1024, + .total_sram_size = 52 * 1024, + .n_gpnvms = 3, + .n_banks = 2, + + // System boots at address 0x0 + // gpnvm[1] = selects boot code + // if gpnvm[1] == 0 + // boot is via "SAMBA" (rom) + // else + // boot is via FLASH + // Selection is via gpnvm[2] + // endif + // + // NOTE: banks 0 & 1 switch places + // if gpnvm[2] == 0 + // Bank0 is the boot rom + // else + // Bank1 is the boot rom + // endif +// .bank[0] = { + { + { + .probed = 0, + .pChip = NULL, + .pBank = NULL, + .bank_number = 0, + .base_address = FLASH_BANK0_BASE, + .controller_address = 0x400e0800, + .present = 1, + .size_bytes = 128 * 1024, + .nsectors = 16, + .sector_size = 8192, + .page_size = 256, + }, + +// .bank[1] = { + { + .probed = 0, + .pChip = NULL, + .pBank = NULL, + .bank_number = 1, + .base_address = FLASH_BANK1_BASE, + .controller_address = 0x400e0a00, + .present = 1, + .size_bytes = 128 * 1024, + .nsectors = 16, + .sector_size = 8192, + .page_size = 256, + }, + }, + }, + + { + .chipid_cidr = 0x281a0760, + .name = "at91sam3u2e", + .total_flash_size = 128 * 1024, + .total_sram_size = 36 * 1024, + .n_gpnvms = 2, + .n_banks = 1, + + // System boots at address 0x0 + // gpnvm[1] = selects boot code + // if gpnvm[1] == 0 + // boot is via "SAMBA" (rom) + // else + // boot is via FLASH + // Selection is via gpnvm[2] + // endif +// .bank[0] = { + { + { + .probed = 0, + .pChip = NULL, + .pBank = NULL, + .bank_number = 0, + .base_address = FLASH_BANK0_BASE, + .controller_address = 0x400e0800, + .present = 1, + .size_bytes = 128 * 1024, + .nsectors = 16, + .sector_size = 8192, + .page_size = 256, + }, +// .bank[1] = { + { + .present = 0, + .probed = 0, + .bank_number = 1, + }, + }, + }, + { + .chipid_cidr = 0x28190560, + .name = "at91sam3u1e", + .total_flash_size = 64 * 1024, + .total_sram_size = 20 * 1024, + .n_gpnvms = 2, + .n_banks = 1, + + // System boots at address 0x0 + // gpnvm[1] = selects boot code + // if gpnvm[1] == 0 + // boot is via "SAMBA" (rom) + // else + // boot is via FLASH + // Selection is via gpnvm[2] + // endif + // + +// .bank[0] = { + { + { + .probed = 0, + .pChip = NULL, + .pBank = NULL, + .bank_number = 0, + .base_address = FLASH_BANK0_BASE, + .controller_address = 0x400e0800, + .present = 1, + .size_bytes = 64 * 1024, + .nsectors = 8, + .sector_size = 8192, + .page_size = 256, + }, + +// .bank[1] = { + { + .present = 0, + .probed = 0, + .bank_number = 1, + }, + }, + }, + + { + .chipid_cidr = 0x28000960, + .name = "at91sam3u4c", + .total_flash_size = 256 * 1024, + .total_sram_size = 52 * 1024, + .n_gpnvms = 3, + .n_banks = 2, + + // System boots at address 0x0 + // gpnvm[1] = selects boot code + // if gpnvm[1] == 0 + // boot is via "SAMBA" (rom) + // else + // boot is via FLASH + // Selection is via gpnvm[2] + // endif + // + // NOTE: banks 0 & 1 switch places + // if gpnvm[2] == 0 + // Bank0 is the boot rom + // else + // Bank1 is the boot rom + // endif + { + { +// .bank[0] = { + .probed = 0, + .pChip = NULL, + .pBank = NULL, + .bank_number = 0, + .base_address = FLASH_BANK0_BASE, + .controller_address = 0x400e0800, + .present = 1, + .size_bytes = 128 * 1024, + .nsectors = 16, + .sector_size = 8192, + .page_size = 256, + }, +// .bank[1] = { + { + .probed = 0, + .pChip = NULL, + .pBank = NULL, + .bank_number = 1, + .base_address = FLASH_BANK1_BASE, + .controller_address = 0x400e0a00, + .present = 1, + .size_bytes = 128 * 1024, + .nsectors = 16, + .sector_size = 8192, + .page_size = 256, + }, + }, + }, + + { + .chipid_cidr = 0x280a0760, + .name = "at91sam3u2c", + .total_flash_size = 128 * 1024, + .total_sram_size = 36 * 1024, + .n_gpnvms = 2, + .n_banks = 1, + + // System boots at address 0x0 + // gpnvm[1] = selects boot code + // if gpnvm[1] == 0 + // boot is via "SAMBA" (rom) + // else + // boot is via FLASH + // Selection is via gpnvm[2] + // endif + { +// .bank[0] = { + { + .probed = 0, + .pChip = NULL, + .pBank = NULL, + .bank_number = 0, + .base_address = FLASH_BANK0_BASE, + .controller_address = 0x400e0800, + .present = 1, + .size_bytes = 128 * 1024, + .nsectors = 16, + .sector_size = 8192, + .page_size = 256, + }, +// .bank[1] = { + { + .present = 0, + .probed = 0, + .bank_number = 1, + }, + }, + }, + { + .chipid_cidr = 0x28090560, + .name = "at91sam3u1c", + .total_flash_size = 64 * 1024, + .total_sram_size = 20 * 1024, + .n_gpnvms = 2, + .n_banks = 1, + + // System boots at address 0x0 + // gpnvm[1] = selects boot code + // if gpnvm[1] == 0 + // boot is via "SAMBA" (rom) + // else + // boot is via FLASH + // Selection is via gpnvm[2] + // endif + // + + { +// .bank[0] = { + { + .probed = 0, + .pChip = NULL, + .pBank = NULL, + .bank_number = 0, + .base_address = FLASH_BANK0_BASE, + .controller_address = 0x400e0800, + .present = 1, + .size_bytes = 64 * 1024, + .nsectors = 8, + .sector_size = 8192, + .page_size = 256, + }, +// .bank[1] = { + { + .present = 0, + .probed = 0, + .bank_number = 1, + + }, + }, + }, + + // terminate + { + .chipid_cidr = 0, + .name = NULL, + } +}; + +/* Globals above */ +/*********************************************************************** + ********************************************************************** + ********************************************************************** + ********************************************************************** + ********************************************************************** + **********************************************************************/ +/* *ATMEL* style code - from the SAM3 driver code */ + +/** + * Get the current status of the EEFC and + * the value of some status bits (LOCKE, PROGE). + * @param pPrivate - info about the bank + * @param v - result goes here + */ +static int +EFC_GetStatus(struct sam3_bank_private *pPrivate, uint32_t *v) +{ + int r; + r = target_read_u32(pPrivate->pChip->target, pPrivate->controller_address + offset_EFC_FSR, v); + LOG_DEBUG("Status: 0x%08x (lockerror: %d, cmderror: %d, ready: %d)", + (unsigned int)(*v), + ((unsigned int)((*v >> 2) & 1)), + ((unsigned int)((*v >> 1) & 1)), + ((unsigned int)((*v >> 0) & 1))); + + return r; +} + +/** + * Get the result of the last executed command. + * @param pPrivate - info about the bank + * @param v - result goes here + */ +static int +EFC_GetResult(struct sam3_bank_private *pPrivate, uint32_t *v) +{ + int r; + uint32_t rv; + r = target_read_u32(pPrivate->pChip->target, pPrivate->controller_address + offset_EFC_FRR, &rv); + if (v) { + *v = rv; + } + LOG_DEBUG("Result: 0x%08x", ((unsigned int)(rv))); + return r; +} + +static int +EFC_StartCommand(struct sam3_bank_private *pPrivate, + unsigned command, unsigned argument) +{ + uint32_t n,v; + int r; + int retry; + + retry = 0; + do_retry: + + // Check command & argument + switch (command) { + + case AT91C_EFC_FCMD_WP: + case AT91C_EFC_FCMD_WPL: + case AT91C_EFC_FCMD_EWP: + case AT91C_EFC_FCMD_EWPL: + // case AT91C_EFC_FCMD_EPL: + // case AT91C_EFC_FCMD_EPA: + case AT91C_EFC_FCMD_SLB: + case AT91C_EFC_FCMD_CLB: + n = (pPrivate->size_bytes / pPrivate->page_size); + if (argument >= n) { + LOG_ERROR("*BUG*: Embedded flash has only %u pages", (unsigned)(n)); + } + break; + + case AT91C_EFC_FCMD_SFB: + case AT91C_EFC_FCMD_CFB: + if (argument >= pPrivate->pChip->details.n_gpnvms) { + LOG_ERROR("*BUG*: Embedded flash has only %d GPNVMs", + pPrivate->pChip->details.n_gpnvms); + } + break; + + case AT91C_EFC_FCMD_GETD: + case AT91C_EFC_FCMD_EA: + case AT91C_EFC_FCMD_GLB: + case AT91C_EFC_FCMD_GFB: + case AT91C_EFC_FCMD_STUI: + case AT91C_EFC_FCMD_SPUI: + if (argument != 0) { + LOG_ERROR("Argument is meaningless for cmd: %d", command); + } + break; + default: + LOG_ERROR("Unknown command %d", command); + break; + } + + if (command == AT91C_EFC_FCMD_SPUI) { + // this is a very special situation. + // Situation (1) - error/retry - see below + // And we are being called recursively + // Situation (2) - normal, finished reading unique id + } else { + // it should be "ready" + EFC_GetStatus(pPrivate, &v); + if (v & 1) { + // then it is ready + // we go on + } else { + if (retry) { + // we have done this before + // the controller is not responding. + LOG_ERROR("flash controller(%d) is not ready! Error", pPrivate->bank_number); + return ERROR_FAIL; + } else { + retry++; + LOG_ERROR("Flash controller(%d) is not ready, attempting reset", + pPrivate->bank_number); + // we do that by issuing the *STOP* command + EFC_StartCommand(pPrivate, AT91C_EFC_FCMD_SPUI, 0); + // above is recursive, and further recursion is blocked by + // if (command == AT91C_EFC_FCMD_SPUI) above + goto do_retry; + } + } + } + + v = (0x5A << 24) | (argument << 8) | command; + LOG_DEBUG("Command: 0x%08x", ((unsigned int)(v))); + r = target_write_u32(pPrivate->pBank->target, + pPrivate->controller_address + offset_EFC_FCR, + v); + if (r != ERROR_OK) { + LOG_DEBUG("Error Write failed"); + } + return r; +} + +/** + * Performs the given command and wait until its completion (or an error). + * @param pPrivate - info about the bank + * @param command - Command to perform. + * @param argument - Optional command argument. + * @param status - put command status bits here + */ +static int +EFC_PerformCommand(struct sam3_bank_private *pPrivate, + unsigned command, + unsigned argument, + uint32_t *status) +{ + + int r; + uint32_t v; + long long ms_now, ms_end; + + // default + if (status) { + *status = 0; + } + + r = EFC_StartCommand(pPrivate, command, argument); + if (r != ERROR_OK) { + return r; + } + + ms_end = 500 + timeval_ms(); + + + do { + r = EFC_GetStatus(pPrivate, &v); + if (r != ERROR_OK) { + return r; + } + ms_now = timeval_ms(); + if (ms_now > ms_end) { + // error + LOG_ERROR("Command timeout"); + return ERROR_FAIL; + } + } + while ((v & 1) == 0) + ; + + // error bits.. + if (status) { + *status = (v & 0x6); + } + return ERROR_OK; + +} + + + + + +/** + * Read the unique ID. + * @param pPrivate - info about the bank + * The unique ID is stored in the 'pPrivate' structure. + */ +static int +FLASHD_ReadUniqueID (struct sam3_bank_private *pPrivate) +{ + int r; + uint32_t v; + int x; + // assume 0 + pPrivate->pChip->cfg.unique_id[0] = 0; + pPrivate->pChip->cfg.unique_id[1] = 0; + pPrivate->pChip->cfg.unique_id[2] = 0; + pPrivate->pChip->cfg.unique_id[3] = 0; + + LOG_DEBUG("Begin"); + r = EFC_StartCommand(pPrivate, AT91C_EFC_FCMD_STUI, 0); + if (r < 0) { + return r; + } + + for (x = 0 ; x < 4 ; x++) { + r = target_read_u32(pPrivate->pChip->target, + pPrivate->pBank->base + (x * 4), + &v); + if (r < 0) { + return r; + } + pPrivate->pChip->cfg.unique_id[x] = v; + } + + r = EFC_PerformCommand(pPrivate, AT91C_EFC_FCMD_SPUI, 0, NULL); + LOG_DEBUG("End: R=%d, id = 0x%08x, 0x%08x, 0x%08x, 0x%08x", + r, + (unsigned int)(pPrivate->pChip->cfg.unique_id[0]), + (unsigned int)(pPrivate->pChip->cfg.unique_id[1]), + (unsigned int)(pPrivate->pChip->cfg.unique_id[2]), + (unsigned int)(pPrivate->pChip->cfg.unique_id[3])); + return r; + +} + +/** + * Erases the entire flash. + * @param pPrivate - the info about the bank. + */ +static int +FLASHD_EraseEntireBank(struct sam3_bank_private *pPrivate) +{ + LOG_DEBUG("Here"); + return EFC_PerformCommand(pPrivate, AT91C_EFC_FCMD_EA, 0, NULL); +} + + + +/** + * Gets current GPNVM state. + * @param pPrivate - info about the bank. + * @param gpnvm - GPNVM bit index. + * @param puthere - result stored here. + */ +//------------------------------------------------------------------------------ +static int +FLASHD_GetGPNVM(struct sam3_bank_private *pPrivate, unsigned gpnvm, unsigned *puthere) +{ + uint32_t v; + int r; + + LOG_DEBUG("Here"); + if (pPrivate->bank_number != 0) { + LOG_ERROR("GPNVM only works with Bank0"); + return ERROR_FAIL; + } + + if (gpnvm >= pPrivate->pChip->details.n_gpnvms) { + LOG_ERROR("Invalid GPNVM %d, max: %d, ignored", + gpnvm,pPrivate->pChip->details.n_gpnvms); + return ERROR_FAIL; + } + + // Get GPNVMs status + r = EFC_PerformCommand(pPrivate, AT91C_EFC_FCMD_GFB, 0, NULL); + if (r != ERROR_OK) { + LOG_ERROR("Failed"); + return r; + } + + r = EFC_GetResult(pPrivate, &v); + + if (puthere) { + // Check if GPNVM is set + // get the bit and make it a 0/1 + *puthere = (v >> gpnvm) & 1; + } + + return r; +} + + + + +/** + * Clears the selected GPNVM bit. + * @param pPrivate info about the bank + * @param gpnvm GPNVM index. + * @returns 0 if successful; otherwise returns an error code. + */ +static int +FLASHD_ClrGPNVM(struct sam3_bank_private *pPrivate, unsigned gpnvm) +{ + int r; + unsigned v; + + LOG_DEBUG("Here"); + if (pPrivate->bank_number != 0) { + LOG_ERROR("GPNVM only works with Bank0"); + return ERROR_FAIL; + } + + if (gpnvm >= pPrivate->pChip->details.n_gpnvms) { + LOG_ERROR("Invalid GPNVM %d, max: %d, ignored", + gpnvm,pPrivate->pChip->details.n_gpnvms); + return ERROR_FAIL; + } + + r = FLASHD_GetGPNVM(pPrivate, gpnvm, &v); + if (r != ERROR_OK) { + LOG_DEBUG("Failed: %d",r); + return r; + } + r = EFC_PerformCommand(pPrivate, AT91C_EFC_FCMD_CFB, gpnvm, NULL); + LOG_DEBUG("End: %d",r); + return r; +} + + + +/** + * Sets the selected GPNVM bit. + * @param pPrivate info about the bank + * @param gpnvm GPNVM index. + */ +static int +FLASHD_SetGPNVM(struct sam3_bank_private *pPrivate, unsigned gpnvm) +{ + int r; + unsigned v; + + if (pPrivate->bank_number != 0) { + LOG_ERROR("GPNVM only works with Bank0"); + return ERROR_FAIL; + } + + if (gpnvm >= pPrivate->pChip->details.n_gpnvms) { + LOG_ERROR("Invalid GPNVM %d, max: %d, ignored", + gpnvm,pPrivate->pChip->details.n_gpnvms); + return ERROR_FAIL; + } + + r = FLASHD_GetGPNVM(pPrivate, gpnvm, &v); + if (r != ERROR_OK) { + return r; + } + if (v) { + // already set + r = ERROR_OK; + } else { + // set it + r = EFC_PerformCommand(pPrivate, AT91C_EFC_FCMD_SFB, gpnvm, NULL); + } + return r; +} + + +/** + * Returns a bit field (at most 64) of locked regions within a page. + * @param pPrivate info about the bank + * @param v where to store locked bits + */ +static int +FLASHD_GetLockBits(struct sam3_bank_private *pPrivate, uint32_t *v) +{ + int r; + LOG_DEBUG("Here"); + r = EFC_PerformCommand(pPrivate, AT91C_EFC_FCMD_GLB, 0, NULL); + if (r == ERROR_OK) { + r = EFC_GetResult(pPrivate, v); + } + LOG_DEBUG("End: %d",r); + return r; +} + + +/** + * Unlocks all the regions in the given address range. + * @param pPrivate info about the bank + * @param start_sector first sector to unlock + * @param end_sector last (inclusive) to unlock + */ + +static int +FLASHD_Unlock(struct sam3_bank_private *pPrivate, + unsigned start_sector, + unsigned end_sector) +{ + int r; + uint32_t status; + uint32_t pg; + uint32_t pages_per_sector; + + pages_per_sector = pPrivate->sector_size / pPrivate->page_size; + + /* Unlock all pages */ + while (start_sector <= end_sector) { + pg = start_sector * pages_per_sector; + + r = EFC_PerformCommand(pPrivate, AT91C_EFC_FCMD_CLB, pg, &status); + if (r != ERROR_OK) { + return r; + } + start_sector++; + } + + return ERROR_OK; +} + + +/** + * Locks regions + * @param pPrivate - info about the bank + * @param start_sector - first sector to lock + * @param end_sector - last sector (inclusive) to lock + */ +static int +FLASHD_Lock(struct sam3_bank_private *pPrivate, + unsigned start_sector, + unsigned end_sector) +{ + uint32_t status; + uint32_t pg; + uint32_t pages_per_sector; + int r; + + pages_per_sector = pPrivate->sector_size / pPrivate->page_size; + + /* Lock all pages */ + while (start_sector <= end_sector) { + pg = start_sector * pages_per_sector; + + r = EFC_PerformCommand(pPrivate, AT91C_EFC_FCMD_SLB, pg, &status); + if (r != ERROR_OK) { + return r; + } + start_sector++; + } + return ERROR_OK; +} + + +/****** END SAM3 CODE ********/ + +/* begin helpful debug code */ + +static void +sam3_sprintf(struct sam3_chip *pChip , const char *fmt, ...) +{ + va_list ap; + va_start(ap,fmt); + if (pChip->mbuf == NULL) { + return; + } + + membuf_vsprintf(pChip->mbuf, fmt, ap); + va_end(ap); +} + +// print the fieldname, the field value, in dec & hex, and return field value +static uint32_t +sam3_reg_fieldname(struct sam3_chip *pChip, + const char *regname, + uint32_t value, + unsigned shift, + unsigned width) +{ + uint32_t v; + int hwidth, dwidth; + + + // extract the field + v = value >> shift; + v = v & ((1 << width)-1); + if (width <= 16) { + hwidth = 4; + dwidth = 5; + } else { + hwidth = 8; + dwidth = 12; + } + + // show the basics + sam3_sprintf(pChip, "\t%*s: %*d [0x%0*x] ", + REG_NAME_WIDTH, regname, + dwidth, v, + hwidth, v); + return v; +} + + +static const char _unknown[] = "unknown"; +static const char * const eproc_names[] = { + _unknown, // 0 + "arm946es", // 1 + "arm7tdmi", // 2 + "cortex-m3", // 3 + "arm920t", // 4 + "arm926ejs", // 5 + _unknown, // 6 + _unknown, // 7 + _unknown, // 8 + _unknown, // 9 + _unknown, // 10 + _unknown, // 11 + _unknown, // 12 + _unknown, // 13 + _unknown, // 14 + _unknown, // 15 +}; + +#define nvpsize2 nvpsize // these two tables are identical +static const char * const nvpsize[] = { + "none", // 0 + "8K bytes", // 1 + "16K bytes", // 2 + "32K bytes", // 3 + _unknown, // 4 + "64K bytes", // 5 + _unknown, // 6 + "128K bytes", // 7 + _unknown, // 8 + "256K bytes", // 9 + "512K bytes", // 10 + _unknown, // 11 + "1024K bytes", // 12 + _unknown, // 13 + "2048K bytes", // 14 + _unknown, // 15 +}; + + +static const char * const sramsize[] = { + "48K Bytes", // 0 + "1K Bytes", // 1 + "2K Bytes", // 2 + "6K Bytes", // 3 + "112K Bytes", // 4 + "4K Bytes", // 5 + "80K Bytes", // 6 + "160K Bytes", // 7 + "8K Bytes", // 8 + "16K Bytes", // 9 + "32K Bytes", // 10 + "64K Bytes", // 11 + "128K Bytes", // 12 + "256K Bytes", // 13 + "96K Bytes", // 14 + "512K Bytes", // 15 + +}; + +static const struct archnames { unsigned value; const char *name; } archnames[] = { + { 0x19, "AT91SAM9xx Series" }, + { 0x29, "AT91SAM9XExx Series" }, + { 0x34, "AT91x34 Series" }, + { 0x37, "CAP7 Series" }, + { 0x39, "CAP9 Series" }, + { 0x3B, "CAP11 Series" }, + { 0x40, "AT91x40 Series" }, + { 0x42, "AT91x42 Series" }, + { 0x55, "AT91x55 Series" }, + { 0x60, "AT91SAM7Axx Series" }, + { 0x61, "AT91SAM7AQxx Series" }, + { 0x63, "AT91x63 Series" }, + { 0x70, "AT91SAM7Sxx Series" }, + { 0x71, "AT91SAM7XCxx Series" }, + { 0x72, "AT91SAM7SExx Series" }, + { 0x73, "AT91SAM7Lxx Series" }, + { 0x75, "AT91SAM7Xxx Series" }, + { 0x76, "AT91SAM7SLxx Series" }, + { 0x80, "ATSAM3UxC Series (100-pin version)" }, + { 0x81, "ATSAM3UxE Series (144-pin version)" }, + { 0x83, "ATSAM3AxC Series (100-pin version)" }, + { 0x84, "ATSAM3XxC Series (100-pin version)" }, + { 0x85, "ATSAM3XxE Series (144-pin version)" }, + { 0x86, "ATSAM3XxG Series (208/217-pin version)" }, + { 0x88, "ATSAM3SxA Series (48-pin version)" }, + { 0x89, "ATSAM3SxB Series (64-pin version)" }, + { 0x8A, "ATSAM3SxC Series (100-pin version)" }, + { 0x92, "AT91x92 Series" }, + { 0xF0, "AT75Cxx Series" }, + { -1, NULL }, + +}; + +static const char * const nvptype[] = { + "rom", // 0 + "romless or onchip flash", // 1 + "embedded flash memory", // 2 + "rom(nvpsiz) + embedded flash (nvpsiz2)", //3 + "sram emulating flash", // 4 + _unknown, // 5 + _unknown, // 6 + _unknown, // 7 + +}; + +static const char *_yes_or_no(uint32_t v) +{ + if (v) { + return "YES"; + } else { + return "NO"; + } +} + +static const char * const _rc_freq[] = { + "4 MHz", "8 MHz", "12 MHz", "reserved" +}; + +static void +sam3_explain_ckgr_mor(struct sam3_chip *pChip) +{ + uint32_t v; + uint32_t rcen; + + v = sam3_reg_fieldname(pChip, "MOSCXTEN", pChip->cfg.CKGR_MOR, 0, 1); + sam3_sprintf(pChip, "(main xtal enabled: %s)\n", + _yes_or_no(v)); + v = sam3_reg_fieldname(pChip, "MOSCXTBY", pChip->cfg.CKGR_MOR, 1, 1); + sam3_sprintf(pChip, "(main osc bypass: %s)\n", + _yes_or_no(v)); + rcen = sam3_reg_fieldname(pChip, "MOSCRCEN", pChip->cfg.CKGR_MOR, 2, 1); + sam3_sprintf(pChip, "(onchip RC-OSC enabled: %s)\n", + _yes_or_no(rcen)); + v = sam3_reg_fieldname(pChip, "MOSCRCF", pChip->cfg.CKGR_MOR, 4, 3); + sam3_sprintf(pChip, "(onchip RC-OSC freq: %s)\n", + _rc_freq[v]); + + pChip->cfg.rc_freq = 0; + if (rcen) { + switch (v) { + default: + pChip->cfg.rc_freq = 0; + case 0: + pChip->cfg.rc_freq = 4 * 1000 * 1000; + break; + case 1: + pChip->cfg.rc_freq = 8 * 1000 * 1000; + break; + case 2: + pChip->cfg.rc_freq = 12* 1000 * 1000; + break; + } + } + + v = sam3_reg_fieldname(pChip,"MOSCXTST", pChip->cfg.CKGR_MOR, 8, 8); + sam3_sprintf(pChip, "(startup clks, time= %f uSecs)\n", + ((float)(v * 1000000)) / ((float)(pChip->cfg.slow_freq))); + v = sam3_reg_fieldname(pChip, "MOSCSEL", pChip->cfg.CKGR_MOR, 24, 1); + sam3_sprintf(pChip, "(mainosc source: %s)\n", + v ? "external xtal" : "internal RC"); + + v = sam3_reg_fieldname(pChip,"CFDEN", pChip->cfg.CKGR_MOR, 25, 1); + sam3_sprintf(pChip, "(clock failure enabled: %s)\n", + _yes_or_no(v)); +} + + + +static void +sam3_explain_chipid_cidr(struct sam3_chip *pChip) +{ + int x; + uint32_t v; + const char *cp; + + sam3_reg_fieldname(pChip, "Version", pChip->cfg.CHIPID_CIDR, 0, 5); + sam3_sprintf(pChip,"\n"); + + v = sam3_reg_fieldname(pChip, "EPROC", pChip->cfg.CHIPID_CIDR, 5, 3); + sam3_sprintf(pChip, "%s\n", eproc_names[v]); + + v = sam3_reg_fieldname(pChip, "NVPSIZE", pChip->cfg.CHIPID_CIDR, 8, 4); + sam3_sprintf(pChip, "%s\n", nvpsize[v]); + + v = sam3_reg_fieldname(pChip, "NVPSIZE2", pChip->cfg.CHIPID_CIDR, 12, 4); + sam3_sprintf(pChip, "%s\n", nvpsize2[v]); + + v = sam3_reg_fieldname(pChip, "SRAMSIZE", pChip->cfg.CHIPID_CIDR, 16,4); + sam3_sprintf(pChip, "%s\n", sramsize[ v ]); + + v = sam3_reg_fieldname(pChip, "ARCH", pChip->cfg.CHIPID_CIDR, 20, 8); + cp = _unknown; + for (x = 0 ; archnames[x].name ; x++) { + if (v == archnames[x].value) { + cp = archnames[x].name; + break; + } + } + + sam3_sprintf(pChip, "%s\n", cp); + + v = sam3_reg_fieldname(pChip, "NVPTYP", pChip->cfg.CHIPID_CIDR, 28, 3); + sam3_sprintf(pChip, "%s\n", nvptype[ v ]); + + v = sam3_reg_fieldname(pChip, "EXTID", pChip->cfg.CHIPID_CIDR, 31, 1); + sam3_sprintf(pChip, "(exists: %s)\n", _yes_or_no(v)); +} + +static void +sam3_explain_ckgr_mcfr(struct sam3_chip *pChip) +{ + uint32_t v; + + + v = sam3_reg_fieldname(pChip, "MAINFRDY", pChip->cfg.CKGR_MCFR, 16, 1); + sam3_sprintf(pChip, "(main ready: %s)\n", _yes_or_no(v)); + + v = sam3_reg_fieldname(pChip, "MAINF", pChip->cfg.CKGR_MCFR, 0, 16); + + v = (v * pChip->cfg.slow_freq) / 16; + pChip->cfg.mainosc_freq = v; + + sam3_sprintf(pChip, "(%3.03f Mhz (%d.%03dkhz slowclk)\n", + _tomhz(v), + pChip->cfg.slow_freq / 1000, + pChip->cfg.slow_freq % 1000); + +} + +static void +sam3_explain_ckgr_plla(struct sam3_chip *pChip) +{ + uint32_t mula,diva; + + diva = sam3_reg_fieldname(pChip, "DIVA", pChip->cfg.CKGR_PLLAR, 0, 8); + sam3_sprintf(pChip,"\n"); + mula = sam3_reg_fieldname(pChip, "MULA", pChip->cfg.CKGR_PLLAR, 16, 11); + sam3_sprintf(pChip,"\n"); + pChip->cfg.plla_freq = 0; + if (mula == 0) { + sam3_sprintf(pChip,"\tPLLA Freq: (Disabled,mula = 0)\n"); + } else if (diva == 0) { + sam3_sprintf(pChip,"\tPLLA Freq: (Disabled,diva = 0)\n"); + } else if (diva == 1) { + pChip->cfg.plla_freq = (pChip->cfg.mainosc_freq * (mula + 1)); + sam3_sprintf(pChip,"\tPLLA Freq: %3.03f MHz\n", + _tomhz(pChip->cfg.plla_freq)); + } +} + + +static void +sam3_explain_mckr(struct sam3_chip *pChip) +{ + uint32_t css, pres, fin = 0; + int pdiv = 0; + const char *cp = NULL; + + css = sam3_reg_fieldname(pChip, "CSS", pChip->cfg.PMC_MCKR, 0, 2); + switch (css & 3) { + case 0: + fin = pChip->cfg.slow_freq; + cp = "slowclk"; + break; + case 1: + fin = pChip->cfg.mainosc_freq; + cp = "mainosc"; + break; + case 2: + fin = pChip->cfg.plla_freq; + cp = "plla"; + break; + case 3: + if (pChip->cfg.CKGR_UCKR & (1 << 16)) { + fin = 480 * 1000 * 1000; + cp = "upll"; + } else { + fin = 0; + cp = "upll (*ERROR* UPLL is disabled)"; + } + break; + default: + assert(0); + break; + } + + sam3_sprintf(pChip, "%s (%3.03f Mhz)\n", + cp, + _tomhz(fin)); + pres = sam3_reg_fieldname(pChip, "PRES", pChip->cfg.PMC_MCKR, 4, 3); + switch (pres & 0x07) { + case 0: + pdiv = 1; + cp = "selected clock"; + case 1: + pdiv = 2; + cp = "clock/2"; + break; + case 2: + pdiv = 4; + cp = "clock/4"; + break; + case 3: + pdiv = 8; + cp = "clock/8"; + break; + case 4: + pdiv = 16; + cp = "clock/16"; + break; + case 5: + pdiv = 32; + cp = "clock/32"; + break; + case 6: + pdiv = 64; + cp = "clock/64"; + break; + case 7: + pdiv = 6; + cp = "clock/6"; + break; + default: + assert(0); + break; + } + sam3_sprintf(pChip, "(%s)\n", cp); + fin = fin / pdiv; + // sam3 has a *SINGLE* clock - + // other at91 series parts have divisors for these. + pChip->cfg.cpu_freq = fin; + pChip->cfg.mclk_freq = fin; + pChip->cfg.fclk_freq = fin; + sam3_sprintf(pChip, "\t\tResult CPU Freq: %3.03f\n", + _tomhz(fin)); +} + +#if 0 +static struct sam3_chip * +target2sam3(struct target *pTarget) +{ + struct sam3_chip *pChip; + + if (pTarget == NULL) { + return NULL; + } + + pChip = all_sam3_chips; + while (pChip) { + if (pChip->target == pTarget) { + break; // return below + } else { + pChip = pChip->next; + } + } + return pChip; +} +#endif + +static uint32_t * +sam3_get_reg_ptr(struct sam3_cfg *pCfg, const struct sam3_reg_list *pList) +{ + // this function exists to help + // keep funky offsetof() errors + // and casting from causing bugs + + // By using prototypes - we can detect what would + // be casting errors. + + return ((uint32_t *)(((char *)(pCfg)) + pList->struct_offset)); +} + + +#define SAM3_ENTRY(NAME, FUNC) { .address = SAM3_ ## NAME, .struct_offset = offsetof(struct sam3_cfg, NAME), #NAME, FUNC } +static const struct sam3_reg_list sam3_all_regs[] = { + SAM3_ENTRY(CKGR_MOR , sam3_explain_ckgr_mor), + SAM3_ENTRY(CKGR_MCFR , sam3_explain_ckgr_mcfr), + SAM3_ENTRY(CKGR_PLLAR , sam3_explain_ckgr_plla), + SAM3_ENTRY(CKGR_UCKR , NULL), + SAM3_ENTRY(PMC_FSMR , NULL), + SAM3_ENTRY(PMC_FSPR , NULL), + SAM3_ENTRY(PMC_IMR , NULL), + SAM3_ENTRY(PMC_MCKR , sam3_explain_mckr), + SAM3_ENTRY(PMC_PCK0 , NULL), + SAM3_ENTRY(PMC_PCK1 , NULL), + SAM3_ENTRY(PMC_PCK2 , NULL), + SAM3_ENTRY(PMC_PCSR , NULL), + SAM3_ENTRY(PMC_SCSR , NULL), + SAM3_ENTRY(PMC_SR , NULL), + SAM3_ENTRY(CHIPID_CIDR , sam3_explain_chipid_cidr), + SAM3_ENTRY(CHIPID_EXID , NULL), + SAM3_ENTRY(SUPC_CR, NULL), + + // TERMINATE THE LIST + { .name = NULL } +}; +#undef SAM3_ENTRY + + + + +static struct sam3_bank_private * +get_sam3_bank_private(struct flash_bank *bank) +{ + return (struct sam3_bank_private *)(bank->driver_priv); +} + +/** + * Given a pointer to where it goes in the structure, + * determine the register name, address from the all registers table. + */ +static const struct sam3_reg_list * +sam3_GetReg(struct sam3_chip *pChip, uint32_t *goes_here) +{ + const struct sam3_reg_list *pReg; + + pReg = &(sam3_all_regs[0]); + while (pReg->name) { + uint32_t *pPossible; + + // calculate where this one go.. + // it is "possibly" this register. + + pPossible = ((uint32_t *)(((char *)(&(pChip->cfg))) + pReg->struct_offset)); + + // well? Is it this register + if (pPossible == goes_here) { + // Jump for joy! + return pReg; + } + + // next... + pReg++; + } + // This is *TOTAL*PANIC* - we are totally screwed. + LOG_ERROR("INVALID SAM3 REGISTER"); + return NULL; +} + + +static int +sam3_ReadThisReg(struct sam3_chip *pChip, uint32_t *goes_here) +{ + const struct sam3_reg_list *pReg; + int r; + + pReg = sam3_GetReg(pChip, goes_here); + if (!pReg) { + return ERROR_FAIL; + } + + r = target_read_u32(pChip->target, pReg->address, goes_here); + if (r != ERROR_OK) { + LOG_ERROR("Cannot read SAM3 register: %s @ 0x%08x, Err: %d\n", + pReg->name, (unsigned)(pReg->address), r); + } + return r; +} + + + +static int +sam3_ReadAllRegs(struct sam3_chip *pChip) +{ + int r; + const struct sam3_reg_list *pReg; + + pReg = &(sam3_all_regs[0]); + while (pReg->name) { + r = sam3_ReadThisReg(pChip, + sam3_get_reg_ptr(&(pChip->cfg), pReg)); + if (r != ERROR_OK) { + LOG_ERROR("Cannot read SAM3 registere: %s @ 0x%08x, Error: %d\n", + pReg->name, ((unsigned)(pReg->address)), r); + return r; + } + + pReg++; + } + + return ERROR_OK; +} + + +static int +sam3_GetInfo(struct sam3_chip *pChip) +{ + const struct sam3_reg_list *pReg; + uint32_t regval; + + membuf_reset(pChip->mbuf); + + + pReg = &(sam3_all_regs[0]); + while (pReg->name) { + // display all regs + LOG_DEBUG("Start: %s", pReg->name); + regval = *sam3_get_reg_ptr(&(pChip->cfg), pReg); + sam3_sprintf(pChip, "%*s: [0x%08x] -> 0x%08x\n", + REG_NAME_WIDTH, + pReg->name, + pReg->address, + regval); + if (pReg->explain_func) { + (*(pReg->explain_func))(pChip); + } + LOG_DEBUG("End: %s", pReg->name); + pReg++; + } + sam3_sprintf(pChip," rc-osc: %3.03f MHz\n", _tomhz(pChip->cfg.rc_freq)); + sam3_sprintf(pChip," mainosc: %3.03f MHz\n", _tomhz(pChip->cfg.mainosc_freq)); + sam3_sprintf(pChip," plla: %3.03f MHz\n", _tomhz(pChip->cfg.plla_freq)); + sam3_sprintf(pChip," cpu-freq: %3.03f MHz\n", _tomhz(pChip->cfg.cpu_freq)); + sam3_sprintf(pChip,"mclk-freq: %3.03f MHz\n", _tomhz(pChip->cfg.mclk_freq)); + + + sam3_sprintf(pChip, " UniqueId: 0x%08x 0x%08x 0x%08x 0x%08x\n", + pChip->cfg.unique_id[0], + pChip->cfg.unique_id[1], + pChip->cfg.unique_id[2], + pChip->cfg.unique_id[3]); + + + return ERROR_OK; +} + + +static int +sam3_erase_check(struct flash_bank *bank) +{ + int x; + + LOG_DEBUG("Here"); + if (bank->target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + if (0 == bank->num_sectors) { + LOG_ERROR("Target: not supported/not probed\n"); + return ERROR_FAIL; + } + + LOG_INFO("sam3 - supports auto-erase, erase_check ignored"); + for (x = 0 ; x < bank->num_sectors ; x++) { + bank->sectors[x].is_erased = 1; + } + + LOG_DEBUG("Done"); + return ERROR_OK; +} + +static int +sam3_protect_check(struct flash_bank *bank) +{ + int r; + uint32_t v=0; + unsigned x; + struct sam3_bank_private *pPrivate; + + LOG_DEBUG("Begin"); + if (bank->target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + pPrivate = get_sam3_bank_private(bank); + if (!pPrivate) { + LOG_ERROR("no private for this bank?"); + return ERROR_FAIL; + } + if (!(pPrivate->probed)) { + return ERROR_FLASH_BANK_NOT_PROBED; + } + + r = FLASHD_GetLockBits(pPrivate , &v); + if (r != ERROR_OK) { + LOG_DEBUG("Failed: %d",r); + return r; + } + + for (x = 0 ; x < pPrivate->nsectors ; x++) { + bank->sectors[x].is_protected = (!!(v & (1 << x))); + } + LOG_DEBUG("Done"); + return ERROR_OK; +} + +FLASH_BANK_COMMAND_HANDLER(sam3_flash_bank_command) +{ + struct sam3_chip *pChip; + + pChip = all_sam3_chips; + + // is this an existing chip? + while (pChip) { + if (pChip->target == bank->target) { + break; + } + pChip = pChip->next; + } + + if (!pChip) { + // this is a *NEW* chip + pChip = calloc(1, sizeof(struct sam3_chip)); + if (!pChip) { + LOG_ERROR("NO RAM!"); + return ERROR_FAIL; + } + pChip->target = bank->target; + // insert at head + pChip->next = all_sam3_chips; + all_sam3_chips = pChip; + pChip->target = bank->target; + // assumption is this runs at 32khz + pChip->cfg.slow_freq = 32768; + pChip->probed = 0; + pChip->mbuf = membuf_new(); + if (!(pChip->mbuf)) { + LOG_ERROR("no memory"); + return ERROR_FAIL; + } + } + + switch (bank->base) { + default: + LOG_ERROR("Address 0x%08x invalid bank address (try 0x%08x or 0x%08x)", + ((unsigned int)(bank->base)), + ((unsigned int)(FLASH_BANK0_BASE)), + ((unsigned int)(FLASH_BANK1_BASE))); + return ERROR_FAIL; + break; + case FLASH_BANK0_BASE: + bank->driver_priv = &(pChip->details.bank[0]); + bank->bank_number = 0; + pChip->details.bank[0].pChip = pChip; + pChip->details.bank[0].pBank = bank; + break; + case FLASH_BANK1_BASE: + bank->driver_priv = &(pChip->details.bank[1]); + bank->bank_number = 1; + pChip->details.bank[1].pChip = pChip; + pChip->details.bank[1].pBank = bank; + break; + } + + // we initialize after probing. + return ERROR_OK; +} + +static int +sam3_GetDetails(struct sam3_bank_private *pPrivate) +{ + const struct sam3_chip_details *pDetails; + struct sam3_chip *pChip; + void *vp; + struct flash_bank *saved_banks[SAM3_MAX_FLASH_BANKS]; + + unsigned x; + const char *cp; + + LOG_DEBUG("Begin"); + pDetails = all_sam3_details; + while (pDetails->name) { + if (pDetails->chipid_cidr == pPrivate->pChip->cfg.CHIPID_CIDR) { + break; + } else { + pDetails++; + } + } + if (pDetails->name == NULL) { + LOG_ERROR("SAM3 ChipID 0x%08x not found in table (perhaps you can this chip?)", + (unsigned int)(pPrivate->pChip->cfg.CHIPID_CIDR)); + // Help the victim, print details about the chip + membuf_reset(pPrivate->pChip->mbuf); + membuf_sprintf(pPrivate->pChip->mbuf, + "SAM3 CHIPID_CIDR: 0x%08x decodes as follows\n", + pPrivate->pChip->cfg.CHIPID_CIDR); + sam3_explain_chipid_cidr(pPrivate->pChip); + cp = membuf_strtok(pPrivate->pChip->mbuf, "\n", &vp); + while (cp) { + LOG_INFO("%s", cp); + cp = membuf_strtok(NULL, "\n", &vp); + } + return ERROR_FAIL; + } + + // DANGER: THERE ARE DRAGONS HERE + + // get our pChip - it is going + // to be over-written shortly + pChip = pPrivate->pChip; + + // Note that, in reality: + // + // pPrivate = &(pChip->details.bank[0]) + // or pPrivate = &(pChip->details.bank[1]) + // + + // save the "bank" pointers + for (x = 0 ; x < SAM3_MAX_FLASH_BANKS ; x++) { + saved_banks[ x ] = pChip->details.bank[x].pBank; + } + + // Overwrite the "details" structure. + memcpy(&(pPrivate->pChip->details), + pDetails, + sizeof(pPrivate->pChip->details)); + + // now fix the ghosted pointers + for (x = 0 ; x < SAM3_MAX_FLASH_BANKS ; x++) { + pChip->details.bank[x].pChip = pChip; + pChip->details.bank[x].pBank = saved_banks[x]; + } + + // update the *BANK*SIZE* + + LOG_DEBUG("End"); + return ERROR_OK; +} + + + +static int +_sam3_probe(struct flash_bank *bank, int noise) +{ + unsigned x; + int r; + struct sam3_bank_private *pPrivate; + + + LOG_DEBUG("Begin: Bank: %d, Noise: %d", bank->bank_number, noise); + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + pPrivate = get_sam3_bank_private(bank); + if (!pPrivate) { + LOG_ERROR("Invalid/unknown bank number\n"); + return ERROR_FAIL; + } + + r = sam3_ReadAllRegs(pPrivate->pChip); + if (r != ERROR_OK) { + return r; + } + + + LOG_DEBUG("Here"); + r = sam3_GetInfo(pPrivate->pChip); + if (r != ERROR_OK) { + return r; + } + if (!(pPrivate->pChip->probed)) { + pPrivate->pChip->probed = 1; + LOG_DEBUG("Here"); + r = sam3_GetDetails(pPrivate); + if (r != ERROR_OK) { + return r; + } + } + + // update the flash bank size + for (x = 0 ; x < SAM3_MAX_FLASH_BANKS ; x++) { + if (bank->base == pPrivate->pChip->details.bank[0].base_address) { + bank->size = pPrivate->pChip->details.bank[0].size_bytes; + break; + } + } + + if (bank->sectors == NULL) { + bank->sectors = calloc(pPrivate->nsectors, (sizeof((bank->sectors)[0]))); + if (bank->sectors == NULL) { + LOG_ERROR("No memory!"); + return ERROR_FAIL; + } + bank->num_sectors = pPrivate->nsectors; + + for (x = 0 ; ((int)(x)) < bank->num_sectors ; x++) { + bank->sectors[x].size = pPrivate->sector_size; + bank->sectors[x].offset = x * (pPrivate->sector_size); + // mark as unknown + bank->sectors[x].is_erased = -1; + bank->sectors[x].is_protected = -1; + } + } + + pPrivate->probed = 1; + + r = sam3_protect_check(bank); + if (r != ERROR_OK) { + return r; + } + + LOG_DEBUG("Bank = %d, nbanks = %d", + pPrivate->bank_number , pPrivate->pChip->details.n_banks); + if ((pPrivate->bank_number + 1) == pPrivate->pChip->details.n_banks) { + // read unique id, + // it appears to be associated with the *last* flash bank. + FLASHD_ReadUniqueID(pPrivate); + } + + return r; +} + +static int +sam3_probe(struct flash_bank *bank) +{ + return _sam3_probe(bank, 1); +} + +static int +sam3_auto_probe(struct flash_bank *bank) +{ + return _sam3_probe(bank, 0); +} + + + +static int +sam3_erase(struct flash_bank *bank, int first, int last) +{ + struct sam3_bank_private *pPrivate; + int r; + + LOG_DEBUG("Here"); + if (bank->target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + r = sam3_auto_probe(bank); + if (r != ERROR_OK) { + LOG_DEBUG("Here,r=%d",r); + return r; + } + + pPrivate = get_sam3_bank_private(bank); + if (!(pPrivate->probed)) { + return ERROR_FLASH_BANK_NOT_PROBED; + } + + if ((first == 0) && ((last + 1)== ((int)(pPrivate->nsectors)))) { + // whole chip + LOG_DEBUG("Here"); + return FLASHD_EraseEntireBank(pPrivate); + } + LOG_INFO("sam3 auto-erases while programing (request ignored)"); + return ERROR_OK; +} + +static int +sam3_protect(struct flash_bank *bank, int set, int first, int last) +{ + struct sam3_bank_private *pPrivate; + int r; + + LOG_DEBUG("Here"); + if (bank->target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + pPrivate = get_sam3_bank_private(bank); + if (!(pPrivate->probed)) { + return ERROR_FLASH_BANK_NOT_PROBED; + } + + if (set) { + r = FLASHD_Lock(pPrivate, (unsigned)(first), (unsigned)(last)); + } else { + r = FLASHD_Unlock(pPrivate, (unsigned)(first), (unsigned)(last)); + } + LOG_DEBUG("End: r=%d",r); + + return r; + +} + + +static int +sam3_info(struct flash_bank *bank, char *buf, int buf_size) +{ + if (bank->target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + buf[ 0 ] = 0; + return ERROR_OK; +} + +static int +sam3_page_read(struct sam3_bank_private *pPrivate, unsigned pagenum, uint8_t *buf) +{ + uint32_t adr; + int r; + + adr = pagenum * pPrivate->page_size; + adr += adr + pPrivate->base_address; + + r = target_read_memory(pPrivate->pChip->target, + adr, + 4, /* THIS*MUST*BE* in 32bit values */ + pPrivate->page_size / 4, + buf); + if (r != ERROR_OK) { + LOG_ERROR("SAM3: Flash program failed to read page phys address: 0x%08x", (unsigned int)(adr)); + } + return r; +} + +// The code below is basically this: +// compiled with +// arm-none-eabi-gcc -mthumb -mcpu = cortex-m3 -O9 -S ./foobar.c -o foobar.s +// +// Only the *CPU* can write to the flash buffer. +// the DAP cannot... so - we download this 28byte thing +// Run the algorithm - (below) +// to program the device +// +// ======================================== +// #include +// +// struct foo { +// uint32_t *dst; +// const uint32_t *src; +// int n; +// volatile uint32_t *base; +// uint32_t cmd; +// }; +// +// +// uint32_t sam3_function(struct foo *p) +// { +// volatile uint32_t *v; +// uint32_t *d; +// const uint32_t *s; +// int n; +// uint32_t r; +// +// d = p->dst; +// s = p->src; +// n = p->n; +// +// do { +// *d++ = *s++; +// } while (--n) +// ; +// +// v = p->base; +// +// v[ 1 ] = p->cmd; +// do { +// r = v[8/4]; +// } while (!(r&1)) +// ; +// return r; +// } +// ======================================== + + + +static const uint8_t +sam3_page_write_opcodes[] = { + // 24 0000 0446 mov r4, r0 + 0x04,0x46, + // 25 0002 6168 ldr r1, [r4, #4] + 0x61,0x68, + // 26 0004 0068 ldr r0, [r0, #0] + 0x00,0x68, + // 27 0006 A268 ldr r2, [r4, #8] + 0xa2,0x68, + // 28 @ lr needed for prologue + // 29 .L2: + // 30 0008 51F8043B ldr r3, [r1], #4 + 0x51,0xf8,0x04,0x3b, + // 31 000c 12F1FF32 adds r2, r2, #-1 + 0x12,0xf1,0xff,0x32, + // 32 0010 40F8043B str r3, [r0], #4 + 0x40,0xf8,0x04,0x3b, + // 33 0014 F8D1 bne .L2 + 0xf8,0xd1, + // 34 0016 E268 ldr r2, [r4, #12] + 0xe2,0x68, + // 35 0018 2369 ldr r3, [r4, #16] + 0x23,0x69, + // 36 001a 5360 str r3, [r2, #4] + 0x53,0x60, + // 37 001c 0832 adds r2, r2, #8 + 0x08,0x32, + // 38 .L4: + // 39 001e 1068 ldr r0, [r2, #0] + 0x10,0x68, + // 40 0020 10F0010F tst r0, #1 + 0x10,0xf0,0x01,0x0f, + // 41 0024 FBD0 beq .L4 + 0xfb,0xd0, + // 42 .done: + // 43 0026 FEE7 b .done + 0xfe,0xe7 +}; + + +static int +sam3_page_write(struct sam3_bank_private *pPrivate, unsigned pagenum, uint8_t *buf) +{ + uint32_t adr; + uint32_t status; + int r; + + adr = pagenum * pPrivate->page_size; + adr += (adr + pPrivate->base_address); + + LOG_DEBUG("Wr Page %u @ phys address: 0x%08x", pagenum, (unsigned int)(adr)); + r = target_write_memory(pPrivate->pChip->target, + adr, + 4, /* THIS*MUST*BE* in 32bit values */ + pPrivate->page_size / 4, + buf); + if (r != ERROR_OK) { + LOG_ERROR("SAM3: Failed to write (buffer) page at phys address 0x%08x", (unsigned int)(adr)); + return r; + } + + r = EFC_PerformCommand(pPrivate, + // send Erase & Write Page + AT91C_EFC_FCMD_EWP, + pagenum, + &status); + + if (r != ERROR_OK) { + LOG_ERROR("SAM3: Error performing Erase & Write page @ phys address 0x%08x", (unsigned int)(adr)); + } + if (status & (1 << 2)) { + LOG_ERROR("SAM3: Page @ Phys address 0x%08x is locked", (unsigned int)(adr)); + return ERROR_FAIL; + } + if (status & (1 << 1)) { + LOG_ERROR("SAM3: Flash Command error @phys address 0x%08x", (unsigned int)(adr)); + return ERROR_FAIL; + } + return ERROR_OK; +} + + + + + +static int +sam3_write(struct flash_bank *bank, + uint8_t *buffer, + uint32_t offset, + uint32_t count) +{ + int n; + unsigned page_cur; + unsigned page_end; + int r; + unsigned page_offset; + struct sam3_bank_private *pPrivate; + uint8_t *pagebuffer; + + // incase we bail further below, set this to null + pagebuffer = NULL; + + // ignore dumb requests + if (count == 0) { + r = ERROR_OK; + goto done; + } + + if (bank->target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + r = ERROR_TARGET_NOT_HALTED; + goto done; + } + + pPrivate = get_sam3_bank_private(bank); + if (!(pPrivate->probed)) { + r = ERROR_FLASH_BANK_NOT_PROBED; + goto done; + } + + + if ((offset + count) > pPrivate->size_bytes) { + LOG_ERROR("Flash write error - past end of bank"); + LOG_ERROR(" offset: 0x%08x, count 0x%08x, BankEnd: 0x%08x", + (unsigned int)(offset), + (unsigned int)(count), + (unsigned int)(pPrivate->size_bytes)); + r = ERROR_FAIL; + goto done; + } + + pagebuffer = malloc(pPrivate->page_size); + if( !pagebuffer ){ + LOG_ERROR("No memory for %d Byte page buffer", (int)(pPrivate->page_size)); + r = ERROR_FAIL; + goto done; + } + + // what page do we start & end in? + page_cur = offset / pPrivate->page_size; + page_end = (offset + count - 1) / pPrivate->page_size; + + LOG_DEBUG("Offset: 0x%08x, Count: 0x%08x", (unsigned int)(offset), (unsigned int)(count)); + LOG_DEBUG("Page start: %d, Page End: %d", (int)(page_cur), (int)(page_end)); + + // Special case: all one page + // + // Otherwise: + // (1) non-aligned start + // (2) body pages + // (3) non-aligned end. + + // Handle special case - all one page. + if (page_cur == page_end) { + LOG_DEBUG("Special case, all in one page"); + r = sam3_page_read(pPrivate, page_cur, pagebuffer); + if (r != ERROR_OK) { + goto done; + } + + page_offset = (offset & (pPrivate->page_size-1)); + memcpy(pagebuffer + page_offset, + buffer, + count); + + r = sam3_page_write(pPrivate, page_cur, pagebuffer); + if (r != ERROR_OK) { + goto done; + } + r = ERROR_OK; + goto done; + } + + // non-aligned start + page_offset = offset & (pPrivate->page_size - 1); + if (page_offset) { + LOG_DEBUG("Not-Aligned start"); + // read the partial + r = sam3_page_read(pPrivate, page_cur, pagebuffer); + if (r != ERROR_OK) { + goto done; + } + + // over-write with new data + n = (pPrivate->page_size - page_offset); + memcpy(pagebuffer + page_offset, + buffer, + n); + + r = sam3_page_write(pPrivate, page_cur, pagebuffer); + if (r != ERROR_OK) { + goto done; + } + + count -= n; + offset += n; + buffer += n; + page_cur++; + } + + // intermediate large pages + // also - the final *terminal* + // if that terminal page is a full page + LOG_DEBUG("Full Page Loop: cur=%d, end=%d, count = 0x%08x", + (int)page_cur, (int)page_end, (unsigned int)(count)); + + while ((page_cur < page_end) && + (count >= pPrivate->page_size)) { + r = sam3_page_write(pPrivate, page_cur, buffer); + if (r != ERROR_OK) { + goto done; + } + count -= pPrivate->page_size; + buffer += pPrivate->page_size; + page_cur += 1; + } + + // terminal partial page? + if (count) { + LOG_DEBUG("Terminal partial page, count = 0x%08x", (unsigned int)(count)); + // we have a partial page + r = sam3_page_read(pPrivate, page_cur, pagebuffer); + if (r != ERROR_OK) { + goto done; + } + // data goes at start + memcpy(pagebuffer, buffer, count); + r = sam3_page_write(pPrivate, page_cur, pagebuffer); + if (r != ERROR_OK) { + goto done; + } + buffer += count; + count -= count; + } + LOG_DEBUG("Done!"); + r = ERROR_OK; + done: + if( pagebuffer ){ + free(pagebuffer); + } + return r; +} + +COMMAND_HANDLER(sam3_handle_info_command) +{ + struct sam3_chip *pChip; + void *vp; + const char *cp; + unsigned x; + int r; + + pChip = get_current_sam3(CMD_CTX); + if (!pChip) { + return ERROR_OK; + } + + r = 0; + + // bank0 must exist before we can do anything + if (pChip->details.bank[0].pBank == NULL) { + x = 0; + need_define: + command_print(CMD_CTX, + "Please define bank %d via command: flash bank %s ... ", + x, + at91sam3_flash.name); + return ERROR_FAIL; + } + + // if bank 0 is not probed, then probe it + if (!(pChip->details.bank[0].probed)) { + r = sam3_auto_probe(pChip->details.bank[0].pBank); + if (r != ERROR_OK) { + return ERROR_FAIL; + } + } + // above garentees the "chip details" structure is valid + // and thus, bank private areas are valid + // and we have a SAM3 chip, what a concept! + + + // auto-probe other banks, 0 done above + for (x = 1 ; x < SAM3_MAX_FLASH_BANKS ; x++) { + // skip banks not present + if (!(pChip->details.bank[x].present)) { + continue; + } + + if (pChip->details.bank[x].pBank == NULL) { + goto need_define; + } + + if (pChip->details.bank[x].probed) { + continue; + } + + r = sam3_auto_probe(pChip->details.bank[x].pBank); + if (r != ERROR_OK) { + return r; + } + } + + + r = sam3_GetInfo(pChip); + if (r != ERROR_OK) { + LOG_DEBUG("Sam3Info, Failed %d\n",r); + return r; + } + + + // print results + cp = membuf_strtok(pChip->mbuf, "\n", &vp); + while (cp) { + command_print(CMD_CTX,"%s", cp); + cp = membuf_strtok(NULL, "\n", &vp); + } + return ERROR_OK; +} + +COMMAND_HANDLER(sam3_handle_gpnvm_command) +{ + unsigned x,v; + int r,who; + struct sam3_chip *pChip; + + pChip = get_current_sam3(CMD_CTX); + if (!pChip) { + return ERROR_OK; + } + + if (pChip->target->state != TARGET_HALTED) { + LOG_ERROR("sam3 - target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + + if (pChip->details.bank[0].pBank == NULL) { + command_print(CMD_CTX, "Bank0 must be defined first via: flash bank %s ...", + at91sam3_flash.name); + return ERROR_FAIL; + } + if (!pChip->details.bank[0].probed) { + r = sam3_auto_probe(pChip->details.bank[0].pBank); + if (r != ERROR_OK) { + return r; + } + } + + + switch (CMD_ARGC) { + default: + command_print(CMD_CTX,"Too many parameters\n"); + return ERROR_COMMAND_SYNTAX_ERROR; + break; + case 0: + who = -1; + goto showall; + break; + case 1: + who = -1; + break; + case 2: + if ((0 == strcmp(CMD_ARGV[0], "show")) && (0 == strcmp(CMD_ARGV[1], "all"))) { + who = -1; + } else { + uint32_t v32; + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], v32); + who = v32; + } + break; + } + + if (0 == strcmp("show", CMD_ARGV[0])) { + if (who == -1) { + showall: + r = ERROR_OK; + for (x = 0 ; x < pChip->details.n_gpnvms ; x++) { + r = FLASHD_GetGPNVM(&(pChip->details.bank[0]), x, &v); + if (r != ERROR_OK) { + break; + } + command_print(CMD_CTX, "sam3-gpnvm%u: %u", x, v); + } + return r; + } + if ((who >= 0) && (((unsigned)(who)) < pChip->details.n_gpnvms)) { + r = FLASHD_GetGPNVM(&(pChip->details.bank[0]), who, &v); + command_print(CMD_CTX, "sam3-gpnvm%u: %u", who, v); + return r; + } else { + command_print(CMD_CTX, "sam3-gpnvm invalid GPNVM: %u", who); + return ERROR_COMMAND_SYNTAX_ERROR; + } + } + + if (who == -1) { + command_print(CMD_CTX, "Missing GPNVM number"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + if (0 == strcmp("set", CMD_ARGV[0])) { + r = FLASHD_SetGPNVM(&(pChip->details.bank[0]), who); + } else if ((0 == strcmp("clr", CMD_ARGV[0])) || + (0 == strcmp("clear", CMD_ARGV[0]))) { // quietly accept both + r = FLASHD_ClrGPNVM(&(pChip->details.bank[0]), who); + } else { + command_print(CMD_CTX, "Unkown command: %s", CMD_ARGV[0]); + r = ERROR_COMMAND_SYNTAX_ERROR; + } + return r; +} + +COMMAND_HANDLER(sam3_handle_slowclk_command) +{ + struct sam3_chip *pChip; + + pChip = get_current_sam3(CMD_CTX); + if (!pChip) { + return ERROR_OK; + } + + + switch (CMD_ARGC) { + case 0: + // show + break; + case 1: + { + // set + uint32_t v; + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], v); + if (v > 200000) { + // absurd slow clock of 200Khz? + command_print(CMD_CTX,"Absurd/illegal slow clock freq: %d\n", (int)(v)); + return ERROR_COMMAND_SYNTAX_ERROR; + } + pChip->cfg.slow_freq = v; + break; + } + default: + // error + command_print(CMD_CTX,"Too many parameters"); + return ERROR_COMMAND_SYNTAX_ERROR; + break; + } + command_print(CMD_CTX, "Slowclk freq: %d.%03dkhz", + (int)(pChip->cfg.slow_freq/ 1000), + (int)(pChip->cfg.slow_freq% 1000)); + return ERROR_OK; +} + +static const struct command_registration at91sam3_exec_command_handlers[] = { + { + .name = "gpnvm", + .handler = &sam3_handle_gpnvm_command, + .mode = COMMAND_EXEC, + .usage = "[(set|clear) []]", + .help = "Without arguments, shows the gpnvm register; " + "otherwise, sets or clear the specified bit.", + }, + { + .name = "info", + .handler = &sam3_handle_info_command, + .mode = COMMAND_EXEC, + .help = "print information about the current sam3 chip", + }, + { + .name = "slowclk", + .handler = &sam3_handle_slowclk_command, + .mode = COMMAND_EXEC, + .usage = "", + .help = "set the slowclock frequency (default 32768hz)", + }, + COMMAND_REGISTRATION_DONE +}; +static const struct command_registration at91sam3_command_handlers[] = { + { + .name = "at91sam3", + .mode = COMMAND_ANY, + .help = "at91sam3 flash command group", + .chain = at91sam3_exec_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +struct flash_driver at91sam3_flash = { + .name = "at91sam3", + .commands = at91sam3_command_handlers, + .flash_bank_command = &sam3_flash_bank_command, + .erase = &sam3_erase, + .protect = &sam3_protect, + .write = &sam3_write, + .probe = &sam3_probe, + .auto_probe = &sam3_auto_probe, + .erase_check = &sam3_erase_check, + .protect_check = &sam3_protect_check, + .info = &sam3_info, + }; diff --git a/src/flash/nor/at91sam3.h b/src/flash/nor/at91sam3.h new file mode 100644 index 00000000..4fa7f467 --- /dev/null +++ b/src/flash/nor/at91sam3.h @@ -0,0 +1,23 @@ +/*************************************************************************** + * Copyright (C) 2009 by Duane Ellis * + * openocd@duaneellis.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + + +// nothing to do here other then export this. +extern struct flash_driver at91sam3_flash; diff --git a/src/flash/nor/at91sam7.c b/src/flash/nor/at91sam7.c new file mode 100644 index 00000000..f9b87bab --- /dev/null +++ b/src/flash/nor/at91sam7.c @@ -0,0 +1,1213 @@ +/*************************************************************************** + * Copyright (C) 2006 by Magnus Lundin * + * lundin@mlu.mine.nu * + * * + * Copyright (C) 2008 by Gheorghe Guran (atlas) * + * * + * 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. * +****************************************************************************/ + +/*************************************************************************** +* +* New flash setup command: +* +* flash bank +* [ +* +* +* ] +* +* - MUST be used if clock is from external source, +* CAN be used if main oscillator frequency is known (recommended) +* Examples: +* ==== RECOMMENDED (covers clock speed) ============ +* flash bank at91sam7 0x00100000 0 0 4 $_TARGETNAME AT91SAM7XC256 1 16 64 256 3 25000 +* (if auto-detect fails; provides clock spec) +* flash bank at91sam7 0 0 0 0 $_TARGETNAME 0 0 0 0 0 0 25000 +* (auto-detect everything except the clock) +* ==== NOT RECOMMENDED !!! (clock speed is not configured) ==== +* flash bank at91sam7 0x00100000 0 0 4 $_TARGETNAME AT91SAM7XC256 1 16 64 256 3 0 +* (if auto-detect fails) +* flash bank at91sam7 0 0 0 0 $_TARGETNAME +* (old style, auto-detect everything) +****************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "at91sam7.h" +#include "binarybuffer.h" + +static int at91sam7_protect_check(struct flash_bank *bank); +static int at91sam7_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count); + +static uint32_t at91sam7_get_flash_status(struct target *target, int bank_number); +static void at91sam7_set_flash_mode(struct flash_bank *bank, int mode); +static uint32_t at91sam7_wait_status_busy(struct flash_bank *bank, uint32_t waitbits, int timeout); +static int at91sam7_flash_command(struct flash_bank *bank, uint8_t cmd, uint16_t pagen); + +static uint32_t MC_FMR[4] = { 0xFFFFFF60, 0xFFFFFF70, 0xFFFFFF80, 0xFFFFFF90 }; +static uint32_t MC_FCR[4] = { 0xFFFFFF64, 0xFFFFFF74, 0xFFFFFF84, 0xFFFFFF94 }; +static uint32_t MC_FSR[4] = { 0xFFFFFF68, 0xFFFFFF78, 0xFFFFFF88, 0xFFFFFF98 }; + +static char * EPROC[8]= {"Unknown","ARM946-E","ARM7TDMI","Unknown","ARM920T","ARM926EJ-S","Unknown","Unknown"}; + +#if 0 +static 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 */ +}; +#endif + + +static uint32_t at91sam7_get_flash_status(struct target *target, int bank_number) +{ + uint32_t fsr; + target_read_u32(target, MC_FSR[bank_number], &fsr); + + return fsr; +} + +/* Read clock configuration and set at91sam7_info->mck_freq */ +static void at91sam7_read_clock_info(struct flash_bank *bank) +{ + struct at91sam7_flash_bank *at91sam7_info = bank->driver_priv; + struct target *target = bank->target; + uint32_t mckr, mcfr, pllr, mor; + unsigned long tmp = 0, mainfreq; + + /* Read Clock Generator Main Oscillator Register */ + target_read_u32(target, CKGR_MOR, &mor); + /* Read Clock Generator Main Clock Frequency 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; + at91sam7_info->mck_freq = 0; + switch (mckr & PMC_MCKR_CSS) + { + case 0: /* Slow Clock */ + at91sam7_info->mck_valid = 1; + tmp = RC_FREQ; + break; + + case 1: /* Main Clock */ + if ((mcfr & CKGR_MCFR_MAINRDY) && + (at91sam7_info->ext_freq == 0)) + { + at91sam7_info->mck_valid = 1; + tmp = RC_FREQ / 16ul * (mcfr & 0xffff); + } + else if (at91sam7_info->ext_freq != 0) + { + at91sam7_info->mck_valid = 1; + tmp = at91sam7_info->ext_freq; + } + break; + + case 2: /* Reserved */ + break; + + case 3: /* PLL Clock */ + if ((mcfr & CKGR_MCFR_MAINRDY) && + (at91sam7_info->ext_freq == 0)) + { + 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); + } + else if ((at91sam7_info->ext_freq != 0) && + ((pllr&CKGR_PLLR_DIV) != 0)) + { + at91sam7_info->mck_valid = 1; + tmp = at91sam7_info->ext_freq / (pllr&CKGR_PLLR_DIV)* + (((pllr & CKGR_PLLR_MUL) >> 16) + 1); + } + break; + } + + /* Prescaler adjust */ + if ((((mckr & PMC_MCKR_PRES) >> 2) == 7) || (tmp == 0)) + { + at91sam7_info->mck_valid = 0; + at91sam7_info->mck_freq = 0; + } + else if (((mckr & PMC_MCKR_PRES) >> 2) != 0) + at91sam7_info->mck_freq = tmp >> ((mckr & PMC_MCKR_PRES) >> 2); + else + at91sam7_info->mck_freq = tmp; +} + +/* Setup the timimg registers for nvbits or normal flash */ +static void at91sam7_set_flash_mode(struct flash_bank *bank, int mode) +{ + uint32_t fmr, fmcn = 0, fws = 0; + struct at91sam7_flash_bank *at91sam7_info = bank->driver_priv; + struct target *target = bank->target; + + if (mode && (mode != at91sam7_info->flashmode)) + { + /* 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/1000000ul)+ + (at91sam7_info->mck_freq/2000000ul) + 1; + } + + /* hard overclocking */ + if (fmcn > 0xFF) + fmcn = 0xFF; + + /* 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; + + LOG_DEBUG("fmcn[%i]: %i", bank->bank_number, (int)(fmcn)); + fmr = fmcn << 16 | fws << 8; + target_write_u32(target, MC_FMR[bank->bank_number], fmr); + } + + at91sam7_info->flashmode = mode; +} + +static uint32_t at91sam7_wait_status_busy(struct flash_bank *bank, uint32_t waitbits, int timeout) +{ + uint32_t status; + + while ((!((status = at91sam7_get_flash_status(bank->target, bank->bank_number)) & waitbits)) && (timeout-- > 0)) + { + LOG_DEBUG("status[%i]: 0x%" PRIx32 "", (int)bank->bank_number, status); + alive_sleep(1); + } + + LOG_DEBUG("status[%i]: 0x%" PRIx32 "", bank->bank_number, status); + + if (status & 0x0C) + { + LOG_ERROR("status register: 0x%" PRIx32 "", status); + if (status & 0x4) + LOG_ERROR("Lock Error Bit Detected, Operation Abort"); + if (status & 0x8) + LOG_ERROR("Invalid command and/or bad keyword, Operation Abort"); + if (status & 0x10) + LOG_ERROR("Security Bit Set, Operation Abort"); + } + + return status; +} + +/* Send one command to the AT91SAM flash controller */ +static int at91sam7_flash_command(struct flash_bank *bank, uint8_t cmd, uint16_t pagen) +{ + uint32_t fcr; + struct at91sam7_flash_bank *at91sam7_info = bank->driver_priv; + struct target *target = bank->target; + + fcr = (0x5A << 24) | ((pagen&0x3FF) << 8) | cmd; + target_write_u32(target, MC_FCR[bank->bank_number], fcr); + LOG_DEBUG("Flash command: 0x%" PRIx32 ", flash bank: %i, page number: %u", fcr, bank->bank_number + 1, 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, MC_FSR_EOL, 10)&0x0C) + { + return ERROR_FLASH_OPERATION_FAILED; + } + return ERROR_OK; + } + + if (at91sam7_wait_status_busy(bank, 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 */ +static int at91sam7_read_part_info(struct flash_bank *bank) +{ + struct flash_bank *t_bank = bank; + struct at91sam7_flash_bank *at91sam7_info; + struct target *target = t_bank->target; + + uint16_t bnk, sec; + uint16_t arch; + uint32_t cidr; + uint8_t banks_num = 0; + uint16_t num_nvmbits = 0; + uint16_t sectors_num = 0; + uint16_t pages_per_sector = 0; + uint16_t page_size = 0; + uint32_t ext_freq; + uint32_t bank_size; + uint32_t base_address = 0; + char *target_name = "Unknown"; + + at91sam7_info = t_bank->driver_priv; + + if (at91sam7_info->cidr != 0) + { + /* flash already configured, update clock and check for protected sectors */ + struct flash_bank *fb = bank; + t_bank = fb; + + while (t_bank) + { + /* re-calculate master clock frequency */ + at91sam7_read_clock_info(t_bank); + + /* no timming */ + at91sam7_set_flash_mode(t_bank, FMR_TIMING_NONE); + + /* check protect state */ + at91sam7_protect_check(t_bank); + + t_bank = fb->next; + fb = t_bank; + } + + return ERROR_OK; + } + + /* Read and parse chip identification register */ + target_read_u32(target, DBGU_CIDR, &cidr); + if (cidr == 0) + { + LOG_WARNING("Cannot identify target as an AT91SAM"); + return ERROR_FLASH_OPERATION_FAILED; + } + + if (at91sam7_info->flash_autodetection == 0) + { + /* banks and sectors are already created, based on data from input file */ + struct flash_bank *fb = bank; + t_bank = fb; + while (t_bank) + { + at91sam7_info = t_bank->driver_priv; + + 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; + + /* calculate master clock frequency */ + at91sam7_read_clock_info(t_bank); + + /* no timming */ + at91sam7_set_flash_mode(t_bank, FMR_TIMING_NONE); + + /* check protect state */ + at91sam7_protect_check(t_bank); + + t_bank = fb->next; + fb = t_bank; + } + + return ERROR_OK; + } + + arch = (cidr >> 20)&0x00FF; + + /* check flash size */ + switch ((cidr >> 8)&0x000F) + { + case FLASH_SIZE_8KB: + break; + + case FLASH_SIZE_16KB: + banks_num = 1; + sectors_num = 8; + pages_per_sector = 32; + page_size = 64; + base_address = 0x00100000; + if (arch == 0x70) + { + num_nvmbits = 2; + target_name = "AT91SAM7S161/16"; + } + break; + + case FLASH_SIZE_32KB: + banks_num = 1; + sectors_num = 8; + pages_per_sector = 32; + page_size = 128; + base_address = 0x00100000; + if (arch == 0x70) + { + num_nvmbits = 2; + target_name = "AT91SAM7S321/32"; + } + if (arch == 0x72) + { + num_nvmbits = 3; + target_name = "AT91SAM7SE32"; + } + break; + + case FLASH_SIZE_64KB: + banks_num = 1; + sectors_num = 16; + pages_per_sector = 32; + page_size = 128; + base_address = 0x00100000; + if (arch == 0x70) + { + num_nvmbits = 2; + target_name = "AT91SAM7S64"; + } + break; + + case FLASH_SIZE_128KB: + banks_num = 1; + sectors_num = 8; + pages_per_sector = 64; + page_size = 256; + base_address = 0x00100000; + if (arch == 0x70) + { + num_nvmbits = 2; + target_name = "AT91SAM7S128"; + } + if (arch == 0x71) + { + num_nvmbits = 3; + target_name = "AT91SAM7XC128"; + } + if (arch == 0x72) + { + num_nvmbits = 3; + target_name = "AT91SAM7SE128"; + } + if (arch == 0x75) + { + num_nvmbits = 3; + target_name = "AT91SAM7X128"; + } + break; + + case FLASH_SIZE_256KB: + banks_num = 1; + sectors_num = 16; + pages_per_sector = 64; + page_size = 256; + base_address = 0x00100000; + if (arch == 0x60) + { + num_nvmbits = 3; + target_name = "AT91SAM7A3"; + } + if (arch == 0x70) + { + num_nvmbits = 2; + target_name = "AT91SAM7S256"; + } + if (arch == 0x71) + { + num_nvmbits = 3; + target_name = "AT91SAM7XC256"; + } + if (arch == 0x72) + { + num_nvmbits = 3; + target_name = "AT91SAM7SE256"; + } + if (arch == 0x75) + { + num_nvmbits = 3; + target_name = "AT91SAM7X256"; + } + break; + + case FLASH_SIZE_512KB: + banks_num = 2; + sectors_num = 16; + pages_per_sector = 64; + page_size = 256; + base_address = 0x00100000; + if (arch == 0x70) + { + num_nvmbits = 2; + target_name = "AT91SAM7S512"; + } + if (arch == 0x71) + { + num_nvmbits = 3; + target_name = "AT91SAM7XC512"; + } + if (arch == 0x72) + { + num_nvmbits = 3; + target_name = "AT91SAM7SE512"; + } + if (arch == 0x75) + { + num_nvmbits = 3; + target_name = "AT91SAM7X512"; + } + break; + + case FLASH_SIZE_1024KB: + break; + + case FLASH_SIZE_2048KB: + break; + } + + if (strcmp(target_name, "Unknown") == 0) + { + LOG_ERROR("Target autodetection failed! Please specify target parameters in configuration file"); + return ERROR_FLASH_OPERATION_FAILED; + } + + ext_freq = at91sam7_info->ext_freq; + + /* calculate bank size */ + bank_size = sectors_num * pages_per_sector * page_size; + + for (bnk = 0; bnk < banks_num; bnk++) + { + if (bnk > 0) + { + /* create a new flash bank element */ + struct flash_bank *fb = malloc(sizeof(struct flash_bank)); + fb->target = target; + fb->driver = bank->driver; + fb->driver_priv = malloc(sizeof(struct at91sam7_flash_bank)); + fb->next = NULL; + + /* link created bank in 'flash_banks' list and redirect t_bank */ + t_bank->next = fb; + t_bank = fb; + } + + t_bank->bank_number = bnk; + t_bank->base = base_address + bnk * bank_size; + t_bank->size = bank_size; + t_bank->chip_width = 0; + t_bank->bus_width = 4; + t_bank->num_sectors = sectors_num; + + /* allocate sectors */ + t_bank->sectors = malloc(sectors_num * sizeof(struct flash_sector)); + for (sec = 0; sec < sectors_num; sec++) + { + t_bank->sectors[sec].offset = sec * pages_per_sector * page_size; + t_bank->sectors[sec].size = pages_per_sector * page_size; + t_bank->sectors[sec].is_erased = -1; + t_bank->sectors[sec].is_protected = -1; + } + + at91sam7_info = t_bank->driver_priv; + + 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; + + at91sam7_info->target_name = target_name; + at91sam7_info->flashmode = 0; + at91sam7_info->ext_freq = ext_freq; + at91sam7_info->num_nvmbits = num_nvmbits; + at91sam7_info->num_nvmbits_on = 0; + at91sam7_info->pagesize = page_size; + at91sam7_info->pages_per_sector = pages_per_sector; + + /* calculate master clock frequency */ + at91sam7_read_clock_info(t_bank); + + /* no timming */ + at91sam7_set_flash_mode(t_bank, FMR_TIMING_NONE); + + /* check protect state */ + at91sam7_protect_check(t_bank); + } + + LOG_DEBUG("nvptyp: 0x%3.3x, arch: 0x%4.4x", at91sam7_info->cidr_nvptyp, at91sam7_info->cidr_arch); + + return ERROR_OK; +} + +static int at91sam7_erase_check(struct flash_bank *bank) +{ + struct target *target = bank->target; + uint16_t retval; + uint32_t blank; + uint16_t fast_check; + uint8_t *buffer; + uint16_t nSector; + uint16_t nByte; + + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* Configure the flash controller timing */ + at91sam7_read_clock_info(bank); + at91sam7_set_flash_mode(bank, FMR_TIMING_FLASH); + + fast_check = 1; + for (nSector = 0; nSector < bank->num_sectors; nSector++) + { + retval = target_blank_check_memory(target, bank->base + bank->sectors[nSector].offset, + bank->sectors[nSector].size, &blank); + if (retval != ERROR_OK) + { + fast_check = 0; + break; + } + if (blank == 0xFF) + bank->sectors[nSector].is_erased = 1; + else + bank->sectors[nSector].is_erased = 0; + } + + if (fast_check) + { + return ERROR_OK; + } + + LOG_USER("Running slow fallback erase check - add working memory"); + + buffer = malloc(bank->sectors[0].size); + for (nSector = 0; nSector < bank->num_sectors; nSector++) + { + bank->sectors[nSector].is_erased = 1; + retval = target_read_memory(target, bank->base + bank->sectors[nSector].offset, 4, + bank->sectors[nSector].size/4, buffer); + if (retval != ERROR_OK) + return retval; + + for (nByte = 0; nByte < bank->sectors[nSector].size; nByte++) + { + if (buffer[nByte] != 0xFF) + { + bank->sectors[nSector].is_erased = 0; + break; + } + } + } + free(buffer); + + return ERROR_OK; +} + +static int at91sam7_protect_check(struct flash_bank *bank) +{ + uint8_t lock_pos, gpnvm_pos; + uint32_t status; + + struct at91sam7_flash_bank *at91sam7_info = bank->driver_priv; + + if (at91sam7_info->cidr == 0) + { + return ERROR_FLASH_BANK_NOT_PROBED; + } + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + status = at91sam7_get_flash_status(bank->target, bank->bank_number); + at91sam7_info->lockbits = (status >> 16); + + at91sam7_info->num_lockbits_on = 0; + for (lock_pos = 0; lock_pos < bank->num_sectors; lock_pos++) + { + if (((status >> (16 + lock_pos))&(0x0001)) == 1) + { + at91sam7_info->num_lockbits_on++; + bank->sectors[lock_pos].is_protected = 1; + } + else + bank->sectors[lock_pos].is_protected = 0; + } + + /* GPNVM and SECURITY bits apply only for MC_FSR of EFC0 */ + status = at91sam7_get_flash_status(bank->target, 0); + + at91sam7_info->securitybit = (status >> 4)&0x01; + at91sam7_info->nvmbits = (status >> 8)&0xFF; + + at91sam7_info->num_nvmbits_on = 0; + for (gpnvm_pos = 0; gpnvm_pos < at91sam7_info->num_nvmbits; gpnvm_pos++) + { + if (((status >> (8 + gpnvm_pos))&(0x01)) == 1) + { + at91sam7_info->num_nvmbits_on++; + } + } + + return ERROR_OK; +} + +FLASH_BANK_COMMAND_HANDLER(at91sam7_flash_bank_command) +{ + struct flash_bank *t_bank = bank; + struct at91sam7_flash_bank *at91sam7_info; + struct target *target = t_bank->target; + + uint32_t base_address; + uint32_t bank_size; + uint32_t ext_freq = 0; + + int chip_width; + int bus_width; + int banks_num; + int num_sectors; + + uint16_t pages_per_sector; + uint16_t page_size; + uint16_t num_nvmbits; + + char *target_name; + + int bnk, sec; + + at91sam7_info = malloc(sizeof(struct at91sam7_flash_bank)); + t_bank->driver_priv = at91sam7_info; + + /* part wasn't probed for info yet */ + at91sam7_info->cidr = 0; + at91sam7_info->flashmode = 0; + at91sam7_info->ext_freq = 0; + at91sam7_info->flash_autodetection = 0; + + if (CMD_ARGC < 13) + { + at91sam7_info->flash_autodetection = 1; + return ERROR_OK; + } + + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], base_address); + + COMMAND_PARSE_NUMBER(int, CMD_ARGV[3], chip_width); + COMMAND_PARSE_NUMBER(int, CMD_ARGV[4], bus_width); + + COMMAND_PARSE_NUMBER(int, CMD_ARGV[8], banks_num); + COMMAND_PARSE_NUMBER(int, CMD_ARGV[9], num_sectors); + COMMAND_PARSE_NUMBER(u16, CMD_ARGV[10], pages_per_sector); + COMMAND_PARSE_NUMBER(u16, CMD_ARGV[11], page_size); + COMMAND_PARSE_NUMBER(u16, CMD_ARGV[12], num_nvmbits); + + if (CMD_ARGC == 14) { + unsigned long freq; + COMMAND_PARSE_NUMBER(ulong, CMD_ARGV[13], freq); + ext_freq = freq * 1000; + at91sam7_info->ext_freq = ext_freq; + } + + if ((bus_width == 0) || (banks_num == 0) || (num_sectors == 0) || + (pages_per_sector == 0) || (page_size == 0) || (num_nvmbits == 0)) + { + at91sam7_info->flash_autodetection = 1; + return ERROR_OK; + } + + target_name = calloc(strlen(CMD_ARGV[7]) + 1, sizeof(char)); + strcpy(target_name, CMD_ARGV[7]); + + /* calculate bank size */ + bank_size = num_sectors * pages_per_sector * page_size; + + for (bnk = 0; bnk < banks_num; bnk++) + { + if (bnk > 0) + { + /* create a new bank element */ + struct flash_bank *fb = malloc(sizeof(struct flash_bank)); + fb->target = target; + fb->driver = bank->driver; + fb->driver_priv = malloc(sizeof(struct at91sam7_flash_bank)); + fb->next = NULL; + + /* link created bank in 'flash_banks' list and redirect t_bank */ + t_bank->next = fb; + t_bank = fb; + } + + t_bank->bank_number = bnk; + t_bank->base = base_address + bnk * bank_size; + t_bank->size = bank_size; + t_bank->chip_width = chip_width; + t_bank->bus_width = bus_width; + t_bank->num_sectors = num_sectors; + + /* allocate sectors */ + t_bank->sectors = malloc(num_sectors * sizeof(struct flash_sector)); + for (sec = 0; sec < num_sectors; sec++) + { + t_bank->sectors[sec].offset = sec * pages_per_sector * page_size; + t_bank->sectors[sec].size = pages_per_sector * page_size; + t_bank->sectors[sec].is_erased = -1; + t_bank->sectors[sec].is_protected = -1; + } + + at91sam7_info = t_bank->driver_priv; + + at91sam7_info->target_name = target_name; + at91sam7_info->flashmode = 0; + at91sam7_info->ext_freq = ext_freq; + at91sam7_info->num_nvmbits = num_nvmbits; + at91sam7_info->num_nvmbits_on = 0; + at91sam7_info->pagesize = page_size; + at91sam7_info->pages_per_sector = pages_per_sector; + } + + return ERROR_OK; +} + +static int at91sam7_erase(struct flash_bank *bank, int first, int last) +{ + struct at91sam7_flash_bank *at91sam7_info = bank->driver_priv; + int sec; + uint32_t nbytes, pos; + uint8_t *buffer; + uint8_t erase_all; + + if (at91sam7_info->cidr == 0) + { + return ERROR_FLASH_BANK_NOT_PROBED; + } + + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if ((first < 0) || (last < first) || (last >= bank->num_sectors)) + { + return ERROR_FLASH_SECTOR_INVALID; + } + + erase_all = 0; + if ((first == 0) && (last == (bank->num_sectors-1))) + { + erase_all = 1; + } + + /* Configure the flash controller timing */ + at91sam7_read_clock_info(bank); + at91sam7_set_flash_mode(bank, FMR_TIMING_FLASH); + + if (erase_all) + { + if (at91sam7_flash_command(bank, EA, 0) != ERROR_OK) + { + return ERROR_FLASH_OPERATION_FAILED; + } + } + else + { + /* allocate and clean buffer */ + nbytes = (last - first + 1) * bank->sectors[first].size; + buffer = malloc(nbytes * sizeof(uint8_t)); + for (pos = 0; pos < nbytes; pos++) + { + buffer[pos] = 0xFF; + } + + if (at91sam7_write(bank, buffer, bank->sectors[first].offset, nbytes) != ERROR_OK) + { + return ERROR_FLASH_OPERATION_FAILED; + } + + free(buffer); + } + + /* mark erased sectors */ + for (sec = first; sec <= last; sec++) + { + bank->sectors[sec].is_erased = 1; + } + + return ERROR_OK; +} + +static int at91sam7_protect(struct flash_bank *bank, int set, int first, int last) +{ + uint32_t cmd; + int sector; + uint32_t pagen; + + struct at91sam7_flash_bank *at91sam7_info = bank->driver_priv; + + if (at91sam7_info->cidr == 0) + { + return ERROR_FLASH_BANK_NOT_PROBED; + } + + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if ((first < 0) || (last < first) || (last >= bank->num_sectors)) + { + return ERROR_FLASH_SECTOR_INVALID; + } + + /* Configure the flash controller timing */ + at91sam7_read_clock_info(bank); + at91sam7_set_flash_mode(bank, FMR_TIMING_NVBITS); + + for (sector = first; sector <= last; sector++) + { + if (set) + cmd = SLB; + else + cmd = CLB; + + /* if we lock a page from one sector then entire sector will be locked, also, + * if we unlock a page from a locked sector, entire sector will be unlocked */ + pagen = sector * at91sam7_info->pages_per_sector; + + if (at91sam7_flash_command(bank, cmd, pagen) != ERROR_OK) + { + return ERROR_FLASH_OPERATION_FAILED; + } + } + + at91sam7_protect_check(bank); + + return ERROR_OK; +} + +static int at91sam7_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) +{ + int retval; + struct at91sam7_flash_bank *at91sam7_info = bank->driver_priv; + struct target *target = bank->target; + uint32_t dst_min_alignment, wcount, bytes_remaining = count; + uint32_t first_page, last_page, pagen, buffer_pos; + + if (at91sam7_info->cidr == 0) + { + return ERROR_FLASH_BANK_NOT_PROBED; + } + + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (offset + count > bank->size) + return ERROR_FLASH_DST_OUT_OF_BANK; + + dst_min_alignment = at91sam7_info->pagesize; + + if (offset % dst_min_alignment) + { + LOG_WARNING("offset 0x%" PRIx32 " breaks required alignment 0x%" PRIx32 "", 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 = DIV_ROUND_UP(offset + count, dst_min_alignment); + + LOG_DEBUG("first_page: %i, last_page: %i, count %i", (int)first_page, (int)last_page, (int)count); + + /* Configure the flash controller timing */ + at91sam7_read_clock_info(bank); + at91sam7_set_flash_mode(bank, FMR_TIMING_FLASH); + + 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 = DIV_ROUND_UP(count,4); + if ((retval = target_write_memory(target, bank->base + pagen*dst_min_alignment, 4, wcount, buffer + buffer_pos)) != ERROR_OK) + { + return retval; + } + + /* Send Write Page command to Flash Controller */ + if (at91sam7_flash_command(bank, WP, pagen) != ERROR_OK) + { + return ERROR_FLASH_OPERATION_FAILED; + } + LOG_DEBUG("Write flash bank:%i page number:%" PRIi32 "", bank->bank_number, pagen); + } + + return ERROR_OK; +} + +static int at91sam7_probe(struct flash_bank *bank) +{ + /* we can't probe on an at91sam7 + * if this is an at91sam7, it has the configured flash */ + int retval; + + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + retval = at91sam7_read_part_info(bank); + if (retval != ERROR_OK) + return retval; + + return ERROR_OK; +} + +static int at91sam7_info(struct flash_bank *bank, char *buf, int buf_size) +{ + int printed; + struct at91sam7_flash_bank *at91sam7_info = bank->driver_priv; + + if (at91sam7_info->cidr == 0) + { + return ERROR_FLASH_BANK_NOT_PROBED; + } + + printed = snprintf(buf, buf_size, + "\n at91sam7 driver information: Chip is %s\n", + at91sam7_info->target_name); + + buf += printed; + buf_size -= printed; + + printed = snprintf(buf, + buf_size, + " Cidr: 0x%8.8" PRIx32 " | Arch: 0x%4.4x | Eproc: %s | Version: 0x%3.3x | Flashsize: 0x%8.8" PRIx32 "\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): %u KHz | External clock: %u KHz\n", + (unsigned)(at91sam7_info->mck_freq / 1000), (unsigned)(at91sam7_info->ext_freq / 1000)); + + buf += printed; + buf_size -= printed; + + printed = snprintf(buf, buf_size, + " Pagesize: %i bytes | Lockbits(%i): %i 0x%4.4x | Pages in lock region: %i \n", + at91sam7_info->pagesize, bank->num_sectors, at91sam7_info->num_lockbits_on, + at91sam7_info->lockbits, at91sam7_info->pages_per_sector*at91sam7_info->num_lockbits_on); + + buf += printed; + buf_size -= printed; + + printed = snprintf(buf, buf_size, + " Securitybit: %i | Nvmbits(%i): %i 0x%1.1x\n", + at91sam7_info->securitybit, at91sam7_info->num_nvmbits, + at91sam7_info->num_nvmbits_on, at91sam7_info->nvmbits); + + buf += printed; + buf_size -= printed; + + return ERROR_OK; +} + +/* +* On AT91SAM7S: When the gpnvm bits are set with +* > at91sam7 gpnvm 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. +*/ +COMMAND_HANDLER(at91sam7_handle_gpnvm_command) +{ + struct flash_bank *bank; + int bit; + uint8_t flashcmd; + uint32_t status; + struct at91sam7_flash_bank *at91sam7_info; + int retval; + + if (CMD_ARGC != 2) + { + command_print(CMD_CTX, "at91sam7 gpnvm "); + return ERROR_OK; + } + + bank = get_flash_bank_by_num_noprobe(0); + if (bank == NULL) + { + return ERROR_FLASH_BANK_INVALID; + } + if (strcmp(bank->driver->name, "at91sam7")) + { + command_print(CMD_CTX, "not an at91sam7 flash bank '%s'", CMD_ARGV[0]); + return ERROR_FLASH_BANK_INVALID; + } + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("target has to be halted to perform flash operation"); + return ERROR_TARGET_NOT_HALTED; + } + + if (strcmp(CMD_ARGV[1], "set") == 0) + { + flashcmd = SGPB; + } + else if (strcmp(CMD_ARGV[1], "clear") == 0) + { + flashcmd = CGPB; + } + else + { + return ERROR_COMMAND_SYNTAX_ERROR; + } + + at91sam7_info = bank->driver_priv; + if (at91sam7_info->cidr == 0) + { + retval = at91sam7_read_part_info(bank); + if (retval != ERROR_OK) + { + return retval; + } + } + + COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], bit); + if ((bit < 0) || (bit >= at91sam7_info->num_nvmbits)) + { + command_print(CMD_CTX, "gpnvm bit '#%s' is out of bounds for target %s", CMD_ARGV[0], at91sam7_info->target_name); + return ERROR_OK; + } + + /* Configure the flash controller timing */ + at91sam7_read_clock_info(bank); + at91sam7_set_flash_mode(bank, FMR_TIMING_NVBITS); + + if (at91sam7_flash_command(bank, flashcmd, bit) != ERROR_OK) + { + return ERROR_FLASH_OPERATION_FAILED; + } + + /* GPNVM and SECURITY bits apply only for MC_FSR of EFC0 */ + status = at91sam7_get_flash_status(bank->target, 0); + LOG_DEBUG("at91sam7_handle_gpnvm_command: cmd 0x%x, value %d, status 0x%" PRIx32 " \n", flashcmd, bit, status); + + /* check protect state */ + at91sam7_protect_check(bank); + + return ERROR_OK; +} + +static const struct command_registration at91sam7_exec_command_handlers[] = { + { + .name = "gpnvm", + .handler = &at91sam7_handle_gpnvm_command, + .mode = COMMAND_EXEC, + .usage = "gpnvm set | clear, " + "set or clear one gpnvm bit", + }, + COMMAND_REGISTRATION_DONE +}; +static const struct command_registration at91sam7_command_handlers[] = { + { + .name = "at91sam7", + .mode = COMMAND_ANY, + .help = "at91sam7 flash command group", + .chain = at91sam7_exec_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +struct flash_driver at91sam7_flash = { + .name = "at91sam7", + .commands = at91sam7_command_handlers, + .flash_bank_command = &at91sam7_flash_bank_command, + .erase = &at91sam7_erase, + .protect = &at91sam7_protect, + .write = &at91sam7_write, + .probe = &at91sam7_probe, + .auto_probe = &at91sam7_probe, + .erase_check = &at91sam7_erase_check, + .protect_check = &at91sam7_protect_check, + .info = &at91sam7_info, + }; diff --git a/src/flash/nor/at91sam7.h b/src/flash/nor/at91sam7.h new file mode 100644 index 00000000..45106869 --- /dev/null +++ b/src/flash/nor/at91sam7.h @@ -0,0 +1,118 @@ +/*************************************************************************** + * Copyright (C) 2006 by Magnus Lundin * + * lundin@mlu.mine.nu * + * * + * Copyright (C) 2006 by Gheorghe Guran (atlas) * + * * + * 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 AT91SAM7_H +#define AT91SAM7_H + +#include "flash.h" + +struct at91sam7_flash_bank +{ + /* chip id register */ + uint32_t cidr; + uint16_t cidr_ext; + uint16_t cidr_nvptyp; + uint16_t cidr_arch; + uint16_t cidr_sramsiz; + uint16_t cidr_nvpsiz; + uint16_t cidr_nvpsiz2; + uint16_t cidr_eproc; + uint16_t cidr_version; + char *target_name; + + /* flash auto-detection */ + uint8_t flash_autodetection; + + /* flash geometry */ + uint16_t pages_per_sector; + uint16_t pagesize; + uint16_t pages_in_lockregion; + + /* nv memory bits */ + uint16_t num_lockbits_on; + uint16_t lockbits; + uint16_t num_nvmbits; + uint16_t num_nvmbits_on; + uint16_t nvmbits; + uint8_t securitybit; + + /* 0: not init + * 1: fmcn for nvbits (1uS) + * 2: fmcn for flash (1.5uS) */ + uint8_t flashmode; + + /* main clock status */ + uint8_t mck_valid; + uint32_t mck_freq; + + /* external clock frequency */ + uint32_t ext_freq; + +}; + + +/* AT91SAM7 control registers */ +#define DBGU_CIDR 0xFFFFF240 +#define CKGR_MCFR 0xFFFFFC24 +#define CKGR_MOR 0xFFFFFC20 +#define CKGR_MCFR_MAINRDY 0x10000 +#define CKGR_PLLR 0xFFFFFC2c +#define CKGR_PLLR_DIV 0xff +#define CKGR_PLLR_MUL 0x07ff0000 +#define PMC_MCKR 0xFFFFFC30 +#define PMC_MCKR_CSS 0x03 +#define PMC_MCKR_PRES 0x1c + +/* Flash Controller Commands */ +#define WP 0x01 +#define SLB 0x02 +#define WPL 0x03 +#define CLB 0x04 +#define EA 0x08 +#define SGPB 0x0B +#define CGPB 0x0D +#define SSB 0x0F + +/* MC_FSR bit definitions */ +#define MC_FSR_FRDY 1 +#define MC_FSR_EOL 2 + +/* AT91SAM7 constants */ +#define RC_FREQ 32000 + +/* Flash timing modes */ +#define FMR_TIMING_NONE 0 +#define FMR_TIMING_NVBITS 1 +#define FMR_TIMING_FLASH 2 + +/* Flash size constants */ +#define FLASH_SIZE_8KB 1 +#define FLASH_SIZE_16KB 2 +#define FLASH_SIZE_32KB 3 +#define FLASH_SIZE_64KB 5 +#define FLASH_SIZE_128KB 7 +#define FLASH_SIZE_256KB 9 +#define FLASH_SIZE_512KB 10 +#define FLASH_SIZE_1024KB 12 +#define FLASH_SIZE_2048KB 14 + +#endif /* AT91SAM7_H */ diff --git a/src/flash/nor/avrf.c b/src/flash/nor/avrf.c new file mode 100644 index 00000000..687dd4b5 --- /dev/null +++ b/src/flash/nor/avrf.c @@ -0,0 +1,483 @@ +/*************************************************************************** + * Copyright (C) 2009 by Simon Qian * + * SimonQian@SimonQian.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "avrf.h" +#include "avrt.h" +#include "flash.h" + + +/* AVR_JTAG_Instructions */ +#define AVR_JTAG_INS_LEN 4 +// Public Instructions: +#define AVR_JTAG_INS_EXTEST 0x00 +#define AVR_JTAG_INS_IDCODE 0x01 +#define AVR_JTAG_INS_SAMPLE_PRELOAD 0x02 +#define AVR_JTAG_INS_BYPASS 0x0F +// AVR Specified Public Instructions: +#define AVR_JTAG_INS_AVR_RESET 0x0C +#define AVR_JTAG_INS_PROG_ENABLE 0x04 +#define AVR_JTAG_INS_PROG_COMMANDS 0x05 +#define AVR_JTAG_INS_PROG_PAGELOAD 0x06 +#define AVR_JTAG_INS_PROG_PAGEREAD 0x07 + +// Data Registers: +#define AVR_JTAG_REG_Bypass_Len 1 +#define AVR_JTAG_REG_DeviceID_Len 32 + +#define AVR_JTAG_REG_Reset_Len 1 +#define AVR_JTAG_REG_JTAGID_Len 32 +#define AVR_JTAG_REG_ProgrammingEnable_Len 16 +#define AVR_JTAG_REG_ProgrammingCommand_Len 15 +#define AVR_JTAG_REG_FlashDataByte_Len 16 + +struct avrf_type avft_chips_info[] = +{ +// name, chip_id, flash_page_size, flash_page_num, eeprom_page_size, eeprom_page_num + {"atmega128", 0x9702, 256, 512, 8, 512}, +}; + +int avr_jtag_sendinstr(struct jtag_tap *tap, uint8_t *ir_in, uint8_t ir_out); +int avr_jtag_senddat(struct jtag_tap *tap, uint32_t *dr_in, uint32_t dr_out, int len); + +int mcu_write_ir(struct jtag_tap *tap, uint8_t *ir_in, uint8_t *ir_out, int ir_len, int rti); +int mcu_write_dr(struct jtag_tap *tap, uint8_t *ir_in, uint8_t *ir_out, int dr_len, int rti); +int mcu_write_ir_u8(struct jtag_tap *tap, uint8_t *ir_in, uint8_t ir_out, int ir_len, int rti); +int mcu_write_dr_u8(struct jtag_tap *tap, uint8_t *ir_in, uint8_t ir_out, int dr_len, int rti); +int mcu_write_ir_u16(struct jtag_tap *tap, uint16_t *ir_in, uint16_t ir_out, int ir_len, int rti); +int mcu_write_dr_u16(struct jtag_tap *tap, uint16_t *ir_in, uint16_t ir_out, int dr_len, int rti); +int mcu_write_ir_u32(struct jtag_tap *tap, uint32_t *ir_in, uint32_t ir_out, int ir_len, int rti); +int mcu_write_dr_u32(struct jtag_tap *tap, uint32_t *ir_in, uint32_t ir_out, int dr_len, int rti); +int mcu_execute_queue(void); + +/* avr program functions */ +static int avr_jtag_reset(struct avr_common *avr, uint32_t reset) +{ + avr_jtag_sendinstr(avr->jtag_info.tap, NULL, AVR_JTAG_INS_AVR_RESET); + avr_jtag_senddat(avr->jtag_info.tap, NULL, reset ,AVR_JTAG_REG_Reset_Len); + + return ERROR_OK; +} + +static int avr_jtag_read_jtagid(struct avr_common *avr, uint32_t *id) +{ + avr_jtag_sendinstr(avr->jtag_info.tap, NULL, AVR_JTAG_INS_IDCODE); + avr_jtag_senddat(avr->jtag_info.tap, id, 0, AVR_JTAG_REG_JTAGID_Len); + + return ERROR_OK; +} + +static int avr_jtagprg_enterprogmode(struct avr_common *avr) +{ + avr_jtag_reset(avr, 1); + + avr_jtag_sendinstr(avr->jtag_info.tap, NULL, AVR_JTAG_INS_PROG_ENABLE); + avr_jtag_senddat(avr->jtag_info.tap, NULL, 0xA370, AVR_JTAG_REG_ProgrammingEnable_Len); + + return ERROR_OK; +} + +static int avr_jtagprg_leaveprogmode(struct avr_common *avr) +{ + avr_jtag_sendinstr(avr->jtag_info.tap, NULL, AVR_JTAG_INS_PROG_COMMANDS); + avr_jtag_senddat(avr->jtag_info.tap, NULL, 0x2300, AVR_JTAG_REG_ProgrammingCommand_Len); + avr_jtag_senddat(avr->jtag_info.tap, NULL, 0x3300, AVR_JTAG_REG_ProgrammingCommand_Len); + + avr_jtag_sendinstr(avr->jtag_info.tap, NULL, AVR_JTAG_INS_PROG_ENABLE); + avr_jtag_senddat(avr->jtag_info.tap, NULL, 0, AVR_JTAG_REG_ProgrammingEnable_Len); + + avr_jtag_reset(avr, 0); + + return ERROR_OK; +} + +static int avr_jtagprg_chiperase(struct avr_common *avr) +{ + uint32_t poll_value; + + avr_jtag_sendinstr(avr->jtag_info.tap, NULL, AVR_JTAG_INS_PROG_COMMANDS); + avr_jtag_senddat(avr->jtag_info.tap, NULL, 0x2380, AVR_JTAG_REG_ProgrammingCommand_Len); + avr_jtag_senddat(avr->jtag_info.tap, NULL, 0x3180, AVR_JTAG_REG_ProgrammingCommand_Len); + avr_jtag_senddat(avr->jtag_info.tap, NULL, 0x3380, AVR_JTAG_REG_ProgrammingCommand_Len); + avr_jtag_senddat(avr->jtag_info.tap, NULL, 0x3380, AVR_JTAG_REG_ProgrammingCommand_Len); + + do { + poll_value = 0; + avr_jtag_senddat(avr->jtag_info.tap, &poll_value, 0x3380, AVR_JTAG_REG_ProgrammingCommand_Len); + if (ERROR_OK != mcu_execute_queue()) + { + return ERROR_FAIL; + } + LOG_DEBUG("poll_value = 0x%04" PRIx32 "", poll_value); + } while (!(poll_value & 0x0200)); + + return ERROR_OK; +} + +static int avr_jtagprg_writeflashpage(struct avr_common *avr, uint8_t *page_buf, uint32_t buf_size, uint32_t addr, uint32_t page_size) +{ + uint32_t i, poll_value; + + avr_jtag_sendinstr(avr->jtag_info.tap, NULL, AVR_JTAG_INS_PROG_COMMANDS); + avr_jtag_senddat(avr->jtag_info.tap, NULL, 0x2310, AVR_JTAG_REG_ProgrammingCommand_Len); + + // load addr high byte + avr_jtag_senddat(avr->jtag_info.tap, NULL, 0x0700 | ((addr >> 9) & 0xFF), AVR_JTAG_REG_ProgrammingCommand_Len); + + // load addr low byte + avr_jtag_senddat(avr->jtag_info.tap, NULL, 0x0300 | ((addr >> 1) & 0xFF), AVR_JTAG_REG_ProgrammingCommand_Len); + + avr_jtag_sendinstr(avr->jtag_info.tap, NULL, AVR_JTAG_INS_PROG_PAGELOAD); + + for (i = 0; i < page_size; i++) + { + if (i < buf_size) + { + avr_jtag_senddat(avr->jtag_info.tap, NULL, page_buf[i], 8); + } + else + { + avr_jtag_senddat(avr->jtag_info.tap, NULL, 0xFF, 8); + } + } + + avr_jtag_sendinstr(avr->jtag_info.tap, NULL, AVR_JTAG_INS_PROG_COMMANDS); + + avr_jtag_senddat(avr->jtag_info.tap, NULL, 0x3700, AVR_JTAG_REG_ProgrammingCommand_Len); + avr_jtag_senddat(avr->jtag_info.tap, NULL, 0x3500, AVR_JTAG_REG_ProgrammingCommand_Len); + avr_jtag_senddat(avr->jtag_info.tap, NULL, 0x3700, AVR_JTAG_REG_ProgrammingCommand_Len); + avr_jtag_senddat(avr->jtag_info.tap, NULL, 0x3700, AVR_JTAG_REG_ProgrammingCommand_Len); + + do { + poll_value = 0; + avr_jtag_senddat(avr->jtag_info.tap, &poll_value, 0x3700, AVR_JTAG_REG_ProgrammingCommand_Len); + if (ERROR_OK != mcu_execute_queue()) + { + return ERROR_FAIL; + } + LOG_DEBUG("poll_value = 0x%04" PRIx32 "", poll_value); + } while (!(poll_value & 0x0200)); + + return ERROR_OK; +} + +FLASH_BANK_COMMAND_HANDLER(avrf_flash_bank_command) +{ + struct avrf_flash_bank *avrf_info; + + if (CMD_ARGC < 6) + { + LOG_WARNING("incomplete flash_bank avr configuration"); + return ERROR_FLASH_BANK_INVALID; + } + + avrf_info = malloc(sizeof(struct avrf_flash_bank)); + bank->driver_priv = avrf_info; + + avrf_info->probed = 0; + + return ERROR_OK; +} + +static int avrf_erase(struct flash_bank *bank, int first, int last) +{ + LOG_INFO("%s", __FUNCTION__); + return ERROR_OK; +} + +static int avrf_protect(struct flash_bank *bank, int set, int first, int last) +{ + LOG_INFO("%s", __FUNCTION__); + return ERROR_OK; +} + +static int avrf_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) +{ + struct target *target = bank->target; + struct avr_common *avr = target->arch_info; + uint32_t cur_size, cur_buffer_size, page_size; + + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + page_size = bank->sectors[0].size; + if ((offset % page_size) != 0) + { + LOG_WARNING("offset 0x%" PRIx32 " breaks required %" PRIu32 "-byte alignment", offset, page_size); + return ERROR_FLASH_DST_BREAKS_ALIGNMENT; + } + + LOG_DEBUG("offset is 0x%08" PRIx32 "", offset); + LOG_DEBUG("count is %" PRId32 "", count); + + if (ERROR_OK != avr_jtagprg_enterprogmode(avr)) + { + return ERROR_FAIL; + } + + cur_size = 0; + while (count > 0) + { + if (count > page_size) + { + cur_buffer_size = page_size; + } + else + { + cur_buffer_size = count; + } + avr_jtagprg_writeflashpage(avr, buffer + cur_size, cur_buffer_size, offset + cur_size, page_size); + count -= cur_buffer_size; + cur_size += cur_buffer_size; + + keep_alive(); + } + + return avr_jtagprg_leaveprogmode(avr); +} + +#define EXTRACT_MFG(X) (((X) & 0xffe) >> 1) +#define EXTRACT_PART(X) (((X) & 0xffff000) >> 12) +#define EXTRACT_VER(X) (((X) & 0xf0000000) >> 28) +static int avrf_probe(struct flash_bank *bank) +{ + struct target *target = bank->target; + struct avrf_flash_bank *avrf_info = bank->driver_priv; + struct avr_common *avr = target->arch_info; + struct avrf_type *avr_info = NULL; + int i; + uint32_t device_id; + + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + avrf_info->probed = 0; + + avr_jtag_read_jtagid(avr, &device_id); + if (ERROR_OK != mcu_execute_queue()) + { + return ERROR_FAIL; + } + + LOG_INFO("device id = 0x%08" PRIx32 "", device_id); + if (EXTRACT_MFG(device_id) != 0x1F) + { + LOG_ERROR("0x%" PRIx32 " is invalid Manufacturer for avr, 0x%X is expected", EXTRACT_MFG(device_id), 0x1F); + } + + for (i = 0; i < (int)ARRAY_SIZE(avft_chips_info); i++) + { + if (avft_chips_info[i].chip_id == EXTRACT_PART(device_id)) + { + avr_info = &avft_chips_info[i]; + LOG_INFO("target device is %s", avr_info->name); + break; + } + } + + if (avr_info != NULL) + { + // chip found + bank->base = 0x00000000; + bank->size = (avr_info->flash_page_size * avr_info->flash_page_num); + bank->num_sectors = avr_info->flash_page_num; + bank->sectors = malloc(sizeof(struct flash_sector) * avr_info->flash_page_num); + + for (i = 0; i < avr_info->flash_page_num; i++) + { + bank->sectors[i].offset = i * avr_info->flash_page_size; + bank->sectors[i].size = avr_info->flash_page_size; + bank->sectors[i].is_erased = -1; + bank->sectors[i].is_protected = 1; + } + + avrf_info->probed = 1; + return ERROR_OK; + } + else + { + // chip not supported + LOG_ERROR("0x%" PRIx32 " is not support for avr", EXTRACT_PART(device_id)); + + avrf_info->probed = 1; + return ERROR_FAIL; + } +} + +static int avrf_auto_probe(struct flash_bank *bank) +{ + struct avrf_flash_bank *avrf_info = bank->driver_priv; + if (avrf_info->probed) + return ERROR_OK; + return avrf_probe(bank); +} + +static int avrf_protect_check(struct flash_bank *bank) +{ + LOG_INFO("%s", __FUNCTION__); + return ERROR_OK; +} + +static int avrf_info(struct flash_bank *bank, char *buf, int buf_size) +{ + struct target *target = bank->target; + struct avr_common *avr = target->arch_info; + struct avrf_type *avr_info = NULL; + int i; + uint32_t device_id; + + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + avr_jtag_read_jtagid(avr, &device_id); + if (ERROR_OK != mcu_execute_queue()) + { + return ERROR_FAIL; + } + + LOG_INFO("device id = 0x%08" PRIx32 "", device_id); + if (EXTRACT_MFG(device_id) != 0x1F) + { + LOG_ERROR("0x%" PRIx32 " is invalid Manufacturer for avr, 0x%X is expected", EXTRACT_MFG(device_id), 0x1F); + } + + for (i = 0; i < (int)ARRAY_SIZE(avft_chips_info); i++) + { + if (avft_chips_info[i].chip_id == EXTRACT_PART(device_id)) + { + avr_info = &avft_chips_info[i]; + LOG_INFO("target device is %s", avr_info->name); + + break; + } + } + + if (avr_info != NULL) + { + // chip found + snprintf(buf, buf_size, "%s - Rev: 0x%" PRIx32 "", avr_info->name, EXTRACT_VER(device_id)); + return ERROR_OK; + } + else + { + // chip not supported + snprintf(buf, buf_size, "Cannot identify target as a avr\n"); + return ERROR_FLASH_OPERATION_FAILED; + } +} + +static int avrf_mass_erase(struct flash_bank *bank) +{ + struct target *target = bank->target; + struct avr_common *avr = target->arch_info; + + if (target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if ((ERROR_OK != avr_jtagprg_enterprogmode(avr)) + || (ERROR_OK != avr_jtagprg_chiperase(avr)) + || (ERROR_OK != avr_jtagprg_leaveprogmode(avr))) + { + return ERROR_FAIL; + } + + return ERROR_OK; +} + +COMMAND_HANDLER(avrf_handle_mass_erase_command) +{ + int i; + + if (CMD_ARGC < 1) + { + command_print(CMD_CTX, "avr mass_erase "); + return ERROR_OK; + } + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + if (avrf_mass_erase(bank) == ERROR_OK) + { + /* set all sectors as erased */ + for (i = 0; i < bank->num_sectors; i++) + { + bank->sectors[i].is_erased = 1; + } + + command_print(CMD_CTX, "avr mass erase complete"); + } + else + { + command_print(CMD_CTX, "avr mass erase failed"); + } + + LOG_DEBUG("%s", __FUNCTION__); + return ERROR_OK; +} + +static const struct command_registration avrf_exec_command_handlers[] = { + { + .name = "mass_erase", + .handler = &avrf_handle_mass_erase_command, + .mode = COMMAND_EXEC, + .help = "erase entire device", + }, + COMMAND_REGISTRATION_DONE +}; +static const struct command_registration avrf_command_handlers[] = { + { + .name = "avrf", + .mode = COMMAND_ANY, + .help = "AVR flash command group", + .chain = avrf_exec_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +struct flash_driver avr_flash = { + .name = "avr", + .commands = avrf_command_handlers, + .flash_bank_command = &avrf_flash_bank_command, + .erase = &avrf_erase, + .protect = &avrf_protect, + .write = &avrf_write, + .probe = &avrf_probe, + .auto_probe = &avrf_auto_probe, + .erase_check = &default_flash_mem_blank_check, + .protect_check = &avrf_protect_check, + .info = &avrf_info, + }; diff --git a/src/flash/nor/avrf.h b/src/flash/nor/avrf.h new file mode 100644 index 00000000..e75d9d7e --- /dev/null +++ b/src/flash/nor/avrf.h @@ -0,0 +1,41 @@ +/*************************************************************************** + * Copyright (C) 2009 by Simon Qian * + * SimonQian@SimonQian.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef AVRF_H +#define AVRF_H + +#include "types.h" + +struct avrf_type +{ + char name[15]; + uint16_t chip_id; + int flash_page_size; + int flash_page_num; + int eeprom_page_size; + int eeprom_page_num; +}; + +struct avrf_flash_bank +{ + int ppage_size; + int probed; +}; + +#endif /* AVRF_H */ diff --git a/src/flash/nor/cfi.c b/src/flash/nor/cfi.c new file mode 100644 index 00000000..6dbffb9e --- /dev/null +++ b/src/flash/nor/cfi.c @@ -0,0 +1,2630 @@ +/*************************************************************************** + * Copyright (C) 2005, 2007 by Dominic Rath * + * Dominic.Rath@gmx.de * + * Copyright (C) 2009 Michael Schwingen * + * michael@schwingen.org * + * * + * 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 "cfi.h" +#include "non_cfi.h" +#include "armv4_5.h" +#include "binarybuffer.h" +#include "algorithm.h" + + +#define CFI_MAX_BUS_WIDTH 4 +#define CFI_MAX_CHIP_WIDTH 4 + +/* defines internal maximum size for code fragment in cfi_intel_write_block() */ +#define CFI_MAX_INTEL_CODESIZE 256 + +static struct cfi_unlock_addresses cfi_unlock_addresses[] = +{ + [CFI_UNLOCK_555_2AA] = { .unlock1 = 0x555, .unlock2 = 0x2aa }, + [CFI_UNLOCK_5555_2AAA] = { .unlock1 = 0x5555, .unlock2 = 0x2aaa }, +}; + +/* CFI fixups foward declarations */ +static void cfi_fixup_0002_erase_regions(struct flash_bank *flash, void *param); +static void cfi_fixup_0002_unlock_addresses(struct flash_bank *flash, void *param); +static void cfi_fixup_atmel_reversed_erase_regions(struct flash_bank *flash, void *param); + +/* fixup after reading cmdset 0002 primary query table */ +static const struct cfi_fixup cfi_0002_fixups[] = { + {CFI_MFR_SST, 0x00D4, cfi_fixup_0002_unlock_addresses, &cfi_unlock_addresses[CFI_UNLOCK_5555_2AAA]}, + {CFI_MFR_SST, 0x00D5, cfi_fixup_0002_unlock_addresses, &cfi_unlock_addresses[CFI_UNLOCK_5555_2AAA]}, + {CFI_MFR_SST, 0x00D6, cfi_fixup_0002_unlock_addresses, &cfi_unlock_addresses[CFI_UNLOCK_5555_2AAA]}, + {CFI_MFR_SST, 0x00D7, cfi_fixup_0002_unlock_addresses, &cfi_unlock_addresses[CFI_UNLOCK_5555_2AAA]}, + {CFI_MFR_SST, 0x2780, cfi_fixup_0002_unlock_addresses, &cfi_unlock_addresses[CFI_UNLOCK_5555_2AAA]}, + {CFI_MFR_ATMEL, 0x00C8, cfi_fixup_atmel_reversed_erase_regions, NULL}, + {CFI_MFR_FUJITSU, 0x226b, cfi_fixup_0002_unlock_addresses, &cfi_unlock_addresses[CFI_UNLOCK_5555_2AAA]}, + {CFI_MFR_AMIC, 0xb31a, cfi_fixup_0002_unlock_addresses, &cfi_unlock_addresses[CFI_UNLOCK_555_2AA]}, + {CFI_MFR_MX, 0x225b, cfi_fixup_0002_unlock_addresses, &cfi_unlock_addresses[CFI_UNLOCK_555_2AA]}, + {CFI_MFR_AMD, 0x225b, cfi_fixup_0002_unlock_addresses, &cfi_unlock_addresses[CFI_UNLOCK_555_2AA]}, + {CFI_MFR_ANY, CFI_ID_ANY, cfi_fixup_0002_erase_regions, NULL}, + {0, 0, NULL, NULL} +}; + +/* fixup after reading cmdset 0001 primary query table */ +static const struct cfi_fixup cfi_0001_fixups[] = { + {0, 0, NULL, NULL} +}; + +static void cfi_fixup(struct flash_bank *bank, const struct cfi_fixup *fixups) +{ + struct cfi_flash_bank *cfi_info = bank->driver_priv; + const struct cfi_fixup *f; + + for (f = fixups; f->fixup; f++) + { + if (((f->mfr == CFI_MFR_ANY) || (f->mfr == cfi_info->manufacturer)) && + ((f->id == CFI_ID_ANY) || (f->id == cfi_info->device_id))) + { + f->fixup(bank, f->param); + } + } +} + +/* inline uint32_t flash_address(struct flash_bank *bank, int sector, uint32_t offset) */ +static __inline__ uint32_t flash_address(struct flash_bank *bank, int sector, uint32_t offset) +{ + struct cfi_flash_bank *cfi_info = bank->driver_priv; + + if (cfi_info->x16_as_x8) offset *= 2; + + /* while the sector list isn't built, only accesses to sector 0 work */ + if (sector == 0) + return bank->base + offset * bank->bus_width; + else + { + if (!bank->sectors) + { + LOG_ERROR("BUG: sector list not yet built"); + exit(-1); + } + return bank->base + bank->sectors[sector].offset + offset * bank->bus_width; + } + +} + +static void cfi_command(struct flash_bank *bank, uint8_t cmd, uint8_t *cmd_buf) +{ + int i; + + /* clear whole buffer, to ensure bits that exceed the bus_width + * are set to zero + */ + for (i = 0; i < CFI_MAX_BUS_WIDTH; i++) + cmd_buf[i] = 0; + + if (bank->target->endianness == TARGET_LITTLE_ENDIAN) + { + for (i = bank->bus_width; i > 0; i--) + { + *cmd_buf++ = (i & (bank->chip_width - 1)) ? 0x0 : cmd; + } + } + else + { + for (i = 1; i <= bank->bus_width; i++) + { + *cmd_buf++ = (i & (bank->chip_width - 1)) ? 0x0 : cmd; + } + } +} + +/* read unsigned 8-bit value from the bank + * flash banks are expected to be made of similar chips + * the query result should be the same for all + */ +static uint8_t cfi_query_u8(struct flash_bank *bank, int sector, uint32_t offset) +{ + struct target *target = bank->target; + uint8_t data[CFI_MAX_BUS_WIDTH]; + + target_read_memory(target, flash_address(bank, sector, offset), bank->bus_width, 1, data); + + if (bank->target->endianness == TARGET_LITTLE_ENDIAN) + return data[0]; + else + return data[bank->bus_width - 1]; +} + +/* read unsigned 8-bit value from the bank + * in case of a bank made of multiple chips, + * the individual values are ORed + */ +static uint8_t cfi_get_u8(struct flash_bank *bank, int sector, uint32_t offset) +{ + struct target *target = bank->target; + uint8_t data[CFI_MAX_BUS_WIDTH]; + int i; + + target_read_memory(target, flash_address(bank, sector, offset), bank->bus_width, 1, data); + + if (bank->target->endianness == TARGET_LITTLE_ENDIAN) + { + for (i = 0; i < bank->bus_width / bank->chip_width; i++) + data[0] |= data[i]; + + return data[0]; + } + else + { + uint8_t value = 0; + for (i = 0; i < bank->bus_width / bank->chip_width; i++) + value |= data[bank->bus_width - 1 - i]; + + return value; + } +} + +static uint16_t cfi_query_u16(struct flash_bank *bank, int sector, uint32_t offset) +{ + struct target *target = bank->target; + struct cfi_flash_bank *cfi_info = bank->driver_priv; + uint8_t data[CFI_MAX_BUS_WIDTH * 2]; + + if (cfi_info->x16_as_x8) + { + uint8_t i; + for (i = 0;i < 2;i++) + target_read_memory(target, flash_address(bank, sector, offset + i), bank->bus_width, 1, + &data[i*bank->bus_width]); + } + else + target_read_memory(target, flash_address(bank, sector, offset), bank->bus_width, 2, data); + + if (bank->target->endianness == TARGET_LITTLE_ENDIAN) + return data[0] | data[bank->bus_width] << 8; + else + return data[bank->bus_width - 1] | data[(2 * bank->bus_width) - 1] << 8; +} + +static uint32_t cfi_query_u32(struct flash_bank *bank, int sector, uint32_t offset) +{ + struct target *target = bank->target; + struct cfi_flash_bank *cfi_info = bank->driver_priv; + uint8_t data[CFI_MAX_BUS_WIDTH * 4]; + + if (cfi_info->x16_as_x8) + { + uint8_t i; + for (i = 0;i < 4;i++) + target_read_memory(target, flash_address(bank, sector, offset + i), bank->bus_width, 1, + &data[i*bank->bus_width]); + } + else + target_read_memory(target, flash_address(bank, sector, offset), bank->bus_width, 4, data); + + if (bank->target->endianness == TARGET_LITTLE_ENDIAN) + return data[0] | data[bank->bus_width] << 8 | data[bank->bus_width * 2] << 16 | data[bank->bus_width * 3] << 24; + else + return data[bank->bus_width - 1] | data[(2* bank->bus_width) - 1] << 8 | + data[(3 * bank->bus_width) - 1] << 16 | data[(4 * bank->bus_width) - 1] << 24; +} + +static void cfi_intel_clear_status_register(struct flash_bank *bank) +{ + struct target *target = bank->target; + uint8_t command[8]; + + if (target->state != TARGET_HALTED) + { + LOG_ERROR("BUG: attempted to clear status register while target wasn't halted"); + exit(-1); + } + + cfi_command(bank, 0x50, command); + target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command); +} + +uint8_t cfi_intel_wait_status_busy(struct flash_bank *bank, int timeout) +{ + uint8_t status; + + while ((!((status = cfi_get_u8(bank, 0, 0x0)) & 0x80)) && (timeout-- > 0)) + { + LOG_DEBUG("status: 0x%x", status); + alive_sleep(1); + } + + /* mask out bit 0 (reserved) */ + status = status & 0xfe; + + LOG_DEBUG("status: 0x%x", status); + + if ((status & 0x80) != 0x80) + { + LOG_ERROR("timeout while waiting for WSM to become ready"); + } + else if (status != 0x80) + { + LOG_ERROR("status register: 0x%x", status); + if (status & 0x2) + LOG_ERROR("Block Lock-Bit Detected, Operation Abort"); + if (status & 0x4) + LOG_ERROR("Program suspended"); + if (status & 0x8) + LOG_ERROR("Low Programming Voltage Detected, Operation Aborted"); + if (status & 0x10) + LOG_ERROR("Program Error / Error in Setting Lock-Bit"); + if (status & 0x20) + LOG_ERROR("Error in Block Erasure or Clear Lock-Bits"); + if (status & 0x40) + LOG_ERROR("Block Erase Suspended"); + + cfi_intel_clear_status_register(bank); + } + + return status; +} + +int cfi_spansion_wait_status_busy(struct flash_bank *bank, int timeout) +{ + uint8_t status, oldstatus; + struct cfi_flash_bank *cfi_info = bank->driver_priv; + + oldstatus = cfi_get_u8(bank, 0, 0x0); + + do { + status = cfi_get_u8(bank, 0, 0x0); + if ((status ^ oldstatus) & 0x40) { + if (status & cfi_info->status_poll_mask & 0x20) { + oldstatus = cfi_get_u8(bank, 0, 0x0); + status = cfi_get_u8(bank, 0, 0x0); + if ((status ^ oldstatus) & 0x40) { + LOG_ERROR("dq5 timeout, status: 0x%x", status); + return(ERROR_FLASH_OPERATION_FAILED); + } else { + LOG_DEBUG("status: 0x%x", status); + return(ERROR_OK); + } + } + } else { /* no toggle: finished, OK */ + LOG_DEBUG("status: 0x%x", status); + return(ERROR_OK); + } + + oldstatus = status; + alive_sleep(1); + } while (timeout-- > 0); + + LOG_ERROR("timeout, status: 0x%x", status); + + return(ERROR_FLASH_BUSY); +} + +static int cfi_read_intel_pri_ext(struct flash_bank *bank) +{ + int retval; + struct cfi_flash_bank *cfi_info = bank->driver_priv; + struct cfi_intel_pri_ext *pri_ext = malloc(sizeof(struct cfi_intel_pri_ext)); + struct target *target = bank->target; + uint8_t command[8]; + + cfi_info->pri_ext = pri_ext; + + pri_ext->pri[0] = cfi_query_u8(bank, 0, cfi_info->pri_addr + 0); + pri_ext->pri[1] = cfi_query_u8(bank, 0, cfi_info->pri_addr + 1); + pri_ext->pri[2] = cfi_query_u8(bank, 0, cfi_info->pri_addr + 2); + + if ((pri_ext->pri[0] != 'P') || (pri_ext->pri[1] != 'R') || (pri_ext->pri[2] != 'I')) + { + cfi_command(bank, 0xf0, command); + if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + cfi_command(bank, 0xff, command); + if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + LOG_ERROR("Could not read bank flash bank information"); + return ERROR_FLASH_BANK_INVALID; + } + + pri_ext->major_version = cfi_query_u8(bank, 0, cfi_info->pri_addr + 3); + pri_ext->minor_version = cfi_query_u8(bank, 0, cfi_info->pri_addr + 4); + + LOG_DEBUG("pri: '%c%c%c', version: %c.%c", pri_ext->pri[0], pri_ext->pri[1], pri_ext->pri[2], pri_ext->major_version, pri_ext->minor_version); + + pri_ext->feature_support = cfi_query_u32(bank, 0, cfi_info->pri_addr + 5); + pri_ext->suspend_cmd_support = cfi_query_u8(bank, 0, cfi_info->pri_addr + 9); + pri_ext->blk_status_reg_mask = cfi_query_u16(bank, 0, cfi_info->pri_addr + 0xa); + + LOG_DEBUG("feature_support: 0x%" PRIx32 ", suspend_cmd_support: 0x%x, blk_status_reg_mask: 0x%x", + pri_ext->feature_support, + pri_ext->suspend_cmd_support, + pri_ext->blk_status_reg_mask); + + pri_ext->vcc_optimal = cfi_query_u8(bank, 0, cfi_info->pri_addr + 0xc); + pri_ext->vpp_optimal = cfi_query_u8(bank, 0, cfi_info->pri_addr + 0xd); + + LOG_DEBUG("Vcc opt: %1.1x.%1.1x, Vpp opt: %1.1x.%1.1x", + (pri_ext->vcc_optimal & 0xf0) >> 4, pri_ext->vcc_optimal & 0x0f, + (pri_ext->vpp_optimal & 0xf0) >> 4, pri_ext->vpp_optimal & 0x0f); + + pri_ext->num_protection_fields = cfi_query_u8(bank, 0, cfi_info->pri_addr + 0xe); + if (pri_ext->num_protection_fields != 1) + { + LOG_WARNING("expected one protection register field, but found %i", pri_ext->num_protection_fields); + } + + pri_ext->prot_reg_addr = cfi_query_u16(bank, 0, cfi_info->pri_addr + 0xf); + pri_ext->fact_prot_reg_size = cfi_query_u8(bank, 0, cfi_info->pri_addr + 0x11); + pri_ext->user_prot_reg_size = cfi_query_u8(bank, 0, cfi_info->pri_addr + 0x12); + + LOG_DEBUG("protection_fields: %i, prot_reg_addr: 0x%x, factory pre-programmed: %i, user programmable: %i", pri_ext->num_protection_fields, pri_ext->prot_reg_addr, 1 << pri_ext->fact_prot_reg_size, 1 << pri_ext->user_prot_reg_size); + + return ERROR_OK; +} + +static int cfi_read_spansion_pri_ext(struct flash_bank *bank) +{ + int retval; + struct cfi_flash_bank *cfi_info = bank->driver_priv; + struct cfi_spansion_pri_ext *pri_ext = malloc(sizeof(struct cfi_spansion_pri_ext)); + struct target *target = bank->target; + uint8_t command[8]; + + cfi_info->pri_ext = pri_ext; + + pri_ext->pri[0] = cfi_query_u8(bank, 0, cfi_info->pri_addr + 0); + pri_ext->pri[1] = cfi_query_u8(bank, 0, cfi_info->pri_addr + 1); + pri_ext->pri[2] = cfi_query_u8(bank, 0, cfi_info->pri_addr + 2); + + if ((pri_ext->pri[0] != 'P') || (pri_ext->pri[1] != 'R') || (pri_ext->pri[2] != 'I')) + { + cfi_command(bank, 0xf0, command); + if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + LOG_ERROR("Could not read spansion bank information"); + return ERROR_FLASH_BANK_INVALID; + } + + pri_ext->major_version = cfi_query_u8(bank, 0, cfi_info->pri_addr + 3); + pri_ext->minor_version = cfi_query_u8(bank, 0, cfi_info->pri_addr + 4); + + LOG_DEBUG("pri: '%c%c%c', version: %c.%c", pri_ext->pri[0], pri_ext->pri[1], pri_ext->pri[2], pri_ext->major_version, pri_ext->minor_version); + + pri_ext->SiliconRevision = cfi_query_u8(bank, 0, cfi_info->pri_addr + 5); + pri_ext->EraseSuspend = cfi_query_u8(bank, 0, cfi_info->pri_addr + 6); + pri_ext->BlkProt = cfi_query_u8(bank, 0, cfi_info->pri_addr + 7); + pri_ext->TmpBlkUnprotect = cfi_query_u8(bank, 0, cfi_info->pri_addr + 8); + pri_ext->BlkProtUnprot = cfi_query_u8(bank, 0, cfi_info->pri_addr + 9); + pri_ext->SimultaneousOps = cfi_query_u8(bank, 0, cfi_info->pri_addr + 10); + pri_ext->BurstMode = cfi_query_u8(bank, 0, cfi_info->pri_addr + 11); + pri_ext->PageMode = cfi_query_u8(bank, 0, cfi_info->pri_addr + 12); + pri_ext->VppMin = cfi_query_u8(bank, 0, cfi_info->pri_addr + 13); + pri_ext->VppMax = cfi_query_u8(bank, 0, cfi_info->pri_addr + 14); + pri_ext->TopBottom = cfi_query_u8(bank, 0, cfi_info->pri_addr + 15); + + LOG_DEBUG("Silicon Revision: 0x%x, Erase Suspend: 0x%x, Block protect: 0x%x", pri_ext->SiliconRevision, + pri_ext->EraseSuspend, pri_ext->BlkProt); + + LOG_DEBUG("Temporary Unprotect: 0x%x, Block Protect Scheme: 0x%x, Simultaneous Ops: 0x%x", pri_ext->TmpBlkUnprotect, + pri_ext->BlkProtUnprot, pri_ext->SimultaneousOps); + + LOG_DEBUG("Burst Mode: 0x%x, Page Mode: 0x%x, ", pri_ext->BurstMode, pri_ext->PageMode); + + + LOG_DEBUG("Vpp min: %2.2d.%1.1d, Vpp max: %2.2d.%1.1x", + (pri_ext->VppMin & 0xf0) >> 4, pri_ext->VppMin & 0x0f, + (pri_ext->VppMax & 0xf0) >> 4, pri_ext->VppMax & 0x0f); + + LOG_DEBUG("WP# protection 0x%x", pri_ext->TopBottom); + + /* default values for implementation specific workarounds */ + pri_ext->_unlock1 = cfi_unlock_addresses[CFI_UNLOCK_555_2AA].unlock1; + pri_ext->_unlock2 = cfi_unlock_addresses[CFI_UNLOCK_555_2AA].unlock2; + pri_ext->_reversed_geometry = 0; + + return ERROR_OK; +} + +static int cfi_read_atmel_pri_ext(struct flash_bank *bank) +{ + int retval; + struct cfi_atmel_pri_ext atmel_pri_ext; + struct cfi_flash_bank *cfi_info = bank->driver_priv; + struct cfi_spansion_pri_ext *pri_ext = malloc(sizeof(struct cfi_spansion_pri_ext)); + struct target *target = bank->target; + uint8_t command[8]; + + /* ATMEL devices use the same CFI primary command set (0x2) as AMD/Spansion, + * but a different primary extended query table. + * We read the atmel table, and prepare a valid AMD/Spansion query table. + */ + + memset(pri_ext, 0, sizeof(struct cfi_spansion_pri_ext)); + + cfi_info->pri_ext = pri_ext; + + atmel_pri_ext.pri[0] = cfi_query_u8(bank, 0, cfi_info->pri_addr + 0); + atmel_pri_ext.pri[1] = cfi_query_u8(bank, 0, cfi_info->pri_addr + 1); + atmel_pri_ext.pri[2] = cfi_query_u8(bank, 0, cfi_info->pri_addr + 2); + + if ((atmel_pri_ext.pri[0] != 'P') || (atmel_pri_ext.pri[1] != 'R') || (atmel_pri_ext.pri[2] != 'I')) + { + cfi_command(bank, 0xf0, command); + if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + LOG_ERROR("Could not read atmel bank information"); + return ERROR_FLASH_BANK_INVALID; + } + + pri_ext->pri[0] = atmel_pri_ext.pri[0]; + pri_ext->pri[1] = atmel_pri_ext.pri[1]; + pri_ext->pri[2] = atmel_pri_ext.pri[2]; + + atmel_pri_ext.major_version = cfi_query_u8(bank, 0, cfi_info->pri_addr + 3); + atmel_pri_ext.minor_version = cfi_query_u8(bank, 0, cfi_info->pri_addr + 4); + + LOG_DEBUG("pri: '%c%c%c', version: %c.%c", atmel_pri_ext.pri[0], atmel_pri_ext.pri[1], atmel_pri_ext.pri[2], atmel_pri_ext.major_version, atmel_pri_ext.minor_version); + + pri_ext->major_version = atmel_pri_ext.major_version; + pri_ext->minor_version = atmel_pri_ext.minor_version; + + atmel_pri_ext.features = cfi_query_u8(bank, 0, cfi_info->pri_addr + 5); + atmel_pri_ext.bottom_boot = cfi_query_u8(bank, 0, cfi_info->pri_addr + 6); + atmel_pri_ext.burst_mode = cfi_query_u8(bank, 0, cfi_info->pri_addr + 7); + atmel_pri_ext.page_mode = cfi_query_u8(bank, 0, cfi_info->pri_addr + 8); + + LOG_DEBUG("features: 0x%2.2x, bottom_boot: 0x%2.2x, burst_mode: 0x%2.2x, page_mode: 0x%2.2x", + atmel_pri_ext.features, atmel_pri_ext.bottom_boot, atmel_pri_ext.burst_mode, atmel_pri_ext.page_mode); + + if (atmel_pri_ext.features & 0x02) + pri_ext->EraseSuspend = 2; + + if (atmel_pri_ext.bottom_boot) + pri_ext->TopBottom = 2; + else + pri_ext->TopBottom = 3; + + pri_ext->_unlock1 = cfi_unlock_addresses[CFI_UNLOCK_555_2AA].unlock1; + pri_ext->_unlock2 = cfi_unlock_addresses[CFI_UNLOCK_555_2AA].unlock2; + + return ERROR_OK; +} + +static int cfi_read_0002_pri_ext(struct flash_bank *bank) +{ + struct cfi_flash_bank *cfi_info = bank->driver_priv; + + if (cfi_info->manufacturer == CFI_MFR_ATMEL) + { + return cfi_read_atmel_pri_ext(bank); + } + else + { + return cfi_read_spansion_pri_ext(bank); + } +} + +static int cfi_spansion_info(struct flash_bank *bank, char *buf, int buf_size) +{ + int printed; + struct cfi_flash_bank *cfi_info = bank->driver_priv; + struct cfi_spansion_pri_ext *pri_ext = cfi_info->pri_ext; + + printed = snprintf(buf, buf_size, "\nSpansion primary algorithm extend information:\n"); + buf += printed; + buf_size -= printed; + + printed = snprintf(buf, buf_size, "pri: '%c%c%c', version: %c.%c\n", pri_ext->pri[0], + pri_ext->pri[1], pri_ext->pri[2], + pri_ext->major_version, pri_ext->minor_version); + buf += printed; + buf_size -= printed; + + printed = snprintf(buf, buf_size, "Silicon Rev.: 0x%x, Address Sensitive unlock: 0x%x\n", + (pri_ext->SiliconRevision) >> 2, + (pri_ext->SiliconRevision) & 0x03); + buf += printed; + buf_size -= printed; + + printed = snprintf(buf, buf_size, "Erase Suspend: 0x%x, Sector Protect: 0x%x\n", + pri_ext->EraseSuspend, + pri_ext->BlkProt); + buf += printed; + buf_size -= printed; + + printed = snprintf(buf, buf_size, "VppMin: %2.2d.%1.1x, VppMax: %2.2d.%1.1x\n", + (pri_ext->VppMin & 0xf0) >> 4, pri_ext->VppMin & 0x0f, + (pri_ext->VppMax & 0xf0) >> 4, pri_ext->VppMax & 0x0f); + + return ERROR_OK; +} + +static int cfi_intel_info(struct flash_bank *bank, char *buf, int buf_size) +{ + int printed; + struct cfi_flash_bank *cfi_info = bank->driver_priv; + struct cfi_intel_pri_ext *pri_ext = cfi_info->pri_ext; + + printed = snprintf(buf, buf_size, "\nintel primary algorithm extend information:\n"); + buf += printed; + buf_size -= printed; + + printed = snprintf(buf, buf_size, "pri: '%c%c%c', version: %c.%c\n", pri_ext->pri[0], pri_ext->pri[1], pri_ext->pri[2], pri_ext->major_version, pri_ext->minor_version); + buf += printed; + buf_size -= printed; + + printed = snprintf(buf, buf_size, "feature_support: 0x%" PRIx32 ", suspend_cmd_support: 0x%x, blk_status_reg_mask: 0x%x\n", pri_ext->feature_support, pri_ext->suspend_cmd_support, pri_ext->blk_status_reg_mask); + buf += printed; + buf_size -= printed; + + printed = snprintf(buf, buf_size, "Vcc opt: %1.1x.%1.1x, Vpp opt: %1.1x.%1.1x\n", + (pri_ext->vcc_optimal & 0xf0) >> 4, pri_ext->vcc_optimal & 0x0f, + (pri_ext->vpp_optimal & 0xf0) >> 4, pri_ext->vpp_optimal & 0x0f); + buf += printed; + buf_size -= printed; + + printed = snprintf(buf, buf_size, "protection_fields: %i, prot_reg_addr: 0x%x, factory pre-programmed: %i, user programmable: %i\n", pri_ext->num_protection_fields, pri_ext->prot_reg_addr, 1 << pri_ext->fact_prot_reg_size, 1 << pri_ext->user_prot_reg_size); + + return ERROR_OK; +} + +/* flash_bank cfi [options] + */ +FLASH_BANK_COMMAND_HANDLER(cfi_flash_bank_command) +{ + struct cfi_flash_bank *cfi_info; + + if (CMD_ARGC < 6) + { + LOG_WARNING("incomplete flash_bank cfi configuration"); + return ERROR_FLASH_BANK_INVALID; + } + + uint16_t chip_width, bus_width; + COMMAND_PARSE_NUMBER(u16, CMD_ARGV[3], bus_width); + COMMAND_PARSE_NUMBER(u16, CMD_ARGV[4], chip_width); + + if ((chip_width > CFI_MAX_CHIP_WIDTH) + || (bus_width > CFI_MAX_BUS_WIDTH)) + { + LOG_ERROR("chip and bus width have to specified in bytes"); + return ERROR_FLASH_BANK_INVALID; + } + + cfi_info = malloc(sizeof(struct cfi_flash_bank)); + cfi_info->probed = 0; + bank->driver_priv = cfi_info; + + cfi_info->write_algorithm = NULL; + + cfi_info->x16_as_x8 = 0; + cfi_info->jedec_probe = 0; + cfi_info->not_cfi = 0; + + for (unsigned i = 6; i < CMD_ARGC; i++) + { + if (strcmp(CMD_ARGV[i], "x16_as_x8") == 0) + { + cfi_info->x16_as_x8 = 1; + } + else if (strcmp(CMD_ARGV[i], "jedec_probe") == 0) + { + cfi_info->jedec_probe = 1; + } + } + + cfi_info->write_algorithm = NULL; + + /* bank wasn't probed yet */ + cfi_info->qry[0] = -1; + + return ERROR_OK; +} + +static int cfi_intel_erase(struct flash_bank *bank, int first, int last) +{ + int retval; + struct cfi_flash_bank *cfi_info = bank->driver_priv; + struct target *target = bank->target; + uint8_t command[8]; + int i; + + cfi_intel_clear_status_register(bank); + + for (i = first; i <= last; i++) + { + cfi_command(bank, 0x20, command); + if ((retval = target_write_memory(target, flash_address(bank, i, 0x0), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + + cfi_command(bank, 0xd0, command); + if ((retval = target_write_memory(target, flash_address(bank, i, 0x0), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + + if (cfi_intel_wait_status_busy(bank, 1000 * (1 << cfi_info->block_erase_timeout_typ)) == 0x80) + bank->sectors[i].is_erased = 1; + else + { + cfi_command(bank, 0xff, command); + if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + + LOG_ERROR("couldn't erase block %i of flash bank at base 0x%" PRIx32 , i, bank->base); + return ERROR_FLASH_OPERATION_FAILED; + } + } + + cfi_command(bank, 0xff, command); + return target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command); + +} + +static int cfi_spansion_erase(struct flash_bank *bank, int first, int last) +{ + int retval; + struct cfi_flash_bank *cfi_info = bank->driver_priv; + struct cfi_spansion_pri_ext *pri_ext = cfi_info->pri_ext; + struct target *target = bank->target; + uint8_t command[8]; + int i; + + for (i = first; i <= last; i++) + { + cfi_command(bank, 0xaa, command); + if ((retval = target_write_memory(target, flash_address(bank, 0, pri_ext->_unlock1), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + + cfi_command(bank, 0x55, command); + if ((retval = target_write_memory(target, flash_address(bank, 0, pri_ext->_unlock2), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + + cfi_command(bank, 0x80, command); + if ((retval = target_write_memory(target, flash_address(bank, 0, pri_ext->_unlock1), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + + cfi_command(bank, 0xaa, command); + if ((retval = target_write_memory(target, flash_address(bank, 0, pri_ext->_unlock1), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + + cfi_command(bank, 0x55, command); + if ((retval = target_write_memory(target, flash_address(bank, 0, pri_ext->_unlock2), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + + cfi_command(bank, 0x30, command); + if ((retval = target_write_memory(target, flash_address(bank, i, 0x0), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + + if (cfi_spansion_wait_status_busy(bank, 1000 * (1 << cfi_info->block_erase_timeout_typ)) == ERROR_OK) + bank->sectors[i].is_erased = 1; + else + { + cfi_command(bank, 0xf0, command); + if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + + LOG_ERROR("couldn't erase block %i of flash bank at base 0x%" PRIx32, i, bank->base); + return ERROR_FLASH_OPERATION_FAILED; + } + } + + cfi_command(bank, 0xf0, command); + return target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command); +} + +static int cfi_erase(struct flash_bank *bank, int first, int last) +{ + struct cfi_flash_bank *cfi_info = bank->driver_priv; + + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if ((first < 0) || (last < first) || (last >= bank->num_sectors)) + { + return ERROR_FLASH_SECTOR_INVALID; + } + + if (cfi_info->qry[0] != 'Q') + return ERROR_FLASH_BANK_NOT_PROBED; + + switch (cfi_info->pri_id) + { + case 1: + case 3: + return cfi_intel_erase(bank, first, last); + break; + case 2: + return cfi_spansion_erase(bank, first, last); + break; + default: + LOG_ERROR("cfi primary command set %i unsupported", cfi_info->pri_id); + break; + } + + return ERROR_OK; +} + +static int cfi_intel_protect(struct flash_bank *bank, int set, int first, int last) +{ + int retval; + struct cfi_flash_bank *cfi_info = bank->driver_priv; + struct cfi_intel_pri_ext *pri_ext = cfi_info->pri_ext; + struct target *target = bank->target; + uint8_t command[8]; + int retry = 0; + int i; + + /* if the device supports neither legacy lock/unlock (bit 3) nor + * instant individual block locking (bit 5). + */ + if (!(pri_ext->feature_support & 0x28)) + return ERROR_FLASH_OPERATION_FAILED; + + cfi_intel_clear_status_register(bank); + + for (i = first; i <= last; i++) + { + cfi_command(bank, 0x60, command); + LOG_DEBUG("address: 0x%4.4" PRIx32 ", command: 0x%4.4" PRIx32, flash_address(bank, i, 0x0), target_buffer_get_u32(target, command)); + if ((retval = target_write_memory(target, flash_address(bank, i, 0x0), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + if (set) + { + cfi_command(bank, 0x01, command); + LOG_DEBUG("address: 0x%4.4" PRIx32 ", command: 0x%4.4" PRIx32 , flash_address(bank, i, 0x0), target_buffer_get_u32(target, command)); + if ((retval = target_write_memory(target, flash_address(bank, i, 0x0), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + bank->sectors[i].is_protected = 1; + } + else + { + cfi_command(bank, 0xd0, command); + LOG_DEBUG("address: 0x%4.4" PRIx32 ", command: 0x%4.4" PRIx32, flash_address(bank, i, 0x0), target_buffer_get_u32(target, command)); + if ((retval = target_write_memory(target, flash_address(bank, i, 0x0), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + bank->sectors[i].is_protected = 0; + } + + /* instant individual block locking doesn't require reading of the status register */ + if (!(pri_ext->feature_support & 0x20)) + { + /* Clear lock bits operation may take up to 1.4s */ + cfi_intel_wait_status_busy(bank, 1400); + } + else + { + uint8_t block_status; + /* read block lock bit, to verify status */ + cfi_command(bank, 0x90, command); + if ((retval = target_write_memory(target, flash_address(bank, 0, 0x55), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + block_status = cfi_get_u8(bank, i, 0x2); + + if ((block_status & 0x1) != set) + { + LOG_ERROR("couldn't change block lock status (set = %i, block_status = 0x%2.2x)", set, block_status); + cfi_command(bank, 0x70, command); + if ((retval = target_write_memory(target, flash_address(bank, 0, 0x55), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + cfi_intel_wait_status_busy(bank, 10); + + if (retry > 10) + return ERROR_FLASH_OPERATION_FAILED; + else + { + i--; + retry++; + } + } + } + } + + /* if the device doesn't support individual block lock bits set/clear, + * all blocks have been unlocked in parallel, so we set those that should be protected + */ + if ((!set) && (!(pri_ext->feature_support & 0x20))) + { + for (i = 0; i < bank->num_sectors; i++) + { + if (bank->sectors[i].is_protected == 1) + { + cfi_intel_clear_status_register(bank); + + cfi_command(bank, 0x60, command); + if ((retval = target_write_memory(target, flash_address(bank, i, 0x0), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + + cfi_command(bank, 0x01, command); + if ((retval = target_write_memory(target, flash_address(bank, i, 0x0), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + + cfi_intel_wait_status_busy(bank, 100); + } + } + } + + cfi_command(bank, 0xff, command); + return target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command); +} + +static int cfi_protect(struct flash_bank *bank, int set, int first, int last) +{ + struct cfi_flash_bank *cfi_info = bank->driver_priv; + + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if ((first < 0) || (last < first) || (last >= bank->num_sectors)) + { + return ERROR_FLASH_SECTOR_INVALID; + } + + if (cfi_info->qry[0] != 'Q') + return ERROR_FLASH_BANK_NOT_PROBED; + + switch (cfi_info->pri_id) + { + case 1: + case 3: + cfi_intel_protect(bank, set, first, last); + break; + default: + LOG_ERROR("protect: cfi primary command set %i unsupported", cfi_info->pri_id); + break; + } + + return ERROR_OK; +} + +/* FIXME Replace this by a simple memcpy() - still unsure about sideeffects */ +static void cfi_add_byte(struct flash_bank *bank, uint8_t *word, uint8_t byte) +{ + /* struct target *target = bank->target; */ + + int i; + + /* NOTE: + * The data to flash must not be changed in endian! We write a bytestrem in + * target byte order already. Only the control and status byte lane of the flash + * WSM is interpreted by the CPU in different ways, when read a uint16_t or uint32_t + * word (data seems to be in the upper or lower byte lane for uint16_t accesses). + */ + +#if 0 + if (target->endianness == TARGET_LITTLE_ENDIAN) + { +#endif + /* shift bytes */ + for (i = 0; i < bank->bus_width - 1; i++) + word[i] = word[i + 1]; + word[bank->bus_width - 1] = byte; +#if 0 + } + else + { + /* shift bytes */ + for (i = bank->bus_width - 1; i > 0; i--) + word[i] = word[i - 1]; + word[0] = byte; + } +#endif +} + +/* Convert code image to target endian */ +/* FIXME create general block conversion fcts in target.c?) */ +static void cfi_fix_code_endian(struct target *target, uint8_t *dest, const uint32_t *src, uint32_t count) +{ + uint32_t i; + for (i = 0; i< count; i++) + { + target_buffer_set_u32(target, dest, *src); + dest += 4; + src++; + } +} + +static uint32_t cfi_command_val(struct flash_bank *bank, uint8_t cmd) +{ + struct target *target = bank->target; + + uint8_t buf[CFI_MAX_BUS_WIDTH]; + cfi_command(bank, cmd, buf); + switch (bank->bus_width) + { + case 1 : + return buf[0]; + break; + case 2 : + return target_buffer_get_u16(target, buf); + break; + case 4 : + return target_buffer_get_u32(target, buf); + break; + default : + LOG_ERROR("Unsupported bank buswidth %d, can't do block memory writes", bank->bus_width); + return 0; + } +} + +static int cfi_intel_write_block(struct flash_bank *bank, uint8_t *buffer, uint32_t address, uint32_t count) +{ + struct cfi_flash_bank *cfi_info = bank->driver_priv; + struct target *target = bank->target; + struct reg_param reg_params[7]; + struct armv4_5_algorithm armv4_5_info; + struct working_area *source; + uint32_t buffer_size = 32768; + uint32_t write_command_val, busy_pattern_val, error_pattern_val; + + /* algorithm register usage: + * r0: source address (in RAM) + * r1: target address (in Flash) + * r2: count + * r3: flash write command + * r4: status byte (returned to host) + * r5: busy test pattern + * r6: error test pattern + */ + + static const uint32_t word_32_code[] = { + 0xe4904004, /* loop: ldr r4, [r0], #4 */ + 0xe5813000, /* str r3, [r1] */ + 0xe5814000, /* str r4, [r1] */ + 0xe5914000, /* busy: ldr r4, [r1] */ + 0xe0047005, /* and r7, r4, r5 */ + 0xe1570005, /* cmp r7, r5 */ + 0x1afffffb, /* bne busy */ + 0xe1140006, /* tst r4, r6 */ + 0x1a000003, /* bne done */ + 0xe2522001, /* subs r2, r2, #1 */ + 0x0a000001, /* beq done */ + 0xe2811004, /* add r1, r1 #4 */ + 0xeafffff2, /* b loop */ + 0xeafffffe /* done: b -2 */ + }; + + static const uint32_t word_16_code[] = { + 0xe0d040b2, /* loop: ldrh r4, [r0], #2 */ + 0xe1c130b0, /* strh r3, [r1] */ + 0xe1c140b0, /* strh r4, [r1] */ + 0xe1d140b0, /* busy ldrh r4, [r1] */ + 0xe0047005, /* and r7, r4, r5 */ + 0xe1570005, /* cmp r7, r5 */ + 0x1afffffb, /* bne busy */ + 0xe1140006, /* tst r4, r6 */ + 0x1a000003, /* bne done */ + 0xe2522001, /* subs r2, r2, #1 */ + 0x0a000001, /* beq done */ + 0xe2811002, /* add r1, r1 #2 */ + 0xeafffff2, /* b loop */ + 0xeafffffe /* done: b -2 */ + }; + + static const uint32_t word_8_code[] = { + 0xe4d04001, /* loop: ldrb r4, [r0], #1 */ + 0xe5c13000, /* strb r3, [r1] */ + 0xe5c14000, /* strb r4, [r1] */ + 0xe5d14000, /* busy ldrb r4, [r1] */ + 0xe0047005, /* and r7, r4, r5 */ + 0xe1570005, /* cmp r7, r5 */ + 0x1afffffb, /* bne busy */ + 0xe1140006, /* tst r4, r6 */ + 0x1a000003, /* bne done */ + 0xe2522001, /* subs r2, r2, #1 */ + 0x0a000001, /* beq done */ + 0xe2811001, /* add r1, r1 #1 */ + 0xeafffff2, /* b loop */ + 0xeafffffe /* done: b -2 */ + }; + uint8_t target_code[4*CFI_MAX_INTEL_CODESIZE]; + const uint32_t *target_code_src; + uint32_t target_code_size; + int retval = ERROR_OK; + + + cfi_intel_clear_status_register(bank); + + 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; + + /* If we are setting up the write_algorith, we need target_code_src */ + /* if not we only need target_code_size. */ + + /* However, we don't want to create multiple code paths, so we */ + /* do the unecessary evaluation of target_code_src, which the */ + /* compiler will probably nicely optimize away if not needed */ + + /* prepare algorithm code for target endian */ + switch (bank->bus_width) + { + case 1 : + target_code_src = word_8_code; + target_code_size = sizeof(word_8_code); + break; + case 2 : + target_code_src = word_16_code; + target_code_size = sizeof(word_16_code); + break; + case 4 : + target_code_src = word_32_code; + target_code_size = sizeof(word_32_code); + break; + default: + LOG_ERROR("Unsupported bank buswidth %d, can't do block memory writes", bank->bus_width); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + /* flash write code */ + if (!cfi_info->write_algorithm) + { + if (target_code_size > sizeof(target_code)) + { + LOG_WARNING("Internal error - target code buffer to small. Increase CFI_MAX_INTEL_CODESIZE and recompile."); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + cfi_fix_code_endian(target, target_code, target_code_src, target_code_size / 4); + + /* Get memory for block write handler */ + retval = target_alloc_working_area(target, target_code_size, &cfi_info->write_algorithm); + if (retval != ERROR_OK) + { + LOG_WARNING("No working area available, can't do block memory writes"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + }; + + /* write algorithm code to working area */ + retval = target_write_buffer(target, cfi_info->write_algorithm->address, target_code_size, target_code); + if (retval != ERROR_OK) + { + LOG_ERROR("Unable to write block write code to target"); + goto cleanup; + } + } + + /* Get a workspace buffer for the data to flash starting with 32k size. + Half size until buffer would be smaller 256 Bytem then fail back */ + /* FIXME Why 256 bytes, why not 32 bytes (smallest flash write page */ + while (target_alloc_working_area(target, buffer_size, &source) != ERROR_OK) + { + buffer_size /= 2; + if (buffer_size <= 256) + { + LOG_WARNING("no large enough working area available, can't do block memory writes"); + retval = ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + goto cleanup; + } + }; + + /* setup algo registers */ + 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); + init_reg_param(®_params[6], "r6", 32, PARAM_OUT); + + /* prepare command and status register patterns */ + write_command_val = cfi_command_val(bank, 0x40); + busy_pattern_val = cfi_command_val(bank, 0x80); + error_pattern_val = cfi_command_val(bank, 0x7e); + + LOG_INFO("Using target buffer at 0x%08" PRIx32 " and of size 0x%04" PRIx32, source->address, buffer_size); + + /* Programming main loop */ + while (count > 0) + { + uint32_t thisrun_count = (count > buffer_size) ? buffer_size : count; + uint32_t wsm_error; + + if ((retval = target_write_buffer(target, source->address, thisrun_count, buffer)) != ERROR_OK) + { + goto cleanup; + } + + 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 / bank->bus_width); + + buf_set_u32(reg_params[3].value, 0, 32, write_command_val); + buf_set_u32(reg_params[5].value, 0, 32, busy_pattern_val); + buf_set_u32(reg_params[6].value, 0, 32, error_pattern_val); + + LOG_INFO("Write 0x%04" PRIx32 " bytes to flash at 0x%08" PRIx32 , thisrun_count, address); + + /* Execute algorithm, assume breakpoint for last instruction */ + retval = target_run_algorithm(target, 0, NULL, 7, reg_params, + cfi_info->write_algorithm->address, + cfi_info->write_algorithm->address + target_code_size - sizeof(uint32_t), + 10000, /* 10s should be enough for max. 32k of data */ + &armv4_5_info); + + /* On failure try a fall back to direct word writes */ + if (retval != ERROR_OK) + { + cfi_intel_clear_status_register(bank); + LOG_ERROR("Execution of flash algorythm failed. Can't fall back. Please report."); + retval = ERROR_FLASH_OPERATION_FAILED; + /* retval = ERROR_TARGET_RESOURCE_NOT_AVAILABLE; */ + /* FIXME To allow fall back or recovery, we must save the actual status + somewhere, so that a higher level code can start recovery. */ + goto cleanup; + } + + /* Check return value from algo code */ + wsm_error = buf_get_u32(reg_params[4].value, 0, 32) & error_pattern_val; + if (wsm_error) + { + /* read status register (outputs debug inforation) */ + cfi_intel_wait_status_busy(bank, 100); + cfi_intel_clear_status_register(bank); + retval = ERROR_FLASH_OPERATION_FAILED; + goto cleanup; + } + + buffer += thisrun_count; + address += thisrun_count; + count -= thisrun_count; + } + + /* free up resources */ +cleanup: + if (source) + target_free_working_area(target, source); + + if (cfi_info->write_algorithm) + { + target_free_working_area(target, cfi_info->write_algorithm); + cfi_info->write_algorithm = NULL; + } + + 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]); + + return retval; +} + +static int cfi_spansion_write_block(struct flash_bank *bank, uint8_t *buffer, uint32_t address, uint32_t count) +{ + struct cfi_flash_bank *cfi_info = bank->driver_priv; + struct cfi_spansion_pri_ext *pri_ext = cfi_info->pri_ext; + struct target *target = bank->target; + struct reg_param reg_params[10]; + struct armv4_5_algorithm armv4_5_info; + struct working_area *source; + uint32_t buffer_size = 32768; + uint32_t status; + int retval, retvaltemp; + int exit_code = ERROR_OK; + + /* input parameters - */ + /* R0 = source address */ + /* R1 = destination address */ + /* R2 = number of writes */ + /* R3 = flash write command */ + /* R4 = constant to mask DQ7 bits (also used for Dq5 with shift) */ + /* output parameters - */ + /* R5 = 0x80 ok 0x00 bad */ + /* temp registers - */ + /* R6 = value read from flash to test status */ + /* R7 = holding register */ + /* unlock registers - */ + /* R8 = unlock1_addr */ + /* R9 = unlock1_cmd */ + /* R10 = unlock2_addr */ + /* R11 = unlock2_cmd */ + + static const uint32_t word_32_code[] = { + /* 00008100 : */ + 0xe4905004, /* ldr r5, [r0], #4 */ + 0xe5889000, /* str r9, [r8] */ + 0xe58ab000, /* str r11, [r10] */ + 0xe5883000, /* str r3, [r8] */ + 0xe5815000, /* str r5, [r1] */ + 0xe1a00000, /* nop */ + /* */ + /* 00008110 : */ + 0xe5916000, /* ldr r6, [r1] */ + 0xe0257006, /* eor r7, r5, r6 */ + 0xe0147007, /* ands r7, r4, r7 */ + 0x0a000007, /* beq 8140 ; b if DQ7 == Data7 */ + 0xe0166124, /* ands r6, r6, r4, lsr #2 */ + 0x0afffff9, /* beq 8110 ; b if DQ5 low */ + 0xe5916000, /* ldr r6, [r1] */ + 0xe0257006, /* eor r7, r5, r6 */ + 0xe0147007, /* ands r7, r4, r7 */ + 0x0a000001, /* beq 8140 ; b if DQ7 == Data7 */ + 0xe3a05000, /* mov r5, #0 ; 0x0 - return 0x00, error */ + 0x1a000004, /* bne 8154 */ + /* */ + /* 00008140 : */ + 0xe2522001, /* subs r2, r2, #1 ; 0x1 */ + 0x03a05080, /* moveq r5, #128 ; 0x80 */ + 0x0a000001, /* beq 8154 */ + 0xe2811004, /* add r1, r1, #4 ; 0x4 */ + 0xeaffffe8, /* b 8100 */ + /* */ + /* 00008154 : */ + 0xeafffffe /* b 8154 */ + }; + + static const uint32_t word_16_code[] = { + /* 00008158 : */ + 0xe0d050b2, /* ldrh r5, [r0], #2 */ + 0xe1c890b0, /* strh r9, [r8] */ + 0xe1cab0b0, /* strh r11, [r10] */ + 0xe1c830b0, /* strh r3, [r8] */ + 0xe1c150b0, /* strh r5, [r1] */ + 0xe1a00000, /* nop (mov r0,r0) */ + /* */ + /* 00008168 : */ + 0xe1d160b0, /* ldrh r6, [r1] */ + 0xe0257006, /* eor r7, r5, r6 */ + 0xe0147007, /* ands r7, r4, r7 */ + 0x0a000007, /* beq 8198 */ + 0xe0166124, /* ands r6, r6, r4, lsr #2 */ + 0x0afffff9, /* beq 8168 */ + 0xe1d160b0, /* ldrh r6, [r1] */ + 0xe0257006, /* eor r7, r5, r6 */ + 0xe0147007, /* ands r7, r4, r7 */ + 0x0a000001, /* beq 8198 */ + 0xe3a05000, /* mov r5, #0 ; 0x0 */ + 0x1a000004, /* bne 81ac */ + /* */ + /* 00008198 : */ + 0xe2522001, /* subs r2, r2, #1 ; 0x1 */ + 0x03a05080, /* moveq r5, #128 ; 0x80 */ + 0x0a000001, /* beq 81ac */ + 0xe2811002, /* add r1, r1, #2 ; 0x2 */ + 0xeaffffe8, /* b 8158 */ + /* */ + /* 000081ac : */ + 0xeafffffe /* b 81ac */ + }; + + static const uint32_t word_16_code_dq7only[] = { + /* : */ + 0xe0d050b2, /* ldrh r5, [r0], #2 */ + 0xe1c890b0, /* strh r9, [r8] */ + 0xe1cab0b0, /* strh r11, [r10] */ + 0xe1c830b0, /* strh r3, [r8] */ + 0xe1c150b0, /* strh r5, [r1] */ + 0xe1a00000, /* nop (mov r0,r0) */ + /* */ + /* : */ + 0xe1d160b0, /* ldrh r6, [r1] */ + 0xe0257006, /* eor r7, r5, r6 */ + 0xe2177080, /* ands r7, #0x80 */ + 0x1afffffb, /* bne 8168 */ + /* */ + 0xe2522001, /* subs r2, r2, #1 ; 0x1 */ + 0x03a05080, /* moveq r5, #128 ; 0x80 */ + 0x0a000001, /* beq 81ac */ + 0xe2811002, /* add r1, r1, #2 ; 0x2 */ + 0xeafffff0, /* b 8158 */ + /* */ + /* 000081ac : */ + 0xeafffffe /* b 81ac */ + }; + + static const uint32_t word_8_code[] = { + /* 000081b0 : */ + 0xe4d05001, /* ldrb r5, [r0], #1 */ + 0xe5c89000, /* strb r9, [r8] */ + 0xe5cab000, /* strb r11, [r10] */ + 0xe5c83000, /* strb r3, [r8] */ + 0xe5c15000, /* strb r5, [r1] */ + 0xe1a00000, /* nop (mov r0,r0) */ + /* */ + /* 000081c0 : */ + 0xe5d16000, /* ldrb r6, [r1] */ + 0xe0257006, /* eor r7, r5, r6 */ + 0xe0147007, /* ands r7, r4, r7 */ + 0x0a000007, /* beq 81f0 */ + 0xe0166124, /* ands r6, r6, r4, lsr #2 */ + 0x0afffff9, /* beq 81c0 */ + 0xe5d16000, /* ldrb r6, [r1] */ + 0xe0257006, /* eor r7, r5, r6 */ + 0xe0147007, /* ands r7, r4, r7 */ + 0x0a000001, /* beq 81f0 */ + 0xe3a05000, /* mov r5, #0 ; 0x0 */ + 0x1a000004, /* bne 8204 */ + /* */ + /* 000081f0 : */ + 0xe2522001, /* subs r2, r2, #1 ; 0x1 */ + 0x03a05080, /* moveq r5, #128 ; 0x80 */ + 0x0a000001, /* beq 8204 */ + 0xe2811001, /* add r1, r1, #1 ; 0x1 */ + 0xeaffffe8, /* b 81b0 */ + /* */ + /* 00008204 : */ + 0xeafffffe /* b 8204 */ + }; + + 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; + + int target_code_size; + const uint32_t *target_code_src; + + switch (bank->bus_width) + { + case 1 : + target_code_src = word_8_code; + target_code_size = sizeof(word_8_code); + break; + case 2 : + /* Check for DQ5 support */ + if( cfi_info->status_poll_mask & (1 << 5) ) + { + target_code_src = word_16_code; + target_code_size = sizeof(word_16_code); + } + else + { + /* No DQ5 support. Use DQ7 DATA# polling only. */ + target_code_src = word_16_code_dq7only; + target_code_size = sizeof(word_16_code_dq7only); + } + break; + case 4 : + target_code_src = word_32_code; + target_code_size = sizeof(word_32_code); + break; + default: + LOG_ERROR("Unsupported bank buswidth %d, can't do block memory writes", bank->bus_width); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + /* flash write code */ + if (!cfi_info->write_algorithm) + { + uint8_t *target_code; + + /* convert bus-width dependent algorithm code to correct endiannes */ + target_code = malloc(target_code_size); + cfi_fix_code_endian(target, target_code, target_code_src, target_code_size / 4); + + /* allocate working area */ + retval = target_alloc_working_area(target, target_code_size, + &cfi_info->write_algorithm); + if (retval != ERROR_OK) + { + free(target_code); + return retval; + } + + /* write algorithm code to working area */ + if ((retval = target_write_buffer(target, cfi_info->write_algorithm->address, + target_code_size, target_code)) != ERROR_OK) + { + free(target_code); + return retval; + } + + free(target_code); + } + /* the following code still assumes target code is fixed 24*4 bytes */ + + 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 (cfi_info->write_algorithm) + target_free_working_area(target, cfi_info->write_algorithm); + + LOG_WARNING("not enough working area available, can't do block memory writes"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + }; + + 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_IN); + init_reg_param(®_params[6], "r8", 32, PARAM_OUT); + init_reg_param(®_params[7], "r9", 32, PARAM_OUT); + init_reg_param(®_params[8], "r10", 32, PARAM_OUT); + init_reg_param(®_params[9], "r11", 32, PARAM_OUT); + + while (count > 0) + { + uint32_t thisrun_count = (count > buffer_size) ? buffer_size : count; + + retvaltemp = target_write_buffer(target, source->address, thisrun_count, 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 / bank->bus_width); + buf_set_u32(reg_params[3].value, 0, 32, cfi_command_val(bank, 0xA0)); + buf_set_u32(reg_params[4].value, 0, 32, cfi_command_val(bank, 0x80)); + buf_set_u32(reg_params[6].value, 0, 32, flash_address(bank, 0, pri_ext->_unlock1)); + buf_set_u32(reg_params[7].value, 0, 32, 0xaaaaaaaa); + buf_set_u32(reg_params[8].value, 0, 32, flash_address(bank, 0, pri_ext->_unlock2)); + buf_set_u32(reg_params[9].value, 0, 32, 0x55555555); + + retval = target_run_algorithm(target, 0, NULL, 10, reg_params, + cfi_info->write_algorithm->address, + cfi_info->write_algorithm->address + ((target_code_size) - 4), + 10000, &armv4_5_info); + + status = buf_get_u32(reg_params[5].value, 0, 32); + + if ((retval != ERROR_OK) || (retvaltemp != ERROR_OK) || status != 0x80) + { + LOG_DEBUG("status: 0x%" PRIx32 , status); + exit_code = ERROR_FLASH_OPERATION_FAILED; + break; + } + + buffer += thisrun_count; + address += thisrun_count; + count -= thisrun_count; + } + + target_free_all_working_areas(target); + + 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]); + destroy_reg_param(®_params[8]); + destroy_reg_param(®_params[9]); + + return exit_code; +} + +static int cfi_intel_write_word(struct flash_bank *bank, uint8_t *word, uint32_t address) +{ + int retval; + struct cfi_flash_bank *cfi_info = bank->driver_priv; + struct target *target = bank->target; + uint8_t command[8]; + + cfi_intel_clear_status_register(bank); + cfi_command(bank, 0x40, command); + if ((retval = target_write_memory(target, address, bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + + if ((retval = target_write_memory(target, address, bank->bus_width, 1, word)) != ERROR_OK) + { + return retval; + } + + if (cfi_intel_wait_status_busy(bank, 1000 * (1 << cfi_info->word_write_timeout_max)) != 0x80) + { + cfi_command(bank, 0xff, command); + if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + + LOG_ERROR("couldn't write word at base 0x%" PRIx32 ", address %" PRIx32 , bank->base, address); + return ERROR_FLASH_OPERATION_FAILED; + } + + return ERROR_OK; +} + +static int cfi_intel_write_words(struct flash_bank *bank, uint8_t *word, uint32_t wordcount, uint32_t address) +{ + int retval; + struct cfi_flash_bank *cfi_info = bank->driver_priv; + struct target *target = bank->target; + uint8_t command[8]; + + /* Calculate buffer size and boundary mask */ + uint32_t buffersize = (1UL << cfi_info->max_buf_write_size) * (bank->bus_width / bank->chip_width); + uint32_t buffermask = buffersize-1; + uint32_t bufferwsize; + + /* Check for valid range */ + if (address & buffermask) + { + LOG_ERROR("Write address at base 0x%" PRIx32 ", address %" PRIx32 " not aligned to 2^%d boundary", + bank->base, address, cfi_info->max_buf_write_size); + return ERROR_FLASH_OPERATION_FAILED; + } + switch (bank->chip_width) + { + case 4 : bufferwsize = buffersize / 4; break; + case 2 : bufferwsize = buffersize / 2; break; + case 1 : bufferwsize = buffersize; break; + default: + LOG_ERROR("Unsupported chip width %d", bank->chip_width); + return ERROR_FLASH_OPERATION_FAILED; + } + + bufferwsize/=(bank->bus_width / bank->chip_width); + + + /* Check for valid size */ + if (wordcount > bufferwsize) + { + LOG_ERROR("Number of data words %" PRId32 " exceeds available buffersize %" PRId32 , wordcount, buffersize); + return ERROR_FLASH_OPERATION_FAILED; + } + + /* Write to flash buffer */ + cfi_intel_clear_status_register(bank); + + /* Initiate buffer operation _*/ + cfi_command(bank, 0xE8, command); + if ((retval = target_write_memory(target, address, bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + if (cfi_intel_wait_status_busy(bank, 1000 * (1 << cfi_info->buf_write_timeout_max)) != 0x80) + { + cfi_command(bank, 0xff, command); + if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + + LOG_ERROR("couldn't start buffer write operation at base 0x%" PRIx32 ", address %" PRIx32 , bank->base, address); + return ERROR_FLASH_OPERATION_FAILED; + } + + /* Write buffer wordcount-1 and data words */ + cfi_command(bank, bufferwsize-1, command); + if ((retval = target_write_memory(target, address, bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + + if ((retval = target_write_memory(target, address, bank->bus_width, bufferwsize, word)) != ERROR_OK) + { + return retval; + } + + /* Commit write operation */ + cfi_command(bank, 0xd0, command); + if ((retval = target_write_memory(target, address, bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + if (cfi_intel_wait_status_busy(bank, 1000 * (1 << cfi_info->buf_write_timeout_max)) != 0x80) + { + cfi_command(bank, 0xff, command); + if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + + LOG_ERROR("Buffer write at base 0x%" PRIx32 ", address %" PRIx32 " failed.", bank->base, address); + return ERROR_FLASH_OPERATION_FAILED; + } + + return ERROR_OK; +} + +static int cfi_spansion_write_word(struct flash_bank *bank, uint8_t *word, uint32_t address) +{ + int retval; + struct cfi_flash_bank *cfi_info = bank->driver_priv; + struct cfi_spansion_pri_ext *pri_ext = cfi_info->pri_ext; + struct target *target = bank->target; + uint8_t command[8]; + + cfi_command(bank, 0xaa, command); + if ((retval = target_write_memory(target, flash_address(bank, 0, pri_ext->_unlock1), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + + cfi_command(bank, 0x55, command); + if ((retval = target_write_memory(target, flash_address(bank, 0, pri_ext->_unlock2), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + + cfi_command(bank, 0xa0, command); + if ((retval = target_write_memory(target, flash_address(bank, 0, pri_ext->_unlock1), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + + if ((retval = target_write_memory(target, address, bank->bus_width, 1, word)) != ERROR_OK) + { + return retval; + } + + if (cfi_spansion_wait_status_busy(bank, 1000 * (1 << cfi_info->word_write_timeout_max)) != ERROR_OK) + { + cfi_command(bank, 0xf0, command); + if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + + LOG_ERROR("couldn't write word at base 0x%" PRIx32 ", address %" PRIx32 , bank->base, address); + return ERROR_FLASH_OPERATION_FAILED; + } + + return ERROR_OK; +} + +static int cfi_spansion_write_words(struct flash_bank *bank, uint8_t *word, uint32_t wordcount, uint32_t address) +{ + int retval; + struct cfi_flash_bank *cfi_info = bank->driver_priv; + struct target *target = bank->target; + uint8_t command[8]; + struct cfi_spansion_pri_ext *pri_ext = cfi_info->pri_ext; + + /* Calculate buffer size and boundary mask */ + uint32_t buffersize = (1UL << cfi_info->max_buf_write_size) * (bank->bus_width / bank->chip_width); + uint32_t buffermask = buffersize-1; + uint32_t bufferwsize; + + /* Check for valid range */ + if (address & buffermask) + { + LOG_ERROR("Write address at base 0x%" PRIx32 ", address %" PRIx32 " not aligned to 2^%d boundary", bank->base, address, cfi_info->max_buf_write_size); + return ERROR_FLASH_OPERATION_FAILED; + } + switch (bank->chip_width) + { + case 4 : bufferwsize = buffersize / 4; break; + case 2 : bufferwsize = buffersize / 2; break; + case 1 : bufferwsize = buffersize; break; + default: + LOG_ERROR("Unsupported chip width %d", bank->chip_width); + return ERROR_FLASH_OPERATION_FAILED; + } + + bufferwsize/=(bank->bus_width / bank->chip_width); + + /* Check for valid size */ + if (wordcount > bufferwsize) + { + LOG_ERROR("Number of data words %" PRId32 " exceeds available buffersize %" PRId32, wordcount, buffersize); + return ERROR_FLASH_OPERATION_FAILED; + } + + // Unlock + cfi_command(bank, 0xaa, command); + if ((retval = target_write_memory(target, flash_address(bank, 0, pri_ext->_unlock1), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + + cfi_command(bank, 0x55, command); + if ((retval = target_write_memory(target, flash_address(bank, 0, pri_ext->_unlock2), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + + // Buffer load command + cfi_command(bank, 0x25, command); + if ((retval = target_write_memory(target, address, bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + + /* Write buffer wordcount-1 and data words */ + cfi_command(bank, bufferwsize-1, command); + if ((retval = target_write_memory(target, address, bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + + if ((retval = target_write_memory(target, address, bank->bus_width, bufferwsize, word)) != ERROR_OK) + { + return retval; + } + + /* Commit write operation */ + cfi_command(bank, 0x29, command); + if ((retval = target_write_memory(target, address, bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + + if (cfi_spansion_wait_status_busy(bank, 1000 * (1 << cfi_info->word_write_timeout_max)) != ERROR_OK) + { + cfi_command(bank, 0xf0, command); + if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + + LOG_ERROR("couldn't write block at base 0x%" PRIx32 ", address %" PRIx32 ", size %" PRIx32 , bank->base, address, bufferwsize); + return ERROR_FLASH_OPERATION_FAILED; + } + + return ERROR_OK; +} + +static int cfi_write_word(struct flash_bank *bank, uint8_t *word, uint32_t address) +{ + struct cfi_flash_bank *cfi_info = bank->driver_priv; + + switch (cfi_info->pri_id) + { + case 1: + case 3: + return cfi_intel_write_word(bank, word, address); + break; + case 2: + return cfi_spansion_write_word(bank, word, address); + break; + default: + LOG_ERROR("cfi primary command set %i unsupported", cfi_info->pri_id); + break; + } + + return ERROR_FLASH_OPERATION_FAILED; +} + +static int cfi_write_words(struct flash_bank *bank, uint8_t *word, uint32_t wordcount, uint32_t address) +{ + struct cfi_flash_bank *cfi_info = bank->driver_priv; + + switch (cfi_info->pri_id) + { + case 1: + case 3: + return cfi_intel_write_words(bank, word, wordcount, address); + break; + case 2: + return cfi_spansion_write_words(bank, word, wordcount, address); + break; + default: + LOG_ERROR("cfi primary command set %i unsupported", cfi_info->pri_id); + break; + } + + return ERROR_FLASH_OPERATION_FAILED; +} + +int cfi_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) +{ + struct cfi_flash_bank *cfi_info = bank->driver_priv; + struct target *target = bank->target; + uint32_t address = bank->base + offset; /* address of first byte to be programmed */ + uint32_t write_p, copy_p; + int align; /* number of unaligned bytes */ + int blk_count; /* number of bus_width bytes for block copy */ + uint8_t current_word[CFI_MAX_BUS_WIDTH * 4]; /* word (bus_width size) currently being programmed */ + int i; + int retval; + + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (offset + count > bank->size) + return ERROR_FLASH_DST_OUT_OF_BANK; + + if (cfi_info->qry[0] != 'Q') + return ERROR_FLASH_BANK_NOT_PROBED; + + /* start at the first byte of the first word (bus_width size) */ + write_p = address & ~(bank->bus_width - 1); + if ((align = address - write_p) != 0) + { + LOG_INFO("Fixup %d unaligned head bytes", align); + + for (i = 0; i < bank->bus_width; i++) + current_word[i] = 0; + copy_p = write_p; + + /* copy bytes before the first write address */ + for (i = 0; i < align; ++i, ++copy_p) + { + uint8_t byte; + if ((retval = target_read_memory(target, copy_p, 1, 1, &byte)) != ERROR_OK) + { + return retval; + } + cfi_add_byte(bank, current_word, byte); + } + + /* add bytes from the buffer */ + for (; (i < bank->bus_width) && (count > 0); i++) + { + cfi_add_byte(bank, current_word, *buffer++); + count--; + copy_p++; + } + + /* if the buffer is already finished, copy bytes after the last write address */ + for (; (count == 0) && (i < bank->bus_width); ++i, ++copy_p) + { + uint8_t byte; + if ((retval = target_read_memory(target, copy_p, 1, 1, &byte)) != ERROR_OK) + { + return retval; + } + cfi_add_byte(bank, current_word, byte); + } + + retval = cfi_write_word(bank, current_word, write_p); + if (retval != ERROR_OK) + return retval; + write_p = copy_p; + } + + /* handle blocks of bus_size aligned bytes */ + blk_count = count & ~(bank->bus_width - 1); /* round down, leave tail bytes */ + switch (cfi_info->pri_id) + { + /* try block writes (fails without working area) */ + case 1: + case 3: + retval = cfi_intel_write_block(bank, buffer, write_p, blk_count); + break; + case 2: + retval = cfi_spansion_write_block(bank, buffer, write_p, blk_count); + break; + default: + LOG_ERROR("cfi primary command set %i unsupported", cfi_info->pri_id); + retval = ERROR_FLASH_OPERATION_FAILED; + break; + } + if (retval == ERROR_OK) + { + /* Increment pointers and decrease count on succesful block write */ + buffer += blk_count; + write_p += blk_count; + count -= blk_count; + } + else + { + if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) + { + //adjust buffersize for chip width + uint32_t buffersize = (1UL << cfi_info->max_buf_write_size) * (bank->bus_width / bank->chip_width); + uint32_t buffermask = buffersize-1; + uint32_t bufferwsize; + + switch (bank->chip_width) + { + case 4 : bufferwsize = buffersize / 4; break; + case 2 : bufferwsize = buffersize / 2; break; + case 1 : bufferwsize = buffersize; break; + default: + LOG_ERROR("Unsupported chip width %d", bank->chip_width); + return ERROR_FLASH_OPERATION_FAILED; + } + + bufferwsize/=(bank->bus_width / bank->chip_width); + + /* fall back to memory writes */ + while (count >= (uint32_t)bank->bus_width) + { + int fallback; + if ((write_p & 0xff) == 0) + { + LOG_INFO("Programming at %08" PRIx32 ", count %08" PRIx32 " bytes remaining", write_p, count); + } + fallback = 1; + if ((bufferwsize > 0) && (count >= buffersize) && !(write_p & buffermask)) + { + retval = cfi_write_words(bank, buffer, bufferwsize, write_p); + if (retval == ERROR_OK) + { + buffer += buffersize; + write_p += buffersize; + count -= buffersize; + fallback = 0; + } + } + /* try the slow way? */ + if (fallback) + { + for (i = 0; i < bank->bus_width; i++) + current_word[i] = 0; + + for (i = 0; i < bank->bus_width; i++) + { + cfi_add_byte(bank, current_word, *buffer++); + } + + retval = cfi_write_word(bank, current_word, write_p); + if (retval != ERROR_OK) + return retval; + + write_p += bank->bus_width; + count -= bank->bus_width; + } + } + } + else + return retval; + } + + /* return to read array mode, so we can read from flash again for padding */ + cfi_command(bank, 0xf0, current_word); + if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, current_word)) != ERROR_OK) + { + return retval; + } + cfi_command(bank, 0xff, current_word); + if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, current_word)) != ERROR_OK) + { + return retval; + } + + /* handle unaligned tail bytes */ + if (count > 0) + { + LOG_INFO("Fixup %" PRId32 " unaligned tail bytes", count); + + copy_p = write_p; + for (i = 0; i < bank->bus_width; i++) + current_word[i] = 0; + + for (i = 0; (i < bank->bus_width) && (count > 0); ++i, ++copy_p) + { + cfi_add_byte(bank, current_word, *buffer++); + count--; + } + for (; i < bank->bus_width; ++i, ++copy_p) + { + uint8_t byte; + if ((retval = target_read_memory(target, copy_p, 1, 1, &byte)) != ERROR_OK) + { + return retval; + } + cfi_add_byte(bank, current_word, byte); + } + retval = cfi_write_word(bank, current_word, write_p); + if (retval != ERROR_OK) + return retval; + } + + /* return to read array mode */ + cfi_command(bank, 0xf0, current_word); + if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, current_word)) != ERROR_OK) + { + return retval; + } + cfi_command(bank, 0xff, current_word); + return target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, current_word); +} + +static void cfi_fixup_atmel_reversed_erase_regions(struct flash_bank *bank, void *param) +{ + (void) param; + struct cfi_flash_bank *cfi_info = bank->driver_priv; + struct cfi_spansion_pri_ext *pri_ext = cfi_info->pri_ext; + + pri_ext->_reversed_geometry = 1; +} + +static void cfi_fixup_0002_erase_regions(struct flash_bank *bank, void *param) +{ + int i; + struct cfi_flash_bank *cfi_info = bank->driver_priv; + struct cfi_spansion_pri_ext *pri_ext = cfi_info->pri_ext; + (void) param; + + if ((pri_ext->_reversed_geometry) || (pri_ext->TopBottom == 3)) + { + LOG_DEBUG("swapping reversed erase region information on cmdset 0002 device"); + + for (i = 0; i < cfi_info->num_erase_regions / 2; i++) + { + int j = (cfi_info->num_erase_regions - 1) - i; + uint32_t swap; + + swap = cfi_info->erase_region_info[i]; + cfi_info->erase_region_info[i] = cfi_info->erase_region_info[j]; + cfi_info->erase_region_info[j] = swap; + } + } +} + +static void cfi_fixup_0002_unlock_addresses(struct flash_bank *bank, void *param) +{ + struct cfi_flash_bank *cfi_info = bank->driver_priv; + struct cfi_spansion_pri_ext *pri_ext = cfi_info->pri_ext; + struct cfi_unlock_addresses *unlock_addresses = param; + + pri_ext->_unlock1 = unlock_addresses->unlock1; + pri_ext->_unlock2 = unlock_addresses->unlock2; +} + + +static int cfi_query_string(struct flash_bank *bank, int address) +{ + struct cfi_flash_bank *cfi_info = bank->driver_priv; + struct target *target = bank->target; + int retval; + uint8_t command[8]; + + cfi_command(bank, 0x98, command); + if ((retval = target_write_memory(target, flash_address(bank, 0, address), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + + cfi_info->qry[0] = cfi_query_u8(bank, 0, 0x10); + cfi_info->qry[1] = cfi_query_u8(bank, 0, 0x11); + cfi_info->qry[2] = cfi_query_u8(bank, 0, 0x12); + + LOG_DEBUG("CFI qry returned: 0x%2.2x 0x%2.2x 0x%2.2x", cfi_info->qry[0], cfi_info->qry[1], cfi_info->qry[2]); + + if ((cfi_info->qry[0] != 'Q') || (cfi_info->qry[1] != 'R') || (cfi_info->qry[2] != 'Y')) + { + cfi_command(bank, 0xf0, command); + if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + cfi_command(bank, 0xff, command); + if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + LOG_ERROR("Could not probe bank: no QRY"); + return ERROR_FLASH_BANK_INVALID; + } + + return ERROR_OK; +} + +static int cfi_probe(struct flash_bank *bank) +{ + struct cfi_flash_bank *cfi_info = bank->driver_priv; + struct target *target = bank->target; + uint8_t command[8]; + int num_sectors = 0; + int i; + int sector = 0; + uint32_t unlock1 = 0x555; + uint32_t unlock2 = 0x2aa; + int retval; + + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + cfi_info->probed = 0; + + /* JEDEC standard JESD21C uses 0x5555 and 0x2aaa as unlock addresses, + * while CFI compatible AMD/Spansion flashes use 0x555 and 0x2aa + */ + if (cfi_info->jedec_probe) + { + unlock1 = 0x5555; + unlock2 = 0x2aaa; + } + + /* switch to read identifier codes mode ("AUTOSELECT") */ + cfi_command(bank, 0xaa, command); + if ((retval = target_write_memory(target, flash_address(bank, 0, unlock1), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + cfi_command(bank, 0x55, command); + if ((retval = target_write_memory(target, flash_address(bank, 0, unlock2), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + cfi_command(bank, 0x90, command); + if ((retval = target_write_memory(target, flash_address(bank, 0, unlock1), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + + if (bank->chip_width == 1) + { + uint8_t manufacturer, device_id; + if ((retval = target_read_u8(target, flash_address(bank, 0, 0x00), &manufacturer)) != ERROR_OK) + { + return retval; + } + if ((retval = target_read_u8(target, flash_address(bank, 0, 0x01), &device_id)) != ERROR_OK) + { + return retval; + } + cfi_info->manufacturer = manufacturer; + cfi_info->device_id = device_id; + } + else if (bank->chip_width == 2) + { + if ((retval = target_read_u16(target, flash_address(bank, 0, 0x00), &cfi_info->manufacturer)) != ERROR_OK) + { + return retval; + } + if ((retval = target_read_u16(target, flash_address(bank, 0, 0x01), &cfi_info->device_id)) != ERROR_OK) + { + return retval; + } + } + + LOG_INFO("Flash Manufacturer/Device: 0x%04x 0x%04x", cfi_info->manufacturer, cfi_info->device_id); + /* switch back to read array mode */ + cfi_command(bank, 0xf0, command); + if ((retval = target_write_memory(target, flash_address(bank, 0, 0x00), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + cfi_command(bank, 0xff, command); + if ((retval = target_write_memory(target, flash_address(bank, 0, 0x00), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + + /* check device/manufacturer ID for known non-CFI flashes. */ + cfi_fixup_non_cfi(bank); + + /* query only if this is a CFI compatible flash, + * otherwise the relevant info has already been filled in + */ + if (cfi_info->not_cfi == 0) + { + int retval; + + /* enter CFI query mode + * according to JEDEC Standard No. 68.01, + * a single bus sequence with address = 0x55, data = 0x98 should put + * the device into CFI query mode. + * + * SST flashes clearly violate this, and we will consider them incompatbile for now + */ + + retval = cfi_query_string(bank, 0x55); + if (retval != ERROR_OK) + { + /* + * Spansion S29WS-N CFI query fix is to try 0x555 if 0x55 fails. Should + * be harmless enough: + * + * http://www.infradead.org/pipermail/linux-mtd/2005-September/013618.html + */ + LOG_USER("Try workaround w/0x555 instead of 0x55 to get QRY."); + retval = cfi_query_string(bank, 0x555); + } + if (retval != ERROR_OK) + return retval; + + cfi_info->pri_id = cfi_query_u16(bank, 0, 0x13); + cfi_info->pri_addr = cfi_query_u16(bank, 0, 0x15); + cfi_info->alt_id = cfi_query_u16(bank, 0, 0x17); + cfi_info->alt_addr = cfi_query_u16(bank, 0, 0x19); + + LOG_DEBUG("qry: '%c%c%c', pri_id: 0x%4.4x, pri_addr: 0x%4.4x, alt_id: 0x%4.4x, alt_addr: 0x%4.4x", cfi_info->qry[0], cfi_info->qry[1], cfi_info->qry[2], cfi_info->pri_id, cfi_info->pri_addr, cfi_info->alt_id, cfi_info->alt_addr); + + cfi_info->vcc_min = cfi_query_u8(bank, 0, 0x1b); + cfi_info->vcc_max = cfi_query_u8(bank, 0, 0x1c); + cfi_info->vpp_min = cfi_query_u8(bank, 0, 0x1d); + cfi_info->vpp_max = cfi_query_u8(bank, 0, 0x1e); + cfi_info->word_write_timeout_typ = cfi_query_u8(bank, 0, 0x1f); + cfi_info->buf_write_timeout_typ = cfi_query_u8(bank, 0, 0x20); + cfi_info->block_erase_timeout_typ = cfi_query_u8(bank, 0, 0x21); + cfi_info->chip_erase_timeout_typ = cfi_query_u8(bank, 0, 0x22); + cfi_info->word_write_timeout_max = cfi_query_u8(bank, 0, 0x23); + cfi_info->buf_write_timeout_max = cfi_query_u8(bank, 0, 0x24); + cfi_info->block_erase_timeout_max = cfi_query_u8(bank, 0, 0x25); + cfi_info->chip_erase_timeout_max = cfi_query_u8(bank, 0, 0x26); + + LOG_DEBUG("Vcc min: %1.1x.%1.1x, Vcc max: %1.1x.%1.1x, Vpp min: %1.1x.%1.1x, Vpp max: %1.1x.%1.1x", + (cfi_info->vcc_min & 0xf0) >> 4, cfi_info->vcc_min & 0x0f, + (cfi_info->vcc_max & 0xf0) >> 4, cfi_info->vcc_max & 0x0f, + (cfi_info->vpp_min & 0xf0) >> 4, cfi_info->vpp_min & 0x0f, + (cfi_info->vpp_max & 0xf0) >> 4, cfi_info->vpp_max & 0x0f); + LOG_DEBUG("typ. word write timeout: %u, typ. buf write timeout: %u, typ. block erase timeout: %u, typ. chip erase timeout: %u", 1 << cfi_info->word_write_timeout_typ, 1 << cfi_info->buf_write_timeout_typ, + 1 << cfi_info->block_erase_timeout_typ, 1 << cfi_info->chip_erase_timeout_typ); + LOG_DEBUG("max. word write timeout: %u, max. buf write timeout: %u, max. block erase timeout: %u, max. chip erase timeout: %u", (1 << cfi_info->word_write_timeout_max) * (1 << cfi_info->word_write_timeout_typ), + (1 << cfi_info->buf_write_timeout_max) * (1 << cfi_info->buf_write_timeout_typ), + (1 << cfi_info->block_erase_timeout_max) * (1 << cfi_info->block_erase_timeout_typ), + (1 << cfi_info->chip_erase_timeout_max) * (1 << cfi_info->chip_erase_timeout_typ)); + + cfi_info->dev_size = 1 << cfi_query_u8(bank, 0, 0x27); + cfi_info->interface_desc = cfi_query_u16(bank, 0, 0x28); + cfi_info->max_buf_write_size = cfi_query_u16(bank, 0, 0x2a); + cfi_info->num_erase_regions = cfi_query_u8(bank, 0, 0x2c); + + LOG_DEBUG("size: 0x%" PRIx32 ", interface desc: %i, max buffer write size: %x", cfi_info->dev_size, cfi_info->interface_desc, (1 << cfi_info->max_buf_write_size)); + + if (cfi_info->num_erase_regions) + { + cfi_info->erase_region_info = malloc(4 * cfi_info->num_erase_regions); + for (i = 0; i < cfi_info->num_erase_regions; i++) + { + cfi_info->erase_region_info[i] = cfi_query_u32(bank, 0, 0x2d + (4 * i)); + LOG_DEBUG("erase region[%i]: %" PRIu32 " blocks of size 0x%" PRIx32 "", + i, + (cfi_info->erase_region_info[i] & 0xffff) + 1, + (cfi_info->erase_region_info[i] >> 16) * 256); + } + } + else + { + cfi_info->erase_region_info = NULL; + } + + /* We need to read the primary algorithm extended query table before calculating + * the sector layout to be able to apply fixups + */ + switch (cfi_info->pri_id) + { + /* Intel command set (standard and extended) */ + case 0x0001: + case 0x0003: + cfi_read_intel_pri_ext(bank); + break; + /* AMD/Spansion, Atmel, ... command set */ + case 0x0002: + cfi_info->status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7; /* default for all CFI flashs */ + cfi_read_0002_pri_ext(bank); + break; + default: + LOG_ERROR("cfi primary command set %i unsupported", cfi_info->pri_id); + break; + } + + /* return to read array mode + * we use both reset commands, as some Intel flashes fail to recognize the 0xF0 command + */ + cfi_command(bank, 0xf0, command); + if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + cfi_command(bank, 0xff, command); + if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + } /* end CFI case */ + + /* apply fixups depending on the primary command set */ + switch (cfi_info->pri_id) + { + /* Intel command set (standard and extended) */ + case 0x0001: + case 0x0003: + cfi_fixup(bank, cfi_0001_fixups); + break; + /* AMD/Spansion, Atmel, ... command set */ + case 0x0002: + cfi_fixup(bank, cfi_0002_fixups); + break; + default: + LOG_ERROR("cfi primary command set %i unsupported", cfi_info->pri_id); + break; + } + + if ((cfi_info->dev_size * bank->bus_width / bank->chip_width) != bank->size) + { + LOG_WARNING("configuration specifies 0x%" PRIx32 " size, but a 0x%" PRIx32 " size flash was found", bank->size, cfi_info->dev_size); + } + + if (cfi_info->num_erase_regions == 0) + { + /* a device might have only one erase block, spanning the whole device */ + bank->num_sectors = 1; + bank->sectors = malloc(sizeof(struct flash_sector)); + + bank->sectors[sector].offset = 0x0; + bank->sectors[sector].size = bank->size; + bank->sectors[sector].is_erased = -1; + bank->sectors[sector].is_protected = -1; + } + else + { + uint32_t offset = 0; + + for (i = 0; i < cfi_info->num_erase_regions; i++) + { + num_sectors += (cfi_info->erase_region_info[i] & 0xffff) + 1; + } + + bank->num_sectors = num_sectors; + bank->sectors = malloc(sizeof(struct flash_sector) * num_sectors); + + for (i = 0; i < cfi_info->num_erase_regions; i++) + { + uint32_t j; + for (j = 0; j < (cfi_info->erase_region_info[i] & 0xffff) + 1; j++) + { + bank->sectors[sector].offset = offset; + bank->sectors[sector].size = ((cfi_info->erase_region_info[i] >> 16) * 256) * bank->bus_width / bank->chip_width; + offset += bank->sectors[sector].size; + bank->sectors[sector].is_erased = -1; + bank->sectors[sector].is_protected = -1; + sector++; + } + } + if (offset != (cfi_info->dev_size * bank->bus_width / bank->chip_width)) + { + LOG_WARNING("CFI size is 0x%" PRIx32 ", but total sector size is 0x%" PRIx32 "", \ + (cfi_info->dev_size * bank->bus_width / bank->chip_width), offset); + } + } + + cfi_info->probed = 1; + + return ERROR_OK; +} + +static int cfi_auto_probe(struct flash_bank *bank) +{ + struct cfi_flash_bank *cfi_info = bank->driver_priv; + if (cfi_info->probed) + return ERROR_OK; + return cfi_probe(bank); +} + + +static int cfi_intel_protect_check(struct flash_bank *bank) +{ + int retval; + struct cfi_flash_bank *cfi_info = bank->driver_priv; + struct cfi_intel_pri_ext *pri_ext = cfi_info->pri_ext; + struct target *target = bank->target; + uint8_t command[CFI_MAX_BUS_WIDTH]; + int i; + + /* check if block lock bits are supported on this device */ + if (!(pri_ext->blk_status_reg_mask & 0x1)) + return ERROR_FLASH_OPERATION_FAILED; + + cfi_command(bank, 0x90, command); + if ((retval = target_write_memory(target, flash_address(bank, 0, 0x55), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + + for (i = 0; i < bank->num_sectors; i++) + { + uint8_t block_status = cfi_get_u8(bank, i, 0x2); + + if (block_status & 1) + bank->sectors[i].is_protected = 1; + else + bank->sectors[i].is_protected = 0; + } + + cfi_command(bank, 0xff, command); + return target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command); +} + +static int cfi_spansion_protect_check(struct flash_bank *bank) +{ + int retval; + struct cfi_flash_bank *cfi_info = bank->driver_priv; + struct cfi_spansion_pri_ext *pri_ext = cfi_info->pri_ext; + struct target *target = bank->target; + uint8_t command[8]; + int i; + + cfi_command(bank, 0xaa, command); + if ((retval = target_write_memory(target, flash_address(bank, 0, pri_ext->_unlock1), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + + cfi_command(bank, 0x55, command); + if ((retval = target_write_memory(target, flash_address(bank, 0, pri_ext->_unlock2), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + + cfi_command(bank, 0x90, command); + if ((retval = target_write_memory(target, flash_address(bank, 0, pri_ext->_unlock1), bank->bus_width, 1, command)) != ERROR_OK) + { + return retval; + } + + for (i = 0; i < bank->num_sectors; i++) + { + uint8_t block_status = cfi_get_u8(bank, i, 0x2); + + if (block_status & 1) + bank->sectors[i].is_protected = 1; + else + bank->sectors[i].is_protected = 0; + } + + cfi_command(bank, 0xf0, command); + return target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command); +} + +static int cfi_protect_check(struct flash_bank *bank) +{ + struct cfi_flash_bank *cfi_info = bank->driver_priv; + + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (cfi_info->qry[0] != 'Q') + return ERROR_FLASH_BANK_NOT_PROBED; + + switch (cfi_info->pri_id) + { + case 1: + case 3: + return cfi_intel_protect_check(bank); + break; + case 2: + return cfi_spansion_protect_check(bank); + break; + default: + LOG_ERROR("cfi primary command set %i unsupported", cfi_info->pri_id); + break; + } + + return ERROR_OK; +} + +static int cfi_info(struct flash_bank *bank, char *buf, int buf_size) +{ + int printed; + struct cfi_flash_bank *cfi_info = bank->driver_priv; + + if (cfi_info->qry[0] == (char)-1) + { + printed = snprintf(buf, buf_size, "\ncfi flash bank not probed yet\n"); + return ERROR_OK; + } + + if (cfi_info->not_cfi == 0) + printed = snprintf(buf, buf_size, "\ncfi information:\n"); + else + printed = snprintf(buf, buf_size, "\nnon-cfi flash:\n"); + buf += printed; + buf_size -= printed; + + printed = snprintf(buf, buf_size, "\nmfr: 0x%4.4x, id:0x%4.4x\n", + cfi_info->manufacturer, cfi_info->device_id); + buf += printed; + buf_size -= printed; + + if (cfi_info->not_cfi == 0) + { + printed = snprintf(buf, buf_size, "qry: '%c%c%c', pri_id: 0x%4.4x, pri_addr: 0x%4.4x, alt_id: 0x%4.4x, alt_addr: 0x%4.4x\n", cfi_info->qry[0], cfi_info->qry[1], cfi_info->qry[2], cfi_info->pri_id, cfi_info->pri_addr, cfi_info->alt_id, cfi_info->alt_addr); + buf += printed; + buf_size -= printed; + + printed = snprintf(buf, buf_size, "Vcc min: %1.1x.%1.1x, Vcc max: %1.1x.%1.1x, Vpp min: %1.1x.%1.1x, Vpp max: %1.1x.%1.1x\n", + (cfi_info->vcc_min & 0xf0) >> 4, cfi_info->vcc_min & 0x0f, + (cfi_info->vcc_max & 0xf0) >> 4, cfi_info->vcc_max & 0x0f, + (cfi_info->vpp_min & 0xf0) >> 4, cfi_info->vpp_min & 0x0f, + (cfi_info->vpp_max & 0xf0) >> 4, cfi_info->vpp_max & 0x0f); + buf += printed; + buf_size -= printed; + + printed = snprintf(buf, buf_size, "typ. word write timeout: %u, typ. buf write timeout: %u, typ. block erase timeout: %u, typ. chip erase timeout: %u\n", + 1 << cfi_info->word_write_timeout_typ, + 1 << cfi_info->buf_write_timeout_typ, + 1 << cfi_info->block_erase_timeout_typ, + 1 << cfi_info->chip_erase_timeout_typ); + buf += printed; + buf_size -= printed; + + printed = snprintf(buf, buf_size, "max. word write timeout: %u, max. buf write timeout: %u, max. block erase timeout: %u, max. chip erase timeout: %u\n", + (1 << cfi_info->word_write_timeout_max) * (1 << cfi_info->word_write_timeout_typ), + (1 << cfi_info->buf_write_timeout_max) * (1 << cfi_info->buf_write_timeout_typ), + (1 << cfi_info->block_erase_timeout_max) * (1 << cfi_info->block_erase_timeout_typ), + (1 << cfi_info->chip_erase_timeout_max) * (1 << cfi_info->chip_erase_timeout_typ)); + buf += printed; + buf_size -= printed; + + printed = snprintf(buf, buf_size, "size: 0x%" PRIx32 ", interface desc: %i, max buffer write size: %x\n", + cfi_info->dev_size, + cfi_info->interface_desc, + 1 << cfi_info->max_buf_write_size); + buf += printed; + buf_size -= printed; + + switch (cfi_info->pri_id) + { + case 1: + case 3: + cfi_intel_info(bank, buf, buf_size); + break; + case 2: + cfi_spansion_info(bank, buf, buf_size); + break; + default: + LOG_ERROR("cfi primary command set %i unsupported", cfi_info->pri_id); + break; + } + } + + return ERROR_OK; +} + +struct flash_driver cfi_flash = { + .name = "cfi", + .flash_bank_command = &cfi_flash_bank_command, + .erase = &cfi_erase, + .protect = &cfi_protect, + .write = &cfi_write, + .probe = &cfi_probe, + .auto_probe = &cfi_auto_probe, + .erase_check = &default_flash_blank_check, + .protect_check = &cfi_protect_check, + .info = &cfi_info, + }; diff --git a/src/flash/nor/cfi.h b/src/flash/nor/cfi.h new file mode 100644 index 00000000..d55fd34e --- /dev/null +++ b/src/flash/nor/cfi.h @@ -0,0 +1,164 @@ +/*************************************************************************** + * 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 CFI_H +#define CFI_H + +#include "flash.h" + +#define CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7 0xE0 /* DQ5..DQ7 */ +#define CFI_STATUS_POLL_MASK_DQ6_DQ7 0xC0 /* DQ6..DQ7 */ + +struct cfi_flash_bank +{ + struct working_area *write_algorithm; + + int x16_as_x8; + int jedec_probe; + int not_cfi; + int probed; + + uint16_t manufacturer; + uint16_t device_id; + + char qry[3]; + + /* identification string */ + uint16_t pri_id; + uint16_t pri_addr; + uint16_t alt_id; + uint16_t alt_addr; + + /* device-system interface */ + uint8_t vcc_min; + uint8_t vcc_max; + uint8_t vpp_min; + uint8_t vpp_max; + uint8_t word_write_timeout_typ; + uint8_t buf_write_timeout_typ; + uint8_t block_erase_timeout_typ; + uint8_t chip_erase_timeout_typ; + uint8_t word_write_timeout_max; + uint8_t buf_write_timeout_max; + uint8_t block_erase_timeout_max; + uint8_t chip_erase_timeout_max; + + uint8_t status_poll_mask; + + /* flash geometry */ + uint32_t dev_size; + uint16_t interface_desc; + uint16_t max_buf_write_size; + uint8_t num_erase_regions; + uint32_t *erase_region_info; + + void *pri_ext; + void *alt_ext; +}; + +/* Intel primary extended query table + * as defined for the Advanced+ Boot Block Flash Memory (C3) + * and used by the linux kernel cfi driver (as of 2.6.14) + */ +struct cfi_intel_pri_ext +{ + char pri[3]; + uint8_t major_version; + uint8_t minor_version; + uint32_t feature_support; + uint8_t suspend_cmd_support; + uint16_t blk_status_reg_mask; + uint8_t vcc_optimal; + uint8_t vpp_optimal; + uint8_t num_protection_fields; + uint16_t prot_reg_addr; + uint8_t fact_prot_reg_size; + uint8_t user_prot_reg_size; + uint8_t extra[0]; +}; + +/* Spansion primary extended query table as defined for and used by + * the linux kernel cfi driver (as of 2.6.15) + */ +struct cfi_spansion_pri_ext +{ + uint8_t pri[3]; + uint8_t major_version; + uint8_t minor_version; + uint8_t SiliconRevision; /* bits 1-0: Address Sensitive Unlock */ + uint8_t EraseSuspend; + uint8_t BlkProt; + uint8_t TmpBlkUnprotect; + uint8_t BlkProtUnprot; + uint8_t SimultaneousOps; + uint8_t BurstMode; + uint8_t PageMode; + uint8_t VppMin; + uint8_t VppMax; + uint8_t TopBottom; + int _reversed_geometry; + uint32_t _unlock1; + uint32_t _unlock2; +}; + +/* Atmel primary extended query table as defined for and used by + * the linux kernel cfi driver (as of 2.6.20+) + */ +struct cfi_atmel_pri_ext +{ + uint8_t pri[3]; + uint8_t major_version; + uint8_t minor_version; + uint8_t features; + uint8_t bottom_boot; + uint8_t burst_mode; + uint8_t page_mode; +}; + +enum { + CFI_UNLOCK_555_2AA, + CFI_UNLOCK_5555_2AAA, +}; + +struct cfi_unlock_addresses +{ + uint32_t unlock1; + uint32_t unlock2; +}; + +struct cfi_fixup +{ + uint16_t mfr; + uint16_t id; + void (*fixup)(struct flash_bank *flash, void *param); + void *param; +}; + +#define CFI_MFR_AMD 0x0001 +#define CFI_MFR_FUJITSU 0x0004 +#define CFI_MFR_ATMEL 0x001F +#define CFI_MFR_ST 0x0020 /* STMicroelectronics */ +#define CFI_MFR_AMIC 0x0037 +#define CFI_MFR_SST 0x00BF +#define CFI_MFR_MX 0x00C2 + +#define CFI_MFR_ANY 0xffff +#define CFI_ID_ANY 0xffff + +#endif /* CFI_H */ diff --git a/src/flash/nor/ecos.c b/src/flash/nor/ecos.c new file mode 100644 index 00000000..7a0b26f3 --- /dev/null +++ b/src/flash/nor/ecos.c @@ -0,0 +1,444 @@ +/*************************************************************************** + * Copyright (C) 2007,2008 Øyvind Harboe * + * oyvind.harboe@zylin.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "flash.h" +#include "embeddedice.h" +#include "image.h" +#include "algorithm.h" + + +#if 0 +static uint32_t ecosflash_get_flash_status(struct flash_bank *bank); +static void ecosflash_set_flash_mode(struct flash_bank *bank,int mode); +static uint32_t ecosflash_wait_status_busy(struct flash_bank *bank, uint32_t waitbits, int timeout); +static int ecosflash_handle_gpnvm_command(struct command_context *cmd_ctx, char *cmd, char **args, int argc); +#endif + +struct ecosflash_flash_bank +{ + struct target *target; + struct working_area *write_algorithm; + struct working_area *erase_check_algorithm; + char *driverPath; + uint32_t start_address; +}; + +static const int sectorSize = 0x10000; + +char * +flash_errmsg(int err); + +#ifndef __ECOS +#define FLASH_ERR_OK 0x00 /* No error - operation complete */ +#define FLASH_ERR_INVALID 0x01 /* Invalid FLASH address */ +#define FLASH_ERR_ERASE 0x02 /* Error trying to erase */ +#define FLASH_ERR_LOCK 0x03 /* Error trying to lock/unlock */ +#define FLASH_ERR_PROGRAM 0x04 /* Error trying to program */ +#define FLASH_ERR_PROTOCOL 0x05 /* Generic error */ +#define FLASH_ERR_PROTECT 0x06 /* Device/region is write-protected */ +#define FLASH_ERR_NOT_INIT 0x07 /* FLASH info not yet initialized */ +#define FLASH_ERR_HWR 0x08 /* Hardware (configuration?) problem */ +#define FLASH_ERR_ERASE_SUSPEND 0x09 /* Device is in erase suspend mode */ +#define FLASH_ERR_PROGRAM_SUSPEND 0x0a /* Device is in in program suspend mode */ +#define FLASH_ERR_DRV_VERIFY 0x0b /* Driver failed to verify data */ +#define FLASH_ERR_DRV_TIMEOUT 0x0c /* Driver timed out waiting for device */ +#define FLASH_ERR_DRV_WRONG_PART 0x0d /* Driver does not support device */ +#define FLASH_ERR_LOW_VOLTAGE 0x0e /* Not enough juice to complete job */ + +char * +flash_errmsg(int err) +{ + switch (err) { + case FLASH_ERR_OK: + return "No error - operation complete"; + case FLASH_ERR_ERASE_SUSPEND: + return "Device is in erase suspend state"; + case FLASH_ERR_PROGRAM_SUSPEND: + return "Device is in program suspend state"; + case FLASH_ERR_INVALID: + return "Invalid FLASH address"; + case FLASH_ERR_ERASE: + return "Error trying to erase"; + case FLASH_ERR_LOCK: + return "Error trying to lock/unlock"; + case FLASH_ERR_PROGRAM: + return "Error trying to program"; + case FLASH_ERR_PROTOCOL: + return "Generic error"; + case FLASH_ERR_PROTECT: + return "Device/region is write-protected"; + case FLASH_ERR_NOT_INIT: + return "FLASH sub-system not initialized"; + case FLASH_ERR_DRV_VERIFY: + return "Data verify failed after operation"; + case FLASH_ERR_DRV_TIMEOUT: + return "Driver timed out waiting for device"; + case FLASH_ERR_DRV_WRONG_PART: + return "Driver does not support device"; + case FLASH_ERR_LOW_VOLTAGE: + return "Device reports low voltage"; + default: + return "Unknown error"; + } +} +#endif + +/* flash bank ecosflash + */ +FLASH_BANK_COMMAND_HANDLER(ecosflash_flash_bank_command) +{ + struct ecosflash_flash_bank *info; + + if (CMD_ARGC < 7) + { + LOG_WARNING("incomplete flash_bank ecosflash configuration"); + return ERROR_FLASH_BANK_INVALID; + } + + info = malloc(sizeof(struct ecosflash_flash_bank)); + if (info == NULL) + { + LOG_ERROR("no memory for flash bank info"); + exit(-1); + } + bank->driver_priv = info; + info->driverPath = strdup(CMD_ARGV[6]); + + /* eCos flash sector sizes are not exposed to OpenOCD, use 0x10000 as + * a way to improve impedance match between OpenOCD and eCos flash + * driver. + */ + int i = 0; + uint32_t offset = 0; + bank->num_sectors = bank->size/sectorSize; + bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors); + for (i = 0; i < bank->num_sectors; i++) + { + bank->sectors[i].offset = offset; + bank->sectors[i].size = sectorSize; + offset += bank->sectors[i].size; + bank->sectors[i].is_erased = -1; + bank->sectors[i].is_protected = 0; + } + + info->target = get_target(CMD_ARGV[5]); + if (info->target == NULL) + { + LOG_ERROR("target '%s' not defined", CMD_ARGV[5]); + return ERROR_FAIL; + } + return ERROR_OK; +} + +static int loadDriver(struct ecosflash_flash_bank *info) +{ + size_t buf_cnt; + size_t image_size; + struct image image; + + image.base_address_set = 0; + image.start_address_set = 0; + struct target *target = info->target; + int retval; + + if ((retval = image_open(&image, info->driverPath, NULL)) != ERROR_OK) + { + return retval; + } + + info->start_address = image.start_address; + + image_size = 0x0; + int i; + for (i = 0; i < image.num_sections; i++) + { + void *buffer = malloc(image.sections[i].size); + int retval; + if ((retval = image_read_section(&image, i, 0x0, image.sections[i].size, buffer, &buf_cnt)) != ERROR_OK) + { + free(buffer); + image_close(&image); + return retval; + } + target_write_buffer(target, image.sections[i].base_address, buf_cnt, buffer); + image_size += buf_cnt; + LOG_DEBUG("%zu bytes written at address 0x%8.8" PRIx32 "", + buf_cnt, image.sections[i].base_address); + + free(buffer); + } + + image_close(&image); + + return ERROR_OK; +} + +static int const OFFSET_ERASE = 0x0; +static int const OFFSET_ERASE_SIZE = 0x8; +static int const OFFSET_FLASH = 0xc; +static int const OFFSET_FLASH_SIZE = 0x8; +static int const OFFSET_GET_WORKAREA = 0x18; +static int const OFFSET_GET_WORKAREA_SIZE = 0x4; + +static int runCode(struct ecosflash_flash_bank *info, + uint32_t codeStart, uint32_t codeStop, uint32_t r0, uint32_t r1, uint32_t r2, + uint32_t *result, + /* timeout in ms */ + int timeout) +{ + struct target *target = info->target; + + struct reg_param reg_params[3]; + struct armv4_5_algorithm armv4_5_info; + 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_IN_OUT); + init_reg_param(®_params[1], "r1", 32, PARAM_OUT); + init_reg_param(®_params[2], "r2", 32, PARAM_OUT); + + buf_set_u32(reg_params[0].value, 0, 32, r0); + buf_set_u32(reg_params[1].value, 0, 32, r1); + buf_set_u32(reg_params[2].value, 0, 32, r2); + + int retval; + if ((retval = target_run_algorithm(target, 0, NULL, 3, reg_params, + codeStart, + codeStop, timeout, + &armv4_5_info)) != ERROR_OK) + { + LOG_ERROR("error executing eCos flash algorithm"); + return retval; + } + + *result = buf_get_u32(reg_params[0].value, 0, 32); + + destroy_reg_param(®_params[0]); + destroy_reg_param(®_params[1]); + destroy_reg_param(®_params[2]); + + return ERROR_OK; +} + +static int eCosBoard_erase(struct ecosflash_flash_bank *info, uint32_t address, uint32_t len) +{ + int retval; + int timeout = (len / 20480 + 1) * 1000; /*asume 20 KB/s*/ + + retval = loadDriver(info); + if (retval != ERROR_OK) + return retval; + + uint32_t flashErr; + retval = runCode(info, + info->start_address + OFFSET_ERASE, + info->start_address + OFFSET_ERASE + OFFSET_ERASE_SIZE, + address, + len, + 0, + &flashErr, + timeout +); + if (retval != ERROR_OK) + return retval; + + if (flashErr != 0x0) + { + LOG_ERROR("Flash erase failed with %d (%s)\n", (int)flashErr, flash_errmsg(flashErr)); + return ERROR_FAIL; + } + + return ERROR_OK; +} + +static int eCosBoard_flash(struct ecosflash_flash_bank *info, void *data, uint32_t address, uint32_t len) +{ + struct target *target = info->target; + const int chunk = 8192; + int retval = ERROR_OK; + int timeout = (chunk / 20480 + 1) * 1000; /*asume 20 KB/s + 1 second*/ + + retval = loadDriver(info); + if (retval != ERROR_OK) + return retval; + + uint32_t buffer; + retval = runCode(info, + info->start_address + OFFSET_GET_WORKAREA, + info->start_address + OFFSET_GET_WORKAREA + OFFSET_GET_WORKAREA_SIZE, + 0, + 0, + 0, + &buffer, + 1000); + if (retval != ERROR_OK) + return retval; + + + uint32_t i; + for (i = 0; i < len; i += chunk) + { + int t = len-i; + if (t > chunk) + { + t = chunk; + } + + int retval; + retval = target_write_buffer(target, buffer, t, ((uint8_t *)data) + i); + if (retval != ERROR_OK) + return retval; + + uint32_t flashErr; + retval = runCode(info, + info->start_address + OFFSET_FLASH, + info->start_address + OFFSET_FLASH + OFFSET_FLASH_SIZE, + buffer, + address + i, + t, + &flashErr, + timeout); + if (retval != ERROR_OK) + return retval; + + if (flashErr != 0x0) + { + LOG_ERROR("Flash prog failed with %d (%s)\n", (int)flashErr, flash_errmsg(flashErr)); + return ERROR_FAIL; + } + } + return ERROR_OK; +} + +static int ecosflash_probe(struct flash_bank *bank) +{ + return ERROR_OK; +} + +#if 0 +static void command(struct flash_bank *bank, uint8_t cmd, uint8_t *cmd_buf) +{ + struct ecosflash_flash_bank *info = bank->driver_priv; + int i; + + if (info->target->endianness == TARGET_LITTLE_ENDIAN) + { + for (i = bank->bus_width; i > 0; i--) + { + *cmd_buf++ = (i & (bank->chip_width - 1)) ? 0x0 : cmd; + } + } + else + { + for (i = 1; i <= bank->bus_width; i++) + { + *cmd_buf++ = (i & (bank->chip_width - 1)) ? 0x0 : cmd; + } + } +} +#endif + +#if 0 +static uint32_t ecosflash_address(struct flash_bank *bank, uint32_t address) +{ + uint32_t retval = 0; + switch (bank->bus_width) + { + case 4: + retval = address & 0xfffffffc; + case 2: + retval = address & 0xfffffffe; + case 1: + retval = address; + } + + return retval + bank->base; +} +#endif + +static int ecosflash_erase(struct flash_bank *bank, int first, int last) +{ + struct flash_bank *c = bank; + struct ecosflash_flash_bank *info = bank->driver_priv; + return eCosBoard_erase(info, c->base + first*sectorSize, sectorSize*(last-first + 1)); +} + +static int ecosflash_protect(struct flash_bank *bank, int set, int first, int last) +{ + return ERROR_OK; +} + +static int ecosflash_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) +{ + struct ecosflash_flash_bank *info = bank->driver_priv; + struct flash_bank *c = bank; + return eCosBoard_flash(info, buffer, c->base + offset, count); +} + +static int ecosflash_protect_check(struct flash_bank *bank) +{ + return ERROR_OK; +} + +static int ecosflash_info(struct flash_bank *bank, char *buf, int buf_size) +{ + struct ecosflash_flash_bank *info = bank->driver_priv; + snprintf(buf, buf_size, "eCos flash driver: %s", info->driverPath); + return ERROR_OK; +} + +#if 0 +static uint32_t ecosflash_get_flash_status(struct flash_bank *bank) +{ + return ERROR_OK; +} + +static void ecosflash_set_flash_mode(struct flash_bank *bank,int mode) +{ + +} + +static uint32_t ecosflash_wait_status_busy(struct flash_bank *bank, uint32_t waitbits, int timeout) +{ + return ERROR_OK; +} + +static int ecosflash_handle_gpnvm_command(struct command_context *cmd_ctx, char *cmd, char **args, int argc) +{ + return ERROR_OK; +} +#endif + +struct flash_driver ecosflash_flash = { + .name = "ecosflash", + .flash_bank_command = &ecosflash_flash_bank_command, + .erase = &ecosflash_erase, + .protect = &ecosflash_protect, + .write = &ecosflash_write, + .probe = &ecosflash_probe, + .auto_probe = &ecosflash_probe, + .erase_check = &default_flash_blank_check, + .protect_check = &ecosflash_protect_check, + .info = &ecosflash_info + }; diff --git a/src/flash/nor/faux.c b/src/flash/nor/faux.c new file mode 100644 index 00000000..caec2c79 --- /dev/null +++ b/src/flash/nor/faux.c @@ -0,0 +1,149 @@ +/*************************************************************************** + * Copyright (C) 2009 Øyvind Harboe * + * oyvind.harboe@zylin.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "flash.h" +#include "image.h" +#include "../hello.h" + + +struct faux_flash_bank +{ + struct target *target; + uint8_t *memory; + uint32_t start_address; +}; + +static const int sectorSize = 0x10000; + + +/* flash bank faux + */ +FLASH_BANK_COMMAND_HANDLER(faux_flash_bank_command) +{ + struct faux_flash_bank *info; + + if (CMD_ARGC < 6) + { + LOG_WARNING("incomplete flash_bank faux configuration"); + return ERROR_FLASH_BANK_INVALID; + } + + info = malloc(sizeof(struct faux_flash_bank)); + if (info == NULL) + { + LOG_ERROR("no memory for flash bank info"); + return ERROR_FAIL; + } + info->memory = malloc(bank->size); + if (info == NULL) + { + free(info); + LOG_ERROR("no memory for flash bank info"); + return ERROR_FAIL; + } + bank->driver_priv = info; + + /* Use 0x10000 as a fixed sector size. */ + int i = 0; + uint32_t offset = 0; + bank->num_sectors = bank->size/sectorSize; + bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors); + for (i = 0; i < bank->num_sectors; i++) + { + bank->sectors[i].offset = offset; + bank->sectors[i].size = sectorSize; + offset += bank->sectors[i].size; + bank->sectors[i].is_erased = -1; + bank->sectors[i].is_protected = 0; + } + + info->target = get_target(CMD_ARGV[5]); + if (info->target == NULL) + { + LOG_ERROR("target '%s' not defined", CMD_ARGV[5]); + free(info->memory); + free(info); + return ERROR_FAIL; + } + return ERROR_OK; +} + +static int faux_erase(struct flash_bank *bank, int first, int last) +{ + struct faux_flash_bank *info = bank->driver_priv; + memset(info->memory + first*sectorSize, 0xff, sectorSize*(last-first + 1)); + return ERROR_OK; +} + +static int faux_protect(struct flash_bank *bank, int set, int first, int last) +{ + LOG_USER("set protection sector %d to %d to %s", first, last, set?"on":"off"); + return ERROR_OK; +} + +static int faux_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) +{ + struct faux_flash_bank *info = bank->driver_priv; + memcpy(info->memory + offset, buffer, count); + return ERROR_OK; +} + +static int faux_protect_check(struct flash_bank *bank) +{ + return ERROR_OK; +} + +static int faux_info(struct flash_bank *bank, char *buf, int buf_size) +{ + snprintf(buf, buf_size, "faux flash driver"); + return ERROR_OK; +} + +static int faux_probe(struct flash_bank *bank) +{ + return ERROR_OK; +} + +static const struct command_registration faux_command_handlers[] = { + { + .name = "faux", + .mode = COMMAND_ANY, + .help = "faux flash command group", + .chain = hello_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +struct flash_driver faux_flash = { + .name = "faux", + .commands = faux_command_handlers, + .flash_bank_command = &faux_flash_bank_command, + .erase = &faux_erase, + .protect = &faux_protect, + .write = &faux_write, + .probe = &faux_probe, + .auto_probe = &faux_probe, + .erase_check = &default_flash_blank_check, + .protect_check = &faux_protect_check, + .info = &faux_info + }; diff --git a/src/flash/nor/lpc2000.c b/src/flash/nor/lpc2000.c new file mode 100644 index 00000000..418b5b03 --- /dev/null +++ b/src/flash/nor/lpc2000.c @@ -0,0 +1,812 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * LPC1700 support Copyright (C) 2009 by Audrius Urmanavicius * + * didele.deze@gmail.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "lpc2000.h" +#include "armv7m.h" +#include "binarybuffer.h" +#include "algorithm.h" + + +/* flash programming support for NXP LPC17xx and 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 + * + * lpc1700: + * - 175x + * - 176x (tested with LPC1768) + */ + +static int lpc2000_build_sector_list(struct flash_bank *bank) +{ + struct lpc2000_flash_bank *lpc2000_info = bank->driver_priv; + int i; + uint32_t offset = 0; + + /* default to a 4096 write buffer */ + lpc2000_info->cmd51_max_buffer = 4096; + + if (lpc2000_info->variant == lpc2000_v1) + { + /* variant 1 has different layout for 128kb and 256kb flashes */ + if (bank->size == 128 * 1024) + { + bank->num_sectors = 16; + bank->sectors = malloc(sizeof(struct flash_sector) * 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(struct flash_sector) * 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 + { + LOG_ERROR("BUG: unknown bank->size encountered"); + exit(-1); + } + } + else if (lpc2000_info->variant == lpc2000_v2) + { + /* variant 2 has a uniform layout, only number of sectors differs */ + switch (bank->size) + { + case 4 * 1024: + lpc2000_info->cmd51_max_buffer = 1024; + bank->num_sectors = 1; + break; + case 8 * 1024: + lpc2000_info->cmd51_max_buffer = 1024; + bank->num_sectors = 2; + break; + case 16 * 1024: + bank->num_sectors = 4; + break; + case 32 * 1024: + bank->num_sectors = 8; + break; + case 64 * 1024: + bank->num_sectors = 9; + break; + case 128 * 1024: + bank->num_sectors = 11; + break; + case 256 * 1024: + bank->num_sectors = 15; + break; + case 512 * 1024: + case 500 * 1024: + bank->num_sectors = 27; + break; + default: + LOG_ERROR("BUG: unknown bank->size encountered"); + exit(-1); + break; + } + + bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors); + + for (i = 0; i < bank->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 if (lpc2000_info->variant == lpc1700) + { + switch(bank->size) + { + case 32 * 1024: + bank->num_sectors = 8; + break; + case 64 * 1024: + bank->num_sectors = 16; + break; + case 128 * 1024: + bank->num_sectors = 18; + break; + case 256 * 1024: + bank->num_sectors = 22; + break; + case 512 * 1024: + bank->num_sectors = 30; + break; + default: + LOG_ERROR("BUG: unknown bank->size encountered"); + exit(-1); + } + + bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors); + + for(i = 0; i < bank->num_sectors; i++) + { + bank->sectors[i].offset = offset; + /* sectors 0-15 are 4kB-sized, 16 and above are 32kB-sized for LPC17xx devices */ + bank->sectors[i].size = (i < 16)? 4 * 1024 : 32 * 1024; + offset += bank->sectors[i].size; + bank->sectors[i].is_erased = -1; + bank->sectors[i].is_protected = 1; + } + } + else + { + LOG_ERROR("BUG: unknown lpc2000_info->variant encountered"); + exit(-1); + } + + return ERROR_OK; +} + +/* call LPC1700/LPC2000 IAP function + * uses 180 bytes working area + * 0x0 to 0x7: jump gate (BX to thumb state, b -2 to wait) + * 0x8 to 0x1f: command parameter table (1+5 words) + * 0x20 to 0x33: command result table (1+4 words) + * 0x34 to 0xb3: stack (only 128b needed) + */ +static int lpc2000_iap_call(struct flash_bank *bank, int code, uint32_t param_table[5], uint32_t result_table[4]) +{ + int retval; + struct lpc2000_flash_bank *lpc2000_info = bank->driver_priv; + struct target *target = bank->target; + struct mem_param mem_params[2]; + struct reg_param reg_params[5]; + struct armv4_5_algorithm armv4_5_info; /* for LPC2000 */ + struct armv7m_algorithm armv7m_info; /* for LPC1700 */ + uint32_t status_code; + uint32_t iap_entry_point = 0; /* to make compiler happier */ + + /* regrab previously allocated working_area, or allocate a new one */ + if (!lpc2000_info->iap_working_area) + { + uint8_t jump_gate[8]; + + /* make sure we have a working area */ + if (target_alloc_working_area(target, 180, &lpc2000_info->iap_working_area) != ERROR_OK) + { + LOG_ERROR("no working area specified, can't write LPC2000 internal flash"); + return ERROR_FLASH_OPERATION_FAILED; + } + + /* write IAP code to working area */ + switch(lpc2000_info->variant) + { + case lpc1700: + target_buffer_set_u32(target, jump_gate, ARMV7M_T_BX(12)); + target_buffer_set_u32(target, jump_gate + 4, ARMV7M_T_B(0xfffffe)); + break; + case lpc2000_v1: + case lpc2000_v2: + target_buffer_set_u32(target, jump_gate, ARMV4_5_BX(12)); + target_buffer_set_u32(target, jump_gate + 4, ARMV4_5_B(0xfffffe, 0)); + break; + default: + LOG_ERROR("BUG: unknown bank->size encountered"); + exit(-1); + } + + if ((retval = target_write_memory(target, lpc2000_info->iap_working_area->address, 4, 2, jump_gate)) != ERROR_OK) + { + LOG_ERROR("Write memory at address 0x%8.8" PRIx32 " failed (check work_area definition)", lpc2000_info->iap_working_area->address); + return retval; + } + } + + switch(lpc2000_info->variant) + { + case lpc1700: + armv7m_info.common_magic = ARMV7M_COMMON_MAGIC; + armv7m_info.core_mode = ARMV7M_MODE_ANY; + iap_entry_point = 0x1fff1ff1; + break; + case lpc2000_v1: + case lpc2000_v2: + 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; + iap_entry_point = 0x7ffffff1; + break; + default: + LOG_ERROR("BUG: unknown lpc2000->variant encountered"); + exit(-1); + } + + /* command parameter table */ + init_mem_param(&mem_params[0], lpc2000_info->iap_working_area->address + 8, 6 * 4, PARAM_OUT); + target_buffer_set_u32(target, mem_params[0].value, code); + target_buffer_set_u32(target, mem_params[0].value + 0x04, param_table[0]); + target_buffer_set_u32(target, mem_params[0].value + 0x08, param_table[1]); + target_buffer_set_u32(target, mem_params[0].value + 0x0c, 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 + 0x08); + + /* command result table */ + init_mem_param(&mem_params[1], lpc2000_info->iap_working_area->address + 0x20, 5 * 4, 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, iap_entry_point); + + switch(lpc2000_info->variant) + { + case lpc1700: + /* IAP stack */ + init_reg_param(®_params[3], "sp", 32, PARAM_OUT); + buf_set_u32(reg_params[3].value, 0, 32, lpc2000_info->iap_working_area->address + 0xb4); + + /* return address */ + init_reg_param(®_params[4], "lr", 32, PARAM_OUT); + buf_set_u32(reg_params[4].value, 0, 32, (lpc2000_info->iap_working_area->address + 0x04) | 1); /* bit0 of LR = 1 to return in Thumb mode */ + + target_run_algorithm(target, 2, mem_params, 5, reg_params, lpc2000_info->iap_working_area->address, lpc2000_info->iap_working_area->address + 0x4, 10000, &armv7m_info); + break; + case lpc2000_v1: + case lpc2000_v2: + /* 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 + 0xb4); + + /* 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 + 0x04); + + target_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); + break; + default: + LOG_ERROR("BUG: unknown lpc2000->variant encountered"); + exit(-1); + } + + + status_code = target_buffer_get_u32(target, mem_params[1].value); + result_table[0] = target_buffer_get_u32(target, mem_params[1].value + 0x04); + result_table[1] = target_buffer_get_u32(target, mem_params[1].value + 0x08); + result_table[2] = target_buffer_get_u32(target, mem_params[1].value + 0x0c); + result_table[3] = target_buffer_get_u32(target, mem_params[1].value + 0x10); + + LOG_DEBUG("IAP command = %i (0x%8.8" PRIx32", 0x%8.8" PRIx32", 0x%8.8" PRIx32", 0x%8.8" PRIx32", 0x%8.8" PRIx32") completed with result = %8.8" PRIx32, + code, param_table[0], param_table[1], param_table[2], param_table[3], param_table[4], status_code); + + 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; +} + +static int lpc2000_iap_blank_check(struct flash_bank *bank, int first, int last) +{ + uint32_t param_table[5]; + uint32_t result_table[4]; + 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: + LOG_ERROR("BUG: unknown LPC2000 status code %i", status_code); + exit(-1); + } + } + + return ERROR_OK; +} + +/* + * flash bank lpc2000 0 0 [calc_checksum] + */ +FLASH_BANK_COMMAND_HANDLER(lpc2000_flash_bank_command) +{ + struct lpc2000_flash_bank *lpc2000_info; + + if (CMD_ARGC < 8) + { + LOG_WARNING("incomplete flash_bank lpc2000 configuration"); + return ERROR_FLASH_BANK_INVALID; + } + + lpc2000_info = malloc(sizeof(struct lpc2000_flash_bank)); + bank->driver_priv = lpc2000_info; + + if (strcmp(CMD_ARGV[6], "lpc2000_v1") == 0) + { + lpc2000_info->variant = lpc2000_v1; + lpc2000_info->cmd51_dst_boundary = 512; + lpc2000_info->cmd51_can_256b = 0; + lpc2000_info->cmd51_can_8192b = 1; + lpc2000_info->checksum_vector = 5; + } + else if (strcmp(CMD_ARGV[6], "lpc2000_v2") == 0) + { + lpc2000_info->variant = lpc2000_v2; + lpc2000_info->cmd51_dst_boundary = 256; + lpc2000_info->cmd51_can_256b = 1; + lpc2000_info->cmd51_can_8192b = 0; + lpc2000_info->checksum_vector = 5; + } + else if (strcmp(CMD_ARGV[6], "lpc1700") == 0) + { + lpc2000_info->variant = lpc1700; + lpc2000_info->cmd51_dst_boundary = 256; + lpc2000_info->cmd51_can_256b = 1; + lpc2000_info->cmd51_can_8192b = 0; + lpc2000_info->checksum_vector = 7; + } + else + { + LOG_ERROR("unknown LPC2000 variant: %s", CMD_ARGV[6]); + free(lpc2000_info); + return ERROR_FLASH_BANK_INVALID; + } + + lpc2000_info->iap_working_area = NULL; + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[7], lpc2000_info->cclk); + lpc2000_info->calc_checksum = 0; + lpc2000_build_sector_list(bank); + + if (CMD_ARGC >= 9) + { + if (strcmp(CMD_ARGV[8], "calc_checksum") == 0) + lpc2000_info->calc_checksum = 1; + } + + return ERROR_OK; +} + +static int lpc2000_erase(struct flash_bank *bank, int first, int last) +{ + struct lpc2000_flash_bank *lpc2000_info = bank->driver_priv; + uint32_t param_table[5]; + uint32_t result_table[4]; + int status_code; + + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + 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: + LOG_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: + LOG_WARNING("lpc2000 erase sectors returned %i", status_code); + return ERROR_FLASH_OPERATION_FAILED; + } + + return ERROR_OK; +} + +static int lpc2000_protect(struct flash_bank *bank, int set, int first, int last) +{ + /* can't protect/unprotect on the lpc2000 */ + return ERROR_OK; +} + +static int lpc2000_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) +{ + struct lpc2000_flash_bank *lpc2000_info = bank->driver_priv; + struct target *target = bank->target; + uint32_t dst_min_alignment; + uint32_t bytes_remaining = count; + uint32_t bytes_written = 0; + int first_sector = 0; + int last_sector = 0; + uint32_t param_table[5]; + uint32_t result_table[4]; + int status_code; + int i; + struct working_area *download_area; + int retval = ERROR_OK; + + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (offset + count > bank->size) + return ERROR_FLASH_DST_OUT_OF_BANK; + + dst_min_alignment = lpc2000_info->cmd51_dst_boundary; + + if (offset % dst_min_alignment) + { + LOG_WARNING("offset 0x%" PRIx32 " breaks required alignment 0x%" PRIx32, 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 + DIV_ROUND_UP(count, dst_min_alignment) * dst_min_alignment > bank->sectors[i].offset) + last_sector = i; + } + + LOG_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) + { + uint32_t checksum = 0; + int i; + for (i = 0; i < 8; i++) + { + LOG_DEBUG("Vector 0x%2.2x: 0x%8.8" PRIx32, i * 4, buf_get_u32(buffer + (i * 4), 0, 32)); + if (i != lpc2000_info->checksum_vector) + checksum += buf_get_u32(buffer + (i * 4), 0, 32); + } + checksum = 0 - checksum; + LOG_DEBUG("checksum: 0x%8.8" PRIx32, checksum); + + uint32_t original_value = buf_get_u32(buffer + (lpc2000_info->checksum_vector * 4), 0, 32); + if (original_value != checksum) + { + LOG_WARNING("Verification will fail since checksum in image (0x%8.8" PRIx32 ") to be written to flash is different from calculated vector checksum (0x%8.8" PRIx32 ").", + original_value, checksum); + LOG_WARNING("To remove this warning modify build tools on developer PC to inject correct LPC vector checksum."); + } + + buf_set_u32(buffer + (lpc2000_info->checksum_vector * 4), 0, 32, checksum); + } + + /* allocate a working area */ + if (target_alloc_working_area(target, lpc2000_info->cmd51_max_buffer, &download_area) != ERROR_OK) + { + LOG_ERROR("no working area specified, can't write LPC2000 internal flash"); + return ERROR_FLASH_OPERATION_FAILED; + } + + while (bytes_remaining > 0) + { + uint32_t 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: + retval = ERROR_FLASH_OPERATION_FAILED; + break; + case LPC2000_CMD_SUCCESS: + break; + case LPC2000_INVALID_SECTOR: + retval = ERROR_FLASH_SECTOR_INVALID; + break; + default: + LOG_WARNING("lpc2000 prepare sectors returned %i", status_code); + retval = ERROR_FLASH_OPERATION_FAILED; + break; + } + + /* Exit if error occured */ + if (retval != ERROR_OK) + break; + + if (bytes_remaining >= thisrun_bytes) + { + if ((retval = target_write_buffer(bank->target, download_area->address, thisrun_bytes, buffer + bytes_written)) != ERROR_OK) + { + retval = ERROR_FLASH_OPERATION_FAILED; + break; + } + } + else + { + uint8_t *last_buffer = malloc(thisrun_bytes); + memcpy(last_buffer, buffer + bytes_written, bytes_remaining); + memset(last_buffer + bytes_remaining, 0xff, thisrun_bytes - bytes_remaining); + target_write_buffer(bank->target, download_area->address, thisrun_bytes, last_buffer); + free(last_buffer); + } + + LOG_DEBUG("writing 0x%" PRIx32 " bytes to address 0x%" PRIx32 , 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: + retval = ERROR_FLASH_OPERATION_FAILED; + break; + case LPC2000_CMD_SUCCESS: + break; + case LPC2000_INVALID_SECTOR: + retval = ERROR_FLASH_SECTOR_INVALID; + break; + default: + LOG_WARNING("lpc2000 returned %i", status_code); + retval = ERROR_FLASH_OPERATION_FAILED; + break; + } + + /* Exit if error occured */ + if (retval != ERROR_OK) + break; + + 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 retval; +} + +static int lpc2000_probe(struct flash_bank *bank) +{ + /* we can't probe on an lpc2000 + * if this is an lpc2xxx, it has the configured flash + */ + return ERROR_OK; +} + +static int lpc2000_erase_check(struct flash_bank *bank) +{ + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + return lpc2000_iap_blank_check(bank, 0, bank->num_sectors - 1); +} + +static int lpc2000_protect_check(struct flash_bank *bank) +{ + /* sectors are always protected */ + return ERROR_OK; +} + +static int lpc2000_info(struct flash_bank *bank, char *buf, int buf_size) +{ + struct lpc2000_flash_bank *lpc2000_info = bank->driver_priv; + + snprintf(buf, buf_size, "lpc2000 flash driver variant: %i, clk: %" PRIi32 "kHz" , lpc2000_info->variant, lpc2000_info->cclk); + + return ERROR_OK; +} + +COMMAND_HANDLER(lpc2000_handle_part_id_command) +{ + uint32_t param_table[5]; + uint32_t result_table[4]; + int status_code; + + if (CMD_ARGC < 1) + { + return ERROR_COMMAND_SYNTAX_ERROR; + } + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not 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.8" PRIx32 , result_table[0]); + } + + return ERROR_OK; +} + +static const struct command_registration lpc2000_exec_command_handlers[] = { + { + .name = "part_id", + .handler = &lpc2000_handle_part_id_command, + .mode = COMMAND_EXEC, + .help = "print part id of lpc2000 flash bank ", + }, + COMMAND_REGISTRATION_DONE +}; +static const struct command_registration lpc2000_command_handlers[] = { + { + .name = "lpc2000", + .mode = COMMAND_ANY, + .help = "lpc2000 flash command group", + .chain = lpc2000_exec_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +struct flash_driver lpc2000_flash = { + .name = "lpc2000", + .commands = lpc2000_command_handlers, + .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, + }; + + diff --git a/src/flash/nor/lpc2000.h b/src/flash/nor/lpc2000.h new file mode 100644 index 00000000..08e278a3 --- /dev/null +++ b/src/flash/nor/lpc2000.h @@ -0,0 +1,73 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * LPC1700 support Copyright (C) 2009 by Audrius Urmanavicius * + * didele.deze@gmail.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef LPC2000_H +#define LPC2000_H + +#include "flash.h" + +typedef enum +{ + lpc2000_v1, + lpc2000_v2, + lpc1700 +} lpc2000_variant; + +struct lpc2000_flash_bank +{ + lpc2000_variant variant; + struct working_area *iap_working_area; + uint32_t cclk; + int cmd51_dst_boundary; + int cmd51_can_256b; + int cmd51_can_8192b; + int calc_checksum; + uint32_t cmd51_max_buffer; + int checksum_vector; +}; + +enum lpc2000_status_codes +{ + LPC2000_CMD_SUCCESS = 0, + LPC2000_INVALID_COMMAND = 1, + LPC2000_SRC_ADDR_ERROR = 2, + LPC2000_DST_ADDR_ERROR = 3, + LPC2000_SRC_ADDR_NOT_MAPPED = 4, + LPC2000_DST_ADDR_NOT_MAPPED = 5, + LPC2000_COUNT_ERROR = 6, + LPC2000_INVALID_SECTOR = 7, + LPC2000_SECTOR_NOT_BLANK = 8, + LPC2000_SECTOR_NOT_PREPARED = 9, + LPC2000_COMPARE_ERROR = 10, + LPC2000_BUSY = 11, + LPC2000_PARAM_ERROR = 12, + LPC2000_ADDR_ERROR = 13, + LPC2000_ADDR_NOT_MAPPED = 14, + LPC2000_CMD_NOT_LOCKED = 15, + LPC2000_INVALID_CODE = 16, + LPC2000_INVALID_BAUD_RATE = 17, + LPC2000_INVALID_STOP_BIT = 18, + LPC2000_CRP_ENABLED = 19 + +}; + +#endif /* LPC2000_H */ diff --git a/src/flash/nor/lpc288x.c b/src/flash/nor/lpc288x.c new file mode 100644 index 00000000..446fc9da --- /dev/null +++ b/src/flash/nor/lpc288x.c @@ -0,0 +1,485 @@ +/*************************************************************************** + * Copyright (C) 2008 by * + * Karl RobinSod * + * * + * 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 +* +* You need to unprotect flash sectors each time you connect the OpenOCD +* Dumping 1MB takes about 60 Seconds +* Full erase (sectors 0-22 inclusive) takes 2-4 seconds +* Writing 1MB takes 88 seconds +* + ***************************************************************************/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "lpc288x.h" +#include "binarybuffer.h" + + +#define LOAD_TIMER_ERASE 0 +#define LOAD_TIMER_WRITE 1 + +#define FLASH_PAGE_SIZE 512 + +/* LPC288X control registers */ +#define DBGU_CIDR 0x8000507C +/* LPC288X flash registers */ +#define F_CTRL 0x80102000 /* Flash control register R/W 0x5 */ +#define F_STAT 0x80102004 /* Flash status register RO 0x45 */ +#define F_PROG_TIME 0x80102008 /* Flash program time register R/W 0 */ +#define F_WAIT 0x80102010 /* Flash read wait state register R/W 0xC004 */ +#define F_CLK_TIME 0x8010201C /* Flash clock divider for 66 kHz generation R/W 0 */ +#define F_INTEN_CLR 0x80102FD8 /* Clear interrupt enable bits WO - */ +#define F_INTEN_SET 0x80102FDC /* Set interrupt enable bits WO - */ +#define F_INT_STAT 0x80102FE0 /* Interrupt status bits RO 0 */ +#define F_INTEN 0x80102FE4 /* Interrupt enable bits RO 0 */ +#define F_INT_CLR 0x80102FE8 /* Clear interrupt status bits WO */ +#define F_INT_SET 0x80102FEC /* Set interrupt status bits WO - */ +#define FLASH_PD 0x80005030 /* Allows turning off the Flash memory for power savings. R/W 1*/ +#define FLASH_INIT 0x80005034 /* Monitors Flash readiness, such as recovery from Power Down mode. R/W -*/ + +/* F_CTRL bits */ +#define FC_CS 0x0001 +#define FC_FUNC 0x0002 +#define FC_WEN 0x0004 +#define FC_RD_LATCH 0x0020 +#define FC_PROTECT 0x0080 +#define FC_SET_DATA 0x0400 +#define FC_RSSL 0x0800 +#define FC_PROG_REQ 0x1000 +#define FC_CLR_BUF 0x4000 +#define FC_LOAD_REQ 0x8000 +/* F_STAT bits */ +#define FS_DONE 0x0001 +#define FS_PROGGNT 0x0002 +#define FS_RDY 0x0004 +#define FS_ERR 0x0020 +/* F_PROG_TIME */ +#define FPT_TIME_MASK 0x7FFF + +#define FPT_ENABLE 0x8000 +/* F_WAIT */ +#define FW_WAIT_STATES_MASK 0x00FF +#define FW_SET_MASK 0xC000 + +/* F_CLK_TIME */ +#define FCT_CLK_DIV_MASK 0x0FFF + +static uint32_t lpc288x_wait_status_busy(struct flash_bank *bank, int timeout); +static void lpc288x_load_timer(int erase, struct target *target); +static void lpc288x_set_flash_clk(struct flash_bank *bank); +static uint32_t lpc288x_system_ready(struct flash_bank *bank); + +static uint32_t lpc288x_wait_status_busy(struct flash_bank *bank, int timeout) +{ + uint32_t status; + struct target *target = bank->target; + do + { + alive_sleep(1); + timeout--; + target_read_u32(target, F_STAT, &status); + } while (((status & FS_DONE) == 0) && timeout); + + if (timeout == 0) + { + LOG_DEBUG("Timedout!"); + return ERROR_FLASH_OPERATION_FAILED; + } + return ERROR_OK; +} + +/* Read device id register and fill in driver info structure */ +static int lpc288x_read_part_info(struct flash_bank *bank) +{ + struct lpc288x_flash_bank *lpc288x_info = bank->driver_priv; + struct target *target = bank->target; + uint32_t cidr; + + int i = 0; + uint32_t offset; + + if (lpc288x_info->cidr == 0x0102100A) + return ERROR_OK; /* already probed, multiple probes may cause memory leak, not allowed */ + + /* Read and parse chip identification register */ + target_read_u32(target, DBGU_CIDR, &cidr); + + if (cidr != 0x0102100A) + { + LOG_WARNING("Cannot identify target as an LPC288X (%08" PRIx32 ")",cidr); + return ERROR_FLASH_OPERATION_FAILED; + } + + lpc288x_info->cidr = cidr; + lpc288x_info->sector_size_break = 0x000F0000; + lpc288x_info->target_name = "LPC288x"; + + /* setup the sector info... */ + offset = bank->base; + bank->num_sectors = 23; + bank->sectors = malloc(sizeof(struct flash_sector) * 23); + + for (i = 0; i < 15; 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 = 15; i < 23; 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; + } + + return ERROR_OK; +} + +static int lpc288x_protect_check(struct flash_bank *bank) +{ + return ERROR_OK; +} + +/* flash_bank LPC288x 0 0 0 0 */ +FLASH_BANK_COMMAND_HANDLER(lpc288x_flash_bank_command) +{ + struct lpc288x_flash_bank *lpc288x_info; + + if (CMD_ARGC < 6) + { + LOG_WARNING("incomplete flash_bank LPC288x configuration"); + return ERROR_FLASH_BANK_INVALID; + } + + lpc288x_info = malloc(sizeof(struct lpc288x_flash_bank)); + bank->driver_priv = lpc288x_info; + + /* part wasn't probed for info yet */ + lpc288x_info->cidr = 0; + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[6], lpc288x_info->cclk); + + return ERROR_OK; +} + +/* The frequency is the AHB clock frequency divided by (CLK_DIV ×3) + 1. + * This must be programmed such that the Flash Programming clock frequency is 66 kHz ± 20%. + * AHB = 12 MHz ? + * 12000000/66000 = 182 + * CLK_DIV = 60 ? */ +static void lpc288x_set_flash_clk(struct flash_bank *bank) +{ + uint32_t clk_time; + struct lpc288x_flash_bank *lpc288x_info = bank->driver_priv; + clk_time = (lpc288x_info->cclk / 66000) / 3; + target_write_u32(bank->target, F_CTRL, FC_CS | FC_WEN); + target_write_u32(bank->target, F_CLK_TIME, clk_time); +} + +/* AHB tcyc (in ns) 83 ns + * LOAD_TIMER_ERASE FPT_TIME = ((400,000,000 / AHB tcyc (in ns)) - 2) / 512 + * = 9412 (9500) (AN10548 9375) + * LOAD_TIMER_WRITE FPT_TIME = ((1,000,000 / AHB tcyc (in ns)) - 2) / 512 + * = 23 (75) (AN10548 72 - is this wrong?) + * TODO: Sort out timing calcs ;) */ +static void lpc288x_load_timer(int erase, struct target *target) +{ + if (erase == LOAD_TIMER_ERASE) + { + target_write_u32(target, F_PROG_TIME, FPT_ENABLE | 9500); + } + else + { + target_write_u32(target, F_PROG_TIME, FPT_ENABLE | 75); + } +} + +static uint32_t lpc288x_system_ready(struct flash_bank *bank) +{ + struct lpc288x_flash_bank *lpc288x_info = bank->driver_priv; + if (lpc288x_info->cidr == 0) + { + return ERROR_FLASH_BANK_NOT_PROBED; + } + + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + return ERROR_OK; +} + +static int lpc288x_erase_check(struct flash_bank *bank) +{ + uint32_t status = lpc288x_system_ready(bank); /* probed? halted? */ + if (status != ERROR_OK) + { + LOG_INFO("Processor not halted/not probed"); + return status; + } + + return ERROR_OK; +} + +static int lpc288x_erase(struct flash_bank *bank, int first, int last) +{ + uint32_t status; + int sector; + struct target *target = bank->target; + + status = lpc288x_system_ready(bank); /* probed? halted? */ + if (status != ERROR_OK) + { + return status; + } + + if ((first < 0) || (last < first) || (last >= bank->num_sectors)) + { + LOG_INFO("Bad sector range"); + return ERROR_FLASH_SECTOR_INVALID; + } + + /* Configure the flash controller timing */ + lpc288x_set_flash_clk(bank); + + for (sector = first; sector <= last; sector++) + { + if (lpc288x_wait_status_busy(bank, 1000) != ERROR_OK) + { + return ERROR_FLASH_OPERATION_FAILED; + } + + lpc288x_load_timer(LOAD_TIMER_ERASE,target); + + target_write_u32(target, bank->sectors[sector].offset, 0x00); + + target_write_u32(target, F_CTRL, FC_PROG_REQ | FC_PROTECT | FC_CS); + } + if (lpc288x_wait_status_busy(bank, 1000) != ERROR_OK) + { + return ERROR_FLASH_OPERATION_FAILED; + } + return ERROR_OK; +} + +static int lpc288x_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) +{ + uint8_t page_buffer[FLASH_PAGE_SIZE]; + uint32_t status, source_offset,dest_offset; + struct target *target = bank->target; + uint32_t bytes_remaining = count; + uint32_t first_sector, last_sector, sector, page; + int i; + + /* probed? halted? */ + status = lpc288x_system_ready(bank); + if (status != ERROR_OK) + { + return status; + } + + /* Initialise search indices */ + first_sector = last_sector = 0xffffffff; + + /* validate the write range... */ + for (i = 0; i < bank->num_sectors; i++) + { + if ((offset >= bank->sectors[i].offset) && + (offset < (bank->sectors[i].offset + bank->sectors[i].size)) && + (first_sector == 0xffffffff)) + { + first_sector = i; + /* all writes must start on a sector boundary... */ + if (offset % bank->sectors[i].size) + { + LOG_INFO("offset 0x%" PRIx32 " breaks required alignment 0x%" PRIx32 "", offset, bank->sectors[i].size); + return ERROR_FLASH_DST_BREAKS_ALIGNMENT; + } + } + if (((offset + count) > bank->sectors[i].offset) && + ((offset + count) <= (bank->sectors[i].offset + bank->sectors[i].size)) && + (last_sector == 0xffffffff)) + { + last_sector = i; + } + } + + /* Range check... */ + if (first_sector == 0xffffffff || last_sector == 0xffffffff) + { + LOG_INFO("Range check failed %" PRIx32 " %" PRIx32 "", offset, count); + return ERROR_FLASH_DST_OUT_OF_BANK; + } + + /* Configure the flash controller timing */ + lpc288x_set_flash_clk(bank); + + /* initialise the offsets */ + source_offset = 0; + dest_offset = 0; + + for (sector = first_sector; sector <= last_sector; sector++) + { + for (page = 0; page < bank->sectors[sector].size / FLASH_PAGE_SIZE; page++) + { + if (bytes_remaining == 0) + { + count = 0; + memset(page_buffer, 0xFF, FLASH_PAGE_SIZE); + } + else if (bytes_remaining < FLASH_PAGE_SIZE) + { + count = bytes_remaining; + memset(page_buffer, 0xFF, FLASH_PAGE_SIZE); + memcpy(page_buffer, &buffer[source_offset], count); + } + else + { + count = FLASH_PAGE_SIZE; + memcpy(page_buffer, &buffer[source_offset], count); + } + + /* Wait for flash to become ready */ + if (lpc288x_wait_status_busy(bank, 1000) != ERROR_OK) + { + return ERROR_FLASH_OPERATION_FAILED; + } + + /* fill flash data latches with 1's */ + target_write_u32(target, F_CTRL, FC_CS | FC_SET_DATA | FC_WEN | FC_FUNC); + + target_write_u32(target, F_CTRL, FC_CS | FC_WEN | FC_FUNC); + /*would be better to use the clean target_write_buffer() interface but + * it seems not to be a LOT slower.... + * bulk_write_memory() is no quicker :(*/ +#if 1 + if (target_write_memory(target, offset + dest_offset, 4, 128, page_buffer) != ERROR_OK) + { + LOG_ERROR("Write failed s %" PRIx32 " p %" PRIx32 "", sector, page); + return ERROR_FLASH_OPERATION_FAILED; + } +#else + if (target_write_buffer(target, offset + dest_offset, FLASH_PAGE_SIZE, page_buffer) != ERROR_OK) + { + LOG_INFO("Write to flash buffer failed"); + return ERROR_FLASH_OPERATION_FAILED; + } +#endif + dest_offset += FLASH_PAGE_SIZE; + source_offset += count; + bytes_remaining -= count; + + lpc288x_load_timer(LOAD_TIMER_WRITE, target); + + target_write_u32(target, F_CTRL, FC_PROG_REQ | FC_PROTECT | FC_FUNC | FC_CS); + } + } + + return ERROR_OK; +} + +static int lpc288x_probe(struct flash_bank *bank) +{ + /* we only deal with LPC2888 so flash config is fixed */ + struct lpc288x_flash_bank *lpc288x_info = bank->driver_priv; + int retval; + + if (lpc288x_info->cidr != 0) + { + return ERROR_OK; /* already probed */ + } + + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + retval = lpc288x_read_part_info(bank); + if (retval != ERROR_OK) + return retval; + return ERROR_OK; +} + +static int lpc288x_info(struct flash_bank *bank, char *buf, int buf_size) +{ + snprintf(buf, buf_size, "lpc288x flash driver"); + return ERROR_OK; +} + +static int lpc288x_protect(struct flash_bank *bank, int set, int first, int last) +{ + int lockregion, status; + uint32_t value; + struct target *target = bank->target; + + /* probed? halted? */ + status = lpc288x_system_ready(bank); + if (status != ERROR_OK) + { + return status; + } + + if ((first < 0) || (last < first) || (last >= bank->num_sectors)) + { + return ERROR_FLASH_SECTOR_INVALID; + } + + /* Configure the flash controller timing */ + lpc288x_set_flash_clk(bank); + + for (lockregion = first; lockregion <= last; lockregion++) + { + if (set) + { + /* write an odd value to base addy to protect... */ + value = 0x01; + } + else + { + /* write an even value to base addy to unprotect... */ + value = 0x00; + } + target_write_u32(target, bank->sectors[lockregion].offset, value); + target_write_u32(target, F_CTRL, FC_LOAD_REQ | FC_PROTECT | FC_WEN | FC_FUNC | FC_CS); + } + + return ERROR_OK; +} + +struct flash_driver lpc288x_flash = { + .name = "lpc288x", + .flash_bank_command = &lpc288x_flash_bank_command, + .erase = &lpc288x_erase, + .protect = &lpc288x_protect, + .write = &lpc288x_write, + .probe = &lpc288x_probe, + .auto_probe = &lpc288x_probe, + .erase_check = &lpc288x_erase_check, + .protect_check = &lpc288x_protect_check, + .info = &lpc288x_info, + }; diff --git a/src/flash/nor/lpc288x.h b/src/flash/nor/lpc288x.h new file mode 100644 index 00000000..5a71ee08 --- /dev/null +++ b/src/flash/nor/lpc288x.h @@ -0,0 +1,39 @@ +/*************************************************************************** + * Copyright (C) 2008 by * + * Karl RobinSod * + * * + * 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 lpc288x_H +#define lpc288x_H + +#include "flash.h" + +struct lpc288x_flash_bank +{ + uint32_t working_area; + uint32_t working_area_size; + + /* chip id register */ + uint32_t cidr; + char * target_name; + uint32_t cclk; + + uint32_t sector_size_break; +}; + +#endif /* lpc288x_H */ diff --git a/src/flash/nor/lpc2900.c b/src/flash/nor/lpc2900.c new file mode 100644 index 00000000..81e2def4 --- /dev/null +++ b/src/flash/nor/lpc2900.c @@ -0,0 +1,1834 @@ +/*************************************************************************** + * Copyright (C) 2009 by * + * Rolf Meeser * + * * + * 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 "image.h" +#include "flash.h" +#include "binarybuffer.h" +#include "armv4_5.h" +#include "algorithm.h" + + +/* 1024 bytes */ +#define KiB 1024 + +/* Some flash constants */ +#define FLASH_PAGE_SIZE 512 /* bytes */ +#define FLASH_ERASE_TIME 100000 /* microseconds */ +#define FLASH_PROGRAM_TIME 1000 /* microseconds */ + +/* Chip ID / Feature Registers */ +#define CHIPID 0xE0000000 /* Chip ID */ +#define FEAT0 0xE0000100 /* Chip feature 0 */ +#define FEAT1 0xE0000104 /* Chip feature 1 */ +#define FEAT2 0xE0000108 /* Chip feature 2 (contains flash size indicator) */ +#define FEAT3 0xE000010C /* Chip feature 3 */ + +#define EXPECTED_CHIPID 0x209CE02B /* Chip ID of all LPC2900 devices */ + +/* Flash/EEPROM Control Registers */ +#define FCTR 0x20200000 /* Flash control */ +#define FPTR 0x20200008 /* Flash program-time */ +#define FTCTR 0x2020000C /* Flash test control */ +#define FBWST 0x20200010 /* Flash bridge wait-state */ +#define FCRA 0x2020001C /* Flash clock divider */ +#define FMSSTART 0x20200020 /* Flash Built-In Selft Test start address */ +#define FMSSTOP 0x20200024 /* Flash Built-In Selft Test stop address */ +#define FMS16 0x20200028 /* Flash 16-bit signature */ +#define FMSW0 0x2020002C /* Flash 128-bit signature Word 0 */ +#define FMSW1 0x20200030 /* Flash 128-bit signature Word 1 */ +#define FMSW2 0x20200034 /* Flash 128-bit signature Word 2 */ +#define FMSW3 0x20200038 /* Flash 128-bit signature Word 3 */ + +#define EECMD 0x20200080 /* EEPROM command */ +#define EEADDR 0x20200084 /* EEPROM address */ +#define EEWDATA 0x20200088 /* EEPROM write data */ +#define EERDATA 0x2020008C /* EEPROM read data */ +#define EEWSTATE 0x20200090 /* EEPROM wait state */ +#define EECLKDIV 0x20200094 /* EEPROM clock divider */ +#define EEPWRDWN 0x20200098 /* EEPROM power-down/start */ +#define EEMSSTART 0x2020009C /* EEPROM BIST start address */ +#define EEMSSTOP 0x202000A0 /* EEPROM BIST stop address */ +#define EEMSSIG 0x202000A4 /* EEPROM 24-bit BIST signature */ + +#define INT_CLR_ENABLE 0x20200FD8 /* Flash/EEPROM interrupt clear enable */ +#define INT_SET_ENABLE 0x20200FDC /* Flash/EEPROM interrupt set enable */ +#define INT_STATUS 0x20200FE0 /* Flash/EEPROM interrupt status */ +#define INT_ENABLE 0x20200FE4 /* Flash/EEPROM interrupt enable */ +#define INT_CLR_STATUS 0x20200FE8 /* Flash/EEPROM interrupt clear status */ +#define INT_SET_STATUS 0x20200FEC /* Flash/EEPROM interrupt set status */ + +/* Interrupt sources */ +#define INTSRC_END_OF_PROG (1 << 28) +#define INTSRC_END_OF_BIST (1 << 27) +#define INTSRC_END_OF_RDWR (1 << 26) +#define INTSRC_END_OF_MISR (1 << 2) +#define INTSRC_END_OF_BURN (1 << 1) +#define INTSRC_END_OF_ERASE (1 << 0) + + +/* FCTR bits */ +#define FCTR_FS_LOADREQ (1 << 15) +#define FCTR_FS_CACHECLR (1 << 14) +#define FCTR_FS_CACHEBYP (1 << 13) +#define FCTR_FS_PROGREQ (1 << 12) +#define FCTR_FS_RLS (1 << 11) +#define FCTR_FS_PDL (1 << 10) +#define FCTR_FS_PD (1 << 9) +#define FCTR_FS_WPB (1 << 7) +#define FCTR_FS_ISS (1 << 6) +#define FCTR_FS_RLD (1 << 5) +#define FCTR_FS_DCR (1 << 4) +#define FCTR_FS_WEB (1 << 2) +#define FCTR_FS_WRE (1 << 1) +#define FCTR_FS_CS (1 << 0) +/* FPTR bits */ +#define FPTR_EN_T (1 << 15) +/* FTCTR bits */ +#define FTCTR_FS_BYPASS_R (1 << 29) +#define FTCTR_FS_BYPASS_W (1 << 28) +/* FMSSTOP bits */ +#define FMSSTOP_MISR_START (1 << 17) +/* EEMSSTOP bits */ +#define EEMSSTOP_STRTBIST (1 << 31) + +/* Index sector */ +#define ISS_CUSTOMER_START1 (0x830) +#define ISS_CUSTOMER_END1 (0xA00) +#define ISS_CUSTOMER_SIZE1 (ISS_CUSTOMER_END1 - ISS_CUSTOMER_START1) +#define ISS_CUSTOMER_NWORDS1 (ISS_CUSTOMER_SIZE1 / 4) +#define ISS_CUSTOMER_START2 (0xA40) +#define ISS_CUSTOMER_END2 (0xC00) +#define ISS_CUSTOMER_SIZE2 (ISS_CUSTOMER_END2 - ISS_CUSTOMER_START2) +#define ISS_CUSTOMER_NWORDS2 (ISS_CUSTOMER_SIZE2 / 4) +#define ISS_CUSTOMER_SIZE (ISS_CUSTOMER_SIZE1 + ISS_CUSTOMER_SIZE2) + + + +/** + * Private data for \c lpc2900 flash driver. + */ +struct lpc2900_flash_bank +{ + /** + * Holds the value read from CHIPID register. + * The driver will not load if the chipid doesn't match the expected + * value of 0x209CE02B of the LPC2900 family. A probe will only be done + * if the chipid does not yet contain the expected value. + */ + uint32_t chipid; + + /** + * String holding device name. + * This string is set by the probe function to the type number of the + * device. It takes the form "LPC29xx". + */ + char * target_name; + + /** + * System clock frequency. + * Holds the clock frequency in Hz, as passed by the configuration file + * to the flash bank command. + */ + uint32_t clk_sys_fmc; + + /** + * Flag to indicate that dangerous operations are possible. + * This flag can be set by passing the correct password to the + * lpc2900 password command. If set, other dangerous commands, + * which operate on the index sector, can be executed. + */ + uint32_t risky; + + /** + * Maximum contiguous block of internal SRAM (bytes). + * Autodetected by the driver. Not the total amount of SRAM, only the + * the largest \em contiguous block! + */ + uint32_t max_ram_block; + +}; + + +static uint32_t lpc2900_wait_status(struct flash_bank *bank, uint32_t mask, int timeout); +static void lpc2900_setup(struct flash_bank *bank); +static uint32_t lpc2900_is_ready(struct flash_bank *bank); +static uint32_t lpc2900_read_security_status(struct flash_bank *bank); +static uint32_t lpc2900_run_bist128(struct flash_bank *bank, + uint32_t addr_from, uint32_t addr_to, + uint32_t (*signature)[4] ); +static uint32_t lpc2900_address2sector(struct flash_bank *bank, uint32_t offset); +static uint32_t lpc2900_calc_tr( uint32_t clock, uint32_t time ); + + +/*********************** Helper functions **************************/ + + +/** + * Wait for an event in mask to occur in INT_STATUS. + * + * Return when an event occurs, or after a timeout. + * + * @param[in] bank Pointer to the flash bank descriptor + * @param[in] mask Mask to be used for INT_STATUS + * @param[in] timeout Timeout in ms + */ +static uint32_t lpc2900_wait_status( struct flash_bank *bank, + uint32_t mask, + int timeout ) +{ + uint32_t int_status; + struct target *target = bank->target; + + + do + { + alive_sleep(1); + timeout--; + target_read_u32(target, INT_STATUS, &int_status); + } + while( ((int_status & mask) == 0) && (timeout != 0) ); + + if (timeout == 0) + { + LOG_DEBUG("Timeout!"); + return ERROR_FLASH_OPERATION_FAILED; + } + + return ERROR_OK; +} + + + +/** + * Set up the flash for erase/program operations. + * + * Enable the flash, and set the correct CRA clock of 66 kHz. + * + * @param bank Pointer to the flash bank descriptor + */ +static void lpc2900_setup( struct flash_bank *bank ) +{ + uint32_t fcra; + struct lpc2900_flash_bank *lpc2900_info = bank->driver_priv; + + + /* Power up the flash block */ + target_write_u32( bank->target, FCTR, FCTR_FS_WEB | FCTR_FS_CS ); + + + fcra = (lpc2900_info->clk_sys_fmc / (3 * 66000)) - 1; + target_write_u32( bank->target, FCRA, fcra ); +} + + + +/** + * Check if device is ready. + * + * Check if device is ready for flash operation: + * Must have been successfully probed. + * Must be halted. + */ +static uint32_t lpc2900_is_ready( struct flash_bank *bank ) +{ + struct lpc2900_flash_bank *lpc2900_info = bank->driver_priv; + + if( lpc2900_info->chipid != EXPECTED_CHIPID ) + { + return ERROR_FLASH_BANK_NOT_PROBED; + } + + if( bank->target->state != TARGET_HALTED ) + { + LOG_ERROR( "Target not halted" ); + return ERROR_TARGET_NOT_HALTED; + } + + return ERROR_OK; +} + + +/** + * Read the status of sector security from the index sector. + * + * @param bank Pointer to the flash bank descriptor + */ +static uint32_t lpc2900_read_security_status( struct flash_bank *bank ) +{ + uint32_t status; + if( (status = lpc2900_is_ready( bank )) != ERROR_OK ) + { + return status; + } + + struct target *target = bank->target; + + /* Enable ISS access */ + target_write_u32(target, FCTR, FCTR_FS_CS | FCTR_FS_WEB | FCTR_FS_ISS); + + /* Read the relevant block of memory from the ISS sector */ + uint32_t iss_secured_field[ 0x230/16 ][ 4 ]; + target_read_memory(target, bank->base + 0xC00, 4, 0x230/4, + (uint8_t *)iss_secured_field); + + /* Disable ISS access */ + target_write_u32(target, FCTR, FCTR_FS_CS | FCTR_FS_WEB); + + /* Check status of each sector. Note that the sector numbering in the LPC2900 + * is different from the logical sector numbers used in OpenOCD! + * Refer to the user manual for details. + * + * All zeros (16x 0x00) are treated as a secured sector (is_protected = 1) + * All ones (16x 0xFF) are treated as a non-secured sector (is_protected = 0) + * Anything else is undefined (is_protected = -1). This is treated as + * a protected sector! + */ + int sector; + int index; + for( sector = 0; sector < bank->num_sectors; sector++ ) + { + /* Convert logical sector number to physical sector number */ + if( sector <= 4 ) + { + index = sector + 11; + } + else if( sector <= 7 ) + { + index = sector + 27; + } + else + { + index = sector - 8; + } + + bank->sectors[sector].is_protected = -1; + + if ( + (iss_secured_field[index][0] == 0x00000000) && + (iss_secured_field[index][1] == 0x00000000) && + (iss_secured_field[index][2] == 0x00000000) && + (iss_secured_field[index][3] == 0x00000000) ) + { + bank->sectors[sector].is_protected = 1; + } + + if ( + (iss_secured_field[index][0] == 0xFFFFFFFF) && + (iss_secured_field[index][1] == 0xFFFFFFFF) && + (iss_secured_field[index][2] == 0xFFFFFFFF) && + (iss_secured_field[index][3] == 0xFFFFFFFF) ) + { + bank->sectors[sector].is_protected = 0; + } + } + + return ERROR_OK; +} + + +/** + * Use BIST to calculate a 128-bit hash value over a range of flash. + * + * @param bank Pointer to the flash bank descriptor + * @param addr_from + * @param addr_to + * @param signature + */ +static uint32_t lpc2900_run_bist128(struct flash_bank *bank, + uint32_t addr_from, + uint32_t addr_to, + uint32_t (*signature)[4] ) +{ + struct target *target = bank->target; + + /* Clear END_OF_MISR interrupt status */ + target_write_u32( target, INT_CLR_STATUS, INTSRC_END_OF_MISR ); + + /* Start address */ + target_write_u32( target, FMSSTART, addr_from >> 4); + /* End address, and issue start command */ + target_write_u32( target, FMSSTOP, (addr_to >> 4) | FMSSTOP_MISR_START ); + + /* Poll for end of operation. Calculate a reasonable timeout. */ + if( lpc2900_wait_status( bank, INTSRC_END_OF_MISR, 1000 ) != ERROR_OK ) + { + return ERROR_FLASH_OPERATION_FAILED; + } + + /* Return the signature */ + target_read_memory( target, FMSW0, 4, 4, (uint8_t *)signature ); + + return ERROR_OK; +} + + +/** + * Return sector number for given address. + * + * Return the (logical) sector number for a given relative address. + * No sanity check is done. It assumed that the address is valid. + * + * @param bank Pointer to the flash bank descriptor + * @param offset Offset address relative to bank start + */ +static uint32_t lpc2900_address2sector( struct flash_bank *bank, + uint32_t offset ) +{ + uint32_t address = bank->base + offset; + + + /* Run through all sectors of this bank */ + int sector; + for( sector = 0; sector < bank->num_sectors; sector++ ) + { + /* Return immediately if address is within the current sector */ + if( address < (bank->sectors[sector].offset + bank->sectors[sector].size) ) + { + return sector; + } + } + + /* We should never come here. If we do, return an arbitrary sector number. */ + return 0; +} + + + + +/** + * Write one page to the index sector. + * + * @param bank Pointer to the flash bank descriptor + * @param pagenum Page number (0...7) + * @param page Page array (FLASH_PAGE_SIZE bytes) + */ +static int lpc2900_write_index_page( struct flash_bank *bank, + int pagenum, + uint8_t (*page)[FLASH_PAGE_SIZE] ) +{ + /* Only pages 4...7 are user writable */ + if ((pagenum < 4) || (pagenum > 7)) + { + LOG_ERROR("Refuse to burn index sector page %d", pagenum); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + + /* Get target, and check if it's halted */ + struct target *target = bank->target; + if( target->state != TARGET_HALTED ) + { + LOG_ERROR( "Target not halted" ); + return ERROR_TARGET_NOT_HALTED; + } + + /* Private info */ + struct lpc2900_flash_bank *lpc2900_info = bank->driver_priv; + + /* Enable flash block and set the correct CRA clock of 66 kHz */ + lpc2900_setup( bank ); + + /* Un-protect the index sector */ + target_write_u32( target, bank->base, 0 ); + target_write_u32( target, FCTR, + FCTR_FS_LOADREQ | FCTR_FS_WPB | FCTR_FS_ISS | + FCTR_FS_WEB | FCTR_FS_WRE | FCTR_FS_CS ); + + /* Set latch load mode */ + target_write_u32( target, FCTR, + FCTR_FS_ISS | FCTR_FS_WEB | FCTR_FS_WRE | FCTR_FS_CS ); + + /* Write whole page to flash data latches */ + if( target_write_memory( target, + bank->base + pagenum * FLASH_PAGE_SIZE, + 4, FLASH_PAGE_SIZE / 4, (uint8_t *)page) != ERROR_OK ) + { + LOG_ERROR("Index sector write failed @ page %d", pagenum); + target_write_u32( target, FCTR, FCTR_FS_CS | FCTR_FS_WEB ); + + return ERROR_FLASH_OPERATION_FAILED; + } + + /* Clear END_OF_BURN interrupt status */ + target_write_u32( target, INT_CLR_STATUS, INTSRC_END_OF_BURN ); + + /* Set the program/erase time to FLASH_PROGRAM_TIME */ + target_write_u32(target, FPTR, + FPTR_EN_T | lpc2900_calc_tr( lpc2900_info->clk_sys_fmc, + FLASH_PROGRAM_TIME )); + + /* Trigger flash write */ + target_write_u32( target, FCTR, + FCTR_FS_PROGREQ | FCTR_FS_ISS | + FCTR_FS_WPB | FCTR_FS_WRE | FCTR_FS_CS ); + + /* Wait for the end of the write operation. If it's not over after one + * second, something went dreadfully wrong... :-( + */ + if (lpc2900_wait_status(bank, INTSRC_END_OF_BURN, 1000) != ERROR_OK) + { + LOG_ERROR("Index sector write failed @ page %d", pagenum); + target_write_u32(target, FCTR, FCTR_FS_CS | FCTR_FS_WEB); + + return ERROR_FLASH_OPERATION_FAILED; + } + + target_write_u32( target, FCTR, FCTR_FS_CS | FCTR_FS_WEB ); + + return ERROR_OK; +} + + + +/** + * Calculate FPTR.TR register value for desired program/erase time. + * + * @param clock System clock in Hz + * @param time Program/erase time in µs + */ +static uint32_t lpc2900_calc_tr( uint32_t clock, uint32_t time ) +{ + /* ((time[µs]/1e6) * f[Hz]) + 511 + * FPTR.TR = ------------------------------- + * 512 + * + * The result is the + */ + + uint32_t tr_val = (uint32_t)((((time / 1e6) * clock) + 511.0) / 512.0); + + return tr_val; +} + + +/*********************** Private flash commands **************************/ + + +/** + * Command to determine the signature of the whole flash. + * + * Uses the Built-In-Self-Test (BIST) to generate a 128-bit hash value + * of the flash content. + */ +COMMAND_HANDLER(lpc2900_handle_signature_command) +{ + uint32_t status; + uint32_t signature[4]; + + + if( CMD_ARGC < 1 ) + { + LOG_WARNING( "Too few arguments. Call: lpc2900 signature " ); + return ERROR_FLASH_BANK_INVALID; + } + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + if( bank->target->state != TARGET_HALTED ) + { + LOG_ERROR( "Target not halted" ); + return ERROR_TARGET_NOT_HALTED; + } + + /* Run BIST over whole flash range */ + if( (status = lpc2900_run_bist128( bank, + bank->base, + bank->base + (bank->size - 1), + &signature) + ) != ERROR_OK ) + { + return status; + } + + command_print( CMD_CTX, "signature: 0x%8.8" PRIx32 + ":0x%8.8" PRIx32 + ":0x%8.8" PRIx32 + ":0x%8.8" PRIx32, + signature[3], signature[2], signature[1], signature[0] ); + + return ERROR_OK; +} + + + +/** + * Store customer info in file. + * + * Read customer info from index sector, and store that block of data into + * a disk file. The format is binary. + */ +COMMAND_HANDLER(lpc2900_handle_read_custom_command) +{ + if( CMD_ARGC < 2 ) + { + return ERROR_COMMAND_SYNTAX_ERROR; + } + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + struct lpc2900_flash_bank *lpc2900_info = bank->driver_priv; + lpc2900_info->risky = 0; + + /* Get target, and check if it's halted */ + struct target *target = bank->target; + if( target->state != TARGET_HALTED ) + { + LOG_ERROR( "Target not halted" ); + return ERROR_TARGET_NOT_HALTED; + } + + /* Storage for customer info. Read in two parts */ + uint32_t customer[ ISS_CUSTOMER_NWORDS1 + ISS_CUSTOMER_NWORDS2 ]; + + /* Enable access to index sector */ + target_write_u32( target, FCTR, FCTR_FS_CS | FCTR_FS_WEB | FCTR_FS_ISS ); + + /* Read two parts */ + target_read_memory( target, bank->base+ISS_CUSTOMER_START1, 4, + ISS_CUSTOMER_NWORDS1, + (uint8_t *)&customer[0] ); + target_read_memory( target, bank->base+ISS_CUSTOMER_START2, 4, + ISS_CUSTOMER_NWORDS2, + (uint8_t *)&customer[ISS_CUSTOMER_NWORDS1] ); + + /* Deactivate access to index sector */ + target_write_u32( target, FCTR, FCTR_FS_CS | FCTR_FS_WEB ); + + /* Try and open the file */ + struct fileio fileio; + const char *filename = CMD_ARGV[1]; + int ret = fileio_open( &fileio, filename, FILEIO_WRITE, FILEIO_BINARY ); + if( ret != ERROR_OK ) + { + LOG_WARNING( "Could not open file %s", filename ); + return ret; + } + + size_t nwritten; + ret = fileio_write( &fileio, sizeof(customer), + (const uint8_t *)customer, &nwritten ); + if( ret != ERROR_OK ) + { + LOG_ERROR( "Write operation to file %s failed", filename ); + fileio_close( &fileio ); + return ret; + } + + fileio_close( &fileio ); + + return ERROR_OK; +} + + + + +/** + * Enter password to enable potentially dangerous options. + */ +COMMAND_HANDLER(lpc2900_handle_password_command) +{ + if (CMD_ARGC < 2) + { + return ERROR_COMMAND_SYNTAX_ERROR; + } + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + struct lpc2900_flash_bank *lpc2900_info = bank->driver_priv; + +#define ISS_PASSWORD "I_know_what_I_am_doing" + + lpc2900_info->risky = !strcmp( CMD_ARGV[1], ISS_PASSWORD ); + + if( !lpc2900_info->risky ) + { + command_print(CMD_CTX, "Wrong password (use '%s')", ISS_PASSWORD); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + + command_print(CMD_CTX, + "Potentially dangerous operation allowed in next command!"); + + return ERROR_OK; +} + + + +/** + * Write customer info from file to the index sector. + */ +COMMAND_HANDLER(lpc2900_handle_write_custom_command) +{ + if (CMD_ARGC < 2) + { + return ERROR_COMMAND_SYNTAX_ERROR; + } + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + struct lpc2900_flash_bank *lpc2900_info = bank->driver_priv; + + /* Check if command execution is allowed. */ + if( !lpc2900_info->risky ) + { + command_print( CMD_CTX, "Command execution not allowed!" ); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + lpc2900_info->risky = 0; + + /* Get target, and check if it's halted */ + struct target *target = bank->target; + if (target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* The image will always start at offset 0 */ + struct image image; + image.base_address_set = 1; + image.base_address = 0; + image.start_address_set = 0; + + const char *filename = CMD_ARGV[1]; + const char *type = (CMD_ARGC >= 3) ? CMD_ARGV[2] : NULL; + retval = image_open(&image, filename, type); + if (retval != ERROR_OK) + { + return retval; + } + + /* Do a sanity check: The image must be exactly the size of the customer + programmable area. Any other size is rejected. */ + if( image.num_sections != 1 ) + { + LOG_ERROR("Only one section allowed in image file."); + return ERROR_COMMAND_SYNTAX_ERROR; + } + if( (image.sections[0].base_address != 0) || + (image.sections[0].size != ISS_CUSTOMER_SIZE) ) + { + LOG_ERROR("Incorrect image file size. Expected %d, " + "got %" PRIu32, + ISS_CUSTOMER_SIZE, image.sections[0].size); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + /* Well boys, I reckon this is it... */ + + /* Customer info is split into two blocks in pages 4 and 5. */ + uint8_t page[FLASH_PAGE_SIZE]; + + /* Page 4 */ + uint32_t offset = ISS_CUSTOMER_START1 % FLASH_PAGE_SIZE; + memset( page, 0xff, FLASH_PAGE_SIZE ); + size_t size_read; + retval = image_read_section( &image, 0, 0, + ISS_CUSTOMER_SIZE1, &page[offset], &size_read); + if( retval != ERROR_OK ) + { + LOG_ERROR("couldn't read from file '%s'", filename); + image_close(&image); + return retval; + } + if( (retval = lpc2900_write_index_page( bank, 4, &page )) != ERROR_OK ) + { + image_close(&image); + return retval; + } + + /* Page 5 */ + offset = ISS_CUSTOMER_START2 % FLASH_PAGE_SIZE; + memset( page, 0xff, FLASH_PAGE_SIZE ); + retval = image_read_section( &image, 0, ISS_CUSTOMER_SIZE1, + ISS_CUSTOMER_SIZE2, &page[offset], &size_read); + if( retval != ERROR_OK ) + { + LOG_ERROR("couldn't read from file '%s'", filename); + image_close(&image); + return retval; + } + if( (retval = lpc2900_write_index_page( bank, 5, &page )) != ERROR_OK ) + { + image_close(&image); + return retval; + } + + image_close(&image); + + return ERROR_OK; +} + + + +/** + * Activate 'sector security' for a range of sectors. + */ +COMMAND_HANDLER(lpc2900_handle_secure_sector_command) +{ + if (CMD_ARGC < 3) + { + return ERROR_COMMAND_SYNTAX_ERROR; + } + + /* Get the bank descriptor */ + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + struct lpc2900_flash_bank *lpc2900_info = bank->driver_priv; + + /* Check if command execution is allowed. */ + if( !lpc2900_info->risky ) + { + command_print( CMD_CTX, "Command execution not allowed! " + "(use 'password' command first)"); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + lpc2900_info->risky = 0; + + /* Read sector range, and do a sanity check. */ + int first, last; + COMMAND_PARSE_NUMBER(int, CMD_ARGV[1], first); + COMMAND_PARSE_NUMBER(int, CMD_ARGV[2], last); + if( (first >= bank->num_sectors) || + (last >= bank->num_sectors) || + (first > last) ) + { + command_print( CMD_CTX, "Illegal sector range" ); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + + uint8_t page[FLASH_PAGE_SIZE]; + int sector; + + /* Sectors in page 6 */ + if( (first <= 4) || (last >= 8) ) + { + memset( &page, 0xff, FLASH_PAGE_SIZE ); + for( sector = first; sector <= last; sector++ ) + { + if( sector <= 4 ) + { + memset( &page[0xB0 + 16*sector], 0, 16 ); + } + else if( sector >= 8 ) + { + memset( &page[0x00 + 16*(sector - 8)], 0, 16 ); + } + } + + if( (retval = lpc2900_write_index_page( bank, 6, &page )) != ERROR_OK ) + { + LOG_ERROR("failed to update index sector page 6"); + return retval; + } + } + + /* Sectors in page 7 */ + if( (first <= 7) && (last >= 5) ) + { + memset( &page, 0xff, FLASH_PAGE_SIZE ); + for( sector = first; sector <= last; sector++ ) + { + if( (sector >= 5) && (sector <= 7) ) + { + memset( &page[0x00 + 16*(sector - 5)], 0, 16 ); + } + } + + if( (retval = lpc2900_write_index_page( bank, 7, &page )) != ERROR_OK ) + { + LOG_ERROR("failed to update index sector page 7"); + return retval; + } + } + + command_print( CMD_CTX, + "Sectors security will become effective after next power cycle"); + + /* Update the sector security status */ + if ( lpc2900_read_security_status(bank) != ERROR_OK ) + { + LOG_ERROR( "Cannot determine sector security status" ); + return ERROR_FLASH_OPERATION_FAILED; + } + + return ERROR_OK; +} + + + +/** + * Activate JTAG protection. + */ +COMMAND_HANDLER(lpc2900_handle_secure_jtag_command) +{ + if (CMD_ARGC < 1) + { + return ERROR_COMMAND_SYNTAX_ERROR; + } + + /* Get the bank descriptor */ + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + struct lpc2900_flash_bank *lpc2900_info = bank->driver_priv; + + /* Check if command execution is allowed. */ + if( !lpc2900_info->risky ) + { + command_print( CMD_CTX, "Command execution not allowed! " + "(use 'password' command first)"); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + lpc2900_info->risky = 0; + + /* Prepare page */ + uint8_t page[FLASH_PAGE_SIZE]; + memset( &page, 0xff, FLASH_PAGE_SIZE ); + + + /* Insert "soft" protection word */ + page[0x30 + 15] = 0x7F; + page[0x30 + 11] = 0x7F; + page[0x30 + 7] = 0x7F; + page[0x30 + 3] = 0x7F; + + /* Write to page 5 */ + if( (retval = lpc2900_write_index_page( bank, 5, &page )) + != ERROR_OK ) + { + LOG_ERROR("failed to update index sector page 5"); + return retval; + } + + LOG_INFO("JTAG security set. Good bye!"); + + return ERROR_OK; +} + + + +/*********************** Flash interface functions **************************/ + +static const struct command_registration lpc2900_exec_command_handlers[] = { + { + .name = "signature", + .handler = &lpc2900_handle_signature_command, + .mode = COMMAND_EXEC, + .usage = "", + .help = "print device signature of flash bank", + }, + { + .name = "read_custom", + .handler = &lpc2900_handle_read_custom_command, + .mode = COMMAND_EXEC, + .usage = " ", + .help = "read customer information from index sector to file", + }, + { + .name = "password", + .handler = &lpc2900_handle_password_command, + .mode = COMMAND_EXEC, + .usage = " ", + .help = "enter password to enable 'dangerous' options", + }, + { + .name = "write_custom", + .handler = &lpc2900_handle_write_custom_command, + .mode = COMMAND_EXEC, + .usage = " []", + .help = "write customer info from file to index sector", + }, + { + .name = "secure_sector", + .handler = &lpc2900_handle_secure_sector_command, + .mode = COMMAND_EXEC, + .usage = " ", + .help = "activate sector security for a range of sectors", + }, + { + .name = "secure_jtag", + .handler = &lpc2900_handle_secure_jtag_command, + .mode = COMMAND_EXEC, + .usage = " ", + .help = "activate JTAG security", + }, + COMMAND_REGISTRATION_DONE +}; +static const struct command_registration lpc2900_command_handlers[] = { + { + .name = "lpc2900", + .mode = COMMAND_ANY, + .help = "LPC2900 flash command group", + .chain = lpc2900_exec_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +/// Evaluate flash bank command. +FLASH_BANK_COMMAND_HANDLER(lpc2900_flash_bank_command) +{ + struct lpc2900_flash_bank *lpc2900_info; + + if (CMD_ARGC < 6) + { + LOG_WARNING("incomplete flash_bank LPC2900 configuration"); + return ERROR_FLASH_BANK_INVALID; + } + + lpc2900_info = malloc(sizeof(struct lpc2900_flash_bank)); + bank->driver_priv = lpc2900_info; + + /* Get flash clock. + * Reject it if we can't meet the requirements for program time + * (if clock too slow), or for erase time (clock too fast). + */ + uint32_t clk_sys_fmc; + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[6], clk_sys_fmc); + lpc2900_info->clk_sys_fmc = clk_sys_fmc * 1000; + + uint32_t clock_limit; + /* Check program time limit */ + clock_limit = 512000000l / FLASH_PROGRAM_TIME; + if (lpc2900_info->clk_sys_fmc < clock_limit) + { + LOG_WARNING("flash clock must be at least %" PRIu32 " kHz", + (clock_limit / 1000)); + return ERROR_FLASH_BANK_INVALID; + } + + /* Check erase time limit */ + clock_limit = (uint32_t)((32767.0 * 512.0 * 1e6) / FLASH_ERASE_TIME); + if (lpc2900_info->clk_sys_fmc > clock_limit) + { + LOG_WARNING("flash clock must be a maximum of %" PRIu32" kHz", + (clock_limit / 1000)); + return ERROR_FLASH_BANK_INVALID; + } + + /* Chip ID will be obtained by probing the device later */ + lpc2900_info->chipid = 0; + + return ERROR_OK; +} + + +/** + * Erase sector(s). + * + * @param bank Pointer to the flash bank descriptor + * @param first First sector to be erased + * @param last Last sector (including) to be erased + */ +static int lpc2900_erase(struct flash_bank *bank, int first, int last) +{ + uint32_t status; + int sector; + int last_unsecured_sector; + struct target *target = bank->target; + struct lpc2900_flash_bank *lpc2900_info = bank->driver_priv; + + + status = lpc2900_is_ready(bank); + if (status != ERROR_OK) + { + return status; + } + + /* Sanity check on sector range */ + if ((first < 0) || (last < first) || (last >= bank->num_sectors)) + { + LOG_INFO("Bad sector range"); + return ERROR_FLASH_SECTOR_INVALID; + } + + /* Update the info about secured sectors */ + lpc2900_read_security_status( bank ); + + /* The selected sector range might include secured sectors. An attempt + * to erase such a sector will cause the erase to fail also for unsecured + * sectors. It is necessary to determine the last unsecured sector now, + * because we have to treat the last relevant sector in the list in + * a special way. + */ + last_unsecured_sector = -1; + for (sector = first; sector <= last; sector++) + { + if ( !bank->sectors[sector].is_protected ) + { + last_unsecured_sector = sector; + } + } + + /* Exit now, in case of the rare constellation where all sectors in range + * are secured. This is regarded a success, since erasing/programming of + * secured sectors shall be handled transparently. + */ + if ( last_unsecured_sector == -1 ) + { + return ERROR_OK; + } + + /* Enable flash block and set the correct CRA clock of 66 kHz */ + lpc2900_setup(bank); + + /* Clear END_OF_ERASE interrupt status */ + target_write_u32(target, INT_CLR_STATUS, INTSRC_END_OF_ERASE); + + /* Set the program/erase timer to FLASH_ERASE_TIME */ + target_write_u32(target, FPTR, + FPTR_EN_T | lpc2900_calc_tr( lpc2900_info->clk_sys_fmc, + FLASH_ERASE_TIME )); + + /* Sectors are marked for erasure, then erased all together */ + for (sector = first; sector <= last_unsecured_sector; sector++) + { + /* Only mark sectors that aren't secured. Any attempt to erase a group + * of sectors will fail if any single one of them is secured! + */ + if ( !bank->sectors[sector].is_protected ) + { + /* Unprotect the sector */ + target_write_u32(target, bank->sectors[sector].offset, 0); + target_write_u32(target, FCTR, + FCTR_FS_LOADREQ | FCTR_FS_WPB | + FCTR_FS_WEB | FCTR_FS_WRE | FCTR_FS_CS); + + /* Mark the sector for erasure. The last sector in the list + triggers the erasure. */ + target_write_u32(target, bank->sectors[sector].offset, 0); + if ( sector == last_unsecured_sector ) + { + target_write_u32(target, FCTR, + FCTR_FS_PROGREQ | FCTR_FS_WPB | FCTR_FS_CS); + } + else + { + target_write_u32(target, FCTR, + FCTR_FS_LOADREQ | FCTR_FS_WPB | + FCTR_FS_WEB | FCTR_FS_CS); + } + } + } + + /* Wait for the end of the erase operation. If it's not over after two seconds, + * something went dreadfully wrong... :-( + */ + if( lpc2900_wait_status(bank, INTSRC_END_OF_ERASE, 2000) != ERROR_OK ) + { + return ERROR_FLASH_OPERATION_FAILED; + } + + /* Normal flash operating mode */ + target_write_u32(target, FCTR, FCTR_FS_CS | FCTR_FS_WEB); + + return ERROR_OK; +} + + + +static int lpc2900_protect(struct flash_bank *bank, int set, int first, int last) +{ + /* This command is not supported. + * "Protection" in LPC2900 terms is handled transparently. Sectors will + * automatically be unprotected as needed. + * Instead we use the concept of sector security. A secured sector is shown + * as "protected" in OpenOCD. Sector security is a permanent feature, and + * cannot be disabled once activated. + */ + + return ERROR_OK; +} + + +/** + * Write data to flash. + * + * @param bank Pointer to the flash bank descriptor + * @param buffer Buffer with data + * @param offset Start address (relative to bank start) + * @param count Number of bytes to be programmed + */ +static int lpc2900_write(struct flash_bank *bank, uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + uint8_t page[FLASH_PAGE_SIZE]; + uint32_t status; + uint32_t num_bytes; + struct target *target = bank->target; + struct lpc2900_flash_bank *lpc2900_info = bank->driver_priv; + int sector; + int retval; + + static const uint32_t write_target_code[] = { + /* Set auto latch mode: FCTR=CS|WRE|WEB */ + 0xe3a0a007, /* loop mov r10, #0x007 */ + 0xe583a000, /* str r10,[r3,#0] */ + + /* Load complete page into latches */ + 0xe3a06020, /* mov r6,#(512/16) */ + 0xe8b00f00, /* next ldmia r0!,{r8-r11} */ + 0xe8a10f00, /* stmia r1!,{r8-r11} */ + 0xe2566001, /* subs r6,#1 */ + 0x1afffffb, /* bne next */ + + /* Clear END_OF_BURN interrupt status */ + 0xe3a0a002, /* mov r10,#(1 << 1) */ + 0xe583afe8, /* str r10,[r3,#0xfe8] */ + + /* Set the erase time to FLASH_PROGRAM_TIME */ + 0xe5834008, /* str r4,[r3,#8] */ + + /* Trigger flash write + FCTR = CS | WRE | WPB | PROGREQ */ + 0xe3a0a083, /* mov r10,#0x83 */ + 0xe38aaa01, /* orr r10,#0x1000 */ + 0xe583a000, /* str r10,[r3,#0] */ + + /* Wait for end of burn */ + 0xe593afe0, /* wait ldr r10,[r3,#0xfe0] */ + 0xe21aa002, /* ands r10,#(1 << 1) */ + 0x0afffffc, /* beq wait */ + + /* End? */ + 0xe2522001, /* subs r2,#1 */ + 0x1affffed, /* bne loop */ + + 0xeafffffe /* done b done */ + }; + + + status = lpc2900_is_ready(bank); + if (status != ERROR_OK) + { + return status; + } + + /* Enable flash block and set the correct CRA clock of 66 kHz */ + lpc2900_setup(bank); + + /* Update the info about secured sectors */ + lpc2900_read_security_status( bank ); + + /* Unprotect all involved sectors */ + for (sector = 0; sector < bank->num_sectors; sector++) + { + /* Start address in or before this sector? */ + /* End address in or behind this sector? */ + if ( ((bank->base + offset) < + (bank->sectors[sector].offset + bank->sectors[sector].size)) && + ((bank->base + (offset + count - 1)) >= bank->sectors[sector].offset) ) + { + /* This sector is involved and needs to be unprotected. + * Don't do it for secured sectors. + */ + if ( !bank->sectors[sector].is_protected ) + { + target_write_u32(target, bank->sectors[sector].offset, 0); + target_write_u32(target, FCTR, + FCTR_FS_LOADREQ | FCTR_FS_WPB | + FCTR_FS_WEB | FCTR_FS_WRE | FCTR_FS_CS); + } + } + } + + /* Set the program/erase time to FLASH_PROGRAM_TIME */ + uint32_t prog_time = FPTR_EN_T | lpc2900_calc_tr( lpc2900_info->clk_sys_fmc, + FLASH_PROGRAM_TIME ); + + /* If there is a working area of reasonable size, use it to program via + a target algorithm. If not, fall back to host programming. */ + + /* We need some room for target code. */ + uint32_t target_code_size = sizeof(write_target_code); + + /* Try working area allocation. Start with a large buffer, and try with + reduced size if that fails. */ + struct working_area *warea; + uint32_t buffer_size = lpc2900_info->max_ram_block - 1 * KiB; + while( (retval = target_alloc_working_area(target, + buffer_size + target_code_size, + &warea)) != ERROR_OK ) + { + /* Try a smaller buffer now, and stop if it's too small. */ + buffer_size -= 1 * KiB; + if (buffer_size < 2 * KiB) + { + LOG_INFO( "no (large enough) working area" + ", falling back to host mode" ); + warea = NULL; + break; + } + }; + + if( warea ) + { + struct reg_param reg_params[5]; + struct armv4_5_algorithm armv4_5_info; + + /* We can use target mode. Download the algorithm. */ + retval = target_write_buffer( target, + (warea->address)+buffer_size, + target_code_size, + (uint8_t *)write_target_code); + if (retval != ERROR_OK) + { + LOG_ERROR("Unable to write block write code to target"); + target_free_all_working_areas(target); + return ERROR_FLASH_OPERATION_FAILED; + } + + 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); + + /* Write to flash in large blocks */ + while ( count != 0 ) + { + uint32_t this_npages; + uint8_t *this_buffer; + int start_sector = lpc2900_address2sector( bank, offset ); + + /* First page / last page / rest */ + if( offset % FLASH_PAGE_SIZE ) + { + /* Block doesn't start on page boundary. + Burn first partial page separately. */ + memset( &page, 0xff, sizeof(page) ); + memcpy( &page[offset % FLASH_PAGE_SIZE], + buffer, + FLASH_PAGE_SIZE - (offset % FLASH_PAGE_SIZE) ); + this_npages = 1; + this_buffer = &page[0]; + count = count + (offset % FLASH_PAGE_SIZE); + offset = offset - (offset % FLASH_PAGE_SIZE); + } + else if( count < FLASH_PAGE_SIZE ) + { + /* Download last incomplete page separately. */ + memset( &page, 0xff, sizeof(page) ); + memcpy( &page, buffer, count ); + this_npages = 1; + this_buffer = &page[0]; + count = FLASH_PAGE_SIZE; + } + else + { + /* Download as many full pages as possible */ + this_npages = (count < buffer_size) ? + count / FLASH_PAGE_SIZE : + buffer_size / FLASH_PAGE_SIZE; + this_buffer = buffer; + + /* Make sure we stop at the next secured sector */ + int sector = start_sector + 1; + while( sector < bank->num_sectors ) + { + /* Secured? */ + if( bank->sectors[sector].is_protected ) + { + /* Is that next sector within the current block? */ + if( (bank->sectors[sector].offset - bank->base) < + (offset + (this_npages * FLASH_PAGE_SIZE)) ) + { + /* Yes! Split the block */ + this_npages = + (bank->sectors[sector].offset - bank->base - offset) + / FLASH_PAGE_SIZE; + break; + } + } + + sector++; + } + } + + /* Skip the current sector if it is secured */ + if (bank->sectors[start_sector].is_protected) + { + LOG_DEBUG("Skip secured sector %d", + start_sector); + + /* Stop if this is the last sector */ + if (start_sector == bank->num_sectors - 1) + { + break; + } + + /* Skip */ + uint32_t nskip = bank->sectors[start_sector].size - + (offset % bank->sectors[start_sector].size); + offset += nskip; + buffer += nskip; + count = (count >= nskip) ? (count - nskip) : 0; + continue; + } + + /* Execute buffer download */ + if ((retval = target_write_buffer(target, + warea->address, + this_npages * FLASH_PAGE_SIZE, + this_buffer)) != ERROR_OK) + { + LOG_ERROR("Unable to write data to target"); + target_free_all_working_areas(target); + return ERROR_FLASH_OPERATION_FAILED; + } + + /* Prepare registers */ + buf_set_u32(reg_params[0].value, 0, 32, warea->address); + buf_set_u32(reg_params[1].value, 0, 32, offset); + buf_set_u32(reg_params[2].value, 0, 32, this_npages); + buf_set_u32(reg_params[3].value, 0, 32, FCTR); + buf_set_u32(reg_params[4].value, 0, 32, FPTR_EN_T | prog_time); + + /* Execute algorithm, assume breakpoint for last instruction */ + 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; + + retval = target_run_algorithm(target, 0, NULL, 5, reg_params, + (warea->address) + buffer_size, + (warea->address) + buffer_size + target_code_size - 4, + 10000, /* 10s should be enough for max. 16 KiB of data */ + &armv4_5_info); + + if (retval != ERROR_OK) + { + LOG_ERROR("Execution of flash algorithm failed."); + target_free_all_working_areas(target); + retval = ERROR_FLASH_OPERATION_FAILED; + break; + } + + count -= this_npages * FLASH_PAGE_SIZE; + buffer += this_npages * FLASH_PAGE_SIZE; + offset += this_npages * FLASH_PAGE_SIZE; + } + + /* Free all resources */ + 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]); + target_free_all_working_areas(target); + } + else + { + /* Write to flash memory page-wise */ + while ( count != 0 ) + { + /* How many bytes do we copy this time? */ + num_bytes = (count >= FLASH_PAGE_SIZE) ? + FLASH_PAGE_SIZE - (offset % FLASH_PAGE_SIZE) : + count; + + /* Don't do anything with it if the page is in a secured sector. */ + if ( !bank->sectors[lpc2900_address2sector(bank, offset)].is_protected ) + { + /* Set latch load mode */ + target_write_u32(target, FCTR, + FCTR_FS_CS | FCTR_FS_WRE | FCTR_FS_WEB); + + /* Always clear the buffer (a little overhead, but who cares) */ + memset(page, 0xFF, FLASH_PAGE_SIZE); + + /* Copy them to the buffer */ + memcpy( &page[offset % FLASH_PAGE_SIZE], + &buffer[offset % FLASH_PAGE_SIZE], + num_bytes ); + + /* Write whole page to flash data latches */ + if (target_write_memory( + target, + bank->base + (offset - (offset % FLASH_PAGE_SIZE)), + 4, FLASH_PAGE_SIZE / 4, page) != ERROR_OK) + { + LOG_ERROR("Write failed @ 0x%8.8" PRIx32, offset); + target_write_u32(target, FCTR, FCTR_FS_CS | FCTR_FS_WEB); + + return ERROR_FLASH_OPERATION_FAILED; + } + + /* Clear END_OF_BURN interrupt status */ + target_write_u32(target, INT_CLR_STATUS, INTSRC_END_OF_BURN); + + /* Set the programming time */ + target_write_u32(target, FPTR, FPTR_EN_T | prog_time); + + /* Trigger flash write */ + target_write_u32(target, FCTR, + FCTR_FS_CS | FCTR_FS_WRE | FCTR_FS_WPB | FCTR_FS_PROGREQ); + + /* Wait for the end of the write operation. If it's not over + * after one second, something went dreadfully wrong... :-( + */ + if (lpc2900_wait_status(bank, INTSRC_END_OF_BURN, 1000) != ERROR_OK) + { + LOG_ERROR("Write failed @ 0x%8.8" PRIx32, offset); + target_write_u32(target, FCTR, FCTR_FS_CS | FCTR_FS_WEB); + + return ERROR_FLASH_OPERATION_FAILED; + } + } + + /* Update pointers and counters */ + offset += num_bytes; + buffer += num_bytes; + count -= num_bytes; + } + + retval = ERROR_OK; + } + + /* Normal flash operating mode */ + target_write_u32(target, FCTR, FCTR_FS_CS | FCTR_FS_WEB); + + return retval; +} + + +/** + * Try and identify the device. + * + * Determine type number and its memory layout. + * + * @param bank Pointer to the flash bank descriptor + */ +static int lpc2900_probe(struct flash_bank *bank) +{ + struct lpc2900_flash_bank *lpc2900_info = bank->driver_priv; + struct target *target = bank->target; + int i = 0; + uint32_t offset; + + + if (target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* We want to do this only once. Check if we already have a valid CHIPID, + * because then we will have already successfully probed the device. + */ + if (lpc2900_info->chipid == EXPECTED_CHIPID) + { + return ERROR_OK; + } + + /* Probing starts with reading the CHIPID register. We will continue only + * if this identifies as an LPC2900 device. + */ + target_read_u32(target, CHIPID, &lpc2900_info->chipid); + + if (lpc2900_info->chipid != EXPECTED_CHIPID) + { + LOG_WARNING("Device is not an LPC29xx"); + return ERROR_FLASH_OPERATION_FAILED; + } + + /* It's an LPC29xx device. Now read the feature register FEAT0...FEAT3. */ + uint32_t feat0, feat1, feat2, feat3; + target_read_u32(target, FEAT0, &feat0); + target_read_u32(target, FEAT1, &feat1); + target_read_u32(target, FEAT2, &feat2); + target_read_u32(target, FEAT3, &feat3); + + /* Base address */ + bank->base = 0x20000000; + + /* Determine flash layout from FEAT2 register */ + uint32_t num_64k_sectors = (feat2 >> 16) & 0xFF; + uint32_t num_8k_sectors = (feat2 >> 0) & 0xFF; + bank->num_sectors = num_64k_sectors + num_8k_sectors; + bank->size = KiB * (64 * num_64k_sectors + 8 * num_8k_sectors); + + /* Determine maximum contiguous RAM block */ + lpc2900_info->max_ram_block = 16 * KiB; + if( (feat1 & 0x30) == 0x30 ) + { + lpc2900_info->max_ram_block = 32 * KiB; + if( (feat1 & 0x0C) == 0x0C ) + { + lpc2900_info->max_ram_block = 48 * KiB; + } + } + + /* Determine package code and ITCM size */ + uint32_t package_code = feat0 & 0x0F; + uint32_t itcm_code = (feat1 >> 16) & 0x1F; + + /* Determine the exact type number. */ + uint32_t found = 1; + if ( (package_code == 4) && (itcm_code == 5) ) + { + /* Old LPC2917 or LPC2919 (non-/01 devices) */ + lpc2900_info->target_name = (bank->size == 768*KiB) ? "LPC2919" : "LPC2917"; + } + else + { + if ( package_code == 2 ) + { + /* 100-pin package */ + if ( bank->size == 128*KiB ) + { + lpc2900_info->target_name = "LPC2921"; + } + else if ( bank->size == 256*KiB ) + { + lpc2900_info->target_name = "LPC2923"; + } + else if ( bank->size == 512*KiB ) + { + lpc2900_info->target_name = "LPC2925"; + } + else + { + found = 0; + } + } + else if ( package_code == 4 ) + { + /* 144-pin package */ + if ( (bank->size == 512*KiB) && (feat3 == 0xFFFFFCF0) ) + { + lpc2900_info->target_name = "LPC2917/01"; + } + else if ( (bank->size == 512*KiB) && (feat3 == 0xFFFFFFF1) ) + { + lpc2900_info->target_name = "LPC2927"; + } + else if ( (bank->size == 768*KiB) && (feat3 == 0xFFFFFCF8) ) + { + lpc2900_info->target_name = "LPC2919/01"; + } + else if ( (bank->size == 768*KiB) && (feat3 == 0xFFFFFFF9) ) + { + lpc2900_info->target_name = "LPC2929"; + } + else + { + found = 0; + } + } + else if ( package_code == 5 ) + { + /* 208-pin package */ + lpc2900_info->target_name = (bank->size == 0) ? "LPC2930" : "LPC2939"; + } + else + { + found = 0; + } + } + + if ( !found ) + { + LOG_WARNING("Unknown LPC29xx derivative"); + return ERROR_FLASH_OPERATION_FAILED; + } + + /* Show detected device */ + LOG_INFO("Flash bank %d" + ": Device %s, %" PRIu32 + " KiB in %d sectors", + bank->bank_number, + lpc2900_info->target_name, bank->size / KiB, + bank->num_sectors); + + /* Flashless devices cannot be handled */ + if ( bank->num_sectors == 0 ) + { + LOG_WARNING("Flashless device cannot be handled"); + return ERROR_FLASH_OPERATION_FAILED; + } + + /* Sector layout. + * These are logical sector numbers. When doing real flash operations, + * the logical flash number are translated into the physical flash numbers + * of the device. + */ + bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors); + + offset = 0; + for (i = 0; i < bank->num_sectors; i++) + { + bank->sectors[i].offset = offset; + bank->sectors[i].is_erased = -1; + bank->sectors[i].is_protected = -1; + + if ( i <= 7 ) + { + bank->sectors[i].size = 8 * KiB; + } + else if ( i <= 18 ) + { + bank->sectors[i].size = 64 * KiB; + } + else + { + /* We shouldn't come here. But there might be a new part out there + * that has more than 19 sectors. Politely ask for a fix then. + */ + bank->sectors[i].size = 0; + LOG_ERROR("Never heard about sector %d", i); + } + + offset += bank->sectors[i].size; + } + + /* Read sector security status */ + if ( lpc2900_read_security_status(bank) != ERROR_OK ) + { + LOG_ERROR("Cannot determine sector security status"); + return ERROR_FLASH_OPERATION_FAILED; + } + + return ERROR_OK; +} + + +/** + * Run a blank check for each sector. + * + * For speed reasons, the device isn't read word by word. + * A hash value is calculated by the hardware ("BIST") for each sector. + * This value is then compared against the known hash of an empty sector. + * + * @param bank Pointer to the flash bank descriptor + */ +static int lpc2900_erase_check(struct flash_bank *bank) +{ + uint32_t status = lpc2900_is_ready(bank); + if (status != ERROR_OK) + { + LOG_INFO("Processor not halted/not probed"); + return status; + } + + /* Use the BIST (Built-In Selft Test) to generate a signature of each flash + * sector. Compare against the expected signature of an empty sector. + */ + int sector; + for ( sector = 0; sector < bank->num_sectors; sector++ ) + { + uint32_t signature[4]; + if ( (status = lpc2900_run_bist128( bank, + bank->sectors[sector].offset, + bank->sectors[sector].offset + + (bank->sectors[sector].size - 1), + &signature)) != ERROR_OK ) + { + return status; + } + + /* The expected signatures for an empty sector are different + * for 8 KiB and 64 KiB sectors. + */ + if ( bank->sectors[sector].size == 8*KiB ) + { + bank->sectors[sector].is_erased = + (signature[3] == 0x01ABAAAA) && + (signature[2] == 0xAAAAAAAA) && + (signature[1] == 0xAAAAAAAA) && + (signature[0] == 0xAAA00AAA); + } + if ( bank->sectors[sector].size == 64*KiB ) + { + bank->sectors[sector].is_erased = + (signature[3] == 0x11801222) && + (signature[2] == 0xB88844FF) && + (signature[1] == 0x11A22008) && + (signature[0] == 0x2B1BFE44); + } + } + + return ERROR_OK; +} + + +/** + * Get protection (sector security) status. + * + * Determine the status of "sector security" for each sector. + * A secured sector is one that can never be erased/programmed again. + * + * @param bank Pointer to the flash bank descriptor + */ +static int lpc2900_protect_check(struct flash_bank *bank) +{ + return lpc2900_read_security_status(bank); +} + + +/** + * Print info about the driver (not the device). + * + * @param bank Pointer to the flash bank descriptor + * @param buf Buffer to take the string + * @param buf_size Maximum number of characters that the buffer can take + */ +static int lpc2900_info(struct flash_bank *bank, char *buf, int buf_size) +{ + snprintf(buf, buf_size, "lpc2900 flash driver"); + + return ERROR_OK; +} + + +struct flash_driver lpc2900_flash = +{ + .name = "lpc2900", + .commands = lpc2900_command_handlers, + .flash_bank_command = lpc2900_flash_bank_command, + .erase = lpc2900_erase, + .protect = lpc2900_protect, + .write = lpc2900_write, + .probe = lpc2900_probe, + .auto_probe = lpc2900_probe, + .erase_check = lpc2900_erase_check, + .protect_check = lpc2900_protect_check, + .info = lpc2900_info +}; diff --git a/src/flash/nor/non_cfi.c b/src/flash/nor/non_cfi.c new file mode 100644 index 00000000..f98b1080 --- /dev/null +++ b/src/flash/nor/non_cfi.c @@ -0,0 +1,491 @@ +/*************************************************************************** + * Copyright (C) 2007 by Dominic Rath * + * Dominic.Rath@gmx.de * + * Copyright (C) 2009 Michael Schwingen * + * michael@schwingen.org * + * * + * 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 "non_cfi.h" +#include "cfi.h" + + +#define KB 1024 +#define MB (1024*1024) +#define ERASE_REGION(num, size) (((size/256) << 16) | (num-1)) + +/* non-CFI compatible flashes */ +static struct non_cfi non_cfi_flashes[] = { + { + .mfr = CFI_MFR_SST, + .id = 0xd4, + .pri_id = 0x02, + .dev_size = 64*KB, + .interface_desc = 0x0, /* x8 only device */ + .max_buf_write_size = 0x0, + .status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7, + .num_erase_regions = 1, + .erase_region_info = + { + ERASE_REGION(16, 4*KB) + } + }, + { + .mfr = CFI_MFR_SST, + .id = 0xd5, + .pri_id = 0x02, + .dev_size = 128*KB, + .interface_desc = 0x0, /* x8 only device */ + .max_buf_write_size = 0x0, + .status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7, + .num_erase_regions = 1, + .erase_region_info = + { + ERASE_REGION(32, 4*KB) + } + }, + { + .mfr = CFI_MFR_SST, + .id = 0xd6, + .pri_id = 0x02, + .dev_size = 256*KB, + .interface_desc = 0x0, /* x8 only device */ + .max_buf_write_size = 0x0, + .status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7, + .num_erase_regions = 1, + .erase_region_info = + { + ERASE_REGION(64, 4*KB) + } + }, + { + .mfr = CFI_MFR_SST, + .id = 0xd7, + .pri_id = 0x02, + .dev_size = 512*KB, + .interface_desc = 0x0, /* x8 only device */ + .max_buf_write_size = 0x0, + .status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7, + .num_erase_regions = 1, + .erase_region_info = + { + ERASE_REGION(128, 4*KB) + } + }, + { + .mfr = CFI_MFR_SST, + .id = 0x2780, + .pri_id = 0x02, + .dev_size = 512*KB, + .interface_desc = 0x2, /* x8 or x16 device */ + .max_buf_write_size = 0x0, + .status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7, + .num_erase_regions = 1, + .erase_region_info = + { + ERASE_REGION(128, 4*KB) + } + }, + { + .mfr = CFI_MFR_ST, + .id = 0xd6, /* ST29F400BB */ + .pri_id = 0x02, + .dev_size = 512*KB, + .interface_desc = 0x2, /* x8 or x16 device with nBYTE */ + .max_buf_write_size = 0x0, + .status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7, + .num_erase_regions = 4, + .erase_region_info = + { + ERASE_REGION(1, 16*KB), + ERASE_REGION(2, 8*KB), + ERASE_REGION(1, 32*KB), + ERASE_REGION(7, 64*KB) + } + }, + { + .mfr = CFI_MFR_ST, + .id = 0xd5, /* ST29F400BT */ + .pri_id = 0x02, + .dev_size = 512*KB, + .interface_desc = 0x2, /* x8 or x16 device with nBYTE */ + .max_buf_write_size = 0x0, + .status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7, + .num_erase_regions = 4, + .erase_region_info = + { + ERASE_REGION(7, 64*KB), + ERASE_REGION(1, 32*KB), + ERASE_REGION(2, 8*KB), + ERASE_REGION(1, 16*KB) + } + }, + + /* SST 39VF* do not support DQ5 status polling - this currently is + only supported by the host algorithm, not by the target code using + the work area. + Only true for 8-bit and 32-bit wide memories. 16-bit wide memories + without DQ5 status polling are supported by the target code. + */ + { + .mfr = CFI_MFR_SST, + .id = 0x2782, /* SST39xF160 */ + .pri_id = 0x02, + .dev_size = 2*MB, + .interface_desc = 0x2, /* x8 or x16 device with nBYTE */ + .max_buf_write_size = 0x0, + .status_poll_mask = CFI_STATUS_POLL_MASK_DQ6_DQ7, + .num_erase_regions = 1, + .erase_region_info = + { + ERASE_REGION(512, 4*KB) + } + }, + { + .mfr = CFI_MFR_SST, + .id = 0x2783, /* SST39VF320 */ + .pri_id = 0x02, + .dev_size = 4*MB, + .interface_desc = 0x2, /* x8 or x16 device with nBYTE */ + .max_buf_write_size = 0x0, + .status_poll_mask = CFI_STATUS_POLL_MASK_DQ6_DQ7, + .num_erase_regions = 1, + .erase_region_info = + { + ERASE_REGION(1024, 4*KB) + } + }, + { + .mfr = CFI_MFR_SST, + .id = 0x234b, /* SST39VF1601 */ + .pri_id = 0x02, + .dev_size = 2*MB, + .interface_desc = 0x2, /* x8 or x16 device with nBYTE */ + .max_buf_write_size = 0x0, + .status_poll_mask = CFI_STATUS_POLL_MASK_DQ6_DQ7, + .num_erase_regions = 1, + .erase_region_info = + { + ERASE_REGION(512, 4*KB) + } + }, + { + .mfr = CFI_MFR_SST, + .id = 0x234a, /* SST39VF1602 */ + .pri_id = 0x02, + .dev_size = 2*MB, + .interface_desc = 0x2, /* x8 or x16 device with nBYTE */ + .max_buf_write_size = 0x0, + .status_poll_mask = CFI_STATUS_POLL_MASK_DQ6_DQ7, + .num_erase_regions = 1, + .erase_region_info = + { + ERASE_REGION(512, 4*KB) + } + }, + { + .mfr = CFI_MFR_SST, + .id = 0x235b, /* SST39VF3201 */ + .pri_id = 0x02, + .dev_size = 4*MB, + .interface_desc = 0x2, /* x8 or x16 device with nBYTE */ + .max_buf_write_size = 0x0, + .status_poll_mask = CFI_STATUS_POLL_MASK_DQ6_DQ7, + .num_erase_regions = 1, + .erase_region_info = + { + ERASE_REGION(1024, 4*KB) + } + }, + { + .mfr = CFI_MFR_SST, + .id = 0x235a, /* SST39VF3202 */ + .pri_id = 0x02, + .dev_size = 4*MB, + .interface_desc = 0x2, /* x8 or x16 device with nBYTE */ + .max_buf_write_size = 0x0, + .status_poll_mask = CFI_STATUS_POLL_MASK_DQ6_DQ7, + .num_erase_regions = 1, + .erase_region_info = + { + ERASE_REGION(1024, 4*KB) + } + }, + { + .mfr = CFI_MFR_AMD, + .id = 0x22ab, /* AM29F400BB */ + .pri_id = 0x02, + .dev_size = 512*KB, + .interface_desc = 0x2, /* x8 or x16 device with nBYTE */ + .max_buf_write_size = 0x0, + .status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7, + .num_erase_regions = 4, + .erase_region_info = + { + ERASE_REGION(1, 16*KB), + ERASE_REGION(2, 8*KB), + ERASE_REGION(1, 32*KB), + ERASE_REGION(7, 64*KB) + } + }, + { + .mfr = CFI_MFR_AMD, + .id = 0x2223, /* AM29F400BT */ + .pri_id = 0x02, + .dev_size = 512*KB, + .interface_desc = 0x2, /* x8 or x16 device with nBYTE */ + .max_buf_write_size = 0x0, + .status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7, + .num_erase_regions = 4, + .erase_region_info = + { + ERASE_REGION(7, 64*KB), + ERASE_REGION(1, 32*KB), + ERASE_REGION(2, 8*KB), + ERASE_REGION(1, 16*KB) + } + }, + { + .mfr = CFI_MFR_FUJITSU, + .id = 0x226b, /* AM29SL800DB */ + .pri_id = 0x02, + .dev_size = 1*MB, + .interface_desc = 0x2, /* x8 or x16 device with nBYTE */ + .max_buf_write_size = 0x0, + .status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7, + .num_erase_regions = 4, + .erase_region_info = + { + ERASE_REGION(1, 16*KB), + ERASE_REGION(2, 8*KB), + ERASE_REGION(1, 32*KB), + ERASE_REGION(15, 64*KB) + } + }, + { + .mfr = CFI_MFR_AMIC, + .id = 0xb31a, /* A29L800A */ + .pri_id = 0x02, + .dev_size = 1*MB, + .interface_desc = 0x2, + .max_buf_write_size = 0x0, + .status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7, + .num_erase_regions = 4, + .erase_region_info = + { + ERASE_REGION(1, 16*KB), + ERASE_REGION(2, 8*KB), + ERASE_REGION(1, 32*KB), + ERASE_REGION(15, 64*KB) + } + }, + { + .mfr = CFI_MFR_MX, + .id = 0x225b, /* MX29LV800B */ + .pri_id = 0x02, + .dev_size = 1*MB, + .interface_desc = 0x2, /* x8 or x16 device with nBYTE */ + .max_buf_write_size = 0x0, + .status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7, + .num_erase_regions = 4, + .erase_region_info = + { + ERASE_REGION(1, 16*KB), + ERASE_REGION(2, 8*KB), + ERASE_REGION(1, 32*KB), + ERASE_REGION(15, 64*KB) + } + }, + + { + .mfr = CFI_MFR_MX, + .id = 0x2249, /* MX29LV160AB: 2MB */ + .pri_id = 0x02, + .dev_size = 2*MB, + .interface_desc = 0x2, /* x8 or x16 device with nBYTE */ + .max_buf_write_size = 0x0, + .status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7, + .num_erase_regions = 4, + .erase_region_info = + { + ERASE_REGION(1, 16*KB), + ERASE_REGION(2, 8*KB), + ERASE_REGION(1, 32*KB), + ERASE_REGION(31, 64*KB) + } + }, + { + .mfr = CFI_MFR_MX, + .id = 0x22C4, /* MX29LV160AT: 2MB */ + .pri_id = 0x02, + .dev_size = 2*MB, + .interface_desc = 0x2, /* x8 or x16 device with nBYTE */ + .max_buf_write_size = 0x0, + .status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7, + .num_erase_regions = 4, + .erase_region_info = + { + ERASE_REGION(31, 64*KB), + ERASE_REGION(1, 32*KB), + ERASE_REGION(2, 8*KB), + ERASE_REGION(1, 16*KB) + } + }, + { + .mfr = CFI_MFR_ATMEL, + .id = 0x00c0, /* Atmel 49BV1614 */ + .pri_id = 0x02, + .dev_size = 2*MB, + .interface_desc = 0x2, /* x8 or x16 device with nBYTE */ + .max_buf_write_size = 0x0, + .status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7, + .num_erase_regions = 3, + .erase_region_info = + { + ERASE_REGION(8, 8*KB), + ERASE_REGION(2, 32*KB), + ERASE_REGION(30, 64*KB) + } + }, + { + .mfr = CFI_MFR_ATMEL, + .id = 0xC2, /* Atmel 49BV1614T */ + .pri_id = 0x02, + .dev_size = 2*MB, + .interface_desc = 0x2, /* x8 or x16 device with nBYTE */ + .max_buf_write_size = 0x0, + .status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7, + .num_erase_regions = 3, + .erase_region_info = + { + ERASE_REGION(30, 64*KB), + ERASE_REGION(2, 32*KB), + ERASE_REGION(8, 8*KB) + } + }, + { + .mfr = CFI_MFR_AMD, + .id = 0x225b, /* S29AL008D */ + .pri_id = 0x02, + .dev_size = 1*MB, + .interface_desc = 0x2, /* x8 or x16 device with nBYTE */ + .max_buf_write_size = 0x0, + .status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7, + .num_erase_regions = 4, + .erase_region_info = + { + ERASE_REGION(1, 16*KB), + ERASE_REGION(2, 8*KB), + ERASE_REGION(1, 32*KB), + ERASE_REGION(15, 64*KB) + } + }, + { + .mfr = 0, + .id = 0, + } +}; + +void cfi_fixup_non_cfi(struct flash_bank *bank) +{ + struct cfi_flash_bank *cfi_info = bank->driver_priv; + struct non_cfi *non_cfi = non_cfi_flashes; + + for (non_cfi = non_cfi_flashes; non_cfi->mfr; non_cfi++) + { + if ((cfi_info->manufacturer == non_cfi->mfr) + && (cfi_info->device_id == non_cfi->id)) + { + break; + } + } + + /* only fixup jedec flashs found in table */ + if (!non_cfi->mfr) + return; + + cfi_info->not_cfi = 1; + + /* fill in defaults for non-critical data */ + cfi_info->vcc_min = 0x0; + cfi_info->vcc_max = 0x0; + cfi_info->vpp_min = 0x0; + cfi_info->vpp_max = 0x0; + cfi_info->word_write_timeout_typ = 0x0; + cfi_info->buf_write_timeout_typ = 0x0; + cfi_info->block_erase_timeout_typ = 0x0; + cfi_info->chip_erase_timeout_typ = 0x0; + cfi_info->word_write_timeout_max = 0x0; + cfi_info->buf_write_timeout_max = 0x0; + cfi_info->block_erase_timeout_max = 0x0; + cfi_info->chip_erase_timeout_max = 0x0; + + cfi_info->qry[0] = 'Q'; + cfi_info->qry[1] = 'R'; + cfi_info->qry[2] = 'Y'; + + cfi_info->pri_id = non_cfi->pri_id; + cfi_info->pri_addr = 0x0; + cfi_info->alt_id = 0x0; + cfi_info->alt_addr = 0x0; + cfi_info->alt_ext = NULL; + + cfi_info->interface_desc = non_cfi->interface_desc; + cfi_info->max_buf_write_size = non_cfi->max_buf_write_size; + cfi_info->status_poll_mask = non_cfi->status_poll_mask; + cfi_info->num_erase_regions = non_cfi->num_erase_regions; + cfi_info->erase_region_info = non_cfi->erase_region_info; + cfi_info->dev_size = non_cfi->dev_size; + + if (cfi_info->pri_id == 0x2) + { + struct cfi_spansion_pri_ext *pri_ext = malloc(sizeof(struct cfi_spansion_pri_ext)); + + pri_ext->pri[0] = 'P'; + pri_ext->pri[1] = 'R'; + pri_ext->pri[2] = 'I'; + + pri_ext->major_version = '1'; + pri_ext->minor_version = '0'; + + pri_ext->SiliconRevision = 0x0; + pri_ext->EraseSuspend = 0x0; + pri_ext->EraseSuspend = 0x0; + pri_ext->BlkProt = 0x0; + pri_ext->TmpBlkUnprotect = 0x0; + pri_ext->BlkProtUnprot = 0x0; + pri_ext->SimultaneousOps = 0x0; + pri_ext->BurstMode = 0x0; + pri_ext->PageMode = 0x0; + pri_ext->VppMin = 0x0; + pri_ext->VppMax = 0x0; + pri_ext->TopBottom = 0x0; + + pri_ext->_unlock1 = 0x5555; + pri_ext->_unlock2 = 0x2AAA; + pri_ext->_reversed_geometry = 0; + + cfi_info->pri_ext = pri_ext; + } else if ((cfi_info->pri_id == 0x1) || (cfi_info->pri_id == 0x3)) + { + LOG_ERROR("BUG: non-CFI flashes using the Intel commandset are not yet supported"); + exit(-1); + } +} diff --git a/src/flash/nor/non_cfi.h b/src/flash/nor/non_cfi.h new file mode 100644 index 00000000..44c92db4 --- /dev/null +++ b/src/flash/nor/non_cfi.h @@ -0,0 +1,40 @@ +/*************************************************************************** + * 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. * + ***************************************************************************/ +#ifndef NON_CFI_H +#define NON_CFI_H + +#include "flash.h" + +struct non_cfi +{ + uint16_t mfr; + uint16_t id; + uint16_t pri_id; + uint32_t dev_size; + uint16_t interface_desc; + uint16_t max_buf_write_size; + uint8_t num_erase_regions; + uint32_t erase_region_info[6]; + uint8_t status_poll_mask; +}; + +void cfi_fixup_non_cfi(struct flash_bank *bank); + +#endif /* NON_CFI_H */ diff --git a/src/flash/nor/ocl.c b/src/flash/nor/ocl.c new file mode 100644 index 00000000..388395f0 --- /dev/null +++ b/src/flash/nor/ocl.c @@ -0,0 +1,361 @@ +/*************************************************************************** + * Copyright (C) 2007 by Pavel Chromy * + * chromy@asix.cz * + * * + * 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 "ocl.h" +#include "flash.h" +#include "embeddedice.h" + + +struct ocl_priv +{ + struct arm_jtag *jtag_info; + unsigned int buflen; + unsigned int bufalign; +}; + +static int ocl_erase_check(struct flash_bank *bank) +{ + return ERROR_OK; +} + +static int ocl_protect_check(struct flash_bank *bank) +{ + return ERROR_OK; +} + +/* flash_bank ocl 0 0 0 0 */ +FLASH_BANK_COMMAND_HANDLER(ocl_flash_bank_command) +{ + struct arm7_9_common *arm7_9; + struct ocl_priv *ocl; + + if (CMD_ARGC < 6) + { + LOG_WARNING("incomplete flash_bank ocl configuration"); + return ERROR_FLASH_BANK_INVALID; + } + + arm7_9 = target_to_arm7_9(bank->target); + if (!is_arm7_9(arm7_9)) + return ERROR_TARGET_INVALID; + + ocl = bank->driver_priv = malloc(sizeof(struct ocl_priv)); + ocl->jtag_info = &arm7_9->jtag_info; + ocl->buflen = 0; + ocl->bufalign = 1; + + return ERROR_OK; +} + +static int ocl_erase(struct flash_bank *bank, int first, int last) +{ + struct ocl_priv *ocl = bank->driver_priv; + int retval; + uint32_t dcc_buffer[3]; + + /* check preconditions */ + if (bank->num_sectors == 0) + return ERROR_FLASH_BANK_NOT_PROBED; + + if (bank->target->state != TARGET_RUNNING) + { + LOG_ERROR("target has to be running to communicate with the loader"); + return ERROR_TARGET_NOT_RUNNING; + } + + if ((first == 0) && (last == bank->num_sectors - 1)) + { + dcc_buffer[0] = OCL_ERASE_ALL; + if ((retval = embeddedice_send(ocl->jtag_info, dcc_buffer, 1) != ERROR_OK)) + return retval; + } + else + { + dcc_buffer[0] = OCL_ERASE_BLOCK; + dcc_buffer[1] = first; + dcc_buffer[2] = last; + if ((retval = embeddedice_send(ocl->jtag_info, dcc_buffer, 3) != ERROR_OK)) + return retval; + } + + /* wait for response, fixed timeout of 1 s */ + if ((retval = embeddedice_handshake(ocl->jtag_info, EICE_COMM_CTRL_WBIT, 1000) != ERROR_OK)) + { + if (retval == ERROR_TARGET_TIMEOUT) + LOG_ERROR("loader not responding"); + return retval; + } + + /* receive response */ + if ((retval = embeddedice_receive(ocl->jtag_info, dcc_buffer + 1, 1) != ERROR_OK)) + return retval; + + if (dcc_buffer[1] != OCL_CMD_DONE) + { + if (dcc_buffer[0] == OCL_ERASE_ALL) + LOG_ERROR("loader response to OCL_ERASE_ALL 0x%08" PRIx32 "", dcc_buffer[1]); + else + LOG_ERROR("loader response to OCL_ERASE_BLOCK 0x%08" PRIx32 "", dcc_buffer[1]); + return ERROR_FLASH_OPERATION_FAILED; + } + + return ERROR_OK; +} + +static int ocl_protect(struct flash_bank *bank, int set, int first, int last) +{ + return ERROR_OK; +} + +static int ocl_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) +{ + struct ocl_priv *ocl = bank->driver_priv; + int retval; + uint32_t *dcc_buffer; + uint32_t *dcc_bufptr; + int byteofs; + int runlen; + uint32_t chksum; + + int i; + + /* check preconditions */ + if (ocl->buflen == 0 || ocl->bufalign == 0) + return ERROR_FLASH_BANK_NOT_PROBED; + + if (bank->target->state != TARGET_RUNNING) + { + LOG_ERROR("target has to be running to communicate with the loader"); + return ERROR_TARGET_NOT_RUNNING; + } + + /* allocate buffer for max. ocl buffer + overhead */ + dcc_buffer = malloc(sizeof(uint32_t)*(ocl->buflen/4 + 3)); + + while (count) + { + if (count + (offset % ocl->bufalign) > ocl->buflen) + runlen = ocl->buflen - (offset % ocl->bufalign); + else + runlen = count; + + dcc_buffer[0] = OCL_FLASH_BLOCK | runlen; + dcc_buffer[1] = offset; + dcc_bufptr = &dcc_buffer[2]; + + *dcc_bufptr = 0xffffffff; + byteofs = (offset % ocl->bufalign) % 4; + chksum = OCL_CHKS_INIT; + + /* copy data to DCC buffer in proper byte order and properly aligned */ + for (i = 0; i < runlen; i++) + { + switch (byteofs++) + { + case 0: + *dcc_bufptr &= *(buffer++) | 0xffffff00; + break; + case 1: + *dcc_bufptr &= ((*(buffer++)) << 8) | 0xffff00ff; + break; + case 2: + *dcc_bufptr &= ((*(buffer++)) << 16) | 0xff00ffff; + break; + case 3: + *dcc_bufptr &= ((*(buffer++)) << 24) | 0x00ffffff; + chksum ^= *(dcc_bufptr++); + *dcc_bufptr = 0xffffffff; + byteofs = 0; + break; + } + } + + /* add the remaining word to checksum */ + if (byteofs) + chksum ^= *(dcc_bufptr++); + + *(dcc_bufptr++) = chksum; + + /* send the data */ + if ((retval = embeddedice_send(ocl->jtag_info, dcc_buffer, dcc_bufptr-dcc_buffer)) != ERROR_OK) + { + free(dcc_buffer); + return retval; + } + + /* wait for response, fixed timeout of 1 s */ + if ((retval = embeddedice_handshake(ocl->jtag_info, EICE_COMM_CTRL_WBIT, 1000) != ERROR_OK)) + { + if (retval == ERROR_TARGET_TIMEOUT) + LOG_ERROR("loader not responding"); + free(dcc_buffer); + return retval; + } + + /* receive response */ + if ((retval = embeddedice_receive(ocl->jtag_info, dcc_buffer, 1) != ERROR_OK)) + { + free(dcc_buffer); + return retval; + } + + if (dcc_buffer[0] != OCL_CMD_DONE) + { + LOG_ERROR("loader response to OCL_FLASH_BLOCK 0x%08" PRIx32 "", dcc_buffer[0]); + free(dcc_buffer); + return ERROR_FLASH_OPERATION_FAILED; + } + + count -= runlen; + offset += runlen; + } + + free(dcc_buffer); + return ERROR_OK; +} + +static int ocl_probe(struct flash_bank *bank) +{ + struct ocl_priv *ocl = bank->driver_priv; + int retval; + uint32_t dcc_buffer[1]; + int sectsize; + int i; + + /* purge pending data in DCC */ + embeddedice_receive(ocl->jtag_info, dcc_buffer, 1); + + dcc_buffer[0] = OCL_PROBE; + if ((retval = embeddedice_send(ocl->jtag_info, dcc_buffer, 1) != ERROR_OK)) + return retval; + + /* wait for response, fixed timeout of 1 s */ + if ((retval = embeddedice_handshake(ocl->jtag_info, EICE_COMM_CTRL_WBIT, 1000) != ERROR_OK)) + { + if (retval == ERROR_TARGET_TIMEOUT) + LOG_ERROR("loader not responding"); + return retval; + } + + /* receive response */ + if ((retval = embeddedice_receive(ocl->jtag_info, dcc_buffer, 1) != ERROR_OK)) + return retval; + + if (dcc_buffer[0] != OCL_CMD_DONE) + { + LOG_ERROR("loader response to OCL_PROBE 0x%08" PRIx32 "", dcc_buffer[0]); + return ERROR_FLASH_OPERATION_FAILED; + } + + /* receive and fill in parameters, detection of loader is important, receive it one by one */ + if ((retval = embeddedice_handshake(ocl->jtag_info, EICE_COMM_CTRL_WBIT, 0) != ERROR_OK) + || (retval = embeddedice_receive(ocl->jtag_info, dcc_buffer, 1) != ERROR_OK)) + return retval; + bank->base = dcc_buffer[0]; + + if ((retval = embeddedice_handshake(ocl->jtag_info, EICE_COMM_CTRL_WBIT, 0) != ERROR_OK) + || (retval = embeddedice_receive(ocl->jtag_info, dcc_buffer, 1) != ERROR_OK)) + return retval; + bank->size = dcc_buffer[0]; + + if ((retval = embeddedice_handshake(ocl->jtag_info, EICE_COMM_CTRL_WBIT, 0) != ERROR_OK) + || (retval = embeddedice_receive(ocl->jtag_info, dcc_buffer, 1) != ERROR_OK)) + return retval; + bank->num_sectors = dcc_buffer[0]; + + if ((retval = embeddedice_handshake(ocl->jtag_info, EICE_COMM_CTRL_WBIT, 0) != ERROR_OK) + || (retval = embeddedice_receive(ocl->jtag_info, dcc_buffer, 1) != ERROR_OK)) + return retval; + ocl->buflen = dcc_buffer[0] & 0xffff; + ocl->bufalign = dcc_buffer[0] >> 16; + + bank->sectors = realloc(bank->sectors, sizeof(struct flash_sector)*bank->num_sectors); + if (bank->num_sectors == 0) + { + LOG_ERROR("number of sectors shall be non zero value"); + return ERROR_FLASH_BANK_INVALID; + } + if (bank->size % bank->num_sectors) { + LOG_ERROR("bank size not divisible by number of sectors"); + return ERROR_FLASH_BANK_INVALID; + } + sectsize = bank->size / bank->num_sectors; + for (i = 0; i < bank->num_sectors; i++) + { + bank->sectors[i].offset = i * sectsize; + bank->sectors[i].size = sectsize; + bank->sectors[i].is_erased = -1; + bank->sectors[i].is_protected = -1; + } + + if (ocl->bufalign == 0) + ocl->bufalign = 1; + + if (ocl->buflen == 0) + { + LOG_ERROR("buflen shall be non zero value"); + return ERROR_FLASH_BANK_INVALID; + } + + if ((ocl->bufalign > ocl->buflen) || (ocl->buflen % ocl->bufalign)) + { + LOG_ERROR("buflen is not multiple of bufalign"); + return ERROR_FLASH_BANK_INVALID; + } + + if (ocl->buflen % 4) + { + LOG_ERROR("buflen shall be divisible by 4"); + return ERROR_FLASH_BANK_INVALID; + } + + return ERROR_OK; +} + +static int ocl_info(struct flash_bank *bank, char *buf, int buf_size) +{ + return ERROR_OK; +} + +static int ocl_auto_probe(struct flash_bank *bank) +{ + struct ocl_priv *ocl = bank->driver_priv; + + if (ocl->buflen == 0 || ocl->bufalign == 0) + return ERROR_FLASH_BANK_NOT_PROBED; + + return ERROR_OK; +} + +struct flash_driver ocl_flash = { + .name = "ocl", + .flash_bank_command = &ocl_flash_bank_command, + .erase = &ocl_erase, + .protect = &ocl_protect, + .write = &ocl_write, + .probe = &ocl_probe, + .erase_check = &ocl_erase_check, + .protect_check = &ocl_protect_check, + .info = &ocl_info, + .auto_probe = &ocl_auto_probe, + }; diff --git a/src/flash/nor/ocl.h b/src/flash/nor/ocl.h new file mode 100644 index 00000000..d5c430be --- /dev/null +++ b/src/flash/nor/ocl.h @@ -0,0 +1,40 @@ +/*************************************************************************** + * Copyright (C) 2007 by Pavel Chromy * + * chromy@asix.cz * + * * + * 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 OCL_H +#define OCL_H + +/* command/response mask */ +#define OCL_CMD_MASK 0xFFFF0000L + +/* commads */ +#define OCL_FLASH_BLOCK 0x0CFB0000L +#define OCL_ERASE_BLOCK 0x0CEB0000L +#define OCL_ERASE_ALL 0x0CEA0000L +#define OCL_PROBE 0x0CBE0000L + +/* responses */ +#define OCL_CMD_DONE 0x0ACD0000L +#define OCL_CMD_ERR 0x0ACE0000L +#define OCL_CHKS_FAIL 0x0ACF0000L +#define OCL_BUFF_OVER 0x0AB00000L + +#define OCL_CHKS_INIT 0xC100CD0CL + +#endif /* OCL_H */ diff --git a/src/flash/nor/pic32mx.c b/src/flash/nor/pic32mx.c new file mode 100644 index 00000000..9bb6c97e --- /dev/null +++ b/src/flash/nor/pic32mx.c @@ -0,0 +1,922 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * Copyright (C) 2008 by Spencer Oliver * + * spen@spen-soft.co.uk * + * * + * Copyright (C) 2008 by John McCarthy * + * jgmcc@magma.ca * + * * + * 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 "pic32mx.h" +#include "mips32.h" + + +static +struct pic32mx_devs_s { + uint8_t devid; + char *name; + uint32_t pfm_size; +} pic32mx_devs[] = { + { 0x78, "460F512L USB", 512 }, + { 0x74, "460F256L USB", 256 }, + { 0x6D, "440F128L USB", 128 }, + { 0x56, "440F512H USB", 512 }, + { 0x52, "440F256H USB", 256 }, + { 0x4D, "440F128H USB", 128 }, + { 0x42, "420F032H USB", 32 }, + { 0x38, "360F512L", 512 }, + { 0x34, "360F256L", 256 }, + { 0x2D, "340F128L", 128 }, + { 0x2A, "320F128L", 128 }, + { 0x16, "340F512H", 512 }, + { 0x12, "340F256H", 256 }, + { 0x0D, "340F128H", 128 }, + { 0x0A, "320F128H", 128 }, + { 0x06, "320F064H", 64 }, + { 0x02, "320F032H", 32 }, + { 0x00, NULL, 0 } +}; + +static int pic32mx_write_row(struct flash_bank *bank, uint32_t address, uint32_t srcaddr); +static int pic32mx_write_word(struct flash_bank *bank, uint32_t address, uint32_t word); + +/* flash bank pic32mx 0 0 + */ +FLASH_BANK_COMMAND_HANDLER(pic32mx_flash_bank_command) +{ + struct pic32mx_flash_bank *pic32mx_info; + + if (CMD_ARGC < 6) + { + LOG_WARNING("incomplete flash_bank pic32mx configuration"); + return ERROR_FLASH_BANK_INVALID; + } + + pic32mx_info = malloc(sizeof(struct pic32mx_flash_bank)); + bank->driver_priv = pic32mx_info; + + pic32mx_info->write_algorithm = NULL; + pic32mx_info->probed = 0; + + return ERROR_OK; +} + +static uint32_t pic32mx_get_flash_status(struct flash_bank *bank) +{ + struct target *target = bank->target; + uint32_t status; + + target_read_u32(target, PIC32MX_NVMCON, &status); + + return status; +} + +static uint32_t pic32mx_wait_status_busy(struct flash_bank *bank, int timeout) +{ + uint32_t status; + + /* wait for busy to clear */ + while (((status = pic32mx_get_flash_status(bank)) & NVMCON_NVMWR) && (timeout-- > 0)) + { + LOG_DEBUG("status: 0x%" PRIx32, status); + alive_sleep(1); + } + if (timeout <= 0) + LOG_DEBUG("timeout: status: 0x%" PRIx32, status); + + return status; +} + +static int pic32mx_nvm_exec(struct flash_bank *bank, uint32_t op, uint32_t timeout) +{ + struct target *target = bank->target; + uint32_t status; + + target_write_u32(target, PIC32MX_NVMCON, NVMCON_NVMWREN | op); + + /* unlock flash registers */ + target_write_u32(target, PIC32MX_NVMKEY, NVMKEY1); + target_write_u32(target, PIC32MX_NVMKEY, NVMKEY2); + + /* start operation */ + target_write_u32(target, PIC32MX_NVMCONSET, NVMCON_NVMWR); + + status = pic32mx_wait_status_busy(bank, timeout); + + /* lock flash registers */ + target_write_u32(target, PIC32MX_NVMCONCLR, NVMCON_NVMWREN); + + return status; +} + +static int pic32mx_protect_check(struct flash_bank *bank) +{ + struct target *target = bank->target; + + uint32_t devcfg0; + int s; + int num_pages; + + if (target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + target_read_u32(target, PIC32MX_DEVCFG0, &devcfg0); + if ((devcfg0 & (1 << 28)) == 0) /* code protect bit */ + num_pages = 0xffff; /* All pages protected */ + else if (bank->base == PIC32MX_KSEG1_BOOT_FLASH) + { + if (devcfg0 & (1 << 24)) + num_pages = 0; /* All pages unprotected */ + else + num_pages = 0xffff; /* All pages protected */ + } + else /* pgm flash */ + num_pages = (~devcfg0 >> 12) & 0xff; + for (s = 0; s < bank->num_sectors && s < num_pages; s++) + bank->sectors[s].is_protected = 1; + for (; s < bank->num_sectors; s++) + bank->sectors[s].is_protected = 0; + + return ERROR_OK; +} + +static int pic32mx_erase(struct flash_bank *bank, int first, int last) +{ + struct target *target = bank->target; + int i; + uint32_t status; + + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if ((first == 0) && (last == (bank->num_sectors - 1)) && (bank->base == PIC32MX_KSEG0_PGM_FLASH || bank->base == PIC32MX_KSEG1_PGM_FLASH)) + { + LOG_DEBUG("Erasing entire program flash"); + status = pic32mx_nvm_exec(bank, NVMCON_OP_PFM_ERASE, 50); + if (status & NVMCON_NVMERR) + return ERROR_FLASH_OPERATION_FAILED; + if (status & NVMCON_LVDERR) + return ERROR_FLASH_OPERATION_FAILED; + return ERROR_OK; + } + + for (i = first; i <= last; i++) + { + if (bank->base >= PIC32MX_KSEG1_PGM_FLASH) + target_write_u32(target, PIC32MX_NVMADDR, KS1Virt2Phys(bank->base + bank->sectors[i].offset)); + else + target_write_u32(target, PIC32MX_NVMADDR, KS0Virt2Phys(bank->base + bank->sectors[i].offset)); + + status = pic32mx_nvm_exec(bank, NVMCON_OP_PAGE_ERASE, 10); + + if (status & NVMCON_NVMERR) + return ERROR_FLASH_OPERATION_FAILED; + if (status & NVMCON_LVDERR) + return ERROR_FLASH_OPERATION_FAILED; + bank->sectors[i].is_erased = 1; + } + + return ERROR_OK; +} + +static int pic32mx_protect(struct flash_bank *bank, int set, int first, int last) +{ + struct pic32mx_flash_bank *pic32mx_info = NULL; + struct target *target = bank->target; +#if 0 + uint16_t prot_reg[4] = {0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF}; + int i, reg, bit; + int status; + uint32_t protection; +#endif + + pic32mx_info = bank->driver_priv; + + if (target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + +#if 0 + if ((first && (first % pic32mx_info->ppage_size)) || ((last + 1) && (last + 1) % pic32mx_info->ppage_size)) + { + LOG_WARNING("sector start/end incorrect - stm32 has %dK sector protection", pic32mx_info->ppage_size); + return ERROR_FLASH_SECTOR_INVALID; + } + + /* medium density - each bit refers to a 4bank protection + * high density - each bit refers to a 2bank protection */ + target_read_u32(target, PIC32MX_FLASH_WRPR, &protection); + + prot_reg[0] = (uint16_t)protection; + prot_reg[1] = (uint16_t)(protection >> 8); + prot_reg[2] = (uint16_t)(protection >> 16); + prot_reg[3] = (uint16_t)(protection >> 24); + + if (pic32mx_info->ppage_size == 2) + { + /* high density flash */ + + /* bit 7 controls sector 62 - 255 protection */ + if (last > 61) + { + if (set) + prot_reg[3] &= ~(1 << 7); + else + prot_reg[3] |= (1 << 7); + } + + if (first > 61) + first = 62; + if (last > 61) + last = 61; + + for (i = first; i <= last; i++) + { + reg = (i / pic32mx_info->ppage_size) / 8; + bit = (i / pic32mx_info->ppage_size) - (reg * 8); + + if (set) + prot_reg[reg] &= ~(1 << bit); + else + prot_reg[reg] |= (1 << bit); + } + } + else + { + /* medium density flash */ + for (i = first; i <= last; i++) + { + reg = (i / pic32mx_info->ppage_size) / 8; + bit = (i / pic32mx_info->ppage_size) - (reg * 8); + + if (set) + prot_reg[reg] &= ~(1 << bit); + else + prot_reg[reg] |= (1 << bit); + } + } + + if ((status = pic32mx_erase_options(bank)) != ERROR_OK) + return status; + + pic32mx_info->option_bytes.protection[0] = prot_reg[0]; + pic32mx_info->option_bytes.protection[1] = prot_reg[1]; + pic32mx_info->option_bytes.protection[2] = prot_reg[2]; + pic32mx_info->option_bytes.protection[3] = prot_reg[3]; + + return pic32mx_write_options(bank); +#else + return ERROR_OK; +#endif +} + +static int pic32mx_write_block(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) +{ + struct target *target = bank->target; + uint32_t buffer_size = 512; + struct working_area *source; + uint32_t address = bank->base + offset; + int retval = ERROR_OK; +#if 0 + struct pic32mx_flash_bank *pic32mx_info = bank->driver_priv; + struct armv7m_algorithm armv7m_info; + + uint8_t pic32mx_flash_write_code[] = { + /* write: */ + 0xDF, 0xF8, 0x24, 0x40, /* ldr r4, PIC32MX_FLASH_CR */ + 0x09, 0x4D, /* ldr r5, PIC32MX_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, /* PIC32MX_FLASH_CR: .word 0x40022010 */ + 0x0C, 0x20, 0x02, 0x40 /* PIC32MX_FLASH_SR: .word 0x4002200C */ + }; + + /* flash write code */ + if (target_alloc_working_area(target, sizeof(pic32mx_flash_write_code), &pic32mx_info->write_algorithm) != ERROR_OK) + { + LOG_WARNING("no working area available, can't do block memory writes"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + }; + + if ((retval = target_write_buffer(target, pic32mx_info->write_algorithm->address, sizeof(pic32mx_flash_write_code), pic32mx_flash_write_code)) != ERROR_OK) + return retval; +#endif + + /* memory buffer */ + if (target_alloc_working_area(target, buffer_size, &source) != ERROR_OK) + { +#if 0 + /* if we already allocated the writing code, but failed to get a buffer, free the algorithm */ + if (pic32mx_info->write_algorithm) + target_free_working_area(target, pic32mx_info->write_algorithm); +#endif + + LOG_WARNING("no large enough working area available, can't do block memory writes"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + while (count >= buffer_size/4) + { + uint32_t status; + + if ((retval = target_write_buffer(target, source->address, buffer_size, buffer)) != ERROR_OK) { + LOG_ERROR("Failed to write row buffer (%d words) to RAM", (int)(buffer_size/4)); + break; + } + +#if 0 + 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, buffer_size/4); + + if ((retval = target_run_algorithm(target, 0, NULL, 4, reg_params, pic32mx_info->write_algorithm->address, \ + pic32mx_info->write_algorithm->address + (sizeof(pic32mx_flash_write_code) - 10), 10000, &armv7m_info)) != ERROR_OK) + { + LOG_ERROR("error executing pic32mx flash write algorithm"); + retval = ERROR_FLASH_OPERATION_FAILED; + break; + } + + if (buf_get_u32(reg_params[3].value, 0, 32) & 0x14) + { + retval = ERROR_FLASH_OPERATION_FAILED; + break; + } +#endif + status = pic32mx_write_row(bank, address, source->address); + if (status & NVMCON_NVMERR) { + LOG_ERROR("Flash write error NVMERR (status = 0x%08" PRIx32 ")", status); + retval = ERROR_FLASH_OPERATION_FAILED; + break; + } + if (status & NVMCON_LVDERR) { + LOG_ERROR("Flash write error LVDERR (status = 0x%08" PRIx32 ")", status); + retval = ERROR_FLASH_OPERATION_FAILED; + break; + } + + buffer += buffer_size; + address += buffer_size; + count -= buffer_size/4; + } + + target_free_working_area(target, source); + + while (count > 0) + { + uint32_t value; + memcpy(&value, buffer, sizeof(uint32_t)); + + uint32_t status = pic32mx_write_word(bank, address, value); + if (status & NVMCON_NVMERR) { + LOG_ERROR("Flash write error NVMERR (status = 0x%08" PRIx32 ")", status); + retval = ERROR_FLASH_OPERATION_FAILED; + break; + } + if (status & NVMCON_LVDERR) { + LOG_ERROR("Flash write error LVDERR (status = 0x%08" PRIx32 ")", status); + retval = ERROR_FLASH_OPERATION_FAILED; + break; + } + + buffer += 4; + address += 4; + count--; + } + + return retval; +} + +static int pic32mx_write_word(struct flash_bank *bank, uint32_t address, uint32_t word) +{ + struct target *target = bank->target; + + if (bank->base >= PIC32MX_KSEG1_PGM_FLASH) + target_write_u32(target, PIC32MX_NVMADDR, KS1Virt2Phys(address)); + else + target_write_u32(target, PIC32MX_NVMADDR, KS0Virt2Phys(address)); + target_write_u32(target, PIC32MX_NVMDATA, word); + + return pic32mx_nvm_exec(bank, NVMCON_OP_WORD_PROG, 5); +} + +/* + * Write a 128 word (512 byte) row to flash address from RAM srcaddr. + */ +static int pic32mx_write_row(struct flash_bank *bank, uint32_t address, uint32_t srcaddr) +{ + struct target *target = bank->target; + + LOG_DEBUG("addr: 0x%08" PRIx32 " srcaddr: 0x%08" PRIx32 "", address, srcaddr); + + if (address >= PIC32MX_KSEG1_PGM_FLASH) + target_write_u32(target, PIC32MX_NVMADDR, KS1Virt2Phys(address)); + else + target_write_u32(target, PIC32MX_NVMADDR, KS0Virt2Phys(address)); + if (srcaddr >= PIC32MX_KSEG1_RAM) + target_write_u32(target, PIC32MX_NVMSRCADDR, KS1Virt2Phys(srcaddr)); + else + target_write_u32(target, PIC32MX_NVMSRCADDR, KS0Virt2Phys(srcaddr)); + + return pic32mx_nvm_exec(bank, NVMCON_OP_ROW_PROG, 100); +} + +static int pic32mx_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) +{ + uint32_t words_remaining = (count / 4); + uint32_t bytes_remaining = (count & 0x00000003); + uint32_t address = bank->base + offset; + uint32_t bytes_written = 0; + uint32_t status; + int retval; + + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (offset & 0x3) + { + LOG_WARNING("offset 0x%" PRIx32 "breaks required 4-byte alignment", offset); + return ERROR_FLASH_DST_BREAKS_ALIGNMENT; + } + + /* multiple words (4-byte) to be programmed? */ + if (words_remaining > 0) + { + /* try using a block write */ + if ((retval = pic32mx_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 */ + LOG_WARNING("couldn't use block writes, falling back to single memory accesses"); + } + else if (retval == ERROR_FLASH_OPERATION_FAILED) + { + LOG_ERROR("flash writing failed with error code: 0x%x", retval); + return ERROR_FLASH_OPERATION_FAILED; + } + } + else + { + buffer += words_remaining * 4; + address += words_remaining * 4; + words_remaining = 0; + } + } + + while (words_remaining > 0) + { + uint32_t value; + memcpy(&value, buffer + bytes_written, sizeof(uint32_t)); + + status = pic32mx_write_word(bank, address, value); + if (status & NVMCON_NVMERR) + return ERROR_FLASH_OPERATION_FAILED; + if (status & NVMCON_LVDERR) + return ERROR_FLASH_OPERATION_FAILED; + + bytes_written += 4; + words_remaining--; + address += 4; + } + + if (bytes_remaining) + { + uint32_t value = 0xffffffff; + memcpy(&value, buffer + bytes_written, bytes_remaining); + + status = pic32mx_write_word(bank, address, value); + if (status & NVMCON_NVMERR) + return ERROR_FLASH_OPERATION_FAILED; + if (status & NVMCON_LVDERR) + return ERROR_FLASH_OPERATION_FAILED; + } + + return ERROR_OK; +} + +static int pic32mx_probe(struct flash_bank *bank) +{ + struct target *target = bank->target; + struct pic32mx_flash_bank *pic32mx_info = bank->driver_priv; + struct mips32_common *mips32 = target->arch_info; + struct mips_ejtag *ejtag_info = &mips32->ejtag_info; + int i; + uint16_t num_pages = 0; + uint32_t device_id; + int page_size; + + pic32mx_info->probed = 0; + + device_id = ejtag_info->idcode; + LOG_INFO("device id = 0x%08" PRIx32 " (manuf 0x%03x dev 0x%02x, ver 0x%03x)", + device_id, + (unsigned)((device_id >> 1)&0x7ff), + (unsigned)((device_id >> 12)&0xff), + (unsigned)((device_id >> 20)&0xfff)); + + if (((device_id >> 1)&0x7ff) != PIC32MX_MANUF_ID) { + LOG_WARNING("Cannot identify target as a PIC32MX family."); + return ERROR_FLASH_OPERATION_FAILED; + } + + page_size = 4096; + if (bank->base == PIC32MX_KSEG1_BOOT_FLASH || bank->base == 1) { + /* 0xBFC00000: Boot flash size fixed at 12k */ + num_pages = 12; + } else { + /* 0xBD000000: Program flash size varies with device */ + for (i = 0; pic32mx_devs[i].name != NULL; i++) + if (pic32mx_devs[i].devid == ((device_id >> 12) & 0xff)) { + num_pages = pic32mx_devs[i].pfm_size; + break; + } + if (pic32mx_devs[i].name == NULL) { + LOG_WARNING("Cannot identify target as a PIC32MX family."); + return ERROR_FLASH_OPERATION_FAILED; + } + } + +#if 0 + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* get flash size from target */ + if (target_read_u16(target, 0x1FFFF7E0, &num_pages) != ERROR_OK) + { + /* failed reading flash size, default to max target family */ + num_pages = 0xffff; + } +#endif + + LOG_INFO("flash size = %dkbytes", num_pages); + + /* calculate numbers of pages */ + num_pages /= (page_size / 1024); + + if (bank->base == 0) bank->base = PIC32MX_KSEG1_PGM_FLASH; + if (bank->base == 1) bank->base = PIC32MX_KSEG1_BOOT_FLASH; + bank->size = (num_pages * page_size); + bank->num_sectors = num_pages; + bank->chip_width = 4; + bank->bus_width = 4; + bank->sectors = malloc(sizeof(struct flash_sector) * num_pages); + + for (i = 0; i < num_pages; i++) + { + bank->sectors[i].offset = i * page_size; + bank->sectors[i].size = page_size; + bank->sectors[i].is_erased = -1; + bank->sectors[i].is_protected = 1; + } + + pic32mx_info->probed = 1; + + return ERROR_OK; +} + +static int pic32mx_auto_probe(struct flash_bank *bank) +{ + struct pic32mx_flash_bank *pic32mx_info = bank->driver_priv; + if (pic32mx_info->probed) + return ERROR_OK; + return pic32mx_probe(bank); +} + +#if 0 +COMMAND_HANDLER(pic32mx_handle_part_id_command) +{ + return ERROR_OK; +} +#endif + +static int pic32mx_info(struct flash_bank *bank, char *buf, int buf_size) +{ + struct target *target = bank->target; + struct mips32_common *mips32 = target->arch_info; + struct mips_ejtag *ejtag_info = &mips32->ejtag_info; + uint32_t device_id; + int printed = 0, i; + + device_id = ejtag_info->idcode; + + if (((device_id >> 1)&0x7ff) != PIC32MX_MANUF_ID) { + snprintf(buf, buf_size, + "Cannot identify target as a PIC32MX family (manufacturer 0x%03d != 0x%03d)\n", + (unsigned)((device_id >> 1)&0x7ff), + PIC32MX_MANUF_ID); + return ERROR_FLASH_OPERATION_FAILED; + } + for (i = 0; pic32mx_devs[i].name != NULL; i++) + if (pic32mx_devs[i].devid == ((device_id >> 12) & 0xff)) { + printed = snprintf(buf, buf_size, "PIC32MX%s", pic32mx_devs[i].name); + break; + } + if (pic32mx_devs[i].name == NULL) { + snprintf(buf, buf_size, "Cannot identify target as a PIC32MX family\n"); + return ERROR_FLASH_OPERATION_FAILED; + } + buf += printed; + buf_size -= printed; + printed = snprintf(buf, buf_size, " Ver: 0x%03x", + (unsigned)((device_id >> 20)&0xfff)); + + return ERROR_OK; +} + +#if 0 +COMMAND_HANDLER(pic32mx_handle_lock_command) +{ + struct target *target = NULL; + struct pic32mx_flash_bank *pic32mx_info = NULL; + + if (CMD_ARGC < 1) + { + command_print(CMD_CTX, "pic32mx lock "); + return ERROR_OK; + } + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + pic32mx_info = bank->driver_priv; + + target = bank->target; + + if (target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (pic32mx_erase_options(bank) != ERROR_OK) + { + command_print(CMD_CTX, "pic32mx failed to erase options"); + return ERROR_OK; + } + + /* set readout protection */ + pic32mx_info->option_bytes.RDP = 0; + + if (pic32mx_write_options(bank) != ERROR_OK) + { + command_print(CMD_CTX, "pic32mx failed to lock device"); + return ERROR_OK; + } + + command_print(CMD_CTX, "pic32mx locked"); + + return ERROR_OK; +} + +COMMAND_HANDLER(pic32mx_handle_unlock_command) +{ + struct target *target = NULL; + struct pic32mx_flash_bank *pic32mx_info = NULL; + + if (CMD_ARGC < 1) + { + command_print(CMD_CTX, "pic32mx unlock "); + return ERROR_OK; + } + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + pic32mx_info = bank->driver_priv; + + target = bank->target; + + if (target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (pic32mx_erase_options(bank) != ERROR_OK) + { + command_print(CMD_CTX, "pic32mx failed to unlock device"); + return ERROR_OK; + } + + if (pic32mx_write_options(bank) != ERROR_OK) + { + command_print(CMD_CTX, "pic32mx failed to lock device"); + return ERROR_OK; + } + + command_print(CMD_CTX, "pic32mx unlocked"); + + return ERROR_OK; +} +#endif + +#if 0 +static int pic32mx_chip_erase(struct flash_bank *bank) +{ + struct target *target = bank->target; +#if 0 + uint32_t status; +#endif + + if (target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + LOG_INFO("PIC32MX chip erase called"); + +#if 0 + /* unlock option flash registers */ + target_write_u32(target, PIC32MX_FLASH_KEYR, KEY1); + target_write_u32(target, PIC32MX_FLASH_KEYR, KEY2); + + /* chip erase flash memory */ + target_write_u32(target, PIC32MX_FLASH_CR, FLASH_MER); + target_write_u32(target, PIC32MX_FLASH_CR, FLASH_MER | FLASH_STRT); + + status = pic32mx_wait_status_busy(bank, 10); + + target_write_u32(target, PIC32MX_FLASH_CR, FLASH_LOCK); + + if (status & FLASH_WRPRTERR) + { + LOG_ERROR("pic32mx device protected"); + return ERROR_OK; + } + + if (status & FLASH_PGERR) + { + LOG_ERROR("pic32mx device programming failed"); + return ERROR_OK; + } +#endif + + return ERROR_OK; +} +#endif + +COMMAND_HANDLER(pic32mx_handle_chip_erase_command) +{ +#if 0 + int i; + + if (CMD_ARGC != 0) + { + command_print(CMD_CTX, "pic32mx chip_erase"); + return ERROR_OK; + } + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + if (pic32mx_chip_erase(bank) == ERROR_OK) + { + /* set all sectors as erased */ + for (i = 0; i < bank->num_sectors; i++) + { + bank->sectors[i].is_erased = 1; + } + + command_print(CMD_CTX, "pic32mx chip erase complete"); + } + else + { + command_print(CMD_CTX, "pic32mx chip erase failed"); + } +#endif + + return ERROR_OK; +} + +COMMAND_HANDLER(pic32mx_handle_pgm_word_command) +{ + uint32_t address, value; + int status, res; + + if (CMD_ARGC != 3) + { + command_print(CMD_CTX, "pic32mx pgm_word "); + return ERROR_OK; + } + + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], address); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value); + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 2, &bank); + if (ERROR_OK != retval) + return retval; + + if (address < bank->base || address >= (bank->base + bank->size)) + { + command_print(CMD_CTX, "flash address '%s' is out of bounds", CMD_ARGV[0]); + return ERROR_OK; + } + + res = ERROR_OK; + status = pic32mx_write_word(bank, address, value); + if (status & NVMCON_NVMERR) + res = ERROR_FLASH_OPERATION_FAILED; + if (status & NVMCON_LVDERR) + res = ERROR_FLASH_OPERATION_FAILED; + + if (res == ERROR_OK) + command_print(CMD_CTX, "pic32mx pgm word complete"); + else + command_print(CMD_CTX, "pic32mx pgm word failed (status = 0x%x)", status); + + return ERROR_OK; +} +static const struct command_registration pic32mx_exec_command_handlers[] = { + { + .name = "chip_erase", + .handler = &pic32mx_handle_chip_erase_command, + .mode = COMMAND_EXEC, + .help = "erase device", + }, + { + .name = "pgm_word", + .handler = &pic32mx_handle_pgm_word_command, + .mode = COMMAND_EXEC, + .help = "program a word", + }, + COMMAND_REGISTRATION_DONE +}; +static const struct command_registration pic32mx_command_handlers[] = { + { + .name = "pic32mx", + .mode = COMMAND_ANY, + .help = "pic32mx flash command group", + .chain = pic32mx_exec_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +struct flash_driver pic32mx_flash = { + .name = "pic32mx", + .commands = pic32mx_command_handlers, + .flash_bank_command = &pic32mx_flash_bank_command, + .erase = &pic32mx_erase, + .protect = &pic32mx_protect, + .write = &pic32mx_write, + .probe = &pic32mx_probe, + .auto_probe = &pic32mx_auto_probe, + .erase_check = &default_flash_mem_blank_check, + .protect_check = &pic32mx_protect_check, + .info = &pic32mx_info, + }; diff --git a/src/flash/nor/pic32mx.h b/src/flash/nor/pic32mx.h new file mode 100644 index 00000000..92f40c2e --- /dev/null +++ b/src/flash/nor/pic32mx.h @@ -0,0 +1,113 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * Copyright (C) 2008 by Spencer Oliver * + * spen@spen-soft.co.uk * + * * + * Copyright (C) 2008 by John McCarthy * + * jgmcc@magma.ca * + * * + * 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 PIC32MX_H +#define PIC32MX_H + +#include "flash.h" + +struct pic32mx_flash_bank +{ + struct working_area *write_algorithm; + int devid; + int ppage_size; + int probed; +}; + +#define PIC32MX_MANUF_ID 0x029 + +/* pic32mx memory locations */ + +#define PIC32MX_KUSEG_PGM_FLASH 0x7D000000 +#define PIC32MX_KUSEG_RAM 0x7F000000 + +#define PIC32MX_KSEG0_RAM 0x80000000 +#define PIC32MX_KSEG0_PGM_FLASH 0x9D000000 +#define PIC32MX_KSEG0_BOOT_FLASH 0x9FC00000 + +#define PIC32MX_KSEG1_RAM 0xA0000000 +#define PIC32MX_KSEG1_PGM_FLASH 0xBD000000 +#define PIC32MX_KSEG1_PERIPHERAL 0xBF800000 +#define PIC32MX_KSEG1_BOOT_FLASH 0xBFC00000 + +#define PIC32MX_PHYS_RAM 0x00000000 +#define PIC32MX_PHYS_PGM_FLASH 0x1D000000 +#define PIC32MX_PHYS_PERIPHERALS 0x1F800000 +#define PIC32MX_PHYS_BOOT_FLASH 0x1FC00000 + +/* + * Translate Virtual and Physical addresses. + * Note: These macros only work for KSEG0/KSEG1 addresses. + */ +#define KS1Virt2Phys(vaddr) ((vaddr)-0xA0000000) +#define Phys2KS1Virt(paddr) ((paddr) + 0xA0000000) +#define KS0Virt2Phys(vaddr) ((vaddr)-0x80000000) +#define Phys2KS0Virt(paddr) ((paddr) + 0x80000000) + +/* pic32mx configuration register locations */ + +#define PIC32MX_DEVCFG0 0xBFC02FFC +#define PIC32MX_DEVCFG1 0xBFC02FF8 +#define PIC32MX_DEVCFG2 0xBFC02FF4 +#define PIC32MX_DEVCFG3 0XBFC02FF0 +#define PIC32MX_DEVID 0xBF80F220 + +/* pic32mx flash controller register locations */ + +#define PIC32MX_NVMCON 0xBF80F400 +#define PIC32MX_NVMCONCLR 0xBF80F404 +#define PIC32MX_NVMCONSET 0xBF80F408 +#define PIC32MX_NVMCONINV 0xBF80F40C +#define NVMCON_NVMWR (1 << 15) +#define NVMCON_NVMWREN (1 << 14) +#define NVMCON_NVMERR (1 << 13) +#define NVMCON_LVDERR (1 << 12) +#define NVMCON_LVDSTAT (1 << 11) +#define NVMCON_OP_PFM_ERASE 0x5 +#define NVMCON_OP_PAGE_ERASE 0x4 +#define NVMCON_OP_ROW_PROG 0x3 +#define NVMCON_OP_WORD_PROG 0x1 +#define NVMCON_OP_NOP 0x0 + +#define PIC32MX_NVMKEY 0xBF80F410 +#define PIC32MX_NVMADDR 0xBF80F420 +#define PIC32MX_NVMADDRCLR 0xBF80F424 +#define PIC32MX_NVMADDRSET 0xBF80F428 +#define PIC32MX_NVMADDRINV 0xBF80F42C +#define PIC32MX_NVMDATA 0xBF80F430 +#define PIC32MX_NVMSRCADDR 0xBF80F440 + +/* flash unlock keys */ + +#define NVMKEY1 0xAA996655 +#define NVMKEY2 0x556699AA + +struct pic32mx_mem_layout { + uint32_t sector_start; + uint32_t sector_size; +}; + +#endif /* PIC32MX_H */ + diff --git a/src/flash/nor/stellaris.c b/src/flash/nor/stellaris.c new file mode 100644 index 00000000..771f0a71 --- /dev/null +++ b/src/flash/nor/stellaris.c @@ -0,0 +1,1195 @@ +/*************************************************************************** + * Copyright (C) 2006 by Magnus Lundin * + * lundin@mlu.mine.nu * + * * + * Copyright (C) 2008 by Spencer Oliver * + * spen@spen-soft.co.uk * + * * + * 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, LM3S6965 +***************************************************************************/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "stellaris.h" +#include "armv7m.h" +#include "binarybuffer.h" +#include "algorithm.h" + + +#define DID0_VER(did0) ((did0 >> 28)&0x07) + +static int stellaris_read_part_info(struct flash_bank *bank); +static uint32_t stellaris_get_flash_status(struct flash_bank *bank); +static void stellaris_set_flash_mode(struct flash_bank *bank,int mode); +//static uint32_t stellaris_wait_status_busy(struct flash_bank *bank, uint32_t waitbits, int timeout); + +static int stellaris_mass_erase(struct flash_bank *bank); + +static struct { + uint32_t partno; + char *partname; +} StellarisParts[] = +{ + {0x01,"LM3S101"}, + {0x02,"LM3S102"}, + {0x03,"LM3S1625"}, + {0x04,"LM3S1626"}, + {0x05,"LM3S1627"}, + {0x06,"LM3S1607"}, + {0x10,"LM3S1776"}, + {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"}, + /*{0x33,"LM3S2616"},*/ + {0x34,"LM3S815"}, + {0x36,"LM3S817"}, + {0x37,"LM3S818"}, + {0x35,"LM3S828"}, + {0x39,"LM3S2276"}, + {0x3A,"LM3S2776"}, + {0x43,"LM3S3651"}, + {0x44,"LM3S3739"}, + {0x45,"LM3S3749"}, + {0x46,"LM3S3759"}, + {0x48,"LM3S3768"}, + {0x49,"LM3S3748"}, + {0x50,"LM3S2678"}, + {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"}, + {0x80,"LM3S2671"}, + {0x81,"LM3S5632"}, + {0x82,"LM3S6422"}, + {0x83,"LM3S6633"}, + {0x84,"LM3S2139"}, + {0x85,"LM3S2637"}, + {0x86,"LM3S8738"}, + {0x88,"LM3S8938"}, + {0x89,"LM3S6938"}, + {0x8A,"LM3S5652"}, + {0x8B,"LM3S6637"}, + {0x8C,"LM3S8933"}, + {0x8D,"LM3S8733"}, + {0x8E,"LM3S8538"}, + {0x8F,"LM3S2948"}, + {0x91,"LM3S5662"}, + {0x96,"LM3S5732"}, + {0x97,"LM3S5737"}, + {0x99,"LM3S5747"}, + {0x9A,"LM3S5752"}, + {0x9B,"LM3S5757"}, + {0x9C,"LM3S5762"}, + {0x9D,"LM3S5767"}, + {0xA0,"LM3S5739"}, + {0xA1,"LM3S6100"}, + {0xA2,"LM3S2410"}, + {0xA3,"LM3S6730"}, + {0xA4,"LM3S2730"}, + {0xA5,"LM3S6420"}, + {0xA6,"LM3S8962"}, + {0xA7,"LM3S5749"}, + {0xA8,"LM3S5769"}, + {0xA9,"LM3S5768"}, + {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"} +}; + +static char * StellarisClassname[5] = +{ + "Sandstorm", + "Fury", + "Unknown", + "DustDevil", + "Tempest" +}; + +/*************************************************************************** +* openocd command interface * +***************************************************************************/ + +/* flash_bank stellaris 0 0 + */ +FLASH_BANK_COMMAND_HANDLER(stellaris_flash_bank_command) +{ + struct stellaris_flash_bank *stellaris_info; + + if (CMD_ARGC < 6) + { + LOG_WARNING("incomplete flash_bank stellaris configuration"); + return ERROR_FLASH_BANK_INVALID; + } + + stellaris_info = calloc(sizeof(struct stellaris_flash_bank), 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 Specify the main crystal speed in kHz using an optional + * argument; ditto, the speed of an external oscillator used + * instead of a crystal. Avoid programming flash using IOSC. + */ + return ERROR_OK; +} + +static int stellaris_info(struct flash_bank *bank, char *buf, int buf_size) +{ + int printed, device_class; + struct stellaris_flash_bank *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, + "\nTI/LMI Stellaris information: Chip is " + "class %i (%s) %s rev %c%i\n", + device_class, + StellarisClassname[device_class], + stellaris_info->target_name, + (int)('A' + ((stellaris_info->did0 >> 8) & 0xFF)), + (int)((stellaris_info->did0) & 0xFF)); + buf += printed; + buf_size -= printed; + + printed = snprintf(buf, + buf_size, + "did1: 0x%8.8" PRIx32 ", arch: 0x%4.4" PRIx32 + ", eproc: %s, ramsize: %ik, flashsize: %ik\n", + stellaris_info->did1, + stellaris_info->did1, + "ARMv7M", + (int)((1 + ((stellaris_info->dc0 >> 16) & 0xFFFF))/4), + (int)((1 + (stellaris_info->dc0 & 0xFFFF))*2)); + buf += printed; + buf_size -= printed; + + printed = snprintf(buf, + buf_size, + "master clock: %ikHz%s, " + "rcc is 0x%" PRIx32 ", rcc2 is 0x%" PRIx32 "\n", + (int)(stellaris_info->mck_freq / 1000), + stellaris_info->mck_desc, + stellaris_info->rcc, + stellaris_info->rcc2); + buf += printed; + buf_size -= printed; + + if (stellaris_info->num_lockbits > 0) + { + printed = snprintf(buf, + buf_size, + "pagesize: %" PRIi32 ", lockbits: %i 0x%4.4" PRIx32 ", pages in lock region: %i \n", + stellaris_info->pagesize, + stellaris_info->num_lockbits, + stellaris_info->lockbits, + (int)(stellaris_info->num_pages/stellaris_info->num_lockbits)); + buf += printed; + buf_size -= printed; + } + return ERROR_OK; +} + +/*************************************************************************** +* chip identification and status * +***************************************************************************/ + +static uint32_t stellaris_get_flash_status(struct flash_bank *bank) +{ + struct target *target = bank->target; + uint32_t fmc; + + target_read_u32(target, FLASH_CONTROL_BASE | FLASH_FMC, &fmc); + + return fmc; +} + +/** Read clock configuration and set stellaris_info->usec_clocks*/ + +static const unsigned rcc_xtal[32] = { + [0x00] = 1000000, /* no pll */ + [0x01] = 1843200, /* no pll */ + [0x02] = 2000000, /* no pll */ + [0x03] = 2457600, /* no pll */ + + [0x04] = 3579545, + [0x05] = 3686400, + [0x06] = 4000000, /* usb */ + [0x07] = 4096000, + + [0x08] = 4915200, + [0x09] = 5000000, /* usb */ + [0x0a] = 5120000, + [0x0b] = 6000000, /* (reset) usb */ + + [0x0c] = 6144000, + [0x0d] = 7372800, + [0x0e] = 8000000, /* usb */ + [0x0f] = 8192000, + + /* parts before DustDevil use just 4 bits for xtal spec */ + + [0x10] = 10000000, /* usb */ + [0x11] = 12000000, /* usb */ + [0x12] = 12288000, + [0x13] = 13560000, + + [0x14] = 14318180, + [0x15] = 16000000, /* usb */ + [0x16] = 16384000, +}; + +static void stellaris_read_clock_info(struct flash_bank *bank) +{ + struct stellaris_flash_bank *stellaris_info = bank->driver_priv; + struct target *target = bank->target; + uint32_t rcc, rcc2, pllcfg, sysdiv, usesysdiv, bypass, oscsrc; + unsigned xtal; + unsigned long mainfreq; + + target_read_u32(target, SCB_BASE | RCC, &rcc); + LOG_DEBUG("Stellaris RCC %" PRIx32 "", rcc); + + target_read_u32(target, SCB_BASE | RCC2, &rcc2); + LOG_DEBUG("Stellaris RCC2 %" PRIx32 "", rcc); + + target_read_u32(target, SCB_BASE | PLLCFG, &pllcfg); + LOG_DEBUG("Stellaris PLLCFG %" PRIx32 "", pllcfg); + + stellaris_info->rcc = rcc; + stellaris_info->rcc = rcc2; + + sysdiv = (rcc >> 23) & 0xF; + usesysdiv = (rcc >> 22) & 0x1; + bypass = (rcc >> 11) & 0x1; + oscsrc = (rcc >> 4) & 0x3; + xtal = (rcc >> 6) & stellaris_info->xtal_mask; + + /* NOTE: post-Sandstorm parts have RCC2 which may override + * parts of RCC ... with more sysdiv options, option for + * 32768 Hz mainfreq, PLL controls. On Sandstorm it reads + * as zero, so the "use RCC2" flag is always clear. + */ + if (rcc2 & (1 << 31)) { + sysdiv = (rcc2 >> 23) & 0x3F; + bypass = (rcc2 >> 11) & 0x1; + oscsrc = (rcc2 >> 4) & 0x7; + + /* FIXME Tempest parts have an additional lsb for + * fractional sysdiv (200 MHz / 2.5 == 80 MHz) + */ + } + + stellaris_info->mck_desc = ""; + + switch (oscsrc) + { + case 0: /* MOSC */ + mainfreq = rcc_xtal[xtal]; + break; + case 1: /* IOSC */ + mainfreq = stellaris_info->iosc_freq; + stellaris_info->mck_desc = stellaris_info->iosc_desc; + break; + case 2: /* IOSC/4 */ + mainfreq = stellaris_info->iosc_freq / 4; + stellaris_info->mck_desc = stellaris_info->iosc_desc; + break; + case 3: /* lowspeed */ + /* Sandstorm doesn't have this 30K +/- 30% osc */ + mainfreq = 30000; + stellaris_info->mck_desc = " (±30%)"; + break; + case 8: /* hibernation osc */ + /* not all parts support hibernation */ + mainfreq = 32768; + break; + + default: /* NOTREACHED */ + mainfreq = 0; + break; + } + + /* PLL is used if it's not bypassed; its output is 200 MHz + * even when it runs at 400 MHz (adds divide-by-two stage). + */ + if (!bypass) + mainfreq = 200000000; + + 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 */ +static void stellaris_set_flash_mode(struct flash_bank *bank,int mode) +{ + struct stellaris_flash_bank *stellaris_info = bank->driver_priv; + struct target *target = bank->target; + + uint32_t usecrl = (stellaris_info->mck_freq/1000000ul-1); + LOG_DEBUG("usecrl = %i",(int)(usecrl)); + target_write_u32(target, SCB_BASE | USECRL, usecrl); +} + +#if 0 +static uint32_t stellaris_wait_status_busy(struct flash_bank *bank, uint32_t waitbits, int timeout) +{ + uint32_t status; + + /* Stellaris waits for cmdbit to clear */ + while (((status = stellaris_get_flash_status(bank)) & waitbits) && (timeout-- > 0)) + { + LOG_DEBUG("status: 0x%x", status); + alive_sleep(1); + } + + /* Flash errors are reflected in the FLASH_CRIS register */ + + return status; +} + +/* Send one command to the flash controller */ +static int stellaris_flash_command(struct flash_bank *bank,uint8_t cmd,uint16_t pagen) +{ + uint32_t fmc; + struct target *target = bank->target; + + fmc = FMC_WRKEY | cmd; + target_write_u32(target, FLASH_CONTROL_BASE | FLASH_FMC, fmc); + LOG_DEBUG("Flash command: 0x%x", fmc); + + if (stellaris_wait_status_busy(bank, cmd, 100)) + { + return ERROR_FLASH_OPERATION_FAILED; + } + + return ERROR_OK; +} +#endif + +/* Read device id register, main clock frequency register and fill in driver info structure */ +static int stellaris_read_part_info(struct flash_bank *bank) +{ + struct stellaris_flash_bank *stellaris_info = bank->driver_priv; + struct target *target = bank->target; + uint32_t 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); + LOG_DEBUG("did0 0x%" PRIx32 ", did1 0x%" PRIx32 ", dc0 0x%" PRIx32 ", dc1 0x%" PRIx32 "", + did0, did1, stellaris_info->dc0, stellaris_info->dc1); + + ver = did0 >> 28; + if ((ver != 0) && (ver != 1)) + { + LOG_WARNING("Unknown did0 version, cannot identify target"); + return ERROR_FLASH_OPERATION_FAILED; + } + + if (did1 == 0) + { + LOG_WARNING("Cannot identify target as a Stellaris"); + return ERROR_FLASH_OPERATION_FAILED; + } + + ver = did1 >> 28; + fam = (did1 >> 24) & 0xF; + if (((ver != 0) && (ver != 1)) || (fam != 0)) + { + LOG_WARNING("Unknown did1 version/family, cannot positively identify target as a Stellaris"); + } + + /* For Sandstorm, Fury, DustDevil: current data sheets say IOSC + * is 12 MHz, but some older parts have 15 MHz. A few data sheets + * even give _both_ numbers! We'll use current numbers; IOSC is + * always approximate. + * + * For Tempest: IOSC is calibrated, 16 MHz + */ + stellaris_info->iosc_freq = 12000000; + stellaris_info->iosc_desc = " (±30%)"; + stellaris_info->xtal_mask = 0x0f; + + switch ((did0 >> 28) & 0x7) { + case 0: /* Sandstorm */ + /* + * Current (2009-August) parts seem to be rev C2 and use 12 MHz. + * Parts before rev C0 used 15 MHz; some C0 parts use 15 MHz + * (LM3S618), but some other C0 parts are 12 MHz (LM3S811). + */ + if (((did0 >> 8) & 0xff) < 2) { + stellaris_info->iosc_freq = 15000000; + stellaris_info->iosc_desc = " (±50%)"; + } + break; + case 1: + switch ((did0 >> 16) & 0xff) { + case 1: /* Fury */ + break; + case 4: /* Tempest */ + stellaris_info->iosc_freq = 16000000; /* +/- 1% */ + stellaris_info->iosc_desc = " (±1%)"; + /* FALL THROUGH */ + case 3: /* DustDevil */ + stellaris_info->xtal_mask = 0x1f; + break; + default: + LOG_WARNING("Unknown did0 class"); + } + default: + break; + LOG_WARNING("Unknown did0 version"); + } + + 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); + + /* provide this for the benefit of the higher flash driver layers */ + bank->num_sectors = stellaris_info->num_pages; + bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors); + for (i = 0; i < bank->num_sectors; i++) + { + bank->sectors[i].offset = i * stellaris_info->pagesize; + bank->sectors[i].size = stellaris_info->pagesize; + bank->sectors[i].is_erased = -1; + bank->sectors[i].is_protected = -1; + } + + /* Read main and master clock freqency register */ + stellaris_read_clock_info(bank); + + status = stellaris_get_flash_status(bank); + + return ERROR_OK; +} + +/*************************************************************************** +* flash operations * +***************************************************************************/ + +static int stellaris_protect_check(struct flash_bank *bank) +{ + uint32_t status; + + struct stellaris_flash_bank *stellaris_info = bank->driver_priv; + + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (stellaris_info->did1 == 0) + { + stellaris_read_part_info(bank); + } + + if (stellaris_info->did1 == 0) + { + LOG_WARNING("Cannot identify target as Stellaris"); + return ERROR_FLASH_OPERATION_FAILED; + } + + status = stellaris_get_flash_status(bank); + stellaris_info->lockbits = status >> 16; + + return ERROR_OK; +} + +static int stellaris_erase(struct flash_bank *bank, int first, int last) +{ + int banknr; + uint32_t flash_fmc, flash_cris; + struct stellaris_flash_bank *stellaris_info = bank->driver_priv; + struct target *target = bank->target; + + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (stellaris_info->did1 == 0) + { + stellaris_read_part_info(bank); + } + + if (stellaris_info->did1 == 0) + { + LOG_WARNING("Cannot identify target as Stellaris"); + return ERROR_FLASH_OPERATION_FAILED; + } + + if ((first < 0) || (last < first) || (last >= (int)stellaris_info->num_pages)) + { + return ERROR_FLASH_SECTOR_INVALID; + } + + if ((first == 0) && (last == ((int)stellaris_info->num_pages-1))) + { + return stellaris_mass_erase(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); + + 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)) + { + LOG_WARNING("Error erasing flash page %i, flash_cris 0x%" PRIx32 "", banknr, flash_cris); + target_write_u32(target, FLASH_CRIS, 0); + return ERROR_FLASH_OPERATION_FAILED; + } + + bank->sectors[banknr].is_erased = 1; + } + + return ERROR_OK; +} + +static int stellaris_protect(struct flash_bank *bank, int set, int first, int last) +{ + uint32_t fmppe, flash_fmc, flash_cris; + int lockregion; + + struct stellaris_flash_bank *stellaris_info = bank->driver_priv; + struct target *target = bank->target; + + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not 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) + { + LOG_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); + + LOG_DEBUG("fmppe 0x%" PRIx32 "",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 */ + LOG_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)) + { + LOG_WARNING("Error setting flash page protection, flash_cris 0x%" PRIx32 "", 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; +} + +static uint8_t 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 */ + /* exit: */ + 0xFE,0xE7, /* b exit */ +/* pFLASH_CTRL_BASE: */ + 0x00,0xD0,0x0F,0x40, /* .word 0x400FD000 */ +/* FLASHWRITECMD: */ + 0x01,0x00,0x42,0xA4 /* .word 0xA4420001 */ +}; + +static int stellaris_write_block(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t wcount) +{ + struct target *target = bank->target; + uint32_t buffer_size = 8192; + struct working_area *source; + struct working_area *write_algorithm; + uint32_t address = bank->base + offset; + struct reg_param reg_params[3]; + struct armv7m_algorithm armv7m_info; + int retval = ERROR_OK; + + LOG_DEBUG("(bank=%p buffer=%p offset=%08" PRIx32 " wcount=%08" PRIx32 "", + bank, buffer, offset, wcount); + + /* flash write code */ + if (target_alloc_working_area(target, sizeof(stellaris_write_code), &write_algorithm) != ERROR_OK) + { + LOG_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) + { + LOG_DEBUG("called target_alloc_working_area(target=%p buffer_size=%08" PRIx32 " source=%p)", + target, buffer_size, 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); + + LOG_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; + + 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); + + while (wcount > 0) + { + uint32_t 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); + LOG_INFO("Algorithm flash write %" PRIi32 " words to 0x%" PRIx32 ", %" PRIi32 " remaining", thisrun_count, address, (wcount - thisrun_count)); + LOG_DEBUG("Algorithm flash write %" PRIi32 " words to 0x%" PRIx32 ", %" PRIi32 " remaining", thisrun_count, address, (wcount - thisrun_count)); + if ((retval = target_run_algorithm(target, 0, NULL, 3, reg_params, write_algorithm->address, write_algorithm->address + sizeof(stellaris_write_code)-10, 10000, &armv7m_info)) != ERROR_OK) + { + LOG_ERROR("error executing stellaris flash write algorithm"); + retval = ERROR_FLASH_OPERATION_FAILED; + break; + } + + 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]); + + return retval; +} + +static int stellaris_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) +{ + struct stellaris_flash_bank *stellaris_info = bank->driver_priv; + struct target *target = bank->target; + uint32_t address = offset; + uint32_t flash_cris, flash_fmc; + uint32_t words_remaining = (count / 4); + uint32_t bytes_remaining = (count & 0x00000003); + uint32_t bytes_written = 0; + int retval; + + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + LOG_DEBUG("(bank=%p buffer=%p offset=%08" PRIx32 " count=%08" PRIx32 "", + bank, buffer, offset, count); + + if (stellaris_info->did1 == 0) + { + stellaris_read_part_info(bank); + } + + if (stellaris_info->did1 == 0) + { + LOG_WARNING("Cannot identify target as a Stellaris processor"); + return ERROR_FLASH_OPERATION_FAILED; + } + + if (offset & 0x3) + { + LOG_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 (words_remaining > 0) + { + /* try using a block write */ + if ((retval = stellaris_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 */ + LOG_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); + + LOG_ERROR("flash writing failed with CRIS: 0x%" PRIx32 "", flash_cris); + return ERROR_FLASH_OPERATION_FAILED; + } + } + else + { + buffer += words_remaining * 4; + address += words_remaining * 4; + words_remaining = 0; + } + } + + while (words_remaining > 0) + { + if (!(address & 0xff)) + LOG_DEBUG("0x%" PRIx32 "", 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); + /* LOG_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; + words_remaining--; + } + + if (bytes_remaining) + { + uint8_t last_word[4] = {0xff, 0xff, 0xff, 0xff}; + int i = 0; + + while (bytes_remaining > 0) + { + last_word[i++] = *(buffer + bytes_written); + bytes_remaining--; + bytes_written++; + } + + if (!(address & 0xff)) + LOG_DEBUG("0x%" PRIx32 "", address); + + /* Program one word */ + target_write_u32(target, FLASH_FMA, address); + target_write_buffer(target, FLASH_FMD, 4, last_word); + target_write_u32(target, FLASH_FMC, FMC_WRKEY | FMC_WRITE); + /* LOG_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); + } + + /* Check access violations */ + target_read_u32(target, FLASH_CRIS, &flash_cris); + if (flash_cris & (AMASK)) + { + LOG_DEBUG("flash_cris 0x%" PRIx32 "", flash_cris); + return ERROR_FLASH_OPERATION_FAILED; + } + return ERROR_OK; +} + +static int stellaris_probe(struct flash_bank *bank) +{ + /* we can't probe on an stellaris + * if this is an stellaris, it has the configured flash + */ + + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* stellaris_read_part_info() already takes care about error checking and reporting */ + return stellaris_read_part_info(bank); +} + +static int stellaris_auto_probe(struct flash_bank *bank) +{ + struct stellaris_flash_bank *stellaris_info = bank->driver_priv; + if (stellaris_info->did1) + return ERROR_OK; + return stellaris_probe(bank); +} + +static int stellaris_mass_erase(struct flash_bank *bank) +{ + struct target *target = NULL; + struct stellaris_flash_bank *stellaris_info = NULL; + uint32_t flash_fmc; + + stellaris_info = bank->driver_priv; + target = bank->target; + + if (target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (stellaris_info->did1 == 0) + { + stellaris_read_part_info(bank); + } + + if (stellaris_info->did1 == 0) + { + LOG_WARNING("Cannot identify target as Stellaris"); + return ERROR_FLASH_OPERATION_FAILED; + } + + /* 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); + + 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 + * this is only valid for older devices, but will not hurt */ + 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; +} + +COMMAND_HANDLER(stellaris_handle_mass_erase_command) +{ + int i; + + if (CMD_ARGC < 1) + { + command_print(CMD_CTX, "stellaris mass_erase "); + return ERROR_OK; + } + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + if (stellaris_mass_erase(bank) == ERROR_OK) + { + /* set all sectors as erased */ + for (i = 0; i < bank->num_sectors; i++) + { + bank->sectors[i].is_erased = 1; + } + + command_print(CMD_CTX, "stellaris mass erase complete"); + } + else + { + command_print(CMD_CTX, "stellaris mass erase failed"); + } + + return ERROR_OK; +} + +static const struct command_registration stellaris_exec_command_handlers[] = { + { + .name = "mass_erase", + .handler = &stellaris_handle_mass_erase_command, + .mode = COMMAND_EXEC, + .help = "erase entire device", + }, + COMMAND_REGISTRATION_DONE +}; +static const struct command_registration stellaris_command_handlers[] = { + { + .name = "stellaris", + .mode = COMMAND_ANY, + .help = "Stellaris flash command group", + .chain = stellaris_exec_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +struct flash_driver stellaris_flash = { + .name = "stellaris", + .commands = stellaris_command_handlers, + .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 = &default_flash_mem_blank_check, + .protect_check = &stellaris_protect_check, + .info = &stellaris_info, + }; diff --git a/src/flash/nor/stellaris.h b/src/flash/nor/stellaris.h new file mode 100644 index 00000000..949a346d --- /dev/null +++ b/src/flash/nor/stellaris.h @@ -0,0 +1,99 @@ +/*************************************************************************** + * 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. * + ***************************************************************************/ +#ifndef STELLARIS_FLASH_H +#define STELLARIS_FLASH_H + +#include "flash.h" + +struct stellaris_flash_bank +{ + /* chip id register */ + uint32_t did0; + uint32_t did1; + uint32_t dc0; + uint32_t dc1; + + char * target_name; + + uint32_t sramsiz; + uint32_t flshsz; + /* flash geometry */ + uint32_t num_pages; + uint32_t pagesize; + uint32_t pages_in_lockregion; + + /* nv memory bits */ + uint16_t num_lockbits; + uint32_t lockbits; + + /* main clock status */ + uint32_t rcc; + uint32_t rcc2; + uint8_t mck_valid; + uint8_t xtal_mask; + uint32_t iosc_freq; + uint32_t mck_freq; + const char *iosc_desc; + const char *mck_desc; +}; + +/* STELLARIS control registers */ +#define SCB_BASE 0x400FE000 +#define DID0 0x000 +#define DID1 0x004 +#define DC0 0x008 +#define DC1 0x010 +#define DC2 0x014 +#define DC3 0x018 +#define DC4 0x01C + +#define RIS 0x050 +#define RCC 0x060 +#define PLLCFG 0x064 +#define RCC2 0x070 + +#define FMPRE 0x130 +#define FMPPE 0x134 +#define USECRL 0x140 + +#define FLASH_CONTROL_BASE 0x400FD000 +#define FLASH_FMA (FLASH_CONTROL_BASE | 0x000) +#define FLASH_FMD (FLASH_CONTROL_BASE | 0x004) +#define FLASH_FMC (FLASH_CONTROL_BASE | 0x008) +#define FLASH_CRIS (FLASH_CONTROL_BASE | 0x00C) +#define FLASH_CIM (FLASH_CONTROL_BASE | 0x010) +#define FLASH_MISC (FLASH_CONTROL_BASE | 0x014) + +#define AMISC 1 +#define PMISC 2 + +#define AMASK 1 +#define PMASK 2 + +/* Flash Controller Command bits */ +#define FMC_WRKEY (0xA442 << 16) +#define FMC_COMT (1 << 3) +#define FMC_MERASE (1 << 2) +#define FMC_ERASE (1 << 1) +#define FMC_WRITE (1 << 0) + +/* STELLARIS constants */ + +#endif /* STELLARIS_H */ diff --git a/src/flash/nor/stm32x.c b/src/flash/nor/stm32x.c new file mode 100644 index 00000000..2f51aa55 --- /dev/null +++ b/src/flash/nor/stm32x.c @@ -0,0 +1,1240 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * Copyright (C) 2008 by Spencer Oliver * + * spen@spen-soft.co.uk * + * * + * 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 "stm32x.h" +#include "armv7m.h" +#include "binarybuffer.h" +#include "algorithm.h" + + +static int stm32x_mass_erase(struct flash_bank *bank); + +/* flash bank stm32x 0 0 + */ +FLASH_BANK_COMMAND_HANDLER(stm32x_flash_bank_command) +{ + struct stm32x_flash_bank *stm32x_info; + + if (CMD_ARGC < 6) + { + LOG_WARNING("incomplete flash_bank stm32x configuration"); + return ERROR_FLASH_BANK_INVALID; + } + + stm32x_info = malloc(sizeof(struct stm32x_flash_bank)); + bank->driver_priv = stm32x_info; + + stm32x_info->write_algorithm = NULL; + stm32x_info->probed = 0; + + return ERROR_OK; +} + +static uint32_t stm32x_get_flash_status(struct flash_bank *bank) +{ + struct target *target = bank->target; + uint32_t status; + + target_read_u32(target, STM32_FLASH_SR, &status); + + return status; +} + +static uint32_t stm32x_wait_status_busy(struct flash_bank *bank, int timeout) +{ + struct target *target = bank->target; + uint32_t status; + + /* wait for busy to clear */ + while (((status = stm32x_get_flash_status(bank)) & FLASH_BSY) && (timeout-- > 0)) + { + LOG_DEBUG("status: 0x%" PRIx32 "", status); + alive_sleep(1); + } + /* Clear but report errors */ + if (status & (FLASH_WRPRTERR | FLASH_PGERR)) + { + target_write_u32(target, STM32_FLASH_SR, FLASH_WRPRTERR | FLASH_PGERR); + } + return status; +} + +static int stm32x_read_options(struct flash_bank *bank) +{ + uint32_t optiondata; + struct stm32x_flash_bank *stm32x_info = NULL; + struct target *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 = (uint16_t)0xFFF8 | ((optiondata >> 2) & 0x07); + stm32x_info->option_bytes.RDP = (optiondata & (1 << OPT_READOUT)) ? 0xFFFF : 0x5AA5; + + if (optiondata & (1 << OPT_READOUT)) + LOG_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] = (uint16_t)optiondata; + stm32x_info->option_bytes.protection[1] = (uint16_t)(optiondata >> 8); + stm32x_info->option_bytes.protection[2] = (uint16_t)(optiondata >> 16); + stm32x_info->option_bytes.protection[3] = (uint16_t)(optiondata >> 24); + + return ERROR_OK; +} + +static int stm32x_erase_options(struct flash_bank *bank) +{ + struct stm32x_flash_bank *stm32x_info = NULL; + struct target *target = bank->target; + uint32_t 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; +} + +static int stm32x_write_options(struct flash_bank *bank) +{ + struct stm32x_flash_bank *stm32x_info = NULL; + struct target *target = bank->target; + uint32_t 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; +} + +static int stm32x_protect_check(struct flash_bank *bank) +{ + struct target *target = bank->target; + struct stm32x_flash_bank *stm32x_info = bank->driver_priv; + + uint32_t protection; + int i, s; + int num_bits; + int set; + + if (target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* medium density - each bit refers to a 4bank protection + * high density - each bit refers to a 2bank protection */ + target_read_u32(target, STM32_FLASH_WRPR, &protection); + + /* medium density - each protection bit is for 4 * 1K pages + * high density - each protection bit is for 2 * 2K pages */ + num_bits = (bank->num_sectors / stm32x_info->ppage_size); + + if (stm32x_info->ppage_size == 2) + { + /* high density flash/connectivity line protection */ + + set = 1; + + if (protection & (1 << 31)) + set = 0; + + /* bit 31 controls sector 62 - 255 protection for high density + * bit 31 controls sector 62 - 127 protection for connectivity line */ + for (s = 62; s < bank->num_sectors; s++) + { + bank->sectors[s].is_protected = set; + } + + if (bank->num_sectors > 61) + num_bits = 31; + + for (i = 0; i < num_bits; i++) + { + set = 1; + + if (protection & (1 << i)) + set = 0; + + for (s = 0; s < stm32x_info->ppage_size; s++) + bank->sectors[(i * stm32x_info->ppage_size) + s].is_protected = set; + } + } + else + { + /* low/medium density flash protection */ + for (i = 0; i < num_bits; i++) + { + set = 1; + + if (protection & (1 << i)) + set = 0; + + for (s = 0; s < stm32x_info->ppage_size; s++) + bank->sectors[(i * stm32x_info->ppage_size) + s].is_protected = set; + } + } + + return ERROR_OK; +} + +static int stm32x_erase(struct flash_bank *bank, int first, int last) +{ + struct target *target = bank->target; + int i; + uint32_t status; + + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if ((first == 0) && (last == (bank->num_sectors - 1))) + { + return stm32x_mass_erase(bank); + } + + /* 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; +} + +static int stm32x_protect(struct flash_bank *bank, int set, int first, int last) +{ + struct stm32x_flash_bank *stm32x_info = NULL; + struct target *target = bank->target; + uint16_t prot_reg[4] = {0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF}; + int i, reg, bit; + int status; + uint32_t protection; + + stm32x_info = bank->driver_priv; + + if (target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if ((first && (first % stm32x_info->ppage_size)) || ((last + 1) && (last + 1) % stm32x_info->ppage_size)) + { + LOG_WARNING("Error: start and end sectors must be on a %d sector boundary", stm32x_info->ppage_size); + return ERROR_FLASH_SECTOR_INVALID; + } + + /* medium density - each bit refers to a 4bank protection + * high density - each bit refers to a 2bank protection */ + target_read_u32(target, STM32_FLASH_WRPR, &protection); + + prot_reg[0] = (uint16_t)protection; + prot_reg[1] = (uint16_t)(protection >> 8); + prot_reg[2] = (uint16_t)(protection >> 16); + prot_reg[3] = (uint16_t)(protection >> 24); + + if (stm32x_info->ppage_size == 2) + { + /* high density flash */ + + /* bit 7 controls sector 62 - 255 protection */ + if (last > 61) + { + if (set) + prot_reg[3] &= ~(1 << 7); + else + prot_reg[3] |= (1 << 7); + } + + if (first > 61) + first = 62; + if (last > 61) + last = 61; + + for (i = first; i <= last; i++) + { + reg = (i / stm32x_info->ppage_size) / 8; + bit = (i / stm32x_info->ppage_size) - (reg * 8); + + if (set) + prot_reg[reg] &= ~(1 << bit); + else + prot_reg[reg] |= (1 << bit); + } + } + else + { + /* medium density flash */ + for (i = first; i <= last; i++) + { + reg = (i / stm32x_info->ppage_size) / 8; + bit = (i / stm32x_info->ppage_size) - (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); +} + +static int stm32x_write_block(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) +{ + struct stm32x_flash_bank *stm32x_info = bank->driver_priv; + struct target *target = bank->target; + uint32_t buffer_size = 16384; + struct working_area *source; + uint32_t address = bank->base + offset; + struct reg_param reg_params[4]; + struct armv7m_algorithm armv7m_info; + int retval = ERROR_OK; + + uint8_t 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) + { + LOG_WARNING("no working area available, can't do block memory writes"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + }; + + if ((retval = target_write_buffer(target, stm32x_info->write_algorithm->address, sizeof(stm32x_flash_write_code), stm32x_flash_write_code)) != ERROR_OK) + return retval; + + /* 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); + + LOG_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; + + 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) + { + uint32_t thisrun_count = (count > (buffer_size / 2)) ? (buffer_size / 2) : count; + + if ((retval = target_write_buffer(target, source->address, thisrun_count * 2, buffer)) != ERROR_OK) + break; + + 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_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) + { + LOG_ERROR("error executing stm32x flash write algorithm"); + retval = ERROR_FLASH_OPERATION_FAILED; + break; + } + + if (buf_get_u32(reg_params[3].value, 0, 32) & FLASH_PGERR) + { + LOG_ERROR("flash memory not erased before writing"); + /* Clear but report errors */ + target_write_u32(target, STM32_FLASH_SR, FLASH_PGERR); + retval = ERROR_FLASH_OPERATION_FAILED; + break; + } + + if (buf_get_u32(reg_params[3].value, 0, 32) & FLASH_WRPRTERR) + { + LOG_ERROR("flash memory write protected"); + /* Clear but report errors */ + target_write_u32(target, STM32_FLASH_SR, FLASH_WRPRTERR); + 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; +} + +static int stm32x_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) +{ + struct target *target = bank->target; + uint32_t words_remaining = (count / 2); + uint32_t bytes_remaining = (count & 0x00000001); + uint32_t address = bank->base + offset; + uint32_t bytes_written = 0; + uint8_t status; + int retval; + + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (offset & 0x1) + { + LOG_WARNING("offset 0x%" PRIx32 " 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 */ + LOG_WARNING("couldn't use block writes, falling back to single memory accesses"); + } + else if (retval == ERROR_FLASH_OPERATION_FAILED) + { + LOG_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) + { + uint16_t value; + memcpy(&value, buffer + bytes_written, sizeof(uint16_t)); + + target_write_u32(target, STM32_FLASH_CR, FLASH_PG); + target_write_u16(target, address, value); + + status = stm32x_wait_status_busy(bank, 5); + + if (status & FLASH_WRPRTERR) + { + LOG_ERROR("flash memory not erased before writing"); + return ERROR_FLASH_OPERATION_FAILED; + } + if (status & FLASH_PGERR) + { + LOG_ERROR("flash memory write protected"); + return ERROR_FLASH_OPERATION_FAILED; + } + + bytes_written += 2; + words_remaining--; + address += 2; + } + + if (bytes_remaining) + { + uint16_t value = 0xffff; + memcpy(&value, buffer + bytes_written, bytes_remaining); + + target_write_u32(target, STM32_FLASH_CR, FLASH_PG); + target_write_u16(target, address, value); + + status = stm32x_wait_status_busy(bank, 5); + + if (status & FLASH_WRPRTERR) + { + LOG_ERROR("flash memory not erased before writing"); + return ERROR_FLASH_OPERATION_FAILED; + } + if (status & FLASH_PGERR) + { + LOG_ERROR("flash memory write protected"); + return ERROR_FLASH_OPERATION_FAILED; + } + } + + target_write_u32(target, STM32_FLASH_CR, FLASH_LOCK); + + return ERROR_OK; +} + +static int stm32x_probe(struct flash_bank *bank) +{ + struct target *target = bank->target; + struct stm32x_flash_bank *stm32x_info = bank->driver_priv; + int i; + uint16_t num_pages; + uint32_t device_id; + int page_size; + + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + stm32x_info->probed = 0; + + /* read stm32 device id register */ + target_read_u32(target, 0xE0042000, &device_id); + LOG_INFO("device id = 0x%08" PRIx32 "", device_id); + + /* get flash size from target */ + if (target_read_u16(target, 0x1FFFF7E0, &num_pages) != ERROR_OK) + { + /* failed reading flash size, default to max target family */ + num_pages = 0xffff; + } + + if ((device_id & 0x7ff) == 0x410) + { + /* medium density - we have 1k pages + * 4 pages for a protection area */ + page_size = 1024; + stm32x_info->ppage_size = 4; + + /* check for early silicon */ + if (num_pages == 0xffff) + { + /* number of sectors incorrect on revA */ + LOG_WARNING("STM32 flash size failed, probe inaccurate - assuming 128k flash"); + num_pages = 128; + } + } + else if ((device_id & 0x7ff) == 0x412) + { + /* low density - we have 1k pages + * 4 pages for a protection area */ + page_size = 1024; + stm32x_info->ppage_size = 4; + + /* check for early silicon */ + if (num_pages == 0xffff) + { + /* number of sectors incorrect on revA */ + LOG_WARNING("STM32 flash size failed, probe inaccurate - assuming 32k flash"); + num_pages = 32; + } + } + else if ((device_id & 0x7ff) == 0x414) + { + /* high density - we have 2k pages + * 2 pages for a protection area */ + page_size = 2048; + stm32x_info->ppage_size = 2; + + /* check for early silicon */ + if (num_pages == 0xffff) + { + /* number of sectors incorrect on revZ */ + LOG_WARNING("STM32 flash size failed, probe inaccurate - assuming 512k flash"); + num_pages = 512; + } + } + else if ((device_id & 0x7ff) == 0x418) + { + /* connectivity line density - we have 2k pages + * 2 pages for a protection area */ + page_size = 2048; + stm32x_info->ppage_size = 2; + + /* check for early silicon */ + if (num_pages == 0xffff) + { + /* number of sectors incorrect on revZ */ + LOG_WARNING("STM32 flash size failed, probe inaccurate - assuming 256k flash"); + num_pages = 256; + } + } + else + { + LOG_WARNING("Cannot identify target as a STM32 family."); + return ERROR_FLASH_OPERATION_FAILED; + } + + LOG_INFO("flash size = %dkbytes", num_pages); + + /* calculate numbers of pages */ + num_pages /= (page_size / 1024); + + bank->base = 0x08000000; + bank->size = (num_pages * page_size); + bank->num_sectors = num_pages; + bank->sectors = malloc(sizeof(struct flash_sector) * num_pages); + + for (i = 0; i < num_pages; i++) + { + bank->sectors[i].offset = i * page_size; + bank->sectors[i].size = page_size; + bank->sectors[i].is_erased = -1; + bank->sectors[i].is_protected = 1; + } + + stm32x_info->probed = 1; + + return ERROR_OK; +} + +static int stm32x_auto_probe(struct flash_bank *bank) +{ + struct stm32x_flash_bank *stm32x_info = bank->driver_priv; + if (stm32x_info->probed) + return ERROR_OK; + return stm32x_probe(bank); +} + +#if 0 +COMMAND_HANDLER(stm32x_handle_part_id_command) +{ + return ERROR_OK; +} +#endif + +static int stm32x_info(struct flash_bank *bank, char *buf, int buf_size) +{ + struct target *target = bank->target; + uint32_t device_id; + int printed; + + /* read stm32 device id register */ + target_read_u32(target, 0xE0042000, &device_id); + + if ((device_id & 0x7ff) == 0x410) + { + printed = snprintf(buf, buf_size, "stm32x (Medium Density) - Rev: "); + buf += printed; + buf_size -= printed; + + switch (device_id >> 16) + { + case 0x0000: + snprintf(buf, buf_size, "A"); + break; + + case 0x2000: + snprintf(buf, buf_size, "B"); + break; + + case 0x2001: + snprintf(buf, buf_size, "Z"); + break; + + case 0x2003: + snprintf(buf, buf_size, "Y"); + break; + + default: + snprintf(buf, buf_size, "unknown"); + break; + } + } + else if ((device_id & 0x7ff) == 0x412) + { + printed = snprintf(buf, buf_size, "stm32x (Low Density) - Rev: "); + buf += printed; + buf_size -= printed; + + switch (device_id >> 16) + { + case 0x1000: + snprintf(buf, buf_size, "A"); + break; + + default: + snprintf(buf, buf_size, "unknown"); + break; + } + } + else if ((device_id & 0x7ff) == 0x414) + { + printed = snprintf(buf, buf_size, "stm32x (High Density) - Rev: "); + buf += printed; + buf_size -= printed; + + switch (device_id >> 16) + { + case 0x1000: + snprintf(buf, buf_size, "A"); + break; + + case 0x1001: + snprintf(buf, buf_size, "Z"); + break; + + default: + snprintf(buf, buf_size, "unknown"); + break; + } + } + else if ((device_id & 0x7ff) == 0x418) + { + printed = snprintf(buf, buf_size, "stm32x (Connectivity) - Rev: "); + buf += printed; + buf_size -= printed; + + switch (device_id >> 16) + { + case 0x1000: + snprintf(buf, buf_size, "A"); + break; + + case 0x1001: + snprintf(buf, buf_size, "Z"); + break; + + default: + snprintf(buf, buf_size, "unknown"); + break; + } + } + else + { + snprintf(buf, buf_size, "Cannot identify target as a stm32x\n"); + return ERROR_FLASH_OPERATION_FAILED; + } + + return ERROR_OK; +} + +COMMAND_HANDLER(stm32x_handle_lock_command) +{ + struct target *target = NULL; + struct stm32x_flash_bank *stm32x_info = NULL; + + if (CMD_ARGC < 1) + { + command_print(CMD_CTX, "stm32x lock "); + return ERROR_OK; + } + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + stm32x_info = bank->driver_priv; + + target = bank->target; + + if (target->state != TARGET_HALTED) + { + LOG_ERROR("Target not 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; +} + +COMMAND_HANDLER(stm32x_handle_unlock_command) +{ + struct target *target = NULL; + struct stm32x_flash_bank *stm32x_info = NULL; + + if (CMD_ARGC < 1) + { + command_print(CMD_CTX, "stm32x unlock "); + return ERROR_OK; + } + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + stm32x_info = bank->driver_priv; + + target = bank->target; + + if (target->state != TARGET_HALTED) + { + LOG_ERROR("Target not 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; +} + +COMMAND_HANDLER(stm32x_handle_options_read_command) +{ + uint32_t optionbyte; + struct target *target = NULL; + struct stm32x_flash_bank *stm32x_info = NULL; + + if (CMD_ARGC < 1) + { + command_print(CMD_CTX, "stm32x options_read "); + return ERROR_OK; + } + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + stm32x_info = bank->driver_priv; + + target = bank->target; + + if (target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + target_read_u32(target, STM32_FLASH_OBR, &optionbyte); + command_print(CMD_CTX, "Option Byte: 0x%" PRIx32 "", optionbyte); + + if (buf_get_u32((uint8_t*)&optionbyte, OPT_ERROR, 1)) + command_print(CMD_CTX, "Option Byte Complement Error"); + + if (buf_get_u32((uint8_t*)&optionbyte, OPT_READOUT, 1)) + command_print(CMD_CTX, "Readout Protection On"); + else + command_print(CMD_CTX, "Readout Protection Off"); + + if (buf_get_u32((uint8_t*)&optionbyte, OPT_RDWDGSW, 1)) + command_print(CMD_CTX, "Software Watchdog"); + else + command_print(CMD_CTX, "Hardware Watchdog"); + + if (buf_get_u32((uint8_t*)&optionbyte, OPT_RDRSTSTOP, 1)) + command_print(CMD_CTX, "Stop: No reset generated"); + else + command_print(CMD_CTX, "Stop: Reset generated"); + + if (buf_get_u32((uint8_t*)&optionbyte, OPT_RDRSTSTDBY, 1)) + command_print(CMD_CTX, "Standby: No reset generated"); + else + command_print(CMD_CTX, "Standby: Reset generated"); + + return ERROR_OK; +} + +COMMAND_HANDLER(stm32x_handle_options_write_command) +{ + struct target *target = NULL; + struct stm32x_flash_bank *stm32x_info = NULL; + uint16_t optionbyte = 0xF8; + + if (CMD_ARGC < 4) + { + command_print(CMD_CTX, "stm32x options_write "); + return ERROR_OK; + } + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + stm32x_info = bank->driver_priv; + + target = bank->target; + + if (target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (strcmp(CMD_ARGV[1], "SWWDG") == 0) + { + optionbyte |= (1 << 0); + } + else + { + optionbyte &= ~(1 << 0); + } + + if (strcmp(CMD_ARGV[2], "NORSTSTNDBY") == 0) + { + optionbyte |= (1 << 1); + } + else + { + optionbyte &= ~(1 << 1); + } + + if (strcmp(CMD_ARGV[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; +} + +static int stm32x_mass_erase(struct flash_bank *bank) +{ + struct target *target = bank->target; + uint32_t status; + + if (target->state != TARGET_HALTED) + { + LOG_ERROR("Target not 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) + { + LOG_ERROR("stm32x device protected"); + return ERROR_OK; + } + + if (status & FLASH_PGERR) + { + LOG_ERROR("stm32x device programming failed"); + return ERROR_OK; + } + + return ERROR_OK; +} + +COMMAND_HANDLER(stm32x_handle_mass_erase_command) +{ + int i; + + if (CMD_ARGC < 1) + { + command_print(CMD_CTX, "stm32x mass_erase "); + return ERROR_OK; + } + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + if (stm32x_mass_erase(bank) == ERROR_OK) + { + /* set all sectors as erased */ + for (i = 0; i < bank->num_sectors; i++) + { + bank->sectors[i].is_erased = 1; + } + + command_print(CMD_CTX, "stm32x mass erase complete"); + } + else + { + command_print(CMD_CTX, "stm32x mass erase failed"); + } + + return ERROR_OK; +} + +static const struct command_registration stm32x_exec_command_handlers[] = { + { + .name = "lock", + .handler = &stm32x_handle_lock_command, + .mode = COMMAND_EXEC, + .help = "lock device", + }, + { + .name = "unlock", + .handler = &stm32x_handle_unlock_command, + .mode = COMMAND_EXEC, + .help = "unlock protected device", + }, + { + .name = "mass_erase", + .handler = &stm32x_handle_mass_erase_command, + .mode = COMMAND_EXEC, + .help = "mass erase device", + }, + { + .name = "options_read", + .handler = &stm32x_handle_options_read_command, + .mode = COMMAND_EXEC, + .help = "read device option bytes", + }, + { + .name = "options_write", + .handler = &stm32x_handle_options_write_command, + .mode = COMMAND_EXEC, + .help = "write device option bytes", + }, + COMMAND_REGISTRATION_DONE +}; +static const struct command_registration stm32x_command_handlers[] = { + { + .name = "stm32x", + .mode = COMMAND_ANY, + .help = "stm32x flash command group", + .chain = stm32x_exec_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +struct flash_driver stm32x_flash = { + .name = "stm32x", + .commands = stm32x_command_handlers, + .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 = &default_flash_mem_blank_check, + .protect_check = &stm32x_protect_check, + .info = &stm32x_info, + }; diff --git a/src/flash/nor/stm32x.h b/src/flash/nor/stm32x.h new file mode 100644 index 00000000..6cd047e1 --- /dev/null +++ b/src/flash/nor/stm32x.h @@ -0,0 +1,101 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * Copyright (C) 2008 by Spencer Oliver * + * spen@spen-soft.co.uk * + * * + * 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 STM32X_H +#define STM32X_H + +#include "flash.h" + +struct stm32x_options +{ + uint16_t RDP; + uint16_t user_options; + uint16_t protection[4]; +}; + +struct stm32x_flash_bank +{ + struct stm32x_options option_bytes; + struct working_area *write_algorithm; + int ppage_size; + int probed; +}; + +/* stm32x register locations */ + +#define STM32_FLASH_ACR 0x40022000 +#define STM32_FLASH_KEYR 0x40022004 +#define STM32_FLASH_OPTKEYR 0x40022008 +#define STM32_FLASH_SR 0x4002200C +#define STM32_FLASH_CR 0x40022010 +#define STM32_FLASH_AR 0x40022014 +#define STM32_FLASH_OBR 0x4002201C +#define STM32_FLASH_WRPR 0x40022020 + +/* option byte location */ + +#define STM32_OB_RDP 0x1FFFF800 +#define STM32_OB_USER 0x1FFFF802 +#define STM32_OB_DATA0 0x1FFFF804 +#define STM32_OB_DATA1 0x1FFFF806 +#define STM32_OB_WRP0 0x1FFFF808 +#define STM32_OB_WRP1 0x1FFFF80A +#define STM32_OB_WRP2 0x1FFFF80C +#define STM32_OB_WRP3 0x1FFFF80E + +/* FLASH_CR register bits */ + +#define FLASH_PG (1 << 0) +#define FLASH_PER (1 << 1) +#define FLASH_MER (1 << 2) +#define FLASH_OPTPG (1 << 4) +#define FLASH_OPTER (1 << 5) +#define FLASH_STRT (1 << 6) +#define FLASH_LOCK (1 << 7) +#define FLASH_OPTWRE (1 << 9) + +/* FLASH_SR register bits */ + +#define FLASH_BSY (1 << 0) +#define FLASH_PGERR (1 << 2) +#define FLASH_WRPRTERR (1 << 4) +#define FLASH_EOP (1 << 5) + +/* STM32_FLASH_OBR bit definitions (reading) */ + +#define OPT_ERROR 0 +#define OPT_READOUT 1 +#define OPT_RDWDGSW 2 +#define OPT_RDRSTSTOP 3 +#define OPT_RDRSTSTDBY 4 + +/* register unlock keys */ + +#define KEY1 0x45670123 +#define KEY2 0xCDEF89AB + +struct stm32x_mem_layout { + uint32_t sector_start; + uint32_t sector_size; +}; + +#endif /* STM32X_H */ diff --git a/src/flash/nor/str7x.c b/src/flash/nor/str7x.c new file mode 100644 index 00000000..7edffac9 --- /dev/null +++ b/src/flash/nor/str7x.c @@ -0,0 +1,706 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * Copyright (C) 2008 by Spencer Oliver * + * spen@spen-soft.co.uk * + * * + * 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 "str7x.h" +#include "armv4_5.h" +#include "binarybuffer.h" +#include "algorithm.h" + + +struct str7x_mem_layout mem_layout_str7bank0[] = { + {0x00000000, 0x02000, 0x01}, + {0x00002000, 0x02000, 0x02}, + {0x00004000, 0x02000, 0x04}, + {0x00006000, 0x02000, 0x08}, + {0x00008000, 0x08000, 0x10}, + {0x00010000, 0x10000, 0x20}, + {0x00020000, 0x10000, 0x40}, + {0x00030000, 0x10000, 0x80} +}; + +struct str7x_mem_layout mem_layout_str7bank1[] = { + {0x00000000, 0x02000, 0x10000}, + {0x00002000, 0x02000, 0x20000} +}; + +static int str7x_get_flash_adr(struct flash_bank *bank, uint32_t reg) +{ + struct str7x_flash_bank *str7x_info = bank->driver_priv; + return (str7x_info->register_base | reg); +} + +static int str7x_build_block_list(struct flash_bank *bank) +{ + struct str7x_flash_bank *str7x_info = bank->driver_priv; + + int i; + int num_sectors; + int b0_sectors = 0, b1_sectors = 0; + + switch (bank->size) + { + case 16 * 1024: + b1_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: + LOG_ERROR("BUG: unknown bank->size encountered"); + exit(-1); + } + + num_sectors = b0_sectors + b1_sectors; + + bank->num_sectors = num_sectors; + bank->sectors = malloc(sizeof(struct flash_sector) * num_sectors); + str7x_info->sector_bits = malloc(sizeof(uint32_t) * num_sectors); + + num_sectors = 0; + + for (i = 0; i < b0_sectors; i++) + { + bank->sectors[num_sectors].offset = mem_layout_str7bank0[i].sector_start; + bank->sectors[num_sectors].size = mem_layout_str7bank0[i].sector_size; + bank->sectors[num_sectors].is_erased = -1; + bank->sectors[num_sectors].is_protected = 1; + str7x_info->sector_bits[num_sectors++] = mem_layout_str7bank0[i].sector_bit; + } + + for (i = 0; i < b1_sectors; i++) + { + bank->sectors[num_sectors].offset = mem_layout_str7bank1[i].sector_start; + bank->sectors[num_sectors].size = mem_layout_str7bank1[i].sector_size; + bank->sectors[num_sectors].is_erased = -1; + bank->sectors[num_sectors].is_protected = 1; + str7x_info->sector_bits[num_sectors++] = mem_layout_str7bank1[i].sector_bit; + } + + return ERROR_OK; +} + +/* flash bank str7x 0 0 + */ +FLASH_BANK_COMMAND_HANDLER(str7x_flash_bank_command) +{ + struct str7x_flash_bank *str7x_info; + + if (CMD_ARGC < 7) + { + LOG_WARNING("incomplete flash_bank str7x configuration"); + return ERROR_FLASH_BANK_INVALID; + } + + str7x_info = malloc(sizeof(struct str7x_flash_bank)); + bank->driver_priv = str7x_info; + + /* set default bits for str71x flash */ + str7x_info->busy_bits = (FLASH_LOCK | FLASH_BSYA1 | FLASH_BSYA0); + str7x_info->disable_bit = (1 << 1); + + if (strcmp(CMD_ARGV[6], "STR71x") == 0) + { + str7x_info->register_base = 0x40100000; + } + else if (strcmp(CMD_ARGV[6], "STR73x") == 0) + { + str7x_info->register_base = 0x80100000; + str7x_info->busy_bits = (FLASH_LOCK | FLASH_BSYA0); + } + else if (strcmp(CMD_ARGV[6], "STR75x") == 0) + { + str7x_info->register_base = 0x20100000; + str7x_info->disable_bit = (1 << 0); + } + else + { + LOG_ERROR("unknown STR7x variant: '%s'", CMD_ARGV[6]); + free(str7x_info); + return ERROR_FLASH_BANK_INVALID; + } + + str7x_build_block_list(bank); + + str7x_info->write_algorithm = NULL; + + return ERROR_OK; +} + +static uint32_t str7x_status(struct flash_bank *bank) +{ + struct target *target = bank->target; + uint32_t retval; + + target_read_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), &retval); + + return retval; +} + +static uint32_t str7x_result(struct flash_bank *bank) +{ + struct target *target = bank->target; + uint32_t retval; + + target_read_u32(target, str7x_get_flash_adr(bank, FLASH_ER), &retval); + + return retval; +} + +static int str7x_protect_check(struct flash_bank *bank) +{ + struct str7x_flash_bank *str7x_info = bank->driver_priv; + struct target *target = bank->target; + + int i; + uint32_t retval; + + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not 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; +} + +static int str7x_erase(struct flash_bank *bank, int first, int last) +{ + struct str7x_flash_bank *str7x_info = bank->driver_priv; + struct target *target = bank->target; + + int i; + uint32_t cmd; + uint32_t retval; + uint32_t sectors = 0; + + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + for (i = first; i <= last; i++) + { + sectors |= str7x_info->sector_bits[i]; + } + + LOG_DEBUG("sectors: 0x%" PRIx32 "", 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 = 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)) { + alive_sleep(1); + } + + retval = str7x_result(bank); + + if (retval) + { + LOG_ERROR("error erasing flash bank, FLASH_ER: 0x%" PRIx32 "", retval); + return ERROR_FLASH_OPERATION_FAILED; + } + + for (i = first; i <= last; i++) + bank->sectors[i].is_erased = 1; + + return ERROR_OK; +} + +static int str7x_protect(struct flash_bank *bank, int set, int first, int last) +{ + struct str7x_flash_bank *str7x_info = bank->driver_priv; + struct target *target = bank->target; + int i; + uint32_t cmd; + uint32_t retval; + uint32_t protect_blocks; + + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not 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)) { + alive_sleep(1); + } + + retval = str7x_result(bank); + + LOG_DEBUG("retval: 0x%8.8" PRIx32 "", retval); + + if (retval & FLASH_ERER) + return ERROR_FLASH_SECTOR_NOT_ERASED; + else if (retval & FLASH_WPF) + return ERROR_FLASH_OPERATION_FAILED; + + return ERROR_OK; +} + +static int str7x_write_block(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) +{ + struct str7x_flash_bank *str7x_info = bank->driver_priv; + struct target *target = bank->target; + uint32_t buffer_size = 8192; + struct working_area *source; + uint32_t address = bank->base + offset; + struct reg_param reg_params[6]; + struct armv4_5_algorithm armv4_5_info; + int retval = ERROR_OK; + + uint32_t 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) + { + LOG_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, (uint8_t*)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); + + LOG_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) + { + uint32_t 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_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) + { + LOG_ERROR("error executing str7x flash write algorithm"); + retval = ERROR_FLASH_OPERATION_FAILED; + 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; +} + +static int str7x_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) +{ + struct target *target = bank->target; + struct str7x_flash_bank *str7x_info = bank->driver_priv; + uint32_t dwords_remaining = (count / 8); + uint32_t bytes_remaining = (count & 0x00000007); + uint32_t address = bank->base + offset; + uint32_t bytes_written = 0; + uint32_t cmd; + int retval; + uint32_t check_address = offset; + int i; + + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (offset & 0x7) + { + LOG_WARNING("offset 0x%" PRIx32 " breaks required 8-byte alignment", offset); + return ERROR_FLASH_DST_BREAKS_ALIGNMENT; + } + + for (i = 0; i < bank->num_sectors; i++) + { + uint32_t sec_start = bank->sectors[i].offset; + uint32_t 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 */ + LOG_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); + + LOG_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_write_memory(target, str7x_get_flash_adr(bank, FLASH_DR0), 4, 1, buffer + bytes_written); + bytes_written += 4; + + /* data word 2 */ + target_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)) + { + alive_sleep(1); + } + + 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) + { + uint8_t 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_write_memory(target, str7x_get_flash_adr(bank, FLASH_DR0), 4, 1, last_dword); + bytes_written += 4; + + /* data word 2 */ + target_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)) + { + alive_sleep(1); + } + + 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; +} + +static int str7x_probe(struct flash_bank *bank) +{ + return ERROR_OK; +} + +#if 0 +COMMAND_HANDLER(str7x_handle_part_id_command) +{ + return ERROR_OK; +} +#endif + +static int str7x_info(struct flash_bank *bank, char *buf, int buf_size) +{ + snprintf(buf, buf_size, "str7x flash driver info"); + return ERROR_OK; +} + +COMMAND_HANDLER(str7x_handle_disable_jtag_command) +{ + struct target *target = NULL; + struct str7x_flash_bank *str7x_info = NULL; + + uint32_t flash_cmd; + uint16_t ProtectionLevel = 0; + uint16_t ProtectionRegs; + + if (CMD_ARGC < 1) + { + command_print(CMD_CTX, "str7x disable_jtag "); + return ERROR_OK; + } + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + str7x_info = bank->driver_priv; + + target = bank->target; + + if (target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* first we get protection status */ + uint32_t reg; + target_read_u32(target, str7x_get_flash_adr(bank, FLASH_NVAPR0), ®); + + if (!(reg & str7x_info->disable_bit)) + { + ProtectionLevel = 1; + } + + target_read_u32(target, str7x_get_flash_adr(bank, FLASH_NVAPR1), ®); + ProtectionRegs = ~(reg >> 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; +} + +static const struct command_registration str7x_exec_command_handlers[] = { + { + .name = "disable_jtag", + .handler = &str7x_handle_disable_jtag_command, + .mode = COMMAND_EXEC, + .help = "disable jtag access", + }, + COMMAND_REGISTRATION_DONE +}; +static const struct command_registration str7x_command_handlers[] = { + { + .name = "str7x", + .mode = COMMAND_ANY, + .help = "str7x flash command group", + .chain = str7x_exec_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +struct flash_driver str7x_flash = { + .name = "str7x", + .commands = str7x_command_handlers, + .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 = &default_flash_blank_check, + .protect_check = &str7x_protect_check, + .info = &str7x_info, + }; diff --git a/src/flash/nor/str7x.h b/src/flash/nor/str7x.h new file mode 100644 index 00000000..81af0f1e --- /dev/null +++ b/src/flash/nor/str7x.h @@ -0,0 +1,110 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * Copyright (C) 2008 by Spencer Oliver * + * spen@spen-soft.co.uk * + * * + * 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 STR7X_H +#define STR7X_H + +#include "flash.h" + +struct str7x_flash_bank +{ + uint32_t *sector_bits; + uint32_t disable_bit; + uint32_t busy_bits; + uint32_t register_base; + struct working_area *write_algorithm; +}; + +enum str7x_status_codes +{ + STR7X_CMD_SUCCESS = 0, + STR7X_INVALID_COMMAND = 1, + STR7X_SRC_ADDR_ERROR = 2, + STR7X_DST_ADDR_ERROR = 3, + STR7X_SRC_ADDR_NOT_MAPPED = 4, + STR7X_DST_ADDR_NOT_MAPPED = 5, + STR7X_COUNT_ERROR = 6, + STR7X_INVALID_SECTOR = 7, + STR7X_SECTOR_NOT_BLANK = 8, + STR7X_SECTOR_NOT_PREPARED = 9, + STR7X_COMPARE_ERROR = 10, + STR7X_BUSY = 11 +}; + +/* Flash registers */ + +#define FLASH_CR0 0x00000000 +#define FLASH_CR1 0x00000004 +#define FLASH_DR0 0x00000008 +#define FLASH_DR1 0x0000000C +#define FLASH_AR 0x00000010 +#define FLASH_ER 0x00000014 +#define FLASH_NVWPAR 0x0000DFB0 +#define FLASH_NVAPR0 0x0000DFB8 +#define FLASH_NVAPR1 0x0000DFBC + +/* FLASH_CR0 register bits */ + +#define FLASH_WMS 0x80000000 +#define FLASH_SUSP 0x40000000 +#define FLASH_WPG 0x20000000 +#define FLASH_DWPG 0x10000000 +#define FLASH_SER 0x08000000 +#define FLASH_SPR 0x01000000 +#define FLASH_BER 0x04000000 +#define FLASH_MER 0x02000000 +#define FLASH_LOCK 0x00000010 +#define FLASH_BSYA1 0x00000004 +#define FLASH_BSYA0 0x00000002 + +/* FLASH_CR1 register bits */ + +#define FLASH_B1S 0x02000000 +#define FLASH_B0S 0x01000000 +#define FLASH_B1F1 0x00020000 +#define FLASH_B1F0 0x00010000 +#define FLASH_B0F7 0x00000080 +#define FLASH_B0F6 0x00000040 +#define FLASH_B0F5 0x00000020 +#define FLASH_B0F4 0x00000010 +#define FLASH_B0F3 0x00000008 +#define FLASH_B0F2 0x00000004 +#define FLASH_B0F1 0x00000002 +#define FLASH_B0F0 0x00000001 + +/* FLASH_ER register bits */ + +#define FLASH_WPF 0x00000100 +#define FLASH_RESER 0x00000080 +#define FLASH_SEQER 0x00000040 +#define FLASH_10ER 0x00000008 +#define FLASH_PGER 0x00000004 +#define FLASH_ERER 0x00000002 +#define FLASH_ERR 0x00000001 + +struct str7x_mem_layout { + uint32_t sector_start; + uint32_t sector_size; + uint32_t sector_bit; +}; + +#endif /* STR7X_H */ diff --git a/src/flash/nor/str9x.c b/src/flash/nor/str9x.c new file mode 100644 index 00000000..98f15e75 --- /dev/null +++ b/src/flash/nor/str9x.c @@ -0,0 +1,711 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * Copyright (C) 2008 by Spencer Oliver * + * spen@spen-soft.co.uk * + * + * Copyright (C) 2008 by Oyvind Harboe * + * oyvind.harboe@zylin.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "str9x.h" +#include "arm966e.h" +#include "algorithm.h" + + +static uint32_t bank1start = 0x00080000; + +static int str9x_build_block_list(struct flash_bank *bank) +{ + struct str9x_flash_bank *str9x_info = bank->driver_priv; + + int i; + int num_sectors; + int b0_sectors = 0, b1_sectors = 0; + uint32_t offset = 0; + + /* set if we have large flash str9 */ + str9x_info->variant = 0; + str9x_info->bank1 = 0; + + switch (bank->size) + { + case (256 * 1024): + b0_sectors = 4; + break; + case (512 * 1024): + b0_sectors = 8; + break; + case (1024 * 1024): + bank1start = 0x00100000; + str9x_info->variant = 1; + b0_sectors = 16; + break; + case (2048 * 1024): + bank1start = 0x00200000; + str9x_info->variant = 1; + b0_sectors = 32; + break; + case (128 * 1024): + str9x_info->variant = 1; + str9x_info->bank1 = 1; + b1_sectors = 8; + bank1start = bank->base; + break; + case (32 * 1024): + str9x_info->bank1 = 1; + b1_sectors = 4; + bank1start = bank->base; + break; + default: + LOG_ERROR("BUG: unknown bank->size encountered"); + exit(-1); + } + + num_sectors = b0_sectors + b1_sectors; + + bank->num_sectors = num_sectors; + bank->sectors = malloc(sizeof(struct flash_sector) * num_sectors); + str9x_info->sector_bits = malloc(sizeof(uint32_t) * num_sectors); + + num_sectors = 0; + + for (i = 0; i < b0_sectors; i++) + { + bank->sectors[num_sectors].offset = offset; + bank->sectors[num_sectors].size = 0x10000; + offset += bank->sectors[i].size; + bank->sectors[num_sectors].is_erased = -1; + bank->sectors[num_sectors].is_protected = 1; + str9x_info->sector_bits[num_sectors++] = (1 << i); + } + + for (i = 0; i < b1_sectors; i++) + { + bank->sectors[num_sectors].offset = offset; + bank->sectors[num_sectors].size = str9x_info->variant == 0 ? 0x2000 : 0x4000; + offset += bank->sectors[i].size; + bank->sectors[num_sectors].is_erased = -1; + bank->sectors[num_sectors].is_protected = 1; + if (str9x_info->variant) + str9x_info->sector_bits[num_sectors++] = (1 << i); + else + str9x_info->sector_bits[num_sectors++] = (1 << (i + 8)); + } + + return ERROR_OK; +} + +/* flash bank str9x 0 0 + */ +FLASH_BANK_COMMAND_HANDLER(str9x_flash_bank_command) +{ + struct str9x_flash_bank *str9x_info; + + if (CMD_ARGC < 6) + { + LOG_WARNING("incomplete flash_bank str9x configuration"); + return ERROR_FLASH_BANK_INVALID; + } + + str9x_info = malloc(sizeof(struct str9x_flash_bank)); + bank->driver_priv = str9x_info; + + str9x_build_block_list(bank); + + str9x_info->write_algorithm = NULL; + + return ERROR_OK; +} + +static int str9x_protect_check(struct flash_bank *bank) +{ + int retval; + struct str9x_flash_bank *str9x_info = bank->driver_priv; + struct target *target = bank->target; + + int i; + uint32_t adr; + uint32_t status = 0; + uint16_t hstatus = 0; + + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* read level one protection */ + + if (str9x_info->variant) + { + if (str9x_info->bank1) + { + adr = bank1start + 0x18; + if ((retval = target_write_u16(target, adr, 0x90)) != ERROR_OK) + { + return retval; + } + if ((retval = target_read_u16(target, adr, &hstatus)) != ERROR_OK) + { + return retval; + } + status = hstatus; + } + else + { + adr = bank1start + 0x14; + if ((retval = target_write_u16(target, adr, 0x90)) != ERROR_OK) + { + return retval; + } + if ((retval = target_read_u32(target, adr, &status)) != ERROR_OK) + { + return retval; + } + } + } + else + { + adr = bank1start + 0x10; + if ((retval = target_write_u16(target, adr, 0x90)) != ERROR_OK) + { + return retval; + } + if ((retval = target_read_u16(target, adr, &hstatus)) != ERROR_OK) + { + return retval; + } + status = hstatus; + } + + /* read array command */ + if ((retval = target_write_u16(target, adr, 0xFF)) != ERROR_OK) + { + return retval; + } + + 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; +} + +static int str9x_erase(struct flash_bank *bank, int first, int last) +{ + struct target *target = bank->target; + int i; + uint32_t adr; + uint8_t status; + uint8_t erase_cmd; + + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* Check if we erase whole bank */ + if ((first == 0) && (last == (bank->num_sectors - 1))) + { + /* Optimize to run erase bank command instead of sector */ + erase_cmd = 0x80; + } + else + { + /* Erase sector command */ + erase_cmd = 0x20; + } + + for (i = first; i <= last; i++) + { + int retval; + adr = bank->base + bank->sectors[i].offset; + + /* erase sectors */ + if ((retval = target_write_u16(target, adr, erase_cmd)) != ERROR_OK) + { + return retval; + } + if ((retval = target_write_u16(target, adr, 0xD0)) != ERROR_OK) + { + return retval; + } + + /* get status */ + if ((retval = target_write_u16(target, adr, 0x70)) != ERROR_OK) + { + return retval; + } + + int timeout; + for (timeout = 0; timeout < 1000; timeout++) { + if ((retval = target_read_u8(target, adr, &status)) != ERROR_OK) + { + return retval; + } + if (status & 0x80) + break; + alive_sleep(1); + } + if (timeout == 1000) + { + LOG_ERROR("erase timed out"); + return ERROR_FAIL; + } + + /* clear status, also clear read array */ + if ((retval = target_write_u16(target, adr, 0x50)) != ERROR_OK) + { + return retval; + } + + /* read array command */ + if ((retval = target_write_u16(target, adr, 0xFF)) != ERROR_OK) + { + return retval; + } + + if (status & 0x22) + { + LOG_ERROR("error erasing flash bank, status: 0x%x", status); + return ERROR_FLASH_OPERATION_FAILED; + } + + /* If we ran erase bank command, we are finished */ + if (erase_cmd == 0x80) + break; + } + + for (i = first; i <= last; i++) + bank->sectors[i].is_erased = 1; + + return ERROR_OK; +} + +static int str9x_protect(struct flash_bank *bank, + int set, int first, int last) +{ + struct target *target = bank->target; + int i; + uint32_t adr; + uint8_t status; + + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not 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); + + /* clear status, also clear read array */ + target_write_u16(target, adr, 0x50); + + /* read array command */ + target_write_u16(target, adr, 0xFF); + } + + return ERROR_OK; +} + +static int str9x_write_block(struct flash_bank *bank, + uint8_t *buffer, uint32_t offset, uint32_t count) +{ + struct str9x_flash_bank *str9x_info = bank->driver_priv; + struct target *target = bank->target; + uint32_t buffer_size = 8192; + struct working_area *source; + uint32_t address = bank->base + offset; + struct reg_param reg_params[4]; + struct armv4_5_algorithm armv4_5_info; + int retval = ERROR_OK; + + uint32_t 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) + { + LOG_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, (uint8_t*)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); + + LOG_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) + { + uint32_t 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_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) + { + LOG_ERROR("error executing str9x flash write algorithm"); + retval = ERROR_FLASH_OPERATION_FAILED; + break; + } + + if (buf_get_u32(reg_params[3].value, 0, 32) != 0x80) + { + 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, 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 retval; +} + +static int str9x_write(struct flash_bank *bank, + uint8_t *buffer, uint32_t offset, uint32_t count) +{ + struct target *target = bank->target; + uint32_t words_remaining = (count / 2); + uint32_t bytes_remaining = (count & 0x00000001); + uint32_t address = bank->base + offset; + uint32_t bytes_written = 0; + uint8_t status; + int retval; + uint32_t check_address = offset; + uint32_t bank_adr; + int i; + + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (offset & 0x1) + { + LOG_WARNING("offset 0x%" PRIx32 " breaks required 2-byte alignment", offset); + return ERROR_FLASH_DST_BREAKS_ALIGNMENT; + } + + for (i = 0; i < bank->num_sectors; i++) + { + uint32_t sec_start = bank->sectors[i].offset; + uint32_t 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 */ + LOG_WARNING("couldn't use block writes, falling back to single memory accesses"); + } + else if (retval == ERROR_FLASH_OPERATION_FAILED) + { + LOG_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_write_memory(target, address, 2, 1, buffer + bytes_written); + + /* get status command */ + target_write_u16(target, bank_adr, 0x70); + + int timeout; + for (timeout = 0; timeout < 1000; timeout++) + { + target_read_u8(target, bank_adr, &status); + if (status & 0x80) + break; + alive_sleep(1); + } + if (timeout == 1000) + { + LOG_ERROR("write timed out"); + return ERROR_FAIL; + } + + /* 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) + { + uint8_t 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 command */ + target_write_u16(target, bank_adr, 0x40); + target_write_memory(target, address, 2, 1, last_halfword); + + /* query status command */ + target_write_u16(target, bank_adr, 0x70); + + int timeout; + for (timeout = 0; timeout < 1000; timeout++) + { + target_read_u8(target, bank_adr, &status); + if (status & 0x80) + break; + alive_sleep(1); + } + if (timeout == 1000) + { + LOG_ERROR("write timed out"); + return ERROR_FAIL; + } + + /* 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; +} + +static int str9x_probe(struct flash_bank *bank) +{ + return ERROR_OK; +} + +#if 0 +COMMAND_HANDLER(str9x_handle_part_id_command) +{ + return ERROR_OK; +} +#endif + +static int str9x_info(struct flash_bank *bank, char *buf, int buf_size) +{ + snprintf(buf, buf_size, "str9x flash driver info"); + return ERROR_OK; +} + +COMMAND_HANDLER(str9x_handle_flash_config_command) +{ + struct str9x_flash_bank *str9x_info; + struct target *target = NULL; + + if (CMD_ARGC < 5) + { + return ERROR_COMMAND_SYNTAX_ERROR; + } + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + uint32_t bbsr, nbbsr, bbadr, nbbadr; + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], bbsr); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], nbbsr); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[3], bbadr); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[4], nbbadr); + + str9x_info = bank->driver_priv; + + target = bank->target; + + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* config flash controller */ + target_write_u32(target, FLASH_BBSR, bbsr); + target_write_u32(target, FLASH_NBBSR, nbbsr); + target_write_u32(target, FLASH_BBADR, bbadr >> 2); + target_write_u32(target, FLASH_NBBADR, nbbadr >> 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; +} + +static const struct command_registration str9x_config_command_handlers[] = { + { + .name = "disable_jtag", + .handler = &str9x_handle_flash_config_command, + .mode = COMMAND_EXEC, + .help = "configure str9x flash controller", + .usage = " ", + }, + COMMAND_REGISTRATION_DONE +}; +static const struct command_registration str9x_command_handlers[] = { + { + .name = "str9x", + .mode = COMMAND_ANY, + .help = "str9x flash command group", + .chain = str9x_config_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +struct flash_driver str9x_flash = { + .name = "str9x", + .commands = str9x_command_handlers, + .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 = &default_flash_blank_check, + .protect_check = &str9x_protect_check, + .info = &str9x_info, + }; diff --git a/src/flash/nor/str9x.h b/src/flash/nor/str9x.h new file mode 100644 index 00000000..c9d5152f --- /dev/null +++ b/src/flash/nor/str9x.h @@ -0,0 +1,62 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * Copyright (C) 2008 by Spencer Oliver * + * spen@spen-soft.co.uk * + * * + * 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 STR9X_H +#define STR9X_H + +#include "flash.h" + +struct str9x_flash_bank +{ + uint32_t *sector_bits; + int variant; + int bank1; + struct working_area *write_algorithm; +}; + +enum str9x_status_codes +{ + STR9X_CMD_SUCCESS = 0, + STR9X_INVALID_COMMAND = 1, + STR9X_SRC_ADDR_ERROR = 2, + STR9X_DST_ADDR_ERROR = 3, + STR9X_SRC_ADDR_NOT_MAPPED = 4, + STR9X_DST_ADDR_NOT_MAPPED = 5, + STR9X_COUNT_ERROR = 6, + STR9X_INVALID_SECTOR = 7, + STR9X_SECTOR_NOT_BLANK = 8, + STR9X_SECTOR_NOT_PREPARED = 9, + STR9X_COMPARE_ERROR = 10, + STR9X_BUSY = 11 +}; + +/* Flash registers */ + +#define FLASH_BBSR 0x54000000 /* Boot Bank Size Register */ +#define FLASH_NBBSR 0x54000004 /* Non-Boot Bank Size Register */ +#define FLASH_BBADR 0x5400000C /* Boot Bank Base Address Register */ +#define FLASH_NBBADR 0x54000010 /* Non-Boot Bank Base Address Register */ +#define FLASH_CR 0x54000018 /* Control Register */ +#define FLASH_SR 0x5400001C /* Status Register */ +#define FLASH_BCE5ADDR 0x54000020 /* BC Fifth Entry Target Address Register */ + +#endif /* STR9X_H */ diff --git a/src/flash/nor/str9xpec.c b/src/flash/nor/str9xpec.c new file mode 100644 index 00000000..96e12596 --- /dev/null +++ b/src/flash/nor/str9xpec.c @@ -0,0 +1,1257 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * Copyright (C) 2008 by Spencer Oliver * + * spen@spen-soft.co.uk * + * * + * 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 "str9xpec.h" +#include "arm7_9_common.h" + + +static int str9xpec_erase_area(struct flash_bank *bank, int first, int last); +static int str9xpec_set_address(struct flash_bank *bank, uint8_t sector); +static int str9xpec_write_options(struct flash_bank *bank); + +int str9xpec_set_instr(struct jtag_tap *tap, uint32_t new_instr, tap_state_t end_state) +{ + if (tap == NULL) { + return ERROR_TARGET_INVALID; + } + + if (buf_get_u32(tap->cur_instr, 0, tap->ir_length) != new_instr) + { + struct scan_field field; + + field.tap = tap; + field.num_bits = tap->ir_length; + field.out_value = calloc(DIV_ROUND_UP(field.num_bits, 8), 1); + buf_set_u32(field.out_value, 0, field.num_bits, new_instr); + field.in_value = NULL; + + jtag_add_ir_scan(1, &field, end_state); + + free(field.out_value); + } + + return ERROR_OK; +} + +static uint8_t str9xpec_isc_status(struct jtag_tap *tap) +{ + struct scan_field field; + uint8_t status; + + if (str9xpec_set_instr(tap, ISC_NOOP, TAP_IRPAUSE) != ERROR_OK) + return ISC_STATUS_ERROR; + + field.tap = tap; + field.num_bits = 8; + field.out_value = NULL; + field.in_value = &status; + + + jtag_add_dr_scan(1, &field, jtag_set_end_state(TAP_IDLE)); + jtag_execute_queue(); + + LOG_DEBUG("status: 0x%2.2x", status); + + if (status & ISC_STATUS_SECURITY) + LOG_INFO("Device Security Bit Set"); + + return status; +} + +static int str9xpec_isc_enable(struct flash_bank *bank) +{ + uint8_t status; + struct jtag_tap *tap; + struct str9xpec_flash_controller *str9xpec_info = bank->driver_priv; + + tap = str9xpec_info->tap; + + if (str9xpec_info->isc_enable) + return ERROR_OK; + + /* enter isc mode */ + if (str9xpec_set_instr(tap, ISC_ENABLE, TAP_IDLE) != ERROR_OK) + return ERROR_TARGET_INVALID; + + /* check ISC status */ + status = str9xpec_isc_status(tap); + if (status & ISC_STATUS_MODE) + { + /* we have entered isc mode */ + str9xpec_info->isc_enable = 1; + LOG_DEBUG("ISC_MODE Enabled"); + } + + return ERROR_OK; +} + +static int str9xpec_isc_disable(struct flash_bank *bank) +{ + uint8_t status; + struct jtag_tap *tap; + struct str9xpec_flash_controller *str9xpec_info = bank->driver_priv; + + tap = str9xpec_info->tap; + + if (!str9xpec_info->isc_enable) + return ERROR_OK; + + if (str9xpec_set_instr(tap, ISC_DISABLE, TAP_IDLE) != ERROR_OK) + return ERROR_TARGET_INVALID; + + /* delay to handle aborts */ + jtag_add_sleep(50); + + /* check ISC status */ + status = str9xpec_isc_status(tap); + if (!(status & ISC_STATUS_MODE)) + { + /* we have left isc mode */ + str9xpec_info->isc_enable = 0; + LOG_DEBUG("ISC_MODE Disabled"); + } + + return ERROR_OK; +} + +static int str9xpec_read_config(struct flash_bank *bank) +{ + struct scan_field field; + uint8_t status; + struct jtag_tap *tap; + + struct str9xpec_flash_controller *str9xpec_info = bank->driver_priv; + + tap = str9xpec_info->tap; + + LOG_DEBUG("ISC_CONFIGURATION"); + + /* execute ISC_CONFIGURATION command */ + str9xpec_set_instr(tap, ISC_CONFIGURATION, TAP_IRPAUSE); + + field.tap = tap; + field.num_bits = 64; + field.out_value = NULL; + field.in_value = str9xpec_info->options; + + + jtag_add_dr_scan(1, &field, jtag_set_end_state(TAP_IDLE)); + jtag_execute_queue(); + + status = str9xpec_isc_status(tap); + + return status; +} + +static int str9xpec_build_block_list(struct flash_bank *bank) +{ + struct str9xpec_flash_controller *str9xpec_info = bank->driver_priv; + + int i; + int num_sectors; + int b0_sectors = 0, b1_sectors = 0; + uint32_t offset = 0; + int b1_size = 0x2000; + + switch (bank->size) + { + case (256 * 1024): + b0_sectors = 4; + break; + case (512 * 1024): + b0_sectors = 8; + break; + case (1024 * 1024): + b0_sectors = 16; + break; + case (2048 * 1024): + b0_sectors = 32; + break; + case (128 * 1024): + b1_size = 0x4000; + b1_sectors = 8; + break; + case (32 * 1024): + b1_sectors = 4; + break; + default: + LOG_ERROR("BUG: unknown bank->size encountered"); + exit(-1); + } + + num_sectors = b0_sectors + b1_sectors; + + bank->num_sectors = num_sectors; + bank->sectors = malloc(sizeof(struct flash_sector) * num_sectors); + str9xpec_info->sector_bits = malloc(sizeof(uint32_t) * num_sectors); + + num_sectors = 0; + + for (i = 0; i < b0_sectors; i++) + { + bank->sectors[num_sectors].offset = offset; + bank->sectors[num_sectors].size = 0x10000; + offset += bank->sectors[i].size; + bank->sectors[num_sectors].is_erased = -1; + bank->sectors[num_sectors].is_protected = 1; + str9xpec_info->sector_bits[num_sectors++] = i; + } + + for (i = 0; i < b1_sectors; i++) + { + bank->sectors[num_sectors].offset = offset; + bank->sectors[num_sectors].size = b1_size; + offset += bank->sectors[i].size; + bank->sectors[num_sectors].is_erased = -1; + bank->sectors[num_sectors].is_protected = 1; + str9xpec_info->sector_bits[num_sectors++] = i + 32; + } + + return ERROR_OK; +} + +/* flash bank str9x 0 0 + */ +FLASH_BANK_COMMAND_HANDLER(str9xpec_flash_bank_command) +{ + struct str9xpec_flash_controller *str9xpec_info; + struct arm *armv4_5 = NULL; + struct arm7_9_common *arm7_9 = NULL; + struct arm_jtag *jtag_info = NULL; + + if (CMD_ARGC < 6) + { + LOG_WARNING("incomplete flash_bank str9x configuration"); + return ERROR_FLASH_BANK_INVALID; + } + + str9xpec_info = malloc(sizeof(struct str9xpec_flash_controller)); + bank->driver_priv = str9xpec_info; + + /* REVISIT verify that the jtag position of flash controller is + * right after *THIS* core, which must be a STR9xx core ... + */ + armv4_5 = bank->target->arch_info; + arm7_9 = armv4_5->arch_info; + jtag_info = &arm7_9->jtag_info; + + str9xpec_info->tap = bank->target->tap; + str9xpec_info->isc_enable = 0; + + str9xpec_build_block_list(bank); + + /* clear option byte register */ + buf_set_u32(str9xpec_info->options, 0, 64, 0); + + return ERROR_OK; +} + +static int str9xpec_blank_check(struct flash_bank *bank, int first, int last) +{ + struct scan_field field; + uint8_t status; + struct jtag_tap *tap; + int i; + uint8_t *buffer = NULL; + + struct str9xpec_flash_controller *str9xpec_info = bank->driver_priv; + + tap = str9xpec_info->tap; + + if (!str9xpec_info->isc_enable) { + str9xpec_isc_enable(bank); + } + + if (!str9xpec_info->isc_enable) { + return ERROR_FLASH_OPERATION_FAILED; + } + + buffer = calloc(DIV_ROUND_UP(64, 8), 1); + + LOG_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(tap, ISC_BLANK_CHECK, TAP_IRPAUSE); + + field.tap = tap; + field.num_bits = 64; + field.out_value = buffer; + field.in_value = NULL; + + jtag_add_dr_scan(1, &field, jtag_set_end_state(TAP_IDLE)); + jtag_add_sleep(40000); + + /* read blank check result */ + field.tap = tap; + field.num_bits = 64; + field.out_value = NULL; + field.in_value = buffer; + + jtag_add_dr_scan(1, &field, TAP_IRPAUSE); + jtag_execute_queue(); + + status = str9xpec_isc_status(tap); + + 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; +} + +static int str9xpec_protect_check(struct flash_bank *bank) +{ + uint8_t status; + int i; + + struct str9xpec_flash_controller *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; +} + +static int str9xpec_erase_area(struct flash_bank *bank, int first, int last) +{ + struct scan_field field; + uint8_t status; + struct jtag_tap *tap; + int i; + uint8_t *buffer = NULL; + + struct str9xpec_flash_controller *str9xpec_info = bank->driver_priv; + + tap = str9xpec_info->tap; + + if (!str9xpec_info->isc_enable) { + str9xpec_isc_enable(bank); + } + + if (!str9xpec_info->isc_enable) { + return ISC_STATUS_ERROR; + } + + buffer = calloc(DIV_ROUND_UP(64, 8), 1); + + LOG_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); + } + } + + LOG_DEBUG("ISC_ERASE"); + + /* execute ISC_ERASE command */ + str9xpec_set_instr(tap, ISC_ERASE, TAP_IRPAUSE); + + field.tap = tap; + field.num_bits = 64; + field.out_value = buffer; + field.in_value = NULL; + + jtag_add_dr_scan(1, &field, jtag_set_end_state(TAP_IDLE)); + jtag_execute_queue(); + + jtag_add_sleep(10); + + /* wait for erase completion */ + while (!((status = str9xpec_isc_status(tap)) & ISC_STATUS_BUSY)) { + alive_sleep(1); + } + + free(buffer); + + str9xpec_isc_disable(bank); + + return status; +} + +static int str9xpec_erase(struct flash_bank *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; +} + +static int str9xpec_lock_device(struct flash_bank *bank) +{ + struct scan_field field; + uint8_t status; + struct jtag_tap *tap; + struct str9xpec_flash_controller *str9xpec_info = NULL; + + str9xpec_info = bank->driver_priv; + tap = str9xpec_info->tap; + + 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(tap, ISC_PROGRAM_SECURITY, TAP_IDLE); + + str9xpec_set_instr(tap, ISC_NOOP, TAP_IRPAUSE); + + do { + field.tap = tap; + field.num_bits = 8; + field.out_value = NULL; + field.in_value = &status; + + jtag_add_dr_scan(1, &field, jtag_get_end_state()); + jtag_execute_queue(); + + } while (!(status & ISC_STATUS_BUSY)); + + str9xpec_isc_disable(bank); + + return status; +} + +static int str9xpec_unlock_device(struct flash_bank *bank) +{ + uint8_t status; + + status = str9xpec_erase_area(bank, 0, 255); + + return status; +} + +static int str9xpec_protect(struct flash_bank *bank, int set, int first, int last) +{ + uint8_t status; + int i; + + struct str9xpec_flash_controller *str9xpec_info = bank->driver_priv; + + status = str9xpec_read_config(bank); + + if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS) + return ERROR_FLASH_OPERATION_FAILED; + + LOG_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; +} + +static int str9xpec_set_address(struct flash_bank *bank, uint8_t sector) +{ + struct jtag_tap *tap; + struct scan_field field; + struct str9xpec_flash_controller *str9xpec_info = bank->driver_priv; + + tap = str9xpec_info->tap; + + /* set flash controller address */ + str9xpec_set_instr(tap, ISC_ADDRESS_SHIFT, TAP_IRPAUSE); + + field.tap = tap; + field.num_bits = 8; + field.out_value = §or; + field.in_value = NULL; + + jtag_add_dr_scan(1, &field, jtag_get_end_state()); + + return ERROR_OK; +} + +static int str9xpec_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) +{ + struct str9xpec_flash_controller *str9xpec_info = bank->driver_priv; + uint32_t dwords_remaining = (count / 8); + uint32_t bytes_remaining = (count & 0x00000007); + uint32_t bytes_written = 0; + uint8_t status; + uint32_t check_address = offset; + struct jtag_tap *tap; + struct scan_field field; + uint8_t *scanbuf; + int i; + int first_sector = 0; + int last_sector = 0; + + tap = str9xpec_info->tap; + + if (!str9xpec_info->isc_enable) { + str9xpec_isc_enable(bank); + } + + if (!str9xpec_info->isc_enable) { + return ERROR_FLASH_OPERATION_FAILED; + } + + if (offset & 0x7) + { + LOG_WARNING("offset 0x%" PRIx32 " breaks required 8-byte alignment", offset); + return ERROR_FLASH_DST_BREAKS_ALIGNMENT; + } + + for (i = 0; i < bank->num_sectors; i++) + { + uint32_t sec_start = bank->sectors[i].offset; + uint32_t 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; + + LOG_DEBUG("first_sector: %i, last_sector: %i", first_sector, last_sector); + + scanbuf = calloc(DIV_ROUND_UP(64, 8), 1); + + LOG_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(tap, ISC_PROGRAM, TAP_IRPAUSE); + + field.tap = tap; + field.num_bits = 64; + field.out_value = (buffer + bytes_written); + field.in_value = NULL; + + jtag_add_dr_scan(1, &field, jtag_set_end_state(TAP_IDLE)); + + /* small delay before polling */ + jtag_add_sleep(50); + + str9xpec_set_instr(tap, ISC_NOOP, TAP_IRPAUSE); + + do { + field.tap = tap; + field.num_bits = 8; + field.out_value = NULL; + field.in_value = scanbuf; + + jtag_add_dr_scan(1, &field, jtag_get_end_state()); + 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) + { + uint8_t 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(tap, ISC_PROGRAM, TAP_IRPAUSE); + + field.tap = tap; + field.num_bits = 64; + field.out_value = last_dword; + field.in_value = NULL; + + jtag_add_dr_scan(1, &field, jtag_set_end_state(TAP_IDLE)); + + /* small delay before polling */ + jtag_add_sleep(50); + + str9xpec_set_instr(tap, ISC_NOOP, TAP_IRPAUSE); + + do { + field.tap = tap; + field.num_bits = 8; + field.out_value = NULL; + field.in_value = scanbuf; + + jtag_add_dr_scan(1, &field, jtag_get_end_state()); + 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; +} + +static int str9xpec_probe(struct flash_bank *bank) +{ + return ERROR_OK; +} + +COMMAND_HANDLER(str9xpec_handle_part_id_command) +{ + struct scan_field field; + uint8_t *buffer = NULL; + struct jtag_tap *tap; + uint32_t idcode; + struct str9xpec_flash_controller *str9xpec_info = NULL; + + if (CMD_ARGC < 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + str9xpec_info = bank->driver_priv; + tap = str9xpec_info->tap; + + buffer = calloc(DIV_ROUND_UP(32, 8), 1); + + str9xpec_set_instr(tap, ISC_IDCODE, TAP_IRPAUSE); + + field.tap = tap; + field.num_bits = 32; + field.out_value = NULL; + field.in_value = buffer; + + jtag_add_dr_scan(1, &field, jtag_set_end_state(TAP_IDLE)); + jtag_execute_queue(); + + idcode = buf_get_u32(buffer, 0, 32); + + command_print(CMD_CTX, "str9xpec part id: 0x%8.8" PRIx32 "", idcode); + + free(buffer); + + return ERROR_OK; +} + +static int str9xpec_erase_check(struct flash_bank *bank) +{ + return str9xpec_blank_check(bank, 0, bank->num_sectors - 1); +} + +static int str9xpec_info(struct flash_bank *bank, char *buf, int buf_size) +{ + snprintf(buf, buf_size, "str9xpec flash driver info"); + return ERROR_OK; +} + +COMMAND_HANDLER(str9xpec_handle_flash_options_read_command) +{ + uint8_t status; + struct str9xpec_flash_controller *str9xpec_info = NULL; + + if (CMD_ARGC < 1) + { + command_print(CMD_CTX, "str9xpec options_read "); + return ERROR_OK; + } + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + 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; +} + +static int str9xpec_write_options(struct flash_bank *bank) +{ + struct scan_field field; + uint8_t status; + struct jtag_tap *tap; + struct str9xpec_flash_controller *str9xpec_info = NULL; + + str9xpec_info = bank->driver_priv; + tap = str9xpec_info->tap; + + /* 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(tap, ISC_PROGRAM, TAP_IRPAUSE); + + field.tap = tap; + field.num_bits = 64; + field.out_value = str9xpec_info->options; + field.in_value = NULL; + + jtag_add_dr_scan(1, &field, jtag_set_end_state(TAP_IDLE)); + + /* small delay before polling */ + jtag_add_sleep(50); + + str9xpec_set_instr(tap, ISC_NOOP, TAP_IRPAUSE); + + do { + field.tap = tap; + field.num_bits = 8; + field.out_value = NULL; + field.in_value = &status; + + jtag_add_dr_scan(1, &field, jtag_get_end_state()); + jtag_execute_queue(); + + } while (!(status & ISC_STATUS_BUSY)); + + str9xpec_isc_disable(bank); + + return status; +} + +COMMAND_HANDLER(str9xpec_handle_flash_options_write_command) +{ + uint8_t status; + + if (CMD_ARGC < 1) + { + command_print(CMD_CTX, "str9xpec options_write "); + return ERROR_OK; + } + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + status = str9xpec_write_options(bank); + + if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS) + return ERROR_FLASH_OPERATION_FAILED; + + return ERROR_OK; +} + +COMMAND_HANDLER(str9xpec_handle_flash_options_cmap_command) +{ + struct str9xpec_flash_controller *str9xpec_info = NULL; + + if (CMD_ARGC < 2) + { + command_print(CMD_CTX, "str9xpec options_cmap "); + return ERROR_OK; + } + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + str9xpec_info = bank->driver_priv; + + if (strcmp(CMD_ARGV[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; +} + +COMMAND_HANDLER(str9xpec_handle_flash_options_lvdthd_command) +{ + struct str9xpec_flash_controller *str9xpec_info = NULL; + + if (CMD_ARGC < 2) + { + command_print(CMD_CTX, "str9xpec options_lvdthd <2.4v | 2.7v>"); + return ERROR_OK; + } + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + str9xpec_info = bank->driver_priv; + + if (strcmp(CMD_ARGV[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; +} + +COMMAND_HANDLER(str9xpec_handle_flash_options_lvdsel_command) +{ + struct str9xpec_flash_controller *str9xpec_info = NULL; + + if (CMD_ARGC < 2) + { + command_print(CMD_CTX, "str9xpec options_lvdsel "); + return ERROR_OK; + } + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + str9xpec_info = bank->driver_priv; + + if (strcmp(CMD_ARGV[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; +} + +COMMAND_HANDLER(str9xpec_handle_flash_options_lvdwarn_command) +{ + struct str9xpec_flash_controller *str9xpec_info = NULL; + + if (CMD_ARGC < 2) + { + command_print(CMD_CTX, "str9xpec options_lvdwarn "); + return ERROR_OK; + } + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + str9xpec_info = bank->driver_priv; + + if (strcmp(CMD_ARGV[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; +} + +COMMAND_HANDLER(str9xpec_handle_flash_lock_command) +{ + uint8_t status; + + if (CMD_ARGC < 1) + { + command_print(CMD_CTX, "str9xpec lock "); + return ERROR_OK; + } + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + status = str9xpec_lock_device(bank); + + if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS) + return ERROR_FLASH_OPERATION_FAILED; + + return ERROR_OK; +} + +COMMAND_HANDLER(str9xpec_handle_flash_unlock_command) +{ + uint8_t status; + + if (CMD_ARGC < 1) + { + command_print(CMD_CTX, "str9xpec unlock "); + return ERROR_OK; + } + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + status = str9xpec_unlock_device(bank); + + if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS) + return ERROR_FLASH_OPERATION_FAILED; + + return ERROR_OK; +} + +COMMAND_HANDLER(str9xpec_handle_flash_enable_turbo_command) +{ + struct jtag_tap *tap0; + struct jtag_tap *tap1; + struct jtag_tap *tap2; + struct str9xpec_flash_controller *str9xpec_info = NULL; + + if (CMD_ARGC < 1) + { + command_print(CMD_CTX, "str9xpec enable_turbo "); + return ERROR_OK; + } + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + str9xpec_info = bank->driver_priv; + + tap0 = str9xpec_info->tap; + + /* remove arm core from chain - enter turbo mode */ + tap1 = tap0->next_tap; + if (tap1 == NULL) + { + /* things are *WRONG* */ + command_print(CMD_CTX,"**STR9FLASH** (tap1) invalid chain?"); + return ERROR_OK; + } + tap2 = tap1->next_tap; + if (tap2 == NULL) + { + /* things are *WRONG* */ + command_print(CMD_CTX,"**STR9FLASH** (tap2) invalid chain?"); + return ERROR_OK; + } + + /* enable turbo mode - TURBO-PROG-ENABLE */ + str9xpec_set_instr(tap2, 0xD, TAP_IDLE); + if ((retval = jtag_execute_queue()) != ERROR_OK) + return retval; + + /* modify scan chain - str9 core has been removed */ + tap1->enabled = 0; + + return ERROR_OK; +} + +COMMAND_HANDLER(str9xpec_handle_flash_disable_turbo_command) +{ + struct jtag_tap *tap; + struct str9xpec_flash_controller *str9xpec_info = NULL; + + if (CMD_ARGC < 1) + { + command_print(CMD_CTX, "str9xpec disable_turbo "); + return ERROR_OK; + } + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + str9xpec_info = bank->driver_priv; + tap = str9xpec_info->tap; + + if (tap == NULL) + return ERROR_FAIL; + + /* exit turbo mode via RESET */ + str9xpec_set_instr(tap, ISC_NOOP, TAP_IDLE); + jtag_add_tlr(); + jtag_execute_queue(); + + /* restore previous scan chain */ + if (tap->next_tap) { + tap->next_tap->enabled = 1; + } + + return ERROR_OK; +} + +static const struct command_registration str9xpec_config_command_handlers[] = { + { + .name = "enable_turbo", + .handler = str9xpec_handle_flash_enable_turbo_command, + .mode = COMMAND_EXEC, + .help = "enable str9xpec turbo mode", + }, + { + .name = "disable_turbo", + .handler = str9xpec_handle_flash_disable_turbo_command, + .mode = COMMAND_EXEC, + .help = "disable str9xpec turbo mode", + }, + { + .name = "options_cmap", + .handler = str9xpec_handle_flash_options_cmap_command, + .mode = COMMAND_EXEC, + .help = "configure str9xpec boot sector", + }, + { + .name = "options_lvdthd", + .handler = str9xpec_handle_flash_options_lvdthd_command, + .mode = COMMAND_EXEC, + .help = "configure str9xpec lvd threshold", + }, + { + .name = "options_lvdsel", + .handler = str9xpec_handle_flash_options_lvdsel_command, + .mode = COMMAND_EXEC, + .help = "configure str9xpec lvd selection", + }, + { + .name = "options_lvdwarn", + .handler = str9xpec_handle_flash_options_lvdwarn_command, + .mode = COMMAND_EXEC, + .help = "configure str9xpec lvd warning", + }, + { + .name = "options_read", + .handler = str9xpec_handle_flash_options_read_command, + .mode = COMMAND_EXEC, + .help = "read str9xpec options", + }, + { + .name = "options_write", + .handler = str9xpec_handle_flash_options_write_command, + .mode = COMMAND_EXEC, + .help = "write str9xpec options", + }, + { + .name = "lock", + .handler = str9xpec_handle_flash_lock_command, + .mode = COMMAND_EXEC, + .help = "lock str9xpec device", + }, + { + .name = "unlock", + .handler = str9xpec_handle_flash_unlock_command, + .mode = COMMAND_EXEC, + .help = "unlock str9xpec device", + }, + { + .name = "part_id", + .handler = str9xpec_handle_part_id_command, + .mode = COMMAND_EXEC, + .help = "print part id of str9xpec flash bank ", + }, + COMMAND_REGISTRATION_DONE +}; +static const struct command_registration str9xpec_command_handlers[] = { + { + .name = "str9xpec", + .mode = COMMAND_ANY, + .help = "str9xpec flash command group", + .chain = str9xpec_config_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +struct flash_driver str9xpec_flash = { + .name = "str9xpec", + .commands = str9xpec_command_handlers, + .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, + }; diff --git a/src/flash/nor/str9xpec.h b/src/flash/nor/str9xpec.h new file mode 100644 index 00000000..1c8d41b1 --- /dev/null +++ b/src/flash/nor/str9xpec.h @@ -0,0 +1,79 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * Copyright (C) 2008 by Spencer Oliver * + * spen@spen-soft.co.uk * + * * + * 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 STR9XPEC_H +#define STR9XPEC_H + +#include "flash.h" +#include "jtag.h" + +struct str9xpec_flash_controller +{ + struct jtag_tap *tap; + uint32_t *sector_bits; + int chain_pos; + int isc_enable; + uint8_t options[8]; +}; + +enum str9xpec_status_codes +{ + STR9XPEC_INVALID_COMMAND = 1, + STR9XPEC_ISC_SUCCESS = 2, + STR9XPEC_ISC_DISABLED = 3, + STR9XPEC_ISC_INTFAIL = 32, +}; + +/* ISC commands */ + +#define ISC_IDCODE 0xFE +#define ISC_MFG_READ 0x4C +#define ISC_CONFIGURATION 0x07 +#define ISC_ENABLE 0x0C +#define ISC_DISABLE 0x0F +#define ISC_NOOP 0x10 +#define ISC_ADDRESS_SHIFT 0x11 +#define ISC_CLR_STATUS 0x13 +#define ISC_PROGRAM 0x20 +#define ISC_PROGRAM_SECURITY 0x22 +#define ISC_PROGRAM_UC 0x23 +#define ISC_ERASE 0x30 +#define ISC_READ 0x50 +#define ISC_BLANK_CHECK 0x60 + +/* ISC_DEFAULT bit definitions */ + +#define ISC_STATUS_SECURITY 0x40 +#define ISC_STATUS_INT_ERROR 0x30 +#define ISC_STATUS_MODE 0x08 +#define ISC_STATUS_BUSY 0x04 +#define ISC_STATUS_ERROR 0x03 + +/* Option bytes definitions */ + +#define STR9XPEC_OPT_CSMAPBIT 48 +#define STR9XPEC_OPT_LVDTHRESBIT 49 +#define STR9XPEC_OPT_LVDSELBIT 50 +#define STR9XPEC_OPT_LVDWARNBIT 51 +#define STR9XPEC_OPT_OTPBIT 63 + +#endif /* STR9XPEC_H */ diff --git a/src/flash/nor/tms470.c b/src/flash/nor/tms470.c new file mode 100644 index 00000000..59659346 --- /dev/null +++ b/src/flash/nor/tms470.c @@ -0,0 +1,1271 @@ +/*************************************************************************** + * Copyright (C) 2007,2008 by Christopher Kilgour * + * techie |_at_| whiterocker |_dot_| com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "tms470.h" + + +/* ---------------------------------------------------------------------- + Internal Support, Helpers + ---------------------------------------------------------------------- */ + +const struct flash_sector TMS470R1A256_SECTORS[] = { + {0x00000000, 0x00002000, -1, -1}, + {0x00002000, 0x00002000, -1, -1}, + {0x00004000, 0x00002000, -1, -1}, + {0x00006000, 0x00002000, -1, -1}, + {0x00008000, 0x00008000, -1, -1}, + {0x00010000, 0x00008000, -1, -1}, + {0x00018000, 0x00008000, -1, -1}, + {0x00020000, 0x00008000, -1, -1}, + {0x00028000, 0x00008000, -1, -1}, + {0x00030000, 0x00008000, -1, -1}, + {0x00038000, 0x00002000, -1, -1}, + {0x0003A000, 0x00002000, -1, -1}, + {0x0003C000, 0x00002000, -1, -1}, + {0x0003E000, 0x00002000, -1, -1}, +}; + +#define TMS470R1A256_NUM_SECTORS \ + ARRAY_SIZE(TMS470R1A256_SECTORS) + +const struct flash_sector TMS470R1A288_BANK0_SECTORS[] = { + {0x00000000, 0x00002000, -1, -1}, + {0x00002000, 0x00002000, -1, -1}, + {0x00004000, 0x00002000, -1, -1}, + {0x00006000, 0x00002000, -1, -1}, +}; + +#define TMS470R1A288_BANK0_NUM_SECTORS \ + ARRAY_SIZE(TMS470R1A288_BANK0_SECTORS) + +const struct flash_sector TMS470R1A288_BANK1_SECTORS[] = { + {0x00040000, 0x00010000, -1, -1}, + {0x00050000, 0x00010000, -1, -1}, + {0x00060000, 0x00010000, -1, -1}, + {0x00070000, 0x00010000, -1, -1}, +}; + +#define TMS470R1A288_BANK1_NUM_SECTORS \ + ARRAY_SIZE(TMS470R1A288_BANK1_SECTORS) + +const struct flash_sector TMS470R1A384_BANK0_SECTORS[] = { + {0x00000000, 0x00002000, -1, -1}, + {0x00002000, 0x00002000, -1, -1}, + {0x00004000, 0x00004000, -1, -1}, + {0x00008000, 0x00004000, -1, -1}, + {0x0000C000, 0x00004000, -1, -1}, + {0x00010000, 0x00004000, -1, -1}, + {0x00014000, 0x00004000, -1, -1}, + {0x00018000, 0x00002000, -1, -1}, + {0x0001C000, 0x00002000, -1, -1}, + {0x0001E000, 0x00002000, -1, -1}, +}; + +#define TMS470R1A384_BANK0_NUM_SECTORS \ + ARRAY_SIZE(TMS470R1A384_BANK0_SECTORS) + +const struct flash_sector TMS470R1A384_BANK1_SECTORS[] = { + {0x00020000, 0x00008000, -1, -1}, + {0x00028000, 0x00008000, -1, -1}, + {0x00030000, 0x00008000, -1, -1}, + {0x00038000, 0x00008000, -1, -1}, +}; + +#define TMS470R1A384_BANK1_NUM_SECTORS \ + ARRAY_SIZE(TMS470R1A384_BANK1_SECTORS) + +const struct flash_sector TMS470R1A384_BANK2_SECTORS[] = { + {0x00040000, 0x00008000, -1, -1}, + {0x00048000, 0x00008000, -1, -1}, + {0x00050000, 0x00008000, -1, -1}, + {0x00058000, 0x00008000, -1, -1}, +}; + +#define TMS470R1A384_BANK2_NUM_SECTORS \ + ARRAY_SIZE(TMS470R1A384_BANK2_SECTORS) + +/* ---------------------------------------------------------------------- */ + +static int tms470_read_part_info(struct flash_bank *bank) +{ + struct tms470_flash_bank *tms470_info = bank->driver_priv; + struct target *target = bank->target; + uint32_t device_ident_reg; + uint32_t silicon_version; + uint32_t technology_family; + uint32_t rom_flash; + uint32_t part_number; + char *part_name; + + /* we shall not rely on the caller in this test, this function allocates memory, + thus and executing the code more than once may cause memory leak */ + if (tms470_info->device_ident_reg) + return ERROR_OK; + + /* read and parse the device identification register */ + target_read_u32(target, 0xFFFFFFF0, &device_ident_reg); + + LOG_INFO("device_ident_reg = 0x%08" PRIx32 "", device_ident_reg); + + if ((device_ident_reg & 7) == 0) + { + LOG_WARNING("Cannot identify target as a TMS470 family."); + return ERROR_FLASH_OPERATION_FAILED; + } + + silicon_version = (device_ident_reg >> 12) & 0xF; + technology_family = (device_ident_reg >> 11) & 1; + rom_flash = (device_ident_reg >> 10) & 1; + part_number = (device_ident_reg >> 3) & 0x7f; + + /* + * If the part number is known, determine if the flash bank is valid + * based on the base address being within the known flash bank + * ranges. Then fixup/complete the remaining fields of the flash + * bank structure. + */ + switch (part_number) + { + case 0x0a: + part_name = "TMS470R1A256"; + + if (bank->base >= 0x00040000) + { + LOG_ERROR("No %s flash bank contains base address 0x%08" PRIx32 ".", part_name, bank->base); + return ERROR_FLASH_OPERATION_FAILED; + } + tms470_info->ordinal = 0; + bank->base = 0x00000000; + bank->size = 256 * 1024; + bank->num_sectors = TMS470R1A256_NUM_SECTORS; + bank->sectors = malloc(sizeof(TMS470R1A256_SECTORS)); + if (!bank->sectors) + { + return ERROR_FLASH_OPERATION_FAILED; + } + (void)memcpy(bank->sectors, TMS470R1A256_SECTORS, sizeof(TMS470R1A256_SECTORS)); + break; + + case 0x2b: + part_name = "TMS470R1A288"; + + if (bank->base < 0x00008000) + { + tms470_info->ordinal = 0; + bank->base = 0x00000000; + bank->size = 32 * 1024; + bank->num_sectors = TMS470R1A288_BANK0_NUM_SECTORS; + bank->sectors = malloc(sizeof(TMS470R1A288_BANK0_SECTORS)); + if (!bank->sectors) + { + return ERROR_FLASH_OPERATION_FAILED; + } + (void)memcpy(bank->sectors, TMS470R1A288_BANK0_SECTORS, sizeof(TMS470R1A288_BANK0_SECTORS)); + } + else if ((bank->base >= 0x00040000) && (bank->base < 0x00080000)) + { + tms470_info->ordinal = 1; + bank->base = 0x00040000; + bank->size = 256 * 1024; + bank->num_sectors = TMS470R1A288_BANK1_NUM_SECTORS; + bank->sectors = malloc(sizeof(TMS470R1A288_BANK1_SECTORS)); + if (!bank->sectors) + { + return ERROR_FLASH_OPERATION_FAILED; + } + (void)memcpy(bank->sectors, TMS470R1A288_BANK1_SECTORS, sizeof(TMS470R1A288_BANK1_SECTORS)); + } + else + { + LOG_ERROR("No %s flash bank contains base address 0x%08" PRIx32 ".", part_name, bank->base); + return ERROR_FLASH_OPERATION_FAILED; + } + break; + + case 0x2d: + part_name = "TMS470R1A384"; + + if (bank->base < 0x00020000) + { + tms470_info->ordinal = 0; + bank->base = 0x00000000; + bank->size = 128 * 1024; + bank->num_sectors = TMS470R1A384_BANK0_NUM_SECTORS; + bank->sectors = malloc(sizeof(TMS470R1A384_BANK0_SECTORS)); + if (!bank->sectors) + { + return ERROR_FLASH_OPERATION_FAILED; + } + (void)memcpy(bank->sectors, TMS470R1A384_BANK0_SECTORS, sizeof(TMS470R1A384_BANK0_SECTORS)); + } + else if ((bank->base >= 0x00020000) && (bank->base < 0x00040000)) + { + tms470_info->ordinal = 1; + bank->base = 0x00020000; + bank->size = 128 * 1024; + bank->num_sectors = TMS470R1A384_BANK1_NUM_SECTORS; + bank->sectors = malloc(sizeof(TMS470R1A384_BANK1_SECTORS)); + if (!bank->sectors) + { + return ERROR_FLASH_OPERATION_FAILED; + } + (void)memcpy(bank->sectors, TMS470R1A384_BANK1_SECTORS, sizeof(TMS470R1A384_BANK1_SECTORS)); + } + else if ((bank->base >= 0x00040000) && (bank->base < 0x00060000)) + { + tms470_info->ordinal = 2; + bank->base = 0x00040000; + bank->size = 128 * 1024; + bank->num_sectors = TMS470R1A384_BANK2_NUM_SECTORS; + bank->sectors = malloc(sizeof(TMS470R1A384_BANK2_SECTORS)); + if (!bank->sectors) + { + return ERROR_FLASH_OPERATION_FAILED; + } + (void)memcpy(bank->sectors, TMS470R1A384_BANK2_SECTORS, sizeof(TMS470R1A384_BANK2_SECTORS)); + } + else + { + LOG_ERROR("No %s flash bank contains base address 0x%08" PRIx32 ".", part_name, bank->base); + return ERROR_FLASH_OPERATION_FAILED; + } + break; + + default: + LOG_WARNING("Could not identify part 0x%02x as a member of the TMS470 family.", (unsigned)part_number); + return ERROR_FLASH_OPERATION_FAILED; + } + + /* turn off memory selects */ + target_write_u32(target, 0xFFFFFFE4, 0x00000000); + target_write_u32(target, 0xFFFFFFE0, 0x00000000); + + bank->chip_width = 32; + bank->bus_width = 32; + + LOG_INFO("Identified %s, ver=%d, core=%s, nvmem=%s.", + part_name, + (int)(silicon_version), + (technology_family ? "1.8v" : "3.3v"), + (rom_flash ? "rom" : "flash")); + + tms470_info->device_ident_reg = device_ident_reg; + tms470_info->silicon_version = silicon_version; + tms470_info->technology_family = technology_family; + tms470_info->rom_flash = rom_flash; + tms470_info->part_number = part_number; + tms470_info->part_name = part_name; + + /* + * Disable reset on address access violation. + */ + target_write_u32(target, 0xFFFFFFE0, 0x00004007); + + return ERROR_OK; +} + +/* ---------------------------------------------------------------------- */ + +static uint32_t keysSet = 0; +static uint32_t flashKeys[4]; + +COMMAND_HANDLER(tms470_handle_flash_keyset_command) +{ + if (CMD_ARGC > 4) + { + command_print(CMD_CTX, "tms470 flash_keyset "); + return ERROR_INVALID_ARGUMENTS; + } + else if (CMD_ARGC == 4) + { + int i; + + for (i = 0; i < 4; i++) + { + int start = (0 == strncmp(CMD_ARGV[i], "0x", 2)) ? 2 : 0; + + if (1 != sscanf(&CMD_ARGV[i][start], "%" SCNx32 "", &flashKeys[i])) + { + command_print(CMD_CTX, "could not process flash key %s", CMD_ARGV[i]); + LOG_ERROR("could not process flash key %s", CMD_ARGV[i]); + return ERROR_INVALID_ARGUMENTS; + } + } + + keysSet = 1; + } + else if (CMD_ARGC != 0) + { + command_print(CMD_CTX, "tms470 flash_keyset "); + return ERROR_INVALID_ARGUMENTS; + } + + if (keysSet) + { + command_print(CMD_CTX, "using flash keys 0x%08" PRIx32 ", 0x%08" PRIx32 ", 0x%08" PRIx32 ", 0x%08" PRIx32 "", + flashKeys[0], flashKeys[1], flashKeys[2], flashKeys[3]); + } + else + { + command_print(CMD_CTX, "flash keys not set"); + } + + return ERROR_OK; +} + +static const uint32_t FLASH_KEYS_ALL_ONES[] = { 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, +}; + +static const uint32_t FLASH_KEYS_ALL_ZEROS[] = { 0x00000000, 0x00000000, + 0x00000000, 0x00000000, +}; + +static const uint32_t FLASH_KEYS_MIX1[] = { 0xf0fff0ff, 0xf0fff0ff, + 0xf0fff0ff, 0xf0fff0ff +}; + +static const uint32_t FLASH_KEYS_MIX2[] = { 0x0000ffff, 0x0000ffff, + 0x0000ffff, 0x0000ffff +}; + +/* ---------------------------------------------------------------------- */ + +static int oscMHz = 12; + +COMMAND_HANDLER(tms470_handle_osc_megahertz_command) +{ + if (CMD_ARGC > 1) + { + command_print(CMD_CTX, "tms470 osc_megahertz "); + return ERROR_INVALID_ARGUMENTS; + } + else if (CMD_ARGC == 1) + { + sscanf(CMD_ARGV[0], "%d", &oscMHz); + } + + if (oscMHz <= 0) + { + LOG_ERROR("osc_megahertz must be positive and non-zero!"); + command_print(CMD_CTX, "osc_megahertz must be positive and non-zero!"); + oscMHz = 12; + return ERROR_INVALID_ARGUMENTS; + } + + command_print(CMD_CTX, "osc_megahertz=%d", oscMHz); + + return ERROR_OK; +} + +/* ---------------------------------------------------------------------- */ + +static int plldis = 0; + +COMMAND_HANDLER(tms470_handle_plldis_command) +{ + if (CMD_ARGC > 1) + { + command_print(CMD_CTX, "tms470 plldis <0 | 1>"); + return ERROR_INVALID_ARGUMENTS; + } + else if (CMD_ARGC == 1) + { + sscanf(CMD_ARGV[0], "%d", &plldis); + plldis = plldis ? 1 : 0; + } + + command_print(CMD_CTX, "plldis=%d", plldis); + + return ERROR_OK; +} + +/* ---------------------------------------------------------------------- */ + +static int tms470_check_flash_unlocked(struct target * target) +{ + uint32_t fmbbusy; + + target_read_u32(target, 0xFFE89C08, &fmbbusy); + LOG_INFO("tms470 fmbbusy = 0x%08" PRIx32 " -> %s", fmbbusy, fmbbusy & 0x8000 ? "unlocked" : "LOCKED"); + return fmbbusy & 0x8000 ? ERROR_OK : ERROR_FLASH_OPERATION_FAILED; +} + +/* ---------------------------------------------------------------------- */ + +static int tms470_try_flash_keys(struct target * target, const uint32_t * key_set) +{ + uint32_t glbctrl, fmmstat; + int retval = ERROR_FLASH_OPERATION_FAILED; + + /* set GLBCTRL.4 */ + target_read_u32(target, 0xFFFFFFDC, &glbctrl); + target_write_u32(target, 0xFFFFFFDC, glbctrl | 0x10); + + /* only perform the key match when 3VSTAT is clear */ + target_read_u32(target, 0xFFE8BC0C, &fmmstat); + if (!(fmmstat & 0x08)) + { + unsigned i; + uint32_t fmbptr, fmbac2, orig_fmregopt; + + target_write_u32(target, 0xFFE8BC04, fmmstat & ~0x07); + + /* wait for pump ready */ + do + { + target_read_u32(target, 0xFFE8A814, &fmbptr); + alive_sleep(1); + } + while (!(fmbptr & 0x0200)); + + /* force max wait states */ + target_read_u32(target, 0xFFE88004, &fmbac2); + target_write_u32(target, 0xFFE88004, fmbac2 | 0xff); + + /* save current access mode, force normal read mode */ + target_read_u32(target, 0xFFE89C00, &orig_fmregopt); + target_write_u32(target, 0xFFE89C00, 0x00); + + for (i = 0; i < 4; i++) + { + uint32_t tmp; + + /* There is no point displaying the value of tmp, it is + * filtered by the chip. The purpose of this read is to + * prime the unlocking logic rather than read out the value. + */ + target_read_u32(target, 0x00001FF0 + 4 * i, &tmp); + + LOG_INFO("tms470 writing fmpkey = 0x%08" PRIx32 "", key_set[i]); + target_write_u32(target, 0xFFE89C0C, key_set[i]); + } + + if (ERROR_OK == tms470_check_flash_unlocked(target)) + { + /* + * There seems to be a side-effect of reading the FMPKEY + * register in that it re-enables the protection. So we + * re-enable it. + */ + for (i = 0; i < 4; i++) + { + uint32_t tmp; + + target_read_u32(target, 0x00001FF0 + 4 * i, &tmp); + target_write_u32(target, 0xFFE89C0C, key_set[i]); + } + retval = ERROR_OK; + } + + /* restore settings */ + target_write_u32(target, 0xFFE89C00, orig_fmregopt); + target_write_u32(target, 0xFFE88004, fmbac2); + } + + /* clear config bit */ + target_write_u32(target, 0xFFFFFFDC, glbctrl); + + return retval; +} + +/* ---------------------------------------------------------------------- */ + +static int tms470_unlock_flash(struct flash_bank *bank) +{ + struct target *target = bank->target; + const uint32_t *p_key_sets[5]; + unsigned i, key_set_count; + + if (keysSet) + { + key_set_count = 5; + p_key_sets[0] = flashKeys; + p_key_sets[1] = FLASH_KEYS_ALL_ONES; + p_key_sets[2] = FLASH_KEYS_ALL_ZEROS; + p_key_sets[3] = FLASH_KEYS_MIX1; + p_key_sets[4] = FLASH_KEYS_MIX2; + } + else + { + key_set_count = 4; + p_key_sets[0] = FLASH_KEYS_ALL_ONES; + p_key_sets[1] = FLASH_KEYS_ALL_ZEROS; + p_key_sets[2] = FLASH_KEYS_MIX1; + p_key_sets[3] = FLASH_KEYS_MIX2; + } + + for (i = 0; i < key_set_count; i++) + { + if (tms470_try_flash_keys(target, p_key_sets[i]) == ERROR_OK) + { + LOG_INFO("tms470 flash is unlocked"); + return ERROR_OK; + } + } + + LOG_WARNING("tms470 could not unlock flash memory protection level 2"); + return ERROR_FLASH_OPERATION_FAILED; +} + +/* ---------------------------------------------------------------------- */ + +static int tms470_flash_initialize_internal_state_machine(struct flash_bank *bank) +{ + uint32_t fmmac2, fmmac1, fmmaxep, k, delay, glbctrl, sysclk; + struct target *target = bank->target; + struct tms470_flash_bank *tms470_info = bank->driver_priv; + int result = ERROR_OK; + + /* + * Select the desired bank to be programmed by writing BANK[2:0] of + * FMMAC2. + */ + target_read_u32(target, 0xFFE8BC04, &fmmac2); + fmmac2 &= ~0x0007; + fmmac2 |= (tms470_info->ordinal & 7); + target_write_u32(target, 0xFFE8BC04, fmmac2); + LOG_DEBUG("set fmmac2 = 0x%04" PRIx32 "", fmmac2); + + /* + * Disable level 1 sector protection by setting bit 15 of FMMAC1. + */ + target_read_u32(target, 0xFFE8BC00, &fmmac1); + fmmac1 |= 0x8000; + target_write_u32(target, 0xFFE8BC00, fmmac1); + LOG_DEBUG("set fmmac1 = 0x%04" PRIx32 "", fmmac1); + + /* + * FMTCREG = 0x2fc0; + */ + target_write_u32(target, 0xFFE8BC10, 0x2fc0); + LOG_DEBUG("set fmtcreg = 0x2fc0"); + + /* + * MAXPP = 50 + */ + target_write_u32(target, 0xFFE8A07C, 50); + LOG_DEBUG("set fmmaxpp = 50"); + + /* + * MAXCP = 0xf000 + 2000 + */ + target_write_u32(target, 0xFFE8A084, 0xf000 + 2000); + LOG_DEBUG("set fmmaxcp = 0x%04x", 0xf000 + 2000); + + /* + * configure VHV + */ + target_read_u32(target, 0xFFE8A080, &fmmaxep); + if (fmmaxep == 0xf000) + { + fmmaxep = 0xf000 + 4095; + target_write_u32(target, 0xFFE8A80C, 0x9964); + LOG_DEBUG("set fmptr3 = 0x9964"); + } + else + { + fmmaxep = 0xa000 + 4095; + target_write_u32(target, 0xFFE8A80C, 0x9b64); + LOG_DEBUG("set fmptr3 = 0x9b64"); + } + target_write_u32(target, 0xFFE8A080, fmmaxep); + LOG_DEBUG("set fmmaxep = 0x%04" PRIx32 "", fmmaxep); + + /* + * FMPTR4 = 0xa000 + */ + target_write_u32(target, 0xFFE8A810, 0xa000); + LOG_DEBUG("set fmptr4 = 0xa000"); + + /* + * FMPESETUP, delay parameter selected based on clock frequency. + * + * According to the TI App Note SPNU257 and flashing code, delay is + * int((sysclk(MHz) + 1) / 2), with a minimum of 5. The system + * clock is usually derived from the ZPLL module, and selected by + * the plldis global. + */ + target_read_u32(target, 0xFFFFFFDC, &glbctrl); + sysclk = (plldis ? 1 : (glbctrl & 0x08) ? 4 : 8) * oscMHz / (1 + (glbctrl & 7)); + delay = (sysclk > 10) ? (sysclk + 1) / 2 : 5; + target_write_u32(target, 0xFFE8A018, (delay << 4) | (delay << 8)); + LOG_DEBUG("set fmpsetup = 0x%04" PRIx32 "", (delay << 4) | (delay << 8)); + + /* + * FMPVEVACCESS, based on delay. + */ + k = delay | (delay << 8); + target_write_u32(target, 0xFFE8A05C, k); + LOG_DEBUG("set fmpvevaccess = 0x%04" PRIx32 "", k); + + /* + * FMPCHOLD, FMPVEVHOLD, FMPVEVSETUP, based on delay. + */ + k <<= 1; + target_write_u32(target, 0xFFE8A034, k); + LOG_DEBUG("set fmpchold = 0x%04" PRIx32 "", k); + target_write_u32(target, 0xFFE8A040, k); + LOG_DEBUG("set fmpvevhold = 0x%04" PRIx32 "", k); + target_write_u32(target, 0xFFE8A024, k); + LOG_DEBUG("set fmpvevsetup = 0x%04" PRIx32 "", k); + + /* + * FMCVACCESS, based on delay. + */ + k = delay * 16; + target_write_u32(target, 0xFFE8A060, k); + LOG_DEBUG("set fmcvaccess = 0x%04" PRIx32 "", k); + + /* + * FMCSETUP, based on delay. + */ + k = 0x3000 | delay * 20; + target_write_u32(target, 0xFFE8A020, k); + LOG_DEBUG("set fmcsetup = 0x%04" PRIx32 "", k); + + /* + * FMEHOLD, based on delay. + */ + k = (delay * 20) << 2; + target_write_u32(target, 0xFFE8A038, k); + LOG_DEBUG("set fmehold = 0x%04" PRIx32 "", k); + + /* + * PWIDTH, CWIDTH, EWIDTH, based on delay. + */ + target_write_u32(target, 0xFFE8A050, delay * 8); + LOG_DEBUG("set fmpwidth = 0x%04" PRIx32 "", delay * 8); + target_write_u32(target, 0xFFE8A058, delay * 1000); + LOG_DEBUG("set fmcwidth = 0x%04" PRIx32 "", delay * 1000); + target_write_u32(target, 0xFFE8A054, delay * 5400); + LOG_DEBUG("set fmewidth = 0x%04" PRIx32 "", delay * 5400); + + return result; +} + +/* ---------------------------------------------------------------------- */ + +int tms470_flash_status(struct flash_bank *bank) +{ + struct target *target = bank->target; + int result = ERROR_OK; + uint32_t fmmstat; + + target_read_u32(target, 0xFFE8BC0C, &fmmstat); + LOG_DEBUG("set fmmstat = 0x%04" PRIx32 "", fmmstat); + + if (fmmstat & 0x0080) + { + LOG_WARNING("tms470 flash command: erase still active after busy clear."); + result = ERROR_FLASH_OPERATION_FAILED; + } + + if (fmmstat & 0x0040) + { + LOG_WARNING("tms470 flash command: program still active after busy clear."); + result = ERROR_FLASH_OPERATION_FAILED; + } + + if (fmmstat & 0x0020) + { + LOG_WARNING("tms470 flash command: invalid data command."); + result = ERROR_FLASH_OPERATION_FAILED; + } + + if (fmmstat & 0x0010) + { + LOG_WARNING("tms470 flash command: program, erase or validate sector failed."); + result = ERROR_FLASH_OPERATION_FAILED; + } + + if (fmmstat & 0x0008) + { + LOG_WARNING("tms470 flash command: voltage instability detected."); + result = ERROR_FLASH_OPERATION_FAILED; + } + + if (fmmstat & 0x0006) + { + LOG_WARNING("tms470 flash command: command suspend detected."); + result = ERROR_FLASH_OPERATION_FAILED; + } + + if (fmmstat & 0x0001) + { + LOG_WARNING("tms470 flash command: sector was locked."); + result = ERROR_FLASH_OPERATION_FAILED; + } + + return result; +} + +/* ---------------------------------------------------------------------- */ + +static int tms470_erase_sector(struct flash_bank *bank, int sector) +{ + uint32_t glbctrl, orig_fmregopt, fmbsea, fmbseb, fmmstat; + struct target *target = bank->target; + uint32_t flashAddr = bank->base + bank->sectors[sector].offset; + int result = ERROR_OK; + + /* + * Set the bit GLBCTRL4 of the GLBCTRL register (in the System + * module) to enable writing to the flash registers }. + */ + target_read_u32(target, 0xFFFFFFDC, &glbctrl); + target_write_u32(target, 0xFFFFFFDC, glbctrl | 0x10); + LOG_DEBUG("set glbctrl = 0x%08" PRIx32 "", glbctrl | 0x10); + + /* Force normal read mode. */ + target_read_u32(target, 0xFFE89C00, &orig_fmregopt); + target_write_u32(target, 0xFFE89C00, 0); + LOG_DEBUG("set fmregopt = 0x%08x", 0); + + (void)tms470_flash_initialize_internal_state_machine(bank); + + /* + * Select one or more bits in FMBSEA or FMBSEB to disable Level 1 + * protection for the particular sector to be erased/written. + */ + if (sector < 16) + { + target_read_u32(target, 0xFFE88008, &fmbsea); + target_write_u32(target, 0xFFE88008, fmbsea | (1 << sector)); + LOG_DEBUG("set fmbsea = 0x%04" PRIx32 "", fmbsea | (1 << sector)); + } + else + { + target_read_u32(target, 0xFFE8800C, &fmbseb); + target_write_u32(target, 0xFFE8800C, fmbseb | (1 << (sector - 16))); + LOG_DEBUG("set fmbseb = 0x%04" PRIx32 "", fmbseb | (1 << (sector - 16))); + } + bank->sectors[sector].is_protected = 0; + + /* + * clear status regiser, sent erase command, kickoff erase + */ + target_write_u16(target, flashAddr, 0x0040); + LOG_DEBUG("write *(uint16_t *)0x%08" PRIx32 "=0x0040", flashAddr); + target_write_u16(target, flashAddr, 0x0020); + LOG_DEBUG("write *(uint16_t *)0x%08" PRIx32 "=0x0020", flashAddr); + target_write_u16(target, flashAddr, 0xffff); + LOG_DEBUG("write *(uint16_t *)0x%08" PRIx32 "=0xffff", flashAddr); + + /* + * Monitor FMMSTAT, busy until clear, then check and other flags for + * ultimate result of the operation. + */ + do + { + target_read_u32(target, 0xFFE8BC0C, &fmmstat); + if (fmmstat & 0x0100) + { + alive_sleep(1); + } + } + while (fmmstat & 0x0100); + + result = tms470_flash_status(bank); + + if (sector < 16) + { + target_write_u32(target, 0xFFE88008, fmbsea); + LOG_DEBUG("set fmbsea = 0x%04" PRIx32 "", fmbsea); + bank->sectors[sector].is_protected = fmbsea & (1 << sector) ? 0 : 1; + } + else + { + target_write_u32(target, 0xFFE8800C, fmbseb); + LOG_DEBUG("set fmbseb = 0x%04" PRIx32 "", fmbseb); + bank->sectors[sector].is_protected = fmbseb & (1 << (sector - 16)) ? 0 : 1; + } + target_write_u32(target, 0xFFE89C00, orig_fmregopt); + LOG_DEBUG("set fmregopt = 0x%08" PRIx32 "", orig_fmregopt); + target_write_u32(target, 0xFFFFFFDC, glbctrl); + LOG_DEBUG("set glbctrl = 0x%08" PRIx32 "", glbctrl); + + if (result == ERROR_OK) + { + bank->sectors[sector].is_erased = 1; + } + + return result; +} + +/* ---------------------------------------------------------------------- + Implementation of Flash Driver Interfaces + ---------------------------------------------------------------------- */ + +static const struct command_registration tms470_any_command_handlers[] = { + { + .name = "flash_keyset", + .handler = &tms470_handle_flash_keyset_command, + .mode = COMMAND_ANY, + .help = "tms470 flash_keyset ", + }, + { + .name = "osc_megahertz", + .handler = &tms470_handle_osc_megahertz_command, + .mode = COMMAND_ANY, + .help = "tms470 osc_megahertz ", + }, + { + .name = "plldis", + .handler = &tms470_handle_plldis_command, + .mode = COMMAND_ANY, + .help = "tms470 plldis <0/1>", + }, + COMMAND_REGISTRATION_DONE +}; +static const struct command_registration tms470_command_handlers[] = { + { + .name = "tms470", + .mode = COMMAND_ANY, + .help = "TI tms470 flash command group", + .chain = tms470_any_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +/* ---------------------------------------------------------------------- */ + +static int tms470_erase(struct flash_bank *bank, int first, int last) +{ + struct tms470_flash_bank *tms470_info = bank->driver_priv; + int sector, result = ERROR_OK; + + if (bank->target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + tms470_read_part_info(bank); + + if ((first < 0) || (first >= bank->num_sectors) || (last < 0) || (last >= bank->num_sectors) || (first > last)) + { + LOG_ERROR("Sector range %d to %d invalid.", first, last); + return ERROR_FLASH_SECTOR_INVALID; + } + + result = tms470_unlock_flash(bank); + if (result != ERROR_OK) + { + return result; + } + + for (sector = first; sector <= last; sector++) + { + LOG_INFO("Erasing tms470 bank %d sector %d...", tms470_info->ordinal, sector); + + result = tms470_erase_sector(bank, sector); + + if (result != ERROR_OK) + { + LOG_ERROR("tms470 could not erase flash sector."); + break; + } + else + { + LOG_INFO("sector erased successfully."); + } + } + + return result; +} + +/* ---------------------------------------------------------------------- */ + +static int tms470_protect(struct flash_bank *bank, int set, int first, int last) +{ + struct tms470_flash_bank *tms470_info = bank->driver_priv; + struct target *target = bank->target; + uint32_t fmmac2, fmbsea, fmbseb; + int sector; + + if (target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + tms470_read_part_info(bank); + + if ((first < 0) || (first >= bank->num_sectors) || (last < 0) || (last >= bank->num_sectors) || (first > last)) + { + LOG_ERROR("Sector range %d to %d invalid.", first, last); + return ERROR_FLASH_SECTOR_INVALID; + } + + /* enable the appropriate bank */ + target_read_u32(target, 0xFFE8BC04, &fmmac2); + target_write_u32(target, 0xFFE8BC04, (fmmac2 & ~7) | tms470_info->ordinal); + + /* get the original sector proection flags for this bank */ + target_read_u32(target, 0xFFE88008, &fmbsea); + target_read_u32(target, 0xFFE8800C, &fmbseb); + + for (sector = 0; sector < bank->num_sectors; sector++) + { + if (sector < 16) + { + fmbsea = set ? fmbsea & ~(1 << sector) : fmbsea | (1 << sector); + bank->sectors[sector].is_protected = set ? 1 : 0; + } + else + { + fmbseb = set ? fmbseb & ~(1 << (sector - 16)) : fmbseb | (1 << (sector - 16)); + bank->sectors[sector].is_protected = set ? 1 : 0; + } + } + + /* update the protection bits */ + target_write_u32(target, 0xFFE88008, fmbsea); + target_write_u32(target, 0xFFE8800C, fmbseb); + + return ERROR_OK; +} + +/* ---------------------------------------------------------------------- */ + +static int tms470_write(struct flash_bank *bank, uint8_t * buffer, uint32_t offset, uint32_t count) +{ + struct target *target = bank->target; + uint32_t glbctrl, fmbac2, orig_fmregopt, fmbsea, fmbseb, fmmaxpp, fmmstat; + int result = ERROR_OK; + uint32_t i; + + if (target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + tms470_read_part_info(bank); + + LOG_INFO("Writing %" PRId32 " bytes starting at 0x%08" PRIx32 "", count, bank->base + offset); + + /* set GLBCTRL.4 */ + target_read_u32(target, 0xFFFFFFDC, &glbctrl); + target_write_u32(target, 0xFFFFFFDC, glbctrl | 0x10); + + (void)tms470_flash_initialize_internal_state_machine(bank); + + /* force max wait states */ + target_read_u32(target, 0xFFE88004, &fmbac2); + target_write_u32(target, 0xFFE88004, fmbac2 | 0xff); + + /* save current access mode, force normal read mode */ + target_read_u32(target, 0xFFE89C00, &orig_fmregopt); + target_write_u32(target, 0xFFE89C00, 0x00); + + /* + * Disable Level 1 protection for all sectors to be erased/written. + */ + target_read_u32(target, 0xFFE88008, &fmbsea); + target_write_u32(target, 0xFFE88008, 0xffff); + target_read_u32(target, 0xFFE8800C, &fmbseb); + target_write_u32(target, 0xFFE8800C, 0xffff); + + /* read MAXPP */ + target_read_u32(target, 0xFFE8A07C, &fmmaxpp); + + for (i = 0; i < count; i += 2) + { + uint32_t addr = bank->base + offset + i; + uint16_t word = (((uint16_t) buffer[i]) << 8) | (uint16_t) buffer[i + 1]; + + if (word != 0xffff) + { + LOG_INFO("writing 0x%04x at 0x%08" PRIx32 "", word, addr); + + /* clear status register */ + target_write_u16(target, addr, 0x0040); + /* program flash command */ + target_write_u16(target, addr, 0x0010); + /* burn the 16-bit word (big-endian) */ + target_write_u16(target, addr, word); + + /* + * Monitor FMMSTAT, busy until clear, then check and other flags + * for ultimate result of the operation. + */ + do + { + target_read_u32(target, 0xFFE8BC0C, &fmmstat); + if (fmmstat & 0x0100) + { + alive_sleep(1); + } + } + while (fmmstat & 0x0100); + + if (fmmstat & 0x3ff) + { + LOG_ERROR("fmstat = 0x%04" PRIx32 "", fmmstat); + LOG_ERROR("Could not program word 0x%04x at address 0x%08" PRIx32 ".", word, addr); + result = ERROR_FLASH_OPERATION_FAILED; + break; + } + } + else + { + LOG_INFO("skipping 0xffff at 0x%08" PRIx32 "", addr); + } + } + + /* restore */ + target_write_u32(target, 0xFFE88008, fmbsea); + target_write_u32(target, 0xFFE8800C, fmbseb); + target_write_u32(target, 0xFFE88004, fmbac2); + target_write_u32(target, 0xFFE89C00, orig_fmregopt); + target_write_u32(target, 0xFFFFFFDC, glbctrl); + + return result; +} + +/* ---------------------------------------------------------------------- */ + +static int tms470_probe(struct flash_bank *bank) +{ + if (bank->target->state != TARGET_HALTED) + { + LOG_WARNING("Cannot communicate... target not halted."); + return ERROR_TARGET_NOT_HALTED; + } + + return tms470_read_part_info(bank); +} + +static int tms470_auto_probe(struct flash_bank *bank) +{ + struct tms470_flash_bank *tms470_info = bank->driver_priv; + + if (tms470_info->device_ident_reg) + return ERROR_OK; + return tms470_probe(bank); +} + +/* ---------------------------------------------------------------------- */ + +static int tms470_erase_check(struct flash_bank *bank) +{ + struct target *target = bank->target; + struct tms470_flash_bank *tms470_info = bank->driver_priv; + int sector, result = ERROR_OK; + uint32_t fmmac2, fmbac2, glbctrl, orig_fmregopt; + static uint8_t buffer[64 * 1024]; + + if (target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (!tms470_info->device_ident_reg) + { + tms470_read_part_info(bank); + } + + /* set GLBCTRL.4 */ + target_read_u32(target, 0xFFFFFFDC, &glbctrl); + target_write_u32(target, 0xFFFFFFDC, glbctrl | 0x10); + + /* save current access mode, force normal read mode */ + target_read_u32(target, 0xFFE89C00, &orig_fmregopt); + target_write_u32(target, 0xFFE89C00, 0x00); + + /* enable the appropriate bank */ + target_read_u32(target, 0xFFE8BC04, &fmmac2); + target_write_u32(target, 0xFFE8BC04, (fmmac2 & ~7) | tms470_info->ordinal); + + /* TCR = 0 */ + target_write_u32(target, 0xFFE8BC10, 0x2fc0); + + /* clear TEZ in fmbrdy */ + target_write_u32(target, 0xFFE88010, 0x0b); + + /* save current wait states, force max */ + target_read_u32(target, 0xFFE88004, &fmbac2); + target_write_u32(target, 0xFFE88004, fmbac2 | 0xff); + + /* + * The TI primitives inspect the flash memory by reading one 32-bit + * word at a time. Here we read an entire sector and inspect it in + * an attempt to reduce the JTAG overhead. + */ + for (sector = 0; sector < bank->num_sectors; sector++) + { + if (bank->sectors[sector].is_erased != 1) + { + uint32_t i, addr = bank->base + bank->sectors[sector].offset; + + LOG_INFO("checking flash bank %d sector %d", tms470_info->ordinal, sector); + + target_read_buffer(target, addr, bank->sectors[sector].size, buffer); + + bank->sectors[sector].is_erased = 1; + for (i = 0; i < bank->sectors[sector].size; i++) + { + if (buffer[i] != 0xff) + { + LOG_WARNING("tms470 bank %d, sector %d, not erased.", tms470_info->ordinal, sector); + LOG_WARNING("at location 0x%08" PRIx32 ": flash data is 0x%02x.", addr + i, buffer[i]); + + bank->sectors[sector].is_erased = 0; + break; + } + } + } + if (bank->sectors[sector].is_erased != 1) + { + result = ERROR_FLASH_SECTOR_NOT_ERASED; + break; + } + else + { + LOG_INFO("sector erased"); + } + } + + /* reset TEZ, wait states, read mode, GLBCTRL.4 */ + target_write_u32(target, 0xFFE88010, 0x0f); + target_write_u32(target, 0xFFE88004, fmbac2); + target_write_u32(target, 0xFFE89C00, orig_fmregopt); + target_write_u32(target, 0xFFFFFFDC, glbctrl); + + return result; +} + +/* ---------------------------------------------------------------------- */ + +static int tms470_protect_check(struct flash_bank *bank) +{ + struct target *target = bank->target; + struct tms470_flash_bank *tms470_info = bank->driver_priv; + int sector, result = ERROR_OK; + uint32_t fmmac2, fmbsea, fmbseb; + + if (target->state != TARGET_HALTED) + { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (!tms470_info->device_ident_reg) + { + tms470_read_part_info(bank); + } + + /* enable the appropriate bank */ + target_read_u32(target, 0xFFE8BC04, &fmmac2); + target_write_u32(target, 0xFFE8BC04, (fmmac2 & ~7) | tms470_info->ordinal); + + target_read_u32(target, 0xFFE88008, &fmbsea); + target_read_u32(target, 0xFFE8800C, &fmbseb); + + for (sector = 0; sector < bank->num_sectors; sector++) + { + int protected; + + if (sector < 16) + { + protected = fmbsea & (1 << sector) ? 0 : 1; + bank->sectors[sector].is_protected = protected; + } + else + { + protected = fmbseb & (1 << (sector - 16)) ? 0 : 1; + bank->sectors[sector].is_protected = protected; + } + + LOG_DEBUG("bank %d sector %d is %s", tms470_info->ordinal, sector, protected ? "protected" : "not protected"); + } + + return result; +} + +/* ---------------------------------------------------------------------- */ + +static int tms470_info(struct flash_bank *bank, char *buf, int buf_size) +{ + int used = 0; + struct tms470_flash_bank *tms470_info = bank->driver_priv; + + if (!tms470_info->device_ident_reg) + { + tms470_read_part_info(bank); + } + + if (!tms470_info->device_ident_reg) + { + (void)snprintf(buf, buf_size, "Cannot identify target as a TMS470\n"); + return ERROR_FLASH_OPERATION_FAILED; + } + + used += snprintf(buf, buf_size, "\ntms470 information: Chip is %s\n", tms470_info->part_name); + buf += used; + buf_size -= used; + + used += snprintf(buf, buf_size, "Flash protection level 2 is %s\n", tms470_check_flash_unlocked(bank->target) == ERROR_OK ? "disabled" : "enabled"); + buf += used; + buf_size -= used; + + return ERROR_OK; +} + +/* ---------------------------------------------------------------------- */ + +/* + * flash bank tms470 + * [options...] + */ + +FLASH_BANK_COMMAND_HANDLER(tms470_flash_bank_command) +{ + bank->driver_priv = malloc(sizeof(struct tms470_flash_bank)); + + if (!bank->driver_priv) + { + return ERROR_FLASH_OPERATION_FAILED; + } + + (void)memset(bank->driver_priv, 0, sizeof(struct tms470_flash_bank)); + + return ERROR_OK; +} + +struct flash_driver tms470_flash = { + .name = "tms470", + .commands = tms470_command_handlers, + .flash_bank_command = &tms470_flash_bank_command, + .erase = &tms470_erase, + .protect = &tms470_protect, + .write = &tms470_write, + .probe = &tms470_probe, + .auto_probe = &tms470_auto_probe, + .erase_check = &tms470_erase_check, + .protect_check = &tms470_protect_check, + .info = &tms470_info, + }; diff --git a/src/flash/nor/tms470.h b/src/flash/nor/tms470.h new file mode 100644 index 00000000..f275e510 --- /dev/null +++ b/src/flash/nor/tms470.h @@ -0,0 +1,39 @@ +/*************************************************************************** + * Copyright (C) 2007,2008 by Christopher Kilgour * + * techie |_at_| whiterocker |_dot_| com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef TMS470_DOT_H +#define TMS470_DOT_H + +#include "flash.h" + +struct tms470_flash_bank +{ + unsigned ordinal; + + /* device identification register */ + uint32_t device_ident_reg; + uint32_t silicon_version; + uint32_t technology_family; + uint32_t rom_flash; + uint32_t part_number; + char * part_name; + +}; + +#endif /* TMS470_DOT_H */ diff --git a/src/flash/ocl.c b/src/flash/ocl.c deleted file mode 100644 index 388395f0..00000000 --- a/src/flash/ocl.c +++ /dev/null @@ -1,361 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2007 by Pavel Chromy * - * chromy@asix.cz * - * * - * 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 "ocl.h" -#include "flash.h" -#include "embeddedice.h" - - -struct ocl_priv -{ - struct arm_jtag *jtag_info; - unsigned int buflen; - unsigned int bufalign; -}; - -static int ocl_erase_check(struct flash_bank *bank) -{ - return ERROR_OK; -} - -static int ocl_protect_check(struct flash_bank *bank) -{ - return ERROR_OK; -} - -/* flash_bank ocl 0 0 0 0 */ -FLASH_BANK_COMMAND_HANDLER(ocl_flash_bank_command) -{ - struct arm7_9_common *arm7_9; - struct ocl_priv *ocl; - - if (CMD_ARGC < 6) - { - LOG_WARNING("incomplete flash_bank ocl configuration"); - return ERROR_FLASH_BANK_INVALID; - } - - arm7_9 = target_to_arm7_9(bank->target); - if (!is_arm7_9(arm7_9)) - return ERROR_TARGET_INVALID; - - ocl = bank->driver_priv = malloc(sizeof(struct ocl_priv)); - ocl->jtag_info = &arm7_9->jtag_info; - ocl->buflen = 0; - ocl->bufalign = 1; - - return ERROR_OK; -} - -static int ocl_erase(struct flash_bank *bank, int first, int last) -{ - struct ocl_priv *ocl = bank->driver_priv; - int retval; - uint32_t dcc_buffer[3]; - - /* check preconditions */ - if (bank->num_sectors == 0) - return ERROR_FLASH_BANK_NOT_PROBED; - - if (bank->target->state != TARGET_RUNNING) - { - LOG_ERROR("target has to be running to communicate with the loader"); - return ERROR_TARGET_NOT_RUNNING; - } - - if ((first == 0) && (last == bank->num_sectors - 1)) - { - dcc_buffer[0] = OCL_ERASE_ALL; - if ((retval = embeddedice_send(ocl->jtag_info, dcc_buffer, 1) != ERROR_OK)) - return retval; - } - else - { - dcc_buffer[0] = OCL_ERASE_BLOCK; - dcc_buffer[1] = first; - dcc_buffer[2] = last; - if ((retval = embeddedice_send(ocl->jtag_info, dcc_buffer, 3) != ERROR_OK)) - return retval; - } - - /* wait for response, fixed timeout of 1 s */ - if ((retval = embeddedice_handshake(ocl->jtag_info, EICE_COMM_CTRL_WBIT, 1000) != ERROR_OK)) - { - if (retval == ERROR_TARGET_TIMEOUT) - LOG_ERROR("loader not responding"); - return retval; - } - - /* receive response */ - if ((retval = embeddedice_receive(ocl->jtag_info, dcc_buffer + 1, 1) != ERROR_OK)) - return retval; - - if (dcc_buffer[1] != OCL_CMD_DONE) - { - if (dcc_buffer[0] == OCL_ERASE_ALL) - LOG_ERROR("loader response to OCL_ERASE_ALL 0x%08" PRIx32 "", dcc_buffer[1]); - else - LOG_ERROR("loader response to OCL_ERASE_BLOCK 0x%08" PRIx32 "", dcc_buffer[1]); - return ERROR_FLASH_OPERATION_FAILED; - } - - return ERROR_OK; -} - -static int ocl_protect(struct flash_bank *bank, int set, int first, int last) -{ - return ERROR_OK; -} - -static int ocl_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) -{ - struct ocl_priv *ocl = bank->driver_priv; - int retval; - uint32_t *dcc_buffer; - uint32_t *dcc_bufptr; - int byteofs; - int runlen; - uint32_t chksum; - - int i; - - /* check preconditions */ - if (ocl->buflen == 0 || ocl->bufalign == 0) - return ERROR_FLASH_BANK_NOT_PROBED; - - if (bank->target->state != TARGET_RUNNING) - { - LOG_ERROR("target has to be running to communicate with the loader"); - return ERROR_TARGET_NOT_RUNNING; - } - - /* allocate buffer for max. ocl buffer + overhead */ - dcc_buffer = malloc(sizeof(uint32_t)*(ocl->buflen/4 + 3)); - - while (count) - { - if (count + (offset % ocl->bufalign) > ocl->buflen) - runlen = ocl->buflen - (offset % ocl->bufalign); - else - runlen = count; - - dcc_buffer[0] = OCL_FLASH_BLOCK | runlen; - dcc_buffer[1] = offset; - dcc_bufptr = &dcc_buffer[2]; - - *dcc_bufptr = 0xffffffff; - byteofs = (offset % ocl->bufalign) % 4; - chksum = OCL_CHKS_INIT; - - /* copy data to DCC buffer in proper byte order and properly aligned */ - for (i = 0; i < runlen; i++) - { - switch (byteofs++) - { - case 0: - *dcc_bufptr &= *(buffer++) | 0xffffff00; - break; - case 1: - *dcc_bufptr &= ((*(buffer++)) << 8) | 0xffff00ff; - break; - case 2: - *dcc_bufptr &= ((*(buffer++)) << 16) | 0xff00ffff; - break; - case 3: - *dcc_bufptr &= ((*(buffer++)) << 24) | 0x00ffffff; - chksum ^= *(dcc_bufptr++); - *dcc_bufptr = 0xffffffff; - byteofs = 0; - break; - } - } - - /* add the remaining word to checksum */ - if (byteofs) - chksum ^= *(dcc_bufptr++); - - *(dcc_bufptr++) = chksum; - - /* send the data */ - if ((retval = embeddedice_send(ocl->jtag_info, dcc_buffer, dcc_bufptr-dcc_buffer)) != ERROR_OK) - { - free(dcc_buffer); - return retval; - } - - /* wait for response, fixed timeout of 1 s */ - if ((retval = embeddedice_handshake(ocl->jtag_info, EICE_COMM_CTRL_WBIT, 1000) != ERROR_OK)) - { - if (retval == ERROR_TARGET_TIMEOUT) - LOG_ERROR("loader not responding"); - free(dcc_buffer); - return retval; - } - - /* receive response */ - if ((retval = embeddedice_receive(ocl->jtag_info, dcc_buffer, 1) != ERROR_OK)) - { - free(dcc_buffer); - return retval; - } - - if (dcc_buffer[0] != OCL_CMD_DONE) - { - LOG_ERROR("loader response to OCL_FLASH_BLOCK 0x%08" PRIx32 "", dcc_buffer[0]); - free(dcc_buffer); - return ERROR_FLASH_OPERATION_FAILED; - } - - count -= runlen; - offset += runlen; - } - - free(dcc_buffer); - return ERROR_OK; -} - -static int ocl_probe(struct flash_bank *bank) -{ - struct ocl_priv *ocl = bank->driver_priv; - int retval; - uint32_t dcc_buffer[1]; - int sectsize; - int i; - - /* purge pending data in DCC */ - embeddedice_receive(ocl->jtag_info, dcc_buffer, 1); - - dcc_buffer[0] = OCL_PROBE; - if ((retval = embeddedice_send(ocl->jtag_info, dcc_buffer, 1) != ERROR_OK)) - return retval; - - /* wait for response, fixed timeout of 1 s */ - if ((retval = embeddedice_handshake(ocl->jtag_info, EICE_COMM_CTRL_WBIT, 1000) != ERROR_OK)) - { - if (retval == ERROR_TARGET_TIMEOUT) - LOG_ERROR("loader not responding"); - return retval; - } - - /* receive response */ - if ((retval = embeddedice_receive(ocl->jtag_info, dcc_buffer, 1) != ERROR_OK)) - return retval; - - if (dcc_buffer[0] != OCL_CMD_DONE) - { - LOG_ERROR("loader response to OCL_PROBE 0x%08" PRIx32 "", dcc_buffer[0]); - return ERROR_FLASH_OPERATION_FAILED; - } - - /* receive and fill in parameters, detection of loader is important, receive it one by one */ - if ((retval = embeddedice_handshake(ocl->jtag_info, EICE_COMM_CTRL_WBIT, 0) != ERROR_OK) - || (retval = embeddedice_receive(ocl->jtag_info, dcc_buffer, 1) != ERROR_OK)) - return retval; - bank->base = dcc_buffer[0]; - - if ((retval = embeddedice_handshake(ocl->jtag_info, EICE_COMM_CTRL_WBIT, 0) != ERROR_OK) - || (retval = embeddedice_receive(ocl->jtag_info, dcc_buffer, 1) != ERROR_OK)) - return retval; - bank->size = dcc_buffer[0]; - - if ((retval = embeddedice_handshake(ocl->jtag_info, EICE_COMM_CTRL_WBIT, 0) != ERROR_OK) - || (retval = embeddedice_receive(ocl->jtag_info, dcc_buffer, 1) != ERROR_OK)) - return retval; - bank->num_sectors = dcc_buffer[0]; - - if ((retval = embeddedice_handshake(ocl->jtag_info, EICE_COMM_CTRL_WBIT, 0) != ERROR_OK) - || (retval = embeddedice_receive(ocl->jtag_info, dcc_buffer, 1) != ERROR_OK)) - return retval; - ocl->buflen = dcc_buffer[0] & 0xffff; - ocl->bufalign = dcc_buffer[0] >> 16; - - bank->sectors = realloc(bank->sectors, sizeof(struct flash_sector)*bank->num_sectors); - if (bank->num_sectors == 0) - { - LOG_ERROR("number of sectors shall be non zero value"); - return ERROR_FLASH_BANK_INVALID; - } - if (bank->size % bank->num_sectors) { - LOG_ERROR("bank size not divisible by number of sectors"); - return ERROR_FLASH_BANK_INVALID; - } - sectsize = bank->size / bank->num_sectors; - for (i = 0; i < bank->num_sectors; i++) - { - bank->sectors[i].offset = i * sectsize; - bank->sectors[i].size = sectsize; - bank->sectors[i].is_erased = -1; - bank->sectors[i].is_protected = -1; - } - - if (ocl->bufalign == 0) - ocl->bufalign = 1; - - if (ocl->buflen == 0) - { - LOG_ERROR("buflen shall be non zero value"); - return ERROR_FLASH_BANK_INVALID; - } - - if ((ocl->bufalign > ocl->buflen) || (ocl->buflen % ocl->bufalign)) - { - LOG_ERROR("buflen is not multiple of bufalign"); - return ERROR_FLASH_BANK_INVALID; - } - - if (ocl->buflen % 4) - { - LOG_ERROR("buflen shall be divisible by 4"); - return ERROR_FLASH_BANK_INVALID; - } - - return ERROR_OK; -} - -static int ocl_info(struct flash_bank *bank, char *buf, int buf_size) -{ - return ERROR_OK; -} - -static int ocl_auto_probe(struct flash_bank *bank) -{ - struct ocl_priv *ocl = bank->driver_priv; - - if (ocl->buflen == 0 || ocl->bufalign == 0) - return ERROR_FLASH_BANK_NOT_PROBED; - - return ERROR_OK; -} - -struct flash_driver ocl_flash = { - .name = "ocl", - .flash_bank_command = &ocl_flash_bank_command, - .erase = &ocl_erase, - .protect = &ocl_protect, - .write = &ocl_write, - .probe = &ocl_probe, - .erase_check = &ocl_erase_check, - .protect_check = &ocl_protect_check, - .info = &ocl_info, - .auto_probe = &ocl_auto_probe, - }; diff --git a/src/flash/ocl.h b/src/flash/ocl.h deleted file mode 100644 index d5c430be..00000000 --- a/src/flash/ocl.h +++ /dev/null @@ -1,40 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2007 by Pavel Chromy * - * chromy@asix.cz * - * * - * 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 OCL_H -#define OCL_H - -/* command/response mask */ -#define OCL_CMD_MASK 0xFFFF0000L - -/* commads */ -#define OCL_FLASH_BLOCK 0x0CFB0000L -#define OCL_ERASE_BLOCK 0x0CEB0000L -#define OCL_ERASE_ALL 0x0CEA0000L -#define OCL_PROBE 0x0CBE0000L - -/* responses */ -#define OCL_CMD_DONE 0x0ACD0000L -#define OCL_CMD_ERR 0x0ACE0000L -#define OCL_CHKS_FAIL 0x0ACF0000L -#define OCL_BUFF_OVER 0x0AB00000L - -#define OCL_CHKS_INIT 0xC100CD0CL - -#endif /* OCL_H */ diff --git a/src/flash/pic32mx.c b/src/flash/pic32mx.c deleted file mode 100644 index 9bb6c97e..00000000 --- a/src/flash/pic32mx.c +++ /dev/null @@ -1,922 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2005 by Dominic Rath * - * Dominic.Rath@gmx.de * - * * - * Copyright (C) 2008 by Spencer Oliver * - * spen@spen-soft.co.uk * - * * - * Copyright (C) 2008 by John McCarthy * - * jgmcc@magma.ca * - * * - * 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 "pic32mx.h" -#include "mips32.h" - - -static -struct pic32mx_devs_s { - uint8_t devid; - char *name; - uint32_t pfm_size; -} pic32mx_devs[] = { - { 0x78, "460F512L USB", 512 }, - { 0x74, "460F256L USB", 256 }, - { 0x6D, "440F128L USB", 128 }, - { 0x56, "440F512H USB", 512 }, - { 0x52, "440F256H USB", 256 }, - { 0x4D, "440F128H USB", 128 }, - { 0x42, "420F032H USB", 32 }, - { 0x38, "360F512L", 512 }, - { 0x34, "360F256L", 256 }, - { 0x2D, "340F128L", 128 }, - { 0x2A, "320F128L", 128 }, - { 0x16, "340F512H", 512 }, - { 0x12, "340F256H", 256 }, - { 0x0D, "340F128H", 128 }, - { 0x0A, "320F128H", 128 }, - { 0x06, "320F064H", 64 }, - { 0x02, "320F032H", 32 }, - { 0x00, NULL, 0 } -}; - -static int pic32mx_write_row(struct flash_bank *bank, uint32_t address, uint32_t srcaddr); -static int pic32mx_write_word(struct flash_bank *bank, uint32_t address, uint32_t word); - -/* flash bank pic32mx 0 0 - */ -FLASH_BANK_COMMAND_HANDLER(pic32mx_flash_bank_command) -{ - struct pic32mx_flash_bank *pic32mx_info; - - if (CMD_ARGC < 6) - { - LOG_WARNING("incomplete flash_bank pic32mx configuration"); - return ERROR_FLASH_BANK_INVALID; - } - - pic32mx_info = malloc(sizeof(struct pic32mx_flash_bank)); - bank->driver_priv = pic32mx_info; - - pic32mx_info->write_algorithm = NULL; - pic32mx_info->probed = 0; - - return ERROR_OK; -} - -static uint32_t pic32mx_get_flash_status(struct flash_bank *bank) -{ - struct target *target = bank->target; - uint32_t status; - - target_read_u32(target, PIC32MX_NVMCON, &status); - - return status; -} - -static uint32_t pic32mx_wait_status_busy(struct flash_bank *bank, int timeout) -{ - uint32_t status; - - /* wait for busy to clear */ - while (((status = pic32mx_get_flash_status(bank)) & NVMCON_NVMWR) && (timeout-- > 0)) - { - LOG_DEBUG("status: 0x%" PRIx32, status); - alive_sleep(1); - } - if (timeout <= 0) - LOG_DEBUG("timeout: status: 0x%" PRIx32, status); - - return status; -} - -static int pic32mx_nvm_exec(struct flash_bank *bank, uint32_t op, uint32_t timeout) -{ - struct target *target = bank->target; - uint32_t status; - - target_write_u32(target, PIC32MX_NVMCON, NVMCON_NVMWREN | op); - - /* unlock flash registers */ - target_write_u32(target, PIC32MX_NVMKEY, NVMKEY1); - target_write_u32(target, PIC32MX_NVMKEY, NVMKEY2); - - /* start operation */ - target_write_u32(target, PIC32MX_NVMCONSET, NVMCON_NVMWR); - - status = pic32mx_wait_status_busy(bank, timeout); - - /* lock flash registers */ - target_write_u32(target, PIC32MX_NVMCONCLR, NVMCON_NVMWREN); - - return status; -} - -static int pic32mx_protect_check(struct flash_bank *bank) -{ - struct target *target = bank->target; - - uint32_t devcfg0; - int s; - int num_pages; - - if (target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - target_read_u32(target, PIC32MX_DEVCFG0, &devcfg0); - if ((devcfg0 & (1 << 28)) == 0) /* code protect bit */ - num_pages = 0xffff; /* All pages protected */ - else if (bank->base == PIC32MX_KSEG1_BOOT_FLASH) - { - if (devcfg0 & (1 << 24)) - num_pages = 0; /* All pages unprotected */ - else - num_pages = 0xffff; /* All pages protected */ - } - else /* pgm flash */ - num_pages = (~devcfg0 >> 12) & 0xff; - for (s = 0; s < bank->num_sectors && s < num_pages; s++) - bank->sectors[s].is_protected = 1; - for (; s < bank->num_sectors; s++) - bank->sectors[s].is_protected = 0; - - return ERROR_OK; -} - -static int pic32mx_erase(struct flash_bank *bank, int first, int last) -{ - struct target *target = bank->target; - int i; - uint32_t status; - - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - if ((first == 0) && (last == (bank->num_sectors - 1)) && (bank->base == PIC32MX_KSEG0_PGM_FLASH || bank->base == PIC32MX_KSEG1_PGM_FLASH)) - { - LOG_DEBUG("Erasing entire program flash"); - status = pic32mx_nvm_exec(bank, NVMCON_OP_PFM_ERASE, 50); - if (status & NVMCON_NVMERR) - return ERROR_FLASH_OPERATION_FAILED; - if (status & NVMCON_LVDERR) - return ERROR_FLASH_OPERATION_FAILED; - return ERROR_OK; - } - - for (i = first; i <= last; i++) - { - if (bank->base >= PIC32MX_KSEG1_PGM_FLASH) - target_write_u32(target, PIC32MX_NVMADDR, KS1Virt2Phys(bank->base + bank->sectors[i].offset)); - else - target_write_u32(target, PIC32MX_NVMADDR, KS0Virt2Phys(bank->base + bank->sectors[i].offset)); - - status = pic32mx_nvm_exec(bank, NVMCON_OP_PAGE_ERASE, 10); - - if (status & NVMCON_NVMERR) - return ERROR_FLASH_OPERATION_FAILED; - if (status & NVMCON_LVDERR) - return ERROR_FLASH_OPERATION_FAILED; - bank->sectors[i].is_erased = 1; - } - - return ERROR_OK; -} - -static int pic32mx_protect(struct flash_bank *bank, int set, int first, int last) -{ - struct pic32mx_flash_bank *pic32mx_info = NULL; - struct target *target = bank->target; -#if 0 - uint16_t prot_reg[4] = {0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF}; - int i, reg, bit; - int status; - uint32_t protection; -#endif - - pic32mx_info = bank->driver_priv; - - if (target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - -#if 0 - if ((first && (first % pic32mx_info->ppage_size)) || ((last + 1) && (last + 1) % pic32mx_info->ppage_size)) - { - LOG_WARNING("sector start/end incorrect - stm32 has %dK sector protection", pic32mx_info->ppage_size); - return ERROR_FLASH_SECTOR_INVALID; - } - - /* medium density - each bit refers to a 4bank protection - * high density - each bit refers to a 2bank protection */ - target_read_u32(target, PIC32MX_FLASH_WRPR, &protection); - - prot_reg[0] = (uint16_t)protection; - prot_reg[1] = (uint16_t)(protection >> 8); - prot_reg[2] = (uint16_t)(protection >> 16); - prot_reg[3] = (uint16_t)(protection >> 24); - - if (pic32mx_info->ppage_size == 2) - { - /* high density flash */ - - /* bit 7 controls sector 62 - 255 protection */ - if (last > 61) - { - if (set) - prot_reg[3] &= ~(1 << 7); - else - prot_reg[3] |= (1 << 7); - } - - if (first > 61) - first = 62; - if (last > 61) - last = 61; - - for (i = first; i <= last; i++) - { - reg = (i / pic32mx_info->ppage_size) / 8; - bit = (i / pic32mx_info->ppage_size) - (reg * 8); - - if (set) - prot_reg[reg] &= ~(1 << bit); - else - prot_reg[reg] |= (1 << bit); - } - } - else - { - /* medium density flash */ - for (i = first; i <= last; i++) - { - reg = (i / pic32mx_info->ppage_size) / 8; - bit = (i / pic32mx_info->ppage_size) - (reg * 8); - - if (set) - prot_reg[reg] &= ~(1 << bit); - else - prot_reg[reg] |= (1 << bit); - } - } - - if ((status = pic32mx_erase_options(bank)) != ERROR_OK) - return status; - - pic32mx_info->option_bytes.protection[0] = prot_reg[0]; - pic32mx_info->option_bytes.protection[1] = prot_reg[1]; - pic32mx_info->option_bytes.protection[2] = prot_reg[2]; - pic32mx_info->option_bytes.protection[3] = prot_reg[3]; - - return pic32mx_write_options(bank); -#else - return ERROR_OK; -#endif -} - -static int pic32mx_write_block(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) -{ - struct target *target = bank->target; - uint32_t buffer_size = 512; - struct working_area *source; - uint32_t address = bank->base + offset; - int retval = ERROR_OK; -#if 0 - struct pic32mx_flash_bank *pic32mx_info = bank->driver_priv; - struct armv7m_algorithm armv7m_info; - - uint8_t pic32mx_flash_write_code[] = { - /* write: */ - 0xDF, 0xF8, 0x24, 0x40, /* ldr r4, PIC32MX_FLASH_CR */ - 0x09, 0x4D, /* ldr r5, PIC32MX_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, /* PIC32MX_FLASH_CR: .word 0x40022010 */ - 0x0C, 0x20, 0x02, 0x40 /* PIC32MX_FLASH_SR: .word 0x4002200C */ - }; - - /* flash write code */ - if (target_alloc_working_area(target, sizeof(pic32mx_flash_write_code), &pic32mx_info->write_algorithm) != ERROR_OK) - { - LOG_WARNING("no working area available, can't do block memory writes"); - return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; - }; - - if ((retval = target_write_buffer(target, pic32mx_info->write_algorithm->address, sizeof(pic32mx_flash_write_code), pic32mx_flash_write_code)) != ERROR_OK) - return retval; -#endif - - /* memory buffer */ - if (target_alloc_working_area(target, buffer_size, &source) != ERROR_OK) - { -#if 0 - /* if we already allocated the writing code, but failed to get a buffer, free the algorithm */ - if (pic32mx_info->write_algorithm) - target_free_working_area(target, pic32mx_info->write_algorithm); -#endif - - LOG_WARNING("no large enough working area available, can't do block memory writes"); - return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; - } - - while (count >= buffer_size/4) - { - uint32_t status; - - if ((retval = target_write_buffer(target, source->address, buffer_size, buffer)) != ERROR_OK) { - LOG_ERROR("Failed to write row buffer (%d words) to RAM", (int)(buffer_size/4)); - break; - } - -#if 0 - 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, buffer_size/4); - - if ((retval = target_run_algorithm(target, 0, NULL, 4, reg_params, pic32mx_info->write_algorithm->address, \ - pic32mx_info->write_algorithm->address + (sizeof(pic32mx_flash_write_code) - 10), 10000, &armv7m_info)) != ERROR_OK) - { - LOG_ERROR("error executing pic32mx flash write algorithm"); - retval = ERROR_FLASH_OPERATION_FAILED; - break; - } - - if (buf_get_u32(reg_params[3].value, 0, 32) & 0x14) - { - retval = ERROR_FLASH_OPERATION_FAILED; - break; - } -#endif - status = pic32mx_write_row(bank, address, source->address); - if (status & NVMCON_NVMERR) { - LOG_ERROR("Flash write error NVMERR (status = 0x%08" PRIx32 ")", status); - retval = ERROR_FLASH_OPERATION_FAILED; - break; - } - if (status & NVMCON_LVDERR) { - LOG_ERROR("Flash write error LVDERR (status = 0x%08" PRIx32 ")", status); - retval = ERROR_FLASH_OPERATION_FAILED; - break; - } - - buffer += buffer_size; - address += buffer_size; - count -= buffer_size/4; - } - - target_free_working_area(target, source); - - while (count > 0) - { - uint32_t value; - memcpy(&value, buffer, sizeof(uint32_t)); - - uint32_t status = pic32mx_write_word(bank, address, value); - if (status & NVMCON_NVMERR) { - LOG_ERROR("Flash write error NVMERR (status = 0x%08" PRIx32 ")", status); - retval = ERROR_FLASH_OPERATION_FAILED; - break; - } - if (status & NVMCON_LVDERR) { - LOG_ERROR("Flash write error LVDERR (status = 0x%08" PRIx32 ")", status); - retval = ERROR_FLASH_OPERATION_FAILED; - break; - } - - buffer += 4; - address += 4; - count--; - } - - return retval; -} - -static int pic32mx_write_word(struct flash_bank *bank, uint32_t address, uint32_t word) -{ - struct target *target = bank->target; - - if (bank->base >= PIC32MX_KSEG1_PGM_FLASH) - target_write_u32(target, PIC32MX_NVMADDR, KS1Virt2Phys(address)); - else - target_write_u32(target, PIC32MX_NVMADDR, KS0Virt2Phys(address)); - target_write_u32(target, PIC32MX_NVMDATA, word); - - return pic32mx_nvm_exec(bank, NVMCON_OP_WORD_PROG, 5); -} - -/* - * Write a 128 word (512 byte) row to flash address from RAM srcaddr. - */ -static int pic32mx_write_row(struct flash_bank *bank, uint32_t address, uint32_t srcaddr) -{ - struct target *target = bank->target; - - LOG_DEBUG("addr: 0x%08" PRIx32 " srcaddr: 0x%08" PRIx32 "", address, srcaddr); - - if (address >= PIC32MX_KSEG1_PGM_FLASH) - target_write_u32(target, PIC32MX_NVMADDR, KS1Virt2Phys(address)); - else - target_write_u32(target, PIC32MX_NVMADDR, KS0Virt2Phys(address)); - if (srcaddr >= PIC32MX_KSEG1_RAM) - target_write_u32(target, PIC32MX_NVMSRCADDR, KS1Virt2Phys(srcaddr)); - else - target_write_u32(target, PIC32MX_NVMSRCADDR, KS0Virt2Phys(srcaddr)); - - return pic32mx_nvm_exec(bank, NVMCON_OP_ROW_PROG, 100); -} - -static int pic32mx_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) -{ - uint32_t words_remaining = (count / 4); - uint32_t bytes_remaining = (count & 0x00000003); - uint32_t address = bank->base + offset; - uint32_t bytes_written = 0; - uint32_t status; - int retval; - - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - if (offset & 0x3) - { - LOG_WARNING("offset 0x%" PRIx32 "breaks required 4-byte alignment", offset); - return ERROR_FLASH_DST_BREAKS_ALIGNMENT; - } - - /* multiple words (4-byte) to be programmed? */ - if (words_remaining > 0) - { - /* try using a block write */ - if ((retval = pic32mx_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 */ - LOG_WARNING("couldn't use block writes, falling back to single memory accesses"); - } - else if (retval == ERROR_FLASH_OPERATION_FAILED) - { - LOG_ERROR("flash writing failed with error code: 0x%x", retval); - return ERROR_FLASH_OPERATION_FAILED; - } - } - else - { - buffer += words_remaining * 4; - address += words_remaining * 4; - words_remaining = 0; - } - } - - while (words_remaining > 0) - { - uint32_t value; - memcpy(&value, buffer + bytes_written, sizeof(uint32_t)); - - status = pic32mx_write_word(bank, address, value); - if (status & NVMCON_NVMERR) - return ERROR_FLASH_OPERATION_FAILED; - if (status & NVMCON_LVDERR) - return ERROR_FLASH_OPERATION_FAILED; - - bytes_written += 4; - words_remaining--; - address += 4; - } - - if (bytes_remaining) - { - uint32_t value = 0xffffffff; - memcpy(&value, buffer + bytes_written, bytes_remaining); - - status = pic32mx_write_word(bank, address, value); - if (status & NVMCON_NVMERR) - return ERROR_FLASH_OPERATION_FAILED; - if (status & NVMCON_LVDERR) - return ERROR_FLASH_OPERATION_FAILED; - } - - return ERROR_OK; -} - -static int pic32mx_probe(struct flash_bank *bank) -{ - struct target *target = bank->target; - struct pic32mx_flash_bank *pic32mx_info = bank->driver_priv; - struct mips32_common *mips32 = target->arch_info; - struct mips_ejtag *ejtag_info = &mips32->ejtag_info; - int i; - uint16_t num_pages = 0; - uint32_t device_id; - int page_size; - - pic32mx_info->probed = 0; - - device_id = ejtag_info->idcode; - LOG_INFO("device id = 0x%08" PRIx32 " (manuf 0x%03x dev 0x%02x, ver 0x%03x)", - device_id, - (unsigned)((device_id >> 1)&0x7ff), - (unsigned)((device_id >> 12)&0xff), - (unsigned)((device_id >> 20)&0xfff)); - - if (((device_id >> 1)&0x7ff) != PIC32MX_MANUF_ID) { - LOG_WARNING("Cannot identify target as a PIC32MX family."); - return ERROR_FLASH_OPERATION_FAILED; - } - - page_size = 4096; - if (bank->base == PIC32MX_KSEG1_BOOT_FLASH || bank->base == 1) { - /* 0xBFC00000: Boot flash size fixed at 12k */ - num_pages = 12; - } else { - /* 0xBD000000: Program flash size varies with device */ - for (i = 0; pic32mx_devs[i].name != NULL; i++) - if (pic32mx_devs[i].devid == ((device_id >> 12) & 0xff)) { - num_pages = pic32mx_devs[i].pfm_size; - break; - } - if (pic32mx_devs[i].name == NULL) { - LOG_WARNING("Cannot identify target as a PIC32MX family."); - return ERROR_FLASH_OPERATION_FAILED; - } - } - -#if 0 - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - /* get flash size from target */ - if (target_read_u16(target, 0x1FFFF7E0, &num_pages) != ERROR_OK) - { - /* failed reading flash size, default to max target family */ - num_pages = 0xffff; - } -#endif - - LOG_INFO("flash size = %dkbytes", num_pages); - - /* calculate numbers of pages */ - num_pages /= (page_size / 1024); - - if (bank->base == 0) bank->base = PIC32MX_KSEG1_PGM_FLASH; - if (bank->base == 1) bank->base = PIC32MX_KSEG1_BOOT_FLASH; - bank->size = (num_pages * page_size); - bank->num_sectors = num_pages; - bank->chip_width = 4; - bank->bus_width = 4; - bank->sectors = malloc(sizeof(struct flash_sector) * num_pages); - - for (i = 0; i < num_pages; i++) - { - bank->sectors[i].offset = i * page_size; - bank->sectors[i].size = page_size; - bank->sectors[i].is_erased = -1; - bank->sectors[i].is_protected = 1; - } - - pic32mx_info->probed = 1; - - return ERROR_OK; -} - -static int pic32mx_auto_probe(struct flash_bank *bank) -{ - struct pic32mx_flash_bank *pic32mx_info = bank->driver_priv; - if (pic32mx_info->probed) - return ERROR_OK; - return pic32mx_probe(bank); -} - -#if 0 -COMMAND_HANDLER(pic32mx_handle_part_id_command) -{ - return ERROR_OK; -} -#endif - -static int pic32mx_info(struct flash_bank *bank, char *buf, int buf_size) -{ - struct target *target = bank->target; - struct mips32_common *mips32 = target->arch_info; - struct mips_ejtag *ejtag_info = &mips32->ejtag_info; - uint32_t device_id; - int printed = 0, i; - - device_id = ejtag_info->idcode; - - if (((device_id >> 1)&0x7ff) != PIC32MX_MANUF_ID) { - snprintf(buf, buf_size, - "Cannot identify target as a PIC32MX family (manufacturer 0x%03d != 0x%03d)\n", - (unsigned)((device_id >> 1)&0x7ff), - PIC32MX_MANUF_ID); - return ERROR_FLASH_OPERATION_FAILED; - } - for (i = 0; pic32mx_devs[i].name != NULL; i++) - if (pic32mx_devs[i].devid == ((device_id >> 12) & 0xff)) { - printed = snprintf(buf, buf_size, "PIC32MX%s", pic32mx_devs[i].name); - break; - } - if (pic32mx_devs[i].name == NULL) { - snprintf(buf, buf_size, "Cannot identify target as a PIC32MX family\n"); - return ERROR_FLASH_OPERATION_FAILED; - } - buf += printed; - buf_size -= printed; - printed = snprintf(buf, buf_size, " Ver: 0x%03x", - (unsigned)((device_id >> 20)&0xfff)); - - return ERROR_OK; -} - -#if 0 -COMMAND_HANDLER(pic32mx_handle_lock_command) -{ - struct target *target = NULL; - struct pic32mx_flash_bank *pic32mx_info = NULL; - - if (CMD_ARGC < 1) - { - command_print(CMD_CTX, "pic32mx lock "); - return ERROR_OK; - } - - struct flash_bank *bank; - int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) - return retval; - - pic32mx_info = bank->driver_priv; - - target = bank->target; - - if (target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - if (pic32mx_erase_options(bank) != ERROR_OK) - { - command_print(CMD_CTX, "pic32mx failed to erase options"); - return ERROR_OK; - } - - /* set readout protection */ - pic32mx_info->option_bytes.RDP = 0; - - if (pic32mx_write_options(bank) != ERROR_OK) - { - command_print(CMD_CTX, "pic32mx failed to lock device"); - return ERROR_OK; - } - - command_print(CMD_CTX, "pic32mx locked"); - - return ERROR_OK; -} - -COMMAND_HANDLER(pic32mx_handle_unlock_command) -{ - struct target *target = NULL; - struct pic32mx_flash_bank *pic32mx_info = NULL; - - if (CMD_ARGC < 1) - { - command_print(CMD_CTX, "pic32mx unlock "); - return ERROR_OK; - } - - struct flash_bank *bank; - int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) - return retval; - - pic32mx_info = bank->driver_priv; - - target = bank->target; - - if (target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - if (pic32mx_erase_options(bank) != ERROR_OK) - { - command_print(CMD_CTX, "pic32mx failed to unlock device"); - return ERROR_OK; - } - - if (pic32mx_write_options(bank) != ERROR_OK) - { - command_print(CMD_CTX, "pic32mx failed to lock device"); - return ERROR_OK; - } - - command_print(CMD_CTX, "pic32mx unlocked"); - - return ERROR_OK; -} -#endif - -#if 0 -static int pic32mx_chip_erase(struct flash_bank *bank) -{ - struct target *target = bank->target; -#if 0 - uint32_t status; -#endif - - if (target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - LOG_INFO("PIC32MX chip erase called"); - -#if 0 - /* unlock option flash registers */ - target_write_u32(target, PIC32MX_FLASH_KEYR, KEY1); - target_write_u32(target, PIC32MX_FLASH_KEYR, KEY2); - - /* chip erase flash memory */ - target_write_u32(target, PIC32MX_FLASH_CR, FLASH_MER); - target_write_u32(target, PIC32MX_FLASH_CR, FLASH_MER | FLASH_STRT); - - status = pic32mx_wait_status_busy(bank, 10); - - target_write_u32(target, PIC32MX_FLASH_CR, FLASH_LOCK); - - if (status & FLASH_WRPRTERR) - { - LOG_ERROR("pic32mx device protected"); - return ERROR_OK; - } - - if (status & FLASH_PGERR) - { - LOG_ERROR("pic32mx device programming failed"); - return ERROR_OK; - } -#endif - - return ERROR_OK; -} -#endif - -COMMAND_HANDLER(pic32mx_handle_chip_erase_command) -{ -#if 0 - int i; - - if (CMD_ARGC != 0) - { - command_print(CMD_CTX, "pic32mx chip_erase"); - return ERROR_OK; - } - - struct flash_bank *bank; - int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) - return retval; - - if (pic32mx_chip_erase(bank) == ERROR_OK) - { - /* set all sectors as erased */ - for (i = 0; i < bank->num_sectors; i++) - { - bank->sectors[i].is_erased = 1; - } - - command_print(CMD_CTX, "pic32mx chip erase complete"); - } - else - { - command_print(CMD_CTX, "pic32mx chip erase failed"); - } -#endif - - return ERROR_OK; -} - -COMMAND_HANDLER(pic32mx_handle_pgm_word_command) -{ - uint32_t address, value; - int status, res; - - if (CMD_ARGC != 3) - { - command_print(CMD_CTX, "pic32mx pgm_word "); - return ERROR_OK; - } - - COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], address); - COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value); - - struct flash_bank *bank; - int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 2, &bank); - if (ERROR_OK != retval) - return retval; - - if (address < bank->base || address >= (bank->base + bank->size)) - { - command_print(CMD_CTX, "flash address '%s' is out of bounds", CMD_ARGV[0]); - return ERROR_OK; - } - - res = ERROR_OK; - status = pic32mx_write_word(bank, address, value); - if (status & NVMCON_NVMERR) - res = ERROR_FLASH_OPERATION_FAILED; - if (status & NVMCON_LVDERR) - res = ERROR_FLASH_OPERATION_FAILED; - - if (res == ERROR_OK) - command_print(CMD_CTX, "pic32mx pgm word complete"); - else - command_print(CMD_CTX, "pic32mx pgm word failed (status = 0x%x)", status); - - return ERROR_OK; -} -static const struct command_registration pic32mx_exec_command_handlers[] = { - { - .name = "chip_erase", - .handler = &pic32mx_handle_chip_erase_command, - .mode = COMMAND_EXEC, - .help = "erase device", - }, - { - .name = "pgm_word", - .handler = &pic32mx_handle_pgm_word_command, - .mode = COMMAND_EXEC, - .help = "program a word", - }, - COMMAND_REGISTRATION_DONE -}; -static const struct command_registration pic32mx_command_handlers[] = { - { - .name = "pic32mx", - .mode = COMMAND_ANY, - .help = "pic32mx flash command group", - .chain = pic32mx_exec_command_handlers, - }, - COMMAND_REGISTRATION_DONE -}; - -struct flash_driver pic32mx_flash = { - .name = "pic32mx", - .commands = pic32mx_command_handlers, - .flash_bank_command = &pic32mx_flash_bank_command, - .erase = &pic32mx_erase, - .protect = &pic32mx_protect, - .write = &pic32mx_write, - .probe = &pic32mx_probe, - .auto_probe = &pic32mx_auto_probe, - .erase_check = &default_flash_mem_blank_check, - .protect_check = &pic32mx_protect_check, - .info = &pic32mx_info, - }; diff --git a/src/flash/pic32mx.h b/src/flash/pic32mx.h deleted file mode 100644 index 92f40c2e..00000000 --- a/src/flash/pic32mx.h +++ /dev/null @@ -1,113 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2005 by Dominic Rath * - * Dominic.Rath@gmx.de * - * * - * Copyright (C) 2008 by Spencer Oliver * - * spen@spen-soft.co.uk * - * * - * Copyright (C) 2008 by John McCarthy * - * jgmcc@magma.ca * - * * - * 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 PIC32MX_H -#define PIC32MX_H - -#include "flash.h" - -struct pic32mx_flash_bank -{ - struct working_area *write_algorithm; - int devid; - int ppage_size; - int probed; -}; - -#define PIC32MX_MANUF_ID 0x029 - -/* pic32mx memory locations */ - -#define PIC32MX_KUSEG_PGM_FLASH 0x7D000000 -#define PIC32MX_KUSEG_RAM 0x7F000000 - -#define PIC32MX_KSEG0_RAM 0x80000000 -#define PIC32MX_KSEG0_PGM_FLASH 0x9D000000 -#define PIC32MX_KSEG0_BOOT_FLASH 0x9FC00000 - -#define PIC32MX_KSEG1_RAM 0xA0000000 -#define PIC32MX_KSEG1_PGM_FLASH 0xBD000000 -#define PIC32MX_KSEG1_PERIPHERAL 0xBF800000 -#define PIC32MX_KSEG1_BOOT_FLASH 0xBFC00000 - -#define PIC32MX_PHYS_RAM 0x00000000 -#define PIC32MX_PHYS_PGM_FLASH 0x1D000000 -#define PIC32MX_PHYS_PERIPHERALS 0x1F800000 -#define PIC32MX_PHYS_BOOT_FLASH 0x1FC00000 - -/* - * Translate Virtual and Physical addresses. - * Note: These macros only work for KSEG0/KSEG1 addresses. - */ -#define KS1Virt2Phys(vaddr) ((vaddr)-0xA0000000) -#define Phys2KS1Virt(paddr) ((paddr) + 0xA0000000) -#define KS0Virt2Phys(vaddr) ((vaddr)-0x80000000) -#define Phys2KS0Virt(paddr) ((paddr) + 0x80000000) - -/* pic32mx configuration register locations */ - -#define PIC32MX_DEVCFG0 0xBFC02FFC -#define PIC32MX_DEVCFG1 0xBFC02FF8 -#define PIC32MX_DEVCFG2 0xBFC02FF4 -#define PIC32MX_DEVCFG3 0XBFC02FF0 -#define PIC32MX_DEVID 0xBF80F220 - -/* pic32mx flash controller register locations */ - -#define PIC32MX_NVMCON 0xBF80F400 -#define PIC32MX_NVMCONCLR 0xBF80F404 -#define PIC32MX_NVMCONSET 0xBF80F408 -#define PIC32MX_NVMCONINV 0xBF80F40C -#define NVMCON_NVMWR (1 << 15) -#define NVMCON_NVMWREN (1 << 14) -#define NVMCON_NVMERR (1 << 13) -#define NVMCON_LVDERR (1 << 12) -#define NVMCON_LVDSTAT (1 << 11) -#define NVMCON_OP_PFM_ERASE 0x5 -#define NVMCON_OP_PAGE_ERASE 0x4 -#define NVMCON_OP_ROW_PROG 0x3 -#define NVMCON_OP_WORD_PROG 0x1 -#define NVMCON_OP_NOP 0x0 - -#define PIC32MX_NVMKEY 0xBF80F410 -#define PIC32MX_NVMADDR 0xBF80F420 -#define PIC32MX_NVMADDRCLR 0xBF80F424 -#define PIC32MX_NVMADDRSET 0xBF80F428 -#define PIC32MX_NVMADDRINV 0xBF80F42C -#define PIC32MX_NVMDATA 0xBF80F430 -#define PIC32MX_NVMSRCADDR 0xBF80F440 - -/* flash unlock keys */ - -#define NVMKEY1 0xAA996655 -#define NVMKEY2 0x556699AA - -struct pic32mx_mem_layout { - uint32_t sector_start; - uint32_t sector_size; -}; - -#endif /* PIC32MX_H */ - diff --git a/src/flash/stellaris.c b/src/flash/stellaris.c deleted file mode 100644 index 771f0a71..00000000 --- a/src/flash/stellaris.c +++ /dev/null @@ -1,1195 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2006 by Magnus Lundin * - * lundin@mlu.mine.nu * - * * - * Copyright (C) 2008 by Spencer Oliver * - * spen@spen-soft.co.uk * - * * - * 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, LM3S6965 -***************************************************************************/ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "stellaris.h" -#include "armv7m.h" -#include "binarybuffer.h" -#include "algorithm.h" - - -#define DID0_VER(did0) ((did0 >> 28)&0x07) - -static int stellaris_read_part_info(struct flash_bank *bank); -static uint32_t stellaris_get_flash_status(struct flash_bank *bank); -static void stellaris_set_flash_mode(struct flash_bank *bank,int mode); -//static uint32_t stellaris_wait_status_busy(struct flash_bank *bank, uint32_t waitbits, int timeout); - -static int stellaris_mass_erase(struct flash_bank *bank); - -static struct { - uint32_t partno; - char *partname; -} StellarisParts[] = -{ - {0x01,"LM3S101"}, - {0x02,"LM3S102"}, - {0x03,"LM3S1625"}, - {0x04,"LM3S1626"}, - {0x05,"LM3S1627"}, - {0x06,"LM3S1607"}, - {0x10,"LM3S1776"}, - {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"}, - /*{0x33,"LM3S2616"},*/ - {0x34,"LM3S815"}, - {0x36,"LM3S817"}, - {0x37,"LM3S818"}, - {0x35,"LM3S828"}, - {0x39,"LM3S2276"}, - {0x3A,"LM3S2776"}, - {0x43,"LM3S3651"}, - {0x44,"LM3S3739"}, - {0x45,"LM3S3749"}, - {0x46,"LM3S3759"}, - {0x48,"LM3S3768"}, - {0x49,"LM3S3748"}, - {0x50,"LM3S2678"}, - {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"}, - {0x80,"LM3S2671"}, - {0x81,"LM3S5632"}, - {0x82,"LM3S6422"}, - {0x83,"LM3S6633"}, - {0x84,"LM3S2139"}, - {0x85,"LM3S2637"}, - {0x86,"LM3S8738"}, - {0x88,"LM3S8938"}, - {0x89,"LM3S6938"}, - {0x8A,"LM3S5652"}, - {0x8B,"LM3S6637"}, - {0x8C,"LM3S8933"}, - {0x8D,"LM3S8733"}, - {0x8E,"LM3S8538"}, - {0x8F,"LM3S2948"}, - {0x91,"LM3S5662"}, - {0x96,"LM3S5732"}, - {0x97,"LM3S5737"}, - {0x99,"LM3S5747"}, - {0x9A,"LM3S5752"}, - {0x9B,"LM3S5757"}, - {0x9C,"LM3S5762"}, - {0x9D,"LM3S5767"}, - {0xA0,"LM3S5739"}, - {0xA1,"LM3S6100"}, - {0xA2,"LM3S2410"}, - {0xA3,"LM3S6730"}, - {0xA4,"LM3S2730"}, - {0xA5,"LM3S6420"}, - {0xA6,"LM3S8962"}, - {0xA7,"LM3S5749"}, - {0xA8,"LM3S5769"}, - {0xA9,"LM3S5768"}, - {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"} -}; - -static char * StellarisClassname[5] = -{ - "Sandstorm", - "Fury", - "Unknown", - "DustDevil", - "Tempest" -}; - -/*************************************************************************** -* openocd command interface * -***************************************************************************/ - -/* flash_bank stellaris 0 0 - */ -FLASH_BANK_COMMAND_HANDLER(stellaris_flash_bank_command) -{ - struct stellaris_flash_bank *stellaris_info; - - if (CMD_ARGC < 6) - { - LOG_WARNING("incomplete flash_bank stellaris configuration"); - return ERROR_FLASH_BANK_INVALID; - } - - stellaris_info = calloc(sizeof(struct stellaris_flash_bank), 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 Specify the main crystal speed in kHz using an optional - * argument; ditto, the speed of an external oscillator used - * instead of a crystal. Avoid programming flash using IOSC. - */ - return ERROR_OK; -} - -static int stellaris_info(struct flash_bank *bank, char *buf, int buf_size) -{ - int printed, device_class; - struct stellaris_flash_bank *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, - "\nTI/LMI Stellaris information: Chip is " - "class %i (%s) %s rev %c%i\n", - device_class, - StellarisClassname[device_class], - stellaris_info->target_name, - (int)('A' + ((stellaris_info->did0 >> 8) & 0xFF)), - (int)((stellaris_info->did0) & 0xFF)); - buf += printed; - buf_size -= printed; - - printed = snprintf(buf, - buf_size, - "did1: 0x%8.8" PRIx32 ", arch: 0x%4.4" PRIx32 - ", eproc: %s, ramsize: %ik, flashsize: %ik\n", - stellaris_info->did1, - stellaris_info->did1, - "ARMv7M", - (int)((1 + ((stellaris_info->dc0 >> 16) & 0xFFFF))/4), - (int)((1 + (stellaris_info->dc0 & 0xFFFF))*2)); - buf += printed; - buf_size -= printed; - - printed = snprintf(buf, - buf_size, - "master clock: %ikHz%s, " - "rcc is 0x%" PRIx32 ", rcc2 is 0x%" PRIx32 "\n", - (int)(stellaris_info->mck_freq / 1000), - stellaris_info->mck_desc, - stellaris_info->rcc, - stellaris_info->rcc2); - buf += printed; - buf_size -= printed; - - if (stellaris_info->num_lockbits > 0) - { - printed = snprintf(buf, - buf_size, - "pagesize: %" PRIi32 ", lockbits: %i 0x%4.4" PRIx32 ", pages in lock region: %i \n", - stellaris_info->pagesize, - stellaris_info->num_lockbits, - stellaris_info->lockbits, - (int)(stellaris_info->num_pages/stellaris_info->num_lockbits)); - buf += printed; - buf_size -= printed; - } - return ERROR_OK; -} - -/*************************************************************************** -* chip identification and status * -***************************************************************************/ - -static uint32_t stellaris_get_flash_status(struct flash_bank *bank) -{ - struct target *target = bank->target; - uint32_t fmc; - - target_read_u32(target, FLASH_CONTROL_BASE | FLASH_FMC, &fmc); - - return fmc; -} - -/** Read clock configuration and set stellaris_info->usec_clocks*/ - -static const unsigned rcc_xtal[32] = { - [0x00] = 1000000, /* no pll */ - [0x01] = 1843200, /* no pll */ - [0x02] = 2000000, /* no pll */ - [0x03] = 2457600, /* no pll */ - - [0x04] = 3579545, - [0x05] = 3686400, - [0x06] = 4000000, /* usb */ - [0x07] = 4096000, - - [0x08] = 4915200, - [0x09] = 5000000, /* usb */ - [0x0a] = 5120000, - [0x0b] = 6000000, /* (reset) usb */ - - [0x0c] = 6144000, - [0x0d] = 7372800, - [0x0e] = 8000000, /* usb */ - [0x0f] = 8192000, - - /* parts before DustDevil use just 4 bits for xtal spec */ - - [0x10] = 10000000, /* usb */ - [0x11] = 12000000, /* usb */ - [0x12] = 12288000, - [0x13] = 13560000, - - [0x14] = 14318180, - [0x15] = 16000000, /* usb */ - [0x16] = 16384000, -}; - -static void stellaris_read_clock_info(struct flash_bank *bank) -{ - struct stellaris_flash_bank *stellaris_info = bank->driver_priv; - struct target *target = bank->target; - uint32_t rcc, rcc2, pllcfg, sysdiv, usesysdiv, bypass, oscsrc; - unsigned xtal; - unsigned long mainfreq; - - target_read_u32(target, SCB_BASE | RCC, &rcc); - LOG_DEBUG("Stellaris RCC %" PRIx32 "", rcc); - - target_read_u32(target, SCB_BASE | RCC2, &rcc2); - LOG_DEBUG("Stellaris RCC2 %" PRIx32 "", rcc); - - target_read_u32(target, SCB_BASE | PLLCFG, &pllcfg); - LOG_DEBUG("Stellaris PLLCFG %" PRIx32 "", pllcfg); - - stellaris_info->rcc = rcc; - stellaris_info->rcc = rcc2; - - sysdiv = (rcc >> 23) & 0xF; - usesysdiv = (rcc >> 22) & 0x1; - bypass = (rcc >> 11) & 0x1; - oscsrc = (rcc >> 4) & 0x3; - xtal = (rcc >> 6) & stellaris_info->xtal_mask; - - /* NOTE: post-Sandstorm parts have RCC2 which may override - * parts of RCC ... with more sysdiv options, option for - * 32768 Hz mainfreq, PLL controls. On Sandstorm it reads - * as zero, so the "use RCC2" flag is always clear. - */ - if (rcc2 & (1 << 31)) { - sysdiv = (rcc2 >> 23) & 0x3F; - bypass = (rcc2 >> 11) & 0x1; - oscsrc = (rcc2 >> 4) & 0x7; - - /* FIXME Tempest parts have an additional lsb for - * fractional sysdiv (200 MHz / 2.5 == 80 MHz) - */ - } - - stellaris_info->mck_desc = ""; - - switch (oscsrc) - { - case 0: /* MOSC */ - mainfreq = rcc_xtal[xtal]; - break; - case 1: /* IOSC */ - mainfreq = stellaris_info->iosc_freq; - stellaris_info->mck_desc = stellaris_info->iosc_desc; - break; - case 2: /* IOSC/4 */ - mainfreq = stellaris_info->iosc_freq / 4; - stellaris_info->mck_desc = stellaris_info->iosc_desc; - break; - case 3: /* lowspeed */ - /* Sandstorm doesn't have this 30K +/- 30% osc */ - mainfreq = 30000; - stellaris_info->mck_desc = " (±30%)"; - break; - case 8: /* hibernation osc */ - /* not all parts support hibernation */ - mainfreq = 32768; - break; - - default: /* NOTREACHED */ - mainfreq = 0; - break; - } - - /* PLL is used if it's not bypassed; its output is 200 MHz - * even when it runs at 400 MHz (adds divide-by-two stage). - */ - if (!bypass) - mainfreq = 200000000; - - 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 */ -static void stellaris_set_flash_mode(struct flash_bank *bank,int mode) -{ - struct stellaris_flash_bank *stellaris_info = bank->driver_priv; - struct target *target = bank->target; - - uint32_t usecrl = (stellaris_info->mck_freq/1000000ul-1); - LOG_DEBUG("usecrl = %i",(int)(usecrl)); - target_write_u32(target, SCB_BASE | USECRL, usecrl); -} - -#if 0 -static uint32_t stellaris_wait_status_busy(struct flash_bank *bank, uint32_t waitbits, int timeout) -{ - uint32_t status; - - /* Stellaris waits for cmdbit to clear */ - while (((status = stellaris_get_flash_status(bank)) & waitbits) && (timeout-- > 0)) - { - LOG_DEBUG("status: 0x%x", status); - alive_sleep(1); - } - - /* Flash errors are reflected in the FLASH_CRIS register */ - - return status; -} - -/* Send one command to the flash controller */ -static int stellaris_flash_command(struct flash_bank *bank,uint8_t cmd,uint16_t pagen) -{ - uint32_t fmc; - struct target *target = bank->target; - - fmc = FMC_WRKEY | cmd; - target_write_u32(target, FLASH_CONTROL_BASE | FLASH_FMC, fmc); - LOG_DEBUG("Flash command: 0x%x", fmc); - - if (stellaris_wait_status_busy(bank, cmd, 100)) - { - return ERROR_FLASH_OPERATION_FAILED; - } - - return ERROR_OK; -} -#endif - -/* Read device id register, main clock frequency register and fill in driver info structure */ -static int stellaris_read_part_info(struct flash_bank *bank) -{ - struct stellaris_flash_bank *stellaris_info = bank->driver_priv; - struct target *target = bank->target; - uint32_t 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); - LOG_DEBUG("did0 0x%" PRIx32 ", did1 0x%" PRIx32 ", dc0 0x%" PRIx32 ", dc1 0x%" PRIx32 "", - did0, did1, stellaris_info->dc0, stellaris_info->dc1); - - ver = did0 >> 28; - if ((ver != 0) && (ver != 1)) - { - LOG_WARNING("Unknown did0 version, cannot identify target"); - return ERROR_FLASH_OPERATION_FAILED; - } - - if (did1 == 0) - { - LOG_WARNING("Cannot identify target as a Stellaris"); - return ERROR_FLASH_OPERATION_FAILED; - } - - ver = did1 >> 28; - fam = (did1 >> 24) & 0xF; - if (((ver != 0) && (ver != 1)) || (fam != 0)) - { - LOG_WARNING("Unknown did1 version/family, cannot positively identify target as a Stellaris"); - } - - /* For Sandstorm, Fury, DustDevil: current data sheets say IOSC - * is 12 MHz, but some older parts have 15 MHz. A few data sheets - * even give _both_ numbers! We'll use current numbers; IOSC is - * always approximate. - * - * For Tempest: IOSC is calibrated, 16 MHz - */ - stellaris_info->iosc_freq = 12000000; - stellaris_info->iosc_desc = " (±30%)"; - stellaris_info->xtal_mask = 0x0f; - - switch ((did0 >> 28) & 0x7) { - case 0: /* Sandstorm */ - /* - * Current (2009-August) parts seem to be rev C2 and use 12 MHz. - * Parts before rev C0 used 15 MHz; some C0 parts use 15 MHz - * (LM3S618), but some other C0 parts are 12 MHz (LM3S811). - */ - if (((did0 >> 8) & 0xff) < 2) { - stellaris_info->iosc_freq = 15000000; - stellaris_info->iosc_desc = " (±50%)"; - } - break; - case 1: - switch ((did0 >> 16) & 0xff) { - case 1: /* Fury */ - break; - case 4: /* Tempest */ - stellaris_info->iosc_freq = 16000000; /* +/- 1% */ - stellaris_info->iosc_desc = " (±1%)"; - /* FALL THROUGH */ - case 3: /* DustDevil */ - stellaris_info->xtal_mask = 0x1f; - break; - default: - LOG_WARNING("Unknown did0 class"); - } - default: - break; - LOG_WARNING("Unknown did0 version"); - } - - 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); - - /* provide this for the benefit of the higher flash driver layers */ - bank->num_sectors = stellaris_info->num_pages; - bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors); - for (i = 0; i < bank->num_sectors; i++) - { - bank->sectors[i].offset = i * stellaris_info->pagesize; - bank->sectors[i].size = stellaris_info->pagesize; - bank->sectors[i].is_erased = -1; - bank->sectors[i].is_protected = -1; - } - - /* Read main and master clock freqency register */ - stellaris_read_clock_info(bank); - - status = stellaris_get_flash_status(bank); - - return ERROR_OK; -} - -/*************************************************************************** -* flash operations * -***************************************************************************/ - -static int stellaris_protect_check(struct flash_bank *bank) -{ - uint32_t status; - - struct stellaris_flash_bank *stellaris_info = bank->driver_priv; - - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - if (stellaris_info->did1 == 0) - { - stellaris_read_part_info(bank); - } - - if (stellaris_info->did1 == 0) - { - LOG_WARNING("Cannot identify target as Stellaris"); - return ERROR_FLASH_OPERATION_FAILED; - } - - status = stellaris_get_flash_status(bank); - stellaris_info->lockbits = status >> 16; - - return ERROR_OK; -} - -static int stellaris_erase(struct flash_bank *bank, int first, int last) -{ - int banknr; - uint32_t flash_fmc, flash_cris; - struct stellaris_flash_bank *stellaris_info = bank->driver_priv; - struct target *target = bank->target; - - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - if (stellaris_info->did1 == 0) - { - stellaris_read_part_info(bank); - } - - if (stellaris_info->did1 == 0) - { - LOG_WARNING("Cannot identify target as Stellaris"); - return ERROR_FLASH_OPERATION_FAILED; - } - - if ((first < 0) || (last < first) || (last >= (int)stellaris_info->num_pages)) - { - return ERROR_FLASH_SECTOR_INVALID; - } - - if ((first == 0) && (last == ((int)stellaris_info->num_pages-1))) - { - return stellaris_mass_erase(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); - - 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)) - { - LOG_WARNING("Error erasing flash page %i, flash_cris 0x%" PRIx32 "", banknr, flash_cris); - target_write_u32(target, FLASH_CRIS, 0); - return ERROR_FLASH_OPERATION_FAILED; - } - - bank->sectors[banknr].is_erased = 1; - } - - return ERROR_OK; -} - -static int stellaris_protect(struct flash_bank *bank, int set, int first, int last) -{ - uint32_t fmppe, flash_fmc, flash_cris; - int lockregion; - - struct stellaris_flash_bank *stellaris_info = bank->driver_priv; - struct target *target = bank->target; - - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("Target not 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) - { - LOG_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); - - LOG_DEBUG("fmppe 0x%" PRIx32 "",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 */ - LOG_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)) - { - LOG_WARNING("Error setting flash page protection, flash_cris 0x%" PRIx32 "", 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; -} - -static uint8_t 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 */ - /* exit: */ - 0xFE,0xE7, /* b exit */ -/* pFLASH_CTRL_BASE: */ - 0x00,0xD0,0x0F,0x40, /* .word 0x400FD000 */ -/* FLASHWRITECMD: */ - 0x01,0x00,0x42,0xA4 /* .word 0xA4420001 */ -}; - -static int stellaris_write_block(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t wcount) -{ - struct target *target = bank->target; - uint32_t buffer_size = 8192; - struct working_area *source; - struct working_area *write_algorithm; - uint32_t address = bank->base + offset; - struct reg_param reg_params[3]; - struct armv7m_algorithm armv7m_info; - int retval = ERROR_OK; - - LOG_DEBUG("(bank=%p buffer=%p offset=%08" PRIx32 " wcount=%08" PRIx32 "", - bank, buffer, offset, wcount); - - /* flash write code */ - if (target_alloc_working_area(target, sizeof(stellaris_write_code), &write_algorithm) != ERROR_OK) - { - LOG_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) - { - LOG_DEBUG("called target_alloc_working_area(target=%p buffer_size=%08" PRIx32 " source=%p)", - target, buffer_size, 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); - - LOG_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; - - 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); - - while (wcount > 0) - { - uint32_t 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); - LOG_INFO("Algorithm flash write %" PRIi32 " words to 0x%" PRIx32 ", %" PRIi32 " remaining", thisrun_count, address, (wcount - thisrun_count)); - LOG_DEBUG("Algorithm flash write %" PRIi32 " words to 0x%" PRIx32 ", %" PRIi32 " remaining", thisrun_count, address, (wcount - thisrun_count)); - if ((retval = target_run_algorithm(target, 0, NULL, 3, reg_params, write_algorithm->address, write_algorithm->address + sizeof(stellaris_write_code)-10, 10000, &armv7m_info)) != ERROR_OK) - { - LOG_ERROR("error executing stellaris flash write algorithm"); - retval = ERROR_FLASH_OPERATION_FAILED; - break; - } - - 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]); - - return retval; -} - -static int stellaris_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) -{ - struct stellaris_flash_bank *stellaris_info = bank->driver_priv; - struct target *target = bank->target; - uint32_t address = offset; - uint32_t flash_cris, flash_fmc; - uint32_t words_remaining = (count / 4); - uint32_t bytes_remaining = (count & 0x00000003); - uint32_t bytes_written = 0; - int retval; - - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - LOG_DEBUG("(bank=%p buffer=%p offset=%08" PRIx32 " count=%08" PRIx32 "", - bank, buffer, offset, count); - - if (stellaris_info->did1 == 0) - { - stellaris_read_part_info(bank); - } - - if (stellaris_info->did1 == 0) - { - LOG_WARNING("Cannot identify target as a Stellaris processor"); - return ERROR_FLASH_OPERATION_FAILED; - } - - if (offset & 0x3) - { - LOG_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 (words_remaining > 0) - { - /* try using a block write */ - if ((retval = stellaris_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 */ - LOG_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); - - LOG_ERROR("flash writing failed with CRIS: 0x%" PRIx32 "", flash_cris); - return ERROR_FLASH_OPERATION_FAILED; - } - } - else - { - buffer += words_remaining * 4; - address += words_remaining * 4; - words_remaining = 0; - } - } - - while (words_remaining > 0) - { - if (!(address & 0xff)) - LOG_DEBUG("0x%" PRIx32 "", 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); - /* LOG_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; - words_remaining--; - } - - if (bytes_remaining) - { - uint8_t last_word[4] = {0xff, 0xff, 0xff, 0xff}; - int i = 0; - - while (bytes_remaining > 0) - { - last_word[i++] = *(buffer + bytes_written); - bytes_remaining--; - bytes_written++; - } - - if (!(address & 0xff)) - LOG_DEBUG("0x%" PRIx32 "", address); - - /* Program one word */ - target_write_u32(target, FLASH_FMA, address); - target_write_buffer(target, FLASH_FMD, 4, last_word); - target_write_u32(target, FLASH_FMC, FMC_WRKEY | FMC_WRITE); - /* LOG_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); - } - - /* Check access violations */ - target_read_u32(target, FLASH_CRIS, &flash_cris); - if (flash_cris & (AMASK)) - { - LOG_DEBUG("flash_cris 0x%" PRIx32 "", flash_cris); - return ERROR_FLASH_OPERATION_FAILED; - } - return ERROR_OK; -} - -static int stellaris_probe(struct flash_bank *bank) -{ - /* we can't probe on an stellaris - * if this is an stellaris, it has the configured flash - */ - - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - /* stellaris_read_part_info() already takes care about error checking and reporting */ - return stellaris_read_part_info(bank); -} - -static int stellaris_auto_probe(struct flash_bank *bank) -{ - struct stellaris_flash_bank *stellaris_info = bank->driver_priv; - if (stellaris_info->did1) - return ERROR_OK; - return stellaris_probe(bank); -} - -static int stellaris_mass_erase(struct flash_bank *bank) -{ - struct target *target = NULL; - struct stellaris_flash_bank *stellaris_info = NULL; - uint32_t flash_fmc; - - stellaris_info = bank->driver_priv; - target = bank->target; - - if (target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - if (stellaris_info->did1 == 0) - { - stellaris_read_part_info(bank); - } - - if (stellaris_info->did1 == 0) - { - LOG_WARNING("Cannot identify target as Stellaris"); - return ERROR_FLASH_OPERATION_FAILED; - } - - /* 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); - - 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 - * this is only valid for older devices, but will not hurt */ - 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; -} - -COMMAND_HANDLER(stellaris_handle_mass_erase_command) -{ - int i; - - if (CMD_ARGC < 1) - { - command_print(CMD_CTX, "stellaris mass_erase "); - return ERROR_OK; - } - - struct flash_bank *bank; - int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) - return retval; - - if (stellaris_mass_erase(bank) == ERROR_OK) - { - /* set all sectors as erased */ - for (i = 0; i < bank->num_sectors; i++) - { - bank->sectors[i].is_erased = 1; - } - - command_print(CMD_CTX, "stellaris mass erase complete"); - } - else - { - command_print(CMD_CTX, "stellaris mass erase failed"); - } - - return ERROR_OK; -} - -static const struct command_registration stellaris_exec_command_handlers[] = { - { - .name = "mass_erase", - .handler = &stellaris_handle_mass_erase_command, - .mode = COMMAND_EXEC, - .help = "erase entire device", - }, - COMMAND_REGISTRATION_DONE -}; -static const struct command_registration stellaris_command_handlers[] = { - { - .name = "stellaris", - .mode = COMMAND_ANY, - .help = "Stellaris flash command group", - .chain = stellaris_exec_command_handlers, - }, - COMMAND_REGISTRATION_DONE -}; - -struct flash_driver stellaris_flash = { - .name = "stellaris", - .commands = stellaris_command_handlers, - .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 = &default_flash_mem_blank_check, - .protect_check = &stellaris_protect_check, - .info = &stellaris_info, - }; diff --git a/src/flash/stellaris.h b/src/flash/stellaris.h deleted file mode 100644 index 949a346d..00000000 --- a/src/flash/stellaris.h +++ /dev/null @@ -1,99 +0,0 @@ -/*************************************************************************** - * 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. * - ***************************************************************************/ -#ifndef STELLARIS_FLASH_H -#define STELLARIS_FLASH_H - -#include "flash.h" - -struct stellaris_flash_bank -{ - /* chip id register */ - uint32_t did0; - uint32_t did1; - uint32_t dc0; - uint32_t dc1; - - char * target_name; - - uint32_t sramsiz; - uint32_t flshsz; - /* flash geometry */ - uint32_t num_pages; - uint32_t pagesize; - uint32_t pages_in_lockregion; - - /* nv memory bits */ - uint16_t num_lockbits; - uint32_t lockbits; - - /* main clock status */ - uint32_t rcc; - uint32_t rcc2; - uint8_t mck_valid; - uint8_t xtal_mask; - uint32_t iosc_freq; - uint32_t mck_freq; - const char *iosc_desc; - const char *mck_desc; -}; - -/* STELLARIS control registers */ -#define SCB_BASE 0x400FE000 -#define DID0 0x000 -#define DID1 0x004 -#define DC0 0x008 -#define DC1 0x010 -#define DC2 0x014 -#define DC3 0x018 -#define DC4 0x01C - -#define RIS 0x050 -#define RCC 0x060 -#define PLLCFG 0x064 -#define RCC2 0x070 - -#define FMPRE 0x130 -#define FMPPE 0x134 -#define USECRL 0x140 - -#define FLASH_CONTROL_BASE 0x400FD000 -#define FLASH_FMA (FLASH_CONTROL_BASE | 0x000) -#define FLASH_FMD (FLASH_CONTROL_BASE | 0x004) -#define FLASH_FMC (FLASH_CONTROL_BASE | 0x008) -#define FLASH_CRIS (FLASH_CONTROL_BASE | 0x00C) -#define FLASH_CIM (FLASH_CONTROL_BASE | 0x010) -#define FLASH_MISC (FLASH_CONTROL_BASE | 0x014) - -#define AMISC 1 -#define PMISC 2 - -#define AMASK 1 -#define PMASK 2 - -/* Flash Controller Command bits */ -#define FMC_WRKEY (0xA442 << 16) -#define FMC_COMT (1 << 3) -#define FMC_MERASE (1 << 2) -#define FMC_ERASE (1 << 1) -#define FMC_WRITE (1 << 0) - -/* STELLARIS constants */ - -#endif /* STELLARIS_H */ diff --git a/src/flash/stm32x.c b/src/flash/stm32x.c deleted file mode 100644 index 2f51aa55..00000000 --- a/src/flash/stm32x.c +++ /dev/null @@ -1,1240 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2005 by Dominic Rath * - * Dominic.Rath@gmx.de * - * * - * Copyright (C) 2008 by Spencer Oliver * - * spen@spen-soft.co.uk * - * * - * 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 "stm32x.h" -#include "armv7m.h" -#include "binarybuffer.h" -#include "algorithm.h" - - -static int stm32x_mass_erase(struct flash_bank *bank); - -/* flash bank stm32x 0 0 - */ -FLASH_BANK_COMMAND_HANDLER(stm32x_flash_bank_command) -{ - struct stm32x_flash_bank *stm32x_info; - - if (CMD_ARGC < 6) - { - LOG_WARNING("incomplete flash_bank stm32x configuration"); - return ERROR_FLASH_BANK_INVALID; - } - - stm32x_info = malloc(sizeof(struct stm32x_flash_bank)); - bank->driver_priv = stm32x_info; - - stm32x_info->write_algorithm = NULL; - stm32x_info->probed = 0; - - return ERROR_OK; -} - -static uint32_t stm32x_get_flash_status(struct flash_bank *bank) -{ - struct target *target = bank->target; - uint32_t status; - - target_read_u32(target, STM32_FLASH_SR, &status); - - return status; -} - -static uint32_t stm32x_wait_status_busy(struct flash_bank *bank, int timeout) -{ - struct target *target = bank->target; - uint32_t status; - - /* wait for busy to clear */ - while (((status = stm32x_get_flash_status(bank)) & FLASH_BSY) && (timeout-- > 0)) - { - LOG_DEBUG("status: 0x%" PRIx32 "", status); - alive_sleep(1); - } - /* Clear but report errors */ - if (status & (FLASH_WRPRTERR | FLASH_PGERR)) - { - target_write_u32(target, STM32_FLASH_SR, FLASH_WRPRTERR | FLASH_PGERR); - } - return status; -} - -static int stm32x_read_options(struct flash_bank *bank) -{ - uint32_t optiondata; - struct stm32x_flash_bank *stm32x_info = NULL; - struct target *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 = (uint16_t)0xFFF8 | ((optiondata >> 2) & 0x07); - stm32x_info->option_bytes.RDP = (optiondata & (1 << OPT_READOUT)) ? 0xFFFF : 0x5AA5; - - if (optiondata & (1 << OPT_READOUT)) - LOG_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] = (uint16_t)optiondata; - stm32x_info->option_bytes.protection[1] = (uint16_t)(optiondata >> 8); - stm32x_info->option_bytes.protection[2] = (uint16_t)(optiondata >> 16); - stm32x_info->option_bytes.protection[3] = (uint16_t)(optiondata >> 24); - - return ERROR_OK; -} - -static int stm32x_erase_options(struct flash_bank *bank) -{ - struct stm32x_flash_bank *stm32x_info = NULL; - struct target *target = bank->target; - uint32_t 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; -} - -static int stm32x_write_options(struct flash_bank *bank) -{ - struct stm32x_flash_bank *stm32x_info = NULL; - struct target *target = bank->target; - uint32_t 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; -} - -static int stm32x_protect_check(struct flash_bank *bank) -{ - struct target *target = bank->target; - struct stm32x_flash_bank *stm32x_info = bank->driver_priv; - - uint32_t protection; - int i, s; - int num_bits; - int set; - - if (target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - /* medium density - each bit refers to a 4bank protection - * high density - each bit refers to a 2bank protection */ - target_read_u32(target, STM32_FLASH_WRPR, &protection); - - /* medium density - each protection bit is for 4 * 1K pages - * high density - each protection bit is for 2 * 2K pages */ - num_bits = (bank->num_sectors / stm32x_info->ppage_size); - - if (stm32x_info->ppage_size == 2) - { - /* high density flash/connectivity line protection */ - - set = 1; - - if (protection & (1 << 31)) - set = 0; - - /* bit 31 controls sector 62 - 255 protection for high density - * bit 31 controls sector 62 - 127 protection for connectivity line */ - for (s = 62; s < bank->num_sectors; s++) - { - bank->sectors[s].is_protected = set; - } - - if (bank->num_sectors > 61) - num_bits = 31; - - for (i = 0; i < num_bits; i++) - { - set = 1; - - if (protection & (1 << i)) - set = 0; - - for (s = 0; s < stm32x_info->ppage_size; s++) - bank->sectors[(i * stm32x_info->ppage_size) + s].is_protected = set; - } - } - else - { - /* low/medium density flash protection */ - for (i = 0; i < num_bits; i++) - { - set = 1; - - if (protection & (1 << i)) - set = 0; - - for (s = 0; s < stm32x_info->ppage_size; s++) - bank->sectors[(i * stm32x_info->ppage_size) + s].is_protected = set; - } - } - - return ERROR_OK; -} - -static int stm32x_erase(struct flash_bank *bank, int first, int last) -{ - struct target *target = bank->target; - int i; - uint32_t status; - - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - if ((first == 0) && (last == (bank->num_sectors - 1))) - { - return stm32x_mass_erase(bank); - } - - /* 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; -} - -static int stm32x_protect(struct flash_bank *bank, int set, int first, int last) -{ - struct stm32x_flash_bank *stm32x_info = NULL; - struct target *target = bank->target; - uint16_t prot_reg[4] = {0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF}; - int i, reg, bit; - int status; - uint32_t protection; - - stm32x_info = bank->driver_priv; - - if (target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - if ((first && (first % stm32x_info->ppage_size)) || ((last + 1) && (last + 1) % stm32x_info->ppage_size)) - { - LOG_WARNING("Error: start and end sectors must be on a %d sector boundary", stm32x_info->ppage_size); - return ERROR_FLASH_SECTOR_INVALID; - } - - /* medium density - each bit refers to a 4bank protection - * high density - each bit refers to a 2bank protection */ - target_read_u32(target, STM32_FLASH_WRPR, &protection); - - prot_reg[0] = (uint16_t)protection; - prot_reg[1] = (uint16_t)(protection >> 8); - prot_reg[2] = (uint16_t)(protection >> 16); - prot_reg[3] = (uint16_t)(protection >> 24); - - if (stm32x_info->ppage_size == 2) - { - /* high density flash */ - - /* bit 7 controls sector 62 - 255 protection */ - if (last > 61) - { - if (set) - prot_reg[3] &= ~(1 << 7); - else - prot_reg[3] |= (1 << 7); - } - - if (first > 61) - first = 62; - if (last > 61) - last = 61; - - for (i = first; i <= last; i++) - { - reg = (i / stm32x_info->ppage_size) / 8; - bit = (i / stm32x_info->ppage_size) - (reg * 8); - - if (set) - prot_reg[reg] &= ~(1 << bit); - else - prot_reg[reg] |= (1 << bit); - } - } - else - { - /* medium density flash */ - for (i = first; i <= last; i++) - { - reg = (i / stm32x_info->ppage_size) / 8; - bit = (i / stm32x_info->ppage_size) - (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); -} - -static int stm32x_write_block(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) -{ - struct stm32x_flash_bank *stm32x_info = bank->driver_priv; - struct target *target = bank->target; - uint32_t buffer_size = 16384; - struct working_area *source; - uint32_t address = bank->base + offset; - struct reg_param reg_params[4]; - struct armv7m_algorithm armv7m_info; - int retval = ERROR_OK; - - uint8_t 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) - { - LOG_WARNING("no working area available, can't do block memory writes"); - return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; - }; - - if ((retval = target_write_buffer(target, stm32x_info->write_algorithm->address, sizeof(stm32x_flash_write_code), stm32x_flash_write_code)) != ERROR_OK) - return retval; - - /* 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); - - LOG_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; - - 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) - { - uint32_t thisrun_count = (count > (buffer_size / 2)) ? (buffer_size / 2) : count; - - if ((retval = target_write_buffer(target, source->address, thisrun_count * 2, buffer)) != ERROR_OK) - break; - - 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_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) - { - LOG_ERROR("error executing stm32x flash write algorithm"); - retval = ERROR_FLASH_OPERATION_FAILED; - break; - } - - if (buf_get_u32(reg_params[3].value, 0, 32) & FLASH_PGERR) - { - LOG_ERROR("flash memory not erased before writing"); - /* Clear but report errors */ - target_write_u32(target, STM32_FLASH_SR, FLASH_PGERR); - retval = ERROR_FLASH_OPERATION_FAILED; - break; - } - - if (buf_get_u32(reg_params[3].value, 0, 32) & FLASH_WRPRTERR) - { - LOG_ERROR("flash memory write protected"); - /* Clear but report errors */ - target_write_u32(target, STM32_FLASH_SR, FLASH_WRPRTERR); - 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; -} - -static int stm32x_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) -{ - struct target *target = bank->target; - uint32_t words_remaining = (count / 2); - uint32_t bytes_remaining = (count & 0x00000001); - uint32_t address = bank->base + offset; - uint32_t bytes_written = 0; - uint8_t status; - int retval; - - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - if (offset & 0x1) - { - LOG_WARNING("offset 0x%" PRIx32 " 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 */ - LOG_WARNING("couldn't use block writes, falling back to single memory accesses"); - } - else if (retval == ERROR_FLASH_OPERATION_FAILED) - { - LOG_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) - { - uint16_t value; - memcpy(&value, buffer + bytes_written, sizeof(uint16_t)); - - target_write_u32(target, STM32_FLASH_CR, FLASH_PG); - target_write_u16(target, address, value); - - status = stm32x_wait_status_busy(bank, 5); - - if (status & FLASH_WRPRTERR) - { - LOG_ERROR("flash memory not erased before writing"); - return ERROR_FLASH_OPERATION_FAILED; - } - if (status & FLASH_PGERR) - { - LOG_ERROR("flash memory write protected"); - return ERROR_FLASH_OPERATION_FAILED; - } - - bytes_written += 2; - words_remaining--; - address += 2; - } - - if (bytes_remaining) - { - uint16_t value = 0xffff; - memcpy(&value, buffer + bytes_written, bytes_remaining); - - target_write_u32(target, STM32_FLASH_CR, FLASH_PG); - target_write_u16(target, address, value); - - status = stm32x_wait_status_busy(bank, 5); - - if (status & FLASH_WRPRTERR) - { - LOG_ERROR("flash memory not erased before writing"); - return ERROR_FLASH_OPERATION_FAILED; - } - if (status & FLASH_PGERR) - { - LOG_ERROR("flash memory write protected"); - return ERROR_FLASH_OPERATION_FAILED; - } - } - - target_write_u32(target, STM32_FLASH_CR, FLASH_LOCK); - - return ERROR_OK; -} - -static int stm32x_probe(struct flash_bank *bank) -{ - struct target *target = bank->target; - struct stm32x_flash_bank *stm32x_info = bank->driver_priv; - int i; - uint16_t num_pages; - uint32_t device_id; - int page_size; - - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - stm32x_info->probed = 0; - - /* read stm32 device id register */ - target_read_u32(target, 0xE0042000, &device_id); - LOG_INFO("device id = 0x%08" PRIx32 "", device_id); - - /* get flash size from target */ - if (target_read_u16(target, 0x1FFFF7E0, &num_pages) != ERROR_OK) - { - /* failed reading flash size, default to max target family */ - num_pages = 0xffff; - } - - if ((device_id & 0x7ff) == 0x410) - { - /* medium density - we have 1k pages - * 4 pages for a protection area */ - page_size = 1024; - stm32x_info->ppage_size = 4; - - /* check for early silicon */ - if (num_pages == 0xffff) - { - /* number of sectors incorrect on revA */ - LOG_WARNING("STM32 flash size failed, probe inaccurate - assuming 128k flash"); - num_pages = 128; - } - } - else if ((device_id & 0x7ff) == 0x412) - { - /* low density - we have 1k pages - * 4 pages for a protection area */ - page_size = 1024; - stm32x_info->ppage_size = 4; - - /* check for early silicon */ - if (num_pages == 0xffff) - { - /* number of sectors incorrect on revA */ - LOG_WARNING("STM32 flash size failed, probe inaccurate - assuming 32k flash"); - num_pages = 32; - } - } - else if ((device_id & 0x7ff) == 0x414) - { - /* high density - we have 2k pages - * 2 pages for a protection area */ - page_size = 2048; - stm32x_info->ppage_size = 2; - - /* check for early silicon */ - if (num_pages == 0xffff) - { - /* number of sectors incorrect on revZ */ - LOG_WARNING("STM32 flash size failed, probe inaccurate - assuming 512k flash"); - num_pages = 512; - } - } - else if ((device_id & 0x7ff) == 0x418) - { - /* connectivity line density - we have 2k pages - * 2 pages for a protection area */ - page_size = 2048; - stm32x_info->ppage_size = 2; - - /* check for early silicon */ - if (num_pages == 0xffff) - { - /* number of sectors incorrect on revZ */ - LOG_WARNING("STM32 flash size failed, probe inaccurate - assuming 256k flash"); - num_pages = 256; - } - } - else - { - LOG_WARNING("Cannot identify target as a STM32 family."); - return ERROR_FLASH_OPERATION_FAILED; - } - - LOG_INFO("flash size = %dkbytes", num_pages); - - /* calculate numbers of pages */ - num_pages /= (page_size / 1024); - - bank->base = 0x08000000; - bank->size = (num_pages * page_size); - bank->num_sectors = num_pages; - bank->sectors = malloc(sizeof(struct flash_sector) * num_pages); - - for (i = 0; i < num_pages; i++) - { - bank->sectors[i].offset = i * page_size; - bank->sectors[i].size = page_size; - bank->sectors[i].is_erased = -1; - bank->sectors[i].is_protected = 1; - } - - stm32x_info->probed = 1; - - return ERROR_OK; -} - -static int stm32x_auto_probe(struct flash_bank *bank) -{ - struct stm32x_flash_bank *stm32x_info = bank->driver_priv; - if (stm32x_info->probed) - return ERROR_OK; - return stm32x_probe(bank); -} - -#if 0 -COMMAND_HANDLER(stm32x_handle_part_id_command) -{ - return ERROR_OK; -} -#endif - -static int stm32x_info(struct flash_bank *bank, char *buf, int buf_size) -{ - struct target *target = bank->target; - uint32_t device_id; - int printed; - - /* read stm32 device id register */ - target_read_u32(target, 0xE0042000, &device_id); - - if ((device_id & 0x7ff) == 0x410) - { - printed = snprintf(buf, buf_size, "stm32x (Medium Density) - Rev: "); - buf += printed; - buf_size -= printed; - - switch (device_id >> 16) - { - case 0x0000: - snprintf(buf, buf_size, "A"); - break; - - case 0x2000: - snprintf(buf, buf_size, "B"); - break; - - case 0x2001: - snprintf(buf, buf_size, "Z"); - break; - - case 0x2003: - snprintf(buf, buf_size, "Y"); - break; - - default: - snprintf(buf, buf_size, "unknown"); - break; - } - } - else if ((device_id & 0x7ff) == 0x412) - { - printed = snprintf(buf, buf_size, "stm32x (Low Density) - Rev: "); - buf += printed; - buf_size -= printed; - - switch (device_id >> 16) - { - case 0x1000: - snprintf(buf, buf_size, "A"); - break; - - default: - snprintf(buf, buf_size, "unknown"); - break; - } - } - else if ((device_id & 0x7ff) == 0x414) - { - printed = snprintf(buf, buf_size, "stm32x (High Density) - Rev: "); - buf += printed; - buf_size -= printed; - - switch (device_id >> 16) - { - case 0x1000: - snprintf(buf, buf_size, "A"); - break; - - case 0x1001: - snprintf(buf, buf_size, "Z"); - break; - - default: - snprintf(buf, buf_size, "unknown"); - break; - } - } - else if ((device_id & 0x7ff) == 0x418) - { - printed = snprintf(buf, buf_size, "stm32x (Connectivity) - Rev: "); - buf += printed; - buf_size -= printed; - - switch (device_id >> 16) - { - case 0x1000: - snprintf(buf, buf_size, "A"); - break; - - case 0x1001: - snprintf(buf, buf_size, "Z"); - break; - - default: - snprintf(buf, buf_size, "unknown"); - break; - } - } - else - { - snprintf(buf, buf_size, "Cannot identify target as a stm32x\n"); - return ERROR_FLASH_OPERATION_FAILED; - } - - return ERROR_OK; -} - -COMMAND_HANDLER(stm32x_handle_lock_command) -{ - struct target *target = NULL; - struct stm32x_flash_bank *stm32x_info = NULL; - - if (CMD_ARGC < 1) - { - command_print(CMD_CTX, "stm32x lock "); - return ERROR_OK; - } - - struct flash_bank *bank; - int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) - return retval; - - stm32x_info = bank->driver_priv; - - target = bank->target; - - if (target->state != TARGET_HALTED) - { - LOG_ERROR("Target not 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; -} - -COMMAND_HANDLER(stm32x_handle_unlock_command) -{ - struct target *target = NULL; - struct stm32x_flash_bank *stm32x_info = NULL; - - if (CMD_ARGC < 1) - { - command_print(CMD_CTX, "stm32x unlock "); - return ERROR_OK; - } - - struct flash_bank *bank; - int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) - return retval; - - stm32x_info = bank->driver_priv; - - target = bank->target; - - if (target->state != TARGET_HALTED) - { - LOG_ERROR("Target not 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; -} - -COMMAND_HANDLER(stm32x_handle_options_read_command) -{ - uint32_t optionbyte; - struct target *target = NULL; - struct stm32x_flash_bank *stm32x_info = NULL; - - if (CMD_ARGC < 1) - { - command_print(CMD_CTX, "stm32x options_read "); - return ERROR_OK; - } - - struct flash_bank *bank; - int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) - return retval; - - stm32x_info = bank->driver_priv; - - target = bank->target; - - if (target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - target_read_u32(target, STM32_FLASH_OBR, &optionbyte); - command_print(CMD_CTX, "Option Byte: 0x%" PRIx32 "", optionbyte); - - if (buf_get_u32((uint8_t*)&optionbyte, OPT_ERROR, 1)) - command_print(CMD_CTX, "Option Byte Complement Error"); - - if (buf_get_u32((uint8_t*)&optionbyte, OPT_READOUT, 1)) - command_print(CMD_CTX, "Readout Protection On"); - else - command_print(CMD_CTX, "Readout Protection Off"); - - if (buf_get_u32((uint8_t*)&optionbyte, OPT_RDWDGSW, 1)) - command_print(CMD_CTX, "Software Watchdog"); - else - command_print(CMD_CTX, "Hardware Watchdog"); - - if (buf_get_u32((uint8_t*)&optionbyte, OPT_RDRSTSTOP, 1)) - command_print(CMD_CTX, "Stop: No reset generated"); - else - command_print(CMD_CTX, "Stop: Reset generated"); - - if (buf_get_u32((uint8_t*)&optionbyte, OPT_RDRSTSTDBY, 1)) - command_print(CMD_CTX, "Standby: No reset generated"); - else - command_print(CMD_CTX, "Standby: Reset generated"); - - return ERROR_OK; -} - -COMMAND_HANDLER(stm32x_handle_options_write_command) -{ - struct target *target = NULL; - struct stm32x_flash_bank *stm32x_info = NULL; - uint16_t optionbyte = 0xF8; - - if (CMD_ARGC < 4) - { - command_print(CMD_CTX, "stm32x options_write "); - return ERROR_OK; - } - - struct flash_bank *bank; - int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) - return retval; - - stm32x_info = bank->driver_priv; - - target = bank->target; - - if (target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - if (strcmp(CMD_ARGV[1], "SWWDG") == 0) - { - optionbyte |= (1 << 0); - } - else - { - optionbyte &= ~(1 << 0); - } - - if (strcmp(CMD_ARGV[2], "NORSTSTNDBY") == 0) - { - optionbyte |= (1 << 1); - } - else - { - optionbyte &= ~(1 << 1); - } - - if (strcmp(CMD_ARGV[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; -} - -static int stm32x_mass_erase(struct flash_bank *bank) -{ - struct target *target = bank->target; - uint32_t status; - - if (target->state != TARGET_HALTED) - { - LOG_ERROR("Target not 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) - { - LOG_ERROR("stm32x device protected"); - return ERROR_OK; - } - - if (status & FLASH_PGERR) - { - LOG_ERROR("stm32x device programming failed"); - return ERROR_OK; - } - - return ERROR_OK; -} - -COMMAND_HANDLER(stm32x_handle_mass_erase_command) -{ - int i; - - if (CMD_ARGC < 1) - { - command_print(CMD_CTX, "stm32x mass_erase "); - return ERROR_OK; - } - - struct flash_bank *bank; - int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) - return retval; - - if (stm32x_mass_erase(bank) == ERROR_OK) - { - /* set all sectors as erased */ - for (i = 0; i < bank->num_sectors; i++) - { - bank->sectors[i].is_erased = 1; - } - - command_print(CMD_CTX, "stm32x mass erase complete"); - } - else - { - command_print(CMD_CTX, "stm32x mass erase failed"); - } - - return ERROR_OK; -} - -static const struct command_registration stm32x_exec_command_handlers[] = { - { - .name = "lock", - .handler = &stm32x_handle_lock_command, - .mode = COMMAND_EXEC, - .help = "lock device", - }, - { - .name = "unlock", - .handler = &stm32x_handle_unlock_command, - .mode = COMMAND_EXEC, - .help = "unlock protected device", - }, - { - .name = "mass_erase", - .handler = &stm32x_handle_mass_erase_command, - .mode = COMMAND_EXEC, - .help = "mass erase device", - }, - { - .name = "options_read", - .handler = &stm32x_handle_options_read_command, - .mode = COMMAND_EXEC, - .help = "read device option bytes", - }, - { - .name = "options_write", - .handler = &stm32x_handle_options_write_command, - .mode = COMMAND_EXEC, - .help = "write device option bytes", - }, - COMMAND_REGISTRATION_DONE -}; -static const struct command_registration stm32x_command_handlers[] = { - { - .name = "stm32x", - .mode = COMMAND_ANY, - .help = "stm32x flash command group", - .chain = stm32x_exec_command_handlers, - }, - COMMAND_REGISTRATION_DONE -}; - -struct flash_driver stm32x_flash = { - .name = "stm32x", - .commands = stm32x_command_handlers, - .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 = &default_flash_mem_blank_check, - .protect_check = &stm32x_protect_check, - .info = &stm32x_info, - }; diff --git a/src/flash/stm32x.h b/src/flash/stm32x.h deleted file mode 100644 index 6cd047e1..00000000 --- a/src/flash/stm32x.h +++ /dev/null @@ -1,101 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2005 by Dominic Rath * - * Dominic.Rath@gmx.de * - * * - * Copyright (C) 2008 by Spencer Oliver * - * spen@spen-soft.co.uk * - * * - * 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 STM32X_H -#define STM32X_H - -#include "flash.h" - -struct stm32x_options -{ - uint16_t RDP; - uint16_t user_options; - uint16_t protection[4]; -}; - -struct stm32x_flash_bank -{ - struct stm32x_options option_bytes; - struct working_area *write_algorithm; - int ppage_size; - int probed; -}; - -/* stm32x register locations */ - -#define STM32_FLASH_ACR 0x40022000 -#define STM32_FLASH_KEYR 0x40022004 -#define STM32_FLASH_OPTKEYR 0x40022008 -#define STM32_FLASH_SR 0x4002200C -#define STM32_FLASH_CR 0x40022010 -#define STM32_FLASH_AR 0x40022014 -#define STM32_FLASH_OBR 0x4002201C -#define STM32_FLASH_WRPR 0x40022020 - -/* option byte location */ - -#define STM32_OB_RDP 0x1FFFF800 -#define STM32_OB_USER 0x1FFFF802 -#define STM32_OB_DATA0 0x1FFFF804 -#define STM32_OB_DATA1 0x1FFFF806 -#define STM32_OB_WRP0 0x1FFFF808 -#define STM32_OB_WRP1 0x1FFFF80A -#define STM32_OB_WRP2 0x1FFFF80C -#define STM32_OB_WRP3 0x1FFFF80E - -/* FLASH_CR register bits */ - -#define FLASH_PG (1 << 0) -#define FLASH_PER (1 << 1) -#define FLASH_MER (1 << 2) -#define FLASH_OPTPG (1 << 4) -#define FLASH_OPTER (1 << 5) -#define FLASH_STRT (1 << 6) -#define FLASH_LOCK (1 << 7) -#define FLASH_OPTWRE (1 << 9) - -/* FLASH_SR register bits */ - -#define FLASH_BSY (1 << 0) -#define FLASH_PGERR (1 << 2) -#define FLASH_WRPRTERR (1 << 4) -#define FLASH_EOP (1 << 5) - -/* STM32_FLASH_OBR bit definitions (reading) */ - -#define OPT_ERROR 0 -#define OPT_READOUT 1 -#define OPT_RDWDGSW 2 -#define OPT_RDRSTSTOP 3 -#define OPT_RDRSTSTDBY 4 - -/* register unlock keys */ - -#define KEY1 0x45670123 -#define KEY2 0xCDEF89AB - -struct stm32x_mem_layout { - uint32_t sector_start; - uint32_t sector_size; -}; - -#endif /* STM32X_H */ diff --git a/src/flash/str7x.c b/src/flash/str7x.c deleted file mode 100644 index 7edffac9..00000000 --- a/src/flash/str7x.c +++ /dev/null @@ -1,706 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2005 by Dominic Rath * - * Dominic.Rath@gmx.de * - * * - * Copyright (C) 2008 by Spencer Oliver * - * spen@spen-soft.co.uk * - * * - * 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 "str7x.h" -#include "armv4_5.h" -#include "binarybuffer.h" -#include "algorithm.h" - - -struct str7x_mem_layout mem_layout_str7bank0[] = { - {0x00000000, 0x02000, 0x01}, - {0x00002000, 0x02000, 0x02}, - {0x00004000, 0x02000, 0x04}, - {0x00006000, 0x02000, 0x08}, - {0x00008000, 0x08000, 0x10}, - {0x00010000, 0x10000, 0x20}, - {0x00020000, 0x10000, 0x40}, - {0x00030000, 0x10000, 0x80} -}; - -struct str7x_mem_layout mem_layout_str7bank1[] = { - {0x00000000, 0x02000, 0x10000}, - {0x00002000, 0x02000, 0x20000} -}; - -static int str7x_get_flash_adr(struct flash_bank *bank, uint32_t reg) -{ - struct str7x_flash_bank *str7x_info = bank->driver_priv; - return (str7x_info->register_base | reg); -} - -static int str7x_build_block_list(struct flash_bank *bank) -{ - struct str7x_flash_bank *str7x_info = bank->driver_priv; - - int i; - int num_sectors; - int b0_sectors = 0, b1_sectors = 0; - - switch (bank->size) - { - case 16 * 1024: - b1_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: - LOG_ERROR("BUG: unknown bank->size encountered"); - exit(-1); - } - - num_sectors = b0_sectors + b1_sectors; - - bank->num_sectors = num_sectors; - bank->sectors = malloc(sizeof(struct flash_sector) * num_sectors); - str7x_info->sector_bits = malloc(sizeof(uint32_t) * num_sectors); - - num_sectors = 0; - - for (i = 0; i < b0_sectors; i++) - { - bank->sectors[num_sectors].offset = mem_layout_str7bank0[i].sector_start; - bank->sectors[num_sectors].size = mem_layout_str7bank0[i].sector_size; - bank->sectors[num_sectors].is_erased = -1; - bank->sectors[num_sectors].is_protected = 1; - str7x_info->sector_bits[num_sectors++] = mem_layout_str7bank0[i].sector_bit; - } - - for (i = 0; i < b1_sectors; i++) - { - bank->sectors[num_sectors].offset = mem_layout_str7bank1[i].sector_start; - bank->sectors[num_sectors].size = mem_layout_str7bank1[i].sector_size; - bank->sectors[num_sectors].is_erased = -1; - bank->sectors[num_sectors].is_protected = 1; - str7x_info->sector_bits[num_sectors++] = mem_layout_str7bank1[i].sector_bit; - } - - return ERROR_OK; -} - -/* flash bank str7x 0 0 - */ -FLASH_BANK_COMMAND_HANDLER(str7x_flash_bank_command) -{ - struct str7x_flash_bank *str7x_info; - - if (CMD_ARGC < 7) - { - LOG_WARNING("incomplete flash_bank str7x configuration"); - return ERROR_FLASH_BANK_INVALID; - } - - str7x_info = malloc(sizeof(struct str7x_flash_bank)); - bank->driver_priv = str7x_info; - - /* set default bits for str71x flash */ - str7x_info->busy_bits = (FLASH_LOCK | FLASH_BSYA1 | FLASH_BSYA0); - str7x_info->disable_bit = (1 << 1); - - if (strcmp(CMD_ARGV[6], "STR71x") == 0) - { - str7x_info->register_base = 0x40100000; - } - else if (strcmp(CMD_ARGV[6], "STR73x") == 0) - { - str7x_info->register_base = 0x80100000; - str7x_info->busy_bits = (FLASH_LOCK | FLASH_BSYA0); - } - else if (strcmp(CMD_ARGV[6], "STR75x") == 0) - { - str7x_info->register_base = 0x20100000; - str7x_info->disable_bit = (1 << 0); - } - else - { - LOG_ERROR("unknown STR7x variant: '%s'", CMD_ARGV[6]); - free(str7x_info); - return ERROR_FLASH_BANK_INVALID; - } - - str7x_build_block_list(bank); - - str7x_info->write_algorithm = NULL; - - return ERROR_OK; -} - -static uint32_t str7x_status(struct flash_bank *bank) -{ - struct target *target = bank->target; - uint32_t retval; - - target_read_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), &retval); - - return retval; -} - -static uint32_t str7x_result(struct flash_bank *bank) -{ - struct target *target = bank->target; - uint32_t retval; - - target_read_u32(target, str7x_get_flash_adr(bank, FLASH_ER), &retval); - - return retval; -} - -static int str7x_protect_check(struct flash_bank *bank) -{ - struct str7x_flash_bank *str7x_info = bank->driver_priv; - struct target *target = bank->target; - - int i; - uint32_t retval; - - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("Target not 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; -} - -static int str7x_erase(struct flash_bank *bank, int first, int last) -{ - struct str7x_flash_bank *str7x_info = bank->driver_priv; - struct target *target = bank->target; - - int i; - uint32_t cmd; - uint32_t retval; - uint32_t sectors = 0; - - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - for (i = first; i <= last; i++) - { - sectors |= str7x_info->sector_bits[i]; - } - - LOG_DEBUG("sectors: 0x%" PRIx32 "", 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 = 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)) { - alive_sleep(1); - } - - retval = str7x_result(bank); - - if (retval) - { - LOG_ERROR("error erasing flash bank, FLASH_ER: 0x%" PRIx32 "", retval); - return ERROR_FLASH_OPERATION_FAILED; - } - - for (i = first; i <= last; i++) - bank->sectors[i].is_erased = 1; - - return ERROR_OK; -} - -static int str7x_protect(struct flash_bank *bank, int set, int first, int last) -{ - struct str7x_flash_bank *str7x_info = bank->driver_priv; - struct target *target = bank->target; - int i; - uint32_t cmd; - uint32_t retval; - uint32_t protect_blocks; - - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("Target not 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)) { - alive_sleep(1); - } - - retval = str7x_result(bank); - - LOG_DEBUG("retval: 0x%8.8" PRIx32 "", retval); - - if (retval & FLASH_ERER) - return ERROR_FLASH_SECTOR_NOT_ERASED; - else if (retval & FLASH_WPF) - return ERROR_FLASH_OPERATION_FAILED; - - return ERROR_OK; -} - -static int str7x_write_block(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) -{ - struct str7x_flash_bank *str7x_info = bank->driver_priv; - struct target *target = bank->target; - uint32_t buffer_size = 8192; - struct working_area *source; - uint32_t address = bank->base + offset; - struct reg_param reg_params[6]; - struct armv4_5_algorithm armv4_5_info; - int retval = ERROR_OK; - - uint32_t 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) - { - LOG_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, (uint8_t*)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); - - LOG_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) - { - uint32_t 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_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) - { - LOG_ERROR("error executing str7x flash write algorithm"); - retval = ERROR_FLASH_OPERATION_FAILED; - 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; -} - -static int str7x_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) -{ - struct target *target = bank->target; - struct str7x_flash_bank *str7x_info = bank->driver_priv; - uint32_t dwords_remaining = (count / 8); - uint32_t bytes_remaining = (count & 0x00000007); - uint32_t address = bank->base + offset; - uint32_t bytes_written = 0; - uint32_t cmd; - int retval; - uint32_t check_address = offset; - int i; - - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - if (offset & 0x7) - { - LOG_WARNING("offset 0x%" PRIx32 " breaks required 8-byte alignment", offset); - return ERROR_FLASH_DST_BREAKS_ALIGNMENT; - } - - for (i = 0; i < bank->num_sectors; i++) - { - uint32_t sec_start = bank->sectors[i].offset; - uint32_t 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 */ - LOG_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); - - LOG_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_write_memory(target, str7x_get_flash_adr(bank, FLASH_DR0), 4, 1, buffer + bytes_written); - bytes_written += 4; - - /* data word 2 */ - target_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)) - { - alive_sleep(1); - } - - 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) - { - uint8_t 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_write_memory(target, str7x_get_flash_adr(bank, FLASH_DR0), 4, 1, last_dword); - bytes_written += 4; - - /* data word 2 */ - target_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)) - { - alive_sleep(1); - } - - 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; -} - -static int str7x_probe(struct flash_bank *bank) -{ - return ERROR_OK; -} - -#if 0 -COMMAND_HANDLER(str7x_handle_part_id_command) -{ - return ERROR_OK; -} -#endif - -static int str7x_info(struct flash_bank *bank, char *buf, int buf_size) -{ - snprintf(buf, buf_size, "str7x flash driver info"); - return ERROR_OK; -} - -COMMAND_HANDLER(str7x_handle_disable_jtag_command) -{ - struct target *target = NULL; - struct str7x_flash_bank *str7x_info = NULL; - - uint32_t flash_cmd; - uint16_t ProtectionLevel = 0; - uint16_t ProtectionRegs; - - if (CMD_ARGC < 1) - { - command_print(CMD_CTX, "str7x disable_jtag "); - return ERROR_OK; - } - - struct flash_bank *bank; - int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) - return retval; - - str7x_info = bank->driver_priv; - - target = bank->target; - - if (target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - /* first we get protection status */ - uint32_t reg; - target_read_u32(target, str7x_get_flash_adr(bank, FLASH_NVAPR0), ®); - - if (!(reg & str7x_info->disable_bit)) - { - ProtectionLevel = 1; - } - - target_read_u32(target, str7x_get_flash_adr(bank, FLASH_NVAPR1), ®); - ProtectionRegs = ~(reg >> 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; -} - -static const struct command_registration str7x_exec_command_handlers[] = { - { - .name = "disable_jtag", - .handler = &str7x_handle_disable_jtag_command, - .mode = COMMAND_EXEC, - .help = "disable jtag access", - }, - COMMAND_REGISTRATION_DONE -}; -static const struct command_registration str7x_command_handlers[] = { - { - .name = "str7x", - .mode = COMMAND_ANY, - .help = "str7x flash command group", - .chain = str7x_exec_command_handlers, - }, - COMMAND_REGISTRATION_DONE -}; - -struct flash_driver str7x_flash = { - .name = "str7x", - .commands = str7x_command_handlers, - .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 = &default_flash_blank_check, - .protect_check = &str7x_protect_check, - .info = &str7x_info, - }; diff --git a/src/flash/str7x.h b/src/flash/str7x.h deleted file mode 100644 index 81af0f1e..00000000 --- a/src/flash/str7x.h +++ /dev/null @@ -1,110 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2005 by Dominic Rath * - * Dominic.Rath@gmx.de * - * * - * Copyright (C) 2008 by Spencer Oliver * - * spen@spen-soft.co.uk * - * * - * 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 STR7X_H -#define STR7X_H - -#include "flash.h" - -struct str7x_flash_bank -{ - uint32_t *sector_bits; - uint32_t disable_bit; - uint32_t busy_bits; - uint32_t register_base; - struct working_area *write_algorithm; -}; - -enum str7x_status_codes -{ - STR7X_CMD_SUCCESS = 0, - STR7X_INVALID_COMMAND = 1, - STR7X_SRC_ADDR_ERROR = 2, - STR7X_DST_ADDR_ERROR = 3, - STR7X_SRC_ADDR_NOT_MAPPED = 4, - STR7X_DST_ADDR_NOT_MAPPED = 5, - STR7X_COUNT_ERROR = 6, - STR7X_INVALID_SECTOR = 7, - STR7X_SECTOR_NOT_BLANK = 8, - STR7X_SECTOR_NOT_PREPARED = 9, - STR7X_COMPARE_ERROR = 10, - STR7X_BUSY = 11 -}; - -/* Flash registers */ - -#define FLASH_CR0 0x00000000 -#define FLASH_CR1 0x00000004 -#define FLASH_DR0 0x00000008 -#define FLASH_DR1 0x0000000C -#define FLASH_AR 0x00000010 -#define FLASH_ER 0x00000014 -#define FLASH_NVWPAR 0x0000DFB0 -#define FLASH_NVAPR0 0x0000DFB8 -#define FLASH_NVAPR1 0x0000DFBC - -/* FLASH_CR0 register bits */ - -#define FLASH_WMS 0x80000000 -#define FLASH_SUSP 0x40000000 -#define FLASH_WPG 0x20000000 -#define FLASH_DWPG 0x10000000 -#define FLASH_SER 0x08000000 -#define FLASH_SPR 0x01000000 -#define FLASH_BER 0x04000000 -#define FLASH_MER 0x02000000 -#define FLASH_LOCK 0x00000010 -#define FLASH_BSYA1 0x00000004 -#define FLASH_BSYA0 0x00000002 - -/* FLASH_CR1 register bits */ - -#define FLASH_B1S 0x02000000 -#define FLASH_B0S 0x01000000 -#define FLASH_B1F1 0x00020000 -#define FLASH_B1F0 0x00010000 -#define FLASH_B0F7 0x00000080 -#define FLASH_B0F6 0x00000040 -#define FLASH_B0F5 0x00000020 -#define FLASH_B0F4 0x00000010 -#define FLASH_B0F3 0x00000008 -#define FLASH_B0F2 0x00000004 -#define FLASH_B0F1 0x00000002 -#define FLASH_B0F0 0x00000001 - -/* FLASH_ER register bits */ - -#define FLASH_WPF 0x00000100 -#define FLASH_RESER 0x00000080 -#define FLASH_SEQER 0x00000040 -#define FLASH_10ER 0x00000008 -#define FLASH_PGER 0x00000004 -#define FLASH_ERER 0x00000002 -#define FLASH_ERR 0x00000001 - -struct str7x_mem_layout { - uint32_t sector_start; - uint32_t sector_size; - uint32_t sector_bit; -}; - -#endif /* STR7X_H */ diff --git a/src/flash/str9x.c b/src/flash/str9x.c deleted file mode 100644 index 98f15e75..00000000 --- a/src/flash/str9x.c +++ /dev/null @@ -1,711 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2005 by Dominic Rath * - * Dominic.Rath@gmx.de * - * * - * Copyright (C) 2008 by Spencer Oliver * - * spen@spen-soft.co.uk * - * - * Copyright (C) 2008 by Oyvind Harboe * - * oyvind.harboe@zylin.com * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "str9x.h" -#include "arm966e.h" -#include "algorithm.h" - - -static uint32_t bank1start = 0x00080000; - -static int str9x_build_block_list(struct flash_bank *bank) -{ - struct str9x_flash_bank *str9x_info = bank->driver_priv; - - int i; - int num_sectors; - int b0_sectors = 0, b1_sectors = 0; - uint32_t offset = 0; - - /* set if we have large flash str9 */ - str9x_info->variant = 0; - str9x_info->bank1 = 0; - - switch (bank->size) - { - case (256 * 1024): - b0_sectors = 4; - break; - case (512 * 1024): - b0_sectors = 8; - break; - case (1024 * 1024): - bank1start = 0x00100000; - str9x_info->variant = 1; - b0_sectors = 16; - break; - case (2048 * 1024): - bank1start = 0x00200000; - str9x_info->variant = 1; - b0_sectors = 32; - break; - case (128 * 1024): - str9x_info->variant = 1; - str9x_info->bank1 = 1; - b1_sectors = 8; - bank1start = bank->base; - break; - case (32 * 1024): - str9x_info->bank1 = 1; - b1_sectors = 4; - bank1start = bank->base; - break; - default: - LOG_ERROR("BUG: unknown bank->size encountered"); - exit(-1); - } - - num_sectors = b0_sectors + b1_sectors; - - bank->num_sectors = num_sectors; - bank->sectors = malloc(sizeof(struct flash_sector) * num_sectors); - str9x_info->sector_bits = malloc(sizeof(uint32_t) * num_sectors); - - num_sectors = 0; - - for (i = 0; i < b0_sectors; i++) - { - bank->sectors[num_sectors].offset = offset; - bank->sectors[num_sectors].size = 0x10000; - offset += bank->sectors[i].size; - bank->sectors[num_sectors].is_erased = -1; - bank->sectors[num_sectors].is_protected = 1; - str9x_info->sector_bits[num_sectors++] = (1 << i); - } - - for (i = 0; i < b1_sectors; i++) - { - bank->sectors[num_sectors].offset = offset; - bank->sectors[num_sectors].size = str9x_info->variant == 0 ? 0x2000 : 0x4000; - offset += bank->sectors[i].size; - bank->sectors[num_sectors].is_erased = -1; - bank->sectors[num_sectors].is_protected = 1; - if (str9x_info->variant) - str9x_info->sector_bits[num_sectors++] = (1 << i); - else - str9x_info->sector_bits[num_sectors++] = (1 << (i + 8)); - } - - return ERROR_OK; -} - -/* flash bank str9x 0 0 - */ -FLASH_BANK_COMMAND_HANDLER(str9x_flash_bank_command) -{ - struct str9x_flash_bank *str9x_info; - - if (CMD_ARGC < 6) - { - LOG_WARNING("incomplete flash_bank str9x configuration"); - return ERROR_FLASH_BANK_INVALID; - } - - str9x_info = malloc(sizeof(struct str9x_flash_bank)); - bank->driver_priv = str9x_info; - - str9x_build_block_list(bank); - - str9x_info->write_algorithm = NULL; - - return ERROR_OK; -} - -static int str9x_protect_check(struct flash_bank *bank) -{ - int retval; - struct str9x_flash_bank *str9x_info = bank->driver_priv; - struct target *target = bank->target; - - int i; - uint32_t adr; - uint32_t status = 0; - uint16_t hstatus = 0; - - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - /* read level one protection */ - - if (str9x_info->variant) - { - if (str9x_info->bank1) - { - adr = bank1start + 0x18; - if ((retval = target_write_u16(target, adr, 0x90)) != ERROR_OK) - { - return retval; - } - if ((retval = target_read_u16(target, adr, &hstatus)) != ERROR_OK) - { - return retval; - } - status = hstatus; - } - else - { - adr = bank1start + 0x14; - if ((retval = target_write_u16(target, adr, 0x90)) != ERROR_OK) - { - return retval; - } - if ((retval = target_read_u32(target, adr, &status)) != ERROR_OK) - { - return retval; - } - } - } - else - { - adr = bank1start + 0x10; - if ((retval = target_write_u16(target, adr, 0x90)) != ERROR_OK) - { - return retval; - } - if ((retval = target_read_u16(target, adr, &hstatus)) != ERROR_OK) - { - return retval; - } - status = hstatus; - } - - /* read array command */ - if ((retval = target_write_u16(target, adr, 0xFF)) != ERROR_OK) - { - return retval; - } - - 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; -} - -static int str9x_erase(struct flash_bank *bank, int first, int last) -{ - struct target *target = bank->target; - int i; - uint32_t adr; - uint8_t status; - uint8_t erase_cmd; - - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - /* Check if we erase whole bank */ - if ((first == 0) && (last == (bank->num_sectors - 1))) - { - /* Optimize to run erase bank command instead of sector */ - erase_cmd = 0x80; - } - else - { - /* Erase sector command */ - erase_cmd = 0x20; - } - - for (i = first; i <= last; i++) - { - int retval; - adr = bank->base + bank->sectors[i].offset; - - /* erase sectors */ - if ((retval = target_write_u16(target, adr, erase_cmd)) != ERROR_OK) - { - return retval; - } - if ((retval = target_write_u16(target, adr, 0xD0)) != ERROR_OK) - { - return retval; - } - - /* get status */ - if ((retval = target_write_u16(target, adr, 0x70)) != ERROR_OK) - { - return retval; - } - - int timeout; - for (timeout = 0; timeout < 1000; timeout++) { - if ((retval = target_read_u8(target, adr, &status)) != ERROR_OK) - { - return retval; - } - if (status & 0x80) - break; - alive_sleep(1); - } - if (timeout == 1000) - { - LOG_ERROR("erase timed out"); - return ERROR_FAIL; - } - - /* clear status, also clear read array */ - if ((retval = target_write_u16(target, adr, 0x50)) != ERROR_OK) - { - return retval; - } - - /* read array command */ - if ((retval = target_write_u16(target, adr, 0xFF)) != ERROR_OK) - { - return retval; - } - - if (status & 0x22) - { - LOG_ERROR("error erasing flash bank, status: 0x%x", status); - return ERROR_FLASH_OPERATION_FAILED; - } - - /* If we ran erase bank command, we are finished */ - if (erase_cmd == 0x80) - break; - } - - for (i = first; i <= last; i++) - bank->sectors[i].is_erased = 1; - - return ERROR_OK; -} - -static int str9x_protect(struct flash_bank *bank, - int set, int first, int last) -{ - struct target *target = bank->target; - int i; - uint32_t adr; - uint8_t status; - - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("Target not 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); - - /* clear status, also clear read array */ - target_write_u16(target, adr, 0x50); - - /* read array command */ - target_write_u16(target, adr, 0xFF); - } - - return ERROR_OK; -} - -static int str9x_write_block(struct flash_bank *bank, - uint8_t *buffer, uint32_t offset, uint32_t count) -{ - struct str9x_flash_bank *str9x_info = bank->driver_priv; - struct target *target = bank->target; - uint32_t buffer_size = 8192; - struct working_area *source; - uint32_t address = bank->base + offset; - struct reg_param reg_params[4]; - struct armv4_5_algorithm armv4_5_info; - int retval = ERROR_OK; - - uint32_t 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) - { - LOG_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, (uint8_t*)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); - - LOG_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) - { - uint32_t 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_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) - { - LOG_ERROR("error executing str9x flash write algorithm"); - retval = ERROR_FLASH_OPERATION_FAILED; - break; - } - - if (buf_get_u32(reg_params[3].value, 0, 32) != 0x80) - { - 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, 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 retval; -} - -static int str9x_write(struct flash_bank *bank, - uint8_t *buffer, uint32_t offset, uint32_t count) -{ - struct target *target = bank->target; - uint32_t words_remaining = (count / 2); - uint32_t bytes_remaining = (count & 0x00000001); - uint32_t address = bank->base + offset; - uint32_t bytes_written = 0; - uint8_t status; - int retval; - uint32_t check_address = offset; - uint32_t bank_adr; - int i; - - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - if (offset & 0x1) - { - LOG_WARNING("offset 0x%" PRIx32 " breaks required 2-byte alignment", offset); - return ERROR_FLASH_DST_BREAKS_ALIGNMENT; - } - - for (i = 0; i < bank->num_sectors; i++) - { - uint32_t sec_start = bank->sectors[i].offset; - uint32_t 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 */ - LOG_WARNING("couldn't use block writes, falling back to single memory accesses"); - } - else if (retval == ERROR_FLASH_OPERATION_FAILED) - { - LOG_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_write_memory(target, address, 2, 1, buffer + bytes_written); - - /* get status command */ - target_write_u16(target, bank_adr, 0x70); - - int timeout; - for (timeout = 0; timeout < 1000; timeout++) - { - target_read_u8(target, bank_adr, &status); - if (status & 0x80) - break; - alive_sleep(1); - } - if (timeout == 1000) - { - LOG_ERROR("write timed out"); - return ERROR_FAIL; - } - - /* 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) - { - uint8_t 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 command */ - target_write_u16(target, bank_adr, 0x40); - target_write_memory(target, address, 2, 1, last_halfword); - - /* query status command */ - target_write_u16(target, bank_adr, 0x70); - - int timeout; - for (timeout = 0; timeout < 1000; timeout++) - { - target_read_u8(target, bank_adr, &status); - if (status & 0x80) - break; - alive_sleep(1); - } - if (timeout == 1000) - { - LOG_ERROR("write timed out"); - return ERROR_FAIL; - } - - /* 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; -} - -static int str9x_probe(struct flash_bank *bank) -{ - return ERROR_OK; -} - -#if 0 -COMMAND_HANDLER(str9x_handle_part_id_command) -{ - return ERROR_OK; -} -#endif - -static int str9x_info(struct flash_bank *bank, char *buf, int buf_size) -{ - snprintf(buf, buf_size, "str9x flash driver info"); - return ERROR_OK; -} - -COMMAND_HANDLER(str9x_handle_flash_config_command) -{ - struct str9x_flash_bank *str9x_info; - struct target *target = NULL; - - if (CMD_ARGC < 5) - { - return ERROR_COMMAND_SYNTAX_ERROR; - } - - struct flash_bank *bank; - int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) - return retval; - - uint32_t bbsr, nbbsr, bbadr, nbbadr; - COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], bbsr); - COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], nbbsr); - COMMAND_PARSE_NUMBER(u32, CMD_ARGV[3], bbadr); - COMMAND_PARSE_NUMBER(u32, CMD_ARGV[4], nbbadr); - - str9x_info = bank->driver_priv; - - target = bank->target; - - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - /* config flash controller */ - target_write_u32(target, FLASH_BBSR, bbsr); - target_write_u32(target, FLASH_NBBSR, nbbsr); - target_write_u32(target, FLASH_BBADR, bbadr >> 2); - target_write_u32(target, FLASH_NBBADR, nbbadr >> 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; -} - -static const struct command_registration str9x_config_command_handlers[] = { - { - .name = "disable_jtag", - .handler = &str9x_handle_flash_config_command, - .mode = COMMAND_EXEC, - .help = "configure str9x flash controller", - .usage = " ", - }, - COMMAND_REGISTRATION_DONE -}; -static const struct command_registration str9x_command_handlers[] = { - { - .name = "str9x", - .mode = COMMAND_ANY, - .help = "str9x flash command group", - .chain = str9x_config_command_handlers, - }, - COMMAND_REGISTRATION_DONE -}; - -struct flash_driver str9x_flash = { - .name = "str9x", - .commands = str9x_command_handlers, - .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 = &default_flash_blank_check, - .protect_check = &str9x_protect_check, - .info = &str9x_info, - }; diff --git a/src/flash/str9x.h b/src/flash/str9x.h deleted file mode 100644 index c9d5152f..00000000 --- a/src/flash/str9x.h +++ /dev/null @@ -1,62 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2005 by Dominic Rath * - * Dominic.Rath@gmx.de * - * * - * Copyright (C) 2008 by Spencer Oliver * - * spen@spen-soft.co.uk * - * * - * 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 STR9X_H -#define STR9X_H - -#include "flash.h" - -struct str9x_flash_bank -{ - uint32_t *sector_bits; - int variant; - int bank1; - struct working_area *write_algorithm; -}; - -enum str9x_status_codes -{ - STR9X_CMD_SUCCESS = 0, - STR9X_INVALID_COMMAND = 1, - STR9X_SRC_ADDR_ERROR = 2, - STR9X_DST_ADDR_ERROR = 3, - STR9X_SRC_ADDR_NOT_MAPPED = 4, - STR9X_DST_ADDR_NOT_MAPPED = 5, - STR9X_COUNT_ERROR = 6, - STR9X_INVALID_SECTOR = 7, - STR9X_SECTOR_NOT_BLANK = 8, - STR9X_SECTOR_NOT_PREPARED = 9, - STR9X_COMPARE_ERROR = 10, - STR9X_BUSY = 11 -}; - -/* Flash registers */ - -#define FLASH_BBSR 0x54000000 /* Boot Bank Size Register */ -#define FLASH_NBBSR 0x54000004 /* Non-Boot Bank Size Register */ -#define FLASH_BBADR 0x5400000C /* Boot Bank Base Address Register */ -#define FLASH_NBBADR 0x54000010 /* Non-Boot Bank Base Address Register */ -#define FLASH_CR 0x54000018 /* Control Register */ -#define FLASH_SR 0x5400001C /* Status Register */ -#define FLASH_BCE5ADDR 0x54000020 /* BC Fifth Entry Target Address Register */ - -#endif /* STR9X_H */ diff --git a/src/flash/str9xpec.c b/src/flash/str9xpec.c deleted file mode 100644 index 96e12596..00000000 --- a/src/flash/str9xpec.c +++ /dev/null @@ -1,1257 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2005 by Dominic Rath * - * Dominic.Rath@gmx.de * - * * - * Copyright (C) 2008 by Spencer Oliver * - * spen@spen-soft.co.uk * - * * - * 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 "str9xpec.h" -#include "arm7_9_common.h" - - -static int str9xpec_erase_area(struct flash_bank *bank, int first, int last); -static int str9xpec_set_address(struct flash_bank *bank, uint8_t sector); -static int str9xpec_write_options(struct flash_bank *bank); - -int str9xpec_set_instr(struct jtag_tap *tap, uint32_t new_instr, tap_state_t end_state) -{ - if (tap == NULL) { - return ERROR_TARGET_INVALID; - } - - if (buf_get_u32(tap->cur_instr, 0, tap->ir_length) != new_instr) - { - struct scan_field field; - - field.tap = tap; - field.num_bits = tap->ir_length; - field.out_value = calloc(DIV_ROUND_UP(field.num_bits, 8), 1); - buf_set_u32(field.out_value, 0, field.num_bits, new_instr); - field.in_value = NULL; - - jtag_add_ir_scan(1, &field, end_state); - - free(field.out_value); - } - - return ERROR_OK; -} - -static uint8_t str9xpec_isc_status(struct jtag_tap *tap) -{ - struct scan_field field; - uint8_t status; - - if (str9xpec_set_instr(tap, ISC_NOOP, TAP_IRPAUSE) != ERROR_OK) - return ISC_STATUS_ERROR; - - field.tap = tap; - field.num_bits = 8; - field.out_value = NULL; - field.in_value = &status; - - - jtag_add_dr_scan(1, &field, jtag_set_end_state(TAP_IDLE)); - jtag_execute_queue(); - - LOG_DEBUG("status: 0x%2.2x", status); - - if (status & ISC_STATUS_SECURITY) - LOG_INFO("Device Security Bit Set"); - - return status; -} - -static int str9xpec_isc_enable(struct flash_bank *bank) -{ - uint8_t status; - struct jtag_tap *tap; - struct str9xpec_flash_controller *str9xpec_info = bank->driver_priv; - - tap = str9xpec_info->tap; - - if (str9xpec_info->isc_enable) - return ERROR_OK; - - /* enter isc mode */ - if (str9xpec_set_instr(tap, ISC_ENABLE, TAP_IDLE) != ERROR_OK) - return ERROR_TARGET_INVALID; - - /* check ISC status */ - status = str9xpec_isc_status(tap); - if (status & ISC_STATUS_MODE) - { - /* we have entered isc mode */ - str9xpec_info->isc_enable = 1; - LOG_DEBUG("ISC_MODE Enabled"); - } - - return ERROR_OK; -} - -static int str9xpec_isc_disable(struct flash_bank *bank) -{ - uint8_t status; - struct jtag_tap *tap; - struct str9xpec_flash_controller *str9xpec_info = bank->driver_priv; - - tap = str9xpec_info->tap; - - if (!str9xpec_info->isc_enable) - return ERROR_OK; - - if (str9xpec_set_instr(tap, ISC_DISABLE, TAP_IDLE) != ERROR_OK) - return ERROR_TARGET_INVALID; - - /* delay to handle aborts */ - jtag_add_sleep(50); - - /* check ISC status */ - status = str9xpec_isc_status(tap); - if (!(status & ISC_STATUS_MODE)) - { - /* we have left isc mode */ - str9xpec_info->isc_enable = 0; - LOG_DEBUG("ISC_MODE Disabled"); - } - - return ERROR_OK; -} - -static int str9xpec_read_config(struct flash_bank *bank) -{ - struct scan_field field; - uint8_t status; - struct jtag_tap *tap; - - struct str9xpec_flash_controller *str9xpec_info = bank->driver_priv; - - tap = str9xpec_info->tap; - - LOG_DEBUG("ISC_CONFIGURATION"); - - /* execute ISC_CONFIGURATION command */ - str9xpec_set_instr(tap, ISC_CONFIGURATION, TAP_IRPAUSE); - - field.tap = tap; - field.num_bits = 64; - field.out_value = NULL; - field.in_value = str9xpec_info->options; - - - jtag_add_dr_scan(1, &field, jtag_set_end_state(TAP_IDLE)); - jtag_execute_queue(); - - status = str9xpec_isc_status(tap); - - return status; -} - -static int str9xpec_build_block_list(struct flash_bank *bank) -{ - struct str9xpec_flash_controller *str9xpec_info = bank->driver_priv; - - int i; - int num_sectors; - int b0_sectors = 0, b1_sectors = 0; - uint32_t offset = 0; - int b1_size = 0x2000; - - switch (bank->size) - { - case (256 * 1024): - b0_sectors = 4; - break; - case (512 * 1024): - b0_sectors = 8; - break; - case (1024 * 1024): - b0_sectors = 16; - break; - case (2048 * 1024): - b0_sectors = 32; - break; - case (128 * 1024): - b1_size = 0x4000; - b1_sectors = 8; - break; - case (32 * 1024): - b1_sectors = 4; - break; - default: - LOG_ERROR("BUG: unknown bank->size encountered"); - exit(-1); - } - - num_sectors = b0_sectors + b1_sectors; - - bank->num_sectors = num_sectors; - bank->sectors = malloc(sizeof(struct flash_sector) * num_sectors); - str9xpec_info->sector_bits = malloc(sizeof(uint32_t) * num_sectors); - - num_sectors = 0; - - for (i = 0; i < b0_sectors; i++) - { - bank->sectors[num_sectors].offset = offset; - bank->sectors[num_sectors].size = 0x10000; - offset += bank->sectors[i].size; - bank->sectors[num_sectors].is_erased = -1; - bank->sectors[num_sectors].is_protected = 1; - str9xpec_info->sector_bits[num_sectors++] = i; - } - - for (i = 0; i < b1_sectors; i++) - { - bank->sectors[num_sectors].offset = offset; - bank->sectors[num_sectors].size = b1_size; - offset += bank->sectors[i].size; - bank->sectors[num_sectors].is_erased = -1; - bank->sectors[num_sectors].is_protected = 1; - str9xpec_info->sector_bits[num_sectors++] = i + 32; - } - - return ERROR_OK; -} - -/* flash bank str9x 0 0 - */ -FLASH_BANK_COMMAND_HANDLER(str9xpec_flash_bank_command) -{ - struct str9xpec_flash_controller *str9xpec_info; - struct arm *armv4_5 = NULL; - struct arm7_9_common *arm7_9 = NULL; - struct arm_jtag *jtag_info = NULL; - - if (CMD_ARGC < 6) - { - LOG_WARNING("incomplete flash_bank str9x configuration"); - return ERROR_FLASH_BANK_INVALID; - } - - str9xpec_info = malloc(sizeof(struct str9xpec_flash_controller)); - bank->driver_priv = str9xpec_info; - - /* REVISIT verify that the jtag position of flash controller is - * right after *THIS* core, which must be a STR9xx core ... - */ - armv4_5 = bank->target->arch_info; - arm7_9 = armv4_5->arch_info; - jtag_info = &arm7_9->jtag_info; - - str9xpec_info->tap = bank->target->tap; - str9xpec_info->isc_enable = 0; - - str9xpec_build_block_list(bank); - - /* clear option byte register */ - buf_set_u32(str9xpec_info->options, 0, 64, 0); - - return ERROR_OK; -} - -static int str9xpec_blank_check(struct flash_bank *bank, int first, int last) -{ - struct scan_field field; - uint8_t status; - struct jtag_tap *tap; - int i; - uint8_t *buffer = NULL; - - struct str9xpec_flash_controller *str9xpec_info = bank->driver_priv; - - tap = str9xpec_info->tap; - - if (!str9xpec_info->isc_enable) { - str9xpec_isc_enable(bank); - } - - if (!str9xpec_info->isc_enable) { - return ERROR_FLASH_OPERATION_FAILED; - } - - buffer = calloc(DIV_ROUND_UP(64, 8), 1); - - LOG_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(tap, ISC_BLANK_CHECK, TAP_IRPAUSE); - - field.tap = tap; - field.num_bits = 64; - field.out_value = buffer; - field.in_value = NULL; - - jtag_add_dr_scan(1, &field, jtag_set_end_state(TAP_IDLE)); - jtag_add_sleep(40000); - - /* read blank check result */ - field.tap = tap; - field.num_bits = 64; - field.out_value = NULL; - field.in_value = buffer; - - jtag_add_dr_scan(1, &field, TAP_IRPAUSE); - jtag_execute_queue(); - - status = str9xpec_isc_status(tap); - - 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; -} - -static int str9xpec_protect_check(struct flash_bank *bank) -{ - uint8_t status; - int i; - - struct str9xpec_flash_controller *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; -} - -static int str9xpec_erase_area(struct flash_bank *bank, int first, int last) -{ - struct scan_field field; - uint8_t status; - struct jtag_tap *tap; - int i; - uint8_t *buffer = NULL; - - struct str9xpec_flash_controller *str9xpec_info = bank->driver_priv; - - tap = str9xpec_info->tap; - - if (!str9xpec_info->isc_enable) { - str9xpec_isc_enable(bank); - } - - if (!str9xpec_info->isc_enable) { - return ISC_STATUS_ERROR; - } - - buffer = calloc(DIV_ROUND_UP(64, 8), 1); - - LOG_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); - } - } - - LOG_DEBUG("ISC_ERASE"); - - /* execute ISC_ERASE command */ - str9xpec_set_instr(tap, ISC_ERASE, TAP_IRPAUSE); - - field.tap = tap; - field.num_bits = 64; - field.out_value = buffer; - field.in_value = NULL; - - jtag_add_dr_scan(1, &field, jtag_set_end_state(TAP_IDLE)); - jtag_execute_queue(); - - jtag_add_sleep(10); - - /* wait for erase completion */ - while (!((status = str9xpec_isc_status(tap)) & ISC_STATUS_BUSY)) { - alive_sleep(1); - } - - free(buffer); - - str9xpec_isc_disable(bank); - - return status; -} - -static int str9xpec_erase(struct flash_bank *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; -} - -static int str9xpec_lock_device(struct flash_bank *bank) -{ - struct scan_field field; - uint8_t status; - struct jtag_tap *tap; - struct str9xpec_flash_controller *str9xpec_info = NULL; - - str9xpec_info = bank->driver_priv; - tap = str9xpec_info->tap; - - 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(tap, ISC_PROGRAM_SECURITY, TAP_IDLE); - - str9xpec_set_instr(tap, ISC_NOOP, TAP_IRPAUSE); - - do { - field.tap = tap; - field.num_bits = 8; - field.out_value = NULL; - field.in_value = &status; - - jtag_add_dr_scan(1, &field, jtag_get_end_state()); - jtag_execute_queue(); - - } while (!(status & ISC_STATUS_BUSY)); - - str9xpec_isc_disable(bank); - - return status; -} - -static int str9xpec_unlock_device(struct flash_bank *bank) -{ - uint8_t status; - - status = str9xpec_erase_area(bank, 0, 255); - - return status; -} - -static int str9xpec_protect(struct flash_bank *bank, int set, int first, int last) -{ - uint8_t status; - int i; - - struct str9xpec_flash_controller *str9xpec_info = bank->driver_priv; - - status = str9xpec_read_config(bank); - - if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS) - return ERROR_FLASH_OPERATION_FAILED; - - LOG_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; -} - -static int str9xpec_set_address(struct flash_bank *bank, uint8_t sector) -{ - struct jtag_tap *tap; - struct scan_field field; - struct str9xpec_flash_controller *str9xpec_info = bank->driver_priv; - - tap = str9xpec_info->tap; - - /* set flash controller address */ - str9xpec_set_instr(tap, ISC_ADDRESS_SHIFT, TAP_IRPAUSE); - - field.tap = tap; - field.num_bits = 8; - field.out_value = §or; - field.in_value = NULL; - - jtag_add_dr_scan(1, &field, jtag_get_end_state()); - - return ERROR_OK; -} - -static int str9xpec_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) -{ - struct str9xpec_flash_controller *str9xpec_info = bank->driver_priv; - uint32_t dwords_remaining = (count / 8); - uint32_t bytes_remaining = (count & 0x00000007); - uint32_t bytes_written = 0; - uint8_t status; - uint32_t check_address = offset; - struct jtag_tap *tap; - struct scan_field field; - uint8_t *scanbuf; - int i; - int first_sector = 0; - int last_sector = 0; - - tap = str9xpec_info->tap; - - if (!str9xpec_info->isc_enable) { - str9xpec_isc_enable(bank); - } - - if (!str9xpec_info->isc_enable) { - return ERROR_FLASH_OPERATION_FAILED; - } - - if (offset & 0x7) - { - LOG_WARNING("offset 0x%" PRIx32 " breaks required 8-byte alignment", offset); - return ERROR_FLASH_DST_BREAKS_ALIGNMENT; - } - - for (i = 0; i < bank->num_sectors; i++) - { - uint32_t sec_start = bank->sectors[i].offset; - uint32_t 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; - - LOG_DEBUG("first_sector: %i, last_sector: %i", first_sector, last_sector); - - scanbuf = calloc(DIV_ROUND_UP(64, 8), 1); - - LOG_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(tap, ISC_PROGRAM, TAP_IRPAUSE); - - field.tap = tap; - field.num_bits = 64; - field.out_value = (buffer + bytes_written); - field.in_value = NULL; - - jtag_add_dr_scan(1, &field, jtag_set_end_state(TAP_IDLE)); - - /* small delay before polling */ - jtag_add_sleep(50); - - str9xpec_set_instr(tap, ISC_NOOP, TAP_IRPAUSE); - - do { - field.tap = tap; - field.num_bits = 8; - field.out_value = NULL; - field.in_value = scanbuf; - - jtag_add_dr_scan(1, &field, jtag_get_end_state()); - 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) - { - uint8_t 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(tap, ISC_PROGRAM, TAP_IRPAUSE); - - field.tap = tap; - field.num_bits = 64; - field.out_value = last_dword; - field.in_value = NULL; - - jtag_add_dr_scan(1, &field, jtag_set_end_state(TAP_IDLE)); - - /* small delay before polling */ - jtag_add_sleep(50); - - str9xpec_set_instr(tap, ISC_NOOP, TAP_IRPAUSE); - - do { - field.tap = tap; - field.num_bits = 8; - field.out_value = NULL; - field.in_value = scanbuf; - - jtag_add_dr_scan(1, &field, jtag_get_end_state()); - 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; -} - -static int str9xpec_probe(struct flash_bank *bank) -{ - return ERROR_OK; -} - -COMMAND_HANDLER(str9xpec_handle_part_id_command) -{ - struct scan_field field; - uint8_t *buffer = NULL; - struct jtag_tap *tap; - uint32_t idcode; - struct str9xpec_flash_controller *str9xpec_info = NULL; - - if (CMD_ARGC < 1) - return ERROR_COMMAND_SYNTAX_ERROR; - - struct flash_bank *bank; - int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) - return retval; - - str9xpec_info = bank->driver_priv; - tap = str9xpec_info->tap; - - buffer = calloc(DIV_ROUND_UP(32, 8), 1); - - str9xpec_set_instr(tap, ISC_IDCODE, TAP_IRPAUSE); - - field.tap = tap; - field.num_bits = 32; - field.out_value = NULL; - field.in_value = buffer; - - jtag_add_dr_scan(1, &field, jtag_set_end_state(TAP_IDLE)); - jtag_execute_queue(); - - idcode = buf_get_u32(buffer, 0, 32); - - command_print(CMD_CTX, "str9xpec part id: 0x%8.8" PRIx32 "", idcode); - - free(buffer); - - return ERROR_OK; -} - -static int str9xpec_erase_check(struct flash_bank *bank) -{ - return str9xpec_blank_check(bank, 0, bank->num_sectors - 1); -} - -static int str9xpec_info(struct flash_bank *bank, char *buf, int buf_size) -{ - snprintf(buf, buf_size, "str9xpec flash driver info"); - return ERROR_OK; -} - -COMMAND_HANDLER(str9xpec_handle_flash_options_read_command) -{ - uint8_t status; - struct str9xpec_flash_controller *str9xpec_info = NULL; - - if (CMD_ARGC < 1) - { - command_print(CMD_CTX, "str9xpec options_read "); - return ERROR_OK; - } - - struct flash_bank *bank; - int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) - return retval; - - 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; -} - -static int str9xpec_write_options(struct flash_bank *bank) -{ - struct scan_field field; - uint8_t status; - struct jtag_tap *tap; - struct str9xpec_flash_controller *str9xpec_info = NULL; - - str9xpec_info = bank->driver_priv; - tap = str9xpec_info->tap; - - /* 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(tap, ISC_PROGRAM, TAP_IRPAUSE); - - field.tap = tap; - field.num_bits = 64; - field.out_value = str9xpec_info->options; - field.in_value = NULL; - - jtag_add_dr_scan(1, &field, jtag_set_end_state(TAP_IDLE)); - - /* small delay before polling */ - jtag_add_sleep(50); - - str9xpec_set_instr(tap, ISC_NOOP, TAP_IRPAUSE); - - do { - field.tap = tap; - field.num_bits = 8; - field.out_value = NULL; - field.in_value = &status; - - jtag_add_dr_scan(1, &field, jtag_get_end_state()); - jtag_execute_queue(); - - } while (!(status & ISC_STATUS_BUSY)); - - str9xpec_isc_disable(bank); - - return status; -} - -COMMAND_HANDLER(str9xpec_handle_flash_options_write_command) -{ - uint8_t status; - - if (CMD_ARGC < 1) - { - command_print(CMD_CTX, "str9xpec options_write "); - return ERROR_OK; - } - - struct flash_bank *bank; - int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) - return retval; - - status = str9xpec_write_options(bank); - - if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS) - return ERROR_FLASH_OPERATION_FAILED; - - return ERROR_OK; -} - -COMMAND_HANDLER(str9xpec_handle_flash_options_cmap_command) -{ - struct str9xpec_flash_controller *str9xpec_info = NULL; - - if (CMD_ARGC < 2) - { - command_print(CMD_CTX, "str9xpec options_cmap "); - return ERROR_OK; - } - - struct flash_bank *bank; - int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) - return retval; - - str9xpec_info = bank->driver_priv; - - if (strcmp(CMD_ARGV[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; -} - -COMMAND_HANDLER(str9xpec_handle_flash_options_lvdthd_command) -{ - struct str9xpec_flash_controller *str9xpec_info = NULL; - - if (CMD_ARGC < 2) - { - command_print(CMD_CTX, "str9xpec options_lvdthd <2.4v | 2.7v>"); - return ERROR_OK; - } - - struct flash_bank *bank; - int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) - return retval; - - str9xpec_info = bank->driver_priv; - - if (strcmp(CMD_ARGV[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; -} - -COMMAND_HANDLER(str9xpec_handle_flash_options_lvdsel_command) -{ - struct str9xpec_flash_controller *str9xpec_info = NULL; - - if (CMD_ARGC < 2) - { - command_print(CMD_CTX, "str9xpec options_lvdsel "); - return ERROR_OK; - } - - struct flash_bank *bank; - int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) - return retval; - - str9xpec_info = bank->driver_priv; - - if (strcmp(CMD_ARGV[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; -} - -COMMAND_HANDLER(str9xpec_handle_flash_options_lvdwarn_command) -{ - struct str9xpec_flash_controller *str9xpec_info = NULL; - - if (CMD_ARGC < 2) - { - command_print(CMD_CTX, "str9xpec options_lvdwarn "); - return ERROR_OK; - } - - struct flash_bank *bank; - int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) - return retval; - - str9xpec_info = bank->driver_priv; - - if (strcmp(CMD_ARGV[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; -} - -COMMAND_HANDLER(str9xpec_handle_flash_lock_command) -{ - uint8_t status; - - if (CMD_ARGC < 1) - { - command_print(CMD_CTX, "str9xpec lock "); - return ERROR_OK; - } - - struct flash_bank *bank; - int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) - return retval; - - status = str9xpec_lock_device(bank); - - if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS) - return ERROR_FLASH_OPERATION_FAILED; - - return ERROR_OK; -} - -COMMAND_HANDLER(str9xpec_handle_flash_unlock_command) -{ - uint8_t status; - - if (CMD_ARGC < 1) - { - command_print(CMD_CTX, "str9xpec unlock "); - return ERROR_OK; - } - - struct flash_bank *bank; - int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) - return retval; - - status = str9xpec_unlock_device(bank); - - if ((status & ISC_STATUS_ERROR) != STR9XPEC_ISC_SUCCESS) - return ERROR_FLASH_OPERATION_FAILED; - - return ERROR_OK; -} - -COMMAND_HANDLER(str9xpec_handle_flash_enable_turbo_command) -{ - struct jtag_tap *tap0; - struct jtag_tap *tap1; - struct jtag_tap *tap2; - struct str9xpec_flash_controller *str9xpec_info = NULL; - - if (CMD_ARGC < 1) - { - command_print(CMD_CTX, "str9xpec enable_turbo "); - return ERROR_OK; - } - - struct flash_bank *bank; - int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) - return retval; - - str9xpec_info = bank->driver_priv; - - tap0 = str9xpec_info->tap; - - /* remove arm core from chain - enter turbo mode */ - tap1 = tap0->next_tap; - if (tap1 == NULL) - { - /* things are *WRONG* */ - command_print(CMD_CTX,"**STR9FLASH** (tap1) invalid chain?"); - return ERROR_OK; - } - tap2 = tap1->next_tap; - if (tap2 == NULL) - { - /* things are *WRONG* */ - command_print(CMD_CTX,"**STR9FLASH** (tap2) invalid chain?"); - return ERROR_OK; - } - - /* enable turbo mode - TURBO-PROG-ENABLE */ - str9xpec_set_instr(tap2, 0xD, TAP_IDLE); - if ((retval = jtag_execute_queue()) != ERROR_OK) - return retval; - - /* modify scan chain - str9 core has been removed */ - tap1->enabled = 0; - - return ERROR_OK; -} - -COMMAND_HANDLER(str9xpec_handle_flash_disable_turbo_command) -{ - struct jtag_tap *tap; - struct str9xpec_flash_controller *str9xpec_info = NULL; - - if (CMD_ARGC < 1) - { - command_print(CMD_CTX, "str9xpec disable_turbo "); - return ERROR_OK; - } - - struct flash_bank *bank; - int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) - return retval; - - str9xpec_info = bank->driver_priv; - tap = str9xpec_info->tap; - - if (tap == NULL) - return ERROR_FAIL; - - /* exit turbo mode via RESET */ - str9xpec_set_instr(tap, ISC_NOOP, TAP_IDLE); - jtag_add_tlr(); - jtag_execute_queue(); - - /* restore previous scan chain */ - if (tap->next_tap) { - tap->next_tap->enabled = 1; - } - - return ERROR_OK; -} - -static const struct command_registration str9xpec_config_command_handlers[] = { - { - .name = "enable_turbo", - .handler = str9xpec_handle_flash_enable_turbo_command, - .mode = COMMAND_EXEC, - .help = "enable str9xpec turbo mode", - }, - { - .name = "disable_turbo", - .handler = str9xpec_handle_flash_disable_turbo_command, - .mode = COMMAND_EXEC, - .help = "disable str9xpec turbo mode", - }, - { - .name = "options_cmap", - .handler = str9xpec_handle_flash_options_cmap_command, - .mode = COMMAND_EXEC, - .help = "configure str9xpec boot sector", - }, - { - .name = "options_lvdthd", - .handler = str9xpec_handle_flash_options_lvdthd_command, - .mode = COMMAND_EXEC, - .help = "configure str9xpec lvd threshold", - }, - { - .name = "options_lvdsel", - .handler = str9xpec_handle_flash_options_lvdsel_command, - .mode = COMMAND_EXEC, - .help = "configure str9xpec lvd selection", - }, - { - .name = "options_lvdwarn", - .handler = str9xpec_handle_flash_options_lvdwarn_command, - .mode = COMMAND_EXEC, - .help = "configure str9xpec lvd warning", - }, - { - .name = "options_read", - .handler = str9xpec_handle_flash_options_read_command, - .mode = COMMAND_EXEC, - .help = "read str9xpec options", - }, - { - .name = "options_write", - .handler = str9xpec_handle_flash_options_write_command, - .mode = COMMAND_EXEC, - .help = "write str9xpec options", - }, - { - .name = "lock", - .handler = str9xpec_handle_flash_lock_command, - .mode = COMMAND_EXEC, - .help = "lock str9xpec device", - }, - { - .name = "unlock", - .handler = str9xpec_handle_flash_unlock_command, - .mode = COMMAND_EXEC, - .help = "unlock str9xpec device", - }, - { - .name = "part_id", - .handler = str9xpec_handle_part_id_command, - .mode = COMMAND_EXEC, - .help = "print part id of str9xpec flash bank ", - }, - COMMAND_REGISTRATION_DONE -}; -static const struct command_registration str9xpec_command_handlers[] = { - { - .name = "str9xpec", - .mode = COMMAND_ANY, - .help = "str9xpec flash command group", - .chain = str9xpec_config_command_handlers, - }, - COMMAND_REGISTRATION_DONE -}; - -struct flash_driver str9xpec_flash = { - .name = "str9xpec", - .commands = str9xpec_command_handlers, - .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, - }; diff --git a/src/flash/str9xpec.h b/src/flash/str9xpec.h deleted file mode 100644 index 1c8d41b1..00000000 --- a/src/flash/str9xpec.h +++ /dev/null @@ -1,79 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2005 by Dominic Rath * - * Dominic.Rath@gmx.de * - * * - * Copyright (C) 2008 by Spencer Oliver * - * spen@spen-soft.co.uk * - * * - * 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 STR9XPEC_H -#define STR9XPEC_H - -#include "flash.h" -#include "jtag.h" - -struct str9xpec_flash_controller -{ - struct jtag_tap *tap; - uint32_t *sector_bits; - int chain_pos; - int isc_enable; - uint8_t options[8]; -}; - -enum str9xpec_status_codes -{ - STR9XPEC_INVALID_COMMAND = 1, - STR9XPEC_ISC_SUCCESS = 2, - STR9XPEC_ISC_DISABLED = 3, - STR9XPEC_ISC_INTFAIL = 32, -}; - -/* ISC commands */ - -#define ISC_IDCODE 0xFE -#define ISC_MFG_READ 0x4C -#define ISC_CONFIGURATION 0x07 -#define ISC_ENABLE 0x0C -#define ISC_DISABLE 0x0F -#define ISC_NOOP 0x10 -#define ISC_ADDRESS_SHIFT 0x11 -#define ISC_CLR_STATUS 0x13 -#define ISC_PROGRAM 0x20 -#define ISC_PROGRAM_SECURITY 0x22 -#define ISC_PROGRAM_UC 0x23 -#define ISC_ERASE 0x30 -#define ISC_READ 0x50 -#define ISC_BLANK_CHECK 0x60 - -/* ISC_DEFAULT bit definitions */ - -#define ISC_STATUS_SECURITY 0x40 -#define ISC_STATUS_INT_ERROR 0x30 -#define ISC_STATUS_MODE 0x08 -#define ISC_STATUS_BUSY 0x04 -#define ISC_STATUS_ERROR 0x03 - -/* Option bytes definitions */ - -#define STR9XPEC_OPT_CSMAPBIT 48 -#define STR9XPEC_OPT_LVDTHRESBIT 49 -#define STR9XPEC_OPT_LVDSELBIT 50 -#define STR9XPEC_OPT_LVDWARNBIT 51 -#define STR9XPEC_OPT_OTPBIT 63 - -#endif /* STR9XPEC_H */ diff --git a/src/flash/tms470.c b/src/flash/tms470.c deleted file mode 100644 index 59659346..00000000 --- a/src/flash/tms470.c +++ /dev/null @@ -1,1271 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2007,2008 by Christopher Kilgour * - * techie |_at_| whiterocker |_dot_| com * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "tms470.h" - - -/* ---------------------------------------------------------------------- - Internal Support, Helpers - ---------------------------------------------------------------------- */ - -const struct flash_sector TMS470R1A256_SECTORS[] = { - {0x00000000, 0x00002000, -1, -1}, - {0x00002000, 0x00002000, -1, -1}, - {0x00004000, 0x00002000, -1, -1}, - {0x00006000, 0x00002000, -1, -1}, - {0x00008000, 0x00008000, -1, -1}, - {0x00010000, 0x00008000, -1, -1}, - {0x00018000, 0x00008000, -1, -1}, - {0x00020000, 0x00008000, -1, -1}, - {0x00028000, 0x00008000, -1, -1}, - {0x00030000, 0x00008000, -1, -1}, - {0x00038000, 0x00002000, -1, -1}, - {0x0003A000, 0x00002000, -1, -1}, - {0x0003C000, 0x00002000, -1, -1}, - {0x0003E000, 0x00002000, -1, -1}, -}; - -#define TMS470R1A256_NUM_SECTORS \ - ARRAY_SIZE(TMS470R1A256_SECTORS) - -const struct flash_sector TMS470R1A288_BANK0_SECTORS[] = { - {0x00000000, 0x00002000, -1, -1}, - {0x00002000, 0x00002000, -1, -1}, - {0x00004000, 0x00002000, -1, -1}, - {0x00006000, 0x00002000, -1, -1}, -}; - -#define TMS470R1A288_BANK0_NUM_SECTORS \ - ARRAY_SIZE(TMS470R1A288_BANK0_SECTORS) - -const struct flash_sector TMS470R1A288_BANK1_SECTORS[] = { - {0x00040000, 0x00010000, -1, -1}, - {0x00050000, 0x00010000, -1, -1}, - {0x00060000, 0x00010000, -1, -1}, - {0x00070000, 0x00010000, -1, -1}, -}; - -#define TMS470R1A288_BANK1_NUM_SECTORS \ - ARRAY_SIZE(TMS470R1A288_BANK1_SECTORS) - -const struct flash_sector TMS470R1A384_BANK0_SECTORS[] = { - {0x00000000, 0x00002000, -1, -1}, - {0x00002000, 0x00002000, -1, -1}, - {0x00004000, 0x00004000, -1, -1}, - {0x00008000, 0x00004000, -1, -1}, - {0x0000C000, 0x00004000, -1, -1}, - {0x00010000, 0x00004000, -1, -1}, - {0x00014000, 0x00004000, -1, -1}, - {0x00018000, 0x00002000, -1, -1}, - {0x0001C000, 0x00002000, -1, -1}, - {0x0001E000, 0x00002000, -1, -1}, -}; - -#define TMS470R1A384_BANK0_NUM_SECTORS \ - ARRAY_SIZE(TMS470R1A384_BANK0_SECTORS) - -const struct flash_sector TMS470R1A384_BANK1_SECTORS[] = { - {0x00020000, 0x00008000, -1, -1}, - {0x00028000, 0x00008000, -1, -1}, - {0x00030000, 0x00008000, -1, -1}, - {0x00038000, 0x00008000, -1, -1}, -}; - -#define TMS470R1A384_BANK1_NUM_SECTORS \ - ARRAY_SIZE(TMS470R1A384_BANK1_SECTORS) - -const struct flash_sector TMS470R1A384_BANK2_SECTORS[] = { - {0x00040000, 0x00008000, -1, -1}, - {0x00048000, 0x00008000, -1, -1}, - {0x00050000, 0x00008000, -1, -1}, - {0x00058000, 0x00008000, -1, -1}, -}; - -#define TMS470R1A384_BANK2_NUM_SECTORS \ - ARRAY_SIZE(TMS470R1A384_BANK2_SECTORS) - -/* ---------------------------------------------------------------------- */ - -static int tms470_read_part_info(struct flash_bank *bank) -{ - struct tms470_flash_bank *tms470_info = bank->driver_priv; - struct target *target = bank->target; - uint32_t device_ident_reg; - uint32_t silicon_version; - uint32_t technology_family; - uint32_t rom_flash; - uint32_t part_number; - char *part_name; - - /* we shall not rely on the caller in this test, this function allocates memory, - thus and executing the code more than once may cause memory leak */ - if (tms470_info->device_ident_reg) - return ERROR_OK; - - /* read and parse the device identification register */ - target_read_u32(target, 0xFFFFFFF0, &device_ident_reg); - - LOG_INFO("device_ident_reg = 0x%08" PRIx32 "", device_ident_reg); - - if ((device_ident_reg & 7) == 0) - { - LOG_WARNING("Cannot identify target as a TMS470 family."); - return ERROR_FLASH_OPERATION_FAILED; - } - - silicon_version = (device_ident_reg >> 12) & 0xF; - technology_family = (device_ident_reg >> 11) & 1; - rom_flash = (device_ident_reg >> 10) & 1; - part_number = (device_ident_reg >> 3) & 0x7f; - - /* - * If the part number is known, determine if the flash bank is valid - * based on the base address being within the known flash bank - * ranges. Then fixup/complete the remaining fields of the flash - * bank structure. - */ - switch (part_number) - { - case 0x0a: - part_name = "TMS470R1A256"; - - if (bank->base >= 0x00040000) - { - LOG_ERROR("No %s flash bank contains base address 0x%08" PRIx32 ".", part_name, bank->base); - return ERROR_FLASH_OPERATION_FAILED; - } - tms470_info->ordinal = 0; - bank->base = 0x00000000; - bank->size = 256 * 1024; - bank->num_sectors = TMS470R1A256_NUM_SECTORS; - bank->sectors = malloc(sizeof(TMS470R1A256_SECTORS)); - if (!bank->sectors) - { - return ERROR_FLASH_OPERATION_FAILED; - } - (void)memcpy(bank->sectors, TMS470R1A256_SECTORS, sizeof(TMS470R1A256_SECTORS)); - break; - - case 0x2b: - part_name = "TMS470R1A288"; - - if (bank->base < 0x00008000) - { - tms470_info->ordinal = 0; - bank->base = 0x00000000; - bank->size = 32 * 1024; - bank->num_sectors = TMS470R1A288_BANK0_NUM_SECTORS; - bank->sectors = malloc(sizeof(TMS470R1A288_BANK0_SECTORS)); - if (!bank->sectors) - { - return ERROR_FLASH_OPERATION_FAILED; - } - (void)memcpy(bank->sectors, TMS470R1A288_BANK0_SECTORS, sizeof(TMS470R1A288_BANK0_SECTORS)); - } - else if ((bank->base >= 0x00040000) && (bank->base < 0x00080000)) - { - tms470_info->ordinal = 1; - bank->base = 0x00040000; - bank->size = 256 * 1024; - bank->num_sectors = TMS470R1A288_BANK1_NUM_SECTORS; - bank->sectors = malloc(sizeof(TMS470R1A288_BANK1_SECTORS)); - if (!bank->sectors) - { - return ERROR_FLASH_OPERATION_FAILED; - } - (void)memcpy(bank->sectors, TMS470R1A288_BANK1_SECTORS, sizeof(TMS470R1A288_BANK1_SECTORS)); - } - else - { - LOG_ERROR("No %s flash bank contains base address 0x%08" PRIx32 ".", part_name, bank->base); - return ERROR_FLASH_OPERATION_FAILED; - } - break; - - case 0x2d: - part_name = "TMS470R1A384"; - - if (bank->base < 0x00020000) - { - tms470_info->ordinal = 0; - bank->base = 0x00000000; - bank->size = 128 * 1024; - bank->num_sectors = TMS470R1A384_BANK0_NUM_SECTORS; - bank->sectors = malloc(sizeof(TMS470R1A384_BANK0_SECTORS)); - if (!bank->sectors) - { - return ERROR_FLASH_OPERATION_FAILED; - } - (void)memcpy(bank->sectors, TMS470R1A384_BANK0_SECTORS, sizeof(TMS470R1A384_BANK0_SECTORS)); - } - else if ((bank->base >= 0x00020000) && (bank->base < 0x00040000)) - { - tms470_info->ordinal = 1; - bank->base = 0x00020000; - bank->size = 128 * 1024; - bank->num_sectors = TMS470R1A384_BANK1_NUM_SECTORS; - bank->sectors = malloc(sizeof(TMS470R1A384_BANK1_SECTORS)); - if (!bank->sectors) - { - return ERROR_FLASH_OPERATION_FAILED; - } - (void)memcpy(bank->sectors, TMS470R1A384_BANK1_SECTORS, sizeof(TMS470R1A384_BANK1_SECTORS)); - } - else if ((bank->base >= 0x00040000) && (bank->base < 0x00060000)) - { - tms470_info->ordinal = 2; - bank->base = 0x00040000; - bank->size = 128 * 1024; - bank->num_sectors = TMS470R1A384_BANK2_NUM_SECTORS; - bank->sectors = malloc(sizeof(TMS470R1A384_BANK2_SECTORS)); - if (!bank->sectors) - { - return ERROR_FLASH_OPERATION_FAILED; - } - (void)memcpy(bank->sectors, TMS470R1A384_BANK2_SECTORS, sizeof(TMS470R1A384_BANK2_SECTORS)); - } - else - { - LOG_ERROR("No %s flash bank contains base address 0x%08" PRIx32 ".", part_name, bank->base); - return ERROR_FLASH_OPERATION_FAILED; - } - break; - - default: - LOG_WARNING("Could not identify part 0x%02x as a member of the TMS470 family.", (unsigned)part_number); - return ERROR_FLASH_OPERATION_FAILED; - } - - /* turn off memory selects */ - target_write_u32(target, 0xFFFFFFE4, 0x00000000); - target_write_u32(target, 0xFFFFFFE0, 0x00000000); - - bank->chip_width = 32; - bank->bus_width = 32; - - LOG_INFO("Identified %s, ver=%d, core=%s, nvmem=%s.", - part_name, - (int)(silicon_version), - (technology_family ? "1.8v" : "3.3v"), - (rom_flash ? "rom" : "flash")); - - tms470_info->device_ident_reg = device_ident_reg; - tms470_info->silicon_version = silicon_version; - tms470_info->technology_family = technology_family; - tms470_info->rom_flash = rom_flash; - tms470_info->part_number = part_number; - tms470_info->part_name = part_name; - - /* - * Disable reset on address access violation. - */ - target_write_u32(target, 0xFFFFFFE0, 0x00004007); - - return ERROR_OK; -} - -/* ---------------------------------------------------------------------- */ - -static uint32_t keysSet = 0; -static uint32_t flashKeys[4]; - -COMMAND_HANDLER(tms470_handle_flash_keyset_command) -{ - if (CMD_ARGC > 4) - { - command_print(CMD_CTX, "tms470 flash_keyset "); - return ERROR_INVALID_ARGUMENTS; - } - else if (CMD_ARGC == 4) - { - int i; - - for (i = 0; i < 4; i++) - { - int start = (0 == strncmp(CMD_ARGV[i], "0x", 2)) ? 2 : 0; - - if (1 != sscanf(&CMD_ARGV[i][start], "%" SCNx32 "", &flashKeys[i])) - { - command_print(CMD_CTX, "could not process flash key %s", CMD_ARGV[i]); - LOG_ERROR("could not process flash key %s", CMD_ARGV[i]); - return ERROR_INVALID_ARGUMENTS; - } - } - - keysSet = 1; - } - else if (CMD_ARGC != 0) - { - command_print(CMD_CTX, "tms470 flash_keyset "); - return ERROR_INVALID_ARGUMENTS; - } - - if (keysSet) - { - command_print(CMD_CTX, "using flash keys 0x%08" PRIx32 ", 0x%08" PRIx32 ", 0x%08" PRIx32 ", 0x%08" PRIx32 "", - flashKeys[0], flashKeys[1], flashKeys[2], flashKeys[3]); - } - else - { - command_print(CMD_CTX, "flash keys not set"); - } - - return ERROR_OK; -} - -static const uint32_t FLASH_KEYS_ALL_ONES[] = { 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, -}; - -static const uint32_t FLASH_KEYS_ALL_ZEROS[] = { 0x00000000, 0x00000000, - 0x00000000, 0x00000000, -}; - -static const uint32_t FLASH_KEYS_MIX1[] = { 0xf0fff0ff, 0xf0fff0ff, - 0xf0fff0ff, 0xf0fff0ff -}; - -static const uint32_t FLASH_KEYS_MIX2[] = { 0x0000ffff, 0x0000ffff, - 0x0000ffff, 0x0000ffff -}; - -/* ---------------------------------------------------------------------- */ - -static int oscMHz = 12; - -COMMAND_HANDLER(tms470_handle_osc_megahertz_command) -{ - if (CMD_ARGC > 1) - { - command_print(CMD_CTX, "tms470 osc_megahertz "); - return ERROR_INVALID_ARGUMENTS; - } - else if (CMD_ARGC == 1) - { - sscanf(CMD_ARGV[0], "%d", &oscMHz); - } - - if (oscMHz <= 0) - { - LOG_ERROR("osc_megahertz must be positive and non-zero!"); - command_print(CMD_CTX, "osc_megahertz must be positive and non-zero!"); - oscMHz = 12; - return ERROR_INVALID_ARGUMENTS; - } - - command_print(CMD_CTX, "osc_megahertz=%d", oscMHz); - - return ERROR_OK; -} - -/* ---------------------------------------------------------------------- */ - -static int plldis = 0; - -COMMAND_HANDLER(tms470_handle_plldis_command) -{ - if (CMD_ARGC > 1) - { - command_print(CMD_CTX, "tms470 plldis <0 | 1>"); - return ERROR_INVALID_ARGUMENTS; - } - else if (CMD_ARGC == 1) - { - sscanf(CMD_ARGV[0], "%d", &plldis); - plldis = plldis ? 1 : 0; - } - - command_print(CMD_CTX, "plldis=%d", plldis); - - return ERROR_OK; -} - -/* ---------------------------------------------------------------------- */ - -static int tms470_check_flash_unlocked(struct target * target) -{ - uint32_t fmbbusy; - - target_read_u32(target, 0xFFE89C08, &fmbbusy); - LOG_INFO("tms470 fmbbusy = 0x%08" PRIx32 " -> %s", fmbbusy, fmbbusy & 0x8000 ? "unlocked" : "LOCKED"); - return fmbbusy & 0x8000 ? ERROR_OK : ERROR_FLASH_OPERATION_FAILED; -} - -/* ---------------------------------------------------------------------- */ - -static int tms470_try_flash_keys(struct target * target, const uint32_t * key_set) -{ - uint32_t glbctrl, fmmstat; - int retval = ERROR_FLASH_OPERATION_FAILED; - - /* set GLBCTRL.4 */ - target_read_u32(target, 0xFFFFFFDC, &glbctrl); - target_write_u32(target, 0xFFFFFFDC, glbctrl | 0x10); - - /* only perform the key match when 3VSTAT is clear */ - target_read_u32(target, 0xFFE8BC0C, &fmmstat); - if (!(fmmstat & 0x08)) - { - unsigned i; - uint32_t fmbptr, fmbac2, orig_fmregopt; - - target_write_u32(target, 0xFFE8BC04, fmmstat & ~0x07); - - /* wait for pump ready */ - do - { - target_read_u32(target, 0xFFE8A814, &fmbptr); - alive_sleep(1); - } - while (!(fmbptr & 0x0200)); - - /* force max wait states */ - target_read_u32(target, 0xFFE88004, &fmbac2); - target_write_u32(target, 0xFFE88004, fmbac2 | 0xff); - - /* save current access mode, force normal read mode */ - target_read_u32(target, 0xFFE89C00, &orig_fmregopt); - target_write_u32(target, 0xFFE89C00, 0x00); - - for (i = 0; i < 4; i++) - { - uint32_t tmp; - - /* There is no point displaying the value of tmp, it is - * filtered by the chip. The purpose of this read is to - * prime the unlocking logic rather than read out the value. - */ - target_read_u32(target, 0x00001FF0 + 4 * i, &tmp); - - LOG_INFO("tms470 writing fmpkey = 0x%08" PRIx32 "", key_set[i]); - target_write_u32(target, 0xFFE89C0C, key_set[i]); - } - - if (ERROR_OK == tms470_check_flash_unlocked(target)) - { - /* - * There seems to be a side-effect of reading the FMPKEY - * register in that it re-enables the protection. So we - * re-enable it. - */ - for (i = 0; i < 4; i++) - { - uint32_t tmp; - - target_read_u32(target, 0x00001FF0 + 4 * i, &tmp); - target_write_u32(target, 0xFFE89C0C, key_set[i]); - } - retval = ERROR_OK; - } - - /* restore settings */ - target_write_u32(target, 0xFFE89C00, orig_fmregopt); - target_write_u32(target, 0xFFE88004, fmbac2); - } - - /* clear config bit */ - target_write_u32(target, 0xFFFFFFDC, glbctrl); - - return retval; -} - -/* ---------------------------------------------------------------------- */ - -static int tms470_unlock_flash(struct flash_bank *bank) -{ - struct target *target = bank->target; - const uint32_t *p_key_sets[5]; - unsigned i, key_set_count; - - if (keysSet) - { - key_set_count = 5; - p_key_sets[0] = flashKeys; - p_key_sets[1] = FLASH_KEYS_ALL_ONES; - p_key_sets[2] = FLASH_KEYS_ALL_ZEROS; - p_key_sets[3] = FLASH_KEYS_MIX1; - p_key_sets[4] = FLASH_KEYS_MIX2; - } - else - { - key_set_count = 4; - p_key_sets[0] = FLASH_KEYS_ALL_ONES; - p_key_sets[1] = FLASH_KEYS_ALL_ZEROS; - p_key_sets[2] = FLASH_KEYS_MIX1; - p_key_sets[3] = FLASH_KEYS_MIX2; - } - - for (i = 0; i < key_set_count; i++) - { - if (tms470_try_flash_keys(target, p_key_sets[i]) == ERROR_OK) - { - LOG_INFO("tms470 flash is unlocked"); - return ERROR_OK; - } - } - - LOG_WARNING("tms470 could not unlock flash memory protection level 2"); - return ERROR_FLASH_OPERATION_FAILED; -} - -/* ---------------------------------------------------------------------- */ - -static int tms470_flash_initialize_internal_state_machine(struct flash_bank *bank) -{ - uint32_t fmmac2, fmmac1, fmmaxep, k, delay, glbctrl, sysclk; - struct target *target = bank->target; - struct tms470_flash_bank *tms470_info = bank->driver_priv; - int result = ERROR_OK; - - /* - * Select the desired bank to be programmed by writing BANK[2:0] of - * FMMAC2. - */ - target_read_u32(target, 0xFFE8BC04, &fmmac2); - fmmac2 &= ~0x0007; - fmmac2 |= (tms470_info->ordinal & 7); - target_write_u32(target, 0xFFE8BC04, fmmac2); - LOG_DEBUG("set fmmac2 = 0x%04" PRIx32 "", fmmac2); - - /* - * Disable level 1 sector protection by setting bit 15 of FMMAC1. - */ - target_read_u32(target, 0xFFE8BC00, &fmmac1); - fmmac1 |= 0x8000; - target_write_u32(target, 0xFFE8BC00, fmmac1); - LOG_DEBUG("set fmmac1 = 0x%04" PRIx32 "", fmmac1); - - /* - * FMTCREG = 0x2fc0; - */ - target_write_u32(target, 0xFFE8BC10, 0x2fc0); - LOG_DEBUG("set fmtcreg = 0x2fc0"); - - /* - * MAXPP = 50 - */ - target_write_u32(target, 0xFFE8A07C, 50); - LOG_DEBUG("set fmmaxpp = 50"); - - /* - * MAXCP = 0xf000 + 2000 - */ - target_write_u32(target, 0xFFE8A084, 0xf000 + 2000); - LOG_DEBUG("set fmmaxcp = 0x%04x", 0xf000 + 2000); - - /* - * configure VHV - */ - target_read_u32(target, 0xFFE8A080, &fmmaxep); - if (fmmaxep == 0xf000) - { - fmmaxep = 0xf000 + 4095; - target_write_u32(target, 0xFFE8A80C, 0x9964); - LOG_DEBUG("set fmptr3 = 0x9964"); - } - else - { - fmmaxep = 0xa000 + 4095; - target_write_u32(target, 0xFFE8A80C, 0x9b64); - LOG_DEBUG("set fmptr3 = 0x9b64"); - } - target_write_u32(target, 0xFFE8A080, fmmaxep); - LOG_DEBUG("set fmmaxep = 0x%04" PRIx32 "", fmmaxep); - - /* - * FMPTR4 = 0xa000 - */ - target_write_u32(target, 0xFFE8A810, 0xa000); - LOG_DEBUG("set fmptr4 = 0xa000"); - - /* - * FMPESETUP, delay parameter selected based on clock frequency. - * - * According to the TI App Note SPNU257 and flashing code, delay is - * int((sysclk(MHz) + 1) / 2), with a minimum of 5. The system - * clock is usually derived from the ZPLL module, and selected by - * the plldis global. - */ - target_read_u32(target, 0xFFFFFFDC, &glbctrl); - sysclk = (plldis ? 1 : (glbctrl & 0x08) ? 4 : 8) * oscMHz / (1 + (glbctrl & 7)); - delay = (sysclk > 10) ? (sysclk + 1) / 2 : 5; - target_write_u32(target, 0xFFE8A018, (delay << 4) | (delay << 8)); - LOG_DEBUG("set fmpsetup = 0x%04" PRIx32 "", (delay << 4) | (delay << 8)); - - /* - * FMPVEVACCESS, based on delay. - */ - k = delay | (delay << 8); - target_write_u32(target, 0xFFE8A05C, k); - LOG_DEBUG("set fmpvevaccess = 0x%04" PRIx32 "", k); - - /* - * FMPCHOLD, FMPVEVHOLD, FMPVEVSETUP, based on delay. - */ - k <<= 1; - target_write_u32(target, 0xFFE8A034, k); - LOG_DEBUG("set fmpchold = 0x%04" PRIx32 "", k); - target_write_u32(target, 0xFFE8A040, k); - LOG_DEBUG("set fmpvevhold = 0x%04" PRIx32 "", k); - target_write_u32(target, 0xFFE8A024, k); - LOG_DEBUG("set fmpvevsetup = 0x%04" PRIx32 "", k); - - /* - * FMCVACCESS, based on delay. - */ - k = delay * 16; - target_write_u32(target, 0xFFE8A060, k); - LOG_DEBUG("set fmcvaccess = 0x%04" PRIx32 "", k); - - /* - * FMCSETUP, based on delay. - */ - k = 0x3000 | delay * 20; - target_write_u32(target, 0xFFE8A020, k); - LOG_DEBUG("set fmcsetup = 0x%04" PRIx32 "", k); - - /* - * FMEHOLD, based on delay. - */ - k = (delay * 20) << 2; - target_write_u32(target, 0xFFE8A038, k); - LOG_DEBUG("set fmehold = 0x%04" PRIx32 "", k); - - /* - * PWIDTH, CWIDTH, EWIDTH, based on delay. - */ - target_write_u32(target, 0xFFE8A050, delay * 8); - LOG_DEBUG("set fmpwidth = 0x%04" PRIx32 "", delay * 8); - target_write_u32(target, 0xFFE8A058, delay * 1000); - LOG_DEBUG("set fmcwidth = 0x%04" PRIx32 "", delay * 1000); - target_write_u32(target, 0xFFE8A054, delay * 5400); - LOG_DEBUG("set fmewidth = 0x%04" PRIx32 "", delay * 5400); - - return result; -} - -/* ---------------------------------------------------------------------- */ - -int tms470_flash_status(struct flash_bank *bank) -{ - struct target *target = bank->target; - int result = ERROR_OK; - uint32_t fmmstat; - - target_read_u32(target, 0xFFE8BC0C, &fmmstat); - LOG_DEBUG("set fmmstat = 0x%04" PRIx32 "", fmmstat); - - if (fmmstat & 0x0080) - { - LOG_WARNING("tms470 flash command: erase still active after busy clear."); - result = ERROR_FLASH_OPERATION_FAILED; - } - - if (fmmstat & 0x0040) - { - LOG_WARNING("tms470 flash command: program still active after busy clear."); - result = ERROR_FLASH_OPERATION_FAILED; - } - - if (fmmstat & 0x0020) - { - LOG_WARNING("tms470 flash command: invalid data command."); - result = ERROR_FLASH_OPERATION_FAILED; - } - - if (fmmstat & 0x0010) - { - LOG_WARNING("tms470 flash command: program, erase or validate sector failed."); - result = ERROR_FLASH_OPERATION_FAILED; - } - - if (fmmstat & 0x0008) - { - LOG_WARNING("tms470 flash command: voltage instability detected."); - result = ERROR_FLASH_OPERATION_FAILED; - } - - if (fmmstat & 0x0006) - { - LOG_WARNING("tms470 flash command: command suspend detected."); - result = ERROR_FLASH_OPERATION_FAILED; - } - - if (fmmstat & 0x0001) - { - LOG_WARNING("tms470 flash command: sector was locked."); - result = ERROR_FLASH_OPERATION_FAILED; - } - - return result; -} - -/* ---------------------------------------------------------------------- */ - -static int tms470_erase_sector(struct flash_bank *bank, int sector) -{ - uint32_t glbctrl, orig_fmregopt, fmbsea, fmbseb, fmmstat; - struct target *target = bank->target; - uint32_t flashAddr = bank->base + bank->sectors[sector].offset; - int result = ERROR_OK; - - /* - * Set the bit GLBCTRL4 of the GLBCTRL register (in the System - * module) to enable writing to the flash registers }. - */ - target_read_u32(target, 0xFFFFFFDC, &glbctrl); - target_write_u32(target, 0xFFFFFFDC, glbctrl | 0x10); - LOG_DEBUG("set glbctrl = 0x%08" PRIx32 "", glbctrl | 0x10); - - /* Force normal read mode. */ - target_read_u32(target, 0xFFE89C00, &orig_fmregopt); - target_write_u32(target, 0xFFE89C00, 0); - LOG_DEBUG("set fmregopt = 0x%08x", 0); - - (void)tms470_flash_initialize_internal_state_machine(bank); - - /* - * Select one or more bits in FMBSEA or FMBSEB to disable Level 1 - * protection for the particular sector to be erased/written. - */ - if (sector < 16) - { - target_read_u32(target, 0xFFE88008, &fmbsea); - target_write_u32(target, 0xFFE88008, fmbsea | (1 << sector)); - LOG_DEBUG("set fmbsea = 0x%04" PRIx32 "", fmbsea | (1 << sector)); - } - else - { - target_read_u32(target, 0xFFE8800C, &fmbseb); - target_write_u32(target, 0xFFE8800C, fmbseb | (1 << (sector - 16))); - LOG_DEBUG("set fmbseb = 0x%04" PRIx32 "", fmbseb | (1 << (sector - 16))); - } - bank->sectors[sector].is_protected = 0; - - /* - * clear status regiser, sent erase command, kickoff erase - */ - target_write_u16(target, flashAddr, 0x0040); - LOG_DEBUG("write *(uint16_t *)0x%08" PRIx32 "=0x0040", flashAddr); - target_write_u16(target, flashAddr, 0x0020); - LOG_DEBUG("write *(uint16_t *)0x%08" PRIx32 "=0x0020", flashAddr); - target_write_u16(target, flashAddr, 0xffff); - LOG_DEBUG("write *(uint16_t *)0x%08" PRIx32 "=0xffff", flashAddr); - - /* - * Monitor FMMSTAT, busy until clear, then check and other flags for - * ultimate result of the operation. - */ - do - { - target_read_u32(target, 0xFFE8BC0C, &fmmstat); - if (fmmstat & 0x0100) - { - alive_sleep(1); - } - } - while (fmmstat & 0x0100); - - result = tms470_flash_status(bank); - - if (sector < 16) - { - target_write_u32(target, 0xFFE88008, fmbsea); - LOG_DEBUG("set fmbsea = 0x%04" PRIx32 "", fmbsea); - bank->sectors[sector].is_protected = fmbsea & (1 << sector) ? 0 : 1; - } - else - { - target_write_u32(target, 0xFFE8800C, fmbseb); - LOG_DEBUG("set fmbseb = 0x%04" PRIx32 "", fmbseb); - bank->sectors[sector].is_protected = fmbseb & (1 << (sector - 16)) ? 0 : 1; - } - target_write_u32(target, 0xFFE89C00, orig_fmregopt); - LOG_DEBUG("set fmregopt = 0x%08" PRIx32 "", orig_fmregopt); - target_write_u32(target, 0xFFFFFFDC, glbctrl); - LOG_DEBUG("set glbctrl = 0x%08" PRIx32 "", glbctrl); - - if (result == ERROR_OK) - { - bank->sectors[sector].is_erased = 1; - } - - return result; -} - -/* ---------------------------------------------------------------------- - Implementation of Flash Driver Interfaces - ---------------------------------------------------------------------- */ - -static const struct command_registration tms470_any_command_handlers[] = { - { - .name = "flash_keyset", - .handler = &tms470_handle_flash_keyset_command, - .mode = COMMAND_ANY, - .help = "tms470 flash_keyset ", - }, - { - .name = "osc_megahertz", - .handler = &tms470_handle_osc_megahertz_command, - .mode = COMMAND_ANY, - .help = "tms470 osc_megahertz ", - }, - { - .name = "plldis", - .handler = &tms470_handle_plldis_command, - .mode = COMMAND_ANY, - .help = "tms470 plldis <0/1>", - }, - COMMAND_REGISTRATION_DONE -}; -static const struct command_registration tms470_command_handlers[] = { - { - .name = "tms470", - .mode = COMMAND_ANY, - .help = "TI tms470 flash command group", - .chain = tms470_any_command_handlers, - }, - COMMAND_REGISTRATION_DONE -}; - -/* ---------------------------------------------------------------------- */ - -static int tms470_erase(struct flash_bank *bank, int first, int last) -{ - struct tms470_flash_bank *tms470_info = bank->driver_priv; - int sector, result = ERROR_OK; - - if (bank->target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - tms470_read_part_info(bank); - - if ((first < 0) || (first >= bank->num_sectors) || (last < 0) || (last >= bank->num_sectors) || (first > last)) - { - LOG_ERROR("Sector range %d to %d invalid.", first, last); - return ERROR_FLASH_SECTOR_INVALID; - } - - result = tms470_unlock_flash(bank); - if (result != ERROR_OK) - { - return result; - } - - for (sector = first; sector <= last; sector++) - { - LOG_INFO("Erasing tms470 bank %d sector %d...", tms470_info->ordinal, sector); - - result = tms470_erase_sector(bank, sector); - - if (result != ERROR_OK) - { - LOG_ERROR("tms470 could not erase flash sector."); - break; - } - else - { - LOG_INFO("sector erased successfully."); - } - } - - return result; -} - -/* ---------------------------------------------------------------------- */ - -static int tms470_protect(struct flash_bank *bank, int set, int first, int last) -{ - struct tms470_flash_bank *tms470_info = bank->driver_priv; - struct target *target = bank->target; - uint32_t fmmac2, fmbsea, fmbseb; - int sector; - - if (target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - tms470_read_part_info(bank); - - if ((first < 0) || (first >= bank->num_sectors) || (last < 0) || (last >= bank->num_sectors) || (first > last)) - { - LOG_ERROR("Sector range %d to %d invalid.", first, last); - return ERROR_FLASH_SECTOR_INVALID; - } - - /* enable the appropriate bank */ - target_read_u32(target, 0xFFE8BC04, &fmmac2); - target_write_u32(target, 0xFFE8BC04, (fmmac2 & ~7) | tms470_info->ordinal); - - /* get the original sector proection flags for this bank */ - target_read_u32(target, 0xFFE88008, &fmbsea); - target_read_u32(target, 0xFFE8800C, &fmbseb); - - for (sector = 0; sector < bank->num_sectors; sector++) - { - if (sector < 16) - { - fmbsea = set ? fmbsea & ~(1 << sector) : fmbsea | (1 << sector); - bank->sectors[sector].is_protected = set ? 1 : 0; - } - else - { - fmbseb = set ? fmbseb & ~(1 << (sector - 16)) : fmbseb | (1 << (sector - 16)); - bank->sectors[sector].is_protected = set ? 1 : 0; - } - } - - /* update the protection bits */ - target_write_u32(target, 0xFFE88008, fmbsea); - target_write_u32(target, 0xFFE8800C, fmbseb); - - return ERROR_OK; -} - -/* ---------------------------------------------------------------------- */ - -static int tms470_write(struct flash_bank *bank, uint8_t * buffer, uint32_t offset, uint32_t count) -{ - struct target *target = bank->target; - uint32_t glbctrl, fmbac2, orig_fmregopt, fmbsea, fmbseb, fmmaxpp, fmmstat; - int result = ERROR_OK; - uint32_t i; - - if (target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - tms470_read_part_info(bank); - - LOG_INFO("Writing %" PRId32 " bytes starting at 0x%08" PRIx32 "", count, bank->base + offset); - - /* set GLBCTRL.4 */ - target_read_u32(target, 0xFFFFFFDC, &glbctrl); - target_write_u32(target, 0xFFFFFFDC, glbctrl | 0x10); - - (void)tms470_flash_initialize_internal_state_machine(bank); - - /* force max wait states */ - target_read_u32(target, 0xFFE88004, &fmbac2); - target_write_u32(target, 0xFFE88004, fmbac2 | 0xff); - - /* save current access mode, force normal read mode */ - target_read_u32(target, 0xFFE89C00, &orig_fmregopt); - target_write_u32(target, 0xFFE89C00, 0x00); - - /* - * Disable Level 1 protection for all sectors to be erased/written. - */ - target_read_u32(target, 0xFFE88008, &fmbsea); - target_write_u32(target, 0xFFE88008, 0xffff); - target_read_u32(target, 0xFFE8800C, &fmbseb); - target_write_u32(target, 0xFFE8800C, 0xffff); - - /* read MAXPP */ - target_read_u32(target, 0xFFE8A07C, &fmmaxpp); - - for (i = 0; i < count; i += 2) - { - uint32_t addr = bank->base + offset + i; - uint16_t word = (((uint16_t) buffer[i]) << 8) | (uint16_t) buffer[i + 1]; - - if (word != 0xffff) - { - LOG_INFO("writing 0x%04x at 0x%08" PRIx32 "", word, addr); - - /* clear status register */ - target_write_u16(target, addr, 0x0040); - /* program flash command */ - target_write_u16(target, addr, 0x0010); - /* burn the 16-bit word (big-endian) */ - target_write_u16(target, addr, word); - - /* - * Monitor FMMSTAT, busy until clear, then check and other flags - * for ultimate result of the operation. - */ - do - { - target_read_u32(target, 0xFFE8BC0C, &fmmstat); - if (fmmstat & 0x0100) - { - alive_sleep(1); - } - } - while (fmmstat & 0x0100); - - if (fmmstat & 0x3ff) - { - LOG_ERROR("fmstat = 0x%04" PRIx32 "", fmmstat); - LOG_ERROR("Could not program word 0x%04x at address 0x%08" PRIx32 ".", word, addr); - result = ERROR_FLASH_OPERATION_FAILED; - break; - } - } - else - { - LOG_INFO("skipping 0xffff at 0x%08" PRIx32 "", addr); - } - } - - /* restore */ - target_write_u32(target, 0xFFE88008, fmbsea); - target_write_u32(target, 0xFFE8800C, fmbseb); - target_write_u32(target, 0xFFE88004, fmbac2); - target_write_u32(target, 0xFFE89C00, orig_fmregopt); - target_write_u32(target, 0xFFFFFFDC, glbctrl); - - return result; -} - -/* ---------------------------------------------------------------------- */ - -static int tms470_probe(struct flash_bank *bank) -{ - if (bank->target->state != TARGET_HALTED) - { - LOG_WARNING("Cannot communicate... target not halted."); - return ERROR_TARGET_NOT_HALTED; - } - - return tms470_read_part_info(bank); -} - -static int tms470_auto_probe(struct flash_bank *bank) -{ - struct tms470_flash_bank *tms470_info = bank->driver_priv; - - if (tms470_info->device_ident_reg) - return ERROR_OK; - return tms470_probe(bank); -} - -/* ---------------------------------------------------------------------- */ - -static int tms470_erase_check(struct flash_bank *bank) -{ - struct target *target = bank->target; - struct tms470_flash_bank *tms470_info = bank->driver_priv; - int sector, result = ERROR_OK; - uint32_t fmmac2, fmbac2, glbctrl, orig_fmregopt; - static uint8_t buffer[64 * 1024]; - - if (target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - if (!tms470_info->device_ident_reg) - { - tms470_read_part_info(bank); - } - - /* set GLBCTRL.4 */ - target_read_u32(target, 0xFFFFFFDC, &glbctrl); - target_write_u32(target, 0xFFFFFFDC, glbctrl | 0x10); - - /* save current access mode, force normal read mode */ - target_read_u32(target, 0xFFE89C00, &orig_fmregopt); - target_write_u32(target, 0xFFE89C00, 0x00); - - /* enable the appropriate bank */ - target_read_u32(target, 0xFFE8BC04, &fmmac2); - target_write_u32(target, 0xFFE8BC04, (fmmac2 & ~7) | tms470_info->ordinal); - - /* TCR = 0 */ - target_write_u32(target, 0xFFE8BC10, 0x2fc0); - - /* clear TEZ in fmbrdy */ - target_write_u32(target, 0xFFE88010, 0x0b); - - /* save current wait states, force max */ - target_read_u32(target, 0xFFE88004, &fmbac2); - target_write_u32(target, 0xFFE88004, fmbac2 | 0xff); - - /* - * The TI primitives inspect the flash memory by reading one 32-bit - * word at a time. Here we read an entire sector and inspect it in - * an attempt to reduce the JTAG overhead. - */ - for (sector = 0; sector < bank->num_sectors; sector++) - { - if (bank->sectors[sector].is_erased != 1) - { - uint32_t i, addr = bank->base + bank->sectors[sector].offset; - - LOG_INFO("checking flash bank %d sector %d", tms470_info->ordinal, sector); - - target_read_buffer(target, addr, bank->sectors[sector].size, buffer); - - bank->sectors[sector].is_erased = 1; - for (i = 0; i < bank->sectors[sector].size; i++) - { - if (buffer[i] != 0xff) - { - LOG_WARNING("tms470 bank %d, sector %d, not erased.", tms470_info->ordinal, sector); - LOG_WARNING("at location 0x%08" PRIx32 ": flash data is 0x%02x.", addr + i, buffer[i]); - - bank->sectors[sector].is_erased = 0; - break; - } - } - } - if (bank->sectors[sector].is_erased != 1) - { - result = ERROR_FLASH_SECTOR_NOT_ERASED; - break; - } - else - { - LOG_INFO("sector erased"); - } - } - - /* reset TEZ, wait states, read mode, GLBCTRL.4 */ - target_write_u32(target, 0xFFE88010, 0x0f); - target_write_u32(target, 0xFFE88004, fmbac2); - target_write_u32(target, 0xFFE89C00, orig_fmregopt); - target_write_u32(target, 0xFFFFFFDC, glbctrl); - - return result; -} - -/* ---------------------------------------------------------------------- */ - -static int tms470_protect_check(struct flash_bank *bank) -{ - struct target *target = bank->target; - struct tms470_flash_bank *tms470_info = bank->driver_priv; - int sector, result = ERROR_OK; - uint32_t fmmac2, fmbsea, fmbseb; - - if (target->state != TARGET_HALTED) - { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - if (!tms470_info->device_ident_reg) - { - tms470_read_part_info(bank); - } - - /* enable the appropriate bank */ - target_read_u32(target, 0xFFE8BC04, &fmmac2); - target_write_u32(target, 0xFFE8BC04, (fmmac2 & ~7) | tms470_info->ordinal); - - target_read_u32(target, 0xFFE88008, &fmbsea); - target_read_u32(target, 0xFFE8800C, &fmbseb); - - for (sector = 0; sector < bank->num_sectors; sector++) - { - int protected; - - if (sector < 16) - { - protected = fmbsea & (1 << sector) ? 0 : 1; - bank->sectors[sector].is_protected = protected; - } - else - { - protected = fmbseb & (1 << (sector - 16)) ? 0 : 1; - bank->sectors[sector].is_protected = protected; - } - - LOG_DEBUG("bank %d sector %d is %s", tms470_info->ordinal, sector, protected ? "protected" : "not protected"); - } - - return result; -} - -/* ---------------------------------------------------------------------- */ - -static int tms470_info(struct flash_bank *bank, char *buf, int buf_size) -{ - int used = 0; - struct tms470_flash_bank *tms470_info = bank->driver_priv; - - if (!tms470_info->device_ident_reg) - { - tms470_read_part_info(bank); - } - - if (!tms470_info->device_ident_reg) - { - (void)snprintf(buf, buf_size, "Cannot identify target as a TMS470\n"); - return ERROR_FLASH_OPERATION_FAILED; - } - - used += snprintf(buf, buf_size, "\ntms470 information: Chip is %s\n", tms470_info->part_name); - buf += used; - buf_size -= used; - - used += snprintf(buf, buf_size, "Flash protection level 2 is %s\n", tms470_check_flash_unlocked(bank->target) == ERROR_OK ? "disabled" : "enabled"); - buf += used; - buf_size -= used; - - return ERROR_OK; -} - -/* ---------------------------------------------------------------------- */ - -/* - * flash bank tms470 - * [options...] - */ - -FLASH_BANK_COMMAND_HANDLER(tms470_flash_bank_command) -{ - bank->driver_priv = malloc(sizeof(struct tms470_flash_bank)); - - if (!bank->driver_priv) - { - return ERROR_FLASH_OPERATION_FAILED; - } - - (void)memset(bank->driver_priv, 0, sizeof(struct tms470_flash_bank)); - - return ERROR_OK; -} - -struct flash_driver tms470_flash = { - .name = "tms470", - .commands = tms470_command_handlers, - .flash_bank_command = &tms470_flash_bank_command, - .erase = &tms470_erase, - .protect = &tms470_protect, - .write = &tms470_write, - .probe = &tms470_probe, - .auto_probe = &tms470_auto_probe, - .erase_check = &tms470_erase_check, - .protect_check = &tms470_protect_check, - .info = &tms470_info, - }; diff --git a/src/flash/tms470.h b/src/flash/tms470.h deleted file mode 100644 index f275e510..00000000 --- a/src/flash/tms470.h +++ /dev/null @@ -1,39 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2007,2008 by Christopher Kilgour * - * techie |_at_| whiterocker |_dot_| com * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ -#ifndef TMS470_DOT_H -#define TMS470_DOT_H - -#include "flash.h" - -struct tms470_flash_bank -{ - unsigned ordinal; - - /* device identification register */ - uint32_t device_ident_reg; - uint32_t silicon_version; - uint32_t technology_family; - uint32_t rom_flash; - uint32_t part_number; - char * part_name; - -}; - -#endif /* TMS470_DOT_H */ -- cgit v1.2.3