This is an automated email from Gerrit.

"Andreas Bolsch <hyphen0br...@gmail.com>" just uploaded a new patch set to 
Gerrit, which you can find at https://review.openocd.org/c/openocd/+/7469

-- gerrit

commit 6b02c19060097ffd40e1e8cf4e1707fdb63bd048
Author: Andreas Bolsch <hyphen0br...@gmail.com>
Date:   Wed Oct 12 18:30:25 2022 +0200

    Octal mode up for stmqspi driver
    
    New octal devices introduced a real mess. Instruction formats may be
    one byte, two identical bytes, two bytes with second one complemented.
    Sometimes RDSR requires a (dummy) address, and sometimes the status
    register is output only after some dummy bytes, similar for RDID.
    This patch settles this for the following device families:
    ATXP, MX25LM, MX66LM, GD25LX, S28H
    
    Change-Id: I3c810f157b0f45ef2abdd6f52e04471a0aedd819
    Signed-off-by: Andreas Bolsch <hyphen0br...@gmail.com>

diff --git a/contrib/cmspi_gpio/gpio_conf_stm32.pl 
b/contrib/cmspi_gpio/gpio_conf_stm32.pl
new file mode 100755
index 0000000000..de770100a1
--- /dev/null
+++ b/contrib/cmspi_gpio/gpio_conf_stm32.pl
@@ -0,0 +1,775 @@
+#!/usr/bin/perl
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Copyright (C) 2022 by Andreas Bolsch <andreas.bol...@mni.thm.de
+#
+# 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: NCS1, NCS2, CLK, then BK_1 followed by. If single NCS for 
both, omit NCS2
+# For octal flash: NCS, CLK, DQS (only for stmqspi, not for cmspi), 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";
+
+# stm32f407ccu6 board w/ cmspi
+#$GPIO_BASE = 0x40020000;
+#$Conf = "PA04:PP:M, 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 = "PE11:AF10:V, PE10:AF10:V, PG06:AF03:V, PD07:AF10:V, PC03:AF10:V, 
PD05:AF10:V, "
+#      . "PD04:AF10:V, PA06:AF10:V, PA07:AF10:V, PE13:AF10:V, PE12:AF10:V, ";
+#$Conf = "PG12:AF05:V, PF04:AF05:V, PF12:AF05:V, PG10:AF05:V, PG09:AF05:V, 
PG01:AF05:V, "
+#      . "PG00:AF05:V  PF03:AF05:V, PF02:AF05:V, PF01:AF05:V, PF00:AF05:V, "
+# w/ cmspi
+#$Conf = "PG12:PPUP:M, PF04:PPUP:V, PG10:INPUP:V, PG09:INPUP:V, PG01:INPUP:V, "
+#      . "PG00:INPUP:V, PF03:INPUP:V, PF02:INPUP:V, PF01:INPUP:V, 
PF00:INPUP: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, PD11:AF09:V, PB02:AF09:V, PC11:AF09:V, PD13:AF09:V, 
PE02:AF09:V, PD12: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";
+# dquad w/ cmspi
+#$Conf = "PB10:PPUP:M, PC11:PPUP:M, PB02:PPUP:V, PD13:INPUP:V, PE02:INPUP:V, 
PD12:INPUP:V, PD11:INPUP:V, "
+#      . "PE10:INPUP:V, PE09:INPUP:V, PE08:INPUP:V, PE07:INPUP:V";
+
+# nucleo-h7a3zi dual quad
+#$GPIO_BASE = 0x58020000;
+#$Conf = "PB10:AF09:V, PC11:AF09:V, PB02:AF09:V, PD13:AF09:V, PE02:AF09:V, 
PD12:AF09:V, PD11: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-h7a3zi octal
+#$GPIO_BASE = 0x58020000;
+#$Conf = "PB06:AF10:V, PA03:AF03:V, PB02:AF10:V, PE10:AF10:V, PC03:AF11:V, 
PC02:AF11:V, "
+#      . "PE07:AF10:V, PD13:AF09:V, PE02:AF09:V, PF09:AF10:V, PF08:AF10:V";
+#$Conf = "PE11:AF11:V, PA03:AF03:V, PB02:AF10:V, PE10:AF10:V, PC03:AF11:V, 
PC02:AF11:V, "
+#      . "PE07:AF10:V, PD13:AF09:V, PE02:AF09:V, PF09:AF10:V, PF08:AF10:V";
+#$Conf = "PC11:AF09:V, PA03:AF03:V, PB02:AF10:V, PE10:AF10:V, PC03:AF11:V, 
PC02:AF11:V, "
+#      ." PE07:AF10:V, PD13:AF09:V, PE02:AF09:V, PF09:AF10:V, PF08:AF10:V";
+# w/ cmspi
+#$Conf = "PB06:PPUP:M, PA03:PPUP:M, PE10:INPUP:V, PC03:INPUP:V, PC02:INPUP:V, "
+#      . "PE07:INPUP:V, PD13:INPUP:V, PE02:INPUP:V, PF09:INPUP:V, 
PF08:INPUP:V";
+#$Conf = "PE11:PPUP:M, PA03:PPUP:M, PE10:INPUP:V, PC03:INPUP:V, PC02:INPUP:V, "
+#      . "PE07:INPUP:V, PD13:INPUP:V, PE02:INPUP:V, PF09:INPUP:V, 
PF08:INPUP:V";
+#$Conf = "PC11:PPUP:M, PA03:PPUP:M, PE10:INPUP:V, PC03:INPUP:V, PC02:INPUP:V, "
+#      . "PE07:INPUP:V, PD13:INPUP:V, PE02:INPUP:V, PF09:INPUP:V, 
PF08:INPUP:V";
+
+# nucleo-h723zg octal/dual quad
+#$GPIO_BASE = 0x58020000;
+#$Conf = "PB06:AF10:M, PA03:AF03:V, PB02:AF10:V, PE10:AF10:V, PD06:AF10:V, 
PD05:AF10:V, "
+#      . "PE07:AF10:V, PD13:AF09:V, PE02:AF09:V, PF09:AF10:V, PF08:AF10:V";
+#$Conf = "PG12:AF03:M, PA03:AF03:V, PB02:AF10:V, PE10:AF10:V, PD06:AF10:V, 
PD05:AF10:V, "
+#      . "PE07:AF10:V, PD13:AF09:V, PE02:AF09:V, PF09:AF10:V, PF08:AF10:V";
+#$Conf = "PE11:AF11:M, PC11:AF09:M, PA03:AF03:V, PB02:AF10:V, PE10:AF10:V, 
PD06:AF10:V, "
+#      . "PD05:AF10:V, PE07:AF10:V, PD13:AF09:V, PE02:AF09:V, PF09:AF10:V, 
PF08:AF10:V";
+# w/ cmspi
+#$Conf = "PB06:PPUP:M, PA03:PPUP:V, PE10:INPUP:V, PD06:INPUP:V, PD05:INPUP:V, "
+#      . "PE07:INPUP:V, PD13:INPUP:V, PE02:INPUP:V, PF09:INPUP:V, 
PF08:INPUP:V";
+#$Conf = "PG12:PPUP:M, PA03:PPUP:V, PE10:INPUP:V, PD06:INPUP:V, PD05:INPUP:V, "
+#      . "PE07:INPUP:V, PD13:INPUP:V, PE02:INPUP:V, PF09:INPUP:V, 
PF08:INPUP:V";
+#$Conf = "PE11:PPUP:M, PC11:PPUP:M, PA03:PPUP:V, PE10:INPUP:V, PD06:INPUP:V, 
PD05:INPUP:V, "
+#      . "PE07:INPUP:V, PD13:INPUP:V, PE02:INPUP:V, PF09:INPUP:V, 
PF08:INPUP:V";
+
+# nucleo-l4r5zi 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";
+# w/ cmspi
+#$Conf = "PA02:PPUP:M, PE11:PPUP:M, PE10:PPUP:V, PD07:INPDO:V, PD06:INPDO:V, 
PD05:INPDO:V, PD04:INPDO:V, "
+#      . "PE15:INPDO:V, PE14:INPDO:V, PE13:INPDO:V, PE12:INPDO:V";
+
+# nucleo-l552ze-q dual quad 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 dual quad mode
+#$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:V, 
PB01:INPUP:V";
+#$Conf = "PA01:PPUP:H, PA04:PPUP:V, PA08:INPUP:V, PB14:INPUP:V, PB04:INPUP:V, 
PB05:INPUP:V";
+# w/ cmspi dual quad mode
+#$Conf = "PA00:PPUP:H, PA01:PPUP:H, PA04:PPUP:V, PB03:INPUP:V, PA10:INPUP:V, 
PB11:INPUP:V, "
+#      . "PB01:INPUP:V, PA08:INPUP:V, PB14:INPUP:V, PB04:INPUP:V, 
PB05:INPUP:V";
+
+# nucleo-g474re dual quad 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/h750b-disco dual quad 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:V, PG09:AF09:V, PH03:AF09:V, PH02:AF09:V";
+# w/ cmspi
+#$Conf = "PG06:PPUP:H, PF10:PPUP:V, PF06:INPUP:V, PF07:INPUP:V, PF09:INPUP:V, 
PD11:INPUP:V, "
+#      . "PG14:INPUP:V, PG09:INPUP:V, PH03:INPUP:V, PH02:INPUP:V";
+
+# stm32h747i-disco dual quad 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:V, 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";
+
+# b-u585-iot02 octal
+#$GPIO_BASE = 0x42020000;
+#$Conf = "PI05:AF05:V, PF04:AF05:V, PF12:AF05:V, PH12:AF05:V, PH11:AF05:V, 
PH10:AF05:V, PH09:AF05:V, "
+#      . "PF03:AF05:V, PF02:AF05:V, PF01:AF05:V, PF00:AF05:V";
+# w/ cmspi
+#$Conf = "PI05:PPUP:H, PF04:PPUP:V, PH12:INPUP:V, PH11:INPUP:V, PH10:INPUP:V, 
PH09:INPUP:V, "
+#      . "PF03:INPUP:V, PF02:INPUP:V, PF01:INPUP:V, PF00:INPUP:V";
+
+# x-nucleo-gfx01m1 for F030R8, F070RB, F072RB
+#$GPIO_BASE = 0x48000000;
+#$Conf = "PB09:PPUP:H, PB13:PPUP:V, PC02:INPUP:V, PC03:INPUP:V";
+
+# x-nucleo-gfx01m1 for F401RE, F410RB, F411RE, F446RE
+#$GPIO_BASE = 0x40020000;
+#$Conf = "PB09:PPUP:H, PB13:PPUP:V, PC02:INPUP:V, PC03:INPUP:V";
+
+# x-nucleo-gfx01m1 for G071RB, L053R8, L073RZ
+#$GPIO_BASE = 0x50000000;
+#$Conf = "PB09:PPUP:H, PB13:PPUP:V, PC02:INPUP:V, PC03:INPUP:V";
+
+# x-nucleo-gfx01m1 for L412RB-P, L433RC-P, L452RE-P
+#$GPIO_BASE = 0x48000000;
+#$Conf = "PB07:PPUP:H, PA05:PPUP:V, PA06:INPUP:V, PA12:INPUP:V";
+
+# x-nucleo-gfx01m1 for NUCLEO-L452RE and NUCLEO-L476RG
+#$GPIO_BASE = 0x48000000;
+#$Conf = "PB09:PPUP:H, PB13:PPUP:V, PC02:INPUP:V, PC03:INPUP: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 [ -1 ] [ -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, G0, G4, H7, L4, L4+, 
L5, WB, WL 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 == 1)
+       {
+               # UNI/O
+               push(@names, 'SCIO');
+       }
+       elsif (@conf == 2)
+       {
+               # I2C
+               push(@names, 'SDA', 'SCL');
+       }
+       elsif (@conf == 3)
+       {
+               # SPI w/ combined MISO/MOSI
+               push(@names, 'NCS', 'CLK', 'IO0/DIO');
+       }
+       elsif (@conf == 4)
+       {
+               # SPI
+               push(@names, 'NCS', 'CLK', 'IO1/MISO', 'IO0/MOSI');
+       }
+       elsif (@conf == 5)
+       {
+               # Microwire
+               push(@names, 'CS', 'CLK', 'PE', 'DO', 'DI');
+       }
+       elsif (@conf == 6)
+       {
+               # SPI/DPI/QPI
+               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', 'BK_2_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');
+       }
+       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%04X 0x%04X 
0x%08X",
+                       ((($pin >= 8) ? ${GPIO_CRH} : ${GPIO_CRL})-${GPIO_ODR}) 
& 0xFFFF,
+                       ((($pin >= 8) ? ${GPIO_CRH} : ${GPIO_CRL})-${GPIO_ODR}) 
& 0xFFFF, $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%04X 0x%04X 0x%08X", 
(${GPIO_MODER}-${GPIO_ODR}) & 0xFFFF,
+                       (${GPIO_MODER}-${GPIO_ODR}) & 0xFFFF, $exor));
+       }
+
+       push(@{$Port[$num]}, sprintf("P%s%02d:%s:%s", $port, $pin, $conf, 
$speed));
+       push(@Col, $Exor);
+}
+
+my $Col = sprintf("${Sep}0x%04X ", (${GPIO_IDR}-${GPIO_ODR}) & 0xFFFF);
+my $Cnt = 0;
+for (my $i = 0; $i < @Col; $i++)
+{
+       if ((length($Col[$i]) >= 32) && (($Cnt++ % 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/gpio_conf_stm32.pl 
b/contrib/loaders/flash/stmqspi/gpio_conf_stm32.pl
index 49b15c2022..10e6f3384a 100755
--- a/contrib/loaders/flash/stmqspi/gpio_conf_stm32.pl
+++ b/contrib/loaders/flash/stmqspi/gpio_conf_stm32.pl
@@ -1,6 +1,7 @@
 #!/usr/bin/perl
 # SPDX-License-Identifier: GPL-2.0-or-later
-
+#
+# Copyright (C) 2022 by Andreas Bolsch <andreas.bol...@mni.thm.de
 #
 # Helper for generating GPIO setup for STM32F0, F4, F7, H7, L0, L1, L4, L4+
 # and F1 (for 'stmqspi' and 'cmspi' drivers).
@@ -21,8 +22,8 @@
 #  - 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
+# For dual flash: NCS1, NCS2, CLK, then BK_1 followed by. If single NCS for 
both, omit NCS2
+# For octal flash: NCS, CLK, DQS (only for stmqspi, not for cmspi), IO7 down 
to IO0
 
 use strict;
 use Getopt::Std;
@@ -45,6 +46,10 @@ my $STM32F1 = 0;
 #$GPIO_BASE = 0x48000000;
 #$Conf = "PB01:PP:V, PA05:PP:V, PA06:INUP:V, PA07:INUP:V";
 
+# stm32f407ccu6 board w/ cmspi
+#$GPIO_BASE = 0x40020000;
+#$Conf = "PA04:PP:M, 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";
@@ -98,10 +103,13 @@ my $STM32F1 = 0;
 
 # 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";
+#$Conf = "PE11:AF10:V, PE10:AF10:V, PG06:AF03:V, PD07:AF10:V, PC03:AF10:V, 
PD05:AF10:V, "
+#      . "PD04:AF10:V, PA06:AF10:V, PA07:AF10:V, PE13:AF10:V, PE12:AF10:V, ";
+#$Conf = "PG12:AF05:V, PF04:AF05:V, PF12:AF05:V, PG10:AF05:V, PG09:AF05:V, 
PG01:AF05:V, "
+#      . "PG00:AF05:V  PF03:AF05:V, PF02:AF05:V, PF01:AF05:V, PF00:AF05:V, "
+# w/ cmspi
+#$Conf = "PG12:PPUP:M, PF04:PPUP:V, PG10:INPUP:V, PG09:INPUP:V, PG01:INPUP:V, "
+#      . "PG00:INPUP:V, PF03:INPUP:V, PF02:INPUP:V, PF01:INPUP:V, 
PF00:INPUP:V";
 
 # nucleo-f767zi dual quad
 #$GPIO_BASE = 0x40020000;
@@ -113,42 +121,83 @@ my $STM32F1 = 0;
 
 # 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";
+#$Conf = "PB10:AF09:V, PD11:AF09:V, PB02:AF09:V, PC11:AF09:V, PD13:AF09:V, 
PE02:AF09:V, PD12: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";
+# dquad w/ cmspi
+#$Conf = "PB10:PPUP:M, PC11:PPUP:M, PB02:PPUP:V, PD13:INPUP:V, PE02:INPUP:V, 
PD12:INPUP:V, PD11:INPUP: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";
+#$Conf = "PB10:AF09:V, PC11:AF09:V, PB02:AF09:V, PD13:AF09:V, PE02:AF09:V, 
PD12:AF09:V, PD11: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
+# nucleo-h7a3zi octal
+#$GPIO_BASE = 0x58020000;
+#$Conf = "PB06:AF10UP:V, PA03:AF03:V, PB02:AF10:V, PE10:AF10:V, PE09:AF10:V, 
PE08:AF10:V, "
+#      . "PE07:AF10:V, PD13:AF09:V, PE02:AF09:V, PF09:AF10:V, PF08:AF10:V";
+#$Conf = "PE11:AF11UP:V, PA03:AF03:V, PB02:AF10:V, PE10:AF10:V, PE09:AF10:V, 
PE08:AF10:V, "
+#      . "PE07:AF10:V, PD13:AF09:V, PE02:AF09:V, PF09:AF10:V, PF08:AF10:V";
+#$Conf = "PC11:AF09UP:V, PA03:AF03:V, PB02:AF10:V, PE10:AF10:V, PE09:AF10:V, 
PE08:AF10:V, "
+#      ." PE07:AF10:V, PD13:AF09:V, PE02:AF09:V, PF09:AF10:V, PF08:AF10:V";
+# w/ cmspi
+#$Conf = "PB06:PPUP:M, PA03:PPUP:M, PE10:INPUP:V, PE09:INPUP:V, PE08:INPUP:V, "
+#      . "PE07:INPUP:V, PD13:INPUP:V, PE02:INPUP:V, PF09:INPUP:V, 
PF08:INPUP:V";
+#$Conf = "PE11:PPUP:M, PA03:PPUP:M, PE10:INPUP:V, PE09:INPUP:V, PE08:INPUP:V, "
+#      . "PE07:INPUP:V, PD13:INPUP:V, PE02:INPUP:V, PF09:INPUP:V, 
PF08:INPUP:V";
+#$Conf = "PC11:PPUP:M, PA03:PPUP:M, PE10:INPUP:V, PE09:INPUP:V, PE08:INPUP:V, "
+#      . "PE07:INPUP:V, PD13:INPUP:V, PE02:INPUP:V, PF09:INPUP:V, 
PF08:INPUP:V";
+
+# nucleo-h723zg octal/dual quad
+#$GPIO_BASE = 0x58020000;
+#$Conf = "PB06:AF10:M, PA03:AF03:V, PB02:AF10:V, PE10:AF10:V, PD06:AF10:V, 
PD05:AF10:V, "
+#      . "PE07:AF10:V, PD13:AF09:V, PE02:AF09:V, PF09:AF10:V, PF08:AF10:V";
+#$Conf = "PG12:AF03:M, PA03:AF03:V, PB02:AF10:V, PE10:AF10:V, PD06:AF10:V, 
PD05:AF10:V, "
+#      . "PE07:AF10:V, PD13:AF09:V, PE02:AF09:V, PF09:AF10:V, PF08:AF10:V";
+#$Conf = "PE11:AF11:M, PC11:AF09:M, PA03:AF03:V, PB02:AF10:V, PE10:AF10:V, 
PD06:AF10:V, "
+#      . "PD05:AF10:V, PE07:AF10:V, PD13:AF09:V, PE02:AF09:V, PF09:AF10:V, 
PF08:AF10:V";
+# w/ cmspi
+#$Conf = "PB06:PPUP:M, PA03:PPUP:V, PE10:INPUP:V, PD06:INPUP:V, PD05:INPUP:V, "
+#      . "PE07:INPUP:V, PD13:INPUP:V, PE02:INPUP:V, PF09:INPUP:V, 
PF08:INPUP:V";
+#$Conf = "PG12:PPUP:M, PA03:PPUP:V, PE10:INPUP:V, PD06:INPUP:V, PD05:INPUP:V, "
+#      . "PE07:INPUP:V, PD13:INPUP:V, PE02:INPUP:V, PF09:INPUP:V, 
PF08:INPUP:V";
+#$Conf = "PE11:PPUP:M, PC11:PPUP:M, PA03:PPUP:V, PE10:INPUP:V, PD06:INPUP:V, 
PD05:INPUP:V, "
+#      . "PE07:INPUP:V, PD13:INPUP:V, PE02:INPUP:V, PF09:INPUP:V, 
PF08:INPUP:V";
+
+# nucleo-l4r5zi 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";
+# w/ cmspi
+#$Conf = "PA02:PPUP:M, PE11:PPUP:M, PE10:PPUP:V, PD07:INPDO:V, PD06:INPDO:V, 
PD05:INPDO:V, PD04:INPDO:V, "
+#      . "PE15:INPDO:V, PE14:INPDO:V, PE13:INPDO:V, PE12:INPDO:V";
 
-# nucleo-l552ze-q dual quad with single NCS
+# nucleo-l552ze-q dual quad 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
+# w/ cmspi dual quad mode
 #$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 = "PA00:PPUP:H, PA04:PPUP:V, PB03:INPUP:V, PA10:INPUP:V, PB11:INPUP:V, 
PB01:INPUP:V";
 #$Conf = "PA01:PPUP:H, PA04:PPUP:V, PA08:INPUP:V, PB14:INPUP:V, PB04:INPUP:V, 
PB05:INPUP:V";
+# w/ cmspi dual quad mode
+#$Conf = "PA00:PPUP:H, PA01:PPUP:H, PA04:PPUP:V, PB03:INPUP:V, PA10:INPUP:V, 
PB11:INPUP:V, "
+#      . "PB01:INPUP:V, PA08:INPUP:V, PB14:INPUP:V, PB04:INPUP:V, 
PB05:INPUP:V";
 
-# nucleo-g474re dual quad with single NCS
+# nucleo-g474re dual quad 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";
@@ -156,15 +205,18 @@ my $STM32F1 = 0;
 #$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
+# stm32h745i/h750b-disco dual quad 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";
+#      . "PG14:AF09:V, PG09:AF09:V, PH03:AF09:V, PH02:AF09:V";
+# w/ cmspi
+#$Conf = "PG06:PPUP:H, PF10:PPUP:V, PF06:INPUP:V, PF07:INPUP:V, PF09:INPUP:V, 
PD11:INPUP:V, "
+#      . "PG14:INPUP:V, PG09:INPUP:V, PH03:INPUP:V, PH02:INPUP:V";
 
-# stm32h747i-disco dual quad with single NCS
+# stm32h747i-disco dual quad 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";
+#      . "PG14:AF09:V, PG09:AF09:V, PH03:AF09:V, PH02:AF09:V";
 
 # stm32h7b3i-disco octal
 #$GPIO_BASE = 0x58020000;
@@ -181,6 +233,34 @@ my $STM32F1 = 0;
 #$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";
 
+# b-u585-iot02 octal
+#$GPIO_BASE = 0x42020000;
+#$Conf = "PI05:AF05:V, PF04:AF05:V, PF12:AF05:V, PH12:AF05:V, PH11:AF05:V, 
PH10:AF05:V, PH09:AF05:V, "
+#      . "PF03:AF05:V, PF02:AF05:V, PF01:AF05:V, PF00:AF05:V";
+# w/ cmspi
+#$Conf = "PI05:PPUP:H, PF04:PPUP:V, PH12:INPUP:V, PH11:INPUP:V, PH10:INPUP:V, 
PH09:INPUP:V, "
+#      . "PF03:INPUP:V, PF02:INPUP:V, PF01:INPUP:V, PF00:INPUP:V";
+
+# x-nucleo-gfx01m1 for F030R8, F070RB, F072RB
+#$GPIO_BASE = 0x48000000;
+#$Conf = "PB09:PPUP:H, PB13:PPUP:V, PC02:INPUP:V, PC03:INPUP:V";
+
+# x-nucleo-gfx01m1 for F401RE, F410RB, F411RE, F446RE
+#$GPIO_BASE = 0x40020000;
+#$Conf = "PB09:PPUP:H, PB13:PPUP:V, PC02:INPUP:V, PC03:INPUP:V";
+
+# x-nucleo-gfx01m1 for G071RB, L053R8, L073RZ
+#$GPIO_BASE = 0x50000000;
+#$Conf = "PB09:PPUP:H, PB13:PPUP:V, PC02:INPUP:V, PC03:INPUP:V";
+
+# x-nucleo-gfx01m1 for L412RB-P, L433RC-P, L452RE-P
+#$GPIO_BASE = 0x48000000;
+#$Conf = "PB07:PPUP:H, PA05:PPUP:V, PA06:INPUP:V, PA12:INPUP:V";
+
+# x-nucleo-gfx01m1 for NUCLEO-L452RE and NUCLEO-L476RG
+#$GPIO_BASE = 0x48000000;
+#$Conf = "PB09:PPUP:H, PB13:PPUP:V, PC02:INPUP:V, PC03:INPUP:V";
+
 &getopts('b:c:f:t');
 if ($Getopt::Std::opt_b eq '')
 {
@@ -198,7 +278,7 @@ 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 )");
+               die("usage: $0 [ -1 ] [ -b io_base ] ( -c port_configuration | 
-f conf_file )");
        }
 }#
 else
@@ -234,7 +314,7 @@ if ($STM32F1)
 }
 else
 {
-       # these offsets are identical on all F0, F4, F7, H7, L4, L4+ devices up 
to now
+       # these offsets are identical on all F0, F4, F7, G0, G4, H7, L4, L4+, 
L5, WB, WL devices up to now
        $GPIO_OFFS = 0x400;
        $GPIO_MODER = 0x00;
        $GPIO_OTYPER = 0x04;
@@ -334,36 +414,49 @@ else
        my @names = ( );
        my @conf = split(/\s*,\s*/, $Conf);
 
-       if (@conf == 2)
+       if (@conf == 1)
+       {
+               # UNI/O
+               push(@names, 'SCIO');
+       }
+       elsif (@conf == 2)
        {
+               # I2C
                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");
-               }
+       }
+       elsif (@conf == 3)
+       {
+               # SPI w/ combined MISO/MOSI
+               push(@names, 'NCS', 'CLK', 'IO0/DIO');
+       }
+       elsif (@conf == 4)
+       {
+               # SPI
+               push(@names, 'NCS', 'CLK', 'IO1/MISO', 'IO0/MOSI');
+       }
+       elsif (@conf == 5)
+       {
+               # Microwire
+               push(@names, 'CS', 'CLK', 'PE', 'DO', 'DI');
+       }
+       elsif (@conf == 6)
+       {
+               # SPI/DPI/QPI
+               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', 'BK_2_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');
+       }
+       else
+       {
+               die("invalid config");
        }
 
        for (my $index = 0; $index < @conf; $index++)
@@ -563,9 +656,9 @@ foreach my $line (@conf)
 
                $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));
