src/flash/nor: flash driver for RSL10
authorToms Stūrmanis <toms.sturmanis@gmail.com>
Thu, 11 Aug 2022 17:22:09 +0000 (20:22 +0300)
committerAntonio Borneo <borneo.antonio@gmail.com>
Tue, 13 Sep 2022 22:07:43 +0000 (22:07 +0000)
Add new flash driver for internal flash of onsemi RSL10 device.

Valgrind-clean. Clang AddressSanitizer shows no errors.

Signed-off-by: Toms Stūrmanis <toms.sturmanis@gmail.com>
Change-Id: I8030542cb9805e94f56d7a69404cef5d88d6dd5a
Reviewed-on: https://review.openocd.org/c/openocd/+/7115
Tested-by: jenkins
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
contrib/loaders/flash/rsl10/Makefile [new file with mode: 0644]
contrib/loaders/flash/rsl10/rom_launcher.S [new file with mode: 0644]
contrib/loaders/flash/rsl10/rom_launcher.inc [new file with mode: 0644]
doc/openocd.texi
src/flash/nor/Makefile.am
src/flash/nor/drivers.c
src/flash/nor/rsl10.c [new file with mode: 0644]
tcl/target/rsl10.cfg [new file with mode: 0644]

diff --git a/contrib/loaders/flash/rsl10/Makefile b/contrib/loaders/flash/rsl10/Makefile
new file mode 100644 (file)
index 0000000..6e99bcc
--- /dev/null
@@ -0,0 +1,30 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+BIN2C = ../../../../src/helper/bin2char.sh
+
+CROSS_COMPILE ?= arm-none-eabi-
+
+CC=$(CROSS_COMPILE)gcc
+OBJCOPY=$(CROSS_COMPILE)objcopy
+OBJDUMP=$(CROSS_COMPILE)objdump
+
+CFLAGS = -static -nostartfiles -mlittle-endian -Wa,-EL
+
+all: rom_launcher.inc
+
+.PHONY: clean
+
+%.elf: %.S
+       $(CC) $(CFLAGS) $< -o $@
+
+%.lst: %.elf
+       $(OBJDUMP) -S $< > $@
+
+%.bin: %.elf
+       $(OBJCOPY) -Obinary $< $@
+
+%.inc: %.bin
+       $(BIN2C) < $< > $@
+
+clean:
+       -rm -f *.elf *.lst *.bin *.inc
diff --git a/contrib/loaders/flash/rsl10/rom_launcher.S b/contrib/loaders/flash/rsl10/rom_launcher.S
new file mode 100644 (file)
index 0000000..70f000e
--- /dev/null
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/***************************************************************************
+ *   Copyright (C) 2022 by Toms Stūrmanis                                  *
+ *   toms.sturmanis@gmail.com                                              *
+ ***************************************************************************/
+
+       .text
+       .syntax unified
+       .cpu cortex-m4
+       .thumb
+    .align 8
+
+/*
+ * Params :
+ * r0-r2 = arguments
+ * r3 = target address in rom
+ */
+
+       .thumb_func
+       .global _start
+_start:
+launch_program_in_rom:
+    // variables are already set, addres to jump is in r3
+    blx r3
+exit:
+       // Wait for OpenOCD
+       bkpt    #0x00
diff --git a/contrib/loaders/flash/rsl10/rom_launcher.inc b/contrib/loaders/flash/rsl10/rom_launcher.inc
new file mode 100644 (file)
index 0000000..795c8e0
--- /dev/null
@@ -0,0 +1,2 @@
+/* Autogenerated with ../../../../src/helper/bin2char.sh */
+0x98,0x47,0x00,0xbe,
index 0df25406ffb886768e1de8c0958a265cc6a7f26c..3e56079fe5a7280f1f7ffb95012d10f88a40dd9b 100644 (file)
@@ -7396,6 +7396,31 @@ flash bank $_FLASHNAME rp2040_flash $_FLASHBASE $_FLASHSIZE 1 32 $_TARGETNAME
 @end example
 @end deffn
 
+@deffn {Flash Driver} {rsl10}
+Supports Onsemi RSL10 microcontroller flash memory.  Uses functions
+stored in ROM to control flash memory interface.
+
+@example
+flash bank $_FLASHNAME rsl10 $_FLASHBASE $_FLASHSIZE 0 0 $_TARGETNAME
+@end example
+
+@deffn {Command} {rsl10 lock} key1 key2 key3 key4
+Writes @var{key1 key2 key3 key4} words to @var{0x81044 0x81048 0x8104c
+0x8050}. Locks debug port by writing @var{0x4C6F634B} to @var{0x81040}.
+
+To unlock use the @command{rsl10 unlock key1 key2 key3 key4} command.
+@end deffn
+
+@deffn {Command} {rsl10 unlock} key1 key2 key3 key4
+Unlocks debug port, by writing @var{key1 key2 key3 key4} words to
+registers through DAP, and clears @var{0x81040} address in flash to 0x1.
+@end deffn
+
+@deffn {Command} {rsl10 mass_erase}
+Erases all unprotected flash sectors.
+@end deffn
+@end deffn
+
 @deffn {Flash Driver} {sim3x}
 All members of the SiM3 microcontroller family from Silicon Laboratories
 include internal flash and use ARM Cortex-M3 cores. It supports both JTAG
