Merge branch 'riscv' into sba_tests
This commit is contained in:
@@ -18,6 +18,9 @@ NOR_DRIVERS = \
|
||||
%D%/ath79.c \
|
||||
%D%/atsamv.c \
|
||||
%D%/avrf.c \
|
||||
%D%/bluenrg-x.c \
|
||||
%D%/cc3220sf.c \
|
||||
%D%/cc26xx.c \
|
||||
%D%/cfi.c \
|
||||
%D%/dsp5680xx_flash.c \
|
||||
%D%/efm32.c \
|
||||
@@ -34,6 +37,7 @@ NOR_DRIVERS = \
|
||||
%D%/lpc2900.c \
|
||||
%D%/lpcspifi.c \
|
||||
%D%/mdr.c \
|
||||
%D%/msp432.c \
|
||||
%D%/mrvlqspi.c \
|
||||
%D%/niietcm4.c \
|
||||
%D%/non_cfi.c \
|
||||
@@ -42,6 +46,8 @@ NOR_DRIVERS = \
|
||||
%D%/ocl.c \
|
||||
%D%/pic32mx.c \
|
||||
%D%/psoc4.c \
|
||||
%D%/psoc5lp.c \
|
||||
%D%/psoc6.c \
|
||||
%D%/sim3x.c \
|
||||
%D%/spi.c \
|
||||
%D%/stmsmi.c \
|
||||
@@ -62,9 +68,12 @@ NOR_DRIVERS = \
|
||||
|
||||
NORHEADERS = \
|
||||
%D%/core.h \
|
||||
%D%/cc3220sf.h \
|
||||
%D%/cc26xx.h \
|
||||
%D%/cfi.h \
|
||||
%D%/driver.h \
|
||||
%D%/imp.h \
|
||||
%D%/non_cfi.h \
|
||||
%D%/ocl.h \
|
||||
%D%/spi.h
|
||||
%D%/spi.h \
|
||||
%D%/msp432.h
|
||||
|
||||
@@ -901,4 +901,5 @@ struct flash_driver ambiqmicro_flash = {
|
||||
.erase_check = default_flash_blank_check,
|
||||
.protect_check = ambiqmicro_protect_check,
|
||||
.info = get_ambiqmicro_info,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
|
||||
@@ -3117,6 +3117,22 @@ FLASH_BANK_COMMAND_HANDLER(sam3_flash_bank_command)
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all chips from the internal list without distingushing which one
|
||||
* is owned by this bank. This simplification works only for one shot
|
||||
* deallocation like current flash_free_all_banks()
|
||||
*/
|
||||
void sam3_free_driver_priv(struct flash_bank *bank)
|
||||
{
|
||||
struct sam3_chip *chip = all_sam3_chips;
|
||||
while (chip) {
|
||||
struct sam3_chip *next = chip->next;
|
||||
free(chip);
|
||||
chip = next;
|
||||
}
|
||||
all_sam3_chips = NULL;
|
||||
}
|
||||
|
||||
static int sam3_GetDetails(struct sam3_bank_private *pPrivate)
|
||||
{
|
||||
const struct sam3_chip_details *pDetails;
|
||||
@@ -3771,4 +3787,5 @@ struct flash_driver at91sam3_flash = {
|
||||
.auto_probe = sam3_auto_probe,
|
||||
.erase_check = sam3_erase_check,
|
||||
.protect_check = sam3_protect_check,
|
||||
.free_driver_priv = sam3_free_driver_priv,
|
||||
};
|
||||
|
||||
+54
-44
@@ -682,6 +682,40 @@ static const struct sam4_chip_details all_sam4_details[] = {
|
||||
},
|
||||
},
|
||||
},
|
||||
/*at91sam4sa16c - TFBGA100/VFBGA100/LQFP100*/
|
||||
{
|
||||
.chipid_cidr = 0x28a70ce0,
|
||||
.name = "at91sam4sa16c",
|
||||
.total_flash_size = 1024 * 1024,
|
||||
.total_sram_size = 160 * 1024,
|
||||
.n_gpnvms = 2,
|
||||
.n_banks = 1,
|
||||
|
||||
/* .bank[0] = { */
|
||||
{
|
||||
{
|
||||
.probed = 0,
|
||||
.pChip = NULL,
|
||||
.pBank = NULL,
|
||||
.bank_number = 0,
|
||||
.base_address = FLASH_BANK_BASE_S,
|
||||
.controller_address = 0x400e0a00,
|
||||
.flash_wait_states = 5,
|
||||
.present = 1,
|
||||
.size_bytes = 1024 * 1024,
|
||||
.nsectors = 128,
|
||||
.sector_size = 8192,
|
||||
.page_size = 512,
|
||||
},
|
||||
/* .bank[1] = {*/
|
||||
{
|
||||
.present = 0,
|
||||
.probed = 0,
|
||||
.bank_number = 1,
|
||||
|
||||
},
|
||||
},
|
||||
},
|
||||
/*atsam4s16b - LQFP64/QFN64/WLCSP64*/
|
||||
{
|
||||
.chipid_cidr = 0x289C0CE0,
|
||||
@@ -1243,50 +1277,6 @@ static const struct sam4_chip_details all_sam4_details[] = {
|
||||
.page_size = 512,
|
||||
},
|
||||
|
||||
/* .bank[1] = { */
|
||||
{
|
||||
.probed = 0,
|
||||
.pChip = NULL,
|
||||
.pBank = NULL,
|
||||
.bank_number = 1,
|
||||
.base_address = FLASH_BANK1_BASE_1024K_SD,
|
||||
.controller_address = 0x400e0c00,
|
||||
.flash_wait_states = 5,
|
||||
.present = 1,
|
||||
.size_bytes = 512 * 1024,
|
||||
.nsectors = 64,
|
||||
.sector_size = 8192,
|
||||
.page_size = 512,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
/*at91sam4sa16c*/
|
||||
{
|
||||
.chipid_cidr = 0x28a70ce0,
|
||||
.name = "at91sam4sa16c",
|
||||
.total_flash_size = 1024 * 1024,
|
||||
.total_sram_size = 160 * 1024,
|
||||
.n_gpnvms = 3,
|
||||
.n_banks = 2,
|
||||
|
||||
/* .bank[0] = { */
|
||||
{
|
||||
{
|
||||
.probed = 0,
|
||||
.pChip = NULL,
|
||||
.pBank = NULL,
|
||||
.bank_number = 0,
|
||||
.base_address = FLASH_BANK0_BASE_SD,
|
||||
.controller_address = 0x400e0a00,
|
||||
.flash_wait_states = 5,
|
||||
.present = 1,
|
||||
.size_bytes = 512 * 1024,
|
||||
.nsectors = 64,
|
||||
.sector_size = 8192,
|
||||
.page_size = 512,
|
||||
},
|
||||
|
||||
/* .bank[1] = { */
|
||||
{
|
||||
.probed = 0,
|
||||
@@ -2514,6 +2504,22 @@ FLASH_BANK_COMMAND_HANDLER(sam4_flash_bank_command)
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all chips from the internal list without distingushing which one
|
||||
* is owned by this bank. This simplification works only for one shot
|
||||
* deallocation like current flash_free_all_banks()
|
||||
*/
|
||||
static void sam4_free_driver_priv(struct flash_bank *bank)
|
||||
{
|
||||
struct sam4_chip *chip = all_sam4_chips;
|
||||
while (chip) {
|
||||
struct sam4_chip *next = chip->next;
|
||||
free(chip);
|
||||
chip = next;
|
||||
}
|
||||
all_sam4_chips = NULL;
|
||||
}
|
||||
|
||||
static int sam4_GetDetails(struct sam4_bank_private *pPrivate)
|
||||
{
|
||||
const struct sam4_chip_details *pDetails;
|
||||
@@ -2538,6 +2544,8 @@ static int sam4_GetDetails(struct sam4_bank_private *pPrivate)
|
||||
pPrivate->pChip->cfg.CHIPID_CIDR);
|
||||
sam4_explain_chipid_cidr(pPrivate->pChip);
|
||||
return ERROR_FAIL;
|
||||
} else {
|
||||
LOG_INFO("SAM4 Found chip %s, CIDR 0x%08x", pDetails->name, pDetails->chipid_cidr);
|
||||
}
|
||||
|
||||
/* DANGER: THERE ARE DRAGONS HERE */
|
||||
@@ -2608,6 +2616,7 @@ static int _sam4_probe(struct flash_bank *bank, int noise)
|
||||
for (x = 0; x < SAM4_MAX_FLASH_BANKS; x++) {
|
||||
if (bank->base == pPrivate->pChip->details.bank[x].base_address) {
|
||||
bank->size = pPrivate->pChip->details.bank[x].size_bytes;
|
||||
LOG_INFO("SAM4 Set flash bank to %08X - %08X, idx %d", bank->base, bank->base + bank->size, x);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -3194,4 +3203,5 @@ struct flash_driver at91sam4_flash = {
|
||||
.auto_probe = sam4_auto_probe,
|
||||
.erase_check = default_flash_blank_check,
|
||||
.protect_check = sam4_protect_check,
|
||||
.free_driver_priv = sam4_free_driver_priv,
|
||||
};
|
||||
|
||||
+14
-27
@@ -129,10 +129,8 @@ struct sam4l_info {
|
||||
|
||||
bool probed;
|
||||
struct target *target;
|
||||
struct sam4l_info *next;
|
||||
};
|
||||
|
||||
static struct sam4l_info *sam4l_chips;
|
||||
|
||||
static int sam4l_flash_wait_until_ready(struct target *target)
|
||||
{
|
||||
@@ -204,30 +202,6 @@ static int sam4l_flash_command(struct target *target, uint8_t cmd, int page)
|
||||
|
||||
FLASH_BANK_COMMAND_HANDLER(sam4l_flash_bank_command)
|
||||
{
|
||||
struct sam4l_info *chip = sam4l_chips;
|
||||
|
||||
while (chip) {
|
||||
if (chip->target == bank->target)
|
||||
break;
|
||||
chip = chip->next;
|
||||
}
|
||||
|
||||
if (!chip) {
|
||||
/* Create a new chip */
|
||||
chip = calloc(1, sizeof(*chip));
|
||||
if (!chip)
|
||||
return ERROR_FAIL;
|
||||
|
||||
chip->target = bank->target;
|
||||
chip->probed = false;
|
||||
|
||||
bank->driver_priv = chip;
|
||||
|
||||
/* Insert it into the chips list (at head) */
|
||||
chip->next = sam4l_chips;
|
||||
sam4l_chips = chip;
|
||||
}
|
||||
|
||||
if (bank->base != SAM4L_FLASH) {
|
||||
LOG_ERROR("Address 0x%08" PRIx32 " invalid bank address (try 0x%08" PRIx32
|
||||
"[at91sam4l series] )",
|
||||
@@ -235,6 +209,18 @@ FLASH_BANK_COMMAND_HANDLER(sam4l_flash_bank_command)
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
struct sam4l_info *chip;
|
||||
chip = calloc(1, sizeof(*chip));
|
||||
if (!chip) {
|
||||
LOG_ERROR("No memory for flash bank chip info");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
chip->target = bank->target;
|
||||
chip->probed = false;
|
||||
|
||||
bank->driver_priv = chip;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
@@ -396,7 +382,7 @@ static int sam4l_protect_check(struct flash_bank *bank)
|
||||
|
||||
static int sam4l_protect(struct flash_bank *bank, int set, int first, int last)
|
||||
{
|
||||
struct sam4l_info *chip = sam4l_chips;
|
||||
struct sam4l_info *chip = (struct sam4l_info *)bank->driver_priv;
|
||||
|
||||
if (bank->target->state != TARGET_HALTED) {
|
||||
LOG_ERROR("Target not halted");
|
||||
@@ -709,4 +695,5 @@ struct flash_driver at91sam4l_flash = {
|
||||
.auto_probe = sam4l_probe,
|
||||
.erase_check = default_flash_blank_check,
|
||||
.protect_check = sam4l_protect_check,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
|
||||
@@ -639,14 +639,6 @@ static int at91sam7_read_part_info(struct flash_bank *bank)
|
||||
|
||||
static int at91sam7_erase_check(struct flash_bank *bank)
|
||||
{
|
||||
struct target *target = bank->target;
|
||||
uint16_t retval;
|
||||
uint32_t blank;
|
||||
uint16_t fast_check;
|
||||
uint8_t *buffer;
|
||||
uint16_t nSector;
|
||||
uint16_t nByte;
|
||||
|
||||
if (bank->target->state != TARGET_HALTED) {
|
||||
LOG_ERROR("Target not halted");
|
||||
return ERROR_TARGET_NOT_HALTED;
|
||||
@@ -656,45 +648,7 @@ static int at91sam7_erase_check(struct flash_bank *bank)
|
||||
at91sam7_read_clock_info(bank);
|
||||
at91sam7_set_flash_mode(bank, FMR_TIMING_FLASH);
|
||||
|
||||
fast_check = 1;
|
||||
for (nSector = 0; nSector < bank->num_sectors; nSector++) {
|
||||
retval = target_blank_check_memory(target,
|
||||
bank->base + bank->sectors[nSector].offset,
|
||||
bank->sectors[nSector].size,
|
||||
&blank, bank->erased_value);
|
||||
if (retval != ERROR_OK) {
|
||||
fast_check = 0;
|
||||
break;
|
||||
}
|
||||
if (blank == 0xFF)
|
||||
bank->sectors[nSector].is_erased = 1;
|
||||
else
|
||||
bank->sectors[nSector].is_erased = 0;
|
||||
}
|
||||
|
||||
if (fast_check)
|
||||
return ERROR_OK;
|
||||
|
||||
LOG_USER("Running slow fallback erase check - add working memory");
|
||||
|
||||
buffer = malloc(bank->sectors[0].size);
|
||||
for (nSector = 0; nSector < bank->num_sectors; nSector++) {
|
||||
bank->sectors[nSector].is_erased = 1;
|
||||
retval = target_read_memory(target, bank->base + bank->sectors[nSector].offset, 4,
|
||||
bank->sectors[nSector].size/4, buffer);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
for (nByte = 0; nByte < bank->sectors[nSector].size; nByte++) {
|
||||
if (buffer[nByte] != 0xFF) {
|
||||
bank->sectors[nSector].is_erased = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
free(buffer);
|
||||
|
||||
return ERROR_OK;
|
||||
return default_flash_blank_check(bank);
|
||||
}
|
||||
|
||||
static int at91sam7_protect_check(struct flash_bank *bank)
|
||||
|
||||
+276
-127
@@ -83,6 +83,9 @@
|
||||
#define SAMD_GET_SERIES(id) (((id >> 16) & 0x3F))
|
||||
#define SAMD_GET_DEVSEL(id) (id & 0xFF)
|
||||
|
||||
/* Bits to mask out lockbits in user row */
|
||||
#define NVMUSERROW_LOCKBIT_MASK ((uint64_t)0x0000FFFFFFFFFFFF)
|
||||
|
||||
struct samd_part {
|
||||
uint8_t id;
|
||||
const char *name;
|
||||
@@ -112,15 +115,16 @@ static const struct samd_part samd10_parts[] = {
|
||||
|
||||
/* Known SAMD11 parts */
|
||||
static const struct samd_part samd11_parts[] = {
|
||||
{ 0x0, "SAMD11D14AMU", 16, 4 },
|
||||
{ 0x0, "SAMD11D14AM", 16, 4 },
|
||||
{ 0x1, "SAMD11D13AMU", 8, 4 },
|
||||
{ 0x2, "SAMD11D12AMU", 4, 4 },
|
||||
{ 0x3, "SAMD11D14ASU", 16, 4 },
|
||||
{ 0x3, "SAMD11D14ASS", 16, 4 },
|
||||
{ 0x4, "SAMD11D13ASU", 8, 4 },
|
||||
{ 0x5, "SAMD11D12ASU", 4, 4 },
|
||||
{ 0x6, "SAMD11C14A", 16, 4 },
|
||||
{ 0x7, "SAMD11C13A", 8, 4 },
|
||||
{ 0x8, "SAMD11C12A", 4, 4 },
|
||||
{ 0x9, "SAMD11D14AU", 16, 4 },
|
||||
};
|
||||
|
||||
/* Known SAMD20 parts. See Table 12-8 in 42129F–SAM–10/2013 */
|
||||
@@ -159,23 +163,22 @@ static const struct samd_part samd21_parts[] = {
|
||||
{ 0xC, "SAMD21E16A", 64, 8 },
|
||||
{ 0xD, "SAMD21E15A", 32, 4 },
|
||||
{ 0xE, "SAMD21E14A", 16, 2 },
|
||||
/* Below are B Variants (Table 3-7 from rev I of datasheet) */
|
||||
{ 0x20, "SAMD21J16B", 64, 8 },
|
||||
{ 0x21, "SAMD21J15B", 32, 4 },
|
||||
{ 0x23, "SAMD21G16B", 64, 8 },
|
||||
{ 0x24, "SAMD21G15B", 32, 4 },
|
||||
{ 0x26, "SAMD21E16B", 64, 8 },
|
||||
{ 0x27, "SAMD21E15B", 32, 4 },
|
||||
};
|
||||
|
||||
/* Known SAMR21 parts. */
|
||||
static const struct samd_part samr21_parts[] = {
|
||||
/* SAMR21 parts have integrated SAMD21 with a radio */
|
||||
{ 0x19, "SAMR21G18A", 256, 32 },
|
||||
{ 0x1A, "SAMR21G17A", 128, 32 },
|
||||
{ 0x1B, "SAMR21G16A", 64, 32 },
|
||||
{ 0x1C, "SAMR21E18A", 256, 32 },
|
||||
{ 0x1D, "SAMR21E17A", 128, 32 },
|
||||
{ 0x1E, "SAMR21E16A", 64, 32 },
|
||||
|
||||
/* SAMD21 B Variants (Table 3-7 from rev I of datasheet) */
|
||||
{ 0x20, "SAMD21J16B", 64, 8 },
|
||||
{ 0x21, "SAMD21J15B", 32, 4 },
|
||||
{ 0x23, "SAMD21G16B", 64, 8 },
|
||||
{ 0x24, "SAMD21G15B", 32, 4 },
|
||||
{ 0x26, "SAMD21E16B", 64, 8 },
|
||||
{ 0x27, "SAMD21E15B", 32, 4 },
|
||||
};
|
||||
|
||||
/* Known SAML21 parts. */
|
||||
@@ -200,6 +203,10 @@ static const struct samd_part saml21_parts[] = {
|
||||
{ 0x1A, "SAML21E17B", 128, 16 },
|
||||
{ 0x1B, "SAML21E16B", 64, 8 },
|
||||
{ 0x1C, "SAML21E15B", 32, 4 },
|
||||
|
||||
/* SAMR30 parts have integrated SAML21 with a radio */
|
||||
{ 0x1E, "SAMR30G18A", 256, 32 },
|
||||
{ 0x1F, "SAMR30E18A", 256, 32 },
|
||||
};
|
||||
|
||||
/* Known SAML22 parts. */
|
||||
@@ -256,30 +263,38 @@ struct samd_family {
|
||||
uint8_t series;
|
||||
const struct samd_part *parts;
|
||||
size_t num_parts;
|
||||
uint64_t nvm_userrow_res_mask; /* protect bits which are reserved, 0 -> protect */
|
||||
};
|
||||
|
||||
/* Known SAMD families */
|
||||
static const struct samd_family samd_families[] = {
|
||||
{ SAMD_PROCESSOR_M0, SAMD_FAMILY_D, SAMD_SERIES_20,
|
||||
samd20_parts, ARRAY_SIZE(samd20_parts) },
|
||||
samd20_parts, ARRAY_SIZE(samd20_parts),
|
||||
(uint64_t)0xFFFF01FFFE01FF77 },
|
||||
{ SAMD_PROCESSOR_M0, SAMD_FAMILY_D, SAMD_SERIES_21,
|
||||
samd21_parts, ARRAY_SIZE(samd21_parts) },
|
||||
{ SAMD_PROCESSOR_M0, SAMD_FAMILY_D, SAMD_SERIES_21,
|
||||
samr21_parts, ARRAY_SIZE(samr21_parts) },
|
||||
samd21_parts, ARRAY_SIZE(samd21_parts),
|
||||
(uint64_t)0xFFFF01FFFE01FF77 },
|
||||
{ SAMD_PROCESSOR_M0, SAMD_FAMILY_D, SAMD_SERIES_09,
|
||||
samd09_parts, ARRAY_SIZE(samd09_parts) },
|
||||
samd09_parts, ARRAY_SIZE(samd09_parts),
|
||||
(uint64_t)0xFFFF01FFFE01FF77 },
|
||||
{ SAMD_PROCESSOR_M0, SAMD_FAMILY_D, SAMD_SERIES_10,
|
||||
samd10_parts, ARRAY_SIZE(samd10_parts) },
|
||||
samd10_parts, ARRAY_SIZE(samd10_parts),
|
||||
(uint64_t)0xFFFF01FFFE01FF77 },
|
||||
{ SAMD_PROCESSOR_M0, SAMD_FAMILY_D, SAMD_SERIES_11,
|
||||
samd11_parts, ARRAY_SIZE(samd11_parts) },
|
||||
samd11_parts, ARRAY_SIZE(samd11_parts),
|
||||
(uint64_t)0xFFFF01FFFE01FF77 },
|
||||
{ SAMD_PROCESSOR_M0, SAMD_FAMILY_L, SAMD_SERIES_21,
|
||||
saml21_parts, ARRAY_SIZE(saml21_parts) },
|
||||
saml21_parts, ARRAY_SIZE(saml21_parts),
|
||||
(uint64_t)0xFFFF03FFFC01FF77 },
|
||||
{ SAMD_PROCESSOR_M0, SAMD_FAMILY_L, SAMD_SERIES_22,
|
||||
saml22_parts, ARRAY_SIZE(saml22_parts) },
|
||||
saml22_parts, ARRAY_SIZE(saml22_parts),
|
||||
(uint64_t)0xFFFF03FFFC01FF77 },
|
||||
{ SAMD_PROCESSOR_M0, SAMD_FAMILY_C, SAMD_SERIES_20,
|
||||
samc20_parts, ARRAY_SIZE(samc20_parts) },
|
||||
samc20_parts, ARRAY_SIZE(samc20_parts),
|
||||
(uint64_t)0xFFFF03FFFC01FF77 },
|
||||
{ SAMD_PROCESSOR_M0, SAMD_FAMILY_C, SAMD_SERIES_21,
|
||||
samc21_parts, ARRAY_SIZE(samc21_parts) },
|
||||
samc21_parts, ARRAY_SIZE(samc21_parts),
|
||||
(uint64_t)0xFFFF03FFFC01FF77 },
|
||||
};
|
||||
|
||||
struct samd_info {
|
||||
@@ -290,29 +305,45 @@ struct samd_info {
|
||||
|
||||
bool probed;
|
||||
struct target *target;
|
||||
struct samd_info *next;
|
||||
};
|
||||
|
||||
static struct samd_info *samd_chips;
|
||||
|
||||
|
||||
|
||||
static const struct samd_part *samd_find_part(uint32_t id)
|
||||
/**
|
||||
* Gives the family structure to specific device id.
|
||||
* @param id The id of the device.
|
||||
* @return On failure NULL, otherwise a pointer to the structure.
|
||||
*/
|
||||
static const struct samd_family *samd_find_family(uint32_t id)
|
||||
{
|
||||
uint8_t processor = SAMD_GET_PROCESSOR(id);
|
||||
uint8_t family = SAMD_GET_FAMILY(id);
|
||||
uint8_t series = SAMD_GET_SERIES(id);
|
||||
uint8_t devsel = SAMD_GET_DEVSEL(id);
|
||||
|
||||
for (unsigned i = 0; i < ARRAY_SIZE(samd_families); i++) {
|
||||
if (samd_families[i].processor == processor &&
|
||||
samd_families[i].series == series &&
|
||||
samd_families[i].family == family) {
|
||||
for (unsigned j = 0; j < samd_families[i].num_parts; j++) {
|
||||
if (samd_families[i].parts[j].id == devsel)
|
||||
return &samd_families[i].parts[j];
|
||||
}
|
||||
}
|
||||
samd_families[i].family == family)
|
||||
return &samd_families[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives the part structure to specific device id.
|
||||
* @param id The id of the device.
|
||||
* @return On failure NULL, otherwise a pointer to the structure.
|
||||
*/
|
||||
static const struct samd_part *samd_find_part(uint32_t id)
|
||||
{
|
||||
uint8_t devsel = SAMD_GET_DEVSEL(id);
|
||||
const struct samd_family *family = samd_find_family(id);
|
||||
if (family == NULL)
|
||||
return NULL;
|
||||
|
||||
for (unsigned i = 0; i < family->num_parts; i++) {
|
||||
if (family->parts[i].id == devsel)
|
||||
return &family->parts[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
@@ -483,6 +514,12 @@ static int samd_issue_nvmctrl_command(struct target *target, uint16_t cmd)
|
||||
return samd_check_error(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Erases a flash-row at the given address.
|
||||
* @param target Pointer to the target structure.
|
||||
* @param address The address of the row.
|
||||
* @return On success ERROR_OK, on failure an errorcode.
|
||||
*/
|
||||
static int samd_erase_row(struct target *target, uint32_t address)
|
||||
{
|
||||
int res;
|
||||
@@ -504,49 +541,62 @@ static int samd_erase_row(struct target *target, uint32_t address)
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static bool is_user_row_reserved_bit(uint8_t bit)
|
||||
/**
|
||||
* Returns the bitmask of reserved bits in register.
|
||||
* @param target Pointer to the target structure.
|
||||
* @param mask Bitmask, 0 -> value stays untouched.
|
||||
* @return On success ERROR_OK, on failure an errorcode.
|
||||
*/
|
||||
static int samd_get_reservedmask(struct target *target, uint64_t *mask)
|
||||
{
|
||||
/* See Table 9-3 in the SAMD20 datasheet for more information. */
|
||||
switch (bit) {
|
||||
/* Reserved bits */
|
||||
case 3:
|
||||
case 7:
|
||||
/* Voltage regulator internal configuration with default value of 0x70,
|
||||
* may not be changed. */
|
||||
case 17 ... 24:
|
||||
/* 41 is voltage regulator internal configuration and must not be
|
||||
* changed. 42 through 47 are reserved. */
|
||||
case 41 ... 47:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
int res;
|
||||
/* Get the devicetype */
|
||||
uint32_t id;
|
||||
res = target_read_u32(target, SAMD_DSU + SAMD_DSU_DID, &id);
|
||||
if (res != ERROR_OK) {
|
||||
LOG_ERROR("Couldn't read Device ID register");
|
||||
return res;
|
||||
}
|
||||
|
||||
return false;
|
||||
const struct samd_family *family;
|
||||
family = samd_find_family(id);
|
||||
if (family == NULL) {
|
||||
LOG_ERROR("Couldn't determine device family");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
*mask = family->nvm_userrow_res_mask;
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
/* Modify the contents of the User Row in Flash. These are described in Table
|
||||
* 9-3 of the SAMD20 datasheet. The User Row itself has a size of one page
|
||||
* and contains a combination of "fuses" and calibration data in bits 24:17.
|
||||
* We therefore try not to erase the row's contents unless we absolutely have
|
||||
* to and we don't permit modifying reserved bits. */
|
||||
static int samd_modify_user_row(struct target *target, uint32_t value,
|
||||
uint8_t startb, uint8_t endb)
|
||||
static int read_userrow(struct target *target, uint64_t *userrow)
|
||||
{
|
||||
int res;
|
||||
uint8_t buffer[8];
|
||||
|
||||
res = target_read_memory(target, SAMD_USER_ROW, 4, 2, buffer);
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
|
||||
*userrow = target_buffer_get_u64(target, buffer);
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify the contents of the User Row in Flash. The User Row itself
|
||||
* has a size of one page and contains a combination of "fuses" and
|
||||
* calibration data. Bits which have a value of zero in the mask will
|
||||
* not be changed. Up to now devices only use the first 64 bits.
|
||||
* @param target Pointer to the target structure.
|
||||
* @param value_input The value to write.
|
||||
* @param value_mask Bitmask, 0 -> value stays untouched.
|
||||
* @return On success ERROR_OK, on failure an errorcode.
|
||||
*/
|
||||
static int samd_modify_user_row_masked(struct target *target,
|
||||
uint64_t value_input, uint64_t value_mask)
|
||||
{
|
||||
int res;
|
||||
uint32_t nvm_ctrlb;
|
||||
bool manual_wp = true;
|
||||
|
||||
if (is_user_row_reserved_bit(startb) || is_user_row_reserved_bit(endb)) {
|
||||
LOG_ERROR("Can't modify bits in the requested range");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
/* Check if we need to do manual page write commands */
|
||||
res = target_read_u32(target, SAMD_NVMCTRL + SAMD_NVMCTRL_CTRLB, &nvm_ctrlb);
|
||||
if (res == ERROR_OK)
|
||||
manual_wp = (nvm_ctrlb & SAMD_NVM_CTRLB_MANW) != 0;
|
||||
|
||||
/* Retrieve the MCU's page size, in bytes. This is also the size of the
|
||||
* entire User Row. */
|
||||
uint32_t page_size;
|
||||
@@ -556,44 +606,49 @@ static int samd_modify_user_row(struct target *target, uint32_t value,
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Make sure the size is sane before we allocate. */
|
||||
assert(page_size > 0 && page_size <= SAMD_PAGE_SIZE_MAX);
|
||||
|
||||
/* Make sure we're within the single page that comprises the User Row. */
|
||||
if (startb >= (page_size * 8) || endb >= (page_size * 8)) {
|
||||
LOG_ERROR("Can't modify bits outside the User Row page range");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
uint8_t *buf = malloc(page_size);
|
||||
if (!buf)
|
||||
return ERROR_FAIL;
|
||||
/* Make sure the size is sane. */
|
||||
assert(page_size <= SAMD_PAGE_SIZE_MAX &&
|
||||
page_size >= sizeof(value_input));
|
||||
|
||||
uint8_t buf[SAMD_PAGE_SIZE_MAX];
|
||||
/* Read the user row (comprising one page) by words. */
|
||||
res = target_read_memory(target, SAMD_USER_ROW, 4, page_size / 4, buf);
|
||||
if (res != ERROR_OK)
|
||||
goto out_user_row;
|
||||
return res;
|
||||
|
||||
uint64_t value_device;
|
||||
res = read_userrow(target, &value_device);
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
uint64_t value_new = (value_input & value_mask) | (value_device & ~value_mask);
|
||||
|
||||
/* We will need to erase before writing if the new value needs a '1' in any
|
||||
* position for which the current value had a '0'. Otherwise we can avoid
|
||||
* erasing. */
|
||||
uint32_t cur = buf_get_u32(buf, startb, endb - startb + 1);
|
||||
if ((~cur) & value) {
|
||||
if ((~value_device) & value_new) {
|
||||
res = samd_erase_row(target, SAMD_USER_ROW);
|
||||
if (res != ERROR_OK) {
|
||||
LOG_ERROR("Couldn't erase user row");
|
||||
goto out_user_row;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
/* Modify */
|
||||
buf_set_u32(buf, startb, endb - startb + 1, value);
|
||||
target_buffer_set_u64(target, buf, value_new);
|
||||
|
||||
/* Write the page buffer back out to the target. */
|
||||
res = target_write_memory(target, SAMD_USER_ROW, 4, page_size / 4, buf);
|
||||
if (res != ERROR_OK)
|
||||
goto out_user_row;
|
||||
return res;
|
||||
|
||||
/* Check if we need to do manual page write commands */
|
||||
res = target_read_u32(target, SAMD_NVMCTRL + SAMD_NVMCTRL_CTRLB, &nvm_ctrlb);
|
||||
if (res == ERROR_OK)
|
||||
manual_wp = (nvm_ctrlb & SAMD_NVM_CTRLB_MANW) != 0;
|
||||
else {
|
||||
LOG_ERROR("Read of NVM register CTRKB failed.");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
if (manual_wp) {
|
||||
/* Trigger flash write */
|
||||
res = samd_issue_nvmctrl_command(target, SAMD_NVM_CMD_WAP);
|
||||
@@ -601,12 +656,28 @@ static int samd_modify_user_row(struct target *target, uint32_t value,
|
||||
res = samd_check_error(target);
|
||||
}
|
||||
|
||||
out_user_row:
|
||||
free(buf);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies the user row register to the given value.
|
||||
* @param target Pointer to the target structure.
|
||||
* @param value The value to write.
|
||||
* @param startb The bit-offset by which the given value is shifted.
|
||||
* @param endb The bit-offset of the last bit in value to write.
|
||||
* @return On success ERROR_OK, on failure an errorcode.
|
||||
*/
|
||||
static int samd_modify_user_row(struct target *target, uint64_t value,
|
||||
uint8_t startb, uint8_t endb)
|
||||
{
|
||||
uint64_t mask = 0;
|
||||
int i;
|
||||
for (i = startb ; i <= endb ; i++)
|
||||
mask |= ((uint64_t)1) << i;
|
||||
|
||||
return samd_modify_user_row_masked(target, value << startb, mask);
|
||||
}
|
||||
|
||||
static int samd_protect(struct flash_bank *bank, int set, int first_prot_bl, int last_prot_bl)
|
||||
{
|
||||
int res = ERROR_OK;
|
||||
@@ -643,7 +714,8 @@ static int samd_protect(struct flash_bank *bank, int set, int first_prot_bl, int
|
||||
* corresponding to Sector 15. A '1' means unlocked and a '0' means
|
||||
* locked. See Table 9-3 in the SAMD20 datasheet for more details. */
|
||||
|
||||
res = samd_modify_user_row(bank->target, set ? 0x0000 : 0xFFFF,
|
||||
res = samd_modify_user_row(bank->target,
|
||||
set ? (uint64_t)0 : (uint64_t)UINT64_MAX,
|
||||
48 + first_prot_bl, 48 + last_prot_bl);
|
||||
if (res != ERROR_OK)
|
||||
LOG_WARNING("SAMD: protect settings were not made persistent!");
|
||||
@@ -803,30 +875,6 @@ free_pb:
|
||||
|
||||
FLASH_BANK_COMMAND_HANDLER(samd_flash_bank_command)
|
||||
{
|
||||
struct samd_info *chip = samd_chips;
|
||||
|
||||
while (chip) {
|
||||
if (chip->target == bank->target)
|
||||
break;
|
||||
chip = chip->next;
|
||||
}
|
||||
|
||||
if (!chip) {
|
||||
/* Create a new chip */
|
||||
chip = calloc(1, sizeof(*chip));
|
||||
if (!chip)
|
||||
return ERROR_FAIL;
|
||||
|
||||
chip->target = bank->target;
|
||||
chip->probed = false;
|
||||
|
||||
bank->driver_priv = chip;
|
||||
|
||||
/* Insert it into the chips list (at head) */
|
||||
chip->next = samd_chips;
|
||||
samd_chips = chip;
|
||||
}
|
||||
|
||||
if (bank->base != SAMD_FLASH) {
|
||||
LOG_ERROR("Address 0x%08" PRIx32 " invalid bank address (try 0x%08" PRIx32
|
||||
"[at91samd series] )",
|
||||
@@ -834,6 +882,18 @@ FLASH_BANK_COMMAND_HANDLER(samd_flash_bank_command)
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
struct samd_info *chip;
|
||||
chip = calloc(1, sizeof(*chip));
|
||||
if (!chip) {
|
||||
LOG_ERROR("No memory for flash bank chip info");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
chip->target = bank->target;
|
||||
chip->probed = false;
|
||||
|
||||
bank->driver_priv = chip;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
@@ -944,6 +1004,83 @@ COMMAND_HANDLER(samd_handle_eeprom_command)
|
||||
return res;
|
||||
}
|
||||
|
||||
static COMMAND_HELPER(get_u64_from_hexarg, unsigned int num, uint64_t *value)
|
||||
{
|
||||
if (num >= CMD_ARGC) {
|
||||
command_print(CMD_CTX, "Too few Arguments.");
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
if (strlen(CMD_ARGV[num]) >= 3 &&
|
||||
CMD_ARGV[num][0] == '0' &&
|
||||
CMD_ARGV[num][1] == 'x') {
|
||||
char *check = NULL;
|
||||
*value = strtoull(&(CMD_ARGV[num][2]), &check, 16);
|
||||
if ((value == 0 && errno == ERANGE) ||
|
||||
check == NULL || *check != 0) {
|
||||
command_print(CMD_CTX, "Invalid 64-bit hex value in argument %d.",
|
||||
num + 1);
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
} else {
|
||||
command_print(CMD_CTX, "Argument %d needs to be a hex value.", num + 1);
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
COMMAND_HANDLER(samd_handle_nvmuserrow_command)
|
||||
{
|
||||
int res = ERROR_OK;
|
||||
struct target *target = get_current_target(CMD_CTX);
|
||||
|
||||
if (target) {
|
||||
if (CMD_ARGC > 2) {
|
||||
command_print(CMD_CTX, "Too much Arguments given.");
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
if (CMD_ARGC > 0) {
|
||||
if (target->state != TARGET_HALTED) {
|
||||
LOG_ERROR("Target not halted.");
|
||||
return ERROR_TARGET_NOT_HALTED;
|
||||
}
|
||||
|
||||
uint64_t mask;
|
||||
res = samd_get_reservedmask(target, &mask);
|
||||
if (res != ERROR_OK) {
|
||||
LOG_ERROR("Couldn't determine the mask for reserved bits.");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
mask &= NVMUSERROW_LOCKBIT_MASK;
|
||||
|
||||
uint64_t value;
|
||||
res = CALL_COMMAND_HANDLER(get_u64_from_hexarg, 0, &value);
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
if (CMD_ARGC == 2) {
|
||||
uint64_t mask_temp;
|
||||
res = CALL_COMMAND_HANDLER(get_u64_from_hexarg, 1, &mask_temp);
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
mask &= mask_temp;
|
||||
}
|
||||
res = samd_modify_user_row_masked(target, value, mask);
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
}
|
||||
|
||||
/* read register */
|
||||
uint64_t value;
|
||||
res = read_userrow(target, &value);
|
||||
if (res == ERROR_OK)
|
||||
command_print(CMD_CTX, "NVMUSERROW: 0x%016"PRIX64, value);
|
||||
else
|
||||
LOG_ERROR("NVMUSERROW could not be read.");
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
COMMAND_HANDLER(samd_handle_bootloader_command)
|
||||
{
|
||||
int res = ERROR_OK;
|
||||
@@ -1049,29 +1186,29 @@ static const struct command_registration at91samd_exec_command_handlers[] = {
|
||||
.name = "dsu_reset_deassert",
|
||||
.handler = samd_handle_reset_deassert,
|
||||
.mode = COMMAND_EXEC,
|
||||
.help = "deasert internal reset held by DSU"
|
||||
.help = "Deasert internal reset held by DSU."
|
||||
},
|
||||
{
|
||||
.name = "info",
|
||||
.handler = samd_handle_info_command,
|
||||
.mode = COMMAND_EXEC,
|
||||
.help = "Print information about the current at91samd chip"
|
||||
.help = "Print information about the current at91samd chip "
|
||||
"and its flash configuration.",
|
||||
},
|
||||
{
|
||||
.name = "chip-erase",
|
||||
.handler = samd_handle_chip_erase_command,
|
||||
.mode = COMMAND_EXEC,
|
||||
.help = "Erase the entire Flash by using the Chip"
|
||||
.help = "Erase the entire Flash by using the Chip-"
|
||||
"Erase feature in the Device Service Unit (DSU).",
|
||||
},
|
||||
{
|
||||
.name = "set-security",
|
||||
.handler = samd_handle_set_security_command,
|
||||
.mode = COMMAND_EXEC,
|
||||
.help = "Secure the chip's Flash by setting the Security Bit."
|
||||
"This makes it impossible to read the Flash contents."
|
||||
"The only way to undo this is to issue the chip-erase"
|
||||
.help = "Secure the chip's Flash by setting the Security Bit. "
|
||||
"This makes it impossible to read the Flash contents. "
|
||||
"The only way to undo this is to issue the chip-erase "
|
||||
"command.",
|
||||
},
|
||||
{
|
||||
@@ -1079,9 +1216,9 @@ static const struct command_registration at91samd_exec_command_handlers[] = {
|
||||
.usage = "[size_in_bytes]",
|
||||
.handler = samd_handle_eeprom_command,
|
||||
.mode = COMMAND_EXEC,
|
||||
.help = "Show or set the EEPROM size setting, stored in the User Row."
|
||||
"Please see Table 20-3 of the SAMD20 datasheet for allowed values."
|
||||
"Changes are stored immediately but take affect after the MCU is"
|
||||
.help = "Show or set the EEPROM size setting, stored in the User Row. "
|
||||
"Please see Table 20-3 of the SAMD20 datasheet for allowed values. "
|
||||
"Changes are stored immediately but take affect after the MCU is "
|
||||
"reset.",
|
||||
},
|
||||
{
|
||||
@@ -1089,11 +1226,22 @@ static const struct command_registration at91samd_exec_command_handlers[] = {
|
||||
.usage = "[size_in_bytes]",
|
||||
.handler = samd_handle_bootloader_command,
|
||||
.mode = COMMAND_EXEC,
|
||||
.help = "Show or set the bootloader size, stored in the User Row."
|
||||
"Please see Table 20-2 of the SAMD20 datasheet for allowed values."
|
||||
"Changes are stored immediately but take affect after the MCU is"
|
||||
.help = "Show or set the bootloader size, stored in the User Row. "
|
||||
"Please see Table 20-2 of the SAMD20 datasheet for allowed values. "
|
||||
"Changes are stored immediately but take affect after the MCU is "
|
||||
"reset.",
|
||||
},
|
||||
{
|
||||
.name = "nvmuserrow",
|
||||
.usage = "[value] [mask]",
|
||||
.handler = samd_handle_nvmuserrow_command,
|
||||
.mode = COMMAND_EXEC,
|
||||
.help = "Show or set the nvmuserrow register. It is 64 bit wide "
|
||||
"and located at address 0x804000. Use the optional mask argument "
|
||||
"to prevent changes at positions where the bitvalue is zero. "
|
||||
"For security reasons the lock- and reserved-bits are masked out "
|
||||
"in background and therefore cannot be changed.",
|
||||
},
|
||||
COMMAND_REGISTRATION_DONE
|
||||
};
|
||||
|
||||
@@ -1120,4 +1268,5 @@ struct flash_driver at91samd_flash = {
|
||||
.auto_probe = samd_probe,
|
||||
.erase_check = default_flash_blank_check,
|
||||
.protect_check = samd_protect_check,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
|
||||
@@ -898,4 +898,5 @@ struct flash_driver ath79_flash = {
|
||||
.erase_check = ath79_flash_blank_check,
|
||||
.protect_check = ath79_protect_check,
|
||||
.info = get_ath79_info,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
|
||||
@@ -739,4 +739,5 @@ struct flash_driver atsamv_flash = {
|
||||
.erase_check = default_flash_blank_check,
|
||||
.protect_check = samv_protect_check,
|
||||
.info = samv_get_info,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
|
||||
@@ -487,4 +487,5 @@ struct flash_driver avr_flash = {
|
||||
.erase_check = default_flash_blank_check,
|
||||
.protect_check = avrf_protect_check,
|
||||
.info = avrf_info,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,554 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2017 by Michele Sardo *
|
||||
* msmttchr@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, see <http://www.gnu.org/licenses/>. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <target/algorithm.h>
|
||||
#include <target/armv7m.h>
|
||||
#include <target/cortex_m.h>
|
||||
#include "imp.h"
|
||||
|
||||
#define FLASH_SIZE_REG (0x40100014)
|
||||
#define DIE_ID_REG (0x4090001C)
|
||||
#define JTAG_IDCODE_REG (0x40900028)
|
||||
#define BLUENRG2_IDCODE (0x0200A041)
|
||||
#define FLASH_BASE (0x10040000)
|
||||
#define FLASH_PAGE_SIZE (2048)
|
||||
#define FLASH_REG_COMMAND (0x40100000)
|
||||
#define FLASH_REG_IRQRAW (0x40100010)
|
||||
#define FLASH_REG_ADDRESS (0x40100018)
|
||||
#define FLASH_REG_DATA (0x40100040)
|
||||
#define FLASH_CMD_ERASE_PAGE 0x11
|
||||
#define FLASH_CMD_MASSERASE 0x22
|
||||
#define FLASH_CMD_WRITE 0x33
|
||||
#define FLASH_CMD_BURSTWRITE 0xCC
|
||||
#define FLASH_INT_CMDDONE 0x01
|
||||
#define FLASH_WORD_LEN 4
|
||||
|
||||
struct bluenrgx_flash_bank {
|
||||
int probed;
|
||||
uint32_t idcode;
|
||||
uint32_t die_id;
|
||||
};
|
||||
|
||||
static int bluenrgx_protect_check(struct flash_bank *bank)
|
||||
{
|
||||
/* Nothing to do. Protection is only handled in SW. */
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
/* flash_bank bluenrg-x 0 0 0 0 <target#> */
|
||||
FLASH_BANK_COMMAND_HANDLER(bluenrgx_flash_bank_command)
|
||||
{
|
||||
struct bluenrgx_flash_bank *bluenrgx_info;
|
||||
/* Create the bank structure */
|
||||
bluenrgx_info = calloc(1, sizeof(*bluenrgx_info));
|
||||
|
||||
/* Check allocation */
|
||||
if (bluenrgx_info == NULL) {
|
||||
LOG_ERROR("failed to allocate bank structure");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
bank->driver_priv = bluenrgx_info;
|
||||
|
||||
bluenrgx_info->probed = 0;
|
||||
|
||||
if (CMD_ARGC < 6)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int bluenrgx_erase(struct flash_bank *bank, int first, int last)
|
||||
{
|
||||
int retval = ERROR_OK;
|
||||
struct bluenrgx_flash_bank *bluenrgx_info = bank->driver_priv;
|
||||
int num_sectors = (last - first + 1);
|
||||
int mass_erase = (num_sectors == bank->num_sectors);
|
||||
struct target *target = bank->target;
|
||||
uint32_t address, command;
|
||||
|
||||
/* check preconditions */
|
||||
if (bluenrgx_info->probed == 0)
|
||||
return ERROR_FLASH_BANK_NOT_PROBED;
|
||||
|
||||
if (bank->target->state != TARGET_HALTED) {
|
||||
LOG_ERROR("Target not halted");
|
||||
return ERROR_TARGET_NOT_HALTED;
|
||||
}
|
||||
/* Disable blue module */
|
||||
if (target_write_u32(target, 0x200000c0, 0) != ERROR_OK) {
|
||||
LOG_ERROR("Blue disable failed");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (mass_erase) {
|
||||
command = FLASH_CMD_MASSERASE;
|
||||
address = bank->base;
|
||||
if (target_write_u32(target, FLASH_REG_IRQRAW, 0x3f) != ERROR_OK) {
|
||||
LOG_ERROR("Register write failed");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (target_write_u32(target, FLASH_REG_ADDRESS, address >> 2) != ERROR_OK) {
|
||||
LOG_ERROR("Register write failed");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (target_write_u32(target, FLASH_REG_COMMAND, command) != ERROR_OK) {
|
||||
LOG_ERROR("Register write failed");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 100; i++) {
|
||||
uint32_t value;
|
||||
if (target_read_u32(target, FLASH_REG_IRQRAW, &value)) {
|
||||
LOG_ERROR("Register write failed");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
if (value & FLASH_INT_CMDDONE)
|
||||
break;
|
||||
if (i == 99) {
|
||||
LOG_ERROR("Mass erase command failed (timeout)");
|
||||
retval = ERROR_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
command = FLASH_CMD_ERASE_PAGE;
|
||||
for (int i = first; i <= last; i++) {
|
||||
address = bank->base+i*FLASH_PAGE_SIZE;
|
||||
|
||||
if (target_write_u32(target, FLASH_REG_IRQRAW, 0x3f) != ERROR_OK) {
|
||||
LOG_ERROR("Register write failed");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (target_write_u32(target, FLASH_REG_ADDRESS, address >> 2) != ERROR_OK) {
|
||||
LOG_ERROR("Register write failed");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (target_write_u32(target, FLASH_REG_COMMAND, command) != ERROR_OK) {
|
||||
LOG_ERROR("Failed");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
for (int j = 0; j < 100; j++) {
|
||||
uint32_t value;
|
||||
if (target_read_u32(target, FLASH_REG_IRQRAW, &value)) {
|
||||
LOG_ERROR("Register write failed");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
if (value & FLASH_INT_CMDDONE)
|
||||
break;
|
||||
if (j == 99) {
|
||||
LOG_ERROR("Erase command failed (timeout)");
|
||||
retval = ERROR_FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
|
||||
}
|
||||
|
||||
static int bluenrgx_protect(struct flash_bank *bank, int set, int first, int last)
|
||||
{
|
||||
/* Protection is only handled in software: no hardware write protection
|
||||
available in BlueNRG-x devices */
|
||||
int sector;
|
||||
|
||||
for (sector = first; sector <= last; sector++)
|
||||
bank->sectors[sector].is_protected = set;
|
||||
return ERROR_OK;
|
||||
}
|
||||
static int bluenrgx_write_word(struct target *target, uint32_t address_base, uint8_t *values, uint32_t count)
|
||||
{
|
||||
int retval = ERROR_OK;
|
||||
|
||||
retval = target_write_u32(target, FLASH_REG_IRQRAW, 0x3f);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("Register write failed, error code: %d", retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
uint32_t address = address_base + i * FLASH_WORD_LEN;
|
||||
|
||||
retval = target_write_u32(target, FLASH_REG_ADDRESS, address >> 2);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("Register write failed, error code: %d", retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = target_write_buffer(target, FLASH_REG_DATA, FLASH_WORD_LEN, values + i * FLASH_WORD_LEN);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("Register write failed, error code: %d", retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = target_write_u32(target, FLASH_REG_COMMAND, FLASH_CMD_WRITE);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("Register write failed, error code: %d", retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
for (int j = 0; j < 100; j++) {
|
||||
uint32_t reg_value;
|
||||
retval = target_read_u32(target, FLASH_REG_IRQRAW, ®_value);
|
||||
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("Register read failed, error code: %d", retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (reg_value & FLASH_INT_CMDDONE)
|
||||
break;
|
||||
|
||||
if (j == 99) {
|
||||
LOG_ERROR("Write command failed (timeout)");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int bluenrgx_write_bytes(struct target *target, uint32_t address_base, uint8_t *buffer, uint32_t count)
|
||||
{
|
||||
int retval = ERROR_OK;
|
||||
uint8_t *new_buffer = NULL;
|
||||
uint32_t pre_bytes = 0, post_bytes = 0, pre_word, post_word, pre_address, post_address;
|
||||
|
||||
if (count == 0) {
|
||||
/* Just return if there are no bytes to write */
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (address_base & 3) {
|
||||
pre_bytes = address_base & 3;
|
||||
pre_address = address_base - pre_bytes;
|
||||
}
|
||||
|
||||
if ((count + pre_bytes) & 3) {
|
||||
post_bytes = ((count + pre_bytes + 3) & ~3) - (count + pre_bytes);
|
||||
post_address = (address_base + count) & ~3;
|
||||
}
|
||||
|
||||
if (pre_bytes || post_bytes) {
|
||||
uint32_t old_count = count;
|
||||
|
||||
count = old_count + pre_bytes + post_bytes;
|
||||
|
||||
new_buffer = malloc(count);
|
||||
|
||||
if (new_buffer == NULL) {
|
||||
LOG_ERROR("odd number of bytes to write and no memory "
|
||||
"for padding buffer");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
LOG_INFO("Requested number of bytes to write and/or address not word aligned (%" PRIu32 "), extending to %"
|
||||
PRIu32 " ", old_count, count);
|
||||
|
||||
if (pre_bytes) {
|
||||
if (target_read_u32(target, pre_address, &pre_word)) {
|
||||
LOG_ERROR("Memory read failed");
|
||||
free(new_buffer);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (post_bytes) {
|
||||
if (target_read_u32(target, post_address, &post_word)) {
|
||||
LOG_ERROR("Memory read failed");
|
||||
free(new_buffer);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
memcpy(new_buffer, &pre_word, pre_bytes);
|
||||
memcpy((new_buffer+((pre_bytes+old_count) & ~3)), &post_word, 4);
|
||||
memcpy(new_buffer+pre_bytes, buffer, old_count);
|
||||
buffer = new_buffer;
|
||||
}
|
||||
|
||||
retval = bluenrgx_write_word(target, address_base - pre_bytes, buffer, count/4);
|
||||
|
||||
if (new_buffer)
|
||||
free(new_buffer);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int bluenrgx_write(struct flash_bank *bank, const uint8_t *buffer,
|
||||
uint32_t offset, uint32_t count)
|
||||
{
|
||||
struct target *target = bank->target;
|
||||
uint32_t buffer_size = 16384 + 8;
|
||||
struct working_area *write_algorithm;
|
||||
struct working_area *write_algorithm_sp;
|
||||
struct working_area *source;
|
||||
uint32_t address = bank->base + offset;
|
||||
struct reg_param reg_params[5];
|
||||
struct armv7m_algorithm armv7m_info;
|
||||
int retval = ERROR_OK;
|
||||
uint32_t pre_size = 0, fast_size = 0, post_size = 0;
|
||||
uint32_t pre_offset = 0, fast_offset = 0, post_offset = 0;
|
||||
|
||||
/* See contrib/loaders/flash/bluenrg-x/bluenrg-x_write.c for source and
|
||||
* hints how to generate the data!
|
||||
*/
|
||||
static const uint8_t bluenrgx_flash_write_code[] = {
|
||||
#include "../../../contrib/loaders/flash/bluenrg-x/bluenrg-x_write.inc"
|
||||
};
|
||||
|
||||
if ((offset + count) > bank->size) {
|
||||
LOG_ERROR("Requested write past beyond of flash size: (offset+count) = %d, size=%d",
|
||||
(offset + count),
|
||||
bank->size);
|
||||
return ERROR_FLASH_DST_OUT_OF_BANK;
|
||||
}
|
||||
|
||||
if (bank->target->state != TARGET_HALTED) {
|
||||
LOG_ERROR("Target not halted");
|
||||
return ERROR_TARGET_NOT_HALTED;
|
||||
}
|
||||
|
||||
/* We are good here and we need to compute pre_size, fast_size, post_size */
|
||||
pre_size = MIN(count, ((offset+0xF) & ~0xF) - offset);
|
||||
pre_offset = offset;
|
||||
fast_size = 16*((count - pre_size) / 16);
|
||||
fast_offset = offset + pre_size;
|
||||
post_size = (count-pre_size-fast_size) % 16;
|
||||
post_offset = fast_offset + fast_size;
|
||||
|
||||
LOG_DEBUG("pre_size = %08x, pre_offset=%08x", pre_size, pre_offset);
|
||||
LOG_DEBUG("fast_size = %08x, fast_offset=%08x", fast_size, fast_offset);
|
||||
LOG_DEBUG("post_size = %08x, post_offset=%08x", post_size, post_offset);
|
||||
|
||||
/* Program initial chunk not 16 bytes aligned */
|
||||
retval = bluenrgx_write_bytes(target, bank->base+pre_offset, (uint8_t *) buffer, pre_size);
|
||||
if (retval) {
|
||||
LOG_ERROR("bluenrgx_write_bytes failed %d", retval);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
/* Program chunk 16 bytes aligned in fast mode */
|
||||
if (fast_size) {
|
||||
|
||||
if (target_alloc_working_area(target, sizeof(bluenrgx_flash_write_code),
|
||||
&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, write_algorithm->address,
|
||||
sizeof(bluenrgx_flash_write_code),
|
||||
bluenrgx_flash_write_code);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
/* memory buffer */
|
||||
if (target_alloc_working_area(target, buffer_size, &source)) {
|
||||
LOG_WARNING("no large enough working area available");
|
||||
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
/* Stack pointer area */
|
||||
if (target_alloc_working_area(target, 64,
|
||||
&write_algorithm_sp) != ERROR_OK) {
|
||||
LOG_DEBUG("no working area for write code stack pointer");
|
||||
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
|
||||
armv7m_info.core_mode = ARM_MODE_THREAD;
|
||||
|
||||
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);
|
||||
init_reg_param(®_params[3], "r3", 32, PARAM_OUT);
|
||||
init_reg_param(®_params[4], "sp", 32, PARAM_OUT);
|
||||
|
||||
/* FIFO start address (first two words used for write and read pointers) */
|
||||
buf_set_u32(reg_params[0].value, 0, 32, source->address);
|
||||
/* FIFO end address (first two words used for write and read pointers) */
|
||||
buf_set_u32(reg_params[1].value, 0, 32, source->address + source->size);
|
||||
/* Flash memory address */
|
||||
buf_set_u32(reg_params[2].value, 0, 32, address+pre_size);
|
||||
/* Number of bytes */
|
||||
buf_set_u32(reg_params[3].value, 0, 32, fast_size);
|
||||
/* Stack pointer for program working area */
|
||||
buf_set_u32(reg_params[4].value, 0, 32, write_algorithm_sp->address);
|
||||
|
||||
LOG_DEBUG("source->address = %08" TARGET_PRIxADDR, source->address);
|
||||
LOG_DEBUG("source->address+ source->size = %08" TARGET_PRIxADDR, source->address+source->size);
|
||||
LOG_DEBUG("write_algorithm_sp->address = %08" TARGET_PRIxADDR, write_algorithm_sp->address);
|
||||
LOG_DEBUG("address = %08x", address+pre_size);
|
||||
LOG_DEBUG("count = %08x", count);
|
||||
|
||||
retval = target_run_flash_async_algorithm(target,
|
||||
buffer+pre_size,
|
||||
fast_size/16,
|
||||
16, /* Block size: we write in block of 16 bytes to enjoy burstwrite speed */
|
||||
0,
|
||||
NULL,
|
||||
5,
|
||||
reg_params,
|
||||
source->address,
|
||||
source->size,
|
||||
write_algorithm->address,
|
||||
0,
|
||||
&armv7m_info);
|
||||
|
||||
if (retval == ERROR_FLASH_OPERATION_FAILED) {
|
||||
LOG_ERROR("error executing bluenrg-x flash write algorithm");
|
||||
|
||||
uint32_t error = buf_get_u32(reg_params[0].value, 0, 32);
|
||||
|
||||
if (error != 0)
|
||||
LOG_ERROR("flash write failed = %08" PRIx32, error);
|
||||
}
|
||||
if (retval == ERROR_OK) {
|
||||
uint32_t rp;
|
||||
/* Read back rp and check that is valid */
|
||||
retval = target_read_u32(target, source->address+4, &rp);
|
||||
if (retval == ERROR_OK) {
|
||||
if ((rp < source->address+8) || (rp > (source->address + source->size))) {
|
||||
LOG_ERROR("flash write failed = %08" PRIx32, rp);
|
||||
retval = ERROR_FLASH_OPERATION_FAILED;
|
||||
}
|
||||
}
|
||||
}
|
||||
target_free_working_area(target, source);
|
||||
target_free_working_area(target, write_algorithm);
|
||||
target_free_working_area(target, write_algorithm_sp);
|
||||
|
||||
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]);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
}
|
||||
|
||||
/* Program chunk at end, not addressable by fast burst write algorithm */
|
||||
retval = bluenrgx_write_bytes(target, bank->base+post_offset, (uint8_t *) (buffer+pre_size+fast_size), post_size);
|
||||
if (retval) {
|
||||
LOG_ERROR("bluenrgx_write_bytes failed %d", retval);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int bluenrgx_probe(struct flash_bank *bank)
|
||||
{
|
||||
struct bluenrgx_flash_bank *bluenrgx_info = bank->driver_priv;
|
||||
uint32_t idcode, size_info, die_id;
|
||||
int i;
|
||||
int retval = target_read_u32(bank->target, JTAG_IDCODE_REG, &idcode);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
retval = target_read_u32(bank->target, FLASH_SIZE_REG, &size_info);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
retval = target_read_u32(bank->target, DIE_ID_REG, &die_id);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
bank->size = (size_info + 1) * 4;
|
||||
bank->base = FLASH_BASE;
|
||||
bank->num_sectors = bank->size/FLASH_PAGE_SIZE;
|
||||
bank->sectors = realloc(bank->sectors, sizeof(struct flash_sector) * bank->num_sectors);
|
||||
|
||||
for (i = 0; i < bank->num_sectors; i++) {
|
||||
bank->sectors[i].offset = i * FLASH_PAGE_SIZE;
|
||||
bank->sectors[i].size = FLASH_PAGE_SIZE;
|
||||
bank->sectors[i].is_erased = -1;
|
||||
bank->sectors[i].is_protected = 0;
|
||||
}
|
||||
|
||||
bluenrgx_info->probed = 1;
|
||||
bluenrgx_info->die_id = die_id;
|
||||
bluenrgx_info->idcode = idcode;
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int bluenrgx_auto_probe(struct flash_bank *bank)
|
||||
{
|
||||
struct bluenrgx_flash_bank *bluenrgx_info = bank->driver_priv;
|
||||
|
||||
if (bluenrgx_info->probed)
|
||||
return ERROR_OK;
|
||||
|
||||
return bluenrgx_probe(bank);
|
||||
}
|
||||
|
||||
/* This method must return a string displaying information about the bank */
|
||||
static int bluenrgx_get_info(struct flash_bank *bank, char *buf, int buf_size)
|
||||
{
|
||||
struct bluenrgx_flash_bank *bluenrgx_info = bank->driver_priv;
|
||||
int mask_number, cut_number;
|
||||
char *part_name;
|
||||
|
||||
if (!bluenrgx_info->probed) {
|
||||
int retval = bluenrgx_probe(bank);
|
||||
if (retval != ERROR_OK) {
|
||||
snprintf(buf, buf_size,
|
||||
"Unable to find bank information.");
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
if (bluenrgx_info->idcode == BLUENRG2_IDCODE)
|
||||
part_name = "BLUENRG-2";
|
||||
else
|
||||
part_name = "BLUENRG-1";
|
||||
|
||||
mask_number = (bluenrgx_info->die_id >> 4) & 0xF;
|
||||
cut_number = bluenrgx_info->die_id & 0xF;
|
||||
|
||||
snprintf(buf, buf_size,
|
||||
"%s - Rev: %d.%d", part_name, mask_number, cut_number);
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
struct flash_driver bluenrgx_flash = {
|
||||
.name = "bluenrg-x",
|
||||
.flash_bank_command = bluenrgx_flash_bank_command,
|
||||
.erase = bluenrgx_erase,
|
||||
.protect = bluenrgx_protect,
|
||||
.write = bluenrgx_write,
|
||||
.read = default_flash_read,
|
||||
.probe = bluenrgx_probe,
|
||||
.erase_check = default_flash_blank_check,
|
||||
.protect_check = bluenrgx_protect_check,
|
||||
.auto_probe = bluenrgx_auto_probe,
|
||||
.info = bluenrgx_get_info,
|
||||
};
|
||||
@@ -0,0 +1,567 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2017 by Texas Instruments, Inc. *
|
||||
* *
|
||||
* 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, see <http://www.gnu.org/licenses/>. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "imp.h"
|
||||
#include "cc26xx.h"
|
||||
#include <helper/binarybuffer.h>
|
||||
#include <helper/time_support.h>
|
||||
#include <target/algorithm.h>
|
||||
#include <target/armv7m.h>
|
||||
#include <target/image.h>
|
||||
|
||||
#define FLASH_TIMEOUT 8000
|
||||
|
||||
struct cc26xx_bank {
|
||||
const char *family_name;
|
||||
uint32_t icepick_id;
|
||||
uint32_t user_id;
|
||||
uint32_t device_type;
|
||||
uint32_t sector_length;
|
||||
bool probed;
|
||||
struct working_area *working_area;
|
||||
struct armv7m_algorithm armv7m_info;
|
||||
const uint8_t *algo_code;
|
||||
uint32_t algo_size;
|
||||
uint32_t algo_working_size;
|
||||
uint32_t buffer_addr[2];
|
||||
uint32_t params_addr[2];
|
||||
};
|
||||
|
||||
static int cc26xx_auto_probe(struct flash_bank *bank);
|
||||
|
||||
static uint32_t cc26xx_device_type(uint32_t icepick_id, uint32_t user_id)
|
||||
{
|
||||
uint32_t device_type = 0;
|
||||
|
||||
switch (icepick_id & ICEPICK_ID_MASK) {
|
||||
case CC26X0_ICEPICK_ID:
|
||||
device_type = CC26X0_TYPE;
|
||||
break;
|
||||
case CC26X1_ICEPICK_ID:
|
||||
device_type = CC26X1_TYPE;
|
||||
break;
|
||||
case CC13X0_ICEPICK_ID:
|
||||
device_type = CC13X0_TYPE;
|
||||
break;
|
||||
case CC13X2_CC26X2_ICEPICK_ID:
|
||||
default:
|
||||
if ((user_id & USER_ID_CC13_MASK) != 0)
|
||||
device_type = CC13X2_TYPE;
|
||||
else
|
||||
device_type = CC26X2_TYPE;
|
||||
break;
|
||||
}
|
||||
|
||||
return device_type;
|
||||
}
|
||||
|
||||
static uint32_t cc26xx_sector_length(uint32_t icepick_id)
|
||||
{
|
||||
uint32_t sector_length;
|
||||
|
||||
switch (icepick_id & ICEPICK_ID_MASK) {
|
||||
case CC26X0_ICEPICK_ID:
|
||||
case CC26X1_ICEPICK_ID:
|
||||
case CC13X0_ICEPICK_ID:
|
||||
/* Chameleon family device */
|
||||
sector_length = CC26X0_SECTOR_LENGTH;
|
||||
break;
|
||||
case CC13X2_CC26X2_ICEPICK_ID:
|
||||
default:
|
||||
/* Agama family device */
|
||||
sector_length = CC26X2_SECTOR_LENGTH;
|
||||
break;
|
||||
}
|
||||
|
||||
return sector_length;
|
||||
}
|
||||
|
||||
static int cc26xx_wait_algo_done(struct flash_bank *bank, uint32_t params_addr)
|
||||
{
|
||||
struct target *target = bank->target;
|
||||
struct cc26xx_bank *cc26xx_bank = bank->driver_priv;
|
||||
|
||||
uint32_t status_addr = params_addr + CC26XX_STATUS_OFFSET;
|
||||
uint32_t status = CC26XX_BUFFER_FULL;
|
||||
long long start_ms;
|
||||
long long elapsed_ms;
|
||||
|
||||
int retval = ERROR_OK;
|
||||
|
||||
start_ms = timeval_ms();
|
||||
while (CC26XX_BUFFER_FULL == status) {
|
||||
retval = target_read_u32(target, status_addr, &status);
|
||||
if (ERROR_OK != retval)
|
||||
return retval;
|
||||
|
||||
elapsed_ms = timeval_ms() - start_ms;
|
||||
if (elapsed_ms > 500)
|
||||
keep_alive();
|
||||
if (elapsed_ms > FLASH_TIMEOUT)
|
||||
break;
|
||||
};
|
||||
|
||||
if (CC26XX_BUFFER_EMPTY != status) {
|
||||
LOG_ERROR("%s: Flash operation failed", cc26xx_bank->family_name);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int cc26xx_init(struct flash_bank *bank)
|
||||
{
|
||||
struct target *target = bank->target;
|
||||
struct cc26xx_bank *cc26xx_bank = bank->driver_priv;
|
||||
|
||||
int retval;
|
||||
|
||||
/* Make sure we've probed the flash to get the device and size */
|
||||
retval = cc26xx_auto_probe(bank);
|
||||
if (ERROR_OK != retval)
|
||||
return retval;
|
||||
|
||||
/* Check for working area to use for flash helper algorithm */
|
||||
if (NULL != cc26xx_bank->working_area)
|
||||
target_free_working_area(target, cc26xx_bank->working_area);
|
||||
retval = target_alloc_working_area(target, cc26xx_bank->algo_working_size,
|
||||
&cc26xx_bank->working_area);
|
||||
if (ERROR_OK != retval)
|
||||
return retval;
|
||||
|
||||
/* Confirm the defined working address is the area we need to use */
|
||||
if (CC26XX_ALGO_BASE_ADDRESS != cc26xx_bank->working_area->address)
|
||||
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
|
||||
|
||||
/* Write flash helper algorithm into target memory */
|
||||
retval = target_write_buffer(target, CC26XX_ALGO_BASE_ADDRESS,
|
||||
cc26xx_bank->algo_size, cc26xx_bank->algo_code);
|
||||
if (ERROR_OK != retval) {
|
||||
LOG_ERROR("%s: Failed to load flash helper algorithm",
|
||||
cc26xx_bank->family_name);
|
||||
target_free_working_area(target, cc26xx_bank->working_area);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Initialize the ARMv7 specific info to run the algorithm */
|
||||
cc26xx_bank->armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
|
||||
cc26xx_bank->armv7m_info.core_mode = ARM_MODE_THREAD;
|
||||
|
||||
/* Begin executing the flash helper algorithm */
|
||||
retval = target_start_algorithm(target, 0, NULL, 0, NULL,
|
||||
CC26XX_ALGO_BASE_ADDRESS, 0, &cc26xx_bank->armv7m_info);
|
||||
if (ERROR_OK != retval) {
|
||||
LOG_ERROR("%s: Failed to start flash helper algorithm",
|
||||
cc26xx_bank->family_name);
|
||||
target_free_working_area(target, cc26xx_bank->working_area);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* At this point, the algorithm is running on the target and
|
||||
* ready to receive commands and data to flash the target
|
||||
*/
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int cc26xx_quit(struct flash_bank *bank)
|
||||
{
|
||||
struct target *target = bank->target;
|
||||
struct cc26xx_bank *cc26xx_bank = bank->driver_priv;
|
||||
|
||||
int retval;
|
||||
|
||||
/* Regardless of the algo's status, attempt to halt the target */
|
||||
(void)target_halt(target);
|
||||
|
||||
/* Now confirm target halted and clean up from flash helper algorithm */
|
||||
retval = target_wait_algorithm(target, 0, NULL, 0, NULL, 0, FLASH_TIMEOUT,
|
||||
&cc26xx_bank->armv7m_info);
|
||||
|
||||
target_free_working_area(target, cc26xx_bank->working_area);
|
||||
cc26xx_bank->working_area = NULL;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int cc26xx_mass_erase(struct flash_bank *bank)
|
||||
{
|
||||
struct target *target = bank->target;
|
||||
struct cc26xx_bank *cc26xx_bank = bank->driver_priv;
|
||||
struct cc26xx_algo_params algo_params;
|
||||
|
||||
int retval;
|
||||
|
||||
if (TARGET_HALTED != target->state) {
|
||||
LOG_ERROR("Target not halted");
|
||||
return ERROR_TARGET_NOT_HALTED;
|
||||
}
|
||||
|
||||
retval = cc26xx_init(bank);
|
||||
if (ERROR_OK != retval)
|
||||
return retval;
|
||||
|
||||
/* Initialize algorithm parameters */
|
||||
buf_set_u32(algo_params.address, 0, 32, 0);
|
||||
buf_set_u32(algo_params.length, 0, 32, 4);
|
||||
buf_set_u32(algo_params.command, 0, 32, CC26XX_CMD_ERASE_ALL);
|
||||
buf_set_u32(algo_params.status, 0, 32, CC26XX_BUFFER_FULL);
|
||||
|
||||
/* Issue flash helper algorithm parameters for mass erase */
|
||||
retval = target_write_buffer(target, cc26xx_bank->params_addr[0],
|
||||
sizeof(algo_params), (uint8_t *)&algo_params);
|
||||
|
||||
/* Wait for command to complete */
|
||||
if (ERROR_OK == retval)
|
||||
retval = cc26xx_wait_algo_done(bank, cc26xx_bank->params_addr[0]);
|
||||
|
||||
/* Regardless of errors, try to close down algo */
|
||||
(void)cc26xx_quit(bank);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
FLASH_BANK_COMMAND_HANDLER(cc26xx_flash_bank_command)
|
||||
{
|
||||
struct cc26xx_bank *cc26xx_bank;
|
||||
|
||||
if (CMD_ARGC < 6)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
|
||||
cc26xx_bank = malloc(sizeof(struct cc26xx_bank));
|
||||
if (NULL == cc26xx_bank)
|
||||
return ERROR_FAIL;
|
||||
|
||||
/* Initialize private flash information */
|
||||
memset((void *)cc26xx_bank, 0x00, sizeof(struct cc26xx_bank));
|
||||
cc26xx_bank->family_name = "cc26xx";
|
||||
cc26xx_bank->device_type = CC26XX_NO_TYPE;
|
||||
cc26xx_bank->sector_length = 0x1000;
|
||||
|
||||
/* Finish initialization of bank */
|
||||
bank->driver_priv = cc26xx_bank;
|
||||
bank->next = NULL;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int cc26xx_erase(struct flash_bank *bank, int first, int last)
|
||||
{
|
||||
struct target *target = bank->target;
|
||||
struct cc26xx_bank *cc26xx_bank = bank->driver_priv;
|
||||
struct cc26xx_algo_params algo_params;
|
||||
|
||||
uint32_t address;
|
||||
uint32_t length;
|
||||
int retval;
|
||||
|
||||
if (TARGET_HALTED != target->state) {
|
||||
LOG_ERROR("Target not halted");
|
||||
return ERROR_TARGET_NOT_HALTED;
|
||||
}
|
||||
|
||||
/* Do a mass erase if user requested all sectors of flash */
|
||||
if ((first == 0) && (last == (bank->num_sectors - 1))) {
|
||||
/* Request mass erase of flash */
|
||||
return cc26xx_mass_erase(bank);
|
||||
}
|
||||
|
||||
address = first * cc26xx_bank->sector_length;
|
||||
length = (last - first + 1) * cc26xx_bank->sector_length;
|
||||
|
||||
retval = cc26xx_init(bank);
|
||||
if (ERROR_OK != retval)
|
||||
return retval;
|
||||
|
||||
/* Set up algorithm parameters for erase command */
|
||||
buf_set_u32(algo_params.address, 0, 32, address);
|
||||
buf_set_u32(algo_params.length, 0, 32, length);
|
||||
buf_set_u32(algo_params.command, 0, 32, CC26XX_CMD_ERASE_SECTORS);
|
||||
buf_set_u32(algo_params.status, 0, 32, CC26XX_BUFFER_FULL);
|
||||
|
||||
/* Issue flash helper algorithm parameters for erase */
|
||||
retval = target_write_buffer(target, cc26xx_bank->params_addr[0],
|
||||
sizeof(algo_params), (uint8_t *)&algo_params);
|
||||
|
||||
/* If no error, wait for erase to finish */
|
||||
if (ERROR_OK == retval)
|
||||
retval = cc26xx_wait_algo_done(bank, cc26xx_bank->params_addr[0]);
|
||||
|
||||
/* Regardless of errors, try to close down algo */
|
||||
(void)cc26xx_quit(bank);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int cc26xx_protect(struct flash_bank *bank, int set, int first,
|
||||
int last)
|
||||
{
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int cc26xx_write(struct flash_bank *bank, const uint8_t *buffer,
|
||||
uint32_t offset, uint32_t count)
|
||||
{
|
||||
struct target *target = bank->target;
|
||||
struct cc26xx_bank *cc26xx_bank = bank->driver_priv;
|
||||
struct cc26xx_algo_params algo_params[2];
|
||||
uint32_t size = 0;
|
||||
long long start_ms;
|
||||
long long elapsed_ms;
|
||||
uint32_t address;
|
||||
|
||||
uint32_t index;
|
||||
int retval;
|
||||
|
||||
if (TARGET_HALTED != target->state) {
|
||||
LOG_ERROR("Target not halted");
|
||||
return ERROR_TARGET_NOT_HALTED;
|
||||
}
|
||||
|
||||
retval = cc26xx_init(bank);
|
||||
if (ERROR_OK != retval)
|
||||
return retval;
|
||||
|
||||
/* Initialize algorithm parameters to default values */
|
||||
buf_set_u32(algo_params[0].command, 0, 32, CC26XX_CMD_PROGRAM);
|
||||
buf_set_u32(algo_params[1].command, 0, 32, CC26XX_CMD_PROGRAM);
|
||||
|
||||
/* Write requested data, ping-ponging between two buffers */
|
||||
index = 0;
|
||||
start_ms = timeval_ms();
|
||||
address = bank->base + offset;
|
||||
while (count > 0) {
|
||||
|
||||
if (count > cc26xx_bank->sector_length)
|
||||
size = cc26xx_bank->sector_length;
|
||||
else
|
||||
size = count;
|
||||
|
||||
/* Put next block of data to flash into buffer */
|
||||
retval = target_write_buffer(target, cc26xx_bank->buffer_addr[index],
|
||||
size, buffer);
|
||||
if (ERROR_OK != retval) {
|
||||
LOG_ERROR("Unable to write data to target memory");
|
||||
break;
|
||||
}
|
||||
|
||||
/* Update algo parameters for next block */
|
||||
buf_set_u32(algo_params[index].address, 0, 32, address);
|
||||
buf_set_u32(algo_params[index].length, 0, 32, size);
|
||||
buf_set_u32(algo_params[index].status, 0, 32, CC26XX_BUFFER_FULL);
|
||||
|
||||
/* Issue flash helper algorithm parameters for block write */
|
||||
retval = target_write_buffer(target, cc26xx_bank->params_addr[index],
|
||||
sizeof(algo_params[index]), (uint8_t *)&algo_params[index]);
|
||||
if (ERROR_OK != retval)
|
||||
break;
|
||||
|
||||
/* Wait for next ping pong buffer to be ready */
|
||||
index ^= 1;
|
||||
retval = cc26xx_wait_algo_done(bank, cc26xx_bank->params_addr[index]);
|
||||
if (ERROR_OK != retval)
|
||||
break;
|
||||
|
||||
count -= size;
|
||||
buffer += size;
|
||||
address += size;
|
||||
|
||||
elapsed_ms = timeval_ms() - start_ms;
|
||||
if (elapsed_ms > 500)
|
||||
keep_alive();
|
||||
}
|
||||
|
||||
/* If no error yet, wait for last buffer to finish */
|
||||
if (ERROR_OK == retval) {
|
||||
index ^= 1;
|
||||
retval = cc26xx_wait_algo_done(bank, cc26xx_bank->params_addr[index]);
|
||||
}
|
||||
|
||||
/* Regardless of errors, try to close down algo */
|
||||
(void)cc26xx_quit(bank);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int cc26xx_probe(struct flash_bank *bank)
|
||||
{
|
||||
struct target *target = bank->target;
|
||||
struct cc26xx_bank *cc26xx_bank = bank->driver_priv;
|
||||
|
||||
uint32_t sector_length;
|
||||
uint32_t value;
|
||||
int num_sectors;
|
||||
int max_sectors;
|
||||
|
||||
int retval;
|
||||
|
||||
retval = target_read_u32(target, FCFG1_ICEPICK_ID, &value);
|
||||
if (ERROR_OK != retval)
|
||||
return retval;
|
||||
cc26xx_bank->icepick_id = value;
|
||||
|
||||
retval = target_read_u32(target, FCFG1_USER_ID, &value);
|
||||
if (ERROR_OK != retval)
|
||||
return retval;
|
||||
cc26xx_bank->user_id = value;
|
||||
|
||||
cc26xx_bank->device_type = cc26xx_device_type(cc26xx_bank->icepick_id,
|
||||
cc26xx_bank->user_id);
|
||||
|
||||
sector_length = cc26xx_sector_length(cc26xx_bank->icepick_id);
|
||||
|
||||
/* Set up appropriate flash helper algorithm */
|
||||
switch (cc26xx_bank->icepick_id & ICEPICK_ID_MASK) {
|
||||
case CC26X0_ICEPICK_ID:
|
||||
case CC26X1_ICEPICK_ID:
|
||||
case CC13X0_ICEPICK_ID:
|
||||
/* Chameleon family device */
|
||||
cc26xx_bank->algo_code = cc26x0_algo;
|
||||
cc26xx_bank->algo_size = sizeof(cc26x0_algo);
|
||||
cc26xx_bank->algo_working_size = CC26X0_WORKING_SIZE;
|
||||
cc26xx_bank->buffer_addr[0] = CC26X0_ALGO_BUFFER_0;
|
||||
cc26xx_bank->buffer_addr[1] = CC26X0_ALGO_BUFFER_1;
|
||||
cc26xx_bank->params_addr[0] = CC26X0_ALGO_PARAMS_0;
|
||||
cc26xx_bank->params_addr[1] = CC26X0_ALGO_PARAMS_1;
|
||||
max_sectors = CC26X0_MAX_SECTORS;
|
||||
break;
|
||||
case CC13X2_CC26X2_ICEPICK_ID:
|
||||
default:
|
||||
/* Agama family device */
|
||||
cc26xx_bank->algo_code = cc26x2_algo;
|
||||
cc26xx_bank->algo_size = sizeof(cc26x2_algo);
|
||||
cc26xx_bank->algo_working_size = CC26X2_WORKING_SIZE;
|
||||
cc26xx_bank->buffer_addr[0] = CC26X2_ALGO_BUFFER_0;
|
||||
cc26xx_bank->buffer_addr[1] = CC26X2_ALGO_BUFFER_1;
|
||||
cc26xx_bank->params_addr[0] = CC26X2_ALGO_PARAMS_0;
|
||||
cc26xx_bank->params_addr[1] = CC26X2_ALGO_PARAMS_1;
|
||||
max_sectors = CC26X2_MAX_SECTORS;
|
||||
break;
|
||||
}
|
||||
|
||||
retval = target_read_u32(target, CC26XX_FLASH_SIZE_INFO, &value);
|
||||
if (ERROR_OK != retval)
|
||||
return retval;
|
||||
num_sectors = value & 0xff;
|
||||
if (num_sectors > max_sectors)
|
||||
num_sectors = max_sectors;
|
||||
|
||||
bank->sectors = malloc(sizeof(struct flash_sector) * num_sectors);
|
||||
if (NULL == bank->sectors)
|
||||
return ERROR_FAIL;
|
||||
|
||||
bank->base = CC26XX_FLASH_BASE_ADDR;
|
||||
bank->num_sectors = num_sectors;
|
||||
bank->size = num_sectors * sector_length;
|
||||
bank->write_start_alignment = 0;
|
||||
bank->write_end_alignment = 0;
|
||||
cc26xx_bank->sector_length = sector_length;
|
||||
|
||||
for (int i = 0; i < num_sectors; i++) {
|
||||
bank->sectors[i].offset = i * sector_length;
|
||||
bank->sectors[i].size = sector_length;
|
||||
bank->sectors[i].is_erased = -1;
|
||||
bank->sectors[i].is_protected = 0;
|
||||
}
|
||||
|
||||
/* We've successfully determined the stats on the flash bank */
|
||||
cc26xx_bank->probed = true;
|
||||
|
||||
/* If we fall through to here, then all went well */
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int cc26xx_auto_probe(struct flash_bank *bank)
|
||||
{
|
||||
struct cc26xx_bank *cc26xx_bank = bank->driver_priv;
|
||||
|
||||
int retval = ERROR_OK;
|
||||
|
||||
if (bank->bank_number != 0) {
|
||||
/* Invalid bank number somehow */
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (!cc26xx_bank->probed)
|
||||
retval = cc26xx_probe(bank);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int cc26xx_protect_check(struct flash_bank *bank)
|
||||
{
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int cc26xx_info(struct flash_bank *bank, char *buf, int buf_size)
|
||||
{
|
||||
struct cc26xx_bank *cc26xx_bank = bank->driver_priv;
|
||||
int printed = 0;
|
||||
const char *device;
|
||||
|
||||
switch (cc26xx_bank->device_type) {
|
||||
case CC26X0_TYPE:
|
||||
device = "CC26x0";
|
||||
break;
|
||||
case CC26X1_TYPE:
|
||||
device = "CC26x1";
|
||||
break;
|
||||
case CC13X0_TYPE:
|
||||
device = "CC13x0";
|
||||
break;
|
||||
case CC13X2_TYPE:
|
||||
device = "CC13x2";
|
||||
break;
|
||||
case CC26X2_TYPE:
|
||||
device = "CC26x2";
|
||||
break;
|
||||
case CC26XX_NO_TYPE:
|
||||
default:
|
||||
device = "Unrecognized";
|
||||
break;
|
||||
}
|
||||
|
||||
printed = snprintf(buf, buf_size,
|
||||
"%s device: ICEPick ID 0x%08x, USER ID 0x%08x\n",
|
||||
device, cc26xx_bank->icepick_id, cc26xx_bank->user_id);
|
||||
|
||||
if (printed >= buf_size)
|
||||
return ERROR_BUF_TOO_SMALL;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
struct flash_driver cc26xx_flash = {
|
||||
.name = "cc26xx",
|
||||
.flash_bank_command = cc26xx_flash_bank_command,
|
||||
.erase = cc26xx_erase,
|
||||
.protect = cc26xx_protect,
|
||||
.write = cc26xx_write,
|
||||
.read = default_flash_read,
|
||||
.probe = cc26xx_probe,
|
||||
.auto_probe = cc26xx_auto_probe,
|
||||
.erase_check = default_flash_blank_check,
|
||||
.protect_check = cc26xx_protect_check,
|
||||
.info = cc26xx_info,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
@@ -0,0 +1,101 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2017 by Texas Instruments, Inc. *
|
||||
* *
|
||||
* 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, see <http://www.gnu.org/licenses/>. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef OPENOCD_FLASH_NOR_CC26XX_H
|
||||
#define OPENOCD_FLASH_NOR_CC26XX_H
|
||||
|
||||
/* Addresses of FCFG1 registers to access ICEPick Device ID and User ID */
|
||||
#define FCFG1_ICEPICK_ID 0x50001318
|
||||
#define FCFG1_USER_ID 0x50001294
|
||||
|
||||
/* ICEPick device ID mask and values */
|
||||
#define ICEPICK_ID_MASK 0x0fffffff
|
||||
#define ICEPICK_REV_MASK 0xf0000000
|
||||
#define CC26X0_ICEPICK_ID 0x0b99a02f
|
||||
#define CC26X1_ICEPICK_ID 0x0b9bd02f
|
||||
#define CC13X0_ICEPICK_ID 0x0b9be02f
|
||||
#define CC13X2_CC26X2_ICEPICK_ID 0x0bb4102f
|
||||
|
||||
/* User ID mask for Agama CC13x2 vs CC26x2 */
|
||||
#define USER_ID_CC13_MASK 0x00800000
|
||||
|
||||
/* Common CC26xx/CC13xx flash and memory parameters */
|
||||
#define CC26XX_FLASH_BASE_ADDR 0x00000000
|
||||
#define CC26XX_FLASH_SIZE_INFO 0x4003002c
|
||||
#define CC26XX_SRAM_SIZE_INFO 0x40082250
|
||||
#define CC26XX_ALGO_BASE_ADDRESS 0x20000000
|
||||
|
||||
/* Chameleon CC26x0/CC13x0 specific parameters */
|
||||
#define CC26X0_MAX_SECTORS 32
|
||||
#define CC26X0_SECTOR_LENGTH 0x1000
|
||||
#define CC26X0_ALGO_BUFFER_0 0x20001c00
|
||||
#define CC26X0_ALGO_BUFFER_1 0x20002c00
|
||||
#define CC26X0_ALGO_PARAMS_0 0x20001bd8
|
||||
#define CC26X0_ALGO_PARAMS_1 0x20001bec
|
||||
#define CC26X0_WORKING_SIZE (CC26X0_ALGO_BUFFER_1 + CC26X0_SECTOR_LENGTH - \
|
||||
CC26XX_ALGO_BASE_ADDRESS)
|
||||
|
||||
/* Agama CC26x2/CC13x2 specific parameters */
|
||||
#define CC26X2_MAX_SECTORS 128
|
||||
#define CC26X2_SECTOR_LENGTH 0x2000
|
||||
#define CC26X2_ALGO_BUFFER_0 0x20002000
|
||||
#define CC26X2_ALGO_BUFFER_1 0x20004000
|
||||
#define CC26X2_ALGO_PARAMS_0 0x20001fd8
|
||||
#define CC26X2_ALGO_PARAMS_1 0x20001fec
|
||||
#define CC26X2_WORKING_SIZE (CC26X2_ALGO_BUFFER_1 + CC26X2_SECTOR_LENGTH - \
|
||||
CC26XX_ALGO_BASE_ADDRESS)
|
||||
|
||||
/* CC26xx flash helper algorithm buffer flags */
|
||||
#define CC26XX_BUFFER_EMPTY 0x00000000
|
||||
#define CC26XX_BUFFER_FULL 0xffffffff
|
||||
|
||||
/* CC26XX flash helper algorithm commands */
|
||||
#define CC26XX_CMD_NO_ACTION 0
|
||||
#define CC26XX_CMD_ERASE_ALL 1
|
||||
#define CC26XX_CMD_PROGRAM 2
|
||||
#define CC26XX_CMD_ERASE_AND_PROGRAM 3
|
||||
#define CC26XX_CMD_ERASE_AND_PROGRAM_WITH_RETAIN 4
|
||||
#define CC26XX_CMD_ERASE_SECTORS 5
|
||||
|
||||
/* CC26xx and CC13xx device types */
|
||||
#define CC26XX_NO_TYPE 0 /* Device type not determined yet */
|
||||
#define CC26X0_TYPE 1 /* CC26x0 Chameleon device */
|
||||
#define CC26X1_TYPE 2 /* CC26x1 Chameleon device */
|
||||
#define CC26X2_TYPE 3 /* CC26x2 Agama device */
|
||||
#define CC13X0_TYPE 4 /* CC13x0 Chameleon device */
|
||||
#define CC13X2_TYPE 5 /* CC13x2 Agama device */
|
||||
|
||||
/* Flash helper algorithm parameter block struct */
|
||||
#define CC26XX_STATUS_OFFSET 0x0c
|
||||
struct cc26xx_algo_params {
|
||||
uint8_t address[4];
|
||||
uint8_t length[4];
|
||||
uint8_t command[4];
|
||||
uint8_t status[4];
|
||||
};
|
||||
|
||||
/* Flash helper algorithm for CC26x0 Chameleon targets */
|
||||
const uint8_t cc26x0_algo[] = {
|
||||
#include "../../../contrib/loaders/flash/cc26xx/cc26x0_algo.inc"
|
||||
};
|
||||
|
||||
/* Flash helper algorithm for CC26x2 Agama targets */
|
||||
const uint8_t cc26x2_algo[] = {
|
||||
#include "../../../contrib/loaders/flash/cc26xx/cc26x2_algo.inc"
|
||||
};
|
||||
|
||||
#endif /* OPENOCD_FLASH_NOR_CC26XX_H */
|
||||
@@ -0,0 +1,529 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2017 by Texas Instruments, Inc. *
|
||||
* *
|
||||
* 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, see <http://www.gnu.org/licenses/>. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "imp.h"
|
||||
#include "cc3220sf.h"
|
||||
#include <helper/time_support.h>
|
||||
#include <target/algorithm.h>
|
||||
#include <target/armv7m.h>
|
||||
|
||||
#define FLASH_TIMEOUT 5000
|
||||
|
||||
struct cc3220sf_bank {
|
||||
bool probed;
|
||||
struct armv7m_algorithm armv7m_info;
|
||||
};
|
||||
|
||||
static int cc3220sf_mass_erase(struct flash_bank *bank)
|
||||
{
|
||||
struct target *target = bank->target;
|
||||
bool done;
|
||||
long long start_ms;
|
||||
long long elapsed_ms;
|
||||
uint32_t value;
|
||||
|
||||
int retval = ERROR_OK;
|
||||
|
||||
if (TARGET_HALTED != target->state) {
|
||||
LOG_ERROR("Target not halted");
|
||||
return ERROR_TARGET_NOT_HALTED;
|
||||
}
|
||||
|
||||
/* Set starting address to erase to zero */
|
||||
retval = target_write_u32(target, FMA_REGISTER_ADDR, 0);
|
||||
if (ERROR_OK != retval)
|
||||
return retval;
|
||||
|
||||
/* Write the MERASE bit of the FMC register */
|
||||
retval = target_write_u32(target, FMC_REGISTER_ADDR, FMC_MERASE_VALUE);
|
||||
if (ERROR_OK != retval)
|
||||
return retval;
|
||||
|
||||
/* Poll the MERASE bit until the mass erase is complete */
|
||||
done = false;
|
||||
start_ms = timeval_ms();
|
||||
while (!done) {
|
||||
retval = target_read_u32(target, FMC_REGISTER_ADDR, &value);
|
||||
if (ERROR_OK != retval)
|
||||
return retval;
|
||||
|
||||
if ((value & FMC_MERASE_BIT) == 0) {
|
||||
/* Bit clears when mass erase is finished */
|
||||
done = true;
|
||||
} else {
|
||||
elapsed_ms = timeval_ms() - start_ms;
|
||||
if (elapsed_ms > 500)
|
||||
keep_alive();
|
||||
if (elapsed_ms > FLASH_TIMEOUT)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!done) {
|
||||
/* Mass erase timed out waiting for confirmation */
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
FLASH_BANK_COMMAND_HANDLER(cc3220sf_flash_bank_command)
|
||||
{
|
||||
struct cc3220sf_bank *cc3220sf_bank;
|
||||
|
||||
if (CMD_ARGC < 6)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
|
||||
cc3220sf_bank = malloc(sizeof(struct cc3220sf_bank));
|
||||
if (NULL == cc3220sf_bank)
|
||||
return ERROR_FAIL;
|
||||
|
||||
/* Initialize private flash information */
|
||||
cc3220sf_bank->probed = false;
|
||||
|
||||
/* Finish initialization of flash bank */
|
||||
bank->driver_priv = cc3220sf_bank;
|
||||
bank->next = NULL;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int cc3220sf_erase(struct flash_bank *bank, int first, int last)
|
||||
{
|
||||
struct target *target = bank->target;
|
||||
bool done;
|
||||
long long start_ms;
|
||||
long long elapsed_ms;
|
||||
uint32_t address;
|
||||
uint32_t value;
|
||||
|
||||
int retval = ERROR_OK;
|
||||
|
||||
if (TARGET_HALTED != target->state) {
|
||||
LOG_ERROR("Target not halted");
|
||||
return ERROR_TARGET_NOT_HALTED;
|
||||
}
|
||||
|
||||
/* Do a mass erase if user requested all sectors of flash */
|
||||
if ((first == 0) && (last == (bank->num_sectors - 1))) {
|
||||
/* Request mass erase of flash */
|
||||
return cc3220sf_mass_erase(bank);
|
||||
}
|
||||
|
||||
/* Erase requested sectors one by one */
|
||||
for (int i = first; i <= last; i++) {
|
||||
|
||||
/* Determine address of sector to erase */
|
||||
address = FLASH_BASE_ADDR + i * FLASH_SECTOR_SIZE;
|
||||
|
||||
/* Set starting address to erase */
|
||||
retval = target_write_u32(target, FMA_REGISTER_ADDR, address);
|
||||
if (ERROR_OK != retval)
|
||||
return retval;
|
||||
|
||||
/* Write the ERASE bit of the FMC register */
|
||||
retval = target_write_u32(target, FMC_REGISTER_ADDR, FMC_ERASE_VALUE);
|
||||
if (ERROR_OK != retval)
|
||||
return retval;
|
||||
|
||||
/* Poll the ERASE bit until the erase is complete */
|
||||
done = false;
|
||||
start_ms = timeval_ms();
|
||||
while (!done) {
|
||||
retval = target_read_u32(target, FMC_REGISTER_ADDR, &value);
|
||||
if (ERROR_OK != retval)
|
||||
return retval;
|
||||
|
||||
if ((value & FMC_ERASE_BIT) == 0) {
|
||||
/* Bit clears when mass erase is finished */
|
||||
done = true;
|
||||
} else {
|
||||
elapsed_ms = timeval_ms() - start_ms;
|
||||
if (elapsed_ms > 500)
|
||||
keep_alive();
|
||||
if (elapsed_ms > FLASH_TIMEOUT)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!done) {
|
||||
/* Sector erase timed out waiting for confirmation */
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int cc3220sf_protect(struct flash_bank *bank, int set, int first,
|
||||
int last)
|
||||
{
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int cc3220sf_write(struct flash_bank *bank, const uint8_t *buffer,
|
||||
uint32_t offset, uint32_t count)
|
||||
{
|
||||
struct target *target = bank->target;
|
||||
struct cc3220sf_bank *cc3220sf_bank = bank->driver_priv;
|
||||
struct working_area *algo_working_area;
|
||||
struct working_area *buffer_working_area;
|
||||
struct reg_param reg_params[3];
|
||||
uint32_t algo_base_address;
|
||||
uint32_t algo_buffer_address;
|
||||
uint32_t algo_buffer_size;
|
||||
uint32_t address;
|
||||
uint32_t remaining;
|
||||
uint32_t words;
|
||||
uint32_t result;
|
||||
|
||||
int retval = ERROR_OK;
|
||||
|
||||
if (TARGET_HALTED != target->state) {
|
||||
LOG_ERROR("Target not halted");
|
||||
return ERROR_TARGET_NOT_HALTED;
|
||||
}
|
||||
|
||||
/* Obtain working area to use for flash helper algorithm */
|
||||
retval = target_alloc_working_area(target, sizeof(cc3220sf_algo),
|
||||
&algo_working_area);
|
||||
if (ERROR_OK != retval)
|
||||
return retval;
|
||||
|
||||
/* Obtain working area to use for flash buffer */
|
||||
retval = target_alloc_working_area(target,
|
||||
target_get_working_area_avail(target), &buffer_working_area);
|
||||
if (ERROR_OK != retval) {
|
||||
target_free_working_area(target, algo_working_area);
|
||||
return retval;
|
||||
}
|
||||
|
||||
algo_base_address = algo_working_area->address;
|
||||
algo_buffer_address = buffer_working_area->address;
|
||||
algo_buffer_size = buffer_working_area->size;
|
||||
|
||||
/* Make sure buffer size is a multiple of 32 word (0x80 byte) chunks */
|
||||
/* (algo runs more efficiently if it operates on 32 words at a time) */
|
||||
if (algo_buffer_size > 0x80)
|
||||
algo_buffer_size &= ~0x7f;
|
||||
|
||||
/* Write flash helper algorithm into target memory */
|
||||
retval = target_write_buffer(target, algo_base_address,
|
||||
sizeof(cc3220sf_algo), cc3220sf_algo);
|
||||
if (ERROR_OK != retval) {
|
||||
target_free_working_area(target, algo_working_area);
|
||||
target_free_working_area(target, buffer_working_area);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Initialize the ARMv7m specific info to run the algorithm */
|
||||
cc3220sf_bank->armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
|
||||
cc3220sf_bank->armv7m_info.core_mode = ARM_MODE_THREAD;
|
||||
|
||||
/* Initialize register params for flash helper algorithm */
|
||||
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_IN_OUT);
|
||||
|
||||
/* Prepare to write to flash */
|
||||
address = FLASH_BASE_ADDR + offset;
|
||||
remaining = count;
|
||||
|
||||
/* The flash hardware can only write complete words to flash. If
|
||||
* an unaligned address is passed in, we must do a read-modify-write
|
||||
* on a word with enough bytes to align the rest of the buffer. And
|
||||
* if less than a whole word remains at the end, we must also do a
|
||||
* read-modify-write on a final word to finish up.
|
||||
*/
|
||||
|
||||
/* Do one word write to align address on 32-bit boundary if needed */
|
||||
if (0 != (address & 0x3)) {
|
||||
uint8_t head[4];
|
||||
|
||||
/* Get starting offset for data to write (will be 1 to 3) */
|
||||
uint32_t head_offset = address & 0x03;
|
||||
|
||||
/* Get the aligned address to write this first word to */
|
||||
uint32_t head_address = address & 0xfffffffc;
|
||||
|
||||
/* Retrieve what is already in flash at the head address */
|
||||
retval = target_read_buffer(target, head_address, sizeof(head), head);
|
||||
|
||||
if (ERROR_OK == retval) {
|
||||
/* Substitute in the new data to write */
|
||||
while ((remaining > 0) && (head_offset < 4)) {
|
||||
head[head_offset] = *buffer;
|
||||
head_offset++;
|
||||
address++;
|
||||
buffer++;
|
||||
remaining--;
|
||||
}
|
||||
}
|
||||
|
||||
if (ERROR_OK == retval) {
|
||||
/* Helper parameters are passed in registers R0-R2 */
|
||||
/* Set start of data buffer, address to write to, and word count */
|
||||
buf_set_u32(reg_params[0].value, 0, 32, algo_buffer_address);
|
||||
buf_set_u32(reg_params[1].value, 0, 32, head_address);
|
||||
buf_set_u32(reg_params[2].value, 0, 32, 1);
|
||||
|
||||
/* Write head value into buffer to flash */
|
||||
retval = target_write_buffer(target, algo_buffer_address,
|
||||
sizeof(head), head);
|
||||
}
|
||||
|
||||
if (ERROR_OK == retval) {
|
||||
/* Execute the flash helper algorithm */
|
||||
retval = target_run_algorithm(target, 0, NULL, 3, reg_params,
|
||||
algo_base_address, 0, FLASH_TIMEOUT,
|
||||
&cc3220sf_bank->armv7m_info);
|
||||
if (ERROR_OK != retval)
|
||||
LOG_ERROR("cc3220sf: Flash algorithm failed to run");
|
||||
|
||||
/* Check that the head value was written to flash */
|
||||
result = buf_get_u32(reg_params[2].value, 0, 32);
|
||||
if (0 != result) {
|
||||
retval = ERROR_FAIL;
|
||||
LOG_ERROR("cc3220sf: Flash operation failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if there's data at end of buffer that isn't a full word */
|
||||
uint32_t tail_count = remaining & 0x03;
|
||||
/* Adjust remaining so it is a multiple of whole words */
|
||||
remaining -= tail_count;
|
||||
|
||||
while ((ERROR_OK == retval) && (remaining > 0)) {
|
||||
/* Set start of data buffer and address to write to */
|
||||
buf_set_u32(reg_params[0].value, 0, 32, algo_buffer_address);
|
||||
buf_set_u32(reg_params[1].value, 0, 32, address);
|
||||
|
||||
/* Download data to write into memory buffer */
|
||||
if (remaining >= algo_buffer_size) {
|
||||
/* Fill up buffer with data to flash */
|
||||
retval = target_write_buffer(target, algo_buffer_address,
|
||||
algo_buffer_size, buffer);
|
||||
if (ERROR_OK != retval)
|
||||
break;
|
||||
|
||||
/* Count to write is in 32-bit words */
|
||||
words = algo_buffer_size / 4;
|
||||
|
||||
/* Bump variables to next data */
|
||||
address += algo_buffer_size;
|
||||
buffer += algo_buffer_size;
|
||||
remaining -= algo_buffer_size;
|
||||
} else {
|
||||
/* Fill buffer with what's left of the data */
|
||||
retval = target_write_buffer(target, algo_buffer_address,
|
||||
remaining, buffer);
|
||||
if (ERROR_OK != retval)
|
||||
break;
|
||||
|
||||
/* Calculate the final word count to write */
|
||||
words = remaining / 4;
|
||||
if (0 != (remaining % 4))
|
||||
words++;
|
||||
|
||||
/* Bump variables to any final data */
|
||||
address += remaining;
|
||||
buffer += remaining;
|
||||
remaining = 0;
|
||||
}
|
||||
|
||||
/* Set number of words to write */
|
||||
buf_set_u32(reg_params[2].value, 0, 32, words);
|
||||
|
||||
/* Execute the flash helper algorithm */
|
||||
retval = target_run_algorithm(target, 0, NULL, 3, reg_params,
|
||||
algo_base_address, 0, FLASH_TIMEOUT,
|
||||
&cc3220sf_bank->armv7m_info);
|
||||
if (ERROR_OK != retval) {
|
||||
LOG_ERROR("cc3220sf: Flash algorithm failed to run");
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check that all words were written to flash */
|
||||
result = buf_get_u32(reg_params[2].value, 0, 32);
|
||||
if (0 != result) {
|
||||
retval = ERROR_FAIL;
|
||||
LOG_ERROR("cc3220sf: Flash operation failed");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Do one word write for any final bytes less than a full word */
|
||||
if ((ERROR_OK == retval) && (0 != tail_count)) {
|
||||
uint8_t tail[4];
|
||||
|
||||
/* Set starting byte offset for data to write */
|
||||
uint32_t tail_offset = 0;
|
||||
|
||||
/* Retrieve what is already in flash at the tail address */
|
||||
retval = target_read_buffer(target, address, sizeof(tail), tail);
|
||||
|
||||
if (ERROR_OK == retval) {
|
||||
/* Substitute in the new data to write */
|
||||
while (tail_count > 0) {
|
||||
tail[tail_offset] = *buffer;
|
||||
tail_offset++;
|
||||
buffer++;
|
||||
tail_count--;
|
||||
}
|
||||
}
|
||||
|
||||
if (ERROR_OK == retval) {
|
||||
/* Set start of data buffer, address to write to, and word count */
|
||||
buf_set_u32(reg_params[0].value, 0, 32, algo_buffer_address);
|
||||
buf_set_u32(reg_params[1].value, 0, 32, address);
|
||||
buf_set_u32(reg_params[2].value, 0, 32, 1);
|
||||
|
||||
/* Write tail value into buffer to flash */
|
||||
retval = target_write_buffer(target, algo_buffer_address,
|
||||
sizeof(tail), tail);
|
||||
}
|
||||
|
||||
if (ERROR_OK == retval) {
|
||||
/* Execute the flash helper algorithm */
|
||||
retval = target_run_algorithm(target, 0, NULL, 3, reg_params,
|
||||
algo_base_address, 0, FLASH_TIMEOUT,
|
||||
&cc3220sf_bank->armv7m_info);
|
||||
if (ERROR_OK != retval)
|
||||
LOG_ERROR("cc3220sf: Flash algorithm failed to run");
|
||||
|
||||
/* Check that the tail was written to flash */
|
||||
result = buf_get_u32(reg_params[2].value, 0, 32);
|
||||
if (0 != result) {
|
||||
retval = ERROR_FAIL;
|
||||
LOG_ERROR("cc3220sf: Flash operation failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Free resources */
|
||||
destroy_reg_param(®_params[0]);
|
||||
destroy_reg_param(®_params[1]);
|
||||
destroy_reg_param(®_params[2]);
|
||||
target_free_working_area(target, algo_working_area);
|
||||
target_free_working_area(target, buffer_working_area);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int cc3220sf_probe(struct flash_bank *bank)
|
||||
{
|
||||
struct cc3220sf_bank *cc3220sf_bank = bank->driver_priv;
|
||||
|
||||
uint32_t base;
|
||||
uint32_t size;
|
||||
int num_sectors;
|
||||
int bank_id;
|
||||
|
||||
bank_id = bank->bank_number;
|
||||
|
||||
if (0 == bank_id) {
|
||||
base = FLASH_BASE_ADDR;
|
||||
size = FLASH_NUM_SECTORS * FLASH_SECTOR_SIZE;
|
||||
num_sectors = FLASH_NUM_SECTORS;
|
||||
} else {
|
||||
/* Invalid bank number somehow */
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (NULL != bank->sectors) {
|
||||
free(bank->sectors);
|
||||
bank->sectors = NULL;
|
||||
}
|
||||
|
||||
bank->sectors = malloc(sizeof(struct flash_sector) * num_sectors);
|
||||
if (NULL == bank->sectors)
|
||||
return ERROR_FAIL;
|
||||
|
||||
bank->base = base;
|
||||
bank->size = size;
|
||||
bank->write_start_alignment = 0;
|
||||
bank->write_end_alignment = 0;
|
||||
bank->num_sectors = num_sectors;
|
||||
|
||||
for (int i = 0; i < num_sectors; i++) {
|
||||
bank->sectors[i].offset = i * FLASH_SECTOR_SIZE;
|
||||
bank->sectors[i].size = FLASH_SECTOR_SIZE;
|
||||
bank->sectors[i].is_erased = -1;
|
||||
bank->sectors[i].is_protected = 0;
|
||||
}
|
||||
|
||||
/* We've successfully recorded the stats on this flash bank */
|
||||
cc3220sf_bank->probed = true;
|
||||
|
||||
/* If we fall through to here, then all went well */
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int cc3220sf_auto_probe(struct flash_bank *bank)
|
||||
{
|
||||
struct cc3220sf_bank *cc3220sf_bank = bank->driver_priv;
|
||||
|
||||
int retval = ERROR_OK;
|
||||
|
||||
if (0 != bank->bank_number) {
|
||||
/* Invalid bank number somehow */
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (!cc3220sf_bank->probed)
|
||||
retval = cc3220sf_probe(bank);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int cc3220sf_protect_check(struct flash_bank *bank)
|
||||
{
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int cc3220sf_info(struct flash_bank *bank, char *buf, int buf_size)
|
||||
{
|
||||
int printed;
|
||||
|
||||
printed = snprintf(buf, buf_size, "CC3220SF with 1MB internal flash\n");
|
||||
|
||||
if (printed >= buf_size)
|
||||
return ERROR_BUF_TOO_SMALL;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
struct flash_driver cc3220sf_flash = {
|
||||
.name = "cc3220sf",
|
||||
.flash_bank_command = cc3220sf_flash_bank_command,
|
||||
.erase = cc3220sf_erase,
|
||||
.protect = cc3220sf_protect,
|
||||
.write = cc3220sf_write,
|
||||
.read = default_flash_read,
|
||||
.probe = cc3220sf_probe,
|
||||
.auto_probe = cc3220sf_auto_probe,
|
||||
.erase_check = default_flash_blank_check,
|
||||
.protect_check = cc3220sf_protect_check,
|
||||
.info = cc3220sf_info,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2017 by Texas Instruments, Inc. *
|
||||
* *
|
||||
* 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, see <http://www.gnu.org/licenses/>. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef OPENOCD_FLASH_NOR_CC3220SF_H
|
||||
#define OPENOCD_FLASH_NOR_CC3220SF_H
|
||||
|
||||
/* CC3220SF device types */
|
||||
#define CC3220_NO_TYPE 0 /* Device type not determined yet */
|
||||
#define CC3220_OTHER 1 /* CC3220 variant without flash */
|
||||
#define CC3220SF 2 /* CC3220SF variant with flash */
|
||||
|
||||
/* Flash parameters */
|
||||
#define FLASH_BASE_ADDR 0x01000000
|
||||
#define FLASH_SECTOR_SIZE 2048
|
||||
#define FLASH_NUM_SECTORS 512
|
||||
|
||||
/* CC2200SF flash registers */
|
||||
#define FMA_REGISTER_ADDR 0x400FD000
|
||||
#define FMC_REGISTER_ADDR 0x400FD008
|
||||
#define FMC_DEFAULT_VALUE 0xA4420000
|
||||
#define FMC_ERASE_BIT 0x00000002
|
||||
#define FMC_MERASE_BIT 0x00000004
|
||||
#define FMC_ERASE_VALUE (FMC_DEFAULT_VALUE | FMC_ERASE_BIT)
|
||||
#define FMC_MERASE_VALUE (FMC_DEFAULT_VALUE | FMC_MERASE_BIT)
|
||||
|
||||
/* Flash helper algorithm for CC3220SF */
|
||||
const uint8_t cc3220sf_algo[] = {
|
||||
#include "../../../contrib/loaders/flash/cc3220sf/cc3220sf.inc"
|
||||
};
|
||||
|
||||
#endif /* OPENOCD_FLASH_NOR_CC3220SF_H */
|
||||
@@ -3128,4 +3128,5 @@ struct flash_driver cfi_flash = {
|
||||
.erase_check = default_flash_blank_check,
|
||||
.protect_check = cfi_protect_check,
|
||||
.info = get_cfi_info,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
|
||||
+230
-60
@@ -4,6 +4,7 @@
|
||||
* Copyright (C) 2008 by Spencer Oliver <spen@spen-soft.co.uk> *
|
||||
* Copyright (C) 2009 Zachary T Welch <zw@superlucidity.net> *
|
||||
* Copyright (C) 2010 by Antonio Borneo <borneo.antonio@gmail.com> *
|
||||
* Copyright (C) 2017-2018 Tomas Vanek <vanekt@fbl.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 *
|
||||
@@ -171,6 +172,39 @@ int flash_get_bank_count(void)
|
||||
return i;
|
||||
}
|
||||
|
||||
void default_flash_free_driver_priv(struct flash_bank *bank)
|
||||
{
|
||||
free(bank->driver_priv);
|
||||
bank->driver_priv = NULL;
|
||||
}
|
||||
|
||||
void flash_free_all_banks(void)
|
||||
{
|
||||
struct flash_bank *bank = flash_banks;
|
||||
while (bank) {
|
||||
struct flash_bank *next = bank->next;
|
||||
if (bank->driver->free_driver_priv)
|
||||
bank->driver->free_driver_priv(bank);
|
||||
else
|
||||
LOG_WARNING("Flash driver of %s does not support free_driver_priv()", bank->name);
|
||||
|
||||
/* For 'virtual' flash driver bank->sectors and bank->prot_blocks pointers are copied from
|
||||
* master flash_bank structure. They point to memory locations allocated by master flash driver
|
||||
* so master driver is responsible for releasing them.
|
||||
* Avoid UB caused by double-free memory corruption if flash bank is 'virtual'. */
|
||||
|
||||
if (strcmp(bank->driver->name, "virtual") != 0) {
|
||||
free(bank->sectors);
|
||||
free(bank->prot_blocks);
|
||||
}
|
||||
|
||||
free(bank->name);
|
||||
free(bank);
|
||||
bank = next;
|
||||
}
|
||||
flash_banks = NULL;
|
||||
}
|
||||
|
||||
struct flash_bank *get_flash_bank_by_name_noprobe(const char *name)
|
||||
{
|
||||
unsigned requested = get_flash_name_index(name);
|
||||
@@ -314,36 +348,49 @@ int default_flash_blank_check(struct flash_bank *bank)
|
||||
struct target *target = bank->target;
|
||||
int i;
|
||||
int retval;
|
||||
int fast_check = 0;
|
||||
uint32_t blank;
|
||||
|
||||
if (bank->target->state != TARGET_HALTED) {
|
||||
LOG_ERROR("Target not halted");
|
||||
return ERROR_TARGET_NOT_HALTED;
|
||||
}
|
||||
|
||||
for (i = 0; i < bank->num_sectors; i++) {
|
||||
uint32_t address = bank->base + bank->sectors[i].offset;
|
||||
uint32_t size = bank->sectors[i].size;
|
||||
struct target_memory_check_block *block_array;
|
||||
block_array = malloc(bank->num_sectors * sizeof(struct target_memory_check_block));
|
||||
if (block_array == NULL)
|
||||
return default_flash_mem_blank_check(bank);
|
||||
|
||||
retval = target_blank_check_memory(target, address, size, &blank, bank->erased_value);
|
||||
if (retval != ERROR_OK) {
|
||||
fast_check = 0;
|
||||
for (i = 0; i < bank->num_sectors; i++) {
|
||||
block_array[i].address = bank->base + bank->sectors[i].offset;
|
||||
block_array[i].size = bank->sectors[i].size;
|
||||
block_array[i].result = UINT32_MAX; /* erase state unknown */
|
||||
}
|
||||
|
||||
bool fast_check = true;
|
||||
for (i = 0; i < bank->num_sectors; ) {
|
||||
retval = target_blank_check_memory(target,
|
||||
block_array + i, bank->num_sectors - i,
|
||||
bank->erased_value);
|
||||
if (retval < 1) {
|
||||
/* Run slow fallback if the first run gives no result
|
||||
* otherwise use possibly incomplete results */
|
||||
if (i == 0)
|
||||
fast_check = false;
|
||||
break;
|
||||
}
|
||||
if (blank == bank->erased_value)
|
||||
bank->sectors[i].is_erased = 1;
|
||||
else
|
||||
bank->sectors[i].is_erased = 0;
|
||||
fast_check = 1;
|
||||
i += retval; /* add number of blocks done this round */
|
||||
}
|
||||
|
||||
if (!fast_check) {
|
||||
if (fast_check) {
|
||||
for (i = 0; i < bank->num_sectors; i++)
|
||||
bank->sectors[i].is_erased = block_array[i].result;
|
||||
retval = ERROR_OK;
|
||||
} else {
|
||||
LOG_USER("Running slow fallback erase check - add working memory");
|
||||
return default_flash_mem_blank_check(bank);
|
||||
retval = default_flash_mem_blank_check(bank);
|
||||
}
|
||||
free(block_array);
|
||||
|
||||
return ERROR_OK;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Manipulate given flash region, selecting the bank according to target
|
||||
@@ -399,18 +446,21 @@ static int flash_iterate_address_range_inner(struct target *target,
|
||||
return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
|
||||
}
|
||||
|
||||
addr -= c->base;
|
||||
last_addr -= c->base;
|
||||
if (c->prot_blocks == NULL || c->num_prot_blocks == 0) {
|
||||
/* flash driver does not define protect blocks, use sectors instead */
|
||||
iterate_protect_blocks = false;
|
||||
}
|
||||
|
||||
if (iterate_protect_blocks && c->prot_blocks && c->num_prot_blocks) {
|
||||
if (iterate_protect_blocks) {
|
||||
block_array = c->prot_blocks;
|
||||
num_blocks = c->num_prot_blocks;
|
||||
} else {
|
||||
block_array = c->sectors;
|
||||
num_blocks = c->num_sectors;
|
||||
iterate_protect_blocks = false;
|
||||
}
|
||||
|
||||
addr -= c->base;
|
||||
last_addr -= c->base;
|
||||
|
||||
for (i = 0; i < num_blocks; i++) {
|
||||
struct flash_sector *f = &block_array[i];
|
||||
@@ -559,6 +609,87 @@ static int compare_section(const void *a, const void *b)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get aligned start address of a flash write region
|
||||
*/
|
||||
target_addr_t flash_write_align_start(struct flash_bank *bank, target_addr_t addr)
|
||||
{
|
||||
if (addr < bank->base || addr >= bank->base + bank->size
|
||||
|| bank->write_start_alignment <= 1)
|
||||
return addr;
|
||||
|
||||
if (bank->write_start_alignment == FLASH_WRITE_ALIGN_SECTOR) {
|
||||
uint32_t offset = addr - bank->base;
|
||||
uint32_t aligned = 0;
|
||||
int sect;
|
||||
for (sect = 0; sect < bank->num_sectors; sect++) {
|
||||
if (bank->sectors[sect].offset > offset)
|
||||
break;
|
||||
|
||||
aligned = bank->sectors[sect].offset;
|
||||
}
|
||||
return bank->base + aligned;
|
||||
}
|
||||
|
||||
return addr & ~(bank->write_start_alignment - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get aligned end address of a flash write region
|
||||
*/
|
||||
target_addr_t flash_write_align_end(struct flash_bank *bank, target_addr_t addr)
|
||||
{
|
||||
if (addr < bank->base || addr >= bank->base + bank->size
|
||||
|| bank->write_end_alignment <= 1)
|
||||
return addr;
|
||||
|
||||
if (bank->write_end_alignment == FLASH_WRITE_ALIGN_SECTOR) {
|
||||
uint32_t offset = addr - bank->base;
|
||||
uint32_t aligned = 0;
|
||||
int sect;
|
||||
for (sect = 0; sect < bank->num_sectors; sect++) {
|
||||
aligned = bank->sectors[sect].offset + bank->sectors[sect].size - 1;
|
||||
if (aligned >= offset)
|
||||
break;
|
||||
}
|
||||
return bank->base + aligned;
|
||||
}
|
||||
|
||||
return addr | (bank->write_end_alignment - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if gap between sections is bigger than minimum required to discontinue flash write
|
||||
*/
|
||||
static bool flash_write_check_gap(struct flash_bank *bank,
|
||||
target_addr_t addr1, target_addr_t addr2)
|
||||
{
|
||||
if (bank->minimal_write_gap == FLASH_WRITE_CONTINUOUS
|
||||
|| addr1 < bank->base || addr1 >= bank->base + bank->size
|
||||
|| addr2 < bank->base || addr2 >= bank->base + bank->size)
|
||||
return false;
|
||||
|
||||
if (bank->minimal_write_gap == FLASH_WRITE_GAP_SECTOR) {
|
||||
int sect;
|
||||
uint32_t offset1 = addr1 - bank->base;
|
||||
/* find the sector following the one containing addr1 */
|
||||
for (sect = 0; sect < bank->num_sectors; sect++) {
|
||||
if (bank->sectors[sect].offset > offset1)
|
||||
break;
|
||||
}
|
||||
if (sect >= bank->num_sectors)
|
||||
return false;
|
||||
|
||||
uint32_t offset2 = addr2 - bank->base;
|
||||
return bank->sectors[sect].offset + bank->sectors[sect].size <= offset2;
|
||||
}
|
||||
|
||||
target_addr_t aligned1 = flash_write_align_end(bank, addr1);
|
||||
target_addr_t aligned2 = flash_write_align_start(bank, addr2);
|
||||
return aligned1 + bank->minimal_write_gap < aligned2;
|
||||
}
|
||||
|
||||
|
||||
int flash_write_unlock(struct target *target, struct image *image,
|
||||
uint32_t *written, int erase, bool unlock)
|
||||
{
|
||||
@@ -598,10 +729,10 @@ int flash_write_unlock(struct target *target, struct image *image,
|
||||
|
||||
/* loop until we reach end of the image */
|
||||
while (section < image->num_sections) {
|
||||
uint32_t buffer_size;
|
||||
uint32_t buffer_idx;
|
||||
uint8_t *buffer;
|
||||
int section_last;
|
||||
uint32_t run_address = sections[section]->base_address + section_offset;
|
||||
target_addr_t run_address = sections[section]->base_address + section_offset;
|
||||
uint32_t run_size = sections[section]->size - section_offset;
|
||||
int pad_bytes = 0;
|
||||
|
||||
@@ -617,7 +748,7 @@ int flash_write_unlock(struct target *target, struct image *image,
|
||||
if (retval != ERROR_OK)
|
||||
goto done;
|
||||
if (c == NULL) {
|
||||
LOG_WARNING("no flash bank found for address %" PRIx32, run_address);
|
||||
LOG_WARNING("no flash bank found for address " TARGET_ADDR_FMT, run_address);
|
||||
section++; /* and skip it */
|
||||
section_offset = 0;
|
||||
continue;
|
||||
@@ -635,32 +766,37 @@ int flash_write_unlock(struct target *target, struct image *image,
|
||||
break;
|
||||
}
|
||||
|
||||
/* FIXME This needlessly touches sectors BETWEEN the
|
||||
* sections it's writing. Without auto erase, it just
|
||||
* writes ones. That WILL INVALIDATE data in cases
|
||||
* like Stellaris Tempest chips, corrupting internal
|
||||
* ECC codes; and at least FreeScale suggests issues
|
||||
* with that approach (in HC11 documentation).
|
||||
*
|
||||
* With auto erase enabled, data in those sectors will
|
||||
* be needlessly destroyed; and some of the limited
|
||||
* number of flash erase cycles will be wasted...
|
||||
*
|
||||
* In both cases, the extra writes slow things down.
|
||||
*/
|
||||
|
||||
/* if we have multiple sections within our image,
|
||||
* flash programming could fail due to alignment issues
|
||||
* attempt to rebuild a consecutive buffer for the flash loader */
|
||||
pad_bytes = (sections[section_last + 1]->base_address) - (run_address + run_size);
|
||||
padding[section_last] = pad_bytes;
|
||||
run_size += sections[++section_last]->size;
|
||||
run_size += pad_bytes;
|
||||
target_addr_t run_next_addr = run_address + run_size;
|
||||
target_addr_t next_section_base = sections[section_last + 1]->base_address;
|
||||
if (next_section_base < run_next_addr) {
|
||||
LOG_ERROR("Section at " TARGET_ADDR_FMT
|
||||
" overlaps section ending at " TARGET_ADDR_FMT,
|
||||
next_section_base, run_next_addr);
|
||||
LOG_ERROR("Flash write aborted.");
|
||||
retval = ERROR_FAIL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
pad_bytes = next_section_base - run_next_addr;
|
||||
if (pad_bytes) {
|
||||
if (flash_write_check_gap(c, run_next_addr - 1, next_section_base)) {
|
||||
LOG_INFO("Flash write discontinued at " TARGET_ADDR_FMT
|
||||
", next section at " TARGET_ADDR_FMT,
|
||||
run_next_addr, next_section_base);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pad_bytes > 0)
|
||||
LOG_INFO("Padding image section %d with %d bytes",
|
||||
section_last-1,
|
||||
pad_bytes);
|
||||
LOG_INFO("Padding image section %d at " TARGET_ADDR_FMT
|
||||
" with %d bytes",
|
||||
section_last, run_next_addr, pad_bytes);
|
||||
|
||||
padding[section_last] = pad_bytes;
|
||||
run_size += pad_bytes;
|
||||
run_size += sections[++section_last]->size;
|
||||
}
|
||||
|
||||
if (run_address + run_size - 1 > c->base + c->size - 1) {
|
||||
@@ -673,10 +809,38 @@ int flash_write_unlock(struct target *target, struct image *image,
|
||||
assert(run_size > 0);
|
||||
}
|
||||
|
||||
/* If we're applying any sector automagic, then pad this
|
||||
* (maybe-combined) segment to the end of its last sector.
|
||||
*/
|
||||
if (unlock || erase) {
|
||||
uint32_t padding_at_start = 0;
|
||||
if (c->write_start_alignment || c->write_end_alignment) {
|
||||
/* align write region according to bank requirements */
|
||||
target_addr_t aligned_start = flash_write_align_start(c, run_address);
|
||||
padding_at_start = run_address - aligned_start;
|
||||
if (padding_at_start > 0) {
|
||||
LOG_WARNING("Section start address " TARGET_ADDR_FMT
|
||||
" breaks the required alignment of flash bank %s",
|
||||
run_address, c->name);
|
||||
LOG_WARNING("Padding %d bytes from " TARGET_ADDR_FMT,
|
||||
padding_at_start, aligned_start);
|
||||
|
||||
run_address -= padding_at_start;
|
||||
run_size += padding_at_start;
|
||||
}
|
||||
|
||||
target_addr_t run_end = run_address + run_size - 1;
|
||||
target_addr_t aligned_end = flash_write_align_end(c, run_end);
|
||||
pad_bytes = aligned_end - run_end;
|
||||
if (pad_bytes > 0) {
|
||||
LOG_INFO("Padding image section %d at " TARGET_ADDR_FMT
|
||||
" with %d bytes (bank write end alignment)",
|
||||
section_last, run_end + 1, pad_bytes);
|
||||
|
||||
padding[section_last] += pad_bytes;
|
||||
run_size += pad_bytes;
|
||||
}
|
||||
|
||||
} else if (unlock || erase) {
|
||||
/* If we're applying any sector automagic, then pad this
|
||||
* (maybe-combined) segment to the end of its last sector.
|
||||
*/
|
||||
int sector;
|
||||
uint32_t offset_start = run_address - c->base;
|
||||
uint32_t offset_end = offset_start + run_size;
|
||||
@@ -701,13 +865,17 @@ int flash_write_unlock(struct target *target, struct image *image,
|
||||
retval = ERROR_FAIL;
|
||||
goto done;
|
||||
}
|
||||
buffer_size = 0;
|
||||
|
||||
if (padding_at_start)
|
||||
memset(buffer, c->default_padded_value, padding_at_start);
|
||||
|
||||
buffer_idx = padding_at_start;
|
||||
|
||||
/* read sections to the buffer */
|
||||
while (buffer_size < run_size) {
|
||||
while (buffer_idx < run_size) {
|
||||
size_t size_read;
|
||||
|
||||
size_read = run_size - buffer_size;
|
||||
size_read = run_size - buffer_idx;
|
||||
if (size_read > sections[section]->size - section_offset)
|
||||
size_read = sections[section]->size - section_offset;
|
||||
|
||||
@@ -720,23 +888,25 @@ int flash_write_unlock(struct target *target, struct image *image,
|
||||
int t_section_num = diff / sizeof(struct imagesection);
|
||||
|
||||
LOG_DEBUG("image_read_section: section = %d, t_section_num = %d, "
|
||||
"section_offset = %d, buffer_size = %d, size_read = %d",
|
||||
(int)section, (int)t_section_num, (int)section_offset,
|
||||
(int)buffer_size, (int)size_read);
|
||||
"section_offset = %"PRIu32", buffer_idx = %"PRIu32", size_read = %zu",
|
||||
section, t_section_num, section_offset,
|
||||
buffer_idx, size_read);
|
||||
retval = image_read_section(image, t_section_num, section_offset,
|
||||
size_read, buffer + buffer_size, &size_read);
|
||||
size_read, buffer + buffer_idx, &size_read);
|
||||
if (retval != ERROR_OK || size_read == 0) {
|
||||
free(buffer);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* see if we need to pad the section */
|
||||
while (padding[section]--)
|
||||
(buffer + buffer_size)[size_read++] = c->default_padded_value;
|
||||
|
||||
buffer_size += size_read;
|
||||
buffer_idx += size_read;
|
||||
section_offset += size_read;
|
||||
|
||||
/* see if we need to pad the section */
|
||||
if (padding[section]) {
|
||||
memset(buffer + buffer_idx, c->default_padded_value, padding[section]);
|
||||
buffer_idx += padding[section];
|
||||
}
|
||||
|
||||
if (section_offset >= sections[section]->size) {
|
||||
section++;
|
||||
section_offset = 0;
|
||||
|
||||
+43
-1
@@ -65,6 +65,13 @@ struct flash_sector {
|
||||
int is_protected;
|
||||
};
|
||||
|
||||
/** Special value for write_start_alignment and write_end_alignment field */
|
||||
#define FLASH_WRITE_ALIGN_SECTOR UINT32_MAX
|
||||
|
||||
/** Special values for minimal_write_gap field */
|
||||
#define FLASH_WRITE_CONTINUOUS 0
|
||||
#define FLASH_WRITE_GAP_SECTOR UINT32_MAX
|
||||
|
||||
/**
|
||||
* Provides details of a flash bank, available either on-chip or through
|
||||
* a major interface.
|
||||
@@ -76,7 +83,7 @@ struct flash_sector {
|
||||
* per-bank basis, if required.
|
||||
*/
|
||||
struct flash_bank {
|
||||
const char *name;
|
||||
char *name;
|
||||
|
||||
struct target *target; /**< Target to which this bank belongs. */
|
||||
|
||||
@@ -97,6 +104,18 @@ struct flash_bank {
|
||||
* erased value. Defaults to 0xFF. */
|
||||
uint8_t default_padded_value;
|
||||
|
||||
/** Required alignment of flash write start address.
|
||||
* Default 0, no alignment. Can be any power of two or FLASH_WRITE_ALIGN_SECTOR */
|
||||
uint32_t write_start_alignment;
|
||||
/** Required alignment of flash write end address.
|
||||
* Default 0, no alignment. Can be any power of two or FLASH_WRITE_ALIGN_SECTOR */
|
||||
uint32_t write_end_alignment;
|
||||
/** Minimal gap between sections to discontinue flash write
|
||||
* Default FLASH_WRITE_GAP_SECTOR splits the write if one or more untouched
|
||||
* sectors in between.
|
||||
* Can be size in bytes or FLASH_WRITE_CONTINUOUS */
|
||||
uint32_t minimal_write_gap;
|
||||
|
||||
/**
|
||||
* The number of sectors on this chip. This value will
|
||||
* be set intially to 0, and the flash driver must set this to
|
||||
@@ -135,6 +154,22 @@ int flash_erase_address_range(struct target *target,
|
||||
int flash_unlock_address_range(struct target *target, uint32_t addr,
|
||||
uint32_t length);
|
||||
|
||||
/**
|
||||
* Align start address of a flash write region according to bank requirements.
|
||||
* @param bank Pointer to bank descriptor structure
|
||||
* @param addr Address to align
|
||||
* @returns Aligned address
|
||||
*/
|
||||
target_addr_t flash_write_align_start(struct flash_bank *bank, target_addr_t addr);
|
||||
/**
|
||||
* Align end address of a flash write region according to bank requirements.
|
||||
* Note: Use address of the last byte to write, not the next after the region.
|
||||
* @param bank Pointer to bank descriptor structure
|
||||
* @param addr Address to align (address of the last byte to write)
|
||||
* @returns Aligned address (address of the last byte of padded region)
|
||||
*/
|
||||
target_addr_t flash_write_align_end(struct flash_bank *bank, target_addr_t addr);
|
||||
|
||||
/**
|
||||
* Writes @a image into the @a target flash. The @a written parameter
|
||||
* will contain the
|
||||
@@ -153,8 +188,15 @@ int flash_write(struct target *target,
|
||||
* This routine must be called when the system may modify the status.
|
||||
*/
|
||||
void flash_set_dirty(void);
|
||||
|
||||
/** @returns The number of flash banks currently defined. */
|
||||
int flash_get_bank_count(void);
|
||||
|
||||
/** Deallocates bank->driver_priv */
|
||||
void default_flash_free_driver_priv(struct flash_bank *bank);
|
||||
|
||||
/** Deallocates all flash banks */
|
||||
void flash_free_all_banks(void);
|
||||
/**
|
||||
* Provides default read implementation for flash memory.
|
||||
* @param bank The bank to read.
|
||||
|
||||
@@ -209,6 +209,14 @@ struct flash_driver {
|
||||
* @returns ERROR_OK if successful; otherwise, an error code.
|
||||
*/
|
||||
int (*auto_probe)(struct flash_bank *bank);
|
||||
|
||||
/**
|
||||
* Deallocates private driver structures.
|
||||
* Use default_flash_free_driver_priv() to simply free(bank->driver_priv)
|
||||
*
|
||||
* @param bank - the bank being destroyed
|
||||
*/
|
||||
void (*free_driver_priv)(struct flash_bank *bank);
|
||||
};
|
||||
|
||||
#define FLASH_BANK_COMMAND_HANDLER(name) \
|
||||
|
||||
@@ -31,6 +31,9 @@ extern struct flash_driver at91samd_flash;
|
||||
extern struct flash_driver ath79_flash;
|
||||
extern struct flash_driver atsamv_flash;
|
||||
extern struct flash_driver avr_flash;
|
||||
extern struct flash_driver bluenrgx_flash;
|
||||
extern struct flash_driver cc3220sf_flash;
|
||||
extern struct flash_driver cc26xx_flash;
|
||||
extern struct flash_driver cfi_flash;
|
||||
extern struct flash_driver dsp5680xx_flash;
|
||||
extern struct flash_driver efm32_flash;
|
||||
@@ -48,6 +51,7 @@ extern struct flash_driver lpc2900_flash;
|
||||
extern struct flash_driver lpcspifi_flash;
|
||||
extern struct flash_driver mdr_flash;
|
||||
extern struct flash_driver mrvlqspi_flash;
|
||||
extern struct flash_driver msp432_flash;
|
||||
extern struct flash_driver niietcm4_flash;
|
||||
extern struct flash_driver nrf5_flash;
|
||||
extern struct flash_driver nrf51_flash;
|
||||
@@ -55,6 +59,10 @@ extern struct flash_driver numicro_flash;
|
||||
extern struct flash_driver ocl_flash;
|
||||
extern struct flash_driver pic32mx_flash;
|
||||
extern struct flash_driver psoc4_flash;
|
||||
extern struct flash_driver psoc5lp_flash;
|
||||
extern struct flash_driver psoc5lp_eeprom_flash;
|
||||
extern struct flash_driver psoc5lp_nvl_flash;
|
||||
extern struct flash_driver psoc6_flash;
|
||||
extern struct flash_driver sim3x_flash;
|
||||
extern struct flash_driver stellaris_flash;
|
||||
extern struct flash_driver stm32f1x_flash;
|
||||
@@ -88,6 +96,9 @@ static struct flash_driver *flash_drivers[] = {
|
||||
&ath79_flash,
|
||||
&atsamv_flash,
|
||||
&avr_flash,
|
||||
&bluenrgx_flash,
|
||||
&cc3220sf_flash,
|
||||
&cc26xx_flash,
|
||||
&cfi_flash,
|
||||
&dsp5680xx_flash,
|
||||
&efm32_flash,
|
||||
@@ -105,6 +116,7 @@ static struct flash_driver *flash_drivers[] = {
|
||||
&lpcspifi_flash,
|
||||
&mdr_flash,
|
||||
&mrvlqspi_flash,
|
||||
&msp432_flash,
|
||||
&niietcm4_flash,
|
||||
&nrf5_flash,
|
||||
&nrf51_flash,
|
||||
@@ -112,6 +124,10 @@ static struct flash_driver *flash_drivers[] = {
|
||||
&ocl_flash,
|
||||
&pic32mx_flash,
|
||||
&psoc4_flash,
|
||||
&psoc5lp_flash,
|
||||
&psoc5lp_eeprom_flash,
|
||||
&psoc5lp_nvl_flash,
|
||||
&psoc6_flash,
|
||||
&sim3x_flash,
|
||||
&stellaris_flash,
|
||||
&stm32f1x_flash,
|
||||
|
||||
+146
-125
@@ -38,19 +38,8 @@
|
||||
#include <target/armv7m.h>
|
||||
#include <target/cortex_m.h>
|
||||
|
||||
/* keep family IDs in decimal */
|
||||
#define EFM_FAMILY_ID_GECKO 71
|
||||
#define EFM_FAMILY_ID_GIANT_GECKO 72
|
||||
#define EFM_FAMILY_ID_TINY_GECKO 73
|
||||
#define EFM_FAMILY_ID_LEOPARD_GECKO 74
|
||||
#define EFM_FAMILY_ID_WONDER_GECKO 75
|
||||
#define EFM_FAMILY_ID_ZERO_GECKO 76
|
||||
#define EFM_FAMILY_ID_HAPPY_GECKO 77
|
||||
#define EZR_FAMILY_ID_WONDER_GECKO 120
|
||||
#define EZR_FAMILY_ID_LEOPARD_GECKO 121
|
||||
#define EZR_FAMILY_ID_HAPPY_GECKO 122
|
||||
#define EFR_FAMILY_ID_MIGHTY_GECKO 16
|
||||
#define EFR_FAMILY_ID_BLUE_GECKO 20
|
||||
|
||||
#define EFM32_FLASH_ERASE_TMO 100
|
||||
#define EFM32_FLASH_WDATAREADY_TMO 100
|
||||
@@ -65,7 +54,7 @@
|
||||
#define EFM32_MSC_LOCK_BITS (EFM32_MSC_INFO_BASE+0x4000)
|
||||
#define EFM32_MSC_DEV_INFO (EFM32_MSC_INFO_BASE+0x8000)
|
||||
|
||||
/* PAGE_SIZE is only present in Leopard, Giant and Wonder Gecko MCUs */
|
||||
/* PAGE_SIZE is not present in Zero, Happy and the original Gecko MCU */
|
||||
#define EFM32_MSC_DI_PAGE_SIZE (EFM32_MSC_DEV_INFO+0x1e7)
|
||||
#define EFM32_MSC_DI_FLASH_SZ (EFM32_MSC_DEV_INFO+0x1f8)
|
||||
#define EFM32_MSC_DI_RAM_SZ (EFM32_MSC_DEV_INFO+0x1fa)
|
||||
@@ -74,7 +63,7 @@
|
||||
#define EFM32_MSC_DI_PROD_REV (EFM32_MSC_DEV_INFO+0x1ff)
|
||||
|
||||
#define EFM32_MSC_REGBASE 0x400c0000
|
||||
#define EFR32_MSC_REGBASE 0x400e0000
|
||||
#define EFM32_MSC_REGBASE_SERIES1 0x400e0000
|
||||
#define EFM32_MSC_REG_WRITECTRL 0x008
|
||||
#define EFM32_MSC_WRITECTRL_WREN_MASK 0x1
|
||||
#define EFM32_MSC_REG_WRITECMD 0x00c
|
||||
@@ -91,9 +80,24 @@
|
||||
#define EFM32_MSC_STATUS_WORDTIMEOUT_MASK 0x10
|
||||
#define EFM32_MSC_STATUS_ERASEABORTED_MASK 0x20
|
||||
#define EFM32_MSC_REG_LOCK 0x03c
|
||||
#define EFR32_MSC_REG_LOCK 0x040
|
||||
#define EFM32_MSC_REG_LOCK_SERIES1 0x040
|
||||
#define EFM32_MSC_LOCK_LOCKKEY 0x1b71
|
||||
|
||||
struct efm32_family_data {
|
||||
int family_id;
|
||||
const char *name;
|
||||
|
||||
/* EFM32 series (EFM32LG995F is the "old" series 0, while EFR32MG12P132
|
||||
is the "new" series 1). Determines location of MSC registers. */
|
||||
int series;
|
||||
|
||||
/* Page size in bytes, or 0 to read from EFM32_MSC_DI_PAGE_SIZE */
|
||||
int page_size;
|
||||
|
||||
/* MSC register base address, or 0 to use default */
|
||||
uint32_t msc_regbase;
|
||||
};
|
||||
|
||||
struct efm32x_flash_bank {
|
||||
int probed;
|
||||
uint32_t lb_page[LOCKBITS_PAGE_SZ/4];
|
||||
@@ -102,6 +106,7 @@ struct efm32x_flash_bank {
|
||||
};
|
||||
|
||||
struct efm32_info {
|
||||
const struct efm32_family_data *family_data;
|
||||
uint16_t flash_sz_kib;
|
||||
uint16_t ram_sz_kib;
|
||||
uint16_t part_num;
|
||||
@@ -110,6 +115,64 @@ struct efm32_info {
|
||||
uint16_t page_size;
|
||||
};
|
||||
|
||||
static const struct efm32_family_data efm32_families[] = {
|
||||
{ 16, "EFR32MG1P Mighty", .series = 1 },
|
||||
{ 17, "EFR32MG1B Mighty", .series = 1 },
|
||||
{ 18, "EFR32MG1V Mighty", .series = 1 },
|
||||
{ 19, "EFR32MG1P Blue", .series = 1 },
|
||||
{ 20, "EFR32MG1B Blue", .series = 1 },
|
||||
{ 21, "EFR32MG1V Blue", .series = 1 },
|
||||
{ 25, "EFR32FG1P Flex", .series = 1 },
|
||||
{ 26, "EFR32FG1B Flex", .series = 1 },
|
||||
{ 27, "EFR32FG1V Flex", .series = 1 },
|
||||
{ 28, "EFR32MG2P Mighty", .series = 1 },
|
||||
{ 29, "EFR32MG2B Mighty", .series = 1 },
|
||||
{ 30, "EFR32MG2V Mighty", .series = 1 },
|
||||
{ 31, "EFR32BG12P Blue", .series = 1 },
|
||||
{ 32, "EFR32BG12B Blue", .series = 1 },
|
||||
{ 33, "EFR32BG12V Blue", .series = 1 },
|
||||
{ 37, "EFR32FG12P Flex", .series = 1 },
|
||||
{ 38, "EFR32FG12B Flex", .series = 1 },
|
||||
{ 39, "EFR32FG12V Flex", .series = 1 },
|
||||
{ 40, "EFR32MG13P Mighty", .series = 1 },
|
||||
{ 41, "EFR32MG13B Mighty", .series = 1 },
|
||||
{ 42, "EFR32MG13V Mighty", .series = 1 },
|
||||
{ 43, "EFR32BG13P Blue", .series = 1 },
|
||||
{ 44, "EFR32BG13B Blue", .series = 1 },
|
||||
{ 45, "EFR32BG13V Blue", .series = 1 },
|
||||
{ 49, "EFR32FG13P Flex", .series = 1 },
|
||||
{ 50, "EFR32FG13B Flex", .series = 1 },
|
||||
{ 51, "EFR32FG13V Flex", .series = 1 },
|
||||
{ 52, "EFR32MG14P Mighty", .series = 1 },
|
||||
{ 53, "EFR32MG14B Mighty", .series = 1 },
|
||||
{ 54, "EFR32MG14V Mighty", .series = 1 },
|
||||
{ 55, "EFR32BG14P Blue", .series = 1 },
|
||||
{ 56, "EFR32BG14B Blue", .series = 1 },
|
||||
{ 57, "EFR32BG14V Blue", .series = 1 },
|
||||
{ 61, "EFR32FG14P Flex", .series = 1 },
|
||||
{ 62, "EFR32FG14B Flex", .series = 1 },
|
||||
{ 63, "EFR32FG14V Flex", .series = 1 },
|
||||
{ 71, "EFM32G", .series = 0, .page_size = 512 },
|
||||
{ 72, "EFM32GG Giant", .series = 0 },
|
||||
{ 73, "EFM32TG Tiny", .series = 0, .page_size = 512 },
|
||||
{ 74, "EFM32LG Leopard", .series = 0 },
|
||||
{ 75, "EFM32WG Wonder", .series = 0 },
|
||||
{ 76, "EFM32ZG Zero", .series = 0, .page_size = 1024 },
|
||||
{ 77, "EFM32HG Happy", .series = 0, .page_size = 1024 },
|
||||
{ 81, "EFM32PG1B Pearl", .series = 1 },
|
||||
{ 83, "EFM32JG1B Jade", .series = 1 },
|
||||
{ 85, "EFM32PG12B Pearl", .series = 1 },
|
||||
{ 87, "EFM32JG12B Jade", .series = 1 },
|
||||
{ 89, "EFM32PG13B Pearl", .series = 1 },
|
||||
{ 91, "EFM32JG13B Jade", .series = 1 },
|
||||
{ 100, "EFM32GG11B Giant", .series = 1, .msc_regbase = 0x40000000 },
|
||||
{ 103, "EFM32TG11B Tiny", .series = 1 },
|
||||
{ 120, "EZR32WG Wonder", .series = 0 },
|
||||
{ 121, "EZR32LG Leopard", .series = 0 },
|
||||
{ 122, "EZR32HG Happy", .series = 0, .page_size = 1024 },
|
||||
};
|
||||
|
||||
|
||||
static int efm32x_write(struct flash_bank *bank, const uint8_t *buffer,
|
||||
uint32_t offset, uint32_t count);
|
||||
|
||||
@@ -200,51 +263,33 @@ static int efm32x_read_info(struct flash_bank *bank,
|
||||
if (ERROR_OK != ret)
|
||||
return ret;
|
||||
|
||||
if (EFR_FAMILY_ID_BLUE_GECKO == efm32_info->part_family ||
|
||||
EFR_FAMILY_ID_MIGHTY_GECKO == efm32_info->part_family) {
|
||||
efm32x_info->reg_base = EFR32_MSC_REGBASE;
|
||||
efm32x_info->reg_lock = EFR32_MSC_REG_LOCK;
|
||||
} else {
|
||||
efm32x_info->reg_base = EFM32_MSC_REGBASE;
|
||||
efm32x_info->reg_lock = EFM32_MSC_REG_LOCK;
|
||||
for (size_t i = 0; i < ARRAY_SIZE(efm32_families); i++) {
|
||||
if (efm32_families[i].family_id == efm32_info->part_family)
|
||||
efm32_info->family_data = &efm32_families[i];
|
||||
}
|
||||
|
||||
if (EFM_FAMILY_ID_GECKO == efm32_info->part_family ||
|
||||
EFM_FAMILY_ID_TINY_GECKO == efm32_info->part_family)
|
||||
efm32_info->page_size = 512;
|
||||
else if (EFM_FAMILY_ID_ZERO_GECKO == efm32_info->part_family ||
|
||||
EFM_FAMILY_ID_HAPPY_GECKO == efm32_info->part_family ||
|
||||
EZR_FAMILY_ID_HAPPY_GECKO == efm32_info->part_family)
|
||||
efm32_info->page_size = 1024;
|
||||
else if (EFM_FAMILY_ID_GIANT_GECKO == efm32_info->part_family ||
|
||||
EFM_FAMILY_ID_LEOPARD_GECKO == efm32_info->part_family) {
|
||||
if (efm32_info->prod_rev >= 18) {
|
||||
uint8_t pg_size = 0;
|
||||
ret = target_read_u8(bank->target, EFM32_MSC_DI_PAGE_SIZE,
|
||||
&pg_size);
|
||||
if (ERROR_OK != ret)
|
||||
return ret;
|
||||
if (efm32_info->family_data == NULL) {
|
||||
LOG_ERROR("Unknown MCU family %d", efm32_info->part_family);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
efm32_info->page_size = (1 << ((pg_size+10) & 0xff));
|
||||
} else {
|
||||
/* EFM32 GG/LG errata: MEM_INFO_PAGE_SIZE is invalid
|
||||
for MCUs with PROD_REV < 18 */
|
||||
if (efm32_info->flash_sz_kib < 512)
|
||||
efm32_info->page_size = 2048;
|
||||
else
|
||||
efm32_info->page_size = 4096;
|
||||
}
|
||||
switch (efm32_info->family_data->series) {
|
||||
case 0:
|
||||
efm32x_info->reg_base = EFM32_MSC_REGBASE;
|
||||
efm32x_info->reg_lock = EFM32_MSC_REG_LOCK;
|
||||
break;
|
||||
case 1:
|
||||
efm32x_info->reg_base = EFM32_MSC_REGBASE_SERIES1;
|
||||
efm32x_info->reg_lock = EFM32_MSC_REG_LOCK_SERIES1;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((2048 != efm32_info->page_size) &&
|
||||
(4096 != efm32_info->page_size)) {
|
||||
LOG_ERROR("Invalid page size %u", efm32_info->page_size);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
} else if (EFM_FAMILY_ID_WONDER_GECKO == efm32_info->part_family ||
|
||||
EZR_FAMILY_ID_WONDER_GECKO == efm32_info->part_family ||
|
||||
EZR_FAMILY_ID_LEOPARD_GECKO == efm32_info->part_family ||
|
||||
EFR_FAMILY_ID_BLUE_GECKO == efm32_info->part_family ||
|
||||
EFR_FAMILY_ID_MIGHTY_GECKO == efm32_info->part_family) {
|
||||
if (efm32_info->family_data->msc_regbase != 0)
|
||||
efm32x_info->reg_base = efm32_info->family_data->msc_regbase;
|
||||
|
||||
if (efm32_info->family_data->page_size != 0) {
|
||||
efm32_info->page_size = efm32_info->family_data->page_size;
|
||||
} else {
|
||||
uint8_t pg_size = 0;
|
||||
ret = target_read_u8(bank->target, EFM32_MSC_DI_PAGE_SIZE,
|
||||
&pg_size);
|
||||
@@ -252,13 +297,25 @@ static int efm32x_read_info(struct flash_bank *bank,
|
||||
return ret;
|
||||
|
||||
efm32_info->page_size = (1 << ((pg_size+10) & 0xff));
|
||||
if (2048 != efm32_info->page_size) {
|
||||
|
||||
if (efm32_info->part_family == EFM_FAMILY_ID_GIANT_GECKO ||
|
||||
efm32_info->part_family == EFM_FAMILY_ID_LEOPARD_GECKO) {
|
||||
/* Giant or Leopard Gecko */
|
||||
if (efm32_info->prod_rev < 18) {
|
||||
/* EFM32 GG/LG errata: MEM_INFO_PAGE_SIZE is invalid
|
||||
for MCUs with PROD_REV < 18 */
|
||||
if (efm32_info->flash_sz_kib < 512)
|
||||
efm32_info->page_size = 2048;
|
||||
else
|
||||
efm32_info->page_size = 4096;
|
||||
}
|
||||
}
|
||||
|
||||
if ((efm32_info->page_size != 2048) &&
|
||||
(efm32_info->page_size != 4096)) {
|
||||
LOG_ERROR("Invalid page size %u", efm32_info->page_size);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
} else {
|
||||
LOG_ERROR("Unknown MCU family %d", efm32_info->part_family);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
@@ -270,71 +327,10 @@ static int efm32x_read_info(struct flash_bank *bank,
|
||||
static int efm32x_decode_info(struct efm32_info *info, char *buf, int buf_size)
|
||||
{
|
||||
int printed = 0;
|
||||
printed = snprintf(buf, buf_size, "%s Gecko, rev %d",
|
||||
info->family_data->name, info->prod_rev);
|
||||
|
||||
switch (info->part_family) {
|
||||
case EZR_FAMILY_ID_WONDER_GECKO:
|
||||
case EZR_FAMILY_ID_LEOPARD_GECKO:
|
||||
case EZR_FAMILY_ID_HAPPY_GECKO:
|
||||
printed = snprintf(buf, buf_size, "EZR32 ");
|
||||
break;
|
||||
case EFR_FAMILY_ID_MIGHTY_GECKO:
|
||||
case EFR_FAMILY_ID_BLUE_GECKO:
|
||||
printed = snprintf(buf, buf_size, "EFR32 ");
|
||||
break;
|
||||
default:
|
||||
printed = snprintf(buf, buf_size, "EFM32 ");
|
||||
}
|
||||
|
||||
buf += printed;
|
||||
buf_size -= printed;
|
||||
|
||||
if (0 >= buf_size)
|
||||
return ERROR_BUF_TOO_SMALL;
|
||||
|
||||
switch (info->part_family) {
|
||||
case EFM_FAMILY_ID_GECKO:
|
||||
printed = snprintf(buf, buf_size, "Gecko");
|
||||
break;
|
||||
case EFM_FAMILY_ID_GIANT_GECKO:
|
||||
printed = snprintf(buf, buf_size, "Giant Gecko");
|
||||
break;
|
||||
case EFM_FAMILY_ID_TINY_GECKO:
|
||||
printed = snprintf(buf, buf_size, "Tiny Gecko");
|
||||
break;
|
||||
case EFM_FAMILY_ID_LEOPARD_GECKO:
|
||||
case EZR_FAMILY_ID_LEOPARD_GECKO:
|
||||
printed = snprintf(buf, buf_size, "Leopard Gecko");
|
||||
break;
|
||||
case EFM_FAMILY_ID_WONDER_GECKO:
|
||||
case EZR_FAMILY_ID_WONDER_GECKO:
|
||||
printed = snprintf(buf, buf_size, "Wonder Gecko");
|
||||
break;
|
||||
case EFM_FAMILY_ID_ZERO_GECKO:
|
||||
printed = snprintf(buf, buf_size, "Zero Gecko");
|
||||
break;
|
||||
case EFM_FAMILY_ID_HAPPY_GECKO:
|
||||
case EZR_FAMILY_ID_HAPPY_GECKO:
|
||||
printed = snprintf(buf, buf_size, "Happy Gecko");
|
||||
break;
|
||||
case EFR_FAMILY_ID_BLUE_GECKO:
|
||||
printed = snprintf(buf, buf_size, "Blue Gecko");
|
||||
break;
|
||||
case EFR_FAMILY_ID_MIGHTY_GECKO:
|
||||
printed = snprintf(buf, buf_size, "Mighty Gecko");
|
||||
break;
|
||||
}
|
||||
|
||||
buf += printed;
|
||||
buf_size -= printed;
|
||||
|
||||
if (0 >= buf_size)
|
||||
return ERROR_BUF_TOO_SMALL;
|
||||
|
||||
printed = snprintf(buf, buf_size, " - Rev: %d", info->prod_rev);
|
||||
buf += printed;
|
||||
buf_size -= printed;
|
||||
|
||||
if (0 >= buf_size)
|
||||
if (printed >= buf_size)
|
||||
return ERROR_BUF_TOO_SMALL;
|
||||
|
||||
return ERROR_OK;
|
||||
@@ -522,7 +518,7 @@ static int efm32x_read_lock_data(struct flash_bank *bank)
|
||||
}
|
||||
}
|
||||
|
||||
/* also, read ULW, DLW and MLW */
|
||||
/* also, read ULW, DLW, MLW, ALW and CLW words */
|
||||
|
||||
/* ULW, word 126 */
|
||||
ptr = efm32x_info->lb_page + 126;
|
||||
@@ -540,7 +536,7 @@ static int efm32x_read_lock_data(struct flash_bank *bank)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* MLW, word 125, present in GG and LG */
|
||||
/* MLW, word 125, present in GG, LG, PG, JG, EFR32 */
|
||||
ptr = efm32x_info->lb_page + 125;
|
||||
ret = target_read_u32(target, EFM32_MSC_LOCK_BITS+125*4, ptr);
|
||||
if (ERROR_OK != ret) {
|
||||
@@ -548,6 +544,30 @@ static int efm32x_read_lock_data(struct flash_bank *bank)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* ALW, word 124, present in GG, LG, PG, JG, EFR32 */
|
||||
ptr = efm32x_info->lb_page + 124;
|
||||
ret = target_read_u32(target, EFM32_MSC_LOCK_BITS+124*4, ptr);
|
||||
if (ERROR_OK != ret) {
|
||||
LOG_ERROR("Failed to read ALW");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* CLW1, word 123, present in EFR32 */
|
||||
ptr = efm32x_info->lb_page + 123;
|
||||
ret = target_read_u32(target, EFM32_MSC_LOCK_BITS+123*4, ptr);
|
||||
if (ERROR_OK != ret) {
|
||||
LOG_ERROR("Failed to read CLW1");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* CLW0, word 122, present in GG, LG, PG, JG, EFR32 */
|
||||
ptr = efm32x_info->lb_page + 122;
|
||||
ret = target_read_u32(target, EFM32_MSC_LOCK_BITS+122*4, ptr);
|
||||
if (ERROR_OK != ret) {
|
||||
LOG_ERROR("Failed to read CLW0");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
@@ -1113,4 +1133,5 @@ struct flash_driver efm32_flash = {
|
||||
.erase_check = default_flash_blank_check,
|
||||
.protect_check = efm32x_protect_check,
|
||||
.info = get_efm32x_info,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
|
||||
@@ -941,4 +941,5 @@ struct flash_driver em357_flash = {
|
||||
.auto_probe = em357_auto_probe,
|
||||
.erase_check = default_flash_blank_check,
|
||||
.protect_check = em357_protect_check,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
|
||||
@@ -136,5 +136,6 @@ struct flash_driver faux_flash = {
|
||||
.auto_probe = faux_probe,
|
||||
.erase_check = default_flash_blank_check,
|
||||
.protect_check = faux_protect_check,
|
||||
.info = faux_info
|
||||
.info = faux_info,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
|
||||
+49
-30
@@ -436,6 +436,12 @@ static int slow_fespi_write_buffer(struct flash_bank *bank,
|
||||
uint32_t ctrl_base = fespi_info->ctrl_base;
|
||||
uint32_t ii;
|
||||
|
||||
if (offset & 0xFF000000) {
|
||||
LOG_ERROR("FESPI interface does not support greater than 3B addressing, can't write to offset 0x%x",
|
||||
offset);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
/* TODO!!! assert that len < page size */
|
||||
|
||||
fespi_tx(bank, SPIFLASH_WRITE_ENABLE);
|
||||
@@ -607,11 +613,11 @@ struct algorithm_steps {
|
||||
uint8_t **steps;
|
||||
};
|
||||
|
||||
static struct algorithm_steps *as_new(unsigned size)
|
||||
static struct algorithm_steps *as_new(void)
|
||||
{
|
||||
struct algorithm_steps *as = calloc(1, sizeof(struct algorithm_steps));
|
||||
as->size = size;
|
||||
as->steps = calloc(size, sizeof(as->steps[0]));
|
||||
as->size = 8;
|
||||
as->steps = malloc(as->size * sizeof(as->steps[0]));
|
||||
return as;
|
||||
}
|
||||
|
||||
@@ -701,17 +707,27 @@ static unsigned as_compile(struct algorithm_steps *as, uint8_t *target,
|
||||
return offset;
|
||||
}
|
||||
|
||||
static void as_add_step(struct algorithm_steps *as, uint8_t *step)
|
||||
{
|
||||
if (as->used == as->size) {
|
||||
as->size *= 2;
|
||||
as->steps = realloc(as->steps, sizeof(as->steps[0]) * as->size);
|
||||
LOG_DEBUG("Increased size to 0x%x", as->size);
|
||||
}
|
||||
as->steps[as->used] = step;
|
||||
as->used++;
|
||||
}
|
||||
|
||||
static void as_add_tx(struct algorithm_steps *as, unsigned count, const uint8_t *data)
|
||||
{
|
||||
LOG_DEBUG("count=%d", count);
|
||||
while (count > 0) {
|
||||
unsigned step_count = MIN(count, 255);
|
||||
assert(as->used < as->size);
|
||||
as->steps[as->used] = malloc(step_count + 2);
|
||||
as->steps[as->used][0] = STEP_TX;
|
||||
as->steps[as->used][1] = step_count;
|
||||
memcpy(as->steps[as->used] + 2, data, step_count);
|
||||
as->used++;
|
||||
uint8_t *step = malloc(step_count + 2);
|
||||
step[0] = STEP_TX;
|
||||
step[1] = step_count;
|
||||
memcpy(step + 2, data, step_count);
|
||||
as_add_step(as, step);
|
||||
data += step_count;
|
||||
count -= step_count;
|
||||
}
|
||||
@@ -726,43 +742,45 @@ static void as_add_tx1(struct algorithm_steps *as, uint8_t byte)
|
||||
|
||||
static void as_add_write_reg(struct algorithm_steps *as, uint8_t offset, uint8_t data)
|
||||
{
|
||||
assert(as->used < as->size);
|
||||
as->steps[as->used] = malloc(3);
|
||||
as->steps[as->used][0] = STEP_WRITE_REG;
|
||||
as->steps[as->used][1] = offset;
|
||||
as->steps[as->used][2] = data;
|
||||
as->used++;
|
||||
uint8_t *step = malloc(3);
|
||||
step[0] = STEP_WRITE_REG;
|
||||
step[1] = offset;
|
||||
step[2] = data;
|
||||
as_add_step(as, step);
|
||||
}
|
||||
|
||||
static void as_add_txwm_wait(struct algorithm_steps *as)
|
||||
{
|
||||
assert(as->used < as->size);
|
||||
as->steps[as->used] = malloc(1);
|
||||
as->steps[as->used][0] = STEP_TXWM_WAIT;
|
||||
as->used++;
|
||||
uint8_t *step = malloc(1);
|
||||
step[0] = STEP_TXWM_WAIT;
|
||||
as_add_step(as, step);
|
||||
}
|
||||
|
||||
static void as_add_wip_wait(struct algorithm_steps *as)
|
||||
{
|
||||
assert(as->used < as->size);
|
||||
as->steps[as->used] = malloc(1);
|
||||
as->steps[as->used][0] = STEP_WIP_WAIT;
|
||||
as->used++;
|
||||
uint8_t *step = malloc(1);
|
||||
step[0] = STEP_WIP_WAIT;
|
||||
as_add_step(as, step);
|
||||
}
|
||||
|
||||
static void as_add_set_dir(struct algorithm_steps *as, bool dir)
|
||||
{
|
||||
assert(as->used < as->size);
|
||||
as->steps[as->used] = malloc(2);
|
||||
as->steps[as->used][0] = STEP_SET_DIR;
|
||||
as->steps[as->used][1] = FESPI_FMT_DIR(dir);
|
||||
as->used++;
|
||||
uint8_t *step = malloc(2);
|
||||
step[0] = STEP_SET_DIR;
|
||||
step[1] = FESPI_FMT_DIR(dir);
|
||||
as_add_step(as, step);
|
||||
}
|
||||
|
||||
/* This should write something less than or equal to a page.*/
|
||||
static int steps_add_buffer_write(struct algorithm_steps *as,
|
||||
const uint8_t *buffer, uint32_t chip_offset, uint32_t len)
|
||||
{
|
||||
if (chip_offset & 0xFF000000) {
|
||||
LOG_ERROR("FESPI interface does not support greater than 3B addressing, can't write to offset 0x%x",
|
||||
chip_offset);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
as_add_tx1(as, SPIFLASH_WRITE_ENABLE);
|
||||
as_add_txwm_wait(as);
|
||||
as_add_write_reg(as, FESPI_REG_CSMODE, FESPI_CSMODE_HOLD);
|
||||
@@ -910,7 +928,7 @@ static int fespi_write(struct flash_bank *bank, const uint8_t *buffer,
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
struct algorithm_steps *as = as_new(count / 4);
|
||||
struct algorithm_steps *as = as_new();
|
||||
|
||||
/* unaligned buffer head */
|
||||
if (count > 0 && (offset & 3) != 0) {
|
||||
@@ -1161,5 +1179,6 @@ struct flash_driver fespi_flash = {
|
||||
.auto_probe = fespi_auto_probe,
|
||||
.erase_check = default_flash_blank_check,
|
||||
.protect_check = fespi_protect_check,
|
||||
.info = get_fespi_info
|
||||
.info = get_fespi_info,
|
||||
.free_driver_priv = default_flash_free_driver_priv
|
||||
};
|
||||
|
||||
@@ -997,4 +997,5 @@ struct flash_driver fm3_flash = {
|
||||
.probe = fm3_probe,
|
||||
.auto_probe = fm3_auto_probe,
|
||||
.erase_check = default_flash_blank_check,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
|
||||
@@ -719,4 +719,5 @@ struct flash_driver fm4_flash = {
|
||||
.erase = fm4_flash_erase,
|
||||
.erase_check = default_flash_blank_check,
|
||||
.write = fm4_flash_write,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
|
||||
@@ -432,5 +432,6 @@ struct flash_driver jtagspi_flash = {
|
||||
.auto_probe = jtagspi_probe,
|
||||
.erase_check = default_flash_blank_check,
|
||||
.protect_check = jtagspi_protect_check,
|
||||
.info = jtagspi_info
|
||||
.info = jtagspi_info,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
|
||||
+24
-5
@@ -915,13 +915,29 @@ FLASH_BANK_COMMAND_HANDLER(kinetis_flash_bank_command)
|
||||
}
|
||||
|
||||
|
||||
static void kinetis_free_driver_priv(struct flash_bank *bank)
|
||||
{
|
||||
struct kinetis_flash_bank *k_bank = bank->driver_priv;
|
||||
if (k_bank == NULL)
|
||||
return;
|
||||
|
||||
struct kinetis_chip *k_chip = k_bank->k_chip;
|
||||
if (k_chip == NULL)
|
||||
return;
|
||||
|
||||
k_chip->num_banks--;
|
||||
if (k_chip->num_banks == 0)
|
||||
free(k_chip);
|
||||
}
|
||||
|
||||
|
||||
static int kinetis_create_missing_banks(struct kinetis_chip *k_chip)
|
||||
{
|
||||
unsigned bank_idx;
|
||||
unsigned num_blocks;
|
||||
struct kinetis_flash_bank *k_bank;
|
||||
struct flash_bank *bank;
|
||||
char base_name[80], name[80], num[4];
|
||||
char base_name[69], name[80], num[4];
|
||||
char *class, *p;
|
||||
|
||||
num_blocks = k_chip->num_pflash_blocks + k_chip->num_nvm_blocks;
|
||||
@@ -932,19 +948,21 @@ static int kinetis_create_missing_banks(struct kinetis_chip *k_chip)
|
||||
|
||||
bank = k_chip->banks[0].bank;
|
||||
if (bank && bank->name) {
|
||||
strncpy(base_name, bank->name, sizeof(base_name));
|
||||
strncpy(base_name, bank->name, sizeof(base_name) - 1);
|
||||
base_name[sizeof(base_name) - 1] = '\0';
|
||||
p = strstr(base_name, ".pflash");
|
||||
if (p) {
|
||||
*p = '\0';
|
||||
if (k_chip->num_pflash_blocks > 1) {
|
||||
/* rename first bank if numbering is needed */
|
||||
snprintf(name, sizeof(name), "%s.pflash0", base_name);
|
||||
free((void *)bank->name);
|
||||
free(bank->name);
|
||||
bank->name = strdup(name);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
strncpy(base_name, target_name(k_chip->target), sizeof(base_name));
|
||||
strncpy(base_name, target_name(k_chip->target), sizeof(base_name) - 1);
|
||||
base_name[sizeof(base_name) - 1] = '\0';
|
||||
p = strstr(base_name, ".cpu");
|
||||
if (p)
|
||||
*p = '\0';
|
||||
@@ -1996,7 +2014,7 @@ static int kinetis_probe_chip(struct kinetis_chip *k_chip)
|
||||
unsigned cpu_mhz = 120;
|
||||
unsigned idx;
|
||||
bool use_nvm_marking = false;
|
||||
char flash_marking[11], nvm_marking[2];
|
||||
char flash_marking[12], nvm_marking[2];
|
||||
char name[40];
|
||||
|
||||
k_chip->probed = false;
|
||||
@@ -3132,4 +3150,5 @@ struct flash_driver kinetis_flash = {
|
||||
.erase_check = kinetis_blank_check,
|
||||
.protect_check = kinetis_protect_check,
|
||||
.info = kinetis_info,
|
||||
.free_driver_priv = kinetis_free_driver_priv,
|
||||
};
|
||||
|
||||
@@ -478,14 +478,13 @@ int kinetis_ke_stop_watchdog(struct target *target)
|
||||
watchdog_algorithm->address, 0, 100000, &armv7m_info);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("Error executing Kinetis KE watchdog algorithm");
|
||||
retval = ERROR_FAIL;
|
||||
} else {
|
||||
LOG_INFO("Watchdog stopped");
|
||||
}
|
||||
|
||||
target_free_working_area(target, watchdog_algorithm);
|
||||
|
||||
return ERROR_OK;
|
||||
return retval;
|
||||
}
|
||||
|
||||
COMMAND_HANDLER(kinetis_ke_disable_wdog_handler)
|
||||
@@ -1311,4 +1310,5 @@ struct flash_driver kinetis_ke_flash = {
|
||||
.erase_check = kinetis_ke_blank_check,
|
||||
.protect_check = kinetis_ke_protect_check,
|
||||
.info = kinetis_ke_info,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
|
||||
@@ -1579,4 +1579,5 @@ struct flash_driver lpc2000_flash = {
|
||||
.erase_check = lpc2000_erase_check,
|
||||
.protect_check = lpc2000_protect_check,
|
||||
.info = get_lpc2000_info,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
|
||||
@@ -433,4 +433,5 @@ struct flash_driver lpc288x_flash = {
|
||||
.auto_probe = lpc288x_probe,
|
||||
.erase_check = lpc288x_erase_check,
|
||||
.protect_check = lpc288x_protect_check,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
|
||||
@@ -1598,4 +1598,5 @@ struct flash_driver lpc2900_flash = {
|
||||
.auto_probe = lpc2900_probe,
|
||||
.erase_check = lpc2900_erase_check,
|
||||
.protect_check = lpc2900_protect_check,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
|
||||
@@ -942,4 +942,5 @@ struct flash_driver lpcspifi_flash = {
|
||||
.erase_check = default_flash_blank_check,
|
||||
.protect_check = lpcspifi_protect_check,
|
||||
.info = get_lpcspifi_info,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
|
||||
@@ -633,4 +633,5 @@ struct flash_driver mdr_flash = {
|
||||
.erase_check = default_flash_blank_check,
|
||||
.protect_check = mdr_protect_check,
|
||||
.info = get_mdr_info,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
|
||||
@@ -955,4 +955,5 @@ struct flash_driver mrvlqspi_flash = {
|
||||
.erase_check = mrvlqspi_flash_erase_check,
|
||||
.protect_check = mrvlqspi_protect_check,
|
||||
.info = mrvlqspi_get_info,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,127 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2018 by Texas Instruments, Inc. *
|
||||
* *
|
||||
* 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, see <http://www.gnu.org/licenses/>. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef OPENOCD_FLASH_NOR_MSP432_H
|
||||
#define OPENOCD_FLASH_NOR_MSP432_H
|
||||
|
||||
/* MSP432 family types */
|
||||
#define MSP432_NO_FAMILY 0 /* Family type not determined yet */
|
||||
#define MSP432E4 1 /* MSP432E4 family of devices */
|
||||
#define MSP432P4 2 /* MSP432P4 family of devices */
|
||||
|
||||
/* MSP432 device types */
|
||||
#define MSP432_NO_TYPE 0 /* Device type not determined yet */
|
||||
#define MSP432P401X_DEPR 1 /* Early MSP432P401x offerings, now deprecated */
|
||||
#define MSP432P401X 2 /* MSP432P401x device, revision C or higher */
|
||||
#define MSP432P411X 3 /* MSP432P411x device, revision A or higher */
|
||||
#define MSP432P401X_GUESS 4 /* Assuming it's an MSP432P401x device */
|
||||
#define MSP432P411X_GUESS 5 /* Assuming it's an MSP432P411x device */
|
||||
#define MSP432E401Y 6 /* MSP432E401Y device */
|
||||
#define MSP432E411Y 7 /* MSP432E401Y device */
|
||||
#define MSP432E4X_GUESS 8 /* Assuming it's an MSP432E4x device */
|
||||
|
||||
/* MSP432P4 flash parameters */
|
||||
#define P4_FLASH_MAIN_BASE 0x00000000
|
||||
#define P4_FLASH_INFO_BASE 0x00200000
|
||||
#define P4_SECTOR_LENGTH 0x1000
|
||||
#define P4_ALGO_ENTRY_ADDR 0x01000110
|
||||
|
||||
/* MSP432E4 flash paramters */
|
||||
#define E4_FLASH_BASE 0x00000000
|
||||
#define E4_FLASH_SIZE 0x100000
|
||||
#define E4_SECTOR_LENGTH 0x4000
|
||||
#define E4_ALGO_ENTRY_ADDR 0x20000110
|
||||
|
||||
/* Flash helper algorithm key addresses */
|
||||
#define ALGO_BASE_ADDR 0x20000000
|
||||
#define ALGO_BUFFER1_ADDR 0x20002000
|
||||
#define ALGO_BUFFER2_ADDR 0x20003000
|
||||
#define ALGO_PARAMS_BASE_ADDR 0x20000150
|
||||
#define ALGO_FLASH_COMMAND_ADDR 0x20000150
|
||||
#define ALGO_RETURN_CODE_ADDR 0x20000154
|
||||
#define ALGO_FLASH_DEST_ADDR 0x2000015c
|
||||
#define ALGO_FLASH_LENGTH_ADDR 0x20000160
|
||||
#define ALGO_BUFFER1_STATUS_ADDR 0x20000164
|
||||
#define ALGO_BUFFER2_STATUS_ADDR 0x20000168
|
||||
#define ALGO_ERASE_PARAM_ADDR 0x2000016c
|
||||
#define ALGO_UNLOCK_BSL_ADDR 0x20000170
|
||||
#define ALGO_STACK_POINTER_ADDR 0x20002000
|
||||
|
||||
/* Flash helper algorithm key sizes */
|
||||
#define ALGO_BUFFER_SIZE 0x1000
|
||||
#define ALGO_WORKING_SIZE (ALGO_BUFFER2_ADDR + 0x1000 - ALGO_BASE_ADDR)
|
||||
|
||||
/* Flash helper algorithm flash commands */
|
||||
#define FLASH_NO_COMMAND 0
|
||||
#define FLASH_MASS_ERASE 1
|
||||
#define FLASH_SECTOR_ERASE 2
|
||||
#define FLASH_PROGRAM 4
|
||||
#define FLASH_INIT 8
|
||||
#define FLASH_EXIT 16
|
||||
#define FLASH_CONTINUOUS 32
|
||||
|
||||
/* Flash helper algorithm return codes */
|
||||
#define FLASH_BUSY 0x00000001
|
||||
#define FLASH_SUCCESS 0x00000ACE
|
||||
#define FLASH_ERROR 0x0000DEAD
|
||||
#define FLASH_TIMEOUT_ERROR 0xDEAD0000
|
||||
#define FLASH_VERIFY_ERROR 0xDEADDEAD
|
||||
#define FLASH_WRONG_COMMAND 0x00000BAD
|
||||
#define FLASH_POWER_ERROR 0x00DEAD00
|
||||
|
||||
/* Flash helper algorithm buffer status values */
|
||||
#define BUFFER_INACTIVE 0x00
|
||||
#define BUFFER_ACTIVE 0x01
|
||||
#define BUFFER_DATA_READY 0x10
|
||||
|
||||
/* Flash helper algorithm erase parameters */
|
||||
#define FLASH_ERASE_MAIN 0x01
|
||||
#define FLASH_ERASE_INFO 0x02
|
||||
|
||||
/* Flash helper algorithm lock/unlock BSL options */
|
||||
#define FLASH_LOCK_BSL 0x00
|
||||
#define FLASH_UNLOCK_BSL 0x0b
|
||||
|
||||
/* Flash helper algorithm parameter block struct */
|
||||
struct msp432_algo_params {
|
||||
uint8_t flash_command[4];
|
||||
uint8_t return_code[4];
|
||||
uint8_t _reserved0[4];
|
||||
uint8_t address[4];
|
||||
uint8_t length[4];
|
||||
uint8_t buffer1_status[4];
|
||||
uint8_t buffer2_status[4];
|
||||
uint8_t erase_param[4];
|
||||
uint8_t unlock_bsl[4];
|
||||
};
|
||||
|
||||
/* Flash helper algorithm for MSP432P401x targets */
|
||||
const uint8_t msp432p401x_algo[] = {
|
||||
#include "../../../contrib/loaders/flash/msp432/msp432p401x_algo.inc"
|
||||
};
|
||||
|
||||
/* Flash helper algorithm for MSP432P411x targets */
|
||||
const uint8_t msp432p411x_algo[] = {
|
||||
#include "../../../contrib/loaders/flash/msp432/msp432p411x_algo.inc"
|
||||
};
|
||||
|
||||
/* Flash helper algorithm for MSP432E4x targets */
|
||||
const uint8_t msp432e4x_algo[] = {
|
||||
#include "../../../contrib/loaders/flash/msp432/msp432e4x_algo.inc"
|
||||
};
|
||||
|
||||
#endif /* OPENOCD_FLASH_NOR_MSP432_H */
|
||||
@@ -1741,4 +1741,5 @@ struct flash_driver niietcm4_flash = {
|
||||
.erase_check = default_flash_blank_check,
|
||||
.protect_check = niietcm4_protect_check,
|
||||
.info = get_niietcm4_info,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
|
||||
+28
-58
@@ -108,6 +108,7 @@ enum nrf5_nvmc_config_bits {
|
||||
|
||||
struct nrf5_info {
|
||||
uint32_t code_page_size;
|
||||
uint32_t refcount;
|
||||
|
||||
struct {
|
||||
bool probed;
|
||||
@@ -155,6 +156,11 @@ static const struct nrf5_device_spec nrf5_known_devices_table[] = {
|
||||
NRF5_DEVICE_DEF(0x0020, "51822", "CEAA", "BA", 256),
|
||||
NRF5_DEVICE_DEF(0x002F, "51822", "CEAA", "B0", 256),
|
||||
|
||||
/* Some early nRF51-DK (PCA10028) & nRF51-Dongle (PCA10031) boards
|
||||
with built-in jlink seem to use engineering samples not listed
|
||||
in the nRF51 Series Compatibility Matrix V1.0. */
|
||||
NRF5_DEVICE_DEF(0x0071, "51822", "QFAC", "AB", 256),
|
||||
|
||||
/* nRF51822 Devices (IC rev 2). */
|
||||
NRF5_DEVICE_DEF(0x002A, "51822", "QFAA", "FA0", 256),
|
||||
NRF5_DEVICE_DEF(0x0044, "51822", "QFAA", "GC0", 256),
|
||||
@@ -175,6 +181,7 @@ static const struct nrf5_device_spec nrf5_known_devices_table[] = {
|
||||
NRF5_DEVICE_DEF(0x007D, "51822", "CDAB", "A0", 128),
|
||||
NRF5_DEVICE_DEF(0x0079, "51822", "CEAA", "E0", 256),
|
||||
NRF5_DEVICE_DEF(0x0087, "51822", "CFAC", "A0", 256),
|
||||
NRF5_DEVICE_DEF(0x008F, "51822", "QFAA", "H1", 256),
|
||||
|
||||
/* nRF51422 Devices (IC rev 1). */
|
||||
NRF5_DEVICE_DEF(0x001E, "51422", "QFAA", "CA", 256),
|
||||
@@ -198,11 +205,7 @@ static const struct nrf5_device_spec nrf5_known_devices_table[] = {
|
||||
|
||||
/* nRF52832 Devices */
|
||||
NRF5_DEVICE_DEF(0x00C7, "52832", "QFAA", "B0", 512),
|
||||
|
||||
/* Some early nRF51-DK (PCA10028) & nRF51-Dongle (PCA10031) boards
|
||||
with built-in jlink seem to use engineering samples not listed
|
||||
in the nRF51 Series Compatibility Matrix V1.0. */
|
||||
NRF5_DEVICE_DEF(0x0071, "51822", "QFAC", "AB", 256),
|
||||
NRF5_DEVICE_DEF(0x0139, "52832", "QFAA", "E0", 512),
|
||||
};
|
||||
|
||||
static int nrf5_bank_is_probed(struct flash_bank *bank)
|
||||
@@ -530,7 +533,6 @@ static int nrf5_probe(struct flash_bank *bank)
|
||||
bank->sectors[0].size = bank->size;
|
||||
bank->sectors[0].offset = 0;
|
||||
|
||||
/* mark as unknown */
|
||||
bank->sectors[0].is_erased = 0;
|
||||
bank->sectors[0].is_protected = 0;
|
||||
|
||||
@@ -552,17 +554,6 @@ static int nrf5_auto_probe(struct flash_bank *bank)
|
||||
return nrf5_probe(bank);
|
||||
}
|
||||
|
||||
static struct flash_sector *nrf5_find_sector_by_address(struct flash_bank *bank, uint32_t address)
|
||||
{
|
||||
struct nrf5_info *chip = bank->driver_priv;
|
||||
|
||||
for (int i = 0; i < bank->num_sectors; i++)
|
||||
if (bank->sectors[i].offset <= address &&
|
||||
address < (bank->sectors[i].offset + chip->code_page_size))
|
||||
return &bank->sectors[i];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int nrf5_erase_all(struct nrf5_info *chip)
|
||||
{
|
||||
LOG_DEBUG("Erasing all non-volatile memory");
|
||||
@@ -614,9 +605,6 @@ static int nrf5_erase_page(struct flash_bank *bank,
|
||||
sector->offset);
|
||||
}
|
||||
|
||||
if (res == ERROR_OK)
|
||||
sector->is_erased = 1;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -742,48 +730,22 @@ static int nrf5_write_pages(struct flash_bank *bank, uint32_t start, uint32_t en
|
||||
{
|
||||
int res = ERROR_FAIL;
|
||||
struct nrf5_info *chip = bank->driver_priv;
|
||||
struct flash_sector *sector;
|
||||
uint32_t offset;
|
||||
|
||||
assert(start % chip->code_page_size == 0);
|
||||
assert(end % chip->code_page_size == 0);
|
||||
|
||||
/* Erase all sectors */
|
||||
for (offset = start; offset < end; offset += chip->code_page_size) {
|
||||
sector = nrf5_find_sector_by_address(bank, offset);
|
||||
if (!sector) {
|
||||
LOG_ERROR("Invalid sector @ 0x%08"PRIx32, offset);
|
||||
return ERROR_FLASH_SECTOR_INVALID;
|
||||
}
|
||||
|
||||
if (sector->is_protected) {
|
||||
LOG_ERROR("Can't erase protected sector @ 0x%08"PRIx32, offset);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (sector->is_erased != 1) { /* 1 = erased, 0= not erased, -1 = unknown */
|
||||
res = nrf5_erase_page(bank, chip, sector);
|
||||
if (res != ERROR_OK) {
|
||||
LOG_ERROR("Failed to erase sector @ 0x%08"PRIx32, sector->offset);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
sector->is_erased = 0;
|
||||
}
|
||||
|
||||
res = nrf5_nvmc_write_enable(chip);
|
||||
if (res != ERROR_OK)
|
||||
goto error;
|
||||
|
||||
res = nrf5_ll_flash_write(chip, start, buffer, (end - start));
|
||||
if (res != ERROR_OK)
|
||||
goto set_read_only;
|
||||
goto error;
|
||||
|
||||
return nrf5_nvmc_read_only(chip);
|
||||
|
||||
set_read_only:
|
||||
nrf5_nvmc_read_only(chip);
|
||||
error:
|
||||
nrf5_nvmc_read_only(chip);
|
||||
LOG_ERROR("Failed to write to nrf5 flash");
|
||||
return res;
|
||||
}
|
||||
@@ -875,11 +837,9 @@ static int nrf5_uicr_flash_write(struct flash_bank *bank,
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
|
||||
if (sector->is_erased != 1) {
|
||||
res = nrf5_erase_page(bank, chip, sector);
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
}
|
||||
res = nrf5_erase_page(bank, chip, sector);
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
|
||||
res = nrf5_nvmc_write_enable(chip);
|
||||
if (res != ERROR_OK)
|
||||
@@ -910,6 +870,18 @@ static int nrf5_write(struct flash_bank *bank, const uint8_t *buffer,
|
||||
return chip->bank[bank->bank_number].write(bank, chip, buffer, offset, count);
|
||||
}
|
||||
|
||||
static void nrf5_free_driver_priv(struct flash_bank *bank)
|
||||
{
|
||||
struct nrf5_info *chip = bank->driver_priv;
|
||||
if (chip == NULL)
|
||||
return;
|
||||
|
||||
chip->refcount--;
|
||||
if (chip->refcount == 0) {
|
||||
free(chip);
|
||||
bank->driver_priv = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
FLASH_BANK_COMMAND_HANDLER(nrf5_flash_bank_command)
|
||||
{
|
||||
@@ -945,6 +917,7 @@ FLASH_BANK_COMMAND_HANDLER(nrf5_flash_bank_command)
|
||||
break;
|
||||
}
|
||||
|
||||
chip->refcount++;
|
||||
chip->bank[bank->bank_number].probed = false;
|
||||
bank->driver_priv = chip;
|
||||
|
||||
@@ -991,9 +964,6 @@ COMMAND_HANDLER(nrf5_handle_mass_erase_command)
|
||||
return res;
|
||||
}
|
||||
|
||||
for (int i = 0; i < bank->num_sectors; i++)
|
||||
bank->sectors[i].is_erased = 1;
|
||||
|
||||
res = nrf5_protect_check(bank);
|
||||
if (res != ERROR_OK) {
|
||||
LOG_ERROR("Failed to check chip's write protection");
|
||||
@@ -1004,8 +974,6 @@ COMMAND_HANDLER(nrf5_handle_mass_erase_command)
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
|
||||
bank->sectors[0].is_erased = 1;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
@@ -1174,6 +1142,7 @@ struct flash_driver nrf5_flash = {
|
||||
.auto_probe = nrf5_auto_probe,
|
||||
.erase_check = default_flash_blank_check,
|
||||
.protect_check = nrf5_protect_check,
|
||||
.free_driver_priv = nrf5_free_driver_priv,
|
||||
};
|
||||
|
||||
/* We need to retain the flash-driver name as well as the commands
|
||||
@@ -1191,4 +1160,5 @@ struct flash_driver nrf51_flash = {
|
||||
.auto_probe = nrf5_auto_probe,
|
||||
.erase_check = default_flash_blank_check,
|
||||
.protect_check = nrf5_protect_check,
|
||||
.free_driver_priv = nrf5_free_driver_priv,
|
||||
};
|
||||
|
||||
@@ -1880,4 +1880,5 @@ struct flash_driver numicro_flash = {
|
||||
.auto_probe = numicro_auto_probe,
|
||||
.erase_check = default_flash_blank_check,
|
||||
.protect_check = numicro_protect_check,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
|
||||
@@ -340,4 +340,5 @@ struct flash_driver ocl_flash = {
|
||||
.erase_check = ocl_erase_check,
|
||||
.protect_check = ocl_protect_check,
|
||||
.auto_probe = ocl_auto_probe,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
|
||||
@@ -980,4 +980,5 @@ struct flash_driver pic32mx_flash = {
|
||||
.erase_check = default_flash_blank_check,
|
||||
.protect_check = pic32mx_protect_check,
|
||||
.info = pic32mx_info,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
|
||||
+409
-254
@@ -41,24 +41,67 @@
|
||||
Document Number: 001-87197 Rev. *B Revised August 29, 2013
|
||||
|
||||
PSoC 4100/4200 Family PSoC(R) 4 Architecture TRM
|
||||
Document No. 001-85634 Rev. *C March 25, 2014
|
||||
Document No. 001-85634 Rev. *E June 28, 2016
|
||||
|
||||
PSoC(R) 4 Registers TRM Spec.
|
||||
Document No. 001-85847 Rev. *A June 25, 2013
|
||||
|
||||
PSoC 4000 Family PSoC(R) 4 Technical Reference Manual
|
||||
Document No. 001-89309 Rev. *B May 9, 2016
|
||||
|
||||
PSoC 41XX_BLE/42XX_BLE Family PSoC 4 BLE Architecture TRM
|
||||
Document No. 001-92738 Rev. *C February 12, 2016
|
||||
|
||||
PSoC 4200L Family PSoC 4 Architecture TRM
|
||||
Document No. 001-97952 Rev. *A December 15, 2015
|
||||
|
||||
PSoC 4200L Family PSoC 4 Registers TRM
|
||||
Document No. 001-98126 Rev. *A December 16, 2015
|
||||
|
||||
PSoC 4100M/4200M Family PSoC 4 Architecture TRM
|
||||
Document No. 001-95223 Rev. *B July 29, 2015
|
||||
|
||||
PSoC 4100S Family PSoC 4 Architecture TRM
|
||||
Document No. 002-10621 Rev. *A July 29, 2016
|
||||
|
||||
PSoC 4100S Family PSoC 4 Registers TRM
|
||||
Document No. 002-10523 Rev. *A July 20, 2016
|
||||
|
||||
PSoC Analog Coprocessor Architecture TRM
|
||||
Document No. 002-10404 Rev. ** December 18, 2015
|
||||
|
||||
CY8C4Axx PSoC Analog Coprocessor Registers TRM
|
||||
Document No. 002-10405 Rev. ** December 18, 2015
|
||||
|
||||
CY8C41xx, CY8C42xx Programming Specifications
|
||||
Document No. 001-81799 Rev. *C March 4, 2014
|
||||
|
||||
CYBL10x6x, CY8C4127_BL, CY8C4247_BL Programming Specifications
|
||||
Document No. 001-91508 Rev. *B September 22, 2014
|
||||
|
||||
http://dmitry.gr/index.php?r=05.Projects&proj=24.%20PSoC4%20confidential
|
||||
*/
|
||||
|
||||
/* register locations */
|
||||
#define PSOC4_CPUSS_SYSREQ 0x40000004
|
||||
#define PSOC4_CPUSS_SYSARG 0x40000008
|
||||
#define PSOC4_TEST_MODE 0x40030014
|
||||
#define PSOC4_SPCIF_GEOMETRY 0x400E0000
|
||||
#define PSOC4_SFLASH_MACRO0 0x0FFFF000
|
||||
|
||||
#define PSOC4_CPUSS_SYSREQ_LEGACY 0x40000004
|
||||
#define PSOC4_CPUSS_SYSARG_LEGACY 0x40000008
|
||||
#define PSOC4_SPCIF_GEOMETRY_LEGACY 0x400E0000
|
||||
|
||||
#define PSOC4_CPUSS_SYSREQ_NEW 0x40100004
|
||||
#define PSOC4_CPUSS_SYSARG_NEW 0x40100008
|
||||
#define PSOC4_SPCIF_GEOMETRY_NEW 0x40110000
|
||||
|
||||
#define PSOC4_TEST_MODE 0x40030014
|
||||
|
||||
#define PSOC4_ROMTABLE_PID0 0xF0000FE0
|
||||
|
||||
#define PSOC4_SFLASH_MACRO 0x0ffff000
|
||||
|
||||
/* constants */
|
||||
#define PSOC4_SFLASH_MACRO_SIZE 0x800
|
||||
#define PSOC4_ROWS_PER_MACRO 512
|
||||
|
||||
#define PSOC4_SROM_KEY1 0xb6
|
||||
#define PSOC4_SROM_KEY2 0xd3
|
||||
#define PSOC4_SROM_SYSREQ_BIT (1<<31)
|
||||
@@ -66,6 +109,10 @@
|
||||
#define PSOC4_SROM_PRIVILEGED_BIT (1<<28)
|
||||
#define PSOC4_SROM_STATUS_SUCCEEDED 0xa0000000
|
||||
#define PSOC4_SROM_STATUS_FAILED 0xf0000000
|
||||
#define PSOC4_SROM_STATUS_MASK 0xf0000000
|
||||
|
||||
/* not documented in any TRM */
|
||||
#define PSOC4_SROM_ERR_IMO_NOT_IMPLEM 0xf0000013
|
||||
|
||||
#define PSOC4_CMD_GET_SILICON_ID 0
|
||||
#define PSOC4_CMD_LOAD_LATCH 4
|
||||
@@ -74,76 +121,60 @@
|
||||
#define PSOC4_CMD_ERASE_ALL 0xa
|
||||
#define PSOC4_CMD_CHECKSUM 0xb
|
||||
#define PSOC4_CMD_WRITE_PROTECTION 0xd
|
||||
#define PSOC4_CMD_SET_IMO48 0x15
|
||||
#define PSOC4_CMD_WRITE_SFLASH_ROW 0x18
|
||||
|
||||
#define PSOC4_CHIP_PROT_VIRGIN 0x0
|
||||
#define PSOC4_CHIP_PROT_OPEN 0x1
|
||||
#define PSOC4_CHIP_PROT_PROTECTED 0x2
|
||||
#define PSOC4_CHIP_PROT_KILL 0x4
|
||||
|
||||
#define PSOC4_ROMTABLE_DESIGNER_CHECK 0xb4
|
||||
|
||||
struct psoc4_chip_details {
|
||||
#define PSOC4_FAMILY_FLAG_LEGACY 1
|
||||
|
||||
struct psoc4_chip_family {
|
||||
uint16_t id;
|
||||
const char *type;
|
||||
const char *package;
|
||||
uint32_t flash_size_in_kb;
|
||||
const char *name;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
/* list of PSoC 4 chips
|
||||
* flash_size_in_kb is not necessary as it can be decoded from SPCIF_GEOMETRY
|
||||
*/
|
||||
const struct psoc4_chip_details psoc4_devices[] = {
|
||||
/* 4200 series */
|
||||
{ 0x04A6, "CY8C4245PVI-482", "SSOP-28", .flash_size_in_kb = 32 },
|
||||
{ 0x04B6, "CY8C4245LQI-483", "QFN-40", .flash_size_in_kb = 32 },
|
||||
{ 0x04C8, "CY8C4245AXI-483", "TQFP-44", .flash_size_in_kb = 32 },
|
||||
{ 0x04FB, "CY8C4245AXI-473", "TQFP-44", .flash_size_in_kb = 32 },
|
||||
{ 0x04F0, "CY8C4244PVI-432", "SSOP-28", .flash_size_in_kb = 16 },
|
||||
{ 0x04F1, "CY8C4244PVI-442", "SSOP-28", .flash_size_in_kb = 16 },
|
||||
{ 0x04F6, "CY8C4244LQI-443", "QFN-40", .flash_size_in_kb = 16 },
|
||||
{ 0x04FA, "CY8C4244AXI-443", "TQFP-44", .flash_size_in_kb = 16 },
|
||||
|
||||
/* 4100 series */
|
||||
{ 0x0410, "CY8C4124PVI-432", "SSOP-28", .flash_size_in_kb = 16 },
|
||||
{ 0x0411, "CY8C4124PVI-442", "SSOP-28", .flash_size_in_kb = 16 },
|
||||
{ 0x041C, "CY8C4124LQI-443", "QFN-40", .flash_size_in_kb = 16 },
|
||||
{ 0x041A, "CY8C4124AXI-443", "TQFP-44", .flash_size_in_kb = 16 },
|
||||
{ 0x041B, "CY8C4125AXI-473", "TQFP-44", .flash_size_in_kb = 32 },
|
||||
{ 0x0412, "CY8C4125PVI-482", "SSOP-28", .flash_size_in_kb = 32 },
|
||||
{ 0x0417, "CY8C4125LQI-483", "QFN-40", .flash_size_in_kb = 32 },
|
||||
{ 0x0416, "CY8C4125AXI-483", "TQFP-44", .flash_size_in_kb = 32 },
|
||||
|
||||
/* CCG1 series */
|
||||
{ 0x0490, "CYPD1103-35FNXI", "CSP-35", .flash_size_in_kb = 32 },
|
||||
{ 0x0489, "CYPD1121-40LQXI", "QFN-40", .flash_size_in_kb = 32 },
|
||||
{ 0x048A, "CYPD1122-40LQXI", "QFN-40", .flash_size_in_kb = 32 },
|
||||
{ 0x0491, "CYPD1131-35FNXI", "CSP-35", .flash_size_in_kb = 32 },
|
||||
{ 0x0498, "CYPD1132-16SXI", "SOIC-16", .flash_size_in_kb = 32 },
|
||||
{ 0x0481, "CYPD1134-28PVXI", "SSOP-28", .flash_size_in_kb = 32 },
|
||||
{ 0x048B, "CYPD1134-40LQXI", "QFN-40", .flash_size_in_kb = 32 },
|
||||
const struct psoc4_chip_family psoc4_families[] = {
|
||||
{ 0x93, "PSoC4100/4200", .flags = PSOC4_FAMILY_FLAG_LEGACY },
|
||||
{ 0x9A, "PSoC4000", .flags = 0 },
|
||||
{ 0x9E, "PSoC/PRoC BLE (119E)", .flags = 0 },
|
||||
{ 0xA0, "PSoC4200L", .flags = 0 },
|
||||
{ 0xA1, "PSoC4100M/4200M", .flags = 0 },
|
||||
{ 0xA3, "PSoC/PRoC BLE (11A3)", .flags = 0 },
|
||||
{ 0xA9, "PSoC4000S", .flags = 0 },
|
||||
{ 0xAA, "PSoC/PRoC BLE (11AA)", .flags = 0 },
|
||||
{ 0xAB, "PSoC4100S", .flags = 0 },
|
||||
{ 0xAC, "PSoC Analog Coprocessor", .flags = 0 },
|
||||
{ 0, "Unknown", .flags = 0 }
|
||||
};
|
||||
|
||||
|
||||
struct psoc4_flash_bank {
|
||||
uint32_t row_size;
|
||||
uint32_t user_bank_size;
|
||||
int probed;
|
||||
uint32_t silicon_id;
|
||||
uint8_t chip_protection;
|
||||
int num_macros;
|
||||
bool probed;
|
||||
uint8_t cmd_program_row;
|
||||
uint16_t family_id;
|
||||
bool legacy_family;
|
||||
uint32_t cpuss_sysreq_addr;
|
||||
uint32_t cpuss_sysarg_addr;
|
||||
uint32_t spcif_geometry_addr;
|
||||
};
|
||||
|
||||
|
||||
static const struct psoc4_chip_details *psoc4_details_by_id(uint32_t silicon_id)
|
||||
static const struct psoc4_chip_family *psoc4_family_by_id(uint16_t family_id)
|
||||
{
|
||||
const struct psoc4_chip_details *p = psoc4_devices;
|
||||
unsigned int i;
|
||||
uint16_t id = silicon_id >> 16; /* ignore die revision */
|
||||
for (i = 0; i < sizeof(psoc4_devices)/sizeof(psoc4_devices[0]); i++, p++) {
|
||||
if (p->id == id)
|
||||
return p;
|
||||
}
|
||||
LOG_DEBUG("Unknown PSoC 4 device silicon id 0x%08" PRIx32 ".", silicon_id);
|
||||
return NULL;
|
||||
const struct psoc4_chip_family *p = psoc4_families;
|
||||
while (p->id && p->id != family_id)
|
||||
p++;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static const char *psoc4_decode_chip_protection(uint8_t protection)
|
||||
@@ -176,7 +207,9 @@ FLASH_BANK_COMMAND_HANDLER(psoc4_flash_bank_command)
|
||||
psoc4_info = calloc(1, sizeof(struct psoc4_flash_bank));
|
||||
|
||||
bank->driver_priv = psoc4_info;
|
||||
bank->default_padded_value = bank->erased_value = 0x00;
|
||||
psoc4_info->user_bank_size = bank->size;
|
||||
psoc4_info->cmd_program_row = PSOC4_CMD_WRITE_ROW;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
@@ -189,9 +222,14 @@ FLASH_BANK_COMMAND_HANDLER(psoc4_flash_bank_command)
|
||||
* Otherwise address of memory parameter block is set in CPUSS_SYSARG
|
||||
* and the first parameter is written to the first word of parameter block
|
||||
*/
|
||||
static int psoc4_sysreq(struct target *target, uint8_t cmd, uint16_t cmd_param,
|
||||
uint32_t *sysreq_params, uint32_t sysreq_params_size)
|
||||
static int psoc4_sysreq(struct flash_bank *bank, uint8_t cmd,
|
||||
uint16_t cmd_param,
|
||||
uint32_t *sysreq_params, uint32_t sysreq_params_size,
|
||||
uint32_t *sysarg_out)
|
||||
{
|
||||
struct target *target = bank->target;
|
||||
struct psoc4_flash_bank *psoc4_info = bank->driver_priv;
|
||||
|
||||
struct working_area *sysreq_wait_algorithm;
|
||||
struct working_area *sysreq_mem;
|
||||
|
||||
@@ -212,8 +250,8 @@ static int psoc4_sysreq(struct target *target, uint8_t cmd, uint16_t cmd_param,
|
||||
|
||||
const int code_words = (sizeof(psoc4_sysreq_wait_code) + 3) / 4;
|
||||
/* stack must be aligned */
|
||||
const int stack_size = 196;
|
||||
/* tested stack sizes on PSoC 4:
|
||||
const int stack_size = 256;
|
||||
/* tested stack sizes on PSoC4200:
|
||||
ERASE_ALL 144
|
||||
PROGRAM_ROW 112
|
||||
other sysreq 68
|
||||
@@ -238,6 +276,8 @@ static int psoc4_sysreq(struct target *target, uint8_t cmd, uint16_t cmd_param,
|
||||
}
|
||||
|
||||
if (sysreq_params_size) {
|
||||
LOG_DEBUG("SYSREQ %02" PRIx8 " %04" PRIx16 " %08" PRIx32 " size %" PRIu32,
|
||||
cmd, cmd_param, param1, sysreq_params_size);
|
||||
/* Allocate memory for sysreq_params */
|
||||
retval = target_alloc_working_area(target, sysreq_params_size, &sysreq_mem);
|
||||
if (retval != ERROR_OK) {
|
||||
@@ -250,21 +290,23 @@ static int psoc4_sysreq(struct target *target, uint8_t cmd, uint16_t cmd_param,
|
||||
}
|
||||
|
||||
/* Write sysreq_params */
|
||||
sysreq_params[0] = param1;
|
||||
target_buffer_set_u32(target, (uint8_t *)sysreq_params, param1);
|
||||
retval = target_write_buffer(target, sysreq_mem->address,
|
||||
sysreq_params_size, (uint8_t *)sysreq_params);
|
||||
if (retval != ERROR_OK)
|
||||
goto cleanup_mem;
|
||||
|
||||
/* Set address of sysreq parameters block */
|
||||
retval = target_write_u32(target, PSOC4_CPUSS_SYSARG, sysreq_mem->address);
|
||||
retval = target_write_u32(target, psoc4_info->cpuss_sysarg_addr, sysreq_mem->address);
|
||||
if (retval != ERROR_OK)
|
||||
goto cleanup_mem;
|
||||
|
||||
} else {
|
||||
/* Sysreq without memory block of parameters */
|
||||
LOG_DEBUG("SYSREQ %02" PRIx8 " %04" PRIx16 " %08" PRIx32,
|
||||
cmd, cmd_param, param1);
|
||||
/* Set register parameter */
|
||||
retval = target_write_u32(target, PSOC4_CPUSS_SYSARG, param1);
|
||||
retval = target_write_u32(target, psoc4_info->cpuss_sysarg_addr, param1);
|
||||
if (retval != ERROR_OK)
|
||||
goto cleanup_mem;
|
||||
}
|
||||
@@ -279,14 +321,14 @@ static int psoc4_sysreq(struct target *target, uint8_t cmd, uint16_t cmd_param,
|
||||
|
||||
struct armv7m_common *armv7m = target_to_armv7m(target);
|
||||
if (armv7m == NULL) {
|
||||
|
||||
/* something is very wrong if armv7m is NULL */
|
||||
LOG_ERROR("unable to get armv7m target");
|
||||
retval = ERROR_FAIL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Set SROM request */
|
||||
retval = target_write_u32(target, PSOC4_CPUSS_SYSREQ,
|
||||
retval = target_write_u32(target, psoc4_info->cpuss_sysreq_addr,
|
||||
PSOC4_SROM_SYSREQ_BIT | PSOC4_SROM_HMASTER_BIT | cmd);
|
||||
if (retval != ERROR_OK)
|
||||
goto cleanup;
|
||||
@@ -295,9 +337,23 @@ static int psoc4_sysreq(struct target *target, uint8_t cmd, uint16_t cmd_param,
|
||||
retval = target_run_algorithm(target, 0, NULL,
|
||||
sizeof(reg_params) / sizeof(*reg_params), reg_params,
|
||||
sysreq_wait_algorithm->address, 0, 1000, &armv7m_info);
|
||||
if (retval != ERROR_OK)
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("sysreq wait code execution failed");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
uint32_t sysarg_out_tmp;
|
||||
retval = target_read_u32(target, psoc4_info->cpuss_sysarg_addr, &sysarg_out_tmp);
|
||||
if (retval != ERROR_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (sysarg_out) {
|
||||
*sysarg_out = sysarg_out_tmp;
|
||||
/* If result is an error, do not show now, let caller to decide */
|
||||
} else if ((sysarg_out_tmp & PSOC4_SROM_STATUS_MASK) != PSOC4_SROM_STATUS_SUCCEEDED) {
|
||||
LOG_ERROR("sysreq error 0x%" PRIx32, sysarg_out_tmp);
|
||||
retval = ERROR_FAIL;
|
||||
}
|
||||
cleanup:
|
||||
destroy_reg_param(®_params[0]);
|
||||
|
||||
@@ -313,42 +369,123 @@ cleanup_algo:
|
||||
|
||||
|
||||
/* helper routine to get silicon ID from a PSoC 4 chip */
|
||||
static int psoc4_get_silicon_id(struct target *target, uint32_t *silicon_id, uint8_t *protection)
|
||||
static int psoc4_get_silicon_id(struct flash_bank *bank, uint32_t *silicon_id, uint16_t *family_id, uint8_t *protection)
|
||||
{
|
||||
uint32_t params = PSOC4_SROM_KEY1
|
||||
| ((PSOC4_SROM_KEY2 + PSOC4_CMD_GET_SILICON_ID) << 8);
|
||||
struct target *target = bank->target;
|
||||
struct psoc4_flash_bank *psoc4_info = bank->driver_priv;
|
||||
|
||||
uint32_t part0, part1;
|
||||
|
||||
int retval = psoc4_sysreq(target, PSOC4_CMD_GET_SILICON_ID, 0, NULL, 0);
|
||||
int retval = psoc4_sysreq(bank, PSOC4_CMD_GET_SILICON_ID, 0, NULL, 0, &part0);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
retval = target_read_u32(target, PSOC4_CPUSS_SYSARG, &part0);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
if (part0 == params) {
|
||||
LOG_ERROR("sysreq silicon id request not served");
|
||||
if ((part0 & PSOC4_SROM_STATUS_MASK) != PSOC4_SROM_STATUS_SUCCEEDED) {
|
||||
LOG_ERROR("sysreq error 0x%" PRIx32, part0);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
retval = target_read_u32(target, PSOC4_CPUSS_SYSREQ, &part1);
|
||||
retval = target_read_u32(target, psoc4_info->cpuss_sysreq_addr, &part1);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
uint32_t silicon = ((part0 & 0xffff) << 16)
|
||||
| (((part0 >> 16) & 0xff) << 8)
|
||||
| (part1 & 0xff);
|
||||
uint8_t prot = (part1 >> 12) & 0xff;
|
||||
|
||||
/* build ID as Cypress sw does:
|
||||
* bit 31..16 silicon ID
|
||||
* bit 15..8 revision ID (so far 0x11 for all devices)
|
||||
* bit 7..0 family ID (lowes 8 bits)
|
||||
*/
|
||||
if (silicon_id)
|
||||
*silicon_id = silicon;
|
||||
if (protection)
|
||||
*protection = prot;
|
||||
*silicon_id = ((part0 & 0x0000ffff) << 16)
|
||||
| ((part0 & 0x00ff0000) >> 8)
|
||||
| (part1 & 0x000000ff);
|
||||
|
||||
LOG_DEBUG("silicon id: 0x%08" PRIx32 "", silicon);
|
||||
LOG_DEBUG("protection: 0x%02" PRIx8 "", prot);
|
||||
return retval;
|
||||
if (family_id)
|
||||
*family_id = part1 & 0x0fff;
|
||||
|
||||
if (protection)
|
||||
*protection = (part1 >> 12) & 0x0f;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
|
||||
static int psoc4_get_family(struct target *target, uint16_t *family_id)
|
||||
{
|
||||
int retval, i;
|
||||
uint32_t pidbf[3];
|
||||
uint8_t pid[3];
|
||||
|
||||
retval = target_read_memory(target, PSOC4_ROMTABLE_PID0, 4, 3, (uint8_t *)pidbf);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
uint32_t tmp = target_buffer_get_u32(target, (uint8_t *)(pidbf + i));
|
||||
if (tmp & 0xffffff00) {
|
||||
LOG_ERROR("Unexpected data in ROMTABLE");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
pid[i] = tmp & 0xff;
|
||||
}
|
||||
|
||||
uint16_t family = pid[0] | ((pid[1] & 0xf) << 8);
|
||||
uint32_t designer = ((pid[1] & 0xf0) >> 4) | ((pid[2] & 0xf) << 4);
|
||||
|
||||
if (designer != PSOC4_ROMTABLE_DESIGNER_CHECK) {
|
||||
LOG_ERROR("ROMTABLE designer is not Cypress");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
*family_id = family;
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
|
||||
static int psoc4_flash_prepare(struct flash_bank *bank)
|
||||
{
|
||||
struct target *target = bank->target;
|
||||
struct psoc4_flash_bank *psoc4_info = bank->driver_priv;
|
||||
|
||||
if (target->state != TARGET_HALTED) {
|
||||
LOG_ERROR("Target not halted");
|
||||
return ERROR_TARGET_NOT_HALTED;
|
||||
}
|
||||
|
||||
uint16_t family_id;
|
||||
int retval;
|
||||
|
||||
/* get family ID from SROM call */
|
||||
retval = psoc4_get_silicon_id(bank, NULL, &family_id, NULL);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
/* and check with family ID from ROMTABLE */
|
||||
if (family_id != psoc4_info->family_id) {
|
||||
LOG_ERROR("Family mismatch");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (!psoc4_info->legacy_family) {
|
||||
uint32_t sysreq_status;
|
||||
retval = psoc4_sysreq(bank, PSOC4_CMD_SET_IMO48, 0, NULL, 0, &sysreq_status);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
if ((sysreq_status & PSOC4_SROM_STATUS_MASK) != PSOC4_SROM_STATUS_SUCCEEDED) {
|
||||
/* This undocumented error code is returned probably when
|
||||
* PSOC4_CMD_SET_IMO48 command is not implemented.
|
||||
* Can be safely ignored, programming works.
|
||||
*/
|
||||
if (sysreq_status == PSOC4_SROM_ERR_IMO_NOT_IMPLEM)
|
||||
LOG_INFO("PSOC4_CMD_SET_IMO48 is not implemented on this device.");
|
||||
else {
|
||||
LOG_ERROR("sysreq error 0x%" PRIx32, sysreq_status);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -357,48 +494,37 @@ static int psoc4_protect_check(struct flash_bank *bank)
|
||||
struct target *target = bank->target;
|
||||
struct psoc4_flash_bank *psoc4_info = bank->driver_priv;
|
||||
|
||||
uint32_t prot_addr = PSOC4_SFLASH_MACRO;
|
||||
uint32_t protection;
|
||||
int i, s;
|
||||
int num_bits;
|
||||
int retval = ERROR_OK;
|
||||
uint32_t prot_addr = PSOC4_SFLASH_MACRO0;
|
||||
int retval;
|
||||
int s = 0;
|
||||
int m, i;
|
||||
uint8_t bf[PSOC4_ROWS_PER_MACRO/8];
|
||||
|
||||
num_bits = bank->num_sectors;
|
||||
|
||||
for (i = 0; i < num_bits; i += 32) {
|
||||
retval = target_read_u32(target, prot_addr, &protection);
|
||||
for (m = 0; m < psoc4_info->num_macros; m++, prot_addr += PSOC4_SFLASH_MACRO_SIZE) {
|
||||
retval = target_read_memory(target, prot_addr, 4, PSOC4_ROWS_PER_MACRO/32, bf);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
prot_addr += 4;
|
||||
|
||||
for (s = 0; s < 32; s++) {
|
||||
if (i + s >= num_bits)
|
||||
break;
|
||||
bank->sectors[i + s].is_protected = (protection & (1 << s)) ? 1 : 0;
|
||||
}
|
||||
for (i = 0; i < PSOC4_ROWS_PER_MACRO && s < bank->num_sectors; i++, s++)
|
||||
bank->sectors[s].is_protected = bf[i/8] & (1 << (i%8)) ? 1 : 0;
|
||||
}
|
||||
|
||||
retval = psoc4_get_silicon_id(target, NULL, &(psoc4_info->chip_protection));
|
||||
return retval;
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
|
||||
static int psoc4_mass_erase(struct flash_bank *bank)
|
||||
{
|
||||
struct target *target = bank->target;
|
||||
int i;
|
||||
|
||||
if (bank->target->state != TARGET_HALTED) {
|
||||
LOG_ERROR("Target not halted");
|
||||
return ERROR_TARGET_NOT_HALTED;
|
||||
}
|
||||
int retval = psoc4_flash_prepare(bank);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
/* Call "Erase All" system ROM API */
|
||||
uint32_t param;
|
||||
int retval = psoc4_sysreq(target, PSOC4_CMD_ERASE_ALL,
|
||||
uint32_t param = 0;
|
||||
retval = psoc4_sysreq(bank, PSOC4_CMD_ERASE_ALL,
|
||||
0,
|
||||
¶m, sizeof(param));
|
||||
¶m, sizeof(param), NULL);
|
||||
|
||||
if (retval == ERROR_OK)
|
||||
/* set all sectors as erased */
|
||||
@@ -420,7 +546,7 @@ static int psoc4_erase(struct flash_bank *bank, int first, int last)
|
||||
if ((first == 0) && (last == (bank->num_sectors - 1)))
|
||||
return psoc4_mass_erase(bank);
|
||||
|
||||
LOG_ERROR("Only mass erase available");
|
||||
LOG_ERROR("Only mass erase available! Consider using 'psoc4 flash_autoerase 0 on'");
|
||||
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
@@ -431,56 +557,63 @@ static int psoc4_protect(struct flash_bank *bank, int set, int first, int last)
|
||||
struct target *target = bank->target;
|
||||
struct psoc4_flash_bank *psoc4_info = bank->driver_priv;
|
||||
|
||||
if (psoc4_info->probed == 0)
|
||||
if (!psoc4_info->probed)
|
||||
return ERROR_FAIL;
|
||||
|
||||
if (target->state != TARGET_HALTED) {
|
||||
LOG_ERROR("Target not halted");
|
||||
return ERROR_TARGET_NOT_HALTED;
|
||||
}
|
||||
int retval = psoc4_flash_prepare(bank);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
uint32_t *sysrq_buffer = NULL;
|
||||
int retval;
|
||||
int num_bits = bank->num_sectors;
|
||||
const int param_sz = 8;
|
||||
int prot_sz = num_bits / 8;
|
||||
int chip_prot = PSOC4_CHIP_PROT_OPEN;
|
||||
int flash_macro = 0; /* PSoC 42xx has only macro 0 */
|
||||
int i;
|
||||
int i, m, sect;
|
||||
int num_bits = bank->num_sectors;
|
||||
|
||||
sysrq_buffer = calloc(1, param_sz + prot_sz);
|
||||
if (num_bits > PSOC4_ROWS_PER_MACRO)
|
||||
num_bits = PSOC4_ROWS_PER_MACRO;
|
||||
|
||||
int prot_sz = num_bits / 8;
|
||||
|
||||
sysrq_buffer = malloc(param_sz + prot_sz);
|
||||
if (sysrq_buffer == NULL) {
|
||||
LOG_ERROR("no memory for row buffer");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
for (i = first; i < num_bits && i <= last; i++)
|
||||
for (i = first; i <= last && i < bank->num_sectors; i++)
|
||||
bank->sectors[i].is_protected = set;
|
||||
|
||||
uint32_t *p = sysrq_buffer + 2;
|
||||
for (i = 0; i < num_bits; i++) {
|
||||
if (bank->sectors[i].is_protected)
|
||||
p[i / 32] |= 1 << (i % 32);
|
||||
for (m = 0, sect = 0; m < psoc4_info->num_macros; m++) {
|
||||
uint8_t *p = (uint8_t *)(sysrq_buffer + 2);
|
||||
memset(p, 0, prot_sz);
|
||||
for (i = 0; i < num_bits && sect < bank->num_sectors; i++, sect++) {
|
||||
if (bank->sectors[sect].is_protected)
|
||||
p[i/8] |= 1 << (i%8);
|
||||
}
|
||||
|
||||
/* Call "Load Latch" system ROM API */
|
||||
target_buffer_set_u32(target, (uint8_t *)(sysrq_buffer + 1),
|
||||
prot_sz - 1);
|
||||
retval = psoc4_sysreq(bank, PSOC4_CMD_LOAD_LATCH,
|
||||
0 /* Byte number in latch from what to write */
|
||||
| (m << 8), /* flash macro index */
|
||||
sysrq_buffer, param_sz + prot_sz,
|
||||
NULL);
|
||||
if (retval != ERROR_OK)
|
||||
break;
|
||||
|
||||
/* Call "Write Protection" system ROM API */
|
||||
retval = psoc4_sysreq(bank, PSOC4_CMD_WRITE_PROTECTION,
|
||||
chip_prot | (m << 8), NULL, 0, NULL);
|
||||
if (retval != ERROR_OK)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Call "Load Latch" system ROM API */
|
||||
sysrq_buffer[1] = prot_sz - 1;
|
||||
retval = psoc4_sysreq(target, PSOC4_CMD_LOAD_LATCH,
|
||||
0, /* Byte number in latch from what to write */
|
||||
sysrq_buffer, param_sz + psoc4_info->row_size);
|
||||
if (retval != ERROR_OK)
|
||||
goto cleanup;
|
||||
|
||||
/* Call "Write Protection" system ROM API */
|
||||
retval = psoc4_sysreq(target, PSOC4_CMD_WRITE_PROTECTION,
|
||||
chip_prot | (flash_macro << 8), NULL, 0);
|
||||
cleanup:
|
||||
if (retval != ERROR_OK)
|
||||
psoc4_protect_check(bank);
|
||||
|
||||
if (sysrq_buffer)
|
||||
free(sysrq_buffer);
|
||||
|
||||
psoc4_protect_check(bank);
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -516,21 +649,14 @@ COMMAND_HANDLER(psoc4_handle_flash_autoerase_command)
|
||||
static int psoc4_write(struct flash_bank *bank, const uint8_t *buffer,
|
||||
uint32_t offset, uint32_t count)
|
||||
{
|
||||
struct psoc4_flash_bank *psoc4_info = bank->driver_priv;
|
||||
struct target *target = bank->target;
|
||||
struct psoc4_flash_bank *psoc4_info = bank->driver_priv;
|
||||
uint32_t *sysrq_buffer = NULL;
|
||||
int retval = ERROR_OK;
|
||||
const int param_sz = 8;
|
||||
|
||||
if (bank->target->state != TARGET_HALTED) {
|
||||
LOG_ERROR("Target not halted");
|
||||
return ERROR_TARGET_NOT_HALTED;
|
||||
}
|
||||
|
||||
if (offset & 0x1) {
|
||||
LOG_ERROR("offset 0x%08" PRIx32 " breaks required 2-byte alignment", offset);
|
||||
return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
|
||||
}
|
||||
int retval = psoc4_flash_prepare(bank);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
sysrq_buffer = malloc(param_sz + psoc4_info->row_size);
|
||||
if (sysrq_buffer == NULL) {
|
||||
@@ -542,7 +668,7 @@ static int psoc4_write(struct flash_bank *bank, const uint8_t *buffer,
|
||||
uint32_t row_num = offset / psoc4_info->row_size;
|
||||
uint32_t row_offset = offset - row_num * psoc4_info->row_size;
|
||||
if (row_offset)
|
||||
memset(row_buffer, 0, row_offset);
|
||||
memset(row_buffer, bank->default_padded_value, row_offset);
|
||||
|
||||
bool save_poll = jtag_poll_get_enabled();
|
||||
jtag_poll_set_enabled(false);
|
||||
@@ -551,25 +677,31 @@ static int psoc4_write(struct flash_bank *bank, const uint8_t *buffer,
|
||||
uint32_t chunk_size = psoc4_info->row_size - row_offset;
|
||||
if (chunk_size > count) {
|
||||
chunk_size = count;
|
||||
memset(row_buffer + chunk_size, 0, psoc4_info->row_size - chunk_size);
|
||||
memset(row_buffer + chunk_size, bank->default_padded_value, psoc4_info->row_size - chunk_size);
|
||||
}
|
||||
memcpy(row_buffer + row_offset, buffer, chunk_size);
|
||||
LOG_DEBUG("offset / row: 0x%08" PRIx32 " / %" PRIu32 ", size %" PRIu32 "",
|
||||
offset, row_offset, chunk_size);
|
||||
|
||||
uint32_t macro_idx = row_num / PSOC4_ROWS_PER_MACRO;
|
||||
|
||||
/* Call "Load Latch" system ROM API */
|
||||
sysrq_buffer[1] = psoc4_info->row_size - 1;
|
||||
retval = psoc4_sysreq(target, PSOC4_CMD_LOAD_LATCH,
|
||||
0, /* Byte number in latch from what to write */
|
||||
sysrq_buffer, param_sz + psoc4_info->row_size);
|
||||
target_buffer_set_u32(target, (uint8_t *)(sysrq_buffer + 1),
|
||||
psoc4_info->row_size - 1);
|
||||
retval = psoc4_sysreq(bank, PSOC4_CMD_LOAD_LATCH,
|
||||
0 /* Byte number in latch from what to write */
|
||||
| (macro_idx << 8),
|
||||
sysrq_buffer, param_sz + psoc4_info->row_size,
|
||||
NULL);
|
||||
if (retval != ERROR_OK)
|
||||
goto cleanup;
|
||||
|
||||
/* Call "Program Row" or "Write Row" system ROM API */
|
||||
uint32_t sysrq_param;
|
||||
retval = psoc4_sysreq(target, psoc4_info->cmd_program_row,
|
||||
retval = psoc4_sysreq(bank, psoc4_info->cmd_program_row,
|
||||
row_num & 0xffff,
|
||||
&sysrq_param, sizeof(sysrq_param));
|
||||
&sysrq_param, sizeof(sysrq_param),
|
||||
NULL);
|
||||
if (retval != ERROR_OK)
|
||||
goto cleanup;
|
||||
|
||||
@@ -589,84 +721,82 @@ cleanup:
|
||||
}
|
||||
|
||||
|
||||
/* Due to Cypress's method of market segmentation some devices
|
||||
* have accessible only 1/2, 1/4 or 1/8 of SPCIF described flash */
|
||||
static int psoc4_test_flash_wounding(struct target *target, uint32_t flash_size)
|
||||
{
|
||||
int retval, i;
|
||||
for (i = 3; i >= 1; i--) {
|
||||
uint32_t addr = flash_size >> i;
|
||||
uint32_t dummy;
|
||||
retval = target_read_u32(target, addr, &dummy);
|
||||
if (retval != ERROR_OK)
|
||||
return i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int psoc4_probe(struct flash_bank *bank)
|
||||
{
|
||||
struct psoc4_flash_bank *psoc4_info = bank->driver_priv;
|
||||
struct target *target = bank->target;
|
||||
uint32_t flash_size_in_kb = 0;
|
||||
uint32_t max_flash_size_in_kb;
|
||||
uint32_t cpu_id;
|
||||
uint32_t silicon_id;
|
||||
uint32_t row_size;
|
||||
uint32_t base_address = 0x00000000;
|
||||
uint8_t protection;
|
||||
|
||||
if (target->state != TARGET_HALTED) {
|
||||
LOG_ERROR("Target not halted");
|
||||
return ERROR_TARGET_NOT_HALTED;
|
||||
}
|
||||
int retval;
|
||||
uint16_t family_id;
|
||||
|
||||
psoc4_info->probed = 0;
|
||||
psoc4_info->cmd_program_row = PSOC4_CMD_PROGRAM_ROW;
|
||||
psoc4_info->probed = false;
|
||||
|
||||
/* Get the CPUID from the ARM Core
|
||||
* http://infocenter.arm.com/help/topic/com.arm.doc.ddi0432c/DDI0432C_cortex_m0_r0p0_trm.pdf 4.2.1 */
|
||||
int retval = target_read_u32(target, 0xE000ED00, &cpu_id);
|
||||
retval = psoc4_get_family(target, &family_id);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
LOG_DEBUG("cpu id = 0x%08" PRIx32 "", cpu_id);
|
||||
const struct psoc4_chip_family *family = psoc4_family_by_id(family_id);
|
||||
|
||||
/* set page size, protection granularity and max flash size depending on family */
|
||||
switch ((cpu_id >> 4) & 0xFFF) {
|
||||
case 0xc20: /* M0 -> PSoC4 */
|
||||
row_size = 128;
|
||||
max_flash_size_in_kb = 32;
|
||||
break;
|
||||
default:
|
||||
LOG_WARNING("Cannot identify target as a PSoC 4 family.");
|
||||
if (family->id == 0) {
|
||||
LOG_ERROR("Cannot identify PSoC 4 family.");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
uint32_t spcif_geometry;
|
||||
retval = target_read_u32(target, PSOC4_SPCIF_GEOMETRY, &spcif_geometry);
|
||||
if (retval == ERROR_OK) {
|
||||
row_size = 128 * ((spcif_geometry >> 22) & 3);
|
||||
flash_size_in_kb = (spcif_geometry & 0xffff) * 256 / 1024;
|
||||
LOG_INFO("SPCIF geometry: %" PRIu32 " kb flash, row %" PRIu32 " bytes.",
|
||||
flash_size_in_kb, row_size);
|
||||
if (family->flags & PSOC4_FAMILY_FLAG_LEGACY) {
|
||||
LOG_INFO("%s legacy family detected.", family->name);
|
||||
psoc4_info->legacy_family = true;
|
||||
psoc4_info->cpuss_sysreq_addr = PSOC4_CPUSS_SYSREQ_LEGACY;
|
||||
psoc4_info->cpuss_sysarg_addr = PSOC4_CPUSS_SYSARG_LEGACY;
|
||||
psoc4_info->spcif_geometry_addr = PSOC4_SPCIF_GEOMETRY_LEGACY;
|
||||
} else {
|
||||
LOG_INFO("%s family detected.", family->name);
|
||||
psoc4_info->legacy_family = false;
|
||||
psoc4_info->cpuss_sysreq_addr = PSOC4_CPUSS_SYSREQ_NEW;
|
||||
psoc4_info->cpuss_sysarg_addr = PSOC4_CPUSS_SYSARG_NEW;
|
||||
psoc4_info->spcif_geometry_addr = PSOC4_SPCIF_GEOMETRY_NEW;
|
||||
}
|
||||
|
||||
/* Early revisions of ST-Link v2 have some problem reading PSOC4_SPCIF_GEOMETRY
|
||||
and an error is reported late. Dummy read gets this error. */
|
||||
uint32_t dummy;
|
||||
target_read_u32(target, PSOC4_CPUSS_SYSREQ, &dummy);
|
||||
|
||||
/* get silicon ID from target. */
|
||||
retval = psoc4_get_silicon_id(target, &silicon_id, &protection);
|
||||
uint32_t spcif_geometry;
|
||||
retval = target_read_u32(target, psoc4_info->spcif_geometry_addr, &spcif_geometry);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
const struct psoc4_chip_details *details = psoc4_details_by_id(silicon_id);
|
||||
if (details) {
|
||||
LOG_INFO("%s device detected.", details->type);
|
||||
if (flash_size_in_kb == 0)
|
||||
flash_size_in_kb = details->flash_size_in_kb;
|
||||
else if (flash_size_in_kb != details->flash_size_in_kb)
|
||||
LOG_ERROR("Flash size mismatch");
|
||||
uint32_t flash_size_in_kb = spcif_geometry & 0x3fff;
|
||||
/* TRM of legacy, M and L version describes FLASH field as 16-bit.
|
||||
* S-series and PSoC Analog Coprocessor changes spec to 14-bit only.
|
||||
* Impose PSoC Analog Coprocessor limit to all devices as it
|
||||
* does not make any harm: flash size is safely below 4 MByte limit
|
||||
*/
|
||||
uint32_t row_size = (spcif_geometry >> 22) & 3;
|
||||
uint32_t num_macros = (spcif_geometry >> 20) & 3;
|
||||
|
||||
if (psoc4_info->legacy_family) {
|
||||
flash_size_in_kb = flash_size_in_kb * 256 / 1024;
|
||||
row_size *= 128;
|
||||
} else {
|
||||
flash_size_in_kb = (flash_size_in_kb + 1) * 256 / 1024;
|
||||
row_size = 64 * (row_size + 1);
|
||||
num_macros++;
|
||||
}
|
||||
|
||||
psoc4_info->row_size = row_size;
|
||||
psoc4_info->silicon_id = silicon_id;
|
||||
psoc4_info->chip_protection = protection;
|
||||
|
||||
/* failed reading flash size or flash size invalid (early silicon),
|
||||
* default to max target family */
|
||||
if (retval != ERROR_OK || flash_size_in_kb == 0xffff || flash_size_in_kb == 0) {
|
||||
LOG_WARNING("PSoC 4 flash size failed, probe inaccurate - assuming %" PRIu32 " k flash",
|
||||
max_flash_size_in_kb);
|
||||
flash_size_in_kb = max_flash_size_in_kb;
|
||||
}
|
||||
LOG_DEBUG("SPCIF geometry: %" PRIu32 " kb flash, row %" PRIu32 " bytes.",
|
||||
flash_size_in_kb, row_size);
|
||||
|
||||
/* if the user sets the size manually then ignore the probed value
|
||||
* this allows us to work around devices that have a invalid flash size register value */
|
||||
@@ -675,37 +805,44 @@ static int psoc4_probe(struct flash_bank *bank)
|
||||
flash_size_in_kb = psoc4_info->user_bank_size / 1024;
|
||||
}
|
||||
|
||||
LOG_INFO("flash size = %" PRIu32 " kbytes", flash_size_in_kb);
|
||||
char macros_txt[20] = "";
|
||||
if (num_macros > 1)
|
||||
snprintf(macros_txt, sizeof(macros_txt), " in %" PRIu32 " macros", num_macros);
|
||||
|
||||
/* did we assign flash size? */
|
||||
assert(flash_size_in_kb != 0xffff);
|
||||
LOG_INFO("flash size = %" PRIu32 " kbytes%s", flash_size_in_kb, macros_txt);
|
||||
|
||||
/* calculate numbers of pages */
|
||||
/* calculate number of pages */
|
||||
uint32_t num_rows = flash_size_in_kb * 1024 / row_size;
|
||||
|
||||
/* check that calculation result makes sense */
|
||||
assert(num_rows > 0);
|
||||
/* check number of flash macros */
|
||||
if (num_macros != (num_rows + PSOC4_ROWS_PER_MACRO - 1) / PSOC4_ROWS_PER_MACRO)
|
||||
LOG_WARNING("Number of macros does not correspond with flash size!");
|
||||
|
||||
if (!psoc4_info->legacy_family) {
|
||||
int wounding = psoc4_test_flash_wounding(target, num_rows * row_size);
|
||||
if (wounding > 0) {
|
||||
flash_size_in_kb = flash_size_in_kb >> wounding;
|
||||
num_rows = num_rows >> wounding;
|
||||
LOG_INFO("WOUNDING detected: accessible flash size %" PRIu32 " kbytes", flash_size_in_kb);
|
||||
}
|
||||
}
|
||||
|
||||
if (bank->sectors) {
|
||||
free(bank->sectors);
|
||||
bank->sectors = NULL;
|
||||
}
|
||||
|
||||
bank->base = base_address;
|
||||
psoc4_info->family_id = family_id;
|
||||
psoc4_info->num_macros = num_macros;
|
||||
psoc4_info->row_size = row_size;
|
||||
bank->base = 0x00000000;
|
||||
bank->size = num_rows * row_size;
|
||||
bank->num_sectors = num_rows;
|
||||
bank->sectors = malloc(sizeof(struct flash_sector) * num_rows);
|
||||
bank->sectors = alloc_block_array(0, row_size, num_rows);
|
||||
if (bank->sectors == NULL)
|
||||
return ERROR_FAIL;
|
||||
|
||||
uint32_t i;
|
||||
for (i = 0; i < num_rows; i++) {
|
||||
bank->sectors[i].offset = i * row_size;
|
||||
bank->sectors[i].size = row_size;
|
||||
bank->sectors[i].is_erased = -1;
|
||||
bank->sectors[i].is_protected = 1;
|
||||
}
|
||||
|
||||
LOG_INFO("flash bank set %" PRIu32 " rows", num_rows);
|
||||
psoc4_info->probed = 1;
|
||||
LOG_DEBUG("flash bank set %" PRIu32 " rows", num_rows);
|
||||
psoc4_info->probed = true;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
@@ -721,28 +858,45 @@ static int psoc4_auto_probe(struct flash_bank *bank)
|
||||
|
||||
static int get_psoc4_info(struct flash_bank *bank, char *buf, int buf_size)
|
||||
{
|
||||
struct target *target = bank->target;
|
||||
struct psoc4_flash_bank *psoc4_info = bank->driver_priv;
|
||||
int printed = 0;
|
||||
|
||||
if (psoc4_info->probed == 0)
|
||||
if (!psoc4_info->probed)
|
||||
return ERROR_FAIL;
|
||||
|
||||
const struct psoc4_chip_details *details = psoc4_details_by_id(psoc4_info->silicon_id);
|
||||
const struct psoc4_chip_family *family = psoc4_family_by_id(psoc4_info->family_id);
|
||||
uint32_t size_in_kb = bank->size / 1024;
|
||||
|
||||
if (details) {
|
||||
uint32_t chip_revision = psoc4_info->silicon_id & 0xffff;
|
||||
printed = snprintf(buf, buf_size, "PSoC 4 %s rev 0x%04" PRIx32 " package %s",
|
||||
details->type, chip_revision, details->package);
|
||||
} else
|
||||
printed = snprintf(buf, buf_size, "PSoC 4 silicon id 0x%08" PRIx32 "",
|
||||
psoc4_info->silicon_id);
|
||||
if (target->state != TARGET_HALTED) {
|
||||
snprintf(buf, buf_size, "%s, flash %" PRIu32 " kb"
|
||||
" (halt target to see details)", family->name, size_in_kb);
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int retval;
|
||||
int printed = 0;
|
||||
uint32_t silicon_id;
|
||||
uint16_t family_id;
|
||||
uint8_t protection;
|
||||
|
||||
retval = psoc4_get_silicon_id(bank, &silicon_id, &family_id, &protection);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
if (family_id != psoc4_info->family_id)
|
||||
printed = snprintf(buf, buf_size, "Family id mismatch 0x%02" PRIx16
|
||||
"/0x%02" PRIx16 ", silicon id 0x%08" PRIx32,
|
||||
psoc4_info->family_id, family_id, silicon_id);
|
||||
else {
|
||||
printed = snprintf(buf, buf_size, "%s silicon id 0x%08" PRIx32 "",
|
||||
family->name, silicon_id);
|
||||
}
|
||||
|
||||
buf += printed;
|
||||
buf_size -= printed;
|
||||
|
||||
const char *prot_txt = psoc4_decode_chip_protection(psoc4_info->chip_protection);
|
||||
uint32_t size_in_kb = bank->size / 1024;
|
||||
snprintf(buf, buf_size, " flash %" PRIu32 " kb %s", size_in_kb, prot_txt);
|
||||
const char *prot_txt = psoc4_decode_chip_protection(protection);
|
||||
snprintf(buf, buf_size, ", flash %" PRIu32 " kb %s", size_in_kb, prot_txt);
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
@@ -809,4 +963,5 @@ struct flash_driver psoc4_flash = {
|
||||
.erase_check = default_flash_blank_check,
|
||||
.protect_check = psoc4_protect_check,
|
||||
.info = get_psoc4_info,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1122,5 +1122,6 @@ struct flash_driver sim3x_flash = {
|
||||
.auto_probe = sim3x_auto_probe,
|
||||
.erase_check = default_flash_blank_check,
|
||||
.protect_check = sim3x_flash_protect_check,
|
||||
.info = sim3x_flash_info
|
||||
.info = sim3x_flash_info,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
|
||||
@@ -54,6 +54,7 @@ const struct flash_device flash_devices[] = {
|
||||
FLASH_ID("sp s25fl164k", 0xd8, 0xc7, 0x00174001, 0x100, 0x10000, 0x800000),
|
||||
FLASH_ID("sp s25fl128", 0xd8, 0xc7, 0x00182001, 0x100, 0x10000, 0x1000000),
|
||||
FLASH_ID("sp s25fl256", 0xd8, 0xc7, 0x00190201, 0x100, 0x10000, 0x2000000),
|
||||
FLASH_ID("cy s25fl256", 0xd8, 0xc7, 0x00196001, 0x100, 0x10000, 0x2000000),
|
||||
FLASH_ID("atmel 25f512", 0x52, 0xc7, 0x0065001f, 0x80, 0x8000, 0x10000),
|
||||
FLASH_ID("atmel 25f1024", 0x52, 0x62, 0x0060001f, 0x100, 0x8000, 0x20000),
|
||||
FLASH_ID("atmel 25f2048", 0x52, 0x62, 0x0063001f, 0x100, 0x10000, 0x40000),
|
||||
|
||||
@@ -1452,4 +1452,5 @@ struct flash_driver stellaris_flash = {
|
||||
.erase_check = default_flash_blank_check,
|
||||
.protect_check = stellaris_protect_check,
|
||||
.info = get_stellaris_info,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
|
||||
@@ -572,45 +572,8 @@ static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer,
|
||||
struct armv7m_algorithm armv7m_info;
|
||||
int retval = ERROR_OK;
|
||||
|
||||
/* see contrib/loaders/flash/stm32f1x.S for src */
|
||||
|
||||
static const uint8_t stm32x_flash_write_code[] = {
|
||||
/* #define STM32_FLASH_SR_OFFSET 0x0C */
|
||||
/* wait_fifo: */
|
||||
0x16, 0x68, /* ldr r6, [r2, #0] */
|
||||
0x00, 0x2e, /* cmp r6, #0 */
|
||||
0x18, 0xd0, /* beq exit */
|
||||
0x55, 0x68, /* ldr r5, [r2, #4] */
|
||||
0xb5, 0x42, /* cmp r5, r6 */
|
||||
0xf9, 0xd0, /* beq wait_fifo */
|
||||
0x2e, 0x88, /* ldrh r6, [r5, #0] */
|
||||
0x26, 0x80, /* strh r6, [r4, #0] */
|
||||
0x02, 0x35, /* adds r5, #2 */
|
||||
0x02, 0x34, /* adds r4, #2 */
|
||||
/* busy: */
|
||||
0xc6, 0x68, /* ldr r6, [r0, #STM32_FLASH_SR_OFFSET] */
|
||||
0x01, 0x27, /* movs r7, #1 */
|
||||
0x3e, 0x42, /* tst r6, r7 */
|
||||
0xfb, 0xd1, /* bne busy */
|
||||
0x14, 0x27, /* movs r7, #0x14 */
|
||||
0x3e, 0x42, /* tst r6, r7 */
|
||||
0x08, 0xd1, /* bne error */
|
||||
0x9d, 0x42, /* cmp r5, r3 */
|
||||
0x01, 0xd3, /* bcc no_wrap */
|
||||
0x15, 0x46, /* mov r5, r2 */
|
||||
0x08, 0x35, /* adds r5, #8 */
|
||||
/* no_wrap: */
|
||||
0x55, 0x60, /* str r5, [r2, #4] */
|
||||
0x01, 0x39, /* subs r1, r1, #1 */
|
||||
0x00, 0x29, /* cmp r1, #0 */
|
||||
0x02, 0xd0, /* beq exit */
|
||||
0xe5, 0xe7, /* b wait_fifo */
|
||||
/* error: */
|
||||
0x00, 0x20, /* movs r0, #0 */
|
||||
0x50, 0x60, /* str r0, [r2, #4] */
|
||||
/* exit: */
|
||||
0x30, 0x46, /* mov r0, r6 */
|
||||
0x00, 0xbe, /* bkpt #0 */
|
||||
#include "../../../contrib/loaders/flash/stm32/stm32f1x.inc"
|
||||
};
|
||||
|
||||
/* flash write code */
|
||||
@@ -622,8 +585,10 @@ static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer,
|
||||
|
||||
retval = target_write_buffer(target, write_algorithm->address,
|
||||
sizeof(stm32x_flash_write_code), stm32x_flash_write_code);
|
||||
if (retval != ERROR_OK)
|
||||
if (retval != ERROR_OK) {
|
||||
target_free_working_area(target, write_algorithm);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* memory buffer */
|
||||
while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) {
|
||||
@@ -1647,4 +1612,5 @@ struct flash_driver stm32f1x_flash = {
|
||||
.erase_check = default_flash_blank_check,
|
||||
.protect_check = stm32x_protect_check,
|
||||
.info = get_stm32x_info,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
|
||||
@@ -252,6 +252,8 @@ static int stm32x_wait_status_busy(struct flash_bank *bank, int timeout)
|
||||
|
||||
/* Clear but report errors */
|
||||
if (status & FLASH_ERROR) {
|
||||
if (retval == ERROR_OK)
|
||||
retval = ERROR_FAIL;
|
||||
/* If this operation fails, we ignore it and report the original
|
||||
* retval
|
||||
*/
|
||||
@@ -584,45 +586,8 @@ static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer,
|
||||
struct armv7m_algorithm armv7m_info;
|
||||
int retval = ERROR_OK;
|
||||
|
||||
/* see contrib/loaders/flash/stm32f2x.S for src */
|
||||
|
||||
static const uint8_t stm32x_flash_write_code[] = {
|
||||
/* wait_fifo: */
|
||||
0xD0, 0xF8, 0x00, 0x80, /* ldr r8, [r0, #0] */
|
||||
0xB8, 0xF1, 0x00, 0x0F, /* cmp r8, #0 */
|
||||
0x1A, 0xD0, /* beq exit */
|
||||
0x47, 0x68, /* ldr r7, [r0, #4] */
|
||||
0x47, 0x45, /* cmp r7, r8 */
|
||||
0xF7, 0xD0, /* beq wait_fifo */
|
||||
|
||||
0xDF, 0xF8, 0x34, 0x60, /* ldr r6, STM32_PROG16 */
|
||||
0x26, 0x61, /* str r6, [r4, #STM32_FLASH_CR_OFFSET] */
|
||||
0x37, 0xF8, 0x02, 0x6B, /* ldrh r6, [r7], #0x02 */
|
||||
0x22, 0xF8, 0x02, 0x6B, /* strh r6, [r2], #0x02 */
|
||||
0xBF, 0xF3, 0x4F, 0x8F, /* dsb sy */
|
||||
/* busy: */
|
||||
0xE6, 0x68, /* ldr r6, [r4, #STM32_FLASH_SR_OFFSET] */
|
||||
0x16, 0xF4, 0x80, 0x3F, /* tst r6, #0x10000 */
|
||||
0xFB, 0xD1, /* bne busy */
|
||||
0x16, 0xF0, 0xF0, 0x0F, /* tst r6, #0xf0 */
|
||||
0x07, 0xD1, /* bne error */
|
||||
|
||||
0x8F, 0x42, /* cmp r7, r1 */
|
||||
0x28, 0xBF, /* it cs */
|
||||
0x00, 0xF1, 0x08, 0x07, /* addcs r7, r0, #8 */
|
||||
0x47, 0x60, /* str r7, [r0, #4] */
|
||||
0x01, 0x3B, /* subs r3, r3, #1 */
|
||||
0x13, 0xB1, /* cbz r3, exit */
|
||||
0xDF, 0xE7, /* b wait_fifo */
|
||||
/* error: */
|
||||
0x00, 0x21, /* movs r1, #0 */
|
||||
0x41, 0x60, /* str r1, [r0, #4] */
|
||||
/* exit: */
|
||||
0x30, 0x46, /* mov r0, r6 */
|
||||
0x00, 0xBE, /* bkpt #0x00 */
|
||||
|
||||
/* <STM32_PROG16>: */
|
||||
0x01, 0x01, 0x00, 0x00, /* .word 0x00000101 */
|
||||
#include "../../../contrib/loaders/flash/stm32/stm32f2x.inc"
|
||||
};
|
||||
|
||||
if (target_alloc_working_area(target, sizeof(stm32x_flash_write_code),
|
||||
@@ -634,8 +599,10 @@ static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer,
|
||||
retval = target_write_buffer(target, write_algorithm->address,
|
||||
sizeof(stm32x_flash_write_code),
|
||||
stm32x_flash_write_code);
|
||||
if (retval != ERROR_OK)
|
||||
if (retval != ERROR_OK) {
|
||||
target_free_working_area(target, write_algorithm);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* memory buffer */
|
||||
while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) {
|
||||
@@ -1634,4 +1601,5 @@ struct flash_driver stm32f2x_flash = {
|
||||
.erase_check = default_flash_blank_check,
|
||||
.protect_check = stm32x_protect_check,
|
||||
.info = get_stm32x_info,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
#define FLASH_WRPERR (1 << 17) /* Write protection error */
|
||||
#define FLASH_PGSERR (1 << 18) /* Programming sequence error */
|
||||
#define FLASH_STRBERR (1 << 19) /* Strobe error */
|
||||
#define FLASH_INCERR (1 << 21) /* Increment error */
|
||||
#define FLASH_INCERR (1 << 21) /* Inconsistency error */
|
||||
#define FLASH_OPERR (1 << 22) /* Operation error */
|
||||
#define FLASH_RDPERR (1 << 23) /* Read Protection error */
|
||||
#define FLASH_RDSERR (1 << 24) /* Secure Protection error */
|
||||
@@ -220,6 +220,8 @@ static int stm32x_wait_status_busy(struct flash_bank *bank, int timeout)
|
||||
|
||||
/* Clear error + EOP flags but report errors */
|
||||
if (status & FLASH_ERROR) {
|
||||
if (retval == ERROR_OK)
|
||||
retval = ERROR_FAIL;
|
||||
/* If this operation fails, we ignore it and report the original retval */
|
||||
target_write_u32(target, stm32x_get_flash_reg(bank, FLASH_CCR), status);
|
||||
}
|
||||
@@ -495,7 +497,7 @@ static int stm32x_erase(struct flash_bank *bank, int first, int last)
|
||||
retval = stm32x_wait_status_busy(bank, FLASH_ERASE_TIMEOUT);
|
||||
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("erase time-out error sector %d", i);
|
||||
LOG_ERROR("erase time-out or operation error sector %d", i);
|
||||
return retval;
|
||||
}
|
||||
bank->sectors[i].is_erased = 1;
|
||||
@@ -568,51 +570,8 @@ static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer,
|
||||
struct stm32h7x_flash_bank *stm32x_info = bank->driver_priv;
|
||||
int retval = ERROR_OK;
|
||||
|
||||
/* see contrib/loaders/flash/smt32h7x.S for src */
|
||||
static const uint8_t stm32x_flash_write_code[] = {
|
||||
/* <code>: */
|
||||
0x45, 0x68, /* ldr r5, [r0, #4] */
|
||||
/* <wait_fifo>: */
|
||||
0x06, 0x68, /* ldr r6, [r0, #0] */
|
||||
0x26, 0xb3, /* cbz r6, <exit> */
|
||||
0x76, 0x1b, /* subs r6, r6, r5 */
|
||||
0x42, 0xbf, /* ittt mi */
|
||||
0x76, 0x18, /* addmi r6, r6, r1 */
|
||||
0x36, 0x1a, /* submi r6, r6, r0 */
|
||||
0x08, 0x3e, /* submi r6, #8 */
|
||||
0x20, 0x2e, /* cmp r6, #32 */
|
||||
0xf6, 0xd3, /* bcc.n <wait_fifo> */
|
||||
0x4f, 0xf0, 0x32, 0x06, /* mov.w r6, #STM32_PROG */
|
||||
0xe6, 0x60, /* str r6, [r4, #STM32_FLASH_CR_OFFSET] */
|
||||
0x4f, 0xf0, 0x08, 0x07, /* mov.w r7, #8 */
|
||||
/* <write_flash>: */
|
||||
0x55, 0xf8, 0x04, 0x6b, /* ldr.w r6, [r5], #4 */
|
||||
0x42, 0xf8, 0x04, 0x6b, /* str.w r6, [r2], #4 */
|
||||
0xbf, 0xf3, 0x4f, 0x8f, /* dsb sy */
|
||||
0x8d, 0x42, /* cmp r5, r1 */
|
||||
0x28, 0xbf, /* it cs */
|
||||
0x00, 0xf1, 0x08, 0x05, /* addcs.w r5, r0, #8 */
|
||||
0x01, 0x3f, /* subs r7, #1 */
|
||||
0xf3, 0xd1, /* bne.n <write_flash> */
|
||||
/* <busy>: */
|
||||
0x26, 0x69, /* ldr r6, [r4, #STM32_FLASH_SR_OFFSET] */
|
||||
0x16, 0xf0, 0x01, 0x0f, /* tst.w r6, #STM32_SR_BUSY_MASK */
|
||||
0xfb, 0xd1, /* bne.n <busy> */
|
||||
0x05, 0x4f, /* ldr r7, [pc, #20] ; (<stm32_sr_error_mask>) */
|
||||
0x3e, 0x42, /* tst r6, r7 */
|
||||
0x03, 0xd1, /* bne.n <error> */
|
||||
0x45, 0x60, /* str r5, [r0, #4] */
|
||||
0x01, 0x3b, /* subs r3, #1 */
|
||||
0xdb, 0xd1, /* bne.n <wait_fifo> */
|
||||
0x01, 0xe0, /* b.n <exit> */
|
||||
/* <error>: */
|
||||
0x00, 0x27, /* movs r7, #0 */
|
||||
0x47, 0x60, /* str r7, [r0, #4] */
|
||||
/* <exit>: */
|
||||
0x30, 0x46, /* mov r0, r6 */
|
||||
0x00, 0xbe, /* bkpt 0x0000 */
|
||||
/* <stm32_sr_error_mask>: */
|
||||
0x00, 0x00, 0xee, 0x03 /* .word 0x03ee0000 ; (STM32_SR_ERROR_MASK) */
|
||||
#include "../../../contrib/loaders/flash/stm32/stm32h7x.inc"
|
||||
};
|
||||
|
||||
if (target_alloc_working_area(target, sizeof(stm32x_flash_write_code),
|
||||
@@ -624,8 +583,10 @@ static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer,
|
||||
retval = target_write_buffer(target, write_algorithm->address,
|
||||
sizeof(stm32x_flash_write_code),
|
||||
stm32x_flash_write_code);
|
||||
if (retval != ERROR_OK)
|
||||
if (retval != ERROR_OK) {
|
||||
target_free_working_area(target, write_algorithm);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* memory buffer */
|
||||
while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) {
|
||||
@@ -1180,4 +1141,5 @@ struct flash_driver stm32h7x_flash = {
|
||||
.erase_check = default_flash_blank_check,
|
||||
.protect_check = stm32x_protect_check,
|
||||
.info = stm32x_get_info,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
|
||||
@@ -187,6 +187,8 @@ static int stm32l4_wait_status_busy(struct flash_bank *bank, int timeout)
|
||||
|
||||
/* Clear but report errors */
|
||||
if (status & FLASH_ERROR) {
|
||||
if (retval == ERROR_OK)
|
||||
retval = ERROR_FAIL;
|
||||
/* If this operation fails, we ignore it and report the original
|
||||
* retval
|
||||
*/
|
||||
@@ -461,19 +463,8 @@ static int stm32l4_write_block(struct flash_bank *bank, const uint8_t *buffer,
|
||||
struct armv7m_algorithm armv7m_info;
|
||||
int retval = ERROR_OK;
|
||||
|
||||
/* See contrib/loaders/flash/stm32l4x.S for source and
|
||||
* hints how to generate the data!
|
||||
*/
|
||||
|
||||
static const uint8_t stm32l4_flash_write_code[] = {
|
||||
0xd0, 0xf8, 0x00, 0x80, 0xb8, 0xf1, 0x00, 0x0f, 0x21, 0xd0, 0x45, 0x68,
|
||||
0xb8, 0xeb, 0x05, 0x06, 0x44, 0xbf, 0x76, 0x18, 0x36, 0x1a, 0x08, 0x2e,
|
||||
0xf2, 0xd3, 0xdf, 0xf8, 0x36, 0x60, 0x66, 0x61, 0xf5, 0xe8, 0x02, 0x67,
|
||||
0xe2, 0xe8, 0x02, 0x67, 0xbf, 0xf3, 0x4f, 0x8f, 0x26, 0x69, 0x16, 0xf4,
|
||||
0x80, 0x3f, 0xfb, 0xd1, 0x16, 0xf0, 0xfa, 0x0f, 0x07, 0xd1, 0x8d, 0x42,
|
||||
0x28, 0xbf, 0x00, 0xf1, 0x08, 0x05, 0x45, 0x60, 0x01, 0x3b, 0x13, 0xb1,
|
||||
0xda, 0xe7, 0x00, 0x21, 0x41, 0x60, 0x30, 0x46, 0x00, 0xbe, 0x01, 0x00,
|
||||
0x00, 0x00
|
||||
#include "../../../contrib/loaders/flash/stm32/stm32l4x.inc"
|
||||
};
|
||||
|
||||
if (target_alloc_working_area(target, sizeof(stm32l4_flash_write_code),
|
||||
@@ -485,8 +476,10 @@ static int stm32l4_write_block(struct flash_bank *bank, const uint8_t *buffer,
|
||||
retval = target_write_buffer(target, write_algorithm->address,
|
||||
sizeof(stm32l4_flash_write_code),
|
||||
stm32l4_flash_write_code);
|
||||
if (retval != ERROR_OK)
|
||||
if (retval != ERROR_OK) {
|
||||
target_free_working_area(target, write_algorithm);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* memory buffer */
|
||||
while (target_alloc_working_area_try(target, buffer_size, &source) !=
|
||||
@@ -953,4 +946,5 @@ struct flash_driver stm32l4x_flash = {
|
||||
.erase_check = default_flash_blank_check,
|
||||
.protect_check = stm32l4_protect_check,
|
||||
.info = get_stm32l4_info,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
|
||||
@@ -146,7 +146,7 @@ static const struct stm32lx_rev stm32_425_revs[] = {
|
||||
{ 0x1000, "A" }, { 0x2000, "B" }, { 0x2008, "Y" },
|
||||
};
|
||||
static const struct stm32lx_rev stm32_427_revs[] = {
|
||||
{ 0x1000, "A" }, { 0x1018, "Y" }, { 0x1038, "X" },
|
||||
{ 0x1000, "A" }, { 0x1018, "Y" }, { 0x1038, "X" }, { 0x10f8, "V" },
|
||||
};
|
||||
static const struct stm32lx_rev stm32_429_revs[] = {
|
||||
{ 0x1000, "A" }, { 0x1018, "Z" },
|
||||
@@ -448,10 +448,8 @@ static int stm32lx_write_half_pages(struct flash_bank *bank, const uint8_t *buff
|
||||
|
||||
int retval = ERROR_OK;
|
||||
|
||||
/* see contib/loaders/flash/stm32lx.S for src */
|
||||
|
||||
static const uint8_t stm32lx_flash_write_code[] = {
|
||||
0x92, 0x00, 0x8A, 0x18, 0x01, 0xE0, 0x08, 0xC9, 0x08, 0xC0, 0x91, 0x42, 0xFB, 0xD1, 0x00, 0xBE
|
||||
#include "../../../contrib/loaders/flash/stm32/stm32lx.inc"
|
||||
};
|
||||
|
||||
/* Make sure we're performing a half-page aligned write. */
|
||||
@@ -965,6 +963,7 @@ struct flash_driver stm32lx_flash = {
|
||||
.erase_check = default_flash_blank_check,
|
||||
.protect_check = stm32lx_protect_check,
|
||||
.info = stm32lx_get_info,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
|
||||
/* Static methods implementation */
|
||||
|
||||
@@ -654,4 +654,5 @@ struct flash_driver stmsmi_flash = {
|
||||
.erase_check = default_flash_blank_check,
|
||||
.protect_check = stmsmi_protect_check,
|
||||
.info = get_stmsmi_info,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
|
||||
@@ -812,4 +812,5 @@ struct flash_driver str7x_flash = {
|
||||
.erase_check = default_flash_blank_check,
|
||||
.protect_check = str7x_protect_check,
|
||||
.info = get_str7x_info,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
|
||||
@@ -679,4 +679,5 @@ struct flash_driver str9x_flash = {
|
||||
.auto_probe = str9x_probe,
|
||||
.erase_check = default_flash_blank_check,
|
||||
.protect_check = str9x_protect_check,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
|
||||
@@ -1207,4 +1207,5 @@ struct flash_driver str9xpec_flash = {
|
||||
.auto_probe = str9xpec_probe,
|
||||
.erase_check = str9xpec_erase_check,
|
||||
.protect_check = str9xpec_protect_check,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
|
||||
+157
-107
@@ -3,6 +3,7 @@
|
||||
* Copyright (C) 2007,2008 Øyvind Harboe <oyvind.harboe@zylin.com> *
|
||||
* Copyright (C) 2008 by Spencer Oliver <spen@spen-soft.co.uk> *
|
||||
* Copyright (C) 2009 Zachary T Welch <zw@superlucidity.net> *
|
||||
* Copyright (C) 2017-2018 Tomas Vanek <vanekt@fbl.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 *
|
||||
@@ -287,24 +288,6 @@ COMMAND_HANDLER(handle_flash_erase_address_command)
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int flash_check_sector_parameters(struct command_context *cmd_ctx,
|
||||
uint32_t first, uint32_t last, uint32_t num_sectors)
|
||||
{
|
||||
if (!(first <= last)) {
|
||||
command_print(cmd_ctx, "ERROR: "
|
||||
"first sector must be <= last sector");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (!(last <= (num_sectors - 1))) {
|
||||
command_print(cmd_ctx, "ERROR: last sector must be <= %" PRIu32,
|
||||
num_sectors - 1);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
COMMAND_HANDLER(handle_flash_erase_command)
|
||||
{
|
||||
if (CMD_ARGC != 3)
|
||||
@@ -326,9 +309,18 @@ COMMAND_HANDLER(handle_flash_erase_command)
|
||||
else
|
||||
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], last);
|
||||
|
||||
retval = flash_check_sector_parameters(CMD_CTX, first, last, p->num_sectors);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
if (!(first <= last)) {
|
||||
command_print(CMD_CTX, "ERROR: "
|
||||
"first sector must be <= last");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (!(last <= (uint32_t)(p->num_sectors - 1))) {
|
||||
command_print(CMD_CTX, "ERROR: "
|
||||
"last sector must be <= %" PRIu32,
|
||||
p->num_sectors - 1);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
struct duration bench;
|
||||
duration_start(&bench);
|
||||
@@ -374,15 +366,28 @@ COMMAND_HANDLER(handle_flash_protect_command)
|
||||
bool set;
|
||||
COMMAND_PARSE_ON_OFF(CMD_ARGV[3], set);
|
||||
|
||||
retval = flash_check_sector_parameters(CMD_CTX, first, last, num_blocks);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
if (!(first <= last)) {
|
||||
command_print(CMD_CTX, "ERROR: "
|
||||
"first %s must be <= last",
|
||||
(p->num_prot_blocks) ? "block" : "sector");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (!(last <= (uint32_t)(num_blocks - 1))) {
|
||||
command_print(CMD_CTX, "ERROR: "
|
||||
"last %s must be <= %" PRIu32,
|
||||
(p->num_prot_blocks) ? "block" : "sector",
|
||||
num_blocks - 1);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
retval = flash_driver_protect(p, set, first, last);
|
||||
if (retval == ERROR_OK) {
|
||||
command_print(CMD_CTX, "%s protection for sectors %" PRIu32
|
||||
command_print(CMD_CTX, "%s protection for %s %" PRIu32
|
||||
" through %" PRIu32 " on flash bank %d",
|
||||
(set) ? "set" : "cleared", first, last, p->bank_number);
|
||||
(set) ? "set" : "cleared",
|
||||
(p->num_prot_blocks) ? "blocks" : "sectors",
|
||||
first, last, p->bank_number);
|
||||
}
|
||||
|
||||
return retval;
|
||||
@@ -460,42 +465,29 @@ COMMAND_HANDLER(handle_flash_write_image_command)
|
||||
|
||||
COMMAND_HANDLER(handle_flash_fill_command)
|
||||
{
|
||||
int err = ERROR_OK;
|
||||
uint32_t address;
|
||||
target_addr_t address;
|
||||
uint32_t pattern;
|
||||
uint32_t count;
|
||||
uint32_t wrote = 0;
|
||||
uint32_t cur_size = 0;
|
||||
uint32_t chunk_count;
|
||||
struct target *target = get_current_target(CMD_CTX);
|
||||
unsigned i;
|
||||
uint32_t wordsize;
|
||||
int retval = ERROR_OK;
|
||||
int retval;
|
||||
|
||||
static size_t const chunksize = 1024;
|
||||
uint8_t *chunk = NULL, *readback = NULL;
|
||||
|
||||
if (CMD_ARGC != 3) {
|
||||
retval = ERROR_COMMAND_SYNTAX_ERROR;
|
||||
goto done;
|
||||
}
|
||||
if (CMD_ARGC != 3)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
|
||||
#if BUILD_TARGET64
|
||||
COMMAND_PARSE_NUMBER(u64, CMD_ARGV[0], address);
|
||||
#else
|
||||
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], address);
|
||||
#endif
|
||||
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], pattern);
|
||||
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], count);
|
||||
|
||||
chunk = malloc(chunksize);
|
||||
if (chunk == NULL)
|
||||
return ERROR_FAIL;
|
||||
|
||||
readback = malloc(chunksize);
|
||||
if (readback == NULL) {
|
||||
free(chunk);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
goto done;
|
||||
struct flash_bank *bank;
|
||||
retval = get_flash_bank_by_addr(target, address, true, &bank);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
switch (CMD_NAME[4]) {
|
||||
case 'w':
|
||||
@@ -508,73 +500,109 @@ COMMAND_HANDLER(handle_flash_fill_command)
|
||||
wordsize = 1;
|
||||
break;
|
||||
default:
|
||||
retval = ERROR_COMMAND_SYNTAX_ERROR;
|
||||
goto done;
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
chunk_count = MIN(count, (chunksize / wordsize));
|
||||
if (count == 0)
|
||||
return ERROR_OK;
|
||||
|
||||
if (address + count * wordsize > bank->base + bank->size) {
|
||||
LOG_ERROR("Cannot cross flash bank borders");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
uint32_t size_bytes = count * wordsize;
|
||||
target_addr_t aligned_start = flash_write_align_start(bank, address);
|
||||
target_addr_t end_addr = address + size_bytes - 1;
|
||||
target_addr_t aligned_end = flash_write_align_end(bank, end_addr);
|
||||
uint32_t aligned_size = aligned_end + 1 - aligned_start;
|
||||
uint32_t padding_at_start = address - aligned_start;
|
||||
uint32_t padding_at_end = aligned_end - end_addr;
|
||||
|
||||
uint8_t *buffer = malloc(aligned_size);
|
||||
if (buffer == NULL)
|
||||
return ERROR_FAIL;
|
||||
|
||||
if (padding_at_start) {
|
||||
memset(buffer, bank->default_padded_value, padding_at_start);
|
||||
LOG_WARNING("Start address " TARGET_ADDR_FMT
|
||||
" breaks the required alignment of flash bank %s",
|
||||
address, bank->name);
|
||||
LOG_WARNING("Padding %" PRId32 " bytes from " TARGET_ADDR_FMT,
|
||||
padding_at_start, aligned_start);
|
||||
}
|
||||
|
||||
uint8_t *ptr = buffer + padding_at_start;
|
||||
|
||||
switch (wordsize) {
|
||||
case 4:
|
||||
for (i = 0; i < chunk_count; i++)
|
||||
target_buffer_set_u32(target, chunk + i * wordsize, pattern);
|
||||
for (i = 0; i < count; i++, ptr += wordsize)
|
||||
target_buffer_set_u32(target, ptr, pattern);
|
||||
break;
|
||||
case 2:
|
||||
for (i = 0; i < chunk_count; i++)
|
||||
target_buffer_set_u16(target, chunk + i * wordsize, pattern);
|
||||
for (i = 0; i < count; i++, ptr += wordsize)
|
||||
target_buffer_set_u16(target, ptr, pattern);
|
||||
break;
|
||||
case 1:
|
||||
memset(chunk, pattern, chunk_count);
|
||||
memset(ptr, pattern, count);
|
||||
ptr += count;
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("BUG: can't happen");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (padding_at_end) {
|
||||
memset(ptr, bank->default_padded_value, padding_at_end);
|
||||
LOG_INFO("Padding at " TARGET_ADDR_FMT " with %" PRId32
|
||||
" bytes (bank write end alignment)",
|
||||
end_addr + 1, padding_at_end);
|
||||
}
|
||||
|
||||
struct duration bench;
|
||||
duration_start(&bench);
|
||||
|
||||
for (wrote = 0; wrote < (count*wordsize); wrote += cur_size) {
|
||||
struct flash_bank *bank;
|
||||
retval = flash_driver_write(bank, buffer, aligned_start - bank->base, aligned_size);
|
||||
if (retval != ERROR_OK)
|
||||
goto done;
|
||||
|
||||
retval = get_flash_bank_by_addr(target, address, true, &bank);
|
||||
if (retval != ERROR_OK)
|
||||
goto done;
|
||||
retval = flash_driver_read(bank, buffer, address - bank->base, size_bytes);
|
||||
if (retval != ERROR_OK)
|
||||
goto done;
|
||||
|
||||
cur_size = MIN((count * wordsize - wrote), chunksize);
|
||||
err = flash_driver_write(bank, chunk, address - bank->base + wrote, cur_size);
|
||||
if (err != ERROR_OK) {
|
||||
retval = err;
|
||||
for (i = 0, ptr = buffer; i < count; i++) {
|
||||
uint32_t readback = 0;
|
||||
|
||||
switch (wordsize) {
|
||||
case 4:
|
||||
readback = target_buffer_get_u32(target, ptr);
|
||||
break;
|
||||
case 2:
|
||||
readback = target_buffer_get_u16(target, ptr);
|
||||
break;
|
||||
case 1:
|
||||
readback = *ptr;
|
||||
break;
|
||||
}
|
||||
if (readback != pattern) {
|
||||
LOG_ERROR(
|
||||
"Verification error address " TARGET_ADDR_FMT
|
||||
", read back 0x%02" PRIx32 ", expected 0x%02" PRIx32,
|
||||
address + i * wordsize, readback, pattern);
|
||||
retval = ERROR_FAIL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
err = flash_driver_read(bank, readback, address - bank->base + wrote, cur_size);
|
||||
if (err != ERROR_OK) {
|
||||
retval = err;
|
||||
goto done;
|
||||
}
|
||||
|
||||
for (i = 0; i < cur_size; i++) {
|
||||
if (readback[i] != chunk[i]) {
|
||||
LOG_ERROR(
|
||||
"Verification error address 0x%08" PRIx32 ", read back 0x%02x, expected 0x%02x",
|
||||
address + wrote + i,
|
||||
readback[i],
|
||||
chunk[i]);
|
||||
retval = ERROR_FAIL;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
ptr += wordsize;
|
||||
}
|
||||
|
||||
if ((retval == ERROR_OK) && (duration_measure(&bench) == ERROR_OK)) {
|
||||
command_print(CMD_CTX, "wrote %" PRIu32 " bytes to 0x%8.8" PRIx32
|
||||
" in %fs (%0.3f KiB/s)", wrote, address,
|
||||
duration_elapsed(&bench), duration_kbps(&bench, wrote));
|
||||
command_print(CMD_CTX, "wrote %" PRIu32 " bytes to " TARGET_ADDR_FMT
|
||||
" in %fs (%0.3f KiB/s)", size_bytes, address,
|
||||
duration_elapsed(&bench), duration_kbps(&bench, size_bytes));
|
||||
}
|
||||
|
||||
done:
|
||||
free(readback);
|
||||
free(chunk);
|
||||
free(buffer);
|
||||
|
||||
return retval;
|
||||
}
|
||||
@@ -592,8 +620,8 @@ COMMAND_HANDLER(handle_flash_write_bank_command)
|
||||
struct duration bench;
|
||||
duration_start(&bench);
|
||||
|
||||
struct flash_bank *p;
|
||||
int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &p);
|
||||
struct flash_bank *bank;
|
||||
int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
|
||||
if (ERROR_OK != retval)
|
||||
return retval;
|
||||
|
||||
@@ -602,7 +630,7 @@ COMMAND_HANDLER(handle_flash_write_bank_command)
|
||||
if (CMD_ARGC > 2)
|
||||
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], offset);
|
||||
|
||||
if (offset > p->size) {
|
||||
if (offset > bank->size) {
|
||||
LOG_ERROR("Offset 0x%8.8" PRIx32 " is out of range of the flash bank",
|
||||
offset);
|
||||
return ERROR_COMMAND_ARGUMENT_INVALID;
|
||||
@@ -618,7 +646,7 @@ COMMAND_HANDLER(handle_flash_write_bank_command)
|
||||
return retval;
|
||||
}
|
||||
|
||||
length = MIN(filesize, p->size - offset);
|
||||
length = MIN(filesize, bank->size - offset);
|
||||
|
||||
if (!length) {
|
||||
LOG_INFO("Nothing to write to flash bank");
|
||||
@@ -630,14 +658,33 @@ COMMAND_HANDLER(handle_flash_write_bank_command)
|
||||
LOG_INFO("File content exceeds flash bank size. Only writing the "
|
||||
"first %zu bytes of the file", length);
|
||||
|
||||
buffer = malloc(length);
|
||||
target_addr_t start_addr = bank->base + offset;
|
||||
target_addr_t aligned_start = flash_write_align_start(bank, start_addr);
|
||||
target_addr_t end_addr = start_addr + length - 1;
|
||||
target_addr_t aligned_end = flash_write_align_end(bank, end_addr);
|
||||
uint32_t aligned_size = aligned_end + 1 - aligned_start;
|
||||
uint32_t padding_at_start = start_addr - aligned_start;
|
||||
uint32_t padding_at_end = aligned_end - end_addr;
|
||||
|
||||
buffer = malloc(aligned_size);
|
||||
if (buffer == NULL) {
|
||||
fileio_close(fileio);
|
||||
LOG_ERROR("Out of memory");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (padding_at_start) {
|
||||
memset(buffer, bank->default_padded_value, padding_at_start);
|
||||
LOG_WARNING("Start offset 0x%08" PRIx32
|
||||
" breaks the required alignment of flash bank %s",
|
||||
offset, bank->name);
|
||||
LOG_WARNING("Padding %" PRId32 " bytes from " TARGET_ADDR_FMT,
|
||||
padding_at_start, aligned_start);
|
||||
}
|
||||
|
||||
uint8_t *ptr = buffer + padding_at_start;
|
||||
size_t buf_cnt;
|
||||
if (fileio_read(fileio, length, buffer, &buf_cnt) != ERROR_OK) {
|
||||
if (fileio_read(fileio, length, ptr, &buf_cnt) != ERROR_OK) {
|
||||
free(buffer);
|
||||
fileio_close(fileio);
|
||||
return ERROR_FAIL;
|
||||
@@ -649,15 +696,23 @@ COMMAND_HANDLER(handle_flash_write_bank_command)
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
retval = flash_driver_write(p, buffer, offset, length);
|
||||
ptr += length;
|
||||
|
||||
if (padding_at_end) {
|
||||
memset(ptr, bank->default_padded_value, padding_at_end);
|
||||
LOG_INFO("Padding at " TARGET_ADDR_FMT " with %" PRId32
|
||||
" bytes (bank write end alignment)",
|
||||
end_addr + 1, padding_at_end);
|
||||
}
|
||||
|
||||
retval = flash_driver_write(bank, buffer, aligned_start - bank->base, aligned_size);
|
||||
|
||||
free(buffer);
|
||||
buffer = NULL;
|
||||
|
||||
if ((ERROR_OK == retval) && (duration_measure(&bench) == ERROR_OK)) {
|
||||
command_print(CMD_CTX, "wrote %zu bytes from file %s to flash bank %u"
|
||||
" at offset 0x%8.8" PRIx32 " in %fs (%0.3f KiB/s)",
|
||||
length, CMD_ARGV[1], p->bank_number, offset,
|
||||
length, CMD_ARGV[1], bank->bank_number, offset,
|
||||
duration_elapsed(&bench), duration_kbps(&bench, length));
|
||||
}
|
||||
|
||||
@@ -1071,21 +1126,16 @@ COMMAND_HANDLER(handle_flash_bank_command)
|
||||
}
|
||||
}
|
||||
|
||||
struct flash_bank *c = malloc(sizeof(*c));
|
||||
struct flash_bank *c = calloc(1, sizeof(*c));
|
||||
c->name = strdup(bank_name);
|
||||
c->target = target;
|
||||
c->driver = driver;
|
||||
c->driver_priv = NULL;
|
||||
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], c->base);
|
||||
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], c->size);
|
||||
COMMAND_PARSE_NUMBER(int, CMD_ARGV[3], c->chip_width);
|
||||
COMMAND_PARSE_NUMBER(int, CMD_ARGV[4], c->bus_width);
|
||||
c->default_padded_value = c->erased_value = 0xff;
|
||||
c->num_sectors = 0;
|
||||
c->sectors = NULL;
|
||||
c->num_prot_blocks = 0;
|
||||
c->prot_blocks = NULL;
|
||||
c->next = NULL;
|
||||
c->minimal_write_gap = FLASH_WRITE_GAP_SECTOR;
|
||||
|
||||
int retval;
|
||||
retval = CALL_COMMAND_HANDLER(driver->flash_bank_command, c);
|
||||
|
||||
+10
-21
@@ -151,6 +151,7 @@ static int tms470_read_part_info(struct flash_bank *bank)
|
||||
if (bank->sectors) {
|
||||
free(bank->sectors);
|
||||
bank->sectors = NULL;
|
||||
bank->num_sectors = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -754,9 +755,6 @@ static int tms470_erase_sector(struct flash_bank *bank, int sector)
|
||||
target_write_u32(target, 0xFFFFFFDC, glbctrl);
|
||||
LOG_DEBUG("set glbctrl = 0x%08" PRIx32 "", glbctrl);
|
||||
|
||||
if (result == ERROR_OK)
|
||||
bank->sectors[sector].is_erased = 1;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1044,27 +1042,17 @@ static int tms470_erase_check(struct flash_bank *bank)
|
||||
* an attempt to reduce the JTAG overhead.
|
||||
*/
|
||||
for (sector = 0; sector < bank->num_sectors; sector++) {
|
||||
if (bank->sectors[sector].is_erased != 1) {
|
||||
uint32_t i, addr = bank->base + bank->sectors[sector].offset;
|
||||
uint32_t i, addr = bank->base + bank->sectors[sector].offset;
|
||||
|
||||
LOG_INFO("checking flash bank %d sector %d", tms470_info->ordinal, sector);
|
||||
LOG_INFO("checking flash bank %d sector %d", tms470_info->ordinal, sector);
|
||||
|
||||
target_read_buffer(target, addr, bank->sectors[sector].size, buffer);
|
||||
target_read_buffer(target, addr, bank->sectors[sector].size, buffer);
|
||||
|
||||
bank->sectors[sector].is_erased = 1;
|
||||
for (i = 0; i < bank->sectors[sector].size; i++) {
|
||||
if (buffer[i] != 0xff) {
|
||||
LOG_WARNING("tms470 bank %d, sector %d, not erased.",
|
||||
tms470_info->ordinal,
|
||||
sector);
|
||||
LOG_WARNING(
|
||||
"at location 0x%08" PRIx32 ": flash data is 0x%02x.",
|
||||
addr + i,
|
||||
buffer[i]);
|
||||
|
||||
bank->sectors[sector].is_erased = 0;
|
||||
break;
|
||||
}
|
||||
bank->sectors[sector].is_erased = 1;
|
||||
for (i = 0; i < bank->sectors[sector].size; i++) {
|
||||
if (buffer[i] != 0xff) {
|
||||
bank->sectors[sector].is_erased = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (bank->sectors[sector].is_erased != 1) {
|
||||
@@ -1186,4 +1174,5 @@ struct flash_driver tms470_flash = {
|
||||
.erase_check = tms470_erase_check,
|
||||
.protect_check = tms470_protect_check,
|
||||
.info = get_tms470_info,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
|
||||
@@ -46,8 +46,13 @@ static void virtual_update_bank_info(struct flash_bank *bank)
|
||||
bank->bus_width = master_bank->bus_width;
|
||||
bank->erased_value = master_bank->erased_value;
|
||||
bank->default_padded_value = master_bank->default_padded_value;
|
||||
bank->write_start_alignment = master_bank->write_start_alignment;
|
||||
bank->write_end_alignment = master_bank->write_end_alignment;
|
||||
bank->minimal_write_gap = master_bank->minimal_write_gap;
|
||||
bank->num_sectors = master_bank->num_sectors;
|
||||
bank->sectors = master_bank->sectors;
|
||||
bank->num_prot_blocks = master_bank->num_prot_blocks;
|
||||
bank->prot_blocks = master_bank->prot_blocks;
|
||||
}
|
||||
|
||||
FLASH_BANK_COMMAND_HANDLER(virtual_flash_bank_command)
|
||||
@@ -231,4 +236,5 @@ struct flash_driver virtual_flash = {
|
||||
.erase_check = virtual_blank_check,
|
||||
.protect_check = virtual_protect_check,
|
||||
.info = virtual_info,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
|
||||
+3
-1
@@ -636,6 +636,7 @@ static int xcf_probe(struct flash_bank *bank)
|
||||
fill_sector_table(bank);
|
||||
|
||||
priv->probed = true;
|
||||
/* REVISIT: Why is unchanged bank->driver_priv rewritten by same value? */
|
||||
bank->driver_priv = priv;
|
||||
|
||||
LOG_INFO("product name: %s", product_name(bank));
|
||||
@@ -893,5 +894,6 @@ struct flash_driver xcf_flash = {
|
||||
.auto_probe = xcf_auto_probe,
|
||||
.erase_check = xcf_erase_check,
|
||||
.protect_check = xcf_protect_check,
|
||||
.info = xcf_info
|
||||
.info = xcf_info,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
|
||||
@@ -546,4 +546,5 @@ struct flash_driver xmc1xxx_flash = {
|
||||
.erase = xmc1xxx_erase,
|
||||
.erase_check = xmc1xxx_erase_check,
|
||||
.write = xmc1xxx_write,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
|
||||
@@ -1356,4 +1356,5 @@ struct flash_driver xmc4xxx_flash = {
|
||||
.info = xmc4xxx_get_info_command,
|
||||
.protect_check = xmc4xxx_protect_check,
|
||||
.protect = xmc4xxx_protect,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
|
||||
+19
-5
@@ -608,7 +608,23 @@ static int run_command(struct command_context *context,
|
||||
.argc = num_words - 1,
|
||||
.argv = words + 1,
|
||||
};
|
||||
/* Black magic of overridden current target:
|
||||
* If the command we are going to handle has a target prefix,
|
||||
* override the current target temporarily for the time
|
||||
* of processing the command.
|
||||
* current_target_override is used also for event handlers
|
||||
* therefore we prevent touching it if command has no prefix.
|
||||
* Previous override is saved and restored back to ensure
|
||||
* correct work when run_command() is re-entered. */
|
||||
struct target *saved_target_override = context->current_target_override;
|
||||
if (c->jim_handler_data)
|
||||
context->current_target_override = c->jim_handler_data;
|
||||
|
||||
int retval = c->handler(&cmd);
|
||||
|
||||
if (c->jim_handler_data)
|
||||
context->current_target_override = saved_target_override;
|
||||
|
||||
if (retval == ERROR_COMMAND_SYNTAX_ERROR) {
|
||||
/* Print help for command */
|
||||
char *full_name = command_name(c, ' ');
|
||||
@@ -643,6 +659,8 @@ int command_run_line(struct command_context *context, char *line)
|
||||
* happen when the Jim Tcl interpreter is provided by eCos for
|
||||
* instance.
|
||||
*/
|
||||
context->current_target_override = NULL;
|
||||
|
||||
Jim_Interp *interp = context->interp;
|
||||
Jim_DeleteAssocData(interp, "context");
|
||||
retcode = Jim_SetAssocData(interp, "context", NULL, context);
|
||||
@@ -1269,14 +1287,10 @@ static const struct command_registration command_builtin_handlers[] = {
|
||||
|
||||
struct command_context *command_init(const char *startup_tcl, Jim_Interp *interp)
|
||||
{
|
||||
struct command_context *context = malloc(sizeof(struct command_context));
|
||||
struct command_context *context = calloc(1, sizeof(struct command_context));
|
||||
const char *HostOs;
|
||||
|
||||
context->mode = COMMAND_EXEC;
|
||||
context->commands = NULL;
|
||||
context->current_target = 0;
|
||||
context->output_handler = NULL;
|
||||
context->output_handler_priv = NULL;
|
||||
|
||||
/* Create a jim interpreter if we were not handed one */
|
||||
if (interp == NULL) {
|
||||
|
||||
+18
-1
@@ -22,8 +22,12 @@
|
||||
#ifndef OPENOCD_HELPER_COMMAND_H
|
||||
#define OPENOCD_HELPER_COMMAND_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <jim-nvp.h>
|
||||
|
||||
#include <helper/types.h>
|
||||
|
||||
/* To achieve C99 printf compatibility in MinGW, gnu_printf should be
|
||||
* used for __attribute__((format( ... ))), with GCC v4.4 or later
|
||||
*/
|
||||
@@ -49,7 +53,15 @@ struct command_context {
|
||||
Jim_Interp *interp;
|
||||
enum command_mode mode;
|
||||
struct command *commands;
|
||||
int current_target;
|
||||
struct target *current_target;
|
||||
/* The target set by 'targets xx' command or the latest created */
|
||||
struct target *current_target_override;
|
||||
/* If set overrides current_target
|
||||
* It happens during processing of
|
||||
* 1) a target prefixed command
|
||||
* 2) an event handler
|
||||
* Pay attention to reentrancy when setting override.
|
||||
*/
|
||||
command_output_handler_t output_handler;
|
||||
void *output_handler_priv;
|
||||
};
|
||||
@@ -168,6 +180,11 @@ struct command {
|
||||
command_handler_t handler;
|
||||
Jim_CmdProc *jim_handler;
|
||||
void *jim_handler_data;
|
||||
/* Currently used only for target of target-prefixed cmd.
|
||||
* Native OpenOCD commands use jim_handler_data exclusively
|
||||
* as a target override.
|
||||
* Jim handlers outside of target cmd tree can use
|
||||
* jim_handler_data for any handler specific data */
|
||||
enum command_mode mode;
|
||||
struct command *next;
|
||||
};
|
||||
|
||||
@@ -51,6 +51,21 @@ void add_config_command(const char *cfg)
|
||||
config_file_names[num_config_files] = NULL;
|
||||
}
|
||||
|
||||
void free_config(void)
|
||||
{
|
||||
while (num_config_files)
|
||||
free(config_file_names[--num_config_files]);
|
||||
|
||||
free(config_file_names);
|
||||
config_file_names = NULL;
|
||||
|
||||
while (num_script_dirs)
|
||||
free(script_search_dirs[--num_script_dirs]);
|
||||
|
||||
free(script_search_dirs);
|
||||
script_search_dirs = NULL;
|
||||
}
|
||||
|
||||
/* return full path or NULL according to search rules */
|
||||
char *find_file(const char *file)
|
||||
{
|
||||
|
||||
@@ -32,6 +32,8 @@ void add_config_command(const char *cfg);
|
||||
|
||||
void add_script_search_dir(const char *dir);
|
||||
|
||||
void free_config(void);
|
||||
|
||||
int configuration_output_handler(struct command_context *cmd_ctx,
|
||||
const char *line);
|
||||
|
||||
|
||||
@@ -153,6 +153,11 @@ int fileio_close(struct fileio *fileio)
|
||||
return retval;
|
||||
}
|
||||
|
||||
int fileio_feof(struct fileio *fileio)
|
||||
{
|
||||
return feof(fileio->file);
|
||||
}
|
||||
|
||||
int fileio_seek(struct fileio *fileio, size_t position)
|
||||
{
|
||||
int retval;
|
||||
|
||||
@@ -46,6 +46,7 @@ struct fileio;
|
||||
int fileio_open(struct fileio **fileio, const char *url,
|
||||
enum fileio_access access_type, enum fileio_type type);
|
||||
int fileio_close(struct fileio *fileio);
|
||||
int fileio_feof(struct fileio *fileio);
|
||||
|
||||
int fileio_seek(struct fileio *fileio, size_t position);
|
||||
int fileio_fgets(struct fileio *fileio, size_t size, void *buffer);
|
||||
|
||||
@@ -149,6 +149,8 @@ extern int debug_level;
|
||||
*/
|
||||
#define ERROR_FAIL (-4)
|
||||
#define ERROR_WAIT (-5)
|
||||
/* ERROR_TIMEOUT is already taken by winerror.h. */
|
||||
#define ERROR_TIMEOUT_REACHED (-6)
|
||||
|
||||
|
||||
#endif /* OPENOCD_HELPER_LOG_H */
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
#ifndef OPENOCD_HELPER_REPLACEMENTS_H
|
||||
#define OPENOCD_HELPER_REPLACEMENTS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* MIN,MAX macros */
|
||||
#ifndef MIN
|
||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
|
||||
+11
-6
@@ -22,7 +22,12 @@
|
||||
#ifndef OPENOCD_HELPER_TYPES_H
|
||||
#define OPENOCD_HELPER_TYPES_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
#include <assert.h>
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
@@ -123,17 +128,17 @@ static inline uint64_t le_to_h_u64(const uint8_t *buf)
|
||||
|
||||
static inline uint32_t le_to_h_u32(const uint8_t* buf)
|
||||
{
|
||||
return (uint32_t)(buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24);
|
||||
return (uint32_t)((uint32_t)buf[0] | (uint32_t)buf[1] << 8 | (uint32_t)buf[2] << 16 | (uint32_t)buf[3] << 24);
|
||||
}
|
||||
|
||||
static inline uint32_t le_to_h_u24(const uint8_t* buf)
|
||||
{
|
||||
return (uint32_t)(buf[0] | buf[1] << 8 | buf[2] << 16);
|
||||
return (uint32_t)((uint32_t)buf[0] | (uint32_t)buf[1] << 8 | (uint32_t)buf[2] << 16);
|
||||
}
|
||||
|
||||
static inline uint16_t le_to_h_u16(const uint8_t* buf)
|
||||
{
|
||||
return (uint16_t)(buf[0] | buf[1] << 8);
|
||||
return (uint16_t)((uint16_t)buf[0] | (uint16_t)buf[1] << 8);
|
||||
}
|
||||
|
||||
static inline uint64_t be_to_h_u64(const uint8_t *buf)
|
||||
@@ -150,17 +155,17 @@ static inline uint64_t be_to_h_u64(const uint8_t *buf)
|
||||
|
||||
static inline uint32_t be_to_h_u32(const uint8_t* buf)
|
||||
{
|
||||
return (uint32_t)(buf[3] | buf[2] << 8 | buf[1] << 16 | buf[0] << 24);
|
||||
return (uint32_t)((uint32_t)buf[3] | (uint32_t)buf[2] << 8 | (uint32_t)buf[1] << 16 | (uint32_t)buf[0] << 24);
|
||||
}
|
||||
|
||||
static inline uint32_t be_to_h_u24(const uint8_t* buf)
|
||||
{
|
||||
return (uint32_t)(buf[2] | buf[1] << 8 | buf[0] << 16);
|
||||
return (uint32_t)((uint32_t)buf[2] | (uint32_t)buf[1] << 8 | (uint32_t)buf[0] << 16);
|
||||
}
|
||||
|
||||
static inline uint16_t be_to_h_u16(const uint8_t* buf)
|
||||
{
|
||||
return (uint16_t)(buf[1] | buf[0] << 8);
|
||||
return (uint16_t)((uint16_t)buf[1] | (uint16_t)buf[0] << 8);
|
||||
}
|
||||
|
||||
static inline void h_u64_to_le(uint8_t *buf, int64_t val)
|
||||
|
||||
+8
-8
@@ -149,14 +149,14 @@ COMMAND_HANDLER(handle_interface_command)
|
||||
|
||||
jtag_interface = jtag_interfaces[i];
|
||||
|
||||
/* LEGACY SUPPORT ... adapter drivers must declare what
|
||||
* transports they allow. Until they all do so, assume
|
||||
* the legacy drivers are JTAG-only
|
||||
*/
|
||||
if (!jtag_interface->transports)
|
||||
LOG_WARNING("Adapter driver '%s' did not declare "
|
||||
"which transports it allows; assuming "
|
||||
"legacy JTAG-only", jtag_interface->name);
|
||||
/* LEGACY SUPPORT ... adapter drivers must declare what
|
||||
* transports they allow. Until they all do so, assume
|
||||
* the legacy drivers are JTAG-only
|
||||
*/
|
||||
if (!jtag_interface->transports)
|
||||
LOG_WARNING("Adapter driver '%s' did not declare "
|
||||
"which transports it allows; assuming "
|
||||
"legacy JTAG-only", jtag_interface->name);
|
||||
retval = allow_transports(CMD_CTX, jtag_interface->transports
|
||||
? jtag_interface->transports : jtag_only);
|
||||
if (ERROR_OK != retval)
|
||||
|
||||
@@ -239,6 +239,30 @@ static int aice_khz(int khz, int *jtag_speed)
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int aice_scan_jtag_chain(void)
|
||||
{
|
||||
LOG_DEBUG("=== %s ===", __func__);
|
||||
uint8_t num_of_idcode = 0;
|
||||
struct target *target;
|
||||
|
||||
int res = aice_port->api->idcode(aice_target_id_codes, &num_of_idcode);
|
||||
if (res != ERROR_OK) {
|
||||
LOG_ERROR("<-- TARGET ERROR! Failed to identify AndesCore "
|
||||
"JTAG Manufacture ID in the JTAG scan chain. "
|
||||
"Failed to access EDM registers. -->");
|
||||
return res;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < num_of_idcode; i++)
|
||||
LOG_DEBUG("id_codes[%d] = 0x%x", i, aice_target_id_codes[i]);
|
||||
|
||||
/* Update tap idcode */
|
||||
for (target = all_targets; target; target = target->next)
|
||||
target->tap->idcode = aice_target_id_codes[target->tap->abs_chain_position];
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
/* Command handlers */
|
||||
COMMAND_HANDLER(aice_handle_aice_info_command)
|
||||
|
||||
@@ -31,5 +31,6 @@ struct aice_interface_param_s {
|
||||
};
|
||||
|
||||
int aice_init_targets(void);
|
||||
int aice_scan_jtag_chain(void);
|
||||
|
||||
#endif /* OPENOCD_JTAG_AICE_AICE_INTERFACE_H */
|
||||
|
||||
@@ -158,6 +158,59 @@ COMMAND_HANDLER(handle_aice_init_command)
|
||||
return jtag_init(CMD_CTX);
|
||||
}
|
||||
|
||||
COMMAND_HANDLER(handle_scan_chain_command)
|
||||
{
|
||||
struct jtag_tap *tap;
|
||||
char expected_id[12];
|
||||
|
||||
aice_scan_jtag_chain();
|
||||
tap = jtag_all_taps();
|
||||
command_print(CMD_CTX,
|
||||
" TapName Enabled IdCode Expected IrLen IrCap IrMask");
|
||||
command_print(CMD_CTX,
|
||||
"-- ------------------- -------- ---------- ---------- ----- ----- ------");
|
||||
|
||||
while (tap) {
|
||||
uint32_t expected, expected_mask, ii;
|
||||
|
||||
snprintf(expected_id, sizeof expected_id, "0x%08x",
|
||||
(unsigned)((tap->expected_ids_cnt > 0)
|
||||
? tap->expected_ids[0]
|
||||
: 0));
|
||||
if (tap->ignore_version)
|
||||
expected_id[2] = '*';
|
||||
|
||||
expected = buf_get_u32(tap->expected, 0, tap->ir_length);
|
||||
expected_mask = buf_get_u32(tap->expected_mask, 0, tap->ir_length);
|
||||
|
||||
command_print(CMD_CTX,
|
||||
"%2d %-18s %c 0x%08x %s %5d 0x%02x 0x%02x",
|
||||
tap->abs_chain_position,
|
||||
tap->dotted_name,
|
||||
tap->enabled ? 'Y' : 'n',
|
||||
(unsigned int)(tap->idcode),
|
||||
expected_id,
|
||||
(unsigned int)(tap->ir_length),
|
||||
(unsigned int)(expected),
|
||||
(unsigned int)(expected_mask));
|
||||
|
||||
for (ii = 1; ii < tap->expected_ids_cnt; ii++) {
|
||||
snprintf(expected_id, sizeof expected_id, "0x%08x",
|
||||
(unsigned) tap->expected_ids[ii]);
|
||||
if (tap->ignore_version)
|
||||
expected_id[2] = '*';
|
||||
|
||||
command_print(CMD_CTX,
|
||||
" %s",
|
||||
expected_id);
|
||||
}
|
||||
|
||||
tap = tap->next_tap;
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int jim_aice_arp_init(Jim_Interp *interp, int argc, Jim_Obj * const *argv)
|
||||
{
|
||||
LOG_DEBUG("No implement: jim_aice_arp_init");
|
||||
@@ -307,6 +360,13 @@ aice_transport_jtag_subcommand_handlers[] = {
|
||||
.jim_handler = jim_aice_names,
|
||||
.help = "Returns list of all JTAG tap names.",
|
||||
},
|
||||
{
|
||||
.name = "scan_chain",
|
||||
.handler = handle_scan_chain_command,
|
||||
.mode = COMMAND_ANY,
|
||||
.help = "print current scan chain configuration",
|
||||
.usage = ""
|
||||
},
|
||||
|
||||
COMMAND_REGISTRATION_DONE
|
||||
};
|
||||
|
||||
+27
-25
@@ -2289,37 +2289,39 @@ get_delay:
|
||||
|
||||
static int aice_usb_set_clock(int set_clock)
|
||||
{
|
||||
if (aice_write_ctrl(AICE_WRITE_CTRL_TCK_CONTROL,
|
||||
AICE_TCK_CONTROL_TCK_SCAN) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
if (set_clock & AICE_TCK_CONTROL_TCK_SCAN) {
|
||||
if (aice_write_ctrl(AICE_WRITE_CTRL_TCK_CONTROL,
|
||||
AICE_TCK_CONTROL_TCK_SCAN) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
|
||||
/* Read out TCK_SCAN clock value */
|
||||
uint32_t scan_clock;
|
||||
if (aice_read_ctrl(AICE_READ_CTRL_GET_ICE_STATE, &scan_clock) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
/* Read out TCK_SCAN clock value */
|
||||
uint32_t scan_clock;
|
||||
if (aice_read_ctrl(AICE_READ_CTRL_GET_ICE_STATE, &scan_clock) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
|
||||
scan_clock &= 0x0F;
|
||||
scan_clock &= 0x0F;
|
||||
|
||||
uint32_t scan_base_freq;
|
||||
if (scan_clock & 0x8)
|
||||
scan_base_freq = 48000; /* 48 MHz */
|
||||
else
|
||||
scan_base_freq = 30000; /* 30 MHz */
|
||||
uint32_t scan_base_freq;
|
||||
if (scan_clock & 0x8)
|
||||
scan_base_freq = 48000; /* 48 MHz */
|
||||
else
|
||||
scan_base_freq = 30000; /* 30 MHz */
|
||||
|
||||
uint32_t set_base_freq;
|
||||
if (set_clock & 0x8)
|
||||
set_base_freq = 48000;
|
||||
else
|
||||
set_base_freq = 30000;
|
||||
uint32_t set_base_freq;
|
||||
if (set_clock & 0x8)
|
||||
set_base_freq = 48000;
|
||||
else
|
||||
set_base_freq = 30000;
|
||||
|
||||
uint32_t set_freq;
|
||||
uint32_t scan_freq;
|
||||
set_freq = set_base_freq >> (set_clock & 0x7);
|
||||
scan_freq = scan_base_freq >> (scan_clock & 0x7);
|
||||
uint32_t set_freq;
|
||||
uint32_t scan_freq;
|
||||
set_freq = set_base_freq >> (set_clock & 0x7);
|
||||
scan_freq = scan_base_freq >> (scan_clock & 0x7);
|
||||
|
||||
if (scan_freq < set_freq) {
|
||||
LOG_ERROR("User specifies higher jtag clock than TCK_SCAN clock");
|
||||
return ERROR_FAIL;
|
||||
if (scan_freq < set_freq) {
|
||||
LOG_ERROR("User specifies higher jtag clock than TCK_SCAN clock");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
if (aice_write_ctrl(AICE_WRITE_CTRL_TCK_CONTROL, set_clock) != ERROR_OK)
|
||||
|
||||
@@ -71,6 +71,7 @@
|
||||
|
||||
/* Constants for AICE command WRITE_CTRL:TCK_CONTROL */
|
||||
#define AICE_TCK_CONTROL_TCK3048 0x08
|
||||
#define AICE_TCK_CONTROL_TCK_SCAN 0x10
|
||||
|
||||
/* Constants for AICE command WRITE_CTRL:JTAG_PIN_CONTROL */
|
||||
#define AICE_JTAG_PIN_CONTROL_SRST 0x01
|
||||
|
||||
+23
-71
@@ -836,68 +836,7 @@ int default_interface_jtag_execute_queue(void)
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
int result = jtag->execute_queue();
|
||||
|
||||
struct jtag_command *cmd = jtag_command_queue;
|
||||
while (debug_level >= LOG_LVL_DEBUG && cmd) {
|
||||
switch (cmd->type) {
|
||||
case JTAG_SCAN:
|
||||
DEBUG_JTAG_IO("JTAG %s SCAN to %s",
|
||||
cmd->cmd.scan->ir_scan ? "IR" : "DR",
|
||||
tap_state_name(cmd->cmd.scan->end_state));
|
||||
for (int i = 0; i < cmd->cmd.scan->num_fields; i++) {
|
||||
struct scan_field *field = cmd->cmd.scan->fields + i;
|
||||
if (field->out_value) {
|
||||
char *str = buf_to_str(field->out_value, field->num_bits, 16);
|
||||
DEBUG_JTAG_IO(" %db out: %s", field->num_bits, str);
|
||||
free(str);
|
||||
}
|
||||
if (field->in_value) {
|
||||
char *str = buf_to_str(field->in_value, field->num_bits, 16);
|
||||
DEBUG_JTAG_IO(" %db in: %s", field->num_bits, str);
|
||||
free(str);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case JTAG_TLR_RESET:
|
||||
DEBUG_JTAG_IO("JTAG TLR RESET to %s",
|
||||
tap_state_name(cmd->cmd.statemove->end_state));
|
||||
break;
|
||||
case JTAG_RUNTEST:
|
||||
DEBUG_JTAG_IO("JTAG RUNTEST %d cycles to %s",
|
||||
cmd->cmd.runtest->num_cycles,
|
||||
tap_state_name(cmd->cmd.runtest->end_state));
|
||||
break;
|
||||
case JTAG_RESET:
|
||||
{
|
||||
const char *reset_str[3] = {
|
||||
"leave", "deassert", "assert"
|
||||
};
|
||||
DEBUG_JTAG_IO("JTAG RESET %s TRST, %s SRST",
|
||||
reset_str[cmd->cmd.reset->trst + 1],
|
||||
reset_str[cmd->cmd.reset->srst + 1]);
|
||||
}
|
||||
break;
|
||||
case JTAG_PATHMOVE:
|
||||
DEBUG_JTAG_IO("JTAG PATHMOVE (TODO)");
|
||||
break;
|
||||
case JTAG_SLEEP:
|
||||
DEBUG_JTAG_IO("JTAG SLEEP (TODO)");
|
||||
break;
|
||||
case JTAG_STABLECLOCKS:
|
||||
DEBUG_JTAG_IO("JTAG STABLECLOCKS (TODO)");
|
||||
break;
|
||||
case JTAG_TMS:
|
||||
DEBUG_JTAG_IO("JTAG STABLECLOCKS (TODO)");
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("Unknown JTAG command: %d", cmd->type);
|
||||
break;
|
||||
}
|
||||
cmd = cmd->next;
|
||||
}
|
||||
|
||||
return result;
|
||||
return jtag->execute_queue();
|
||||
}
|
||||
|
||||
void jtag_execute_queue_noclear(void)
|
||||
@@ -1168,8 +1107,7 @@ static int jtag_examine_chain(void)
|
||||
|
||||
if ((idcode & 1) == 0) {
|
||||
/* Zero for LSB indicates a device in bypass */
|
||||
LOG_INFO("TAP %s does not have valid IDCODE (idcode=0x%x)",
|
||||
tap->dotted_name, idcode);
|
||||
LOG_INFO("TAP %s does not have IDCODE", tap->dotted_name);
|
||||
tap->hasidcode = false;
|
||||
tap->idcode = 0;
|
||||
|
||||
@@ -1370,6 +1308,14 @@ void jtag_tap_free(struct jtag_tap *tap)
|
||||
{
|
||||
jtag_unregister_event_callback(&jtag_reset_callback, tap);
|
||||
|
||||
struct jtag_tap_event_action *jteap = tap->event_action;
|
||||
while (jteap) {
|
||||
struct jtag_tap_event_action *next = jteap->next;
|
||||
Jim_DecrRefCount(jteap->interp, jteap->body);
|
||||
free(jteap);
|
||||
jteap = next;
|
||||
}
|
||||
|
||||
free(tap->expected);
|
||||
free(tap->expected_mask);
|
||||
free(tap->expected_ids);
|
||||
@@ -1534,13 +1480,19 @@ int jtag_init_inner(struct command_context *cmd_ctx)
|
||||
|
||||
int adapter_quit(void)
|
||||
{
|
||||
if (!jtag || !jtag->quit)
|
||||
return ERROR_OK;
|
||||
if (jtag && jtag->quit) {
|
||||
/* close the JTAG interface */
|
||||
int result = jtag->quit();
|
||||
if (ERROR_OK != result)
|
||||
LOG_ERROR("failed: %d", result);
|
||||
}
|
||||
|
||||
/* close the JTAG interface */
|
||||
int result = jtag->quit();
|
||||
if (ERROR_OK != result)
|
||||
LOG_ERROR("failed: %d", result);
|
||||
struct jtag_tap *t = jtag_all_taps();
|
||||
while (t) {
|
||||
struct jtag_tap *n = t->next_tap;
|
||||
jtag_tap_free(t);
|
||||
t = n;
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
@@ -1891,7 +1843,7 @@ void adapter_deassert_reset(void)
|
||||
LOG_ERROR("transport is not selected");
|
||||
}
|
||||
|
||||
int adapter_config_trace(bool enabled, enum tpio_pin_protocol pin_protocol,
|
||||
int adapter_config_trace(bool enabled, enum tpiu_pin_protocol pin_protocol,
|
||||
uint32_t port_size, unsigned int *trace_freq)
|
||||
{
|
||||
if (jtag->config_trace)
|
||||
|
||||
@@ -80,6 +80,9 @@ if USB_BLASTER_DRIVER
|
||||
%C%_libocdjtagdrivers_la_LIBADD += %D%/usb_blaster/libocdusbblaster.la
|
||||
include %D%/usb_blaster/Makefile.am
|
||||
endif
|
||||
if FT232R
|
||||
DRIVERFILES += %D%/ft232r.c
|
||||
endif
|
||||
if AMTJTAGACCEL
|
||||
DRIVERFILES += %D%/amt_jtagaccel.c
|
||||
endif
|
||||
@@ -153,10 +156,12 @@ endif
|
||||
if IMX_GPIO
|
||||
DRIVERFILES += %D%/imx_gpio.c
|
||||
endif
|
||||
|
||||
if KITPROG
|
||||
DRIVERFILES += %D%/kitprog.c
|
||||
endif
|
||||
if XDS110
|
||||
DRIVERFILES += %D%/xds110.c
|
||||
endif
|
||||
|
||||
DRIVERHEADERS = \
|
||||
%D%/bitbang.h \
|
||||
|
||||
+459
-50
@@ -22,6 +22,7 @@
|
||||
#endif
|
||||
|
||||
#include <jtag/interface.h>
|
||||
#include <jtag/swd.h>
|
||||
#include <jtag/commands.h>
|
||||
|
||||
#include <termios.h>
|
||||
@@ -48,9 +49,21 @@ static void buspirate_stableclocks(int num_cycles);
|
||||
#define CMD_READ_ADCS 0x03
|
||||
/*#define CMD_TAP_SHIFT 0x04 // old protocol */
|
||||
#define CMD_TAP_SHIFT 0x05
|
||||
#define CMD_ENTER_RWIRE 0x05
|
||||
#define CMD_ENTER_OOCD 0x06
|
||||
#define CMD_UART_SPEED 0x07
|
||||
#define CMD_JTAG_SPEED 0x08
|
||||
#define CMD_RAW_PERIPH 0x40
|
||||
#define CMD_RAW_SPEED 0x60
|
||||
#define CMD_RAW_MODE 0x80
|
||||
|
||||
/* raw-wire mode configuration */
|
||||
#define CMD_RAW_CONFIG_HIZ 0x00
|
||||
#define CMD_RAW_CONFIG_3V3 0x08
|
||||
#define CMD_RAW_CONFIG_2W 0x00
|
||||
#define CMD_RAW_CONFIG_3W 0x04
|
||||
#define CMD_RAW_CONFIG_MSB 0x00
|
||||
#define CMD_RAW_CONFIG_LSB 0x02
|
||||
|
||||
/* Not all OSes have this speed defined */
|
||||
#if !defined(B1000000)
|
||||
@@ -81,6 +94,18 @@ enum {
|
||||
SERIAL_FAST = 1
|
||||
};
|
||||
|
||||
enum {
|
||||
SPEED_RAW_5_KHZ = 0x0,
|
||||
SPEED_RAW_50_KHZ = 0x1,
|
||||
SPEED_RAW_100_KHZ = 0x2,
|
||||
SPEED_RAW_400_KHZ = 0x3
|
||||
};
|
||||
|
||||
/* SWD mode specific */
|
||||
static bool swd_mode;
|
||||
static int queued_retval;
|
||||
static char swd_features;
|
||||
|
||||
static const cc_t SHORT_TIMEOUT = 1; /* Must be at least 1. */
|
||||
static const cc_t NORMAL_TIMEOUT = 10;
|
||||
|
||||
@@ -93,6 +118,12 @@ static char *buspirate_port;
|
||||
|
||||
static enum tap_state last_tap_state = TAP_RESET;
|
||||
|
||||
/* SWD interface */
|
||||
static int buspirate_swd_init(void);
|
||||
static void buspirate_swd_read_reg(uint8_t cmd, uint32_t *value, uint32_t ap_delay_clk);
|
||||
static void buspirate_swd_write_reg(uint8_t cmd, uint32_t value, uint32_t ap_delay_clk);
|
||||
static int buspirate_swd_switch_seq(enum swd_special_seq seq);
|
||||
static int buspirate_swd_run_queue(void);
|
||||
|
||||
/* TAP interface */
|
||||
static void buspirate_tap_init(void);
|
||||
@@ -103,23 +134,31 @@ static void buspirate_tap_append_scan(int length, uint8_t *buffer,
|
||||
static void buspirate_tap_make_space(int scan, int bits);
|
||||
|
||||
static void buspirate_reset(int trst, int srst);
|
||||
static void buspirate_set_feature(int, char, char);
|
||||
static void buspirate_set_mode(int, char);
|
||||
static void buspirate_set_speed(int, char);
|
||||
|
||||
/* low level interface */
|
||||
static void buspirate_bbio_enable(int);
|
||||
static void buspirate_jtag_reset(int);
|
||||
static void buspirate_jtag_enable(int);
|
||||
static unsigned char buspirate_jtag_command(int, char *, int);
|
||||
static unsigned char buspirate_jtag_command(int, uint8_t *, int);
|
||||
static void buspirate_jtag_set_speed(int, char);
|
||||
static void buspirate_jtag_set_mode(int, char);
|
||||
static void buspirate_jtag_set_feature(int, char, char);
|
||||
static void buspirate_jtag_get_adcs(int);
|
||||
|
||||
/* low level two-wire interface */
|
||||
static void buspirate_swd_set_speed(int, char);
|
||||
static void buspirate_swd_set_feature(int, char, char);
|
||||
static void buspirate_swd_set_mode(int, char);
|
||||
|
||||
/* low level HW communication interface */
|
||||
static int buspirate_serial_open(char *port);
|
||||
static int buspirate_serial_setspeed(int fd, char speed, cc_t timeout);
|
||||
static int buspirate_serial_write(int fd, char *buf, int size);
|
||||
static int buspirate_serial_read(int fd, char *buf, int size);
|
||||
static int buspirate_serial_write(int fd, uint8_t *buf, int size);
|
||||
static int buspirate_serial_read(int fd, uint8_t *buf, int size);
|
||||
static void buspirate_serial_close(int fd);
|
||||
static void buspirate_print_buffer(char *buf, int size);
|
||||
static void buspirate_print_buffer(uint8_t *buf, int size);
|
||||
|
||||
static int buspirate_execute_queue(void)
|
||||
{
|
||||
@@ -216,7 +255,7 @@ static bool read_and_discard_all_data(const int fd)
|
||||
bool was_msg_already_printed = false;
|
||||
|
||||
for ( ; ; ) {
|
||||
char buffer[1024]; /* Any size will do, it's a trade-off between stack size and performance. */
|
||||
uint8_t buffer[1024]; /* Any size will do, it's a trade-off between stack size and performance. */
|
||||
|
||||
const ssize_t read_count = read(fd, buffer, sizeof(buffer));
|
||||
|
||||
@@ -295,18 +334,20 @@ static int buspirate_init(void)
|
||||
return ERROR_JTAG_INIT_FAILED;
|
||||
}
|
||||
|
||||
buspirate_jtag_enable(buspirate_fd);
|
||||
buspirate_bbio_enable(buspirate_fd);
|
||||
|
||||
if (buspirate_baudrate != SERIAL_NORMAL)
|
||||
buspirate_jtag_set_speed(buspirate_fd, SERIAL_FAST);
|
||||
if (swd_mode || buspirate_baudrate != SERIAL_NORMAL)
|
||||
buspirate_set_speed(buspirate_fd, SERIAL_FAST);
|
||||
|
||||
LOG_INFO("Buspirate Interface ready!");
|
||||
LOG_INFO("Buspirate %s Interface ready!", swd_mode ? "SWD" : "JTAG");
|
||||
|
||||
buspirate_tap_init();
|
||||
buspirate_jtag_set_mode(buspirate_fd, buspirate_pinmode);
|
||||
buspirate_jtag_set_feature(buspirate_fd, FEATURE_VREG,
|
||||
if (!swd_mode)
|
||||
buspirate_tap_init();
|
||||
|
||||
buspirate_set_mode(buspirate_fd, buspirate_pinmode);
|
||||
buspirate_set_feature(buspirate_fd, FEATURE_VREG,
|
||||
(buspirate_vreg == 1) ? ACTION_ENABLE : ACTION_DISABLE);
|
||||
buspirate_jtag_set_feature(buspirate_fd, FEATURE_PULLUP,
|
||||
buspirate_set_feature(buspirate_fd, FEATURE_PULLUP,
|
||||
(buspirate_pullup == 1) ? ACTION_ENABLE : ACTION_DISABLE);
|
||||
buspirate_reset(0, 0);
|
||||
|
||||
@@ -316,9 +357,9 @@ static int buspirate_init(void)
|
||||
static int buspirate_quit(void)
|
||||
{
|
||||
LOG_INFO("Shutting down buspirate.");
|
||||
buspirate_jtag_set_mode(buspirate_fd, MODE_HIZ);
|
||||
buspirate_set_mode(buspirate_fd, MODE_HIZ);
|
||||
buspirate_set_speed(buspirate_fd, SERIAL_NORMAL);
|
||||
|
||||
buspirate_jtag_set_speed(buspirate_fd, SERIAL_NORMAL);
|
||||
buspirate_jtag_reset(buspirate_fd);
|
||||
|
||||
buspirate_serial_close(buspirate_fd);
|
||||
@@ -336,6 +377,10 @@ COMMAND_HANDLER(buspirate_handle_adc_command)
|
||||
if (buspirate_fd == -1)
|
||||
return ERROR_OK;
|
||||
|
||||
/* unavailable in SWD mode */
|
||||
if (swd_mode)
|
||||
return ERROR_OK;
|
||||
|
||||
/* send the command */
|
||||
buspirate_jtag_get_adcs(buspirate_fd);
|
||||
|
||||
@@ -382,11 +427,11 @@ COMMAND_HANDLER(buspirate_handle_led_command)
|
||||
|
||||
if (atoi(CMD_ARGV[0]) == 1) {
|
||||
/* enable led */
|
||||
buspirate_jtag_set_feature(buspirate_fd, FEATURE_LED,
|
||||
buspirate_set_feature(buspirate_fd, FEATURE_LED,
|
||||
ACTION_ENABLE);
|
||||
} else if (atoi(CMD_ARGV[0]) == 0) {
|
||||
/* disable led */
|
||||
buspirate_jtag_set_feature(buspirate_fd, FEATURE_LED,
|
||||
buspirate_set_feature(buspirate_fd, FEATURE_LED,
|
||||
ACTION_DISABLE);
|
||||
} else {
|
||||
LOG_ERROR("usage: buspirate_led <1|0>");
|
||||
@@ -492,10 +537,22 @@ static const struct command_registration buspirate_command_handlers[] = {
|
||||
COMMAND_REGISTRATION_DONE
|
||||
};
|
||||
|
||||
static const struct swd_driver buspirate_swd = {
|
||||
.init = buspirate_swd_init,
|
||||
.switch_seq = buspirate_swd_switch_seq,
|
||||
.read_reg = buspirate_swd_read_reg,
|
||||
.write_reg = buspirate_swd_write_reg,
|
||||
.run = buspirate_swd_run_queue,
|
||||
};
|
||||
|
||||
static const char * const buspirate_transports[] = { "jtag", "swd", NULL };
|
||||
|
||||
struct jtag_interface buspirate_interface = {
|
||||
.name = "buspirate",
|
||||
.execute_queue = buspirate_execute_queue,
|
||||
.commands = buspirate_command_handlers,
|
||||
.transports = buspirate_transports,
|
||||
.swd = &buspirate_swd,
|
||||
.init = buspirate_init,
|
||||
.quit = buspirate_quit
|
||||
};
|
||||
@@ -633,8 +690,8 @@ static void buspirate_stableclocks(int num_cycles)
|
||||
make it incompatible with the Bus Pirate firmware. */
|
||||
#define BUSPIRATE_MAX_PENDING_SCANS 128
|
||||
|
||||
static char tms_chain[BUSPIRATE_BUFFER_SIZE]; /* send */
|
||||
static char tdi_chain[BUSPIRATE_BUFFER_SIZE]; /* send */
|
||||
static uint8_t tms_chain[BUSPIRATE_BUFFER_SIZE]; /* send */
|
||||
static uint8_t tdi_chain[BUSPIRATE_BUFFER_SIZE]; /* send */
|
||||
static int tap_chain_index;
|
||||
|
||||
struct pending_scan_result /* this was stolen from arm-jtag-ew */
|
||||
@@ -659,7 +716,7 @@ static int buspirate_tap_execute(void)
|
||||
{
|
||||
static const int CMD_TAP_SHIFT_HEADER_LEN = 3;
|
||||
|
||||
char tmp[4096];
|
||||
uint8_t tmp[4096];
|
||||
uint8_t *in_buf;
|
||||
int i;
|
||||
int fill_index = 0;
|
||||
@@ -675,8 +732,8 @@ static int buspirate_tap_execute(void)
|
||||
bytes_to_send = DIV_ROUND_UP(tap_chain_index, 8);
|
||||
|
||||
tmp[0] = CMD_TAP_SHIFT; /* this command expects number of bits */
|
||||
tmp[1] = (char)(tap_chain_index >> 8); /* high */
|
||||
tmp[2] = (char)(tap_chain_index); /* low */
|
||||
tmp[1] = tap_chain_index >> 8; /* high */
|
||||
tmp[2] = tap_chain_index; /* low */
|
||||
|
||||
fill_index = CMD_TAP_SHIFT_HEADER_LEN;
|
||||
for (i = 0; i < bytes_to_send; i++) {
|
||||
@@ -799,7 +856,7 @@ static void buspirate_tap_append_scan(int length, uint8_t *buffer,
|
||||
tap_pending_scans_num++;
|
||||
}
|
||||
|
||||
/*************** jtag wrapper functions *********************/
|
||||
/*************** wrapper functions *********************/
|
||||
|
||||
/* (1) assert or (0) deassert reset lines */
|
||||
static void buspirate_reset(int trst, int srst)
|
||||
@@ -807,33 +864,148 @@ static void buspirate_reset(int trst, int srst)
|
||||
LOG_DEBUG("trst: %i, srst: %i", trst, srst);
|
||||
|
||||
if (trst)
|
||||
buspirate_jtag_set_feature(buspirate_fd,
|
||||
FEATURE_TRST, ACTION_DISABLE);
|
||||
buspirate_set_feature(buspirate_fd, FEATURE_TRST, ACTION_DISABLE);
|
||||
else
|
||||
buspirate_jtag_set_feature(buspirate_fd,
|
||||
FEATURE_TRST, ACTION_ENABLE);
|
||||
buspirate_set_feature(buspirate_fd, FEATURE_TRST, ACTION_ENABLE);
|
||||
|
||||
if (srst)
|
||||
buspirate_jtag_set_feature(buspirate_fd,
|
||||
FEATURE_SRST, ACTION_DISABLE);
|
||||
buspirate_set_feature(buspirate_fd, FEATURE_SRST, ACTION_DISABLE);
|
||||
else
|
||||
buspirate_jtag_set_feature(buspirate_fd,
|
||||
FEATURE_SRST, ACTION_ENABLE);
|
||||
buspirate_set_feature(buspirate_fd, FEATURE_SRST, ACTION_ENABLE);
|
||||
}
|
||||
|
||||
static void buspirate_set_feature(int fd, char feat, char action)
|
||||
{
|
||||
if (swd_mode)
|
||||
buspirate_swd_set_feature(fd, feat, action);
|
||||
else
|
||||
buspirate_jtag_set_feature(fd, feat, action);
|
||||
}
|
||||
|
||||
static void buspirate_set_mode(int fd, char mode)
|
||||
{
|
||||
if (swd_mode)
|
||||
buspirate_swd_set_mode(fd, mode);
|
||||
else
|
||||
buspirate_jtag_set_mode(fd, mode);
|
||||
}
|
||||
|
||||
static void buspirate_set_speed(int fd, char speed)
|
||||
{
|
||||
if (swd_mode)
|
||||
buspirate_swd_set_speed(fd, speed);
|
||||
else
|
||||
buspirate_jtag_set_speed(fd, speed);
|
||||
}
|
||||
|
||||
|
||||
/*************** swd lowlevel functions ********************/
|
||||
|
||||
static void buspirate_swd_set_speed(int fd, char speed)
|
||||
{
|
||||
int ret;
|
||||
uint8_t tmp[1];
|
||||
|
||||
LOG_DEBUG("Buspirate speed setting in SWD mode defaults to 400 kHz");
|
||||
|
||||
/* speed settings */
|
||||
tmp[0] = CMD_RAW_SPEED | SPEED_RAW_400_KHZ;
|
||||
buspirate_serial_write(fd, tmp, 1);
|
||||
ret = buspirate_serial_read(fd, tmp, 1);
|
||||
if (ret != 1) {
|
||||
LOG_ERROR("Buspirate did not answer correctly");
|
||||
exit(-1);
|
||||
}
|
||||
if (tmp[0] != 1) {
|
||||
LOG_ERROR("Buspirate did not reply as expected to the speed change command");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
static void buspirate_swd_set_mode(int fd, char mode)
|
||||
{
|
||||
int ret;
|
||||
uint8_t tmp[1];
|
||||
|
||||
/* raw-wire mode configuration */
|
||||
if (mode == MODE_HIZ)
|
||||
tmp[0] = CMD_RAW_MODE | CMD_RAW_CONFIG_LSB;
|
||||
else
|
||||
tmp[0] = CMD_RAW_MODE | CMD_RAW_CONFIG_LSB | CMD_RAW_CONFIG_3V3;
|
||||
|
||||
buspirate_serial_write(fd, tmp, 1);
|
||||
ret = buspirate_serial_read(fd, tmp, 1);
|
||||
if (ret != 1) {
|
||||
LOG_ERROR("Buspirate did not answer correctly");
|
||||
exit(-1);
|
||||
}
|
||||
if (tmp[0] != 1) {
|
||||
LOG_ERROR("Buspirate did not reply as expected to the configure command");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
static void buspirate_swd_set_feature(int fd, char feat, char action)
|
||||
{
|
||||
int ret;
|
||||
uint8_t tmp[1];
|
||||
|
||||
switch (feat) {
|
||||
case FEATURE_TRST:
|
||||
LOG_DEBUG("Buspirate TRST feature not available in SWD mode");
|
||||
return;
|
||||
case FEATURE_LED:
|
||||
LOG_ERROR("Buspirate LED feature not available in SWD mode");
|
||||
return;
|
||||
case FEATURE_SRST:
|
||||
swd_features = (action == ACTION_ENABLE) ? swd_features | 0x02 : swd_features & 0x0D;
|
||||
break;
|
||||
case FEATURE_PULLUP:
|
||||
swd_features = (action == ACTION_ENABLE) ? swd_features | 0x04 : swd_features & 0x0B;
|
||||
break;
|
||||
case FEATURE_VREG:
|
||||
swd_features = (action == ACTION_ENABLE) ? swd_features | 0x08 : swd_features & 0x07;
|
||||
break;
|
||||
default:
|
||||
LOG_DEBUG("Buspirate unknown feature %d", feat);
|
||||
return;
|
||||
}
|
||||
|
||||
tmp[0] = CMD_RAW_PERIPH | swd_features;
|
||||
buspirate_serial_write(fd, tmp, 1);
|
||||
ret = buspirate_serial_read(fd, tmp, 1);
|
||||
if (ret != 1) {
|
||||
LOG_DEBUG("Buspirate feature %d not supported in SWD mode", feat);
|
||||
} else if (tmp[0] != 1) {
|
||||
LOG_ERROR("Buspirate did not reply as expected to the configure command");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
/*************** jtag lowlevel functions ********************/
|
||||
static void buspirate_jtag_enable(int fd)
|
||||
static void buspirate_bbio_enable(int fd)
|
||||
{
|
||||
int ret;
|
||||
char tmp[21] = { [0 ... 20] = 0x00 };
|
||||
char command;
|
||||
const char *mode_answers[2] = { "OCD1", "RAW1" };
|
||||
const char *correct_ans = NULL;
|
||||
uint8_t tmp[21] = { [0 ... 20] = 0x00 };
|
||||
int done = 0;
|
||||
int cmd_sent = 0;
|
||||
|
||||
LOG_DEBUG("Entering binary mode");
|
||||
if (swd_mode) {
|
||||
command = CMD_ENTER_RWIRE;
|
||||
correct_ans = mode_answers[1];
|
||||
} else {
|
||||
command = CMD_ENTER_OOCD;
|
||||
correct_ans = mode_answers[0];
|
||||
}
|
||||
|
||||
LOG_DEBUG("Entering binary mode, that is %s", correct_ans);
|
||||
buspirate_serial_write(fd, tmp, 20);
|
||||
usleep(10000);
|
||||
|
||||
/* reads 1 to n "BBIO1"s and one "OCD1" */
|
||||
/* reads 1 to n "BBIO1"s and one "OCD1" or "RAW1" */
|
||||
while (!done) {
|
||||
ret = buspirate_serial_read(fd, tmp, 4);
|
||||
if (ret != 4) {
|
||||
@@ -841,7 +1013,7 @@ static void buspirate_jtag_enable(int fd)
|
||||
"/OpenOCD support enabled?");
|
||||
exit(-1);
|
||||
}
|
||||
if (strncmp(tmp, "BBIO", 4) == 0) {
|
||||
if (strncmp((char *)tmp, "BBIO", 4) == 0) {
|
||||
ret = buspirate_serial_read(fd, tmp, 1);
|
||||
if (ret != 1) {
|
||||
LOG_ERROR("Buspirate did not answer correctly! "
|
||||
@@ -854,14 +1026,14 @@ static void buspirate_jtag_enable(int fd)
|
||||
}
|
||||
if (cmd_sent == 0) {
|
||||
cmd_sent = 1;
|
||||
tmp[0] = CMD_ENTER_OOCD;
|
||||
tmp[0] = command;
|
||||
ret = buspirate_serial_write(fd, tmp, 1);
|
||||
if (ret != 1) {
|
||||
LOG_ERROR("error reading");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
} else if (strncmp(tmp, "OCD1", 4) == 0)
|
||||
} else if (strncmp((char *)tmp, correct_ans, 4) == 0)
|
||||
done = 1;
|
||||
else {
|
||||
LOG_ERROR("Buspirate did not answer correctly! "
|
||||
@@ -874,14 +1046,14 @@ static void buspirate_jtag_enable(int fd)
|
||||
|
||||
static void buspirate_jtag_reset(int fd)
|
||||
{
|
||||
char tmp[5];
|
||||
uint8_t tmp[5];
|
||||
|
||||
tmp[0] = 0x00; /* exit OCD1 mode */
|
||||
buspirate_serial_write(fd, tmp, 1);
|
||||
usleep(10000);
|
||||
/* We ignore the return value here purposly, nothing we can do */
|
||||
buspirate_serial_read(fd, tmp, 5);
|
||||
if (strncmp(tmp, "BBIO1", 5) == 0) {
|
||||
if (strncmp((char *)tmp, "BBIO1", 5) == 0) {
|
||||
tmp[0] = 0x0F; /* reset BP */
|
||||
buspirate_serial_write(fd, tmp, 1);
|
||||
} else
|
||||
@@ -891,8 +1063,8 @@ static void buspirate_jtag_reset(int fd)
|
||||
static void buspirate_jtag_set_speed(int fd, char speed)
|
||||
{
|
||||
int ret;
|
||||
char tmp[2];
|
||||
char ack[2];
|
||||
uint8_t tmp[2];
|
||||
uint8_t ack[2];
|
||||
|
||||
ack[0] = 0xAA;
|
||||
ack[1] = 0x55;
|
||||
@@ -924,7 +1096,7 @@ static void buspirate_jtag_set_speed(int fd, char speed)
|
||||
|
||||
static void buspirate_jtag_set_mode(int fd, char mode)
|
||||
{
|
||||
char tmp[2];
|
||||
uint8_t tmp[2];
|
||||
tmp[0] = CMD_PORT_MODE;
|
||||
tmp[1] = mode;
|
||||
buspirate_jtag_command(fd, tmp, 2);
|
||||
@@ -932,7 +1104,7 @@ static void buspirate_jtag_set_mode(int fd, char mode)
|
||||
|
||||
static void buspirate_jtag_set_feature(int fd, char feat, char action)
|
||||
{
|
||||
char tmp[3];
|
||||
uint8_t tmp[3];
|
||||
tmp[0] = CMD_FEATURE;
|
||||
tmp[1] = feat; /* what */
|
||||
tmp[2] = action; /* action */
|
||||
@@ -944,7 +1116,7 @@ static void buspirate_jtag_get_adcs(int fd)
|
||||
uint8_t tmp[10];
|
||||
uint16_t a, b, c, d;
|
||||
tmp[0] = CMD_READ_ADCS;
|
||||
buspirate_jtag_command(fd, (char *)tmp, 1);
|
||||
buspirate_jtag_command(fd, tmp, 1);
|
||||
a = tmp[2] << 8 | tmp[3];
|
||||
b = tmp[4] << 8 | tmp[5];
|
||||
c = tmp[6] << 8 | tmp[7];
|
||||
@@ -957,7 +1129,7 @@ static void buspirate_jtag_get_adcs(int fd)
|
||||
}
|
||||
|
||||
static unsigned char buspirate_jtag_command(int fd,
|
||||
char *cmd, int cmdlen)
|
||||
uint8_t *cmd, int cmdlen)
|
||||
{
|
||||
int res;
|
||||
int len = 0;
|
||||
@@ -1048,7 +1220,7 @@ static int buspirate_serial_setspeed(int fd, char speed, cc_t timeout)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int buspirate_serial_write(int fd, char *buf, int size)
|
||||
static int buspirate_serial_write(int fd, uint8_t *buf, int size)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
@@ -1063,7 +1235,7 @@ static int buspirate_serial_write(int fd, char *buf, int size)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int buspirate_serial_read(int fd, char *buf, int size)
|
||||
static int buspirate_serial_read(int fd, uint8_t *buf, int size)
|
||||
{
|
||||
int len = 0;
|
||||
int ret = 0;
|
||||
@@ -1102,7 +1274,7 @@ static void buspirate_serial_close(int fd)
|
||||
|
||||
#define LINE_SIZE 81
|
||||
#define BYTES_PER_LINE 16
|
||||
static void buspirate_print_buffer(char *buf, int size)
|
||||
static void buspirate_print_buffer(uint8_t *buf, int size)
|
||||
{
|
||||
char line[LINE_SIZE];
|
||||
char tmp[10];
|
||||
@@ -1124,3 +1296,240 @@ static void buspirate_print_buffer(char *buf, int size)
|
||||
if (line[0] != 0)
|
||||
LOG_DEBUG("%s", line);
|
||||
}
|
||||
|
||||
/************************* SWD related stuff **********/
|
||||
|
||||
static int buspirate_swd_init(void)
|
||||
{
|
||||
LOG_INFO("Buspirate SWD mode enabled");
|
||||
swd_mode = true;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int buspirate_swd_switch_seq(enum swd_special_seq seq)
|
||||
{
|
||||
const uint8_t *sequence;
|
||||
int sequence_len;
|
||||
uint8_t tmp[64];
|
||||
|
||||
switch (seq) {
|
||||
case LINE_RESET:
|
||||
LOG_DEBUG("SWD line reset");
|
||||
sequence = swd_seq_line_reset;
|
||||
sequence_len = DIV_ROUND_UP(swd_seq_line_reset_len, 8);
|
||||
break;
|
||||
case JTAG_TO_SWD:
|
||||
LOG_DEBUG("JTAG-to-SWD");
|
||||
sequence = swd_seq_jtag_to_swd;
|
||||
sequence_len = DIV_ROUND_UP(swd_seq_jtag_to_swd_len, 8);
|
||||
break;
|
||||
case SWD_TO_JTAG:
|
||||
LOG_DEBUG("SWD-to-JTAG");
|
||||
sequence = swd_seq_swd_to_jtag;
|
||||
sequence_len = DIV_ROUND_UP(swd_seq_swd_to_jtag_len, 8);
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("Sequence %d not supported", seq);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
/* FIXME: all above sequences fit into one pirate command for now
|
||||
* but it may cause trouble later
|
||||
*/
|
||||
|
||||
tmp[0] = 0x10 + ((sequence_len - 1) & 0x0F);
|
||||
memcpy(tmp + 1, sequence, sequence_len);
|
||||
|
||||
buspirate_serial_write(buspirate_fd, tmp, sequence_len + 1);
|
||||
buspirate_serial_read(buspirate_fd, tmp, sequence_len + 1);
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static uint8_t buspirate_swd_write_header(uint8_t cmd)
|
||||
{
|
||||
uint8_t tmp[8];
|
||||
int to_send;
|
||||
|
||||
tmp[0] = 0x10; /* bus pirate: send 1 byte */
|
||||
tmp[1] = cmd; /* swd cmd */
|
||||
tmp[2] = 0x07; /* ack __x */
|
||||
tmp[3] = 0x07; /* ack _x_ */
|
||||
tmp[4] = 0x07; /* ack x__ */
|
||||
tmp[5] = 0x07; /* write mode trn_1 */
|
||||
tmp[6] = 0x07; /* write mode trn_2 */
|
||||
|
||||
to_send = ((cmd & SWD_CMD_RnW) == 0) ? 7 : 5;
|
||||
buspirate_serial_write(buspirate_fd, tmp, to_send);
|
||||
|
||||
/* read ack */
|
||||
buspirate_serial_read(buspirate_fd, tmp, 2); /* drop pirate command ret vals */
|
||||
buspirate_serial_read(buspirate_fd, tmp, to_send - 2); /* ack bits */
|
||||
|
||||
return tmp[2] << 2 | tmp[1] << 1 | tmp[0];
|
||||
}
|
||||
|
||||
static void buspirate_swd_idle_clocks(uint32_t no_bits)
|
||||
{
|
||||
uint32_t no_bytes;
|
||||
uint8_t tmp[20];
|
||||
|
||||
no_bytes = (no_bits + 7) / 8;
|
||||
memset(tmp + 1, 0x00, sizeof(tmp) - 1);
|
||||
|
||||
/* unfortunately bus pirate misbehaves when clocks are sent in parts
|
||||
* so we need to limit at 128 clock cycles
|
||||
*/
|
||||
if (no_bytes > 16)
|
||||
no_bytes = 16;
|
||||
|
||||
while (no_bytes) {
|
||||
uint8_t to_send = no_bytes > 16 ? 16 : no_bytes;
|
||||
tmp[0] = 0x10 + ((to_send - 1) & 0x0F);
|
||||
|
||||
buspirate_serial_write(buspirate_fd, tmp, to_send + 1);
|
||||
buspirate_serial_read(buspirate_fd, tmp, to_send + 1);
|
||||
|
||||
no_bytes -= to_send;
|
||||
}
|
||||
}
|
||||
|
||||
static void buspirate_swd_clear_sticky_errors(void)
|
||||
{
|
||||
buspirate_swd_write_reg(swd_cmd(false, false, DP_ABORT),
|
||||
STKCMPCLR | STKERRCLR | WDERRCLR | ORUNERRCLR, 0);
|
||||
}
|
||||
|
||||
static void buspirate_swd_read_reg(uint8_t cmd, uint32_t *value, uint32_t ap_delay_clk)
|
||||
{
|
||||
uint8_t tmp[16];
|
||||
|
||||
LOG_DEBUG("buspirate_swd_read_reg");
|
||||
assert(cmd & SWD_CMD_RnW);
|
||||
|
||||
if (queued_retval != ERROR_OK) {
|
||||
LOG_DEBUG("Skip buspirate_swd_read_reg because queued_retval=%d", queued_retval);
|
||||
return;
|
||||
}
|
||||
|
||||
cmd |= SWD_CMD_START | SWD_CMD_PARK;
|
||||
uint8_t ack = buspirate_swd_write_header(cmd);
|
||||
|
||||
/* do a read transaction */
|
||||
tmp[0] = 0x06; /* 4 data bytes */
|
||||
tmp[1] = 0x06;
|
||||
tmp[2] = 0x06;
|
||||
tmp[3] = 0x06;
|
||||
tmp[4] = 0x07; /* parity bit */
|
||||
tmp[5] = 0x21; /* 2 turnaround clocks */
|
||||
|
||||
buspirate_serial_write(buspirate_fd, tmp, 6);
|
||||
buspirate_serial_read(buspirate_fd, tmp, 6);
|
||||
|
||||
/* store the data and parity */
|
||||
uint32_t data = (uint8_t) tmp[0];
|
||||
data |= (uint8_t) tmp[1] << 8;
|
||||
data |= (uint8_t) tmp[2] << 16;
|
||||
data |= (uint8_t) tmp[3] << 24;
|
||||
int parity = tmp[4] ? 0x01 : 0x00;
|
||||
|
||||
LOG_DEBUG("%s %s %s reg %X = %08"PRIx32,
|
||||
ack == SWD_ACK_OK ? "OK" : ack == SWD_ACK_WAIT ? "WAIT" : ack == SWD_ACK_FAULT ? "FAULT" : "JUNK",
|
||||
cmd & SWD_CMD_APnDP ? "AP" : "DP",
|
||||
cmd & SWD_CMD_RnW ? "read" : "write",
|
||||
(cmd & SWD_CMD_A32) >> 1,
|
||||
data);
|
||||
|
||||
switch (ack) {
|
||||
case SWD_ACK_OK:
|
||||
if (parity != parity_u32(data)) {
|
||||
LOG_DEBUG("Read data parity mismatch %x %x", parity, parity_u32(data));
|
||||
queued_retval = ERROR_FAIL;
|
||||
return;
|
||||
}
|
||||
if (value)
|
||||
*value = data;
|
||||
if (cmd & SWD_CMD_APnDP)
|
||||
buspirate_swd_idle_clocks(ap_delay_clk);
|
||||
return;
|
||||
case SWD_ACK_WAIT:
|
||||
LOG_DEBUG("SWD_ACK_WAIT");
|
||||
buspirate_swd_clear_sticky_errors();
|
||||
return;
|
||||
case SWD_ACK_FAULT:
|
||||
LOG_DEBUG("SWD_ACK_FAULT");
|
||||
queued_retval = ack;
|
||||
return;
|
||||
default:
|
||||
LOG_DEBUG("No valid acknowledge: ack=%d", ack);
|
||||
queued_retval = ack;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void buspirate_swd_write_reg(uint8_t cmd, uint32_t value, uint32_t ap_delay_clk)
|
||||
{
|
||||
uint8_t tmp[16];
|
||||
|
||||
LOG_DEBUG("buspirate_swd_write_reg");
|
||||
assert(!(cmd & SWD_CMD_RnW));
|
||||
|
||||
if (queued_retval != ERROR_OK) {
|
||||
LOG_DEBUG("Skip buspirate_swd_write_reg because queued_retval=%d", queued_retval);
|
||||
return;
|
||||
}
|
||||
|
||||
cmd |= SWD_CMD_START | SWD_CMD_PARK;
|
||||
uint8_t ack = buspirate_swd_write_header(cmd);
|
||||
|
||||
/* do a write transaction */
|
||||
tmp[0] = 0x10 + ((4 + 1 - 1) & 0xF); /* bus pirate: send 4+1 bytes */
|
||||
buf_set_u32((uint8_t *) tmp + 1, 0, 32, value);
|
||||
/* write sequence ends with parity bit and 7 idle ticks */
|
||||
tmp[5] = parity_u32(value) ? 0x01 : 0x00;
|
||||
|
||||
buspirate_serial_write(buspirate_fd, tmp, 6);
|
||||
buspirate_serial_read(buspirate_fd, tmp, 6);
|
||||
|
||||
LOG_DEBUG("%s %s %s reg %X = %08"PRIx32,
|
||||
ack == SWD_ACK_OK ? "OK" : ack == SWD_ACK_WAIT ? "WAIT" : ack == SWD_ACK_FAULT ? "FAULT" : "JUNK",
|
||||
cmd & SWD_CMD_APnDP ? "AP" : "DP",
|
||||
cmd & SWD_CMD_RnW ? "read" : "write",
|
||||
(cmd & SWD_CMD_A32) >> 1,
|
||||
value);
|
||||
|
||||
switch (ack) {
|
||||
case SWD_ACK_OK:
|
||||
if (cmd & SWD_CMD_APnDP)
|
||||
buspirate_swd_idle_clocks(ap_delay_clk);
|
||||
return;
|
||||
case SWD_ACK_WAIT:
|
||||
LOG_DEBUG("SWD_ACK_WAIT");
|
||||
buspirate_swd_clear_sticky_errors();
|
||||
return;
|
||||
case SWD_ACK_FAULT:
|
||||
LOG_DEBUG("SWD_ACK_FAULT");
|
||||
queued_retval = ack;
|
||||
return;
|
||||
default:
|
||||
LOG_DEBUG("No valid acknowledge: ack=%d", ack);
|
||||
queued_retval = ack;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static int buspirate_swd_run_queue(void)
|
||||
{
|
||||
LOG_DEBUG("buspirate_swd_run_queue");
|
||||
/* A transaction must be followed by another transaction or at least 8 idle cycles to
|
||||
* ensure that data is clocked through the AP. */
|
||||
buspirate_swd_idle_clocks(8);
|
||||
|
||||
int retval = queued_retval;
|
||||
queued_retval = ERROR_OK;
|
||||
LOG_DEBUG("SWD queue return value: %02x", retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -729,6 +729,20 @@ static void cmsis_dap_swd_read_reg(uint8_t cmd, uint32_t *value, uint32_t ap_del
|
||||
cmsis_dap_swd_queue_cmd(cmd, value, 0);
|
||||
}
|
||||
|
||||
static int cmsis_dap_get_serial_info(void)
|
||||
{
|
||||
uint8_t *data;
|
||||
|
||||
int retval = cmsis_dap_cmd_DAP_Info(INFO_ID_SERNUM, &data);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
if (data[0]) /* strlen */
|
||||
LOG_INFO("CMSIS-DAP: Serial# = %s", &data[1]);
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int cmsis_dap_get_version_info(void)
|
||||
{
|
||||
uint8_t *data;
|
||||
@@ -802,8 +816,7 @@ static int cmsis_dap_swd_switch_seq(enum swd_special_seq seq)
|
||||
|
||||
/* When we are reconnecting, DAP_Connect needs to be rerun, at
|
||||
* least on Keil ULINK-ME */
|
||||
retval = cmsis_dap_cmd_DAP_Connect(seq == LINE_RESET || seq == JTAG_TO_SWD ?
|
||||
CONNECT_SWD : CONNECT_JTAG);
|
||||
retval = cmsis_dap_cmd_DAP_Connect(CONNECT_SWD);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
}
|
||||
@@ -842,17 +855,6 @@ static int cmsis_dap_swd_open(void)
|
||||
{
|
||||
int retval;
|
||||
|
||||
if (cmsis_dap_handle == NULL) {
|
||||
/* SWD init */
|
||||
retval = cmsis_dap_usb_open();
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
retval = cmsis_dap_get_caps_info();
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (!(cmsis_dap_handle->caps & INFO_CAPS_SWD)) {
|
||||
LOG_ERROR("CMSIS-DAP: SWD not supported");
|
||||
return ERROR_JTAG_DEVICE_ERROR;
|
||||
@@ -873,6 +875,22 @@ static int cmsis_dap_init(void)
|
||||
int retval;
|
||||
uint8_t *data;
|
||||
|
||||
retval = cmsis_dap_usb_open();
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
retval = cmsis_dap_get_caps_info();
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
retval = cmsis_dap_get_version_info();
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
retval = cmsis_dap_get_serial_info();
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
if (swd_mode) {
|
||||
retval = cmsis_dap_swd_open();
|
||||
if (retval != ERROR_OK)
|
||||
@@ -880,16 +898,6 @@ static int cmsis_dap_init(void)
|
||||
}
|
||||
|
||||
if (cmsis_dap_handle == NULL) {
|
||||
|
||||
/* JTAG init */
|
||||
retval = cmsis_dap_usb_open();
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
retval = cmsis_dap_get_caps_info();
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
/* Connect in JTAG mode */
|
||||
if (!(cmsis_dap_handle->caps & INFO_CAPS_JTAG)) {
|
||||
LOG_ERROR("CMSIS-DAP: JTAG not supported");
|
||||
@@ -903,10 +911,6 @@ static int cmsis_dap_init(void)
|
||||
LOG_INFO("CMSIS-DAP: Interface Initialised (JTAG)");
|
||||
}
|
||||
|
||||
retval = cmsis_dap_get_version_info();
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
/* INFO_ID_PKT_SZ - short */
|
||||
retval = cmsis_dap_cmd_DAP_Info(INFO_ID_PKT_SZ, &data);
|
||||
if (retval != ERROR_OK)
|
||||
@@ -1458,6 +1462,12 @@ static void cmsis_dap_execute_stableclocks(struct jtag_command *cmd)
|
||||
cmsis_dap_stableclocks(cmd->cmd.runtest->num_cycles);
|
||||
}
|
||||
|
||||
static void cmsis_dap_execute_tms(struct jtag_command *cmd)
|
||||
{
|
||||
DEBUG_JTAG_IO("TMS: %d bits", cmd->cmd.tms->num_bits);
|
||||
cmsis_dap_cmd_DAP_SWJ_Sequence(cmd->cmd.tms->num_bits, cmd->cmd.tms->bits);
|
||||
}
|
||||
|
||||
/* TODO: Is there need to call cmsis_dap_flush() for the JTAG_PATHMOVE,
|
||||
* JTAG_RUNTEST, JTAG_STABLECLOCKS? */
|
||||
static void cmsis_dap_execute_command(struct jtag_command *cmd)
|
||||
@@ -1488,6 +1498,8 @@ static void cmsis_dap_execute_command(struct jtag_command *cmd)
|
||||
cmsis_dap_execute_stableclocks(cmd);
|
||||
break;
|
||||
case JTAG_TMS:
|
||||
cmsis_dap_execute_tms(cmd);
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("BUG: unknown JTAG command type 0x%X encountered", cmd->type);
|
||||
exit(-1);
|
||||
@@ -1650,6 +1662,7 @@ static const char * const cmsis_dap_transport[] = { "swd", "jtag", NULL };
|
||||
|
||||
struct jtag_interface cmsis_dap_interface = {
|
||||
.name = "cmsis-dap",
|
||||
.supported = DEBUG_CAP_TMS_SEQ,
|
||||
.commands = cmsis_dap_command_handlers,
|
||||
.swd = &cmsis_dap_swd_driver,
|
||||
.transports = cmsis_dap_transport,
|
||||
|
||||
@@ -0,0 +1,669 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2010 Serge Vakulenko *
|
||||
* serge@vak.ru *
|
||||
* *
|
||||
* 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, see <http://www.gnu.org/licenses/>. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#if IS_CYGWIN == 1
|
||||
#include "windows.h"
|
||||
#undef LOG_ERROR
|
||||
#endif
|
||||
|
||||
/* project specific includes */
|
||||
#include <jtag/interface.h>
|
||||
#include <jtag/commands.h>
|
||||
#include <helper/time_support.h>
|
||||
#include "libusb1_common.h"
|
||||
|
||||
/* system includes */
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
|
||||
/*
|
||||
* Bit 7 (0x80, pin 6, RI ): unused.
|
||||
* Bit 6 (0x40, pin 10,DCD): /SYSRST output.
|
||||
* Bit 5 (0x20, pin 9, DSR): unused.
|
||||
* Bit 4 (0x10, pin 2, DTR): /TRST output.
|
||||
* Bit 3 (0x08, pin 11,CTS): TMS output.
|
||||
* Bit 2 (0x04, pin 3, RTS): TDO input.
|
||||
* Bit 1 (0x02, pin 5, RXD): TDI output.
|
||||
* Bit 0 (0x01, pin 1, TXD): TCK output.
|
||||
*
|
||||
* Sync bit bang mode is implemented as described in FTDI Application
|
||||
* Note AN232R-01: "Bit Bang Modes for the FT232R and FT245R".
|
||||
*/
|
||||
#define TCK (1 << 0)
|
||||
#define TDI (1 << 1)
|
||||
#define READ_TDO (1 << 2)
|
||||
#define TMS (1 << 3)
|
||||
#define NTRST (1 << 4)
|
||||
#define NSYSRST (1 << 6)
|
||||
|
||||
/*
|
||||
* USB endpoints.
|
||||
*/
|
||||
#define IN_EP 0x02
|
||||
#define OUT_EP 0x81
|
||||
|
||||
/* Requests */
|
||||
#define SIO_RESET 0 /* Reset the port */
|
||||
#define SIO_MODEM_CTRL 1 /* Set the modem control register */
|
||||
#define SIO_SET_FLOW_CTRL 2 /* Set flow control register */
|
||||
#define SIO_SET_BAUD_RATE 3 /* Set baud rate */
|
||||
#define SIO_SET_DATA 4 /* Set the data characteristics of the port */
|
||||
#define SIO_POLL_MODEM_STATUS 5
|
||||
#define SIO_SET_EVENT_CHAR 6
|
||||
#define SIO_SET_ERROR_CHAR 7
|
||||
#define SIO_SET_LATENCY_TIMER 9
|
||||
#define SIO_GET_LATENCY_TIMER 10
|
||||
#define SIO_SET_BITMODE 11
|
||||
#define SIO_READ_PINS 12
|
||||
#define SIO_READ_EEPROM 0x90
|
||||
#define SIO_WRITE_EEPROM 0x91
|
||||
#define SIO_ERASE_EEPROM 0x92
|
||||
|
||||
#define FT232R_BUF_SIZE 4000
|
||||
|
||||
static char *ft232r_serial_desc;
|
||||
static uint16_t ft232r_vid = 0x0403; /* FTDI */
|
||||
static uint16_t ft232r_pid = 0x6001; /* FT232R */
|
||||
static jtag_libusb_device_handle *adapter;
|
||||
|
||||
static uint8_t *ft232r_output;
|
||||
static size_t ft232r_output_len;
|
||||
|
||||
/**
|
||||
* Perform sync bitbang output/input transaction.
|
||||
* Before call, an array ft232r_output[] should be filled with data to send.
|
||||
* Counter ft232r_output_len contains the number of bytes to send.
|
||||
* On return, received data is put back to array ft232r_output[].
|
||||
*/
|
||||
static int ft232r_send_recv(void)
|
||||
{
|
||||
/* FIFO TX buffer has 128 bytes.
|
||||
* FIFO RX buffer has 256 bytes.
|
||||
* First two bytes of received packet contain contain modem
|
||||
* and line status and are ignored.
|
||||
* Unfortunately, transfer sizes bigger than 64 bytes
|
||||
* frequently cause hang ups. */
|
||||
assert(ft232r_output_len > 0);
|
||||
|
||||
size_t total_written = 0;
|
||||
size_t total_read = 0;
|
||||
int rxfifo_free = 128;
|
||||
|
||||
while (total_read < ft232r_output_len) {
|
||||
/* Write */
|
||||
int bytes_to_write = ft232r_output_len - total_written;
|
||||
if (bytes_to_write > 64)
|
||||
bytes_to_write = 64;
|
||||
if (bytes_to_write > rxfifo_free)
|
||||
bytes_to_write = rxfifo_free;
|
||||
|
||||
if (bytes_to_write) {
|
||||
int n = jtag_libusb_bulk_write(adapter, IN_EP,
|
||||
(char *) ft232r_output + total_written,
|
||||
bytes_to_write, 1000);
|
||||
|
||||
if (n == 0) {
|
||||
LOG_ERROR("usb bulk write failed");
|
||||
return ERROR_JTAG_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
total_written += n;
|
||||
rxfifo_free -= n;
|
||||
}
|
||||
|
||||
/* Read */
|
||||
uint8_t reply[64];
|
||||
|
||||
int n = jtag_libusb_bulk_read(adapter, OUT_EP,
|
||||
(char *) reply,
|
||||
sizeof(reply), 1000);
|
||||
|
||||
if (n == 0) {
|
||||
LOG_ERROR("usb bulk read failed");
|
||||
return ERROR_JTAG_DEVICE_ERROR;
|
||||
}
|
||||
if (n > 2) {
|
||||
/* Copy data, ignoring first 2 bytes. */
|
||||
memcpy(ft232r_output + total_read, reply + 2, n - 2);
|
||||
int bytes_read = n - 2;
|
||||
total_read += bytes_read;
|
||||
rxfifo_free += bytes_read;
|
||||
if (total_read > total_written) {
|
||||
LOG_ERROR("read more bytes than wrote");
|
||||
return ERROR_JTAG_DEVICE_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
ft232r_output_len = 0;
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add one TCK/TMS/TDI sample to send buffer.
|
||||
*/
|
||||
static void ft232r_write(int tck, int tms, int tdi)
|
||||
{
|
||||
unsigned out_value = NTRST | NSYSRST;
|
||||
if (tck)
|
||||
out_value |= TCK;
|
||||
if (tms)
|
||||
out_value |= TMS;
|
||||
if (tdi)
|
||||
out_value |= TDI;
|
||||
|
||||
if (ft232r_output_len >= FT232R_BUF_SIZE) {
|
||||
/* FIXME: should we just execute queue here? */
|
||||
LOG_ERROR("ft232r_write: buffer overflow");
|
||||
return;
|
||||
}
|
||||
ft232r_output[ft232r_output_len++] = out_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Control /TRST and /SYSRST pins.
|
||||
* Perform immediate bitbang transaction.
|
||||
*/
|
||||
static void ft232r_reset(int trst, int srst)
|
||||
{
|
||||
unsigned out_value = NTRST | NSYSRST;
|
||||
LOG_DEBUG("ft232r_reset(%d,%d)", trst, srst);
|
||||
|
||||
if (trst == 1)
|
||||
out_value &= ~NTRST; /* switch /TRST low */
|
||||
else if (trst == 0)
|
||||
out_value |= NTRST; /* switch /TRST high */
|
||||
|
||||
if (srst == 1)
|
||||
out_value &= ~NSYSRST; /* switch /SYSRST low */
|
||||
else if (srst == 0)
|
||||
out_value |= NSYSRST; /* switch /SYSRST high */
|
||||
|
||||
if (ft232r_output_len >= FT232R_BUF_SIZE) {
|
||||
/* FIXME: should we just execute queue here? */
|
||||
LOG_ERROR("ft232r_write: buffer overflow");
|
||||
return;
|
||||
}
|
||||
|
||||
ft232r_output[ft232r_output_len++] = out_value;
|
||||
ft232r_send_recv();
|
||||
}
|
||||
|
||||
static int ft232r_speed(int divisor)
|
||||
{
|
||||
int baud = (divisor == 0) ? 3000000 :
|
||||
(divisor == 1) ? 2000000 :
|
||||
3000000 / divisor;
|
||||
LOG_DEBUG("ft232r_speed(%d) rate %d bits/sec", divisor, baud);
|
||||
|
||||
if (jtag_libusb_control_transfer(adapter,
|
||||
LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT,
|
||||
SIO_SET_BAUD_RATE, divisor, 0, 0, 0, 1000) != 0) {
|
||||
LOG_ERROR("cannot set baud rate");
|
||||
return ERROR_JTAG_DEVICE_ERROR;
|
||||
}
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int ft232r_init(void)
|
||||
{
|
||||
uint16_t avids[] = {ft232r_vid, 0};
|
||||
uint16_t apids[] = {ft232r_pid, 0};
|
||||
if (jtag_libusb_open(avids, apids, ft232r_serial_desc, &adapter)) {
|
||||
LOG_ERROR("ft232r not found: vid=%04x, pid=%04x, serial=%s\n",
|
||||
ft232r_vid, ft232r_pid, (ft232r_serial_desc == NULL) ? "[any]" : ft232r_serial_desc);
|
||||
return ERROR_JTAG_INIT_FAILED;
|
||||
}
|
||||
|
||||
libusb_detach_kernel_driver(adapter, 0);
|
||||
|
||||
if (jtag_libusb_claim_interface(adapter, 0)) {
|
||||
LOG_ERROR("unable to claim interface");
|
||||
return ERROR_JTAG_INIT_FAILED;
|
||||
}
|
||||
|
||||
/* Reset the device. */
|
||||
if (jtag_libusb_control_transfer(adapter,
|
||||
LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT,
|
||||
SIO_RESET, 0, 0, 0, 0, 1000) != 0) {
|
||||
LOG_ERROR("unable to reset device");
|
||||
return ERROR_JTAG_INIT_FAILED;
|
||||
}
|
||||
|
||||
/* Sync bit bang mode. */
|
||||
if (jtag_libusb_control_transfer(adapter,
|
||||
LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT,
|
||||
SIO_SET_BITMODE, TCK | TDI | TMS | NTRST | NSYSRST | 0x400,
|
||||
0, 0, 0, 1000) != 0) {
|
||||
LOG_ERROR("cannot set sync bitbang mode");
|
||||
return ERROR_JTAG_INIT_FAILED;
|
||||
}
|
||||
|
||||
/* Exactly 500 nsec between updates. */
|
||||
unsigned divisor = 1;
|
||||
unsigned char latency_timer = 1;
|
||||
|
||||
/* Frequency divisor is 14-bit non-zero value. */
|
||||
if (jtag_libusb_control_transfer(adapter,
|
||||
LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT,
|
||||
SIO_SET_BAUD_RATE, divisor,
|
||||
0, 0, 0, 1000) != 0) {
|
||||
LOG_ERROR("cannot set baud rate");
|
||||
return ERROR_JTAG_INIT_FAILED;
|
||||
}
|
||||
if (jtag_libusb_control_transfer(adapter,
|
||||
LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT,
|
||||
SIO_SET_LATENCY_TIMER, latency_timer, 0, 0, 0, 1000) != 0) {
|
||||
LOG_ERROR("unable to set latency timer");
|
||||
return ERROR_JTAG_INIT_FAILED;
|
||||
}
|
||||
|
||||
ft232r_output = malloc(FT232R_BUF_SIZE);
|
||||
if (ft232r_output == NULL) {
|
||||
LOG_ERROR("Unable to allocate memory for the buffer");
|
||||
return ERROR_JTAG_INIT_FAILED;
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int ft232r_quit(void)
|
||||
{
|
||||
if (jtag_libusb_release_interface(adapter, 0) != 0)
|
||||
LOG_ERROR("usb release interface failed");
|
||||
|
||||
jtag_libusb_close(adapter);
|
||||
free(ft232r_output);
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int ft232r_speed_div(int divisor, int *khz)
|
||||
{
|
||||
/* Maximum 3 Mbaud for bit bang mode. */
|
||||
if (divisor == 0)
|
||||
*khz = 3000;
|
||||
else if (divisor == 1)
|
||||
*khz = 2000;
|
||||
else
|
||||
*khz = 3000 / divisor;
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int ft232r_khz(int khz, int *divisor)
|
||||
{
|
||||
if (khz == 0) {
|
||||
LOG_DEBUG("RCLK not supported");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
/* Calculate frequency divisor. */
|
||||
if (khz > 2500)
|
||||
*divisor = 0; /* Special case: 3 MHz */
|
||||
else if (khz > 1700)
|
||||
*divisor = 1; /* Special case: 2 MHz */
|
||||
else {
|
||||
*divisor = (2*3000 / khz + 1) / 2;
|
||||
if (*divisor > 0x3FFF)
|
||||
*divisor = 0x3FFF;
|
||||
}
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
COMMAND_HANDLER(ft232r_handle_serial_desc_command)
|
||||
{
|
||||
if (CMD_ARGC == 1)
|
||||
ft232r_serial_desc = strdup(CMD_ARGV[0]);
|
||||
else
|
||||
LOG_ERROR("require exactly one argument to "
|
||||
"ft232r_serial_desc <serial>");
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
COMMAND_HANDLER(ft232r_handle_vid_pid_command)
|
||||
{
|
||||
if (CMD_ARGC > 2) {
|
||||
LOG_WARNING("ignoring extra IDs in ft232r_vid_pid "
|
||||
"(maximum is 1 pair)");
|
||||
CMD_ARGC = 2;
|
||||
}
|
||||
if (CMD_ARGC == 2) {
|
||||
COMMAND_PARSE_NUMBER(u16, CMD_ARGV[0], ft232r_vid);
|
||||
COMMAND_PARSE_NUMBER(u16, CMD_ARGV[1], ft232r_pid);
|
||||
} else
|
||||
LOG_WARNING("incomplete ft232r_vid_pid configuration");
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static const struct command_registration ft232r_command_handlers[] = {
|
||||
{
|
||||
.name = "ft232r_serial_desc",
|
||||
.handler = ft232r_handle_serial_desc_command,
|
||||
.mode = COMMAND_CONFIG,
|
||||
.help = "USB serial descriptor of the adapter",
|
||||
.usage = "serial string",
|
||||
},
|
||||
{
|
||||
.name = "ft232r_vid_pid",
|
||||
.handler = ft232r_handle_vid_pid_command,
|
||||
.mode = COMMAND_CONFIG,
|
||||
.help = "USB VID and PID of the adapter",
|
||||
.usage = "vid pid",
|
||||
},
|
||||
COMMAND_REGISTRATION_DONE
|
||||
};
|
||||
|
||||
/*
|
||||
* Synchronous bitbang protocol implementation.
|
||||
*/
|
||||
|
||||
static void syncbb_end_state(tap_state_t state)
|
||||
{
|
||||
if (tap_is_state_stable(state))
|
||||
tap_set_end_state(state);
|
||||
else {
|
||||
LOG_ERROR("BUG: %i is not a valid end state", state);
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
static void syncbb_state_move(int skip)
|
||||
{
|
||||
int i = 0, tms = 0;
|
||||
uint8_t tms_scan = tap_get_tms_path(tap_get_state(), tap_get_end_state());
|
||||
int tms_count = tap_get_tms_path_len(tap_get_state(), tap_get_end_state());
|
||||
|
||||
for (i = skip; i < tms_count; i++) {
|
||||
tms = (tms_scan >> i) & 1;
|
||||
ft232r_write(0, tms, 0);
|
||||
ft232r_write(1, tms, 0);
|
||||
}
|
||||
ft232r_write(0, tms, 0);
|
||||
|
||||
tap_set_state(tap_get_end_state());
|
||||
}
|
||||
|
||||
/**
|
||||
* Clock a bunch of TMS (or SWDIO) transitions, to change the JTAG
|
||||
* (or SWD) state machine.
|
||||
*/
|
||||
static int syncbb_execute_tms(struct jtag_command *cmd)
|
||||
{
|
||||
unsigned num_bits = cmd->cmd.tms->num_bits;
|
||||
const uint8_t *bits = cmd->cmd.tms->bits;
|
||||
|
||||
DEBUG_JTAG_IO("TMS: %d bits", num_bits);
|
||||
|
||||
int tms = 0;
|
||||
for (unsigned i = 0; i < num_bits; i++) {
|
||||
tms = ((bits[i/8] >> (i % 8)) & 1);
|
||||
ft232r_write(0, tms, 0);
|
||||
ft232r_write(1, tms, 0);
|
||||
}
|
||||
ft232r_write(0, tms, 0);
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static void syncbb_path_move(struct pathmove_command *cmd)
|
||||
{
|
||||
int num_states = cmd->num_states;
|
||||
int state_count;
|
||||
int tms = 0;
|
||||
|
||||
state_count = 0;
|
||||
while (num_states) {
|
||||
if (tap_state_transition(tap_get_state(), false) == cmd->path[state_count]) {
|
||||
tms = 0;
|
||||
} else if (tap_state_transition(tap_get_state(), true) == cmd->path[state_count]) {
|
||||
tms = 1;
|
||||
} else {
|
||||
LOG_ERROR("BUG: %s -> %s isn't a valid TAP transition",
|
||||
tap_state_name(tap_get_state()),
|
||||
tap_state_name(cmd->path[state_count]));
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
ft232r_write(0, tms, 0);
|
||||
ft232r_write(1, tms, 0);
|
||||
|
||||
tap_set_state(cmd->path[state_count]);
|
||||
state_count++;
|
||||
num_states--;
|
||||
}
|
||||
|
||||
ft232r_write(0, tms, 0);
|
||||
|
||||
tap_set_end_state(tap_get_state());
|
||||
}
|
||||
|
||||
static void syncbb_runtest(int num_cycles)
|
||||
{
|
||||
int i;
|
||||
|
||||
tap_state_t saved_end_state = tap_get_end_state();
|
||||
|
||||
/* only do a state_move when we're not already in IDLE */
|
||||
if (tap_get_state() != TAP_IDLE) {
|
||||
syncbb_end_state(TAP_IDLE);
|
||||
syncbb_state_move(0);
|
||||
}
|
||||
|
||||
/* execute num_cycles */
|
||||
for (i = 0; i < num_cycles; i++) {
|
||||
ft232r_write(0, 0, 0);
|
||||
ft232r_write(1, 0, 0);
|
||||
}
|
||||
ft232r_write(0, 0, 0);
|
||||
|
||||
/* finish in end_state */
|
||||
syncbb_end_state(saved_end_state);
|
||||
if (tap_get_state() != tap_get_end_state())
|
||||
syncbb_state_move(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function syncbb_stableclocks
|
||||
* issues a number of clock cycles while staying in a stable state.
|
||||
* Because the TMS value required to stay in the RESET state is a 1, whereas
|
||||
* the TMS value required to stay in any of the other stable states is a 0,
|
||||
* this function checks the current stable state to decide on the value of TMS
|
||||
* to use.
|
||||
*/
|
||||
static void syncbb_stableclocks(int num_cycles)
|
||||
{
|
||||
int tms = (tap_get_state() == TAP_RESET ? 1 : 0);
|
||||
int i;
|
||||
|
||||
/* send num_cycles clocks onto the cable */
|
||||
for (i = 0; i < num_cycles; i++) {
|
||||
ft232r_write(1, tms, 0);
|
||||
ft232r_write(0, tms, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void syncbb_scan(bool ir_scan, enum scan_type type, uint8_t *buffer, int scan_size)
|
||||
{
|
||||
tap_state_t saved_end_state = tap_get_end_state();
|
||||
int bit_cnt, bit0_index;
|
||||
|
||||
if (!((!ir_scan && (tap_get_state() == TAP_DRSHIFT)) || (ir_scan && (tap_get_state() == TAP_IRSHIFT)))) {
|
||||
if (ir_scan)
|
||||
syncbb_end_state(TAP_IRSHIFT);
|
||||
else
|
||||
syncbb_end_state(TAP_DRSHIFT);
|
||||
|
||||
syncbb_state_move(0);
|
||||
syncbb_end_state(saved_end_state);
|
||||
}
|
||||
|
||||
bit0_index = ft232r_output_len;
|
||||
for (bit_cnt = 0; bit_cnt < scan_size; bit_cnt++) {
|
||||
int tms = (bit_cnt == scan_size-1) ? 1 : 0;
|
||||
int tdi;
|
||||
int bytec = bit_cnt/8;
|
||||
int bcval = 1 << (bit_cnt % 8);
|
||||
|
||||
/* if we're just reading the scan, but don't care about the output
|
||||
* default to outputting 'low', this also makes valgrind traces more readable,
|
||||
* as it removes the dependency on an uninitialised value
|
||||
*/
|
||||
tdi = 0;
|
||||
if ((type != SCAN_IN) && (buffer[bytec] & bcval))
|
||||
tdi = 1;
|
||||
|
||||
ft232r_write(0, tms, tdi);
|
||||
ft232r_write(1, tms, tdi);
|
||||
}
|
||||
|
||||
if (tap_get_state() != tap_get_end_state()) {
|
||||
/* we *KNOW* the above loop transitioned out of
|
||||
* the shift state, so we skip the first state
|
||||
* and move directly to the end state.
|
||||
*/
|
||||
syncbb_state_move(1);
|
||||
}
|
||||
ft232r_send_recv();
|
||||
|
||||
if (type != SCAN_OUT)
|
||||
for (bit_cnt = 0; bit_cnt < scan_size; bit_cnt++) {
|
||||
int bytec = bit_cnt/8;
|
||||
int bcval = 1 << (bit_cnt % 8);
|
||||
int val = ft232r_output[bit0_index + bit_cnt*2 + 1];
|
||||
|
||||
if (val & READ_TDO)
|
||||
buffer[bytec] |= bcval;
|
||||
else
|
||||
buffer[bytec] &= ~bcval;
|
||||
}
|
||||
}
|
||||
|
||||
static int syncbb_execute_queue(void)
|
||||
{
|
||||
struct jtag_command *cmd = jtag_command_queue; /* currently processed command */
|
||||
int scan_size;
|
||||
enum scan_type type;
|
||||
uint8_t *buffer;
|
||||
int retval;
|
||||
|
||||
/* return ERROR_OK, unless a jtag_read_buffer returns a failed check
|
||||
* that wasn't handled by a caller-provided error handler
|
||||
*/
|
||||
retval = ERROR_OK;
|
||||
|
||||
/* ft232r_blink(1);*/
|
||||
|
||||
while (cmd) {
|
||||
switch (cmd->type) {
|
||||
case JTAG_RESET:
|
||||
LOG_DEBUG_IO("reset trst: %i srst %i", cmd->cmd.reset->trst, cmd->cmd.reset->srst);
|
||||
|
||||
if ((cmd->cmd.reset->trst == 1) ||
|
||||
(cmd->cmd.reset->srst &&
|
||||
(jtag_get_reset_config() & RESET_SRST_PULLS_TRST))) {
|
||||
tap_set_state(TAP_RESET);
|
||||
}
|
||||
ft232r_reset(cmd->cmd.reset->trst, cmd->cmd.reset->srst);
|
||||
break;
|
||||
|
||||
case JTAG_RUNTEST:
|
||||
LOG_DEBUG_IO("runtest %i cycles, end in %s", cmd->cmd.runtest->num_cycles,
|
||||
tap_state_name(cmd->cmd.runtest->end_state));
|
||||
|
||||
syncbb_end_state(cmd->cmd.runtest->end_state);
|
||||
syncbb_runtest(cmd->cmd.runtest->num_cycles);
|
||||
break;
|
||||
|
||||
case JTAG_STABLECLOCKS:
|
||||
/* this is only allowed while in a stable state. A check for a stable
|
||||
* state was done in jtag_add_clocks()
|
||||
*/
|
||||
syncbb_stableclocks(cmd->cmd.stableclocks->num_cycles);
|
||||
break;
|
||||
|
||||
case JTAG_TLR_RESET: /* renamed from JTAG_STATEMOVE */
|
||||
LOG_DEBUG_IO("statemove end in %s", tap_state_name(cmd->cmd.statemove->end_state));
|
||||
|
||||
syncbb_end_state(cmd->cmd.statemove->end_state);
|
||||
syncbb_state_move(0);
|
||||
break;
|
||||
|
||||
case JTAG_PATHMOVE:
|
||||
LOG_DEBUG_IO("pathmove: %i states, end in %s", cmd->cmd.pathmove->num_states,
|
||||
tap_state_name(cmd->cmd.pathmove->path[cmd->cmd.pathmove->num_states - 1]));
|
||||
|
||||
syncbb_path_move(cmd->cmd.pathmove);
|
||||
break;
|
||||
|
||||
case JTAG_SCAN:
|
||||
LOG_DEBUG_IO("%s scan end in %s", (cmd->cmd.scan->ir_scan) ? "IR" : "DR",
|
||||
tap_state_name(cmd->cmd.scan->end_state));
|
||||
|
||||
syncbb_end_state(cmd->cmd.scan->end_state);
|
||||
scan_size = jtag_build_buffer(cmd->cmd.scan, &buffer);
|
||||
type = jtag_scan_type(cmd->cmd.scan);
|
||||
syncbb_scan(cmd->cmd.scan->ir_scan, type, buffer, scan_size);
|
||||
if (jtag_read_buffer(buffer, cmd->cmd.scan) != ERROR_OK)
|
||||
retval = ERROR_JTAG_QUEUE_FAILED;
|
||||
if (buffer)
|
||||
free(buffer);
|
||||
break;
|
||||
|
||||
case JTAG_SLEEP:
|
||||
LOG_DEBUG_IO("sleep %" PRIi32, cmd->cmd.sleep->us);
|
||||
|
||||
jtag_sleep(cmd->cmd.sleep->us);
|
||||
break;
|
||||
|
||||
case JTAG_TMS:
|
||||
retval = syncbb_execute_tms(cmd);
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("BUG: unknown JTAG command type encountered");
|
||||
exit(-1);
|
||||
}
|
||||
if (ft232r_output_len > 0)
|
||||
ft232r_send_recv();
|
||||
cmd = cmd->next;
|
||||
}
|
||||
/* ft232r_blink(0);*/
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
struct jtag_interface ft232r_interface = {
|
||||
.name = "ft232r",
|
||||
.commands = ft232r_command_handlers,
|
||||
.transports = jtag_only,
|
||||
.supported = DEBUG_CAP_TMS_SEQ,
|
||||
|
||||
.execute_queue = syncbb_execute_queue,
|
||||
|
||||
.speed = ft232r_speed,
|
||||
.init = ft232r_init,
|
||||
.quit = ft232r_quit,
|
||||
.speed_div = ft232r_speed_div,
|
||||
.khz = ft232r_khz,
|
||||
};
|
||||
@@ -694,6 +694,18 @@ static int ftdi_quit(void)
|
||||
{
|
||||
mpsse_close(mpsse_ctx);
|
||||
|
||||
struct signal *sig = signals;
|
||||
while (sig) {
|
||||
struct signal *next = sig->next;
|
||||
free((void *)sig->name);
|
||||
free(sig);
|
||||
sig = next;
|
||||
}
|
||||
|
||||
free(ftdi_device_desc);
|
||||
free(ftdi_serial);
|
||||
free(ftdi_location);
|
||||
|
||||
free(swd_cmd_queue);
|
||||
|
||||
return ERROR_OK;
|
||||
|
||||
@@ -1263,7 +1263,7 @@ static bool check_trace_freq(struct jaylink_swo_speed speed,
|
||||
return false;
|
||||
}
|
||||
|
||||
static int config_trace(bool enabled, enum tpio_pin_protocol pin_protocol,
|
||||
static int config_trace(bool enabled, enum tpiu_pin_protocol pin_protocol,
|
||||
uint32_t port_size, unsigned int *trace_freq)
|
||||
{
|
||||
int ret;
|
||||
@@ -1275,7 +1275,7 @@ static int config_trace(bool enabled, enum tpio_pin_protocol pin_protocol,
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (pin_protocol != ASYNC_UART) {
|
||||
if (pin_protocol != TPIU_PIN_PROTOCOL_ASYNC_UART) {
|
||||
LOG_ERROR("Selected pin protocol is not supported.");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
@@ -204,23 +204,20 @@ static int jtag_vpi_queue_tdi_xfer(uint8_t *bits, int nb_bits, int tap_shift)
|
||||
static int jtag_vpi_queue_tdi(uint8_t *bits, int nb_bits, int tap_shift)
|
||||
{
|
||||
int nb_xfer = DIV_ROUND_UP(nb_bits, XFERT_MAX_SIZE * 8);
|
||||
uint8_t *xmit_buffer = bits;
|
||||
int xmit_nb_bits = nb_bits;
|
||||
int i = 0;
|
||||
int retval;
|
||||
|
||||
while (nb_xfer) {
|
||||
|
||||
if (nb_xfer == 1) {
|
||||
retval = jtag_vpi_queue_tdi_xfer(&xmit_buffer[i], xmit_nb_bits, tap_shift);
|
||||
retval = jtag_vpi_queue_tdi_xfer(bits, nb_bits, tap_shift);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
} else {
|
||||
retval = jtag_vpi_queue_tdi_xfer(&xmit_buffer[i], XFERT_MAX_SIZE * 8, NO_TAP_SHIFT);
|
||||
retval = jtag_vpi_queue_tdi_xfer(bits, XFERT_MAX_SIZE * 8, NO_TAP_SHIFT);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
xmit_nb_bits -= XFERT_MAX_SIZE * 8;
|
||||
i += XFERT_MAX_SIZE;
|
||||
nb_bits -= XFERT_MAX_SIZE * 8;
|
||||
if (bits)
|
||||
bits += XFERT_MAX_SIZE;
|
||||
}
|
||||
|
||||
nb_xfer--;
|
||||
@@ -318,7 +315,7 @@ static int jtag_vpi_runtest(int cycles, tap_state_t state)
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
retval = jtag_vpi_queue_tdi(NULL, cycles, NO_TAP_SHIFT);
|
||||
retval = jtag_vpi_queue_tdi(NULL, cycles, TAP_SHIFT);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
|
||||
@@ -741,12 +741,22 @@ static int kitprog_swd_run_queue(void)
|
||||
break;
|
||||
}
|
||||
|
||||
/* We use the maximum buffer size here because the KitProg sometimes
|
||||
* doesn't like bulk reads of fewer than 62 bytes. (?!?!)
|
||||
/* KitProg firmware does not send a zero length packet
|
||||
* after the bulk-in transmission of a length divisible by bulk packet
|
||||
* size (64 bytes) as required by the USB specification.
|
||||
* Therefore libusb would wait for continuation of transmission.
|
||||
* Workaround: Limit bulk read size to expected number of bytes
|
||||
* for problematic tranfer sizes. Otherwise use the maximum buffer
|
||||
* size here because the KitProg sometimes doesn't like bulk reads
|
||||
* of fewer than 62 bytes. (?!?!)
|
||||
*/
|
||||
size_t read_count_workaround = SWD_MAX_BUFFER_LENGTH;
|
||||
if (read_count % 64 == 0)
|
||||
read_count_workaround = read_count;
|
||||
|
||||
ret = jtag_libusb_bulk_read(kitprog_handle->usb_handle,
|
||||
BULK_EP_IN | LIBUSB_ENDPOINT_IN, (char *)buffer,
|
||||
SWD_MAX_BUFFER_LENGTH, 0);
|
||||
read_count_workaround, 1000);
|
||||
if (ret > 0) {
|
||||
/* Handle garbage data by offsetting the initial read index */
|
||||
if ((unsigned int)ret > read_count)
|
||||
@@ -878,13 +888,11 @@ COMMAND_HANDLER(kitprog_handle_acquire_psoc_command)
|
||||
COMMAND_HANDLER(kitprog_handle_serial_command)
|
||||
{
|
||||
if (CMD_ARGC == 1) {
|
||||
size_t len = strlen(CMD_ARGV[0]);
|
||||
kitprog_serial = calloc(len + 1, sizeof(char));
|
||||
kitprog_serial = strdup(CMD_ARGV[0]);
|
||||
if (kitprog_serial == NULL) {
|
||||
LOG_ERROR("Failed to allocate memory for the serial number");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
strncpy(kitprog_serial, CMD_ARGV[0], len + 1);
|
||||
} else {
|
||||
LOG_ERROR("expected exactly one argument to kitprog_serial <serial-number>");
|
||||
return ERROR_FAIL;
|
||||
|
||||
@@ -335,7 +335,13 @@ struct mpsse_ctx *mpsse_open(const uint16_t *vid, const uint16_t *pid, const cha
|
||||
ctx->write_size = 16384;
|
||||
ctx->read_chunk = malloc(ctx->read_chunk_size);
|
||||
ctx->read_buffer = malloc(ctx->read_size);
|
||||
ctx->write_buffer = malloc(ctx->write_size);
|
||||
|
||||
/* Use calloc to make valgrind happy: buffer_write() sets payload
|
||||
* on bit basis, so some bits can be left uninitialized in write_buffer.
|
||||
* Although this is perfectly ok with MPSSE, valgrind reports
|
||||
* Syscall param ioctl(USBDEVFS_SUBMITURB).buffer points to uninitialised byte(s) */
|
||||
ctx->write_buffer = calloc(1, ctx->write_size);
|
||||
|
||||
if (!ctx->read_chunk || !ctx->read_buffer || !ctx->write_buffer)
|
||||
goto error;
|
||||
|
||||
|
||||
@@ -457,8 +457,8 @@ static int stlink_usb_error_check(void *handle)
|
||||
LOG_DEBUG("Write error");
|
||||
return ERROR_FAIL;
|
||||
case STLINK_JTAG_WRITE_VERIF_ERROR:
|
||||
LOG_DEBUG("Verify error");
|
||||
return ERROR_FAIL;
|
||||
LOG_DEBUG("Write verify error, ignoring");
|
||||
return ERROR_OK;
|
||||
case STLINK_SWD_AP_FAULT:
|
||||
/* git://git.ac6.fr/openocd commit 657e3e885b9ee10
|
||||
* returns ERROR_OK with the comment:
|
||||
@@ -2194,12 +2194,13 @@ error_open:
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
int stlink_config_trace(void *handle, bool enabled, enum tpio_pin_protocol pin_protocol,
|
||||
int stlink_config_trace(void *handle, bool enabled, enum tpiu_pin_protocol pin_protocol,
|
||||
uint32_t port_size, unsigned int *trace_freq)
|
||||
{
|
||||
struct stlink_usb_handle_s *h = handle;
|
||||
|
||||
if (enabled && (h->jtag_api < 2 || pin_protocol != ASYNC_UART)) {
|
||||
if (enabled && (h->jtag_api < 2 ||
|
||||
pin_protocol != TPIU_PIN_PROTOCOL_ASYNC_UART)) {
|
||||
LOG_ERROR("The attached ST-LINK version doesn't support this trace mode");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
@@ -58,11 +58,11 @@
|
||||
/*
|
||||
* Helper func to determine if gpio number valid
|
||||
*
|
||||
* Assume here that there will be less than 1000 gpios on a system
|
||||
* Assume here that there will be less than 10000 gpios on a system
|
||||
*/
|
||||
static int is_gpio_valid(int gpio)
|
||||
{
|
||||
return gpio >= 0 && gpio < 1000;
|
||||
return gpio >= 0 && gpio < 10000;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -89,7 +89,7 @@ static int open_write_close(const char *name, const char *valstr)
|
||||
*/
|
||||
static void unexport_sysfs_gpio(int gpio)
|
||||
{
|
||||
char gpiostr[4];
|
||||
char gpiostr[5];
|
||||
|
||||
if (!is_gpio_valid(gpio))
|
||||
return;
|
||||
@@ -113,7 +113,7 @@ static void unexport_sysfs_gpio(int gpio)
|
||||
static int setup_sysfs_gpio(int gpio, int is_output, int init_high)
|
||||
{
|
||||
char buf[40];
|
||||
char gpiostr[4];
|
||||
char gpiostr[5];
|
||||
int ret;
|
||||
|
||||
if (!is_gpio_valid(gpio))
|
||||
|
||||
@@ -445,6 +445,7 @@ static void ublast_queue_bytes(uint8_t *bytes, int nb_bytes)
|
||||
* ublast_tms_seq - write a TMS sequence transition to JTAG
|
||||
* @bits: TMS bits to be written (bit0, bit1 .. bitN)
|
||||
* @nb_bits: number of TMS bits (between 1 and 8)
|
||||
* @skip: number of TMS bits to skip at the beginning of the series
|
||||
*
|
||||
* Write a serie of TMS transitions, where each transition consists in :
|
||||
* - writing out TCK=0, TMS=<new_state>, TDI=<???>
|
||||
@@ -452,12 +453,12 @@ static void ublast_queue_bytes(uint8_t *bytes, int nb_bytes)
|
||||
* The function ensures that at the end of the sequence, the clock (TCK) is put
|
||||
* low.
|
||||
*/
|
||||
static void ublast_tms_seq(const uint8_t *bits, int nb_bits)
|
||||
static void ublast_tms_seq(const uint8_t *bits, int nb_bits, int skip)
|
||||
{
|
||||
int i;
|
||||
|
||||
DEBUG_JTAG_IO("(bits=%02x..., nb_bits=%d)", bits[0], nb_bits);
|
||||
for (i = 0; i < nb_bits; i++)
|
||||
for (i = skip; i < nb_bits; i++)
|
||||
ublast_clock_tms((bits[i / 8] >> (i % 8)) & 0x01);
|
||||
ublast_idle_clock();
|
||||
}
|
||||
@@ -469,7 +470,7 @@ static void ublast_tms_seq(const uint8_t *bits, int nb_bits)
|
||||
static void ublast_tms(struct tms_command *cmd)
|
||||
{
|
||||
DEBUG_JTAG_IO("(num_bits=%d)", cmd->num_bits);
|
||||
ublast_tms_seq(cmd->bits, cmd->num_bits);
|
||||
ublast_tms_seq(cmd->bits, cmd->num_bits, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -501,11 +502,12 @@ static void ublast_path_move(struct pathmove_command *cmd)
|
||||
/**
|
||||
* ublast_state_move - move JTAG state to the target state
|
||||
* @state: the target state
|
||||
* @skip: number of bits to skip at the beginning of the path
|
||||
*
|
||||
* Input the correct TMS sequence to the JTAG TAP so that we end up in the
|
||||
* target state. This assumes the current state (tap_get_state()) is correct.
|
||||
*/
|
||||
static void ublast_state_move(tap_state_t state)
|
||||
static void ublast_state_move(tap_state_t state, int skip)
|
||||
{
|
||||
uint8_t tms_scan;
|
||||
int tms_len;
|
||||
@@ -516,7 +518,7 @@ static void ublast_state_move(tap_state_t state)
|
||||
return;
|
||||
tms_scan = tap_get_tms_path(tap_get_state(), state);
|
||||
tms_len = tap_get_tms_path_len(tap_get_state(), state);
|
||||
ublast_tms_seq(&tms_scan, tms_len);
|
||||
ublast_tms_seq(&tms_scan, tms_len, skip);
|
||||
tap_set_state(state);
|
||||
}
|
||||
|
||||
@@ -688,9 +690,9 @@ static void ublast_runtest(int cycles, tap_state_t state)
|
||||
{
|
||||
DEBUG_JTAG_IO("%s(cycles=%i, end_state=%d)", __func__, cycles, state);
|
||||
|
||||
ublast_state_move(TAP_IDLE);
|
||||
ublast_state_move(TAP_IDLE, 0);
|
||||
ublast_queue_tdi(NULL, cycles, SCAN_OUT);
|
||||
ublast_state_move(state);
|
||||
ublast_state_move(state, 0);
|
||||
}
|
||||
|
||||
static void ublast_stableclocks(int cycles)
|
||||
@@ -720,9 +722,9 @@ static int ublast_scan(struct scan_command *cmd)
|
||||
scan_bits = jtag_build_buffer(cmd, &buf);
|
||||
|
||||
if (cmd->ir_scan)
|
||||
ublast_state_move(TAP_IRSHIFT);
|
||||
ublast_state_move(TAP_IRSHIFT, 0);
|
||||
else
|
||||
ublast_state_move(TAP_DRSHIFT);
|
||||
ublast_state_move(TAP_DRSHIFT, 0);
|
||||
|
||||
log_buf = hexdump(buf, DIV_ROUND_UP(scan_bits, 8));
|
||||
DEBUG_JTAG_IO("%s(scan=%s, type=%s, bits=%d, buf=[%s], end_state=%d)", __func__,
|
||||
@@ -733,20 +735,15 @@ static int ublast_scan(struct scan_command *cmd)
|
||||
|
||||
ublast_queue_tdi(buf, scan_bits, type);
|
||||
|
||||
/*
|
||||
* As our JTAG is in an unstable state (IREXIT1 or DREXIT1), move it
|
||||
* forward to a stable IRPAUSE or DRPAUSE.
|
||||
*/
|
||||
ublast_clock_tms(0);
|
||||
if (cmd->ir_scan)
|
||||
tap_set_state(TAP_IRPAUSE);
|
||||
else
|
||||
tap_set_state(TAP_DRPAUSE);
|
||||
|
||||
ret = jtag_read_buffer(buf, cmd);
|
||||
if (buf)
|
||||
free(buf);
|
||||
ublast_state_move(cmd->end_state);
|
||||
/*
|
||||
* ublast_queue_tdi sends the last bit with TMS=1. We are therefore
|
||||
* already in Exit1-DR/IR and have to skip the first step on our way
|
||||
* to end_state.
|
||||
*/
|
||||
ublast_state_move(cmd->end_state, 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -776,7 +773,7 @@ static void ublast_initial_wipeout(void)
|
||||
/*
|
||||
* Put JTAG in RESET state (five 1 on TMS)
|
||||
*/
|
||||
ublast_tms_seq(&tms_reset, 5);
|
||||
ublast_tms_seq(&tms_reset, 5, 0);
|
||||
tap_set_state(TAP_RESET);
|
||||
}
|
||||
|
||||
@@ -805,7 +802,7 @@ static int ublast_execute_queue(void)
|
||||
ublast_stableclocks(cmd->cmd.stableclocks->num_cycles);
|
||||
break;
|
||||
case JTAG_TLR_RESET:
|
||||
ublast_state_move(cmd->cmd.statemove->end_state);
|
||||
ublast_state_move(cmd->cmd.statemove->end_state, 0);
|
||||
break;
|
||||
case JTAG_PATHMOVE:
|
||||
ublast_path_move(cmd->cmd.pathmove);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -119,6 +119,11 @@ static int hl_interface_quit(void)
|
||||
if (hl_if.layout->api->close)
|
||||
hl_if.layout->api->close(hl_if.handle);
|
||||
|
||||
jtag_command_queue_reset();
|
||||
|
||||
free((void *)hl_if.param.device_desc);
|
||||
free((void *)hl_if.param.serial);
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
@@ -186,7 +191,7 @@ int hl_interface_override_target(const char **targetname)
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
int hl_interface_config_trace(bool enabled, enum tpio_pin_protocol pin_protocol,
|
||||
int hl_interface_config_trace(bool enabled, enum tpiu_pin_protocol pin_protocol,
|
||||
uint32_t port_size, unsigned int *trace_freq)
|
||||
{
|
||||
if (hl_if.layout->api->config_trace)
|
||||
|
||||
@@ -91,7 +91,7 @@ struct hl_layout_api_s {
|
||||
* its maximum supported rate there
|
||||
* @returns ERROR_OK on success, an error code on failure.
|
||||
*/
|
||||
int (*config_trace)(void *handle, bool enabled, enum tpio_pin_protocol pin_protocol,
|
||||
int (*config_trace)(void *handle, bool enabled, enum tpiu_pin_protocol pin_protocol,
|
||||
uint32_t port_size, unsigned int *trace_freq);
|
||||
/**
|
||||
* Poll for new trace data
|
||||
|
||||
+5
-10
@@ -39,20 +39,15 @@ static int jim_newtap_expected_id(Jim_Nvp *n, Jim_GetOptInfo *goi,
|
||||
return e;
|
||||
}
|
||||
|
||||
unsigned expected_len = sizeof(uint32_t) * pTap->expected_ids_cnt;
|
||||
uint32_t *new_expected_ids = malloc(expected_len + sizeof(uint32_t));
|
||||
if (new_expected_ids == NULL) {
|
||||
uint32_t *p = realloc(pTap->expected_ids,
|
||||
(pTap->expected_ids_cnt + 1) * sizeof(uint32_t));
|
||||
if (!p) {
|
||||
Jim_SetResultFormatted(goi->interp, "no memory");
|
||||
return JIM_ERR;
|
||||
}
|
||||
|
||||
memcpy(new_expected_ids, pTap->expected_ids, expected_len);
|
||||
|
||||
new_expected_ids[pTap->expected_ids_cnt] = w;
|
||||
|
||||
free(pTap->expected_ids);
|
||||
pTap->expected_ids = new_expected_ids;
|
||||
pTap->expected_ids_cnt++;
|
||||
pTap->expected_ids = p;
|
||||
pTap->expected_ids[pTap->expected_ids_cnt++] = w;
|
||||
|
||||
return JIM_OK;
|
||||
}
|
||||
|
||||
@@ -233,3 +233,11 @@ static void hl_constructor(void)
|
||||
transport_register(&hl_jtag_transport);
|
||||
transport_register(&stlink_swim_transport);
|
||||
}
|
||||
|
||||
bool transport_is_hla(void)
|
||||
{
|
||||
struct transport *t;
|
||||
t = get_current_transport();
|
||||
return t == &hl_swd_transport || t == &hl_jtag_transport
|
||||
|| t == &stlink_swim_transport;
|
||||
}
|
||||
|
||||
@@ -309,7 +309,7 @@ struct jtag_interface {
|
||||
* its maximum supported rate there
|
||||
* @returns ERROR_OK on success, an error code on failure.
|
||||
*/
|
||||
int (*config_trace)(bool enabled, enum tpio_pin_protocol pin_protocol,
|
||||
int (*config_trace)(bool enabled, enum tpiu_pin_protocol pin_protocol,
|
||||
uint32_t port_size, unsigned int *trace_freq);
|
||||
|
||||
/**
|
||||
@@ -328,7 +328,7 @@ extern const char * const jtag_only[];
|
||||
|
||||
void adapter_assert_reset(void);
|
||||
void adapter_deassert_reset(void);
|
||||
int adapter_config_trace(bool enabled, enum tpio_pin_protocol pin_protocol,
|
||||
int adapter_config_trace(bool enabled, enum tpiu_pin_protocol pin_protocol,
|
||||
uint32_t port_size, unsigned int *trace_freq);
|
||||
int adapter_poll_trace(uint8_t *buf, size_t *size);
|
||||
|
||||
|
||||
@@ -60,6 +60,9 @@ extern struct jtag_interface usb_blaster_interface;
|
||||
#if BUILD_JTAG_VPI == 1
|
||||
extern struct jtag_interface jtag_vpi_interface;
|
||||
#endif
|
||||
#if BUILD_FT232R == 1
|
||||
extern struct jtag_interface ft232r_interface;
|
||||
#endif
|
||||
#if BUILD_AMTJTAGACCEL == 1
|
||||
extern struct jtag_interface amt_jtagaccel_interface;
|
||||
#endif
|
||||
@@ -129,6 +132,9 @@ extern struct jtag_interface kitprog_interface;
|
||||
#if BUILD_IMX_GPIO == 1
|
||||
extern struct jtag_interface imx_gpio_interface;
|
||||
#endif
|
||||
#if BUILD_XDS110 == 1
|
||||
extern struct jtag_interface xds110_interface;
|
||||
#endif
|
||||
#endif /* standard drivers */
|
||||
|
||||
/**
|
||||
@@ -159,6 +165,9 @@ struct jtag_interface *jtag_interfaces[] = {
|
||||
#if BUILD_JTAG_VPI == 1
|
||||
&jtag_vpi_interface,
|
||||
#endif
|
||||
#if BUILD_FT232R == 1
|
||||
&ft232r_interface,
|
||||
#endif
|
||||
#if BUILD_AMTJTAGACCEL == 1
|
||||
&amt_jtagaccel_interface,
|
||||
#endif
|
||||
@@ -228,6 +237,9 @@ struct jtag_interface *jtag_interfaces[] = {
|
||||
#if BUILD_IMX_GPIO == 1
|
||||
&imx_gpio_interface,
|
||||
#endif
|
||||
#if BUILD_XDS110 == 1
|
||||
&xds110_interface,
|
||||
#endif
|
||||
#endif /* standard drivers */
|
||||
NULL,
|
||||
};
|
||||
|
||||
@@ -153,8 +153,6 @@ struct jtag_tap {
|
||||
struct jtag_tap_event_action *event_action;
|
||||
|
||||
struct jtag_tap *next_tap;
|
||||
/* dap instance if some null if no instance , initialized to 0 by calloc*/
|
||||
struct adiv5_dap *dap;
|
||||
/* private pointer to support none-jtag specific functions */
|
||||
void *priv;
|
||||
};
|
||||
@@ -642,8 +640,6 @@ void jtag_poll_set_enabled(bool value);
|
||||
* level APIs that are used in inner loops. */
|
||||
#include <jtag/minidriver.h>
|
||||
|
||||
bool transport_is_jtag(void);
|
||||
|
||||
int jim_jtag_newtap(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
|
||||
|
||||
#endif /* OPENOCD_JTAG_JTAG_H */
|
||||
|
||||
@@ -211,6 +211,4 @@ struct swd_driver {
|
||||
int swd_init_reset(struct command_context *cmd_ctx);
|
||||
void swd_add_reset(int req_srst);
|
||||
|
||||
bool transport_is_swd(void);
|
||||
|
||||
#endif /* OPENOCD_JTAG_SWD_H */
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user