+               (($MODER & 0x3) == 0x0) && ($Exor .= sprintf(" 0x%04X 0x%04X 
0x%08X",
+                       ((($pin >= 8) ? ${GPIO_CRH} : ${GPIO_CRL})-${GPIO_ODR}) 
& 0xFFFF,
+                       ((($pin >= 8) ? ${GPIO_CRH} : ${GPIO_CRL})-${GPIO_ODR}) 
& 0xFFFF, $exor));
        }
        else
        {
@@ -579,18 +672,19 @@ foreach my $line (@conf)
 
                $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));
+               ($MODER == 0x0) && ($Exor .= sprintf(" 0x%04X 0x%04X 0x%08X", 
(${GPIO_MODER}-${GPIO_ODR}) & 0xFFFF,
+                       (${GPIO_MODER}-${GPIO_ODR}) & 0xFFFF, $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);
+my $Col = sprintf("${Sep}0x%04X ", (${GPIO_IDR}-${GPIO_ODR}) & 0xFFFF);
+my $Cnt = 0;
 for (my $i = 0; $i < @Col; $i++)
 {
-       if (($i != 0) && (($i % 2) == 0))
+       if ((length($Col[$i]) >= 32) && (($Cnt++ % 2) == 0))
        {
                (($i + 1) < @Col) && ($Col .= "\\\n${Sep}");
        }
diff --git a/doc/openocd.texi b/doc/openocd.texi
index 6c853f2ce4..3abf55da6b 100644
--- a/doc/openocd.texi
+++ b/doc/openocd.texi
@@ -6050,6 +6050,17 @@ 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.
+In OPI mode the one/two byte command format is handled automatically.
+
+@deffn Command {stmqspi always_4byte} bank_id [ on | off ]
+Some devices use 4-byte addresses for all commands except the legacy 0x03 read
+regardless of device size. This command controls the corresponding hack,
+defaults to 'off'.
+@end deffn
+
+@deffn Command {stmqspi always_sfdp} bank_id [ on | off ]
+Prefer SFDP tables to hardcoded device table, mainly for debugging, defaults 
to 'off'.
+@end deffn
 
 To check basic communication settings, issue
 @example
@@ -6063,13 +6074,23 @@ 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.
+Unfortunately, OPI mode is rather messy:
+Some devices require one byte commands, others two byte commands. Furthemore,
+the second byte can be the same as the first or for other devices the first 
byte inverted.
+This is figured out from the settings for Memory Mapped Mode automatically. 
Additionally
+e.g. Read Status Reg. (0x05) and Read SPDP Data (0x5A) require varying address 
size
+and dummy clocks, the drivers attempts to detect those automatically. The 
one/two byte
+command format is handled automatically by @var{cmd}, however, (dummy) address 
bytes
+are not. E.g. for MX25LM51245G in OPI mode Read Status Reg. must be done that 
way:
 @example
 stmqspi cmd bank_id 1 0x05 0x00 0x00 0x00 0x00
 @end example
-should return the status register contents.
+If DQS is used, dummy cycles are stripped off the response automatically, 
otherwise
+the dummy cycles must be taken into account manually.
+
+Sometimes the datasheet states that an address must be sent, but it's actually 
don't care,
+this can't be distinguished from dummy bytes, of course. However, wrong 
detection of address
+vs. dummy bytes won't matter in this case.
 
 @end deffn
 
diff --git a/src/flash/nor/spi.h b/src/flash/nor/spi.h
index 807af12e78..a80f15996b 100644
--- a/src/flash/nor/spi.h
+++ b/src/flash/nor/spi.h
@@ -71,6 +71,7 @@ extern const struct flash_device flash_devices[];
 /* SPI Flash Commands */
 #define SPIFLASH_READ_ID               0x9F /* Read Flash Identification */
 #define SPIFLASH_READ_MID              0xAF /* Read Flash Identification, 
multi-io */
+#define SPIFLASH_WRITE_DISABLE 0x04 /* Write Disable */
 #define SPIFLASH_READ_STATUS   0x05 /* Read Status Register */
 #define SPIFLASH_WRITE_ENABLE  0x06 /* Write Enable */
 #define SPIFLASH_PAGE_PROGRAM  0x02 /* Page Program */
diff --git a/src/flash/nor/stmqspi.c b/src/flash/nor/stmqspi.c
index 77ea4c40d8..72e2e0789b 100644
--- a/src/flash/nor/stmqspi.c
+++ b/src/flash/nor/stmqspi.c
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 
 /***************************************************************************
- *   Copyright (C) 2016 - 2019 by Andreas Bolsch                           *
+ *   Copyright (C) 2016 - 2022 by Andreas Bolsch                           *
  *   andreas.bol...@mni.thm.de                                             *
  *                                                                         *
  *   Copyright (C) 2010 by Antonio Borneo                                  *
@@ -38,44 +38,44 @@
 #include "stmqspi.h"
 #include "sfdp.h"
 
-/* deprecated */
-#undef SPIFLASH_READ
-#undef SPIFLASH_PAGE_PROGRAM
-
 /* 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 */
+ * in particular, use the dummy cycle setting from this saved setting
+ * if stmqspi_info->always_4byte is true and read command is not 0x03,
+ * force 4-byte addresses */
 #define        QSPI_CCR_READ (QSPI_READ_MODE | (stmqspi_info->saved_ccr & \
-       (0xF0000000U | QSPI_DCYC_MASK | QSPI_4LINE_MODE | QSPI_ALTB_MODE | 
QSPI_ADDR4 | 0xFF)))
+         (0xF0000000U | QSPI_DCYC_MASK | QSPI_4LINE_MODE | QSPI_ALTB_MODE | 
QSPI_ADDR4 | 0xFFU)) | \
+        ((stmqspi_info->always_4byte && ((stmqspi_info->saved_ccr & 0xFFU) != 
SPIFLASH_READ)) ? QSPI_ADDR4 : 0U))
 
 /* 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))
+        (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))
+        (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))
+        (QSPI_READ_MODE | SPIFLASH_READ_MID))
 
-/* always use 3-byte addresses for read SFDP */
-#define        QSPI_CCR_READ_SFDP \
+#define        QSPI_CCR_READ_SFDP(len, dummy) \
        ((QSPI_MODE & ~QSPI_DCYC_MASK & ~QSPI_ADDR4 & QSPI_NO_ALTB) | \
-       (QSPI_READ_MODE | QSPI_ADDR3 | SPIFLASH_READ_SFDP))
+        (QSPI_READ_MODE | ((dummy) << QSPI_DCYC_POS) | \
+        (((len) < 4) ? QSPI_ADDR3 : QSPI_ADDR4) | 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))
+        (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))
+        (stmqspi_info->always_4byte ? QSPI_ADDR4 : 0U) | \
+        (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) | \
@@ -83,60 +83,72 @@
 
 #define QSPI_CCR_PAGE_PROG \
        ((QSPI_MODE & ~QSPI_DCYC_MASK & QSPI_NO_ALTB) | \
-       (QSPI_WRITE_MODE | stmqspi_info->dev.pprog_cmd))
+        (stmqspi_info->always_4byte ? QSPI_ADDR4 : 0U) | \
+        (QSPI_WRITE_MODE | stmqspi_info->dev.pprog_cmd))
 
 /* saved mode settings */
