From ef8184f1935cc62b576ce6b3c45b3b0cd7f9a033 Mon Sep 17 00:00:00 2001 From: wangyanwen Date: Mon, 9 Oct 2023 11:37:52 +0800 Subject: [PATCH] flash/nor: add cm32m4xxr/nuspi/custom flash driver - cm32m4xxr flash driver is for cm32m4xxr devices - nuspi flash driver is for nuclei evalsoc devices - custom flash driver is for customized flash driver without modify openocd source code, see https://github.com/riscv-mcu/openflashloader Change-Id: I86c4f0298707b5cfdfb77b6d8f4cbed3a189ddf0 Signed-off-by: wangyanwen --- contrib/loaders/flash/nuspi/Makefile | 51 + contrib/loaders/flash/nuspi/riscv.lds | 17 + contrib/loaders/flash/nuspi/riscv32_nuspi.inc | 75 ++ contrib/loaders/flash/nuspi/riscv64_nuspi.inc | 87 ++ contrib/loaders/flash/nuspi/riscv_nuspi.c | 349 +++++ contrib/loaders/flash/nuspi/riscv_wrapper.S | 22 + src/flash/nor/Makefile.am | 3 + src/flash/nor/cm32m4xxr.c | 781 +++++++++++ src/flash/nor/custom.c | 469 +++++++ src/flash/nor/driver.h | 3 + src/flash/nor/drivers.c | 3 + src/flash/nor/nuspi.c | 1168 +++++++++++++++++ 12 files changed, 3028 insertions(+) create mode 100644 contrib/loaders/flash/nuspi/Makefile create mode 100644 contrib/loaders/flash/nuspi/riscv.lds create mode 100644 contrib/loaders/flash/nuspi/riscv32_nuspi.inc create mode 100644 contrib/loaders/flash/nuspi/riscv64_nuspi.inc create mode 100644 contrib/loaders/flash/nuspi/riscv_nuspi.c create mode 100644 contrib/loaders/flash/nuspi/riscv_wrapper.S create mode 100644 src/flash/nor/cm32m4xxr.c create mode 100644 src/flash/nor/custom.c create mode 100644 src/flash/nor/nuspi.c diff --git a/contrib/loaders/flash/nuspi/Makefile b/contrib/loaders/flash/nuspi/Makefile new file mode 100644 index 000000000..1a1772850 --- /dev/null +++ b/contrib/loaders/flash/nuspi/Makefile @@ -0,0 +1,51 @@ +BIN2C = ../../../../src/helper/bin2char.sh + +CROSS_COMPILE ?= riscv-nuclei-elf- + +RISCV_CC=$(CROSS_COMPILE)gcc +RISCV_OBJCOPY=$(CROSS_COMPILE)objcopy +RISCV_OBJDUMP=$(CROSS_COMPILE)objdump + +CFLAGS = -nostdlib -nostartfiles -Wall -Werror -Os -fPIC -Wunused-result -g +RISCV32_CFLAGS = -march=rv32e -mabi=ilp32e $(CFLAGS) +RISCV64_CFLAGS = -march=rv64i -mabi=lp64 $(CFLAGS) + +all: riscv32_nuspi.inc riscv64_nuspi.inc + +.PHONY: clean + +# .c -> .o +riscv32_%.o: riscv_%.c + $(RISCV_CC) -c $(RISCV32_CFLAGS) $^ -o $@ + +riscv64_%.o: riscv_%.c + $(RISCV_CC) -c $(RISCV64_CFLAGS) $< -o $@ + +# .S -> .o +riscv32_%.o: riscv_%.S + $(RISCV_CC) -c $(RISCV32_CFLAGS) $^ -o $@ + +riscv64_%.o: riscv_%.S + $(RISCV_CC) -c $(RISCV64_CFLAGS) $^ -o $@ + +# .o -> .elf +riscv32_%.elf: riscv32_%.o riscv32_wrapper.o + $(RISCV_CC) -T riscv.lds $(RISCV32_CFLAGS) $^ -o $@ + +riscv64_%.elf: riscv64_%.o riscv64_wrapper.o + $(RISCV_CC) -T riscv.lds $(RISCV64_CFLAGS) $^ -o $@ + +# .elf -> .bin +%.bin: %.elf + $(RISCV_OBJCOPY) -Obinary $< $@ + +# .bin -> .inc +%.inc: %.bin + $(BIN2C) < $< > $@ + +# utility +%.lst: %.elf + $(RISCV_OBJDUMP) -S $< > $@ + +clean: + -rm -f *.elf *.o *.lst *.bin *.inc diff --git a/contrib/loaders/flash/nuspi/riscv.lds b/contrib/loaders/flash/nuspi/riscv.lds new file mode 100644 index 000000000..53cf0554d --- /dev/null +++ b/contrib/loaders/flash/nuspi/riscv.lds @@ -0,0 +1,17 @@ +OUTPUT_ARCH( "riscv" ) + +SECTIONS +{ + . = 0x12340000; + + .text : + { + *(.text.entry) + *(.text) + } + + .data : + { + *(.data) + } +} diff --git a/contrib/loaders/flash/nuspi/riscv32_nuspi.inc b/contrib/loaders/flash/nuspi/riscv32_nuspi.inc new file mode 100644 index 000000000..4c469ee74 --- /dev/null +++ b/contrib/loaders/flash/nuspi/riscv32_nuspi.inc @@ -0,0 +1,75 @@ +/* Autogenerated with ../../../../src/helper/bin2char.sh */ +0x17,0x01,0x00,0x00,0x13,0x01,0x01,0x4a,0xef,0x00,0x40,0x1d,0x73,0x00,0x10,0x00, +0x13,0x76,0x16,0x00,0x93,0x07,0x10,0x7d,0x63,0x18,0x06,0x02,0x93,0x87,0xf7,0xff, +0x63,0x88,0x07,0x02,0x03,0x27,0x85,0x04,0xe3,0x4a,0x07,0xfe,0x6f,0x00,0x00,0x01, +0x03,0x27,0xc5,0x07,0x13,0x77,0x07,0x01,0x63,0x18,0x07,0x00,0x23,0x24,0xb5,0x04, +0x13,0x05,0x00,0x00,0x67,0x80,0x00,0x00,0x93,0x87,0xf7,0xff,0xe3,0x92,0x07,0xfe, +0x13,0x05,0x00,0x10,0x67,0x80,0x00,0x00,0x93,0xf5,0x15,0x00,0x93,0x07,0x10,0x7d, +0x63,0x98,0x05,0x02,0x93,0x87,0xf7,0xff,0x63,0x88,0x07,0x02,0x03,0x27,0x45,0x07, +0x13,0x77,0x17,0x00,0xe3,0x08,0x07,0xfe,0x6f,0x00,0x00,0x01,0x03,0x27,0xc5,0x07, +0x13,0x77,0x17,0x00,0x63,0x16,0x07,0x00,0x13,0x05,0x00,0x00,0x67,0x80,0x00,0x00, +0x93,0x87,0xf7,0xff,0xe3,0x94,0x07,0xfe,0x13,0x05,0x00,0x01,0x67,0x80,0x00,0x00, +0x83,0x27,0x05,0x04,0x13,0x01,0x01,0xff,0x23,0x24,0x81,0x00,0x23,0x22,0x91,0x00, +0x23,0x26,0x11,0x00,0x93,0xf7,0x77,0xff,0x23,0x20,0xf5,0x04,0x93,0x07,0x20,0x00, +0x23,0x2c,0xf5,0x00,0x03,0xa6,0x45,0x00,0x93,0x84,0x05,0x00,0x93,0x05,0x50,0x00, +0x13,0x04,0x05,0x00,0xef,0xf0,0xdf,0xf3,0xb7,0x07,0x01,0x00,0x63,0x18,0x05,0x08, +0x83,0xa7,0x44,0x00,0x93,0xf7,0x17,0x00,0x63,0x90,0x07,0x04,0x93,0x07,0x10,0x7d, +0x93,0x87,0xf7,0xff,0x63,0x80,0x07,0x04,0x03,0x27,0xc4,0x04,0xe3,0x4a,0x07,0xfe, +0x6f,0x00,0x40,0x01,0x03,0x27,0xc4,0x07,0x13,0x77,0x07,0x02,0x63,0x10,0x07,0x02, +0x83,0x27,0xc4,0x04,0x13,0x07,0x10,0x7d,0x13,0x07,0xf7,0xff,0x63,0x18,0x07,0x02, +0x37,0x05,0x05,0x00,0x6f,0x00,0x40,0x01,0x93,0x07,0x10,0x7d,0x93,0x87,0xf7,0xff, +0xe3,0x9a,0x07,0xfc,0x37,0x15,0x02,0x00,0x83,0x20,0xc1,0x00,0x03,0x24,0x81,0x00, +0x83,0x24,0x41,0x00,0x13,0x01,0x01,0x01,0x67,0x80,0x00,0x00,0x03,0xa6,0x44,0x00, +0x93,0x05,0x00,0x00,0x13,0x05,0x04,0x00,0x23,0x20,0xe1,0x00,0xef,0xf0,0x5f,0xeb, +0x03,0x27,0x01,0x00,0x63,0x08,0x05,0x00,0xb7,0x07,0x03,0x00,0x33,0x65,0xf5,0x00, +0x6f,0xf0,0x9f,0xfc,0x83,0xa7,0x44,0x00,0x93,0x06,0x10,0x7d,0x93,0xf7,0x17,0x00, +0x63,0x88,0x07,0x04,0x93,0x07,0x10,0x7d,0x93,0x87,0xf7,0xff,0x63,0x96,0x07,0x00, +0x37,0x15,0x04,0x00,0x6f,0xf0,0x5f,0xfa,0x83,0x26,0xc4,0x07,0x93,0xf6,0x06,0x02, +0xe3,0x94,0x06,0xfe,0x83,0x27,0xc4,0x04,0x93,0xf7,0xf7,0x0f,0x93,0xf7,0x17,0x00, +0xe3,0x94,0x07,0xf6,0x23,0x2c,0x04,0x00,0x83,0x27,0x04,0x04,0x93,0xe7,0x87,0x00, +0x23,0x20,0xf4,0x04,0x6f,0xf0,0x5f,0xf7,0x83,0x27,0xc4,0x04,0xe3,0xde,0x07,0xfc, +0x93,0x86,0xf6,0xff,0xe3,0x9a,0x06,0xfe,0x6f,0xf0,0x9f,0xfb,0x13,0x01,0xc1,0xfc, +0x23,0x24,0xe1,0x00,0x03,0x27,0xc5,0x01,0x23,0x20,0xf1,0x02,0xb7,0x07,0x01,0x00, +0x23,0x26,0x81,0x02,0x23,0x24,0x91,0x02,0x23,0x28,0x11,0x02,0x23,0x28,0xb1,0x00, +0x23,0x26,0xc1,0x00,0x23,0x22,0x01,0x02,0x93,0x87,0xf7,0x0f,0x13,0x04,0x05,0x00, +0x93,0x84,0x06,0x00,0x63,0xf6,0xe7,0x00,0x93,0x07,0x10,0x00,0x23,0x22,0xf1,0x02, +0x83,0x25,0x41,0x02,0x13,0x05,0x04,0x00,0xef,0xf0,0x1f,0xe3,0x93,0x67,0x15,0x00, +0x63,0x12,0x05,0x02,0x93,0x07,0x01,0x02,0x93,0x85,0x07,0x00,0x13,0x05,0x04,0x00, +0x23,0x2a,0xf1,0x00,0xef,0xf0,0xdf,0xe5,0x23,0x20,0xa1,0x00,0x63,0x08,0x05,0x00, +0x93,0x67,0x25,0x00,0x23,0x20,0xf1,0x00,0x6f,0x00,0x80,0x01,0x83,0x27,0x01,0x01, +0x93,0x87,0xf7,0xff,0xb3,0xf7,0x97,0x00,0x03,0x27,0x81,0x00,0x63,0x1e,0x07,0x00, +0x83,0x20,0x01,0x03,0x03,0x24,0xc1,0x02,0x03,0x25,0x01,0x00,0x83,0x24,0x81,0x02, +0x13,0x01,0x41,0x03,0x67,0x80,0x00,0x00,0x83,0x26,0x81,0x00,0x03,0x27,0x81,0x00, +0x23,0x22,0xd1,0x00,0x83,0x26,0x01,0x01,0x33,0x07,0xf7,0x00,0x63,0xf6,0xe6,0x00, +0xb3,0x87,0xf6,0x40,0x23,0x22,0xf1,0x00,0x03,0x26,0x41,0x02,0x93,0x05,0x60,0x00, +0x13,0x05,0x04,0x00,0xef,0xf0,0xdf,0xd5,0x63,0x0a,0x05,0x00,0xb7,0x04,0x10,0x00, +0xb3,0x64,0x95,0x00,0x93,0xe7,0x34,0x00,0x6f,0xf0,0xdf,0xf8,0x83,0x25,0x41,0x02, +0x13,0x05,0x04,0x00,0xef,0xf0,0x5f,0xd8,0x63,0x06,0x05,0x00,0xb7,0x04,0x20,0x00, +0x6f,0xf0,0x1f,0xfe,0x03,0x26,0x41,0x02,0x83,0x45,0x01,0x02,0x93,0x07,0x20,0x00, +0x23,0x2c,0xf4,0x00,0x13,0x05,0x04,0x00,0xef,0xf0,0x9f,0xd1,0x63,0x06,0x05,0x00, +0xb7,0x04,0x30,0x00,0x6f,0xf0,0xdf,0xfb,0x83,0x27,0x01,0x02,0x93,0xf7,0x07,0x10, +0x63,0x80,0x07,0x02,0x03,0x26,0x41,0x02,0x93,0xd5,0x84,0x01,0x13,0x05,0x04,0x00, +0xef,0xf0,0x1f,0xcf,0x63,0x06,0x05,0x00,0xb7,0x04,0x40,0x00,0x6f,0xf0,0x5f,0xf9, +0x03,0x26,0x41,0x02,0x93,0xd5,0x04,0x01,0x93,0xf5,0xf5,0x0f,0x13,0x05,0x04,0x00, +0xef,0xf0,0x1f,0xcd,0xe3,0x12,0x05,0xfe,0x03,0x26,0x41,0x02,0x93,0xd5,0x84,0x00, +0x93,0xf5,0xf5,0x0f,0x13,0x05,0x04,0x00,0xef,0xf0,0x9f,0xcb,0x63,0x06,0x05,0x00, +0xb7,0x04,0x50,0x00,0x6f,0xf0,0xdf,0xf5,0x03,0x26,0x41,0x02,0x93,0xf5,0xf4,0x0f, +0x13,0x05,0x04,0x00,0xef,0xf0,0xdf,0xc9,0x63,0x18,0x05,0x02,0x83,0x27,0xc1,0x00, +0x03,0x27,0x41,0x00,0x33,0x07,0xf7,0x00,0x03,0x26,0x41,0x02,0x63,0x12,0xf7,0x02, +0x93,0x05,0x06,0x00,0x13,0x05,0x04,0x00,0xef,0xf0,0x1f,0xcc,0x63,0x00,0x05,0x04, +0xb7,0x04,0x80,0x00,0x6f,0xf0,0xdf,0xf1,0xb7,0x04,0x60,0x00,0x6f,0xf0,0x5f,0xf1, +0x83,0xc5,0x07,0x00,0x13,0x05,0x04,0x00,0x23,0x2e,0xe1,0x00,0x23,0x2c,0xf1,0x00, +0xef,0xf0,0x1f,0xc5,0x83,0x27,0x81,0x01,0x03,0x27,0xc1,0x01,0x93,0x87,0x17,0x00, +0xe3,0x0c,0x05,0xfa,0xb7,0x04,0x70,0x00,0x6f,0xf0,0x9f,0xee,0x83,0x25,0x41,0x01, +0x23,0x2c,0x04,0x00,0x13,0x05,0x04,0x00,0xef,0xf0,0x9f,0xcb,0x63,0x16,0x05,0x02, +0x03,0x27,0x41,0x00,0x83,0x27,0xc1,0x00,0xb3,0x84,0xe4,0x00,0xb3,0x87,0xe7,0x00, +0x23,0x26,0xf1,0x00,0x83,0x27,0x81,0x00,0xb3,0x87,0xe7,0x40,0x23,0x24,0xf1,0x00, +0x93,0x07,0x00,0x00,0x6f,0xf0,0x5f,0xe5,0xb7,0x04,0x90,0x00,0x6f,0xf0,0x5f,0xea, +0x09,0x53,0x67,0x08,0x09,0x53,0x67,0x08,0x09,0x53,0x67,0x08,0x09,0x53,0x67,0x08, +0x09,0x53,0x67,0x08,0x09,0x53,0x67,0x08,0x09,0x53,0x67,0x08,0x09,0x53,0x67,0x08, +0x09,0x53,0x67,0x08,0x09,0x53,0x67,0x08,0x09,0x53,0x67,0x08,0x09,0x53,0x67,0x08, +0x09,0x53,0x67,0x08,0x09,0x53,0x67,0x08,0x09,0x53,0x67,0x08,0x09,0x53,0x67,0x08, +0x09,0x53,0x67,0x08,0x09,0x53,0x67,0x08,0x09,0x53,0x67,0x08,0x09,0x53,0x67,0x08, +0x09,0x53,0x67,0x08,0x09,0x53,0x67,0x08,0x09,0x53,0x67,0x08,0x09,0x53,0x67,0x08, +0x09,0x53,0x67,0x08,0x09,0x53,0x67,0x08,0x09,0x53,0x67,0x08,0x09,0x53,0x67,0x08, +0x09,0x53,0x67,0x08,0x09,0x53,0x67,0x08,0x09,0x53,0x67,0x08,0x09,0x53,0x67,0x08, diff --git a/contrib/loaders/flash/nuspi/riscv64_nuspi.inc b/contrib/loaders/flash/nuspi/riscv64_nuspi.inc new file mode 100644 index 000000000..3f934b35c --- /dev/null +++ b/contrib/loaders/flash/nuspi/riscv64_nuspi.inc @@ -0,0 +1,87 @@ +/* Autogenerated with ../../../../src/helper/bin2char.sh */ +0x17,0x01,0x00,0x00,0x13,0x01,0x01,0x56,0xef,0x00,0x00,0x1f,0x73,0x00,0x10,0x00, +0x13,0x76,0x16,0x00,0x93,0x07,0x10,0x7d,0x63,0x1c,0x06,0x02,0x9b,0x87,0xf7,0xff, +0x63,0x8c,0x07,0x02,0x03,0x27,0x85,0x04,0x1b,0x07,0x07,0x00,0xe3,0x48,0x07,0xfe, +0x6f,0x00,0x00,0x01,0x03,0x27,0xc5,0x07,0x13,0x77,0x07,0x01,0x63,0x1a,0x07,0x00, +0x9b,0x85,0x05,0x00,0x23,0x24,0xb5,0x04,0x13,0x05,0x00,0x00,0x67,0x80,0x00,0x00, +0x9b,0x87,0xf7,0xff,0xe3,0x90,0x07,0xfe,0x13,0x05,0x00,0x10,0x67,0x80,0x00,0x00, +0x93,0xf5,0x15,0x00,0x93,0x07,0x10,0x7d,0x63,0x98,0x05,0x02,0x9b,0x87,0xf7,0xff, +0x63,0x88,0x07,0x02,0x03,0x27,0x45,0x07,0x13,0x77,0x17,0x00,0xe3,0x08,0x07,0xfe, +0x6f,0x00,0x00,0x01,0x03,0x27,0xc5,0x07,0x13,0x77,0x17,0x00,0x63,0x16,0x07,0x00, +0x13,0x05,0x00,0x00,0x67,0x80,0x00,0x00,0x9b,0x87,0xf7,0xff,0xe3,0x94,0x07,0xfe, +0x13,0x05,0x00,0x01,0x67,0x80,0x00,0x00,0x83,0x27,0x05,0x04,0x13,0x01,0x01,0xfe, +0x23,0x38,0x81,0x00,0x9b,0x87,0x07,0x00,0x23,0x34,0x91,0x00,0x23,0x3c,0x11,0x00, +0x23,0x30,0x21,0x01,0x93,0xf7,0x77,0xff,0x23,0x20,0xf5,0x04,0x93,0x07,0x20,0x00, +0x23,0x2c,0xf5,0x00,0x03,0xa6,0x45,0x00,0x93,0x84,0x05,0x00,0x93,0x05,0x50,0x00, +0x13,0x04,0x05,0x00,0xef,0xf0,0xdf,0xf2,0xb7,0x07,0x01,0x00,0x63,0x18,0x05,0x08, +0x83,0xa7,0x44,0x00,0x93,0xf7,0x17,0x00,0x63,0x92,0x07,0x04,0x93,0x07,0x10,0x7d, +0x9b,0x87,0xf7,0xff,0x63,0x82,0x07,0x04,0x03,0x27,0xc4,0x04,0x1b,0x07,0x07,0x00, +0xe3,0x48,0x07,0xfe,0x6f,0x00,0x40,0x01,0x03,0x27,0xc4,0x07,0x13,0x77,0x07,0x02, +0x63,0x10,0x07,0x02,0x83,0x27,0xc4,0x04,0x13,0x09,0x10,0x7d,0x1b,0x09,0xf9,0xff, +0x63,0x1a,0x09,0x02,0x37,0x05,0x05,0x00,0x6f,0x00,0x40,0x01,0x93,0x07,0x10,0x7d, +0x9b,0x87,0xf7,0xff,0xe3,0x9a,0x07,0xfc,0x37,0x15,0x02,0x00,0x83,0x30,0x81,0x01, +0x03,0x34,0x01,0x01,0x83,0x34,0x81,0x00,0x03,0x39,0x01,0x00,0x13,0x01,0x01,0x02, +0x67,0x80,0x00,0x00,0x03,0xa6,0x44,0x00,0x93,0x05,0x00,0x00,0x13,0x05,0x04,0x00, +0xef,0xf0,0x1f,0xea,0x63,0x0a,0x05,0x00,0xb7,0x07,0x03,0x00,0x33,0x65,0xf5,0x00, +0x1b,0x05,0x05,0x00,0x6f,0xf0,0x9f,0xfc,0x83,0xa7,0x44,0x00,0x93,0x06,0x10,0x7d, +0x93,0xf7,0x17,0x00,0x63,0x8c,0x07,0x04,0x93,0x07,0x10,0x7d,0x9b,0x87,0xf7,0xff, +0x63,0x96,0x07,0x00,0x37,0x15,0x04,0x00,0x6f,0xf0,0x5f,0xfa,0x03,0x27,0xc4,0x07, +0x13,0x77,0x07,0x02,0xe3,0x14,0x07,0xfe,0x83,0x27,0xc4,0x04,0x93,0xf7,0xf7,0x0f, +0x93,0xf7,0x17,0x00,0xe3,0x94,0x07,0xf6,0x23,0x2c,0x04,0x00,0x83,0x27,0x04,0x04, +0x9b,0x87,0x07,0x00,0x93,0xe7,0x87,0x00,0x23,0x20,0xf4,0x04,0x6f,0xf0,0x1f,0xf7, +0x03,0x27,0xc4,0x04,0x9b,0x07,0x07,0x00,0xe3,0xda,0x07,0xfc,0x9b,0x86,0xf6,0xff, +0xe3,0x98,0x06,0xfe,0x6f,0xf0,0x1f,0xfb,0x13,0x01,0x01,0xf9,0x23,0x34,0x31,0x05, +0x93,0x09,0x07,0x00,0x03,0x27,0xc5,0x01,0x23,0x24,0xf1,0x00,0xb7,0x07,0x01,0x00, +0x23,0x3c,0x91,0x04,0x23,0x38,0x21,0x05,0x23,0x30,0x41,0x05,0x23,0x3c,0x51,0x03, +0x23,0x34,0x11,0x06,0x23,0x30,0x81,0x06,0x23,0x38,0x61,0x03,0x23,0x34,0x71,0x03, +0x23,0x30,0x81,0x03,0x23,0x3c,0x91,0x01,0x23,0x26,0x01,0x00,0x1b,0x07,0x07,0x00, +0x93,0x87,0xf7,0x0f,0x93,0x04,0x05,0x00,0x13,0x8a,0x05,0x00,0x93,0x0a,0x06,0x00, +0x13,0x89,0x06,0x00,0x63,0xf6,0xe7,0x00,0x93,0x07,0x10,0x00,0x23,0x26,0xf1,0x00, +0x83,0x25,0xc1,0x00,0x13,0x85,0x04,0x00,0xef,0xf0,0x9f,0xdf,0x63,0x02,0x05,0x04, +0x13,0x65,0x15,0x00,0x1b,0x04,0x05,0x00,0x83,0x30,0x81,0x06,0x13,0x05,0x04,0x00, +0x03,0x34,0x01,0x06,0x83,0x34,0x81,0x05,0x03,0x39,0x01,0x05,0x83,0x39,0x81,0x04, +0x03,0x3a,0x01,0x04,0x83,0x3a,0x81,0x03,0x03,0x3b,0x01,0x03,0x83,0x3b,0x81,0x02, +0x03,0x3c,0x01,0x02,0x83,0x3c,0x81,0x01,0x13,0x01,0x01,0x07,0x67,0x80,0x00,0x00, +0x93,0x0b,0x81,0x00,0x93,0x85,0x0b,0x00,0x13,0x85,0x04,0x00,0xef,0xf0,0xdf,0xde, +0x13,0x04,0x05,0x00,0x63,0x06,0x05,0x00,0x13,0x65,0x25,0x00,0x6f,0xf0,0x9f,0xfa, +0x9b,0x07,0xfa,0xff,0xb3,0x77,0xf9,0x00,0x9b,0x87,0x07,0x00,0x13,0x0c,0x20,0x00, +0xe3,0x8c,0x09,0xf8,0x3b,0x87,0xf9,0x00,0x13,0x8b,0x09,0x00,0x63,0x74,0xea,0x00, +0x3b,0x0b,0xfa,0x40,0x03,0x26,0xc1,0x00,0x93,0x05,0x60,0x00,0x13,0x85,0x04,0x00, +0xef,0xf0,0x1f,0xd1,0x63,0x0c,0x05,0x00,0xb7,0x07,0x10,0x00,0x33,0xe5,0xa7,0x00, +0x1b,0x05,0x05,0x00,0x13,0x64,0x35,0x00,0x6f,0xf0,0x1f,0xf6,0x83,0x25,0xc1,0x00, +0x13,0x85,0x04,0x00,0xef,0xf0,0xdf,0xd3,0x63,0x06,0x05,0x00,0xb7,0x07,0x20,0x00, +0x6f,0xf0,0xdf,0xfd,0x03,0x26,0xc1,0x00,0x83,0x45,0x81,0x00,0x23,0xac,0x84,0x01, +0x13,0x85,0x04,0x00,0xef,0xf0,0xdf,0xcc,0x63,0x06,0x05,0x00,0xb7,0x07,0x30,0x00, +0x6f,0xf0,0xdf,0xfb,0x83,0x27,0x81,0x00,0x93,0xf7,0x07,0x10,0x63,0x80,0x07,0x02, +0x03,0x26,0xc1,0x00,0x9b,0x55,0x89,0x01,0x13,0x85,0x04,0x00,0xef,0xf0,0x5f,0xca, +0x63,0x06,0x05,0x00,0xb7,0x07,0x40,0x00,0x6f,0xf0,0x5f,0xf9,0x03,0x26,0xc1,0x00, +0x9b,0x55,0x09,0x01,0x93,0xf5,0xf5,0x0f,0x13,0x85,0x04,0x00,0xef,0xf0,0x5f,0xc8, +0xe3,0x12,0x05,0xfe,0x03,0x26,0xc1,0x00,0x9b,0x55,0x89,0x00,0x93,0xf5,0xf5,0x0f, +0x13,0x85,0x04,0x00,0xef,0xf0,0xdf,0xc6,0x63,0x06,0x05,0x00,0xb7,0x07,0x50,0x00, +0x6f,0xf0,0xdf,0xf5,0x03,0x26,0xc1,0x00,0x93,0x75,0xf9,0x0f,0x13,0x85,0x04,0x00, +0xef,0xf0,0x1f,0xc5,0x63,0x08,0x05,0x02,0xb7,0x07,0x60,0x00,0x6f,0xf0,0x1f,0xf4, +0xb3,0x87,0x9a,0x01,0x83,0xc5,0x07,0x00,0x13,0x85,0x04,0x00,0x93,0x8c,0x1c,0x00, +0xef,0xf0,0x1f,0xc3,0x93,0x07,0x05,0x00,0x63,0x08,0x05,0x00,0x37,0x05,0x70,0x00, +0x6f,0xf0,0xdf,0xf1,0x93,0x0c,0x00,0x00,0x9b,0x87,0x0c,0x00,0x03,0x26,0xc1,0x00, +0xe3,0xe8,0x67,0xfd,0x93,0x05,0x06,0x00,0x13,0x85,0x04,0x00,0xef,0xf0,0x5f,0xc5, +0x93,0x07,0x05,0x00,0x63,0x06,0x05,0x00,0x37,0x05,0x80,0x00,0x6f,0xf0,0x1f,0xef, +0x23,0xac,0x04,0x00,0x93,0x85,0x0b,0x00,0x13,0x85,0x04,0x00,0xef,0xf0,0xdf,0xc7, +0x93,0x07,0x05,0x00,0x63,0x10,0x05,0x02,0x93,0x17,0x0b,0x02,0x93,0xd7,0x07,0x02, +0xb3,0x8a,0xfa,0x00,0x3b,0x09,0x69,0x01,0xbb,0x89,0x69,0x41,0x93,0x07,0x00,0x00, +0x6f,0xf0,0x1f,0xe9,0x37,0x05,0x90,0x00,0x6f,0xf0,0x5f,0xeb,0x00,0x00,0x00,0x00, +0x09,0x53,0x67,0x08,0x00,0x00,0x00,0x00,0x09,0x53,0x67,0x08,0x00,0x00,0x00,0x00, +0x09,0x53,0x67,0x08,0x00,0x00,0x00,0x00,0x09,0x53,0x67,0x08,0x00,0x00,0x00,0x00, +0x09,0x53,0x67,0x08,0x00,0x00,0x00,0x00,0x09,0x53,0x67,0x08,0x00,0x00,0x00,0x00, +0x09,0x53,0x67,0x08,0x00,0x00,0x00,0x00,0x09,0x53,0x67,0x08,0x00,0x00,0x00,0x00, +0x09,0x53,0x67,0x08,0x00,0x00,0x00,0x00,0x09,0x53,0x67,0x08,0x00,0x00,0x00,0x00, +0x09,0x53,0x67,0x08,0x00,0x00,0x00,0x00,0x09,0x53,0x67,0x08,0x00,0x00,0x00,0x00, +0x09,0x53,0x67,0x08,0x00,0x00,0x00,0x00,0x09,0x53,0x67,0x08,0x00,0x00,0x00,0x00, +0x09,0x53,0x67,0x08,0x00,0x00,0x00,0x00,0x09,0x53,0x67,0x08,0x00,0x00,0x00,0x00, +0x09,0x53,0x67,0x08,0x00,0x00,0x00,0x00,0x09,0x53,0x67,0x08,0x00,0x00,0x00,0x00, +0x09,0x53,0x67,0x08,0x00,0x00,0x00,0x00,0x09,0x53,0x67,0x08,0x00,0x00,0x00,0x00, +0x09,0x53,0x67,0x08,0x00,0x00,0x00,0x00,0x09,0x53,0x67,0x08,0x00,0x00,0x00,0x00, +0x09,0x53,0x67,0x08,0x00,0x00,0x00,0x00,0x09,0x53,0x67,0x08,0x00,0x00,0x00,0x00, +0x09,0x53,0x67,0x08,0x00,0x00,0x00,0x00,0x09,0x53,0x67,0x08,0x00,0x00,0x00,0x00, +0x09,0x53,0x67,0x08,0x00,0x00,0x00,0x00,0x09,0x53,0x67,0x08,0x00,0x00,0x00,0x00, +0x09,0x53,0x67,0x08,0x00,0x00,0x00,0x00,0x09,0x53,0x67,0x08,0x00,0x00,0x00,0x00, +0x09,0x53,0x67,0x08,0x00,0x00,0x00,0x00,0x09,0x53,0x67,0x08,0x00,0x00,0x00,0x00, diff --git a/contrib/loaders/flash/nuspi/riscv_nuspi.c b/contrib/loaders/flash/nuspi/riscv_nuspi.c new file mode 100644 index 000000000..292995962 --- /dev/null +++ b/contrib/loaders/flash/nuspi/riscv_nuspi.c @@ -0,0 +1,349 @@ +#include +#include +#include + +#include "../../../../src/flash/nor/spi.h" + +/* Register offsets */ + +#define NUSPI_REG_SCKDIV (0x00) +#define NUSPI_REG_SCKMODE (0x04) +#define NUSPI_REG_SCKSAMPLE (0x08) +#define NUSPI_REG_FORCE (0x0C) +#define NUSPI_REG_CSID (0x10) +#define NUSPI_REG_CSDEF (0x14) +#define NUSPI_REG_CSMODE (0x18) +#define NUSPI_REG_VERSION (0x1C) + +#define NUSPI_REG_DCSSCK (0x28) +#define NUSPI_REG_DSCKCS (0x2a) +#define NUSPI_REG_DINTERCS (0x2c) +#define NUSPI_REG_DINTERXFR (0x2e) + +#define NUSPI_REG_FMT (0x40) + +#define NUSPI_REG_TXDATA (0x48) +#define NUSPI_REG_RXDATA (0x4C) +#define NUSPI_REG_TXMARK (0x50) +#define NUSPI_REG_RXMARK (0x54) + +#define NUSPI_REG_FCTRL (0x60) +#define NUSPI_REG_FFMT (0x64) + +#define NUSPI_REG_IE (0x70) +#define NUSPI_REG_IP (0x74) +#define NUSPI_REG_FFMT1 (0x78) +#define NUSPI_REG_STATUS (0x7C) +#define NUSPI_REG_RXEDGE (0x80) +#define NUSPI_REG_CR (0x84) + +/* Fields */ + +#define NUSPI_SCK_POL (0x1) +#define NUSPI_SCK_PHA (0x2) + +#define NUSPI_FMT_PROTO(x) ((x) & 0x3) +#define NUSPI_FMT_ENDIAN(x) (((x) & 0x1) << 2) +#define NUSPI_FMT_DIR(x) (((x) & 0x1) << 3) +#define NUSPI_FMT_LEN(x) (((x) & 0xf) << 16)//TODO: + +/* TXMARK register */ +#define NUSPI_TXWM(x) ((x) & 0xFFFF)//TODO: +/* RXMARK register */ +#define NUSPI_RXWM(x) ((x) & 0xFFFF)//TODO: + +#define NUSPI_IP_TXWM (0x1) +#define NUSPI_IP_RXWM (0x2) + +#define NUSPI_FCTRL_EN (0x1) + +#define NUSPI_INSN_CMD_EN (0x1) +#define NUSPI_INSN_ADDR_LEN(x) (((x) & 0x7) << 1) +#define NUSPI_INSN_PAD_CNT(x) (((x) & 0xf) << 4) +#define NUSPI_INSN_CMD_PROTO(x) (((x) & 0x3) << 8) +#define NUSPI_INSN_ADDR_PROTO(x) (((x) & 0x3) << 10) +#define NUSPI_INSN_DATA_PROTO(x) (((x) & 0x3) << 12) +#define NUSPI_INSN_CMD_CODE(x) (((x) & 0xff) << 16) +#define NUSPI_INSN_PAD_CODE(x) (((x) & 0xff) << 24) + +#define NUSPI_STAT_BUSY (0x1 << 0) +#define NUSPI_STAT_TXFULL (0x1 << 4) +#define NUSPI_STAT_RXEMPTY (0x1 << 5) + +/* Values */ +#define SPIFLASH_3BYTE_FAST_READ (0x03) +#define SPIFLASH_4BYTE_FAST_READ (0x13) +#define SPIFLASH_ENTER_4BYTE (0xB7) +#define SPIFLASH_EXIT_4BYTE (0xE9) +#define SPIFLASH_ENABLE_RESET (0x66) +#define SPIFLASH_RESET_DEVICE (0x99) + +#define NUSPI_CSMODE_AUTO (0) +#define NUSPI_CSMODE_HOLD (2) +#define NUSPI_CSMODE_OFF (3) + +#define NUSPI_DIR_RX (0) +#define NUSPI_DIR_TX (1) + +#define NUSPI_PROTO_S (0) +#define NUSPI_PROTO_D (1) +#define NUSPI_PROTO_Q (2) + +#define NUSPI_ENDIAN_MSB (0) +#define NUSPI_ENDIAN_LSB (1) + +/* Timeouts we use, in number of status checks. */ +#define TIMEOUT (2000) + +#define NUSPI_FLAGS_32B_DAT (1 << 0) + +/* #define DEBUG to make the return error codes provide enough information to + * reconstruct the stack from where the error occurred. This is not enabled + * usually to reduce the program size. */ +#define ERROR_STACK(x) (x) +#define ERROR_NUSPI_TXWM_WAIT (0x10) +#define ERROR_NUSPI_TX (0x100) +#define ERROR_NUSPI_RX (0x1000) +#define ERROR_NUSPI_WIP (0x50000) + +#define ERROR_OK (0) + +typedef struct +{ + uint32_t info; + uint32_t flags; +} nuspi_info_t; + +static uint32_t nuspi_read_reg(volatile uint32_t *ctrl_base, uint32_t address); +static int nuspi_txwm_wait(volatile uint32_t *ctrl_base, nuspi_info_t* nuspi_info); +static int nuspi_wip(volatile uint32_t *ctrl_base, nuspi_info_t* nuspi_info); +static int nuspi_write_buffer(volatile uint32_t *ctrl_base, + const uint8_t *buffer, uint32_t offset, uint32_t len, + nuspi_info_t* nuspi_info); + +/* Can set bits 3:0 in result. */ +/* flash_info contains: + * bits 7:0 -- pprog_cmd + * bit 8 -- 0 means send 3 bytes after pprog_cmd, 1 means send 4 bytes + * after pprog_cmd + */ +int flash_nuspi(volatile uint32_t *ctrl_base, uint32_t page_size, + const uint8_t *buffer, uint32_t offset, uint32_t count, + uint32_t flash_info) +{ + int result; + nuspi_info_t nuspi_info = {0}; + nuspi_info.info = flash_info; + if(nuspi_read_reg(ctrl_base, NUSPI_REG_VERSION) >= 0x00010100) + nuspi_info.flags |= NUSPI_FLAGS_32B_DAT; + + result = nuspi_txwm_wait(ctrl_base, &nuspi_info); + if (result != ERROR_OK) + return result | ERROR_STACK(0x1); + + /* poll WIP */ + result = nuspi_wip(ctrl_base, &nuspi_info); + if (result != ERROR_OK) { + result |= ERROR_STACK(0x2); + goto err; + } + + /* Assume page_size is a power of two so we don't need the modulus code. */ + uint32_t page_offset = offset & (page_size - 1); + + /* central part, aligned words */ + while (count > 0) { + uint32_t cur_count; + /* clip block at page boundary */ + if (page_offset + count > page_size) + cur_count = page_size - page_offset; + else + cur_count = count; + + result = nuspi_write_buffer(ctrl_base, buffer, offset, cur_count, &nuspi_info); + if (result != ERROR_OK) { + result |= ERROR_STACK(0x3); + goto err; + } + + page_offset = 0; + buffer += cur_count; + offset += cur_count; + count -= cur_count; + } + +err: + return result; +} + +static uint32_t nuspi_read_reg(volatile uint32_t *ctrl_base, uint32_t address) +{ + return ctrl_base[address / 4]; +} + +static void nuspi_write_reg(volatile uint32_t *ctrl_base, uint32_t address, uint32_t value) +{ + ctrl_base[address / 4] = value; +} + +/* Can set bits 7:4 in result. */ +static int nuspi_txwm_wait(volatile uint32_t *ctrl_base, nuspi_info_t* nuspi_info) +{ + unsigned timeout = TIMEOUT; + + if (nuspi_info->flags & NUSPI_FLAGS_32B_DAT) { + while (timeout--) { + uint32_t status = nuspi_read_reg(ctrl_base, NUSPI_REG_STATUS); + if (0 == (status & NUSPI_STAT_BUSY)) + return ERROR_OK; + } + } else { + while (timeout--) { + uint32_t ip = nuspi_read_reg(ctrl_base, NUSPI_REG_IP); + if (ip & NUSPI_IP_TXWM) + return ERROR_OK; + } + } + + return ERROR_NUSPI_TXWM_WAIT; +} + +static void nuspi_set_dir(volatile uint32_t *ctrl_base, bool dir) +{ + uint32_t fmt = nuspi_read_reg(ctrl_base, NUSPI_REG_FMT); + nuspi_write_reg(ctrl_base, NUSPI_REG_FMT, + (fmt & ~(NUSPI_FMT_DIR(0xFFFFFFFF))) | NUSPI_FMT_DIR(dir)); +} + +/* Can set bits 11:8 in result. */ +static int nuspi_tx(volatile uint32_t *ctrl_base, uint8_t in, uint32_t flags) +{ + unsigned timeout = TIMEOUT; + if (flags & NUSPI_FLAGS_32B_DAT) { + while (timeout--) { + if (!(nuspi_read_reg(ctrl_base, NUSPI_REG_STATUS) & NUSPI_STAT_TXFULL)) { + nuspi_write_reg(ctrl_base, NUSPI_REG_TXDATA, in); + return ERROR_OK; + } + } + } else { + while (timeout--) { + uint32_t txfifo = nuspi_read_reg(ctrl_base, NUSPI_REG_TXDATA); + if (!(txfifo >> 31)) { + nuspi_write_reg(ctrl_base, NUSPI_REG_TXDATA, in); + return ERROR_OK; + } + } + } + return ERROR_NUSPI_TX; +} + +/* Can set bits 15:12 in result. */ +static int nuspi_rx(volatile uint32_t *ctrl_base, uint8_t *out, uint32_t flags) +{ + unsigned timeout = TIMEOUT; + if (flags & NUSPI_FLAGS_32B_DAT) { + while (timeout--) { + if (!(nuspi_read_reg(ctrl_base, NUSPI_REG_STATUS) & NUSPI_STAT_RXEMPTY)) { + uint32_t value = nuspi_read_reg(ctrl_base, NUSPI_REG_RXDATA); + if (out) + *out = value & 0xff; + return ERROR_OK; + } + } + } else { + while (timeout--) { + uint32_t value = nuspi_read_reg(ctrl_base, NUSPI_REG_RXDATA); + if (!(value >> 31)) { + if (out) + *out = value & 0xff; + return ERROR_OK; + } + } + } + return ERROR_NUSPI_RX; +} + +/* Can set bits 19:16 in result. */ +static int nuspi_wip(volatile uint32_t *ctrl_base, nuspi_info_t* nuspi_info) +{ + nuspi_set_dir(ctrl_base, NUSPI_DIR_RX); + + nuspi_write_reg(ctrl_base, NUSPI_REG_CSMODE, NUSPI_CSMODE_HOLD); + + int result = nuspi_tx(ctrl_base, SPIFLASH_READ_STATUS, nuspi_info->flags); + if (result != ERROR_OK) + return result | ERROR_STACK(0x10000); + result = nuspi_rx(ctrl_base, NULL, nuspi_info->flags); + if (result != ERROR_OK) + return result | ERROR_STACK(0x20000); + + unsigned timeout = TIMEOUT; + while (timeout--) { + result = nuspi_tx(ctrl_base, 0, nuspi_info->flags); + if (result != ERROR_OK) + return result | ERROR_STACK(0x30000); + uint8_t rx; + result = nuspi_rx(ctrl_base, &rx, nuspi_info->flags); + if (result != ERROR_OK) + return result | ERROR_STACK(0x40000); + if ((rx & SPIFLASH_BSY_BIT) == 0) { + nuspi_write_reg(ctrl_base, NUSPI_REG_CSMODE, NUSPI_CSMODE_AUTO); + nuspi_set_dir(ctrl_base, NUSPI_DIR_TX); + return ERROR_OK; + } + } + + return ERROR_NUSPI_WIP; +} + +/* Can set bits 23:20 in result. */ +static int nuspi_write_buffer(volatile uint32_t *ctrl_base, + const uint8_t *buffer, uint32_t offset, uint32_t len, + nuspi_info_t* nuspi_info) +{ + int result = nuspi_tx(ctrl_base, SPIFLASH_WRITE_ENABLE, nuspi_info->flags); + if (result != ERROR_OK) + return result | ERROR_STACK(0x100000); + result = nuspi_txwm_wait(ctrl_base, nuspi_info); + if (result != ERROR_OK) + return result | ERROR_STACK(0x200000); + + nuspi_write_reg(ctrl_base, NUSPI_REG_CSMODE, NUSPI_CSMODE_HOLD); + + result = nuspi_tx(ctrl_base, nuspi_info->info & 0xff, nuspi_info->flags); + if (result != ERROR_OK) + return result | ERROR_STACK(0x300000); + + if (nuspi_info->info & 0x100) { + result = nuspi_tx(ctrl_base, offset >> 24, nuspi_info->flags); + if (result != ERROR_OK) + return result | ERROR_STACK(0x400000); + } + result = nuspi_tx(ctrl_base, offset >> 16, nuspi_info->flags); + if (result != ERROR_OK) + return result | ERROR_STACK(0x400000); + result = nuspi_tx(ctrl_base, offset >> 8, nuspi_info->flags); + if (result != ERROR_OK) + return result | ERROR_STACK(0x500000); + result = nuspi_tx(ctrl_base, offset, nuspi_info->flags); + if (result != ERROR_OK) + return result | ERROR_STACK(0x600000); + + for (unsigned i = 0; i < len; i++) { + result = nuspi_tx(ctrl_base, buffer[i], nuspi_info->flags); + if (result != ERROR_OK) + return result | ERROR_STACK(0x700000); + } + + result = nuspi_txwm_wait(ctrl_base, nuspi_info); + if (result != ERROR_OK) + return result | ERROR_STACK(0x800000); + + nuspi_write_reg(ctrl_base, NUSPI_REG_CSMODE, NUSPI_CSMODE_AUTO); + + result = nuspi_wip(ctrl_base, nuspi_info); + if (result != ERROR_OK) + return result | ERROR_STACK(0x900000); + return ERROR_OK; +} diff --git a/contrib/loaders/flash/nuspi/riscv_wrapper.S b/contrib/loaders/flash/nuspi/riscv_wrapper.S new file mode 100644 index 000000000..a79526637 --- /dev/null +++ b/contrib/loaders/flash/nuspi/riscv_wrapper.S @@ -0,0 +1,22 @@ +#if __riscv_xlen == 64 + # define LREG ld + # define SREG sd + # define REGBYTES 8 +#else + # define LREG lw + # define SREG sw + # define REGBYTES 4 +#endif + + .section .text.entry + .global _start +_start: + lla sp, stack_end + jal flash_nuspi + ebreak + + .section .data + .balign REGBYTES +stack: + .fill 32, REGBYTES, 0x8675309 +stack_end: diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am index afa11e7d4..9ac0d05fa 100644 --- a/src/flash/nor/Makefile.am +++ b/src/flash/nor/Makefile.am @@ -25,6 +25,8 @@ NOR_DRIVERS = \ %D%/cc3220sf.c \ %D%/cc26xx.c \ %D%/cfi.c \ + %D%/cm32m4xxr.c \ + %D%/custom.c \ %D%/dsp5680xx_flash.c \ %D%/efm32.c \ %D%/em357.c \ @@ -50,6 +52,7 @@ NOR_DRIVERS = \ %D%/npcx.c \ %D%/nrf5.c \ %D%/numicro.c \ + %D%/nuspi.c \ %D%/ocl.c \ %D%/pic32mx.c \ %D%/psoc4.c \ diff --git a/src/flash/nor/cm32m4xxr.c b/src/flash/nor/cm32m4xxr.c new file mode 100644 index 000000000..ac6eed13c --- /dev/null +++ b/src/flash/nor/cm32m4xxr.c @@ -0,0 +1,781 @@ +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "imp.h" +#include +#include + +/* CM32M4XXR register locations */ +#define FLASH_REG_BASE (0x40000000 + 0x18000 + 0xA000) + +#define FLASH_BASE (0x08000000) +#define FLASH_PAGE_SIZE (2048) +#define FLASH_TOTAL_KB (512) +#define FLASH_PG_UNIT (4) + +#define FMC_AC 0x00 +#define FMC_KEY 0x04 +#define FMC_OPTKEY 0x08 +#define FMC_STS 0x0C +#define FMC_CTRL 0x10 +#define FMC_ADD 0x14 +#define FMC_OB 0x1C +#define FMC_WRP 0X20 +#define FMC_ECC 0x24 +#define FMC_CAH 0x30 + +/* option byte location */ + +/****************** Bit definition for FLASH_STS register *******************/ +#define FLASH_STS_BUSY_Pos (0U) +#define FLASH_STS_BUSY_Msk (0x1UL << FLASH_STS_BUSY_Pos) +#define FLASH_STS_BUSY FLASH_STS_BUSY_Msk /*!< Busy */ +#define FLASH_STS_PGERR_Pos (2U) +#define FLASH_STS_PGERR_Msk (0x1UL << FLASH_STS_PGERR_Pos) +#define FLASH_STS_PGERR FLASH_STS_PGERR_Msk /*!< Programming Error */ +#define FLASH_STS_PVERR_Pos (3U) +#define FLASH_STS_PVERR_Msk (0x1UL << FLASH_STS_PVERR_Pos) +#define FLASH_STS_PVERR FLASH_STS_PVERR_Msk /*!< Programming Verify ERROR after program */ +#define FLASH_STS_WRPEER_Pos (4U) +#define FLASH_STS_WRPEER_Msk (0x1UL << FLASH_STS_WRPEER_Pos) +#define FLASH_STS_WRPEER FLASH_STS_WRPEER_Msk /*!< Write Protection Error */ +#define FLASH_STS_EOP_Pos (5U) +#define FLASH_STS_EOP_Msk (0x1UL << FLASH_STS_EOP_Pos) +#define FLASH_STS_EOP FLASH_STS_EOP_Msk /*!< End of operation */ +#define FLASH_STS_EVERR_Pos (6U) +#define FLASH_STS_EVERR_Msk (0x1UL << FLASH_STS_EVERR_Pos) +#define FLASH_STS_EVERR FLASH_STS_EVERR_Msk /*!< Erase Verify ERROR after page erase */ +#define FLASH_STS_ECCERR_Pos (7U) +#define FLASH_STS_ECCERR_Msk (0x1UL << FLASH_STS_ECCERR_Pos) +#define FLASH_STS_ECCERR FLASH_STS_ECCERR_Msk /*!< ECC ERROR when Flash Reading */ + +/****************** Bit definition for FLASH_CTRL register ******************/ +#define FLASH_CTRL_PG_Pos (0U) +#define FLASH_CTRL_PG_Msk (0x1UL << FLASH_CTRL_PG_Pos) +#define FLASH_CTRL_PG FLASH_CTRL_PG_Msk /*!< Programming */ +#define FLASH_CTRL_PER_Pos (1U) +#define FLASH_CTRL_PER_Msk (0x1UL << FLASH_CTRL_PER_Pos) +#define FLASH_CTRL_PER FLASH_CTRL_PER_Msk /*!< Page Erase */ +#define FLASH_CTRL_MER_Pos (2U) +#define FLASH_CTRL_MER_Msk (0x1UL << FLASH_CTRL_MER_Pos) +#define FLASH_CTRL_MER FLASH_CTRL_MER_Msk /*!< Mass Erase */ +#define FLASH_CTRL_OPTPG_Pos (4U) +#define FLASH_CTRL_OPTPG_Msk (0x1UL << FLASH_CTRL_OPTPG_Pos) +#define FLASH_CTRL_OPTPG FLASH_CTRL_OPTPG_Msk /*!< Option Byte Programming */ +#define FLASH_CTRL_OPTER_Pos (5U) +#define FLASH_CTRL_OPTER_Msk (0x1UL << FLASH_CTRL_OPTER_Pos) +#define FLASH_CTRL_OPTER FLASH_CTRL_OPTER_Msk /*!< Option Byte Erase */ +#define FLASH_CTRL_START_Pos (6U) +#define FLASH_CTRL_START_Msk (0x1UL << FLASH_CTRL_START_Pos) +#define FLASH_CTRL_START FLASH_CTRL_START_Msk /*!< Start */ +#define FLASH_CTRL_LOCK_Pos (7U) +#define FLASH_CTRL_LOCK_Msk (0x1UL << FLASH_CTRL_LOCK_Pos) +#define FLASH_CTRL_LOCK FLASH_CTRL_LOCK_Msk /*!< Lock */ +#define FLASH_CTRL_SMPSEL_Pos (8U) +#define FLASH_CTRL_SMPSEL_Msk (0x1UL << FLASH_CTRL_SMPSEL_Pos) +#define FLASH_CTRL_SMPSEL FLASH_CTRL_SMPSEL_Msk /*!< Flash Program Option Select */ +#define FLASH_CTRL_OPTWE_Pos (9U) +#define FLASH_CTRL_OPTWE_Msk (0x1UL << FLASH_CTRL_OPTWE_Pos) +#define FLASH_CTRL_OPTWE FLASH_CTRL_OPTWE_Msk /*!< Option Bytes Write Enable */ +#define FLASH_CTRL_ERRITE_Pos (10U) +#define FLASH_CTRL_ERRITE_Msk (0x1UL << FLASH_CTRL_ERRITE_Pos) +#define FLASH_CTRL_ERRITE FLASH_CTRL_ERRITE_Msk /*!< Error Interrupt Enable */ +#define FLASH_CTRL_FERRITE_Pos (11U) +#define FLASH_CTRL_FERRITE_Msk (0x1UL << FLASH_CTRL_FERRITE_Pos) +#define FLASH_CTRL_FERRITE FLASH_CTRL_FERRITE_Msk /*!< EVERR PVERR Error Interrupt Enable */ +#define FLASH_CTRL_EOPITE_Pos (12U) +#define FLASH_CTRL_EOPITE_Msk (0x1UL << FLASH_CTRL_EOPITE_Pos) +#define FLASH_CTRL_EOPITE FLASH_CTRL_EOPITE_Msk /*!< End of operation Interrupt Enable */ +#define FLASH_CTRL_ECCERRITE_Pos (13U) +#define FLASH_CTRL_ECCERRITE_Msk (0x1UL << FLASH_CTRL_ECCERRITE_Pos) +#define FLASH_CTRL_ECCERRITE FLASH_CTRL_ECCERRITE_Msk /*!< ECC Error Interrupt Enable */ + + +#define FLASH_FLAG_BUSY ((uint32_t) 0x00000001) /*!< FLASH Busy flag */ +#define FLASH_FLAG_PGERR ((uint32_t) 0x00000004) /*!< FLASH Program error flag */ +#define FLASH_FLAG_PVERR ((uint32_t) 0x00000008) /*!< FLASH Program Verify ERROR flag after program */ +#define FLASH_FLAG_WRPERR ((uint32_t) 0x00000010) /*!< FLASH Write protected error flag */ +#define FLASH_FLAG_EOP ((uint32_t) 0x00000020) /*!< FLASH End of Operation flag */ +#define FLASH_FLAG_EVERR ((uint32_t) 0x00000040) /*!< FLASH Erase Verify ERROR flag after page erase */ +#define FLASH_FLAG_OBERR ((uint32_t) 0x00000001) /*!< FLASH Option Byte error flag */ +#define FLASH_STS_CLRFLAG (FLASH_FLAG_PGERR | FLASH_FLAG_PVERR | FLASH_FLAG_WRPERR | FLASH_FLAG_EOP | FLASH_FLAG_EVERR) + +/* FMC_OBSTAT bit definitions (reading) */ + +/* Flash Control Register bits */ +#define CR_Set_PG FLASH_CTRL_PG +#define CR_Reset_PG (~FLASH_CTRL_PG) +#define CR_Set_PER FLASH_CTRL_PER +#define CR_Reset_PER (~FLASH_CTRL_PER) +#define CR_Set_MER FLASH_CTRL_MER +#define CR_Reset_MER (~FLASH_CTRL_MER) +#define CR_Set_OPTPG FLASH_CTRL_OPTPG +#define CR_Reset_OPTPG (~FLASH_CTRL_OPTPG) +#define CR_Set_OPTER FLASH_CTRL_OPTER +#define CR_Reset_OPTER (~FLASH_CTRL_OPTER) +#define CR_Set_START FLASH_CTRL_START +#define CR_Set_LOCK FLASH_CTRL_LOCK +#define CR_Reset_OPTWE (~FLASH_CTRL_OPTWE) + +/* register unlock keys */ + +#define UNLOCK_KEY0 0x45670123 +#define UNLOCK_KEY1 0xCDEF89AB + +/* timeout values */ + +#define FLASH_WRITE_TIMEOUT 0x00002000 +#define FLASH_ERASE_TIMEOUT 0x000B0000 + +struct cm32m4xxr_options { + uint16_t RDP; + uint16_t user_options; + uint16_t user_data; + uint16_t protection[4]; +}; + +struct cm32m4xxr_flash_bank { + struct cm32m4xxr_options option_bytes; + int ppage_size; + int probed; + + bool has_dual_banks; + /* used to access flash bank cm32m4xxr */ + uint32_t register_base; + uint16_t default_rdp; + int user_data_offset; + int option_offset; + uint32_t user_bank_size; +}; + +static int cm32m4xxr_mass_erase(struct flash_bank *bank); +static int get_cm32m4xxr_info(struct flash_bank *bank, struct command_invocation *cmd); +static int cm32m4xxr_write_block(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t count); + +/* flash bank cm32m4xxr 0 0 + */ +FLASH_BANK_COMMAND_HANDLER(cm32m4xxr_flash_bank_command) +{ + struct cm32m4xxr_flash_bank *cm32m4xxr_info; + + if (CMD_ARGC < 6) + return ERROR_COMMAND_SYNTAX_ERROR; + + cm32m4xxr_info = malloc(sizeof(struct cm32m4xxr_flash_bank)); + + /* The flash write must be aligned to a word (4-bytes) boundary. + * Ask the flash infrastructure to ensure required alignment */ + bank->write_start_alignment = bank->write_end_alignment = 4; + + bank->driver_priv = cm32m4xxr_info; + cm32m4xxr_info->probed = 0; + cm32m4xxr_info->has_dual_banks = false; + cm32m4xxr_info->register_base = FLASH_REG_BASE; + cm32m4xxr_info->user_bank_size = bank->size; + + return ERROR_OK; +} + +static inline int cm32m4xxr_get_flash_reg(struct flash_bank *bank, uint32_t reg) +{ + struct cm32m4xxr_flash_bank *cm32m4xxr_info = bank->driver_priv; + return reg + cm32m4xxr_info->register_base; +} + +static inline int cm32m4xxr_get_flash_status(struct flash_bank *bank, uint32_t *status) +{ + struct target *target = bank->target; + return target_read_u32(target, cm32m4xxr_get_flash_reg(bank, FMC_STS), status); +} + +static int cm32m4xxr_wait_status_busy(struct flash_bank *bank, int timeout) +{ + struct target *target = bank->target; + uint32_t status; + int retval = ERROR_OK; + + /* wait for busy to clear */ + for (;;) { + retval = cm32m4xxr_get_flash_status(bank, &status); + if (retval != ERROR_OK) + return retval; + LOG_DEBUG("status: 0x%" PRIx32 "", status); + if ((status & FLASH_FLAG_BUSY) == 0) + break; + if (timeout-- <= 0) { + LOG_ERROR("timed out waiting for flash"); + return ERROR_FAIL; + } + alive_sleep(1); + } + + if (status & FLASH_FLAG_WRPERR) { + LOG_ERROR("cm32m4xxr device protected"); + retval = ERROR_FAIL; + } + + if (status & FLASH_FLAG_PGERR) { + LOG_ERROR("cm32m4xxr device programming failed"); + retval = ERROR_FAIL; + } + + /* Clear but report errors */ + if (status & (FLASH_FLAG_WRPERR | FLASH_FLAG_PGERR)) { + /* If this operation fails, we ignore it and report the original + * retval + */ + target_write_u32(target, cm32m4xxr_get_flash_reg(bank, FMC_STS), + FLASH_FLAG_WRPERR | FLASH_FLAG_PGERR); + } + return retval; +} + +static int cm32m4xxr_erase(struct flash_bank *bank, unsigned first, unsigned last) +{ + struct target *target = bank->target; + // uint32_t optiondata; + // uint32_t obstat; + uint32_t ctrl_reg; + + // struct cm32m4xxr_flash_bank *cm32m4xxr_info = NULL; + // cm32m4xxr_info = bank->driver_priv; + + if (bank->target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + //todo: write protect + if ((first == 0) && (last == (bank->num_sectors - 1))) + return cm32m4xxr_mass_erase(bank); + + /* unlock flash registers */ + int retval = target_write_u32(target, cm32m4xxr_get_flash_reg(bank, FMC_KEY), UNLOCK_KEY0); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, cm32m4xxr_get_flash_reg(bank, FMC_KEY), UNLOCK_KEY1); + if (retval != ERROR_OK) + return retval; + + retval = cm32m4xxr_wait_status_busy(bank, FLASH_ERASE_TIMEOUT); + if (retval != ERROR_OK) + return retval; + + for (unsigned i = first; i <= last; i++) { + //clear flash pending flags + retval = target_write_u32(target, cm32m4xxr_get_flash_reg(bank, FMC_STS), FLASH_STS_CLRFLAG); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, cm32m4xxr_get_flash_reg(bank, FMC_CTRL), CR_Set_PER); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, cm32m4xxr_get_flash_reg(bank, FMC_ADD), + bank->base + bank->sectors[i].offset); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, + cm32m4xxr_get_flash_reg(bank, FMC_CTRL), CR_Set_PER | CR_Set_START); + if (retval != ERROR_OK) + return retval; + retval = cm32m4xxr_wait_status_busy(bank, FLASH_ERASE_TIMEOUT); + if (retval != ERROR_OK) + return retval; + + bank->sectors[i].is_erased = 1; + + /* Disable the PER Bit */ + retval = target_read_u32(target, cm32m4xxr_get_flash_reg(bank, FMC_CTRL), &ctrl_reg); + if (retval != ERROR_OK) + return retval; + ctrl_reg &= CR_Reset_PER; + retval = target_write_u32(target, cm32m4xxr_get_flash_reg(bank, FMC_CTRL), ctrl_reg); + if (retval != ERROR_OK) + return retval; + } + + retval = target_write_u32(target, cm32m4xxr_get_flash_reg(bank, FMC_CTRL), CR_Set_LOCK); + if (retval != ERROR_OK) + return retval; + + return ERROR_OK; +} + + +static int cm32m4xxr_write_block(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + struct cm32m4xxr_flash_bank *cm32m4xxr_info = bank->driver_priv; + struct target *target = bank->target; + uint32_t buffer_size = 16384; + struct working_area *write_algorithm; + struct working_area *source; + uint32_t address = bank->base + offset; + struct reg_param reg_params[5]; + int retval = ERROR_OK; + + static const uint8_t cm32m4xxr_flash_write_code[] = { + 0x03,0x28,0x06,0x00,0x63,0x05,0x08,0x04,0x5c,0x42,0xe3,0x8b,0x07,0xff,0x05,0x48, + 0x23,0x28,0x05,0x01,0x03,0xa8,0x07,0x00,0x23,0x20,0x07,0x01,0x91,0x07,0x11,0x07, + 0x03,0x2e,0xc5,0x00,0x93,0x78,0x1e,0x00,0x05,0x48,0xe3,0x0b,0x18,0xff,0x03,0x28, + 0x05,0x01,0x13,0x78,0xe8,0xff,0x23,0x28,0x05,0x01,0x63,0xc4,0xd7,0x00,0xb2,0x87, + 0xa1,0x07,0x5c,0xc2,0xfd,0x15,0x81,0xc5,0x65,0xbf,0x01,0x45,0x48,0xc2,0x72,0x85, + 0x02,0x90, + }; + /* flash write code */ + if (target_alloc_working_area(target, sizeof(cm32m4xxr_flash_write_code), + &write_algorithm) != ERROR_OK) { + LOG_WARNING("no working area available, can't do block memory writes"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + retval = target_write_buffer(target, write_algorithm->address, + sizeof(cm32m4xxr_flash_write_code), cm32m4xxr_flash_write_code); + if (retval != ERROR_OK) { + target_free_working_area(target, write_algorithm); + return retval; + } + + /* memory buffer */ + while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) { + buffer_size /= 2; + buffer_size &= ~3UL; /* Make sure it's 4 byte aligned */ + if (buffer_size <= 256) { + /* we already allocated the writing code, but failed to get a + * buffer, free the algorithm */ + target_free_working_area(target, write_algorithm); + + LOG_WARNING("no large enough working area available, can't do block memory writes"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + } + + init_reg_param(®_params[0], "a0", 32, PARAM_IN_OUT); /* flash base (in), status (out) */ + init_reg_param(®_params[1], "a1", 32, PARAM_OUT); /* count (halfword-16bit) */ + init_reg_param(®_params[2], "a2", 32, PARAM_OUT); /* buffer start */ + init_reg_param(®_params[3], "a3", 32, PARAM_OUT); /* buffer end */ + init_reg_param(®_params[4], "a4", 32, PARAM_IN_OUT); /* target address */ + + + uint32_t wp_addr = source->address; + uint32_t rp_addr = source->address + 4; + uint32_t fifo_start_addr = source->address + 8; + uint32_t fifo_end_addr = source->address + source->size; + + uint32_t wp = fifo_start_addr; + uint32_t rp = fifo_start_addr; + uint32_t thisrun_bytes = fifo_end_addr-fifo_start_addr - 4; /* (2:block size) */ + + retval = target_write_u32(target, rp_addr, rp); + if (retval != ERROR_OK) + return retval; + + while (count > 0) { + retval = target_read_u32(target, rp_addr, &rp); + if (retval != ERROR_OK) { + LOG_ERROR("failed to get read pointer"); + break; + } + if (wp != rp) { + LOG_ERROR("Failed to write flash ;; rp = 0x%x ;;; wp = 0x%x", rp, wp); + break; + } + wp = fifo_start_addr; + rp = fifo_start_addr; + retval = target_write_u32(target, rp_addr, rp); + if (retval != ERROR_OK) + break; + /* Limit to the amount of data we actually want to write */ + if (thisrun_bytes > count * 4) + thisrun_bytes = count * 4; + + /* Write data to fifo */ + retval = target_write_buffer(target, wp, thisrun_bytes, buffer); + if (retval != ERROR_OK) + break; + + /* Update counters and wrap write pointer */ + buffer += thisrun_bytes; + count -= thisrun_bytes / 4; + rp = fifo_start_addr; + wp = fifo_start_addr+thisrun_bytes; + + /* Store updated write pointer to target */ + retval = target_write_u32(target, wp_addr, wp); + if (retval != ERROR_OK) + break; + retval = target_write_u32(target, rp_addr, rp); + if (retval != ERROR_OK) + return retval; + + buf_set_u32(reg_params[0].value, 0, 32, cm32m4xxr_info->register_base); + buf_set_u32(reg_params[1].value, 0, 32, thisrun_bytes/4); + buf_set_u32(reg_params[2].value, 0, 32, source->address); + buf_set_u32(reg_params[3].value, 0, 32, source->address + source->size); + buf_set_u32(reg_params[4].value, 0, 32, address); + + retval = target_run_algorithm(target, 0, NULL, 5, reg_params, + write_algorithm->address, 0, + 10000, NULL); + + if (retval != ERROR_OK) { + LOG_ERROR("Failed to execute algorithm at 0x%" TARGET_PRIxADDR ": %d", + write_algorithm->address, retval); + return retval; + } + address += thisrun_bytes; + + } + + if (retval == ERROR_FLASH_OPERATION_FAILED) { + LOG_ERROR("flash write failed at address 0x%"PRIx32, + buf_get_u32(reg_params[4].value, 0, 32)); + + if (buf_get_u32(reg_params[0].value, 0, 32) & FLASH_FLAG_PGERR) { + LOG_ERROR("flash memory not erased before writing"); + /* Clear but report errors */ + target_write_u32(target, cm32m4xxr_get_flash_reg(bank, FMC_STS), FLASH_FLAG_PGERR); + } + + if (buf_get_u32(reg_params[0].value, 0, 32) & FLASH_FLAG_WRPERR) { + LOG_ERROR("flash memory write protected"); + /* Clear but report errors */ + target_write_u32(target, cm32m4xxr_get_flash_reg(bank, FMC_STS), FLASH_FLAG_WRPERR); + } + } + + target_free_working_area(target, source); + target_free_working_area(target, write_algorithm); + + destroy_reg_param(®_params[0]); + destroy_reg_param(®_params[1]); + destroy_reg_param(®_params[2]); + destroy_reg_param(®_params[3]); + destroy_reg_param(®_params[4]); + + return retval; +} + +static int cm32m4xxr_write(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + struct target *target = bank->target; + uint8_t *new_buffer = NULL; + + + if (bank->target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (offset & 0x1) { + LOG_ERROR("offset 0x%" PRIx32 " breaks required 2-byte alignment", offset); + return ERROR_FLASH_DST_BREAKS_ALIGNMENT; + } + + /* If there's an odd number of bytes, the data has to be padded. Duplicate + * the buffer and use the normal code path with a single block write since + * it's probably cheaper than to special case the last odd write using + * discrete accesses. */ + if (count % 4) { + new_buffer = malloc(count + 3); + if (new_buffer == NULL) { + LOG_ERROR("odd number of bytes to write and no memory for padding buffer"); + return ERROR_FAIL; + } + LOG_INFO("odd number of bytes to write, padding with 0xff"); + buffer = memcpy(new_buffer, buffer, count); + new_buffer[count++] = 0xff; + new_buffer[count++] = 0xff; + new_buffer[count++] = 0xff; + } + + uint32_t words_remaining = (count + 3) / 4; + int retval, retval2; + + /* unlock flash registers */ + retval = target_write_u32(target, cm32m4xxr_get_flash_reg(bank, FMC_KEY), UNLOCK_KEY0); + if (retval != ERROR_OK) + goto cleanup; + retval = target_write_u32(target, cm32m4xxr_get_flash_reg(bank, FMC_KEY), UNLOCK_KEY1); + if (retval != ERROR_OK) + goto cleanup; + + //clear flash pending flags + retval = target_write_u32(target, cm32m4xxr_get_flash_reg(bank, FMC_STS), FLASH_STS_CLRFLAG); + if (retval != ERROR_OK) + goto cleanup; + /* try using a block write */ + retval = cm32m4xxr_write_block(bank, buffer, offset, words_remaining); + if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) { + /* if block write failed (no sufficient working area), + * we use normal (slow) single halfword accesses */ + LOG_WARNING("couldn't use block writes, falling back to single memory accesses"); + + while (words_remaining > 0) { + uint32_t value; + memcpy(&value, buffer, sizeof(uint32_t)); + + retval = target_write_u32(target, cm32m4xxr_get_flash_reg(bank, FMC_CTRL), CR_Set_PG); + if (retval != ERROR_OK) + goto cleanup; + + retval = target_write_u32(target, bank->base + offset, value); + if (retval != ERROR_OK) + goto reset_pg_and_lock; + + retval = cm32m4xxr_wait_status_busy(bank, 100); + if (retval != ERROR_OK) + goto reset_pg_and_lock; + + retval = target_write_u32(target, cm32m4xxr_get_flash_reg(bank, FMC_CTRL), 0); + if (retval != ERROR_OK) + goto cleanup; + + words_remaining--; + buffer += 4; + offset += 4; + } + } + +reset_pg_and_lock: + retval2 = target_write_u32(target, cm32m4xxr_get_flash_reg(bank, FMC_CTRL), CR_Set_LOCK); + if (retval == ERROR_OK) + retval = retval2; + +cleanup: + if (new_buffer) + free(new_buffer); + + return retval; +} + +static int cm32m4xxr_get_device_id(struct flash_bank *bank, uint32_t *device_id) +{ + + struct target *target = bank->target; + uint32_t device_id_register = 0xE0040000; + /* read cm32m4xxr device id register */ + int retval = target_read_u32(target, device_id_register, device_id); + if (retval != ERROR_OK) + return retval; + + return retval; +} + +static int cm32m4xxr_get_flash_size(struct flash_bank *bank, uint16_t *flash_size_in_kb) +{ + *flash_size_in_kb = FLASH_TOTAL_KB; + return ERROR_OK; +} + +static int cm32m4xxr_probe(struct flash_bank *bank) +{ + struct cm32m4xxr_flash_bank *cm32m4xxr_info = bank->driver_priv; + int i; + uint16_t flash_size_in_kb; + uint16_t max_flash_size_in_kb; + uint32_t device_id; + int page_size; + uint32_t base_address = FLASH_BASE; + + cm32m4xxr_info->probed = 0; + cm32m4xxr_info->register_base = FLASH_REG_BASE; + cm32m4xxr_info->user_data_offset = 10; + cm32m4xxr_info->option_offset = 0; + + /* default factory protection level */ + cm32m4xxr_info->default_rdp = 0x5AA5; + + /* read cm32m4xxr device id register */ + int retval = cm32m4xxr_get_device_id(bank, &device_id); + if (retval != ERROR_OK) + return retval; + + LOG_INFO("device id = 0x%08" PRIx32 "", device_id); + page_size = FLASH_PAGE_SIZE; + cm32m4xxr_info->ppage_size = FLASH_PG_UNIT; + max_flash_size_in_kb = FLASH_TOTAL_KB; + + /* get flash size from target. */ + retval = cm32m4xxr_get_flash_size(bank, &flash_size_in_kb); + LOG_INFO("flash_size_in_kb = 0x%08" PRIx32 "", flash_size_in_kb); + /* failed reading flash size or flash size invalid, default to max target family */ + if (retval != ERROR_OK || flash_size_in_kb == 0xffff || flash_size_in_kb == 0) { + LOG_WARNING("cm32m4xxr flash size failed, probe inaccurate - assuming %dk flash", + max_flash_size_in_kb); + flash_size_in_kb = max_flash_size_in_kb; + } + /* if the user sets the size manually then ignore the probed value + * this allows us to work around devices that have a invalid flash size register value */ + if (cm32m4xxr_info->user_bank_size) { + LOG_INFO("ignoring flash probed value, using configured bank size"); + flash_size_in_kb = cm32m4xxr_info->user_bank_size / 1024; + } + + LOG_INFO("flash size = %dkbytes", flash_size_in_kb); + + /* did we assign flash size? */ + assert(flash_size_in_kb != 0xffff); + + /* calculate numbers of pages */ + int num_pages = flash_size_in_kb * 1024 / page_size; + + /* check that calculation result makes sense */ + assert(num_pages > 0); + + if (bank->sectors) { + free(bank->sectors); + bank->sectors = NULL; + } + + bank->base = base_address; + bank->size = (num_pages * page_size); + bank->num_sectors = num_pages; + bank->sectors = malloc(sizeof(struct flash_sector) * num_pages); + + for (i = 0; i < num_pages; i++) { + bank->sectors[i].offset = i * page_size; + bank->sectors[i].size = page_size; + bank->sectors[i].is_erased = -1; + bank->sectors[i].is_protected = 1; + } + + cm32m4xxr_info->probed = 1; + + return ERROR_OK; +} + +static int cm32m4xxr_auto_probe(struct flash_bank *bank) +{ + struct cm32m4xxr_flash_bank *cm32m4xxr_info = bank->driver_priv; + if (cm32m4xxr_info->probed) + return ERROR_OK; + return cm32m4xxr_probe(bank); +} + +static int get_cm32m4xxr_info(struct flash_bank *bank, struct command_invocation *cmd) +{ + uint32_t dbgmcu_idcode; + + /* read cm32m4xxr device id register */ + int retval = cm32m4xxr_get_device_id(bank, &dbgmcu_idcode); + if (retval != ERROR_OK) + return retval; + + command_print_sameline(cmd, "%s - Rev: %s\n", "CM32M4xxR", "1.0"); + return ERROR_OK; +} + + +static int cm32m4xxr_mass_erase(struct flash_bank *bank) +{ + struct target *target = bank->target; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* unlock option flash registers */ + int retval = target_write_u32(target, cm32m4xxr_get_flash_reg(bank, FMC_KEY), UNLOCK_KEY0); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, cm32m4xxr_get_flash_reg(bank, FMC_KEY), UNLOCK_KEY1); + if (retval != ERROR_OK) + return retval; + + /* mass erase flash memory */ + retval = target_write_u32(target, cm32m4xxr_get_flash_reg(bank, FMC_CTRL), CR_Set_MER); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, cm32m4xxr_get_flash_reg(bank, FMC_CTRL), + CR_Set_MER | CR_Set_START); + if (retval != ERROR_OK) + return retval; + + retval = cm32m4xxr_wait_status_busy(bank, FLASH_ERASE_TIMEOUT); + if (retval != ERROR_OK) + return retval; + + retval = target_write_u32(target, cm32m4xxr_get_flash_reg(bank, FMC_CTRL), CR_Set_LOCK); + if (retval != ERROR_OK) + return retval; + + return ERROR_OK; +} + +COMMAND_HANDLER(cm32m4xxr_handle_mass_erase_command) +{ + if (CMD_ARGC < 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + retval = cm32m4xxr_mass_erase(bank); + if (retval == ERROR_OK) { + /* set all sectors as erased */ + for (unsigned i = 0; i < bank->num_sectors; i++) + bank->sectors[i].is_erased = 1; + + command_print(CMD, "cm32m4xxr mass erase complete"); + } else + command_print(CMD, "cm32m4xxr mass erase failed"); + + return retval; +} + +static const struct command_registration cm32m4xxr_exec_command_handlers[] = { + { + .name = "mass_erase", + .handler = cm32m4xxr_handle_mass_erase_command, + .mode = COMMAND_EXEC, + .usage = "bank_id", + .help = "Erase entire flash device.", + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration cm32m4xxr_command_handlers[] = { + { + .name = "cm32m4xxr", + .mode = COMMAND_ANY, + .help = "cm32m4xxr flash command group", + .usage = "", + .chain = cm32m4xxr_exec_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +const struct flash_driver cm32m4xxr_flash = { + .name = "cm32m4xxr", + .commands = cm32m4xxr_command_handlers, + .flash_bank_command = cm32m4xxr_flash_bank_command, + .erase = cm32m4xxr_erase, + .write = cm32m4xxr_write, + .read = default_flash_read, + .probe = cm32m4xxr_probe, + .auto_probe = cm32m4xxr_auto_probe, + .erase_check = default_flash_blank_check, + .info = get_cm32m4xxr_info, + .free_driver_priv = default_flash_free_driver_priv, +}; diff --git a/src/flash/nor/custom.c b/src/flash/nor/custom.c new file mode 100644 index 000000000..0da76ce83 --- /dev/null +++ b/src/flash/nor/custom.c @@ -0,0 +1,469 @@ +/*************************************************************************** + * Copyright (C) 2010 by Antonio Borneo * + * Modified by Yanwen Wang based on fespi.c * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "imp.h" +#include "spi.h" +#include +#include +#include +#include "target/riscv/riscv.h" +#include + +#define ERASE_CMD (1) +#define WRITE_CMD (2) +#define READ_CMD (3) +#define PROBE_CMD (4) + +struct flash_bank_msg { + bool probed; + const struct flash_device *dev; + target_addr_t ctrl_base; + char *loader_path; + uint8_t cs; + uint8_t *buffer; + uint32_t param_0; + uint32_t param_1; + bool simulation; + uint32_t sectorsize; +}; + +static int custom_run_algorithm(struct flash_bank *bank) +{ + struct flash_bank_msg *bank_msg = bank->driver_priv; + struct target *target = bank->target; + int retval = ERROR_OK; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + int xlen = riscv_xlen(target); + struct working_area *algorithm_wa = NULL; + struct working_area *data_wa = NULL; + uint8_t* bin = (uint8_t*)malloc(target->working_area_size); + size_t bin_size; + + FILE* fd = fopen((char*)bank_msg->loader_path, "rb"); + if (NULL == fd) { + fd = fopen(find_file(strrchr((char*)bank_msg->loader_path, '/')), "rb"); + } + if (fd) { + fseek(fd, 0, SEEK_END); + bin_size = ftell(fd); + rewind(fd); + if (target->working_area_size < bin_size) { + LOG_ERROR("working_area_size less than loader_bin_size"); + goto err; + } + if (1 != fread(bin, bin_size, 1, fd)) { + LOG_ERROR("read loader error"); + goto err; + } + fclose(fd); + } else { + LOG_ERROR("Failed to open loader:%s ", bank_msg->loader_path); + goto err; + } + + unsigned data_wa_size = 0; + if (target_alloc_working_area(target, bin_size, &algorithm_wa) == ERROR_OK) { + retval = target_write_buffer(target, algorithm_wa->address, bin_size, bin); + if (retval != ERROR_OK) { + LOG_ERROR("Failed to write code to " TARGET_ADDR_FMT ": %d", + algorithm_wa->address, retval); + target_free_working_area(target, algorithm_wa); + algorithm_wa = NULL; + } else if ((bank_msg->cs == WRITE_CMD) || (bank_msg->cs == READ_CMD)) { + data_wa_size = MIN(target->working_area_size - algorithm_wa->size, bank_msg->param_0); + while (1) { + if (target_alloc_working_area_try(target, data_wa_size, &data_wa) == ERROR_OK) + break; + data_wa_size = data_wa_size * 3 / 4; + } + } + } else { + LOG_WARNING("Couldn't allocate %zd-byte working area.", bin_size); + algorithm_wa = NULL; + } + + if (algorithm_wa) { + uint32_t count = 0; + uint32_t offset = 0; + uint32_t first_addr = 0; + uint32_t end_addr = 0; + uint32_t cur_count = 0; + int algorithm_result = 0; + struct reg_param reg_params[5]; + init_reg_param(®_params[0], "a0", xlen, PARAM_IN_OUT); + init_reg_param(®_params[1], "a1", xlen, PARAM_OUT); + init_reg_param(®_params[2], "a2", xlen, PARAM_OUT); + init_reg_param(®_params[3], "a3", xlen, PARAM_OUT); + init_reg_param(®_params[4], "a4", xlen, PARAM_OUT); + switch (bank_msg->cs) + { + case ERASE_CMD: + first_addr = bank_msg->param_0; + end_addr = bank_msg->param_1; + buf_set_u64(reg_params[0].value, 0, xlen, bank_msg->cs); + buf_set_u64(reg_params[1].value, 0, xlen, bank_msg->ctrl_base); + buf_set_u64(reg_params[2].value, 0, xlen, first_addr); + buf_set_u64(reg_params[3].value, 0, xlen, end_addr); + buf_set_u64(reg_params[4].value, 0, xlen, 0); + if (bank_msg->simulation) { + retval = target_run_algorithm(target, 0, NULL, + ARRAY_SIZE(reg_params), reg_params, + algorithm_wa->address, 0, 0x7FFFFFFF, NULL); + } else { + retval = target_run_algorithm(target, 0, NULL, + ARRAY_SIZE(reg_params), reg_params, + algorithm_wa->address, 0, (end_addr - first_addr) * 2, NULL); + } + if (retval != ERROR_OK) { + LOG_ERROR("Failed to execute algorithm at " TARGET_ADDR_FMT ": %d", + algorithm_wa->address, retval); + goto err; + } + algorithm_result = buf_get_u64(reg_params[0].value, 0, xlen); + if (algorithm_result != 0) { + LOG_ERROR("Algorithm returned error %d", algorithm_result); + LOG_ERROR("erase command error"); + retval = ERROR_FAIL; + goto err; + } + break; + case WRITE_CMD: + count = bank_msg->param_0; + offset = bank_msg->param_1; + cur_count = 0; + while (count > 0) { + cur_count = MIN(count, data_wa_size); + buf_set_u64(reg_params[0].value, 0, xlen, bank_msg->cs); + buf_set_u64(reg_params[1].value, 0, xlen, bank_msg->ctrl_base); + buf_set_u64(reg_params[2].value, 0, xlen, data_wa->address); + buf_set_u64(reg_params[3].value, 0, xlen, offset); + buf_set_u64(reg_params[4].value, 0, xlen, cur_count); + retval = target_write_buffer(target, data_wa->address, cur_count, bank_msg->buffer); + if (retval != ERROR_OK) { + LOG_DEBUG("Failed to write %d bytes to " TARGET_ADDR_FMT ": %d", + cur_count, data_wa->address, retval); + goto err; + } + if (bank_msg->simulation) { + retval = target_run_algorithm(target, 0, NULL, + ARRAY_SIZE(reg_params), reg_params, + algorithm_wa->address, 0, 0x7FFFFFFF, NULL); + } else { + retval = target_run_algorithm(target, 0, NULL, + ARRAY_SIZE(reg_params), reg_params, + algorithm_wa->address, 0, cur_count * 2, NULL); + } + if (retval != ERROR_OK) { + LOG_ERROR("Failed to execute algorithm at " TARGET_ADDR_FMT ": %d", + algorithm_wa->address, retval); + goto err; + } + algorithm_result = buf_get_u64(reg_params[0].value, 0, xlen); + if (algorithm_result != 0) { + LOG_ERROR("Algorithm returned error %d", algorithm_result); + LOG_ERROR("write command error"); + retval = ERROR_FAIL; + goto err; + } + bank_msg->buffer += cur_count; + offset += cur_count; + count -= cur_count; + } + break; + case READ_CMD: + count = bank_msg->param_0; + offset = bank_msg->param_1; + cur_count = 0; + while (count > 0) { + cur_count = MIN(count, data_wa_size); + buf_set_u64(reg_params[0].value, 0, xlen, bank_msg->cs); + buf_set_u64(reg_params[1].value, 0, xlen, bank_msg->ctrl_base); + buf_set_u64(reg_params[2].value, 0, xlen, data_wa->address); + buf_set_u64(reg_params[3].value, 0, xlen, offset); + buf_set_u64(reg_params[4].value, 0, xlen, cur_count); + if (bank_msg->simulation) { + retval = target_run_algorithm(target, 0, NULL, + ARRAY_SIZE(reg_params), reg_params, + algorithm_wa->address, 0, 0x7FFFFFFF, NULL); + } else { + retval = target_run_algorithm(target, 0, NULL, + ARRAY_SIZE(reg_params), reg_params, + algorithm_wa->address, 0, cur_count * 2, NULL); + } + if (retval != ERROR_OK) { + LOG_ERROR("Failed to execute algorithm at " TARGET_ADDR_FMT ": %d", + algorithm_wa->address, retval); + goto err; + } + algorithm_result = buf_get_u64(reg_params[0].value, 0, xlen); + if (algorithm_result != 0) { + LOG_ERROR("Algorithm returned error %d", algorithm_result); + LOG_ERROR("read command error"); + retval = ERROR_FAIL; + goto err; + } + retval = target_read_buffer(target, data_wa->address, cur_count, bank_msg->buffer); + if (retval != ERROR_OK) { + LOG_DEBUG("Failed to read %d bytes from " TARGET_ADDR_FMT ": %d", + cur_count, data_wa->address, retval); + goto err; + } + bank_msg->buffer += cur_count; + offset += cur_count; + count -= cur_count; + } + break; + case PROBE_CMD: + buf_set_u64(reg_params[0].value, 0, xlen, bank_msg->cs); + buf_set_u64(reg_params[1].value, 0, xlen, bank_msg->ctrl_base); + buf_set_u64(reg_params[2].value, 0, xlen, 0); + buf_set_u64(reg_params[3].value, 0, xlen, 0); + buf_set_u64(reg_params[4].value, 0, xlen, 0); + if (bank_msg->simulation) { + retval = target_run_algorithm(target, 0, NULL, + ARRAY_SIZE(reg_params), reg_params, + algorithm_wa->address, 0, 0x7FFFFFFF, NULL); + } else { + retval = target_run_algorithm(target, 0, NULL, + ARRAY_SIZE(reg_params), reg_params, + algorithm_wa->address, 0, 10000, NULL); + } + if (retval != ERROR_OK) { + LOG_ERROR("Failed to execute algorithm at " TARGET_ADDR_FMT ": %d", + algorithm_wa->address, retval); + goto err; + } + algorithm_result = buf_get_u64(reg_params[0].value, 0, xlen); + retval = algorithm_result; + break; + default: + break; + } + target_free_working_area(target, data_wa); + target_free_working_area(target, algorithm_wa); + } + +err: + if (bin) { + free(bin); + } + if (algorithm_wa) { + target_free_working_area(target, data_wa); + target_free_working_area(target, algorithm_wa); + } + return retval; +} + +FLASH_BANK_COMMAND_HANDLER(custom_flash_bank_command) +{ + struct flash_bank_msg *bank_msg; + + LOG_DEBUG("%s", __func__); + + if (CMD_ARGC < 8) { + LOG_ERROR("Parameter error:"); + LOG_ERROR("flash bank $FLASHNAME custom 0x20000000 0 0 0 $TARGETNAME 0x10014000 ~/work/riscv.bin [simulation] [sectorsize=]"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + bank_msg = malloc(sizeof(struct flash_bank_msg)); + if (bank_msg == NULL) { + LOG_ERROR("not enough memory"); + return ERROR_FAIL; + } + + bank->driver_priv = bank_msg; + bank_msg->probed = false; + bank_msg->ctrl_base = 0; + bank_msg->loader_path = NULL; + bank_msg->cs = 0; + bank_msg->buffer = NULL; + bank_msg->param_0 = 0; + bank_msg->param_1 = 0; + + COMMAND_PARSE_ADDRESS(CMD_ARGV[6], bank_msg->ctrl_base); + LOG_DEBUG("ASSUMING CUSTOM device at ctrl_base = " TARGET_ADDR_FMT, + bank_msg->ctrl_base); + bank_msg->loader_path = malloc(strlen(CMD_ARGV[7])); + strcpy((char*)bank_msg->loader_path, CMD_ARGV[7]); + for (char *p = bank_msg->loader_path; *p; p++) { + if (*p == '\\') + *p = '/'; + } + bank_msg->simulation = false; + bank_msg->sectorsize = 0; + for (unsigned int i = 8; i < CMD_ARGC; i++) { + if(strcmp(CMD_ARGV[i], "simulation") == 0) { + bank_msg->simulation = true; + LOG_DEBUG("Custom Simulation Mode"); + } + if(strncmp(CMD_ARGV[i], "sectorsize=", strlen("sectorsize=")) == 0) { + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[i]+strlen("sectorsize="), bank_msg->sectorsize); + LOG_DEBUG("Custom flash sectorsize is %x", bank_msg->sectorsize); + } + } + + return ERROR_OK; +} + +static int custom_erase(struct flash_bank *bank, unsigned int first, + unsigned int last) +{ + struct flash_bank_msg *bank_msg = bank->driver_priv; + + bank_msg->cs = ERASE_CMD; + bank_msg->buffer = NULL; + bank_msg->param_0 = bank->sectors[first].offset; + bank_msg->param_1 = bank->sectors[last].offset + bank_msg->sectorsize; + + return custom_run_algorithm(bank); +} + +static int custom_write(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + struct flash_bank_msg *bank_msg = bank->driver_priv; + + bank_msg->cs = WRITE_CMD; + bank_msg->buffer = (uint8_t*)buffer; + bank_msg->param_0 = count; + bank_msg->param_1 = offset; + + return custom_run_algorithm(bank); +} + +static int custom_read(struct flash_bank *bank, uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + struct flash_bank_msg *bank_msg = bank->driver_priv; + + bank_msg->cs = READ_CMD; + bank_msg->buffer = buffer; + bank_msg->param_0 = count; + bank_msg->param_1 = offset; + + return custom_run_algorithm(bank); +} + +static int custom_probe(struct flash_bank *bank) +{ + struct flash_bank_msg *bank_msg = bank->driver_priv; + + uint32_t id = 0x12345678; + struct flash_sector *sectors; + + if (bank_msg->probed) + free(bank->sectors); + bank_msg->probed = false; + + bank_msg->dev = NULL; + for (const struct flash_device *p = flash_devices; p->name ; p++) { + if (p->device_id == id) { + bank_msg->dev = p; + break; + } + } + /* if no sectors, treat whole bank as single sector */ + if (0 == bank_msg->sectorsize) { + bank_msg->sectorsize = bank_msg->dev->sectorsize ? + bank_msg->dev->sectorsize : bank->size; + } + /* create and fill sectors array */ + bank->num_sectors = bank->size / bank_msg->sectorsize; + sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors); + if (sectors == NULL) { + LOG_ERROR("not enough memory"); + return ERROR_FAIL; + } + for (unsigned int sector = 0; sector < bank->num_sectors; sector++) { + sectors[sector].offset = sector * bank_msg->sectorsize; + sectors[sector].size = bank_msg->sectorsize; + sectors[sector].is_erased = -1; + sectors[sector].is_protected = 0; + } + bank->sectors = sectors; + + bank_msg->cs = PROBE_CMD; + bank_msg->buffer = NULL; + bank_msg->param_0 = 0; + bank_msg->param_1 = 0; + + id = custom_run_algorithm(bank); + + LOG_INFO("Found custom flash device (ID 0x%08" PRIx32 ")", id); + bank_msg->probed = true; + return ERROR_OK; +} + +static int custom_info(struct flash_bank *bank, struct command_invocation *command) +{ + struct flash_bank_msg *bank_msg = bank->driver_priv; + + if (!(bank_msg->probed)) { + return ERROR_OK; + } + + return ERROR_OK; +} + +static int custom_auto_probe(struct flash_bank *bank) +{ + struct flash_bank_msg *bank_msg = bank->driver_priv; + if (bank_msg->probed) + return ERROR_OK; + return custom_probe(bank); +} + +static int custom_protect(struct flash_bank *bank, int set, + unsigned int first, unsigned int last) +{ + for (unsigned int sector = first; sector <= last; sector++) + bank->sectors[sector].is_protected = set; + return ERROR_OK; +} + +static int custom_protect_check(struct flash_bank *bank) +{ + /* Nothing to do. Protection is only handled in SW. */ + return ERROR_OK; +} + +const struct flash_driver custom_flash = { + .name = "custom", + .flash_bank_command = custom_flash_bank_command, + .erase = custom_erase, + .protect = custom_protect, + .write = custom_write, + .read = custom_read, + .probe = custom_probe, + .auto_probe = custom_auto_probe, + .erase_check = default_flash_blank_check, + .protect_check = custom_protect_check, + .info = custom_info, + .free_driver_priv = default_flash_free_driver_priv +}; diff --git a/src/flash/nor/driver.h b/src/flash/nor/driver.h index 211661e21..94a065b4b 100644 --- a/src/flash/nor/driver.h +++ b/src/flash/nor/driver.h @@ -253,6 +253,8 @@ 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 cm32m4xxr_flash; +extern const struct flash_driver custom_flash; extern const struct flash_driver dsp5680xx_flash; extern const struct flash_driver efm32_flash; extern const struct flash_driver em357_flash; @@ -278,6 +280,7 @@ extern const struct flash_driver npcx_flash; extern const struct flash_driver nrf51_flash; extern const struct flash_driver nrf5_flash; extern const struct flash_driver numicro_flash; +extern const struct flash_driver nuspi_flash; extern const struct flash_driver ocl_flash; extern const struct flash_driver pic32mx_flash; extern const struct flash_driver psoc4_flash; diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c index 981f5b599..9b452cc4e 100644 --- a/src/flash/nor/drivers.c +++ b/src/flash/nor/drivers.c @@ -31,6 +31,8 @@ static const struct flash_driver * const flash_drivers[] = { &cc3220sf_flash, &cc26xx_flash, &cfi_flash, + &cm32m4xxr_flash, + &custom_flash, &dsp5680xx_flash, &efm32_flash, &em357_flash, @@ -56,6 +58,7 @@ static const struct flash_driver * const flash_drivers[] = { &nrf5_flash, &nrf51_flash, &numicro_flash, + &nuspi_flash, &ocl_flash, &pic32mx_flash, &psoc4_flash, diff --git a/src/flash/nor/nuspi.c b/src/flash/nor/nuspi.c new file mode 100644 index 000000000..caabd2ab0 --- /dev/null +++ b/src/flash/nor/nuspi.c @@ -0,0 +1,1168 @@ +/*************************************************************************** + * Copyright (C) 2010 by Antonio Borneo * + * Modified by Yanwen Wang based on fespi.c * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +/* The Nuclei SPI controller is a SPI bus controller + * specifically designed for SPI Flash Memories on Nuclei RISC-V platforms. + * + * Two working modes are available: + * - SW mode: the SPI is controlled by SW. Any custom commands can be sent + * on the bus. Writes are only possible in this mode. + * - HW mode: Memory content is directly + * accessible in CPU memory space. CPU can read and execute memory content. + */ + +/* ATTENTION: + * To have flash memory mapped in CPU memory space, the controller + * must have "HW mode" enabled. + * 1) The command "reset init" has to initialize the controller and put + * it in HW mode (this is actually the default out of reset for Freedom E systems). + * 2) every command in this file have to return to prompt in HW mode. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "imp.h" +#include "spi.h" +#include +#include +#include +#include "target/riscv/riscv.h" + +/* Register offsets */ + +#define NUSPI_REG_SCKDIV (0x00) +#define NUSPI_REG_SCKMODE (0x04) +#define NUSPI_REG_SCKSAMPLE (0x08) +#define NUSPI_REG_FORCE (0x0C) +#define NUSPI_REG_CSID (0x10) +#define NUSPI_REG_CSDEF (0x14) +#define NUSPI_REG_CSMODE (0x18) +#define NUSPI_REG_VERSION (0x1C) + +#define NUSPI_REG_DCSSCK (0x28) +#define NUSPI_REG_DSCKCS (0x2a) +#define NUSPI_REG_DINTERCS (0x2c) +#define NUSPI_REG_DINTERXFR (0x2e) + +#define NUSPI_REG_FMT (0x40) + +#define NUSPI_REG_TXDATA (0x48) +#define NUSPI_REG_RXDATA (0x4C) +#define NUSPI_REG_TXMARK (0x50) +#define NUSPI_REG_RXMARK (0x54) + +#define NUSPI_REG_FCTRL (0x60) +#define NUSPI_REG_FFMT (0x64) + +#define NUSPI_REG_IE (0x70) +#define NUSPI_REG_IP (0x74) +#define NUSPI_REG_FFMT1 (0x78) +#define NUSPI_REG_STATUS (0x7C) +#define NUSPI_REG_RXEDGE (0x80) +#define NUSPI_REG_CR (0x84) + +/* Fields */ + +#define NUSPI_SCK_POL (0x1) +#define NUSPI_SCK_PHA (0x2) + +#define NUSPI_FMT_PROTO(x) ((x) & 0x3) +#define NUSPI_FMT_ENDIAN(x) (((x) & 0x1) << 2) +#define NUSPI_FMT_DIR(x) (((x) & 0x1) << 3) +#define NUSPI_FMT_LEN(x) (((x) & 0xf) << 16)//TODO: + +/* TXMARK register */ +#define NUSPI_TXWM(x) ((x) & 0xFFFF)//TODO: +/* RXMARK register */ +#define NUSPI_RXWM(x) ((x) & 0xFFFF)//TODO: + +#define NUSPI_IP_TXWM (0x1) +#define NUSPI_IP_RXWM (0x2) + +#define NUSPI_FCTRL_EN (0x1) + +#define NUSPI_INSN_CMD_EN (0x1) +#define NUSPI_INSN_ADDR_LEN(x) (((x) & 0x7) << 1) +#define NUSPI_INSN_PAD_CNT(x) (((x) & 0xf) << 4) +#define NUSPI_INSN_CMD_PROTO(x) (((x) & 0x3) << 8) +#define NUSPI_INSN_ADDR_PROTO(x) (((x) & 0x3) << 10) +#define NUSPI_INSN_DATA_PROTO(x) (((x) & 0x3) << 12) +#define NUSPI_INSN_CMD_CODE(x) (((x) & 0xff) << 16) +#define NUSPI_INSN_PAD_CODE(x) (((x) & 0xff) << 24) + +#define NUSPI_STAT_BUSY (0x1 << 0) +#define NUSPI_STAT_TXFULL (0x1 << 4) +#define NUSPI_STAT_RXEMPTY (0x1 << 5) + +/* Values */ +#define SPIFLASH_3BYTE_FAST_READ (0x03) +#define SPIFLASH_4BYTE_FAST_READ (0x13) +#define SPIFLASH_ENTER_4BYTE (0xB7) +#define SPIFLASH_EXIT_4BYTE (0xE9) +#define SPIFLASH_ENABLE_RESET (0x66) +#define SPIFLASH_RESET_DEVICE (0x99) +#define SPIFLASH_WRITE_STATUS1 (0x01) + +#define NUSPI_CSMODE_AUTO (0) +#define NUSPI_CSMODE_HOLD (2) +#define NUSPI_CSMODE_OFF (3) + +#define NUSPI_DIR_RX (0) +#define NUSPI_DIR_TX (1) + +#define NUSPI_PROTO_S (0) +#define NUSPI_PROTO_D (1) +#define NUSPI_PROTO_Q (2) + +#define NUSPI_ENDIAN_MSB (0) +#define NUSPI_ENDIAN_LSB (1) + +/* Timeout in ms */ +#define NUSPI_CMD_TIMEOUT (100) +#define NUSPI_PROBE_TIMEOUT (100) +#define NUSPI_MAX_TIMEOUT (3000) +#define NUSPI_SIM_TIMEOUT (0x7FFFFFFF) + +#define NUSPI_FLAGS_32B_DAT (1 << 0) + +struct nuspi_flash_bank { + uint32_t version; + int nuspi_flags; + bool probed; + target_addr_t ctrl_base; + const struct flash_device *dev; + bool simulation; +}; + +struct nuspi_target { + char *name; + uint32_t tap_idcode; + uint32_t tap_idmask; + uint32_t ctrl_base; +}; + +/* TODO !!! What is the right naming convention here? */ +static const struct nuspi_target target_devices[] = { + /* name, tap_idcode, ctrl_base */ + { "Freedom E310-G000 SPI Flash", 0x10e31913, 0xFFFFFFFF, 0x10014000 }, + { "Nuclei SoC SPI Flash", 0x00000a6d, 0xFFF, 0x10014000}, + { NULL, 0, 0, 0} +}; + +FLASH_BANK_COMMAND_HANDLER(nuspi_flash_bank_command) +{ + struct nuspi_flash_bank *nuspi_info; + + LOG_DEBUG("%s", __func__); + + if (CMD_ARGC < 6) + return ERROR_COMMAND_SYNTAX_ERROR; + + nuspi_info = malloc(sizeof(struct nuspi_flash_bank)); + if (nuspi_info == NULL) { + LOG_ERROR("not enough memory"); + return ERROR_FAIL; + } + + bank->driver_priv = nuspi_info; + nuspi_info->probed = false; + nuspi_info->ctrl_base = 0; + if (CMD_ARGC >= 7) { + COMMAND_PARSE_ADDRESS(CMD_ARGV[6], nuspi_info->ctrl_base); + LOG_DEBUG("ASSUMING NUSPI device at ctrl_base = " TARGET_ADDR_FMT, + nuspi_info->ctrl_base); + } + nuspi_info->simulation = false; + if (CMD_ARGC >= 8) { + if(strcmp(CMD_ARGV[7], "simulation") == 0) { + nuspi_info->simulation = true; + LOG_DEBUG("Nuspi Simulation Mode"); + } + } + + return ERROR_OK; +} + +static int nuspi_read_reg(struct flash_bank *bank, uint32_t *value, target_addr_t address) +{ + struct target *target = bank->target; + struct nuspi_flash_bank *nuspi_info = bank->driver_priv; + + int result = target_read_u32(target, nuspi_info->ctrl_base + address, value); + if (result != ERROR_OK) { + LOG_ERROR("nuspi_read_reg() error at " TARGET_ADDR_FMT, + nuspi_info->ctrl_base + address); + return result; + } + return ERROR_OK; +} + +static int nuspi_write_reg(struct flash_bank *bank, target_addr_t address, uint32_t value) +{ + struct target *target = bank->target; + struct nuspi_flash_bank *nuspi_info = bank->driver_priv; + + int result = target_write_u32(target, nuspi_info->ctrl_base + address, value); + if (result != ERROR_OK) { + LOG_ERROR("nuspi_write_reg() error writing 0x%" PRIx32 " to " TARGET_ADDR_FMT, + value, nuspi_info->ctrl_base + address); + return result; + } + return ERROR_OK; +} + +static int nuspi_disable_hw_mode(struct flash_bank *bank) +{ + uint32_t fctrl; + if (nuspi_read_reg(bank, &fctrl, NUSPI_REG_FCTRL) != ERROR_OK) + return ERROR_FAIL; + return nuspi_write_reg(bank, NUSPI_REG_FCTRL, fctrl & ~NUSPI_FCTRL_EN); +} + +static int nuspi_enable_hw_mode(struct flash_bank *bank) +{ + uint32_t fctrl; + if (nuspi_read_reg(bank, &fctrl, NUSPI_REG_FCTRL) != ERROR_OK) + return ERROR_FAIL; + return nuspi_write_reg(bank, NUSPI_REG_FCTRL, fctrl | NUSPI_FCTRL_EN); +} + +static int nuspi_set_dir(struct flash_bank *bank, bool dir) +{ + uint32_t fmt; + if (nuspi_read_reg(bank, &fmt, NUSPI_REG_FMT) != ERROR_OK) + return ERROR_FAIL; + + return nuspi_write_reg(bank, NUSPI_REG_FMT, + (fmt & ~(NUSPI_FMT_DIR(0xFFFFFFFF))) | NUSPI_FMT_DIR(dir)); +} + +static int nuspi_txwm_wait(struct flash_bank *bank) +{ + int64_t start = timeval_ms(); + struct nuspi_flash_bank *nuspi_info = bank->driver_priv; + if(nuspi_info->nuspi_flags & NUSPI_FLAGS_32B_DAT) { + while (1) { + uint32_t status; + if (nuspi_read_reg(bank, &status, NUSPI_REG_STATUS) != ERROR_OK) + return ERROR_FAIL; + if (0 == (status & NUSPI_STAT_BUSY)) + break; + int64_t now = timeval_ms(); + if (now - start > 1000) { + LOG_ERROR("ip.txwm didn't get set."); + return ERROR_TARGET_TIMEOUT; + } + } + } else { + while (1) { + uint32_t ip; + if (nuspi_read_reg(bank, &ip, NUSPI_REG_IP) != ERROR_OK) + return ERROR_FAIL; + if (ip & NUSPI_IP_TXWM) + break; + int64_t now = timeval_ms(); + if (now - start > 1000) { + LOG_ERROR("ip.txwm didn't get set."); + return ERROR_TARGET_TIMEOUT; + } + } + } + + return ERROR_OK; +} + +static int nuspi_tx(struct flash_bank *bank, uint8_t in) +{ + int64_t start = timeval_ms(); + struct nuspi_flash_bank *nuspi_info = bank->driver_priv; + if(nuspi_info->nuspi_flags & NUSPI_FLAGS_32B_DAT) { + while (1) { + uint32_t status; + if (nuspi_read_reg(bank, &status, NUSPI_REG_STATUS) != ERROR_OK) + return ERROR_FAIL; + if (!(status & NUSPI_STAT_TXFULL)) + break; + int64_t now = timeval_ms(); + if (now - start > 1000) { + LOG_ERROR("txfifo stayed negative."); + return ERROR_TARGET_TIMEOUT; + } + } + } else { + while (1) { + uint32_t txfifo; + if (nuspi_read_reg(bank, &txfifo, NUSPI_REG_TXDATA) != ERROR_OK) + return ERROR_FAIL; + if (!(txfifo >> 31)) + break; + int64_t now = timeval_ms(); + if (now - start > 1000) { + LOG_ERROR("txfifo stayed negative."); + return ERROR_TARGET_TIMEOUT; + } + } + } + return nuspi_write_reg(bank, NUSPI_REG_TXDATA, in); +} + +static int nuspi_rx(struct flash_bank *bank, uint8_t *out) +{ + int64_t start = timeval_ms(); + uint32_t value; + struct nuspi_flash_bank *nuspi_info = bank->driver_priv; + if(nuspi_info->nuspi_flags & NUSPI_FLAGS_32B_DAT) { + while (1) { + if (nuspi_read_reg(bank, &value, NUSPI_REG_STATUS) != ERROR_OK) + return ERROR_FAIL; + if (!(value & NUSPI_STAT_RXEMPTY)) + break; + int64_t now = timeval_ms(); + if (now - start > 1000) { + LOG_ERROR("rxfifo didn't go positive (value=0x%x).", value); + return ERROR_TARGET_TIMEOUT; + } + } + if (nuspi_read_reg(bank, &value, NUSPI_REG_RXDATA) != ERROR_OK) + return ERROR_FAIL; + } else { + while (1) { + if (nuspi_read_reg(bank, &value, NUSPI_REG_RXDATA) != ERROR_OK) + return ERROR_FAIL; + if (!(value >> 31)) + break; + int64_t now = timeval_ms(); + if (now - start > 1000) { + LOG_ERROR("rxfifo didn't go positive (value=0x%" PRIx32 ").", value); + return ERROR_TARGET_TIMEOUT; + } + } + } + if (out) + *out = value & 0xff; + + return ERROR_OK; +} + +/* TODO!!! Why don't we need to call this after writing? */ +static int nuspi_wip(struct flash_bank *bank, int timeout) +{ + int64_t endtime; + + nuspi_set_dir(bank, NUSPI_DIR_RX); + + if (nuspi_write_reg(bank, NUSPI_REG_CSMODE, NUSPI_CSMODE_HOLD) != ERROR_OK) + return ERROR_FAIL; + endtime = timeval_ms() + timeout; + + nuspi_tx(bank, SPIFLASH_READ_STATUS); + if (nuspi_rx(bank, NULL) != ERROR_OK) + return ERROR_FAIL; + do { + alive_sleep(1); + nuspi_tx(bank, 0); + uint8_t rx; + if (nuspi_rx(bank, &rx) != ERROR_OK) + return ERROR_FAIL; + if ((rx & SPIFLASH_BSY_BIT) == 0) { + if (nuspi_write_reg(bank, NUSPI_REG_CSMODE, NUSPI_CSMODE_AUTO) != ERROR_OK) + return ERROR_FAIL; + nuspi_set_dir(bank, NUSPI_DIR_TX); + return ERROR_OK; + } + } while (timeval_ms() < endtime); + + LOG_ERROR("timeout"); + return ERROR_FAIL; +} + +static int nuspi_reset(struct flash_bank *bank) +{ + /* Clear Rx FIFO */ + uint32_t value; + struct nuspi_flash_bank *nuspi_info = bank->driver_priv; + if(nuspi_info->nuspi_flags & NUSPI_FLAGS_32B_DAT) { + while (1) { + if (nuspi_read_reg(bank, &value, NUSPI_REG_STATUS) != ERROR_OK) + return ERROR_FAIL; + if (value & NUSPI_STAT_RXEMPTY) + break; + if (nuspi_read_reg(bank, &value, NUSPI_REG_RXDATA) != ERROR_OK) + return ERROR_FAIL; + } + } else { + while (1) { + if (nuspi_read_reg(bank, &value, NUSPI_REG_RXDATA) != ERROR_OK) + return ERROR_FAIL; + if (value >> 31) + break; + } + } + if (nuspi_write_reg(bank, NUSPI_REG_SCKMODE, 0x0) != ERROR_OK) + return ERROR_FAIL; + if (nuspi_write_reg(bank, NUSPI_REG_FORCE, 0x3) != ERROR_OK) + return ERROR_FAIL; + if (nuspi_write_reg(bank, NUSPI_REG_FCTRL, 0x0) != ERROR_OK) + return ERROR_FAIL; + if (nuspi_write_reg(bank, NUSPI_REG_FMT, 0x80008) != ERROR_OK) + return ERROR_FAIL; + if (nuspi_write_reg(bank, NUSPI_REG_FFMT, 0x30007) != ERROR_OK) + return ERROR_FAIL; + if (nuspi_write_reg(bank, NUSPI_REG_RXEDGE, 0x0) != ERROR_OK) + return ERROR_FAIL; + return ERROR_OK; +} + +static int nuspi_set_xip_read_cmd(struct flash_bank *bank) +{ + uint32_t temp; + struct nuspi_flash_bank *nuspi_info = bank->driver_priv; + if (nuspi_read_reg(bank, &temp, NUSPI_REG_FFMT) != ERROR_OK) + return ERROR_FAIL; + temp &= ~(0x7 << 1); + temp &= ~(0xF << 4); + temp &= ~(0xFF << 16); + temp |= NUSPI_INSN_PAD_CNT(0); + if (bank->size > 0x1000000) { + temp |= NUSPI_INSN_ADDR_LEN(4); + } else { + temp |= NUSPI_INSN_ADDR_LEN(3); + } + if ((bank->size > 0x1000000) & (nuspi_info->dev->erase_cmd == 0xd8)) { + temp |= NUSPI_INSN_CMD_CODE(SPIFLASH_4BYTE_FAST_READ); + } else { + temp |= NUSPI_INSN_CMD_CODE(nuspi_info->dev->read_cmd); + } + return nuspi_write_reg(bank, NUSPI_REG_FFMT, temp); +} + +static int flash_reset(struct flash_bank *bank) +{ + struct target *target = bank->target; + struct nuspi_flash_bank *nuspi_info = bank->driver_priv; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + /* Send SPI command "66h" */ + if (nuspi_write_reg(bank, NUSPI_REG_CSMODE, NUSPI_CSMODE_HOLD) != ERROR_OK) + return ERROR_FAIL; + + nuspi_tx(bank, SPIFLASH_ENABLE_RESET); + if (nuspi_txwm_wait(bank) != ERROR_OK) + return ERROR_FAIL; + + if (nuspi_write_reg(bank, NUSPI_REG_CSMODE, NUSPI_CSMODE_AUTO) != ERROR_OK) + return ERROR_FAIL; + + /* Send SPI command "99h" */ + if (nuspi_write_reg(bank, NUSPI_REG_CSMODE, NUSPI_CSMODE_HOLD) != ERROR_OK) + return ERROR_FAIL; + + nuspi_tx(bank, SPIFLASH_RESET_DEVICE); + if (nuspi_txwm_wait(bank) != ERROR_OK) + return ERROR_FAIL; + + if (nuspi_write_reg(bank, NUSPI_REG_CSMODE, NUSPI_CSMODE_AUTO) != ERROR_OK) + return ERROR_FAIL; + + if (nuspi_info->simulation) { + if (nuspi_wip(bank, NUSPI_SIM_TIMEOUT) != ERROR_OK) + return ERROR_FAIL; + } else { + if (nuspi_wip(bank, NUSPI_MAX_TIMEOUT) != ERROR_OK) + return ERROR_FAIL; + } + + //unlock protect + /* Send SPI command "01h" */ + if (nuspi_write_reg(bank, NUSPI_REG_CSMODE, NUSPI_CSMODE_HOLD) != ERROR_OK) + return ERROR_FAIL; + + nuspi_tx(bank, SPIFLASH_WRITE_STATUS1); + nuspi_tx(bank, 0x00); + if (nuspi_txwm_wait(bank) != ERROR_OK) + return ERROR_FAIL; + + if (nuspi_write_reg(bank, NUSPI_REG_CSMODE, NUSPI_CSMODE_AUTO) != ERROR_OK) + return ERROR_FAIL; + + return ERROR_OK; +} + +static int nuspi_enter_4byte_address_mode(struct flash_bank *bank) +{ + int retval = ERROR_OK; + struct target *target = bank->target; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* Send SPI command "enter 4byte" */ + if (nuspi_write_reg(bank, NUSPI_REG_CSMODE, NUSPI_CSMODE_HOLD) != ERROR_OK) + return ERROR_FAIL; + + retval = nuspi_tx(bank, SPIFLASH_ENTER_4BYTE); + if (retval != ERROR_OK) + return retval; + + if (nuspi_write_reg(bank, NUSPI_REG_CSMODE, NUSPI_CSMODE_AUTO) != ERROR_OK) + return ERROR_FAIL; + + return ERROR_OK; +} + +static int nuspi_exit_4byte_address_mode(struct flash_bank *bank) +{ + int retval = ERROR_OK; + struct target *target = bank->target; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* Send SPI command "enter 4byte" */ + if (nuspi_write_reg(bank, NUSPI_REG_CSMODE, NUSPI_CSMODE_HOLD) != ERROR_OK) + return ERROR_FAIL; + + retval = nuspi_tx(bank, SPIFLASH_EXIT_4BYTE); + if (retval != ERROR_OK) + return retval; + + if (nuspi_write_reg(bank, NUSPI_REG_CSMODE, NUSPI_CSMODE_AUTO) != ERROR_OK) + return ERROR_FAIL; + + return ERROR_OK; +} + +static int nuspi_erase_sector(struct flash_bank *bank, int sector) +{ + struct nuspi_flash_bank *nuspi_info = bank->driver_priv; + int retval = ERROR_OK; + + retval = nuspi_tx(bank, SPIFLASH_WRITE_ENABLE); + if (retval != ERROR_OK) + return retval; + retval = nuspi_txwm_wait(bank); + if (retval != ERROR_OK) + return retval; + + if (nuspi_write_reg(bank, NUSPI_REG_CSMODE, NUSPI_CSMODE_HOLD) != ERROR_OK) + return ERROR_FAIL; + retval = nuspi_tx(bank, nuspi_info->dev->erase_cmd); + if (retval != ERROR_OK) + return retval; + sector = bank->sectors[sector].offset; + if (bank->size > 0x1000000) { + retval = nuspi_tx(bank, sector >> 24); + if (retval != ERROR_OK) + return retval; + } + retval = nuspi_tx(bank, sector >> 16); + if (retval != ERROR_OK) + return retval; + retval = nuspi_tx(bank, sector >> 8); + if (retval != ERROR_OK) + return retval; + retval = nuspi_tx(bank, sector); + if (retval != ERROR_OK) + return retval; + retval = nuspi_txwm_wait(bank); + if (retval != ERROR_OK) + return retval; + if (nuspi_write_reg(bank, NUSPI_REG_CSMODE, NUSPI_CSMODE_AUTO) != ERROR_OK) + return ERROR_FAIL; + + if (nuspi_info->simulation) { + retval = nuspi_wip(bank, NUSPI_SIM_TIMEOUT); + } else { + retval = nuspi_wip(bank, NUSPI_MAX_TIMEOUT); + } + if (retval != ERROR_OK) + return retval; + + return ERROR_OK; +} + +static int nuspi_erase(struct flash_bank *bank, unsigned int first, + unsigned int last) +{ + struct target *target = bank->target; + struct nuspi_flash_bank *nuspi_info = bank->driver_priv; + int retval = ERROR_OK; + + LOG_DEBUG("%s: from sector %u to sector %u", __func__, first, last); + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if ((last < first) || (last >= bank->num_sectors)) { + LOG_ERROR("Flash sector invalid"); + return ERROR_FLASH_SECTOR_INVALID; + } + + if (!(nuspi_info->probed)) { + LOG_ERROR("Flash bank not probed"); + return ERROR_FLASH_BANK_NOT_PROBED; + } + + for (unsigned int sector = first; sector <= last; sector++) { + if (bank->sectors[sector].is_protected) { + LOG_ERROR("Flash sector %u protected", sector); + return ERROR_FAIL; + } + } + + if (nuspi_info->dev->erase_cmd == 0x00) + return ERROR_FLASH_OPER_UNSUPPORTED; + + if (nuspi_reset(bank) != ERROR_OK) + return ERROR_FAIL; + if (nuspi_write_reg(bank, NUSPI_REG_TXMARK, NUSPI_TXWM(1)) != ERROR_OK) + return ERROR_FAIL; + retval = nuspi_txwm_wait(bank); + if (retval != ERROR_OK) { + LOG_ERROR("WM Didn't go high before attempting."); + return retval; + } + + /* Disable Hardware accesses*/ + if (nuspi_disable_hw_mode(bank) != ERROR_OK) + return ERROR_FAIL; + + /* poll WIP */ + if (nuspi_info->simulation) { + retval = nuspi_wip(bank, NUSPI_SIM_TIMEOUT); + } else { + retval = nuspi_wip(bank, NUSPI_MAX_TIMEOUT); + } + if (retval != ERROR_OK) + goto done; + + if ((bank->size > 0x1000000) & (nuspi_info->dev->erase_cmd == 0xd8)) { + retval = nuspi_enter_4byte_address_mode(bank); + if (retval != ERROR_OK) + return retval; + } + + for (unsigned int sector = first; sector <= last; sector++) { + retval = nuspi_erase_sector(bank, sector); + if (retval != ERROR_OK) + goto done; + keep_alive(); + } + + /* Switch to HW mode before return to prompt */ +done: + if (nuspi_enable_hw_mode(bank) != ERROR_OK) + return ERROR_FAIL; + + if (nuspi_set_xip_read_cmd(bank) != ERROR_OK) + return ERROR_FAIL; + + return retval; +} + +static int nuspi_protect(struct flash_bank *bank, int set, + unsigned int first, unsigned int last) +{ + for (unsigned int sector = first; sector <= last; sector++) + bank->sectors[sector].is_protected = set; + return ERROR_OK; +} + +static int slow_nuspi_write_buffer(struct flash_bank *bank, + const uint8_t *buffer, uint32_t offset, uint32_t len) +{ + struct nuspi_flash_bank *nuspi_info = bank->driver_priv; + uint32_t ii; + + /* TODO!!! assert that len < page size */ + + if (nuspi_tx(bank, SPIFLASH_WRITE_ENABLE) != ERROR_OK) + return ERROR_FAIL; + if (nuspi_txwm_wait(bank) != ERROR_OK) + return ERROR_FAIL; + + if (nuspi_write_reg(bank, NUSPI_REG_CSMODE, NUSPI_CSMODE_HOLD) != ERROR_OK) + return ERROR_FAIL; + + if (nuspi_tx(bank, nuspi_info->dev->pprog_cmd) != ERROR_OK) + return ERROR_FAIL; + + if (bank->size > 0x1000000 && nuspi_tx(bank, offset >> 24) != ERROR_OK) + return ERROR_FAIL; + if (nuspi_tx(bank, offset >> 16) != ERROR_OK) + return ERROR_FAIL; + if (nuspi_tx(bank, offset >> 8) != ERROR_OK) + return ERROR_FAIL; + if (nuspi_tx(bank, offset) != ERROR_OK) + return ERROR_FAIL; + + for (ii = 0; ii < len; ii++) { + if (nuspi_tx(bank, buffer[ii]) != ERROR_OK) + return ERROR_FAIL; + } + + if (nuspi_txwm_wait(bank) != ERROR_OK) + return ERROR_FAIL; + + if (nuspi_write_reg(bank, NUSPI_REG_CSMODE, NUSPI_CSMODE_AUTO) != ERROR_OK) + return ERROR_FAIL; + + keep_alive(); + + return ERROR_OK; +} + +static const uint8_t riscv32_bin[] = { +#include "../../../contrib/loaders/flash/nuspi/riscv32_nuspi.inc" +}; + +static const uint8_t riscv64_bin[] = { +#include "../../../contrib/loaders/flash/nuspi/riscv64_nuspi.inc" +}; + +static int nuspi_write(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + struct target *target = bank->target; + struct nuspi_flash_bank *nuspi_info = bank->driver_priv; + uint32_t cur_count, page_size; + int retval = ERROR_OK; + + LOG_DEBUG("bank->size=0x%x offset=0x%08" PRIx32 " count=0x%08" PRIx32, + bank->size, offset, count); + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (offset + count > nuspi_info->dev->size_in_bytes) { + LOG_WARNING("Write past end of flash. Extra data discarded."); + count = nuspi_info->dev->size_in_bytes - offset; + } + + /* Check sector protection */ + for (unsigned int sector = 0; sector < bank->num_sectors; sector++) { + /* Start offset in or before this sector? */ + /* End offset in or behind this sector? */ + if ((offset < + (bank->sectors[sector].offset + bank->sectors[sector].size)) + && ((offset + count - 1) >= bank->sectors[sector].offset) + && bank->sectors[sector].is_protected) { + LOG_ERROR("Flash sector %u protected", sector); + return ERROR_FAIL; + } + } + + int xlen = riscv_xlen(target); + struct working_area *algorithm_wa = NULL; + struct working_area *data_wa = NULL; + const uint8_t *bin; + size_t bin_size; + if (xlen == 32) { + bin = riscv32_bin; + bin_size = sizeof(riscv32_bin); + } else { + bin = riscv64_bin; + bin_size = sizeof(riscv64_bin); + } + + unsigned data_wa_size = 0; + if (target_alloc_working_area(target, bin_size, &algorithm_wa) == ERROR_OK) { + retval = target_write_buffer(target, algorithm_wa->address, + bin_size, bin); + if (retval != ERROR_OK) { + LOG_ERROR("Failed to write code to " TARGET_ADDR_FMT ": %d", + algorithm_wa->address, retval); + target_free_working_area(target, algorithm_wa); + algorithm_wa = NULL; + + } else { + data_wa_size = MIN(target->working_area_size - algorithm_wa->size, count); + while (1) { + if (data_wa_size < 128) { + LOG_WARNING("Couldn't allocate data working area."); + target_free_working_area(target, algorithm_wa); + algorithm_wa = NULL; + } + if (target_alloc_working_area_try(target, data_wa_size, &data_wa) == + ERROR_OK) { + break; + } + + data_wa_size = data_wa_size * 3 / 4; + } + } + } else { + LOG_WARNING("Couldn't allocate %zd-byte working area.", bin_size); + algorithm_wa = NULL; + } + + /* If no valid page_size, use reasonable default. */ + page_size = nuspi_info->dev->pagesize ? + nuspi_info->dev->pagesize : SPIFLASH_DEF_PAGESIZE; + + /* Disable Hardware accesses*/ + if (nuspi_disable_hw_mode(bank) != ERROR_OK) + return ERROR_FAIL; + + if (algorithm_wa) { + struct reg_param reg_params[6]; + init_reg_param(®_params[0], "a0", xlen, PARAM_IN_OUT); + init_reg_param(®_params[1], "a1", xlen, PARAM_OUT); + init_reg_param(®_params[2], "a2", xlen, PARAM_OUT); + init_reg_param(®_params[3], "a3", xlen, PARAM_OUT); + init_reg_param(®_params[4], "a4", xlen, PARAM_OUT); + init_reg_param(®_params[5], "a5", xlen, PARAM_OUT); + + while (count > 0) { + cur_count = MIN(count, data_wa_size); + buf_set_u64(reg_params[0].value, 0, xlen, nuspi_info->ctrl_base); + buf_set_u64(reg_params[1].value, 0, xlen, page_size); + buf_set_u64(reg_params[2].value, 0, xlen, data_wa->address); + buf_set_u64(reg_params[3].value, 0, xlen, offset); + buf_set_u64(reg_params[4].value, 0, xlen, cur_count); + buf_set_u64(reg_params[5].value, 0, xlen, + nuspi_info->dev->pprog_cmd | (bank->size > 0x1000000 ? 0x100 : 0)); + + retval = target_write_buffer(target, data_wa->address, cur_count, + buffer); + if (retval != ERROR_OK) { + LOG_DEBUG("Failed to write %d bytes to " TARGET_ADDR_FMT ": %d", + cur_count, data_wa->address, retval); + goto err; + } + + LOG_DEBUG("write(ctrl_base=0x%" TARGET_PRIxADDR ", page_size=0x%x, " + "address=0x%" TARGET_PRIxADDR ", offset=0x%" PRIx32 + ", count=0x%" PRIx32 "), buffer=%02x %02x %02x %02x %02x %02x ..." PRIx32, + nuspi_info->ctrl_base, page_size, data_wa->address, offset, cur_count, + buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5]); + if (nuspi_info->simulation) { + retval = target_run_algorithm(target, 0, NULL, + ARRAY_SIZE(reg_params), reg_params, + algorithm_wa->address, 0, NUSPI_SIM_TIMEOUT, NULL); + } else { + retval = target_run_algorithm(target, 0, NULL, + ARRAY_SIZE(reg_params), reg_params, + algorithm_wa->address, 0, cur_count * 2, NULL); + } + if (retval != ERROR_OK) { + LOG_ERROR("Failed to execute algorithm at " TARGET_ADDR_FMT ": %d", + algorithm_wa->address, retval); + goto err; + } + + int algorithm_result = buf_get_u64(reg_params[0].value, 0, xlen); + if (algorithm_result != 0) { + LOG_ERROR("Algorithm returned error %d", algorithm_result); + retval = ERROR_FAIL; + goto err; + } + + buffer += cur_count; + offset += cur_count; + count -= cur_count; + } + + target_free_working_area(target, data_wa); + target_free_working_area(target, algorithm_wa); + + } else { + nuspi_txwm_wait(bank); + + /* poll WIP */ + if (nuspi_info->simulation) { + retval = nuspi_wip(bank, NUSPI_SIM_TIMEOUT); + } else { + retval = nuspi_wip(bank, NUSPI_PROBE_TIMEOUT); + } + if (retval != ERROR_OK) + goto err; + + uint32_t page_offset = offset % page_size; + /* central part, aligned words */ + while (count > 0) { + /* clip block at page boundary */ + if (page_offset + count > page_size) + cur_count = page_size - page_offset; + else + cur_count = count; + + retval = slow_nuspi_write_buffer(bank, buffer, offset, cur_count); + if (retval != ERROR_OK) + goto err; + + page_offset = 0; + buffer += cur_count; + offset += cur_count; + count -= cur_count; + } + } + +err: + if (algorithm_wa) { + target_free_working_area(target, data_wa); + target_free_working_area(target, algorithm_wa); + } + + if ((bank->size > 0x1000000) & (nuspi_info->dev->erase_cmd == 0xd8)) { + if (nuspi_exit_4byte_address_mode(bank) != ERROR_OK) + return ERROR_FAIL; + } + /* Switch to HW mode before return to prompt */ + if (nuspi_enable_hw_mode(bank) != ERROR_OK) + return ERROR_FAIL; + + if (nuspi_set_xip_read_cmd(bank) != ERROR_OK) + return ERROR_FAIL; + + return retval; +} + +static int nuspi_read(struct flash_bank *bank, uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + if (nuspi_set_xip_read_cmd(bank) != ERROR_OK) + return ERROR_FAIL; + return default_flash_read(bank, buffer, offset, count); +} + +/* Return ID of flash device */ +/* On exit, SW mode is kept */ +static int nuspi_read_flash_id(struct flash_bank *bank, uint32_t *id) +{ + struct target *target = bank->target; + int retval = ERROR_OK; + struct nuspi_flash_bank *nuspi_info = bank->driver_priv; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + nuspi_txwm_wait(bank); + + /* poll WIP */ + if (nuspi_info->simulation) { + retval = nuspi_wip(bank, NUSPI_SIM_TIMEOUT); + } else { + retval = nuspi_wip(bank, NUSPI_PROBE_TIMEOUT); + } + if (retval != ERROR_OK) + return retval; + + nuspi_set_dir(bank, NUSPI_DIR_RX); + + /* Send SPI command "read ID" */ + if (nuspi_write_reg(bank, NUSPI_REG_CSMODE, NUSPI_CSMODE_HOLD) != ERROR_OK) + return ERROR_FAIL; + + nuspi_tx(bank, SPIFLASH_READ_ID); + /* Send dummy bytes to actually read the ID.*/ + nuspi_tx(bank, 0); + nuspi_tx(bank, 0); + nuspi_tx(bank, 0); + + /* read ID from Receive Register */ + *id = 0; + if (nuspi_rx(bank, NULL) != ERROR_OK) + return ERROR_FAIL; + uint8_t rx; + if (nuspi_rx(bank, &rx) != ERROR_OK) + return ERROR_FAIL; + *id = rx; + if (nuspi_rx(bank, &rx) != ERROR_OK) + return ERROR_FAIL; + *id |= (rx << 8); + if (nuspi_rx(bank, &rx) != ERROR_OK) + return ERROR_FAIL; + *id |= (rx << 16); + + if (nuspi_write_reg(bank, NUSPI_REG_CSMODE, NUSPI_CSMODE_AUTO) != ERROR_OK) + return ERROR_FAIL; + + nuspi_set_dir(bank, NUSPI_DIR_TX); + + return ERROR_OK; +} + +static int gdb_flash_write_end_callback(struct target *target, + enum target_event event, void *priv) +{ + if (TARGET_EVENT_GDB_FLASH_WRITE_END == event) + { + if (nuspi_set_xip_read_cmd(priv) != ERROR_OK) + return ERROR_FAIL; + } + return ERROR_OK; +} + +static int nuspi_probe(struct flash_bank *bank) +{ + struct target *target = bank->target; + struct nuspi_flash_bank *nuspi_info = bank->driver_priv; + struct flash_sector *sectors; + uint32_t id = 0; /* silence uninitialized warning */ + const struct nuspi_target *target_device; + int retval = ERROR_OK; + uint32_t sectorsize; + + if (nuspi_info->probed) + free(bank->sectors); + nuspi_info->probed = false; + + for (target_device = target_devices ; target_device->name ; ++target_device) + /* NUCLEI: Special handling for nuclei risc-v target id code using mask */ + if (target_device->tap_idcode == (target->tap->idcode & target_device->tap_idmask)) + break; + + if (nuspi_info->ctrl_base == 0) { + nuspi_info->ctrl_base = target_device->ctrl_base; + } + + if (target_device->name) { + LOG_INFO("Valid NUSPI on device %s at address " TARGET_ADDR_FMT + " with spictrl regbase at " TARGET_ADDR_FMT, target_device->name, bank->base, nuspi_info->ctrl_base); + } else { + LOG_INFO("Valid NUSPI on unknown device at address " TARGET_ADDR_FMT + " with spictrl regbase at " TARGET_ADDR_FMT, bank->base, nuspi_info->ctrl_base); + } + + if (nuspi_read_reg(bank, &nuspi_info->version, NUSPI_REG_VERSION) != ERROR_OK) + return ERROR_FAIL; + nuspi_info->nuspi_flags = 0; + LOG_INFO("Nuclei SPI controller version 0x%08x", nuspi_info->version); + + if(nuspi_info->version >= 0x00010100) + nuspi_info->nuspi_flags |= NUSPI_FLAGS_32B_DAT; + + /* read and decode flash ID; returns in SW mode */ + if (nuspi_write_reg(bank, NUSPI_REG_TXMARK, NUSPI_TXWM(1)) != ERROR_OK) + return ERROR_FAIL; + nuspi_set_dir(bank, NUSPI_DIR_TX); + + /* Disable Hardware accesses*/ + if (nuspi_disable_hw_mode(bank) != ERROR_OK) + return ERROR_FAIL; + + if (nuspi_reset(bank) != ERROR_OK) + return ERROR_FAIL; + + if (flash_reset(bank) != ERROR_OK) + return ERROR_FAIL; + + target_register_event_callback(gdb_flash_write_end_callback, bank); + + for (int i = 0; i < 4; i++) { + if (nuspi_write_reg(bank, NUSPI_REG_SCKDIV, i) != ERROR_OK) + continue; + retval = nuspi_read_flash_id(bank, &id); + if (retval != ERROR_OK) + continue; + nuspi_info->dev = NULL; + for (const struct flash_device *p = flash_devices; p->name ; p++) { + if (p->device_id == id) { + nuspi_info->dev = p; + break; + } + } + if (NULL != nuspi_info->dev) + break; + } + + if (nuspi_enable_hw_mode(bank) != ERROR_OK) + return ERROR_FAIL; + + if (!nuspi_info->dev) { + LOG_ERROR("Unknown flash device (ID 0x%08" PRIx32 ")", id); + return ERROR_FAIL; + } + + LOG_INFO("Found flash device \'%s\' (ID 0x%08" PRIx32 ")", + nuspi_info->dev->name, nuspi_info->dev->device_id); + + /* Set correct size value */ + bank->size = nuspi_info->dev->size_in_bytes; + + if (bank->size <= (1UL << 16)) + LOG_WARNING("device needs 2-byte addresses - not implemented"); + + /* if no sectors, treat whole bank as single sector */ + sectorsize = nuspi_info->dev->sectorsize ? + nuspi_info->dev->sectorsize : nuspi_info->dev->size_in_bytes; + + /* create and fill sectors array */ + bank->num_sectors = nuspi_info->dev->size_in_bytes / sectorsize; + sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors); + if (sectors == NULL) { + LOG_ERROR("not enough memory"); + return ERROR_FAIL; + } + + for (unsigned int sector = 0; sector < bank->num_sectors; sector++) { + sectors[sector].offset = sector * sectorsize; + sectors[sector].size = sectorsize; + sectors[sector].is_erased = -1; + sectors[sector].is_protected = 0; + } + + bank->sectors = sectors; + if (nuspi_set_xip_read_cmd(bank) != ERROR_OK) + return ERROR_FAIL; + nuspi_info->probed = true; + return ERROR_OK; +} + +static int nuspi_auto_probe(struct flash_bank *bank) +{ + struct nuspi_flash_bank *nuspi_info = bank->driver_priv; + if (nuspi_info->probed) + return ERROR_OK; + return nuspi_probe(bank); +} + +static int nuspi_protect_check(struct flash_bank *bank) +{ + /* Nothing to do. Protection is only handled in SW. */ + return ERROR_OK; +} + +const struct flash_driver nuspi_flash = { + .name = "nuspi", + .flash_bank_command = nuspi_flash_bank_command, + .erase = nuspi_erase, + .protect = nuspi_protect, + .write = nuspi_write, + .read = nuspi_read, + .probe = nuspi_probe, + .auto_probe = nuspi_auto_probe, + .erase_check = default_flash_blank_check, + .protect_check = nuspi_protect_check, + .free_driver_priv = default_flash_free_driver_priv +};