This is an automated email from Gerrit.

"Benjamin Vernoux <bvern...@gmail.com>" just uploaded a new patch set to 
Gerrit, which you can find at https://review.openocd.org/c/openocd/+/7804

-- gerrit

commit 38c8d789b66c25c455ed4f8be854e4eb308cf065
Author: Benjamin Vernoux <bvern...@gmail.com>
Date:   Thu Aug 31 12:48:00 2023 +0200

    Add support of Zbit cx32l003 target and nor flash loader
    
    Note: Fully tested with Ebyte E220-900T22D which use a LoRa LLCC68 chip + 
CX32L003F8 MCU (Arm Cortex-M0+ 64KB Flash/4KB SRAM)
    
    Change-Id: Ibf5692be9db9e80e88442f3f1e349e02e9a1ff22
    Signed-off-by: Benjamin Vernoux <bvern...@gmail.com>

diff --git a/contrib/loaders/flash/cx32l003/Makefile 
b/contrib/loaders/flash/cx32l003/Makefile
new file mode 100644
index 0000000000..9f53629fb6
--- /dev/null
+++ b/contrib/loaders/flash/cx32l003/Makefile
@@ -0,0 +1,73 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+# 
+# Copyright (C) 2023 Benjamin VERNOUX 31 Aug 2023 (based on Makefile of npcx)
+# 
+BIN2C = ../../../../src/helper/bin2char.sh
+
+# Toolchain used in makefile
+CROSS_COMPILE ?= arm-none-eabi-
+CC             = $(CROSS_COMPILE)gcc
+CPLUS          = $(CROSS_COMPILE)g++
+CPP            = $(CROSS_COMPILE)cpp
+LD             = $(CROSS_COMPILE)gcc
+AS             = $(CROSS_COMPILE)as
+OBJCOPY        = $(CROSS_COMPILE)objcopy
+OBJDUMP        = $(CROSS_COMPILE)objdump
+OBJSIZE        = $(CROSS_COMPILE)size
+
+TARGET         = cx32l003_algo
+OBJS           = entry_cx32l003.o cx32l003_flash.o
+FLAGS          = -mcpu=cortex-m0plus -mthumb -Os -ffunction-sections 
-fdata-sections -g -gdwarf-3 --specs=nano.specs
+FLAGS         += -gstrict-dwarf -Wall -fno-strict-aliasing --asm
+
+CFLAGS         = -c -I. -mcpu=cortex-m0plus -mthumb -fpack-struct
+
+PRE_LD_FILE    = cx32l003_flash.lds
+LD_FILE        = cx32l003_flash_generated.lds
+LDFLAGS        = -T$(LD_FILE) -Xlinker --gc-sections -Xlinker 
--print-memory-usage -Wl,-Map,"$(TARGET).map" -nostartfiles
+
+all: $(TARGET).inc
+
+# Implicit rules
+%.o: %.c
+       -@ echo CC $@ from $<
+       @$(CC) $< $(FLAGS) $(CFLAGS) -o $@
+
+%.o: %.S
+       -@ echo CC $@ from $<
+       @$(CC) $(FLAGS) $(CFLAGS) -x assembler -c -o "$@" "$<"
+
+$(LD_FILE): $(PRE_LD_FILE)
+       -@ echo Generate $@ from $<
+       -@$(CPP) $(PRE_LD_FILE) | grep -v '^#' >>$(LD_FILE)
+
+$(TARGET).elf: $(OBJS) $(LD_FILE)
+       -@ echo LD  $@ from $(OBJS)
+       @$(CC) -o $@ $(FLAGS) $(LDFLAGS) $(OBJS)
+
+%.bin: %.elf
+       -@ echo OBJCOPY $@ from $<
+       -@ $(OBJCOPY) $< -O binary $@
+       -@ $(OBJSIZE) $< --format=berkeley
+
+%.inc: %.bin
+       @echo 'Building target: $@'
+       @echo 'Invoking Bin2Char Script'
+       $(BIN2C) < $< > $@
+       @echo 'Finished building target: $@'
+       @echo 'Cleanup files'
+       rm $< $*.elf $*.map *.o cx32l003_flash_generated.lds
+       @echo ' '
+
+clean:
+       @echo 'Cleaning Targets and Build Artifacts'
+       rm -rf *.inc *.bin *.elf *.map
+       rm -rf *.o *.d
+       rm -rf $(LD_FILE)
+       @echo 'Finished clean'
+       @echo ' '
+
+.PRECIOUS: %.bin
+
+.PHONY: all clean
diff --git a/contrib/loaders/flash/cx32l003/cx32l003_algo.inc 
b/contrib/loaders/flash/cx32l003/cx32l003_algo.inc
new file mode 100644
index 0000000000..46136f305e
--- /dev/null
+++ b/contrib/loaders/flash/cx32l003/cx32l003_algo.inc
@@ -0,0 +1,66 @@
+/* Autogenerated with ../../../../src/helper/bin2char.sh */
+0x02,0x48,0x85,0x46,0x00,0xf0,0x24,0xf9,0xfe,0xe7,0x00,0x00,0x20,0x08,0x00,0x20,
+0x70,0xb5,0x2d,0x4b,0x5a,0x68,0x52,0x07,0x05,0xd5,0x2c,0x4a,0x9a,0x61,0x2c,0x4a,
+0x9a,0x60,0x2c,0x4a,0x9a,0x61,0x80,0x24,0x2b,0x4b,0x2c,0x4a,0x2c,0x49,0x24,0x02,
+0xa0,0x42,0x26,0xd2,0x01,0x24,0x86,0x0a,0xb4,0x40,0xda,0x60,0xd9,0x60,0x1d,0x69,
+0x2c,0x43,0x1c,0x61,0x02,0x24,0xda,0x60,0xd9,0x60,0x1c,0x60,0x00,0x24,0x04,0x25,
+0x04,0x60,0x1e,0x68,0x34,0x00,0x2c,0x40,0x2e,0x42,0xfa,0xd1,0xff,0x26,0xda,0x60,
+0xd9,0x60,0x1d,0x68,0xb5,0x43,0x1d,0x60,0x80,0x25,0x2d,0x02,0xa8,0x42,0x1a,0xd2,
+0xda,0x60,0xd9,0x60,0x01,0x21,0x80,0x0a,0x81,0x40,0x1a,0x69,0x8a,0x43,0x1a,0x61,
+0x70,0xbd,0x80,0x24,0x64,0x02,0xa0,0x42,0xdc,0xd2,0x16,0x4c,0xda,0x60,0x05,0x19,
+0x01,0x24,0xad,0x0a,0xac,0x40,0xd9,0x60,0x5e,0x69,0x34,0x43,0x5c,0x61,0x00,0x24,
+0xda,0x60,0xdc,0x60,0xce,0xe7,0x80,0x25,0x6d,0x02,0xa8,0x42,0xe8,0xd2,0x0d,0x4d,
+0xda,0x60,0x40,0x19,0x01,0x25,0x80,0x0a,0x85,0x40,0xd9,0x60,0x59,0x69,0xa9,0x43,
+0x59,0x61,0xda,0x60,0xdc,0x60,0xdb,0xe7,0x00,0x24,0x00,0x40,0x99,0x66,0xaa,0x55,
+0xff,0xff,0x0f,0x00,0x98,0x66,0xaa,0x55,0x00,0x04,0x02,0x40,0x5a,0x5a,0x00,0x00,
+0xa5,0xa5,0x00,0x00,0x00,0x80,0xff,0xff,0xf0,0xb5,0x3c,0x4b,0x85,0xb0,0x01,0x92,
+0x5c,0x68,0x62,0x07,0x05,0xd5,0x3a,0x4c,0x9c,0x61,0x3a,0x4c,0x9c,0x60,0x3a,0x4c,
+0x9c,0x61,0x43,0x18,0x02,0x93,0xff,0x23,0x01,0x25,0x9c,0x46,0x02,0x9b,0x98,0x42,
+0x01,0xd1,0x05,0xb0,0xf0,0xbd,0x80,0x22,0x34,0x4b,0x35,0x49,0x35,0x4c,0x12,0x02,
+0x90,0x42,0x32,0xd2,0x62,0x46,0x86,0x0a,0x16,0x40,0x2a,0x00,0xb2,0x40,0x16,0x00,
+0xd9,0x60,0xdc,0x60,0x1f,0x69,0x3e,0x43,0x1e,0x61,0x62,0x46,0xd9,0x60,0xdc,0x60,
+0x1e,0x68,0x96,0x43,0x1e,0x60,0xd9,0x60,0xdc,0x60,0x1e,0x68,0x2e,0x43,0x1e,0x60,
+0x04,0x26,0x01,0x9a,0x12,0x68,0x02,0x60,0x1f,0x68,0x3a,0x00,0x32,0x40,0x03,0x92,
+0x37,0x42,0xf9,0xd1,0x80,0x22,0x12,0x02,0x90,0x42,0x23,0xd2,0x62,0x46,0xd9,0x60,
+0xdc,0x60,0x84,0x0a,0x14,0x40,0x2a,0x00,0xa2,0x40,0x19,0x69,0x91,0x43,0x19,0x61,
+0x01,0x9b,0x04,0x30,0x04,0x33,0x01,0x93,0xc0,0xe7,0x80,0x22,0x52,0x02,0x90,0x42,
+0xd3,0xd2,0x19,0x4a,0xd9,0x60,0x86,0x18,0x62,0x46,0xb6,0x0a,0x16,0x40,0x2a,0x00,
+0xb2,0x40,0x16,0x00,0xdc,0x60,0x5f,0x69,0x3e,0x43,0x5e,0x61,0x00,0x26,0xd9,0x60,
+0xde,0x60,0xc2,0xe7,0x80,0x22,0x52,0x02,0x90,0x42,0xe1,0xd2,0x0e,0x4a,0xd9,0x60,
+0xdc,0x60,0x84,0x18,0x62,0x46,0xa4,0x0a,0x14,0x40,0x2a,0x00,0xa2,0x40,0x5e,0x69,
+0x96,0x43,0x5e,0x61,0xd9,0x60,0x03,0x9a,0xda,0x60,0xd1,0xe7,0x00,0x24,0x00,0x40,
+0x99,0x66,0xaa,0x55,0xff,0xff,0x0f,0x00,0x98,0x66,0xaa,0x55,0x00,0x04,0x02,0x40,
+0x5a,0x5a,0x00,0x00,0xa5,0xa5,0x00,0x00,0x00,0x80,0xff,0xff,0x10,0xb5,0x10,0x22,
+0x00,0x21,0x00,0xf0,0xf9,0xf8,0x00,0x20,0x10,0xbd,0x00,0x00,0x80,0x23,0x0a,0x4a,
+0x5b,0x00,0x91,0x68,0x0b,0x43,0x93,0x60,0x08,0x4b,0x5a,0x68,0x52,0x07,0x05,0xd5,
+0x07,0x4a,0x9a,0x61,0x07,0x4a,0x9a,0x60,0x07,0x4a,0x9a,0x61,0x04,0x23,0x07,0x49,
+0x0a,0x68,0x1a,0x42,0xfc,0xd1,0x70,0x47,0x00,0x00,0x02,0x40,0x00,0x24,0x00,0x40,
+0x99,0x66,0xaa,0x55,0xff,0xff,0x0f,0x00,0x98,0x66,0xaa,0x55,0x00,0x04,0x02,0x40,
+0xf7,0xb5,0xff,0xf7,0xdb,0xff,0x59,0x4c,0x20,0x00,0xff,0xf7,0xcf,0xff,0x23,0x7b,
+0x61,0x7b,0xa2,0x7b,0x09,0x02,0x19,0x43,0xe3,0x7b,0x12,0x04,0x11,0x43,0x1b,0x06,
+0x0b,0x43,0x11,0xd0,0x20,0x7a,0x62,0x7a,0xa3,0x7a,0x12,0x02,0x02,0x43,0xe0,0x7a,
+0x1b,0x04,0x1a,0x43,0x00,0x06,0x10,0x43,0x01,0x38,0x03,0x28,0x00,0xd9,0x88,0xe0,
+0x00,0xf0,0xa8,0xf8,0x0d,0x2d,0x56,0x6f,0x49,0x4b,0x5a,0x68,0x52,0x07,0xde,0xd5,
+0x48,0x4a,0x9a,0x61,0x48,0x4a,0x9a,0x60,0x48,0x4a,0x9a,0x61,0xd7,0xe7,0xf8,0x22,
+0x47,0x4b,0x1a,0x70,0xf5,0x3a,0x5a,0x70,0x2f,0x32,0x9a,0x70,0x00,0x22,0xda,0x70,
+0x44,0x4b,0x1a,0x60,0x43,0x4b,0x1b,0x68,0x00,0x2b,0x6d,0xd0,0xff,0x21,0x1a,0x00,
+0x0a,0x40,0x20,0x7b,0x22,0x73,0x1a,0x0a,0x0a,0x40,0x60,0x7b,0x62,0x73,0x1a,0x0c,
+0x0a,0x40,0x1b,0x0e,0xa1,0x7b,0xa2,0x73,0xe2,0x7b,0xe3,0x73,0xfe,0xe7,0x22,0x78,
+0x63,0x78,0x39,0x4f,0x1b,0x02,0x13,0x43,0x3b,0x80,0xa2,0x78,0xe2,0x78,0x23,0x79,
+0x61,0x79,0xa2,0x79,0x09,0x02,0x19,0x43,0xe3,0x79,0x12,0x04,0x33,0x4e,0x11,0x43,
+0x1b,0x06,0x0b,0x43,0x33,0x60,0x35,0x68,0x00,0x2d,0x02,0xd1,0x00,0x22,0x2d,0x4b,
+0xcf,0xe7,0x3b,0x88,0x18,0x00,0x01,0x93,0xff,0xf7,0x72,0xfe,0x80,0x22,0x92,0x00,
+0x94,0x46,0x2b,0x4b,0xed,0x18,0x01,0x9b,0x35,0x60,0x63,0x44,0x3b,0x80,0xea,0xe7,
+0x00,0x23,0x25,0x4f,0x25,0x4e,0x3b,0x80,0x80,0x23,0x5b,0x02,0x33,0x60,0x35,0x68,
+0x00,0x2d,0xe3,0xd0,0x3b,0x88,0x18,0x00,0x01,0x93,0xff,0xf7,0x59,0xfe,0x80,0x22,
+0x92,0x00,0x94,0x46,0x1e,0x4b,0xed,0x18,0x01,0x9b,0x35,0x60,0x63,0x44,0x3b,0x80,
+0xed,0xe7,0x22,0x78,0x63,0x78,0xa0,0x78,0x1b,0x02,0x1a,0x43,0xe3,0x78,0x00,0x04,
+0x10,0x43,0x1b,0x06,0x21,0x79,0x18,0x43,0x63,0x79,0xa2,0x79,0x1b,0x02,0x0b,0x43,
+0xe1,0x79,0x12,0x04,0x13,0x43,0x09,0x06,0x0d,0x4a,0x19,0x43,0xff,0xf7,0xa4,0xfe,
+0xbc,0xe7,0x01,0x22,0x0b,0x4b,0x8c,0xe7,0x22,0x7b,0x23,0x73,0x62,0x7b,0x63,0x73,
+0xa2,0x7b,0xa3,0x73,0xe2,0x7b,0xe3,0x73,0x51,0xe7,0xc0,0x46,0x00,0x00,0x00,0x20,
+0x00,0x24,0x00,0x40,0x99,0x66,0xaa,0x55,0xff,0xff,0x0f,0x00,0x98,0x66,0xaa,0x55,
+0x10,0x00,0x00,0x20,0x20,0x06,0x00,0x20,0x18,0x06,0x00,0x20,0x1c,0x06,0x00,0x20,
+0x00,0xfe,0xff,0xff,0x02,0xb4,0x71,0x46,0x49,0x08,0x49,0x00,0x09,0x5c,0x49,0x00,
+0x8e,0x44,0x02,0xbc,0x70,0x47,0xc0,0x46,0x03,0x00,0x82,0x18,0x93,0x42,0x00,0xd1,
+0x70,0x47,0x19,0x70,0x01,0x33,0xf9,0xe7,
diff --git a/contrib/loaders/flash/cx32l003/cx32l003_flash.c 
b/contrib/loaders/flash/cx32l003/cx32l003_flash.c
new file mode 100644
index 0000000000..32611e803c
--- /dev/null
+++ b/contrib/loaders/flash/cx32l003/cx32l003_flash.c
@@ -0,0 +1,314 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/*
+ * Copyright (C) 2020 by Nuvoton Technology Corporation
+ * Mulin Chao <mlc...@nuvoton.com>
+ * Wealian Liao <whl...@nuvoton.com>
+ *
+ * Copyright (C) 2023 Benjamin VERNOUX 31 Aug 2023 (based on npcx_flash.c)
+ */
+#include <stdint.h>
+#include <string.h>
+#include "cx32l003_flash.h"
+
+/*----------------------------------------------------------------------------
+ *                             CX32L003 Driver
+ 
*----------------------------------------------------------------------------*/
+#ifdef __cplusplus
+  #define __I volatile /*!< Defines 'read only' permissions */
+#else
+  #define __I volatile const /*!< Defines 'read only' permissions */
+#endif
+#define __IO volatile /*!< Defines 'read / write' permissions */
+
+/**
+  * @brief System RCC (RCC)
+  */
+typedef struct { /*!< (@ 0x40020000) RCC Structure */
+  __IO uint32_t  HCLKDIV; /*!< (@ 0x00000000) AHB CLK Prescale */
+  __IO uint32_t  PCLKDIV; /*!< (@ 0x00000004) APB CLK Prescale */
+  __IO uint32_t  HCLKEN; /*!< (@ 0x00000008) AHB Peripheral Model Clk Enable */
+  __IO uint32_t  PCLKEN; /*!< (@ 0x0000000C) APB Peripheral Model CLK Enable */
+  __IO uint32_t  MCOCR; /*!< (@ 0x00000010) Clock Output Control Register */
+  __I  uint32_t  RESERVED;
+  __IO uint32_t  RSTCR; /*!< (@ 0x00000018) System Reset Control */
+  __IO uint32_t  RSTSR; /*!< (@ 0x0000001C) Reset Status */
+  __IO uint32_t  SYSCLKCR; /*!< (@ 0x00000020) CLK Setting */
+  __IO uint32_t  SYSCLKSEL; /*!< (@ 0x00000024) System Clock Select */
+  __IO uint32_t  HIRCCR; /*!< (@ 0x00000028) HIRC Control */
+  __IO uint32_t  HXTCR; /*!< (@ 0x0000002C) HXT Control */
+  __IO uint32_t  LIRCCR; /*!< (@ 0x00000030) LIRC Control */
+  __IO uint32_t  LXTCR; /*!< (@ 0x00000034) LXT Control */
+  __IO uint32_t  IRQLATENCY; /*!< (@ 0x00000038) M0 IRQ Delay */
+  __IO uint32_t  STICKCR; /*!< (@ 0x0000003C) SysTick Timer Circle Adjust */
+  __IO uint32_t  SWDIOCR; /*!< (@ 0x00000040) Endpoint Function Select */
+  __IO uint32_t  PERIRST; /*!< (@ 0x00000044) Peripheral Model Control */
+  __IO uint32_t  RTCRST; /*!< (@ 0x00000048) RTC Control */
+  __I  uint32_t  RESERVED1[5];
+  __IO uint32_t  UNLOCK; /*!< (@ 0x00000060) Register Protect */
+} RCC_TypeDef; /*!< Size = 100 (0x64) */
+#define RCC_BASE 0x40020000UL
+#define RCC ((RCC_TypeDef*) RCC_BASE)
+
+ /**
+  * @brief Independent Watch Dog (IWDG)
+  */
+typedef struct { /*!< (@ 0x40002400) IWDG Structure */
+  __IO uint32_t  CMDCR; /*!< (@ 0x00000000) IWDG Control Command Register */
+  __IO uint32_t  CFGR;  /*!< (@ 0x00000004) IWDG Config Register */
+  __IO uint32_t  RLOAD; /*!< (@ 0x00000008) Count ReLoad Register */
+  __IO uint32_t  CNTVAL;/*!< (@ 0x0000000C) Count Value */
+  __IO uint32_t  SR;    /*!< (@ 0x00000010) IWDG Interrupt Status Register */
+  __IO uint32_t  INTCLR;/*!< (@ 0x00000014) IWDG Interrupt Status Register */
+  __IO uint32_t  UNLOCK;/*!< (@ 0x00000018) IWDG register Writer Protect */
+} IWDG_TypeDef; /*!< Size = 28 (0x1c) */
+#define IWDG_BASE 0x40002400UL
+#define IWDG ((IWDG_TypeDef*) IWDG_BASE)
+
+/**
+  * @brief Flash Controller (FLASH)
+  */
+typedef struct { /*!< (@ 0x40020400) FLASH Structure */
+  __IO uint32_t  CR; /*!< (@ 0x00000000) Control Register */
+  __IO uint32_t  IFR; /*!< (@ 0x00000004) Interrupt flag Register */
+  __IO uint32_t  ICLR; /*!< (@ 0x00000008) Interrupt Flag Clear Register */
+  __IO uint32_t  BYPASS; /*!< (@ 0x0000000C) 0X5A5A-0XA5A5 sequence Register */
+  __IO uint32_t  SLOCK0; /*!< (@ 0x00000010) Sector Write Protect Register0 */
+  __IO uint32_t  SLOCK1; /*!< (@ 0x00000014) Sector Write Protect Register1 */
+  __IO uint32_t  ISPCON; /*!< (@ 0x00000018) Flash ISP Control register */
+} FLASH_TypeDef; /*!< Size = 28 (0x1c) */
+#define FLASH_BASE 0x40020400UL
+#define FLASH ((FLASH_TypeDef*) FLASH_BASE)
+
+void cx32l003_FlashEraseSector(uint32_t page_addr)
+{
+       uint32_t val;
+       /* If IWDG is running, reload it to max value about 27s */
+       val = IWDG->CFGR;
+       if ((val & 4) == 4) {
+               IWDG->UNLOCK = 0x55aa6699;
+               IWDG->RLOAD = 0xfffff;
+               IWDG->UNLOCK = 0x55aa6698;
+       }
+
+       if (page_addr < 0x8000) {
+               FLASH->BYPASS = 0x5a5a;
+               FLASH->BYPASS = 0xa5a5;
+               val = FLASH->SLOCK0;
+               FLASH->SLOCK0 = val | 1 << (page_addr >> 10 & 0xff);
+       } else if (page_addr < 0x10000) {
+               FLASH->BYPASS = 0x5a5a;
+               FLASH->BYPASS = 0xa5a5;
+               val = FLASH->SLOCK1;
+               FLASH->SLOCK1 = val | 1 << ((page_addr - 0x8000) >> 10 & 0xff);
+               FLASH->BYPASS = 0x5a5a;
+               FLASH->BYPASS = 0;
+       }
+       FLASH->BYPASS = 0x5a5a;
+       FLASH->BYPASS = 0xa5a5;
+       FLASH->CR = 2;
+       /* Erase flash sector */
+       *(uint32_t *)page_addr = 0;
+
+       /* Wait for end of erase "idle state" */
+       do {
+               val = FLASH->CR;
+       } while ((val & 4) == 4);
+
+       FLASH->BYPASS = 0x5a5a;
+       FLASH->BYPASS = 0xa5a5;
+       val = FLASH->CR;
+       FLASH->CR = val & 0xffffff00;
+       if (page_addr < 0x8000) {
+               FLASH->BYPASS = 0x5a5a;
+               FLASH->BYPASS = 0xa5a5;
+               val = FLASH->SLOCK0;
+               FLASH->SLOCK0 = val & ~(1 << (page_addr >> 10 & 0xff));
+       } else if (page_addr < 0x10000) {
+               FLASH->BYPASS = 0x5a5a;
+               FLASH->BYPASS = 0xa5a5;
+               val = FLASH->SLOCK1;
+               FLASH->SLOCK1 = val & ~(1 << ((page_addr - 0x8000) >> 10 & 
0xff));
+               FLASH->BYPASS = 0x5a5a;
+               FLASH->BYPASS = 0;
+       }
+}
+
+void cx32l003_FlashProgram(uint32_t *flash_addr_dst,int size,uint32_t 
*ram_data_src)
+{
+       uint32_t val;
+
+       /* If IWDG is running, reload it to max value about 27s */
+       val = IWDG->CFGR;
+       if ((val & 4) == 4) {
+               IWDG->UNLOCK = 0x55aa6699;
+               IWDG->RLOAD = 0xfffff;
+               IWDG->UNLOCK = 0x55aa6698;
+       }
+       for (; size != 0; size = size-4) {
+               if (flash_addr_dst < (uint32_t *)0x8000) {
+                       FLASH->BYPASS = 0x5a5a;
+                       FLASH->BYPASS = 0xa5a5;
+                       val = FLASH->SLOCK0;
+                       FLASH->SLOCK0 = val | 1 << ((uint32_t)flash_addr_dst >> 
10 & 0xff);
+               } else if (flash_addr_dst < (uint32_t *)0x10000) {
+                       FLASH->BYPASS = 0x5a5a;
+                       FLASH->BYPASS = 0xa5a5;
+                       val = FLASH->SLOCK1;
+                       FLASH->SLOCK1 = val | 1 << ((uint32_t)(flash_addr_dst - 
0x2000) >> 10 & 0xff);
+                       FLASH->BYPASS = 0x5a5a;
+                       FLASH->BYPASS = 0;
+               }
+               FLASH->BYPASS = 0x5a5a;
+               FLASH->BYPASS = 0xa5a5;
+               val = FLASH->CR;
+               FLASH->CR = val & 0xffffff00;
+               FLASH->BYPASS = 0x5a5a;
+               FLASH->BYPASS = 0xa5a5;
+               val = FLASH->CR;
+               FLASH->CR = val | 1;
+
+               /* Program flash */
+               *flash_addr_dst = *ram_data_src;
+
+               /* Wait end of program "idle state" */
+               do {
+               val = FLASH->CR;
+               } while ((val & 4) == 4);
+
+               if (flash_addr_dst < (uint32_t *)0x8000) {
+                       FLASH->BYPASS = 0x5a5a;
+                       FLASH->BYPASS = 0xa5a5;
+                       val = FLASH->SLOCK0;
+                       FLASH->SLOCK0 = val & ~(1 << ((uint32_t)flash_addr_dst 
>> 10 & 0xff));
+               } else if (flash_addr_dst < (uint32_t *)0x10000) {
+                       FLASH->BYPASS = 0x5a5a;
+                       FLASH->BYPASS = 0xa5a5;
+                       val = FLASH->SLOCK1;
+                       FLASH->SLOCK1 = val & ~(1 << ((uint32_t)(flash_addr_dst 
- 0x2000) >> 10 & 0xff));
+                       FLASH->BYPASS = 0x5a5a;
+                       FLASH->BYPASS = 0;
+               }
+               flash_addr_dst = flash_addr_dst + 1;
+               ram_data_src = ram_data_src + 1;
+       }
+}
+
+/*----------------------------------------------------------------------------
+ *                             flash loader function
+ 
*----------------------------------------------------------------------------*/
+uint32_t flashloader_init(struct cx32l003_flash_params *params)
+{
+       /* Initialize params buffers */
+       memset(params, 0, sizeof(struct cx32l003_flash_params));
+
+       return CX32L003_FLASH_STATUS_OK;
+}
+
+/*----------------------------------------------------------------------------
+ *                                      Functions
+ 
*----------------------------------------------------------------------------*/
+/* flashloader parameter structure */
+__attribute__ ((section(".buffers.g_cfg")))
+volatile struct cx32l003_flash_params g_cfg;
+/* data buffer */
+__attribute__ ((section(".buffers.g_buf")))
+uint8_t g_buf[CX32L003_FLASH_LOADER_BUFFER_SIZE];
+
+static uint32_t val;
+static int status;
+static uint16_t address;
+static uint32_t size;
+
+void Init(void)
+{
+       // RCC Unlock is done in openocd cx32l003.cfg
+       /* Flash controller block clock enable */
+       val = RCC->HCLKEN;
+       RCC->HCLKEN = val | 0x100;
+
+       /* If IWDG is running, reload it to max value about 27s */
+       val = IWDG->CFGR;
+       if ((val & 4) == 4) {
+               IWDG->UNLOCK = 0x55aa6699;
+               IWDG->RLOAD = 0xfffff;
+               IWDG->UNLOCK = 0x55aa6698;
+       }
+
+       /* Wait Flash "idle state" */
+       do {
+               val = FLASH->CR;
+       } while ((val & 4) == 4);
+}
+
+int main(void)
+{
+       Init();
+
+       /* set buffer */
+       flashloader_init((struct cx32l003_flash_params *)&g_cfg);
+
+       while (1) {
+               /* wait command*/
+               while (g_cfg.sync == CX32L003_FLASH_LOADER_WAIT)
+               {
+                       /* If IWDG is running, reload it to max value about 27s 
*/
+                       val = IWDG->CFGR;
+                       if ((val & 4) == 4) {
+                               IWDG->UNLOCK = 0x55aa6699;
+                               IWDG->RLOAD = 0xfffff;
+                               IWDG->UNLOCK = 0x55aa6698;
+                       }
+               }
+
+               /* command handler */
+               switch (g_cfg.cmd) {
+                       case CX32L003_FLASH_CMD_GET_FLASH_ID:
+                               /* Hard coded identifier 0x003203F8 for 
CX32L003F8 */
+                               g_buf[0] = 0xF8; /* LSB */
+                               g_buf[1] = 0x03;
+                               g_buf[2] = 0x32;
+                               g_buf[3] = 0x00; /* MSB */
+                               status = CX32L003_FLASH_STATUS_OK;
+                               break;
+                       case CX32L003_FLASH_CMD_ERASE_SECTORS:
+                               /* Alignment has been checked in upper layer */
+                               address = g_cfg.addr;
+                               size = g_cfg.len;
+                               for (; size > 0; size -= 
CX32L003_FLASH_ERASE_SIZE,
+                                       address += CX32L003_FLASH_ERASE_SIZE) {
+                                       cx32l003_FlashEraseSector(address);
+                               }
+                               status = CX32L003_FLASH_STATUS_OK;
+                               break;
+                       case CX32L003_FLASH_CMD_ERASE_ALL:
+                               address = 0x0;
+                               size = 64 * 1024; /* 64KB */
+                               for (; size > 0; size -= 
CX32L003_FLASH_ERASE_SIZE,
+                                       address += CX32L003_FLASH_ERASE_SIZE) {
+                                       cx32l003_FlashEraseSector(address);
+                               }
+                               status = CX32L003_FLASH_STATUS_OK;
+                               break;
+                       case CX32L003_FLASH_CMD_PROGRAM:
+                               cx32l003_FlashProgram((uint32_t *)g_cfg.addr, 
g_cfg.len, (uint32_t *)g_buf);
+                               status = CX32L003_FLASH_STATUS_OK;
+                               break;
+                       default:
+                               status = 
CX32L003_FLASH_STATUS_FAILED_UNKNOWN_COMMAND;
+                               break;
+               }
+
+               /* clear & set result for next command */
+               if (status != CX32L003_FLASH_STATUS_OK) {
+                       g_cfg.sync = status;
+                       // Fatal error infinite loop
+                       while (1)
+                               ;
+               } else {
+                       g_cfg.sync = CX32L003_FLASH_LOADER_WAIT;
+               }
+       }
+
+       return 0;
+}
diff --git a/contrib/loaders/flash/cx32l003/cx32l003_flash.h 
b/contrib/loaders/flash/cx32l003/cx32l003_flash.h
new file mode 100644
index 0000000000..58950bf387
--- /dev/null
+++ b/contrib/loaders/flash/cx32l003/cx32l003_flash.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/*
+ * Copyright (C) 2020 by Nuvoton Technology Corporation
+ * Mulin Chao <mlc...@nuvoton.com>
+ * Wealian Liao <whl...@nuvoton.com>
+ *
+ * Copyright (C) 2023 Benjamin VERNOUX 31 Aug 2023 (based on npcx_flash.h)
+ */
+#ifndef OPENOCD_LOADERS_FLASH_CX32L003_H
+#define OPENOCD_LOADERS_FLASH_CX32L003_H
+
+#include "cx32l003_flash_config.h"
+
+/* Flash loader parameters */
+struct __attribute__((__packed__)) cx32l003_flash_params {
+       uint32_t addr; /* Address in flash */
+       uint32_t len;  /* Number of bytes */
+       uint32_t cmd;  /* Command */
+       uint32_t sync; /* Handshake signal */
+};
+
+/* Flash trigger signal */
+enum cx32l003_flash_handshake {
+       CX32L003_FLASH_LOADER_WAIT    = 0x0,       /* Idle */
+       CX32L003_FLASH_LOADER_EXECUTE = 0xFFFFFFFF /* Execute Command */
+};
+
+/* Flash loader command */
+enum cx32l003_flash_commands {
+       CX32L003_FLASH_CMD_NO_ACTION = 0, /* No action, default value */
+       CX32L003_FLASH_CMD_GET_FLASH_ID,  /* Get the internal flash ID */
+       CX32L003_FLASH_CMD_ERASE_SECTORS, /* Erase unprotected sectors */
+       CX32L003_FLASH_CMD_ERASE_ALL,     /* Erase all */
+       CX32L003_FLASH_CMD_PROGRAM,       /* Program data */
+};
+
+/* Status */
+enum cx32l003_flash_status {
+       CX32L003_FLASH_STATUS_OK = 0,
+       CX32L003_FLASH_STATUS_FAILED_UNKNOWN_COMMAND,
+       CX32L003_FLASH_STATUS_FAILED,
+       CX32L003_FLASH_STATUS_FAILED_TIMEOUT,
+};
+
+#endif /* OPENOCD_LOADERS_FLASH_CX32L003_H */
diff --git a/contrib/loaders/flash/cx32l003/cx32l003_flash.lds 
b/contrib/loaders/flash/cx32l003/cx32l003_flash.lds
new file mode 100644
index 0000000000..a620dc0cca
--- /dev/null
+++ b/contrib/loaders/flash/cx32l003/cx32l003_flash.lds
@@ -0,0 +1,61 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/* Copyright (C) 2023 Benjamin VERNOUX 31 Aug 2023 */
+
+#include "cx32l003_flash_config.h"
+
+/* Application memory map */
+MEMORY {
+       /* buffer + parameters */
+    BUFFER (RWX)  : ORIGIN = CX32L003_FLASH_LOADER_PARAMS_ADDR,
+                      LENGTH = CX32L003_FLASH_LOADER_PARAMS_SIZE + 
CX32L003_FLASH_LOADER_BUFFER_SIZE
+
+    PROGRAM (RWX)  : ORIGIN = CX32L003_FLASH_LOADER_PROGRAM_ADDR,
+                     LENGTH = CX32L003_FLASH_LOADER_PROGRAM_SIZE
+}
+
+/* Sections used for flashing */
+SECTIONS
+{
+       .buffers (NOLOAD) :
+       {
+               _buffers = .;
+               *(.buffers.g_cfg)
+               *(.buffers.g_buf)
+               *(.buffers*)
+               _ebuffers = .;
+       } > BUFFER
+
+       .text :
+       {
+               _text = .;
+        KEEP(*(.entry*))
+        KEEP(*(.text*))
+               _etext = .;
+       } > PROGRAM
+
+       .data :
+       {       _data = .;
+               *(.rodata*)
+               *(.data*)
+               _edata = .;
+       } > PROGRAM
+
+       .bss :
+       {
+               __bss_start__ = .;
+               _bss = .;
+               *(.bss*)
+               *(COMMON)
+               _ebss = .;
+               __bss_end__ = .;
+       } > PROGRAM
+
+       .stack (NOLOAD) :
+       {
+               _stack = .;
+               . = . + CX32L003_FLASH_LOADER_STACK_SIZE;
+               *(.stack*)
+               _estack = .;
+       } > PROGRAM
+}
diff --git a/contrib/loaders/flash/cx32l003/cx32l003_flash_config.h 
b/contrib/loaders/flash/cx32l003/cx32l003_flash_config.h
new file mode 100644
index 0000000000..803792290a
--- /dev/null
+++ b/contrib/loaders/flash/cx32l003/cx32l003_flash_config.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/*
+ * Copyright (C) 2021 by Nuvoton Technology Corporation
+ * Mulin Chao <mlc...@nuvoton.com>
+ * Wealian Liao <whl...@nuvoton.com>
+ *
+ * Copyright (C) 2023 Benjamin VERNOUX 31 Aug 2023 (based on cx32l003_flash.h)
+ */
+#ifndef OPENOCD_LOADERS_FLASH_CX32L003_CONFIG_H
+#define OPENOCD_LOADERS_FLASH_CX32L003_CONFIG_H
+
+/* CX32L003 chip information */
+#define CX32L003_FLASH_ERASE_SIZE 512
+
+/* CX32L003 flash loader information */
+#define CX32L003_FLASH_LOADER_WORKING_ADDR 0x20000000
+#define CX32L003_FLASH_LOADER_PARAMS_ADDR CX32L003_FLASH_LOADER_WORKING_ADDR
+#define CX32L003_FLASH_LOADER_PARAMS_SIZE 16
+#define CX32L003_FLASH_LOADER_BUFFER_ADDR (CX32L003_FLASH_LOADER_PARAMS_ADDR + 
CX32L003_FLASH_LOADER_PARAMS_SIZE)
+#define CX32L003_FLASH_LOADER_BUFFER_SIZE CX32L003_FLASH_ERASE_SIZE
+#define CX32L003_FLASH_LOADER_PROGRAM_ADDR (CX32L003_FLASH_LOADER_BUFFER_ADDR 
+ CX32L003_FLASH_LOADER_BUFFER_SIZE)
+#define CX32L003_FLASH_LOADER_PROGRAM_SIZE 3568
+
+/* Stack size in byte. 4 byte size alignment */
+#define CX32L003_FLASH_LOADER_STACK_SIZE 512
+
+#endif /* OPENOCD_LOADERS_FLASH_CX32L003_CONFIG_H */
diff --git a/contrib/loaders/flash/cx32l003/entry_cx32l003.S 
b/contrib/loaders/flash/cx32l003/entry_cx32l003.S
new file mode 100644
index 0000000000..dd3ae8408a
--- /dev/null
+++ b/contrib/loaders/flash/cx32l003/entry_cx32l003.S
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/*
+ * Copyright (C) 2023 Benjamin VERNOUX 31 Aug 2023
+ */
+  .syntax unified
+  .cpu cortex-m0plus
+  .thumb
+
+  .section .entry
+  .weak entry
+  .type entry, %function
+entry:
+  ldr   r0, =_estack - 4
+  mov   sp, r0 /* set stack pointer */
+
+  bl main
+
+LoopForever:
+    b LoopForever
diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am
index 534a7a804e..3a0261d110 100644
--- a/src/flash/nor/Makefile.am
+++ b/src/flash/nor/Makefile.am
@@ -25,6 +25,7 @@ NOR_DRIVERS = \
        %D%/cc3220sf.c \
        %D%/cc26xx.c \
        %D%/cfi.c \