-#define OCTOSPI_MODE (stmqspi_info->saved_cr & 0xCFFFFFFF)
+#define OCTOSPI_MODE (stmqspi_info->saved_cr & 0xCFFFFFFFU)
 
-#define OPI_MODE ((stmqspi_info->saved_ccr & OCTOSPI_ISIZE_MASK) != 0)
+#define OPI_MODE ((stmqspi_info->saved_ccr & OCTOSPI_IMODE_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
+#define OCTOSPI_CCR_READ (OCTOSPI_MODE_CCR | (((stmqspi_info->always_4byte && \
+          ((stmqspi_info->saved_ir & 0xFFU) != SPIFLASH_READ) && \
+          ((stmqspi_info->saved_ir >> 8) & 0xFFU) != SPIFLASH_READ)) ? 
OCTOSPI_ADDR4 : 0U))
 
 /* 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         
*
+ * 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.                                       
                                *
+ * When DQS is disabled, use the setting as evaluated by 'find_opi_params'     
*
  */
-#define OPI_DUMMY \
-       ((stmqspi_info->saved_ccr & OCTOSPI_DQSEN) ? 6U : 4U)
+#define OPI_DUMMY(dummy) \
+       ((stmqspi_info->saved_ccr & BIT(OCTOSPI_DQSEN)) ? 6U : (dummy))
 
 #define        OCTOSPI_CCR_READ_STATUS \
-       ((OCTOSPI_MODE_CCR & OCTOSPI_NO_DDTR & \
-       (OPI_MODE ? ~0U : OCTOSPI_NO_ADDR) & OCTOSPI_NO_ALTB))
+       ((OCTOSPI_MODE_CCR & OCTOSPI_NO_DDTR & OCTOSPI_NO_ALTB & \
+        ((OPI_MODE && stmqspi_info->opi_addr) ? ~0U : OCTOSPI_NO_ADDR)) | \
+        (OPI_MODE ? ((stmqspi_info->opi_addr - 1U) & 0x3) << SPI_ADSIZE_POS : 
0U))
 
 #define        OCTOSPI_CCR_READ_ID \
-       ((OCTOSPI_MODE_CCR & OCTOSPI_NO_DDTR & \
-       (OPI_MODE ? ~0U : OCTOSPI_NO_ADDR) & OCTOSPI_NO_ALTB))
+       ((OCTOSPI_MODE_CCR & OCTOSPI_NO_DDTR & OCTOSPI_NO_ALTB & \
+        ((OPI_MODE && stmqspi_info->opi_addr) ? ~0U : OCTOSPI_NO_ADDR)) | \
+        (OPI_MODE ? ((stmqspi_info->opi_addr - 1U) & 0x3) << SPI_ADSIZE_POS : 
0U))
 
 #define        OCTOSPI_CCR_READ_MID OCTOSPI_CCR_READ_ID
 
-/* 4-byte address in octo mode, else 3-byte address for read SFDP */
+/* 4-byte address in octo mode, else 3/4-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))
+        (((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))
+       ((OCTOSPI_MODE_CCR & OCTOSPI_NO_ALTB & OCTOSPI_NO_DATA) | \
+        (stmqspi_info->always_4byte ? OCTOSPI_ADDR4 : 0U))
 
 #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))
+       ((OCTOSPI_MODE_CCR & QSPI_NO_ALTB) | \
+        (stmqspi_info->always_4byte ? OCTOSPI_ADDR4 : 0U))
 
-#define SPI_ADSIZE (((stmqspi_info->saved_ccr >> SPI_ADSIZE_POS) & 0x3) + 1)
+#define SPI_ADSIZE ((unsigned int)(((stmqspi_info->saved_ccr >> 
SPI_ADSIZE_POS) & 0x3U) + 1))
 
-#define OPI_CMD(cmd) ((OPI_MODE ? ((((uint16_t)(cmd)) << 8) | (~(cmd) & 
0xFFU)) : (cmd)))
+/*
+ * for 8-bit command size, just copy supplied cmd byte, for 8-bit command      
*
+ * size the command consists of original command byte (first byte) and         
*
+ * possibly complemented command byte (second)                                 
                        *
+ */
+#define OPI_2ND_BYTE ((stmqspi_info->saved_ccr & OCTOSPI_ISIZE_MASK) != 0)
+#define OPI_CMD(cmd) (((cmd) * 0x101U) ^ stmqspi_info->exor_ir)
 
 /* convert uint32_t into 4 uint8_t in little endian byte order */
 static inline uint32_t h_to_le_32(uint32_t val)
