move nor drivers to src/flash/nor
Moves NOR flash drivers to 'src/flash/nor/'. Adds 'src/flash/nor/Makefile.am'. Builds 'libocdflashnor.la'.
This commit is contained in:
46
src/flash/nor/Makefile.am
Normal file
46
src/flash/nor/Makefile.am
Normal file
@@ -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
|
||||
425
src/flash/nor/aduc702x.c
Normal file
425
src/flash/nor/aduc702x.c
Normal file
@@ -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 <target#>
|
||||
* 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
|
||||
//<next>:
|
||||
0xe1c421b0, // strh r2, [r4, #16]
|
||||
0xe0d050b2, // ldrh r5, [r0], #2
|
||||
0xe1c450bc, // strh r5, [r4, #12]
|
||||
0xe5c46008, // strb r6, [r4, #8]
|
||||
//<wait_complete>:
|
||||
0xe1d430b0, // ldrh r3, [r4]
|
||||
0xe3130004, // tst r3, #4 ; 0x4
|
||||
0x1afffffc, // bne 1001c <wait_complete>
|
||||
0xe2822002, // add r2, r2, #2 ; 0x2
|
||||
0xe2511001, // subs r1, r1, #1 ; 0x1
|
||||
0x0a000001, // beq 1003c <done>
|
||||
0xe3130001, // tst r3, #1 ; 0x1
|
||||
0x1afffff3, // bne 1000c <next>
|
||||
//<done>:
|
||||
0xeafffffe // b 1003c <done>
|
||||
};
|
||||
|
||||
/* 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
|
||||
};
|
||||
2516
src/flash/nor/at91sam3.c
Normal file
2516
src/flash/nor/at91sam3.c
Normal file
File diff suppressed because it is too large
Load Diff
23
src/flash/nor/at91sam3.h
Normal file
23
src/flash/nor/at91sam3.h
Normal file
@@ -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;
|
||||
1213
src/flash/nor/at91sam7.c
Normal file
1213
src/flash/nor/at91sam7.c
Normal file
File diff suppressed because it is too large
Load Diff
118
src/flash/nor/at91sam7.h
Normal file
118
src/flash/nor/at91sam7.h
Normal file
@@ -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 */
|
||||
483
src/flash/nor/avrf.c
Normal file
483
src/flash/nor/avrf.c
Normal file
@@ -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 <bank>");
|
||||
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,
|
||||
};
|
||||
41
src/flash/nor/avrf.h
Normal file
41
src/flash/nor/avrf.h
Normal file
@@ -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 */
|
||||
2630
src/flash/nor/cfi.c
Normal file
2630
src/flash/nor/cfi.c
Normal file
File diff suppressed because it is too large
Load Diff
164
src/flash/nor/cfi.h
Normal file
164
src/flash/nor/cfi.h
Normal file
@@ -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 */
|
||||
444
src/flash/nor/ecos.c
Normal file
444
src/flash/nor/ecos.c
Normal file
@@ -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 <base> <size> <chip_width> <bus_width> <target#> <driverPath>
|
||||
*/
|
||||
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
|
||||
};
|
||||
149
src/flash/nor/faux.c
Normal file
149
src/flash/nor/faux.c
Normal file
@@ -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 <base> <size> <chip_width> <bus_width> <target#> <driverPath>
|
||||
*/
|
||||
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
|
||||
};
|
||||
812
src/flash/nor/lpc2000.c
Normal file
812
src/flash/nor/lpc2000.c
Normal file
@@ -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 <base> <size> 0 0 <target#> <lpc_variant> <cclk> [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 <num>",
|
||||
},
|
||||
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,
|
||||
};
|
||||
|
||||
|
||||
73
src/flash/nor/lpc2000.h
Normal file
73
src/flash/nor/lpc2000.h
Normal file
@@ -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 */
|
||||
485
src/flash/nor/lpc288x.c
Normal file
485
src/flash/nor/lpc288x.c
Normal file
@@ -0,0 +1,485 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2008 by *
|
||||
* Karl RobinSod <karl.robinsod@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. *
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* 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 <target#> <cclk> */
|
||||
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,
|
||||
};
|
||||
39
src/flash/nor/lpc288x.h
Normal file
39
src/flash/nor/lpc288x.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2008 by *
|
||||
* Karl RobinSod <karl.robinsod@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 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 */
|
||||
1834
src/flash/nor/lpc2900.c
Normal file
1834
src/flash/nor/lpc2900.c
Normal file
File diff suppressed because it is too large
Load Diff
491
src/flash/nor/non_cfi.c
Normal file
491
src/flash/nor/non_cfi.c
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
40
src/flash/nor/non_cfi.h
Normal file
40
src/flash/nor/non_cfi.h
Normal file
@@ -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 */
|
||||
361
src/flash/nor/ocl.c
Normal file
361
src/flash/nor/ocl.c
Normal file
@@ -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 <target#> */
|
||||
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,
|
||||
};
|
||||
40
src/flash/nor/ocl.h
Normal file
40
src/flash/nor/ocl.h
Normal file
@@ -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 */
|
||||
922
src/flash/nor/pic32mx.c
Normal file
922
src/flash/nor/pic32mx.c
Normal file
@@ -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 <base> <size> 0 0 <target#>
|
||||
*/
|
||||
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 <bank>");
|
||||
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 <bank>");
|
||||
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 <addr> <value> <bank>");
|
||||
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,
|
||||
};
|
||||
113
src/flash/nor/pic32mx.h
Normal file
113
src/flash/nor/pic32mx.h
Normal file
@@ -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 */
|
||||
|
||||
1195
src/flash/nor/stellaris.c
Normal file
1195
src/flash/nor/stellaris.c
Normal file
File diff suppressed because it is too large
Load Diff
99
src/flash/nor/stellaris.h
Normal file
99
src/flash/nor/stellaris.h
Normal file
@@ -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 */
|
||||
1240
src/flash/nor/stm32x.c
Normal file
1240
src/flash/nor/stm32x.c
Normal file
File diff suppressed because it is too large
Load Diff
101
src/flash/nor/stm32x.h
Normal file
101
src/flash/nor/stm32x.h
Normal file
@@ -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 */
|
||||
706
src/flash/nor/str7x.c
Normal file
706
src/flash/nor/str7x.c
Normal file
@@ -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 <base> <size> 0 0 <target#> <str71_variant>
|
||||
*/
|
||||
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 <bank>");
|
||||
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,
|
||||
};
|
||||
110
src/flash/nor/str7x.h
Normal file
110
src/flash/nor/str7x.h
Normal file
@@ -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 */
|
||||
711
src/flash/nor/str9x.c
Normal file
711
src/flash/nor/str9x.c
Normal file
@@ -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 <base> <size> 0 0 <target#>
|
||||
*/
|
||||
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 = "<bank_id> <BBSR> <NBBSR> <BBADR> <NBBADR>",
|
||||
},
|
||||
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,
|
||||
};
|
||||
62
src/flash/nor/str9x.h
Normal file
62
src/flash/nor/str9x.h
Normal file
@@ -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 */
|
||||
1257
src/flash/nor/str9xpec.c
Normal file
1257
src/flash/nor/str9xpec.c
Normal file
File diff suppressed because it is too large
Load Diff
79
src/flash/nor/str9xpec.h
Normal file
79
src/flash/nor/str9xpec.h
Normal file
@@ -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 */
|
||||
1271
src/flash/nor/tms470.c
Normal file
1271
src/flash/nor/tms470.c
Normal file
File diff suppressed because it is too large
Load Diff
39
src/flash/nor/tms470.h
Normal file
39
src/flash/nor/tms470.h
Normal file
@@ -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 */
|
||||
Reference in New Issue
Block a user