Flash, FRAM and EEPROM driver for STM32 QUAD-/OCTOSPI interface 21/4321/15
authorAndreas Bolsch <hyphen0break@gmail.com>
Wed, 21 Dec 2016 09:35:58 +0000 (10:35 +0100)
committerTomas Vanek <vanekt@fbl.cz>
Sun, 8 Nov 2020 22:46:00 +0000 (22:46 +0000)
- write speed up to 150 kByte/s on STM32F469I-disco (due to
  SWD clock and USB connection), up to 1 MByte/s on Nucleo-F767ZI
  with external STLink-V3 or Nucleo-G474RE with two W25Q256FV in
  dual 4-line mode or STM32H73BI-Disco in octal mode
- tested with STM32L476G-disco (64MBit flash, 3-byte addr),
  STM32F412G-Disco, STM32F469I-Disco, STM32F746G-Disco, and
  STM32L476G-Disco (all 128Mbit flash, 3-byte addr),
  STM32F723E-Disco, STM32F769I-Disco (512Mbit flash, 4-byte addr)
  STM32L4R9I-Disco, STM32L4P5G-Disco (512MBit octo-flash, DTR, 4-byte addr)
  STM32H745I-Disco, STM32H747I-Disco (two 512MBit flash, 4-byte addr)
  STM32H73BI-Disco, STM32H735G-Disco (512MBit octo-flash, DTR, 4-byte addr)
- suitable cfg for Discovery boards included
- limited parsing of SFDP data if flash device not hardcoded
  (tested only in single/quad mode as most devices either don't
  support SFDP at all or have empty(!) SFDP memory)
- 'set' command for auto detection override (e. g. for EEPROMs)
- 'cmd' command for arbitrary SPI commands (reconfiguration, testing etc.)
- makefile for creation of binary loader files
- tcl/board/stm32f469discovery.cfg superseded by stm32f469i-disco.cfg
- tcl/board/stm32f7discovery.cfg removed as name is ambiguous
  (superseded by stm32f746g-disco.cfg vs. stm32f769i-disco.cfg)
- dual 4-line mode tested on Nucleo-F767ZI, Nucleo-H743ZI and Nucleo-H7A3ZI-Q
  with two W25Q256FV, and on Nucleo-L496ZP-P and Nucleo-L4R5ZI
  with two W25Q128FV, sample cfg files included and on STM32H745I-Disco,
  STM32H747I-Disco, STM32H750B-Disco
- read/verify/erase_check uses indirect read mode to work around silicon bug in
  H7, L4+ and MP1 memory mapped mode (last bytes not readable, accessing last
  bytes causes debug interface to hang)
- octospi supported only in single/dual 1-line, 2-line, 4-line
  and single 8-line modes, (not in hyper flash mode)

Requirements:
GPIOs must be initialized appropriately, and SPI flash chip be configured
appropriately (1-line ..., QPI, 4-byte addresses ...). This is board/chip
specific, cf. included cfg files. The driver infers most parameters from
current setting in CR, CCR, ... registers.

Change-Id: I54858fbbe8758c3a5fe58812e93f5f39514704f8
Signed-off-by: Andreas Bolsch <hyphen0break@gmail.com>
Reviewed-on: http://openocd.zylin.com/4321
Tested-by: jenkins
Reviewed-by: Tarek BOCHKATI <tarek.bouchkati@gmail.com>
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-by: Christopher Head <chead@zaber.com>
57 files changed:
contrib/loaders/flash/stmqspi/Makefile [new file with mode: 0644]
contrib/loaders/flash/stmqspi/gpio_conf_stm32.pl [new file with mode: 0755]
contrib/loaders/flash/stmqspi/stmoctospi_crc32.S [new file with mode: 0644]
contrib/loaders/flash/stmqspi/stmoctospi_crc32.inc [new file with mode: 0644]
contrib/loaders/flash/stmqspi/stmoctospi_erase_check.S [new file with mode: 0644]
contrib/loaders/flash/stmqspi/stmoctospi_erase_check.inc [new file with mode: 0644]
contrib/loaders/flash/stmqspi/stmoctospi_read.S [new file with mode: 0644]
contrib/loaders/flash/stmqspi/stmoctospi_read.inc [new file with mode: 0644]
contrib/loaders/flash/stmqspi/stmoctospi_write.S [new file with mode: 0644]
contrib/loaders/flash/stmqspi/stmoctospi_write.inc [new file with mode: 0644]
contrib/loaders/flash/stmqspi/stmqspi_crc32.S [new file with mode: 0644]
contrib/loaders/flash/stmqspi/stmqspi_crc32.inc [new file with mode: 0644]
contrib/loaders/flash/stmqspi/stmqspi_erase_check.S [new file with mode: 0644]
contrib/loaders/flash/stmqspi/stmqspi_erase_check.inc [new file with mode: 0644]
contrib/loaders/flash/stmqspi/stmqspi_read.S [new file with mode: 0644]
contrib/loaders/flash/stmqspi/stmqspi_read.inc [new file with mode: 0644]
contrib/loaders/flash/stmqspi/stmqspi_write.S [new file with mode: 0644]
contrib/loaders/flash/stmqspi/stmqspi_write.inc [new file with mode: 0644]
doc/openocd.texi
src/flash/nor/Makefile.am
src/flash/nor/core.c
src/flash/nor/core.h
src/flash/nor/driver.h
src/flash/nor/drivers.c
src/flash/nor/imp.h
src/flash/nor/sfdp.c [new file with mode: 0644]
src/flash/nor/sfdp.h [new file with mode: 0644]
src/flash/nor/spi.h
src/flash/nor/stmqspi.c [new file with mode: 0644]
src/flash/nor/stmqspi.h [new file with mode: 0644]
src/flash/nor/stmsmi.c
src/flash/nor/tcl.c
src/target/image.c
src/target/image.h
src/target/target.c
src/target/target.h
tcl/board/b-l475e-iot01a.cfg [new file with mode: 0644]
tcl/board/stm32f412g-disco.cfg [new file with mode: 0644]
tcl/board/stm32f413h-disco.cfg [new file with mode: 0644]
tcl/board/stm32f469i-disco.cfg [new file with mode: 0644]
tcl/board/stm32f723e-disco.cfg [new file with mode: 0644]
tcl/board/stm32f746g-disco.cfg [new file with mode: 0644]
tcl/board/stm32f769i-disco.cfg [new file with mode: 0644]
tcl/board/stm32h735g-disco.cfg [new file with mode: 0644]
tcl/board/stm32h745i-disco.cfg [new file with mode: 0644]
tcl/board/stm32h747i-disco.cfg [new file with mode: 0644]
tcl/board/stm32h750b-disco.cfg [new file with mode: 0644]
tcl/board/stm32h7b3i-disco.cfg [new file with mode: 0644]
tcl/board/stm32h7x_dual_qspi.cfg [new file with mode: 0644]
tcl/board/stm32l476g-disco.cfg [new file with mode: 0644]
tcl/board/stm32l496g-disco.cfg [new file with mode: 0644]
tcl/board/stm32l4p5g-disco.cfg [new file with mode: 0644]
tcl/board/stm32l4r9i-disco.cfg [new file with mode: 0644]
tcl/target/stm32f4x.cfg
tcl/target/stm32f7x.cfg
tcl/target/stm32h7x.cfg
tcl/target/stm32l4x.cfg

diff --git a/contrib/loaders/flash/stmqspi/Makefile b/contrib/loaders/flash/stmqspi/Makefile
new file mode 100644 (file)
index 0000000..810c7e8
--- /dev/null
@@ -0,0 +1,34 @@
+BIN2C = ../../../../src/helper/bin2char.sh
+
+SRCS=stmqspi_erase_check.S stmqspi_crc32.S stmqspi_read.S stmqspi_write.S \
+     stmoctospi_erase_check.S stmoctospi_crc32.S stmoctospi_read.S stmoctospi_write.S
+OBJS=$(patsubst %.S,%.inc,$(SRCS))
+
+CROSS_COMPILE ?= arm-none-eabi-
+
+CC=$(CROSS_COMPILE)gcc
+OBJCOPY=$(CROSS_COMPILE)objcopy
+OBJDUMP=$(CROSS_COMPILE)objdump
+LD=$(CROSS_COMPILE)ld
+
+all: $(OBJS)
+
+%.o: %.S Makefile
+       $(CC) -Wall -Werror -Wa,-adhlmn -o $@ -c $< > $(@:.o=.lst)
+       @enscript -Easm -T 4 -G -o - $(@:.o=.lst) | ps2pdf - $(@:.o=.pdf) || true
+
+%.elf: %.o
+       $(LD) -s -defsym=_start=0 -o $@ $<
+
+%.bin: %.elf
+       $(OBJCOPY) -S -O binary $< $@
+
+%.inc: %.bin
+       $(BIN2C) < $< > $@
+
+clean:
+       -rm -f *.o *.elf *.lst *.pdf *.bin *.inc
+
+.PHONY:        all clean
+
+.INTERMEDIATE: $(patsubst %.S,%.o,$(SRCS)) $(patsubst %.S,%.elf,$(SRCS)) $(patsubst %.S,%.bin,$(SRCS))
diff --git a/contrib/loaders/flash/stmqspi/gpio_conf_stm32.pl b/contrib/loaders/flash/stmqspi/gpio_conf_stm32.pl
new file mode 100755 (executable)
index 0000000..b753864
--- /dev/null
@@ -0,0 +1,679 @@
+#!/usr/bin/perl
+#
+# Helper for generating GPIO setup for STM32F0, F4, F7, H7, L0, L1, L4, L4+
+# and F1 (for 'stmqspi' and 'cmspi' drivers).
+#
+# Each pin is configured by "PortAndBit:Conf:Speed"
+#  'PortAndBit' specifies Port and bit number
+#  'Conf' is one of 'AFx' (alternate), 'P' (output), 'IN' (input),
+#    (each optionally by 'P' (push-pull) or 'O' (open-drain)),
+#    (all optionally followed by 'UP' (pull-up), or 'DO' (pull-down))
+#  'Speed' is one of 'L' (low), 'M' (medium), 'H' (high), 'V' (very high)
+#
+# Port configuration can be given on command line as a single string (pins separated by commas)
+# or via CubeMX generated file. The latter must consist of the quadspi.c / octospi.c and the
+# corresponding header. The precise spelling in these files doesn't seem to be consistent, though ...
+#
+# Pins have to be ordered this way:
+#  - I2C: SDA, SCL
+#  - SPI (1 line): NCS, CLK, IO1/MISO, IO0/MOSI
+#  - DPI (2 lines): NCS, CLK, IO1/MISO, IO0/MOSI
+#  - QPI (4 lines): NCS, CLK, IO3/NHOLD, IO2/NWP, IO1/MISO, IO0/MOSI
+# For dual flash: BK_1 first, then BK_2. If single NCS for both, omit NCS in BK_2
+# For octal flash: NCS, CLK, DQS, IO7 down to IO0
+
+use strict;
+use Getopt::Std;
+
+my $GPIO_BASE;
+my $Conf;
+my $STM32F1 = 0;
+
+# "Blue-Pill stm32f103cbt6 board w/ cmspi
+#$STM32F1 = 1;
+#$GPIO_BASE = 0x40010800;
+#$Conf = "PB12:PP:M, PB13:PP:V, PB14:INUP:V, PB15:INUP:V";
+#$Conf = "PB12:PP:M, PB13:PP:V, PB14:INUP:V, PB01:INUP:V";
+
+#$STM32F1 = 1;
+#$GPIO_BASE = 0x40010800;
+#$Conf = "PB07:INUP:V, PB06:INUP:V";
+
+# mini-stm32f030f4p6 board w/ cmspi
+#$GPIO_BASE = 0x48000000;
+#$Conf = "PB01:PP:V, PA05:PP:V, PA06:INUP:V, PA07:INUP:V";
+
+# stm32f407vet6 board w/ cmspi
+#$GPIO_BASE = 0x40020000;
+#$Conf = "PB00:PP:M, PB03:PP:V, PB04:INUP:V, PB05:INUP:V";
+
+# stm32f412g-disco quad
+#$GPIO_BASE = 0x40020000;
+#$Conf = "PB02:AF09:V, PF09:AF10:V, PF08:AF10:V, PF07:AF09:V, PF06:AF09:V, PG06:AF10:V";
+
+# stm32f413h-disco
+#$GPIO_BASE = 0x40020000;
+#$Conf = "PB02:AF09:V, PD13:AF09:V, PE02:AF09:V, PF09:AF10:V, PF08:AF10:V, PG06:AF10:V";
+
+# stm32f469i-disco quad
+#$GPIO_BASE = 0x40020000;
+#$Conf = "PB06:AF10:V, PF10:AF09:V, PF09:AF10:V, PF08:AF10:V, PF07:AF09:V, PF06:AF09:V";
+# w/ cmspi
+#$Conf = "PB06:PP:M, PF10:PP:V, PF06:INUP:V, PF07:INUP:V, PF09:INUP:V, PF08:INUP:V";
+
+# stm32f723e-disco quad
+#$GPIO_BASE = 0x40020000;
+#$Conf = "PB06:AF10:V, PB02:AF09:V, PC10:AF09:V, PC09:AF09:V, PD13:AF09:V, PE02:AF09:V";
+
+# stm32f746g-disco quad
+#$GPIO_BASE = 0x40020000;
+#Conf = "PB06:AF10:V, PB02:AF09:V, PD13:AF09:V, PE02:AF09:V, PD12:AF09:V, PD11:AF09:V";
+# w/ cmspi
+#$Conf = "PB06:PP:M, PB02:PP:V, PD13:INUP:V, PE02:INUP:V, PD12:INUP:V, PD11:INUP:V";
+
+# stm32f769i-disco quad
+#$GPIO_BASE = 0x40020000;
+#$Conf = "PB06:AF10:V, PB02:AF09:V, PC10:AF09:V, PC09:AF09:V, PD13:AF09:V, PE02:AF09:V";
+# w/ cmspi
+#$Conf = "PB06:PP:M, PB02:PP:V, PD13:INUP:V, PE02:INUP:V, PC10:INUP:V, PC09:INUP:V, ";
+
+# b-l475e-iot01a quad
+#$GPIO_BASE = 0x48000000;
+#$Conf = "PE15:AF10:V, PE14:AF10:V, PE13:AF10:V, PE12:AF10:V, PE11:AF10:V, PE10:AF10:V";
+
+# stm32l476g-disco quad
+#$GPIO_BASE = 0x48000000;
+#$Conf = "PE15:AF10:V, PE14:AF10:V, PE13:AF10:V, PE12:AF10:V, PE11:AF10:V, PE10:AF10:V";
+
+# stm32l496g-disco quad
+#$GPIO_BASE = 0x48000000;
+#$Conf = "PA07:AF10:V, PA06:AF10:V, PA03:AF10:V, PB11:AF10:V, PB01:AF10:V, PB00:AF10:V";
+
+# stm32l4r9i-disco octal
+#$GPIO_BASE = 0x48000000;
+#$Conf = "PG15:AF05:V, PG12:AF05:V, PG10:AF05:V, PG09:AF05:V, PH10:AF05:V, PH09:AF05:V, "
+#      . "PH08:AF05:V, PI11:AF05:V, PI10:AF05:V, PI09:AF05:V, PI06:AF05:V";
+
+# stm32l4p5g-disco octal/octal
+#$GPIO_BASE = 0x48000000;
+#$Conf = "PA07:AF10:V, PA06:AF10:V, PC03:AF10:V, PD07:AF10:V, PD05:AF10:V, PD04:AF10:V, "
+#      . "PE13:AF10:V, PE12:AF10:V, PE11:AF10:V, PE10:AF10:V, PG06:AF03:V";
+#$Conf = "PF12:AF05:V, PF04:AF05:V, PF03:AF05:V, PF02:AF05:V, PF01:AF05:V, PF00:AF05:V, "
+#      . "PG12:AF05:V, PG10:AF05:V, PG09:AF05:V, PG01:AF05:V, PG00:AF05:V";
+
+# nucleo-f767zi dual quad
+#$GPIO_BASE = 0x40020000;
+#$Conf = "PB06:AF10:V, PB02:AF09:V, PC11:AF09:V, PD13:AF09:V, PE02:AF09:V, PD12:AF09:V, "
+#      . "PD11:AF09:V, PE10:AF10:V, PE09:AF10:V, PE08:AF10:V, PE07:AF10:V";
+# w/ cmspi
+#$Conf = "PB10:PPUP:M, PB02:PPUP:V, PD13:INPUP:V, PE02:INPUP:V, PD12:INPUP:V, PD11:INPUP:V";
+#$Conf = "PC11:PPUP:M, PB02:PPUP:V, PE10:INPUP:V, PE09:INPUP:V, PE08:INPUP:V, PE07:INPUP:V";
+
+# nucleo-h743zi dual quad
+#$GPIO_BASE = 0x58020000;
+#$Conf = "PB10:AF09:V, PB02:AF09:V, PC11:AF09:V, PD13:AF09:V, PE02:AF09:V, PD12:AF09:V, "
+#      . "PD11:AF09:V, PE10:AF10:V, PE09:AF10:V, PE08:AF10:V, PE07:AF10:V";
+# w/ cmspi
+#$Conf = "PB10:PPUP:M, PB02:PPUP:V, PD13:INPUP:V, PE02:INPUP:V, PD12:INPUP:V, PD11:INPUP:V";
+#$Conf = "PC11:PPUP:M, PB02:PPUP:V, PE10:INPUP:V, PE09:INPUP:V, PE08:INPUP:V, PE07:INPUP:V";
+
+# nucleo-h7a3zi dual quad
+#$GPIO_BASE = 0x58020000;
+#$Conf = "PB10:AF09:V, PB02:AF09:V, PD13:AF09:V, PE02:AF09:V, PD12:AF09:V, PD11:AF09:V, "
+#      . "PC11:AF09:V, PE10:AF10:V, PD06:AF10:V, PE08:AF10:V, PE07:AF10:V";
+# w/ cmspi
+#$Conf = "PB10:PPUP:M, PB02:PPUP:V, PD13:INPUP:V, PE02:INPUP:V, PD12:INPUP:V, PD11:INPUP:V";
+#$Conf = "PC11:PPUP:M, PB02:PPUP:V, PE10:INPUP:V, PD06:INPUP:V, PE08:INPUP:V, PE07:INPUP:V";
+
+# nucleo-l4r5zi one dual quad single NCS
+#$GPIO_BASE = 0x48000000;
+#$Conf = "PA02:AF10:V, PE10:AF10:V, PD07:AF10:V, PD06:AF10:V, PD05:AF10:V, PD04:AF10:V, "
+#      . "PE15:AF10:V, PE14:AF10:V, PE13:AF10:V, PE12:AF10:V";
+# w/ cmspi
+#$Conf = "PA02:PPUP:M,  PE10:PPUP:V, PD07:INPDO:V, PD06:INPDO:V, PD05:INPDO:V, PD04:INPDO:V";
+#$Conf = "PA02:PPUP:M,  PE10:PPUP:V, PE15:INPDO:V, PE14:INPDO:V, PE13:INPDO:V, PE12:INPDO:V";
+
+# nucleo-l552ze-q dual quad with single NCS
+#$GPIO_BASE = 0x42020000;
+#$Conf = "PA02:AF10:V, PE10:AF10:V, PD07:AF10:V, PD06:AF10:V, PD05:AF10:V, PD04:AF10:V, "
+#      . "PE15:AF10:V, PE14:AF10:V, PE13:AF10:V, PE12:AF10:V";
+# w/ cmspi
+#$Conf = "PA02:PPUP:M,  PE10:PPUP:V, PD07:INPDO:V, PD06:INPDO:V, PD05:INPDO:V, PD04:INPDO:V";
+#$Conf = "PA02:PPUP:M,  PE10:PPUP:V, PE15:INPDO:V, PE14:INPDO:V, PE13:INPDO:V, PE12:INPDO:V";
+
+# nucleo-g071rb dual quad
+#$GPIO_BASE = 0x50000000;
+#$Conf = "PA00:PPUP:H, PA04:PPUP:V, PB03:INPUP:V, PA10:INPUP:V, PB11:INPUP:H, PB01:INPUP:H";
+#$Conf = "PA01:PPUP:H, PA04:PPUP:V, PA08:INPUP:V, PB14:INPUP:V, PB04:INPUP:V, PB05:INPUP:V";
+
+# nucleo-g474re dual quad with single NCS
+#$GPIO_BASE = 0x48000000;
+#$Conf = "PB11:AF10:H, PB10:AF10:V, PA06:AF10:V, PA07:AF10:V, PB00:AF10:V, PB01:AF10:V, "
+#      . "PC04:AF10:V, PC03:AF10:V, PC02:AF10:V, PC01:AF10:V";
+# w/ cmspi
+#$Conf = "PB11:PPUP:H, PB10:PPUP:V, PA06:INPUP:V, PA07:INPUP:V, PB00:INPUP:V, PB01:INPUP:V";
+#$Conf = "PB11:PPUP:H, PB10:PPUP:V, PC04:INPUP:V, PC03:INPUP:V, PC02:INPUP:V, PC01:INPUP:V";
+
+# stm32h745i-disco dual quad with single NCS
+#$GPIO_BASE = 0x58020000;
+#$Conf = "PG06:AF10:H, PF10:AF09:V, PF06:AF09:V, PF07:AF09:V, PF09:AF10:V, PD11:AF09:V, "
+#      . "PG14:AF09:H, PG09:AF09:V, PH03:AF09:V, PH02:AF09:V";
+
+# stm32h747i-disco dual quad with single NCS
+#GPIO_BASE = 0x58020000;
+#$Conf = "PG06:AF10:H, PB02:AF09:V, PF06:AF09:V, PF07:AF09:V, PF09:AF10:V, PD11:AF09:V, "
+#      . "PG14:AF09:H, PG09:AF09:V, PH03:AF09:V, PH02:AF09:V";
+
+# stm32h7b3i-disco octal
+#$GPIO_BASE = 0x58020000;
+#$Conf = "PG06:AF10:V, PB02:AF09:V, PC05:AF10:V, PD07:AF10:V, PG09:AF09:V, PH03:AF09:V, PC01:AF10:V, "
+#      . "PF06:AF10:V, PF07:AF10:V, PF09:AF10:V, PD11:AF09:V";
+
+# stm32h735g-disco octal
+#$GPIO_BASE = 0x58020000;
+#$Conf = "PG06:AF10:V, PF10:AF09:V, PB02:AF10:V, PD07:AF10:V, PG09:AF09:V, PD05:AF10:V, PD04:AF10:V, "
+#      . "PD13:AF09:V, PE02:AF09:V, PD12:AF09:V, PD11:AF09:V";
+
+# stm32l562e-disco octal
+#$GPIO_BASE = 0x42020000;
+#$Conf = "PA02:AF10:V, PA03:AF10:V, PB02:AF10:V, PC00:AF03:V, PC03:AF10:V, PC02:AF10:V, PC01:AF10:V, "
+#      . "PA06:AF10:V, PA07:AF10:V, PB00:AF10:V, PB01:AF10:V";
+
+&getopts('b:c:f:t');
+if ($Getopt::Std::opt_b eq '')
+{
+       if ($GPIO_BASE eq '')
+       {
+               die("usage: $0 [ -1 ] -b io_base [ -c port_configuration ] [ -f conf_file ]");
+       }
+}
+else
+{
+       $GPIO_BASE = eval $Getopt::Std::opt_b;
+}
+
+if ($Getopt::Std::opt_c eq '')
+{
+       if (($Conf eq '') && ($Getopt::Std::opt_f eq ''))
+       {
+               die("usage: $0 [ -b io_base ] ( -c port_configuration | -f conf_file )");
+       }
+}#
+else
+{
+       $Conf = $Getopt::Std::opt_c . ',';
+}
+
+$STM32F1 = $Getopt::Std::opt_t;
+
+my $Sep = "\t";
+my $Form = "${Sep}mmw 0x%08X 0x%08X 0x%08X\t;# ";
+
+my $GPIO_OFFS;
+my $GPIO_CRL;
+my $GPIO_CRH;
+my $GPIO_MODER;
+my $GPIO_OTYPER;
+my $GPIO_OSPEEDR;
+my $GPIO_PUPDR;
+my $GPIO_IDR;
+my $GPIO_ODR;
+my $GPIO_AFRL;
+my $GPIO_AFRH;
+
+if ($STM32F1)
+{
+       # offsets for F1 devices
+       $GPIO_OFFS = 0x400;
+       $GPIO_CRL = 0x00;
+       $GPIO_CRH = 0x04;
+       $GPIO_IDR = 0x08;
+       $GPIO_ODR = 0x0C;
+}
+else
+{
+       # these offsets are identical on all F0, F4, F7, H7, L4, L4+ devices up to now
+       $GPIO_OFFS = 0x400;
+       $GPIO_MODER = 0x00;
+       $GPIO_OTYPER = 0x04;
+       $GPIO_OSPEEDR = 0x08;
+       $GPIO_PUPDR = 0x0C;
+       $GPIO_IDR = 0x10;
+       $GPIO_ODR = 0x14;
+       $GPIO_AFRL = 0x20;
+       $GPIO_AFRH = 0x24;
+}
+
+my @Out = ( { }, { }, { }, { }, { }, { }, { }, { }, { }, { }, { } );
+my @Port = ( );
+my $Exor;
+my %Conf;
+my $Pins = "${Sep}#";
+
+my $pins;
+my $altn;
+my %defs;
+
+if ($Getopt::Std::opt_f ne '')
+{
+       open(CONF_FILE, '<', $Getopt::Std::opt_f) || die("can't open $Getopt::Std::opt_f");
+       while (my $line = <CONF_FILE>)
+       {
+               if ($line =~ /^\s*#define\s+.?(QSPI|QUAD_?SPI|OCTOSPI[^_]*)\w+_(Port|Pin)\s/)
+               {
+                       if ($line =~ /#define\s+(\w+)\s+(\w+)/)
+                       {
+                               $defs{$1} = $2;
+                       }
+                       else
+                       {
+                               die($line);
+                       }
+               }
+               elsif ($line =~ /^\s*(P[A-Z])([0-9]+)\s*-+>\s+.?(QSPI|QUAD_?SPI|OCTO_?SPI[^_]*)_(\w+)/)
+               {
+                       $Conf{$4} = sprintf("%s%02d", $1, $2);
+               }
+               elsif ($line =~ /^\s*GPIO_InitStruct.Pin\s*=\s*([^;]+\w)/)
+               {
+                       $pins = $1;
+                       while ($line !~ /;/)
+                       {
+                               $line = <CONF_FILE>;
+                               $line =~ /^\s*([^;]+\w)/;
+                               $pins .= $1;
+                       }
+               }
+               elsif ($line =~ /^\s*GPIO_InitStruct.Alternate\s*=\s*GPIO_AF([0-9]+)/)
+               {
+                       $altn = $1;
+               }
+               elsif ($line =~ /^\s*HAL_GPIO_Init\s*\(\s*(\w+)\s*,/)
+               {
+                       my $port = $1;
+                       if ($port =~ /GPIO([A-Z])/)
+                       {
+                               $port = $1;
+                       }
+                       elsif (exists($defs{$port}))
+                       {
+                               $defs{$port} =~ /GPIO([A-Z])/;
+                               $port = $1;
+                       }
+                       else
+                       {
+                               printf("\n");
+                               next;
+                       }
+                       my @pin = split(/\s*\|\s*/, $pins);
+                       foreach my $pin (@pin)
+                       {
+                               my $bit;
+                               if (exists($defs{$pin}))
+                               {
+                                       $defs{$pin} =~ /GPIO_PIN_([0-9]+)/;
+                                       $bit = $1;
+                               }
+                               else
+                               {
+                                       $pin =~ /GPIO_PIN_([0-9]+)/;
+                                       $bit = $1;
+                               }
+                               $Conf .= sprintf("P%s%02d:AF%02d:V, ", $port, $bit, $altn);
+                       }
+                       $pins = '';
+                       $altn = 0;
+               }
+       }
+       close(CONF_FILE);
+}
+else
+{
+       my @names = ( );
+       my @conf = split(/\s*,\s*/, $Conf);
+
+       if (@conf == 2)
+       {
+               push(@names, 'SDA', 'SCL');
+       } else {
+               if (@conf == 3)
+               {
+                       push(@names, 'NCS', 'CLK', 'IO0/DIO');
+               }
+               elsif (@conf == 4)
+               {
+                       push(@names, 'NCS', 'CLK','IO1/MISO', 'IO0/MOSI');
+               }
+               elsif (@conf == 6)
+               {
+                       push(@names, 'NCS', 'CLK', 'IO3/NHOLD', 'IO2/NWP', 'IO1/MISO', 'IO0/MOSI');
+               }
+               elsif (@conf == 10)
+               {
+                       push(@names, 'NCS', 'CLK', 'BK_1_IO3/NHOLD', 'BK1_IO2/NWP', 'BK1_IO1/MISO', 'BK1_IO0/MOSI');
+                       push(@names, 'BK_2_IO3/NHOLD', 'BK2_IO2/NWP', 'BK2_IO1/MISO', 'BK2_IO0/MOSI');
+               }
+               elsif (@conf == 11)
+               {
+                       push(@names, 'BK_1_NCS', 'CLK', 'BK_1_IO3/NHOLD', 'BK1_IO2/NWP', 'BK1_IO1/MISO', 'BK1_IO0/MOSI');
+                       push(@names, 'BK_2_NCS', 'BK_2_IO3/NHOLD', 'BK2_IO2/NWP', 'BK2_IO1/MISO', 'BK2_IO0/MOSI');
+               }
+               else
+               {
+                       die("invalid config");
+               }
+       }
+
+       for (my $index = 0; $index < @conf; $index++)
+       {
+               uc($conf[$index]) =~ /^P([A-K])([0-9]+):\s*([A-Z0-9]+):(L|M|H|V)$/;
+               $Pins .= sprintf(" %s: P%s%02d,", $names[$index], $1, $2);
+       }
+       chop($Pins);
+}
+
+if (exists $Conf{'BK1_IO0'})
+{
+       # QuadSPI on F4, F7, H7
+       my $line;
+       for my $i ('NCS', 'BK1_NCS', 'CLK', 'BK1_IO3', 'BK1_IO2', 'BK1_IO1', 'BK1_IO0')
+       {
+               (exists $Conf{$i}) && ($Pins .= sprintf(" %s: %s,", $Conf{$i}, $i));
+       }
+}
+
+if (exists $Conf{'BK2_IO0'})
+{
+       # QuadSPI on F4, F7, H7
+       my $line;
+       for my $i ('NCS', 'BK2_NCS', 'CLK', 'BK2_IO3', 'BK2_IO2', 'BK2_IO1', 'BK2_IO0')
+       {
+               (exists $Conf{$i}) && ($Pins .= sprintf(" %s: %s,", $Conf{$i}, $i));
+       }
+}
+
+if (exists $Conf{'P1_IO0'})
+{
+       # OctoSPI on L4+, L5, H7
+       my $line;
+       for my $i ('P1_NCS', 'P1_CLK', 'P1_DQS', 'P1_IO7', 'P1_IO6', 'P1_IO5', 'P1_IO4',
+                          'P1_IO3', 'P1_IO2', 'P1_IO1', 'P1_IO0')
+       {
+               (exists $Conf{$i}) && ($Pins .= sprintf(" %s: %s,", $Conf{$i}, $i));
+       }
+}
+
+if (exists $Conf{'P2_IO0'})
+{
+       # OctoSPI on L4+, H7
+       my $line;
+       for my $i ('P2_NCS', 'P2_CLK', 'P2_DQS', 'P2_IO7', 'P2_IO6', 'P2_IO5', 'P2_IO4',
+                          'P2_IO3', 'P2_IO2', 'P2_IO1', 'P2_IO0')
+       {
+               (exists $Conf{$i}) && ($Pins .= sprintf(" %s: %s,", $Conf{$i}, $i));
+       }
+}
+
+my @Col = ( );
+my @conf = split(/\s*,\s*/, $Conf);
+
+if (@conf == 3)
+{
+       splice(@conf, 2, 0, 'NONE', 'NONE', 'NONE');
+}
+elsif (@conf == 4)
+{
+       splice(@conf, 2, 0, 'NONE', 'NONE');
+}
+
+foreach my $line (@conf)
+{
+       $line = uc($line);
+       $line =~ /^P([A-K])([0-9]+):\s*([A-Z0-9]+):(L|M|H|V)$/;
+       my $port = $1;
+       my $pin = $2;
+       my $conf = $3;
+       my $speed = $4;
+
+       my $MODER = 0x0;
+       my $OTYPER = 0x0;
+       my $OSPEEDR = 0x0;
+       my $PUPDR = 0x0;
+       my $AFR = 0x0;
+       my $num = ord(${port}) - ord('A');
+       my $out = $Out[$num];
+
+       (exists $$out{'DEF'}) || ($$out{'DEF'} = 0);
+
+       if ($conf eq '')
+       {
+               if ($line ne 'NONE')
+               {
+                       printf(STDERR "invalid conf %s\n", $line);
+               }
+               next;
+       }
+       elsif ($conf =~ /^AF([0-9]+)(|P|O)(|UP|DO)$/)
+       {
+               if ($STM32F1)
+               {
+                       printf(STDERR "no alternate %s for F1 family\n", $line);
+                       next;
+               }
+               if (($1 < 0) || ($1 > 15))
+               {
+                       printf(STDERR "invalid alternate %s\n", $line);
+                       next;
+               }
+               $MODER = 0x2;
+               $AFR = $1;
+               if ($pin <= 7)
+               {
+                       $$out{'AFRL_H'} |= ($AFR << (${pin} << 2));
+                       $$out{'AFRL_L'} |= (($AFR ^ 0xF) << (${pin} << 2));
+               }
+               else
+               {
+                       $$out{'AFRH_H'} |= ($AFR << ((${pin} - 8) << 2));
+                       $$out{'AFRH_L'} |= (($AFR ^ 0xF) << ((${pin} - 8) << 2));
+               }
+               if ($2 ne '') {
+                       $OTYPER = ($1 eq 'O') ? 0x1 : 0x0;
+                       $$out{'OTYPER_H'} |= ($OTYPER << $pin);
+                       $$out{'OTYPER_L'} |= (($OTYPER ^ 0x1) << $pin);
+               }
+               $PUPDR = ($3 eq 'UP') ? 0x1 : (($3 eq 'DO') ? 0x2 : 0x0);
+               $$out{'PUPDR_H'} |= ($PUPDR << (${pin} << 1));
+               $$out{'PUPDR_L'} |= (($PUPDR ^0x3) << (${pin} << 1));
+               $conf = sprintf("AF%02d%s%s", $AFR, $2, $3);
+       }
+       elsif ($conf =~ /^IN(|P|O)(|UP|DO)$/)
+       {
+               if ($STM32F1)
+               {
+                       $MODER = ($1 eq '') ? 0x4 : 0x8;
+                       ($2 eq 'UP') && ($$out{'PUPDR_H'} |= (1 << ${pin}));
+                       ($2 eq 'DO') && ($$out{'PUPDR_L'} |= (1 << ${pin}));
+               }
+               else
+               {
+                       $MODER = 0x0;
+                       if ($1 ne '')
+                       {
+                               $OTYPER = ($1 eq 'O') ? 0x1 : 0x0;
+                               $$out{'OTYPER_H'} |= ($OTYPER << $pin);
+                               $$out{'OTYPER_L'} |= (($OTYPER ^ 0x1) << $pin);
+                       }
+                       $PUPDR = ($2 eq 'UP') ? 0x1 : (($2 eq 'DO') ? 0x2 : 0x0);
+                       $$out{'PUPDR_H'} |= ($PUPDR << (${pin} << 1));
+                       $$out{'PUPDR_L'} |= (($PUPDR ^0x3) << (${pin} << 1));
+               }
+               ($2 eq 'UP') && ($$out{'ODR_H'} |= (1 << ${pin}));
+               ($2 eq 'DO') && ($$out{'ODR_L'} |= (1 << ${pin}));
+       }
+       elsif ($conf =~ /^P(P|O)(|UP|DO)$/)
+       {
+               if ($STM32F1)
+               {
+                       $MODER = ($1 eq 'O') ? 0x4 : 0x0;
+                       $MODER |= (($speed eq 'V') ? 0x03 : (($speed eq 'L') ? 0x2 : 0x1));
+                       if ($2 ne '')
+                       {
+                               printf(STDERR "WARNING: no output w/ pull-up/pull-down for F1 family %s\n", $line);
+                       }
+               }
+               else
+               {
+                       $MODER = 0x1;
+                       $OTYPER = ($1 eq 'O') ? 0x1 : 0x0;
+                       $$out{'OTYPER_H'} |= ($OTYPER << $pin);
+                       $$out{'OTYPER_L'} |= (($OTYPER ^ 0x1) << $pin);
+                       $PUPDR = ($2 eq 'UP') ? 0x1 : (($2 eq 'DO') ? 0x2 : 0x0);
+                       $$out{'PUPDR_H'} |= ($PUPDR << ($pin << 1));
+                       $$out{'PUPDR_L'} |= (($PUPDR ^ 0x3) << ($pin << 1));
+               }
+               ($2 eq 'UP') && ($$out{'ODR_H'} |= (1 << ${pin}));
+               ($2 eq 'DO') && ($$out{'ODR_L'} |= (1 << ${pin}));
+       }
+       else
+       {
+               printf(STDERR "invalid conf %s\n", $line);
+               next;
+       }
+
+       if ($$out{'DEF'} & (1<< ${pin}))
+       {
+               printf(STDERR "redefinition: %s\n", $line);
+       }
+
+       if ($STM32F1)
+       {
+               if ($pin >= 8)
+               {
+                       $$out{'CRH_H'} |= ($MODER << (($pin & 0x7) << 2));
+                       $$out{'CRH_L'} |= (($MODER ^ 0xF) << (($pin & 0x7) << 2));
+               }
+               else
+               {
+                       $$out{'CRL_H'} |= ($MODER << (($pin & 0x7) << 2));
+                       $$out{'CRL_L'} |= (($MODER ^ 0xF) << (($pin & 0x7) << 2));
+               }
+
+               $Exor = sprintf("0x%08X %2d", ${GPIO_BASE} + (ord($port)  - ord('A')) * ${GPIO_OFFS} + ${GPIO_ODR}, $pin);
+               my $exor = 0xB << (($pin & 0x7) << 2);
+               (($MODER & 0x3) == 0x0) && ($Exor .= sprintf(" 0x%03X 0x%03X 0x%08X",
+                       ((($pin >= 8) ? ${GPIO_CRH} : ${GPIO_CRL})-${GPIO_ODR}) & 0x3FF,
+                       ((($pin >= 8) ? ${GPIO_CRH} : ${GPIO_CRL})-${GPIO_ODR}) & 0x3FF, $exor));
+       }
+       else
+       {
+               $$out{'DEF'} |= (1 << ${pin});
+               $$out{'MODER_H'} |= ($MODER << (${pin} << 1));
+               $$out{'MODER_L'} |= (($MODER ^ 0x3) << (${pin} << 1));
+
+               $OSPEEDR = (($speed eq 'V') ? 0x3 : (($speed eq 'H') ? 0x2 : (($speed eq 'M') ? 0x1 : 0x0)));
+               $$out{'OSPEEDR_H'} |= ($OSPEEDR << (${pin} << 1));
+               $$out{'OSPEEDR_L'} |= (($OSPEEDR ^ 0x3) << (${pin} << 1));
+
+               $Exor = sprintf("0x%08X %2d", ${GPIO_BASE} + (ord($port)  - ord('A')) * ${GPIO_OFFS} + ${GPIO_ODR}, $pin);
+               my $exor = (0x1 << ($pin << 1));
+               ($MODER == 0x0) && ($Exor .= sprintf(" 0x%03X 0x%03X 0x%08X", (${GPIO_MODER}-${GPIO_ODR}) & 0x3FF,
+                       (${GPIO_MODER}-${GPIO_ODR}) & 0x3FF, $exor));
+       }
+
+       push(@{$Port[$num]}, sprintf("P%s%02d:%s:%s", $port, $pin, $conf, $speed));
+       push(@Col, $Exor);
+}
+
+my $Col = sprintf("${Sep}0x%03X ", (${GPIO_IDR}-${GPIO_ODR}) & 0x3FF);
+for (my $i = 0; $i < @Col; $i++)
+{
+       if (($i != 0) && (($i % 2) == 0))
+       {
+               (($i + 1) < @Col) && ($Col .= "\\\n${Sep}");
+       }
+       $Col .= sprintf("%s ", $Col[$i]);
+}
+printf("%s\n", $Col);
+
+my @Col = ( );
+my $Set;
+for (my $i = 0; $i < @Out; $i++)
+{
+       my $out = $Out[$i];
+       my $addr = ${GPIO_BASE} + $i * ${GPIO_OFFS};
+       my $count = 0;
+
+       if ($STM32F1)
+       {
+               if (($$out{'CRH_H'} | $$out{'CRH_L'} | $$out{'CRL_H'} | $$out{'CRL_L'} |
+                               $$out{'PUPDR_H'} | $$out{'PUPDR_L'}) != 0)
+               {
+                       push(@Col, sort({ $b cmp $a } @{$Port[$i]}));
+
+                       $Set .= sprintf("\n%s# Port %s: %s\n", ${Sep}, chr($i + ord('A')),
+                               join(", ", sort({ $b cmp $a } @{$Port[$i]})));
+
+                       (($$out{'CRL_H'} | $$out{'CRL_L'}) != 0) &&
+                               ($Set .= sprintf("${Form}CRL\n", $addr + ${GPIO_CRL}, $$out{'CRL_H'}, $$out{'CRL_L'}));
+
+                       (($$out{'CRH_H'} | $$out{'CRH_L'}) != 0) &&
+                               ($Set .= sprintf("${Form}CRH\n", $addr + ${GPIO_CRH}, $$out{'CRH_H'}, $$out{'CRH_L'}));
+
+                       (($$out{'ODR_H'} | $$out{'ODR_L'}) != 0) &&
+                               ($Set .= sprintf("${Form}ODR/PUPDR\n", $addr + ${GPIO_ODR}, $$out{'ODR_H'}, $$out{'ODR_L'}));
+               }
+       }
+       else
+       {
+               if (($$out{'MODER_H'} | $$out{'MODER_L'} |
+                       $$out{'OTYPER_H'} | $$out{'OTYPER_L'} |
+                       $$out{'OSPEEDR_H'} | $$out{'OSPEEDR_L'} |
+                       $$out{'PUPDR_H'} | $$out{'PUPDR_L'} |
+                       $$out{'ODR_H'} | $$out{'ODR_L'} |
+                       $$out{'AFRL_H'} | $$out{'AFRL_L'} |
+                       $$out{'AFRH_H'} | $$out{'AFRH_L'}) != 0)
+               {
+                       push(@Col, sort({ $b cmp $a } @{$Port[$i]}));
+
+                       $Set .= sprintf("%s# Port %s: %s\n", ${Sep}, chr($i + ord('A')),
+                               join(", ", sort({ $b cmp $a } @{$Port[$i]})));
+
+                       (($$out{'MODER_H'} | $$out{'MODER_L'}) != 0) &&
+                               ($Set .= sprintf("${Form}MODER\n", $addr + ${GPIO_MODER}, $$out{'MODER_H'}, $$out{'MODER_L'}));
+
+                       (($$out{'OTYPER_H'} | $$out{'OTYPER_L'}) != 0) &&
+                               ($Set .= sprintf("${Form}OTYPER\n", $addr + ${GPIO_OTYPER}, $$out{'OTYPER_H'}, $$out{'OTYPER_L'}));
+
+                       (($$out{'OSPEEDR_H'} | $$out{'OSPEEDR_L'}) != 0) &&
+                               ($Set .= sprintf("${Form}OSPEEDR\n", $addr + ${GPIO_OSPEEDR}, $$out{'OSPEEDR_H'}, $$out{'OSPEEDR_L'}));
+
+                       (($$out{'PUPDR_H'} | $$out{'PUPDR_L'}) != 0) &&
+                               ($Set .= sprintf("${Form}PUPDR\n", $addr + ${GPIO_PUPDR}, $$out{'PUPDR_H'}, $$out{'PUPDR_L'}));
+
+                       (($$out{'ODR_H'} | $$out{'ODR_L'}) != 0) &&
+                               ($Set .= sprintf("${Form}ODR\n", $addr + ${GPIO_ODR}, $$out{'ODR_H'}, $$out{'ODR_L'}));
+
+                       (($$out{'AFRL_H'} | $$out{'AFRL_L'}) != 0) &&
+                               ($Set .= sprintf("${Form}AFRL\n", $addr + ${GPIO_AFRL}, $$out{'AFRL_H'}, $$out{'AFRL_L'}));
+
+                       (($$out{'AFRH_H'} | $$out{'AFRH_L'}) != 0) &&
+                               ($Set .= sprintf("${Form}AFRH\n", $addr + ${GPIO_AFRH}, $$out{'AFRH_H'}, $$out{'AFRH_L'}));
+               }
+       }
+}
+
+my $Col = '';
+for (my $i = 0; $i < @Col; $i++)
+{
+       if (($i % 6) == 0)
+       {
+               chop($Col);
+               (($i + 1) < @Col) && ($Col .= "\n${Sep}#");
+       }
+       $Col .= sprintf(" %s,", $Col[$i]);
+}
+chop($Col);
+#printf("\n%s\n", $Pins);
+printf("%s\n", $Col);
+printf("%s\n", $Set);
diff --git a/contrib/loaders/flash/stmqspi/stmoctospi_crc32.S b/contrib/loaders/flash/stmqspi/stmoctospi_crc32.S
new file mode 100644 (file)
index 0000000..941ea42
--- /dev/null
@@ -0,0 +1,123 @@
+/***************************************************************************
+ *   Copyright (C) 2019 by Andreas Bolsch                                  *
+ *   andreas.bolsch@mni.thm.de                                             *
+ *                                                                         *
+ *   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 <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+       .text
+       .syntax unified
+       .cpu cortex-m0
+       .thumb
+       .thumb_func
+
+/* Params:
+ * r0 - total count (bytes), crc32 (out)
+ * r1 - flash page size
+ * r2 - address offset into flash
+ * r3 - OCTOSPI io_base
+
+ * Clobbered:
+ * r4 - tmp
+ * r5 - address of OCTOSPI_DR
+ * r6 - address of OCTOSPI_CCR
+ * r7 - tmp
+ */
+
+#include "../../../../src/flash/nor/stmqspi.h"
+
+#define OCTOSPI_CCR_CCR                                        (OCTOSPI_CCR - OCTOSPI_CCR)
+#define OCTOSPI_TCR_CCR                                        (OCTOSPI_TCR - OCTOSPI_CCR)
+#define OCTOSPI_IR_CCR                                 (OCTOSPI_IR - OCTOSPI_CCR)
+
+       .macro  octospi_abort
+       movs    r5, #(1<<SPI_ABORT)                     /* abort bit mask */
+       ldr             r7, [r3, #OCTOSPI_CR]           /* get OCTOSPI CR register */
+       orrs    r7, r7, r5                                      /* set abort bit */
+       str             r7, [r3, #OCTOSPI_CR]           /* store new CR register */
+       .endm
+
+       .macro  wait_busy
+0:
+       ldr             r7, [r3, #OCTOSPI_SR]           /* load status */
+       lsrs    r7, r7, #(SPI_BUSY+1)           /* shift BUSY into C */
+       bcs             0b                                                      /* loop until BUSY cleared */
+       movs    r7, #(1<<SPI_TCF)                       /* TCF bitmask */
+       str             r7, [r3, #OCTOSPI_FCR]          /* clear TCF flag */
+       .endm
+
+start:
+       subs    r0, r0, #1                                      /* decrement count for DLR */
+       subs    r1, r1, #1                                      /* page size mask and for DLR */
+       movs    r4, #0x00                                       /* initialize crc */
+       mvns    r4, r4                                          /* to 0xFFFFFFFF */
+start_read:
+       octospi_abort                                           /* start in clean state */
+       movs    r5, #OCTOSPI_DR                         /* load OCTOSPI_DR address offset */
+       adds    r5, r5, r3                                      /* address of OCTOSPI_DR */
+       movs    r6, #OCTOSPI_CCR-OCTOSPI_DR     /* load OCTOSPI_CCR address offset */
+       adds    r6, r6, r5                                      /* address of OCTOSPI_CCR */
+       wait_busy
+       ldr             r7, cr_page_read                        /* indirect read mode */
+       str             r7, [r3, #OCTOSPI_CR]           /* set mode */
+       mov             r7, r2                                          /* get current start address */
+       orrs    r7, r7, r1                                      /* end of current page */
+       subs    r7, r7, r2                                      /* count-1 to end of page */
+       cmp             r7, r0                                          /* if this count <= remaining */
+       bls             write_dlr                                       /* then read to end of page */
+       mov             r7, r0                                          /* else read all remaining */
+write_dlr:
+       str             r7, [r3, #OCTOSPI_DLR]          /* size-1 in DLR register */
+       ldr             r7, ccr_page_read                       /* CCR for read */
+       str             r7, [r6, #OCTOSPI_CCR_CCR]      /* initiate transfer */
+       ldr             r7, tcr_page_read                       /* TCR for read */
+       str             r7, [r6, #OCTOSPI_TCR_CCR]      /* instruction */
+       ldr             r7, ir_page_read                        /* IR for read */
+       str             r7, [r6, #OCTOSPI_IR_CCR]       /* instruction */
+       str             r2, [r3, #OCTOSPI_AR]           /* store SPI start address */
+       ldr             r6, =0x04C11DB7                         /* CRC32 polynomial */
+read_loop:
+       ldrb    r7, [r5]                                        /* read next byte from DR */
+       lsls    r7, r7, #24                                     /* shift into msb */
+       eors    r4, r4, r7
+       .rept   8                                                       /* unrolled bit loop */
+       asrs    r7, r4, #31                                     /* copy bit 31 into bits 0 to 31 */
+       ands    r7, r7, r6                                      /* r7 neg. -> CRC32XOR, pos. -> 0x0 */
+       lsls    r4, r4, #1                                      /* shift result */
+       eors    r4, r4, r7                                      /* eor by CRC32XOR or 0x0 */
+       .endr
+       adds    r2, r2, #1                                      /* increment address */
+       subs    r0, r0, #1                                      /* decrement (count-1) */
+       bmi             exit                                            /* stop if no data left */
+       tst             r2, r1                                          /* page end ? */
+       bne             read_loop                                       /* if not, then next byte */
+page_end:
+       bal             start_read                                      /* then next page */
+       .pool
+
+exit:
+       mvns    r0, r4                                          /* invert to get final result */
+       octospi_abort                                           /* to idle state */
+       .align  2                                                       /* align to word, bkpt is 4 words */
+       bkpt    #0                                                      /* before code end for exit_point */
+       .align  2                                                       /* align to word */
+
+cr_page_read:
+       .space  4                                                       /* OCTOSPI_CR value for read command */
+ccr_page_read:
+       .space  4                                                       /* OCTOSPI_CCR value for read command */
+tcr_page_read:
+       .space  4                                                       /* OCTOSPI_TCR value for read command */
+ir_page_read:
+       .space  4                                                       /* OCTOSPI_IR value for read command */
diff --git a/contrib/loaders/flash/stmqspi/stmoctospi_crc32.inc b/contrib/loaders/flash/stmqspi/stmoctospi_crc32.inc
new file mode 100644 (file)
index 0000000..afc6168
--- /dev/null
@@ -0,0 +1,13 @@
+/* Autogenerated with ../../../../src/helper/bin2char.sh */
+0x01,0x38,0x01,0x39,0x00,0x24,0xe4,0x43,0x02,0x25,0x1f,0x68,0x2f,0x43,0x1f,0x60,
+0x50,0x25,0xed,0x18,0xb0,0x26,0x76,0x19,0x1f,0x6a,0xbf,0x09,0xfc,0xd2,0x02,0x27,
+0x5f,0x62,0x22,0x4f,0x1f,0x60,0x17,0x46,0x0f,0x43,0xbf,0x1a,0x87,0x42,0x00,0xd9,
+0x07,0x46,0x1f,0x64,0x1e,0x4f,0x37,0x60,0x1e,0x4f,0xb7,0x60,0x1e,0x4f,0x37,0x61,
+0x9a,0x64,0x15,0x4e,0x2f,0x78,0x3f,0x06,0x7c,0x40,0xe7,0x17,0x37,0x40,0x64,0x00,
+0x7c,0x40,0xe7,0x17,0x37,0x40,0x64,0x00,0x7c,0x40,0xe7,0x17,0x37,0x40,0x64,0x00,
+0x7c,0x40,0xe7,0x17,0x37,0x40,0x64,0x00,0x7c,0x40,0xe7,0x17,0x37,0x40,0x64,0x00,
+0x7c,0x40,0xe7,0x17,0x37,0x40,0x64,0x00,0x7c,0x40,0xe7,0x17,0x37,0x40,0x64,0x00,
+0x7c,0x40,0xe7,0x17,0x37,0x40,0x64,0x00,0x7c,0x40,0x01,0x32,0x01,0x38,0x05,0xd4,
+0x0a,0x42,0xd7,0xd1,0xb8,0xe7,0x00,0x00,0xb7,0x1d,0xc1,0x04,0xe0,0x43,0x02,0x25,
+0x1f,0x68,0x2f,0x43,0x1f,0x60,0xc0,0x46,0x00,0xbe,0xc0,0x46,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
diff --git a/contrib/loaders/flash/stmqspi/stmoctospi_erase_check.S b/contrib/loaders/flash/stmqspi/stmoctospi_erase_check.S
new file mode 100644 (file)
index 0000000..3af82d4
--- /dev/null
@@ -0,0 +1,108 @@
+/***************************************************************************
+ *   Copyright (C) 2019 by Andreas Bolsch                                  *
+ *   andreas.bolsch@mni.thm.de                                             *
+ *                                                                         *
+ *   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 <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+       .text
+       .syntax unified
+       .cpu cortex-m0
+       .thumb
+       .thumb_func
+
+/* Params:
+ * r0 - sector count
+ * r1 - QSPI io_base
+
+ * Clobbered:
+ * r2 - r7 tmp */
+
+#include "../../../../src/flash/nor/stmqspi.h"
+
+#define OCTOSPI_CCR_CCR                                        (OCTOSPI_CCR - OCTOSPI_CCR)
+#define OCTOSPI_TCR_CCR                                        (OCTOSPI_TCR - OCTOSPI_CCR)
+#define OCTOSPI_IR_CCR                                 (OCTOSPI_IR - OCTOSPI_CCR)
+
+       .macro  octospi_abort
+       movs    r5, #(1<<SPI_ABORT)                     /* abort bit mask */
+       ldr             r7, [r1, #OCTOSPI_CR]           /* get OCTOSPI_CR register */
+       orrs    r7, r7, r5                                      /* set abort bit */
+       str             r7, [r1, #OCTOSPI_CR]           /* store new CR register */
+       .endm
+
+       .macro  wait_busy
+0:
+       ldr             r7, [r1, #OCTOSPI_SR]           /* load status */
+       lsrs    r7, r7, #(SPI_BUSY+1)           /* shift BUSY into C */
+       bcs             0b                                                      /* loop until BUSY cleared */
+       movs    r7, #(1<<SPI_TCF)                       /* TCF bitmask */
+       str             r7, [r1, #OCTOSPI_FCR]          /* clear TCF flag */
+       .endm
+
+start:
+       adr             r2, buffer                                      /* pointer to start of buffer */
+       movs    r3, #OCTOSPI_DR                         /* load OCTOSPI_DR address offset */
+       adds    r3, r3, r1                                      /* address of OCTOSPI_DR */
+sector_start:
+       octospi_abort                                           /* start in clean state */
+       movs    r6, #OCTOSPI_CCR-OCTOSPI_DR     /* load OCTOSPI_CCR address offset */
+       adds    r6, r6, r3                                      /* address of OCTOSPI_CCR */
+       wait_busy
+       ldr             r7, cr_page_read                        /* indirect read mode */
+       str             r7, [r1, #OCTOSPI_CR]           /* set mode */
+       ldmia   r2!, {r4, r5}                           /* load address offset, length */
+       subs    r2, r2, #4                                      /* point to length */
+       subs    r5, r5, #1                                      /* decrement sector length for DLR */
+       str             r5, [r1, #OCTOSPI_DLR]          /* size-1 in DLR register */
+       ldr             r7, ccr_page_read                       /* CCR for read */
+       str             r7, [r6, #OCTOSPI_CCR_CCR]      /* initiate transfer */
+       ldr             r7, tcr_page_read                       /* TCR for read */
+       str             r7, [r6, #OCTOSPI_TCR_CCR]      /* instruction */
+       ldr             r7, ir_page_read                        /* IR for read */
+       str             r7, [r6, #OCTOSPI_IR_CCR]       /* instruction */
+       str             r4, [r1, #OCTOSPI_AR]           /* store SPI start address */
+       ldr             r6, [r2, #4]                            /* load initial value */
+read_loop:
+       ldrb    r4, [r3, #0]                            /* read next byte from DR */
+       movs    r7, #0xFF                                       /* fill bits 8-15 */
+       lsls    r7, r7, #8                                      /* with ones */
+       orrs    r4, r4, r7                                      /* copy ones to left of read byte */
+       ands    r6, r6, r4                                      /* and read byte to result */
+       lsls    r4, r4, #8                                      /* shift result into higher byte */
+       orrs    r6, r6, r4                                      /* or read byte to result */
+       subs    r5, r5, #1                                      /* decrement byte (count-1) */
+       bpl             read_loop                                       /* again if sector not completed */
+       adds    r5, r5, #1                                      /* increment count due to the -1 */
+       stmia   r2!, {r5, r6}                           /* save final count and result for sector */
+       subs    r0, r0, #1                                      /* decrement sector count */
+       bne             sector_start                            /* next sector? */
+       octospi_abort                                           /* to idle state */
+
+exit:
+       .align  2                                                       /* align to word, bkpt is 4 words */
+       bkpt    #0                                                      /* before code end for exit_point */
+       .align  2                                                       /* align to word */
+
+cr_page_read:
+       .space  4                                                       /* OCTOSPI_CR value for read command */
+ccr_page_read:
+       .space  4                                                       /* OCTOSPI_CCR value for read command */
+tcr_page_read:
+       .space  4                                                       /* OCTOSPI_TCR value for read command */
+ir_page_read:
+       .space  4                                                       /* OCTOSPI_IR value for read command */
+
+       .equ buffer, .
+
diff --git a/contrib/loaders/flash/stmqspi/stmoctospi_erase_check.inc b/contrib/loaders/flash/stmqspi/stmoctospi_erase_check.inc
new file mode 100644 (file)
index 0000000..c0e124a
--- /dev/null
@@ -0,0 +1,8 @@
+/* Autogenerated with ../../../../src/helper/bin2char.sh */
+0x1b,0xa2,0x50,0x23,0x5b,0x18,0x02,0x25,0x0f,0x68,0x2f,0x43,0x0f,0x60,0xb0,0x26,
+0xf6,0x18,0x0f,0x6a,0xbf,0x09,0xfc,0xd2,0x02,0x27,0x4f,0x62,0x10,0x4f,0x0f,0x60,
+0x30,0xca,0x04,0x3a,0x01,0x3d,0x0d,0x64,0x0e,0x4f,0x37,0x60,0x0e,0x4f,0xb7,0x60,
+0x0e,0x4f,0x37,0x61,0x8c,0x64,0x56,0x68,0x1c,0x78,0xff,0x27,0x3f,0x02,0x3c,0x43,
+0x26,0x40,0x24,0x02,0x26,0x43,0x01,0x3d,0xf6,0xd5,0x01,0x35,0x60,0xc2,0x01,0x38,
+0xd9,0xd1,0x02,0x25,0x0f,0x68,0x2f,0x43,0x0f,0x60,0xc0,0x46,0x00,0xbe,0xc0,0x46,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
diff --git a/contrib/loaders/flash/stmqspi/stmoctospi_read.S b/contrib/loaders/flash/stmqspi/stmoctospi_read.S
new file mode 100644 (file)
index 0000000..fb5ff1f
--- /dev/null
@@ -0,0 +1,142 @@
+/***************************************************************************
+ *   Copyright (C) 2019 by Andreas Bolsch                                  *
+ *   andreas.bolsch@mni.thm.de                                             *
+ *                                                                         *
+ *   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 <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+       .text
+       .syntax unified
+       .cpu cortex-m0
+       .thumb
+       .thumb_func
+
+/* Params:
+ * r0 - total count (bytes), remaining bytes (out, 0 means successful)
+ * r1 - flash page size
+ * r2 - address offset into flash
+ * r3 - OCTOSPI io_base
+ * r8 - fifo start
+ * r9 - fifo end + 1
+
+ * Clobbered:
+ * r4 - wp
+ * r5 - address of OCTOSPI_DR
+ * r6 - address of OCTOSPI_CCR
+ * r7 - tmp
+ */
+
+#include "../../../../src/flash/nor/stmqspi.h"
+
+#define OCTOSPI_CCR_CCR                                        (OCTOSPI_CCR - OCTOSPI_CCR)
+#define OCTOSPI_TCR_CCR                                        (OCTOSPI_TCR - OCTOSPI_CCR)
+#define OCTOSPI_IR_CCR                                 (OCTOSPI_IR - OCTOSPI_CCR)
+
+       .macro  octospi_abort
+       movs    r5, #(1<<SPI_ABORT)                     /* abort bit mask */
+       ldr             r7, [r3, #OCTOSPI_CR]           /* get OCTOSPI CR register */
+       orrs    r7, r7, r5                                      /* set abort bit */
+       str             r7, [r3, #OCTOSPI_CR]           /* store new CR register */
+       .endm
+
+       .macro  wait_busy
+0:
+       ldr             r7, [r3, #OCTOSPI_SR]           /* load status */
+       lsrs    r7, r7, #(SPI_BUSY+1)           /* shift BUSY into C */
+       bcs             0b                                                      /* loop until BUSY cleared */
+       movs    r7, #(1<<SPI_TCF)                       /* TCF bitmask */
+       str             r7, [r3, #OCTOSPI_FCR]          /* clear TCF flag */
+       .endm
+
+start:
+       subs    r0, r0, #1                                      /* decrement count for DLR */
+       subs    r1, r1, #1                                      /* page size mask and for DLR */
+       ldr             r4, wp                                          /* load wp */
+start_read:
+       octospi_abort                                           /* start in clean state */
+       movs    r5, #OCTOSPI_DR                         /* load OCTOSPI_DR address offset */
+       adds    r5, r5, r3                                      /* address of OCTOSPI_DR */
+       movs    r6, #OCTOSPI_CCR-OCTOSPI_DR     /* load OCTOSPI_CCR address offset */
+       adds    r6, r6, r5                                      /* address of OCTOSPI_CCR */
+       wait_busy
+       ldr             r7, cr_page_read                        /* indirect read mode */
+       str             r7, [r3, #OCTOSPI_CR]           /* set mode */
+       mov             r7, r2                                          /* get current start address */
+       orrs    r7, r7, r1                                      /* end of current page */
+       subs    r7, r7, r2                                      /* count-1 to end of page */
+       cmp             r7, r0                                          /* if this count <= remaining */
+       bls             write_dlr                                       /* then write to end of page */
+       mov             r7, r0                                          /* else write all remaining */
+write_dlr:
+       str             r7, [r3, #OCTOSPI_DLR]          /* size-1 in DLR register */
+       ldr             r7, ccr_page_read                       /* CCR for read */
+       str             r7, [r6, #OCTOSPI_CCR_CCR]      /* initiate transfer */
+       ldr             r7, tcr_page_read                       /* TCR for read */
+       str             r7, [r6, #OCTOSPI_TCR_CCR]      /* instruction */
+       ldr             r7, ir_page_read                        /* IR for read */
+       str             r7, [r6, #OCTOSPI_IR_CCR]       /* instruction */
+       str             r2, [r3, #OCTOSPI_AR]           /* store SPI start address */
+read_loop:
+       ldrb    r7, [r5]                                        /* read next byte from DR */
+       strb    r7, [r4, #0]                            /* write next byte */
+       adds    r4, r4, #1                                      /* increment internal wp */
+       cmp             r4, r9                                          /* internal wp beyond end? */
+       blo             wait_fifo                                       /* if no, then ok */
+       mov             r4, r8                                          /* else wrap around */
+wait_fifo:
+       ldr             r7, rp                                          /* get rp */
+       cmp             r7, #0                                          /* if rp equals 0 */
+       beq             exit                                            /* then abort */
+       cmp             r4, r7                                          /* check if fifo full */
+       beq             wait_fifo                                       /* wait until not full */
+       adr             r7, wp                                          /* get address of wp */
+       str             r4, [r7]                                        /* store updated wp */
+       adds    r2, r2, #1                                      /* increment address */
+       subs    r0, r0, #1                                      /* decrement (count-1) */
+       bmi             exit                                            /* stop if no data left */
+       tst             r2, r1                                          /* page end ? */
+       bne             read_loop                                       /* if not, then next byte */
+page_end:
+       bal             start_read                                      /* then next page */
+
+exit:
+       adds    r0, r0, #1                                      /* increment count due to the -1 */
+       octospi_abort                                           /* to idle state */
+
+       .align  2                                                       /* align to word, bkpt is 4 words */
+       bkpt    #0                                                      /* before code end for exit_point */
+       .align  2                                                       /* align to word */
+
+       .space  4                                                       /* not used */
+       .space  4                                                       /* not used */
+       .space  4                                                       /* not used */
+       .space  4                                                       /* not used */
+
+       .space  4                                                       /* not used */
+       .space  4                                                       /* not used */
+       .space  4                                                       /* not used */
+       .space  4                                                       /* not used */
+
+cr_page_read:
+       .space  4                                                       /* OCTOSPI_CR value for read command */
+ccr_page_read:
+       .space  4                                                       /* OCTOSPI_CCR value for read command */
+tcr_page_read:
+       .space  4                                                       /* OCTOSPI_TCR value for read command */
+ir_page_read:
+       .space  4                                                       /* OCTOSPI_IR value for read command */
+
+       .equ wp, .                                                      /* wp, uint32_t */
+       .equ rp, wp + 4                                         /* rp, uint32_t */
+       .equ buffer, rp + 4                                     /* buffer follows right away */
diff --git a/contrib/loaders/flash/stmqspi/stmoctospi_read.inc b/contrib/loaders/flash/stmqspi/stmoctospi_read.inc
new file mode 100644 (file)
index 0000000..583f316
--- /dev/null
@@ -0,0 +1,12 @@
+/* Autogenerated with ../../../../src/helper/bin2char.sh */
+0x01,0x38,0x01,0x39,0x27,0x4c,0x02,0x25,0x1f,0x68,0x2f,0x43,0x1f,0x60,0x50,0x25,
+0xed,0x18,0xb0,0x26,0x76,0x19,0x1f,0x6a,0xbf,0x09,0xfc,0xd2,0x02,0x27,0x5f,0x62,
+0x1c,0x4f,0x1f,0x60,0x17,0x46,0x0f,0x43,0xbf,0x1a,0x87,0x42,0x00,0xd9,0x07,0x46,
+0x1f,0x64,0x19,0x4f,0x37,0x60,0x19,0x4f,0xb7,0x60,0x19,0x4f,0x37,0x61,0x9a,0x64,
+0x2f,0x78,0x27,0x70,0x01,0x34,0x4c,0x45,0x00,0xd3,0x44,0x46,0x16,0x4f,0x00,0x2f,
+0x09,0xd0,0xbc,0x42,0xfa,0xd0,0x13,0xa7,0x3c,0x60,0x01,0x32,0x01,0x38,0x02,0xd4,
+0x0a,0x42,0xed,0xd1,0xcf,0xe7,0x01,0x30,0x02,0x25,0x1f,0x68,0x2f,0x43,0x1f,0x60,
+0x00,0xbe,0xc0,0x46,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,
diff --git a/contrib/loaders/flash/stmqspi/stmoctospi_write.S b/contrib/loaders/flash/stmqspi/stmoctospi_write.S
new file mode 100644 (file)
index 0000000..867a024
--- /dev/null
@@ -0,0 +1,219 @@
+/***************************************************************************
+ *   Copyright (C) 2018 by Andreas Bolsch                                  *
+ *   andreas.bolsch@mni.thm.de                                             *
+ *                                                                         *
+ *   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 <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+       .text
+       .syntax unified
+       .cpu cortex-m0
+       .thumb
+       .thumb_func
+
+/* Params:
+ * r0 - total count (bytes), remaining bytes (out, 0 means successful)
+ * r1 - flash page size
+ * r2 - address offset into flash
+ * r3 - OCTOSPI io_base
+ * r8 - fifo start
+ * r9 - fifo end + 1
+
+ * Clobbered:
+ * r4 - rp
+ * r5 - address of OCTOSPI_DR
+ * r6 - address of OCTOSPI_CCR
+ * r7 - tmp
+ * r10 - single 0x0 / dual 0x1
+ */
+
+#include "../../../../src/flash/nor/stmqspi.h"
+
+#define OCTOSPI_CCR_CCR                                        (OCTOSPI_CCR - OCTOSPI_CCR)
+#define OCTOSPI_TCR_CCR                                        (OCTOSPI_TCR - OCTOSPI_CCR)
+#define OCTOSPI_IR_CCR                                 (OCTOSPI_IR - OCTOSPI_CCR)
+
+       .macro  octospi_abort
+       movs    r5, #(1<<SPI_ABORT)                     /* abort bit mask */
+       ldr             r7, [r3, #OCTOSPI_CR]           /* get OCTOSPI CR register */
+       orrs    r7, r7, r5                                      /* set abort bit */
+       str             r7, [r3, #OCTOSPI_CR]           /* store new CR register */
+       .endm
+
+       .macro  wait_busy
+0:
+       ldr             r7, [r3, #OCTOSPI_SR]           /* load status */
+       lsrs    r7, r7, #(SPI_BUSY+1)           /* shift BUSY into C */
+       bcs             0b                                                      /* loop until BUSY cleared */
+       movs    r7, #(1<<SPI_TCF)                       /* TCF bitmask */
+       str             r7, [r3, #OCTOSPI_FCR]          /* clear TCF flag */
+       .endm
+
+start:
+       subs    r0, r0, #1                                      /* decrement count for DLR */
+       subs    r1, r1, #1                                      /* page size mask and for DLR */
+       ldr             r4, rp                                          /* load rp */
+       ldr             r7, [r3, #OCTOSPI_CR]           /* get OCTOSPI_CR register */
+       lsls    r7, r7, #(31-SPI_DUAL_FLASH)    /* clear higher order bits */
+       lsrs    r7, r7, #31                                     /* DUAL_FLASH bit into bit 0 */
+       mov             r10, r7                                         /* save in r10 */
+wip_loop:
+       octospi_abort                                           /* start in clean state */
+       movs    r5, #OCTOSPI_DR                         /* load OCTOSPI_DR address offset */
+       adds    r5, r5, r3                                      /* address of OCTOSPI_DR */
+       movs    r6, #OCTOSPI_CCR-OCTOSPI_DR     /* load OCTOSPI_CCR address offset */
+       adds    r6, r6, r5                                      /* address of OCTOSPI_CCR */
+       wait_busy
+       ldr             r7, cr_read_status                      /* indirect read mode */
+       str             r7, [r3, #OCTOSPI_CR]           /* set mode */
+       mov             r7, r10                                         /* get dual bit */
+       str             r7, [r3, #OCTOSPI_DLR]          /* one or two (for dual) bytes */
+       ldr             r7, ccr_read_status                     /* CCR for status read */
+       str             r7, [r6, #OCTOSPI_CCR_CCR]      /* initiate status read */
+       ldr             r7, tcr_read_status                     /* TCR for status read */
+       str             r7, [r6, #OCTOSPI_TCR_CCR]      /* instruction */
+       ldr             r7, ir_read_status                      /* IR for status read */
+       str             r7, [r6, #OCTOSPI_IR_CCR]       /* instruction */
+       movs    r7, #0                                          /* dummy address */
+       str             r7, [r3, #OCTOSPI_AR]           /* into AR (for 8-line mode) */
+       ldrb    r7, [r5]                                        /* get first status register */
+       lsrs    r7, r7, #(SPIFLASH_BSY+1)       /* if first flash busy, */
+       bcs             wip_loop                                        /* then poll again */
+       mov             r7, r10                                         /* get dual bit */
+       tst             r7, r7                                          /* dual mode ? */
+       beq             write_enable                            /* not dual, then ok */
+       ldrb    r7, [r5]                                        /* get second status register */
+       lsrs    r7, r7, #(SPIFLASH_BSY+1)       /* if second flash busy, */
+       bcs             wip_loop                                        /* then poll again */
+write_enable:
+       tst             r0, r0                                          /* test residual count */
+       bmi             exit                                            /* if negative, then finished */
+       wait_busy
+       ldr             r7, cr_write_enable                     /* indirect write mode */
+       str             r7, [r3, #OCTOSPI_CR]           /* set mode */
+       ldr             r7, ccr_write_enable            /* CCR for write enable */
+       str             r7, [r6, #OCTOSPI_CCR_CCR]      /* initiate write enable */
+       ldr             r7, tcr_write_enable            /* TCR for write enable */
+       str             r7, [r6, #OCTOSPI_TCR_CCR]      /* write enable instruction */
+       ldr             r7, ir_write_enable                     /* IR for write enable */
+       str             r7, [r6, #OCTOSPI_IR_CCR]       /* instruction */
+       movs    r7, #0                                          /* silicon bug in L5? dummy write */
+       str             r7, [r3, #OCTOSPI_AR]           /* into AR resolves issue */
+       wait_busy
+       ldr             r7, cr_read_status                      /* indirect read mode */
+       str             r7, [r3, #OCTOSPI_CR]           /* set mode */
+       mov             r7, r10                                         /* get dual count */
+       str             r7, [r3, #OCTOSPI_DLR]          /* one or two (for dual) bytes */
+       ldr             r7, ccr_read_status                     /* CCR for status read */
+       str             r7, [r6, #OCTOSPI_CCR_CCR]      /* initiate status read */
+       ldr             r7, tcr_read_status                     /* TCR for status read */
+       str             r7, [r6, #OCTOSPI_TCR_CCR]      /* instruction */
+       ldr             r7, ir_read_status                      /* IR for status read */
+       str             r7, [r6, #OCTOSPI_IR_CCR]       /* instruction */
+       movs    r7, #0                                          /* dummy address */
+       str             r7, [r3, #OCTOSPI_AR]           /* into AR (for 8-line mode) */
+       ldrb    r7, [r5]                                        /* get first status register */
+       lsrs    r7, r7, #(SPIFLASH_WE+1)        /* if first flash not */
+       bcc             error                                           /* write enabled, then error */
+       mov             r7, r10                                         /* get dual bit */
+       tst             r7, r7                                          /* dual mode ? */
+       beq             start_write                                     /* not dual, then ok */
+       ldrb    r7, [r5]                                        /* get second status register */
+       lsrs    r7, r7, #(SPIFLASH_WE+1)        /* if second flash not */
+       bcc             error                                           /* write enabled, then error */
+start_write:
+       wait_busy
+       ldr             r7, cr_page_write                       /* indirect write mode */
+       str             r7, [r3, #OCTOSPI_CR]           /* set mode */
+       mov             r7, r2                                          /* get current start address */
+       orrs    r7, r7, r1                                      /* end of current page */
+       subs    r7, r7, r2                                      /* count-1 to end of page */
+       cmp             r7, r0                                          /* if this count <= remaining */
+       bls             write_dlr                                       /* then write to end of page */
+       mov             r7, r0                                          /* else write all remaining */
+write_dlr:
+       str             r7, [r3, #OCTOSPI_DLR]          /* size-1 in DLR register */
+       ldr             r7, ccr_page_write                      /* CCR for page write */
+       str             r7, [r6, #OCTOSPI_CCR_CCR]      /* initiate transfer */
+       ldr             r7, tcr_page_write                      /* TCR for page write */
+       str             r7, [r6, #OCTOSPI_TCR_CCR]      /* instruction */
+       ldr             r7, ir_page_write                       /* IR for page write */
+       str             r7, [r6, #OCTOSPI_IR_CCR]       /* instruction */
+       str             r2, [r3, #OCTOSPI_AR]           /* store SPI start address */
+write_loop:
+       ldr             r7, wp                                          /* get wp */
+       cmp             r7, #0                                          /* if wp equals 0 */
+       beq             exit                                            /* then abort */
+       cmp             r4, r7                                          /* check if fifo empty */
+       beq             write_loop                                      /* wait until not empty */
+       ldrb    r7, [r4, #0]                            /* read next byte */
+       strb    r7, [r5]                                        /* write next byte to DR */
+       adds    r4, r4, #1                                      /* increment internal rp */
+       cmp             r4, r9                                          /* internal rp beyond end? */
+       blo             upd_write                                       /* if no, then ok */
+       mov             r4, r8                                          /* else wrap around */
+upd_write:
+       adr             r7, rp                                          /* get address of rp */
+       str             r4, [r7]                                        /* store updated rp */
+       adds    r2, r2, #1                                      /* increment address */
+       subs    r0, r0, #1                                      /* decrement (count-1) */
+       bmi             page_end                                        /* stop if no data left */
+       tst             r2, r1                                          /* page end ? */
+       bne             write_loop                                      /* if not, then next byte */
+page_end:
+       ldr             r7, [r3, #OCTOSPI_SR]           /* load status */
+       lsrs    r7, r7, #(SPI_TCF+1)            /* shift TCF into C */
+       bcc             page_end                                        /* loop until TCF set */
+       bal             wip_loop                                        /* then next page */
+
+error:
+       movs    r0, #0                                          /* return 0xFFFFFFFF */
+       subs    r0, r0, #2                                      /* for error */
+exit:
+       adds    r0, r0, #1                                      /* increment count due to the -1 */
+       octospi_abort                                           /* to idle state */
+       .align  2                                                       /* align to word, bkpt is 4 words */
+       bkpt    #0                                                      /* before code end for exit_point */
+       .align  2                                                       /* align to word */
+
+cr_read_status:
+       .space  4                                                       /* OCTOSPI_CR value for READ_STATUS command */
+ccr_read_status:
+       .space  4                                                       /* OCTOSPI_CCR value for READ_STATUS command */
+tcr_read_status:
+       .space  4                                                       /* OCTOSPI_TCR value for READ_STATUS command */
+ir_read_status:
+       .space  4                                                       /* OCTOSPI_IR value for READ_STATUS command */
+
+cr_write_enable:
+       .space  4                                                       /* OCTOSPI_CR value for WRITE_ENABLE command */
+ccr_write_enable:
+       .space  4                                                       /* OCTOSPI_CCR value for WRITE_ENABLE command */
+tcr_write_enable:
+       .space  4                                                       /* OCTOSPI_TCR value for WRITE_ENABLE command */
+ir_write_enable:
+       .space  4                                                       /* OCTOSPI_IR value for WRITE_ENABLE command */
+
+cr_page_write:
+       .space  4                                                       /* OCTOSPI_CR value for PAGE_PROG command */
+ccr_page_write:
+       .space  4                                                       /* OCTOSPI_CCR value for PAGE_PROG command */
+tcr_page_write:
+       .space  4                                                       /* OCTOSPI_TCR value for PAGE_PROG command */
+ir_page_write:
+       .space  4                                                       /* OCTOSPI_IR value for PAGE_PROG command */
+
+       .equ wp, .                                                      /* wp, uint32_t */
+       .equ rp, wp + 4                                         /* rp, uint32_t */
+       .equ buffer, rp + 4                                     /* buffer follows right away */
diff --git a/contrib/loaders/flash/stmqspi/stmoctospi_write.inc b/contrib/loaders/flash/stmqspi/stmoctospi_write.inc
new file mode 100644 (file)
index 0000000..81781a2
--- /dev/null
@@ -0,0 +1,21 @@
+/* Autogenerated with ../../../../src/helper/bin2char.sh */
+0x01,0x38,0x01,0x39,0x4f,0x4c,0x1f,0x68,0x7f,0x06,0xff,0x0f,0xba,0x46,0x02,0x25,
+0x1f,0x68,0x2f,0x43,0x1f,0x60,0x50,0x25,0xed,0x18,0xb0,0x26,0x76,0x19,0x1f,0x6a,
+0xbf,0x09,0xfc,0xd2,0x02,0x27,0x5f,0x62,0x39,0x4f,0x1f,0x60,0x57,0x46,0x1f,0x64,
+0x38,0x4f,0x37,0x60,0x38,0x4f,0xb7,0x60,0x38,0x4f,0x37,0x61,0x00,0x27,0x9f,0x64,
+0x2f,0x78,0x7f,0x08,0xe3,0xd2,0x57,0x46,0x3f,0x42,0x02,0xd0,0x2f,0x78,0x7f,0x08,
+0xdd,0xd2,0x00,0x42,0x55,0xd4,0x1f,0x6a,0xbf,0x09,0xfc,0xd2,0x02,0x27,0x5f,0x62,
+0x2f,0x4f,0x1f,0x60,0x2f,0x4f,0x37,0x60,0x2f,0x4f,0xb7,0x60,0x2f,0x4f,0x37,0x61,
+0x00,0x27,0x9f,0x64,0x1f,0x6a,0xbf,0x09,0xfc,0xd2,0x02,0x27,0x5f,0x62,0x24,0x4f,
+0x1f,0x60,0x57,0x46,0x1f,0x64,0x23,0x4f,0x37,0x60,0x23,0x4f,0xb7,0x60,0x23,0x4f,
+0x37,0x61,0x00,0x27,0x9f,0x64,0x2f,0x78,0xbf,0x08,0x30,0xd3,0x57,0x46,0x3f,0x42,
+0x02,0xd0,0x2f,0x78,0xbf,0x08,0x2a,0xd3,0x1f,0x6a,0xbf,0x09,0xfc,0xd2,0x02,0x27,
+0x5f,0x62,0x1f,0x4f,0x1f,0x60,0x17,0x46,0x0f,0x43,0xbf,0x1a,0x87,0x42,0x00,0xd9,
+0x07,0x46,0x1f,0x64,0x1b,0x4f,0x37,0x60,0x1b,0x4f,0xb7,0x60,0x1b,0x4f,0x37,0x61,
+0x9a,0x64,0x1b,0x4f,0x00,0x2f,0x14,0xd0,0xbc,0x42,0xfa,0xd0,0x27,0x78,0x2f,0x70,
+0x01,0x34,0x4c,0x45,0x00,0xd3,0x44,0x46,0x16,0xa7,0x3c,0x60,0x01,0x32,0x01,0x38,
+0x01,0xd4,0x0a,0x42,0xed,0xd1,0x1f,0x6a,0xbf,0x08,0xfc,0xd3,0x87,0xe7,0x00,0x20,
+0x02,0x38,0x01,0x30,0x02,0x25,0x1f,0x68,0x2f,0x43,0x1f,0x60,0x00,0xbe,0xc0,0x46,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
diff --git a/contrib/loaders/flash/stmqspi/stmqspi_crc32.S b/contrib/loaders/flash/stmqspi/stmqspi_crc32.S
new file mode 100644 (file)
index 0000000..bfb2662
--- /dev/null
@@ -0,0 +1,108 @@
+/***************************************************************************
+ *   Copyright (C) 2019 by Andreas Bolsch                                  *
+ *   andreas.bolsch@mni.thm.de                                             *
+ *                                                                         *
+ *   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 <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+       .text
+       .syntax unified
+       .cpu cortex-m0
+       .thumb
+       .thumb_func
+
+/* Params:
+ * r0 - total count (bytes), crc32 (out)
+ * r1 - flash page size
+ * r2 - address offset into flash
+ * r3 - QSPI io_base
+
+ * Clobbered:
+ * r4 - rp
+ * r5 - address of QSPI_DR
+ * r7 - tmp
+ */
+
+#include "../../../../src/flash/nor/stmqspi.h"
+
+       .macro  qspi_abort
+       movs    r5, #(1<<SPI_ABORT)                     /* abort bit mask */
+       ldr             r7, [r3, #QSPI_CR]                      /* get QSPI_CR register */
+       orrs    r7, r7, r5                                      /* set abort bit */
+       str             r7, [r3, #QSPI_CR]                      /* store new CR register */
+       .endm
+
+       .macro  wait_busy
+0:
+       ldr             r7, [r3, #QSPI_SR]                      /* load status */
+       lsrs    r7, r7, #(SPI_BUSY+1)           /* shift BUSY into C */
+       bcs             0b                                                      /* loop until BUSY cleared */
+       movs    r7, #(1<<SPI_TCF)                       /* TCF bitmask */
+       str             r7, [r3, #QSPI_FCR]                     /* clear TCF flag */
+       .endm
+
+start:
+       subs    r0, r0, #1                                      /* decrement count for DLR */
+       subs    r1, r1, #1                                      /* page size mask and for DLR */
+       movs    r4, #0x00                                       /* initialize crc */
+       mvns    r4, r4                                          /* to 0xFFFFFFFF */
+start_read:
+       qspi_abort                                                      /* start in clean state */
+       movs    r5, #QSPI_DR                            /* load QSPI_DR address offset */
+       adds    r5, r5, r3                                      /* address of QSPI_DR */
+       wait_busy
+       mov             r7, r2                                          /* get current start address */
+       orrs    r7, r7, r1                                      /* end of current page */
+       subs    r7, r7, r2                                      /* count-1 to end of page */
+       cmp             r7, r0                                          /* if this count <= remaining */
+       bls             write_dlr                                       /* then read to end of page */
+       mov             r7, r0                                          /* else read all remaining */
+write_dlr:
+       str             r7, [r3, #QSPI_DLR]                     /* size-1 in DLR register */
+       ldr             r7, ccr_page_read                       /* CCR for page read */
+       str             r7, [r3, #QSPI_CCR]                     /* initiate transfer */
+       str             r2, [r3, #QSPI_AR]                      /* store SPI start address */
+       ldr             r7, [r3, #QSPI_SR]                      /* wait for command startup */
+       ldr             r6, =0x04C11DB7                         /* CRC32 polynomial */
+read_loop:
+       ldrb    r7, [r5]                                        /* read next byte from DR */
+       lsls    r7, r7, #24                                     /* shift into msb */
+       eors    r4, r4, r7
+       .rept   8                                                       /* unrolled bit loop */
+       asrs    r7, r4, #31                                     /* copy bit 31 into bits 0 to 31 */
+       ands    r7, r7, r6                                      /* r7 neg. -> CRC32XOR, pos. -> 0x0 */
+       lsls    r4, r4, #1                                      /* shift result */
+       eors    r4, r4, r7                                      /* eor by CRC32XOR or 0x0 */
+       .endr
+       adds    r2, r2, #1                                      /* increment address */
+       subs    r0, r0, #1                                      /* decrement (count-1) */
+       bmi             exit                                            /* stop if no data left */
+       tst             r2, r1                                          /* page end ? */
+       bne             read_loop                                       /* if not, then next byte */
+page_end:
+       bal             start_read                                      /* then next page */
+       .pool
+
+exit:
+       mvns    r0, r4                                          /* invert to get final result */
+       qspi_abort                                                      /* to idle state */
+       .align  2                                                       /* align to word, bkpt is 4 words */
+       bkpt    #0                                                      /* before code end for exit_point */
+       .align  2                                                       /* align to word */
+
+       .space  4                                                       /* not used */
+ccr_page_read:
+       .space  4                                                       /* QSPI_CCR value for read command */
+       .space  4                                                       /* not used */
+       .space  4                                                       /* not used */
diff --git a/contrib/loaders/flash/stmqspi/stmqspi_crc32.inc b/contrib/loaders/flash/stmqspi/stmqspi_crc32.inc
new file mode 100644 (file)
index 0000000..b532a48
--- /dev/null
@@ -0,0 +1,12 @@
+/* Autogenerated with ../../../../src/helper/bin2char.sh */
+0x01,0x38,0x01,0x39,0x00,0x24,0xe4,0x43,0x02,0x25,0x1f,0x68,0x2f,0x43,0x1f,0x60,
+0x20,0x25,0xed,0x18,0x9f,0x68,0xbf,0x09,0xfc,0xd2,0x02,0x27,0xdf,0x60,0x17,0x46,
+0x0f,0x43,0xbf,0x1a,0x87,0x42,0x00,0xd9,0x07,0x46,0x1f,0x61,0x1c,0x4f,0x5f,0x61,
+0x9a,0x61,0x9f,0x68,0x14,0x4e,0x2f,0x78,0x3f,0x06,0x7c,0x40,0xe7,0x17,0x37,0x40,
+0x64,0x00,0x7c,0x40,0xe7,0x17,0x37,0x40,0x64,0x00,0x7c,0x40,0xe7,0x17,0x37,0x40,
+0x64,0x00,0x7c,0x40,0xe7,0x17,0x37,0x40,0x64,0x00,0x7c,0x40,0xe7,0x17,0x37,0x40,
+0x64,0x00,0x7c,0x40,0xe7,0x17,0x37,0x40,0x64,0x00,0x7c,0x40,0xe7,0x17,0x37,0x40,
+0x64,0x00,0x7c,0x40,0xe7,0x17,0x37,0x40,0x64,0x00,0x7c,0x40,0x01,0x32,0x01,0x38,
+0x04,0xd4,0x0a,0x42,0xd7,0xd1,0xbf,0xe7,0xb7,0x1d,0xc1,0x04,0xe0,0x43,0x02,0x25,
+0x1f,0x68,0x2f,0x43,0x1f,0x60,0xc0,0x46,0x00,0xbe,0xc0,0x46,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
diff --git a/contrib/loaders/flash/stmqspi/stmqspi_erase_check.S b/contrib/loaders/flash/stmqspi/stmqspi_erase_check.S
new file mode 100644 (file)
index 0000000..d011103
--- /dev/null
@@ -0,0 +1,91 @@
+/***************************************************************************
+ *   Copyright (C) 2019 by Andreas Bolsch                                  *
+ *   andreas.bolsch@mni.thm.de                                             *
+ *                                                                         *
+ *   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 <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+       .text
+       .syntax unified
+       .cpu cortex-m0
+       .thumb
+       .thumb_func
+
+/* Params:
+ * r0 - sector count
+ * r1 - QSPI io_base
+
+ * Clobbered:
+ * r2 - r7 tmp */
+
+#include "../../../../src/flash/nor/stmqspi.h"
+
+       .macro  qspi_abort
+       movs    r4, #(1<<SPI_ABORT)                     /* abort bit mask */
+       ldr             r7, [r1, #QSPI_CR]                      /* get QSPI_CR register */
+       orrs    r7, r7, r4                                      /* set abort bit */
+       str             r7, [r1, #QSPI_CR]                      /* store new CR register */
+       .endm
+
+       .macro  wait_busy
+0:
+       ldr             r7, [r1, #QSPI_SR]                      /* load status */
+       lsrs    r7, r7, #(SPI_BUSY+1)           /* shift BUSY into C */
+       bcs             0b                                                      /* loop until BUSY cleared */
+       movs    r7, #(1<<SPI_TCF)                       /* TCF bitmask */
+       str             r7, [r1, #QSPI_FCR]                     /* clear TCF flag */
+       .endm
+
+start:
+       adr             r2, buffer                                      /* pointer to start of buffer */
+       movs    r3, #QSPI_DR                            /* load QSPI_DR address offset */
+       add             r3, r3, r1                                      /* address of QSPI_DR */
+sector_start:
+       qspi_abort                                                      /* start in clean state */
+       ldmia   r2!, {r4, r5, r6}                       /* load address offset, length, initial value */
+       subs    r2, r2, #8                                      /* point to length */
+       subs    r5, r5, #1                                      /* decrement sector length for DLR */
+       wait_busy
+       str             r5, [r1, #QSPI_DLR]                     /* size-1 in DLR register */
+       ldr             r7, ccr_page_read                       /* CCR for page read */
+       str             r7, [r1, #QSPI_CCR]                     /* initiate transfer */
+       str             r4, [r1, #QSPI_AR]                      /* store SPI start address */
+       ldr             r7, [r1, #QSPI_SR]                      /* wait for command startup */
+read_loop:
+       ldrb    r4, [r3]                                        /* read next byte from DR */
+       movs    r7, #0xFF                                       /* fill bits 8-15 */
+       lsls    r7, r7, #8                                      /* with ones */
+       orrs    r4, r4, r7                                      /* copy ones to left of read byte */
+       ands    r6, r6, r4                                      /* and read byte to result */
+       lsls    r4, r4, #8                                      /* shift result into higher byte */
+       orrs    r6, r6, r4                                      /* or read byte to result */
+       subs    r5, r5, #1                                      /* decrement byte (count-1) */
+       bpl             read_loop                                       /* again if sector not completed */
+       adds    r5, r5, #1                                      /* increment count due to the -1 */
+       stmia   r2!, {r5, r6}                           /* save final count and result for sector */
+       subs    r0, r0, #1                                      /* decrement sector count */
+       bne             sector_start                            /* next sector? */
+       qspi_abort                                                      /* to idle state */
+       .align  2                                                       /* align to word, bkpt is 4 words */
+       bkpt    #0                                                      /* before code end for exit_point */
+       .align  2                                                       /* align to word */
+
+       .space  4                                                       /* not used */
+ccr_page_read:
+       .space  4                                                       /* QSPI_CCR value for read command */
+       .space  4                                                       /* not used */
+       .space  4                                                       /* not used */
+
+       .equ buffer, .
+
diff --git a/contrib/loaders/flash/stmqspi/stmqspi_erase_check.inc b/contrib/loaders/flash/stmqspi/stmqspi_erase_check.inc
new file mode 100644 (file)
index 0000000..3bf7898
--- /dev/null
@@ -0,0 +1,7 @@
+/* Autogenerated with ../../../../src/helper/bin2char.sh */
+0x17,0xa2,0x20,0x23,0x0b,0x44,0x02,0x24,0x0f,0x68,0x27,0x43,0x0f,0x60,0x70,0xca,
+0x08,0x3a,0x01,0x3d,0x8f,0x68,0xbf,0x09,0xfc,0xd2,0x02,0x27,0xcf,0x60,0x0d,0x61,
+0x0c,0x4f,0x4f,0x61,0x8c,0x61,0x8f,0x68,0x1c,0x78,0xff,0x27,0x3f,0x02,0x3c,0x43,
+0x26,0x40,0x24,0x02,0x26,0x43,0x01,0x3d,0xf6,0xd5,0x01,0x35,0x60,0xc2,0x01,0x38,
+0xe1,0xd1,0x02,0x24,0x0f,0x68,0x27,0x43,0x0f,0x60,0xc0,0x46,0x00,0xbe,0xc0,0x46,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
diff --git a/contrib/loaders/flash/stmqspi/stmqspi_read.S b/contrib/loaders/flash/stmqspi/stmqspi_read.S
new file mode 100644 (file)
index 0000000..b84d4eb
--- /dev/null
@@ -0,0 +1,127 @@
+/***************************************************************************
+ *   Copyright (C) 2019 by Andreas Bolsch                                  *
+ *   andreas.bolsch@mni.thm.de                                             *
+ *                                                                         *
+ *   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 <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+       .text
+       .syntax unified
+       .cpu cortex-m0
+       .thumb
+       .thumb_func
+
+/* Params:
+ * r0 - total count (bytes), remaining bytes (out, 0 means successful)
+ * r1 - flash page size
+ * r2 - address offset into flash
+ * r3 - QSPI io_base
+ * r8 - fifo start
+ * r9 - fifo end + 1
+
+ * Clobbered:
+ * r4 - wp
+ * r5 - address of QSPI_DR
+ * r7 - tmp
+ */
+
+#include "../../../../src/flash/nor/stmqspi.h"
+
+       .macro  qspi_abort
+       movs    r5, #(1<<SPI_ABORT)                     /* abort bit mask */
+       ldr             r7, [r3, #QSPI_CR]                      /* get QSPI_CR register */
+       orrs    r7, r7, r5                                      /* set abort bit */
+       str             r7, [r3, #QSPI_CR]                      /* store new CR register */
+       .endm
+
+       .macro  wait_busy
+0:
+       ldr             r7, [r3, #QSPI_SR]                      /* load status */
+       lsrs    r7, r7, #(SPI_BUSY+1)           /* shift BUSY into C */
+       bcs             0b                                                      /* loop until BUSY cleared */
+       movs    r7, #(1<<SPI_TCF)                       /* TCF bitmask */
+       str             r7, [r3, #QSPI_FCR]                     /* clear TCF flag */
+       .endm
+
+start:
+       subs    r0, r0, #1                                      /* decrement count for DLR */
+       subs    r1, r1, #1                                      /* page size mask and for DLR */
+       ldr             r4, wp                                          /* load wp */
+start_read:
+       qspi_abort                                                      /* start in clean state */
+       movs    r5, #QSPI_DR                            /* load QSPI_DR address offset */
+       adds    r5, r5, r3                                      /* address of QSPI_DR */
+       wait_busy
+       mov             r7, r2                                          /* get current start address */
+       orrs    r7, r7, r1                                      /* end of current page */
+       subs    r7, r7, r2                                      /* count-1 to end of page */
+       cmp             r7, r0                                          /* if this count <= remaining */
+       bls             write_dlr                                       /* then read to end of page */
+       mov             r7, r0                                          /* else read all remaining */
+write_dlr:
+       str             r7, [r3, #QSPI_DLR]                     /* size-1 in DLR register */
+       ldr             r7, ccr_page_read                       /* CCR for page read */
+       str             r7, [r3, #QSPI_CCR]                     /* initiate transfer */
+       str             r2, [r3, #QSPI_AR]                      /* store SPI start address */
+       ldr             r7, [r3, #QSPI_SR]                      /* wait for command startup */
+read_loop:
+       ldrb    r7, [r5]                                        /* read next byte from DR */
+       strb    r7, [r4, #0]                            /* write next byte */
+       adds    r4, r4, #1                                      /* increment internal wp */
+       cmp             r4, r9                                          /* internal wp beyond end? */
+       blo             wait_fifo                                       /* if no, then ok */
+       mov             r4, r8                                          /* else wrap around */
+wait_fifo:
+       ldr             r7, rp                                          /* get rp */
+       cmp             r7, #0                                          /* if rp equals 0 */
+       beq             exit                                            /* then abort */
+       cmp             r4, r7                                          /* check if fifo full */
+       beq             wait_fifo                                       /* wait until not full */
+       adr             r7, wp                                          /* get address of wp */
+       str             r4, [r7]                                        /* store updated wp */
+       adds    r2, r2, #1                                      /* increment address */
+       subs    r0, r0, #1                                      /* decrement (count-1) */
+       bmi             exit                                            /* stop if no data left */
+       tst             r2, r1                                          /* page end ? */
+       bne             read_loop                                       /* if not, then next byte */
+page_end:
+       bal             start_read                                      /* then next page */
+
+exit:
+       adds    r0, r0, #1                                      /* increment count due to the -1 */
+       qspi_abort                                                      /* to idle state */
+
+       .align  2                                                       /* align to word, bkpt is 4 words */
+       bkpt    #0                                                      /* before code end for exit_point */
+       .align  2                                                       /* align to word */
+
+       .space  4                                                       /* not used */
+       .space  4                                                       /* not used */
+       .space  4                                                       /* not used */
+       .space  4                                                       /* not used */
+
+       .space  4                                                       /* not used */
+       .space  4                                                       /* not used */
+       .space  4                                                       /* not used */
+       .space  4                                                       /* not used */
+
+       .space  4                                                       /* not used */
+ccr_page_read:
+       .space  4                                                       /* QSPI_CCR value for read command */
+       .space  4                                                       /* not used */
+       .space  4                                                       /* not used */
+
+       .equ wp, .                                                      /* wp, uint32_t */
+       .equ rp, wp + 4                                         /* rp, uint32_t */
+       .equ buffer, rp + 4                                     /* buffer follows right away */
diff --git a/contrib/loaders/flash/stmqspi/stmqspi_read.inc b/contrib/loaders/flash/stmqspi/stmqspi_read.inc
new file mode 100644 (file)
index 0000000..934b9b1
--- /dev/null
@@ -0,0 +1,11 @@
+/* Autogenerated with ../../../../src/helper/bin2char.sh */
+0x01,0x38,0x01,0x39,0x24,0x4c,0x02,0x25,0x1f,0x68,0x2f,0x43,0x1f,0x60,0x20,0x25,
+0xed,0x18,0x9f,0x68,0xbf,0x09,0xfc,0xd2,0x02,0x27,0xdf,0x60,0x17,0x46,0x0f,0x43,
+0xbf,0x1a,0x87,0x42,0x00,0xd9,0x07,0x46,0x1f,0x61,0x18,0x4f,0x5f,0x61,0x9a,0x61,
+0x9f,0x68,0x2f,0x78,0x27,0x70,0x01,0x34,0x4c,0x45,0x00,0xd3,0x44,0x46,0x17,0x4f,
+0x00,0x2f,0x09,0xd0,0xbc,0x42,0xfa,0xd0,0x13,0xa7,0x3c,0x60,0x01,0x32,0x01,0x38,
+0x02,0xd4,0x0a,0x42,0xed,0xd1,0xd6,0xe7,0x01,0x30,0x02,0x25,0x1f,0x68,0x2f,0x43,
+0x1f,0x60,0xc0,0x46,0x00,0xbe,0xc0,0x46,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
diff --git a/contrib/loaders/flash/stmqspi/stmqspi_write.S b/contrib/loaders/flash/stmqspi/stmqspi_write.S
new file mode 100644 (file)
index 0000000..40953ac
--- /dev/null
@@ -0,0 +1,177 @@
+/***************************************************************************
+ *   Copyright (C) 2016 - 2018 by Andreas Bolsch                           *
+ *   andreas.bolsch@mni.thm.de                                             *
+ *                                                                         *
+ *   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 <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+       .text
+       .syntax unified
+       .cpu cortex-m0
+       .thumb
+       .thumb_func
+
+/* Params:
+ * r0 - total count (bytes), remaining bytes (out, 0 means successful)
+ * r1 - flash page size
+ * r2 - address offset into flash
+ * r3 - QSPI io_base
+ * r8 - fifo start
+ * r9 - fifo end + 1
+
+ * Clobbered:
+ * r4 - rp
+ * r5 - address of QSPI_DR
+ * r7 - tmp
+ * r10 - single 0x0 / dual 0x1
+ */
+
+#include "../../../../src/flash/nor/stmqspi.h"
+
+       .macro  qspi_abort
+       movs    r5, #(1<<SPI_ABORT)                     /* abort bit mask */
+       ldr             r7, [r3, #QSPI_CR]                      /* get QSPI_CR register */
+       orrs    r7, r7, r5                                      /* set abort bit */
+       str             r7, [r3, #QSPI_CR]                      /* store new CR register */
+       .endm
+
+       .macro  wait_busy
+0:
+       ldr             r7, [r3, #QSPI_SR]                      /* load status */
+       lsrs    r7, r7, #(SPI_BUSY+1)           /* shift BUSY into C */
+       bcs             0b                                                      /* loop until BUSY cleared */
+       movs    r7, #(1<<SPI_TCF)                       /* TCF bitmask */
+       str             r7, [r3, #QSPI_FCR]                     /* clear TCF flag */
+       .endm
+
+start:
+       subs    r0, r0, #1                                      /* decrement count for DLR */
+       subs    r1, r1, #1                                      /* page size mask and for DLR */
+       ldr             r4, rp                                          /* load rp */
+       ldr             r7, [r3, #QSPI_CR]                      /* get QSPI_CR register */
+       lsls    r7, r7, #(31-SPI_DUAL_FLASH)    /* clear higher order bits */
+       lsrs    r7, r7, #31                                     /* DUAL_FLASH bit into bit 0 */
+       mov             r10, r7                                         /* save in r10 */
+wip_loop:
+       qspi_abort                                                      /* start in clean state */
+       movs    r5, #QSPI_DR                            /* load QSPI_DR address offset */
+       adds    r5, r5, r3                                      /* address of QSPI_DR */
+       wait_busy
+       mov             r7, r10                                         /* get dual bit */
+       str             r7, [r3, #QSPI_DLR]                     /* one or two (for dual) bytes */
+       ldr             r7, ccr_read_status                     /* CCR for status read */
+       str             r7, [r3, #QSPI_CCR]                     /* initiate status read */
+       ldr             r7, [r3, #QSPI_SR]                      /* wait for command startup */
+       ldrb    r7, [r5]                                        /* get first status register */
+       lsrs    r7, r7, #(SPIFLASH_BSY+1)       /* if first flash busy, */
+       bcs             wip_loop                                        /* then poll again */
+       mov             r7, r10                                         /* get dual bit */
+       tst             r7, r7                                          /* dual mode ? */
+       beq             write_enable                            /* not dual, then ok */
+       ldrb    r7, [r5]                                        /* get second status register */
+       lsrs    r7, r7, #(SPIFLASH_BSY+1)       /* if second flash busy, */
+       bcs             wip_loop                                        /* then poll again */
+write_enable:
+       tst             r0, r0                                          /* test residual count */
+       bmi             exit                                            /* if negative, then finished */
+       wait_busy
+       ldr             r7, ccr_write_enable            /* CCR for write enable */
+       str             r7, [r3, #QSPI_CCR]                     /* initiate write enable */
+       wait_busy
+       mov             r7, r10                                         /* get dual bit */
+       str             r7, [r3, #QSPI_DLR]                     /* one or two (for dual) bytes */
+       ldr             r7, ccr_read_status                     /* CCR for status read */
+       str             r7, [r3, #QSPI_CCR]                     /* initiate status read */
+       ldr             r7, [r3, #QSPI_SR]                      /* wait for command startup */
+       ldrb    r7, [r5]                                        /* get first status register */
+       lsrs    r7, r7, #(SPIFLASH_WE+1)        /* if first flash not */
+       bcc             error                                           /* write enabled, then error */
+       mov             r7, r10                                         /* get dual bit */
+       tst             r7, r7                                          /* dual mode ? */
+       beq             start_write                                     /* not dual, then ok */
+       ldrb    r7, [r5]                                        /* get second status register */
+       lsrs    r7, r7, #(SPIFLASH_WE+1)        /* if second flash not */
+       bcc             error                                           /* write enabled, then error */
+start_write:
+       wait_busy
+       mov             r7, r2                                          /* get current start address */
+       orrs    r7, r7, r1                                      /* end of current page */
+       subs    r7, r7, r2                                      /* count-1 to end of page */
+       cmp             r7, r0                                          /* if this count <= remaining */
+       bls             write_dlr                                       /* then write to end of page */
+       mov             r7, r0                                          /* else write all remaining */
+write_dlr:
+       str             r7, [r3, #QSPI_DLR]                     /* size-1 in DLR register */
+       ldr             r7, ccr_page_write                      /* CCR for page write */
+       str             r7, [r3, #QSPI_CCR]                     /* initiate transfer */
+       str             r2, [r3, #QSPI_AR]                      /* store SPI start address */
+       ldr             r7, [r3, #QSPI_SR]                      /* wait for command startup */
+write_loop:
+       ldr             r7, wp                                          /* get wp */
+       cmp             r7, #0                                          /* if wp equals 0 */
+       beq             exit                                            /* then abort */
+       cmp             r4, r7                                          /* check if fifo empty */
+       beq             write_loop                                      /* wait until not empty */
+       ldrb    r7, [r4, #0]                            /* read next byte */
+       strb    r7, [r5]                                        /* write next byte to DR */
+       adds    r4, r4, #1                                      /* increment internal rp */
+       cmp             r4, r9                                          /* internal rp beyond end? */
+       blo             upd_write                                       /* if no, then ok */
+       mov             r4, r8                                          /* else wrap around */
+upd_write:
+       adr             r7, rp                                          /* get address of rp */
+       str             r4, [r7]                                        /* store updated rp */
+       adds    r2, r2, #1                                      /* increment address */
+       subs    r0, r0, #1                                      /* decrement (count-1) */
+       bmi             page_end                                        /* stop if no data left */
+       tst             r2, r1                                          /* page end ? */
+       bne             write_loop                                      /* if not, then next byte */
+page_end:
+       ldr             r7, [r3, #QSPI_SR]                      /* load status */
+       lsrs    r7, r7, #(SPI_TCF+1)            /* shift TCF into C */
+       bcc             page_end                                        /* loop until TCF set */
+       bal             wip_loop                                        /* then next page */
+
+error:
+       movs    r0, #0                                          /* return 0xFFFFFFFF */
+       subs    r0, r0, #2                                      /* for error */
+exit:
+       adds    r0, r0, #1                                      /* increment count due to the -1 */
+       qspi_abort                                                      /* to idle state */
+
+       .align  2                                                       /* align to word, bkpt is 4 words */
+       bkpt    #0                                                      /* before code end for exit_point */
+       .align  2                                                       /* align to word */
+
+       .space  4                                                       /* not used */
+ccr_read_status:
+       .space  4                                                       /* QSPI_CCR value for READ_STATUS command */
+       .space  4                                                       /* not used */
+       .space  4                                                       /* not used */
+
+       .space  4                                                       /* not used */
+ccr_write_enable:
+       .space  4                                                       /* QSPI_CCR value for WRITE_ENABLE command */
+       .space  4                                                       /* not used */
+       .space  4                                                       /* not used */
+
+       .space  4                                                       /* not used */
+ccr_page_write:
+       .space  4                                                       /* QSPI_CCR value for PAGE_PROG command */
+       .space  4                                                       /* not used */
+       .space  4                                                       /* not used */
+
+       .equ wp, .                                                      /* wp, uint32_t */
+       .equ rp, wp + 4                                         /* rp, uint32_t */
+       .equ buffer, rp + 4                                     /* buffer follows right away */
diff --git a/contrib/loaders/flash/stmqspi/stmqspi_write.inc b/contrib/loaders/flash/stmqspi/stmqspi_write.inc
new file mode 100644 (file)
index 0000000..6c063c8
--- /dev/null
@@ -0,0 +1,18 @@
+/* Autogenerated with ../../../../src/helper/bin2char.sh */
+0x01,0x38,0x01,0x39,0x41,0x4c,0x1f,0x68,0x7f,0x06,0xff,0x0f,0xba,0x46,0x02,0x25,
+0x1f,0x68,0x2f,0x43,0x1f,0x60,0x20,0x25,0xed,0x18,0x9f,0x68,0xbf,0x09,0xfc,0xd2,
+0x02,0x27,0xdf,0x60,0x57,0x46,0x1f,0x61,0x2c,0x4f,0x5f,0x61,0x9f,0x68,0x2f,0x78,
+0x7f,0x08,0xec,0xd2,0x57,0x46,0x3f,0x42,0x02,0xd0,0x2f,0x78,0x7f,0x08,0xe6,0xd2,
+0x00,0x42,0x41,0xd4,0x9f,0x68,0xbf,0x09,0xfc,0xd2,0x02,0x27,0xdf,0x60,0x27,0x4f,
+0x5f,0x61,0x9f,0x68,0xbf,0x09,0xfc,0xd2,0x02,0x27,0xdf,0x60,0x57,0x46,0x1f,0x61,
+0x1e,0x4f,0x5f,0x61,0x9f,0x68,0x2f,0x78,0xbf,0x08,0x2b,0xd3,0x57,0x46,0x3f,0x42,
+0x02,0xd0,0x2f,0x78,0xbf,0x08,0x25,0xd3,0x9f,0x68,0xbf,0x09,0xfc,0xd2,0x02,0x27,
+0xdf,0x60,0x17,0x46,0x0f,0x43,0xbf,0x1a,0x87,0x42,0x00,0xd9,0x07,0x46,0x1f,0x61,
+0x1a,0x4f,0x5f,0x61,0x9a,0x61,0x9f,0x68,0x1b,0x4f,0x00,0x2f,0x14,0xd0,0xbc,0x42,
+0xfa,0xd0,0x27,0x78,0x2f,0x70,0x01,0x34,0x4c,0x45,0x00,0xd3,0x44,0x46,0x17,0xa7,
+0x3c,0x60,0x01,0x32,0x01,0x38,0x01,0xd4,0x0a,0x42,0xed,0xd1,0x9f,0x68,0xbf,0x08,
+0xfc,0xd3,0xa4,0xe7,0x00,0x20,0x02,0x38,0x01,0x30,0x02,0x25,0x1f,0x68,0x2f,0x43,
+0x1f,0x60,0xc0,0x46,0x00,0xbe,0xc0,0x46,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
index 9903b70dfb0631a9843c1f9625494c2a2315b5fa..84ed320602841e045eefe748c1134ce7d5c8b9df 100644 (file)
@@ -5253,6 +5253,18 @@ it has been removed by the @option{unlock} flag.
 
 @end deffn
 
+@deffn Command {flash verify_image} filename [offset] [type]
+Verify the image @file{filename} to the current target's flash bank(s).
+Parameters follow the description of 'flash write_image'.
+In contrast to the 'verify_image' command, for banks with specific
+verify method, that one is used instead of the usual target's read
+memory methods. This is necessary for flash banks not readable by
+ordinary memory reads.
+This command gives only an overall good/bad result for each bank, not
+addresses of individual failed bytes as it's intended only as quick
+check for successful programming.
+@end deffn
+
 @section Other Flash commands
 @cindex flash protection
 
@@ -5511,6 +5523,117 @@ flash bank $_FLASHNAME stmsmi 0xf8000000 0 0 0 $_TARGETNAME
 
 @end deffn
 
+@deffn {Flash Driver} stmqspi
+@cindex STMicroelectronics QuadSPI/OctoSPI Interface
+@cindex QuadSPI
+@cindex OctoSPI
+@cindex stmqspi
+Some devices from STMicroelectronics include a proprietary ``QuadSPI Interface''
+(e.g. STM32F4, STM32F7, STM32L4) or ``OctoSPI Interface'' (e.g. STM32L4+)
+controller able to drive one or even two (dual mode) external SPI flash devices.
+The OctoSPI is a superset of QuadSPI, its presence is detected automatically.
+Currently only the regular command mode is supported, whereas the HyperFlash
+mode is not.
+
+QuadSPI/OctoSPI makes the flash contents directly accessible in the CPU address
+space; in case of dual mode both devices must be of the same type and are
+mapped in the same memory bank (even and odd addresses interleaved).
+CPU can directly read data, execute code (but not boot) from QuadSPI bank.
+
+The 'flash bank' command only requires the @var{base} parameter and the extra
+parameter @var{io_base} in order to identify the memory bank. Both are fixed
+by hardware, see datasheet or RM. All other parameters are ignored.
+
+The controller must be initialized after each reset and properly configured
+for memory-mapped read operation for the particular flash chip(s), for the full
+list of available register settings cf. the controller's RM. This setup is quite
+board specific (that's why booting from this memory is not possible). The
+flash driver infers all parameters from current controller register values when
+'flash probe @var{bank_id}' is executed.
+
+Normal OpenOCD commands like @command{mdw} can be used to display the flash content,
+but only after proper controller initialization as decribed above. However,
+due to a silicon bug in some devices, attempting to access the very last word
+should be avoided.
+
+It is possible to use two (even different) flash chips alternatingly, if individual
+bank chip selects are available. For some package variants, this is not the case
+due to limited pin count. To switch from one to another, adjust FSEL bit accordingly
+and re-issue 'flash probe bank_id'. Note that the bank base address will @emph{not}
+change, so the address spaces of both devices will overlap. In dual flash mode
+both chips must be identical regarding size and most other properties.
+
+Block or sector protection internal to the flash chip is not handled by this
+driver at all, but can be dealt with manually by the 'cmd' command, see below.
+The sector protection via 'flash protect' command etc. is completely internal to
+openocd, intended only to prevent accidental erase or overwrite and it does not
+persist across openocd invocations.
+
+OpenOCD contains a hardcoded list of flash devices with their properties,
+these are auto-detected. If a device is not included in this list, SFDP discovery
+is attempted. If this fails or gives inappropriate results, manual setting is
+required (see 'set' command).
+
+@example
+flash bank $_FLASHNAME stmqspi 0x90000000 0 0 0 $_TARGETNAME 0xA0001000
+flash bank $_FLASHNAME stmqspi 0x70000000 0 0 0 $_TARGETNAME 0xA0001400
+@end example
+
+There are three specific commands
+@deffn Command {stmqspi mass_erase} bank_id
+Clears sector protections and performs a mass erase. Works only if there is no
+chip specific write protection engaged.
+@end deffn
+
+@deffn Command {stmqspi set} bank_id name total_size page_size read_cmd fread_cmd pprg_cmd mass_erase_cmd sector_size sector_erase_cmd
+Set flash parameters: @var{name} human readable string, @var{total_size} size
+in bytes, @var{page_size} is write page size. @var{read_cmd}, @var{fread_cmd} and @var{pprg_cmd}
+are commands for reading and page programming. @var{fread_cmd} is used in DPI and QPI modes,
+@var{read_cmd} in normal SPI (single line) mode. @var{mass_erase_cmd}, @var{sector_size}
+and @var{sector_erase_cmd} are optional.
+
+This command is required if chip id is not hardcoded yet and e.g. for EEPROMs or FRAMs
+which don't support an id command.
+
+In dual mode parameters of both chips are set identically. The parameters refer to
+a single chip, so the whole bank gets twice the specified capacity etc.
+@end deffn
+
+@deffn Command {stmqspi cmd} bank_id resp_num cmd_byte ...
+If @var{resp_num} is zero, sends command @var{cmd_byte} and following data
+bytes. In dual mode command byte is sent to @emph{both} chips but data bytes are
+sent @emph{alternatingly} to chip 1 and 2, first to flash 1, second to flash 2, etc.,
+i.e. the total number of bytes (including cmd_byte) must be odd.
+
+If @var{resp_num} is not zero, cmd and at most four following data bytes are
+sent, in dual mode @emph{simultaneously} to both chips. Then @var{resp_num} bytes
+are read interleaved from both chips starting with chip 1. In this case
+@var{resp_num} must be even.
+
+Note the hardware dictated subtle difference of those two cases in dual-flash mode.
+
+To check basic communication settings, issue
+@example
+stmqspi cmd bank_id 0 0x04; stmqspi cmd bank_id 1 0x05; stmqspi cmd bank_id 0 0x06; stmqspi cmd bank_id 1 0x05
+@end example
+for single flash mode or
+@example
+stmqspi cmd bank_id 0 0x04; stmqspi cmd bank_id 2 0x05; stmqspi cmd bank_id 0 0x06; stmqspi cmd bank_id 2 0x05
+@end example
+for dual flash mode. This should return the status register contents.
+
+In 8-line mode, @var{cmd_byte} is sent twice - first time as given, second time
+complemented. Additionally, in 8-line mode only, some commands (e.g. Read Status)
+need a dummy address, e.g.
+@example
+stmqspi cmd bank_id 1 0x05 0x00 0x00 0x00 0x00
+@end example
+should return the status register contents.
+
+@end deffn
+
+@end deffn
+
 @deffn {Flash Driver} mrvlqspi
 This driver supports QSPI flash controller of Marvell's Wireless
 Microcontroller platform.
index b95b003dff5d96fd0b1fcc4c87fc786a63861374..8c9b2b7ab0f1ffbe6b1ee8d5c970fb5a5ee38586 100644 (file)
@@ -52,10 +52,12 @@ NOR_DRIVERS = \
        %D%/psoc5lp.c \
        %D%/psoc6.c \
        %D%/renesas_rpchf.c \
+       %D%/sfdp.c \
        %D%/sh_qspi.c \
        %D%/sim3x.c \
        %D%/spi.c \
        %D%/stmsmi.c \
+       %D%/stmqspi.c \
        %D%/stellaris.c \
        %D%/stm32f1x.c \
        %D%/stm32f2x.c \
@@ -83,6 +85,8 @@ NORHEADERS = \
        %D%/imp.h \
        %D%/non_cfi.h \
        %D%/ocl.h \
+       %D%/sfdp.h \
        %D%/spi.h \
        %D%/stm32l4x.h \
+       %D%/stmqspi.h \
        %D%/msp432.h
index 6182a5f8207f73cd3e64754a761e846cc3a1579a..c162c7097ba30a7a9bf39a936def640265e870c3 100644 (file)
@@ -94,7 +94,7 @@ int flash_driver_protect(struct flash_bank *bank, int set, unsigned int first,
 }
 
 int flash_driver_write(struct flash_bank *bank,
-       uint8_t *buffer, uint32_t offset, uint32_t count)
+       const uint8_t *buffer, uint32_t offset, uint32_t count)
 {
        int retval;
 
@@ -135,6 +135,43 @@ int default_flash_read(struct flash_bank *bank,
        return target_read_buffer(bank->target, offset + bank->base, count, buffer);
 }
 
+int flash_driver_verify(struct flash_bank *bank,
+       const uint8_t *buffer, uint32_t offset, uint32_t count)
+{
+       int retval;
+
+       retval = bank->driver->verify ? bank->driver->verify(bank, buffer, offset, count) :
+               default_flash_verify(bank, buffer, offset, count);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("verify failed in bank at " TARGET_ADDR_FMT " starting at 0x%8.8" PRIx32,
+                       bank->base, offset);
+       }
+
+       return retval;
+}
+
+int default_flash_verify(struct flash_bank *bank,
+       const uint8_t *buffer, uint32_t offset, uint32_t count)
+{
+       uint32_t target_crc, image_crc;
+       int retval;
+
+       retval = image_calculate_checksum(buffer, count, &image_crc);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = target_checksum_memory(bank->target, offset + bank->base, count, &target_crc);
+       if (retval != ERROR_OK)
+               return retval;
+
+       LOG_DEBUG("addr " TARGET_ADDR_FMT ", len 0x%08" PRIx32 ", crc 0x%08" PRIx32 " 0x%08" PRIx32,
+               offset + bank->base, count, ~image_crc, ~target_crc);
+       if (target_crc == image_crc)
+               return ERROR_OK;
+       else
+               return ERROR_FAIL;
+}
+
 void flash_bank_add(struct flash_bank *bank)
 {
        /* put flash bank in linked list */
@@ -697,8 +734,8 @@ static bool flash_write_check_gap(struct flash_bank *bank,
 }
 
 
-int flash_write_unlock(struct target *target, struct image *image,
-       uint32_t *written, bool erase, bool unlock)
+int flash_write_unlock_verify(struct target *target, struct image *image,
+       uint32_t *written, bool erase, bool unlock, bool write, bool verify)
 {
        int retval = ERROR_OK;
 
@@ -932,8 +969,17 @@ int flash_write_unlock(struct target *target, struct image *image,
                }
 
                if (retval == ERROR_OK) {
-                       /* write flash sectors */
-                       retval = flash_driver_write(c, buffer, run_address - c->base, run_size);
+                       if (write) {
+                               /* write flash sectors */
+                               retval = flash_driver_write(c, buffer, run_address - c->base, run_size);
+                       }
+               }
+
+               if (retval == ERROR_OK) {
+                       if (verify) {
+                               /* verify flash sectors */
+                               retval = flash_driver_verify(c, buffer, run_address - c->base, run_size);
+                       }
                }
 
                free(buffer);
@@ -957,7 +1003,7 @@ done:
 int flash_write(struct target *target, struct image *image,
        uint32_t *written, bool erase)
 {
-       return flash_write_unlock(target, image, written, erase, false);
+       return flash_write_unlock_verify(target, image, written, erase, false, true, false);
 }
 
 struct flash_sector *alloc_block_array(uint32_t offset, uint32_t size,
index 163e57878e5c6eb8f93d43c654b683173fd49913..107a1c56e20182e7d26fc9d8fea54e7156935b36 100644 (file)
@@ -200,6 +200,7 @@ void default_flash_free_driver_priv(struct flash_bank *bank);
 
 /** Deallocates all flash banks */
 void flash_free_all_banks(void);
+
 /**
  * Provides default read implementation for flash memory.
  * @param bank The bank to read.
@@ -210,6 +211,18 @@ void flash_free_all_banks(void);
  */
 int default_flash_read(struct flash_bank *bank,
                uint8_t *buffer, uint32_t offset, uint32_t count);
+
+/**
+ * Provides default verify implementation for flash memory.
+ * @param bank The bank to verify.
+ * @param buffer The data bytes to verify.
+ * @param offset The offset into the chip to verify.
+ * @param count The number of bytes to verify.
+ * @returns ERROR_OK if successful; otherwise, an error code.
+ */
+int default_flash_verify(struct flash_bank *bank,
+               const uint8_t *buffer, uint32_t offset, uint32_t count);
+
 /**
  * Provides default erased-bank check handling. Checks to see if
  * the flash driver knows they are erased; if things look uncertain,
@@ -217,7 +230,6 @@ int default_flash_read(struct flash_bank *bank,
  * @returns ERROR_OK if successful; otherwise, an error code.
  */
 int default_flash_blank_check(struct flash_bank *bank);
-
 /**
  * Returns the flash bank specified by @a name, which matches the
  * driver name and a suffix (option) specify the driver-specific
index 7f66047fef7d02fe253704e8dbb44df3606188c3..e29d4f5280a5f50cedadad0bbc1ff4744c31062c 100644 (file)
@@ -155,6 +155,20 @@ struct flash_driver {
         int (*read)(struct flash_bank *bank,
                        uint8_t *buffer, uint32_t offset, uint32_t count);
 
+       /**
+        * Verify data in flash.  Note CPU address will be
+        * "bank->base + offset", while the physical address is
+        * dependent upon current target MMU mappings.
+        *
+        * @param bank The bank to verify
+        * @param buffer The data bytes to verify against.
+        * @param offset The offset into the chip to verify.
+        * @param count The number of bytes to verify.
+        * @returns ERROR_OK if successful; otherwise, an error code.
+        */
+       int (*verify)(struct flash_bank *bank,
+                       const uint8_t *buffer, uint32_t offset, uint32_t count);
+
        /**
         * Probe to determine what kind of flash is present.
         * This is invoked by the "probe" script command.
index d52e072ee503355f959adaa790506714722ed12b..570861ec57255b883631cd6e473dea1f542b72f6 100644 (file)
@@ -75,6 +75,7 @@ extern const struct flash_driver stm32f2x_flash;
 extern const struct flash_driver stm32lx_flash;
 extern const struct flash_driver stm32l4x_flash;
 extern const struct flash_driver stm32h7x_flash;
+extern const struct flash_driver stmqspi_flash;
 extern const struct flash_driver stmsmi_flash;
 extern const struct flash_driver str7x_flash;
 extern const struct flash_driver str9x_flash;
@@ -148,6 +149,7 @@ static const struct flash_driver * const flash_drivers[] = {
        &stm32l4x_flash,
        &stm32h7x_flash,
        &stmsmi_flash,
+       &stmqspi_flash,
        &str7x_flash,
        &str9x_flash,
        &str9xpec_flash,
index 06fb2a2b6abf83099383c614d0e72b41a9db300e..f66cf032938d583b42251a39cd6a2cb1acbf7485 100644 (file)
@@ -42,12 +42,14 @@ int flash_driver_erase(struct flash_bank *bank, unsigned int first,
 int flash_driver_protect(struct flash_bank *bank, int set, unsigned int first,
                unsigned int last);
 int flash_driver_write(struct flash_bank *bank,
-               uint8_t *buffer, uint32_t offset, uint32_t count);
+               const uint8_t *buffer, uint32_t offset, uint32_t count);
 int flash_driver_read(struct flash_bank *bank,
                uint8_t *buffer, uint32_t offset, uint32_t count);
+int flash_driver_verify(struct flash_bank *bank,
+               const uint8_t *buffer, uint32_t offset, uint32_t count);
 
 /* write (optional verify) an image to flash memory of the given target */
-int flash_write_unlock(struct target *target, struct image *image,
-               uint32_t *written, bool erase, bool unlock);
+int flash_write_unlock_verify(struct target *target, struct image *image,
+               uint32_t *written, bool erase, bool unlock, bool write, bool verify);
 
 #endif /* OPENOCD_FLASH_NOR_IMP_H */
diff --git a/src/flash/nor/sfdp.c b/src/flash/nor/sfdp.c
new file mode 100644 (file)
index 0000000..02b4ced
--- /dev/null
@@ -0,0 +1,263 @@
+/***************************************************************************
+ *   Copyright (C) 2019 by Andreas Bolsch <andreas.bolsch@mni.thm.de      *
+ *   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 <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "imp.h"
+#include "spi.h"
+#include "sfdp.h"
+
+#define SFDP_MAGIC                     0x50444653
+#define SFDP_ACCESS_PROT       0xFF
+#define SFDP_BASIC_FLASH       0xFF00
+#define SFDP_4BYTE_ADDR                0xFF84
+
+static const char *sfdp_name = "sfdp";
+
+struct sfdp_hdr {
+       uint32_t                        signature;
+       uint32_t                        revision;
+};
+
+struct sfdp_phdr {
+       uint32_t                        revision;
+       uint32_t                        ptr;
+};
+
+struct sfdp_basic_flash_param {
+       uint32_t                        fast_addr;              /* 01: fast read and 3/4 address bytes */
+       uint32_t                        density;                /* 02: memory density */
+       uint32_t                        fast_1x4;               /* 03: 1-1-4 and 1-4-4 fast read */
+       uint32_t                        fast_1x2;               /* 04: 1-2-2 and 1-1-2 fast read */
+       uint32_t                        fast_444;               /* 05: 4-4-4 and 2-2-2 fast read */
+       uint32_t                        read_222;               /* 06: 2-2-2 fast read instr and dummy */
+       uint32_t                        read_444;               /* 07: 4-4-4 fast read instr and dummy */
+       uint32_t                        erase_t12;              /* 08: erase types 1, 2 */
+       uint32_t                        erase_t34;              /* 09: erase types 3, 4 */
+       uint32_t                        erase_time;             /* 10: erase times for types 1 - 4 */
+       uint32_t                        chip_byte;              /* 11: chip erase time, byte prog time, page prog */
+       uint32_t                        susp_time;              /* 12: suspend and resume times */
+       uint32_t                        susp_instr;             /* 13: suspend and resume instr */
+       uint32_t                        pwrd_instr;             /* 14: powerdown instr */
+       uint32_t                        quad_req;               /* 15: quad enable requirements */
+       uint32_t                        addr_reset;             /* 16: 3-/4-byte addressing and reset */
+       uint32_t                        read_1x8;               /* 17: 1-1-8 and 1-8-8 fast read instr and dummy */
+       uint32_t                        dtr_drive;              /* 18: dtr modes and drive strength */
+       uint32_t                        octal_req;              /* 19: octal enable requirements */
+       uint32_t                        speed_888;              /* 20: speed in 8-8-8 modes */
+};
+
+struct sfdp_4byte_addr_param {
+       uint32_t                        flags;                  /* 01: various flags */
+       uint32_t                        erase_t1234;    /* 02: erase commands */
+};
+
+/* Try to get parameters from flash via SFDP */
+int spi_sfdp(struct flash_bank *bank, struct flash_device *dev,
+       read_sfdp_block_t read_sfdp_block)
+{
+       struct sfdp_hdr header;
+       struct sfdp_phdr *pheaders = NULL;
+       uint32_t *ptable = NULL;
+       unsigned int j, k, nph;
+       int retval, erase_type = 0;
+
+       memset(dev, 0, sizeof(struct flash_device));
+
+       /* retrieve SFDP header */
+       memset(&header, 0, sizeof(header));
+       retval = read_sfdp_block(bank, 0x0, sizeof(header) >> 2, (uint32_t *) &header);
+       if (retval != ERROR_OK)
+               return retval;
+       LOG_DEBUG("header 0x%08" PRIx32 " 0x%08" PRIx32, header.signature, header.revision);
+       if (header.signature != SFDP_MAGIC) {
+               LOG_INFO("no SDFP found");
+               return ERROR_FLASH_BANK_NOT_PROBED;
+       }
+       if (((header.revision >> 24) & 0xFF) != SFDP_ACCESS_PROT) {
+               LOG_ERROR("access protocol 0x%02" PRIx8 " not implemented",
+                       (header.revision >> 24) & 0xFF);
+               return ERROR_FLASH_BANK_NOT_PROBED;
+       }
+
+       /* retrieve table of parameter headers */
+       nph = ((header.revision >> 16) & 0xFF) + 1;
+       LOG_DEBUG("parameter headers: %d", nph);
+       pheaders = malloc(sizeof(struct sfdp_phdr) * nph);
+       if (pheaders == NULL) {
+               LOG_ERROR("not enough memory");
+               return ERROR_FAIL;
+       }
+       memset(pheaders, 0, sizeof(struct sfdp_phdr) * nph);
+       retval = read_sfdp_block(bank, sizeof(header),
+               (sizeof(struct sfdp_phdr) >> 2) * nph, (uint32_t *) pheaders);
+       if (retval != ERROR_OK)
+               goto err;
+
+       for (k = 0; k < nph; k++) {
+               uint8_t words = (pheaders[k].revision >> 24) & 0xFF;
+               uint16_t id = (((pheaders[k].ptr) >> 16) & 0xFF00) | (pheaders[k].revision & 0xFF);
+               uint32_t ptr = pheaders[k].ptr & 0xFFFFFF;
+
+               LOG_DEBUG("pheader %d len=0x%02" PRIx8 " id=0x%04" PRIx16
+                       " ptr=0x%06" PRIx32, k, words, id, ptr);
+
+               /* retrieve parameter table */
+               ptable = malloc(words << 2);
+               if (ptable == NULL) {
+                       LOG_ERROR("not enough memory");
+                       retval = ERROR_FAIL;
+                       goto err;
+               }
+               retval = read_sfdp_block(bank, ptr, words, ptable);
+               if (retval != ERROR_OK)
+                       goto err;
+
+               for (j = 0; j < words; j++)
+                       LOG_DEBUG("word %02d 0x%08X", j + 1, ptable[j]);
+
+               if (id == SFDP_BASIC_FLASH) {
+                       struct sfdp_basic_flash_param *table = (struct sfdp_basic_flash_param *) ptable;
+                       uint16_t erase;
+
+                       if (words < 9) {
+                               LOG_ERROR("id=0x%04" PRIx16 " invalid length %d", id, words);
+                               retval = ERROR_FLASH_BANK_NOT_PROBED;
+                               goto err;
+                       }
+
+                       LOG_DEBUG("basic flash parameter table");
+                       /* dummy device name */
+                       dev->name = sfdp_name;
+
+                       /* default instructions */
+                       dev->read_cmd = SPIFLASH_READ;
+                       dev->pprog_cmd = SPIFLASH_PAGE_PROGRAM;
+                       dev->chip_erase_cmd = SPIFLASH_MASS_ERASE;
+
+                       /* get device size */
+                       if (table->density & (1UL << 31))
+                               dev->size_in_bytes = 1UL << ((table->density & ~(1UL << 31)) - 3);
+                       else
+                               dev->size_in_bytes = (table->density + 1) >> 3;
+
+                       /* 2-2-2 read instruction, not used */
+                       if (table->fast_444 & (1UL << 0))
+                               dev->qread_cmd = (table->read_222 >> 24) & 0xFF;
+
+                       /* 4-4-4 read instruction */
+                       if (table->fast_444 & (1UL << 4))
+                               dev->qread_cmd = (table->read_444 >> 24) & 0xFF;
+
+                       /* find the largest erase block size and instruction */
+                       erase = (table->erase_t12 >> 0) & 0xFFFF;
+                       erase_type = 1;
+                       if (((table->erase_t12 >> 16) & 0xFF) > (erase & 0xFF)) {
+                               erase = (table->erase_t12 >> 16) & 0xFFFF;
+                               erase_type = 2;
+                       }
+                       if (((table->erase_t34 >> 0) & 0xFF) > (erase & 0xFF)) {
+                               erase = (table->erase_t34 >> 0) & 0xFFFF;
+                               erase_type = 3;
+                       }
+                       if (((table->erase_t34 >> 16) & 0xFF) > (erase & 0xFF)) {
+                               erase = (table->erase_t34 >> 16) & 0xFFFF;
+                               erase_type = 4;
+                       }
+                       dev->erase_cmd = (erase >> 8) & 0xFF;
+                       dev->sectorsize = 1UL << (erase & 0xFF);
+
+                       if ((offsetof(struct sfdp_basic_flash_param, chip_byte) >> 2) < words) {
+                               /* get Program Page Size, if chip_byte present, that's optional */
+                               dev->pagesize = 1UL << ((table->chip_byte >> 4) & 0x0F);
+                       } else {
+                               /* no explicit page size specified ... */
+                               if (table->fast_addr & (1UL << 2)) {
+                                       /* Write Granularity = 1, use 64 bytes */
+                                       dev->pagesize = 1UL << 6;
+                               } else {
+                                       /* Write Granularity = 0, use 16 bytes */
+                                       dev->pagesize = 1UL << 4;
+                               }
+                       }
+
+                       if (dev->size_in_bytes > (1UL << 24)) {
+                               if (((table->fast_addr >> 17) & 0x3) == 0x0)
+                                       LOG_ERROR("device needs paging - not implemented");
+
+                               /* 4-byte addresses needed if more than 16 MBytes */
+                               if (((offsetof(struct sfdp_basic_flash_param, addr_reset) >> 2) < words) &&
+                                       (table->addr_reset & (1UL << 29))) {
+                                       /* dedicated 4-byte-address instructions, hopefully these ...
+                                        * this entry is unfortunately optional as well
+                                        * a subsequent 4-byte address table may overwrite this */
+                                       dev->read_cmd = 0x13;
+                                       dev->pprog_cmd = 0x12;
+                                       dev->erase_cmd = 0xDC;
+                                       if (dev->qread_cmd != 0)
+                                               dev->qread_cmd = 0xEC;
+                               } else if (((table->fast_addr >> 17) & 0x3) == 0x1)
+                                       LOG_INFO("device has to be switched to 4-byte addresses");
+                       }
+               } else if (id == SFDP_4BYTE_ADDR) {
+                       struct sfdp_4byte_addr_param *table = (struct sfdp_4byte_addr_param *) ptable;
+
+                       if (words >= (offsetof(struct sfdp_4byte_addr_param, erase_t1234)
+                               + sizeof(table->erase_t1234)) >> 2) {
+                               LOG_INFO("4-byte address parameter table");
+
+                               /* read and page program instructions */
+                               if (table->flags & (1UL << 0))
+                                       dev->read_cmd = 0x13;
+                               if (table->flags & (1UL << 5))
+                                       dev->qread_cmd = 0xEC;
+                               if (table->flags & (1UL << 6))
+                                       dev->pprog_cmd = 0x12;
+
+                               /* erase instructions */
+                               if ((erase_type == 1) && (table->flags & (1UL << 9)))
+                                       dev->erase_cmd = (table->erase_t1234 >> 0) & 0xFF;
+                               else if ((erase_type == 2) && (table->flags & (1UL << 10)))
+                                       dev->erase_cmd = (table->erase_t1234 >> 8) & 0xFF;
+                               else if ((erase_type == 3) && (table->flags & (1UL << 11)))
+                                       dev->erase_cmd = (table->erase_t1234 >> 16) & 0xFF;
+                               else if ((erase_type == 4) && (table->flags & (1UL << 12)))
+                                       dev->erase_cmd = (table->erase_t1234 >> 24) & 0xFF;
+                       } else
+                               LOG_ERROR("parameter table id=0x%04" PRIx16 " invalid length %d", id, words);
+               } else
+                       LOG_DEBUG("unimplemented parameter table id=0x%04" PRIx16, id);
+
+               free(ptable);
+               ptable = NULL;
+       }
+
+       if (erase_type != 0) {
+               LOG_INFO("valid SFDP detected");
+               retval = ERROR_OK;
+       } else {
+               LOG_ERROR("incomplete/invalid SFDP");
+               retval = ERROR_FLASH_BANK_NOT_PROBED;
+       }
+
+err:
+       free(pheaders);
+       free(ptable);
+
+       return retval;
+}
diff --git a/src/flash/nor/sfdp.h b/src/flash/nor/sfdp.h
new file mode 100644 (file)
index 0000000..f924a4e
--- /dev/null
@@ -0,0 +1,34 @@
+/***************************************************************************
+ *   Copyright (C) 2019 by Andreas Bolsch <andreas.bolsch@mni.thm.de      *
+ *   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 <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#ifndef OPENOCD_FLASH_NOR_SFDP_H
+#define OPENOCD_FLASH_NOR_SFDP_H
+
+/* per JESD216D 'addr' is *byte* based but must be word aligned,
+ * 'buffer' is word based, word aligned and always little-endian encoded,
+ * in the flash, 'addr_len' is 3 or 4, 'dummy' ***usually*** 8
+ *
+ * the actual number of dummy clocks should be worked out by this function
+ * dynamically, i.e. by scanning the first few bytes for the SFDP signature
+ *
+ * buffer contents is supposed to be returned in ***host*** endianness */
+typedef int (*read_sfdp_block_t)(struct flash_bank *bank, uint32_t addr,
+       uint32_t words, uint32_t *buffer);
+
+extern int spi_sfdp(struct flash_bank *bank, struct flash_device *dev,
+       read_sfdp_block_t read_sfdp_block);
+
+#endif /* OPENOCD_FLASH_NOR_SFDP_H */
index 11c381fbd2ee7cbe9573c8a7c8c4d3937068ed7e..f8a0a65801e54b0c6e8c846c5085571c96b5ae56 100644 (file)
@@ -1,5 +1,5 @@
 /***************************************************************************
- *   Copyright (C) 2018 by Andreas Bolsch                                  *
+ *   Copyright (C) 2018-2019 by Andreas Bolsch                             *
  *   andreas.bolsch@mni.thm.de                                             *
  *                                                                         *
  *   Copyright (C) 2012 by George Harris                                   *
@@ -29,7 +29,7 @@
 
 /* data structure to maintain flash ids from different vendors */
 struct flash_device {
-       char *name;
+       const char *name;
        uint8_t read_cmd;
        uint8_t qread_cmd;
        uint8_t pprog_cmd;
@@ -87,6 +87,8 @@ extern const struct flash_device flash_devices[];
 #define SPIFLASH_PAGE_PROGRAM  0x02 /* Page Program */
 #define SPIFLASH_FAST_READ             0x0B /* Fast Read */
 #define SPIFLASH_READ                  0x03 /* Normal Read */
+#define SPIFLASH_MASS_ERASE            0xC7 /* Mass Erase */
+#define SPIFLASH_READ_SFDP             0x5A /* Read Serial Flash Discoverable Parameters */
 
 #define SPIFLASH_DEF_PAGESIZE  256  /* default for non-page-oriented devices (FRAMs) */
 
diff --git a/src/flash/nor/stmqspi.c b/src/flash/nor/stmqspi.c
new file mode 100644 (file)
index 0000000..486ee53
--- /dev/null
@@ -0,0 +1,2452 @@
+/***************************************************************************
+ *   Copyright (C) 2016 - 2019 by Andreas Bolsch                           *
+ *   andreas.bolsch@mni.thm.de                                             *
+ *                                                                         *
+ *   Copyright (C) 2010 by Antonio Borneo                                  *
+ *   borneo.antonio@gmail.com                                              *
+ *                                                                         *
+ *   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 <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+/* STM QuadSPI (QSPI) and OctoSPI (OCTOSPI) controller are SPI bus controllers
+ * specifically designed for SPI memories.
+ * Two working modes are available:
+ * - indirect mode: the SPI is controlled by SW. Any custom commands can be sent
+ *   on the bus.
+ * - memory mapped mode: the SPI is under QSPI/OCTOSPI control. Memory content
+ *   is directly accessible in CPU memory space. CPU can read and execute from
+ *   memory (but not write to) */
+
+/* ATTENTION:
+ * To have flash mapped in CPU memory space, the QSPI/OCTOSPI controller
+ * has to be in "memory mapped mode". This requires following constraints:
+ * 1) The command "reset init" has to initialize QSPI/OCTOSPI controller and put
+ *    it in memory mapped mode;
+ * 2) every command in this file has to return to prompt in memory mapped mode. */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "imp.h"
+#include <helper/time_support.h>
+#include <target/algorithm.h>
+#include <target/armv7m.h>
+#include <target/image.h>
+#include "stmqspi.h"
+#include "sfdp.h"
+
+/* deprecated */
+#undef SPIFLASH_READ
+#undef SPIFLASH_PAGE_PROGRAM
+
+#define READ_REG(a)                                                                                            \
+({                                                                                                                             \
+       uint32_t _result;                                                                                       \
+                                                                                                                               \
+       retval = target_read_u32(target, io_base + (a), &_result);      \
+       (retval == ERROR_OK) ? _result : 0x0;                                           \
+})
+
+/* saved mode settings */
+#define QSPI_MODE (stmqspi_info->saved_ccr & \
+       (0xF0000000U | QSPI_DCYC_MASK | QSPI_4LINE_MODE | QSPI_ALTB_MODE | QSPI_ADDR4))
+
+/* saved read mode settings but indirect read instead of memory mapped
+ * in particular, use the dummy cycle setting from this saved setting */
+#define        QSPI_CCR_READ (QSPI_READ_MODE | (stmqspi_info->saved_ccr & \
+       (0xF0000000U | QSPI_DCYC_MASK | QSPI_4LINE_MODE | QSPI_ALTB_MODE | QSPI_ADDR4 | 0xFF)))
+
+/* QSPI_CCR for various other commands, these never use dummy cycles nor alternate bytes */
+#define        QSPI_CCR_READ_STATUS \
+       ((QSPI_MODE & ~QSPI_DCYC_MASK & QSPI_NO_ADDR & QSPI_NO_ALTB) | \
+       (QSPI_READ_MODE | SPIFLASH_READ_STATUS))
+
+#define        QSPI_CCR_READ_ID \
+       ((QSPI_MODE & ~QSPI_DCYC_MASK & QSPI_NO_ADDR & QSPI_NO_ALTB) | \
+       (QSPI_READ_MODE | SPIFLASH_READ_ID))
+
+#define        QSPI_CCR_READ_MID \
+       ((QSPI_MODE & ~QSPI_DCYC_MASK & QSPI_NO_ADDR & QSPI_NO_ALTB) | \
+       (QSPI_READ_MODE | SPIFLASH_READ_MID))
+
+/* always use 3-byte addresses for read SFDP */
+#define        QSPI_CCR_READ_SFDP \
+       ((QSPI_MODE & ~QSPI_DCYC_MASK & ~QSPI_ADDR4 & QSPI_NO_ALTB) | \
+       (QSPI_READ_MODE | QSPI_ADDR3 | SPIFLASH_READ_SFDP))
+
+#define QSPI_CCR_WRITE_ENABLE \
+       ((QSPI_MODE & ~QSPI_DCYC_MASK & QSPI_NO_ADDR & QSPI_NO_ALTB & QSPI_NO_DATA) | \
+       (QSPI_WRITE_MODE | SPIFLASH_WRITE_ENABLE))
+
+#define QSPI_CCR_SECTOR_ERASE \
+       ((QSPI_MODE & ~QSPI_DCYC_MASK & QSPI_NO_ALTB & QSPI_NO_DATA) | \
+       (QSPI_WRITE_MODE | stmqspi_info->dev.erase_cmd))
+
+#define QSPI_CCR_MASS_ERASE \
+       ((QSPI_MODE & ~QSPI_DCYC_MASK & QSPI_NO_ADDR & QSPI_NO_ALTB & QSPI_NO_DATA) | \
+       (QSPI_WRITE_MODE | stmqspi_info->dev.chip_erase_cmd))
+
+#define QSPI_CCR_PAGE_PROG \
+       ((QSPI_MODE & ~QSPI_DCYC_MASK & QSPI_NO_ALTB) | \
+       (QSPI_WRITE_MODE | stmqspi_info->dev.pprog_cmd))
+
+/* saved mode settings */
+#define OCTOSPI_MODE (stmqspi_info->saved_cr & 0xCFFFFFFF)
+
+#define OPI_MODE ((stmqspi_info->saved_ccr & OCTOSPI_ISIZE_MASK) != 0)
+
+#define OCTOSPI_MODE_CCR (stmqspi_info->saved_ccr & \
+       (0xF0000000U | OCTOSPI_8LINE_MODE | OCTOSPI_ALTB_MODE | OCTOSPI_ADDR4))
+
+/* use saved ccr for read */
+#define OCTOSPI_CCR_READ OCTOSPI_MODE_CCR
+
+/* OCTOSPI_CCR for various other commands, these never use alternate bytes     *
+ * for READ_STATUS and READ_ID, 4-byte address 0                                                       *
+ * 4 dummy cycles must sent in OPI mode when DQS is disabled. However, when    *
+ * DQS is enabled, some STM32 devices need at least 6 dummy cycles for         *
+ * proper operation, but otherwise the actual number has no effect!                    *
+ * E.g. RM0432 Rev. 7 is incorrect regarding this: L4R9 works well with 4      *
+ * dummy clocks whereas L4P5 not at all.                                                                       *
+ */
+#define OPI_DUMMY \
+       ((stmqspi_info->saved_ccr & OCTOSPI_DQSEN) ? 6U : 4U)
+
+#define        OCTOSPI_CCR_READ_STATUS \
+       ((OCTOSPI_MODE_CCR & OCTOSPI_NO_DDTR & \
+       (OPI_MODE ? ~0U : OCTOSPI_NO_ADDR) & OCTOSPI_NO_ALTB))
+
+#define        OCTOSPI_CCR_READ_ID \
+       ((OCTOSPI_MODE_CCR & OCTOSPI_NO_DDTR & \
+       (OPI_MODE ? ~0U : OCTOSPI_NO_ADDR) & OCTOSPI_NO_ALTB))
+
+#define        OCTOSPI_CCR_READ_MID OCTOSPI_CCR_READ_ID
+
+/* 4-byte address in octo mode, else 3-byte address for read SFDP */
+#define        OCTOSPI_CCR_READ_SFDP(len) \
+       ((OCTOSPI_MODE_CCR & OCTOSPI_NO_DDTR & ~OCTOSPI_ADDR4 & OCTOSPI_NO_ALTB) | \
+       ((len < 4) ? OCTOSPI_ADDR3 : OCTOSPI_ADDR4))
+
+#define OCTOSPI_CCR_WRITE_ENABLE \
+       ((OCTOSPI_MODE_CCR & OCTOSPI_NO_ADDR & OCTOSPI_NO_ALTB & OCTOSPI_NO_DATA))
+
+#define OCTOSPI_CCR_SECTOR_ERASE \
+       ((OCTOSPI_MODE_CCR & OCTOSPI_NO_ALTB & OCTOSPI_NO_DATA))
+
+#define OCTOSPI_CCR_MASS_ERASE \
+       ((OCTOSPI_MODE_CCR & OCTOSPI_NO_ADDR & OCTOSPI_NO_ALTB & OCTOSPI_NO_DATA))
+
+#define OCTOSPI_CCR_PAGE_PROG \
+       ((OCTOSPI_MODE_CCR & QSPI_NO_ALTB))
+
+#define SPI_ADSIZE (((stmqspi_info->saved_ccr >> SPI_ADSIZE_POS) & 0x3) + 1)
+
+#define OPI_CMD(cmd) ((OPI_MODE ? ((((uint16_t) cmd)<<8) | (~cmd & 0xFFU)) : cmd))
+
+#define OCTOSPI_CMD(mode, ccr, ir)                                                                             \
+({                                                                                                                                             \
+       retval = target_write_u32(target, io_base + OCTOSPI_CR,                         \
+               OCTOSPI_MODE | mode);                                                                                   \
+       if (retval == ERROR_OK)                                                                                         \
+               retval = target_write_u32(target, io_base + OCTOSPI_TCR,                \
+                       (stmqspi_info->saved_tcr & ~OCTOSPI_DCYC_MASK) |                        \
+                       ((OPI_MODE && (mode == OCTOSPI_READ_MODE)) ?                            \
+                       (OPI_DUMMY<<OCTOSPI_DCYC_POS) : 0));                                            \
+       if (retval == ERROR_OK)                                                                                         \
+               retval = target_write_u32(target, io_base + OCTOSPI_CCR, ccr);  \
+       if (retval == ERROR_OK)                                                                                         \
+               retval = target_write_u32(target, io_base + OCTOSPI_IR,                 \
+                       OPI_CMD(ir));                                                                                           \
+       retval;                                                                                                                         \
+})
+
+/* convert uint32_t into 4 uint8_t in little endian byte order */
+static inline uint32_t h_to_le_32(uint32_t val)
+{
+       uint32_t result;
+
+       h_u32_to_le((uint8_t *) &result, val);
+       return result;
+}
+
+/* Timeout in ms */
+#define SPI_CMD_TIMEOUT                        (100)
+#define SPI_PROBE_TIMEOUT              (100)
+#define SPI_MAX_TIMEOUT                        (2000)
+#define SPI_MASS_ERASE_TIMEOUT (400000)
+
+struct sector_info {
+       uint32_t offset;
+       uint32_t size;
+       uint32_t result;
+};
+
+struct stmqspi_flash_bank {
+       bool probed;
+       char devname[32];
+       bool octo;
+       struct flash_device dev;
+       uint32_t io_base;
+       uint32_t saved_cr;      /* in particalar FSEL, DFM bit mask in QUADSPI_CR *AND* OCTOSPI_CR */
+       uint32_t saved_ccr; /* different meaning for QUADSPI and OCTOSPI */
+       uint32_t saved_tcr;     /* only for OCTOSPI */
+       uint32_t saved_ir;      /* only for OCTOSPI */
+       unsigned int sfdp_dummy1;       /* number of dummy bytes for SFDP read for flash1 and octo */
+       unsigned int sfdp_dummy2;       /* number of dummy bytes for SFDP read for flash2 */
+};
+
+FLASH_BANK_COMMAND_HANDLER(stmqspi_flash_bank_command)
+{
+       struct stmqspi_flash_bank *stmqspi_info;
+       uint32_t io_base;
+
+       LOG_DEBUG("%s", __func__);
+
+       if (CMD_ARGC < 7)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[6], io_base);
+
+       stmqspi_info = malloc(sizeof(struct stmqspi_flash_bank));
+       if (stmqspi_info == NULL) {
+               LOG_ERROR("not enough memory");
+               return ERROR_FAIL;
+       }
+
+       bank->driver_priv = stmqspi_info;
+       stmqspi_info->sfdp_dummy1 = 0;
+       stmqspi_info->sfdp_dummy2 = 0;
+       stmqspi_info->probed = false;
+       stmqspi_info->io_base = io_base;
+
+       return ERROR_OK;
+}
+
+/* Poll busy flag */
+/* timeout in ms */
+static int poll_busy(struct flash_bank *bank, int timeout)
+{
+       struct target *target = bank->target;
+       struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv;
+       uint32_t io_base = stmqspi_info->io_base;
+       uint32_t spi_sr;
+       int retval;
+       long long endtime;
+
+       endtime = timeval_ms() + timeout;
+       do {
+               spi_sr = READ_REG(SPI_SR);
+               if ((spi_sr & (1U<<SPI_BUSY)) == 0) {
+                       if (retval == ERROR_OK) {
+                               /* Clear transmit finished flag */
+                               retval = target_write_u32(target, io_base + SPI_FCR, (1U<<SPI_TCF));
+                       }
+                       return retval;
+               } else
+                       LOG_DEBUG("busy: 0x%08X", spi_sr);
+               alive_sleep(1);
+       } while (timeval_ms() < endtime);
+
+       LOG_ERROR("Timeout while polling BUSY");
+       return ERROR_FLASH_OPERATION_FAILED;
+}
+
+/* Set to memory-mapped mode, e.g. after an error */
+static int set_mm_mode(struct flash_bank *bank)
+{
+       struct target *target = bank->target;
+       struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv;
+       uint32_t io_base = stmqspi_info->io_base;
+       int retval;
+
+       /* Reset Address register bits 0 and 1, see various errata sheets */
+       retval = target_write_u32(target, io_base + SPI_AR, 0x0);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* Abort any previous operation */
+       retval = target_write_u32(target, io_base + SPI_CR,
+               READ_REG(SPI_CR) | (1U<<SPI_ABORT));
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* Wait for busy to be cleared */
+       retval = poll_busy(bank, SPI_PROBE_TIMEOUT);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* Finally switch to memory mapped mode */
+       if (IS_OCTOSPI) {
+               retval = target_write_u32(target, io_base + OCTOSPI_CR,
+                       OCTOSPI_MODE | OCTOSPI_MM_MODE);
+               if (retval == ERROR_OK)
+                       retval = target_write_u32(target, io_base + OCTOSPI_CCR,
+                               stmqspi_info->saved_ccr);
+               if (retval == ERROR_OK)
+                       retval = target_write_u32(target, io_base + OCTOSPI_TCR,
+                               stmqspi_info->saved_tcr);
+               if (retval == ERROR_OK)
+                       retval = target_write_u32(target, io_base + OCTOSPI_IR,
+                               stmqspi_info->saved_ir);
+       } else {
+               retval = target_write_u32(target, io_base + QSPI_CR,
+                       stmqspi_info->saved_cr);
+               if (retval == ERROR_OK)
+                       retval = target_write_u32(target, io_base + QSPI_CCR,
+                               stmqspi_info->saved_ccr);
+       }
+       return retval;
+}
+
+/* Read the status register of the external SPI flash chip(s). */
+static int read_status_reg(struct flash_bank *bank, uint16_t *status)
+{
+       struct target *target = bank->target;
+       struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv;
+       uint32_t io_base = stmqspi_info->io_base;
+       uint8_t data;
+       int count, retval;
+
+       /* Abort any previous operation */
+       retval = target_write_u32(target, io_base + SPI_CR,
+               READ_REG(SPI_CR) | (1U<<SPI_ABORT));
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* Wait for busy to be cleared */
+       retval = poll_busy(bank, SPI_PROBE_TIMEOUT);
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* Read always two (for DTR mode) bytes per chip */
+       count = 2;
+       retval = target_write_u32(target, io_base + SPI_DLR,
+               ((stmqspi_info->saved_cr & (1U<<SPI_DUAL_FLASH)) ? 2 * count : count) - 1);
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* Read status */
+       if (IS_OCTOSPI) {
+               retval = OCTOSPI_CMD(OCTOSPI_READ_MODE, OCTOSPI_CCR_READ_STATUS, SPIFLASH_READ_STATUS);
+               if (OPI_MODE) {
+                       /* Dummy address 0, only required for 8-line mode */
+                       retval = target_write_u32(target, io_base + SPI_AR, 0);
+                       if (retval != ERROR_OK)
+                               goto err;
+               }
+       } else
+               retval = target_write_u32(target, io_base + QSPI_CCR, QSPI_CCR_READ_STATUS);
+       if (retval != ERROR_OK)
+               goto err;
+
+       *status = 0;
+
+       /* for debugging only */
+       (void) READ_REG(SPI_SR);
+
+       for ( ; count > 0; --count) {
+               if ((stmqspi_info->saved_cr & ((1U<<SPI_DUAL_FLASH) | (1U<<SPI_FSEL_FLASH)))
+                       != (1U<<SPI_FSEL_FLASH)) {
+                       /* get status of flash 1 in dual mode or flash 1 only mode */
+                       retval = target_read_u8(target, io_base + SPI_DR, &data);
+                       if (retval != ERROR_OK)
+                               goto err;
+                       *status |= data;
+               }
+
+               if ((stmqspi_info->saved_cr & ((1U<<SPI_DUAL_FLASH) | (1U<<SPI_FSEL_FLASH)))
+                       != (0U<<SPI_FSEL_FLASH)) {
+                       /* get status of flash 2 in dual mode or flash 2 only mode */
+                       retval = target_read_u8(target, io_base + SPI_DR, &data);
+                       if (retval != ERROR_OK)
+                               goto err;
+                       *status |= ((uint16_t) data) << 8;
+               }
+       }
+
+       LOG_DEBUG("flash status regs: 0x%04" PRIx16, *status);
+
+err:
+       return retval;
+}
+
+/* check for WIP (write in progress) bit(s) in status register(s) */
+/* timeout in ms */
+static int wait_till_ready(struct flash_bank *bank, int timeout)
+{
+       uint16_t status;
+       int retval;
+       long long endtime;
+
+       endtime = timeval_ms() + timeout;
+       do {
+               /* Read flash status register(s) */
+               retval = read_status_reg(bank, &status);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               if ((status & ((SPIFLASH_BSY_BIT << 8) | SPIFLASH_BSY_BIT)) == 0)
+                       return retval;
+               alive_sleep(25);
+       } while (timeval_ms() < endtime);
+
+       LOG_ERROR("timeout");
+       return ERROR_FLASH_OPERATION_FAILED;
+}
+
+/* Send "write enable" command to SPI flash chip(s). */
+static int qspi_write_enable(struct flash_bank *bank)
+{
+       struct target *target = bank->target;
+       struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv;
+       uint32_t io_base = stmqspi_info->io_base;
+       uint16_t status;
+       int retval;
+
+       /* Abort any previous operation */
+       retval = target_write_u32(target, io_base + SPI_CR,
+               READ_REG(SPI_CR) | (1U<<SPI_ABORT));
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* Wait for busy to be cleared */
+       retval = poll_busy(bank, SPI_PROBE_TIMEOUT);
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* Send write enable command */
+       if (IS_OCTOSPI) {
+               retval = OCTOSPI_CMD(OCTOSPI_WRITE_MODE, OCTOSPI_CCR_WRITE_ENABLE, SPIFLASH_WRITE_ENABLE);
+               if (OPI_MODE) {
+                       /* Dummy address 0, only required for 8-line mode */
+                       retval = target_write_u32(target, io_base + SPI_AR, 0);
+                       if (retval != ERROR_OK)
+                               goto err;
+               }
+        } else
+               retval = target_write_u32(target, io_base + QSPI_CCR, QSPI_CCR_WRITE_ENABLE);
+       if (retval != ERROR_OK)
+               goto err;
+
+
+       /* Wait for transmit of command completed */
+       poll_busy(bank, SPI_CMD_TIMEOUT);
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* Read flash status register */
+       retval = read_status_reg(bank, &status);
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* Check write enabled for flash 1 */
+       if (((stmqspi_info->saved_cr & ((1U<<SPI_DUAL_FLASH) | (1U<<SPI_FSEL_FLASH)))
+               != (1U<<SPI_FSEL_FLASH)))
+               if ((status & (SPIFLASH_WE_BIT | SPIFLASH_BSY_BIT)) != SPIFLASH_WE_BIT) {
+                       LOG_ERROR("Cannot write enable flash1. Status=0x%02" PRIx8,
+                               status & 0xFF);
+                       return ERROR_FLASH_OPERATION_FAILED;
+               }
+
+       /* Check write enabled for flash 2 */
+       status >>= 8;
+       if (((stmqspi_info->saved_cr & ((1U<<SPI_DUAL_FLASH) | (1U<<SPI_FSEL_FLASH)))
+               != (0U<<SPI_FSEL_FLASH)))
+               if ((status & (SPIFLASH_WE_BIT | SPIFLASH_BSY_BIT)) != SPIFLASH_WE_BIT) {
+                       LOG_ERROR("Cannot write enable flash2. Status=0x%02" PRIx8,
+                               status & 0xFF);
+                       return ERROR_FLASH_OPERATION_FAILED;
+               }
+
+err:
+       return retval;
+}
+
+COMMAND_HANDLER(stmqspi_handle_mass_erase_command)
+{
+       struct target *target = NULL;
+       struct flash_bank *bank;
+       struct stmqspi_flash_bank *stmqspi_info;
+       struct duration bench;
+       uint32_t io_base;
+       uint16_t status;
+       unsigned int sector;
+       int retval;
+
+       LOG_DEBUG("%s", __func__);
+
+       if (CMD_ARGC != 1)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
+       if (ERROR_OK != retval)
+               return retval;
+
+       stmqspi_info = bank->driver_priv;
+       target = bank->target;
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       if (!(stmqspi_info->probed)) {
+               LOG_ERROR("Flash bank not probed");
+               return ERROR_FLASH_BANK_NOT_PROBED;
+       }
+
+       if (stmqspi_info->dev.chip_erase_cmd == 0x00) {
+               LOG_ERROR("Mass erase not available for this device");
+               return ERROR_FLASH_OPER_UNSUPPORTED;
+       }
+
+       for (sector = 0; sector < bank->num_sectors; sector++) {
+               if (bank->sectors[sector].is_protected) {
+                       LOG_ERROR("Flash sector %u protected", sector);
+                       return ERROR_FLASH_PROTECTED;
+               }
+       }
+
+       io_base = stmqspi_info->io_base;
+       duration_start(&bench);
+
+       retval = qspi_write_enable(bank);
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* Send Mass Erase command */
+       if (IS_OCTOSPI)
+               retval = OCTOSPI_CMD(OCTOSPI_WRITE_MODE, OCTOSPI_CCR_MASS_ERASE,
+                       stmqspi_info->dev.chip_erase_cmd);
+       else
+               retval = target_write_u32(target, io_base + QSPI_CCR, QSPI_CCR_MASS_ERASE);
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* Wait for transmit of command completed */
+       poll_busy(bank, SPI_CMD_TIMEOUT);
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* Read flash status register(s) */
+       retval = read_status_reg(bank, &status);
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* Check for command in progress for flash 1 */
+       if (((stmqspi_info->saved_cr & ((1U<<SPI_DUAL_FLASH) | (1U<<SPI_FSEL_FLASH)))
+               != (1U<<SPI_FSEL_FLASH)) && ((status & SPIFLASH_BSY_BIT) == 0) &&
+               ((status & SPIFLASH_WE_BIT) != 0)) {
+               LOG_ERROR("Mass erase command not accepted by flash1. Status=0x%02" PRIx8,
+                       status & 0xFF);
+               retval = ERROR_FLASH_OPERATION_FAILED;
+               goto err;
+       }
+
+       /* Check for command in progress for flash 2 */
+       status >>= 8;
+       if (((stmqspi_info->saved_cr & ((1U<<SPI_DUAL_FLASH) | (1U<<SPI_FSEL_FLASH)))
+               != (0U<<SPI_FSEL_FLASH)) && ((status & SPIFLASH_BSY_BIT) == 0) &&
+               ((status & SPIFLASH_WE_BIT) != 0)) {
+               LOG_ERROR("Mass erase command not accepted by flash2. Status=0x%02" PRIx8,
+                       status & 0xFF);
+               retval = ERROR_FLASH_OPERATION_FAILED;
+               goto err;
+       }
+
+       /* Poll WIP for end of self timed Sector Erase cycle */
+       retval = wait_till_ready(bank, SPI_MASS_ERASE_TIMEOUT);
+
+       duration_measure(&bench);
+       if (retval == ERROR_OK) {
+               /* set all sectors as erased */
+               for (sector = 0; sector < bank->num_sectors; sector++)
+                       bank->sectors[sector].is_erased = 1;
+
+               command_print(CMD, "stmqspi mass erase completed in %fs (%0.3f KiB/s)",
+                       duration_elapsed(&bench),
+                       duration_kbps(&bench, bank->size));
+       } else {
+               command_print(CMD, "stmqspi mass erase not completed even after %fs",
+                       duration_elapsed(&bench));
+       }
+
+err:
+       /* Switch to memory mapped mode before return to prompt */
+       set_mm_mode(bank);
+
+       return retval;
+}
+
+static int log2u(uint32_t word)
+{
+       int result;
+
+       for (result = 0; (unsigned int) result < sizeof(uint32_t) * CHAR_BIT; result++)
+               if (word == (1UL<<result))
+                       return result;
+
+       return -1;
+}
+
+COMMAND_HANDLER(stmqspi_handle_set)
+{
+       struct flash_bank *bank = NULL;
+       struct target *target = NULL;
+       struct stmqspi_flash_bank *stmqspi_info = NULL;
+       struct flash_sector *sectors = NULL;
+       uint32_t io_base;
+       unsigned int index = 0, dual, fsize;
+       int retval;
+
+       LOG_DEBUG("%s", __func__);
+
+       dual = (stmqspi_info->saved_cr & (1U<<SPI_DUAL_FLASH)) ? 1 : 0;
+
+       /* chip_erase_cmd, sectorsize and erase_cmd are optional */
+       if ((CMD_ARGC < 7) || (CMD_ARGC > 10))
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       retval = CALL_COMMAND_HANDLER(flash_command_get_bank, index++, &bank);
+       if (ERROR_OK != retval)
+               return retval;
+
+       target = bank->target;
+       stmqspi_info = bank->driver_priv;
+
+       /* invalidate all old info */
+       if (stmqspi_info->probed)
+               free(bank->sectors);
+       bank->size = 0;
+       bank->num_sectors = 0;
+       bank->sectors = NULL;
+       stmqspi_info->sfdp_dummy1 = 0;
+       stmqspi_info->sfdp_dummy2 = 0;
+       stmqspi_info->probed = false;
+       memset(&stmqspi_info->dev, 0, sizeof(stmqspi_info->dev));
+       stmqspi_info->dev.name = "unknown";
+
+       strncpy(stmqspi_info->devname, CMD_ARGV[index++], sizeof(stmqspi_info->devname) - 1);
+       stmqspi_info->devname[sizeof(stmqspi_info->devname) - 1] = '\0';
+
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[index++], stmqspi_info->dev.size_in_bytes);
+       if (log2u(stmqspi_info->dev.size_in_bytes) < 8) {
+               command_print(CMD, "stmqspi: device size must be 2^n with n >= 8");
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[index++], stmqspi_info->dev.pagesize);
+       if (stmqspi_info->dev.pagesize > stmqspi_info->dev.size_in_bytes ||
+               (log2u(stmqspi_info->dev.pagesize) < 0)) {
+               command_print(CMD, "stmqspi: page size must be 2^n and <= device size");
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       COMMAND_PARSE_NUMBER(u8, CMD_ARGV[index++], stmqspi_info->dev.read_cmd);
+       if ((stmqspi_info->dev.read_cmd != 0x03) &&
+               (stmqspi_info->dev.read_cmd != 0x13)) {
+               command_print(CMD, "stmqspi: only 0x03/0x13 READ cmd allowed");
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       COMMAND_PARSE_NUMBER(u8, CMD_ARGV[index++], stmqspi_info->dev.qread_cmd);
+       if ((stmqspi_info->dev.qread_cmd != 0x00) &&
+               (stmqspi_info->dev.qread_cmd != 0x0B) &&
+               (stmqspi_info->dev.qread_cmd != 0x0C) &&
+               (stmqspi_info->dev.qread_cmd != 0x3B) &&
+               (stmqspi_info->dev.qread_cmd != 0x3C) &&
+               (stmqspi_info->dev.qread_cmd != 0x6B) &&
+               (stmqspi_info->dev.qread_cmd != 0x6C) &&
+               (stmqspi_info->dev.qread_cmd != 0xBB) &&
+               (stmqspi_info->dev.qread_cmd != 0xBC) &&
+               (stmqspi_info->dev.qread_cmd != 0xEB) &&
+               (stmqspi_info->dev.qread_cmd != 0xEC) &&
+               (stmqspi_info->dev.qread_cmd != 0xEE)) {
+               command_print(CMD, "stmqspi: only 0x0B/0x0C/0x3B/0x3C/"
+                       "0x6B/0x6C/0xBB/0xBC/0xEB/0xEC/0xEE QREAD allowed");
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       COMMAND_PARSE_NUMBER(u8, CMD_ARGV[index++], stmqspi_info->dev.pprog_cmd);
+       if ((stmqspi_info->dev.pprog_cmd != 0x02) &&
+               (stmqspi_info->dev.pprog_cmd != 0x12) &&
+               (stmqspi_info->dev.pprog_cmd != 0x32)) {
+               command_print(CMD, "stmqspi: only 0x02/0x12/0x32 PPRG cmd allowed");
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       if (index < CMD_ARGC)
+               COMMAND_PARSE_NUMBER(u8, CMD_ARGV[index++], stmqspi_info->dev.chip_erase_cmd);
+       else
+               stmqspi_info->dev.chip_erase_cmd = 0x00;
+
+       if (index < CMD_ARGC) {
+               COMMAND_PARSE_NUMBER(u32, CMD_ARGV[index++], stmqspi_info->dev.sectorsize);
+               if ((stmqspi_info->dev.sectorsize > stmqspi_info->dev.size_in_bytes) ||
+                       (stmqspi_info->dev.sectorsize < stmqspi_info->dev.pagesize) ||
+                       (log2u(stmqspi_info->dev.sectorsize) < 0)) {
+                       command_print(CMD, "stmqspi: sector size must be 2^n and <= device size");
+                       return ERROR_COMMAND_SYNTAX_ERROR;
+               }
+
+               if (index < CMD_ARGC)
+                       COMMAND_PARSE_NUMBER(u8, CMD_ARGV[index++], stmqspi_info->dev.erase_cmd);
+               else
+                       return ERROR_COMMAND_SYNTAX_ERROR;
+       } else {
+               /* no sector size / sector erase cmd given, treat whole bank as a single sector */
+               stmqspi_info->dev.erase_cmd = 0x00;
+               stmqspi_info->dev.sectorsize = stmqspi_info->dev.size_in_bytes;
+       }
+
+       /* set correct size value */
+       bank->size = stmqspi_info->dev.size_in_bytes << dual;
+
+       io_base = stmqspi_info->io_base;
+       fsize = (READ_REG(SPI_DCR)>>SPI_FSIZE_POS) & ((1U<<SPI_FSIZE_LEN) - 1);
+       if (retval != ERROR_OK)
+               return retval;
+
+       LOG_DEBUG("FSIZE = 0x%04x", fsize);
+       if (bank->size == (1U<<(fsize + 1)))
+               LOG_DEBUG("FSIZE in DCR(1) matches actual capacity. Beware of silicon bug in H7, L4+, MP1.");
+       else if (bank->size == (1U<<(fsize + 0)))
+               LOG_DEBUG("FSIZE in DCR(1) is off by one regarding actual capacity. Fix for silicon bug?");
+       else
+               LOG_ERROR("FSIZE in DCR(1) doesn't match actual capacity.");
+
+       /* create and fill sectors array */
+       bank->num_sectors =
+               stmqspi_info->dev.size_in_bytes / stmqspi_info->dev.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 * (stmqspi_info->dev.sectorsize << dual);
+               sectors[sector].size = (stmqspi_info->dev.sectorsize << dual);
+               sectors[sector].is_erased = -1;
+               sectors[sector].is_protected = 0;
+       }
+
+       bank->sectors = sectors;
+       stmqspi_info->dev.name = stmqspi_info->devname;
+       if (stmqspi_info->dev.size_in_bytes / 4096)
+               LOG_INFO("flash \'%s\' id = unknown\nchip size = %" PRIu32 "kbytes,"
+                       " bank size = %" PRIu32 "kbytes", stmqspi_info->dev.name,
+                       stmqspi_info->dev.size_in_bytes / 1024,
+                       (stmqspi_info->dev.size_in_bytes / 1024)<<dual);
+       else
+               LOG_INFO("flash \'%s\' id = unknown\nchip size = %" PRIu32 "bytes,"
+                       " bank size = %" PRIu32 "bytes", stmqspi_info->dev.name,
+                       stmqspi_info->dev.size_in_bytes,
+                       stmqspi_info->dev.size_in_bytes<<dual);
+
+       stmqspi_info->probed = true;
+
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(stmqspi_handle_cmd)
+{
+       struct target *target = NULL;
+       struct flash_bank *bank;
+       struct stmqspi_flash_bank *stmqspi_info = NULL;
+       uint32_t io_base, addr;
+       uint8_t num_write, num_read, cmd_byte, data;
+       unsigned int count;
+       const int max = 21;
+       char temp[4], output[(2 + max + 256) * 3 + 8];
+       int retval;
+
+       LOG_DEBUG("%s", __func__);
+
+       if (CMD_ARGC < 3)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       num_write = CMD_ARGC - 2;
+       if (num_write > max) {
+               LOG_ERROR("at most %d bytes may be sent", max);
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
+       if (ERROR_OK != retval)
+               return retval;
+
+       target = bank->target;
+       stmqspi_info = bank->driver_priv;
+       io_base = stmqspi_info->io_base;
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       COMMAND_PARSE_NUMBER(u8, CMD_ARGV[1], num_read);
+       COMMAND_PARSE_NUMBER(u8, CMD_ARGV[2], cmd_byte);
+
+       if (num_read == 0) {
+               /* nothing to read, then one command byte and for dual flash
+                * an *even* number of data bytes to follow */
+               if (stmqspi_info->saved_cr & (1U<<SPI_DUAL_FLASH)) {
+                       if ((num_write & 1) == 0) {
+                               LOG_ERROR("number of data bytes to write must be even in dual mode");
+                               return ERROR_COMMAND_SYNTAX_ERROR;
+                       }
+               }
+       } else {
+               /* read mode, one command byte and up to four following address bytes */
+               if (stmqspi_info->saved_cr & (1U<<SPI_DUAL_FLASH)) {
+                       if ((num_read & 1) != 0) {
+                               LOG_ERROR("number of bytes to read must be even in dual mode");
+                               return ERROR_COMMAND_SYNTAX_ERROR;
+                       }
+               }
+               if ((num_write < 1) || (num_write > 5)) {
+                       LOG_ERROR("one cmd and up to four addr bytes must be send when reading");
+                       return ERROR_COMMAND_SYNTAX_ERROR;
+               }
+       }
+
+       /* Abort any previous operation */
+       retval = target_write_u32(target, io_base + SPI_CR,
+               READ_REG(SPI_CR) | (1U<<SPI_ABORT));
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* Wait for busy to be cleared */
+       retval = poll_busy(bank, SPI_PROBE_TIMEOUT);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* send command byte */
+       snprintf(output, sizeof(output), "spi: %02x ", cmd_byte);
+       if (num_read == 0) {
+               /* write, send cmd byte */
+               retval = target_write_u32(target, io_base + SPI_DLR, ((uint32_t) num_write) - 2);
+               if (retval != ERROR_OK)
+                       goto err;
+
+               if (IS_OCTOSPI)
+                       retval = OCTOSPI_CMD(OCTOSPI_WRITE_MODE,
+                               (OCTOSPI_MODE_CCR & OCTOSPI_NO_ALTB & OCTOSPI_NO_ADDR &
+                               ((num_write == 1) ? OCTOSPI_NO_DATA : ~0U)), cmd_byte);
+               else
+                       retval = target_write_u32(target, io_base + QSPI_CCR,
+                               (QSPI_MODE & ~QSPI_DCYC_MASK & QSPI_NO_ALTB & QSPI_NO_ADDR &
+                               ((num_write == 1) ? QSPI_NO_DATA : ~0U)) |
+                               (QSPI_WRITE_MODE | cmd_byte));
+               if (retval != ERROR_OK)
+                       goto err;
+
+               /* send additional data bytes */
+               for (count = 3; count < CMD_ARGC; count++) {
+                       COMMAND_PARSE_NUMBER(u8, CMD_ARGV[count], data);
+                       snprintf(temp, sizeof(temp), "%02" PRIx8 " ", data);
+                       retval = target_write_u8(target, io_base + SPI_DR, data); \
+                       if (retval != ERROR_OK)
+                               goto err;
+                       strncat(output, temp, sizeof(output) - strlen(output) - 1);
+               }
+               strncat(output, "-> ", sizeof(output) - strlen(output) - 1);
+       } else {
+               /* read, pack additional bytes into address */
+               addr = 0;
+               for (count = 3; count < CMD_ARGC; count++) {
+                       COMMAND_PARSE_NUMBER(u8, CMD_ARGV[count], data);
+                       snprintf(temp, sizeof(temp), "%02" PRIx8 " ", data);
+                       addr = (addr << 8) | data;
+                       strncat(output, temp, sizeof(output) - strlen(output) - 1);
+               }
+               strncat(output, "-> ", sizeof(output) - strlen(output) - 1);
+
+               /* send cmd byte, if ADMODE indicates no address, this already triggers command */
+               retval = target_write_u32(target, io_base + SPI_DLR, ((uint32_t) num_read) - 1);
+               if (retval != ERROR_OK)
+                       goto err;
+               if (IS_OCTOSPI)
+                       retval = OCTOSPI_CMD(OCTOSPI_READ_MODE,
+                               (OCTOSPI_MODE_CCR & OCTOSPI_NO_DDTR & OCTOSPI_NO_ALTB & ~OCTOSPI_ADDR4 &
+                                       ((num_write == 1) ? OCTOSPI_NO_ADDR : ~0U)) |
+                               (((num_write - 2) & 0x3U)<<SPI_ADSIZE_POS), cmd_byte);
+               else
+                       retval = target_write_u32(target, io_base + QSPI_CCR,
+                               (QSPI_MODE & ~QSPI_DCYC_MASK & QSPI_NO_ALTB & ~QSPI_ADDR4 &
+                                       ((num_write == 1) ? QSPI_NO_ADDR : ~0U)) |
+                               ((QSPI_READ_MODE | (((num_write - 2) & 0x3U)<<SPI_ADSIZE_POS) | cmd_byte)));
+               if (retval != ERROR_OK)
+                       goto err;
+
+               if (num_write > 1) {
+                       /* if ADMODE indicates address required, only the write to AR triggers command */
+                       retval = target_write_u32(target, io_base + SPI_AR, addr);
+                       if (retval != ERROR_OK)
+                               goto err;
+               }
+
+               /* read response bytes */
+               for ( ; num_read > 0; num_read--) {
+                       retval = target_read_u8(target, io_base + SPI_DR, &data);
+                       if (retval != ERROR_OK)
+                               goto err;
+                       snprintf(temp, sizeof(temp), "%02" PRIx8 " ", data);
+                       strncat(output, temp, sizeof(output) - strlen(output) - 1);
+               }
+       }
+       command_print(CMD, "%s", output);
+
+err:
+       /* Switch to memory mapped mode before return to prompt */
+       set_mm_mode(bank);
+
+       return retval;
+}
+
+static int qspi_erase_sector(struct flash_bank *bank, unsigned int sector)
+{
+       struct target *target = bank->target;
+       struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv;
+       uint32_t io_base = stmqspi_info->io_base;
+       uint16_t status;
+       int retval;
+
+       retval = qspi_write_enable(bank);
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* Send Sector Erase command */
+       if (IS_OCTOSPI)
+               retval = OCTOSPI_CMD(OCTOSPI_WRITE_MODE, OCTOSPI_CCR_SECTOR_ERASE,
+                       stmqspi_info->dev.erase_cmd);
+       else
+               retval = target_write_u32(target, io_base + QSPI_CCR, QSPI_CCR_SECTOR_ERASE);
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* Address is sector offset, this write initiates command transmission */
+       retval = target_write_u32(target, io_base + SPI_AR, bank->sectors[sector].offset);
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* Wait for transmit of command completed */
+       poll_busy(bank, SPI_CMD_TIMEOUT);
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* Read flash status register(s) */
+       retval = read_status_reg(bank, &status);
+       if (retval != ERROR_OK)
+               goto err;
+
+       LOG_DEBUG("erase status regs: 0x%04" PRIx16, status);
+
+       /* Check for command in progress for flash 1 */
+       /* If BSY and WE are already cleared the erase did probably complete already */
+       if (((stmqspi_info->saved_cr & ((1U<<SPI_DUAL_FLASH) | (1U<<SPI_FSEL_FLASH)))
+               != (1U<<SPI_FSEL_FLASH)) && ((status & SPIFLASH_BSY_BIT) == 0) &&
+               ((status & SPIFLASH_WE_BIT) != 0)) {
+               LOG_ERROR("Sector erase command not accepted by flash1. Status=0x%02" PRIx8,
+                       status & 0xFF);
+               retval = ERROR_FLASH_OPERATION_FAILED;
+               goto err;
+       }
+
+       /* Check for command in progress for flash 2 */
+       /* If BSY and WE are already cleared the erase did probably complete already */
+       status >>= 8;
+       if (((stmqspi_info->saved_cr & ((1U<<SPI_DUAL_FLASH) | (1U<<SPI_FSEL_FLASH)))
+               != (0U<<SPI_FSEL_FLASH)) && ((status & SPIFLASH_BSY_BIT) == 0) &&
+               ((status & SPIFLASH_WE_BIT) != 0)) {
+               LOG_ERROR("Sector erase command not accepted by flash2. Status=0x%02" PRIx8,
+                       status & 0xFF);
+               retval = ERROR_FLASH_OPERATION_FAILED;
+               goto err;
+       }
+
+       /* Erase takes a long time, so some sort of progress message is a good idea */
+       LOG_DEBUG("erasing sector %4u", sector);
+
+       /* Poll WIP for end of self timed Sector Erase cycle */
+       retval = wait_till_ready(bank, SPI_MAX_TIMEOUT);
+
+err:
+       return retval;
+}
+
+static int stmqspi_erase(struct flash_bank *bank, unsigned int first, unsigned int last)
+{
+       struct target *target = bank->target;
+       struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv;
+       unsigned int sector;
+       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 (!(stmqspi_info->probed)) {
+               LOG_ERROR("Flash bank not probed");
+               return ERROR_FLASH_BANK_NOT_PROBED;
+       }
+
+       if (stmqspi_info->dev.erase_cmd == 0x00) {
+               LOG_ERROR("Sector erase not available for this device");
+               return ERROR_FLASH_OPER_UNSUPPORTED;
+       }
+
+       if ((last < first) || (last >= bank->num_sectors)) {
+               LOG_ERROR("Flash sector invalid");
+               return ERROR_FLASH_SECTOR_INVALID;
+       }
+
+       for (sector = first; sector <= last; sector++) {
+               if (bank->sectors[sector].is_protected) {
+                       LOG_ERROR("Flash sector %u protected", sector);
+                       return ERROR_FLASH_PROTECTED;
+               }
+       }
+
+       for (sector = first; sector <= last; sector++) {
+               retval = qspi_erase_sector(bank, sector);
+               if (retval != ERROR_OK)
+                       break;
+               alive_sleep(10);
+               keep_alive();
+       }
+
+       if (retval != ERROR_OK)
+               LOG_ERROR("Flash sector_erase failed on sector %u", sector);
+
+       /* Switch to memory mapped mode before return to prompt */
+       set_mm_mode(bank);
+
+       return retval;
+}
+
+static int stmqspi_protect(struct flash_bank *bank, int set,
+       unsigned int first, unsigned int last)
+{
+       unsigned int sector;
+
+       for (sector = first; sector <= last; sector++)
+               bank->sectors[sector].is_protected = set;
+
+       if (set)
+               LOG_WARNING("setting soft protection only, not related to flash's hardware write protection");
+
+       return ERROR_OK;
+}
+
+/* Check whether flash is blank */
+static int stmqspi_blank_check(struct flash_bank *bank)
+{
+       struct target *target = bank->target;
+       struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv;
+       uint32_t io_base = stmqspi_info->io_base;
+       struct duration bench;
+       struct reg_param reg_params[2];
+       struct armv7m_algorithm armv7m_info;
+       struct working_area *algorithm;
+       const uint8_t *code;
+       struct sector_info erase_check_info;
+       uint32_t codesize, maxsize, result, exit_point;
+       unsigned int count, index, num_sectors, sector;
+       int retval;
+       const uint32_t erased = 0x00FF;
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       if (!(stmqspi_info->probed)) {
+               LOG_ERROR("Flash bank not probed");
+               return ERROR_FLASH_BANK_NOT_PROBED;
+       }
+
+       /* Abort any previous operation */
+       retval = target_write_u32(target, io_base + SPI_CR,
+               READ_REG(SPI_CR) | (1U<<SPI_ABORT));
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* Wait for busy to be cleared */
+       retval = poll_busy(bank, SPI_PROBE_TIMEOUT);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* see contrib/loaders/flash/stmqspi/stmqspi_erase_check.S for src */
+       static const uint8_t stmqspi_erase_check_code[] = {
+               #include "../../../contrib/loaders/flash/stmqspi/stmqspi_erase_check.inc"
+       };
+
+       /* see contrib/loaders/flash/stmqspi/stmoctospi_erase_check.S for src */
+       static const uint8_t stmoctospi_erase_check_code[] = {
+               #include "../../../contrib/loaders/flash/stmqspi/stmoctospi_erase_check.inc"
+       };
+
+       if (IS_OCTOSPI) {
+               code = stmoctospi_erase_check_code;
+               codesize = sizeof(stmoctospi_erase_check_code);
+       } else {
+               code = stmqspi_erase_check_code;
+               codesize = sizeof(stmqspi_erase_check_code);
+       }
+
+       /* This will overlay the last 4 words of stmqspi/stmoctospi_erase_check_code in target */
+       /* for read use the saved settings (memory mapped mode) but indirect read mode */
+       uint32_t ccr_buffer[][4] = {
+               /* cr  (not used for QSPI)                      *
+                * ccr (for both QSPI and OCTOSPI)      *
+                * tcr (not used for QSPI)                      *
+                * ir  (not used for QSPI)                      */
+               {
+                       h_to_le_32(OCTOSPI_MODE | OCTOSPI_READ_MODE),
+                       h_to_le_32(IS_OCTOSPI ? OCTOSPI_CCR_READ : QSPI_CCR_READ),
+                       h_to_le_32(stmqspi_info->saved_tcr),
+                       h_to_le_32(stmqspi_info->saved_ir),
+               },
+       };
+
+       maxsize = target_get_working_area_avail(target);
+       if (maxsize < codesize + sizeof(erase_check_info)) {
+               LOG_ERROR("Not enough working area, can't do QSPI blank check");
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+       }
+
+       num_sectors = (maxsize - codesize) / sizeof(erase_check_info);
+       num_sectors = (bank->num_sectors < num_sectors) ? bank->num_sectors : num_sectors;
+
+       if (target_alloc_working_area_try(target,
+                       codesize + num_sectors * sizeof(erase_check_info), &algorithm) != ERROR_OK) {
+               LOG_ERROR("allocating working area failed");
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+       };
+
+       /* prepare blank check code, excluding ccr_buffer */
+       retval = target_write_buffer(target, algorithm->address,
+               codesize - sizeof(ccr_buffer), code);
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* prepare QSPI/OCTOSPI_CCR register values */
+       retval = target_write_buffer(target, algorithm->address
+               + codesize - sizeof(ccr_buffer),
+               sizeof(ccr_buffer), (uint8_t *) ccr_buffer);
+       if (retval != ERROR_OK)
+               goto err;
+
+       duration_start(&bench);
+
+       /* after breakpoint instruction (halfword), one nop (halfword) and
+        * port_buffer till end of code */
+       exit_point = algorithm->address + codesize - sizeof(uint32_t) - sizeof(ccr_buffer);
+
+       init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT);    /* sector count */
+       init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);    /* QSPI/OCTOSPI io_base */
+
+       sector = 0;
+       while (sector < bank->num_sectors) {
+               /* at most num_sectors sectors to handle in one run */
+               count = bank->num_sectors - sector;
+               if (count > num_sectors)
+                       count = num_sectors;
+
+               for (index = 0; index < count; index++) {
+                       erase_check_info.offset = h_to_le_32(bank->sectors[sector + index].offset);
+                       erase_check_info.size = h_to_le_32(bank->sectors[sector + index].size);
+                       erase_check_info.result = h_to_le_32(erased);
+
+                       retval = target_write_buffer(target, algorithm->address
+                               + codesize + index * sizeof(erase_check_info),
+                                       sizeof(erase_check_info), (uint8_t *) &erase_check_info);
+                       if (retval != ERROR_OK)
+                               goto err;
+               }
+
+               buf_set_u32(reg_params[0].value, 0, 32, count);
+               buf_set_u32(reg_params[1].value, 0, 32, stmqspi_info->io_base);
+
+               armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
+               armv7m_info.core_mode = ARM_MODE_THREAD;
+
+               LOG_DEBUG("checking sectors %u to %u", sector, sector + count - 1);
+               /* check a block of sectors */
+               retval = target_run_algorithm(target,
+                       0, NULL,
+                       ARRAY_SIZE(reg_params), reg_params,
+                       algorithm->address, exit_point,
+                       count * ((bank->sectors[sector].size >> 6) + 1) + 1000,
+                       &armv7m_info);
+               if (retval != ERROR_OK)
+                       break;
+
+               for (index = 0; index < count; index++) {
+                       retval = target_read_buffer(target, algorithm->address
+                               + codesize + index * sizeof(erase_check_info),
+                                       sizeof(erase_check_info), (uint8_t *) &erase_check_info);
+                       if (retval != ERROR_OK)
+                               goto err;
+
+                       if ((erase_check_info.offset != h_to_le_32(bank->sectors[sector + index].offset)) ||
+                               (erase_check_info.size != 0)) {
+                               LOG_ERROR("corrupted blank check info");
+                               goto err;
+                       }
+
+                       /* we need le_32_to_h, but that's the same as h_to_le_32 */
+                       result = h_to_le_32(erase_check_info.result);
+                       bank->sectors[sector + index].is_erased = ((result & 0xFF) == 0xFF);
+                       LOG_DEBUG("Flash sector %u checked: 0x%04" PRIx16, sector + index, result & 0xFFFF);
+               }
+               keep_alive();
+               sector += count;
+       }
+
+       destroy_reg_param(&reg_params[0]);
+       destroy_reg_param(&reg_params[1]);
+
+       duration_measure(&bench);
+       LOG_INFO("stmqspi blank checked in %fs (%0.3f KiB/s)", duration_elapsed(&bench),
+               duration_kbps(&bench, bank->size));
+
+err:
+       target_free_working_area(target, algorithm);
+
+       /* Switch to memory mapped mode before return to prompt */
+       set_mm_mode(bank);
+
+       return retval;
+}
+
+/* Verify checksum */
+static int qspi_verify(struct flash_bank *bank, uint8_t *buffer,
+       uint32_t offset, uint32_t count)
+{
+       struct target *target = bank->target;
+       struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv;
+       struct reg_param reg_params[4];
+       struct armv7m_algorithm armv7m_info;
+       struct working_area *algorithm;
+       const uint8_t *code;
+       uint32_t pagesize, codesize, crc32, result, exit_point;
+       int retval;
+
+       /* see contrib/loaders/flash/stmqspi/stmqspi_crc32.S for src */
+       static const uint8_t stmqspi_crc32_code[] = {
+               #include "../../../contrib/loaders/flash/stmqspi/stmqspi_crc32.inc"
+       };
+
+       /* see contrib/loaders/flash/stmqspi/stmoctospi_crc32.S for src */
+       static const uint8_t stmoctospi_crc32_code[] = {
+               #include "../../../contrib/loaders/flash/stmqspi/stmoctospi_crc32.inc"
+       };
+
+       if (IS_OCTOSPI) {
+               code = stmoctospi_crc32_code;
+               codesize = sizeof(stmoctospi_crc32_code);
+       } else {
+               code = stmqspi_crc32_code;
+               codesize = sizeof(stmqspi_crc32_code);
+       }
+
+       /* block size doesn't matter that much here */
+       pagesize = stmqspi_info->dev.sectorsize;
+       if (pagesize == 0)
+               pagesize = stmqspi_info->dev.pagesize;
+       if (pagesize == 0)
+               pagesize = SPIFLASH_DEF_PAGESIZE;
+
+       /* adjust size according to dual flash mode */
+       pagesize = (stmqspi_info->saved_cr & (1U<<SPI_DUAL_FLASH)) ? pagesize << 1 : pagesize;
+
+       /* This will overlay the last 4 words of stmqspi/stmoctospi_crc32_code in target */
+       /* for read use the saved settings (memory mapped mode) but indirect read mode */
+       uint32_t ccr_buffer[][4] = {
+               /* cr  (not used for QSPI)                      *
+                * ccr (for both QSPI and OCTOSPI)      *
+                * tcr (not used for QSPI)                      *
+                * ir  (not used for QSPI)                      */
+               {
+                       h_to_le_32(OCTOSPI_MODE | OCTOSPI_READ_MODE),
+                       h_to_le_32(IS_OCTOSPI ? OCTOSPI_CCR_READ : QSPI_CCR_READ),
+                       h_to_le_32(stmqspi_info->saved_tcr),
+                       h_to_le_32(stmqspi_info->saved_ir),
+               },
+       };
+
+       if (target_alloc_working_area_try(target, codesize, &algorithm) != ERROR_OK) {
+               LOG_ERROR("Not enough working area, can't do QSPI verify");
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+       };
+
+       /* prepare verify code, excluding ccr_buffer */
+       retval = target_write_buffer(target, algorithm->address,
+               codesize - sizeof(ccr_buffer), code);
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* prepare QSPI/OCTOSPI_CCR register values */
+       retval = target_write_buffer(target, algorithm->address
+               + codesize - sizeof(ccr_buffer),
+               sizeof(ccr_buffer), (uint8_t *) ccr_buffer);
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* after breakpoint instruction (halfword), one nop (halfword) and
+        * port_buffer till end of code */
+       exit_point = algorithm->address + codesize - sizeof(uint32_t) - sizeof(ccr_buffer);
+
+       init_reg_param(&reg_params[0], "r0", 32, PARAM_IN_OUT); /* count (in), crc32 (out) */
+       init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);    /* pagesize */
+       init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT);    /* offset into flash address */
+       init_reg_param(&reg_params[3], "r3", 32, PARAM_OUT);    /* QSPI/OCTOSPI io_base */
+
+       buf_set_u32(reg_params[0].value, 0, 32, count);
+       buf_set_u32(reg_params[1].value, 0, 32, pagesize);
+       buf_set_u32(reg_params[2].value, 0, 32, offset);
+       buf_set_u32(reg_params[3].value, 0, 32, stmqspi_info->io_base);
+
+
+       armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
+       armv7m_info.core_mode = ARM_MODE_THREAD;
+
+       retval = target_run_algorithm(target,
+               0, NULL,
+               ARRAY_SIZE(reg_params), reg_params,
+               algorithm->address, exit_point,
+               (count >> 5) + 1000,
+               &armv7m_info);
+       keep_alive();
+
+       image_calculate_checksum(buffer, count, &crc32);
+
+       if (retval == ERROR_OK) {
+               result = buf_get_u32(reg_params[0].value, 0, 32);
+               LOG_DEBUG("addr " TARGET_ADDR_FMT ", len 0x%08" PRIx32 ", crc 0x%08" PRIx32 " 0x%08" PRIx32,
+                       offset + bank->base, count, ~crc32, result);
+               if (~crc32 != result)
+                       retval = ERROR_FAIL;
+       }
+
+       destroy_reg_param(&reg_params[0]);
+       destroy_reg_param(&reg_params[1]);
+       destroy_reg_param(&reg_params[2]);
+       destroy_reg_param(&reg_params[3]);
+
+err:
+       target_free_working_area(target, algorithm);
+
+       /* Switch to memory mapped mode before return to prompt */
+       set_mm_mode(bank);
+
+       return retval;
+}
+
+static int qspi_read_write_block(struct flash_bank *bank, uint8_t *buffer,
+       uint32_t offset, uint32_t count, bool write)
+{
+       struct target *target = bank->target;
+       struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv;
+       uint32_t io_base = stmqspi_info->io_base;
+       struct reg_param reg_params[6];
+       struct armv7m_algorithm armv7m_info;
+       struct working_area *algorithm;
+       uint32_t pagesize, fifo_start, fifosize, remaining;
+       uint32_t maxsize, codesize, exit_point;
+       const uint8_t *code = NULL;
+       unsigned int dual;
+       int retval;
+
+       LOG_DEBUG("%s: offset=0x%08" PRIx32 " len=0x%08" PRIx32,
+               __func__, offset, count);
+
+       dual = (stmqspi_info->saved_cr & (1U<<SPI_DUAL_FLASH)) ? 1 : 0;
+
+       /* see contrib/loaders/flash/stmqspi/stmqspi_read.S for src */
+       static const uint8_t stmqspi_read_code[] = {
+#include "../../../contrib/loaders/flash/stmqspi/stmqspi_read.inc"
+       };
+
+       /* see contrib/loaders/flash/stmqspi/stmoctospi_read.S for src */
+       static const uint8_t stmoctospi_read_code[] = {
+#include "../../../contrib/loaders/flash/stmqspi/stmoctospi_read.inc"
+       };
+
+       /* see contrib/loaders/flash/stmqspi/stmqspi_write.S for src */
+       static const uint8_t stmqspi_write_code[] = {
+#include "../../../contrib/loaders/flash/stmqspi/stmqspi_write.inc"
+       };
+
+       /* see contrib/loaders/flash/stmqspi/stmoctospi_write.S for src */
+       static const uint8_t stmoctospi_write_code[] = {
+#include "../../../contrib/loaders/flash/stmqspi/stmoctospi_write.inc"
+       };
+
+       /* This will overlay the last 12 words of stmqspi/stmoctospi_read/write_code in target */
+       /* for read use the saved settings (memory mapped mode) but indirect read mode */
+       uint32_t ccr_buffer[][4] = {
+               /* cr  (not used for QSPI)                      *
+                * ccr (for both QSPI and OCTOSPI)      *
+                * tcr (not used for QSPI)                      *
+                * ir  (not used for QSPI)                      */
+               {
+                       h_to_le_32(OCTOSPI_MODE | OCTOSPI_READ_MODE),
+                       h_to_le_32(IS_OCTOSPI ? OCTOSPI_CCR_READ_STATUS : QSPI_CCR_READ_STATUS),
+                       h_to_le_32((stmqspi_info->saved_tcr & ~OCTOSPI_DCYC_MASK) |
+                                               (OPI_MODE ? (OPI_DUMMY<<OCTOSPI_DCYC_POS) : 0)),
+                       h_to_le_32(OPI_CMD(SPIFLASH_READ_STATUS)),
+               },
+               {
+                       h_to_le_32(OCTOSPI_MODE | OCTOSPI_WRITE_MODE),
+                       h_to_le_32(IS_OCTOSPI ? OCTOSPI_CCR_WRITE_ENABLE : QSPI_CCR_WRITE_ENABLE),
+                       h_to_le_32(stmqspi_info->saved_tcr & ~OCTOSPI_DCYC_MASK),
+                       h_to_le_32(OPI_CMD(SPIFLASH_WRITE_ENABLE)),
+               },
+               {
+                       h_to_le_32(OCTOSPI_MODE | (write ? OCTOSPI_WRITE_MODE : OCTOSPI_READ_MODE)),
+                       h_to_le_32(write ? (IS_OCTOSPI ? OCTOSPI_CCR_PAGE_PROG : QSPI_CCR_PAGE_PROG) :
+                               (IS_OCTOSPI ? OCTOSPI_CCR_READ : QSPI_CCR_READ)),
+                       h_to_le_32(write ? (stmqspi_info->saved_tcr & ~OCTOSPI_DCYC_MASK) :
+                               stmqspi_info->saved_tcr),
+                       h_to_le_32(write ? OPI_CMD(stmqspi_info->dev.pprog_cmd) : stmqspi_info->saved_ir),
+               },
+       };
+
+       /* force reasonable defaults */
+       fifosize = stmqspi_info->dev.sectorsize ?
+               stmqspi_info->dev.sectorsize : stmqspi_info->dev.size_in_bytes;
+
+       if (write) {
+               if (IS_OCTOSPI) {
+                       code = stmoctospi_write_code;
+                       codesize = sizeof(stmoctospi_write_code);
+               } else {
+                       code = stmqspi_write_code;
+                       codesize = sizeof(stmqspi_write_code);
+               }
+       } else {
+               if (IS_OCTOSPI) {
+                       code = stmoctospi_read_code;
+                       codesize = sizeof(stmoctospi_read_code);
+               } else {
+                       code = stmqspi_read_code;
+                       codesize = sizeof(stmqspi_read_code);
+               }
+       }
+
+       /* for write, pagesize must be taken into account */
+       /* for read, the page size doesn't matter that much */
+       pagesize = stmqspi_info->dev.pagesize;
+       if (pagesize == 0)
+               pagesize = (fifosize <= SPIFLASH_DEF_PAGESIZE) ?
+                       fifosize : SPIFLASH_DEF_PAGESIZE;
+
+       /* adjust sizes according to dual flash mode */
+       pagesize <<= dual;
+       fifosize <<= dual;
+
+       /* memory buffer, we assume sectorsize to be a power of 2 times pagesize */
+       maxsize = target_get_working_area_avail(target);
+       if (maxsize < codesize + 2 * sizeof(uint32_t) + pagesize) {
+               LOG_ERROR("not enough working area, can't do QSPI page reads/writes");
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+       }
+
+       /* fifo size at most sector size, and multiple of page size */
+       maxsize -= (codesize + 2 * sizeof(uint32_t));
+       fifosize = ((maxsize < fifosize) ? maxsize : fifosize) & ~(pagesize - 1);
+
+       if (target_alloc_working_area_try(target,
+               codesize + 2 * sizeof(uint32_t) + fifosize, &algorithm) != ERROR_OK) {
+               LOG_ERROR("allocating working area failed");
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+       };
+
+       /* prepare flash write code, excluding ccr_buffer */
+       retval = target_write_buffer(target, algorithm->address,
+               codesize - sizeof(ccr_buffer), code);
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* prepare QSPI/OCTOSPI_CCR register values */
+       retval = target_write_buffer(target, algorithm->address
+               + codesize - sizeof(ccr_buffer),
+               sizeof(ccr_buffer), (uint8_t *) ccr_buffer);
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* target buffer starts right after flash_write_code, i.e.
+        * wp and rp are implicitly included in buffer!!! */
+       fifo_start = algorithm->address + codesize + 2 * sizeof(uint32_t);
+
+       init_reg_param(&reg_params[0], "r0", 32, PARAM_IN_OUT); /* count (in), status (out) */
+       init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);    /* pagesize */
+       init_reg_param(&reg_params[2], "r2", 32, PARAM_IN_OUT); /* offset into flash address */
+       init_reg_param(&reg_params[3], "r3", 32, PARAM_OUT);    /* QSPI/OCTOSPI io_base */
+       init_reg_param(&reg_params[4], "r8", 32, PARAM_OUT);    /* fifo start */
+       init_reg_param(&reg_params[5], "r9", 32, PARAM_OUT);    /* fifo end + 1 */
+
+       buf_set_u32(reg_params[0].value, 0, 32, count);
+       buf_set_u32(reg_params[1].value, 0, 32, pagesize);
+       buf_set_u32(reg_params[2].value, 0, 32, offset);
+       buf_set_u32(reg_params[3].value, 0, 32, io_base);
+       buf_set_u32(reg_params[4].value, 0, 32, fifo_start);
+       buf_set_u32(reg_params[5].value, 0, 32, fifo_start + fifosize);
+
+       armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
+       armv7m_info.core_mode = ARM_MODE_THREAD;
+
+       /* after breakpoint instruction (halfword), one nop (halfword) and
+        * ccr_buffer follow till end of code */
+       exit_point = algorithm->address + codesize
+               - (sizeof(ccr_buffer) + sizeof(uint32_t));
+
+       if (write) {
+               retval = target_run_flash_async_algorithm(target, buffer, count, 1,
+                               0, NULL,
+                               ARRAY_SIZE(reg_params), reg_params,
+                               algorithm->address + codesize,
+                               fifosize + 2 * sizeof(uint32_t),
+                               algorithm->address, exit_point,
+                               &armv7m_info);
+       } else {
+               retval = target_run_read_async_algorithm(target, buffer, count, 1,
+                               0, NULL,
+                               ARRAY_SIZE(reg_params), reg_params,
+                               algorithm->address + codesize,
+                               fifosize + 2 * sizeof(uint32_t),
+                               algorithm->address, exit_point,
+                               &armv7m_info);
+       }
+
+       remaining = buf_get_u32(reg_params[0].value, 0, 32);
+       if ((retval == ERROR_OK) && remaining)
+               retval = ERROR_FLASH_OPERATION_FAILED;
+
+       if (retval != ERROR_OK) {
+               offset = buf_get_u32(reg_params[2].value, 0, 32);
+               LOG_ERROR("flash %s failed at address 0x%" PRIx32 ", remaining 0x%" PRIx32,
+                       write ? "write" : "read", offset, remaining);
+       }
+
+       destroy_reg_param(&reg_params[0]);
+       destroy_reg_param(&reg_params[1]);
+       destroy_reg_param(&reg_params[2]);
+       destroy_reg_param(&reg_params[3]);
+       destroy_reg_param(&reg_params[4]);
+       destroy_reg_param(&reg_params[5]);
+
+err:
+       target_free_working_area(target, algorithm);
+
+       /* Switch to memory mapped mode before return to prompt */
+       set_mm_mode(bank);
+
+       return retval;
+}
+
+static int stmqspi_read(struct flash_bank *bank, uint8_t *buffer,
+       uint32_t offset, uint32_t count)
+{
+       struct target *target = bank->target;
+       struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv;
+       uint32_t io_base = stmqspi_info->io_base;
+       int retval;
+
+       LOG_DEBUG("%s: offset=0x%08" PRIx32 " count=0x%08" PRIx32,
+               __func__, offset, count);
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       if (!(stmqspi_info->probed)) {
+               LOG_ERROR("Flash bank not probed");
+               return ERROR_FLASH_BANK_NOT_PROBED;
+       }
+
+       if (offset + count > bank->size) {
+               LOG_WARNING("Read beyond end of flash. Extra data to be ignored.");
+               count = bank->size - offset;
+       }
+
+       /* Abort any previous operation */
+       retval = target_write_u32(target, io_base + SPI_CR,
+               READ_REG(SPI_CR) | (1U<<SPI_ABORT));
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* Wait for busy to be cleared */
+       retval = poll_busy(bank, SPI_PROBE_TIMEOUT);
+       if (retval != ERROR_OK)
+               return retval;
+
+       return qspi_read_write_block(bank, buffer, offset, count, false);
+}
+
+static int stmqspi_write(struct flash_bank *bank, const uint8_t *buffer,
+       uint32_t offset, uint32_t count)
+{
+       struct target *target = bank->target;
+       struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv;
+       uint32_t io_base = stmqspi_info->io_base;
+       unsigned int dual, sector;
+       bool octal_dtr;
+       int retval;
+
+       LOG_DEBUG("%s: offset=0x%08" PRIx32 " count=0x%08" PRIx32,
+               __func__, offset, count);
+
+       dual = (stmqspi_info->saved_cr & (1U<<SPI_DUAL_FLASH)) ? 1 : 0;
+       octal_dtr = IS_OCTOSPI && (stmqspi_info->saved_ccr & (1U<<OCTOSPI_DDTR));
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       if (!(stmqspi_info->probed)) {
+               LOG_ERROR("Flash bank not probed");
+               return ERROR_FLASH_BANK_NOT_PROBED;
+       }
+
+       if (offset + count > bank->size) {
+               LOG_WARNING("Write beyond end of flash. Extra data discarded.");
+               count = bank->size - offset;
+       }
+
+       /* Check sector protection */
+       for (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_FLASH_PROTECTED;
+               }
+       }
+
+       if ((dual || octal_dtr) && ((offset & 1) != 0 || (count & 1) != 0)) {
+               LOG_ERROR("In dual-QSPI and octal-DTR modes writes must be two byte aligned: "
+                       "%s: address=0x%08" PRIx32 " len=0x%08" PRIx32, __func__, offset, count);
+               return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
+       }
+
+       /* Abort any previous operation */
+       retval = target_write_u32(target, io_base + SPI_CR,
+               READ_REG(SPI_CR) | (1U<<SPI_ABORT));
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* Wait for busy to be cleared */
+       retval = poll_busy(bank, SPI_PROBE_TIMEOUT);
+       if (retval != ERROR_OK)
+               return retval;
+
+       return qspi_read_write_block(bank, (uint8_t *) buffer, offset, count, true);
+}
+
+static int stmqspi_verify(struct flash_bank *bank, const uint8_t *buffer,
+       uint32_t offset, uint32_t count)
+{
+       struct target *target = bank->target;
+       struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv;
+       uint32_t io_base = stmqspi_info->io_base;
+       unsigned int dual;
+       bool octal_dtr;
+       int retval;
+
+       LOG_DEBUG("%s: offset=0x%08" PRIx32 " count=0x%08" PRIx32,
+               __func__, offset, count);
+
+       dual = (stmqspi_info->saved_cr & (1U<<SPI_DUAL_FLASH)) ? 1 : 0;
+       octal_dtr = IS_OCTOSPI && (stmqspi_info->saved_ccr & (1U<<OCTOSPI_DDTR));
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       if (!(stmqspi_info->probed)) {
+               LOG_ERROR("Flash bank not probed");
+               return ERROR_FLASH_BANK_NOT_PROBED;
+       }
+
+       if (offset + count > bank->size) {
+               LOG_WARNING("Verify beyond end of flash. Extra data ignored.");
+               count = bank->size - offset;
+       }
+
+       if ((dual || octal_dtr) && ((offset & 1) != 0 || (count & 1) != 0)) {
+               LOG_ERROR("In dual-QSPI and octal-DTR modes reads must be two byte aligned: "
+                       "%s: address=0x%08" PRIx32 " len=0x%08" PRIx32, __func__, offset, count);
+               return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
+       }
+
+       /* Abort any previous operation */
+       retval = target_write_u32(target, io_base + SPI_CR,
+               READ_REG(SPI_CR) | (1U<<SPI_ABORT));
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* Wait for busy to be cleared */
+       retval = poll_busy(bank, SPI_PROBE_TIMEOUT);
+       if (retval != ERROR_OK)
+               return retval;
+
+       return qspi_verify(bank, (uint8_t *) buffer, offset, count);
+}
+
+/* Find appropriate dummy setting, in particular octo mode */
+static int find_sfdp_dummy(struct flash_bank *bank, int len)
+{
+       struct target *target = bank->target;
+       struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv;
+       uint32_t io_base = stmqspi_info->io_base;
+       uint8_t data;
+       unsigned int dual, count;
+       bool flash1 = !(stmqspi_info->saved_cr & (1U<<SPI_FSEL_FLASH));
+       int retval;
+       const unsigned int max_bytes = 64;
+
+       dual = (stmqspi_info->saved_cr & (1U<<SPI_DUAL_FLASH)) ? 1 : 0;
+
+       LOG_DEBUG("%s: len=%d, dual=%u, flash1=%d",
+               __func__, len, dual, flash1);
+
+       /* Abort any previous operation */
+       retval = target_write_u32(target, io_base + SPI_CR,
+               stmqspi_info->saved_cr | (1U<<SPI_ABORT));
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* Wait for busy to be cleared */
+       retval = poll_busy(bank, SPI_PROBE_TIMEOUT);
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* Switch to saved_cr (had to be set accordingly before this call) */
+       retval = target_write_u32(target, io_base + SPI_CR, stmqspi_info->saved_cr);
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* Read at most that many bytes */
+       retval = target_write_u32(target, io_base + SPI_DLR, (max_bytes << dual) - 1);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* Read SFDP block */
+       if (IS_OCTOSPI)
+               retval = OCTOSPI_CMD(OCTOSPI_READ_MODE, OCTOSPI_CCR_READ_SFDP(len),
+                       SPIFLASH_READ_SFDP);
+       else
+               retval = target_write_u32(target, io_base + QSPI_CCR, QSPI_CCR_READ_SFDP);
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* Read from start of sfdp block */
+       retval = target_write_u32(target, io_base + SPI_AR, 0);
+       if (retval != ERROR_OK)
+               goto err;
+
+       for (count = 0 ; count < max_bytes; count++) {
+               if ((dual != 0) && !flash1) {
+                       /* discard even byte in dual flash-mode if flash2 */
+                       retval = target_read_u8(target, io_base + SPI_DR, &data);
+                       if (retval != ERROR_OK)
+                               goto err;
+               }
+
+               retval = target_read_u8(target, io_base + SPI_DR, &data);
+               if (retval != ERROR_OK)
+                       goto err;
+
+               if (data == 0x53) {
+                       LOG_DEBUG("start of SFDP header for flash%c after %u dummy bytes",
+                               flash1 ? '1' : '2', count);
+                       if (flash1)
+                               stmqspi_info->sfdp_dummy1 = count;
+                       else
+                               stmqspi_info->sfdp_dummy2 = count;
+                       return ERROR_OK;
+               }
+
+               if ((dual != 0) && flash1) {
+                       /* discard odd byte in dual flash-mode if flash1 */
+                       retval = target_read_u8(target, io_base + SPI_DR, &data);
+                       if (retval != ERROR_OK)
+                               goto err;
+               }
+       }
+
+       retval = ERROR_FAIL;
+       LOG_DEBUG("no start of SFDP header even after %u dummy bytes", count);
+
+err:
+       /* Abort operation */
+       retval = target_write_u32(target, io_base + SPI_CR,
+               READ_REG(SPI_CR) | (1U<<SPI_ABORT));
+
+       return retval;
+}
+
+/* Read SFDP parameter block */
+static int read_sfdp_block(struct flash_bank *bank, uint32_t addr,
+       uint32_t words, uint32_t *buffer)
+{
+       struct target *target = bank->target;
+       struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv;
+       uint32_t io_base = stmqspi_info->io_base;
+       bool flash1 = !(stmqspi_info->saved_cr & (1U<<SPI_FSEL_FLASH));
+       unsigned int dual, count, len, *dummy;
+       int retval;
+
+       dual = (stmqspi_info->saved_cr & (1U<<SPI_DUAL_FLASH)) ? 1 : 0;
+
+       if (IS_OCTOSPI && (((stmqspi_info->saved_ccr >> SPI_DMODE_POS) & 0x7) > 3)) {
+               /* in OCTO mode 4-byte address and (yet) unknown number of dummy clocks */
+               len = 4;
+
+               /* in octo mode, use sfdp_dummy1 only */
+               dummy = &stmqspi_info->sfdp_dummy1;
+               if (*dummy == 0) {
+                       retval = find_sfdp_dummy(bank, len);
+                       if (retval != ERROR_OK)
+                               return retval;
+               }
+       } else {
+               /* in all other modes 3-byte-address and 8(?) dummy clocks */
+               len = 3;
+
+               /* use sfdp_dummy1/2 according to currently selected flash */
+               dummy = (stmqspi_info->saved_cr & (1U<<SPI_FSEL_FLASH)) ?
+                       &stmqspi_info->sfdp_dummy2 : &stmqspi_info->sfdp_dummy1;
+
+               /* according to SFDP standard, there should always be 8 dummy *CLOCKS*
+                * giving 1, 2 or 4 dummy *BYTES*, however, this is apparently not
+                * always implemented correctly, so determine the number of dummy bytes
+                * dynamically */
+               if (*dummy == 0) {
+                       retval = find_sfdp_dummy(bank, len);
+                       if (retval != ERROR_OK)
+                               return retval;
+               }
+       }
+
+       LOG_DEBUG("%s: addr=0x%08" PRIx32 " words=0x%08" PRIx32 " dummy=%u",
+               __func__, addr, words, *dummy);
+
+       /* Abort any previous operation */
+       retval = target_write_u32(target, io_base + SPI_CR,
+               stmqspi_info->saved_cr | (1U<<SPI_ABORT));
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* Wait for busy to be cleared */
+       retval = poll_busy(bank, SPI_PROBE_TIMEOUT);
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* Switch to one flash only */
+       retval = target_write_u32(target, io_base + SPI_CR, stmqspi_info->saved_cr);
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* Read that many words plus dummy bytes */
+       retval = target_write_u32(target, io_base + SPI_DLR,
+               ((*dummy + words * sizeof(uint32_t)) << dual) - 1);
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* Read SFDP block */
+       if (IS_OCTOSPI)
+               retval = OCTOSPI_CMD(OCTOSPI_READ_MODE, OCTOSPI_CCR_READ_SFDP(len),
+                       SPIFLASH_READ_SFDP);
+       else
+               retval = target_write_u32(target, io_base + QSPI_CCR, QSPI_CCR_READ_SFDP);
+       if (retval != ERROR_OK)
+               goto err;
+
+       retval = target_write_u32(target, io_base + SPI_AR, addr << dual);
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* dummy clocks */
+       for (count = *dummy << dual; count > 0; --count) {
+               retval = target_read_u8(target, io_base + SPI_DR, (uint8_t *) buffer);
+               if (retval != ERROR_OK)
+                       goto err;
+       }
+
+       for ( ; words > 0; words--) {
+               if (dual != 0) {
+                       uint32_t word1, word2;
+
+                       retval = target_read_u32(target, io_base + SPI_DR, &word1);
+                       if (retval != ERROR_OK)
+                               goto err;
+                       retval = target_read_u32(target, io_base + SPI_DR, &word2);
+                       if (retval != ERROR_OK)
+                               goto err;
+
+                       if (!flash1) {
+                               /* shift odd numbered bytes into even numbered ones */
+                               word1 >>= 8;
+                               word2 >>= 8;
+                       }
+
+                       /* pack even numbered bytes into one word */
+                       *buffer = (word1 & 0xFFU) | ((word1 & 0xFF0000U) >> 8) |
+                               ((word2 & 0xFFU) << 16) | ((word2 & 0xFF0000U) << 8);
+
+
+               } else {
+                       retval = target_read_u32(target, io_base + SPI_DR, buffer);
+                       if (retval != ERROR_OK)
+                               goto err;
+               }
+               LOG_DEBUG("raw SFDP data 0x%08" PRIx32, *buffer);
+
+               /* endian correction, sfdp data is always le uint32_t based */
+               *buffer = le_to_h_u32((uint8_t *) buffer);
+               buffer++;
+       }
+
+err:
+       return retval;
+}
+
+/* Return ID of flash device(s) */
+/* On exit, indirect mode is kept */
+static int read_flash_id(struct flash_bank *bank, uint32_t *id1, uint32_t *id2)
+{
+       struct target *target = bank->target;
+       struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv;
+       uint32_t io_base = stmqspi_info->io_base;
+       uint8_t byte;
+       unsigned int type, count, len1, len2;
+       int retval;
+
+       /* invalidate both ids */
+       *id1 = 0;
+       *id2 = 0;
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       /* SPIFLASH_READ_MID causes device in octal mode to go berserk, so don't use in this case */
+       for (type = (IS_OCTOSPI && OPI_MODE) ? 1 : 0; type < 2 ; type++) {
+               /* Abort any previous operation */
+               retval = target_write_u32(target, io_base + SPI_CR,
+                       READ_REG(SPI_CR) | (1U<<SPI_ABORT));
+               if (retval != ERROR_OK)
+                       goto err;
+
+               /* Poll WIP */
+               retval = wait_till_ready(bank, SPI_PROBE_TIMEOUT);
+               if (retval != ERROR_OK)
+                       goto err;
+
+               /* Wait for busy to be cleared */
+               retval = poll_busy(bank, SPI_PROBE_TIMEOUT);
+               if (retval != ERROR_OK)
+                       goto err;
+
+               /* Read at most 16 bytes per chip */
+               count = 16;
+               retval = target_write_u32(target, io_base + SPI_DLR,
+                       (stmqspi_info->saved_cr & (1U<<SPI_DUAL_FLASH) ? count * 2 : count) - 1);
+               if (retval != ERROR_OK)
+                       goto err;
+
+               /* Read id: one particular flash chip (N25Q128) switches back to SPI mode when receiving
+                * SPI_FLASH_READ_ID in QPI mode, hence try SPIFLASH_READ_MID first */
+               switch (type) {
+                       case 0:
+                               if (IS_OCTOSPI)
+                                       retval = OCTOSPI_CMD(OCTOSPI_READ_MODE, OCTOSPI_CCR_READ_MID, SPIFLASH_READ_MID);
+                               else
+                                       retval = target_write_u32(target, io_base + QSPI_CCR, QSPI_CCR_READ_MID);
+                               break;
+
+                       case 1:
+                               if (IS_OCTOSPI)
+                                       retval = OCTOSPI_CMD(OCTOSPI_READ_MODE, OCTOSPI_CCR_READ_ID, SPIFLASH_READ_ID);
+                               else
+                                       retval = target_write_u32(target, io_base + QSPI_CCR, QSPI_CCR_READ_ID);
+                               break;
+
+                       default:
+                               return ERROR_FAIL;
+               }
+
+               if (retval != ERROR_OK)
+                       goto err;
+
+               /* Dummy address 0, only required for 8-line mode */
+               if (IS_OCTOSPI && OPI_MODE) {
+                       retval = target_write_u32(target, io_base + SPI_AR, 0);
+                       if (retval != ERROR_OK)
+                               goto err;
+               }
+
+               /* for debugging only */
+               (void) READ_REG(SPI_SR);
+
+               /* Read ID from Data Register */
+               for (len1 = 0, len2 = 0; count > 0; --count) {
+                       if ((stmqspi_info->saved_cr & ((1U<<SPI_DUAL_FLASH) |
+                               (1U<<SPI_FSEL_FLASH))) != (1U<<SPI_FSEL_FLASH)) {
+                               retval = target_read_u8(target, io_base + SPI_DR, &byte);
+                               if (retval != ERROR_OK)
+                                       goto err;
+                               /* collect 3 bytes without continuation codes */
+                               if ((byte != 0x7F) && (len1 < 3)) {
+                                       *id1 = (*id1 >> 8) | ((uint32_t) byte) << 16;
+                                       len1++;
+                               }
+                       }
+                       if ((stmqspi_info->saved_cr & ((1U<<SPI_DUAL_FLASH) |
+                               (1U<<SPI_FSEL_FLASH))) != 0) {
+                               retval = target_read_u8(target, io_base + SPI_DR, &byte);
+                               if (retval != ERROR_OK)
+                                       goto err;
+                               /* collect 3 bytes without continuation codes */
+                               if ((byte != 0x7F) && (len2 < 3)) {
+                                       *id2 = (*id2 >> 8) | ((uint32_t) byte) << 16;
+                                       len2++;
+                               }
+                       }
+               }
+
+               if (((*id1 != 0x000000) && (*id1 != 0xFFFFFF)) ||
+                       ((*id2 != 0x000000) && (*id2 != 0xFFFFFF)))
+                       break;
+       }
+
+       if ((stmqspi_info->saved_cr & ((1U<<SPI_DUAL_FLASH) |
+               (1U<<SPI_FSEL_FLASH))) != (1U<<SPI_FSEL_FLASH)) {
+               if ((*id1 == 0x000000) || (*id1 == 0xFFFFFF)) {
+                       /* no id retrieved, so id must be set manually */
+                       LOG_INFO("No id from flash1");
+                       retval = ERROR_FLASH_BANK_NOT_PROBED;
+               }
+       }
+
+       if ((stmqspi_info->saved_cr & ((1U<<SPI_DUAL_FLASH) |
+               (1U<<SPI_FSEL_FLASH))) != (0U<<SPI_FSEL_FLASH)) {
+               if ((*id2 == 0x000000) || (*id2 == 0xFFFFFF)) {
+                       /* no id retrieved, so id must be set manually */
+                       LOG_INFO("No id from flash2");
+                       retval = ERROR_FLASH_BANK_NOT_PROBED;
+               }
+       }
+
+err:
+       return retval;
+}
+
+static int stmqspi_probe(struct flash_bank *bank)
+{
+       struct target *target = bank->target;
+       struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv;
+       struct flash_sector *sectors = NULL;
+       uint32_t io_base = stmqspi_info->io_base;
+       uint32_t id1 = 0, id2 = 0, data = 0;
+       const struct flash_device *p;
+       const uint32_t magic = 0xAEF1510E;
+       unsigned int dual, fsize;
+       bool octal_dtr;
+       int retval;
+
+       if (stmqspi_info->probed) {
+               bank->size = 0;
+               bank->num_sectors = 0;
+               if (bank->sectors)
+                       free(bank->sectors);
+               bank->sectors = NULL;
+               memset(&stmqspi_info->dev, 0, sizeof(stmqspi_info->dev));
+               stmqspi_info->sfdp_dummy1 = 0;
+               stmqspi_info->sfdp_dummy2 = 0;
+               stmqspi_info->probed = false;
+       }
+
+       /* Abort any previous operation */
+       retval = target_write_u32(target, io_base + SPI_CR,
+               READ_REG(SPI_CR) | (1U<<SPI_ABORT));
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* Wait for busy to be cleared */
+       retval = poll_busy(bank, SPI_PROBE_TIMEOUT);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* check whether QSPI_ABR is writeable and readback returns the value written */
+       retval = target_write_u32(target, io_base + QSPI_ABR, magic);
+       if (retval == ERROR_OK) {
+               retval = target_read_u32(target, io_base + QSPI_ABR, &data);
+               retval = target_write_u32(target, io_base + QSPI_ABR, 0);
+       }
+
+       if (data == magic) {
+               LOG_DEBUG("QSPI_ABR register present");
+               stmqspi_info->octo = false;
+       } else if (READ_REG(OCTOSPI_MAGIC) == OCTO_MAGIC_ID) {
+               LOG_DEBUG("OCTOSPI_MAGIC present");
+               stmqspi_info->octo = true;
+       } else {
+               LOG_ERROR("No QSPI, no OCTOSPI at 0x%08" PRIx32, io_base);
+               stmqspi_info->probed = false;
+               stmqspi_info->dev.name = "none";
+               return ERROR_FAIL;
+       }
+
+       /* save current FSEL and DFM bits in QSPI/OCTOSPI_CR, current QSPI/OCTOSPI_CCR value */
+       stmqspi_info->saved_cr = READ_REG(SPI_CR);
+       if (retval == ERROR_OK)
+               stmqspi_info->saved_ccr = READ_REG(SPI_CCR);
+
+       if (IS_OCTOSPI) {
+               uint32_t mtyp;
+
+               mtyp = ((READ_REG(OCTOSPI_DCR1) & OCTOSPI_MTYP_MASK))>>OCTOSPI_MTYP_POS;
+               if (retval == ERROR_OK)
+                       stmqspi_info->saved_tcr = READ_REG(OCTOSPI_TCR);
+               if (retval == ERROR_OK)
+                       stmqspi_info->saved_ir = READ_REG(OCTOSPI_IR);
+               if ((mtyp != 0x0) && (mtyp != 0x1)) {
+                       retval = ERROR_FAIL;
+                       LOG_ERROR("Only regular SPI protocol supported in OCTOSPI");
+               }
+               if (retval == ERROR_OK) {
+                       LOG_DEBUG("OCTOSPI at 0x%08" PRIx64 ", io_base at 0x%08" PRIx32 ", OCTOSPI_CR 0x%08"
+                               PRIx32 ", OCTOSPI_CCR 0x%08" PRIx32 ", %d-byte addr", bank->base, io_base,
+                               stmqspi_info->saved_cr, stmqspi_info->saved_ccr, SPI_ADSIZE);
+               } else {
+                       LOG_ERROR("No OCTOSPI at io_base 0x%08" PRIx32, io_base);
+                       stmqspi_info->probed = false;
+                       stmqspi_info->dev.name = "none";
+                       return ERROR_FAIL;
+               }
+       } else {
+               if (retval == ERROR_OK) {
+                       LOG_DEBUG("QSPI at 0x%08" PRIx64 ", io_base at 0x%08" PRIx32 ", QSPI_CR 0x%08"
+                               PRIx32 ", QSPI_CCR 0x%08" PRIx32 ", %d-byte addr", bank->base, io_base,
+                               stmqspi_info->saved_cr, stmqspi_info->saved_ccr, SPI_ADSIZE);
+                               if (stmqspi_info->saved_ccr & (1U << QSPI_DDRM))
+                                       LOG_WARNING("DDR mode is untested and suffers from some silicon bugs");
+               } else {
+                       LOG_ERROR("No QSPI at io_base 0x%08" PRIx32, io_base);
+                       stmqspi_info->probed = false;
+                       stmqspi_info->dev.name = "none";
+                       return ERROR_FAIL;
+               }
+       }
+
+       dual = (stmqspi_info->saved_cr & (1U<<SPI_DUAL_FLASH)) ? 1 : 0;
+       octal_dtr = IS_OCTOSPI && (stmqspi_info->saved_ccr & (1U<<OCTOSPI_DDTR));
+       if (dual || octal_dtr)
+               bank->write_start_alignment = bank->write_end_alignment = 2;
+       else
+               bank->write_start_alignment = bank->write_end_alignment = 1;
+
+       /* read and decode flash ID; returns in indirect mode */
+       retval = read_flash_id(bank, &id1, &id2);
+       LOG_DEBUG("id1 0x%06" PRIx32 ", id2 0x%06" PRIx32, id1, id2);
+       if (retval == ERROR_FLASH_BANK_NOT_PROBED) {
+               /* no id retrieved, so id must be set manually */
+               LOG_INFO("No id - set flash parameters manually");
+               retval = ERROR_OK;
+               goto err;
+       }
+
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* identify flash1 */
+       for (p = flash_devices; id1 && p->name ; p++) {
+               if (p->device_id == id1) {
+                       memcpy(&stmqspi_info->dev, p, sizeof(stmqspi_info->dev));
+                       if (p->size_in_bytes / 4096)
+                               LOG_INFO("flash1 \'%s\' id = 0x%06" PRIx32 " size = %" PRIu32
+                                       "kbytes", p->name, id1, p->size_in_bytes / 1024);
+                       else
+                               LOG_INFO("flash1 \'%s\' id = 0x%06" PRIx32 " size = %" PRIu32
+                                       "bytes", p->name, id1, p->size_in_bytes);
+                       break;
+               }
+       }
+
+       if (id1 && !p->name) {
+               /* chip not been identified by id, then try SFDP */
+               struct flash_device temp;
+               uint32_t saved_cr = stmqspi_info->saved_cr;
+
+               /* select flash1 */
+               stmqspi_info->saved_cr = stmqspi_info->saved_cr & ~(1U<<SPI_FSEL_FLASH);
+               retval = spi_sfdp(bank, &temp, &read_sfdp_block);
+
+               /* restore saved_cr */
+               stmqspi_info->saved_cr = saved_cr;
+
+               if (retval == ERROR_OK) {
+                       LOG_INFO("flash1 \'%s\' id = 0x%06" PRIx32 " size = %" PRIu32
+                               "kbytes", temp.name, id1, temp.size_in_bytes / 1024);
+                       /* save info and retrieved *good* id as spi_sfdp clears all info */
+                       memcpy(&stmqspi_info->dev, &temp, sizeof(stmqspi_info->dev));
+                       stmqspi_info->dev.device_id = id1;
+               } else {
+                       /* even not identified by SFDP, then give up */
+                       LOG_WARNING("Unknown flash1 device id = 0x%06" PRIx32
+                               " - set flash parameters manually", id1);
+                       retval = ERROR_OK;
+                       goto err;
+               }
+       }
+
+       /* identify flash2 */
+       for (p = flash_devices; id2 && p->name ; p++) {
+               if (p->device_id == id2) {
+                       if (p->size_in_bytes / 4096)
+                               LOG_INFO("flash2 \'%s\' id = 0x%06" PRIx32 " size = %" PRIu32
+                                       "kbytes", p->name, id2, p->size_in_bytes / 1024);
+                       else
+                               LOG_INFO("flash2 \'%s\' id = 0x%06" PRIx32 " size = %" PRIu32
+                                       "bytes", p->name, id2, p->size_in_bytes);
+
+                       if (!id1)
+                               memcpy(&stmqspi_info->dev, p, sizeof(stmqspi_info->dev));
+                       else {
+                               if ((stmqspi_info->dev.read_cmd != p->read_cmd) ||
+                                       (stmqspi_info->dev.qread_cmd != p->qread_cmd) ||
+                                       (stmqspi_info->dev.pprog_cmd != p->pprog_cmd) ||
+                                       (stmqspi_info->dev.erase_cmd != p->erase_cmd) ||
+                                       (stmqspi_info->dev.chip_erase_cmd != p->chip_erase_cmd) ||
+                                       (stmqspi_info->dev.sectorsize != p->sectorsize) ||
+                                       (stmqspi_info->dev.size_in_bytes != p->size_in_bytes)) {
+                                       LOG_ERROR("Incompatible flash1/flash2 devices");
+                                       goto err;
+                               }
+                               /* page size is optional in SFDP, so accept smallest value */
+                               if (p->pagesize < stmqspi_info->dev.pagesize)
+                                       stmqspi_info->dev.pagesize = p->pagesize;
+                       }
+                       break;
+               }
+       }
+
+       if (id2 && !p->name) {
+               /* chip not been identified by id, then try SFDP */
+               struct flash_device temp;
+               uint32_t saved_cr = stmqspi_info->saved_cr;
+
+               /* select flash2 */
+               stmqspi_info->saved_cr = stmqspi_info->saved_cr | (1U<<SPI_FSEL_FLASH);
+               retval = spi_sfdp(bank, &temp, &read_sfdp_block);
+
+               /* restore saved_cr */
+               stmqspi_info->saved_cr = saved_cr;
+
+               if (retval == ERROR_OK)
+                       LOG_INFO("flash2 \'%s\' id = 0x%06" PRIx32 " size = %" PRIu32
+                               "kbytes", temp.name, id2, temp.size_in_bytes / 1024);
+               else {
+                       /* even not identified by SFDP, then give up */
+                       LOG_WARNING("Unknown flash2 device id = 0x%06" PRIx32
+                               " - set flash parameters manually", id2);
+                       retval = ERROR_OK;
+                       goto err;
+               }
+
+               if (!id1)
+                       memcpy(&stmqspi_info->dev, &temp, sizeof(stmqspi_info->dev));
+               else {
+                       if ((stmqspi_info->dev.read_cmd != temp.read_cmd) ||
+                               (stmqspi_info->dev.qread_cmd != temp.qread_cmd) ||
+                               (stmqspi_info->dev.pprog_cmd != temp.pprog_cmd) ||
+                               (stmqspi_info->dev.erase_cmd != temp.erase_cmd) ||
+                               (stmqspi_info->dev.chip_erase_cmd != temp.chip_erase_cmd) ||
+                               (stmqspi_info->dev.sectorsize != temp.sectorsize) ||
+                               (stmqspi_info->dev.size_in_bytes != temp.size_in_bytes)) {
+                               LOG_ERROR("Incompatible flash1/flash2 devices");
+                               goto err;
+                       }
+                       /* page size is optional in SFDP, so accept smallest value */
+                       if (temp.pagesize < stmqspi_info->dev.pagesize)
+                               stmqspi_info->dev.pagesize = temp.pagesize;
+               }
+       }
+
+       /* Set correct size value */
+       bank->size = stmqspi_info->dev.size_in_bytes << dual;
+
+       fsize = ((READ_REG(SPI_DCR)>>SPI_FSIZE_POS) & ((1U<<SPI_FSIZE_LEN) - 1));
+       if (retval != ERROR_OK)
+               goto err;
+
+       LOG_DEBUG("FSIZE = 0x%04x", fsize);
+       if (bank->size == (1U<<(fsize + 1)))
+               LOG_DEBUG("FSIZE in DCR(1) matches actual capacity. Beware of silicon bug in H7, L4+, MP1.");
+       else if (bank->size == (1U<<(fsize + 0)))
+               LOG_DEBUG("FSIZE in DCR(1) is off by one regarding actual capacity. Fix for silicon bug?");
+       else
+               LOG_ERROR("FSIZE in DCR(1) doesn't match actual capacity.");
+
+       /* if no sectors, then treat whole flash as single sector */
+       if (stmqspi_info->dev.sectorsize == 0)
+               stmqspi_info->dev.sectorsize = stmqspi_info->dev.size_in_bytes;
+       /* if no page_size, then use sectorsize as page_size */
+       if (stmqspi_info->dev.pagesize == 0)
+               stmqspi_info->dev.pagesize = stmqspi_info->dev.sectorsize;
+
+       /* create and fill sectors array */
+       bank->num_sectors = stmqspi_info->dev.size_in_bytes / stmqspi_info->dev.sectorsize;
+       sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors);
+       if (sectors == NULL) {
+               LOG_ERROR("not enough memory");
+               retval = ERROR_FAIL;
+               goto err;
+       }
+
+       for (unsigned int sector = 0; sector < bank->num_sectors; sector++) {
+               sectors[sector].offset = sector * (stmqspi_info->dev.sectorsize << dual);
+               sectors[sector].size = (stmqspi_info->dev.sectorsize << dual);
+               sectors[sector].is_erased = -1;
+               sectors[sector].is_protected = 0;
+       }
+
+       bank->sectors = sectors;
+       stmqspi_info->probed = true;
+
+err:
+       /* Switch to memory mapped mode before return to prompt */
+       set_mm_mode(bank);
+
+       return retval;
+}
+
+static int stmqspi_auto_probe(struct flash_bank *bank)
+{
+       struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv;
+
+       if (stmqspi_info->probed)
+               return ERROR_OK;
+       stmqspi_probe(bank);
+       return ERROR_OK;
+}
+
+static int stmqspi_protect_check(struct flash_bank *bank)
+{
+       /* Nothing to do. Protection is only handled in SW. */
+       return ERROR_OK;
+}
+
+static int get_stmqspi_info(struct flash_bank *bank, char *buf, int buf_size)
+{
+       struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv;
+
+       if (!(stmqspi_info->probed)) {
+               snprintf(buf, buf_size,
+                       "\nQSPI flash bank not probed yet\n");
+               return ERROR_FLASH_BANK_NOT_PROBED;
+       }
+
+       snprintf(buf, buf_size, "flash%s%s \'%s\', device id = 0x%06" PRIx32
+                       ", flash size = %" PRIu32 "%sbytes\n(page size = %" PRIu32
+                       ", read = 0x%02" PRIx8 ", qread = 0x%02" PRIx8
+                       ", pprog = 0x%02" PRIx8 ", mass_erase = 0x%02" PRIx8
+                       ", sector size = %" PRIu32 "%sbytes, sector_erase = 0x%02" PRIx8 ")",
+                       ((stmqspi_info->saved_cr & ((1U<<SPI_DUAL_FLASH) |
+                       (1U<<SPI_FSEL_FLASH))) != (1U<<SPI_FSEL_FLASH)) ? "1" : "",
+                       ((stmqspi_info->saved_cr & ((1U<<SPI_DUAL_FLASH) |
+                       (1U<<SPI_FSEL_FLASH))) != (0U<<SPI_FSEL_FLASH)) ? "2" : "",
+                       stmqspi_info->dev.name, stmqspi_info->dev.device_id,
+                       bank->size / 4096 ? bank->size / 1024 : bank->size,
+                       bank->size / 4096 ? "k" : "", stmqspi_info->dev.pagesize,
+                       stmqspi_info->dev.read_cmd, stmqspi_info->dev.qread_cmd,
+                       stmqspi_info->dev.pprog_cmd, stmqspi_info->dev.chip_erase_cmd,
+                       stmqspi_info->dev.sectorsize / 4096 ?
+                               stmqspi_info->dev.sectorsize / 1024 : stmqspi_info->dev.sectorsize,
+                       stmqspi_info->dev.sectorsize / 4096 ? "k" : "",
+                       stmqspi_info->dev.erase_cmd);
+
+       return ERROR_OK;
+}
+
+static const struct command_registration stmqspi_exec_command_handlers[] = {
+       {
+               .name = "mass_erase",
+               .handler = stmqspi_handle_mass_erase_command,
+               .mode = COMMAND_EXEC,
+               .usage = "bank_id",
+               .help = "Mass erase entire flash device.",
+       },
+       {
+               .name = "set",
+               .handler = stmqspi_handle_set,
+               .mode = COMMAND_EXEC,
+               .usage = "bank_id name chip_size page_size read_cmd qread_cmd pprg_cmd "
+                       "[ mass_erase_cmd ] [ sector_size sector_erase_cmd ]",
+               .help = "Set params of single flash chip",
+       },
+       {
+               .name = "cmd",
+               .handler = stmqspi_handle_cmd,
+               .mode = COMMAND_EXEC,
+               .usage = "bank_id num_resp cmd_byte ...",
+               .help = "Send low-level command cmd_byte and following bytes or read num_resp.",
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration stmqspi_command_handlers[] = {
+       {
+               .name = "stmqspi",
+               .mode = COMMAND_ANY,
+               .help = "stmqspi flash command group",
+               .usage = "",
+               .chain = stmqspi_exec_command_handlers,
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+struct flash_driver stmqspi_flash = {
+       .name = "stmqspi",
+       .commands = stmqspi_command_handlers,
+       .flash_bank_command = stmqspi_flash_bank_command,
+       .erase = stmqspi_erase,
+       .protect = stmqspi_protect,
+       .write = stmqspi_write,
+       .read = stmqspi_read,
+       .verify = stmqspi_verify,
+       .probe = stmqspi_probe,
+       .auto_probe = stmqspi_auto_probe,
+       .erase_check = stmqspi_blank_check,
+       .protect_check = stmqspi_protect_check,
+       .info = get_stmqspi_info,
+       .free_driver_priv = default_flash_free_driver_priv,
+};
diff --git a/src/flash/nor/stmqspi.h b/src/flash/nor/stmqspi.h
new file mode 100644 (file)
index 0000000..d8510ab
--- /dev/null
@@ -0,0 +1,125 @@
+/***************************************************************************
+ *   Copyright (C) 2016 - 2018 by Andreas Bolsch                           *
+ *   andreas.bolsch@mni.thm.de                                             *
+ *                                                                         *
+ *   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 <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#ifndef OPENOCD_FLASH_NOR_STMQSPI_H
+#define OPENOCD_FLASH_NOR_STMQSPI_H
+
+#include "spi.h"
+
+/* QSPI register offsets */
+#define QSPI_CR                        (0x00)  /* Control register */
+#define QSPI_DCR               (0x04)  /* Device configuration register */
+#define QSPI_SR                        (0x08)  /* Status register */
+#define QSPI_FCR               (0x0C)  /* Flag clear register */
+#define QSPI_DLR               (0x10)  /* Data length register */
+#define QSPI_CCR               (0x14)  /* Communication configuration register */
+#define QSPI_AR                        (0x18)  /* Address register */
+#define QSPI_ABR               (0x1C)  /* Alternate bytes register */
+#define QSPI_DR                        (0x20)  /* Data register */
+
+/* common bits in QSPI_CR and OCTOSPI_CR */
+#define SPI_FSEL_FLASH 7               /* Select flash 2 */
+#define SPI_DUAL_FLASH 6               /* Dual flash mode */
+#define SPI_ABORT              1               /* Abort bit */
+
+/* common bits in QSPI_DCR and OCTOSPI_DCR1 */
+#define SPI_FSIZE_POS  16              /* bit position of FSIZE */
+#define SPI_FSIZE_LEN  5               /* width of FSIZE field */
+
+/* common bits in QSPI_SR/FCR and OCTOSPI_SR/FCR */
+#define SPI_BUSY               5               /* Busy flag */
+#define SPI_FTF                        2               /* FIFO threshold flag */
+#define SPI_TCF                        1               /* Transfer complete flag */
+
+/* fields in QSPI_CCR */
+#define QSPI_DDRM                      31                                      /* position of DDRM bit */
+#define SPI_DMODE_POS          24                                      /* bit position of DMODE */
+#define QSPI_DCYC_POS          18                                      /* bit position of DCYC */
+#define QSPI_DCYC_LEN          5                                       /* width of DCYC field */
+#define QSPI_DCYC_MASK         (((1U<<QSPI_DCYC_LEN) - 1)<<QSPI_DCYC_POS)
+#define SPI_ADSIZE_POS         12                                      /* bit position of ADSIZE */
+
+#define QSPI_WRITE_MODE                0x00000000U                     /* indirect write mode */
+#define QSPI_READ_MODE         0x04000000U                     /* indirect read mode */
+#define QSPI_MM_MODE           0x0C000000U                     /* memory mapped mode */
+#define QSPI_ALTB_MODE         0x0003C000U                     /* alternate byte mode */
+#define QSPI_4LINE_MODE                0x03000F00U                     /* 4 lines for data, addr, instr */
+#define QSPI_NO_DATA           (~0x03000000U)          /* no data */
+#define QSPI_NO_ALTB           (~QSPI_ALTB_MODE)       /* no alternate */
+#define QSPI_NO_ADDR           (~0x00000C00U)          /* no address */
+#define QSPI_ADDR3                     (0x2U<<SPI_ADSIZE_POS)  /* 3 byte address */
+#define QSPI_ADDR4                     (0x3U<<SPI_ADSIZE_POS)  /* 4 byte address */
+
+/* OCTOSPI register offsets */
+#define OCTOSPI_CR             (0x000) /* Control register */
+#define OCTOSPI_DCR1   (0x008) /* Device configuration register 1 */
+#define OCTOSPI_DCR2   (0x00C) /* Device configuration register 2 */
+#define OCTOSPI_DCR3   (0x010) /* Device configuration register 3 */
+#define        OCTOSPI_SR              (0x020) /* Status register */
+#define OCTOSPI_FCR            (0x024) /* Flag clear register */
+#define OCTOSPI_DLR            (0x040) /* Data length register */
+#define OCTOSPI_AR             (0x048) /* Address register */
+#define OCTOSPI_DR             (0x050) /* Data register */
+#define OCTOSPI_CCR            (0x100) /* Communication configuration register */
+#define OCTOSPI_TCR            (0x108) /* Timing configuration register */
+#define OCTOSPI_IR             (0x110) /* Instruction register */
+#define OCTOSPI_WCCR   (0x180) /* Write communication configuration register */
+#define OCTOSPI_WIR            (0x190) /* Write instruction register */
+#define OCTOSPI_MAGIC  (0x3FC) /* Magic ID register, deleted from RM, why? */
+
+#define OCTO_MAGIC_ID  0xA3C5DD01      /* Magic ID, deleted from RM, why? */
+
+/* additional bits in OCTOSPI_CR */
+#define OCTOSPI_WRITE_MODE     0x00000000U                     /* indirect write mode */
+#define OCTOSPI_READ_MODE      0x10000000U                     /* indirect read mode */
+#define OCTOSPI_MM_MODE                0x30000000U                     /* memory mapped mode */
+
+/* additional fields in OCTOSPI_DCR1 */
+#define OCTOSPI_MTYP_POS       (24)                            /* bit position of MTYP */
+#define OCTOSPI_MTYP_LEN       (3)                                     /* width of MTYP field */
+#define OCTOSPI_MTYP_MASK      (((1U<<OCTOSPI_MTYP_LEN) - 1)<<OCTOSPI_MTYP_POS)
+
+/* fields in OCTOSPI_CCR */
+#define OCTOSPI_ALTB_MODE      0x001F0000U                             /* alternate byte mode */
+#define OCTOSPI_8LINE_MODE     0x0F003F3FU                             /* 8 lines DTR for data, addr, instr */
+#define OCTOSPI_NO_DATA                (~0x0F000000U)                  /* no data */
+#define OCTOSPI_NO_ALTB                (~OCTOSPI_ALTB_MODE)    /* no alternate */
+#define OCTOSPI_NO_ADDR                (~0x00000F00U)                  /* no address */
+#define OCTOSPI_ADDR3          (0x2U<<SPI_ADSIZE_POS)  /* 3 byte address */
+#define OCTOSPI_ADDR4          (0x3U<<SPI_ADSIZE_POS)  /* 4 byte address */
+#define OCTOSPI_DQSEN          29                                              /* DQS enable */
+#define OCTOSPI_DDTR           27                                              /* DTR for data */
+#define OCTOSPI_NO_DDTR                (~(1U<<OCTOSPI_DDTR))   /* no DTR for data, but maybe still DQS */
+#define OCTOSPI_ISIZE_MASK     (0x30)                                  /* ISIZE field */
+
+/* fields in OCTOSPI_TCR */
+#define OCTOSPI_DCYC_POS       0                                       /* bit position of DCYC */
+#define OCTOSPI_DCYC_LEN       5                                       /* width of DCYC field */
+#define OCTOSPI_DCYC_MASK      (((1U<<OCTOSPI_DCYC_LEN) - 1)<<OCTOSPI_DCYC_POS)
+
+#define IS_OCTOSPI                     (stmqspi_info->octo)
+#define SPI_CR                         (IS_OCTOSPI ? OCTOSPI_CR : QSPI_CR)
+#define SPI_DCR                                (IS_OCTOSPI ? OCTOSPI_DCR1 : QSPI_DCR)
+#define        SPI_SR                          (IS_OCTOSPI ? OCTOSPI_SR : QSPI_SR)
+#define SPI_FCR                                (IS_OCTOSPI ? OCTOSPI_FCR : QSPI_FCR)
+#define SPI_DLR                                (IS_OCTOSPI ? OCTOSPI_DLR : QSPI_DLR)
+#define SPI_AR                         (IS_OCTOSPI ? OCTOSPI_AR : QSPI_AR)
+#define SPI_DR                         (IS_OCTOSPI ? OCTOSPI_DR : QSPI_DR)
+#define SPI_CCR                                (IS_OCTOSPI ? OCTOSPI_CCR : QSPI_CCR)
+
+#endif /* OPENOCD_FLASH_NOR_STMQSPI_H */
index 278c73e7fa18b9ff101f863f32fd0dc0941f59be..e73dd22f61faade1f5a1b3747dcd3db5e1a3b556 100644 (file)
 #define SMI_READ_REG(a) (_SMI_READ_REG(a))
 #define _SMI_READ_REG(a)                       \
 {                                                                      \
-       int __a;                                                \
-       uint32_t __v;                                   \
+       int _ret;                                               \
+       uint32_t _value;                                \
                                                                        \
-       __a = target_read_u32(target, io_base + (a), &__v); \
-       if (__a != ERROR_OK)                    \
-               return __a;                                     \
-       __v;                                                    \
+       _ret = target_read_u32(target, io_base + (a), &_value); \
+       if (_ret != ERROR_OK)                   \
+               return _ret;                            \
+       _value;                                                 \
 }
 
 #define SMI_WRITE_REG(a, v)                    \
 {                                                                      \
-       int __r;                                                \
+       int _retval;                                    \
                                                                        \
-       __r = target_write_u32(target, io_base + (a), (v)); \
-       if (__r != ERROR_OK)                    \
-               return __r;                                     \
+       _retval = target_write_u32(target, io_base + (a), (v)); \
+       if (_retval != ERROR_OK)                \
+               return _retval;                         \
 }
 
 #define SMI_POLL_TFF(timeout)          \
 {                                                                      \
-       int __r;                                                \
+       int _retval;                                    \
                                                                        \
-       __r = poll_tff(target, io_base, timeout); \
-       if (__r != ERROR_OK)                    \
-               return __r;                                     \
+       _retval = poll_tff(target, io_base, timeout); \
+       if (_retval != ERROR_OK)                \
+               return _retval;                         \
 }
 
 #define SMI_SET_SW_MODE()      SMI_WRITE_REG(SMI_CR1, \
index 87c8cede7a9f54ead1d81011934ab5cd0d705668..66b9a4cb6cd956415614750dcaa6e622c7769597 100644 (file)
@@ -454,7 +454,8 @@ COMMAND_HANDLER(handle_flash_write_image_command)
        if (retval != ERROR_OK)
                return retval;
 
-       retval = flash_write_unlock(target, &image, &written, auto_erase, auto_unlock);
+       retval = flash_write_unlock_verify(target, &image, &written, auto_erase,
+               auto_unlock, true, false);
        if (retval != ERROR_OK) {
                image_close(&image);
                return retval;
@@ -471,6 +472,58 @@ COMMAND_HANDLER(handle_flash_write_image_command)
        return retval;
 }
 
+COMMAND_HANDLER(handle_flash_verify_image_command)
+{
+       struct target *target = get_current_target(CMD_CTX);
+
+       struct image image;
+       uint32_t verified;
+
+       int retval;
+
+       if (CMD_ARGC < 1)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       if (!target) {
+               LOG_ERROR("no target selected");
+               return ERROR_FAIL;
+       }
+
+       struct duration bench;
+       duration_start(&bench);
+
+       if (CMD_ARGC >= 2) {
+               image.base_address_set = 1;
+               COMMAND_PARSE_NUMBER(llong, CMD_ARGV[1], image.base_address);
+       } else {
+               image.base_address_set = 0;
+               image.base_address = 0x0;
+       }
+
+       image.start_address_set = 0;
+
+       retval = image_open(&image, CMD_ARGV[0], (CMD_ARGC == 3) ? CMD_ARGV[2] : NULL);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = flash_write_unlock_verify(target, &image, &verified, false,
+               false, false, true);
+       if (retval != ERROR_OK) {
+               image_close(&image);
+               return retval;
+       }
+
+       if ((ERROR_OK == retval) && (duration_measure(&bench) == ERROR_OK)) {
+               command_print(CMD, "verified %" PRIu32 " bytes from file %s "
+                       "in %fs (%0.3f KiB/s)", verified, CMD_ARGV[0],
+                       duration_elapsed(&bench), duration_kbps(&bench, verified));
+       }
+
+       image_close(&image);
+
+       return retval;
+}
+
 COMMAND_HANDLER(handle_flash_fill_command)
 {
        target_addr_t address;
@@ -1142,7 +1195,15 @@ static const struct command_registration flash_exec_command_handlers[] = {
                .mode = COMMAND_EXEC,
                .usage = "[erase] [unlock] filename [offset [file_type]]",
                .help = "Write an image to flash.  Optionally first unprotect "
-                       "and/or erase the region to be used.  Allow optional "
+                       "and/or erase the region to be used. Allow optional "
+                       "offset from beginning of bank (defaults to zero)",
+       },
+       {
+               .name = "verify_image",
+               .handler = handle_flash_verify_image_command,
+               .mode = COMMAND_EXEC,
+               .usage = "filename [offset [file_type]]",
+               .help = "Verify an image against flash. Allow optional "
                        "offset from beginning of bank (defaults to zero)",
        },
        {
index 0b7debaeff2a2816e493be086980b202033c7161..20c6d77b28213e946640e9b6d359544908f22eea 100644 (file)
@@ -1019,7 +1019,7 @@ void image_close(struct image *image)
        image->sections = NULL;
 }
 
-int image_calculate_checksum(uint8_t *buffer, uint32_t nbytes, uint32_t *checksum)
+int image_calculate_checksum(const uint8_t *buffer, uint32_t nbytes, uint32_t *checksum)
 {
        uint32_t crc = 0xffffffff;
        LOG_DEBUG("Calculating checksum");
index 765d29022f0ea493e360d405e97da1957301cccb..53c27d8129e06431f60e3969df70a390ca9dcb07 100644 (file)
@@ -99,7 +99,7 @@ void image_close(struct image *image);
 int image_add_section(struct image *image, uint32_t base, uint32_t size,
                int flags, uint8_t const *data);
 
-int image_calculate_checksum(uint8_t *buffer, uint32_t nbytes,
+int image_calculate_checksum(const uint8_t *buffer, uint32_t nbytes,
                uint32_t *checksum);
 
 #define ERROR_IMAGE_FORMAT_ERROR       (-1400)
index 3b1c666e5e0d8ec6c5ef06c624c5453ef0be7eb1..db759d9c346d12339c7236421ba7af7f507ffcb3 100644 (file)
@@ -1031,11 +1031,11 @@ int target_run_flash_async_algorithm(struct target *target,
                         * programming. The exact delay shouldn't matter as long as it's
                         * less than buffer size / flash speed. This is very unlikely to
                         * run when using high latency connections such as USB. */
-                       alive_sleep(10);
+                       alive_sleep(2);
 
                        /* to stop an infinite loop on some targets check and increment a timeout
                         * this issue was observed on a stellaris using the new ICDI interface */
-                       if (timeout++ >= 500) {
+                       if (timeout++ >= 2500) {
                                LOG_ERROR("timeout waiting for algorithm, a target reset is recommended");
                                return ERROR_FLASH_OPERATION_FAILED;
                        }
@@ -1049,6 +1049,10 @@ int target_run_flash_async_algorithm(struct target *target,
                if (thisrun_bytes > count * block_size)
                        thisrun_bytes = count * block_size;
 
+               /* Force end of large blocks to be word aligned */
+               if (thisrun_bytes >= 16)
+                       thisrun_bytes -= (rp + thisrun_bytes) & 0x03;
+
                /* Write data to fifo */
                retval = target_write_buffer(target, wp, thisrun_bytes, buffer);
                if (retval != ERROR_OK)
@@ -1098,6 +1102,156 @@ int target_run_flash_async_algorithm(struct target *target,
        return retval;
 }
 
+int target_run_read_async_algorithm(struct target *target,
+               uint8_t *buffer, uint32_t count, int block_size,
+               int num_mem_params, struct mem_param *mem_params,
+               int num_reg_params, struct reg_param *reg_params,
+               uint32_t buffer_start, uint32_t buffer_size,
+               uint32_t entry_point, uint32_t exit_point, void *arch_info)
+{
+       int retval;
+       int timeout = 0;
+
+       const uint8_t *buffer_orig = buffer;
+
+       /* Set up working area. First word is write pointer, second word is read pointer,
+        * rest is fifo data area. */
+       uint32_t wp_addr = buffer_start;
+       uint32_t rp_addr = buffer_start + 4;
+       uint32_t fifo_start_addr = buffer_start + 8;
+       uint32_t fifo_end_addr = buffer_start + buffer_size;
+
+       uint32_t wp = fifo_start_addr;
+       uint32_t rp = fifo_start_addr;
+
+       /* validate block_size is 2^n */
+       assert(!block_size || !(block_size & (block_size - 1)));
+
+       retval = target_write_u32(target, wp_addr, wp);
+       if (retval != ERROR_OK)
+               return retval;
+       retval = target_write_u32(target, rp_addr, rp);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* Start up algorithm on target */
+       retval = target_start_algorithm(target, num_mem_params, mem_params,
+                       num_reg_params, reg_params,
+                       entry_point,
+                       exit_point,
+                       arch_info);
+
+       if (retval != ERROR_OK) {
+               LOG_ERROR("error starting target flash read algorithm");
+               return retval;
+       }
+
+       while (count > 0) {
+               retval = target_read_u32(target, wp_addr, &wp);
+               if (retval != ERROR_OK) {
+                       LOG_ERROR("failed to get write pointer");
+                       break;
+               }
+
+               LOG_DEBUG("offs 0x%zx count 0x%" PRIx32 " wp 0x%" PRIx32 " rp 0x%" PRIx32,
+                       (size_t) (buffer - buffer_orig), count, wp, rp);
+
+               if (wp == 0) {
+                       LOG_ERROR("flash read algorithm aborted by target");
+                       retval = ERROR_FLASH_OPERATION_FAILED;
+                       break;
+               }
+
+               if (((wp - fifo_start_addr) & (block_size - 1)) || wp < fifo_start_addr || wp >= fifo_end_addr) {
+                       LOG_ERROR("corrupted fifo write pointer 0x%" PRIx32, wp);
+                       break;
+               }
+
+               /* Count the number of bytes available in the fifo without
+                * crossing the wrap around. */
+               uint32_t thisrun_bytes;
+               if (wp >= rp)
+                       thisrun_bytes = wp - rp;
+               else
+                       thisrun_bytes = fifo_end_addr - rp;
+
+               if (thisrun_bytes == 0) {
+                       /* Throttle polling a bit if transfer is (much) faster than flash
+                        * reading. The exact delay shouldn't matter as long as it's
+                        * less than buffer size / flash speed. This is very unlikely to
+                        * run when using high latency connections such as USB. */
+                       alive_sleep(2);
+
+                       /* to stop an infinite loop on some targets check and increment a timeout
+                        * this issue was observed on a stellaris using the new ICDI interface */
+                       if (timeout++ >= 2500) {
+                               LOG_ERROR("timeout waiting for algorithm, a target reset is recommended");
+                               return ERROR_FLASH_OPERATION_FAILED;
+                       }
+                       continue;
+               }
+
+               /* Reset our timeout */
+               timeout = 0;
+
+               /* Limit to the amount of data we actually want to read */
+               if (thisrun_bytes > count * block_size)
+                       thisrun_bytes = count * block_size;
+
+               /* Force end of large blocks to be word aligned */
+               if (thisrun_bytes >= 16)
+                       thisrun_bytes -= (rp + thisrun_bytes) & 0x03;
+
+               /* Read data from fifo */
+               retval = target_read_buffer(target, rp, thisrun_bytes, buffer);
+               if (retval != ERROR_OK)
+                       break;
+
+               /* Update counters and wrap write pointer */
+               buffer += thisrun_bytes;
+               count -= thisrun_bytes / block_size;
+               rp += thisrun_bytes;
+               if (rp >= fifo_end_addr)
+                       rp = fifo_start_addr;
+
+               /* Store updated write pointer to target */
+               retval = target_write_u32(target, rp_addr, rp);
+               if (retval != ERROR_OK)
+                       break;
+
+               /* Avoid GDB timeouts */
+               keep_alive();
+
+       }
+
+       if (retval != ERROR_OK) {
+               /* abort flash write algorithm on target */
+               target_write_u32(target, rp_addr, 0);
+       }
+
+       int retval2 = target_wait_algorithm(target, num_mem_params, mem_params,
+                       num_reg_params, reg_params,
+                       exit_point,
+                       10000,
+                       arch_info);
+
+       if (retval2 != ERROR_OK) {
+               LOG_ERROR("error waiting for target flash write algorithm");
+               retval = retval2;
+       }
+
+       if (retval == ERROR_OK) {
+               /* check if algorithm set wp = 0 after fifo writer loop finished */
+               retval = target_read_u32(target, wp_addr, &wp);
+               if (retval == ERROR_OK && wp == 0) {
+                       LOG_ERROR("flash read algorithm aborted by target");
+                       retval = ERROR_FLASH_OPERATION_FAILED;
+               }
+       }
+
+       return retval;
+}
+
 int target_read_memory(struct target *target,
                target_addr_t address, uint32_t size, uint32_t count, uint8_t *buffer)
 {
index ee0bdfb658b208ab465a18d710156e84a61b990d..44463b74f55c353777b4161c2d8f40318924b7b0 100644 (file)
@@ -577,6 +577,18 @@ int target_run_flash_async_algorithm(struct target *target,
                uint32_t entry_point, uint32_t exit_point,
                void *arch_info);
 
+/**
+ * This routine is a wrapper for asynchronous algorithms.
+ *
+ */
+int target_run_read_async_algorithm(struct target *target,
+               uint8_t *buffer, uint32_t count, int block_size,
+               int num_mem_params, struct mem_param *mem_params,
+               int num_reg_params, struct reg_param *reg_params,
+               uint32_t buffer_start, uint32_t buffer_size,
+               uint32_t entry_point, uint32_t exit_point,
+               void *arch_info);
+
 /**
  * Read @a count items of @a size bytes from the memory of @a target at
  * the @a address given.
diff --git a/tcl/board/b-l475e-iot01a.cfg b/tcl/board/b-l475e-iot01a.cfg
new file mode 100644 (file)
index 0000000..be411e4
--- /dev/null
@@ -0,0 +1,56 @@
+# This is an B-L475E-IOT01A Discovery kit for IoT node with a single STM32L475VGT6 chip.
+# http://www.st.com/en/evaluation-tools/b-l475e-iot01a.html
+
+# This is for using the onboard STLINK
+source [find interface/stlink.cfg]
+
+transport select hla_swd
+
+# increase working area to 96KB
+set WORKAREASIZE 0x18000
+
+# enable stmqspi
+set QUADSPI 1
+
+source [find target/stm32l4x.cfg]
+
+# QUADSPI initialization
+proc qspi_init { } {
+       global a
+       mmw 0x4002104C 0x000001FF 0                             ;# RCC_AHB2ENR |= GPIOAEN-GPIOIEN (enable clocks)
+       mmw 0x40021050 0x00000100 0                             ;# RCC_AHB3ENR |= QSPIEN (enable clock)
+       sleep 1                                                                 ;# Wait for clock startup
+
+       # PE11: NCS, PE10: CLK, PE15: BK1_IO3, PE14: BK1_IO2, PE13: BK1_IO1, PE12: BK1_IO0
+
+       # PE15:AF10:V, PE14:AF10:V, PE13:AF10:V, PE12:AF10:V, PE11:AF10:V, PE10:AF10:V
+
+       # Port E: PE15:AF10:V, PE14:AF10:V, PE13:AF10:V, PE12:AF10:V, PE11:AF10:V, PE10:AF10:V
+       mmw 0x48001000 0xAAA00000 0x55500000    ;# MODER
+       mmw 0x48001008 0xFFF00000 0x00000000    ;# OSPEEDR
+       mmw 0x48001024 0xAAAAAA00 0x55555500    ;# AFRH
+
+       mww 0xA0001030 0x00001000                               ;# QUADSPI_LPTR: deactivate CS after 4096 clocks when FIFO is full
+       mww 0xA0001000 0x01500008                               ;# QUADSPI_CR: PRESCALER=1, APMS=1, FTHRES=0, FSEL=0, DFM=0, SSHIFT=0, TCEN=1
+       mww 0xA0001004 0x00160100                               ;# QUADSPI_DCR: FSIZE=0x16, CSHT=0x01, CKMODE=0
+       mmw 0xA0001000 0x00000001 0                             ;# QUADSPI_CR: EN=1
+
+       # memory-mapped read mode with 3-byte addresses
+       mww 0xA0001014 0x0D002503                               ;# QUADSPI_CCR: FMODE=0x3, DMODE=0x1, DCYC=0x0, ADSIZE=0x2, ADMODE=0x1, IMODE=0x1, INSTR=READ
+}
+
+$_TARGETNAME configure -event reset-init {
+       mmw 0x40022000 0x00000004 0x00000003    ;# 4 WS for 72 MHz HCLK
+       sleep 1
+       mmw 0x40021000 0x00000100 0x00000000    ;# HSI on
+       mww 0x4002100C 0x01002432                               ;# 72 MHz: PLLREN=1, PLLM=4, PLLN=36, PLLR=2, HSI
+       mww 0x40021008 0x00008001                               ;# always HSI, APB1: /1, APB2: /1
+       mmw 0x40021000 0x01000000 0x00000000    ;# PLL on
+       sleep 1
+       mmw 0x40021008 0x00000003 0x00000000    ;# switch to PLL
+       sleep 1
+
+       adapter speed 4000
+
+       qspi_init
+}
diff --git a/tcl/board/stm32f412g-disco.cfg b/tcl/board/stm32f412g-disco.cfg
new file mode 100644 (file)
index 0000000..b6bdb64
--- /dev/null
@@ -0,0 +1,70 @@
+# This is an STM32F412G discovery board with a single STM32F412ZGT6 chip.
+# http://www.st.com/en/evaluation-tools/32f412gdiscovery.html
+
+# This is for using the onboard STLINK
+source [find interface/stlink.cfg]
+
+transport select hla_swd
+
+# increase working area to 128KB
+set WORKAREASIZE 0x20000
+
+# enable stmqspi
+set QUADSPI 1
+
+source [find target/stm32f4x.cfg]
+
+# QUADSPI initialization
+proc qspi_init { } {
+       global a
+       mmw 0x40023830 0x000000FF 0                             ;# RCC_AHB1ENR |= GPIOAEN-GPIOHEN (enable clocks)
+       mmw 0x40023838 0x00000002 0                             ;# RCC_AHB3ENR |= QSPIEN (enable clock)
+       sleep 1                                                                 ;# Wait for clock startup
+
+       # PB02: CLK, PG06: BK1_NCS, PF06: BK1_IO3, PF07: BK1_IO2, PF09: BK1_IO1, PF08: BK1_IO0
+
+       # PB02:AF09:V, PF09:AF10:V, PF08:AF10:V, PF07:AF09:V, PF06:AF09:V, PG06:AF10:V
+
+       # Port B: PB02:AF09:V
+       mmw 0x40020400 0x00000020 0x00000010    ;# MODER
+       mmw 0x40020408 0x00000030 0x00000000    ;# OSPEEDR
+       mmw 0x40020420 0x00000900 0x00000600    ;# AFRL
+
+       # Port F: PF09:AF10:V, PF08:AF10:V, PF07:AF09:V, PF06:AF09:V
+       mmw 0x40021400 0x000AA000 0x00055000    ;# MODER
+       mmw 0x40021408 0x000FF000 0x00000000    ;# OSPEEDR
+       mmw 0x40021420 0x99000000 0x66000000    ;# AFRL
+       mmw 0x40021424 0x000000AA 0x00000055    ;# AFRH
+
+       # Port G: PG06:AF10:V
+       mmw 0x40021800 0x00002000 0x00001000    ;# MODER
+       mmw 0x40021808 0x00003000 0x00000000    ;# OSPEEDR
+       mmw 0x40021820 0x0A000000 0x05000000    ;# AFRL
+
+       mww 0xA0001030 0x00001000                               ;# QUADSPI_LPTR: deactivate CS after 4096 clocks when FIFO is full
+       mww 0xA0001000 0x03500008                               ;# QUADSPI_CR: PRESCALER=3, APMS=1, FTHRES=0, FSEL=0, DFM=0, SSHIFT=0, TCEN=1
+       mww 0xA0001004 0x00170100                               ;# QUADSPI_DCR: FSIZE=0x17, CSHT=0x01, CKMODE=0
+       mmw 0xA0001000 0x00000001 0                             ;# QUADSPI_CR: EN=1
+
+       # 1-line spi mode
+       mww 0xA0001014 0x000003F5                               ;# QUADSPI_CCR: FMODE=0x0, DMODE=0x0, DCYC=0x0, ADSIZE=0x0, ADMODE=0x0, IMODE=0x3, INSTR=RSTQIO
+       sleep 1
+
+       # memory-mapped read mode with 3-byte addresses
+       mww 0xA0001014 0x0D002503                               ;# QUADSPI_CCR: FMODE=0x3, DMODE=0x1, DCYC=0x0, ADSIZE=0x2, ADMODE=0x1, IMODE=0x1, INSTR=READ
+}
+
+$_TARGETNAME configure -event reset-init {
+       mww 0x40023C00 0x00000003                               ;# 3 WS for 96 MHz HCLK
+       sleep 1
+       mww 0x40023804 0x24001808                               ;# 96 MHz: HSI, PLLM=8, PLLN=96, PLLP=2
+       mww 0x40023808 0x00001000                               ;# APB1: /2, APB2: /1
+       mmw 0x40023800 0x01000000 0x00000000    ;# PLL on
+       sleep 1
+       mmw 0x40023808 0x00000002 0x00000000    ;# switch to PLL
+       sleep 1
+
+       adapter speed 4000
+
+       qspi_init
+}
diff --git a/tcl/board/stm32f413h-disco.cfg b/tcl/board/stm32f413h-disco.cfg
new file mode 100644 (file)
index 0000000..99f2a49
--- /dev/null
@@ -0,0 +1,83 @@
+# This is an STM32F413H discovery board with a single STM32F413ZHT6 chip.
+# http://www.st.com/en/evaluation-tools/32f413hdiscovery.html
+
+#
+# Untested!!!
+#
+
+# This is for using the onboard STLINK
+source [find interface/stlink.cfg]
+
+transport select hla_swd
+
+# increase working area to 128KB
+set WORKAREASIZE 0x20000
+
+# enable stmqspi
+set QUADSPI 1
+
+source [find target/stm32f4x.cfg]
+
+# QUADSPI initialization
+proc qspi_init { } {
+       global a
+       mmw 0x40023830 0x000000FF 0                             ;# RCC_AHB1ENR |= GPIOAEN-GPIOHEN (enable clocks)
+       mmw 0x40023838 0x00000002 0                             ;# RCC_AHB3ENR |= QSPIEN (enable clock)
+       sleep 1                                                                 ;# Wait for clock startup
+
+       # PG06: BK1_NCS, PB02: CLK, PD13: BK1_IO3, PE02: BK1_IO2, PF09: BK1_IO1, PF08: BK1_IO0
+
+       # PB02:AF09:V, PD13:AF09:V, PE02:AF09:V, PF09:AF10:V, PF08:AF10:V, PG06:AF10:V
+
+       # Port B: PB02:AF09:V
+       mmw 0x40020400 0x00000020 0x00000010    ;# MODER
+       mmw 0x40020408 0x00000030 0x00000000    ;# OSPEEDR
+       mmw 0x40020420 0x00000900 0x00000600    ;# AFRL
+
+       # Port D: PD13:AF09:V
+       mmw 0x40020C00 0x08000000 0x04000000    ;# MODER
+       mmw 0x40020C08 0x0C000000 0x00000000    ;# OSPEEDR
+       mmw 0x40020C24 0x00900000 0x00600000    ;# AFRH
+
+       # Port E: PE02:AF09:V
+       mmw 0x40021000 0x00000020 0x00000010    ;# MODER
+       mmw 0x40021008 0x00000030 0x00000000    ;# OSPEEDR
+       mmw 0x40021020 0x00000900 0x00000600    ;# AFRL
+
+       # Port F: PF09:AF10:V, PF08:AF10:V
+       mmw 0x40021400 0x000A0000 0x00050000    ;# MODER
+       mmw 0x40021408 0x000F0000 0x00000000    ;# OSPEEDR
+       mmw 0x40021424 0x000000AA 0x00000055    ;# AFRH
+
+       # Port G: PG06:AF10:V
+       mmw 0x40021800 0x00002000 0x00001000    ;# MODER
+       mmw 0x40021808 0x00003000 0x00000000    ;# OSPEEDR
+       mmw 0x40021820 0x0A000000 0x05000000    ;# AFRL
+
+       mww 0xA0001030 0x00001000                               ;# QUADSPI_LPTR: deactivate CS after 4096 clocks when FIFO is full
+       mww 0xA0001000 0x03500008                               ;# QUADSPI_CR: PRESCALER=3, APMS=1, FTHRES=0, FSEL=0, DFM=0, SSHIFT=0, TCEN=1
+       mww 0xA0001004 0x00170100                               ;# QUADSPI_DCR: FSIZE=0x17, CSHT=0x01, CKMODE=0
+       mmw 0xA0001000 0x00000001 0                             ;# QUADSPI_CR: EN=1
+
+       # 1-line spi mode
+       mww 0xA0001014 0x000003F5                               ;# QUADSPI_CCR: FMODE=0x0, DMODE=0x0, DCYC=0x0, ADSIZE=0x0, ADMODE=0x0, IMODE=0x3, INSTR=RSTQIO
+       sleep 1
+
+       # memory-mapped read mode with 3-byte addresses
+       mww 0xA0001014 0x0D002503                               ;# QUADSPI_CCR: FMODE=0x3, DMODE=0x1, DCYC=0x0, ADSIZE=0x2, ADMODE=0x1, IMODE=0x1, INSTR=READ
+}
+
+$_TARGETNAME configure -event reset-init {
+       mww 0x40023C00 0x00000003                               ;# 3 WS for 96 MHz HCLK
+       sleep 1
+       mww 0x40023804 0x24001808                               ;# 96 MHz: HSI, PLLM=8, PLLN=96, PLLP=2
+       mww 0x40023808 0x00001000                               ;# APB1: /2, APB2: /1
+       mmw 0x40023800 0x01000000 0x00000000    ;# PLL on
+       sleep 1
+       mmw 0x40023808 0x00000002 0x00000000    ;# switch to PLL
+       sleep 1
+
+       adapter speed 4000
+
+       qspi_init
+}
diff --git a/tcl/board/stm32f469i-disco.cfg b/tcl/board/stm32f469i-disco.cfg
new file mode 100644 (file)
index 0000000..ab67512
--- /dev/null
@@ -0,0 +1,65 @@
+# This is an STM32F469I discovery board with a single STM32F469NIH6 chip.
+# http://www.st.com/en/evaluation-tools/32f469idiscovery.html
+
+# This is for using the onboard STLINK
+source [find interface/stlink.cfg]
+
+transport select hla_swd
+
+# increase working area to 128KB
+set WORKAREASIZE 0x20000
+
+# enable stmqspi
+set QUADSPI 1
+
+source [find target/stm32f4x.cfg]
+
+# QUADSPI initialization
+proc qspi_init { } {
+       global a
+       mmw 0x40023830 0x000007FF 0                             ;# RCC_AHB1ENR |= GPIOAEN-GPIOKEN (enable clocks)
+       mmw 0x40023838 0x00000002 0                             ;# RCC_AHB3ENR |= QSPIEN (enable clock)
+       sleep 1                                                                 ;# Wait for clock startup
+
+       # PF10: CLK, PB06: BK1_NCS, PF06: BK1_IO3, PF07: BK1_IO2, PF09: BK1_IO1, PF08: BK1_IO0
+
+       # PB06:AF10:V, PF10:AF09:V, PF09:AF10:V, PF08:AF10:V, PF07:AF09:V, PF06:AF09:V
+
+       # Port B: PB06:AF10:V
+       mmw 0x40020400 0x00002000 0x00001000    ;# MODER
+       mmw 0x40020408 0x00003000 0x00000000    ;# OSPEEDR
+       mmw 0x40020420 0x0A000000 0x05000000    ;# AFRL
+
+       # Port F: PF10:AF09:V, PF09:AF10:V, PF08:AF10:V, PF07:AF09:V, PF06:AF09:V
+       mmw 0x40021400 0x002AA000 0x00155000    ;# MODER
+       mmw 0x40021408 0x003FF000 0x00000000    ;# OSPEEDR
+       mmw 0x40021420 0x99000000 0x66000000    ;# AFRL
+       mmw 0x40021424 0x000009AA 0x00000655    ;# AFRH
+
+       mww 0xA0001030 0x00001000                               ;# QUADSPI_LPTR: deactivate CS after 4096 clocks when FIFO is full
+       mww 0xA0001000 0x03500008                               ;# QUADSPI_CR: PRESCALER=3, APMS=1, FTHRES=0, FSEL=0, DFM=0, SSHIFT=0, TCEN=1
+       mww 0xA0001004 0x00170100                               ;# QUADSPI_DCR: FSIZE=0x17, CSHT=0x01, CKMODE=0
+       mmw 0xA0001000 0x00000001 0                             ;# QUADSPI_CR: EN=1
+
+       # 1-line spi mode
+       mww 0xA0001014 0x000003F5                               ;# QUADSPI_CCR: FMODE=0x0, DMODE=0x0, DCYC=0x0, ADSIZE=0x0, ADMODE=0x0, IMODE=0x3, INSTR=RSTQIO
+       sleep 1
+
+       # memory-mapped read mode with 3-byte addresses
+       mww 0xA0001014 0x0D002503                               ;# QUADSPI_CCR: FMODE=0x3, DMODE=0x1, DCYC=0x0, ADSIZE=0x2, ADMODE=0x1, IMODE=0x1, INSTR=READ
+}
+
+$_TARGETNAME configure -event reset-init {
+       mww 0x40023C00 0x00000005                               ;# 5 WS for 160 MHz HCLK
+       sleep 1
+       mww 0x40023804 0x24002808                               ;# 160 MHz: HSI, PLLM=8, PLLN=160, PLLP=2
+       mww 0x40023808 0x00009400                               ;# APB1: /4, APB2: /2
+       mmw 0x40023800 0x01000000 0x00000000    ;# PLL on
+       sleep 1
+       mmw 0x40023808 0x00000002 0x00000000    ;# switch to PLL
+       sleep 1
+
+       adapter speed 4000
+
+       qspi_init
+}
diff --git a/tcl/board/stm32f723e-disco.cfg b/tcl/board/stm32f723e-disco.cfg
new file mode 100644 (file)
index 0000000..3c04d86
--- /dev/null
@@ -0,0 +1,74 @@
+# This is an STM32F723E discovery board with a single STM32F723IEK6 chip.
+# http://www.st.com/en/evaluation-tools/32f723ediscovery.html
+
+# This is for using the onboard STLINK
+source [find interface/stlink.cfg]
+
+transport select hla_swd
+
+# increase working area to 128KB
+set WORKAREASIZE 0x20000
+
+# enable stmqspi
+set QUADSPI 1
+
+source [find target/stm32f7x.cfg]
+
+# QUADSPI initialization
+proc qspi_init { } {
+       global a
+       mmw 0x40023830 0x000007FF 0                             ;# RCC_AHB1ENR |= GPIOAEN-GPIOKEN (enable clocks)
+       mmw 0x40023838 0x00000002 0                             ;# RCC_AHB3ENR |= QSPIEN (enable clock)
+       sleep 1                                                                 ;# Wait for clock startup
+
+       # PB02: CLK, PB06: BK1_NCS, PD13: BK1_IO3, PE02: BK1_IO2, PC10: BK1_IO1, PC09: BK1_IO0
+
+       # PB06:AF10:V, PB02:AF09:V, PC10:AF09:V, PC09:AF09:V, PD13:AF09:V, PE02:AF09:V
+
+       # Port B: PB06:AF10:V, PB02:AF09:V
+       mmw 0x40020400 0x00002020 0x00001010    ;# MODER
+       mmw 0x40020408 0x00003030 0x00000000    ;# OSPEEDR
+       mmw 0x40020420 0x0A000900 0x05000600    ;# AFRL
+
+       # Port C: PC10:AF09:V, PC09:AF09:V
+       mmw 0x40020800 0x00280000 0x00140000    ;# MODER
+       mmw 0x40020808 0x003C0000 0x00000000    ;# OSPEEDR
+       mmw 0x40020824 0x00000990 0x00000660    ;# AFRH
+
+       # Port D: PD13:AF09:V
+       mmw 0x40020C00 0x08000000 0x04000000    ;# MODER
+       mmw 0x40020C08 0x0C000000 0x00000000    ;# OSPEEDR
+       mmw 0x40020C24 0x00900000 0x00600000    ;# AFRH
+
+       # Port E: PE02:AF09:V
+       mmw 0x40021000 0x00000020 0x00000010    ;# MODER
+       mmw 0x40021008 0x00000030 0x00000000    ;# OSPEEDR
+       mmw 0x40021020 0x00000900 0x00000600    ;# AFRL
+
+       mww 0xA0001030 0x00001000                               ;# QUADSPI_LPTR: deactivate CS after 4096 clocks when FIFO is full
+       mww 0xA0001000 0x03500008                               ;# QUADSPI_CR: PRESCALER=3, APMS=1, FTHRES=0, FSEL=0, DFM=0, SSHIFT=0, TCEN=1
+       mww 0xA0001004 0x00190100                               ;# QUADSPI_DCR: FSIZE=0x19, CSHT=0x01, CKMODE=0
+       mmw 0xA0001000 0x00000001 0                             ;# QUADSPI_CR: EN=1
+
+       # 1-line spi mode
+       mww 0xA0001014 0x000003F5                               ;# QUADSPI_CCR: FMODE=0x0, DMODE=0x0, DCYC=0x0, ADSIZE=0x0, ADMODE=0x0, IMODE=0x3, INSTR=RSTQIO
+       sleep 1
+
+       # memory-mapped read mode with 4-byte addresses
+       mww 0xA0001014 0x0D003513                               ;# QUADSPI_CCR: FMODE=0x3, DMODE=0x1, DCYC=0x0, ADSIZE=0x3, ADMODE=0x1, IMODE=0x1, INSTR=READ
+}
+
+$_TARGETNAME configure -event reset-init {
+       mww 0x40023C00 0x00000006                               ;# 6 WS for 192 MHz HCLK
+       sleep 1
+       mww 0x40023804 0x24003008                               ;# 192 MHz: PLLM=8, PLLN=192, PLLP=2
+       mww 0x40023808 0x00009400                               ;# APB1: /4, APB2: /2
+       mmw 0x40023800 0x01000000 0x00000000    ;# PLL on
+       sleep 1
+       mmw 0x40023808 0x00000002 0x00000000    ;# switch to PLL
+       sleep 1
+
+       adapter speed 4000
+
+       qspi_init
+}
diff --git a/tcl/board/stm32f746g-disco.cfg b/tcl/board/stm32f746g-disco.cfg
new file mode 100644 (file)
index 0000000..14e89e1
--- /dev/null
@@ -0,0 +1,69 @@
+# This is an STM32F746G discovery board with a single STM32F746NGH6 chip.
+# http://www.st.com/en/evaluation-tools/32f746gdiscovery.html
+
+# This is for using the onboard STLINK
+source [find interface/stlink.cfg]
+
+transport select hla_swd
+
+# increase working area to 256KB
+set WORKAREASIZE 0x40000
+
+# enable stmqspi
+set QUADSPI 1
+
+source [find target/stm32f7x.cfg]
+
+# QUADSPI initialization
+proc qspi_init { } {
+       global a
+       mmw 0x40023830 0x000007FF 0                             ;# RCC_AHB1ENR |= GPIOAEN-GPIOKEN (enable clocks)
+       mmw 0x40023838 0x00000002 0                             ;# RCC_AHB3ENR |= QSPIEN (enable clock)
+       sleep 1                                                                 ;# Wait for clock startup
+
+       # PB02: CLK, PB06: BK1_NCS, PD13: BK1_IO3, PE02: BK1_IO2, PD12: BK1_IO1, PD11: BK1_IO0
+
+       # PB06:AF10:V, PB02:AF09:V, PD13:AF09:V, PD12:AF09:V, PD11:AF09:V, PE02:AF09:V
+
+       # Port B: PB06:AF10:V, PB02:AF09:V
+       mmw 0x40020400 0x00002020 0x00001010    ;# MODER
+       mmw 0x40020408 0x00003030 0x00000000    ;# OSPEEDR
+       mmw 0x40020420 0x0A000900 0x05000600    ;# AFRL
+
+       # Port D: PD13:AF09:V, PD12:AF09:V, PD11:AF09:V
+       mmw 0x40020C00 0x0A800000 0x05400000    ;# MODER
+       mmw 0x40020C08 0x0FC00000 0x00000000    ;# OSPEEDR
+       mmw 0x40020C24 0x00999000 0x00666000    ;# AFRH
+
+       # Port E: PE02:AF09:V
+       mmw 0x40021000 0x00000020 0x00000010    ;# MODER
+       mmw 0x40021008 0x00000030 0x00000000    ;# OSPEEDR
+       mmw 0x40021020 0x00000900 0x00000600    ;# AFRL
+
+       mww 0xA0001030 0x00001000                               ;# QUADSPI_LPTR: deactivate CS after 4096 clocks when FIFO is full
+       mww 0xA0001000 0x03500008                               ;# QUADSPI_CR: PRESCALER=3, APMS=1, FTHRES=0, FSEL=0, DFM=0, SSHIFT=0, TCEN=1
+       mww 0xA0001004 0x00170100                               ;# QUADSPI_DCR: FSIZE=0x17, CSHT=0x01, CKMODE=0
+       mmw 0xA0001000 0x00000001 0                             ;# QUADSPI_CR: EN=1
+
+       # 1-line spi mode
+       mww 0xA0001014 0x000003F5                               ;# QUADSPI_CCR: FMODE=0x0, DMODE=0x0, DCYC=0x0, ADSIZE=0x0, ADMODE=0x0, IMODE=0x3, INSTR=RSTQIO
+       sleep 1
+
+       # memory-mapped read mode with 3-byte addresses
+       mww 0xA0001014 0x0D002503                               ;# QUADSPI_CCR: FMODE=0x3, DMODE=0x1, DCYC=0x0, ADSIZE=0x2, ADMODE=0x1, IMODE=0x1, INSTR=READ
+}
+
+$_TARGETNAME configure -event reset-init {
+       mww 0x40023C00 0x00000006                               ;# 6 WS for 192 MHz HCLK
+       sleep 1
+       mww 0x40023804 0x24003008                               ;# 192 MHz: PLLM=8, PLLN=192, PLLP=2
+       mww 0x40023808 0x00009400                               ;# APB1: /4, APB2: /2
+       mmw 0x40023800 0x01000000 0x00000000    ;# PLL on
+       sleep 1
+       mmw 0x40023808 0x00000002 0x00000000    ;# switch to PLL
+       sleep 1
+
+       adapter speed 4000
+
+       qspi_init
+}
diff --git a/tcl/board/stm32f769i-disco.cfg b/tcl/board/stm32f769i-disco.cfg
new file mode 100644 (file)
index 0000000..cc4334b
--- /dev/null
@@ -0,0 +1,79 @@
+# This is an STM32F769I discovery board with a single STM32F769NIH6 chip.
+# http://www.st.com/en/evaluation-tools/32f769idiscovery.html
+
+# This is for using the onboard STLINK
+source [find interface/stlink.cfg]
+
+transport select hla_swd
+
+# increase working area to 256KB
+set WORKAREASIZE 0x40000
+
+# enable stmqspi
+set QUADSPI 1
+
+source [find target/stm32f7x.cfg]
+
+# QUADSPI initialization
+proc qspi_init { } {
+       global a
+       mmw 0x40023830 0x000007FF 0                             ;# RCC_AHB1ENR |= GPIOAEN-GPIOKEN (enable clocks)
+       mmw 0x40023838 0x00000002 0                             ;# RCC_AHB3ENR |= QSPIEN (enable clock)
+       sleep 1                                                                 ;# Wait for clock startup
+
+       # PB02: CLK, PB06: BK1_NCS, PD13: BK1_IO3, PE02: BK1_IO2, PC10: BK1_IO1, PC09: BK1_IO0
+
+       # PB06:AF10:V, PB02:AF09:V, PC10:AF09:V, PC09:AF09:V, PD13:AF09:V, PE02:AF09:V
+
+       # Port B: PB06:AF10:V, PB02:AF09:V
+       mmw 0x40020400 0x00002020 0x00001010    ;# MODER
+       mmw 0x40020408 0x00003030 0x00000000    ;# OSPEEDR
+       mmw 0x40020420 0x0A000900 0x05000600    ;# AFRL
+
+       # Port C: PC10:AF09:V, PC09:AF09:V
+       mmw 0x40020800 0x00280000 0x00140000    ;# MODER
+       mmw 0x40020808 0x003C0000 0x00000000    ;# OSPEEDR
+       mmw 0x40020824 0x00000990 0x00000660    ;# AFRH
+
+       # Port D: PD13:AF09:V
+       mmw 0x40020C00 0x08000000 0x04000000    ;# MODER
+       mmw 0x40020C08 0x0C000000 0x00000000    ;# OSPEEDR
+       mmw 0x40020C24 0x00900000 0x00600000    ;# AFRH
+
+       # Port E: PE02:AF09:V
+       mmw 0x40021000 0x00000020 0x00000010    ;# MODER
+       mmw 0x40021008 0x00000030 0x00000000    ;# OSPEEDR
+       mmw 0x40021020 0x00000900 0x00000600    ;# AFRL
+
+       mww 0xA0001030 0x00001000                               ;# QUADSPI_LPTR: deactivate CS after 4096 clocks when FIFO is full
+       mww 0xA0001000 0x03500008                               ;# QUADSPI_CR: PRESCALER=3, APMS=1, FTHRES=0, FSEL=0, DFM=0, SSHIFT=0, TCEN=1
+       mww 0xA0001004 0x00190100                               ;# QUADSPI_DCR: FSIZE=0x19, CSHT=0x01, CKMODE=0
+       mmw 0xA0001000 0x00000001 0                             ;# QUADSPI_CR: EN=1
+
+       # exit qpi mode
+       mww 0xA0001014 0x000033f5                               ;# QUADSPI_CCR: FMODE=0x0, DMODE=0x0, DCYC=0x0, ADSIZE=0x0, ADMODE=0x0, IMODE=0x3, INSTR=RSTQIO
+
+       # 1-line memory-mapped read mode with 4-byte addresses
+       mww 0xA0001014 0x0D003513                               ;# QUADSPI_CCR: FMODE=0x3, DMODE=0x1, DCYC=0x0, ADSIZE=0x3, ADMODE=0x1, IMODE=0x1, INSTR=READ
+
+       # 4-line qpi mode
+       mww 0xA0001014 0x00003135                               ;# QUADSPI_CCR: FMODE=0x0, DMODE=0x0, DCYC=0x0, ADSIZE=0x3, ADMODE=0x1, IMODE=0x1, INSTR=EQIO
+
+       # 4-line memory-mapped read mode with 4-byte addresses
+       mww 0xA0001014 0x0F283FEC                               ;# QUADSPI_CCR: FMODE=0x3, DMODE=0x1, DCYC=0xA, ADSIZE=0x3, ADMODE=0x1, IMODE=0x1, INSTR=4READ4B
+}
+
+$_TARGETNAME configure -event reset-init {
+       mww 0x40023C00 0x00000006                               ;# 6 WS for 192 MHz HCLK
+       sleep 1
+       mww 0x40023804 0x24003008                               ;# 192 MHz: PLLM=8, PLLN=192, PLLP=2
+       mww 0x40023808 0x00009400                               ;# APB1: /4, APB2: /2
+       mmw 0x40023800 0x01000000 0x00000000    ;# PLL on
+       sleep 1
+       mmw 0x40023808 0x00000002 0x00000000    ;# switch to PLL
+       sleep 1
+
+       adapter speed 4000
+
+       qspi_init
+}
diff --git a/tcl/board/stm32h735g-disco.cfg b/tcl/board/stm32h735g-disco.cfg
new file mode 100644 (file)
index 0000000..405e470
--- /dev/null
@@ -0,0 +1,122 @@
+# This is a stm32h735g-dk with a single STM32H735IGK6 chip.
+# https://www.st.com/en/evaluation-tools/stm32h735g-dk.html
+#
+
+# This is for using the onboard STLINK
+source [find interface/stlink.cfg]
+
+transport select hla_swd
+
+set CHIPNAME stm32h735igk6
+
+# enable stmqspi
+if {![info exists OCTOSPI1]} {
+       set OCTOSPI1 1
+       set OCTOSPI2 0
+}
+
+source [find target/stm32h7x.cfg]
+
+# OCTOSPI initialization
+# octo: 8-line mode
+proc octospi_init { octo } {
+       global a b
+       mmw 0x58024540 0x000006FF 0                             ;# RCC_AHB4ENR |= GPIOAEN-GPIOKEN (enable clocks)
+       mmw 0x58024534 0x00284000 0                             ;# RCC_AHB3ENR |= IOMNGREN, OSPI2EN, OSPI1EN (enable clocks)
+       sleep 1                                                                 ;# Wait for clock startup
+
+       mww 0x5200B404 0x03010111                               ;# OCTOSPIM_P1CR: assign Port 1 to OCTOSPI1
+       mww 0x5200B408 0x00000000                               ;# OCTOSPIM_P2CR: disable Port 2
+
+       # PG06: OCSPI1_NCS, PF10: OCSPI1_CLK, PB02: OCSPI1_DQS, PD07: OCSPI1_IO7, PG09: OCSPI1_IO6, PD05: OCSPI1_IO5,
+       # PD04: OCSPI1_IO4, PD13: OCSPI1_IO3, PE02: OCSPI1_IO2, PD12: OCSPI1_IO1, PD11: OCSPI1_IO0
+
+       # PB02:AF10:V, PD13:AF09:V, PD12:AF09:V, PD11:AF09:V, PD07:AF10:V, PD05:AF10:V
+       # PD04:AF10:V, PE02:AF09:V, PF10:AF09:V, PG09:AF09:V, PG06:AF10:V
+       # Port B: PB02:AF10:V
+       mmw 0x58020400 0x00000020 0x00000010    ;# MODER
+       mmw 0x58020408 0x00000030 0x00000000    ;# OSPEEDR
+       mmw 0x5802040C 0x00000000 0x00000030    ;# PUPDR
+       mmw 0x58020420 0x00000A00 0x00000500    ;# AFRL
+       # Port D: PD13:AF09:V, PD12:AF09:V, PD11:AF09:V, PD07:AF10:V, PD05:AF10:V, PD04:AF10:V
+       mmw 0x58020C00 0x0A808A00 0x05404500    ;# MODER
+       mmw 0x58020C08 0x0FC0CF00 0x00000000    ;# OSPEEDR
+       mmw 0x58020C0C 0x00000000 0x0FC0CF00    ;# PUPDR
+       mmw 0x58020C20 0xA0AA0000 0x50550000    ;# AFRL
+       mmw 0x58020C24 0x00999000 0x00666000    ;# AFRH
+       # Port E: PE02:AF09:V
+       mmw 0x58021000 0x00000020 0x00000010    ;# MODER
+       mmw 0x58021008 0x00000030 0x00000000    ;# OSPEEDR
+       mmw 0x5802100C 0x00000000 0x00000030    ;# PUPDR
+       mmw 0x58021020 0x00000900 0x00000600    ;# AFRL
+       # Port F: PF10:AF09:V
+       mmw 0x58021400 0x00200000 0x00100000    ;# MODER
+       mmw 0x58021408 0x00300000 0x00000000    ;# OSPEEDR
+       mmw 0x5802140C 0x00000000 0x00300000    ;# PUPDR
+       mmw 0x58021424 0x00000900 0x00000600    ;# AFRH
+       # Port G: PG09:AF09:V, PG06:AF10:V
+       mmw 0x58021800 0x00082000 0x00041000    ;# MODER
+       mmw 0x58021808 0x000C3000 0x00000000    ;# OSPEEDR
+       mmw 0x5802180C 0x00000000 0x000C3000    ;# PUPDR
+       mmw 0x58021820 0x0A000000 0x05000000    ;# AFRL
+       mmw 0x58021824 0x00000090 0x00000060    ;# AFRH
+
+       # OCTOSPI1: memory-mapped 1-line read mode with 4-byte addresses
+       mww 0x52005130 0x00001000                               ;# OCTOSPI_LPTR: deactivate CS after 4096 clocks when FIFO is full
+       mww 0x52005000 0x3040000B                               ;# OCTOSPI_CR: FMODE=0x1, APMS=1, FTHRES=0, FSEL=0, DQM=0, TCEN=0
+       mww 0x52005008 0x01190100                               ;# OCTOSPI_DCR1: MTYP=0x1, FSIZE=0x19, CSHT=0x01, CKMODE=0, DLYBYP=0
+       mww 0x5200500C 0x00000005                               ;# OCTOSPI_DCR2: PRESCALER=5
+
+       mww 0x52005108 0x00000000                               ;# OCTOSPI_TCR: SSHIFT=0, DHQC=0, DCYC=0x0
+       mww 0x52005100 0x01003101                               ;# OCTOSPI_CCR: DMODE=0x1, ABMODE=0x0, ADSIZE=0x3, ADMODE=0x1, ISIZE=0x0, IMODE=0x1
+       mww 0x52005110 0x00000013                               ;# OCTOSPI_IR: INSTR=READ4B
+
+       flash probe $a                                                  ;# load configuration from CR, TCR, CCR, IR register values
+
+       if { $octo == 1 } {
+               stmqspi cmd $a 1 0x71 0x00 0x00 0x00 0x00                       ;# Read Conf. Reg. 2, addr 0x00000000: DOPI, SOPI bits
+               stmqspi cmd $a 0 0x06                                                           ;# Write Enable
+               stmqspi cmd $a 1 0x05                                                           ;# Read Status Register
+               stmqspi cmd $a 0 0x72 0x00 0x00 0x00 0x00 0x02          ;# Write Conf. Reg. 2, addr 0x00000000: DTR OPI enable
+
+               # OCTOSPI1: memory-mapped 8-line read mode with 4-byte addresses
+               mww 0x52005000 0x3040000B                               ;# OCTOSPI_CR: FMODE=0x3, APMS=1, FTHRES=0, FSEL=0, DQM=0, TCEN=1, EN=1
+               mww 0x52005108 0x10000006                               ;# OCTOSPI_TCR: SSHIFT=0, DHQC=1, DCYC=0x6
+               mww 0x52005100 0x2C003C1C                               ;# OCTOSPI_CCR: DTR, DMODE=0x4, ABMODE=0x0, ADSIZE=0x3, ADMODE=0x4, ISIZE=0x1, IMODE=0x4
+               mww 0x52005110 0x0000EE11                               ;# OCTOSPI_IR: INSTR=OCTA DTR Read
+
+               flash probe $a                                                  ;# reload configuration from CR, TCR, CCR, IR register values
+
+               stmqspi cmd $a 0 0x06                                                           ;# Write Enable
+               stmqspi cmd $a 1 0x05 0x00 0x00 0x00 0x00                       ;# Read Status Register (note dummy address in 8-line mode)
+               stmqspi cmd $a 0 0x04                                                           ;# Write Disable
+               stmqspi cmd $a 1 0x05 0x00 0x00 0x00 0x00                       ;# Read Status Register (note dummy address in 8-line mode)
+               stmqspi cmd $a 1 0x71 0x00 0x00 0x00 0x00                       ;# Read Conf. Reg. 2, addr 0x00000000: DOPI, SOPI bits
+       }
+}
+
+$_CHIPNAME.cpu0 configure -event reset-init {
+       global OCTOSPI1
+       global OCTOSPI2
+
+       mmw 0x52002000 0x00000004 0x0000000B    ;# FLASH_ACR: 4 WS for 192 MHZ HCLK
+
+       mmw 0x58024400 0x00000001 0x00000018    ;# RCC_CR: HSIDIV=1, HSI on
+       mmw 0x58024410 0x10000000 0xEE000007    ;# RCC_CFGR: MCO2=system, MCO2PRE=8, HSI as system clock
+       mww 0x58024418 0x00000040                               ;# RCC_D1CFGR: D1CPRE=1, D1PPRE=2, HPRE=1
+       mww 0x5802441C 0x00000440                               ;# RCC_D2CFGR: D2PPRE2=2, D2PPRE1=2
+       mww 0x58024420 0x00000040                               ;# RCC_D3CFGR: D3PPRE=2
+       mww 0x58024428 0x00000040                               ;# RCC_PPLCKSELR: DIVM3=0, DIVM2=0, DIVM1=4, PLLSRC=HSI
+       mmw 0x5802442C 0x0001000C 0x00000002    ;# RCC_PLLCFGR: PLL1RGE=8MHz to 16MHz, PLL1VCOSEL=wide
+       mww 0x58024430 0x01070217                               ;# RCC_PLL1DIVR: 192 MHz: DIVR1=2, DIVQ=8, DIVP1=2, DIVN1=24
+       mmw 0x58024400 0x01000000 0                             ;# RCC_CR: PLL1ON=1
+       sleep 1
+       mmw 0x58024410 0x00000003 0                             ;# RCC_CFGR: PLL1 as system clock
+       sleep 1
+
+       adapter speed 24000
+
+       if { $OCTOSPI1 } {
+               octospi_init 1
+       }
+}
diff --git a/tcl/board/stm32h745i-disco.cfg b/tcl/board/stm32h745i-disco.cfg
new file mode 100644 (file)
index 0000000..5adcfea
--- /dev/null
@@ -0,0 +1,45 @@
+# This is a stm32h745i-disco with a single STM32H745XIH6 chip.
+# www.st.com/en/product/stm32h745i-disco.html
+#
+
+# This is for using the onboard STLINK
+source [find interface/stlink.cfg]
+
+transport select hla_swd
+
+set CHIPNAME stm32h745xih6
+
+# enable stmqspi
+if {![info exists QUADSPI]} {
+       set QUADSPI 1
+}
+
+source [find target/stm32h7x_dual_bank.cfg]
+
+source [find board/stm32h7x_dual_qspi.cfg]
+
+$_CHIPNAME.cpu0 configure -event reset-init {
+       global QUADSPI
+
+       mmw 0x52002000 0x00000004 0x0000000B    ;# FLASH_ACR: 4 WS for 192 MHZ HCLK
+
+       mmw 0x58024400 0x00000001 0x00000018    ;# RCC_CR: HSIDIV=1, HSI on
+       mmw 0x58024410 0x10000000 0xEE000007    ;# RCC_CFGR: MCO2=system, MCO2PRE=8, HSI as system clock
+       mww 0x58024418 0x00000040                               ;# RCC_D1CFGR: D1CPRE=1, D1PPRE=2, HPRE=1
+       mww 0x5802441C 0x00000440                               ;# RCC_D2CFGR: D2PPRE2=2, D2PPRE1=2
+       mww 0x58024420 0x00000040                               ;# RCC_D3CFGR: D3PPRE=2
+       mww 0x58024428 0x00000040                               ;# RCC_PPLCKSELR: DIVM3=0, DIVM2=0, DIVM1=4, PLLSRC=HSI
+       mmw 0x5802442C 0x0001000C 0x00000002    ;# RCC_PLLCFGR: PLL1RGE=8MHz to 16MHz, PLL1VCOSEL=wide
+       mww 0x58024430 0x01070217                               ;# RCC_PLL1DIVR: 192 MHz: DIVR1=2, DIVQ=8, DIVP1=2, DIVN1=24
+       mmw 0x58024400 0x01000000 0                             ;# RCC_CR: PLL1ON=1
+       sleep 1
+       mmw 0x58024410 0x00000003 0                             ;# RCC_CFGR: PLL1 as system clock
+       sleep 1
+
+       adapter speed 24000
+
+       if { $QUADSPI } {
+               qspi_init 1
+       }
+}
+
diff --git a/tcl/board/stm32h747i-disco.cfg b/tcl/board/stm32h747i-disco.cfg
new file mode 100644 (file)
index 0000000..22fd74a
--- /dev/null
@@ -0,0 +1,136 @@
+# This is a stm32h747i-disco with a single STM32H747XIH6 chip.
+# www.st.com/en/product/stm32h747i-disco.html
+#
+
+# This is for using the onboard STLINK
+source [find interface/stlink.cfg]
+
+transport select hla_swd
+
+set CHIPNAME stm32h747xih6
+
+# enable stmqspi
+if {![info exists QUADSPI]} {
+       set QUADSPI 1
+}
+
+source [find target/stm32h7x_dual_bank.cfg]
+
+# QUADSPI initialization
+# qpi: 4-line mode
+proc qspi_init { qpi } {
+       global a
+       mmw 0x580244E0 0x000007FF 0                             ;# RCC_AHB4ENR |= GPIOAEN-GPIOKEN (enable clocks)
+       mmw 0x580244D4 0x00004000 0                             ;# RCC_AHB3ENR |= QSPIEN (enable clock)
+       sleep 1                                                                 ;# Wait for clock startup
+
+       # PG06: BK1_NCS, PB02: CLK, PF06: BK1_IO3, PF07: BK1_IO2, PF09: BK1_IO1, PD11: BK1_IO0,
+       # PG14: BK2_IO3, PG09: BK2_IO2, PH03: BK2_IO1, PH02: BK2_IO0
+
+       # PB02:AF09:V, PD11:AF09:V, PF09:AF10:V, PF07:AF09:V, PF06:AF09:V, PG14:AF09:H
+       # PG09:AF09:V, PG06:AF10:H, PH03:AF09:V, PH02:AF09:V
+
+       # Port B: PB02:AF09:V
+       mmw 0x58020400 0x00000020 0x00000010    ;# MODER
+       mmw 0x58020408 0x00000030 0x00000000    ;# OSPEEDR
+       mmw 0x58020420 0x00000900 0x00000600    ;# AFRL
+       # Port D: PD11:AF09:V
+       mmw 0x58020C00 0x00800000 0x00400000    ;# MODER
+       mmw 0x58020C08 0x00C00000 0x00000000    ;# OSPEEDR
+       mmw 0x58020C24 0x00009000 0x00006000    ;# AFRH
+       # Port F: PF09:AF10:V, PF07:AF09:V, PF06:AF09:V
+       mmw 0x58021400 0x0008A000 0x00045000    ;# MODER
+       mmw 0x58021408 0x000CF000 0x00000000    ;# OSPEEDR
+       mmw 0x58021420 0x99000000 0x66000000    ;# AFRL
+       mmw 0x58021424 0x000000A0 0x00000050    ;# AFRH
+       # Port G: PG14:AF09:H, PG09:AF09:V, PG06:AF10:H
+       mmw 0x58021800 0x20082000 0x10041000    ;# MODER
+       mmw 0x58021808 0x200C2000 0x10001000    ;# OSPEEDR
+       mmw 0x58021820 0x0A000000 0x05000000    ;# AFRL
+       mmw 0x58021824 0x09000090 0x06000060    ;# AFRH
+       # Port H: PH03:AF09:V, PH02:AF09:V
+       mmw 0x58021C00 0x000000A0 0x00000050    ;# MODER
+       mmw 0x58021C08 0x000000F0 0x00000000    ;# OSPEEDR
+       mmw 0x58021C20 0x00009900 0x00006600    ;# AFRL
+
+       # correct FSIZE is 0x1A, however, this causes trouble when
+       # reading the last bytes at end of bank in *memory mapped* mode
+
+       # for dual flash mode 2 * mt25ql512
+       mww 0x52005000 0x05500058                               ;# QUADSPI_CR: PRESCALER=5, APMS=1, FTHRES=0, FSEL=0, DFM=1, SSHIFT=1, TCEN=1
+       mww 0x52005004 0x001A0200                               ;# QUADSPI_DCR: FSIZE=0x1A, CSHT=0x02, CKMODE=0
+
+       mww 0x52005030 0x00001000                               ;# QUADSPI_LPTR: deactivate CS after 4096 clocks when FIFO is full
+       mww 0x52005014 0x0D002503                               ;# QUADSPI_CCR: FMODE=0x3, DMODE=0x1, DCYC=0x0, ADSIZE=0x3, ADMODE=0x1, IMODE=0x1
+       mmw 0x52005000 0x00000001 0                             ;# QUADSPI_CR: EN=1
+
+       # Exit QPI mode
+       mmw 0x52005000 0x00000002 0                             ;# QUADSPI_CR: ABORT=1
+       mww 0x52005014 0x000003F5                               ;# QUADSPI_CCR: FMODE=0x0, DMODE=0x0, DCYC=0x0, ADSIZE=0x0, ADMODE=0x0, IMODE=0x3, INSTR=Exit QPI
+       sleep 1
+
+       if { $qpi == 1 } {
+               # Write Enable
+               mmw 0x52005000 0x00000002 0                     ;# QUADSPI_CR: ABORT=1
+               mww 0x52005014 0x00000106                       ;# QUADSPI_CCR: FMODE=0x0, DMODE=0x0, DCYC=0x0, ADSIZE=0x0, ADMODE=0x0, IMODE=0x1, INSTR=Write Enable
+               sleep 1
+
+               # Configure dummy clocks via volatile configuration register
+               mmw 0x52005000 0x00000002 0                     ;# QUADSPI_CR: ABORT=1
+               mww 0x52005010 0x00000001                       ;# QUADSPI_DLR: 2 data bytes
+               mww 0x52005014 0x01000181                       ;# QUADSPI_CCR: FMODE=0x0, DMODE=0x1, DCYC=0x0, ADSIZE=0x0, ADMODE=0x0, IMODE=0x1, INSTR=Write Volatile Conf. Reg.
+               mwh 0x52005020 0xABAB                           ;# QUADSPI_DR: 0xAB 0xAB for 10 dummy clocks
+               sleep 1
+
+               # Write Enable
+               mmw 0x52005000 0x00000002 0                     ;# QUADSPI_CR: ABORT=1
+               mww 0x52005014 0x00000106                       ;# QUADSPI_CCR: FMODE=0x0, DMODE=0x0, DCYC=0x0, ADSIZE=0x0, ADMODE=0x0, IMODE=0x1, INSTR=Write Enable
+               sleep 1
+
+               # Enable QPI mode via enhanced volatile configuration register
+               mmw 0x52005000 0x00000002 0                     ;# QUADSPI_CR: ABORT=1
+               mww 0x52005010 0x00000001                       ;# QUADSPI_DLR: 2 data bytes
+               mww 0x52005014 0x01000161                       ;# QUADSPI_CCR: FMODE=0x0, DMODE=0x1, DCYC=0x0, ADSIZE=0x0, ADMODE=0x0, IMODE=0x1, INSTR=Write Enhanced Conf. Reg.
+               mwh 0x52005020 0x3F3F                           ;# QUADSPI_DR: 0x3F 0x3F to enable QPI and DPI mode
+               sleep 1
+
+               # Enter QPI mode
+               mmw 0x52005000 0x00000002 0                     ;# QUADSPI_CR: ABORT=1
+               mww 0x52005014 0x00000135                       ;# QUADSPI_CCR: FMODE=0x0, DMODE=0x0, DCYC=0x0, ADSIZE=0x0, ADMODE=0x0, IMODE=0x1, INSTR=Enter QPI
+               sleep 1
+
+               # memory-mapped fast read mode with 4-byte addresses and 10 dummy cycles (for read only)
+               mmw 0x52005000 0x00000002 0                     ;# QUADSPI_CR: ABORT=1
+               mww 0x52005014 0x0F283FEC                       ;# QUADSPI_CCR: FMODE=0x3, DMODE=0x3, DCYC=0xA, ADSIZE=0x3, ADMODE=0x3, IMODE=0x3, INSTR=Fast READ
+       } else {
+               # memory-mapped read mode with 4-byte addresses
+               mmw 0x52005000 0x00000002 0                     ;# QUADSPI_CR: ABORT=1
+               mww 0x52005014 0x0D003513                       ;# QUADSPI_CCR: FMODE=0x3, DMODE=0x1, DCYC=0x0, ADSIZE=0x3, ADMODE=0x1, IMODE=0x1, INSTR=READ
+       }
+}
+
+$_CHIPNAME.cpu0 configure -event reset-init {
+       global QUADSPI
+
+       mmw 0x52002000 0x00000004 0x0000000B    ;# FLASH_ACR: 4 WS for 192 MHZ HCLK
+
+       mmw 0x58024400 0x00000001 0x00000018    ;# RCC_CR: HSIDIV=1, HSI on
+       mmw 0x58024410 0x10000000 0xEE000007    ;# RCC_CFGR: MCO2=system, MCO2PRE=8, HSI as system clock
+       mww 0x58024418 0x00000040                               ;# RCC_D1CFGR: D1CPRE=1, D1PPRE=2, HPRE=1
+       mww 0x5802441C 0x00000440                               ;# RCC_D2CFGR: D2PPRE2=2, D2PPRE1=2
+       mww 0x58024420 0x00000040                               ;# RCC_D3CFGR: D3PPRE=2
+       mww 0x58024428 0x00000040                               ;# RCC_PPLCKSELR: DIVM3=0, DIVM2=0, DIVM1=4, PLLSRC=HSI
+       mmw 0x5802442C 0x0001000C 0x00000002    ;# RCC_PLLCFGR: PLL1RGE=8MHz to 16MHz, PLL1VCOSEL=wide
+       mww 0x58024430 0x01070217                               ;# RCC_PLL1DIVR: 192 MHz: DIVR1=2, DIVQ=8, DIVP1=2, DIVN1=24
+       mmw 0x58024400 0x01000000 0                             ;# RCC_CR: PLL1ON=1
+       sleep 1
+       mmw 0x58024410 0x00000003 0                             ;# RCC_CFGR: PLL1 as system clock
+       sleep 1
+
+       adapter speed 24000
+
+       if { $QUADSPI } {
+               qspi_init 1
+       }
+}
+
diff --git a/tcl/board/stm32h750b-disco.cfg b/tcl/board/stm32h750b-disco.cfg
new file mode 100644 (file)
index 0000000..e606203
--- /dev/null
@@ -0,0 +1,45 @@
+# This is a stm32h750b-dk with a single STM32H750XBH6 chip.
+# www.st.com/en/product/stm32h750b-dk.html
+#
+
+# This is for using the onboard STLINK
+source [find interface/stlink.cfg]
+
+transport select hla_swd
+
+set CHIPNAME stm32h750xbh6
+
+# enable stmqspi
+if {![info exists QUADSPI]} {
+       set QUADSPI 1
+}
+
+source [find target/stm32h7x.cfg]
+
+source [find board/stm32h7x_dual_qspi.cfg]
+
+$_CHIPNAME.cpu0 configure -event reset-init {
+       global QUADSPI
+
+       mmw 0x52002000 0x00000004 0x0000000B    ;# FLASH_ACR: 4 WS for 192 MHZ HCLK
+
+       mmw 0x58024400 0x00000001 0x00000018    ;# RCC_CR: HSIDIV=1, HSI on
+       mmw 0x58024410 0x10000000 0xEE000007    ;# RCC_CFGR: MCO2=system, MCO2PRE=8, HSI as system clock
+       mww 0x58024418 0x00000040                               ;# RCC_D1CFGR: D1CPRE=1, D1PPRE=2, HPRE=1
+       mww 0x5802441C 0x00000440                               ;# RCC_D2CFGR: D2PPRE2=2, D2PPRE1=2
+       mww 0x58024420 0x00000040                               ;# RCC_D3CFGR: D3PPRE=2
+       mww 0x58024428 0x00000040                               ;# RCC_PPLCKSELR: DIVM3=0, DIVM2=0, DIVM1=4, PLLSRC=HSI
+       mmw 0x5802442C 0x0001000C 0x00000002    ;# RCC_PLLCFGR: PLL1RGE=8MHz to 16MHz, PLL1VCOSEL=wide
+       mww 0x58024430 0x01070217                               ;# RCC_PLL1DIVR: 192 MHz: DIVR1=2, DIVQ=8, DIVP1=2, DIVN1=24
+       mmw 0x58024400 0x01000000 0                             ;# RCC_CR: PLL1ON=1
+       sleep 1
+       mmw 0x58024410 0x00000003 0                             ;# RCC_CFGR: PLL1 as system clock
+       sleep 1
+
+       adapter speed 24000
+
+       if { $QUADSPI } {
+               qspi_init 1
+       }
+}
+
diff --git a/tcl/board/stm32h7b3i-disco.cfg b/tcl/board/stm32h7b3i-disco.cfg
new file mode 100644 (file)
index 0000000..e5512ea
--- /dev/null
@@ -0,0 +1,128 @@
+# This is a stm32h7b3i-dk with a single STM32H7B3LIH6Q chip.
+# https://www.st.com/en/evaluation-tools/stm32h7b3i-dk.html
+#
+
+# This is for using the onboard STLINK
+source [find interface/stlink.cfg]
+
+transport select hla_swd
+
+set CHIPNAME stm32h7b3lih6q
+
+# enable stmqspi
+if {![info exists OCTOSPI1]} {
+       set OCTOSPI1 1
+       set OCTOSPI2 0
+}
+
+source [find target/stm32h7x_dual_bank.cfg]
+
+# OCTOSPI initialization
+# octo: 8-line mode
+proc octospi_init { octo } {
+       global a b
+       mmw 0x58024540 0x000007FF 0                             ;# RCC_AHB4ENR |= GPIOAEN-GPIOKEN (enable clocks)
+       mmw 0x58024534 0x00284000 0                             ;# RCC_AHB3ENR |= IOMNGREN, OSPI2EN, OSPI1EN (enable clocks)
+       sleep 1                                                                 ;# Wait for clock startup
+
+       mww 0x5200B404 0x03010111                               ;# OCTOSPIM_P1CR: assign Port 1 to OCTOSPI1
+       mww 0x5200B408 0x00000000                               ;# OCTOSPIM_P2CR: disable Port 2
+
+       # PG06: OCSPI1_NCS, PB02: OCSPI1_CLK, PC05: OCSPI1_DQS, PD07: OCSPI1_IO7, PG09: OCSPI1_IO6, PH03: OCSPI1_IO5,
+       # PC01: OCSPI1_IO4, PF06: OCSPI1_IO3, PF07: OCSPI1_IO2, PF09: OCSPI1_IO1, PD11: OCSPI1_IO0
+
+       # PB02:AF09:V, PC05:AF10:V, PC01:AF10:V, PD11:AF09:V, PD07:AF10:V, PF09:AF10:V
+       # PF07:AF10:V, PF06:AF10:V, PG09:AF09:V, PG06:AF10:V, PH03:AF09:V
+       # Port B: PB02:AF09:V
+       mmw 0x58020400 0x00000020 0x00000010    ;# MODER
+       mmw 0x58020408 0x00000030 0x00000000    ;# OSPEEDR
+       mmw 0x5802040C 0x00000000 0x00000030    ;# PUPDR
+       mmw 0x58020420 0x00000900 0x00000600    ;# AFRL
+       # Port C: PC05:AF10:V, PC01:AF10:V
+       mmw 0x58020800 0x00000808 0x00000404    ;# MODER
+       mmw 0x58020808 0x00000C0C 0x00000000    ;# OSPEEDR
+       mmw 0x5802080C 0x00000000 0x00000C0C    ;# PUPDR
+       mmw 0x58020820 0x00A000A0 0x00500050    ;# AFRL
+       # Port D: PD11:AF09:V, PD07:AF10:V
+       mmw 0x58020C00 0x00808000 0x00404000    ;# MODER
+       mmw 0x58020C08 0x00C0C000 0x00000000    ;# OSPEEDR
+       mmw 0x58020C0C 0x00000000 0x00C0C000    ;# PUPDR
+       mmw 0x58020C20 0xA0000000 0x50000000    ;# AFRL
+       mmw 0x58020C24 0x00009000 0x00006000    ;# AFRH
+       # Port F: PF09:AF10:V, PF07:AF10:V, PF06:AF10:V
+       mmw 0x58021400 0x0008A000 0x00045000    ;# MODER
+       mmw 0x58021408 0x000CF000 0x00000000    ;# OSPEEDR
+       mmw 0x5802140C 0x00000000 0x000CF000    ;# PUPDR
+       mmw 0x58021420 0xAA000000 0x55000000    ;# AFRL
+       mmw 0x58021424 0x000000A0 0x00000050    ;# AFRH
+       # Port G: PG09:AF09:V, PG06:AF10:V
+       mmw 0x58021800 0x00082000 0x00041000    ;# MODER
+       mmw 0x58021808 0x000C3000 0x00000000    ;# OSPEEDR
+       mmw 0x5802180C 0x00000000 0x000C3000    ;# PUPDR
+       mmw 0x58021820 0x0A000000 0x05000000    ;# AFRL
+       mmw 0x58021824 0x00000090 0x00000060    ;# AFRH
+       # Port H: PH03:AF09:V
+       mmw 0x58021C00 0x00000080 0x00000040    ;# MODER
+       mmw 0x58021C08 0x000000C0 0x00000000    ;# OSPEEDR
+       mmw 0x58021C0C 0x00000000 0x000000C0    ;# PUPDR
+       mmw 0x58021C20 0x00009000 0x00006000    ;# AFRL
+
+       # OCTOSPI1: memory-mapped 1-line read mode with 4-byte addresses
+       mww 0x52005130 0x00001000                               ;# OCTOSPI_LPTR: deactivate CS after 4096 clocks when FIFO is full
+       mww 0x52005000 0x3040000B                               ;# OCTOSPI_CR: FMODE=0x1, APMS=1, FTHRES=0, FSEL=0, DQM=0, TCEN=0
+       mww 0x52005008 0x01190100                               ;# OCTOSPI_DCR1: MTYP=0x1, FSIZE=0x19, CSHT=0x01, CKMODE=0, DLYBYP=0
+       mww 0x5200500C 0x00000005                               ;# OCTOSPI_DCR2: PRESCALER=5
+
+       mww 0x52005108 0x00000000                               ;# OCTOSPI_TCR: SSHIFT=0, DHQC=0, DCYC=0x0
+       mww 0x52005100 0x01003101                               ;# OCTOSPI_CCR: DMODE=0x1, ABMODE=0x0, ADSIZE=0x3, ADMODE=0x1, ISIZE=0x0, IMODE=0x1
+       mww 0x52005110 0x00000013                               ;# OCTOSPI_IR: INSTR=READ4B
+
+       flash probe $a                                                  ;# load configuration from CR, TCR, CCR, IR register values
+
+       if { $octo == 1 } {
+               stmqspi cmd $a 1 0x71 0x00 0x00 0x00 0x00                       ;# Read Conf. Reg. 2, addr 0x00000000: DOPI, SOPI bits
+               stmqspi cmd $a 0 0x06                                                           ;# Write Enable
+               stmqspi cmd $a 1 0x05                                                           ;# Read Status Register
+               stmqspi cmd $a 0 0x72 0x00 0x00 0x00 0x00 0x02          ;# Write Conf. Reg. 2, addr 0x00000000: DTR OPI enable
+
+               # OCTOSPI1: memory-mapped 8-line read mode with 4-byte addresses
+               mww 0x52005000 0x3040000B                               ;# OCTOSPI_CR: FMODE=0x3, APMS=1, FTHRES=0, FSEL=0, DQM=0, TCEN=1, EN=1
+               mww 0x52005108 0x10000006                               ;# OCTOSPI_TCR: SSHIFT=0, DHQC=1, DCYC=0x6
+               mww 0x52005100 0x2C003C1C                               ;# OCTOSPI_CCR: DTR, DMODE=0x4, ABMODE=0x0, ADSIZE=0x3, ADMODE=0x4, ISIZE=0x1, IMODE=0x4
+               mww 0x52005110 0x0000EE11                               ;# OCTOSPI_IR: INSTR=OCTA DTR Read
+
+               flash probe $a                                                  ;# reload configuration from CR, TCR, CCR, IR register values
+
+               stmqspi cmd $a 0 0x06                                                           ;# Write Enable
+               stmqspi cmd $a 1 0x05 0x00 0x00 0x00 0x00                       ;# Read Status Register (note dummy address in 8-line mode)
+               stmqspi cmd $a 0 0x04                                                           ;# Write Disable
+               stmqspi cmd $a 1 0x05 0x00 0x00 0x00 0x00                       ;# Read Status Register (note dummy address in 8-line mode)
+               stmqspi cmd $a 1 0x71 0x00 0x00 0x00 0x00                       ;# Read Conf. Reg. 2, addr 0x00000000: DOPI, SOPI bits
+       }
+}
+
+$_CHIPNAME.cpu0 configure -event reset-init {
+       global OCTOSPI1
+       global OCTOSPI2
+
+       mmw 0x52002000 0x00000004 0x0000000B    ;# FLASH_ACR: 4 WS for 192 MHZ HCLK
+
+       mmw 0x58024400 0x00000001 0x00000018    ;# RCC_CR: HSIDIV=1, HSI on
+       mmw 0x58024410 0x10000000 0xEE000007    ;# RCC_CFGR: MCO2=system, MCO2PRE=8, HSI as system clock
+       mww 0x58024418 0x00000040                               ;# RCC_D1CFGR: D1CPRE=1, D1PPRE=2, HPRE=1
+       mww 0x5802441C 0x00000440                               ;# RCC_D2CFGR: D2PPRE2=2, D2PPRE1=2
+       mww 0x58024420 0x00000040                               ;# RCC_D3CFGR: D3PPRE=2
+       mww 0x58024428 0x00000040                               ;# RCC_PPLCKSELR: DIVM3=0, DIVM2=0, DIVM1=4, PLLSRC=HSI
+       mmw 0x5802442C 0x0001000C 0x00000002    ;# RCC_PLLCFGR: PLL1RGE=8MHz to 16MHz, PLL1VCOSEL=wide
+       mww 0x58024430 0x01070217                               ;# RCC_PLL1DIVR: 192 MHz: DIVR1=2, DIVQ=8, DIVP1=2, DIVN1=24
+       mmw 0x58024400 0x01000000 0                             ;# RCC_CR: PLL1ON=1
+       sleep 1
+       mmw 0x58024410 0x00000003 0                             ;# RCC_CFGR: PLL1 as system clock
+       sleep 1
+
+       adapter speed 24000
+
+       if { $OCTOSPI1 } {
+               octospi_init 1
+       }
+}
diff --git a/tcl/board/stm32h7x_dual_qspi.cfg b/tcl/board/stm32h7x_dual_qspi.cfg
new file mode 100644 (file)
index 0000000..bdff9c1
--- /dev/null
@@ -0,0 +1,90 @@
+# stm32h754i-disco and stm32h750b-dk dual quad qspi.
+
+# QUADSPI initialization
+# qpi: 4-line mode
+proc qspi_init { qpi } {
+       global a
+       mmw 0x580244E0 0x000007FF 0                             ;# RCC_AHB4ENR |= GPIOAEN-GPIOKEN (enable clocks)
+       mmw 0x580244D4 0x00004000 0                             ;# RCC_AHB3ENR |= QSPIEN (enable clock)
+       sleep 1                                                                 ;# Wait for clock startup
+
+       # PG06: BK1_NCS, PF10: CLK, PF06: BK1_IO3, PF07: BK1_IO2, PF09: BK1_IO1, PD11: BK1_IO0,
+       # PG14: BK2_IO3, PG09: BK2_IO2, PH03: BK2_IO1, PH02: BK2_IO0
+
+       # PD11:AF09:V, PF10:AF09:V, PF09:AF10:V, PF07:AF09:V, PF06:AF09:V, PG14:AF09:H
+       # PG09:AF09:V, PG06:AF10:H, PH03:AF09:V, PH02:AF09:V
+
+       # Port D: PD11:AF09:V
+       mmw 0x58020C00 0x00800000 0x00400000    ;# MODER
+       mmw 0x58020C08 0x00C00000 0x00000000    ;# OSPEEDR
+       mmw 0x58020C24 0x00009000 0x00006000    ;# AFRH
+       # Port F: PF10:AF09:V, PF09:AF10:V, PF07:AF09:V, PF06:AF09:V
+       mmw 0x58021400 0x0028A000 0x00145000    ;# MODER
+       mmw 0x58021408 0x003CF000 0x00000000    ;# OSPEEDR
+       mmw 0x58021420 0x99000000 0x66000000    ;# AFRL
+       mmw 0x58021424 0x000009A0 0x00000650    ;# AFRH
+       # Port G: PG14:AF09:H, PG09:AF09:V, PG06:AF10:H
+       mmw 0x58021800 0x20082000 0x10041000    ;# MODER
+       mmw 0x58021808 0x200C2000 0x10001000    ;# OSPEEDR
+       mmw 0x58021820 0x0A000000 0x05000000    ;# AFRL
+       mmw 0x58021824 0x09000090 0x06000060    ;# AFRH
+       # Port H: PH03:AF09:V, PH02:AF09:V
+       mmw 0x58021C00 0x000000A0 0x00000050    ;# MODER
+       mmw 0x58021C08 0x000000F0 0x00000000    ;# OSPEEDR
+       mmw 0x58021C20 0x00009900 0x00006600    ;# AFRL
+
+       # correct FSIZE is 0x1A, however, this causes trouble when
+       # reading the last bytes at end of bank in *memory mapped* mode
+
+       # for dual flash mode 2 * mt25ql512
+       mww 0x52005000 0x05500058                               ;# QUADSPI_CR: PRESCALER=5, APMS=1, FTHRES=0, FSEL=0, DFM=1, SSHIFT=1, TCEN=1
+       mww 0x52005004 0x001A0200                               ;# QUADSPI_DCR: FSIZE=0x1A, CSHT=0x02, CKMODE=0
+
+       mww 0x52005030 0x00001000                               ;# QUADSPI_LPTR: deactivate CS after 4096 clocks when FIFO is full
+       mww 0x52005014 0x0D002503                               ;# QUADSPI_CCR: FMODE=0x3, DMODE=0x1, DCYC=0x0, ADSIZE=0x3, ADMODE=0x1, IMODE=0x1
+       mmw 0x52005000 0x00000001 0                             ;# QUADSPI_CR: EN=1
+
+       # Exit QPI mode
+       mmw 0x52005000 0x00000002 0                             ;# QUADSPI_CR: ABORT=1
+       mww 0x52005014 0x000003F5                               ;# QUADSPI_CCR: FMODE=0x0, DMODE=0x0, DCYC=0x0, ADSIZE=0x0, ADMODE=0x0, IMODE=0x3, INSTR=Exit QPI
+       sleep 1
+
+       if { $qpi == 1 } {
+               # Write Enable
+               mmw 0x52005000 0x00000002 0                     ;# QUADSPI_CR: ABORT=1
+               mww 0x52005014 0x00000106                       ;# QUADSPI_CCR: FMODE=0x0, DMODE=0x0, DCYC=0x0, ADSIZE=0x0, ADMODE=0x0, IMODE=0x1, INSTR=Write Enable
+               sleep 1
+
+               # Configure dummy clocks via volatile configuration register
+               mmw 0x52005000 0x00000002 0                     ;# QUADSPI_CR: ABORT=1
+               mww 0x52005010 0x00000001                       ;# QUADSPI_DLR: 2 data bytes
+               mww 0x52005014 0x01000181                       ;# QUADSPI_CCR: FMODE=0x0, DMODE=0x1, DCYC=0x0, ADSIZE=0x0, ADMODE=0x0, IMODE=0x1, INSTR=Write Volatile Conf. Reg.
+               mwh 0x52005020 0xABAB                           ;# QUADSPI_DR: 0xAB 0xAB for 10 dummy clocks
+               sleep 1
+
+               # Write Enable
+               mmw 0x52005000 0x00000002 0                     ;# QUADSPI_CR: ABORT=1
+               mww 0x52005014 0x00000106                       ;# QUADSPI_CCR: FMODE=0x0, DMODE=0x0, DCYC=0x0, ADSIZE=0x0, ADMODE=0x0, IMODE=0x1, INSTR=Write Enable
+               sleep 1
+
+               # Enable QPI mode via enhanced volatile configuration register
+               mmw 0x52005000 0x00000002 0                     ;# QUADSPI_CR: ABORT=1
+               mww 0x52005010 0x00000001                       ;# QUADSPI_DLR: 2 data bytes
+               mww 0x52005014 0x01000161                       ;# QUADSPI_CCR: FMODE=0x0, DMODE=0x1, DCYC=0x0, ADSIZE=0x0, ADMODE=0x0, IMODE=0x1, INSTR=Write Enhanced Conf. Reg.
+               mwh 0x52005020 0x3F3F                           ;# QUADSPI_DR: 0x3F 0x3F to enable QPI and DPI mode
+               sleep 1
+
+               # Enter QPI mode
+               mmw 0x52005000 0x00000002 0                     ;# QUADSPI_CR: ABORT=1
+               mww 0x52005014 0x00000135                       ;# QUADSPI_CCR: FMODE=0x0, DMODE=0x0, DCYC=0x0, ADSIZE=0x0, ADMODE=0x0, IMODE=0x1, INSTR=Enter QPI
+               sleep 1
+
+               # memory-mapped fast read mode with 4-byte addresses and 10 dummy cycles (for read only)
+               mmw 0x52005000 0x00000002 0                     ;# QUADSPI_CR: ABORT=1
+               mww 0x52005014 0x0F283FEC                       ;# QUADSPI_CCR: FMODE=0x3, DMODE=0x3, DCYC=0xA, ADSIZE=0x3, ADMODE=0x3, IMODE=0x3, INSTR=Fast READ
+       } else {
+               # memory-mapped read mode with 4-byte addresses
+               mmw 0x52005000 0x00000002 0                     ;# QUADSPI_CR: ABORT=1
+               mww 0x52005014 0x0D003513                       ;# QUADSPI_CCR: FMODE=0x3, DMODE=0x1, DCYC=0x0, ADSIZE=0x3, ADMODE=0x1, IMODE=0x1, INSTR=READ
+       }
+}
diff --git a/tcl/board/stm32l476g-disco.cfg b/tcl/board/stm32l476g-disco.cfg
new file mode 100644 (file)
index 0000000..dab2fe1
--- /dev/null
@@ -0,0 +1,56 @@
+# This is an STM32L476G discovery board with a single STM32L476VGT6 chip.
+# http://www.st.com/en/evaluation-tools/32l476gdiscovery.html
+
+# This is for using the onboard STLINK
+source [find interface/stlink.cfg]
+
+transport select hla_swd
+
+# increase working area to 96KB
+set WORKAREASIZE 0x18000
+
+# enable stmqspi
+set QUADSPI 1
+
+source [find target/stm32l4x.cfg]
+
+# QUADSPI initialization
+proc qspi_init { } {
+       global a
+       mmw 0x4002104C 0x000001FF 0                             ;# RCC_AHB2ENR |= GPIOAEN-GPIOIEN (enable clocks)
+       mmw 0x40021050 0x00000100 0                             ;# RCC_AHB3ENR |= QSPIEN (enable clock)
+       sleep 1                                                                 ;# Wait for clock startup
+
+       # PE11: NCS, PE10: CLK, PE15: BK1_IO3, PE14: BK1_IO2, PE13: BK1_IO1, PE12: BK1_IO0
+
+       # PE15:AF10:V, PE14:AF10:V, PE13:AF10:V, PE12:AF10:V, PE11:AF10:V, PE10:AF10:V
+
+       # Port E: PE15:AF10:V, PE14:AF10:V, PE13:AF10:V, PE12:AF10:V, PE11:AF10:V, PE10:AF10:V
+       mmw 0x48001000 0xAAA00000 0x55500000    ;# MODER
+       mmw 0x48001008 0xFFF00000 0x00000000    ;# OSPEEDR
+       mmw 0x48001024 0xAAAAAA00 0x55555500    ;# AFRH
+
+       mww 0xA0001030 0x00001000                               ;# QUADSPI_LPTR: deactivate CS after 4096 clocks when FIFO is full
+       mww 0xA0001000 0x01500008                               ;# QUADSPI_CR: PRESCALER=1, APMS=1, FTHRES=0, FSEL=0, DFM=0, SSHIFT=0, TCEN=1
+       mww 0xA0001004 0x00170100                               ;# QUADSPI_DCR: FSIZE=0x17, CSHT=0x01, CKMODE=0
+       mmw 0xA0001000 0x00000001 0                             ;# QUADSPI_CR: EN=1
+
+       # memory-mapped read mode with 3-byte addresses
+       mww 0xA0001014 0x0D002503                               ;# QUADSPI_CCR: FMODE=0x3, DMODE=0x1, DCYC=0x0, ADSIZE=0x2, ADMODE=0x1, IMODE=0x1, INSTR=READ
+}
+
+$_TARGETNAME configure -event reset-init {
+       mmw 0x40022000 0x00000004 0x00000003    ;# 4 WS for 72 MHz HCLK
+       sleep 1
+       mmw 0x40021000 0x00000100 0x00000000    ;# HSI on
+       mww 0x4002100C 0x01002432                               ;# 72 MHz: PLLREN=1, PLLM=4, PLLN=36, PLLR=2, HSI
+       mww 0x40021008 0x00008001                               ;# always HSI, APB1: /1, APB2: /1
+       mmw 0x40021000 0x01000000 0x00000000    ;# PLL on
+       sleep 1
+       mmw 0x40021008 0x00000003 0x00000000    ;# switch to PLL
+       sleep 1
+
+       adapter speed 4000
+
+       qspi_init
+}
diff --git a/tcl/board/stm32l496g-disco.cfg b/tcl/board/stm32l496g-disco.cfg
new file mode 100644 (file)
index 0000000..a93b07c
--- /dev/null
@@ -0,0 +1,66 @@
+# This is an STM32L496G discovery board with a single STM32L496AGI6 chip.
+# http://www.st.com/en/evaluation-tools/32l496gdiscovery.html
+
+# This is for using the onboard STLINK
+source [find interface/stlink.cfg]
+
+transport select hla_swd
+
+# increase working area to 96KB
+set WORKAREASIZE 0x18000
+
+# enable stmqspi
+set QUADSPI 1
+
+source [find target/stm32l4x.cfg]
+
+# QUADSPI initialization
+proc qspi_init { } {
+       global a
+       mmw 0x4002104C 0x000001FF 0                             ;# RCC_AHB2ENR |= GPIOAEN-GPIOIEN (enable clocks)
+       mmw 0x40021050 0x00000100 0                             ;# RCC_AHB3ENR |= QSPIEN (enable clock)
+       sleep 1                                                                 ;# Wait for clock startup
+
+       # PB11: BK1_NCS, PA03: CLK, PA06: BK1_IO3, PA07: BK1_IO2, PB00: BK1_IO1, PB01: BK1_IO0
+
+       # PA07:AF10:V, PA06:AF10:V, PA03:AF10:V, PB11:AF10:V, PB01:AF10:V, PB00:AF10:V
+
+       # Port A: PA07:AF10:V, PA06:AF10:V, PA03:AF10:V
+       mmw 0x48000000 0x0000A080 0x00005040    ;# MODER
+       mmw 0x48000008 0x0000F0C0 0x00000000    ;# OSPEEDR
+       mmw 0x48000020 0xAA00A000 0x55005000    ;# AFRL
+
+       # Port B: PB11:AF10:V, PB01:AF10:V, PB00:AF10:V
+       mmw 0x48000400 0x0080000A 0x00400005    ;# MODER
+       mmw 0x48000408 0x00C0000F 0x00000000    ;# OSPEEDR
+       mmw 0x48000420 0x000000AA 0x00000055    ;# AFRL
+       mmw 0x48000424 0x0000A000 0x00005000    ;# AFRH
+
+       mww 0xA0001030 0x00001000                               ;# QUADSPI_LPTR: deactivate CS after 4096 clocks when FIFO is full
+       mww 0xA0001000 0x01500008                               ;# QUADSPI_CR: PRESCALER=1, APMS=1, FTHRES=0, FSEL=0, DFM=0, SSHIFT=0, TCEN=1
+       mww 0xA0001004 0x00160100                               ;# QUADSPI_DCR: FSIZE=0x16, CSHT=0x01, CKMODE=0
+       mmw 0xA0001000 0x00000001 0                             ;# QUADSPI_CR: EN=1
+
+       # 1-line spi mode
+       mww 0xA0001014 0x000003F5                               ;# QUADSPI_CCR: FMODE=0x0, DMODE=0x0, DCYC=0x0, ADSIZE=0x0, ADMODE=0x0, IMODE=0x3, INSTR=RSTQIO
+       sleep 1
+
+       # memory-mapped read mode with 3-byte addresses
+       mww 0xA0001014 0x0D002503                               ;# QUADSPI_CCR: FMODE=0x3, DMODE=0x1, DCYC=0x0, ADSIZE=0x2, ADMODE=0x1, IMODE=0x1, INSTR=READ
+}
+
+$_TARGETNAME configure -event reset-init {
+       mmw 0x40022000 0x00000004 0x00000003    ;# 4 WS for 72 MHz HCLK
+       sleep 1
+       mmw 0x40021000 0x00000100 0x00000000    ;# HSI on
+       mww 0x4002100C 0x01002432                               ;# 72 MHz: PLLREN=1, PLLM=4, PLLN=36, PLLR=2, HSI
+       mww 0x40021008 0x00008001                               ;# always HSI, APB1: /1, APB2: /1
+       mmw 0x40021000 0x01000000 0x00000000    ;# PLL on
+       sleep 1
+       mmw 0x40021008 0x00000003 0x00000000    ;# switch to PLL
+       sleep 1
+
+       adapter speed 4000
+
+       qspi_init
+}
diff --git a/tcl/board/stm32l4p5g-disco.cfg b/tcl/board/stm32l4p5g-disco.cfg
new file mode 100644 (file)
index 0000000..d7420ed
--- /dev/null
@@ -0,0 +1,130 @@
+# This is a STM32L4P5G discovery board with a single STM32L4R9AGI6 chip.
+# http://www.st.com/en/evaluation-tools/stm32l4p5g-dk.html
+
+# This is for using the onboard STLINK
+source [find interface/stlink.cfg]
+
+transport select hla_swd
+
+# increase working area to 96KB
+set WORKAREASIZE 0x18000
+
+# enable stmqspi
+set OCTOSPI1 1
+set OCTOSPI2 0
+
+source [find target/stm32l4x.cfg]
+
+# OCTOSPI initialization
+# octo: 8-line mode
+proc octospi_init { octo } {
+       global a b
+       mmw 0x4002104C 0x001001FF 0                             ;# RCC_AHB2ENR |= OSPIMEN, GPIOAEN-GPIOIEN (enable clocks)
+       mmw 0x40021050 0x00000300 0                             ;# RCC_AHB3ENR |= OSPI2EN, OSPI1EN (enable clocks)
+       mmw 0x40021058 0x10000000 0                             ;# RCC_APB1ENR1 |= PWREN (enable clock)
+       sleep 1                                                                 ;# Wait for clock startup
+
+       mmw 0x40007004 0x00000200 0                             ;# PWR_CR2 |= IOSV (required for use of GPOIG, cf. RM0432)
+
+       mww 0x50061C04 0x07050333                               ;# OCTOSPIM_P1CR: assign Port 1 to OCTOSPI2
+       mww 0x50061C08 0x03010111                               ;# OCTOSPIM_P2CR: assign Port 2 to OCTOSPI1
+
+       # PE11: P1_NCS, PE10: P1_CLK, PG06: P1_DQS, PD07: P1_IO7, PC03: P1_IO6, PD05: P1_IO5
+       # PD04: P1_IO4, PA06: P1_IO3, PA07: P1_IO2, PE13: P1_IO1, PE11: P1_IO0
+
+       # PA07:AF10:V, PA06:AF10:V, PC03:AF10:V, PD07:AF10:V, PD05:AF10:V, PD04:AF10:V
+       # PE13:AF10:V, PE12:AF10:V, PE11:AF10:V, PE10:AF10:V, PG06:AF03:V
+
+       # Port A: PA07:AF10:V, PA06:AF10:V
+       mmw 0x48000000 0x0000A000 0x00005000    ;# MODER
+       mmw 0x48000008 0x0000F000 0x00000000    ;# OSPEEDR
+       mmw 0x4800000C 0x00000000 0x0000F000    ;# PUPDR
+       mmw 0x48000020 0xAA000000 0x55000000    ;# AFRL
+       # Port C: PC03:AF10:V
+       mmw 0x48000800 0x00000080 0x00000040    ;# MODER
+       mmw 0x48000808 0x000000C0 0x00000000    ;# OSPEEDR
+       mmw 0x4800080C 0x00000000 0x000000C0    ;# PUPDR
+       mmw 0x48000820 0x0000A000 0x00005000    ;# AFRL
+       # Port D: PD07:AF10:V, PD05:AF10:V, PD04:AF10:V
+       mmw 0x48000C00 0x00008A00 0x00004500    ;# MODER
+       mmw 0x48000C08 0x0000CF00 0x00000000    ;# OSPEEDR
+       mmw 0x48000C0C 0x00000000 0x0000CF00    ;# PUPDR
+       mmw 0x48000C20 0xA0AA0000 0x50550000    ;# AFRL
+       # Port E: PE13:AF10:V, PE12:AF10:V, PE11:AF10:V, PE10:AF10:V
+       mmw 0x48001000 0x0AA00000 0x05500000    ;# MODER
+       mmw 0x48001008 0x0FF00000 0x00000000    ;# OSPEEDR
+       mmw 0x4800100C 0x00000000 0x0FF00000    ;# PUPDR
+       mmw 0x48001024 0x00AAAA00 0x00555500    ;# AFRH
+       # Port G: PG06:AF03:V
+       mmw 0x48001800 0x00002000 0x00001000    ;# MODER
+       mmw 0x48001808 0x00003000 0x00000000    ;# OSPEEDR
+       mmw 0x4800180C 0x00000000 0x00003000    ;# PUPDR
+       mmw 0x48001820 0x03000000 0x0C000000    ;# AFRL
+
+       # PG12: P2_NCS, PF04: P2_CLK, PF12: P2_DQS, PG10: P2_IO7, PG09: P2_IO6, PG01: P2_IO5
+       # PG00: P2_IO4, PF03: P2_IO3, PF02: P2_IO2, PF01: P2_IO1, PF00: P2_IO0
+
+       # PF12:AF05:V, PF04:AF05:V, PF03:AF05:V, PF02:AF05:V, PF01:AF05:V, PF00:AF05:V
+       # PG12:AF05:V, PG10:AF05:V, PG09:AF05:V, PG01:AF05:V, PG00:AF05:V
+
+       # Port F: PF12:AF05:V, PF04:AF05:V, PF03:AF05:V, PF02:AF05:V, PF01:AF05:V, PF00:AF05:V
+       mmw 0x48001400 0x020002AA 0x01000155    ;# MODER
+       mmw 0x48001408 0x030003FF 0x00000000    ;# OSPEEDR
+       mmw 0x4800140C 0x00000000 0x030003FF    ;# PUPDR
+       mmw 0x48001420 0x00055555 0x000AAAAA    ;# AFRL
+       mmw 0x48001424 0x00050000 0x000A0000    ;# AFRH
+       # Port G: PG12:AF05:V, PG10:AF05:V, PG09:AF05:V, PG01:AF05:V, PG00:AF05:V
+       mmw 0x48001800 0x0228000A 0x01140005    ;# MODER
+       mmw 0x48001808 0x033C000F 0x00000000    ;# OSPEEDR
+       mmw 0x4800180C 0x00000000 0x033C000F    ;# PUPDR
+       mmw 0x48001820 0x00000055 0x000000AA    ;# AFRL
+       mmw 0x48001824 0x00050550 0x000A0AA0    ;# AFRH
+
+       # OCTOSPI1: memory-mapped 1-line read mode with 4-byte addresses
+       mww 0xA0001130 0x00001000                               ;# OCTOSPI_LPTR: deactivate CS after 4096 clocks when FIFO is full
+       mww 0xA0001000 0x3040000B                               ;# OCTOSPI_CR: FMODE=0x1, APMS=1, FTHRES=0, FSEL=0, DQM=0, TCEN=0
+       mww 0xA0001008 0x01190100                               ;# OCTOSPI_DCR1: MTYP=0x1, FSIZE=0x19, CSHT=0x01, CKMODE=0, DLYBYP=0
+       mww 0xA000100C 0x00000001                               ;# OCTOSPI_DCR2: PRESCALER=1
+
+       mww 0xA0001108 0x00000000                               ;# OCTOSPI_TCR: SSHIFT=0, DHQC=0, DCYC=0x0
+       mww 0xA0001100 0x01003101                               ;# OCTOSPI_CCR: DMODE=0x1, ABMODE=0x0, ADSIZE=0x3, ADMODE=0x1, ISIZE=0x0, IMODE=0x1
+       mww 0xA0001110 0x00000013                               ;# OCTOSPI_IR: INSTR=READ4B
+
+       if { $octo == 1 } {
+               stmqspi cmd $a 1 0x71 0x00 0x00 0x00 0x00                       ;# Read Conf. Reg. 2, addr 0x00000000: DOPI, SOPI bits
+               stmqspi cmd $a 0 0x06                                                           ;# Write Enable
+               stmqspi cmd $a 1 0x05                                                           ;# Read Status Register
+               stmqspi cmd $a 0 0x72 0x00 0x00 0x00 0x00 0x02          ;# Write Conf. Reg. 2, addr 0x00000000: DTR OPI enable
+
+               # OCTOSPI1: memory-mapped 8-line read mode with 4-byte addresses
+               mww 0xA0001000 0x3040000B                               ;# OCTOSPI_CR: FMODE=0x3, APMS=1, FTHRES=0, FSEL=0, DQM=0, TCEN=1, EN=1
+               mww 0xA0001108 0x10000006                               ;# OCTOSPI_TCR: SSHIFT=0, DHQC=1, DCYC=0x6
+               mww 0xA0001100 0x2C003C1C                               ;# OCTOSPI_CCR: DTR, DMODE=0x4, ABMODE=0x0, ADSIZE=0x3, ADMODE=0x4, ISIZE=0x1, IMODE=0x4
+               mww 0xA0001110 0x0000EE11                               ;# OCTOSPI_IR: INSTR=OCTA DTR Read
+
+               flash probe $a                                                  ;# reload configuration from CR, TCR, CCR, IR register values
+
+               stmqspi cmd $a 0 0x06                                                           ;# Write Enable
+               stmqspi cmd $a 1 0x05 0x00 0x00 0x00 0x00                       ;# Read Status Register (note dummy address in 8-line mode)
+               stmqspi cmd $a 0 0x04                                                           ;# Write Disable
+               stmqspi cmd $a 1 0x05 0x00 0x00 0x00 0x00                       ;# Read Status Register (note dummy address in 8-line mode)
+               stmqspi cmd $a 1 0x71 0x00 0x00 0x00 0x00                       ;# Read Conf. Reg. 2, addr 0x00000000: DOPI, SOPI bits
+       }
+}
+
+$_TARGETNAME configure -event reset-init {
+       mmw 0x40022000 0x00000003 0x0000000C    ;# 3 WS for 72 MHz HCLK
+       sleep 1
+       mmw 0x40021000 0x00000100 0x00000000    ;# HSI on
+       mww 0x4002100C 0x01002432                               ;# RCC_PLLCFGR 72 MHz: PLLREN=1, PLLM=4, PLLN=36, PLLR=2, HSI
+       mww 0x40021008 0x00008001                               ;# always HSI, APB1: /1, APB2: /1
+       mmw 0x40021000 0x01000000 0x00000000    ;# PLL on
+       sleep 1
+       mmw 0x40021008 0x00000003 0x00000000    ;# switch to PLL
+       sleep 1
+
+       adapter speed 24000
+
+       octospi_init 1
+}
+
diff --git a/tcl/board/stm32l4r9i-disco.cfg b/tcl/board/stm32l4r9i-disco.cfg
new file mode 100644 (file)
index 0000000..70ed199
--- /dev/null
@@ -0,0 +1,100 @@
+# This is a STM32L4R9I discovery board with a single STM32L4R9AII6 chip.
+# http://www.st.com/en/evaluation-tools/32l4r9idiscovery.html
+
+# This is for using the onboard STLINK
+source [find interface/stlink.cfg]
+
+transport select hla_swd
+
+# increase working area to 96KB
+set WORKAREASIZE 0x18000
+
+# enable stmqspi
+set OCTOSPI1 1
+set OCTOSPI2 0
+
+source [find target/stm32l4x.cfg]
+
+# OCTOSPI initialization
+# octo: 8-line mode
+proc octospi_init { octo } {
+       global a b
+       mmw 0x4002104C 0x001001FF 0                             ;# RCC_AHB2ENR |= OSPIMEN, GPIOAEN-GPIOIEN (enable clocks)
+       mmw 0x40021050 0x00000300 0                             ;# RCC_AHB3ENR |= OSPI2EN, OSPI1EN (enable clocks)
+       mmw 0x40021058 0x10000000 0                             ;# RCC_APB1ENR1 |= PWREN (enable clock)
+       sleep 1                                                                 ;# Wait for clock startup
+
+       mmw 0x40007004 0x00000200 0                             ;# PWR_CR2 |= IOSV (required for use of GPOIG, cf. RM0432)
+
+       mww 0x50061C04 0x00000000                               ;# OCTOSPIM_P1CR: disable Port 1
+       mww 0x50061C08 0x03010111                               ;# OCTOSPIM_P2CR: assign Port 2 to OCTOSPI1
+
+       # PG12: P2_NCS, PI06: P2_CLK, PG15: P2_DQS, PG10: P2_IO7, PG09: P2_IO6, PH10: P2_IO5,
+       # PH09: P2_IO4, PH08: P2_IO3, PI09: P2_IO2, PI10: P2_IO1, PI11: P2_IO0
+
+       # PG15:AF05:V, PG12:AF05:V, PG10:AF05:V, PG09:AF05:V, PH10:AF05:V, PH09:AF05:V
+       # PH08:AF05:V, PI11:AF05:V, PI10:AF05:V, PI09:AF05:V, PI06:AF05:V
+
+       # Port G: PG15:AF05:V, PG12:AF05:V, PG10:AF05:V, PG09:AF05:V
+       mmw 0x48001800 0x82280000 0x41140000    ;# MODER
+       mmw 0x48001808 0xC33C0000 0x00000000    ;# OSPEEDR
+       mmw 0x48001824 0x50050550 0xA00A0AA0    ;# AFRH
+
+       # Port H: PH10:AF05:V, PH09:AF05:V, PH08:AF05:V
+       mmw 0x48001C00 0x002A0000 0x00150000    ;# MODER
+       mmw 0x48001C08 0x003F0000 0x00000000    ;# OSPEEDR
+       mmw 0x48001C24 0x00000555 0x00000AAA    ;# AFRH
+
+       # Port I: PI11:AF05:V, PI10:AF05:V, PI09:AF05:V, PI06:AF05:V
+       mmw 0x48002000 0x00A82000 0x00541000    ;# MODER
+       mmw 0x48002008 0x00FC3000 0x00000000    ;# OSPEEDR
+       mmw 0x48002020 0x05000000 0x0A000000    ;# AFRL
+       mmw 0x48002024 0x00005550 0x0000AAA0    ;# AFRH
+
+       # OCTOSPI1: memory-mapped 1-line read mode with 4-byte addresses
+       mww 0xA0001130 0x00001000                               ;# OCTOSPI_LPTR: deactivate CS after 4096 clocks when FIFO is full
+       mww 0xA0001000 0x3040000B                               ;# OCTOSPI_CR: FMODE=0x1, APMS=1, FTHRES=0, FSEL=0, DQM=0, TCEN=0
+       mww 0xA0001008 0x01190100                               ;# OCTOSPI_DCR1: MTYP=0x1, FSIZE=0x19, CSHT=0x01, CKMODE=0, DLYBYP=0
+       mww 0xA000100C 0x00000001                               ;# OCTOSPI_DCR2: PRESCALER=1
+
+       mww 0xA0001108 0x00000000                               ;# OCTOSPI_TCR: SSHIFT=0, DHQC=0, DCYC=0x0
+       mww 0xA0001100 0x01003101                               ;# OCTOSPI_CCR: DMODE=0x1, ABMODE=0x0, ADSIZE=0x3, ADMODE=0x1, ISIZE=0x0, IMODE=0x1
+       mww 0xA0001110 0x00000013                               ;# OCTOSPI_IR: INSTR=READ4B
+
+       if { $octo == 1 } {
+               stmqspi cmd $a 1 0x71 0x00 0x00 0x00 0x00                       ;# Read Conf. Reg. 2, addr 0x00000000: DOPI, SOPI bits
+               stmqspi cmd $a 0 0x06                                                           ;# Write Enable
+               stmqspi cmd $a 1 0x05                                                           ;# Read Status Register
+               stmqspi cmd $a 0 0x72 0x00 0x00 0x00 0x00 0x02          ;# Write Conf. Reg. 2, addr 0x00000000: DTR OPI enable
+
+               # OCTOSPI1: memory-mapped 8-line read mode with 4-byte addresses
+               mww 0xA0001000 0x3040000B                               ;# OCTOSPI_CR: FMODE=0x3, APMS=1, FTHRES=0, FSEL=0, DQM=0, TCEN=1, EN=1
+               mww 0xA0001108 0x10000006                               ;# OCTOSPI_TCR: SSHIFT=0, DHQC=1, DCYC=0x6
+               mww 0xA0001100 0x2C003C1C                               ;# OCTOSPI_CCR: DTR, DMODE=0x4, ABMODE=0x0, ADSIZE=0x3, ADMODE=0x4, ISIZE=0x1, IMODE=0x4
+               mww 0xA0001110 0x0000EE11                               ;# OCTOSPI_IR: INSTR=OCTA DTR Read
+
+               flash probe $a                                                  ;# reload configuration from CR, TCR, CCR, IR register values
+
+               stmqspi cmd $a 0 0x06                                                           ;# Write Enable
+               stmqspi cmd $a 1 0x05 0x00 0x00 0x00 0x00                       ;# Read Status Register (note dummy address in 8-line mode)
+               stmqspi cmd $a 0 0x04                                                           ;# Write Disable
+               stmqspi cmd $a 1 0x05 0x00 0x00 0x00 0x00                       ;# Read Status Register (note dummy address in 8-line mode)
+               stmqspi cmd $a 1 0x71 0x00 0x00 0x00 0x00                       ;# Read Conf. Reg. 2, addr 0x00000000: DOPI, SOPI bits
+       }
+}
+
+$_TARGETNAME configure -event reset-init {
+       mmw 0x40022000 0x00000003 0x0000000C    ;# 3 WS for 72 MHz HCLK
+       sleep 1
+       mmw 0x40021000 0x00000100 0x00000000    ;# HSI on
+       mww 0x4002100C 0x01002432                               ;# RCC_PLLCFGR 72 MHz: PLLREN=1, PLLM=4, PLLN=36, PLLR=2, HSI
+       mww 0x40021008 0x00008001                               ;# always HSI, APB1: /1, APB2: /1
+       mmw 0x40021000 0x01000000 0x00000000    ;# PLL on
+       sleep 1
+       mmw 0x40021008 0x00000003 0x00000000    ;# switch to PLL
+       sleep 1
+
+       adapter speed 4000
+
+       octospi_init 1
+}
index b95e783c526d5669d58278ee3e83300305b4f482..15875336ee7c06a5c19a8bde68721dffc1781d75 100644 (file)
@@ -52,6 +52,12 @@ flash bank $_FLASHNAME stm32f2x 0 0 0 0 $_TARGETNAME
 
 flash bank $_CHIPNAME.otp stm32f2x 0x1fff7800 0 0 0 $_TARGETNAME
 
+if { [info exists QUADSPI] && $QUADSPI } {
+   set a [llength [flash list]]
+   set _QSPINAME $_CHIPNAME.qspi
+   flash bank $_QSPINAME stmqspi 0x90000000 0 0 0 $_TARGETNAME 0xA0001000
+}
+
 # JTAG speed should be <= F_CPU/6. F_CPU after reset is 16MHz, so use F_JTAG = 2MHz
 #
 # Since we may be running of an RC oscilator, we crank down the speed a
index 6ad4b65f8f568dd5b07063b9842000a67456d54a..3c7679de253b5d22c17445edfcf579870d6a4dc9 100644 (file)
@@ -12,7 +12,7 @@ if { [info exists CHIPNAME] } {
    set _CHIPNAME stm32f7x
 }
 
-   set _ENDIAN little
+set _ENDIAN little
 
 # Work-area is a space in RAM used for flash programming
 # By default use 128kB
@@ -64,6 +64,12 @@ flash bank $_CHIPNAME.otp stm32f2x 0x1ff0f000 0 0 0 $_TARGETNAME
 #     the Flash via ITCM alias as virtual
 flash bank $_CHIPNAME.itcm-flash.alias virtual 0x00200000 0 0 0 $_TARGETNAME $_FLASHNAME
 
+if { [info exists QUADSPI] && $QUADSPI } {
+   set a [llength [flash list]]
+   set _QSPINAME $_CHIPNAME.qspi
+   flash bank $_QSPINAME stmqspi 0x90000000 0 0 0 $_TARGETNAME 0xA0001000
+}
+
 # adapter speed should be <= F_CPU/6. F_CPU after reset is 16MHz, so use F_JTAG = 2MHz
 adapter speed 2000
 
index 763a7857a08cb6cffc104658d93805a66b7dc9f7..8258e50312784bc49756e8c1633afc204fa5644e 100644 (file)
@@ -104,6 +104,23 @@ if {[set $_CHIPNAME.DUAL_CORE]} {
 # Make sure that cpu0 is selected
 targets $_CHIPNAME.cpu0
 
+if { [info exists QUADSPI] && $QUADSPI } {
+   set a [llength [flash list]]
+   set _QSPINAME $_CHIPNAME.qspi
+   flash bank $_QSPINAME stmqspi 0x90000000 0 0 0 $_CHIPNAME.cpu0 0x52005000
+} else {
+   if { [info exists OCTOSPI1] && $OCTOSPI1 } {
+      set a [llength [flash list]]
+      set _OCTOSPINAME1 $_CHIPNAME.octospi1
+      flash bank $_OCTOSPINAME1 stmqspi 0x90000000 0 0 0 $_CHIPNAME.cpu0 0x52005000
+   }
+   if { [info exists OCTOSPI2] && $OCTOSPI2 } {
+      set b [llength [flash list]]
+      set _OCTOSPINAME2 $_CHIPNAME.octospi2
+      flash bank $_OCTOSPINAME2 stmqspi 0x70000000 0 0 0 $_CHIPNAME.cpu0 0x5200A000
+   }
+}
+
 # Clock after reset is HSI at 64 MHz, no need of PLL
 adapter speed 1800
 
index 46e6f7e0db9fe8c92b380054dead5c9f482e6fc9..7f08f3c4bab79ac1135c54fc4ae10eec5a5244e3 100644 (file)
@@ -50,6 +50,23 @@ $_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE
 set _FLASHNAME $_CHIPNAME.flash
 flash bank $_FLASHNAME stm32l4x 0 0 0 0 $_TARGETNAME
 
+if { [info exists QUADSPI] && $QUADSPI } {
+   set a [llength [flash list]]
+   set _QSPINAME $_CHIPNAME.qspi
+   flash bank $_QSPINAME stmqspi 0x90000000 0 0 0 $_TARGETNAME 0xA0001000
+} else {
+   if { [info exists OCTOSPI1] && $OCTOSPI1 } {
+      set a [llength [flash list]]
+      set _OCTOSPINAME1 $_CHIPNAME.octospi1
+      flash bank $_OCTOSPINAME1 stmqspi 0x90000000 0 0 0 $_TARGETNAME 0xA0001000
+   }
+   if { [info exists OCTOSPI2] && $OCTOSPI2 } {
+      set b [llength [flash list]]
+      set _OCTOSPINAME2 $_CHIPNAME.octospi2
+      flash bank $_OCTOSPINAME2 stmqspi 0x70000000 0 0 0 $_TARGETNAME 0xA0001400
+   }
+}
+
 # Common knowledges tells JTAG speed should be <= F_CPU/6.
 # F_CPU after reset is MSI 4MHz, so use F_JTAG = 500 kHz to stay on
 # the safe side.

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)