@@ -147,11 +159,11 @@ static inline uint32_t h_to_le_32(uint32_t val)
        return result;
 }
 
-/* Timeout in ms */
+/* Timeout in ms, mass erase per MByte */
 #define SPI_CMD_TIMEOUT                        (100)
 #define SPI_PROBE_TIMEOUT              (100)
 #define SPI_MAX_TIMEOUT                        (2000)
-#define SPI_MASS_ERASE_TIMEOUT (400000)
+#define SPI_MASS_ERASE_TIMEOUT (50000)
 
 struct sector_info {
        uint32_t offset;
@@ -162,19 +174,26 @@ struct sector_info {
 struct stmqspi_flash_bank {
        bool probed;
        char devname[32];
-       bool octo;
+       bool octo;                                      /* OCTOSPI interface 
present vs. QSPI interface */
+       bool always_4byte;                      /* use always 4-byte address 
except for basic read 0x03 */
+       bool always_sfdp;                       /* always try sfdp */
        struct flash_device dev;
        uint32_t io_base;
-       uint32_t saved_cr;      /* in particular 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 */
+       uint32_t saved_cr;                      /* in particular 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 */
+       uint32_t exor_ir;                       /* only for OCTOSPI, inversion 
mask */
+       uint8_t opi_addr;                       /* no. of addr bytes for Read 
Status Reg. */
+       uint8_t opi_rdid;                       /* no. of dummy bytes for Read 
ID, Read Status Reg. (not data read!!!) */
+       unsigned int sfdp_len1;         /* number of address bytes for SFDP 
read for flash1 and octo */
+       unsigned int sfdp_len2;         /* number of address bytes for SFDP 
read for flash2 */
        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 */
 };
 
 static inline int octospi_cmd(struct flash_bank *bank, uint32_t mode,
-               uint32_t ccr, uint32_t ir)
+               uint32_t ccr, uint32_t ir, uint32_t opi_dummy)
 {
        struct target *target = bank->target;
        const struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv;
@@ -189,7 +208,7 @@ static inline int octospi_cmd(struct flash_bank *bank, 
uint32_t mode,
        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));
+               (opi_dummy << OCTOSPI_DCYC_POS) : 0));
 
        if (retval != ERROR_OK)
                return retval;
@@ -221,10 +240,12 @@ FLASH_BANK_COMMAND_HANDLER(stmqspi_flash_bank_command)
        }
 
        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;
+       stmqspi_info->always_4byte = false;
+       stmqspi_info->always_sfdp = false;
+       stmqspi_info->sfdp_dummy1 = 0;
+       stmqspi_info->sfdp_dummy2 = 0;
 
        return ERROR_OK;
 }
@@ -248,7 +269,8 @@ static int poll_busy(struct flash_bank *bank, int timeout)
 
                if ((spi_sr & BIT(SPI_BUSY)) == 0) {
                        /* Clear transmit finished flag */
-                       return target_write_u32(target, io_base + SPI_FCR, 
BIT(SPI_TCF));
+                       return target_write_u32(target, io_base + SPI_FCR,
+                               BIT(SPI_TOF) | BIT(SPI_TCF));
                } else
                        LOG_DEBUG("busy: 0x%08X", spi_sr);
                alive_sleep(1);
@@ -319,14 +341,14 @@ static int set_mm_mode(struct flash_bank *bank)
        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)
+/* Read the status register of the external SPI flash chip(s) continuously. */
+static int probe_status_reg(struct flash_bank *bank, size_t len, 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;
+       uint32_t count, io_base = stmqspi_info->io_base;
        uint8_t data;
-       int count, retval;
+       int retval;
 
        /* Abort any previous operation */
        retval = stmqspi_abort(bank);
@@ -338,8 +360,11 @@ static int read_status_reg(struct flash_bank *bank, 
uint16_t *status)
        if (retval != ERROR_OK)
                goto err;
 
-       /* Read always two (for DTR mode) bytes per chip */
-       count = 2;
+       if (len == 0)
+               return retval;
+
+       /* Read always even number (for DTR mode) of bytes per chip */
+       count = (len + 1) & ~1U;
        retval = target_write_u32(target, io_base + SPI_DLR,
                ((stmqspi_info->saved_cr & BIT(SPI_DUAL_FLASH)) ? 2 * count : 
count) - 1);
        if (retval != ERROR_OK)
@@ -348,7 +373,7 @@ static int read_status_reg(struct flash_bank *bank, 
uint16_t *status)
        /* Read status */
        if (IS_OCTOSPI) {
                retval = octospi_cmd(bank, OCTOSPI_READ_MODE, 
OCTOSPI_CCR_READ_STATUS,
-                       SPIFLASH_READ_STATUS);
+                       SPIFLASH_READ_STATUS, 
OPI_DUMMY(stmqspi_info->opi_rdid));
                if (OPI_MODE) {
                        /* Dummy address 0, only required for 8-line mode */
                        retval = target_write_u32(target, io_base + SPI_AR, 0);
@@ -360,13 +385,12 @@ static int read_status_reg(struct flash_bank *bank, 
uint16_t *status)
        if (retval != ERROR_OK)
                goto err;
 
-       *status = 0;
-
        /* for debugging only */
        uint32_t dummy;
        (void)target_read_u32(target, io_base + SPI_SR, &dummy);
 
        for ( ; count > 0; --count) {
+               *status = 0;
                if ((stmqspi_info->saved_cr & (BIT(SPI_DUAL_FLASH) | 
BIT(SPI_FSEL_FLASH)))
                        != BIT(SPI_FSEL_FLASH)) {
                        /* get status of flash 1 in dual mode or flash 1 only 
mode */
@@ -383,14 +407,22 @@ static int read_status_reg(struct flash_bank *bank, 
uint16_t *status)
                                goto err;
                        *status |= ((uint16_t)data) << 8;
                }
-       }
 
-       LOG_DEBUG("flash status regs: 0x%04" PRIx16, *status);
+               /* overwrite only first len elements regardless of count */
+               if (--len > 0)
+                       status++;
+       }
 
 err:
        return retval;
 }
 
+/* Read status register of the external SPI flash chip(s). */
+static int read_status_reg(struct flash_bank *bank, uint16_t *status)
+{
+       return probe_status_reg(bank, 1, status);
+}
+
 /* 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)
@@ -416,7 +448,7 @@ static int wait_till_ready(struct flash_bank *bank, int 
timeout)
 }
 
 /* Send "write enable" command to SPI flash chip(s). */
-static int qspi_write_enable(struct flash_bank *bank)
+static int qspi_write_enable(struct flash_bank *bank, bool enable, bool check)
 {
        struct target *target = bank->target;
        struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv;
@@ -437,7 +469,8 @@ static int qspi_write_enable(struct flash_bank *bank)
        /* Send write enable command */
        if (IS_OCTOSPI) {
                retval = octospi_cmd(bank, OCTOSPI_WRITE_MODE, 
OCTOSPI_CCR_WRITE_ENABLE,
-                       SPIFLASH_WRITE_ENABLE);
+                       enable ? SPIFLASH_WRITE_ENABLE : SPIFLASH_WRITE_DISABLE,
+                       OPI_DUMMY(stmqspi_info->opi_rdid));
                if (OPI_MODE) {
                        /* Dummy address 0, only required for 8-line mode */
                        retval = target_write_u32(target, io_base + SPI_AR, 0);
@@ -449,34 +482,35 @@ static int qspi_write_enable(struct flash_bank *bank)
        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;
+       if (check) {
+               /* 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 & (BIT(SPI_DUAL_FLASH) | 
BIT(SPI_FSEL_FLASH)))
-               != BIT(SPI_FSEL_FLASH))
-               if ((status & (SPIFLASH_WE_BIT | SPIFLASH_BSY_BIT)) != 
SPIFLASH_WE_BIT) {
-                       LOG_ERROR("Cannot write enable flash1. Status=0x%02x",
-                               status & 0xFFU);
-                       return ERROR_FLASH_OPERATION_FAILED;
-               }
+               /* Check write enabled for flash 1 */
+               if ((stmqspi_info->saved_cr & (BIT(SPI_DUAL_FLASH) | 
BIT(SPI_FSEL_FLASH)))
+                       != BIT(SPI_FSEL_FLASH))
+                       if ((status & (SPIFLASH_WE_BIT | SPIFLASH_BSY_BIT)) != 
SPIFLASH_WE_BIT) {
+                               LOG_ERROR("Cannot write enable flash1. 
Status=0x%02x",
+                                       status & 0xFFU);
+                               return ERROR_FLASH_OPERATION_FAILED;
+                       }
 
-       /* Check write enabled for flash 2 */
-       status >>= 8;
-       if ((stmqspi_info->saved_cr & (BIT(SPI_DUAL_FLASH) | 
BIT(SPI_FSEL_FLASH))) != 0)
-               if ((status & (SPIFLASH_WE_BIT | SPIFLASH_BSY_BIT)) != 
SPIFLASH_WE_BIT) {
-                       LOG_ERROR("Cannot write enable flash2. Status=0x%02x",
-                               status & 0xFFU);
-                       return ERROR_FLASH_OPERATION_FAILED;
-               }
+               /* Check write enabled for flash 2 */
+               status >>= 8;
+               if ((stmqspi_info->saved_cr & (BIT(SPI_DUAL_FLASH) | 
BIT(SPI_FSEL_FLASH))) != 0)
+                       if ((status & (SPIFLASH_WE_BIT | SPIFLASH_BSY_BIT)) != 
SPIFLASH_WE_BIT) {
+                               LOG_ERROR("Cannot write enable flash2. 
Status=0x%02x",
+                                       status & 0xFFU);
+                               return ERROR_FLASH_OPERATION_FAILED;
+                       }
+       }
 
 err:
        return retval;
@@ -530,14 +564,14 @@ COMMAND_HANDLER(stmqspi_handle_mass_erase_command)
        io_base = stmqspi_info->io_base;
        duration_start(&bench);
 
-       retval = qspi_write_enable(bank);
+       retval = qspi_write_enable(bank, true, true);
        if (retval != ERROR_OK)
                goto err;
 
        /* Send Mass Erase command */
        if (IS_OCTOSPI)
                retval = octospi_cmd(bank, OCTOSPI_WRITE_MODE, 
OCTOSPI_CCR_MASS_ERASE,
-                       stmqspi_info->dev.chip_erase_cmd);
+                       stmqspi_info->dev.chip_erase_cmd, 0);
        else
                retval = target_write_u32(target, io_base + QSPI_CCR, 
QSPI_CCR_MASS_ERASE);
        if (retval != ERROR_OK)
@@ -575,7 +609,7 @@ COMMAND_HANDLER(stmqspi_handle_mass_erase_command)
        }
 
        /* Poll WIP for end of self timed Sector Erase cycle */
-       retval = wait_till_ready(bank, SPI_MASS_ERASE_TIMEOUT);
+       retval = wait_till_ready(bank, SPI_MASS_ERASE_TIMEOUT * ((bank->size >> 
20) + 1));
 
        duration_measure(&bench);
        if (retval == ERROR_OK)
@@ -604,6 +638,79 @@ static int log2u(uint32_t word)
        return -1;
 }
 
