From f1427cca3cc6097a4635b4095ad1e30c8a508ac0 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Andreas=20F=C3=A4rber?= Date: Tue, 3 May 2016 01:47:54 +0200 Subject: [PATCH] psoc5lp: Add EEPROM flash driver MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Tested on CY8CKIT-059. Change-Id: Ib02262e8eebf0df3d29492b8a7daa65b262da580 Signed-off-by: Andreas Färber Signed-off-by: Tomas Vanek Reviewed-on: http://openocd.zylin.com/3433 Tested-by: jenkins --- doc/openocd.texi | 11 ++ src/flash/nor/drivers.c | 2 + src/flash/nor/psoc5lp.c | 271 ++++++++++++++++++++++++++++++++++++++++ tcl/target/psoc5lp.cfg | 1 + 4 files changed, 285 insertions(+) diff --git a/doc/openocd.texi b/doc/openocd.texi index 5c828383..d8a7bb92 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -6168,6 +6168,17 @@ and all row latches in all flash arrays on the device. @end deffn @end deffn +@deffn {Flash Driver} psoc5lp_eeprom +All members of the PSoC 5LP microcontroller family from Cypress +include internal EEPROM and use ARM Cortex-M3 cores. +The driver probes for a number of these chips and autoconfigures itself, +apart from the base address. + +@example +flash bank $_CHIPNAME.eeprom psoc5lp_eeprom 0x40008000 0 0 0 $_TARGETNAME +@end example +@end deffn + @deffn {Flash Driver} psoc6 Supports PSoC6 (CY8C6xxx) family of Cypress microcontrollers. PSoC6 is a dual-core device with CM0+ and CM4 cores. Both cores share diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c index f777df62..2d391d28 100644 --- a/src/flash/nor/drivers.c +++ b/src/flash/nor/drivers.c @@ -57,6 +57,7 @@ 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 psoc6_flash; extern struct flash_driver sim3x_flash; extern struct flash_driver stellaris_flash; @@ -117,6 +118,7 @@ static struct flash_driver *flash_drivers[] = { &pic32mx_flash, &psoc4_flash, &psoc5lp_flash, + &psoc5lp_eeprom_flash, &psoc6_flash, &sim3x_flash, &stellaris_flash, diff --git a/src/flash/nor/psoc5lp.c b/src/flash/nor/psoc5lp.c index 44fd033b..87e130fb 100644 --- a/src/flash/nor/psoc5lp.c +++ b/src/flash/nor/psoc5lp.c @@ -26,6 +26,7 @@ #include #define PM_ACT_CFG0 0x400043A0 +#define PM_ACT_CFG12 0x400043AC #define SPC_CPU_DATA 0x40004720 #define SPC_SR 0x40004722 #define PHUB_CH0_BASIC_CFG 0x40007010 @@ -44,6 +45,8 @@ #define PHUB_TDMEM1_ORIG_TD1 0x4000780C #define PANTHER_DEVICE_ID 0x4008001C +#define PM_ACT_CFG12_EN_EE (1 << 4) + #define SPC_KEY1 0xB6 #define SPC_KEY2 0xD3 @@ -96,6 +99,9 @@ #define ROWS_PER_BLOCK 256 #define BLOCK_SIZE (ROWS_PER_BLOCK * ROW_SIZE) #define SECTORS_PER_BLOCK (BLOCK_SIZE / SECTOR_SIZE) +#define EEPROM_ROW_SIZE 16 +#define EEPROM_SECTOR_SIZE (ROWS_PER_SECTOR * EEPROM_ROW_SIZE) +#define EEPROM_BLOCK_SIZE (ROWS_PER_BLOCK * EEPROM_ROW_SIZE) #define PART_NUMBER_LEN (17 + 1) @@ -353,6 +359,32 @@ static int psoc5lp_spc_busy_wait_idle(struct target *target) return ERROR_FLASH_OPERATION_FAILED; } +static int psoc5lp_spc_load_row(struct target *target, + uint8_t array_id, const uint8_t *data, unsigned row_size) +{ + unsigned i; + int retval; + + retval = psoc5lp_spc_write_opcode(target, SPC_LOAD_ROW); + if (retval != ERROR_OK) + return retval; + retval = target_write_u8(target, SPC_CPU_DATA, array_id); + if (retval != ERROR_OK) + return retval; + + for (i = 0; i < row_size; i++) { + retval = target_write_u8(target, SPC_CPU_DATA, data[i]); + 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_read_byte(struct target *target, uint8_t array_id, uint8_t offset, uint8_t *data) { @@ -383,6 +415,37 @@ static int psoc5lp_spc_read_byte(struct target *target, return ERROR_OK; } +static int psoc5lp_spc_write_row(struct target *target, + uint8_t array_id, uint16_t row_id, const uint8_t *temp) +{ + int retval; + + retval = psoc5lp_spc_write_opcode(target, SPC_WRITE_ROW); + 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, row_id >> 8); + if (retval != ERROR_OK) + return retval; + retval = target_write_u8(target, SPC_CPU_DATA, row_id & 0xff); + if (retval != ERROR_OK) + return retval; + retval = target_write_u8(target, SPC_CPU_DATA, temp[0]); + if (retval != ERROR_OK) + return retval; + retval = target_write_u8(target, SPC_CPU_DATA, temp[1]); + 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, uint8_t array_id, uint8_t row_id) { @@ -482,6 +545,214 @@ static int psoc5lp_spc_get_temp(struct target *target, uint8_t samples, return ERROR_OK; } +/* + * EEPROM + */ + +struct psoc5lp_eeprom_flash_bank { + bool probed; + const struct psoc5lp_device *device; +}; + +static int psoc5lp_eeprom_erase(struct flash_bank *bank, int first, int last) +{ + int i, retval; + + for (i = first; i <= last; i++) { + retval = psoc5lp_spc_erase_sector(bank->target, + SPC_ARRAY_EEPROM, i); + if (retval != ERROR_OK) + return retval; + } + + return ERROR_OK; +} + +static int psoc5lp_eeprom_write(struct flash_bank *bank, + const uint8_t *buffer, uint32_t offset, uint32_t byte_count) +{ + struct target *target = bank->target; + uint8_t temp[2]; + unsigned row; + int retval; + + if (offset % EEPROM_ROW_SIZE != 0) { + LOG_ERROR("Writes must be row-aligned, got offset 0x%08" PRIx32, + offset); + return ERROR_FLASH_DST_BREAKS_ALIGNMENT; + } + + retval = psoc5lp_spc_get_temp(target, 3, temp); + if (retval != ERROR_OK) { + LOG_ERROR("Unable to read Die temperature"); + return retval; + } + LOG_DEBUG("Get_Temp: sign 0x%02" PRIx8 ", magnitude 0x%02" PRIx8, + temp[0], temp[1]); + + for (row = offset / EEPROM_ROW_SIZE; byte_count >= EEPROM_ROW_SIZE; row++) { + retval = psoc5lp_spc_load_row(target, SPC_ARRAY_EEPROM, + buffer, EEPROM_ROW_SIZE); + if (retval != ERROR_OK) + return retval; + + retval = psoc5lp_spc_write_row(target, SPC_ARRAY_EEPROM, + row, temp); + if (retval != ERROR_OK) + return retval; + + buffer += EEPROM_ROW_SIZE; + byte_count -= EEPROM_ROW_SIZE; + offset += EEPROM_ROW_SIZE; + } + if (byte_count > 0) { + uint8_t buf[EEPROM_ROW_SIZE]; + + memcpy(buf, buffer, byte_count); + memset(buf + byte_count, bank->default_padded_value, + EEPROM_ROW_SIZE - byte_count); + + LOG_DEBUG("Padding %d bytes", EEPROM_ROW_SIZE - byte_count); + retval = psoc5lp_spc_load_row(target, SPC_ARRAY_EEPROM, + buf, EEPROM_ROW_SIZE); + if (retval != ERROR_OK) + return retval; + + retval = psoc5lp_spc_write_row(target, SPC_ARRAY_EEPROM, + row, temp); + if (retval != ERROR_OK) + return retval; + } + + return ERROR_OK; +} + +static int psoc5lp_eeprom_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_eeprom_get_info_command(struct flash_bank *bank, char *buf, int buf_size) +{ + struct psoc5lp_eeprom_flash_bank *psoc_eeprom_bank = bank->driver_priv; + char part_number[PART_NUMBER_LEN]; + + psoc5lp_get_part_number(psoc_eeprom_bank->device, part_number); + + snprintf(buf, buf_size, "%s", part_number); + + return ERROR_OK; +} + +static int psoc5lp_eeprom_probe(struct flash_bank *bank) +{ + struct psoc5lp_eeprom_flash_bank *psoc_eeprom_bank = bank->driver_priv; + uint32_t flash_addr = bank->base; + uint32_t val; + int i, retval; + + if (psoc_eeprom_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_eeprom_bank->device); + if (retval != ERROR_OK) + return retval; + + retval = target_read_u32(bank->target, PM_ACT_CFG12, &val); + if (retval != ERROR_OK) + return retval; + if (!(val & PM_ACT_CFG12_EN_EE)) { + val |= PM_ACT_CFG12_EN_EE; + retval = target_write_u32(bank->target, PM_ACT_CFG12, val); + if (retval != ERROR_OK) + return retval; + } + + bank->size = psoc_eeprom_bank->device->eeprom_kb * 1024; + bank->num_sectors = DIV_ROUND_UP(bank->size, EEPROM_SECTOR_SIZE); + bank->sectors = calloc(bank->num_sectors, + sizeof(struct flash_sector)); + for (i = 0; i < bank->num_sectors; i++) { + bank->sectors[i].size = EEPROM_SECTOR_SIZE; + bank->sectors[i].offset = flash_addr - bank->base; + bank->sectors[i].is_erased = -1; + bank->sectors[i].is_protected = -1; + + flash_addr += bank->sectors[i].size; + } + + bank->default_padded_value = bank->erased_value = 0x00; + + psoc_eeprom_bank->probed = true; + + return ERROR_OK; +} + +static int psoc5lp_eeprom_auto_probe(struct flash_bank *bank) +{ + struct psoc5lp_eeprom_flash_bank *psoc_eeprom_bank = bank->driver_priv; + + if (psoc_eeprom_bank->probed) + return ERROR_OK; + + return psoc5lp_eeprom_probe(bank); +} + +FLASH_BANK_COMMAND_HANDLER(psoc5lp_eeprom_flash_bank_command) +{ + struct psoc5lp_eeprom_flash_bank *psoc_eeprom_bank; + + psoc_eeprom_bank = malloc(sizeof(struct psoc5lp_eeprom_flash_bank)); + if (!psoc_eeprom_bank) + return ERROR_FLASH_OPERATION_FAILED; + + psoc_eeprom_bank->probed = false; + psoc_eeprom_bank->device = NULL; + + bank->driver_priv = psoc_eeprom_bank; + + return ERROR_OK; +} + +static const struct command_registration psoc5lp_eeprom_exec_command_handlers[] = { + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration psoc5lp_eeprom_command_handlers[] = { + { + .name = "psoc5lp_eeprom", + .mode = COMMAND_ANY, + .help = "PSoC 5LP EEPROM command group", + .usage = "", + .chain = psoc5lp_eeprom_exec_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +struct flash_driver psoc5lp_eeprom_flash = { + .name = "psoc5lp_eeprom", + .commands = psoc5lp_eeprom_command_handlers, + .flash_bank_command = psoc5lp_eeprom_flash_bank_command, + .info = psoc5lp_eeprom_get_info_command, + .probe = psoc5lp_eeprom_probe, + .auto_probe = psoc5lp_eeprom_auto_probe, + .protect_check = psoc5lp_eeprom_protect_check, + .read = default_flash_read, + .erase = psoc5lp_eeprom_erase, + .erase_check = default_flash_blank_check, + .write = psoc5lp_eeprom_write, +}; + /* * Program Flash */ diff --git a/tcl/target/psoc5lp.cfg b/tcl/target/psoc5lp.cfg index 68d83b02..2089158e 100644 --- a/tcl/target/psoc5lp.cfg +++ b/tcl/target/psoc5lp.cfg @@ -57,6 +57,7 @@ $_TARGETNAME configure -event reset-init { set _FLASHNAME $_CHIPNAME.flash flash bank $_FLASHNAME psoc5lp 0x00000000 0 0 0 $_TARGETNAME +flash bank $_CHIPNAME.eeprom psoc5lp_eeprom 0x40008000 0 0 0 $_TARGETNAME if {![using_hla]} { cortex_m reset_config sysresetreq -- 2.39.5