psoc5lp: Add NV Latch flash driver
Erasing is not supported by the hardware, it can be written directly. Tested on CY8CKIT-059, except modifying-write. Change-Id: I6e920ed930dcd5c7f0b10c5b1b4791a828d9080a Signed-off-by: Andreas Färber <afaerber@suse.de> Signed-off-by: Tomas Vanek <vanekt@fbl.cz> Reviewed-on: http://openocd.zylin.com/3434 Tested-by: jenkinsriscv-compliance
parent
53376dbbed
commit
06123153f3
|
@ -6179,6 +6179,31 @@ flash bank $_CHIPNAME.eeprom psoc5lp_eeprom 0x40008000 0 0 0 $_TARGETNAME
|
||||||
@end example
|
@end example
|
||||||
@end deffn
|
@end deffn
|
||||||
|
|
||||||
|
@deffn {Flash Driver} psoc5lp_nvl
|
||||||
|
All members of the PSoC 5LP microcontroller family from Cypress
|
||||||
|
include internal Nonvolatile Latches and use ARM Cortex-M3 cores.
|
||||||
|
The driver probes for a number of these chips and autoconfigures itself.
|
||||||
|
|
||||||
|
@example
|
||||||
|
flash bank $_CHIPNAME.nvl psoc5lp_nvl 0 0 0 0 $_TARGETNAME
|
||||||
|
@end example
|
||||||
|
|
||||||
|
PSoC 5LP chips have multiple NV Latches:
|
||||||
|
|
||||||
|
@itemize
|
||||||
|
@item Device Configuration NV Latch - 4 bytes
|
||||||
|
@item Write Once (WO) NV Latch - 4 bytes
|
||||||
|
@end itemize
|
||||||
|
|
||||||
|
@b{Note:} This driver only implements the Device Configuration NVL.
|
||||||
|
|
||||||
|
The @var{psoc5lp} driver reads the ECC mode from Device Configuration NVL.
|
||||||
|
@quotation Attention
|
||||||
|
Switching ECC mode via write to Device Configuration NVL will require a reset
|
||||||
|
after successful write.
|
||||||
|
@end quotation
|
||||||
|
@end deffn
|
||||||
|
|
||||||
@deffn {Flash Driver} psoc6
|
@deffn {Flash Driver} psoc6
|
||||||
Supports PSoC6 (CY8C6xxx) family of Cypress microcontrollers.
|
Supports PSoC6 (CY8C6xxx) family of Cypress microcontrollers.
|
||||||
PSoC6 is a dual-core device with CM0+ and CM4 cores. Both cores share
|
PSoC6 is a dual-core device with CM0+ and CM4 cores. Both cores share
|
||||||
|
|
|
@ -58,6 +58,7 @@ extern struct flash_driver pic32mx_flash;
|
||||||
extern struct flash_driver psoc4_flash;
|
extern struct flash_driver psoc4_flash;
|
||||||
extern struct flash_driver psoc5lp_flash;
|
extern struct flash_driver psoc5lp_flash;
|
||||||
extern struct flash_driver psoc5lp_eeprom_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 psoc6_flash;
|
||||||
extern struct flash_driver sim3x_flash;
|
extern struct flash_driver sim3x_flash;
|
||||||
extern struct flash_driver stellaris_flash;
|
extern struct flash_driver stellaris_flash;
|
||||||
|
@ -119,6 +120,7 @@ static struct flash_driver *flash_drivers[] = {
|
||||||
&psoc4_flash,
|
&psoc4_flash,
|
||||||
&psoc5lp_flash,
|
&psoc5lp_flash,
|
||||||
&psoc5lp_eeprom_flash,
|
&psoc5lp_eeprom_flash,
|
||||||
|
&psoc5lp_nvl_flash,
|
||||||
&psoc6_flash,
|
&psoc6_flash,
|
||||||
&sim3x_flash,
|
&sim3x_flash,
|
||||||
&stellaris_flash,
|
&stellaris_flash,
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#define PM_ACT_CFG12 0x400043AC
|
#define PM_ACT_CFG12 0x400043AC
|
||||||
#define SPC_CPU_DATA 0x40004720
|
#define SPC_CPU_DATA 0x40004720
|
||||||
#define SPC_SR 0x40004722
|
#define SPC_SR 0x40004722
|
||||||
|
#define PRT1_PC2 0x4000500A
|
||||||
#define PHUB_CH0_BASIC_CFG 0x40007010
|
#define PHUB_CH0_BASIC_CFG 0x40007010
|
||||||
#define PHUB_CH0_ACTION 0x40007014
|
#define PHUB_CH0_ACTION 0x40007014
|
||||||
#define PHUB_CH0_BASIC_STATUS 0x40007018
|
#define PHUB_CH0_BASIC_STATUS 0x40007018
|
||||||
|
@ -45,6 +46,11 @@
|
||||||
#define PHUB_TDMEM1_ORIG_TD1 0x4000780C
|
#define PHUB_TDMEM1_ORIG_TD1 0x4000780C
|
||||||
#define PANTHER_DEVICE_ID 0x4008001C
|
#define PANTHER_DEVICE_ID 0x4008001C
|
||||||
|
|
||||||
|
/* NVL is not actually mapped to the Cortex-M address space
|
||||||
|
* As we need a base addess different from other banks in the device
|
||||||
|
* we use the address of NVL programming data in Cypress images */
|
||||||
|
#define NVL_META_BASE 0x90000000
|
||||||
|
|
||||||
#define PM_ACT_CFG12_EN_EE (1 << 4)
|
#define PM_ACT_CFG12_EN_EE (1 << 4)
|
||||||
|
|
||||||
#define SPC_KEY1 0xB6
|
#define SPC_KEY1 0xB6
|
||||||
|
@ -359,6 +365,31 @@ static int psoc5lp_spc_busy_wait_idle(struct target *target)
|
||||||
return ERROR_FLASH_OPERATION_FAILED;
|
return ERROR_FLASH_OPERATION_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int psoc5lp_spc_load_byte(struct target *target,
|
||||||
|
uint8_t array_id, uint8_t offset, uint8_t value)
|
||||||
|
{
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
retval = psoc5lp_spc_write_opcode(target, SPC_LOAD_BYTE);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
retval = target_write_u8(target, SPC_CPU_DATA, array_id);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
retval = target_write_u8(target, SPC_CPU_DATA, offset);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
retval = target_write_u8(target, SPC_CPU_DATA, value);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
retval = psoc5lp_spc_busy_wait_idle(target);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
static int psoc5lp_spc_load_row(struct target *target,
|
static int psoc5lp_spc_load_row(struct target *target,
|
||||||
uint8_t array_id, const uint8_t *data, unsigned row_size)
|
uint8_t array_id, const uint8_t *data, unsigned row_size)
|
||||||
{
|
{
|
||||||
|
@ -446,6 +477,25 @@ static int psoc5lp_spc_write_row(struct target *target,
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int psoc5lp_spc_write_user_nvl(struct target *target,
|
||||||
|
uint8_t array_id)
|
||||||
|
{
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
retval = psoc5lp_spc_write_opcode(target, SPC_WRITE_USER_NVL);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
retval = target_write_u8(target, SPC_CPU_DATA, array_id);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
retval = psoc5lp_spc_busy_wait_idle(target);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
static int psoc5lp_spc_erase_sector(struct target *target,
|
static int psoc5lp_spc_erase_sector(struct target *target,
|
||||||
uint8_t array_id, uint8_t row_id)
|
uint8_t array_id, uint8_t row_id)
|
||||||
{
|
{
|
||||||
|
@ -545,6 +595,272 @@ static int psoc5lp_spc_get_temp(struct target *target, uint8_t samples,
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int psoc5lp_spc_read_volatile_byte(struct target *target,
|
||||||
|
uint8_t array_id, uint8_t offset, uint8_t *data)
|
||||||
|
{
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
retval = psoc5lp_spc_write_opcode(target, SPC_READ_VOLATILE_BYTE);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
retval = target_write_u8(target, SPC_CPU_DATA, array_id);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
retval = target_write_u8(target, SPC_CPU_DATA, offset);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
retval = psoc5lp_spc_busy_wait_data(target);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
retval = target_read_u8(target, SPC_CPU_DATA, data);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
retval = psoc5lp_spc_busy_wait_idle(target);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NV Latch
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct psoc5lp_nvl_flash_bank {
|
||||||
|
bool probed;
|
||||||
|
const struct psoc5lp_device *device;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int psoc5lp_nvl_read(struct flash_bank *bank,
|
||||||
|
uint8_t *buffer, uint32_t offset, uint32_t count)
|
||||||
|
{
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
retval = psoc5lp_spc_enable_clock(bank->target);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
while (count > 0) {
|
||||||
|
retval = psoc5lp_spc_read_byte(bank->target,
|
||||||
|
SPC_ARRAY_NVL_USER, offset, buffer);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
buffer++;
|
||||||
|
offset++;
|
||||||
|
count--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int psoc5lp_nvl_erase(struct flash_bank *bank, int first, int last)
|
||||||
|
{
|
||||||
|
LOG_WARNING("There is no erase operation for NV Latches");
|
||||||
|
return ERROR_FLASH_OPER_UNSUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int psoc5lp_nvl_erase_check(struct flash_bank *bank)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < bank->num_sectors; i++)
|
||||||
|
bank->sectors[i].is_erased = 0;
|
||||||
|
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int psoc5lp_nvl_write(struct flash_bank *bank,
|
||||||
|
const uint8_t *buffer, uint32_t offset, uint32_t byte_count)
|
||||||
|
{
|
||||||
|
struct target *target = bank->target;
|
||||||
|
uint8_t *current_data, val;
|
||||||
|
bool write_required = false, pullup_needed = false, ecc_changed = false;
|
||||||
|
uint32_t i;
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
if (offset != 0 || byte_count != bank->size) {
|
||||||
|
LOG_ERROR("NVL can only be written in whole");
|
||||||
|
return ERROR_FLASH_OPER_UNSUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
current_data = calloc(1, bank->size);
|
||||||
|
if (!current_data)
|
||||||
|
return ERROR_FAIL;
|
||||||
|
retval = psoc5lp_nvl_read(bank, current_data, offset, byte_count);
|
||||||
|
if (retval != ERROR_OK) {
|
||||||
|
free(current_data);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
for (i = offset; i < byte_count; i++) {
|
||||||
|
if (current_data[i] != buffer[i]) {
|
||||||
|
write_required = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (((buffer[2] & 0x80) == 0x80) && ((current_data[0] & 0x0C) != 0x08))
|
||||||
|
pullup_needed = true;
|
||||||
|
if (((buffer[3] ^ current_data[3]) & 0x08) == 0x08)
|
||||||
|
ecc_changed = true;
|
||||||
|
free(current_data);
|
||||||
|
|
||||||
|
if (!write_required) {
|
||||||
|
LOG_INFO("Unchanged, skipping NVL write");
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
if (pullup_needed) {
|
||||||
|
retval = target_read_u8(target, PRT1_PC2, &val);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
val &= 0xF0;
|
||||||
|
val |= 0x05;
|
||||||
|
retval = target_write_u8(target, PRT1_PC2, val);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = offset; i < byte_count; i++) {
|
||||||
|
retval = psoc5lp_spc_load_byte(target,
|
||||||
|
SPC_ARRAY_NVL_USER, i, buffer[i]);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
retval = psoc5lp_spc_read_volatile_byte(target,
|
||||||
|
SPC_ARRAY_NVL_USER, i, &val);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
if (val != buffer[i]) {
|
||||||
|
LOG_ERROR("Failed to load NVL byte %" PRIu32 ": "
|
||||||
|
"expected 0x%02" PRIx8 ", read 0x%02" PRIx8,
|
||||||
|
i, buffer[i], val);
|
||||||
|
return ERROR_FLASH_OPERATION_FAILED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
retval = psoc5lp_spc_write_user_nvl(target, SPC_ARRAY_NVL_USER);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
if (ecc_changed) {
|
||||||
|
retval = target_call_reset_callbacks(target, RESET_INIT);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
LOG_WARNING("Reset failed after enabling or disabling ECC");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int psoc5lp_nvl_protect_check(struct flash_bank *bank)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < bank->num_sectors; i++)
|
||||||
|
bank->sectors[i].is_protected = -1;
|
||||||
|
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int psoc5lp_nvl_get_info_command(struct flash_bank *bank,
|
||||||
|
char *buf, int buf_size)
|
||||||
|
{
|
||||||
|
struct psoc5lp_nvl_flash_bank *psoc_nvl_bank = bank->driver_priv;
|
||||||
|
char part_number[PART_NUMBER_LEN];
|
||||||
|
|
||||||
|
psoc5lp_get_part_number(psoc_nvl_bank->device, part_number);
|
||||||
|
|
||||||
|
snprintf(buf, buf_size, "%s", part_number);
|
||||||
|
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int psoc5lp_nvl_probe(struct flash_bank *bank)
|
||||||
|
{
|
||||||
|
struct psoc5lp_nvl_flash_bank *psoc_nvl_bank = bank->driver_priv;
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
if (psoc_nvl_bank->probed)
|
||||||
|
return ERROR_OK;
|
||||||
|
|
||||||
|
if (bank->target->state != TARGET_HALTED) {
|
||||||
|
LOG_ERROR("Target not halted");
|
||||||
|
return ERROR_TARGET_NOT_HALTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
retval = psoc5lp_find_device(bank->target, &psoc_nvl_bank->device);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
bank->base = NVL_META_BASE;
|
||||||
|
bank->size = 4;
|
||||||
|
bank->num_sectors = 1;
|
||||||
|
bank->sectors = calloc(bank->num_sectors,
|
||||||
|
sizeof(struct flash_sector));
|
||||||
|
bank->sectors[0].offset = 0;
|
||||||
|
bank->sectors[0].size = 4;
|
||||||
|
bank->sectors[0].is_erased = -1;
|
||||||
|
bank->sectors[0].is_protected = -1;
|
||||||
|
|
||||||
|
psoc_nvl_bank->probed = true;
|
||||||
|
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int psoc5lp_nvl_auto_probe(struct flash_bank *bank)
|
||||||
|
{
|
||||||
|
struct psoc5lp_nvl_flash_bank *psoc_nvl_bank = bank->driver_priv;
|
||||||
|
|
||||||
|
if (psoc_nvl_bank->probed)
|
||||||
|
return ERROR_OK;
|
||||||
|
|
||||||
|
return psoc5lp_nvl_probe(bank);
|
||||||
|
}
|
||||||
|
|
||||||
|
FLASH_BANK_COMMAND_HANDLER(psoc5lp_nvl_flash_bank_command)
|
||||||
|
{
|
||||||
|
struct psoc5lp_nvl_flash_bank *psoc_nvl_bank;
|
||||||
|
|
||||||
|
psoc_nvl_bank = malloc(sizeof(struct psoc5lp_nvl_flash_bank));
|
||||||
|
if (!psoc_nvl_bank)
|
||||||
|
return ERROR_FLASH_OPERATION_FAILED;
|
||||||
|
|
||||||
|
psoc_nvl_bank->probed = false;
|
||||||
|
|
||||||
|
bank->driver_priv = psoc_nvl_bank;
|
||||||
|
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct command_registration psoc5lp_nvl_exec_command_handlers[] = {
|
||||||
|
COMMAND_REGISTRATION_DONE
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct command_registration psoc5lp_nvl_command_handlers[] = {
|
||||||
|
{
|
||||||
|
.name = "psoc5lp_nvl",
|
||||||
|
.mode = COMMAND_ANY,
|
||||||
|
.help = "PSoC 5LP NV Latch command group",
|
||||||
|
.usage = "",
|
||||||
|
.chain = psoc5lp_nvl_exec_command_handlers,
|
||||||
|
},
|
||||||
|
COMMAND_REGISTRATION_DONE
|
||||||
|
};
|
||||||
|
|
||||||
|
struct flash_driver psoc5lp_nvl_flash = {
|
||||||
|
.name = "psoc5lp_nvl",
|
||||||
|
.commands = psoc5lp_nvl_command_handlers,
|
||||||
|
.flash_bank_command = psoc5lp_nvl_flash_bank_command,
|
||||||
|
.info = psoc5lp_nvl_get_info_command,
|
||||||
|
.probe = psoc5lp_nvl_probe,
|
||||||
|
.auto_probe = psoc5lp_nvl_auto_probe,
|
||||||
|
.protect_check = psoc5lp_nvl_protect_check,
|
||||||
|
.read = psoc5lp_nvl_read,
|
||||||
|
.erase = psoc5lp_nvl_erase,
|
||||||
|
.erase_check = psoc5lp_nvl_erase_check,
|
||||||
|
.write = psoc5lp_nvl_write,
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* EEPROM
|
* EEPROM
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -58,6 +58,7 @@ $_TARGETNAME configure -event reset-init {
|
||||||
set _FLASHNAME $_CHIPNAME.flash
|
set _FLASHNAME $_CHIPNAME.flash
|
||||||
flash bank $_FLASHNAME psoc5lp 0x00000000 0 0 0 $_TARGETNAME
|
flash bank $_FLASHNAME psoc5lp 0x00000000 0 0 0 $_TARGETNAME
|
||||||
flash bank $_CHIPNAME.eeprom psoc5lp_eeprom 0x40008000 0 0 0 $_TARGETNAME
|
flash bank $_CHIPNAME.eeprom psoc5lp_eeprom 0x40008000 0 0 0 $_TARGETNAME
|
||||||
|
flash bank $_CHIPNAME.nvl psoc5lp_nvl 0 0 0 0 $_TARGETNAME
|
||||||
|
|
||||||
if {![using_hla]} {
|
if {![using_hla]} {
|
||||||
cortex_m reset_config sysresetreq
|
cortex_m reset_config sysresetreq
|
||||||
|
|
Loading…
Reference in New Issue