+/* Detect OPI parameters required for e.g. read status reg. command            
*
+ * Note that it does not necessarily give the parameters as indicated  *
+ * in the datasheet, e.g. for MX66LM1G45G a RDSR requires 4-byte               
*
+ * address 0, but that's actually don't care.                                  
                *
+ * If DQS is enabled, dummy setting is don't care, too.                        
                *
+ * Furthermore, there seems to be a silicon bug: In OPI mode with              
*
+ * one byte instruction and no address, the transfer never starts but  *
+ * FIFO reads silently return old data.                                        
                                *
+ * As we're in OPI mode, dummy cycles means the same as dummy bytes            
*
+ */
+static int find_opi_params(struct flash_bank *bank)
+{
+       struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv;
+       uint32_t result;
+       const int max = sizeof(result) * CHAR_BIT;
+       uint16_t status[max];
+       char temp[4], out[sizeof(status) * 2 + max + 1];
+       int retval;
+
+       LOG_DEBUG("%s", __func__);
+
+       if (!(IS_OCTOSPI && OPI_MODE))
+               return ERROR_OK;
+       stmqspi_info->opi_rdid = 0;
+
+       /* test with 4 and with 0 address bytes, other possibilities not 
expected */
+       for (int j = 4; j >= 0; j -= 4) {
+               stmqspi_info->opi_addr = j;
+               LOG_DEBUG("trying cmd %d%s, addr %d", OPI_2ND_BYTE ? 2 : 1,
+                       OPI_2ND_BYTE ? (stmqspi_info->exor_ir ? " inv" : " 
dbl") : "", stmqspi_info->opi_addr);
+               result = ~0;
+
+               /* clear WE, set it and finally clear it again */
+               for (int step = 0; step <= 2; step++) {
+                       /* send Write Disable/Enable, but don't check status */
+                       retval = qspi_write_enable(bank, (step & 0x1), false);
+                       if (retval != ERROR_OK)
+                               return retval;
+                       retval = probe_status_reg(bank, max, &status[0]);
+                       if (retval != ERROR_OK)
+                               return retval;
+
+                       snprintf(out, sizeof(out), "status");
+                       for (int cnt = 0; cnt < max; cnt++) {
+                               snprintf(temp, sizeof(temp), " %02" PRIx8, 
status[cnt] & 0xFFU);
+                               strncat(out, temp, sizeof(out) - strlen(out));
+                               /* if WE setting is not correct, this position 
isn't right */
+                               if (((status[cnt] >> SPIFLASH_WE) & 0x1) ^ 
(step & 0x1))
+                                       result &= ~(1U << cnt);
+                       }
+                       LOG_DEBUG("%s", out);
+                       LOG_DEBUG("result 0x%08" PRIx32, result);
+               }
+
+               /* now find the first correct position */
+               for (int cnt = 0; cnt < max; cnt += 1) {
+                               /* predrive may give premature match, this will 
be
+                                * corrected by one in stmqspi_probe later on */
+                       if (result & (1U << cnt)) {
+                               stmqspi_info->opi_rdid = cnt;
+                               LOG_INFO("for RDSR in OPI mode: cmd %d%s, addr 
%d, dummy %d or %d",
+                                       OPI_2ND_BYTE ? 2 : 1, OPI_2ND_BYTE ? 
(stmqspi_info->exor_ir ? " inv" : " dbl") : "",
+                                       stmqspi_info->opi_addr, 
OPI_DUMMY(stmqspi_info->opi_rdid),
+                                       OPI_DUMMY(stmqspi_info->opi_rdid + 1U));
+                               return ERROR_OK;
+                       }
+               }
+       }
+
+       LOG_ERROR("OPI parameters for RDSR not found");
+       return ERROR_FLASH_BANK_NOT_PROBED;
+}
+
 COMMAND_HANDLER(stmqspi_handle_set)
 {
        struct flash_bank *bank = NULL;
@@ -628,7 +735,7 @@ COMMAND_HANDLER(stmqspi_handle_set)
        stmqspi_info = bank->driver_priv;
        dual = (stmqspi_info->saved_cr & BIT(SPI_DUAL_FLASH)) ? 1 : 0;
 
-       /* invalidate all flash device info */
+       /* invalidate all flash deviced related info */
        if (stmqspi_info->probed)
                free(bank->sectors);
        bank->size = 0;
@@ -640,6 +747,10 @@ COMMAND_HANDLER(stmqspi_handle_set)
        memset(&stmqspi_info->dev, 0, sizeof(stmqspi_info->dev));
        stmqspi_info->dev.name = "unknown";
 
+       retval = find_opi_params(bank);
+       if (retval != ERROR_OK)
+               return retval;
+
        strncpy(stmqspi_info->devname, CMD_ARGV[index++], 
sizeof(stmqspi_info->devname) - 1);
        stmqspi_info->devname[sizeof(stmqspi_info->devname) - 1] = '\0';
 
@@ -751,13 +862,13 @@ COMMAND_HANDLER(stmqspi_handle_set)
        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 " 
KiB,"
-                       " bank size = %" PRIu32 " KiB", stmqspi_info->dev.name,
+               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 " B,"
-                       " bank size = %" PRIu32 " B", stmqspi_info->dev.name,
+               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);
 
@@ -774,9 +885,10 @@ COMMAND_HANDLER(stmqspi_handle_cmd)
        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;
+       const int max = 256;
+       char temp[4], output[(3 + max + 256) * 3 + 8];
+       bool dual;
+       int dmode, retval;
 
        LOG_DEBUG("%s", __func__);
 
@@ -796,6 +908,10 @@ COMMAND_HANDLER(stmqspi_handle_cmd)
        target = bank->target;
        stmqspi_info = bank->driver_priv;
        io_base = stmqspi_info->io_base;
+       dual = stmqspi_info->saved_cr & BIT(SPI_DUAL_FLASH);
+
+       /* to distinguish SPI, DPI, QPI only, OPI not required */
+       dmode = (stmqspi_info->saved_ccr >> SPI_DMODE_POS) & 0x3;
 
        if (target->state != TARGET_HALTED) {
                LOG_ERROR("Target not halted");
@@ -808,7 +924,7 @@ COMMAND_HANDLER(stmqspi_handle_cmd)
        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 & BIT(SPI_DUAL_FLASH)) {
+               if (dual) {
                        if ((num_write & 1) == 0) {
                                LOG_ERROR("number of data bytes to write must 
be even in dual mode");
                                return ERROR_COMMAND_SYNTAX_ERROR;
@@ -816,7 +932,7 @@ COMMAND_HANDLER(stmqspi_handle_cmd)
                }
        } else {
                /* read mode, one command byte and up to four following address 
bytes */
-               if (stmqspi_info->saved_cr & BIT(SPI_DUAL_FLASH)) {
+               if (dual) {
                        if ((num_read & 1) != 0) {
                                LOG_ERROR("number of bytes to read must be even 
in dual mode");
                                return ERROR_COMMAND_SYNTAX_ERROR;
@@ -839,7 +955,12 @@ COMMAND_HANDLER(stmqspi_handle_cmd)
                return retval;
 
        /* send command byte */
-       snprintf(output, sizeof(output), "spi: %02x ", cmd_byte);
+       snprintf(output, sizeof(output), "%s%s: %02x ", dual ? "d" : "",
+               OPI_MODE ? "opi" : ((dmode == 3) ? "qpi" : ((dmode == 2) ? 
"dpi" : "spi")), cmd_byte);
+       if (OPI_2ND_BYTE) {
+               snprintf(temp, sizeof(temp), "%02" PRIx8 " ", cmd_byte ^ 
stmqspi_info->exor_ir);
+               strncat(output, temp, sizeof(output) - strlen(output) - 1);
+       }
        if (num_read == 0) {
                /* write, send cmd byte */
                retval = target_write_u32(target, io_base + SPI_DLR, 
((uint32_t)num_write) - 2);
@@ -849,7 +970,7 @@ COMMAND_HANDLER(stmqspi_handle_cmd)
                if (IS_OCTOSPI)
                        retval = octospi_cmd(bank, OCTOSPI_WRITE_MODE,
                                (OCTOSPI_MODE_CCR & OCTOSPI_NO_ALTB & 
OCTOSPI_NO_ADDR &
-                               ((num_write == 1) ? OCTOSPI_NO_DATA : ~0U)), 
cmd_byte);
+                               ((num_write == 1) ? OCTOSPI_NO_DATA : ~0U)), 
cmd_byte, 0);
                else
                        retval = target_write_u32(target, io_base + QSPI_CCR,
                                (QSPI_MODE & ~QSPI_DCYC_MASK & QSPI_NO_ALTB & 
QSPI_NO_ADDR &
@@ -883,16 +1004,21 @@ COMMAND_HANDLER(stmqspi_handle_cmd)
                retval = target_write_u32(target, io_base + SPI_DLR, 
((uint32_t)num_read) - 1);
                if (retval != ERROR_OK)
                        goto err;
-               if (IS_OCTOSPI)
+               if (IS_OCTOSPI) {
+                       /* if DQS is not used, don't take dummy bytes into 
account automatically,
+                        * i.e. all cycles after command are interpreted as 
data cycles,
+                        * that's necessary as the number of dummy cycles may 
depend on command
+                        */
                        retval = octospi_cmd(bank, 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
+                               (((num_write - 2) & 0x3U) << SPI_ADSIZE_POS), 
cmd_byte, OPI_DUMMY(0));
+               } 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;
 
@@ -921,6 +1047,56 @@ err:
        return retval;
 }
 
+COMMAND_HANDLER(stmqspi_handle_always_4byte)
+{
+       struct flash_bank *bank;
+       struct stmqspi_flash_bank *stmqspi_info;
+       int retval;
+
+       LOG_DEBUG("%s", __func__);
+
+       if (CMD_ARGC != 1 && CMD_ARGC != 2)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       stmqspi_info = bank->driver_priv;
+
+       if (CMD_ARGC == 1)
+               command_print(CMD, stmqspi_info->always_4byte ? "on" : "off");
+       else
+               COMMAND_PARSE_BOOL(CMD_ARGV[1], stmqspi_info->always_4byte, 
"on", "off");
+
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(stmqspi_handle_always_sfdp)
+{
+       struct flash_bank *bank;
+       struct stmqspi_flash_bank *stmqspi_info;
+       int retval;
+
+       LOG_DEBUG("%s", __func__);
+
+       if (CMD_ARGC != 1 && CMD_ARGC != 2)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       stmqspi_info = bank->driver_priv;
+
+       if (CMD_ARGC == 1)
+               command_print(CMD, stmqspi_info->always_sfdp ? "on" : "off");
+       else
+               COMMAND_PARSE_BOOL(CMD_ARGV[1], stmqspi_info->always_sfdp, 
"on", "off");
+
+       return ERROR_OK;
+}
+
 static int qspi_erase_sector(struct flash_bank *bank, unsigned int sector)
 {
        struct target *target = bank->target;
@@ -929,14 +1105,14 @@ static int qspi_erase_sector(struct flash_bank *bank, 
unsigned int sector)
        uint16_t status;
        int retval;
 
-       retval = qspi_write_enable(bank);
+       retval = qspi_write_enable(bank, true, true);
        if (retval != ERROR_OK)
                goto err;
 
        /* Send Sector Erase command */
        if (IS_OCTOSPI)
                retval = octospi_cmd(bank, OCTOSPI_WRITE_MODE, 
OCTOSPI_CCR_SECTOR_ERASE,
-                       stmqspi_info->dev.erase_cmd);
+                       stmqspi_info->dev.erase_cmd, 0);
        else
                retval = target_write_u32(target, io_base + QSPI_CCR, 
QSPI_CCR_SECTOR_ERASE);
        if (retval != ERROR_OK)
@@ -1414,7 +1590,7 @@ static int qspi_read_write_block(struct flash_bank *bank, 
uint8_t *buffer,
                        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)),
+                                               (OPI_MODE ? 
(OPI_DUMMY(stmqspi_info->opi_rdid) << OCTOSPI_DCYC_POS) : 0)),
                        h_to_le_32(OPI_CMD(SPIFLASH_READ_STATUS)),
                },
                {
@@ -1714,89 +1890,125 @@ static int stmqspi_verify(struct flash_bank *bank, 
const uint8_t *buffer,
        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)
+/* Find appropriate address length and dummy setting, in particular in octo 
mode */
+static int find_sfdp_params(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;
        uint8_t data;
-       unsigned int dual, count;
+       unsigned int dual, count, *dummy, *len;
        bool flash1 = !(stmqspi_info->saved_cr & BIT(SPI_FSEL_FLASH));
        int retval;
        const unsigned int max_bytes = 64;
 
        dual = (stmqspi_info->saved_cr & BIT(SPI_DUAL_FLASH)) ? 1 : 0;
 
-       LOG_DEBUG("%s: len=%d, dual=%u, flash1=%d",
-               __func__, len, dual, flash1);
+       LOG_DEBUG("%s: dual=%u, flash1=%d", __func__, dual, flash1);
 
-       /* Abort any previous operation */
-       retval = target_write_u32(target, io_base + SPI_CR,
-               stmqspi_info->saved_cr | BIT(SPI_ABORT));
-       if (retval != ERROR_OK)
-               goto err;
+       /* use sfdp_len1/2 according to currently selected flash */
+       len = (stmqspi_info->saved_cr & BIT(SPI_FSEL_FLASH)) ?
+               &stmqspi_info->sfdp_len2 : &stmqspi_info->sfdp_len1;
 
-       /* Wait for busy to be cleared */
-       retval = poll_busy(bank, SPI_PROBE_TIMEOUT);
-       if (retval != ERROR_OK)
-               goto err;
+       /* use sfdp_dummy1/2 according to currently selected flash */
+       dummy = (stmqspi_info->saved_cr & BIT(SPI_FSEL_FLASH)) ?
+               &stmqspi_info->sfdp_dummy2 : &stmqspi_info->sfdp_dummy1;
 
-       /* 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;
+       /* send always four address bytes, regardless of mode */
+       *len = 4;
+       *dummy = 0;
 
-       /* 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;
+       unsigned int pos[2] = { 0, 0 };
 
-       /* Read SFDP block */
-       if (IS_OCTOSPI)
-               retval = octospi_cmd(bank, 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;
+       for (uint32_t k = 0; k < ARRAY_SIZE(pos); k++) {
+               /* Abort any previous operation */
+               retval = target_write_u32(target, io_base + SPI_CR,
+                       stmqspi_info->saved_cr | BIT(SPI_ABORT));
+               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;
+               /* Wait for busy to be cleared */
+               retval = poll_busy(bank, SPI_PROBE_TIMEOUT);
+               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;
-               }
+               /* 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;
 
-               retval = target_read_u8(target, io_base + SPI_DR, &data);
+               /* Read SFDP block */
+               if (IS_OCTOSPI)
+                       retval = octospi_cmd(bank, OCTOSPI_READ_MODE,
+                               OCTOSPI_CCR_READ_SFDP(*len), SPIFLASH_READ_SFDP,
+                               OPI_DUMMY(*dummy));
+               else
+                       retval = target_write_u32(target, io_base + QSPI_CCR,
+                               QSPI_CCR_READ_SFDP(*len, *dummy));
                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;
-               }
+               /* Read from start of sfdp block plus offset, some devices      
        *
+                * require 4-byte aligned addresses for SFDP access             
                *
+                */
+               retval = target_write_u32(target, io_base + SPI_AR, k << 2);
+               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;
+                       }
 
-               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;
+
+                       pos[k] = count;
+                       if (data == 'S') {
+                               LOG_DEBUG("start of SFDP header for flash%c 
after %u dummy"
+                                       " bytes from addr %u", flash1 ? '1' : 
'2', count, k << 2);
+                               break;
+                       }
+
+                       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;
+                       }
                }
        }
 
-       LOG_DEBUG("no start of SFDP header even after %u dummy bytes", count);
+       if (pos[0] >= max_bytes) {
+               retval = ERROR_FAIL;
+               LOG_DEBUG("no start of SFDP header even after %u dummy bytes", 
count);
+               goto err;
+       } else if (pos[0] == pos[ARRAY_SIZE(pos) - 1]) {
+               /* 4th address byte has no effect, hence 3-byte address */
+               *len = 3;
+
+               /* 4th address byte was additional dummy byte */
+               if (stmqspi_info->saved_ccr & BIT(OCTOSPI_DQSEN))
+                       *dummy = pos[0];
+               else
+                       *dummy = pos[0] + 1;
+       } else {
+               *len = 4;
+               *dummy = pos[0];
+       }
+
+       LOG_INFO("read SFDP flash%c: cmd %d%s, addr %d, dummy %d",
+               flash1 ? '1' : '2', OPI_2ND_BYTE ? 2 : 1,
+               OPI_2ND_BYTE ? (stmqspi_info->exor_ir ? " inv" : " dbl") : "", 
*len, *dummy);
 
 err:
        /* Abort operation */
@@ -1813,25 +2025,26 @@ static int read_sfdp_block(struct flash_bank *bank, 
uint32_t addr,
        struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv;
        uint32_t io_base = stmqspi_info->io_base;
        bool flash1 = !(stmqspi_info->saved_cr & BIT(SPI_FSEL_FLASH));
-       unsigned int dual, count, len, *dummy;
+       unsigned int count, dual, *len, *dummy;
        int retval;
 
        dual = (stmqspi_info->saved_cr & BIT(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_len1 only */
+               len = &stmqspi_info->sfdp_len1;
 
                /* in octo mode, use sfdp_dummy1 only */
                dummy = &stmqspi_info->sfdp_dummy1;
-               if (*dummy == 0) {
-                       retval = find_sfdp_dummy(bank, len);
+               if (*len == 0) {
+                       retval = find_sfdp_params(bank);
                        if (retval != ERROR_OK)
                                return retval;
                }
        } else {
-               /* in all other modes 3-byte-address and 8(?) dummy clocks */
-               len = 3;
+               /* use sfdp_len1/2 according to currently selected flash */
+               len = (stmqspi_info->saved_cr & BIT(SPI_FSEL_FLASH)) ?
+                       &stmqspi_info->sfdp_len2 : &stmqspi_info->sfdp_len1;
 
                /* use sfdp_dummy1/2 according to currently selected flash */
                dummy = (stmqspi_info->saved_cr & BIT(SPI_FSEL_FLASH)) ?
@@ -1841,15 +2054,15 @@ static int read_sfdp_block(struct flash_bank *bank, 
uint32_t addr,
                 * 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 (*len == 0) {
+                       retval = find_sfdp_params(bank);
                        if (retval != ERROR_OK)
                                return retval;
                }
        }
 
-       LOG_DEBUG("%s: addr=0x%08" PRIx32 " words=0x%08" PRIx32 " dummy=%u",
-               __func__, addr, words, *dummy);
+       LOG_DEBUG("%s: addr=0x%08" PRIx32 " words=0x%08" PRIx32 " len=%u 
dummy=%u",
+               __func__, addr, words, *len, *dummy);
 
        /* Abort any previous operation */
        retval = target_write_u32(target, io_base + SPI_CR,
@@ -1867,18 +2080,20 @@ static int read_sfdp_block(struct flash_bank *bank, 
uint32_t addr,
        if (retval != ERROR_OK)
                goto err;
 
-       /* Read that many words plus dummy bytes */
+       /* 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 */
+       /* Read SFDP block, always 0 dummy clocks except when DQS ist enabled */
        if (IS_OCTOSPI)
                retval = octospi_cmd(bank, OCTOSPI_READ_MODE,
-                       OCTOSPI_CCR_READ_SFDP(len), SPIFLASH_READ_SFDP);
+                       OCTOSPI_CCR_READ_SFDP(*len), SPIFLASH_READ_SFDP,
+                       OPI_DUMMY(0));
        else
-               retval = target_write_u32(target, io_base + QSPI_CCR, 
QSPI_CCR_READ_SFDP);
+               retval = target_write_u32(target, io_base + QSPI_CCR,
+                       QSPI_CCR_READ_SFDP(*len, 0));
        if (retval != ERROR_OK)
                goto err;
 
@@ -1886,13 +2101,14 @@ static int read_sfdp_block(struct flash_bank *bank, 
uint32_t addr,
        if (retval != ERROR_OK)
                goto err;
 
-       /* dummy clocks */
+       /* dummy bytess */
        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;
@@ -1981,7 +2197,8 @@ static int read_flash_id(struct flash_bank *bank, 
uint32_t *id1, uint32_t *id2)
                        case 0:
                                if (IS_OCTOSPI)
                                        retval = octospi_cmd(bank, 
OCTOSPI_READ_MODE,
-                                               OCTOSPI_CCR_READ_MID, 
SPIFLASH_READ_MID);
+                                               OCTOSPI_CCR_READ_MID, 
SPIFLASH_READ_MID,
+                                               
OPI_DUMMY(stmqspi_info->opi_rdid));
                                else
                                        retval = target_write_u32(target, 
io_base + QSPI_CCR, QSPI_CCR_READ_MID);
                                break;
@@ -1989,7 +2206,8 @@ static int read_flash_id(struct flash_bank *bank, 
uint32_t *id1, uint32_t *id2)
                        case 1:
                                if (IS_OCTOSPI)
                                        retval = octospi_cmd(bank, 
OCTOSPI_READ_MODE,
-                                               OCTOSPI_CCR_READ_ID, 
SPIFLASH_READ_ID);
+                                               OCTOSPI_CCR_READ_ID, 
SPIFLASH_READ_ID,
+                                               
OPI_DUMMY(stmqspi_info->opi_rdid));
                                else
                                        retval = target_write_u32(target, 
io_base + QSPI_CCR, QSPI_CCR_READ_ID);
                                break;
@@ -2013,14 +2231,17 @@ static int read_flash_id(struct flash_bank *bank, 
uint32_t *id1, uint32_t *id2)
                (void)target_read_u32(target, io_base + SPI_SR, &dummy);
 
                /* Read ID from Data Register */
+               uint32_t cont1 = 0, cont2 = 0;
                for (len1 = 0, len2 = 0; count > 0; --count) {
                        if ((stmqspi_info->saved_cr & (BIT(SPI_DUAL_FLASH) |
                                BIT(SPI_FSEL_FLASH))) != BIT(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)) {
+                               /* discard but count continuation codes */
+                               if (len1 < 2 && byte == 0x7F) {
+                                       cont1++;
+                               } else if (len1 < 3) {
                                        *id1 = (*id1 >> 8) | ((uint32_t)byte) 
<< 16;
                                        len1++;
                                }
@@ -2030,22 +2251,28 @@ static int read_flash_id(struct flash_bank *bank, 
uint32_t *id1, uint32_t *id2)
                                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)) {
+                               /* discard but count continuation codes */
+                               if (len2 < 2 && byte == 0x7F) {
+                                       cont2++;
+                               } else if (len2 < 3) {
                                        *id2 = (*id2 >> 8) | ((uint32_t)byte) 
<< 16;
                                        len2++;
                                }
                        }
                }
 
-               if (((*id1 != 0x000000) && (*id1 != 0xFFFFFF)) ||
-                       ((*id2 != 0x000000) && (*id2 != 0xFFFFFF)))
+               /* insert countinuation counts */
+               *id1 |= cont1 << 24;
+               *id2 |= cont2 << 24;
+
+               if (((*id1 != 0x00000000U) && (*id1 != 0x00FFFFFFU)) ||
+                       ((*id2 != 0x00000000U) && (*id2 != 0x00FFFFFFU)))
                        break;
        }
 
        if ((stmqspi_info->saved_cr & (BIT(SPI_DUAL_FLASH) |
                BIT(SPI_FSEL_FLASH))) != BIT(SPI_FSEL_FLASH)) {
-               if ((*id1 == 0x000000) || (*id1 == 0xFFFFFF)) {
+               if ((*id1 == 0x00000000U) || (*id1 == 0x00FFFFFFU)) {
                        /* no id retrieved, so id must be set manually */
                        LOG_INFO("No id from flash1");
                        retval = ERROR_FLASH_BANK_NOT_PROBED;
@@ -2053,7 +2280,7 @@ static int read_flash_id(struct flash_bank *bank, 
uint32_t *id1, uint32_t *id2)
        }
 
        if ((stmqspi_info->saved_cr & (BIT(SPI_DUAL_FLASH) | 
BIT(SPI_FSEL_FLASH))) != 0) {
-               if ((*id2 == 0x000000) || (*id2 == 0xFFFFFF)) {
+               if ((*id2 == 0x00000000U) || (*id2 == 0x00FFFFFFU)) {
                        /* no id retrieved, so id must be set manually */
                        LOG_INFO("No id from flash2");
                        retval = ERROR_FLASH_BANK_NOT_PROBED;
@@ -2077,17 +2304,21 @@ static int stmqspi_probe(struct flash_bank *bank)
        bool octal_dtr;
        int retval;
 
-       /* invalidate all flash device info */
-       if (stmqspi_info->probed)
+       if (stmqspi_info->probed) {
+               /* invalidate all flash deviced related info */
+               bank->size = 0;
+               bank->num_sectors = 0;
                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";
+               bank->sectors = NULL;
+               memset(&stmqspi_info->dev, 0, sizeof(stmqspi_info->dev));
+               stmqspi_info->opi_addr = 0;
+               stmqspi_info->opi_rdid = 0;
+               stmqspi_info->sfdp_len1 = 0;
+               stmqspi_info->sfdp_len2 = 0;
+               stmqspi_info->sfdp_dummy1 = 0;
+               stmqspi_info->sfdp_dummy2 = 0;
+               stmqspi_info->probed = false;
+       }
 
        /* Abort any previous operation */
        retval = stmqspi_abort(bank);
@@ -2113,7 +2344,6 @@ static int stmqspi_probe(struct flash_bank *bank)
                uint32_t magic_id;
 
                retval = target_read_u32(target, io_base + OCTOSPI_MAGIC, 
&magic_id);
-
                if (retval == ERROR_OK && magic_id == OCTO_MAGIC_ID) {
                        LOG_DEBUG("OCTOSPI_MAGIC present");
                        stmqspi_info->octo = true;
@@ -2134,15 +2364,25 @@ static int stmqspi_probe(struct flash_bank *bank)
                uint32_t dcr1;
 
                retval = target_read_u32(target, io_base + OCTOSPI_DCR1, &dcr1);
-
                if (retval == ERROR_OK)
                        retval = target_read_u32(target, io_base + OCTOSPI_TCR,
                                &stmqspi_info->saved_tcr);
-
                if (retval == ERROR_OK)
                        retval = target_read_u32(target, io_base + OCTOSPI_IR,
                                &stmqspi_info->saved_ir);
 
+               if (OPI_2ND_BYTE) {
+                       /* remember whether second instruction byte has to be 
inverted */
+                       stmqspi_info->exor_ir = ((stmqspi_info->saved_ir >> 8)
+                               ^ stmqspi_info->saved_ir) & 0xFFU;
+                       if (stmqspi_info->exor_ir && stmqspi_info->exor_ir != 
0xFFU) {
+                               LOG_ERROR("second cmd byte must be either same 
as first or inverted");
+                               return ERROR_FAIL;
+                       }
+               } else {
+                       stmqspi_info->exor_ir = 0;
+               }
+
                if (retval != ERROR_OK) {
                        LOG_ERROR("No OCTOSPI at io_base 0x%08" PRIx32, 
io_base);
                        stmqspi_info->probed = false;
@@ -2184,34 +2424,47 @@ static int stmqspi_probe(struct flash_bank *bank)
        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;
-       }
-
+       retval = find_opi_params(bank);
        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
-                                       " KiB", p->name, id1, p->size_in_bytes 
/ 1024);
-                       else
-                               LOG_INFO("flash1 \'%s\' id = 0x%06" PRIx32 " 
size = %" PRIu32
-                                       " B", p->name, id1, p->size_in_bytes);
-                       break;
+       /* opi_rdid might be to small by one due to predrive, try to    *
+        * work around by speculative increment in OPI mode if first    *
+        * attempt to identify ID failed                                        
                        */
+       bool predrive = OPI_MODE;
+
+       do {
+               /* read and decode flash ID; returns in indirect mode */
+               retval = read_flash_id(bank, &id1, &id2);
+               LOG_DEBUG("id1 0x%08" PRIx32 ", id2 0x%08" 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 (id1 && !p->name) {
+               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%08" 
PRIx32 " size = %" PRIu32
+                                               "KiB", p->name, id1, 
p->size_in_bytes / 1024);
+                               else
+                                       LOG_INFO("flash1 \'%s\' id = 0x%08" 
PRIx32 " size = %" PRIu32
+                                               "iB", p->name, id1, 
p->size_in_bytes);
+                               break;
+                       }
+               }
+
+               predrive = !predrive;
+       } while (!p->name && !predrive && stmqspi_info->opi_rdid++);
+
+       if (id1 && (stmqspi_info->always_sfdp || !p->name)) {
                /* chip not been identified by id, then try SFDP */
                struct flash_device temp;
                uint32_t saved_cr = stmqspi_info->saved_cr;
@@ -2224,14 +2477,14 @@ static int stmqspi_probe(struct flash_bank *bank)
                stmqspi_info->saved_cr = saved_cr;
 
                if (retval == ERROR_OK) {
-                       LOG_INFO("flash1 \'%s\' id = 0x%06" PRIx32 " size = %" 
PRIu32
-                               " KiB", temp.name, id1, temp.size_in_bytes / 
1024);
+                       LOG_INFO("flash1 \'%s\' id = 0x%08" PRIx32 " size = %" 
PRIu32
+                               "KiB", 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
+                       LOG_WARNING("Unknown flash1 device id = 0x%08" PRIx32
                                " - set flash parameters manually", id1);
                        retval = ERROR_OK;
                        goto err;
@@ -2239,14 +2492,14 @@ static int stmqspi_probe(struct flash_bank *bank)
        }
 
        /* identify flash2 */
-       for (p = flash_devices; id2 && p->name ; p++) {
+       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
-                                       " KiB", p->name, id2, p->size_in_bytes 
/ 1024);
+                               LOG_INFO("flash2 \'%s\' id = 0x%08" PRIx32 " 
size = %" PRIu32
+                                       "KiB", p->name, id2, p->size_in_bytes / 
1024);
                        else
-                               LOG_INFO("flash2 \'%s\' id = 0x%06" PRIx32 " 
size = %" PRIu32
-                                       " B", p->name, id2, p->size_in_bytes);
+                               LOG_INFO("flash2 \'%s\' id = 0x%08" PRIx32 " 
size = %" PRIu32
+                                       "iB", p->name, id2, p->size_in_bytes);
 
                        if (!id1)
                                memcpy(&stmqspi_info->dev, p, 
sizeof(stmqspi_info->dev));
@@ -2269,7 +2522,7 @@ static int stmqspi_probe(struct flash_bank *bank)
                }
        }
 
-       if (id2 && !p->name) {
+       if (id2 && (stmqspi_info->always_sfdp || !p->name)) {
                /* chip not been identified by id, then try SFDP */
                struct flash_device temp;
                uint32_t saved_cr = stmqspi_info->saved_cr;
@@ -2282,11 +2535,11 @@ static int stmqspi_probe(struct flash_bank *bank)
                stmqspi_info->saved_cr = saved_cr;
 
                if (retval == ERROR_OK)
-                       LOG_INFO("flash2 \'%s\' id = 0x%06" PRIx32 " size = %" 
PRIu32
-                               " KiB", temp.name, id2, temp.size_in_bytes / 
1024);
+                       LOG_INFO("flash2 \'%s\' id = 0x%08" PRIx32 " size = %" 
PRIu32
+                               "KiB", 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
+                       LOG_WARNING("Unknown flash2 device id = 0x%08" PRIx32
                                " - set flash parameters manually", id2);
                        retval = ERROR_OK;
                        goto err;
@@ -2388,23 +2641,25 @@ static int get_stmqspi_info(struct flash_bank *bank, 
struct command_invocation *
                return ERROR_FLASH_BANK_NOT_PROBED;
        }
 
-       command_print_sameline(cmd, "flash%s%s \'%s\', device id = 0x%06" PRIx32
-                       ", flash size = %" PRIu32 "%s B\n(page size = %" PRIu32
+       command_print_sameline(cmd, "flash%s%s \'%s\', device id = 0x%08" PRIx32
+                       ", flash size = %" PRIu32 "%siB\n(page size = %" PRIu32
                        ", read = 0x%02" PRIx8 ", qread = 0x%02" PRIx8
                        ", pprog = 0x%02" PRIx8 ", mass_erase = 0x%02" PRIx8
-                       ", sector size = %" PRIu32 " %sB, sector_erase = 0x%02" 
PRIx8 ")",
+                       ", sector size = %" PRIu32 "%siB, sector_erase = 0x%02" 
PRIx8 ")",
                        ((stmqspi_info->saved_cr & (BIT(SPI_DUAL_FLASH) |
                        BIT(SPI_FSEL_FLASH))) != BIT(SPI_FSEL_FLASH)) ? "1" : 
"",
                        ((stmqspi_info->saved_cr & (BIT(SPI_DUAL_FLASH) |
                        BIT(SPI_FSEL_FLASH))) != 0) ? "2" : "",
                        stmqspi_info->dev.name, stmqspi_info->dev.device_id,
-                       bank->size / 4096 ? bank->size / 1024 : bank->size,
-                       bank->size / 4096 ? "Ki" : "", 
stmqspi_info->dev.pagesize,
+                       (bank->size >> 22) ? (bank->size >> 20) :
+                               ((bank->size >> 12) ? bank->size >> 10 : 
bank->size),
+                       (bank->size >> 22) ? "M" : ((bank->size >> 12) ? "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 ? "Ki" : "",
+                       stmqspi_info->dev.sectorsize / 4096 ? "K" : "",
                        stmqspi_info->dev.erase_cmd);
 
        return ERROR_OK;
@@ -2433,6 +2688,20 @@ static const struct command_registration 
stmqspi_exec_command_handlers[] = {
                .usage = "bank_id num_resp cmd_byte ...",
                .help = "Send low-level command cmd_byte and following bytes or 
read num_resp.",
        },
+       {
+               .name = "always_4byte",
+               .handler = stmqspi_handle_always_4byte,
+               .mode = COMMAND_EXEC,
+               .usage = "bank_id [ on | off ]",
+               .help = "Use always 4-byte address except for basic 0x03.",
+       },
+       {
+               .name = "always_sfdp",
+               .handler = stmqspi_handle_always_sfdp,
+               .mode = COMMAND_EXEC,
+               .usage = "bank_id [ on | off ]",
+               .help = "Always prefer SFDP tables to hardcoded device table.",
+       },
        COMMAND_REGISTRATION_DONE
 };
 
diff --git a/src/flash/nor/stmqspi.h b/src/flash/nor/stmqspi.h
index 245df40b38..658f36ddf5 100644
--- a/src/flash/nor/stmqspi.h
+++ b/src/flash/nor/stmqspi.h
@@ -1,7 +1,7 @@
 /* SPDX-License-Identifier: GPL-2.0-or-later */
 
 /***************************************************************************
- *   Copyright (C) 2016 - 2018 by Andreas Bolsch                           *
+ *   Copyright (C) 2016 - 2022 by Andreas Bolsch                           *
  *   andreas.bol...@mni.thm.de                                             *
  ***************************************************************************/
 
@@ -32,6 +32,7 @@
 
 /* common bits in QSPI_SR/FCR and OCTOSPI_SR/FCR */
 #define SPI_BUSY               5               /* Busy flag */
+#define SPI_TOF                        4               /* Timeout flag */
 #define SPI_FTF                        2               /* FIFO threshold flag 
*/
 #define SPI_TCF                        1               /* Transfer complete 
flag */
 
@@ -71,7 +72,7 @@
 #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? */
+#define OCTO_MAGIC_ID  0xA3C5DD01U     /* Magic ID, deleted from RM, why? */
 
 /* additional bits in OCTOSPI_CR */
 #define OCTOSPI_WRITE_MODE     0x00000000U                     /* indirect 
write mode */
@@ -94,7 +95,8 @@
 #define OCTOSPI_DQSEN          29                                              
/* DQS enable */
 #define OCTOSPI_DDTR           27                                              
/* DTR for data */
 #define OCTOSPI_NO_DDTR                (~BIT(OCTOSPI_DDTR))    /* no DTR for 
data, but maybe still DQS */
-#define OCTOSPI_ISIZE_MASK     (0x30)                                  /* 
ISIZE field */
+#define OCTOSPI_ISIZE_MASK     (0x30U)                                 /* 
ISIZE field */
+#define OCTOSPI_IMODE_MASK     (0x4U)                                  /* 
IMODE eight lines */
 
 /* fields in OCTOSPI_TCR */
 #define OCTOSPI_DCYC_POS       0                                       /* bit 
position of DCYC */
diff --git a/tcl/board/st_b-u585i-iot02a.cfg b/tcl/board/st_b-u585i-iot02a.cfg
new file mode 100644
index 0000000000..7e4734423d
--- /dev/null
+++ b/tcl/board/st_b-u585i-iot02a.cfg
@@ -0,0 +1,114 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+# This is an B-U585I-IOT02A Discovery kit for IoT node with a single 
STM32U585AII6Q chip.
+# http://www.st.com/en/evaluation-tools/b-u585i-iot02a.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 0
+set OCTOSPI2 1
+
+source [find target/stm32u5x.cfg]
+
+reset_config trst_and_srst connect_assert_srst
+
+# OCTOSPI initialization
+proc octospi_init { octo } {
+       global b
+       mmw 0x46020C8C 0x002001FF 0                             ;# RCC_AHB2ENR1 
|= OCTOSPIM|GPIOAEN-GPIOIEN
+       mmw 0x46020C90 0x00000100 0                             ;# RCC_AHB2ENR2 
|= OCTOSPI2EN
+       sleep 1                                                                 
;# Wait for clock startup
+
+       mww 0x420C4004 0x00000000                               ;# 
OCTOSPIM_P1CR: disable Port 1
+       mww 0x420C4008 0x07050333                               ;# 
OCTOSPIM_P2CR: assign Port 2 to OCTOSPI2
+
+       # PI05: OCSPI2_NCS, PF04: OCSPI2_CLK, PF12: OCSPI2_DQS, PH12: 
OCSPI2_IO7, PH11: OCSPI2_IO6, PH10: OCSPI2_IO5,
+       # PH09: OCSPI2_IO4, PF03: OCSPI2_IO3, PF02: OCSPI2_IO2, PF01: 
OCSPI2_IO1, PF00: OCSPI2_IO0
+
+       # PF12:AF05:V, PF04:AF05:V, PF03:AF05:V, PF02:AF05:V, PF01:AF05:V, 
PF00:AF05:V
+       # PH12:AF05:V, PH11:AF05:V, PH10:AF05:V, PH09:AF05:V, PI05:AF05:V
+
+       # Port F: PF12:AF05:V, PF04:AF05:V, PF03:AF05:V, PF02:AF05:V, 
PF01:AF05:V, PF00:AF05:V
+       mmw 0x42021400 0x020002AA 0x01000155    ;# MODER
+       mmw 0x42021408 0x030003FF 0x00000000    ;# OSPEEDR
+       mmw 0x4202140C 0x00000000 0x030003FF    ;# PUPDR
+       mmw 0x42021420 0x00055555 0x000AAAAA    ;# AFRL
+       mmw 0x42021424 0x00050000 0x000A0000    ;# AFRH
+       # Port H: PH12:AF05:V, PH11:AF05:V, PH10:AF05:V, PH09:AF05:V
+       mmw 0x42021C00 0x02A80000 0x01540000    ;# MODER
+       mmw 0x42021C08 0x03FC0000 0x00000000    ;# OSPEEDR
+       mmw 0x42021C0C 0x00000000 0x03FC0000    ;# PUPDR
+       mmw 0x42021C24 0x00055550 0x000AAAA0    ;# AFRH
+       # Port I: PI05:AF05:V
+       mmw 0x42022000 0x00000800 0x00000400    ;# MODER
+       mmw 0x42022008 0x00000C00 0x00000000    ;# OSPEEDR
+       mmw 0x4202200C 0x00000000 0x00000C00    ;# PUPDR
+       mmw 0x42022020 0x00500000 0x00A00000    ;# AFRL
+
+       # OCTOSPI2: memory-mapped 1-line read mode with 4-byte addresses
+       mww 0x420D2530 0x00001000                               ;# 
OCTOSPI_LPTR: deactivate CS after 4096 clocks when FIFO is full
+       mww 0x420D2400 0x3040000B                               ;# OCTOSPI_CR: 
FMODE=0x1, APMS=1, FTHRES=0, FSEL=0, DQM=0, TCEN=0
+       mww 0x420D2408 0x01190100                               ;# 
OCTOSPI_DCR1: MTYP=0x1, FSIZE=0x19, CSHT=0x01, CKMODE=0, DLYBYP=0
+       mww 0x420D240C 0x00000003                               ;# 
OCTOSPI_DCR2: PRESCALER=3
+
+       mww 0x420D2508 0x40000000                               ;# OCTOSPI_TCR: 
SSHIFT=0, DHQC=0, DCYC=0x0
+       mww 0x420D2500 0x01003101                               ;# OCTOSPI_CCR: 
DMODE=0x1, ABMODE=0x0, ADSIZE=0x3, ADMODE=0x1, ISIZE=0x0, IMODE=0x1
+       mww 0x420D2510 0x00000013                               ;# OCTOSPI_IR: 
INSTR=READ4B
+
+       flash probe $b                                                  ;# load 
configuration from CR, TCR, CCR, IR register values
+
+       if { $octo == 1 } {
+               stmqspi cmd $b 1 0x71 0x00 0x00 0x00 0x00                       
;# Read Conf. Reg. 2, addr 0x00000000: DOPI, SOPI bits
+               stmqspi cmd $b 0 0x06                                           
                ;# Write Enable
+               stmqspi cmd $b 1 0x05                                           
                ;# Read Status Register
+               stmqspi cmd $b 0 0x72 0x00 0x00 0x00 0x00 0x02          ;# 
Write Conf. Reg. 2, addr 0x00000000: DTR OPI enable
+
+               # OCTOSPI2: memory-mapped 8-line read mode with 4-byte addresses
+               mww 0x420D2400 0x3040000B                               ;# 
OCTOSPI_CR: FMODE=0x3, APMS=1, FTHRES=0, FSEL=0, DQM=0, TCEN=1, EN=1
+               mww 0x420D2508 0x10000006                               ;# 
OCTOSPI_TCR: SSHIFT=0, DHQC=1, DCYC=0x6
+               mww 0x420D2500 0x2C003C1C                               ;# 
OCTOSPI_CCR: DTR, DMODE=0x4, ABMODE=0x0, ADSIZE=0x3, ADMODE=0x4, ISIZE=0x1, 
IMODE=0x4
+               mww 0x420D2510 0x0000EE11                               ;# 
OCTOSPI_IR: INSTR=OCTA DTR Read
+
+               flash probe $b                                                  
;# reload configuration from CR, TCR, CCR, IR register values
+
+               stmqspi cmd $b 0 0x06                                           
                ;# Write Enable
+               stmqspi cmd $b 1 0x05 0x00 0x00 0x00 0x00                       
;# Read Status Register (note dummy address in 8-line mode)
+               stmqspi cmd $b 0 0x04                                           
                ;# Write Disable
+               stmqspi cmd $b 1 0x05 0x00 0x00 0x00 0x00                       
;# Read Status Register (note dummy address in 8-line mode)
+               stmqspi cmd $b 1 0x71 0x00 0x00 0x00 0x00                       
;# Read Conf. Reg. 2, addr 0x00000000: DOPI, SOPI bits
+       }
+}
+
+$_TARGETNAME configure -event reset-init {
+       global OCTOSPI1
+       global OCTOSPI2
+
+       mmw 0x46020C94 0x00000004 0x00000000    ;# RCC_AHB3ENR |= PWREN
+       mmw 0x4602080C 0x00040000 0x00000000    ;# PWR_VOSR |= BOOSTEN
+       sleep 10
+       mmw 0x4602080C 0x00020000 0x00010000    ;# PWR_VOSR: VOS range 2
+       mmw 0x40022000 0x00000003 0x0000000C    ;# FLASH_ACR: 3 WS for 96 MHz 
HCLK
+       sleep 10
+       mmw 0x46020C00 0x00000100 0x00000000    ;# HSI16 on
+       mww 0x46020C28 0x00040102                               ;# 96 MHz: 
PLL1REN=1, PLL1M=2, PPL1RGE=00, HSI16
+       mww 0x46020C34 0x0301022F                               ;# PLL1R=4, 
PLLN=48
+       mww 0x46020C20 0x00000000                               ;# APB1: /1, 
APB2: /1, AHB: /1
+       mmw 0x46020C00 0x01000000 0x00000000    ;# PLL1 on
+       sleep 1
+       mmw 0x46020C1C 0x00000003 0x00000000    ;# switch to PLL
+       sleep 1
+
+       adapter speed 24000
+
+       if { $OCTOSPI2 } {
+               octospi_init 1
+       }
+}
+
diff --git a/tcl/board/stm32h7b3i-disco.cfg b/tcl/board/stm32h7b3i-disco.cfg
index 58ad9f7814..e399c4ff4e 100644
--- a/tcl/board/stm32h7b3i-disco.cfg
+++ b/tcl/board/stm32h7b3i-disco.cfg
@@ -111,6 +111,9 @@ $_CHIPNAME.cpu0 configure -event reset-init {
 
        mmw 0x52002000 0x00000004 0x0000000B    ;# FLASH_ACR: 4 WS for 192 MHZ 
HCLK
 
+       mww 0x58024818 0x00008000                               ;# PWR_SRDCR: 
VOS scale 1
+       sleep 10
+
        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
diff --git a/tcl/board/stm32l562e-disco.cfg b/tcl/board/stm32l562e-disco.cfg
new file mode 100644
index 0000000000..9a12beeb57
--- /dev/null
+++ b/tcl/board/stm32l562e-disco.cfg
@@ -0,0 +1,108 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+# This is a STM32L562E discovery board with a single STM32L562QEI6Q chip.
+# http://www.st.com/en/evaluation-tools/stm32l562e-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
+if {![info exists OCTOSPI]} {
+       set OCTOSPI 1
+}
+
+source [find target/stm32l5x.cfg]
+
+reset_config trst_and_srst connect_assert_srst
+
+# OCTOSPI initialization
+# octo: 8-line mode
+proc octospi_init { octo } {
+       global a
+       mmw 0x4002104C 0x000000FF 0                             ;# RCC_AHB2ENR 
|= GPIOAEN-GPIOHEN (enable clocks)
+       mmw 0x40021050 0x00000100 0                             ;# RCC_AHB3ENR 
|= OSPI1EN (enable clocks)
+       sleep 1                                                                 
;# Wait for clock startup
+
+       mmw 0x40007004 0x00000200 0                             ;# PWR_CR2 |= 
IOSV (required for use of GPOIG, cf. RM0438)
+
+       # PA02: OCSPI1_NCS, PA03: OCSPI1_CLK, PB02: OCSPI1_DQS, PC00: 
OCSPI1_IO7, PC03: OCSPI1_IO6, PC02: OCSPI1_IO5,
+       # PC01: OCSPI1_IO4, PA06: OCSPI1_IO3, PA07: OCSPI1_IO2, PB00: 
OCSPI1_IO1, PB01: OCSPI1_IO0
+
+       # PA07:AF10:V, PA06:AF10:V, PA03:AF10:V, PA02:AF10:V, PB02:AF10:V, 
PB01:AF10:V
+       # PB00:AF10:V, PC03:AF10:V, PC02:AF10:V, PC01:AF10:V, PC00:AF03:V
+       # Port A: PA07:AF10:V, PA06:AF10:V, PA03:AF10:V, PA02:AF10:V
+       mmw 0x42020000 0x0000A0A0 0x00005050    ;# MODER
+       mmw 0x42020008 0x0000F0F0 0x00000000    ;# OSPEEDR
+       mmw 0x4202000C 0x00000000 0x0000F0F0    ;# PUPDR
+       mmw 0x42020020 0xAA00AA00 0x55005500    ;# AFRL
+       # Port B: PB02:AF10:V, PB01:AF10:V, PB00:AF10:V
+       mmw 0x42020400 0x0000002A 0x00000015    ;# MODER
+       mmw 0x42020408 0x0000003F 0x00000000    ;# OSPEEDR
+       mmw 0x4202040C 0x00000000 0x0000003F    ;# PUPDR
+       mmw 0x42020420 0x00000AAA 0x00000555    ;# AFRL
+       # Port C: PC03:AF10:V, PC02:AF10:V, PC01:AF10:V, PC00:AF03:V
+       mmw 0x42020800 0x000000AA 0x00000055    ;# MODER
+       mmw 0x42020808 0x000000FF 0x00000000    ;# OSPEEDR
+       mmw 0x4202080C 0x00000000 0x000000FF    ;# PUPDR
+       mmw 0x42020820 0x0000AAA3 0x0000555C    ;# AFRL
+
+       # OCTOSPI1: memory-mapped 1-line read mode with 4-byte addresses
+       mww 0x44021130 0x00001000                               ;# 
OCTOSPI_LPTR: deactivate CS after 4096 clocks when FIFO is full
+       mww 0x44021000 0x3040000B                               ;# OCTOSPI_CR: 
FMODE=0x1, APMS=1, FTHRES=0, FSEL=0, DQM=0, TCEN=0
+       mww 0x44021008 0x01190100                               ;# 
OCTOSPI_DCR1: MTYP=0x1, FSIZE=0x19, CSHT=0x01, CKMODE=0, DLYBYP=0
+       mww 0x4402100C 0x00000001                               ;# 
OCTOSPI_DCR2: PRESCALER=1
+
+       mww 0x44021108 0x00000000                               ;# OCTOSPI_TCR: 
SSHIFT=0, DHQC=0, DCYC=0x0
+       mww 0x44021100 0x01003101                               ;# OCTOSPI_CCR: 
DMODE=0x1, ABMODE=0x0, ADSIZE=0x3, ADMODE=0x1, ISIZE=0x0, IMODE=0x1
+       mww 0x44021110 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 
addresses0x44021000
+               mww 0x44021000 0x3040000B                               ;# 
OCTOSPI_CR: FMODE=0x3, APMS=1, FTHRES=0, FSEL=0, DQM=0, TCEN=1, EN=1
+               mww 0x44021108 0x10000006                               ;# 
OCTOSPI_TCR: SSHIFT=0, DHQC=1, DCYC=0x6
+               mww 0x44021100 0x2C003C1C                               ;# 
OCTOSPI_CCR: DTR, DMODE=0x4, ABMODE=0x0, ADSIZE=0x3, ADMODE=0x4, ISIZE=0x1, 
IMODE=0x4
+               mww 0x44021110 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
+       mmw 0x40021058 0x10000000 0                             ;# RCC_APB1ENR1 
|= PWREN (enable clocks)
+       sleep 1
+
+       mww 0x40007000 0x00000200                               ;# PWR_CR1: VOS 
range 1
+       sleep 10
+
+       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/target/stm32l5x.cfg b/tcl/target/stm32l5x.cfg
index c43b699d25..11f1d77710 100644
--- a/tcl/target/stm32l5x.cfg
+++ b/tcl/target/stm32l5x.cfg
@@ -14,6 +14,12 @@ if { [info exists CHIPNAME] } {
 
 source [find target/stm32x5x_common.cfg]
 
+if { [info exists OCTOSPI] && $OCTOSPI } {
+   set a [llength [flash list]]
+   set _OCTOSPINAME $_CHIPNAME.octospi
+   flash bank $_OCTOSPINAME stmqspi 0x90000000 0 0 0 $_TARGETNAME 0x44021000
+}
+
 proc stm32l5x_clock_config {} {
        set offset [expr {[stm32x5x_is_secure] ? 0x10000000 : 0}]
        # MCU clock is MSI (4MHz) after reset, set MCU freq at 110 MHz with PLL
diff --git a/tcl/target/stm32u5x.cfg b/tcl/target/stm32u5x.cfg
index 44b51e2b65..e880d0728f 100644
--- a/tcl/target/stm32u5x.cfg
+++ b/tcl/target/stm32u5x.cfg
@@ -14,6 +14,17 @@ if { [info exists CHIPNAME] } {
 
 source [find target/stm32x5x_common.cfg]
 
+if { [info exists OCTOSPI1] && $OCTOSPI1 } {
+   set a [llength [flash list]]
+   set _OCTOSPINAME1 $_CHIPNAME.octospi1
+   flash bank $_OCTOSPINAME1 stmqspi 0x90000000 0 0 0 $_TARGETNAME 0x420D1400
+}
+if { [info exists OCTOSPI2] && $OCTOSPI2 } {
+   set b [llength [flash list]]
+   set _OCTOSPINAME2 $_CHIPNAME.octospi2
+   flash bank $_OCTOSPINAME2 stmqspi 0x70000000 0 0 0 $_TARGETNAME 0x420D2400
+}
+
 proc stm32u5x_clock_config {} {
        set offset [expr {[stm32x5x_is_secure] ? 0x10000000 : 0}]
        # MCU clock is at MSI 4MHz after reset, set MCU freq at 160 MHz with PLL

-- 

Reply via email to