index e3afc0ddf167e0b0de10721562885fd5a5173dfa..f04f0d206a76ca2417b934ad55c19c145431b53e 100644 (file)
@@ -56,6 +56,7 @@ NOR_DRIVERS = \
        %D%/psoc6.c \
        %D%/renesas_rpchf.c \
        %D%/rp2040.c \
+       %D%/rsl10.c \
        %D%/sfdp.c \
        %D%/sh_qspi.c \
        %D%/sim3x.c \
index e3fd36ad7184c0e30f5634321b78e84c6996855d..ee54ef2d67587b8bf694c59c95b64a1eceeec2d5 100644 (file)
@@ -78,6 +78,7 @@ extern const struct flash_driver w600_flash;
 extern const struct flash_driver xcf_flash;
 extern const struct flash_driver xmc1xxx_flash;
 extern const struct flash_driver xmc4xxx_flash;
+extern const struct flash_driver rsl10_flash;
 
 /**
  * The list of built-in flash drivers.
@@ -153,6 +154,7 @@ static const struct flash_driver * const flash_drivers[] = {
        &xmc1xxx_flash,
        &xmc4xxx_flash,
        &w600_flash,
+       &rsl10_flash,
        NULL,
 };
 
diff --git a/src/flash/nor/rsl10.c b/src/flash/nor/rsl10.c
new file mode 100644 (file)
index 0000000..164701f
--- /dev/null
@@ -0,0 +1,841 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/***************************************************************************
+ *   Copyright (C) 2022 by Toms Stūrmanis                                  *
+ *   toms.sturmanis@gmail.com                                              *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdint.h>
+
+#include <helper/binarybuffer.h>
+#include <helper/bits.h>
+
+#include <target/algorithm.h>
+#include <target/arm_adi_v5.h>
+#include <target/armv7m.h>
+#include <target/cortex_m.h>
+
+#include "imp.h"
+
+#define RSL10_FLASH_ADDRESS_MAIN              0x00100000
+#define RSL10_FLASH_ADDRESS_NVR1              0x00080000
+#define RSL10_FLASH_ADDRESS_NVR2              0x00080800
+#define RSL10_FLASH_ADDRESS_NVR3              0x00081000
+#define RSL10_FLASH_ADDRESS_NVR4              0x00081800
+#define RSL10_FLASH_ADDRESS_LOCK_INFO_SETTING 0x00081040
+
+#define RSL10_REG_ID 0x1FFFFFFC
+
+#define RSL10_FLASH_REG_MAIN_WRITE_UNLOCK 0x40000504
+#define RSL10_FLASH_REG_MAIN_CTRL         0x40000508
+#define RSL10_FLASH_REG_IF_STATUS         0x40000538
+#define RSL10_FLASH_REG_NVR_WRITE_UNLOCK  0x40000548
+#define RSL10_FLASH_REG_NVR_CTRL          0x4000054C
+
+#define RSL10_FLASH_REG_DEBUG_UNLOCK_KEY1 0x400000F0
+#define RSL10_FLASH_REG_DEBUG_UNLOCK_KEY2 0x400000F4
+#define RSL10_FLASH_REG_DEBUG_UNLOCK_KEY3 0x400000F8
+#define RSL10_FLASH_REG_DEBUG_UNLOCK_KEY4 0x400000FC
+
+#define RSL10_NVR3_USER_KEY_OFFSET 0x40
+
+#define RSL10_ID             0x09010106
+#define RSL10_FLASH_KEY_MAIN 0xDBC8264E
+#define RSL10_FLASH_KEY_NVR  0x71B371F5
+#define RSL10_KEY_DEBUG_LOCK 0x4C6F634B
+
+#define RSL10_FLASH_REG_MAIN_CTRL_LOW_W_ENABLE    BIT(0)
+#define RSL10_FLASH_REG_MAIN_CTRL_MIDDLE_W_ENABLE BIT(1)
+#define RSL10_FLASH_REG_MAIN_CTRL_HIGH_W_ENABLE   BIT(2)
+
+#define RSL10_FLASH_REG_NVR_CTRL_NVR1_W_ENABLE BIT(1)
+#define RSL10_FLASH_REG_NVR_CTRL_NVR2_W_ENABLE BIT(2)
+#define RSL10_FLASH_REG_NVR_CTRL_NVR3_W_ENABLE BIT(3)
+
+#define RSL10_FLASH_REG_STATUS_LOW_W_UNLOCKED    BIT(0)
+#define RSL10_FLASH_REG_STATUS_MIDDLE_W_UNLOCKED BIT(1)
+#define RSL10_FLASH_REG_STATUS_HIGH_W_UNLOCKED   BIT(2)
+#define RSL10_FLASH_REG_STATUS_NVR1_W_UNLOCKED   BIT(4)
+#define RSL10_FLASH_REG_STATUS_NVR2_W_UNLOCKED   BIT(5)
+#define RSL10_FLASH_REG_STATUS_NVR3_W_UNLOCKED   BIT(6)
+
+#define RSL10_ROM_CMD_WRITE_WORD_PAIR 0x3C
+#define RSL10_ROM_CMD_WRITE_BUFFER    0x40
+#define RSL10_ROM_CMD_ERASE_SECTOR    0x44
+#define RSL10_ROM_CMD_ERASE_ALL       0x48
+
+#define FLASH_SECTOR_SIZE 0x2000
+
+#define RSL10_ROM_CMD_WRITE_BUFFER_MAX_SIZE FLASH_SECTOR_SIZE
+
+#define ALGO_STACK_POINTER_ADDR  0x20002000
+
+/* Used to launch flash related functions from ROM
+ * Params :
+ * r0-r2 = arguments
+ * r3 = target address in rom
+ */
+static const uint8_t rsl10_rom_launcher_code[] = {
+#include "../../../contrib/loaders/flash/rsl10/rom_launcher.inc"
+};
+
+enum rsl10_flash_status {
+       RSL10_FLASH_ERR_NONE              = 0x0,
+       RSL10_FLASH_ERR_GENERAL_FAILURE   = 0x1,
+       RSL10_FLASH_ERR_WRITE_NOT_ENABLED = 0x2,
+       RSL10_FLASH_ERR_BAD_ADDRESS       = 0x3,
+       RSL10_FLASH_ERR_ERASE_FAILED      = 0x4,
+       RSL10_FLASH_ERR_BAD_LENGTH        = 0x5,
+       RSL10_FLASH_ERR_INACCESSIBLE      = 0x6,
+       RSL10_FLASH_ERR_COPIER_BUSY       = 0x7,
+       RSL10_FLASH_ERR_PROG_FAILED       = 0x8,
+       RSL10_FLASH_MAX_ERR_CODES /* must be the last one */
+};
+
+static const char *const rsl10_error_list[] = {
+       [RSL10_FLASH_ERR_GENERAL_FAILURE]   = "general failure",
+       [RSL10_FLASH_ERR_WRITE_NOT_ENABLED] = "write not enabled, protected",
+       [RSL10_FLASH_ERR_BAD_ADDRESS]       = "bad address",
+       [RSL10_FLASH_ERR_ERASE_FAILED]      = "erase failed",
+       [RSL10_FLASH_ERR_BAD_LENGTH]        = "bad length",
+       [RSL10_FLASH_ERR_INACCESSIBLE]      = "inaccessible: not powered up, or isolated",
+       [RSL10_FLASH_ERR_COPIER_BUSY]       = "copier busy",
+       [RSL10_FLASH_ERR_PROG_FAILED]       = "prog failed",
+};
+
+const char *rsl10_error(enum rsl10_flash_status x)
+{
+       if (x >= RSL10_FLASH_MAX_ERR_CODES || !rsl10_error_list[x])
+               return "unknown";
+       return rsl10_error_list[x];
+}
+
+const struct flash_driver rsl10_flash;
+
+struct rsl10_info {
+       unsigned int refcount;
+
+       struct rsl10_bank {
+               struct rsl10_info *chip;
+               bool probed;
+       } bank[5];
+       struct target *target;
+
+       unsigned int flash_size_kb;
+};
+
+static bool rsl10_bank_is_probed(const struct flash_bank *bank)
+{
+       struct rsl10_bank *nbank = bank->driver_priv;
+       assert(nbank);
+       return nbank->probed;
+}
+
+static int rsl10_probe(struct flash_bank *bank);
+
+static int rsl10_get_probed_chip_if_halted(struct flash_bank *bank, struct rsl10_info **chip)
+{
+       if (bank->target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       struct rsl10_bank *nbank = bank->driver_priv;
+       *chip                    = nbank->chip;
+
+       if (rsl10_bank_is_probed(bank))
+               return ERROR_OK;
+
+       return rsl10_probe(bank);
+}
+
+static int rsl10_protect_check(struct flash_bank *bank)
+{
+       struct rsl10_bank *nbank = bank->driver_priv;
+       struct rsl10_info *chip  = nbank->chip;
+
+       assert(chip);
+
+       uint32_t status;
+
+       int retval = target_read_u32(bank->target, RSL10_FLASH_REG_IF_STATUS, &status);
+       if (retval != ERROR_OK)
+               return retval;
+
+       if (bank->base == RSL10_FLASH_ADDRESS_MAIN) {
+               for (unsigned int i = 0; i < bank->num_prot_blocks; i++)
+                       bank->prot_blocks[i].is_protected = (status & (1 << i)) ? 0 : 1;
+
+       } else {
+               uint32_t test_bit = 0;
+               switch (bank->base) {
+               case RSL10_FLASH_ADDRESS_NVR1:
+                       test_bit = RSL10_FLASH_REG_STATUS_NVR1_W_UNLOCKED;
+                       break;
+               case RSL10_FLASH_ADDRESS_NVR2:
+                       test_bit = RSL10_FLASH_REG_STATUS_NVR2_W_UNLOCKED;
+                       break;
+               case RSL10_FLASH_ADDRESS_NVR3:
+                       test_bit = RSL10_FLASH_REG_STATUS_NVR3_W_UNLOCKED;
+                       break;
+               default:
+                       break;
+               }
+
+               bank->sectors[0].is_protected = (status & test_bit) ? 0 : 1;
+       }
+       return ERROR_OK;
+}
+
+static int rsl10_protect(struct flash_bank *bank, int set, unsigned int first, unsigned int last)
+{
+
+       struct rsl10_info *chip;
+       int retval = rsl10_get_probed_chip_if_halted(bank, &chip);
+       if (retval != ERROR_OK)
+               return retval;
+
+       if (bank->base == RSL10_FLASH_ADDRESS_MAIN) {
+               uint32_t status;
+               retval = target_read_u32(bank->target, RSL10_FLASH_REG_MAIN_CTRL, &status);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               for (unsigned int i = first; i <= last; i++) {
+                       if (set)
+                               status &= ~(1 << i);
+                       else
+                               status |= (1 << i);
+               }
+
+               retval = target_write_u32(bank->target, RSL10_FLASH_REG_MAIN_CTRL, status);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               retval = target_write_u32(bank->target, RSL10_FLASH_REG_MAIN_WRITE_UNLOCK, RSL10_FLASH_KEY_MAIN);
+               if (retval != ERROR_OK)
+                       return retval;
+       } else {
+               uint32_t bit = 0;
+               switch (bank->base) {
+               case RSL10_FLASH_ADDRESS_NVR1:
+                       bit = RSL10_FLASH_REG_NVR_CTRL_NVR1_W_ENABLE;
+                       break;
+               case RSL10_FLASH_ADDRESS_NVR2:
+                       bit = RSL10_FLASH_REG_NVR_CTRL_NVR2_W_ENABLE;
+                       break;
+               case RSL10_FLASH_ADDRESS_NVR3:
+                       bit = RSL10_FLASH_REG_NVR_CTRL_NVR3_W_ENABLE;
+                       break;
+               default:
+                       break;
+               }
+
+               uint32_t status;
+               retval = target_read_u32(bank->target, RSL10_FLASH_REG_NVR_CTRL, &status);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               if (set)
+                       status &= ~bit;
+               else
+                       status |= bit;
+
+               retval = target_write_u32(bank->target, RSL10_FLASH_REG_NVR_CTRL, status);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               retval = target_write_u32(bank->target, RSL10_FLASH_REG_NVR_WRITE_UNLOCK, RSL10_FLASH_KEY_NVR);
+               if (retval != ERROR_OK)
+                       return retval;
+       }
+
+       return ERROR_OK;
+}
+
+static int rsl10_check_device(struct flash_bank *bank)
+{
+       uint32_t configid;
+       int retval = target_read_u32(bank->target, RSL10_REG_ID, &configid);
+       if (retval != ERROR_OK)
+               return retval;
+
+       if (configid != RSL10_ID) {
+               LOG_ERROR("This is not supported (RSL10) device, use other flash driver!!!");
+               return ERROR_TARGET_INVALID;
+       }
+       return ERROR_OK;
+}
+
+static int rsl10_probe(struct flash_bank *bank)
+{
+       struct rsl10_bank *nbank = bank->driver_priv;
+       struct rsl10_info *chip  = nbank->chip;
+
+       int retval = rsl10_check_device(bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       unsigned int bank_id;
+       unsigned int num_prot_blocks = 0;
+       switch (bank->base) {
+       case RSL10_FLASH_ADDRESS_MAIN:
+               bank_id         = 0;
+               num_prot_blocks = 3;
+               break;
+       case RSL10_FLASH_ADDRESS_NVR1:
+               bank_id = 1;
+               break;
+       case RSL10_FLASH_ADDRESS_NVR2:
+               bank_id = 2;
+               break;
+       case RSL10_FLASH_ADDRESS_NVR3:
+               bank_id = 3;
+               break;
+       default:
+               return ERROR_FAIL;
+       }
+
+       uint32_t flash_page_size = 2048;
+
+       bank->write_start_alignment = 8;
+       bank->write_end_alignment   = 8;
+
+       bank->num_sectors   = bank->size / flash_page_size;
+       chip->flash_size_kb = bank->size / 1024;
+
+       free(bank->sectors);
+       bank->sectors = NULL;
+
+       bank->sectors = alloc_block_array(0, flash_page_size, bank->num_sectors);
+       if (!bank->sectors)
+               return ERROR_FAIL;
+
+       free(bank->prot_blocks);
+       bank->prot_blocks = NULL;
+
+       if (num_prot_blocks > 0) {
+               bank->num_prot_blocks = num_prot_blocks;
+               bank->prot_blocks     = alloc_block_array(0, bank->num_sectors / 3 * flash_page_size, bank->num_prot_blocks);
+               if (!bank->prot_blocks)
+                       return ERROR_FAIL;
+       }
+
+       chip->bank[bank_id].probed = true;
+       return ERROR_OK;
+}
+
+static int rsl10_auto_probe(struct flash_bank *bank)
+{
+       if (rsl10_bank_is_probed(bank))
+               return ERROR_OK;
+
+       return rsl10_probe(bank);
+}
+
+static int rsl10_ll_flash_erase(struct rsl10_info *chip, uint32_t address)
+{
+       struct target *target = chip->target;
+       struct working_area *write_algorithm;
+
+       LOG_DEBUG("erasing buffer flash address=0x%" PRIx32, address);
+
+       int retval = target_alloc_working_area(target, sizeof(rsl10_rom_launcher_code), &write_algorithm);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Current working area 0x%x is too small! Increase working area size!", target->working_area_size);
+               return ERROR_FAIL;
+       }
+
+       retval =
+               target_write_buffer(target, write_algorithm->address, sizeof(rsl10_rom_launcher_code), rsl10_rom_launcher_code);
+       if (retval != ERROR_OK)
+               goto free_algorithm;
+
+       struct reg_param reg_params[3];
+       struct armv7m_algorithm armv7m_info;
+       armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
+       armv7m_info.core_mode    = ARM_MODE_THREAD;
+
+       init_reg_param(&reg_params[0], "r0", 32, PARAM_IN_OUT); /* address */
+       init_reg_param(&reg_params[1], "r3", 32, PARAM_OUT);    /* cmd */
+       init_reg_param(&reg_params[2], "sp", 32, PARAM_OUT);    /* stack pointer */
+
+       buf_set_u32(reg_params[0].value, 0, 32, address);
+       uint32_t cmd;
+       retval = target_read_u32(target, RSL10_ROM_CMD_ERASE_SECTOR, &cmd);
+       if (retval != ERROR_OK)
+               goto free_reg_params;
+       buf_set_u32(reg_params[1].value, 0, 32, cmd);
+       buf_set_u32(reg_params[2].value, 0, 32, ALGO_STACK_POINTER_ADDR);
+
+       retval = target_run_algorithm(
+               target, 0, NULL, ARRAY_SIZE(reg_params), reg_params, write_algorithm->address,
+               write_algorithm->address + sizeof(rsl10_rom_launcher_code) - 2, 1000, &armv7m_info
+       );
+       if (retval != ERROR_OK)
+               goto free_reg_params;
+
+       int algo_ret = buf_get_u32(reg_params[0].value, 0, 32);
+       if (algo_ret != RSL10_FLASH_ERR_NONE) {
+               LOG_ERROR("RSL10 ERASE ERROR: '%s' (%d)", rsl10_error(algo_ret), algo_ret);
+               retval = ERROR_FLASH_SECTOR_NOT_ERASED;
+       }
+
+free_reg_params:
+       for (unsigned int i = 0; i < ARRAY_SIZE(reg_params); i++)
+               destroy_reg_param(&reg_params[i]);
+
+free_algorithm:
+       target_free_working_area(target, write_algorithm);
+       return retval;
+}
+
+static int rsl10_ll_flash_write(struct rsl10_info *chip, uint32_t address, const uint8_t *buffer, uint32_t bytes)
+{
+       struct target *target = chip->target;
+       struct working_area *write_algorithm;
+
+       if (bytes == 8) {
+               uint32_t data;
+               data = buf_get_u32(buffer, 0, 32);
+               LOG_DEBUG("Writing 0x%" PRIx32 " to flash address=0x%" PRIx32 " bytes=0x%" PRIx32, data, address, bytes);
+       } else
+               LOG_DEBUG("Writing buffer to flash address=0x%" PRIx32 " bytes=0x%" PRIx32, address, bytes);
+
+       /* allocate working area with flash programming code */
+       int retval = target_alloc_working_area(target, sizeof(rsl10_rom_launcher_code), &write_algorithm);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Current working area 0x%x is too small! Increase working area size!", target->working_area_size);
+               return ERROR_FAIL;
+       }
+
+       retval =
+               target_write_buffer(target, write_algorithm->address, sizeof(rsl10_rom_launcher_code), rsl10_rom_launcher_code);
+       if (retval != ERROR_OK)
+               goto free_algorithm;
+
+       /* memory buffer, rounded down, to be multiple of 8 */
+       uint32_t buffer_avail = target_get_working_area_avail(target) & ~7;
+       uint32_t buffer_size  = MIN(RSL10_ROM_CMD_WRITE_BUFFER_MAX_SIZE, buffer_avail);
+       struct working_area *source;
+       retval = target_alloc_working_area(target, buffer_size, &source);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Current working area 0x%x is too small! Increase working area size!", target->working_area_size);
+               goto free_algorithm;
+       }
+
+       struct reg_param reg_params[5];
+       struct armv7m_algorithm armv7m_info;
+       armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
+       armv7m_info.core_mode    = ARM_MODE_THREAD;
+
+       init_reg_param(&reg_params[0], "r0", 32, PARAM_IN_OUT); /* start addr, return value */
+       init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);    /* length */
+       init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT);    /* data */
+       init_reg_param(&reg_params[3], "r3", 32, PARAM_OUT);    /* cmd */
+       init_reg_param(&reg_params[4], "sp", 32, PARAM_OUT);    /* stack pointer */
+       buf_set_u32(reg_params[4].value, 0, 32, ALGO_STACK_POINTER_ADDR);
+
+       uint32_t cmd             = 0;
+       uint32_t sent_bytes      = 0;
+       uint32_t write_address   = 0;
+       uint32_t bytes_to_send   = 0;
+       uint32_t remaining_bytes = 0;
+
+       retval = target_read_u32(target, RSL10_ROM_CMD_WRITE_BUFFER, &cmd);
+       if (retval != ERROR_OK)
+               goto free_everything;
+
+       while (sent_bytes < bytes) {
+               remaining_bytes = bytes - sent_bytes;
+               bytes_to_send   = remaining_bytes >= buffer_size ? buffer_size : remaining_bytes;
+
+               retval = target_write_buffer(target, source->address, bytes_to_send, buffer + sent_bytes);
+               if (retval != ERROR_OK)
+                       goto free_everything;
+
+               write_address = address + sent_bytes;
+
+               LOG_DEBUG(
+                       "write_address: 0x%" PRIx32 ", words: 0x%" PRIx32 ", source: 0x%" PRIx64 ", cmd: 0x%" PRIx32, write_address,
+                       bytes_to_send / 4, source->address, cmd
+               );
+               buf_set_u32(reg_params[0].value, 0, 32, write_address);
+               buf_set_u32(reg_params[1].value, 0, 32, bytes_to_send / 4);
+               buf_set_u32(reg_params[2].value, 0, 32, source->address);
+               buf_set_u32(reg_params[3].value, 0, 32, cmd);
+
+               retval = target_run_algorithm(
+                       target, 0, NULL, ARRAY_SIZE(reg_params), reg_params, write_algorithm->address,
+                       write_algorithm->address + sizeof(rsl10_rom_launcher_code) - 2, 1000, &armv7m_info
+               );
+               if (retval != ERROR_OK)
+                       goto free_everything;
+
+               int algo_ret = buf_get_u32(reg_params[0].value, 0, 32);
+               if (algo_ret != RSL10_FLASH_ERR_NONE) {
+                       LOG_ERROR("RSL10 WRITE ERROR: '%s' (%d)", rsl10_error(algo_ret), algo_ret);
+                       retval = ERROR_FLASH_OPERATION_FAILED;
+                       goto free_everything;
+               }
+
+               sent_bytes += bytes_to_send;
+       }
+
+free_everything:
+       target_free_working_area(target, source);
+
+       for (unsigned int i = 0; i < ARRAY_SIZE(reg_params); i++)
+               destroy_reg_param(&reg_params[i]);
+
+free_algorithm:
+       target_free_working_area(target, write_algorithm);
+
+       return retval;
+}
+
+static int rsl10_mass_erase(struct target *target)
+{
+       struct working_area *write_algorithm;
+
+       int retval = target_alloc_working_area(target, sizeof(rsl10_rom_launcher_code), &write_algorithm);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Current working area 0x%x is too small! Increase working area size!", target->working_area_size);
+               return ERROR_FAIL;
+       }
+
+       retval =
+               target_write_buffer(target, write_algorithm->address, sizeof(rsl10_rom_launcher_code), rsl10_rom_launcher_code);
+       if (retval != ERROR_OK)
+               goto free_algorithm;
+
+       struct reg_param reg_params[3];
+       struct armv7m_algorithm armv7m_info;
+       armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
+       armv7m_info.core_mode    = ARM_MODE_THREAD;
+
+       init_reg_param(&reg_params[0], "r0", 32, PARAM_IN_OUT); /* return value */
+       init_reg_param(&reg_params[1], "r3", 32, PARAM_OUT);    /* cmd */
+       init_reg_param(&reg_params[2], "sp", 32, PARAM_OUT);    /* stack pointer */
+
+       uint32_t cmd;
+       retval = target_read_u32(target, RSL10_ROM_CMD_ERASE_ALL, &cmd);
+       if (retval != ERROR_OK)
+               goto free_reg_params;
+       buf_set_u32(reg_params[1].value, 0, 32, cmd);
+       buf_set_u32(reg_params[2].value, 0, 32, ALGO_STACK_POINTER_ADDR);
+
+       retval = target_run_algorithm(
+               target, 0, NULL, ARRAY_SIZE(reg_params), reg_params, write_algorithm->address,
+               write_algorithm->address + sizeof(rsl10_rom_launcher_code) - 2, 1000, &armv7m_info
+       );
+       if (retval != ERROR_OK)
+               goto free_reg_params;
+
+       int algo_ret = buf_get_u32(reg_params[0].value, 0, 32);
+       if (algo_ret != RSL10_FLASH_ERR_NONE) {
+               LOG_ERROR("RSL10 MASS ERASE ERROR: '%s' (%d)", rsl10_error(algo_ret), algo_ret);
+               retval = ERROR_FLASH_OPERATION_FAILED;
+       }
+
+free_reg_params:
+       for (unsigned int i = 0; i < ARRAY_SIZE(reg_params); i++)
+               destroy_reg_param(&reg_params[i]);
+
+free_algorithm:
+       target_free_working_area(target, write_algorithm);
+       return retval;
+}
+
+static int rsl10_write(struct flash_bank *bank, const uint8_t *buffer, uint32_t offset, uint32_t count)
+{
+       struct rsl10_info *chip;
+
+       int retval = rsl10_get_probed_chip_if_halted(bank, &chip);
+       if (retval != ERROR_OK)
+               return retval;
+
+       return rsl10_ll_flash_write(chip, bank->base + offset, buffer, count);
+}
+
+static int rsl10_erase(struct flash_bank *bank, unsigned int first, unsigned int last)
+{
+       LOG_INFO("erase bank: %x, %x", first, last);
+       int retval;
+       struct rsl10_info *chip;
+
+       retval = rsl10_get_probed_chip_if_halted(bank, &chip);
+       if (retval != ERROR_OK)
+               return retval;
+
+       for (unsigned int i = first; i <= last; i++) {
+               retval = rsl10_ll_flash_erase(chip, bank->base + i * 0x800);
+               if (retval != ERROR_OK)
+                       return retval;
+       }
+
+       return ERROR_OK;
+}
+
+static void rsl10_free_driver_priv(struct flash_bank *bank)
+{
+       struct rsl10_bank *nbank = bank->driver_priv;
+       struct rsl10_info *chip  = nbank->chip;
+       if (!chip)
+               return;
+
+       chip->refcount--;
+       if (chip->refcount == 0) {
+               free(chip);
+               bank->driver_priv = NULL;
+       }
+}
+
+static struct rsl10_info *rsl10_get_chip(struct target *target)
+{
+       struct flash_bank *bank_iter;
+
+       /* iterate over rsl10 banks of same target */
+       for (bank_iter = flash_bank_list(); bank_iter; bank_iter = bank_iter->next) {
+               if (bank_iter->driver != &rsl10_flash)
+                       continue;
+
+               if (bank_iter->target != target)
+                       continue;
+
+               struct rsl10_bank *nbank = bank_iter->driver_priv;
+               if (!nbank)
+                       continue;
+
+               if (nbank->chip)
+                       return nbank->chip;
+       }
+       return NULL;
+}
+
+FLASH_BANK_COMMAND_HANDLER(rsl10_flash_bank_command)
+{
+       struct rsl10_info *chip  = NULL;
+       struct rsl10_bank *nbank = NULL;
+       LOG_INFO("Creating flash @ " TARGET_ADDR_FMT, bank->base);
+
+       switch (bank->base) {
+       case RSL10_FLASH_ADDRESS_MAIN:
+       case RSL10_FLASH_ADDRESS_NVR1:
+       case RSL10_FLASH_ADDRESS_NVR2:
+       case RSL10_FLASH_ADDRESS_NVR3:
+       case RSL10_FLASH_ADDRESS_NVR4:
+               break;
+       default:
+               LOG_ERROR("Invalid bank address " TARGET_ADDR_FMT, bank->base);
+               return ERROR_FAIL;
+       }
+
+       chip = rsl10_get_chip(bank->target);
+       if (!chip) {
+               chip = calloc(1, sizeof(*chip));
+               if (!chip)
+                       return ERROR_FAIL;
+
+               chip->target = bank->target;
+       }
+
+       switch (bank->base) {
+       case RSL10_FLASH_ADDRESS_MAIN:
+               nbank = &chip->bank[0];
+               break;
+       case RSL10_FLASH_ADDRESS_NVR1:
+               nbank = &chip->bank[1];
+               break;
+       case RSL10_FLASH_ADDRESS_NVR2:
+               nbank = &chip->bank[2];
+               break;
+       case RSL10_FLASH_ADDRESS_NVR3:
+               nbank = &chip->bank[3];
+               break;
+       case RSL10_FLASH_ADDRESS_NVR4:
+               nbank = &chip->bank[4];
+               break;
+       }
+       assert(nbank);
+
+       chip->refcount++;
+       nbank->chip       = chip;
+       nbank->probed     = false;
+       bank->driver_priv = nbank;
+
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(rsl10_lock_command)
+{
+       struct target *target = get_current_target(CMD_CTX);
+
+       if (CMD_ARGC != 4)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       struct flash_bank *bank;
+       int retval = get_flash_bank_by_addr(target, RSL10_FLASH_ADDRESS_NVR3, true, &bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       LOG_INFO("Keys used: %s %s %s %s", CMD_ARGV[0], CMD_ARGV[1], CMD_ARGV[2], CMD_ARGV[3]);
+
+       uint32_t user_key[4];
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], user_key[0]);
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], user_key[1]);
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], user_key[2]);
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[3], user_key[3]);
+
+       uint8_t write_buffer[6 * 4];
+       target_buffer_set_u32(target, write_buffer, RSL10_KEY_DEBUG_LOCK);
+       target_buffer_set_u32_array(target, &write_buffer[4], 4, user_key);
+       /* pad the end to 64-bit word boundary */
+       memset(&write_buffer[5 * 4], bank->default_padded_value, 4);
+
+       retval = rsl10_erase(bank, 0, 0);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = rsl10_write(bank, write_buffer, RSL10_NVR3_USER_KEY_OFFSET, sizeof(write_buffer));
+       if (retval != ERROR_OK) {
+               /* erase sector, if write fails, otherwise it can lock debug with wrong keys */
+               return rsl10_erase(bank, 0, 0);
+       }
+
+       command_print(
+               CMD, "****** WARNING ******\n"
+                        "rsl10 device has been successfully prepared to lock.\n"
+                        "Debug port is locked after restart.\n"
+                        "Unlock with 'rsl10_unlock key0 key1 key2 key3'\n"
+                        "****** ....... ******\n"
+       );
+
+       return rsl10_protect(bank, true, 0, 0);
+}
+
+COMMAND_HANDLER(rsl10_unlock_command)
+{
+       if (CMD_ARGC != 4)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       struct target *target            = get_current_target(CMD_CTX);
+       struct cortex_m_common *cortex_m = target_to_cm(target);
+
+       struct adiv5_dap *dap = cortex_m->armv7m.arm.dap;
+       struct adiv5_ap *ap   = dap_get_ap(dap, 0);
+
+       uint32_t user_key[4];
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], user_key[0]);
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], user_key[1]);
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], user_key[2]);
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[3], user_key[3]);
+
+       uint8_t write_buffer1[4 * 4];
+       target_buffer_set_u32_array(target, write_buffer1, 4, user_key);
+       int retval = mem_ap_write_buf(ap, write_buffer1, 4, 4, RSL10_FLASH_REG_DEBUG_UNLOCK_KEY1);
+       if (retval != ERROR_OK) {
+               dap_put_ap(ap);
+               return retval;
+       }
+
+       dap_put_ap(ap);
+
+       uint32_t key;
+       retval = mem_ap_read_atomic_u32(ap, RSL10_FLASH_ADDRESS_LOCK_INFO_SETTING, &key);
+       LOG_INFO("mem read: 0x%08" PRIx32, key);
+
+       if (key == RSL10_KEY_DEBUG_LOCK) {
+               retval = command_run_line(CMD_CTX, "reset init");
+               if (retval != ERROR_OK)
+                       return retval;
+
+               struct flash_bank *bank;
+               retval = get_flash_bank_by_addr(target, RSL10_FLASH_ADDRESS_NVR3, true, &bank);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               retval = rsl10_protect(bank, false, 0, 0);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               uint8_t write_buffer2[4 * 2];
+               target_buffer_set_u32(target, write_buffer2, 0x1);
+               /* pad the end to 64-bit word boundary */
+               memset(&write_buffer2[4], bank->default_padded_value, 4);
+
+               /* let it fail, because sector is not erased, maybe just erase all? */
+               (void)rsl10_write(bank, write_buffer2, RSL10_NVR3_USER_KEY_OFFSET, sizeof(write_buffer2));
+               command_print(CMD, "Debug port is unlocked!");
+       }
+
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(rsl10_mass_erase_command)
+{
+       if (CMD_ARGC)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       struct target *target = get_current_target(CMD_CTX);
+
+       int retval = rsl10_mass_erase(target);
+       if (retval != ERROR_OK)
+               return retval;
+
+       command_print(CMD, "Mass erase was succesfull!");
+       return ERROR_OK;
+}
+
+static const struct command_registration rsl10_exec_command_handlers[] = {
+       {
+               .name    = "lock",
+               .handler = rsl10_lock_command,
+               .mode    = COMMAND_EXEC,
+               .help    = "Lock rsl10 debug, with passed keys",
+               .usage   = "key1 key2 key3 key4",
+       },
+       {
+               .name    = "unlock",
+               .handler = rsl10_unlock_command,
+               .mode    = COMMAND_EXEC,
+               .help    = "Unlock rsl10 debug, with passed keys",
+               .usage   = "key1 key2 key3 key4",
+       },
+       {
+               .name    = "mass_erase",
+               .handler = rsl10_mass_erase_command,
+               .mode    = COMMAND_EXEC,
+               .help    = "Mass erase all unprotected flash areas",
+               .usage   = "",
+       },
+       COMMAND_REGISTRATION_DONE};
+
+static const struct command_registration rsl10_command_handlers[] = {
+       {
+               .name  = "rsl10",
+               .mode  = COMMAND_ANY,
+               .help  = "rsl10 flash command group",
+               .usage = "",
+               .chain = rsl10_exec_command_handlers,
+       },
+       COMMAND_REGISTRATION_DONE};
+
+const struct flash_driver rsl10_flash = {
+       .name               = "rsl10",
+       .commands           = rsl10_command_handlers,
+       .flash_bank_command = rsl10_flash_bank_command,
+       .erase              = rsl10_erase,
+       .protect            = rsl10_protect,
+       .write              = rsl10_write,
+       .read               = default_flash_read,
+       .probe              = rsl10_probe,
+       .auto_probe         = rsl10_auto_probe,
+       .erase_check        = default_flash_blank_check,
+       .protect_check      = rsl10_protect_check,
+       .free_driver_priv   = rsl10_free_driver_priv,
+};
diff --git a/tcl/target/rsl10.cfg b/tcl/target/rsl10.cfg
new file mode 100644 (file)
index 0000000..f4692cc
--- /dev/null
@@ -0,0 +1,70 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+#
+# RSL10: ARM Cortex-M3
+#
+
+source [find target/swj-dp.tcl]
+
+if { [info exists CHIPNAME] } {
+       set _CHIPNAME $CHIPNAME
+} else {
+       set _CHIPNAME rsl10
+}
+
+if { [info exists WORKAREASIZE] } {
+   set _WORKAREASIZE $WORKAREASIZE
+} else {
+   set _WORKAREASIZE 0x8000
+}
+
+if { [info exists CPUTAPID] } {
+       set _CPUTAPID $CPUTAPID
+} else {
+       set _CPUTAPID 0x2ba01477
+}
+
+swj_newdap $_CHIPNAME cpu -expected-id $_CPUTAPID
+dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu
+
+set _TARGETNAME $_CHIPNAME.cpu
+target create $_TARGETNAME cortex_m -dap $_CHIPNAME.dap
+
+$_TARGETNAME configure -work-area-phys 0x200000 -work-area-size $_WORKAREASIZE -work-area-backup 0
+
+# TODO: configure reset
+# reset_config srst_only srst_nogate connect_assert_srst
+
+$_TARGETNAME configure -event examine-fail rsl10_lock_warning
+
+proc rsl10_check_connection {} {
+    set target [target current]
+    set dap [$target cget -dap]
+
+       set IDR [$dap apreg 0 0xfc]
+       if {$IDR != 0x24770011} {
+               echo "Error: Cannot access RSL10 AP, maybe connection problem!"
+               return 1
+       }
+    return 0
+}
+
+proc rsl10_lock_warning {} {
+    if {[rsl10_check_connection]} {return}
+
+    poll off
+    echo "****** WARNING ******"
+    echo "RSL10 device probably has lock engaged."
+    echo "Debug access is denied."
+    echo "Use 'rsl10 unlock key1 key2 key3 key4' to erase and unlock the device."
+    echo "****** ....... ******"
+    echo ""
+}
+
+flash bank $_CHIPNAME.main rsl10 0x00100000 0x60000 0 0 $_TARGETNAME
+flash bank $_CHIPNAME.nvr1 rsl10 0x00080000 0x800 0 0 $_TARGETNAME
+flash bank $_CHIPNAME.nvr2 rsl10 0x00080800 0x800 0 0 $_TARGETNAME
+flash bank $_CHIPNAME.nvr3 rsl10 0x00081000 0x800 0 0 $_TARGETNAME
+
+# TODO: implement flashing for nvr4
+# flash bank $_CHIPNAME.nvr4 rsl10 0x00081800 0x400 0 0 $_TARGETNAME