This is an automated email from Gerrit.

"Paul Fertser <fercer...@gmail.com>" just uploaded a new patch set to Gerrit, 
which you can find at https://review.openocd.org/c/openocd/+/8189

-- gerrit

commit 7f1e8bd108cbd5ac686bf502d9665dd8544a664e
Author: Paul Fertser <fercer...@gmail.com>
Date:   Wed Feb 7 14:02:23 2024 +0300

    flash: nor: lpc55xx support
    
    Tested on LPC55S36 LPCXpresso board.
    
    Valgrind-clean.
    
    Signed-off-by: Paul Fertser <fercer...@gmail.com>
    Change-Id: Ifc73d5774838bf0455e4422d5a63b19eab949646

diff --git a/doc/openocd.texi b/doc/openocd.texi
index 6b71fe869d..a7f381bf07 100644
--- a/doc/openocd.texi
+++ b/doc/openocd.texi
@@ -7158,6 +7158,25 @@ lpc2900 secure_jtag 0
 @end deffn
 @end deffn
 
+@deffn {Flash Driver} {lpc55xx}
+This the flash driver using ROM API functions provided by NXP LPC55xx family
+MCUs.
+
+The @var{lpc55xx} driver defines one mandatory parameter, the address of the
+flash API ROM table. If 0 is passed the driver will try to auto-detect based on
+device ID.
+
+The MCU will appear unresponsive when the first flash sector doesn't contain
+valid firmware header (e.g. when the flash is blank), to attach to the target
+@code{lpc55xx_start_debug_session} procedure is offered by the target config.
+
+Sector protection is not implemented.
+
+@example
+flash bank $_FLASHNAME lpc55xx 0 0 0 0 0 0
+@end example
+@end deffn
+
 @deffn {Flash Driver} {mdr}
 This drivers handles the integrated NOR flash on Milandr Cortex-M
 based controllers. A known limitation is that the Info memory can't be
diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am
index 534a7a804e..23012e3c10 100644
--- a/src/flash/nor/Makefile.am
+++ b/src/flash/nor/Makefile.am
@@ -39,6 +39,7 @@ NOR_DRIVERS = \
        %D%/lpc2000.c \
        %D%/lpc288x.c \
        %D%/lpc2900.c \
+       %D%/lpc55xx.c \
        %D%/lpcspifi.c \
        %D%/max32xxx.c \
        %D%/mdr.c \
diff --git a/src/flash/nor/driver.h b/src/flash/nor/driver.h
index a63b72c8fa..3851058f36 100644
--- a/src/flash/nor/driver.h
+++ b/src/flash/nor/driver.h
@@ -267,6 +267,7 @@ extern const struct flash_driver kinetis_ke_flash;
 extern const struct flash_driver lpc2000_flash;
 extern const struct flash_driver lpc288x_flash;
 extern const struct flash_driver lpc2900_flash;
+extern const struct flash_driver lpc55xx_flash;
 extern const struct flash_driver lpcspifi_flash;
 extern const struct flash_driver max32xxx_flash;
 extern const struct flash_driver mdr_flash;
diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c
index 3157bd3292..fa1d4be554 100644
--- a/src/flash/nor/drivers.c
+++ b/src/flash/nor/drivers.c
@@ -44,6 +44,7 @@ static const struct flash_driver * const flash_drivers[] = {
        &lpc2000_flash,
        &lpc288x_flash,
        &lpc2900_flash,
+       &lpc55xx_flash,
        &lpcspifi_flash,
        &max32xxx_flash,
        &mdr_flash,
diff --git a/src/flash/nor/lpc55xx.c b/src/flash/nor/lpc55xx.c
new file mode 100644
index 0000000000..7138bffe8a
--- /dev/null
+++ b/src/flash/nor/lpc55xx.c
@@ -0,0 +1,288 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/***************************************************************************
+ * Copyright (C) 2024  Transcelestial Technologies PTE LTD                 *
+ * Paul Fertser <fercer...@gmail.com>                                      *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "imp.h"
+#include <helper/binarybuffer.h>
+#include <target/algorithm.h>
+#include <target/arm_opcodes.h>
+#include <target/armv7m.h>
+
+/**
+ * @file
+ * Flash programming support for NXP LPC55xx series (tested on LPC55S36 1B)
+ */
+
+#define LPC55XX_DEVICEID_REG   0x40000ff8
+#define LPC55XX_DIEID_REG      0x40000ffc
+
+#define API_FLASH_INIT         1
+#define API_FLASH_ERASE                2
+#define API_FLASH_PROGRAM      3
+#define API_FLASH_VERIFY_ERASE 4
+
+#define BANK_WA_SIZE   1024
+
+struct lpc55xx_flash_bank {
+       bool probed;
+       uint32_t flash_api_table;
+       struct working_area *wa;
+};
+
+FLASH_BANK_COMMAND_HANDLER(lpc55xx_flash_bank_command)
+{
+       struct lpc55xx_flash_bank *lpc55xx_info;
+
+       if (CMD_ARGC < 7)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       lpc55xx_info = calloc(1, sizeof(struct lpc55xx_flash_bank));
+
+       bank->driver_priv = lpc55xx_info;
+       lpc55xx_info->probed = false;
+       COMMAND_PARSE_NUMBER(uint, CMD_ARGV[6], lpc55xx_info->flash_api_table);
+
+       return ERROR_OK;
+}
+
+static int lpc55xx_api_call(struct flash_bank *bank, unsigned int func, int 
regs, struct reg_param reg_params[])
+{
+       struct lpc55xx_flash_bank *lpc55xx_info = bank->driver_priv;
+       struct target *target = bank->target;
+       struct armv7m_algorithm armv7m_info;
+       uint32_t func_addr;
+       uint32_t status;
+
+       if (!lpc55xx_info->wa) {
+               if (target_alloc_working_area(target, BANK_WA_SIZE, 
&lpc55xx_info->wa) != ERROR_OK) {
+                       LOG_ERROR("No working area specified, can not use flash 
API");
+                       return ERROR_FAIL;
+               }
+
+               /* We'll be using this as LR target to halt after API function 
call */
+               target_write_u32(target, lpc55xx_info->wa->address, 
ARMV5_T_BKPT(0));
+       }
+
+       target_read_u32(target, lpc55xx_info->flash_api_table + 4 * func, 
&func_addr);
+
+       /* we have bkpt #0 there in the beginning of wa */
+       init_reg_param(&reg_params[0], "lr", 32, PARAM_OUT);
+       buf_set_u32(reg_params[0].value, 0, 32, lpc55xx_info->wa->address | 1);
+       /* TODO what to set SP to?.. Can we count on "reset init" to leave it 
in suitable state? */
+       /*
+       init_reg_param(&reg_params[1], "sp", 32, PARAM_OUT);
+       buf_set_u32(reg_params[1].value, 0, 32, lpc55xx_info->wa->address + 
BANK_WA_SIZE);
+       */
+       /* dummy set to keep the count */
+       init_reg_param(&reg_params[1], "r0", 32, PARAM_OUT);
+       buf_set_u32(reg_params[1].value, 0, 32, 0);
+       init_reg_param(&reg_params[2], "r0", 32, PARAM_IN_OUT);
+       buf_set_u32(reg_params[2].value, 0, 32, lpc55xx_info->wa->address + 4);
+
+       armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
+       armv7m_info.core_mode = ARM_MODE_THREAD;
+       target_run_algorithm(target, 0, NULL, regs, reg_params, func_addr - 1, 
0, 10000, &armv7m_info);
+
+       status = buf_get_u32(reg_params[2].value, 0, 32);
+       LOG_DEBUG("API call %d status 0x%8.8" PRIx32, func, status);
+
+       for (int i = 0; i < regs; i++)
+               destroy_reg_param(&reg_params[i]);
+
+       return status;
+}
+
+static int lpc55xx_erase(struct flash_bank *bank, unsigned int first,
+               unsigned int last)
+{
+       struct reg_param reg_params[6];
+       size_t sectors_size = 0;
+       int api_status;
+
+       for (size_t i = first; i <= last; i++)
+               sectors_size += bank->sectors[i].size;
+
+       init_reg_param(&reg_params[3], "r1", 32, PARAM_IN_OUT);
+       buf_set_u32(reg_params[3].value, 0, 32, bank->base + 
bank->sectors[first].offset);
+       init_reg_param(&reg_params[4], "r2", 32, PARAM_IN_OUT);
+       buf_set_u32(reg_params[4].value, 0, 32, sectors_size);
+       init_reg_param(&reg_params[5], "r3", 32, PARAM_IN_OUT);
+       buf_set_u32(reg_params[5].value, 0, 32, 'k' << 24 | 'e' << 16 | 'f' << 
8 | 'l');
+
+       api_status = lpc55xx_api_call(bank, API_FLASH_ERASE, 
ARRAY_SIZE(reg_params), reg_params);
+
+       return api_status == 0 ? ERROR_OK : ERROR_FLASH_OPERATION_FAILED;
+}
+
+static int lpc55xx_erase_check(struct flash_bank *bank)
+{
+       struct reg_param reg_params[5];
+       int retval = ERROR_OK;
+       int api_status;
+
+       for (size_t i = 0; i < bank->num_sectors; i++) {
+               init_reg_param(&reg_params[3], "r1", 32, PARAM_IN_OUT);
+               buf_set_u32(reg_params[3].value, 0, 32, bank->base + 
bank->sectors[i].offset);
+               init_reg_param(&reg_params[4], "r2", 32, PARAM_IN_OUT);
+               buf_set_u32(reg_params[4].value, 0, 32, bank->sectors[i].size);
+
+               api_status = lpc55xx_api_call(bank, API_FLASH_VERIFY_ERASE, 
ARRAY_SIZE(reg_params), reg_params);
+
+               if (api_status == 0) {
+                       bank->sectors[i].is_erased = true;
+               } else if (api_status == 105) {
+                       bank->sectors[i].is_erased = false;
+               } else {
+                       retval = ERROR_FLASH_OPERATION_FAILED;
+                       break;
+               }
+       }
+
+       return retval;
+}
+
+static int lpc55xx_write(struct flash_bank *bank, const uint8_t *buffer, 
uint32_t offset, uint32_t count)
+{
+       struct working_area *download_area;
+       struct target *target = bank->target;
+       struct reg_param reg_params[6];
+       const size_t download_size = 8192;
+       int retval = ERROR_OK;
+       int api_status;
+
+       if (target_alloc_working_area(target, download_size, &download_area) != 
ERROR_OK) {
+               LOG_ERROR("Failed to allocate working area for write buffer");
+               return ERROR_FAIL;
+       }
+
+       while (count != 0) {
+               size_t bytes_to_write = MIN(count, download_size);
+
+               retval = target_write_buffer(target, download_area->address, 
bytes_to_write, buffer + offset);
+               if (retval != ERROR_OK) {
+                       retval = ERROR_FLASH_OPERATION_FAILED;
+                       goto end;
+               }
+
+               init_reg_param(&reg_params[3], "r1", 32, PARAM_IN_OUT);
+               buf_set_u32(reg_params[3].value, 0, 32, offset);
+               init_reg_param(&reg_params[4], "r2", 32, PARAM_IN_OUT);
+               buf_set_u32(reg_params[4].value, 0, 32, download_area->address);
+               init_reg_param(&reg_params[5], "r3", 32, PARAM_IN_OUT);
+               buf_set_u32(reg_params[5].value, 0, 32, bytes_to_write);
+
+               api_status = lpc55xx_api_call(bank, API_FLASH_PROGRAM, 
ARRAY_SIZE(reg_params), reg_params);
+               if (api_status != 0) {
+                       LOG_ERROR("Error writing flash");
+                       retval = ERROR_FLASH_OPERATION_FAILED;
+                       goto end;
+               }
+
+               count -= bytes_to_write;
+               offset += bytes_to_write;
+       }
+
+end:
+       target_free_working_area(target, download_area);
+
+       return retval;
+}
+
+
+static int lpc55xx_probe(struct flash_bank *bank)
+{
+       struct lpc55xx_flash_bank *lpc55xx_info = bank->driver_priv;
+       uint32_t device_id, sector_size, ver, base;
+       struct target *target = bank->target;
+       struct reg_param reg_params[3];
+       uint32_t last_sector_size;
+       int api_status;
+
+       target_read_u32(target, LPC55XX_DEVICEID_REG, &device_id);
+       LOG_DEBUG("Device ID: 0x%8.8" PRIx32, device_id);
+       target_read_u32(target, LPC55XX_DIEID_REG, &ver);
+       LOG_DEBUG("Chip revision ID and Number (DIEID): 0x%8.8" PRIx32, ver);
+
+       if (lpc55xx_info->flash_api_table == 0) {
+               uint32_t rom_api_table;
+
+               if ((device_id & 0xff0ffff0) == 0x500a11a0) { /* LPC553x */
+                       rom_api_table = 0x1302fc00;
+               /* the datasheet has table address but no information about
+                * device ID */
+               /*} else if (device_id == XXXX) */ /* LPC55S6x/LPC55S2x/LPC552x 
*/ /*{
+                       rom_api_table = 0x130010f0;*/
+               } else {
+                       LOG_ERROR("Unknown LPC55xx variant and ROM API table 
address not specified");
+                       return ERROR_FAIL;
+               }
+
+               target_read_u32(target, rom_api_table + 4, &ver);
+               LOG_DEBUG("ROM API version: 0x%8.8" PRIx32, ver);
+               target_read_u32(target, rom_api_table + 0x10, 
&lpc55xx_info->flash_api_table);
+       }
+
+       target_read_u32(target, lpc55xx_info->flash_api_table, &ver);
+       LOG_DEBUG("Flash API version: 0x%8.8" PRIx32, ver);
+
+       api_status = lpc55xx_api_call(bank, API_FLASH_INIT, 
ARRAY_SIZE(reg_params), reg_params);
+       if (api_status != 0) {
+               LOG_ERROR("flash_init() failed");
+               return ERROR_FLASH_OPERATION_FAILED;
+       }
+
+       target_read_u32(target, lpc55xx_info->wa->address + 4, &base);
+       if (base != bank->base) {
+               LOG_ERROR("Flash bank address mismatch: API reported 0x%8.8" 
PRIx32, base);
+               return ERROR_FAIL;
+       }
+
+       target_read_u32(target, lpc55xx_info->wa->address + 8, &bank->size);
+       target_read_u32(target, lpc55xx_info->wa->address + 0x14, &sector_size);
+
+       free(bank->sectors);
+
+       bank->num_sectors = bank->size / sector_size;
+       last_sector_size = bank->size % sector_size;
+       if (last_sector_size != 0)
+               bank->num_sectors++;
+       bank->sectors = alloc_block_array(0, sector_size, bank->num_sectors);
+       if (!bank->sectors)
+               return ERROR_FAIL;
+       if (last_sector_size != 0)
+               bank->sectors[bank->num_sectors - 1].size = last_sector_size;
+
+       target_read_u32(target, lpc55xx_info->wa->address + 0x10, 
&bank->write_start_alignment);
+       bank->write_end_alignment = bank->write_start_alignment;
+
+       lpc55xx_info->probed = true;
+
+       return ERROR_OK;
+}
+
+static int lpc55xx_auto_probe(struct flash_bank *bank)
+{
+       struct lpc55xx_flash_bank *lpc55xx_info = bank->driver_priv;
+       if (lpc55xx_info->probed)
+               return ERROR_OK;
+       return lpc55xx_probe(bank);
+}
+
+const struct flash_driver lpc55xx_flash = {
+       .name = "lpc55xx",
+       .flash_bank_command = lpc55xx_flash_bank_command,
+       .erase = lpc55xx_erase,
+       .write = lpc55xx_write,
+       .read = default_flash_read,
+       .probe = lpc55xx_probe,
+       .auto_probe = lpc55xx_auto_probe,
+       .erase_check = lpc55xx_erase_check,
+       .free_driver_priv = default_flash_free_driver_priv,
+};
diff --git a/tcl/target/lpc55xx.cfg b/tcl/target/lpc55xx.cfg
new file mode 100644
index 0000000000..829f2081aa
--- /dev/null
+++ b/tcl/target/lpc55xx.cfg
@@ -0,0 +1,68 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+source [find target/swj-dp.tcl]
+
+adapter speed 1000
+
+if { [info exists CHIPNAME] } {
+       set _CHIPNAME $CHIPNAME
+} else {
+       set _CHIPNAME lpc55xx
+}
+
+if { [info exists CPUTAPID] } {
+       set _CPUTAPID $CPUTAPID
+} else {
+       # Cortex-M33 SWD DPIDR
+       set _CPUTAPID 0x6ba02477
+}
+
+swj_newdap $_CHIPNAME cpu -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id 
$_CPUTAPID
+dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu
+
+set _TARGETNAME $_CHIPNAME.cpu
+target create $_TARGETNAME cortex_m -endian little -dap $_CHIPNAME.dap
+
+# Reserved RAM regions for ROM functions
+# 0x14000000 -- 0x14003fff
+# 0x30001000 -- 0x30007fff
+#
+# Use 16 KiB of SRAM2 (on LPC553x) for working area
+if { [info exists WORKAREASIZE] } {
+       set _WORKAREASIZE $WORKAREASIZE
+} else {
+       set _WORKAREASIZE 0x4000
+}
+$_TARGETNAME configure -work-area-phys 0x20008000 \
+       -work-area-size $_WORKAREASIZE -work-area-backup 0
+
+if { [info exists FLASH_API_ADDRESS] } {
+       set _FLASH_API_ADDRESS $FLASH_API_ADDRESS
+} else {
+       set _FLASH_API_ADDRESS 0
+}
+
+# Using SRST is not recommended as that also resets the debug domain
+if {![using_hla]} {
+       cortex_m reset_config sysresetreq
+}
+
+proc init_reset { mode } {
+       if { $mode ne "run" } {
+               halt
+               wp 0x50000040 4 r
+               resume
+       }
+}
+$_TARGETNAME configure -event reset-end { catch { rwp 0x50000040 } }
+
+# Run this to initiate a debug session with a target that doesn't have
+# valid firmware on flash
+proc lpc55xx_start_debug_session { } {
+       global _CHIPNAME
+       $_CHIPNAME.dap apreg 2 0 0x21
+       $_CHIPNAME.dap apreg 2 4 7
+       $_CHIPNAME.cpu arp_examine
+}
+
+flash bank $_TARGETNAME.flash lpc55xx 0x00000000 0 0 0 0 $_FLASH_API_ADDRESS

-- 

Reply via email to