Flash, FRAM and EEPROM driver for STM32 QUAD-/OCTOSPI interface
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.