This is an automated email from Gerrit.

Andreas Bolsch ([email protected]) just uploaded a new patch set to 
Gerrit, which you can find at http://openocd.zylin.com/3162

-- gerrit

commit 4f025a42e02b2a0821a7d37fbfaeb1f172e41315
Author: Andreas Bolsch <[email protected]>
Date:   Wed Dec 2 21:14:54 2015 +0100

    Flash driver for QUADSPI interface in various STM32 variants.
    
    Driver for the QUADSPI interface implemented in STM32F7 and STM32L4.
    The QUADSPI interface has to be initialized by a board specific script
    in advance. The flash driver uses flash configuration based on the
    current register values, so speed, single/dual (F7 only) configuration
    etc. must be initialized properly. Watchdogs and interrupts must be
    disabled, i. e. flash programming has to start from reset state!
    Tested on STM32F746 and STM32L476 Discovery boards. Both use a single
    SPI flash, so dual mode is untested!
    
    Address of RCC_CR corrected in stm32l4.cfg, and clock increased to 48 MHz
    
    Change-Id: I5904fe66866de571d97f05fe986929ac93a7f834
    Signed-off-by: Andreas Bolsch <[email protected]>

diff --git a/contrib/loaders/flash/stmqspi.S b/contrib/loaders/flash/stmqspi.S
new file mode 100644
index 0000000..5a41038
--- /dev/null
+++ b/contrib/loaders/flash/stmqspi.S
@@ -0,0 +1,165 @@
+
+/***************************************************************************
+ *   Copyright (C) 2015 by Andreas Bolsch                                  *
+ *   [email protected]                                             *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ ***************************************************************************/
+
+       .text
+       .syntax unified
+       .cpu cortex-m0
+       .thumb
+       .thumb_func
+
+/* To assemble:
+ * arm-none-eabi-gcc -Wa,-adln -c contrib/loaders/flash/stmqspi.S > stmqspi.lst
+ *
+ * To generate binary file:
+ * arm-none-eabi-objcopy -O binary stmqspi.o stmqspi_flash_write_code.bin
+ *
+ * To generate C array definition:
+ * xxd -i stmqspi_flash_write_code.bin
+ */
+
+/* Params:
+ * r0 - total count (bytes)
+ * r1 - flash page size
+ * r2 - adress offset into flash
+ * r3 - QSPI io_base
+ * Clobbered:
+ * r4 - QSPI DR address
+ * r5 - ptr into buffer
+ * r6 - single / dual
+ * r7 - tmp
+ */
+
+#include "../../../src/flash/nor/stmqspi.h"
+
+       .macro  wait_busy
+0:
+       ldr             r7, [r3, #QSPI_SR]              /* load status */
+       lsrs    r7, r7, #(QSPI_BUSY+1)  /* shift BUSY into C */
+       bcs             0b                                              /* loop 
till BUSY cleared */
+       movs    r7, #(1<<QSPI_TCF)              /* TCF bitmask */
+       str             r7, [r3, #QSPI_FCR]             /* clear TCF flag */
+       .endm
+
+       .macro  wait_fifo
+1:
+       ldr             r7,     [r3, #QSPI_SR]          /* load status */
+       lsrs    r7, #(QSPI_FTF+1)               /* wait for TFT to be set */
+       bcc             1b                                              /* if 
not, then test again */
+       .endm
+
+start:
+       subs    r0, #1                                  /* decrement count for 
DLR */
+       subs    r1, #1                                  /* page size mask and 
for DLR */
+       movs    r4, r3                                  /* copy base address */
+       adds    r4, #QSPI_DR                    /* QSPI DR address */
+       adr             r5, buffer                              /* buffer start 
address */
+       movs    r6, #0                                  /* one byte per default 
*/
+       ldr             r7, [r3, #QSPI_CR]              /* get QSPI CR register 
*/
+       lsrs    r7, #(QSPI_DUAL_FLASH+1)        /* dual bit into carry */
+       bcc             wip_loop                                /* skip if 
single */
+       adds    r6, #1                                  /* two bytes for dual */
+
+wip_loop:
+       wait_busy
+       str             r6, [r3, #QSPI_DLR]             /* one or two bytes */
+       ldr             r7, ccr_read_status             /* CCR for status read 
*/
+       str             r7, [r3, #QSPI_CCR]             /* initiate status read 
*/
+       wait_fifo
+       ldrb    r7, [r4, #0]                    /* get first status register */
+       lsrs    r7, #(SPIFLASH_WIP+1)   /* if first flash busy, */
+       bcs             wip_loop                                /* then poll 
again */
+       tst             r6, r6                                  /* dual flash? 
*/
+       beq             write_enable                    /* no, then ok */
+       wait_fifo
+       ldrb    r7, [r4, #0]                    /* get second status register */
+       lsrs    r7, #(SPIFLASH_WIP+1)   /* if second flash busy, */
+       bcs             wip_loop                                /* then poll 
again */
+
+write_enable:
+       wait_busy
+       ldr             r7, ccr_write_enable    /* CCR for write enable */
+       str             r7, [r3, #QSPI_CCR]             /* initiate write 
enable */
+
+is_enabled:
+       wait_busy
+       str             r6, [r3, #QSPI_DLR]             /* one or two bytes */
+       ldr             r7, ccr_read_status             /* CCR for status read 
*/
+       str             r7, [r3, #QSPI_CCR]             /* initiate status read 
*/
+       wait_fifo
+       ldrb    r7, [r4, #0]                    /* get first status register */
+       lsrs    r7, #(SPIFLASH_WEL+1)   /* if first flash */
+       bcc             error                                   /* not enabled, 
then error */
+       tst             r6, r6                                  /* dual flash? 
*/
+       beq             busy_loop                               /* no, then ok 
*/
+       wait_fifo
+       ldrb    r7, [r4, #0]                    /* get second status register */
+       lsrs    r7, #(SPIFLASH_WEL+1)   /* if second flash */
+       bcc             error                                   /* not enabled, 
then error */
+
+busy_loop:
+       wait_busy
+       cmp             r0, r1                                  /* if count > 
page size */
+       bhi             page_write                              /* then write 
one page only */
+       str             r0, [r3, #QSPI_DLR]             /* remaining count in 
DLR register */
+       b               start_write
+
+page_write:
+       str             r1, [r3, #QSPI_DLR]             /* page size in DLR 
register */
+
+start_write:
+       ldr             r7, ccr_page_write              /* CCR for page write */
+       str             r7, [r3, #QSPI_CCR]             /* initiate transfer */
+       str             r2, [r3, #QSPI_AR]              /* store SPI start 
address */
+
+write_loop:
+       wait_fifo
+       ldrb    r7,[r5, #0]                             /* load and */
+       strb    r7,[r4, #0]                             /* store next byte */
+       adds    r5, #1                                  /* increment buffer ptr 
*/
+       adds    r2, #1                                  /* increment address */
+       subs    r0, #1                                  /* decrement count */
+       bmi             page_end                                /* stop if no 
data left */
+       tst             r2, r1                                  /* page end ? */
+       bne             write_loop                              /* if not, then 
next byte */
+
+page_end:
+       ldr             r7, [r3, #QSPI_SR]              /* load status */
+       lsrs    r7, r7, #(QSPI_TCF+1)   /* shift TCF into C */
+       bcc             page_end                                /* wait till 
TCF set */
+       movs    r0, r0                                  /* test residual count 
*/
+       bpl             wip_loop                                /* if 
non-negative, then next page */
+       adds    r0, #1                                  /* correct count */
+       b               exit
+
+error:
+       movs    r0, #0
+       subs    r0, #1                                  /* return 0xFFFFFFFF 
for error */
+
+exit:
+       .align  2                                               /* align to 
word, bkpt is 4 words */
+       bkpt    #0                                              /* before code 
end for exit_point */
+       .align  2                                               /* align to 
word */
+
+ccr_read_status:
+       .space  4                                               /* QSPI_CCR 
value for READ_STATUS command */
+
+ccr_write_enable:
+       .space  4                                               /* QSPI_CCR 
value for WRITE_ENABLE command */
+
+ccr_page_write:
+       .space  4                                               /* QSPI_CCR 
value for PAGE_WRITE command */
+
+buffer:                                                                /* 
buffer follows right away */
diff --git a/doc/openocd.texi b/doc/openocd.texi
index 5c4c981..9c53cdb 100644
--- a/doc/openocd.texi
+++ b/doc/openocd.texi
@@ -4908,6 +4908,41 @@ flash bank $_FLASHNAME stmsmi 0xf8000000 0 0 0 
$_TARGETNAME
 
 @end deffn
 
+@deffn {Flash Driver} stmqspi
+@cindex STMicroelectronics QuadSPI Interface
+@cindex QuadSPI
+@cindex stmqspi
+Some devices form STMicroelectronics (e.g. STM32F7 and STM32L4 MCU family)
+include a proprietary ``QuadSPI Interface'' controller able to drive one
+or even two (dual mode) external SPI flash devices.
+
+QuadSPI makes the flash content directly accessible in the CPU address
+space; in case of dual mode both devices must be of the same type and are
+mapped in the same memory bank (even and odd adresses interleaved).
+CPU can directly read data, execute code (but not boot) from QuadSPI bank.
+
+The setup command only requires the @var{base} parameter in order
+to identify the memory bank. All other parameters are ignored.
+
+The QuadSPI controller must be initialized after each reset, the setup is
+quite board specific (that's why booting from this memory is not possible) and
+the flash driver infers all parameters from current controller register values.
+
+Normal OpenOCD commands like @command{mdw} can be used to display
+the flash content, but only after proper controller initialzation.
+
+@example
+flash bank $_FLASHNAME stmqspi 0x90000000 0 0 0 $_TARGETNAME
+@end example
+
+There is one specific command
+@deffn Command {stmqspi mass_erase} num
+Clears the block protect bits and performs a mass erase. Works only if
+there is no chip specific write protection engaged.
+@end deffn
+
+@end deffn
+
 @deffn {Flash Driver} mrvlqspi
 This driver supports QSPI flash controller of Marvell's Wireless
 Microcontroller platform.
diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am
index 834e4d4..e78c17a 100644
--- a/src/flash/nor/Makefile.am
+++ b/src/flash/nor/Makefile.am
@@ -32,6 +32,7 @@ NOR_DRIVERS = \
        pic32mx.c \
        spi.c \
        stmsmi.c \
+       stmqspi.c \
        stellaris.c \
        stm32f1x.c \
        stm32f2x.c \
diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c
index 48d584e..80f7fa8 100644
--- a/src/flash/nor/drivers.c
+++ b/src/flash/nor/drivers.c
@@ -50,6 +50,7 @@ extern struct flash_driver avr_flash;
 extern struct flash_driver faux_flash;
 extern struct flash_driver virtual_flash;
 extern struct flash_driver stmsmi_flash;
+extern struct flash_driver stmqspi_flash;
 extern struct flash_driver em357_flash;
 extern struct flash_driver dsp5680xx_flash;
 extern struct flash_driver fm3_flash;
@@ -97,6 +98,7 @@ static struct flash_driver *flash_drivers[] = {
        &avr_flash,
        &faux_flash,
        &virtual_flash,
+       &stmqspi_flash,
        &stmsmi_flash,
        &em357_flash,
        &fm3_flash,
diff --git a/src/flash/nor/spi.h b/src/flash/nor/spi.h
index 6e939b7..8849944 100644
--- a/src/flash/nor/spi.h
+++ b/src/flash/nor/spi.h
@@ -52,7 +52,9 @@ extern const struct flash_device flash_devices[];
 /* SPI Flash Commands */
 #define SPIFLASH_READ_ID               0x9F /* Read Flash Identification */
 #define SPIFLASH_READ_STATUS   0x05 /* Read Status Register */
+#define SPIFLASH_WRITE_STATUS  0x01 /* Write Status Register */
 #define SPIFLASH_WRITE_ENABLE  0x06 /* Write Enable */
 #define SPIFLASH_PAGE_PROGRAM  0x02 /* Page Program */
 #define SPIFLASH_FAST_READ             0x0B /* Fast Read */
 #define SPIFLASH_READ                  0x03 /* Normal Read */
+#define SPIFLASH_MASS_ERASE            0xC7 /* Bulk / Mass Erase */
diff --git a/src/flash/nor/stmqspi.c b/src/flash/nor/stmqspi.c
new file mode 100644
index 0000000..9988c5b
--- /dev/null
+++ b/src/flash/nor/stmqspi.c
@@ -0,0 +1,994 @@
+/***************************************************************************
+ *   Copyright (C) 2010 by Antonio Borneo <[email protected]>,     *
+ *        2015 by Andreas Bolsch <[email protected]                    
           *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or    *
+ *   (at your option) any later version.                                       
                           *
+ *                                                                             
                                                                   *
+ *   This program is distributed in the hope that it will be useful,      *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of            
   *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             
   *
+ *   GNU General Public License for more details.                              
                   *
+ ***************************************************************************/
+
+/* STM QuadSPI (QSPI) controller is a SPI bus controller
+ * specifically designed for SPI memories.
+ * Two working modes are available:
+ * - indirect mode: the SPI is controlled by SW. Any custom commands can be 
sent
+ *   on the bus.
+ * - memory mapped mode: the SPI is under QSPI control. Memory content is 
directly
+ *   accessible in CPU memory space. CPU can read and execute from memory
+ *   (but not write to) */
+
+/* ATTENTION:
+ * To have flash mapped in CPU memory space, the QSPI controller
+ * has to be in "memory mapped mode". This requires following constraints:
+ * 1) The command "reset init" has to initialize QSPI controller and put
+ *     it in memory mapped mode;
+ * 2) every command in this file has to return to prompt in memory mapped 
mode. */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "imp.h"
+#include "spi.h"
+#include <helper/time_support.h>
+#include <target/algorithm.h>
+#include <target/armv7m.h>
+#include "stmqspi.h"
+
+#define QSPI_BANK_SIZE  0x01000000
+#define QSPI_SEL_BANK0  0x00000000 /* Select Bank0 */
+
+#define QSPI_READ_REGB(a) (_QSPI_READ_REGB(a))
+#define _QSPI_READ_REGB(a)                     \
+{                                                                      \
+       int __a;                                                \
+       uint8_t __v;                                    \
+                                                                       \
+       __a = target_read_u8(target, io_base + (a), &__v); \
+       if (__a != ERROR_OK)                    \
+               return __a;                                     \
+       __v;                                                    \
+}
+
+#define QSPI_READ_REG(a) (_QSPI_READ_REG(a))
+#define _QSPI_READ_REG(a)                      \
+{                                                                      \
+       int __a;                                                \
+       uint32_t __v;                                   \
+                                                                       \
+       __a = target_read_u32(target, io_base + (a), &__v); \
+       if (__a != ERROR_OK)                    \
+               return __a;                                     \
+       __v;                                                    \
+}
+
+#define QSPI_WRITE_REG(a, v)           \
+{                                                                      \
+       int __r;                                                \
+                                                                       \
+       __r = target_write_u32(target, io_base + (a), (v)); \
+       if (__r != ERROR_OK)                    \
+               return __r;                                     \
+}
+
+#define QSPI_POLL_BUSY(timeout)                \
+{                                                                      \
+       int __r;                                                \
+                                                                       \
+       __r = poll_busy(target, io_base, timeout); \
+       if (__r != ERROR_OK)                    \
+               return __r;                                     \
+}
+
+#define QSPI_POLL_TCF(timeout)         \
+{                                                                      \
+       int __r;                                                \
+                                                                       \
+       __r = poll_tcf(target, io_base, timeout); \
+       if (__r != ERROR_OK)                    \
+               return __r;                                     \
+}
+
+#define QSPI_SET_MM_MODE() QSPI_WRITE_REG(QSPI_CCR, \
+       stmqspi_info->saved_ccr)
+
+#define QSPI_CLEAR_TCF() QSPI_WRITE_REG(QSPI_FCR, (1UL<<QSPI_TCF))
+
+/* QSPI_CCR values for various commands */
+#define        QSPI_CCR_READ_STATUS ((QSPI_READ_REG(QSPI_CCR) & 0xF0000000) | \
+       ((QSPI_READ_MODE | QSPI_1LINE_MODE | QSPI_ADDR3 | SPIFLASH_READ_STATUS) 
& QSPI_NO_ADDR))
+
+#define        QSPI_CCR_WRITE_STATUS ((QSPI_READ_REG(QSPI_CCR) & 0xF0000000) | 
\
+       ((QSPI_WRITE_MODE | QSPI_1LINE_MODE | QSPI_ADDR3 | 
SPIFLASH_WRITE_STATUS) & QSPI_NO_ADDR))
+
+#define QSPI_CCR_WRITE_ENABLE ((QSPI_READ_REG(QSPI_CCR) & 0xF0000000) | \
+       ((QSPI_WRITE_MODE | QSPI_1LINE_MODE | QSPI_ADDR3 | 
SPIFLASH_WRITE_ENABLE) & \
+       QSPI_NO_ADDR & QSPI_NO_DATA))
+
+#define QSPI_CCR_SECTOR_ERASE ((QSPI_READ_REG(QSPI_CCR) & 0xF0000000) | \
+       ((QSPI_WRITE_MODE | QSPI_1LINE_MODE | QSPI_ADDR3 | \
+       stmqspi_info->dev->erase_cmd) & QSPI_NO_DATA))
+
+#define QSPI_CCR_MASS_ERASE ((QSPI_READ_REG(QSPI_CCR) & 0xF0000000) | \
+       ((QSPI_WRITE_MODE | QSPI_1LINE_MODE | QSPI_ADDR3 | \
+       stmqspi_info->dev->chip_erase_cmd) & QSPI_NO_ADDR & QSPI_NO_DATA))
+
+#define QSPI_CCR_PAGE_WRITE ((QSPI_READ_REG(QSPI_CCR) & 0xF0000000) | \
+       ((QSPI_WRITE_MODE | QSPI_1LINE_MODE | QSPI_ADDR3 | 
SPIFLASH_PAGE_PROGRAM)))
+
+/* Timeout in ms */
+#define QSPI_CMD_TIMEOUT               (100)
+#define QSPI_PROBE_TIMEOUT             (100)
+#define QSPI_MAX_TIMEOUT               (2000)
+#define QSPI_MASS_ERASE_TIMEOUT        (400000)
+
+struct stmqspi_flash_bank {
+       int probed;
+       uint32_t io_base;
+       uint32_t bank_num;
+       uint32_t saved_ccr;
+       uint32_t dual_mask; /* FSEL and DFM bit mask in QUADSPI_CR */
+       const struct flash_device *dev;
+};
+
+struct stmqspi_target {
+       char *name;
+       uint32_t device_id;
+       uint32_t qspi_base;
+       uint32_t io_base;
+};
+
+static const struct stmqspi_target target_devices[] = {
+       /* name,                        device_id, qspi_base,   io_base */
+       { "stm32f74x_75x",      0x449,  0x90000000,     0xA0001000 },
+       { "stm32l4x6",          0x415,  0x90000000,     0xA0001000 },
+       { NULL,                         0x000,  0x00000000,     0x00000000 },
+};
+
+FLASH_BANK_COMMAND_HANDLER(stmqspi_flash_bank_command)
+{
+       struct stmqspi_flash_bank *stmqspi_info;
+
+       LOG_DEBUG("%s", __func__);
+
+       if (CMD_ARGC < 6)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       stmqspi_info = malloc(sizeof(struct stmqspi_flash_bank));
+       if (stmqspi_info == NULL) {
+               LOG_ERROR("not enough memory");
+               return ERROR_FAIL;
+       }
+
+       bank->driver_priv = stmqspi_info;
+       stmqspi_info->probed = 0;
+
+       return ERROR_OK;
+}
+
+/* Poll busy flag */
+/* timeout in ms */
+static int poll_busy(struct target *target, uint32_t io_base, int timeout)
+{
+       long long endtime;
+
+       if ((QSPI_READ_REG(QSPI_SR) & (1UL<<QSPI_BUSY)) == 0)
+               return ERROR_OK;
+
+       endtime = timeval_ms() + timeout;
+       do {
+               alive_sleep(1);
+               if ((QSPI_READ_REG(QSPI_SR) & (1UL<<QSPI_BUSY)) == 0)
+                       return ERROR_OK;
+       } while (timeval_ms() < endtime);
+
+       LOG_ERROR("Timeout while polling BUSY");
+       return ERROR_FLASH_OPERATION_FAILED;
+}
+
+/* Poll transmit finished flag */
+/* timeout in ms */
+static int poll_tcf(struct target *target, uint32_t io_base, int timeout)
+{
+       long long endtime;
+
+       if (QSPI_READ_REG(QSPI_SR) & (1UL<<QSPI_TCF))
+               return ERROR_OK;
+
+       endtime = timeval_ms() + timeout;
+       do {
+               alive_sleep(1);
+               if (QSPI_READ_REG(QSPI_SR) & (1UL<<QSPI_TCF))
+                       return ERROR_OK;
+       } while (timeval_ms() < endtime);
+
+       LOG_ERROR("Timeout while polling TCF");
+       return ERROR_FLASH_OPERATION_FAILED;
+}
+
+/* Read the status register of the external SPI flash chip(s). */
+static int read_status_reg(struct flash_bank *bank, uint32_t *status)
+{
+       struct target *target = bank->target;
+       struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv;
+       uint32_t io_base = stmqspi_info->io_base;
+
+       /* Wait for busy to be cleared */
+       QSPI_POLL_BUSY(QSPI_PROBE_TIMEOUT);
+
+       /* Clear transmit finished flag */
+       QSPI_CLEAR_TCF();
+
+       /* Read one byte per chip */
+       QSPI_WRITE_REG(QSPI_DLR, stmqspi_info->dual_mask & QSPI_DUAL_FLASH ? 1 
: 0);
+
+       /* Read status */
+       QSPI_WRITE_REG(QSPI_CCR, QSPI_CCR_READ_STATUS);
+
+       /* Poll transmit finished flag */
+       QSPI_POLL_TCF(QSPI_CMD_TIMEOUT);
+
+       /* clear transmit finished flag */
+       QSPI_CLEAR_TCF();
+
+       *status = QSPI_READ_REG(QSPI_DR) & 0x0000FFFF;
+       if ((stmqspi_info->dual_mask & QSPI_DUAL_FLASH) == 0) {
+               if (stmqspi_info->dual_mask & (1<<QSPI_FSEL_FLASH)) {
+                       /* Mask out flash 1 */
+                       *status &= 0x0000FF00;
+               } else {
+                       /* Mask out flash 0 */
+                       *status &= 0x000000FF;
+               }
+       }
+
+       return ERROR_OK;
+}
+
+/* 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)
+{
+       uint32_t status;
+       int retval;
+       long long endtime;
+
+       endtime = timeval_ms() + timeout;
+       do {
+               /* Read flash status register(s) */
+               retval = read_status_reg(bank, &status);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               if ((status & (((1<<SPIFLASH_WIP)<<8) || (1<<SPIFLASH_WIP))) == 
0)
+                       return ERROR_OK;
+               alive_sleep(1);
+       } while (timeval_ms() < endtime);
+
+       LOG_ERROR("timeout");
+       return ERROR_FAIL;
+}
+
+/* Send "write enable" command to SPI flash chip(s). */
+static int qspi_write_enable(struct flash_bank *bank)
+{
+       struct target *target = bank->target;
+       struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv;
+       uint32_t io_base = stmqspi_info->io_base;
+       uint32_t status;
+       int retval;
+
+       /* Wait for busy to be cleared */
+       QSPI_POLL_BUSY(QSPI_PROBE_TIMEOUT);
+
+       /* clear transmit finished flag */
+       QSPI_CLEAR_TCF();
+
+       /* Send write enable command */
+       QSPI_WRITE_REG(QSPI_CCR, QSPI_CCR_WRITE_ENABLE);
+
+       /* Read flash status register */
+       retval = read_status_reg(bank, &status);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* Check write enabled for flash 1 */
+       if (((stmqspi_info->dual_mask & ((1UL<<QSPI_DUAL_FLASH) | 
(1UL<<QSPI_FSEL_FLASH))) != (1UL<<QSPI_FSEL_FLASH))
+               & ((status & (1<<SPIFLASH_WEL)) == 0)) {
+               LOG_ERROR("Cannot enable write to flash1. Status=0x%08" PRIx32, 
status);
+               return ERROR_FAIL;
+       }
+
+       /* Check write enabled for flash 2 */
+       if (((stmqspi_info->dual_mask & ((1UL<<QSPI_DUAL_FLASH) | 
(1UL<<QSPI_FSEL_FLASH))) != 0)
+               & (((status >> 8) & (1<<SPIFLASH_WEL)) == 0)) {
+               LOG_ERROR("Cannot enable write to flash2. Status=0x%08" PRIx32, 
status);
+               return ERROR_FAIL;
+       }
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(stmqspi_handle_mass_erase_command)
+{
+       struct target *target = NULL;
+       struct flash_bank *bank;
+       struct stmqspi_flash_bank *stmqspi_info;
+       struct duration bench;
+       uint32_t io_base, status;
+       int retval, sector, dual;
+
+       LOG_DEBUG("%s", __func__);
+
+       if (CMD_ARGC != 1)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
+       if (ERROR_OK != retval)
+               return retval;
+
+       stmqspi_info = bank->driver_priv;
+       target = bank->target;
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       if (!(stmqspi_info->probed)) {
+               LOG_ERROR("Flash bank not probed");
+               return ERROR_FLASH_BANK_NOT_PROBED;
+       }
+
+       for (sector = 0; sector <= bank->num_sectors; sector++) {
+               if (bank->sectors[sector].is_protected) {
+                       LOG_ERROR("Flash sector %d protected", sector);
+                       return ERROR_FAIL;
+               }
+       }
+
+       io_base = stmqspi_info->io_base;
+       duration_start(&bench);
+
+       /* Abort any previous operation */
+       QSPI_WRITE_REG(QSPI_CR, QSPI_READ_REG(QSPI_CR) | (1<<QSPI_ABORT));
+
+       /* Wait for busy to be cleared */
+       QSPI_POLL_BUSY(QSPI_PROBE_TIMEOUT);
+
+       retval = qspi_write_enable(bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       dual = (stmqspi_info->dual_mask & (1UL<<QSPI_DUAL_FLASH)) ? 1 : 0;
+
+       /* Clear block protect bits */
+       QSPI_WRITE_REG(QSPI_CCR, QSPI_CCR_WRITE_STATUS);
+       QSPI_WRITE_REG(QSPI_DLR, dual);
+       QSPI_WRITE_REG(QSPI_DR, (1<<SPIFLASH_WEL));
+       if (dual)
+               QSPI_WRITE_REG(QSPI_DR, (1<<SPIFLASH_WEL));
+
+       /* Wait for busy to be cleared */
+       QSPI_POLL_BUSY(QSPI_PROBE_TIMEOUT);
+
+       /* clear transmit finished flag */
+       QSPI_CLEAR_TCF();
+
+       retval = qspi_write_enable(bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* Send Mass Erase command */
+       QSPI_WRITE_REG(QSPI_CCR, QSPI_CCR_MASS_ERASE);
+
+       /* Read flash status register(s) */
+       retval = read_status_reg(bank, &status);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* Check for command in progress for flash 1 */
+       if (((stmqspi_info->dual_mask & ((1<<QSPI_DUAL_FLASH) | 
(1<<QSPI_FSEL_FLASH))) != (1<<QSPI_FSEL_FLASH))
+               & ((status & (1<<SPIFLASH_WEL)) == 0)) {
+               LOG_ERROR("Mass erase command not accepted by flash1. 
Status=0x%08" PRIx32, status);
+               return ERROR_FAIL;
+       }
+
+       /* Check for command in progress for flash 2 */
+       if (((stmqspi_info->dual_mask & ((1<<QSPI_DUAL_FLASH) | 
(1<<QSPI_FSEL_FLASH))) != 0)
+               & (((status >> 8) & (1<<SPIFLASH_WEL)) == 0)) {
+               LOG_ERROR("Mass erase command not accepted by flash2. 
Status=0x%08" PRIx32, status);
+               return ERROR_FAIL;
+       }
+
+       /* poll WIP for end of self timed Sector Erase cycle */
+       retval = wait_till_ready(bank, QSPI_MASS_ERASE_TIMEOUT);
+
+       /* Switch to memory mapped mode before return to prompt */
+       QSPI_SET_MM_MODE();
+
+       duration_measure(&bench);
+       if (retval == ERROR_OK) {
+               /* set all sectors as erased */
+               for (sector = 0; sector < bank->num_sectors; sector++)
+                       bank->sectors[sector].is_erased = 1;
+
+               command_print(CMD_CTX, "stmqspi mass erase completed in"
+                       " %fs (%0.3f KiB/s)", duration_elapsed(&bench),
+                       duration_kbps(&bench, bank->size));
+       } else {
+               command_print(CMD_CTX, "stmqspi mass erase failed after %fs",
+                       duration_elapsed(&bench));
+       }
+
+       return retval;
+}
+
+static int qspi_erase_sector(struct flash_bank *bank, int sector)
+{
+       struct target *target = bank->target;
+       struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv;
+       uint32_t io_base = stmqspi_info->io_base;
+       int dual;
+       int retval;
+
+       retval = qspi_write_enable(bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       dual = (stmqspi_info->dual_mask & (1UL<<QSPI_DUAL_FLASH)) ? 1 : 0;
+
+       /* Wait for busy to be cleared */
+       QSPI_POLL_BUSY(QSPI_PROBE_TIMEOUT);
+
+       /* clear transmit finished flag */
+       QSPI_CLEAR_TCF();
+
+       /* Send Sector Erase command */
+       QSPI_WRITE_REG(QSPI_CCR, QSPI_CCR_SECTOR_ERASE);
+
+       /* Address is sector offset, this write initiates command transmission 
*/
+       QSPI_WRITE_REG(QSPI_AR, bank->sectors[sector].offset >> dual);
+
+       /* poll WIP for end of self timed Sector Erase cycle */
+       retval = wait_till_ready(bank, QSPI_MAX_TIMEOUT);
+
+       /* erasure takes a long time, so some sort of progress message is a 
good idea */
+       LOG_USER("sector %4d erased", sector);
+
+       /* Switch to memory mapped mode before return to prompt */
+       QSPI_SET_MM_MODE();
+
+       return retval;
+}
+
+static int stmqspi_erase(struct flash_bank *bank, int first, int last)
+{
+       struct target *target = bank->target;
+       struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv;
+       uint32_t io_base = stmqspi_info->io_base;
+       int retval = ERROR_OK;
+       int sector;
+
+       LOG_DEBUG("%s: from sector %d to sector %d", __func__, first, last);
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       if (!(stmqspi_info->probed)) {
+               LOG_ERROR("Flash bank not probed");
+               return ERROR_FLASH_BANK_NOT_PROBED;
+       }
+
+       if ((first < 0) || (last < first) || (last >= bank->num_sectors)) {
+               LOG_ERROR("Flash sector invalid");
+               return ERROR_FLASH_SECTOR_INVALID;
+       }
+
+       /* Abort any previous operation */
+       QSPI_WRITE_REG(QSPI_CR, QSPI_READ_REG(QSPI_CR) | (1<<QSPI_ABORT));
+
+       for (sector = first; sector <= last; sector++) {
+               if (bank->sectors[sector].is_protected) {
+                       LOG_ERROR("Flash sector %d protected", sector);
+                       return ERROR_FAIL;
+               }
+       }
+
+       for (sector = first; sector <= last; sector++) {
+               retval = qspi_erase_sector(bank, sector);
+               if (retval != ERROR_OK)
+                       break;
+               keep_alive();
+       }
+
+       return retval;
+}
+
+static int stmqspi_protect(struct flash_bank *bank, int set,
+       int first, int last)
+{
+       int sector;
+
+       for (sector = first; sector <= last; sector++)
+               bank->sectors[sector].is_protected = set;
+       return ERROR_OK;
+}
+
+static int qspi_write_buffer(struct flash_bank *bank, struct working_area 
*write_algorithm,
+       const uint32_t code_len, const uint32_t flash_offset, const uint32_t 
page_size,
+       uint32_t count, const uint8_t *buffer)
+{
+       struct target *target = bank->target;
+       struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv;
+       uint32_t io_base = stmqspi_info->io_base;
+       struct reg_param reg_params[4];
+       struct armv7m_algorithm armv7m_info;
+       uint32_t exit_point, remaining;
+       int retval = ERROR_OK;
+
+       LOG_DEBUG("%s: offset=0x%08" PRIx32 " len=0x%08" PRIx32,
+               __func__, flash_offset, count);
+
+       /* after breakpoint instruction (halfword) one nop (halfword) and
+        * 3 words follow till end of code, that makes exaclty 4 words */
+       exit_point = write_algorithm->address + code_len - 4 * sizeof(uint32_t);
+       target_write_buffer(target, write_algorithm->address + code_len,
+               count, buffer);
+
+       init_reg_param(&reg_params[0], "r0", 32, PARAM_IN_OUT); /* count (in), 
count (out) */
+       init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);    /* page_size */
+       init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT);    /* offset into 
flash address */
+       init_reg_param(&reg_params[3], "r3", 32, PARAM_OUT);    /* QSPI io_base 
*/
+
+       buf_set_u32(reg_params[0].value, 0, 32, count);
+       buf_set_u32(reg_params[1].value, 0, 32, page_size);
+       buf_set_u32(reg_params[2].value, 0, 32, flash_offset);
+       buf_set_u32(reg_params[3].value, 0, 32, io_base);
+
+       armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
+       armv7m_info.core_mode = ARM_MODE_THREAD;
+
+       retval = target_start_algorithm(target, 0, NULL,
+               sizeof(reg_params) / sizeof(struct reg_param), reg_params,
+               write_algorithm->address, exit_point, &armv7m_info);
+
+       retval = target_wait_algorithm(target, 0, NULL,
+               sizeof(reg_params) / sizeof(struct reg_param), reg_params,
+               exit_point, QSPI_MAX_TIMEOUT, &armv7m_info);
+
+       remaining = buf_get_u32(reg_params[0].value, 0, 32);
+       if ((retval == ERROR_OK) && remaining)
+               retval = ERROR_FLASH_OPERATION_FAILED;
+       if (retval != ERROR_OK) {
+               LOG_ERROR("flash write failed at address 0x%" PRIx32 ", 
remaining 0x%" PRIx32,
+                       flash_offset, remaining);
+       } else {
+               /* programming takes a long time, so some sort of progress 
message is a good idea */
+               LOG_USER("pages %6d to %6d programmed", flash_offset / 
page_size,
+                       (flash_offset + count - 1) / page_size);
+       }
+
+       destroy_reg_param(&reg_params[0]);
+       destroy_reg_param(&reg_params[1]);
+       destroy_reg_param(&reg_params[2]);
+       destroy_reg_param(&reg_params[3]);
+
+       /* Switch to memory mapped mode before return to prompt */
+       QSPI_SET_MM_MODE();
+
+       return retval;
+}
+
+static int stmqspi_write(struct flash_bank *bank, const uint8_t *buffer,
+       uint32_t flash_offset, uint32_t count)
+{
+       struct target *target = bank->target;
+       struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv;
+       struct working_area *write_algorithm;
+       uint32_t io_base = stmqspi_info->io_base;
+       uint32_t cur_count, page_size, buffer_size;
+       int sector;
+       int dual;
+       int retval = ERROR_OK;
+
+       /* see contrib/loaders/flash/stmqspi.S for src */
+       static const uint8_t stmqspi_flash_write_code[] = {
+               0x01, 0x38, 0x01, 0x39, 0x1c, 0x00, 0x20, 0x34, 0x30, 0xa5, 
0x00, 0x26,
+               0x1f, 0x68, 0xff, 0x09, 0x00, 0xd3, 0x01, 0x36, 0x9f, 0x68, 
0xbf, 0x09,
+               0xfc, 0xd2, 0x02, 0x27, 0xdf, 0x60, 0x1e, 0x61, 0x27, 0x4f, 
0x5f, 0x61,
+               0x9f, 0x68, 0xff, 0x08, 0xfc, 0xd3, 0x27, 0x78, 0x7f, 0x08, 
0xf1, 0xd2,
+               0x36, 0x42, 0x05, 0xd0, 0x9f, 0x68, 0xff, 0x08, 0xfc, 0xd3, 
0x27, 0x78,
+               0x7f, 0x08, 0xe9, 0xd2, 0x9f, 0x68, 0xbf, 0x09, 0xfc, 0xd2, 
0x02, 0x27,
+               0xdf, 0x60, 0x1e, 0x4f, 0x5f, 0x61, 0x9f, 0x68, 0xbf, 0x09, 
0xfc, 0xd2,
+               0x02, 0x27, 0xdf, 0x60, 0x1e, 0x61, 0x19, 0x4f, 0x5f, 0x61, 
0x9f, 0x68,
+               0xff, 0x08, 0xfc, 0xd3, 0x27, 0x78, 0xbf, 0x08, 0x26, 0xd3, 
0x36, 0x42,
+               0x05, 0xd0, 0x9f, 0x68, 0xff, 0x08, 0xfc, 0xd3, 0x27, 0x78, 
0xbf, 0x08,
+               0x1e, 0xd3, 0x9f, 0x68, 0xbf, 0x09, 0xfc, 0xd2, 0x02, 0x27, 
0xdf, 0x60,
+               0x88, 0x42, 0x01, 0xd8, 0x18, 0x61, 0x00, 0xe0, 0x19, 0x61, 
0x0e, 0x4f,
+               0x5f, 0x61, 0x9a, 0x61, 0x9f, 0x68, 0xff, 0x08, 0xfc, 0xd3, 
0x2f, 0x78,
+               0x27, 0x70, 0x01, 0x35, 0x01, 0x32, 0x01, 0x38, 0x01, 0xd4, 
0x0a, 0x42,
+               0xf4, 0xd1, 0x9f, 0x68, 0xbf, 0x08, 0xfc, 0xd3, 0x00, 0x00, 
0xaf, 0xd5,
+               0x01, 0x30, 0x01, 0xe0, 0x00, 0x20, 0x01, 0x38, 0x00, 0xbe, 
0xc0, 0x46,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00
+       };
+
+       /* This will overlay the last 3 words of stmqspi_flash_write_code in 
target */
+       uint32_t ccr_buffer[] = {
+               htole32(QSPI_CCR_READ_STATUS),
+               htole32(QSPI_CCR_WRITE_ENABLE),
+               htole32(QSPI_CCR_PAGE_WRITE),
+       };
+
+       LOG_DEBUG("%s: offset=0x%08" PRIx32 " count=0x%08" PRIx32,
+               __func__, flash_offset, count);
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       if (flash_offset + count > stmqspi_info->dev->size_in_bytes) {
+               LOG_WARNING("Write past end of flash. Extra data discarded.");
+               count = stmqspi_info->dev->size_in_bytes - flash_offset;
+       }
+
+       /* Check sector protection */
+       for (sector = 0; sector < bank->num_sectors; sector++) {
+               /* Start offset in or before this sector? */
+               /* End offset in or behind this sector? */
+               if ((flash_offset < (bank->sectors[sector].offset + 
bank->sectors[sector].size))
+                       && ((flash_offset + count - 1) >= 
bank->sectors[sector].offset)
+                       && bank->sectors[sector].is_protected) {
+                       LOG_ERROR("Flash sector %d protected", sector);
+                       return ERROR_FAIL;
+               }
+       }
+
+       dual = (stmqspi_info->dual_mask & (1UL<<QSPI_DUAL_FLASH)) ? 1 : 0;
+       if (dual & ((flash_offset & 1) != 0 || (count & 1) != 0)) {
+               LOG_ERROR("For dual-QSPI writes must be two byte aligned: "
+                       "%s: address=0x%08" PRIx32 " len=0x%08" PRIx32, 
__func__,
+                       flash_offset, count);
+               return ERROR_FAIL;
+       }
+       page_size = stmqspi_info->dev->pagesize << dual;
+
+       /* memory buffer, we assume sectorsize to be a power of 2 times 
page_size */
+       buffer_size = stmqspi_info->dev->sectorsize << dual;
+       while (target_alloc_working_area_try(target,
+                       sizeof(stmqspi_flash_write_code) + buffer_size,
+                       &write_algorithm) != ERROR_OK) {
+               buffer_size /= 2;
+               if (buffer_size < page_size) {
+                       /* we already allocated the writing code, but failed to 
get a
+                        * buffer, free the algorithm */
+                       target_free_working_area(target, write_algorithm);
+
+                       LOG_WARNING("not enough working area, can't do QSPI 
page writes");
+                       return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+               }
+       };
+
+       /* prepare flash write code, excluding ccr_buffer */
+       retval = target_write_buffer(target, write_algorithm->address,
+               sizeof(stmqspi_flash_write_code) - sizeof(ccr_buffer),
+               stmqspi_flash_write_code);
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* prepare QSPI_CCR register values */
+       retval = target_write_buffer(target, write_algorithm->address
+               + sizeof(stmqspi_flash_write_code) - sizeof(ccr_buffer),
+               sizeof(ccr_buffer), (uint8_t *) ccr_buffer);
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* buffer head not aligned to page size */
+       if (count > 0 && (flash_offset & (page_size - 1)) != 0) {
+               cur_count = page_size - (flash_offset & (page_size - 1));
+               if (cur_count > count)
+                       cur_count = count;
+               retval = qspi_write_buffer(bank, write_algorithm,
+                       sizeof(stmqspi_flash_write_code), flash_offset,
+                       page_size, cur_count, buffer);
+               if (retval != ERROR_OK)
+                       goto err;
+               flash_offset += cur_count;
+               buffer += cur_count;
+               count -= cur_count;
+       }
+
+       /* Abort any previous operation */
+       QSPI_WRITE_REG(QSPI_CR, QSPI_READ_REG(QSPI_CR) | (1<<QSPI_ABORT));
+
+       /* central part, aligned to page size */
+       while (count > 0) {
+               /* clip block at buffer_size */
+               if (count > buffer_size)
+                       cur_count = buffer_size;
+               else
+                       cur_count = count;
+
+               retval = qspi_write_buffer(bank, write_algorithm,
+                       sizeof(stmqspi_flash_write_code), flash_offset,
+                       page_size, cur_count, buffer);
+               if (retval != ERROR_OK)
+                       goto err;
+
+               flash_offset += cur_count;
+               buffer += cur_count;
+               count -= cur_count;
+
+               keep_alive();
+       }
+
+err:
+       target_free_working_area(target, write_algorithm);
+
+       return retval;
+}
+
+/* Return ID of flash device(s) */
+/* On exit, indirect mode is kept */
+static int read_flash_id(struct flash_bank *bank, uint32_t *id1, uint32_t *id2)
+{
+       struct target *target = bank->target;
+       struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv;
+       uint32_t io_base = stmqspi_info->io_base;
+       int shift, retval;
+
+       if ((target->state != TARGET_HALTED) && (target->state != 
TARGET_RESET)) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       /* poll WIP */
+       retval = wait_till_ready(bank, QSPI_PROBE_TIMEOUT);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* Clear transmit finished flag */
+       QSPI_CLEAR_TCF();
+
+       /* Read three bytes per chip */
+       QSPI_WRITE_REG(QSPI_DLR, (stmqspi_info->dual_mask & 
(1<<QSPI_DUAL_FLASH) ? 6 : 3) - 1);
+
+       /* Read id */
+       QSPI_WRITE_REG(QSPI_CCR, (QSPI_READ_REG(QSPI_CCR) & 0xF0000000) |
+          ((QSPI_READ_MODE | QSPI_1LINE_MODE | QSPI_ADDR3 | SPIFLASH_READ_ID) 
& QSPI_NO_ADDR));
+
+       /* Poll transmit finished flag */
+       QSPI_POLL_TCF(QSPI_CMD_TIMEOUT);
+
+       /* Clear transmit finished flag */
+       QSPI_CLEAR_TCF();
+
+       /* Read ID from Data Register */
+       for (shift = 0; shift <= 16; shift += 8) {
+               if ((stmqspi_info->dual_mask & ((1<<QSPI_DUAL_FLASH) |
+                       (1<<QSPI_FSEL_FLASH))) != (1<<QSPI_FSEL_FLASH)) {
+                       *id1 |= (QSPI_READ_REGB(QSPI_DR) << shift);
+                       if (!*id1) {
+                               LOG_ERROR("No response from QSPI flash1");
+                               return ERROR_FAIL;
+                       }
+
+               }
+               if ((stmqspi_info->dual_mask & ((1<<QSPI_DUAL_FLASH) |
+                       (1<<QSPI_FSEL_FLASH))) != 0) {
+                       *id2 |= (QSPI_READ_REGB(QSPI_DR) << shift);
+                       if (!*id2) {
+                               LOG_ERROR("No response from QSPI flash2");
+                               return ERROR_FAIL;
+                       }
+               }
+       }
+
+       /* Switch to memory mapped mode before return to prompt */
+       QSPI_SET_MM_MODE();
+
+       return ERROR_OK;
+}
+
+static int stmqspi_probe(struct flash_bank *bank)
+{
+       struct target *target = bank->target;
+       struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv;
+       uint32_t device_id, io_base;
+       struct flash_sector *sectors;
+       uint32_t id1 = 0, id2 = 0; /* silence uninitialized warning */
+       const struct stmqspi_target *target_device;
+       const struct flash_device *p;
+       int dual;
+       int retval;
+
+       if (stmqspi_info->probed)
+               free(bank->sectors);
+       stmqspi_info->probed = 0;
+
+       /* read stm32 device id register, but use only bits 11 down to 0 */
+       retval = target_read_u32(target, 0xE0042000, &device_id);
+       if (retval != ERROR_OK)
+               return retval;
+       device_id &= 0xFFF;
+
+       for (target_device = target_devices ; target_device->name ; 
++target_device)
+               if (target_device->device_id == device_id)
+                       break;
+
+       if (!target_device->name) {
+               LOG_ERROR("Device ID 0x%" PRIx32 " is not known as STM QSPI 
capable",
+                               target->tap->idcode);
+               return ERROR_FAIL;
+       }
+
+       switch (bank->base - target_device->qspi_base) {
+               case 0*QSPI_BANK_SIZE:
+                       stmqspi_info->bank_num = QSPI_SEL_BANK0;
+                       break;
+               default:
+                       LOG_ERROR("Invalid QSPI base address 0x%" PRIx32, 
bank->base);
+                       return ERROR_FAIL;
+       }
+       io_base = target_device->io_base;
+       stmqspi_info->io_base = io_base;
+
+       /* save current FSEL and DFM bits in QSPI_CR */
+       stmqspi_info->dual_mask = QSPI_READ_REG(QSPI_CR);
+       /* save current QSPI_CCR value */;
+       stmqspi_info->saved_ccr = QSPI_READ_REG(QSPI_CCR);
+       LOG_DEBUG("Valid QSPI on device %s at 0x%" PRIx32 ", QSPI_CR 0x%"
+               PRIx32 ", QSPI_CCR 0x%" PRIx32, target_device->name, bank->base,
+               stmqspi_info->dual_mask, stmqspi_info->saved_ccr);
+
+       /* read and decode flash ID; returns in memory mapped mode */
+       retval = read_flash_id(bank, &id1, &id2);
+       QSPI_SET_MM_MODE();
+       LOG_DEBUG("id1 0x%06X, id2 0x%06" PRIx32, id1, id2);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* identify flash1 */
+       stmqspi_info->dev = NULL;
+       for (p = flash_devices; id1 && p->name ; p++) {
+               if (p->device_id == id1) {
+                       stmqspi_info->dev = p;
+                       LOG_INFO("Found flash1 device \'%s\' (ID 0x%06" PRIx32 
")",
+                               p->name, id1);
+                       break;
+               }
+       }
+
+       if (id1 && !p->name) {
+               LOG_ERROR("Unknown flash1 device (ID 0x%06" PRIx32 ")", id1);
+               return ERROR_FAIL;
+       }
+
+       /* identify flash2 */
+       for (p = flash_devices; id2 && p->name ; p++) {
+               if (p->device_id == id2) {
+                       LOG_INFO("Found flash2 device \'%s\' (ID 0x%06" PRIx32 
")",
+                               p->name, id2);
+
+                       if (!stmqspi_info->dev)
+                               stmqspi_info->dev = p;
+                       else {
+                               if ((stmqspi_info->dev->erase_cmd != 
p->erase_cmd) ||
+                                       (stmqspi_info->dev->chip_erase_cmd != 
p->chip_erase_cmd) ||
+                                       (stmqspi_info->dev->pagesize != 
p->pagesize) ||
+                                       (stmqspi_info->dev->sectorsize != 
p->sectorsize) ||
+                                       (stmqspi_info->dev->size_in_bytes != 
p->size_in_bytes)) {
+                                       LOG_ERROR("Incompatible flash1/flash2 
devices");
+                                       return ERROR_FAIL;
+                               }
+                       }
+                       break;
+               }
+       }
+
+       if (id2 && !p->name) {
+               LOG_ERROR("Unknown flash2 device (ID 0x%06" PRIx32 ")", id2);
+               return ERROR_FAIL;
+       }
+
+       dual = (stmqspi_info->dual_mask & (1UL<<QSPI_DUAL_FLASH)) ? 1 : 0;
+
+       /* Set correct size value */
+       bank->size = stmqspi_info->dev->size_in_bytes << dual;
+
+       /* create and fill sectors array */
+       bank->num_sectors =
+               stmqspi_info->dev->size_in_bytes / 
stmqspi_info->dev->sectorsize;
+       sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors);
+       if (sectors == NULL) {
+               LOG_ERROR("not enough memory");
+               return ERROR_FAIL;
+       }
+
+       for (int sector = 0; sector < bank->num_sectors; sector++) {
+               sectors[sector].offset = sector * 
(stmqspi_info->dev->sectorsize << dual);
+               sectors[sector].size = (stmqspi_info->dev->sectorsize << dual);
+               sectors[sector].is_erased = -1;
+               sectors[sector].is_protected = 1;
+       }
+
+       bank->sectors = sectors;
+       stmqspi_info->probed = 1;
+
+       return ERROR_OK;
+}
+
+static int stmqspi_auto_probe(struct flash_bank *bank)
+{
+       struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv;
+       if (stmqspi_info->probed)
+               return ERROR_OK;
+       return stmqspi_probe(bank);
+}
+
+static int stmqspi_protect_check(struct flash_bank *bank)
+{
+       /* Nothing to do. Protection is only handled in SW. */
+       return ERROR_OK;
+}
+
+static int get_stmqspi_info(struct flash_bank *bank, char *buf, int buf_size)
+{
+       struct stmqspi_flash_bank *stmqspi_info = bank->driver_priv;
+
+       if (!(stmqspi_info->probed)) {
+               snprintf(buf, buf_size,
+                       "\nQSPI flash bank not probed yet\n");
+               return ERROR_OK;
+       }
+
+       snprintf(buf, buf_size, "\nQSPI flash information:"
+               " Device \'%s\' (ID 0x%06" PRIx32 ")\n",
+               stmqspi_info->dev->name, stmqspi_info->dev->device_id);
+
+       return ERROR_OK;
+}
+
+static const struct command_registration stmqspi_exec_command_handlers[] = {
+       {
+               .name = "mass_erase",
+               .handler = stmqspi_handle_mass_erase_command,
+               .mode = COMMAND_EXEC,
+               .usage = "bank_id",
+               .help = "Mass erase entire flash device.",
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration stmqspi_command_handlers[] = {
+       {
+               .name = "stmqspi",
+               .mode = COMMAND_ANY,
+               .help = "stmqspi flash command group",
+               .usage = "",
+               .chain = stmqspi_exec_command_handlers,
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+struct flash_driver stmqspi_flash = {
+       .name = "stmqspi",
+       .commands = stmqspi_command_handlers,
+       .flash_bank_command = stmqspi_flash_bank_command,
+       .erase = stmqspi_erase,
+       .protect = stmqspi_protect,
+       .write = stmqspi_write,
+       .read = default_flash_read,
+       .probe = stmqspi_probe,
+       .auto_probe = stmqspi_auto_probe,
+       .erase_check = default_flash_blank_check,
+       .protect_check = stmqspi_protect_check,
+       .info = get_stmqspi_info,
+       .usage = "",
+};
diff --git a/src/flash/nor/stmqspi.h b/src/flash/nor/stmqspi.h
new file mode 100644
index 0000000..5d387df
--- /dev/null
+++ b/src/flash/nor/stmqspi.h
@@ -0,0 +1,39 @@
+/* bits in SPI flash status register */
+#define SPIFLASH_WIP           0       /* Write In Progress bit in status 
register */
+#define SPIFLASH_WEL           1       /* Write Enable Latch bit in status 
register */
+
+/* register offsets */
+#define QSPI_CR                        (0x00)  /* Control register */
+#define QSPI_SR                        (0x08)  /* Status register */
+#define QSPI_FCR               (0x0C)  /* Flag clear register */
+#define QSPI_DLR               (0x10)  /* Data length register */
+#define QSPI_CCR               (0x14)  /* Communication configuration register 
*/
+#define QSPI_AR                        (0x18)  /* Address register */
+#define QSPI_DR                        (0x20)  /* Data register */
+#define QSPI_PSMKR             (0x24)  /* Polling status mask register */
+
+/* bits in QSPI_CR */
+#define QSPI_FSEL_FLASH                7       /* Select flash 2 */
+#define QSPI_DUAL_FLASH                6       /* Dual flash mode */
+#define QSPI_ABORT                     1       /* Abort bit */
+
+/* bits in QSPI_SR */
+#define QSPI_BUSY                      5       /* Busy flag */
+#define QSPI_FTF                       2       /* FIFO threshold flag */
+#define QSPI_TCF                       1       /* Transfer complete flag */
+
+/* fields in QSPI_CCR */
+#define QSPI_WRITE_MODE                0x00000000              /* indirect 
write mode */
+#define QSPI_READ_MODE         0x04000000              /* indirect read mode */
+#define QSPI_MM_MODE           0x0C000000              /* memory mapped mode */
+#define QSPI_1LINE_MODE                0x01000500              /* 1 line for 
address, data */
+#define QSPI_2LINE_MODE                0x02000900              /* 2 lines for 
address, data */
+#define QSPI_4LINE_MODE                0x03000D00              /* 4 lines for 
address, data */
+#define QSPI_NO_DATA           (~0x03000000)   /* no data */
+#define QSPI_NO_ADDR           (~0x00000C00)   /* no address */
+#define QSPI_ADDR3                     0x00002000              /* 3 byte 
adress */
+
+#define QSPI_DCYC_POS          18                              /* bit position 
of DCYC */
+#define QSPI_DCYC_LEN          5                               /* length of 
DCYC field */
+#define QSPI_DCYC_MASK         (((1<<QSPI_DCYC_LEN) - 1)<<QSPI_DCYC_POS)
+#define QSPI_DCYCLES           0                               /* for single 
data line mode */
diff --git a/tcl/board/stm32f7discovery.cfg b/tcl/board/stm32f7discovery.cfg
index 085340f..cb535c0 100755
--- a/tcl/board/stm32f7discovery.cfg
+++ b/tcl/board/stm32f7discovery.cfg
@@ -10,3 +10,29 @@ transport select hla_swd
 set WORKAREASIZE 0x40000
 
 source [find target/stm32f7x.cfg]
+
+$_TARGETNAME configure -event reset-init {
+    # QUADSPI initialization
+
+       mmw 0x40023830 0x000007FF 0     ;# RCC_AHB1ENR |= GPIOA-GPIOK (enable 
clocks)
+       mmw 0x40023838 0x00000002 0     ;# RCC_AHB3ENR |= QSPIEN (enable clock)
+       sleep 1                                         ;# Wait for clock 
startup
+
+    # PB2: CLK, PB6: BK1_nCS, PD13: BK1_IO3, PE2: BK1_IO2, PD12: BK1_IO1, 
PD11: BK1_IO0
+       mmw 0x40020400 0x00002020 0x00001010    ;# PB6, PB2 alternate
+       mmw 0x40020408 0x00003030 0x00000000    ;# high speed
+       mmw 0x40020420 0x0A000900 0x05000600    ;# AF10, AF9
+
+       mmw 0x40020C00 0x0A800000 0x05400000    ;# PD13, PD12, PD11 alternate
+       mmw 0x40020C08 0x0FC00000 0x00000000    ;# high speed
+       mmw 0x40020C24 0x00999000 0x00666000    ;# AF9, AF9, AF9
+
+       mmw 0x40021000 0x00000020 0x00000010    ;# PE2 alternate
+       mmw 0x40021008 0x00000030 0x00000000    ;# high speed
+       mmw 0x40021020 0x00000900 0x00000600    ;# AF9
+
+       mww 0xA0001004 0x00170100       ;# QUADSPI_DCR: FSIZE=0x17, CSHT=0x01, 
CKMODE=0
+       mww 0xA0001000 0x01500318       ;# QUADSPI_CR: PRESCALER=1, APMS=1, 
FTHRES=3, SSHIFT=1, TCEN=1
+       mww 0xA0001014 0x0D002503       ;# QUADSPI_CCR: FMODE=0x3, DMODE=0x1, 
DCYC=0x0, ADSIZE=0x3, ADMODE=0x1, IMODE=0x1
+       mmw 0xA0001000 0x00000001 0     ;# QUADSPI_CR: EN=1
+}
diff --git a/tcl/board/stm32l4discovery.cfg b/tcl/board/stm32l4discovery.cfg
index eb19331..a399569 100644
--- a/tcl/board/stm32l4discovery.cfg
+++ b/tcl/board/stm32l4discovery.cfg
@@ -1,13 +1,33 @@
 # Explicitly for the STM32L476 discovery board:
 # http://www.st.com/web/en/catalog/tools/PF261635
 # but perfectly functional for any other STM32L4 board connected via
-# an stlink-v2-1 interface.
+# an stlink-v2-1 interface (except for the QUADSPI part).
 # This is for STM32L4 boards that are connected via stlink-v2-1.
 
 source [find interface/stlink-v2-1.cfg]
 
 transport select hla_swd
 
+set WORKAREASIZE 0x18000
+
 source [find target/stm32l4x.cfg]
 
 reset_config srst_only
+
+$_TARGETNAME configure -event reset-init {
+    # QUADSPI initialization
+
+       mmw 0x4002104C 0x000000FF 0     ;# RCC_AHB2ENR |= GPIOA-GPIOK (enable 
clocks)
+       mmw 0x40021050 0x00000100 0     ;# RCC_AHB3ENR |= QSPIEN (enable clock)
+       sleep 1                                         ;# Wait for clock 
startup
+
+    # PE10: CLK, PE11: BK1_nCS, PE15: BK1_IO3, PE14: BK1_IO2, PE13: BK1_IO1, 
PE12: BK1_IO0
+       mmw 0x48001000 0xAAA00000 0x55500000    ;# PB15-10 alternate
+       mmw 0x48001008 0xFFF00000 0x00000000    ;# high speed
+       mmw 0x48001024 0xAAAAAA00 0x55555500    ;# AF10, AF10, AF10, AF10, 
AF10, AF10
+
+       mww 0xA0001004 0x00170100       ;# QUADSPI_DCR: FSIZE=0x17, CSHT=0x01, 
CKMODE=0
+       mww 0xA0001000 0x01500318       ;# QUADSPI_CR: PRESCALER=1, APMS=1, 
FTHRES=3, SSHIFT=1, TCEN=1
+       mww 0xA0001014 0x0D002503       ;# QUADSPI_CCR: FMODE=0x3, DMODE=0x1, 
DCYC=0x0, ADSIZE=0x3, ADMODE=0x1, IMODE=0x1
+       mmw 0xA0001000 0x00000001 0     ;# QUADSPI_CR: EN=1
+}
diff --git a/tcl/target/stm32f7x.cfg b/tcl/target/stm32f7x.cfg
index 17b26c8..684d676 100755
--- a/tcl/target/stm32f7x.cfg
+++ b/tcl/target/stm32f7x.cfg
@@ -58,6 +58,9 @@ $_TARGETNAME configure -work-area-phys 0x20000000 
-work-area-size $_WORKAREASIZE
 set _FLASHNAME $_CHIPNAME.flash
 flash bank $_FLASHNAME stm32f2x 0 0 0 0 $_TARGETNAME
 
+set _QSPINAME $_CHIPNAME.qspi
+flash bank $_QSPINAME stmqspi 0x90000000 0 0 0 $_TARGETNAME
+
 # adapter speed should be <= F_CPU/6. F_CPU after reset is 16MHz, so use 
F_JTAG = 2MHz
 adapter_khz 2000
 
diff --git a/tcl/target/stm32l4x.cfg b/tcl/target/stm32l4x.cfg
index 8b827ad..eaed092 100644
--- a/tcl/target/stm32l4x.cfg
+++ b/tcl/target/stm32l4x.cfg
@@ -58,6 +58,9 @@ $_TARGETNAME configure -work-area-phys 0x20000000 
-work-area-size $_WORKAREASIZE
 set _FLASHNAME $_CHIPNAME.flash
 flash bank $_FLASHNAME stm32l4x 0 0 0 0 $_TARGETNAME
 
+set _QSPINAME $_CHIPNAME.qspi
+flash bank $_QSPINAME stmqspi 0x90000000 0 0 0 $_TARGETNAME
+
 # JTAG speed should be <= F_CPU/6. F_CPU after reset is MSI 4MHz, so use 
F_JTAG = 500 kHz
 #
 # Since we may be running of an RC oscilator, we crank down the speed a
@@ -81,10 +84,11 @@ if {![using_hla]} {
 
 $_TARGETNAME configure -event reset-init {
        # CPU comes out of reset with MSI_ON | MSI_RDY | MSI Range 6 (4 MHz).
-       # Configure system to use MSI 24 MHz clock, compliant with VOS default 
(2).
-       # 3 WS compliant with VOS=2 and 24 MHz.
-       mww 0x40022000 0x00000102   ;# FLASH_ACR = PRFTBE | 3(Latency)
-       mww 0x4002100C 0x00000099   ;# RCC_CR = MSI_ON | MSIRGSEL| MSI Range 10
+       # Configure system to use MSI 48 MHz clock, compliant with VOS default 
(2).
+       # 3 WS compliant with VOS=2 and 48 MHz.
+       mww 0x40022000 0x00000103   ;# FLASH_ACR = PRFTBE | 3(Latency)
+       mww 0x40021000 0x000000B9   ;# RCC_CR = MSI_ON | MSIRGSEL| MSI Range 11
+
        # Boost JTAG frequency
        adapter_khz 4000
 }

-- 

------------------------------------------------------------------------------
_______________________________________________
OpenOCD-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/openocd-devel

Reply via email to