+       %D%/cx32l003.c \
        %D%/dsp5680xx_flash.c \
        %D%/efm32.c \
        %D%/em357.c \
diff --git a/src/flash/nor/cx32l003.c b/src/flash/nor/cx32l003.c
new file mode 100644
index 0000000000..d0937a1796
--- /dev/null
+++ b/src/flash/nor/cx32l003.c
@@ -0,0 +1,516 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/*
+ * Copyright (C) 2020 by Nuvoton Technology Corporation
+ * Mulin Chao <mlc...@nuvoton.com>
+ * Wealian Liao <whl...@nuvoton.com>
+ *
+ * Copyright (C) 2023 Benjamin VERNOUX 31 Aug 2023 (based on npcx.c)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "imp.h"
+#include <helper/binarybuffer.h>
+#include <helper/time_support.h>
+#include <target/armv7m.h>
+#include "../../../contrib/loaders/flash/cx32l003/cx32l003_flash.h"
+
+/* CX32L003 flash loader */
+static const uint8_t cx32l003_algo[] = {
+#include "../../../contrib/loaders/flash/cx32l003/cx32l003_algo.inc"
+};
+
+#define CX32L003_FLASH_TIMEOUT_MS 10000 // Timeout 10s max
+#define CX32L003_FLASH_BASE_ADDR 0x00000000
+#define CX32L003_FLASH_SECTOR_LENGTH (CX32L003_FLASH_ERASE_SIZE)
+
+/* flash list */
+enum cx32l003_flash_device_index {
+       CX32L003_FLASH_64KB = 0,
+       CX32L003_FLASH_UNKNOWN,
+};
+
+struct cx32l003_flash_bank {
+       const char *family_name;
+       uint32_t sector_length;
+       bool probed;
+       enum cx32l003_flash_device_index flash;
+       struct working_area *working_area;
+       struct armv7m_algorithm armv7m_info;
+       const uint8_t *algo_code;
+       uint32_t algo_size;
+       uint32_t algo_working_size;
+       uint32_t buffer_addr;
+       uint32_t params_addr;
+};
+
+struct cx32l003_flash_info {
+       char *name;
+       uint32_t id;
+       uint32_t size;
+};
+
+static const struct cx32l003_flash_info flash_info[] = {
+       [CX32L003_FLASH_64KB] = {
+               .name = "64KB Flash",
+               .id = 0x003203F8,
+               .size = 64 * 1024,
+       },
+       [CX32L003_FLASH_UNKNOWN] = {
+               .name = "Unknown Flash",
+               .size = 0xFFFFFFFF,
+       },
+};
+
+static int cx32l003_init(struct flash_bank *bank)
+{
+       struct target *target = bank->target;
+       struct cx32l003_flash_bank *cx32l003_bank = bank->driver_priv;
+
+       /* Check for working area to use for flash helper algorithm */
+       target_free_working_area(target, cx32l003_bank->working_area);
+       cx32l003_bank->working_area = NULL;
+
+       int retval = target_alloc_working_area(target, 
cx32l003_bank->algo_working_size,
+                               &cx32l003_bank->working_area);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* Confirm the defined working address is the area we need to use */
+       if (cx32l003_bank->working_area->address != 
CX32L003_FLASH_LOADER_WORKING_ADDR) {
+               LOG_ERROR("%s: Invalid working address", 
cx32l003_bank->family_name);
+               LOG_INFO("Hint: Use '-work-area-phys 0x%" PRIx32 "' in your 
target configuration",
+                       CX32L003_FLASH_LOADER_WORKING_ADDR);
+               target_free_working_area(target, cx32l003_bank->working_area);
+               cx32l003_bank->working_area = NULL;
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+       }
+
+       /* Write flash helper algorithm into target memory */
+       retval = target_write_buffer(target, CX32L003_FLASH_LOADER_PROGRAM_ADDR,
+                               cx32l003_bank->algo_size, 
cx32l003_bank->algo_code);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: Failed to load flash helper algorithm",
+                       cx32l003_bank->family_name);
+               target_free_working_area(target, cx32l003_bank->working_area);
+               cx32l003_bank->working_area = NULL;
+               return retval;
+       }
+
+       /* Initialize the ARMv7 specific info to run the algorithm */
+       cx32l003_bank->armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
+       cx32l003_bank->armv7m_info.core_mode = ARM_MODE_THREAD;
+
+       /* Begin executing the flash helper algorithm */
+       retval = target_start_algorithm(target, 0, NULL, 0, NULL,
+                               CX32L003_FLASH_LOADER_PROGRAM_ADDR, 0,
+                               &cx32l003_bank->armv7m_info);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: Failed to start flash helper algorithm",
+                       cx32l003_bank->family_name);
+               target_free_working_area(target, cx32l003_bank->working_area);
+               cx32l003_bank->working_area = NULL;
+               return retval;
+       }
+       /*
+        * At this point, the algorithm is running on the target and
+        * ready to receive commands and data to flash the target
+        */
+       return retval;
+}
+
+static int cx32l003_quit(struct flash_bank *bank)
+{
+       struct target *target = bank->target;
+       struct cx32l003_flash_bank *cx32l003_bank = bank->driver_priv;
+
+       /* Regardless of the algo's status, attempt to halt the target */
+       (void)target_halt(target);
+
+       /* Now confirm target halted and clean up from flash helper algorithm */
+       int retval = target_wait_algorithm(target, 0, NULL, 0, NULL, 0,
+                                       CX32L003_FLASH_TIMEOUT_MS, 
&cx32l003_bank->armv7m_info);
+
+       target_free_working_area(target, cx32l003_bank->working_area);
+       cx32l003_bank->working_area = NULL;
+
+       return retval;
+}
+
+static int cx32l003_wait_algo_done(struct flash_bank *bank, uint32_t 
params_addr)
+{
+       struct target *target = bank->target;
+       struct cx32l003_flash_bank *cx32l003_bank = bank->driver_priv;
+       uint32_t status_addr = params_addr + offsetof(struct 
cx32l003_flash_params, sync);
+       uint32_t status;
+       int64_t start_ms = timeval_ms();
+
+       do {
+               int retval = target_read_u32(target, status_addr, &status);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               keep_alive();
+
+               int64_t elapsed_ms = timeval_ms() - start_ms;
+               if (elapsed_ms > CX32L003_FLASH_TIMEOUT_MS)
+                       break;
+       } while (status == CX32L003_FLASH_LOADER_EXECUTE);
+
+       if (status != CX32L003_FLASH_LOADER_WAIT) {
+               LOG_ERROR("%s: Flash operation failed, status=0x%" PRIx32,
+                               cx32l003_bank->family_name,
+                               status);
+               return ERROR_FAIL;
+       }
+
+       return ERROR_OK;
+}
+
+static enum cx32l003_flash_device_index cx32l003_get_flash_id(struct 
flash_bank *bank, uint32_t *flash_id)
+{
+       struct target *target = bank->target;
+       struct cx32l003_flash_bank *cx32l003_bank = bank->driver_priv;
+       struct cx32l003_flash_params algo_params;
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       int retval = cx32l003_init(bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* Set up algorithm parameters for get flash ID command */
+       target_buffer_set_u32(target, (uint8_t *)&algo_params.cmd, 
CX32L003_FLASH_CMD_GET_FLASH_ID);
+       target_buffer_set_u32(target, (uint8_t *)&algo_params.sync, 
CX32L003_FLASH_LOADER_WAIT);
+
+       /* Issue flash helper algorithm parameters for get flash ID */
+       retval = target_write_buffer(target, cx32l003_bank->params_addr,
+                               sizeof(algo_params), (uint8_t *)&algo_params);
+       if (retval != ERROR_OK) {
+               (void)cx32l003_quit(bank);
+               return retval;
+       }
+
+       target_buffer_set_u32(target, (uint8_t *)&algo_params.sync, 
CX32L003_FLASH_LOADER_EXECUTE);
+       retval = target_write_buffer(target, cx32l003_bank->params_addr,
+                               sizeof(algo_params), (uint8_t *)&algo_params);
+
+       /* If no error, wait for finishing */
+       if (retval == ERROR_OK) {
+               retval = cx32l003_wait_algo_done(bank, 
cx32l003_bank->params_addr);
+               if (retval == ERROR_OK)
+                       target_read_u32(target, 
CX32L003_FLASH_LOADER_BUFFER_ADDR, flash_id);
+       }
+
+       /* Regardless of errors, try to close down algo */
+       (void)cx32l003_quit(bank);
+
+       return retval;
+}
+
+static int cx32l003_get_flash(uint32_t flash_id)
+{
+       for (uint32_t i = 0; i < ARRAY_SIZE(flash_info) - 1; i++) {
+               if (flash_info[i].id == flash_id)
+                       return i;
+       }
+       return CX32L003_FLASH_UNKNOWN;
+}
+
+static int cx32l003_probe(struct flash_bank *bank)
+{
+       struct cx32l003_flash_bank *cx32l003_bank = bank->driver_priv;
+       uint32_t sector_length = CX32L003_FLASH_ERASE_SIZE;
+       uint32_t flash_id;
+
+       /* Set up appropriate flash helper algorithm */
+       cx32l003_bank->algo_code = cx32l003_algo;
+       cx32l003_bank->algo_size = sizeof(cx32l003_algo);
+       cx32l003_bank->algo_working_size = CX32L003_FLASH_LOADER_PARAMS_SIZE +
+                                       CX32L003_FLASH_LOADER_BUFFER_SIZE +
+                                       CX32L003_FLASH_LOADER_PROGRAM_SIZE;
+       cx32l003_bank->buffer_addr = CX32L003_FLASH_LOADER_BUFFER_ADDR;
+       cx32l003_bank->params_addr = CX32L003_FLASH_LOADER_PARAMS_ADDR;
+
+       int retval = cx32l003_get_flash_id(bank, &flash_id);
+       if (retval != ERROR_OK)
+               return retval;
+
+       cx32l003_bank->flash = cx32l003_get_flash(flash_id);
+       if(cx32l003_bank->flash == CX32L003_FLASH_UNKNOWN) {
+               LOG_ERROR("cx32l003_bank->flash CX32L003_FLASH_UNKNOWN 
id=0x%08X bank id=%d", flash_id, cx32l003_bank->flash);
+               return ERROR_FAIL;
+       }
+
+       unsigned int num_sectors = flash_info[cx32l003_bank->flash].size / 
sector_length;
+       LOG_INFO("cx32l003_get_flash_id size=%d bytes, sector_length=%d bytes 
num_sectors=%d",
+                       flash_info[cx32l003_bank->flash].size, sector_length, 
num_sectors);
+
+       bank->sectors = calloc(num_sectors, sizeof(struct flash_sector));
+       if (!bank->sectors) {
+               LOG_ERROR("Out of memory");
+               return ERROR_FAIL;
+       }
+
+       bank->base = CX32L003_FLASH_BASE_ADDR;
+       bank->num_sectors = num_sectors;
+       bank->size = num_sectors * sector_length;
+       bank->write_start_alignment = 0;
+       bank->write_end_alignment = 0;
+       cx32l003_bank->sector_length = sector_length;
+
+       for (unsigned int i = 0; i < num_sectors; i++) {
+               bank->sectors[i].offset = i * sector_length;
+               bank->sectors[i].size = sector_length;
+               bank->sectors[i].is_erased = -1;
+               bank->sectors[i].is_protected = 0;
+       }
+
+       /* We've successfully determined the stats on the flash bank */
+       cx32l003_bank->probed = true;
+
+       /* If we fall through to here, then all went well */
+       return ERROR_OK;
+}
+
+static int cx32l003_auto_probe(struct flash_bank *bank)
+{
+       struct cx32l003_flash_bank *cx32l003_bank = bank->driver_priv;
+       int retval = ERROR_OK;
+
+       if (!cx32l003_bank->probed)
+               retval = cx32l003_probe(bank);
+
+       return retval;
+}
+
+FLASH_BANK_COMMAND_HANDLER(cx32l003_flash_bank_command)
+{
+       struct cx32l003_flash_bank *cx32l003_bank;
+
+       if (CMD_ARGC < 6)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       cx32l003_bank = calloc(1, sizeof(struct cx32l003_flash_bank));
+       if (!cx32l003_bank) {
+               LOG_ERROR("Out of memory");
+               return ERROR_FAIL;
+       }
+
+       /* Initialize private flash information */
+       cx32l003_bank->family_name = "cx32l003";
+       cx32l003_bank->sector_length = CX32L003_FLASH_ERASE_SIZE;
+
+       /* Finish initialization of bank */
+       bank->driver_priv = cx32l003_bank;
+       bank->next = NULL;
+
+       return ERROR_OK;
+}
+
+static int cx32l003_chip_erase(struct flash_bank *bank)
+{
+       struct target *target = bank->target;
+       struct cx32l003_flash_bank *cx32l003_bank = bank->driver_priv;
+       struct cx32l003_flash_params algo_params;
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       /* Make sure we've probed the flash to get the device and size */
+       int retval = cx32l003_auto_probe(bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = cx32l003_init(bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* Set up algorithm parameters for chip erase command */
+       target_buffer_set_u32(target, (uint8_t *)&algo_params.cmd, 
CX32L003_FLASH_CMD_ERASE_ALL);
+       target_buffer_set_u32(target, (uint8_t *)&algo_params.sync, 
CX32L003_FLASH_LOADER_WAIT);
+
+       /* Set algorithm parameters */
+       retval = target_write_buffer(target, cx32l003_bank->params_addr,
+                               sizeof(algo_params), (uint8_t *)&algo_params);
+       if (retval != ERROR_OK) {
+               (void)cx32l003_quit(bank);
+               return retval;
+       }
+
+       /* Issue flash helper algorithm parameters for chip erase */
+       target_buffer_set_u32(target, (uint8_t *)&algo_params.sync, 
CX32L003_FLASH_LOADER_EXECUTE);
+       retval = target_write_buffer(target, cx32l003_bank->params_addr,
+                               sizeof(algo_params), (uint8_t *)&algo_params);
+
+       /* If no error, wait for chip erase finish */
+       if (retval == ERROR_OK)
+               retval = cx32l003_wait_algo_done(bank, 
cx32l003_bank->params_addr);
+
+       /* Regardless of errors, try to close down algo */
+       (void)cx32l003_quit(bank);
+
+       return retval;
+}
+
+static int cx32l003_erase(struct flash_bank *bank, unsigned int first,
+               unsigned int last)
+{
+       struct target *target = bank->target;
+       struct cx32l003_flash_bank *cx32l003_bank = bank->driver_priv;
+       struct cx32l003_flash_params algo_params;
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       if ((first == 0) && (last == (bank->num_sectors - 1))) {
+               /* Request chip erase */
+               return cx32l003_chip_erase(bank);
+       }
+
+       uint32_t address = first * cx32l003_bank->sector_length;
+       uint32_t length = (last - first + 1) * cx32l003_bank->sector_length;
+
+       /* Make sure we've probed the flash to get the device and size */
+       int retval = cx32l003_auto_probe(bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = cx32l003_init(bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* Set up algorithm parameters for erase command */
+       target_buffer_set_u32(target, (uint8_t *)&algo_params.addr, address);
+       target_buffer_set_u32(target, (uint8_t *)&algo_params.len, length);
+       target_buffer_set_u32(target, (uint8_t *)&algo_params.cmd, 
CX32L003_FLASH_CMD_ERASE_SECTORS);
+       target_buffer_set_u32(target, (uint8_t *)&algo_params.sync, 
CX32L003_FLASH_LOADER_WAIT);
+
+       /* Set algorithm parameters */
+       retval = target_write_buffer(target, cx32l003_bank->params_addr,
+                               sizeof(algo_params), (uint8_t *)&algo_params);
+       if (retval != ERROR_OK) {
+               (void)cx32l003_quit(bank);
+               return retval;
+       }
+
+       /* Issue flash helper algorithm parameters for erase */
+       target_buffer_set_u32(target, (uint8_t *)&algo_params.sync, 
CX32L003_FLASH_LOADER_EXECUTE);
+       retval = target_write_buffer(target, cx32l003_bank->params_addr,
+                               sizeof(algo_params), (uint8_t *)&algo_params);
+
+       /* If no error, wait for erase to finish */
+       if (retval == ERROR_OK)
+               retval = cx32l003_wait_algo_done(bank, 
cx32l003_bank->params_addr);
+
+       /* Regardless of errors, try to close down algo */
+       (void)cx32l003_quit(bank);
+
+       return retval;
+}
+
+static int cx32l003_write(struct flash_bank *bank, const uint8_t *buffer,
+       uint32_t offset, uint32_t count)
+{
+       struct target *target = bank->target;
+       struct cx32l003_flash_bank *cx32l003_bank = bank->driver_priv;
+       struct cx32l003_flash_params algo_params;
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       /* Make sure we've probed the flash to get the device and size */
+       int retval = cx32l003_auto_probe(bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = cx32l003_init(bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* Initialize algorithm parameters to default values */
+       target_buffer_set_u32(target, (uint8_t *)&algo_params.cmd, 
CX32L003_FLASH_CMD_PROGRAM);
+
+       uint32_t address = offset;
+
+       while (count > 0) {
+               uint32_t size = (count > CX32L003_FLASH_LOADER_BUFFER_SIZE) ?
+                                                       
CX32L003_FLASH_LOADER_BUFFER_SIZE : count;
+
+               /* Put the data into buffer */
+               retval = target_write_buffer(target, cx32l003_bank->buffer_addr,
+                                       size, buffer);
+               if (retval != ERROR_OK) {
+                       LOG_ERROR("Unable to write data to target memory");
+                       break;
+               }
+
+               /* Update algo parameters for flash write */
+               target_buffer_set_u32(target, (uint8_t *)&algo_params.addr, 
address);
+               target_buffer_set_u32(target, (uint8_t *)&algo_params.len, 
size);
+               target_buffer_set_u32(target, (uint8_t *)&algo_params.sync, 
CX32L003_FLASH_LOADER_WAIT);
+
+               /* Set algorithm parameters */
+               retval = target_write_buffer(target, cx32l003_bank->params_addr,
+                               sizeof(algo_params), (uint8_t *)&algo_params);
+               if (retval != ERROR_OK)
+                       break;
+
+               /* Issue flash helper algorithm parameters for flash write */
+               target_buffer_set_u32(target, (uint8_t *)&algo_params.sync, 
CX32L003_FLASH_LOADER_EXECUTE);
+               retval = target_write_buffer(target, cx32l003_bank->params_addr,
+                               sizeof(algo_params), (uint8_t *)&algo_params);
+               if (retval != ERROR_OK)
+                       break;
+
+               /* Wait for flash write finish */
+               retval = cx32l003_wait_algo_done(bank, 
cx32l003_bank->params_addr);
+               if (retval != ERROR_OK)
+                       break;
+
+               count -= size;
+               buffer += size;
+               address += size;
+       }
+
+       /* Regardless of errors, try to close down algo */
+       (void)cx32l003_quit(bank);
+
+       return retval;
+}
+
+static int cx32l003_info(struct flash_bank *bank, struct command_invocation 
*cmd)
+{
+       struct cx32l003_flash_bank *cx32l003_bank = bank->driver_priv;
+
+       command_print_sameline(cmd, "%s flash: %s\n",
+                                       cx32l003_bank->family_name,
+                                       flash_info[cx32l003_bank->flash].name);
+
+       return ERROR_OK;
+}
+
+const struct flash_driver cx32l003_flash = {
+       .name = "cx32l003",
+       .flash_bank_command = cx32l003_flash_bank_command,
+       .erase = cx32l003_erase,
+       .write = cx32l003_write,
+       .read = default_flash_read,
+       .probe = cx32l003_probe,
+       .auto_probe = cx32l003_auto_probe,
+       .erase_check = default_flash_blank_check,
+       .info = cx32l003_info,
+       .free_driver_priv = default_flash_free_driver_priv,
+};
diff --git a/src/flash/nor/driver.h b/src/flash/nor/driver.h
index a63b72c8fa..22b76b019e 100644
--- a/src/flash/nor/driver.h
+++ b/src/flash/nor/driver.h
@@ -253,6 +253,7 @@ extern const struct flash_driver bluenrgx_flash;
 extern const struct flash_driver cc26xx_flash;
 extern const struct flash_driver cc3220sf_flash;
 extern const struct flash_driver cfi_flash;
+extern const struct flash_driver cx32l003_flash;
 extern const struct flash_driver dsp5680xx_flash;
 extern const struct flash_driver efm32_flash;
 extern const struct flash_driver em357_flash;
diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c
index 3157bd3292..963c346379 100644
--- a/src/flash/nor/drivers.c
+++ b/src/flash/nor/drivers.c
@@ -30,6 +30,7 @@ static const struct flash_driver * const flash_drivers[] = {
        &cc3220sf_flash,
        &cc26xx_flash,
        &cfi_flash,
+       &cx32l003_flash,
        &dsp5680xx_flash,
        &efm32_flash,
        &em357_flash,
diff --git a/tcl/target/cx32l003.cfg b/tcl/target/cx32l003.cfg
new file mode 100644
index 0000000000..a061e389a0
--- /dev/null
+++ b/tcl/target/cx32l003.cfg
@@ -0,0 +1,89 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+# Based on stm32c0x.cfg
+# script for CX32L003 family tested only with CX32L003F8 B.VERNOUX 31 Aug 2023
+#
+# cx32l003 devices support SWD transports only.
+#
+
+source [find target/swj-dp.tcl]
+source [find mem_helper.tcl]
+
+if { [info exists CHIPNAME] } {
+       set _CHIPNAME $CHIPNAME
+} else {
+       set _CHIPNAME cx32l003
+}
+
+set _ENDIAN little
+
+# Work-area is a space in RAM used for flash programming
+# By default use 4kB
+if { [info exists WORKAREASIZE] } {
+       set _WORKAREASIZE $WORKAREASIZE
+} else {
+       set _WORKAREASIZE 0x1000
+}
+
+#jtag scan chain
+if { [info exists CPUTAPID] } {
+       set _CPUTAPID $CPUTAPID
+} else {
+       # SWD IDCODE (single drop, arm)
+       set _CPUTAPID 0x0bc11477
+}
+
+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 $_ENDIAN -dap $_CHIPNAME.dap
+
+$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size 
$_WORKAREASIZE -work-area-backup 0
+
+flash bank $_CHIPNAME.flash cx32l003 0x00000000 0 0 0 $_TARGETNAME
+
+# CX32L003F8 based on ARM® Cortex®-M0+ / ARM® v6-M Architecture
+# Mapping CX32L003F8
+# Name             AdrSpace    StartAdr    EndAdr      AccType
+# Flash            Memory      0x00000000  0x0000FFFF  R  
+# ROM              Memory      0x1FFFC800  0x1FFFFFFF  R  
+# RAM              Memory      0x20000000  0x20000FFF  RW 
+# PeriphAPB        Memory      0x40000000  0x40010000  W  
+# PeriphAHB        Memory      0x40020000  0x40030000  W  
+# SystemSFR        Memory      0xE0000000  0xE00FFFFF  W  
+
+# reasonable default
+adapter speed 2000
+
+adapter srst delay 100
+if {[using_jtag]} {
+       jtag_ntrst_delay 100
+}
+
+#reset_config srst_nogate
+reset_config trst_and_srst
+
+if {![using_hla]} {
+       # if srst is not fitted use SYSRESETREQ to
+       # perform a soft reset
+       cortex_m reset_config sysresetreq
+       echo "sysresetreq ..."
+}
+
+$_TARGETNAME configure -event examine-end {
+       # RCC_UNLOCK UNLOCK 
+       mww 0x40020060 0x55AA6699
+       # Enable RCC_PCLKEN |= DBGCKEN(bit20) | SYSCONCKEN(bit7)
+       mmw 0x4002000C 0x00100080 0
+       
+       # SYSCON_UNLOCK UNLOCK 
+       mww 0x40001C50 0x55AA6699
+       # SYSCON_CFGR0 |= KEY(0x5A690000) | DBGDLSP_DIS(bit1)
+       mmw 0x40001C00 0x5A690002 0
+       
+       # Stop watchdog counters during halt
+       # DBG_APBFZ |= KEY(0x5A690000) | WWDGDBGSTOP(bit10) | IWDGDBGSTOP(bit9)
+       mmw 0x40004C00 0x5A690600 0
+       echo "Disabling watchdog..."
+}

-- 

Reply via email to