flash/nor: add lpc4300 variant to lpc2000 driver
This patch adds flash programming support for internal flash of the LPC43x2/3/5/7 part, tested on a LPC4337 (also tested on a LPC1768 and LPC2468). It should also work with LPC1800's with onchip flash. The "base" parameter of the "flash bank" command is now significant for the lpc4300 variant and required to determine the bank number parameter needed by the IAP routines. NOTE: I could only program flash successfully when the chip is powered with "P2_7" pulled low to put it in ISP mode. When running from flash (and not the ISP ROM), the target fails to halt and the sector erase fails. This is similar to the behavior I remember when trying out the spifi driver on a LPC4350... lots of power cycles to make progress, one To burn, one to run. So I am not confident my config is set up correctly. Change-Id: I8a75ef1b95cedd5b5898b2dedff477f502fd19f3 Signed-off-by: Matt Dittrich <mdittrich.dev@gmail.com> Reviewed-on: http://openocd.zylin.com/1126 Reviewed-by: Freddie Chopin <freddie.chopin@gmail.com> Tested-by: jenkins__archive__
parent
906d6aaa19
commit
ad1c9cdbcb
|
@ -58,12 +58,17 @@
|
||||||
* lpc1700:
|
* lpc1700:
|
||||||
* - 175x
|
* - 175x
|
||||||
* - 176x (tested with LPC1768)
|
* - 176x (tested with LPC1768)
|
||||||
|
*
|
||||||
|
* lpc4300:
|
||||||
|
* - 43x2 | 3 | 5 | 7 (tested with 4337)
|
||||||
|
* - 18x2 | 3 | 5 | 7
|
||||||
*/
|
*/
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
lpc2000_v1,
|
lpc2000_v1,
|
||||||
lpc2000_v2,
|
lpc2000_v2,
|
||||||
lpc1700
|
lpc1700,
|
||||||
|
lpc4300,
|
||||||
} lpc2000_variant;
|
} lpc2000_variant;
|
||||||
|
|
||||||
struct lpc2000_flash_bank {
|
struct lpc2000_flash_bank {
|
||||||
|
@ -75,6 +80,9 @@ struct lpc2000_flash_bank {
|
||||||
int calc_checksum;
|
int calc_checksum;
|
||||||
uint32_t cmd51_max_buffer;
|
uint32_t cmd51_max_buffer;
|
||||||
int checksum_vector;
|
int checksum_vector;
|
||||||
|
uint32_t iap_max_stack;
|
||||||
|
uint32_t cmd51_src_offset;
|
||||||
|
uint32_t lpc4300_bank;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum lpc2000_status_codes {
|
enum lpc2000_status_codes {
|
||||||
|
@ -97,7 +105,10 @@ enum lpc2000_status_codes {
|
||||||
LPC2000_INVALID_CODE = 16,
|
LPC2000_INVALID_CODE = 16,
|
||||||
LPC2000_INVALID_BAUD_RATE = 17,
|
LPC2000_INVALID_BAUD_RATE = 17,
|
||||||
LPC2000_INVALID_STOP_BIT = 18,
|
LPC2000_INVALID_STOP_BIT = 18,
|
||||||
LPC2000_CRP_ENABLED = 19
|
LPC2000_CRP_ENABLED = 19,
|
||||||
|
LPC2000_INVALID_FLASH_UNIT = 20,
|
||||||
|
LPC2000_USER_CODE_CHECKSUM = 21,
|
||||||
|
LCP2000_ERROR_SETTING_ACTIVE_PARTITION = 22,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int lpc2000_build_sector_list(struct flash_bank *bank)
|
static int lpc2000_build_sector_list(struct flash_bank *bank)
|
||||||
|
@ -243,6 +254,32 @@ static int lpc2000_build_sector_list(struct flash_bank *bank)
|
||||||
bank->sectors[i].is_erased = -1;
|
bank->sectors[i].is_erased = -1;
|
||||||
bank->sectors[i].is_protected = 1;
|
bank->sectors[i].is_protected = 1;
|
||||||
}
|
}
|
||||||
|
} else if (lpc2000_info->variant == lpc4300) {
|
||||||
|
switch (bank->size) {
|
||||||
|
case 256 * 1024:
|
||||||
|
bank->num_sectors = 11;
|
||||||
|
break;
|
||||||
|
case 384 * 1024:
|
||||||
|
bank->num_sectors = 13;
|
||||||
|
break;
|
||||||
|
case 512 * 1024:
|
||||||
|
bank->num_sectors = 15;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG_ERROR("BUG: unknown bank->size encountered");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors);
|
||||||
|
|
||||||
|
for (int i = 0; i < bank->num_sectors; i++) {
|
||||||
|
bank->sectors[i].offset = offset;
|
||||||
|
/* sectors 0-7 are 8kB-sized, 8 and above are 64kB-sized for LPC43xx devices */
|
||||||
|
bank->sectors[i].size = (i < 8) ? 8 * 1024 : 64 * 1024;
|
||||||
|
offset += bank->sectors[i].size;
|
||||||
|
bank->sectors[i].is_erased = -1;
|
||||||
|
bank->sectors[i].is_protected = 1;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
LOG_ERROR("BUG: unknown lpc2000_info->variant encountered");
|
LOG_ERROR("BUG: unknown lpc2000_info->variant encountered");
|
||||||
exit(-1);
|
exit(-1);
|
||||||
|
@ -252,28 +289,29 @@ static int lpc2000_build_sector_list(struct flash_bank *bank)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* this function allocates and initializes working area used for IAP algorithm
|
/* this function allocates and initializes working area used for IAP algorithm
|
||||||
* uses 180 bytes working area
|
* uses 52 + max IAP stack bytes working area
|
||||||
* 0x0 to 0x7: jump gate (BX to thumb state, b -2 to wait)
|
* 0x0 to 0x7: jump gate (BX to thumb state, b -2 to wait)
|
||||||
* 0x8 to 0x1f: command parameter table (1+5 words)
|
* 0x8 to 0x1f: command parameter table (1+5 words)
|
||||||
* 0x20 to 0x33: command result table (1+4 words)
|
* 0x20 to 0x33: command result table (1+4 words)
|
||||||
* 0x34 to 0xb3: stack (only 128b needed)
|
* 0x34 to 0xb3|0x104: stack (only 128b needed for lpc17xx/2000, 208 for lpc43xx)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int lpc2000_iap_working_area_init(struct flash_bank *bank, struct working_area **iap_working_area)
|
static int lpc2000_iap_working_area_init(struct flash_bank *bank, struct working_area **iap_working_area)
|
||||||
{
|
{
|
||||||
struct target *target = bank->target;
|
struct target *target = bank->target;
|
||||||
|
struct lpc2000_flash_bank *lpc2000_info = bank->driver_priv;
|
||||||
|
|
||||||
if (target_alloc_working_area(target, 180, iap_working_area) != ERROR_OK) {
|
if (target_alloc_working_area(target, 0x34 + lpc2000_info->iap_max_stack, iap_working_area) != ERROR_OK) {
|
||||||
LOG_ERROR("no working area specified, can't write LPC2000 internal flash");
|
LOG_ERROR("no working area specified, can't write LPC2000 internal flash");
|
||||||
return ERROR_FLASH_OPERATION_FAILED;
|
return ERROR_FLASH_OPERATION_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct lpc2000_flash_bank *lpc2000_info = bank->driver_priv;
|
|
||||||
uint8_t jump_gate[8];
|
uint8_t jump_gate[8];
|
||||||
|
|
||||||
/* write IAP code to working area */
|
/* write IAP code to working area */
|
||||||
switch (lpc2000_info->variant) {
|
switch (lpc2000_info->variant) {
|
||||||
case lpc1700:
|
case lpc1700:
|
||||||
|
case lpc4300:
|
||||||
target_buffer_set_u32(target, jump_gate, ARMV4_5_T_BX(12));
|
target_buffer_set_u32(target, jump_gate, ARMV4_5_T_BX(12));
|
||||||
target_buffer_set_u32(target, jump_gate + 4, ARMV5_T_BKPT(0));
|
target_buffer_set_u32(target, jump_gate + 4, ARMV5_T_BKPT(0));
|
||||||
break;
|
break;
|
||||||
|
@ -301,6 +339,7 @@ static int lpc2000_iap_call(struct flash_bank *bank, struct working_area *iap_wo
|
||||||
uint32_t param_table[5], uint32_t result_table[4])
|
uint32_t param_table[5], uint32_t result_table[4])
|
||||||
{
|
{
|
||||||
struct lpc2000_flash_bank *lpc2000_info = bank->driver_priv;
|
struct lpc2000_flash_bank *lpc2000_info = bank->driver_priv;
|
||||||
|
struct target *target = bank->target;
|
||||||
|
|
||||||
struct arm_algorithm arm_algo; /* for LPC2000 */
|
struct arm_algorithm arm_algo; /* for LPC2000 */
|
||||||
struct armv7m_algorithm armv7m_info; /* for LPC1700 */
|
struct armv7m_algorithm armv7m_info; /* for LPC1700 */
|
||||||
|
@ -319,12 +358,17 @@ static int lpc2000_iap_call(struct flash_bank *bank, struct working_area *iap_wo
|
||||||
arm_algo.core_state = ARM_STATE_ARM;
|
arm_algo.core_state = ARM_STATE_ARM;
|
||||||
iap_entry_point = 0x7ffffff1;
|
iap_entry_point = 0x7ffffff1;
|
||||||
break;
|
break;
|
||||||
|
case lpc4300:
|
||||||
|
armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
|
||||||
|
armv7m_info.core_mode = ARM_MODE_THREAD;
|
||||||
|
/* read out IAP entry point from ROM driver table at 0x10400100 */
|
||||||
|
target_read_u32(target, 0x10400100, &iap_entry_point);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
LOG_ERROR("BUG: unknown lpc2000->variant encountered");
|
LOG_ERROR("BUG: unknown lpc2000->variant encountered");
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct target *target = bank->target;
|
|
||||||
struct mem_param mem_params[2];
|
struct mem_param mem_params[2];
|
||||||
|
|
||||||
/* command parameter table */
|
/* command parameter table */
|
||||||
|
@ -353,9 +397,10 @@ static int lpc2000_iap_call(struct flash_bank *bank, struct working_area *iap_wo
|
||||||
|
|
||||||
switch (lpc2000_info->variant) {
|
switch (lpc2000_info->variant) {
|
||||||
case lpc1700:
|
case lpc1700:
|
||||||
|
case lpc4300:
|
||||||
/* IAP stack */
|
/* IAP stack */
|
||||||
init_reg_param(®_params[3], "sp", 32, PARAM_OUT);
|
init_reg_param(®_params[3], "sp", 32, PARAM_OUT);
|
||||||
buf_set_u32(reg_params[3].value, 0, 32, iap_working_area->address + 0xb4);
|
buf_set_u32(reg_params[3].value, 0, 32, iap_working_area->address + lpc2000_info->cmd51_src_offset);
|
||||||
|
|
||||||
/* return address */
|
/* return address */
|
||||||
init_reg_param(®_params[4], "lr", 32, PARAM_OUT);
|
init_reg_param(®_params[4], "lr", 32, PARAM_OUT);
|
||||||
|
@ -369,7 +414,7 @@ static int lpc2000_iap_call(struct flash_bank *bank, struct working_area *iap_wo
|
||||||
case lpc2000_v2:
|
case lpc2000_v2:
|
||||||
/* IAP stack */
|
/* IAP stack */
|
||||||
init_reg_param(®_params[3], "sp_svc", 32, PARAM_OUT);
|
init_reg_param(®_params[3], "sp_svc", 32, PARAM_OUT);
|
||||||
buf_set_u32(reg_params[3].value, 0, 32, iap_working_area->address + 0xb4);
|
buf_set_u32(reg_params[3].value, 0, 32, iap_working_area->address + lpc2000_info->cmd51_src_offset);
|
||||||
|
|
||||||
/* return address */
|
/* return address */
|
||||||
init_reg_param(®_params[4], "lr_svc", 32, PARAM_OUT);
|
init_reg_param(®_params[4], "lr_svc", 32, PARAM_OUT);
|
||||||
|
@ -419,6 +464,10 @@ static int lpc2000_iap_blank_check(struct flash_bank *bank, int first, int last)
|
||||||
if (retval != ERROR_OK)
|
if (retval != ERROR_OK)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
|
struct lpc2000_flash_bank *lpc2000_info = bank->driver_priv;
|
||||||
|
if (lpc2000_info->variant == lpc4300)
|
||||||
|
param_table[2] = lpc2000_info->lpc4300_bank;
|
||||||
|
|
||||||
for (int i = first; i <= last && retval == ERROR_OK; i++) {
|
for (int i = first; i <= last && retval == ERROR_OK; i++) {
|
||||||
/* check single sector */
|
/* check single sector */
|
||||||
param_table[0] = param_table[1] = i;
|
param_table[0] = param_table[1] = i;
|
||||||
|
@ -469,28 +518,48 @@ FLASH_BANK_COMMAND_HANDLER(lpc2000_flash_bank_command)
|
||||||
lpc2000_info->cmd51_can_256b = 0;
|
lpc2000_info->cmd51_can_256b = 0;
|
||||||
lpc2000_info->cmd51_can_8192b = 1;
|
lpc2000_info->cmd51_can_8192b = 1;
|
||||||
lpc2000_info->checksum_vector = 5;
|
lpc2000_info->checksum_vector = 5;
|
||||||
|
lpc2000_info->iap_max_stack = 128;
|
||||||
} else if (strcmp(CMD_ARGV[6], "lpc2000_v2") == 0) {
|
} else if (strcmp(CMD_ARGV[6], "lpc2000_v2") == 0) {
|
||||||
lpc2000_info->variant = lpc2000_v2;
|
lpc2000_info->variant = lpc2000_v2;
|
||||||
lpc2000_info->cmd51_dst_boundary = 256;
|
lpc2000_info->cmd51_dst_boundary = 256;
|
||||||
lpc2000_info->cmd51_can_256b = 1;
|
lpc2000_info->cmd51_can_256b = 1;
|
||||||
lpc2000_info->cmd51_can_8192b = 0;
|
lpc2000_info->cmd51_can_8192b = 0;
|
||||||
lpc2000_info->checksum_vector = 5;
|
lpc2000_info->checksum_vector = 5;
|
||||||
|
lpc2000_info->iap_max_stack = 128;
|
||||||
} else if (strcmp(CMD_ARGV[6], "lpc1700") == 0) {
|
} else if (strcmp(CMD_ARGV[6], "lpc1700") == 0) {
|
||||||
lpc2000_info->variant = lpc1700;
|
lpc2000_info->variant = lpc1700;
|
||||||
lpc2000_info->cmd51_dst_boundary = 256;
|
lpc2000_info->cmd51_dst_boundary = 256;
|
||||||
lpc2000_info->cmd51_can_256b = 1;
|
lpc2000_info->cmd51_can_256b = 1;
|
||||||
lpc2000_info->cmd51_can_8192b = 0;
|
lpc2000_info->cmd51_can_8192b = 0;
|
||||||
lpc2000_info->checksum_vector = 7;
|
lpc2000_info->checksum_vector = 7;
|
||||||
|
lpc2000_info->iap_max_stack = 128;
|
||||||
|
} else if (strcmp(CMD_ARGV[6], "lpc4300") == 0) {
|
||||||
|
lpc2000_info->variant = lpc4300;
|
||||||
|
lpc2000_info->cmd51_dst_boundary = 512;
|
||||||
|
lpc2000_info->cmd51_can_256b = 0;
|
||||||
|
lpc2000_info->cmd51_can_8192b = 0;
|
||||||
|
lpc2000_info->checksum_vector = 7;
|
||||||
|
lpc2000_info->iap_max_stack = 208;
|
||||||
} else {
|
} else {
|
||||||
LOG_ERROR("unknown LPC2000 variant: %s", CMD_ARGV[6]);
|
LOG_ERROR("unknown LPC2000 variant: %s", CMD_ARGV[6]);
|
||||||
free(lpc2000_info);
|
free(lpc2000_info);
|
||||||
return ERROR_FLASH_BANK_INVALID;
|
return ERROR_FLASH_BANK_INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* see lpc2000_iap_working_area_init() for the reason behind the 0x34 value */
|
||||||
|
lpc2000_info->cmd51_src_offset = 0x34 + lpc2000_info->iap_max_stack;
|
||||||
|
|
||||||
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[7], lpc2000_info->cclk);
|
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[7], lpc2000_info->cclk);
|
||||||
lpc2000_info->calc_checksum = 0;
|
lpc2000_info->calc_checksum = 0;
|
||||||
lpc2000_build_sector_list(bank);
|
lpc2000_build_sector_list(bank);
|
||||||
|
|
||||||
|
uint32_t temp_base = 0;
|
||||||
|
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], temp_base);
|
||||||
|
if (temp_base >= 0x1B000000)
|
||||||
|
lpc2000_info->lpc4300_bank = 1; /* bank B */
|
||||||
|
else
|
||||||
|
lpc2000_info->lpc4300_bank = 0; /* bank A */
|
||||||
|
|
||||||
if (CMD_ARGC >= 9) {
|
if (CMD_ARGC >= 9) {
|
||||||
if (strcmp(CMD_ARGV[8], "calc_checksum") == 0)
|
if (strcmp(CMD_ARGV[8], "calc_checksum") == 0)
|
||||||
lpc2000_info->calc_checksum = 1;
|
lpc2000_info->calc_checksum = 1;
|
||||||
|
@ -511,6 +580,10 @@ static int lpc2000_erase(struct flash_bank *bank, int first, int last)
|
||||||
|
|
||||||
param_table[0] = first;
|
param_table[0] = first;
|
||||||
param_table[1] = last;
|
param_table[1] = last;
|
||||||
|
|
||||||
|
if (lpc2000_info->variant == lpc4300)
|
||||||
|
param_table[2] = lpc2000_info->lpc4300_bank;
|
||||||
|
else
|
||||||
param_table[2] = lpc2000_info->cclk;
|
param_table[2] = lpc2000_info->cclk;
|
||||||
|
|
||||||
uint32_t result_table[4];
|
uint32_t result_table[4];
|
||||||
|
@ -540,6 +613,10 @@ static int lpc2000_erase(struct flash_bank *bank, int first, int last)
|
||||||
|
|
||||||
if (retval == ERROR_OK) {
|
if (retval == ERROR_OK) {
|
||||||
/* Erase sectors */
|
/* Erase sectors */
|
||||||
|
param_table[2] = lpc2000_info->cclk;
|
||||||
|
if (lpc2000_info->variant == lpc4300)
|
||||||
|
param_table[3] = lpc2000_info->lpc4300_bank;
|
||||||
|
|
||||||
status_code = lpc2000_iap_call(bank, iap_working_area, 52, param_table, result_table);
|
status_code = lpc2000_iap_call(bank, iap_working_area, 52, param_table, result_table);
|
||||||
switch (status_code) {
|
switch (status_code) {
|
||||||
case ERROR_FLASH_OPERATION_FAILED:
|
case ERROR_FLASH_OPERATION_FAILED:
|
||||||
|
@ -659,6 +736,12 @@ static int lpc2000_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offs
|
||||||
/* Prepare sectors */
|
/* Prepare sectors */
|
||||||
param_table[0] = first_sector;
|
param_table[0] = first_sector;
|
||||||
param_table[1] = last_sector;
|
param_table[1] = last_sector;
|
||||||
|
|
||||||
|
if (lpc2000_info->variant == lpc4300)
|
||||||
|
param_table[2] = lpc2000_info->lpc4300_bank;
|
||||||
|
else
|
||||||
|
param_table[2] = lpc2000_info->cclk;
|
||||||
|
|
||||||
int status_code = lpc2000_iap_call(bank, iap_working_area, 50, param_table, result_table);
|
int status_code = lpc2000_iap_call(bank, iap_working_area, 50, param_table, result_table);
|
||||||
switch (status_code) {
|
switch (status_code) {
|
||||||
case ERROR_FLASH_OPERATION_FAILED:
|
case ERROR_FLASH_OPERATION_FAILED:
|
||||||
|
|
Loading…
Reference in New Issue