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/4152

-- gerrit

commit a3dde711dc584cec47b83235d258b79a54b7a5fd
Author: Andreas Bolsch <[email protected]>
Date:   Sat May 27 16:32:32 2017 +0200

    Generic (bitbanging) driver for SPI flash for Cortex-M
    
    Some Cortex-M controllers include a specialized SPI interface
    for external serial flash chips. But most do not, so downloading
    firmware employing an external flash require a second procedure
    for filling that flash.
    This driver overcomes this nuisance by emulating an additional
    flash bank which looks like an internal bank.
    The SPI flash (or EEPROM) is connected via 4 GPIO pins (NCS, SCLK,
    MISO, MOSI) only, no hardware SPI is required, therefore the driver
    should work on any Cortex-M controller supporting word addressable
    GPIO ports.
    Of course, speed is not overwhelming, but on an STM32F746 up to
    1.5 MByte/s raw read is possible. However, the limiting factor is
    the debug interface and its USB attachement: read/write on
    STM32f746-disco via ST-Link up to approx. 150kByte/s.
    Set-up is somewhat complicated, as GPIOs must be initialized by a
    dedicated script (e. g. in reset init hook, two samples included).
    Tested on STM32f746-disco, STM32F769-disco and an STM32F103 board.
    
    Signed-off-by: Andreas Bolsch <[email protected]>
    
    Change-Id: I08da60b3a2ed9e156f9eb26859fe13dfd973b873
    Signed-off-by: Andreas Bolsch <[email protected]>

diff --git a/contrib/loaders/erase_check/msoftspi_erase_check.S 
b/contrib/loaders/erase_check/msoftspi_erase_check.S
new file mode 100644
index 0000000..bb4a2c1
--- /dev/null
+++ b/contrib/loaders/erase_check/msoftspi_erase_check.S
@@ -0,0 +1,112 @@
+
+/***************************************************************************
+ *   Copyright (C) 2017 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.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+       .text
+       .syntax unified
+       .cpu cortex-m0
+       .thumb
+       .thumb_func
+
+/* To assemble:
+ * arm-none-eabi-gcc -Wa,-adlmn 
contrib/loaders/erase_check/msoftspi_erase_check.S > msoftspi_erase_check.lst
+ *
+ * To generate binary file:
+ * arm-none-eabi-objcopy -O binary msoftspi_erase_check.o 
msoftspi_erase_check.bin
+ *
+ * To generate C array definition:
+ * xxd -i msoftspi_erase_check.bin
+ */
+
+/* Params:
+ * r0 - sector count
+ * r1 - flash page size
+ * r2 - unused
+ * r3 - 2/3/4-byte (0x0: 2 byte, 0x1: 3 byte, 0x3: 4-byte)
+ *
+ * Clobbered:
+ * r4 - r7, r10 - r12 tmp */
+
+#include "../flash/msoftspi.inc"
+
+start:
+       mov             r8, r0                                          /* save 
sector count */
+       adr             r0, buffer                                      /* 
pointer to start of buffer */
+       mov             r9, r0                                          /* save 
pointer */
+       setup_regs
+sector_start:
+       mov             r5, r9                                          /* get 
pointer to sector info */
+       ldmia   r5!, {r6, r7}                           /* load address offset 
and length */
+       mov             r12, r6                                         /* save 
address offset*/
+       mov             r11, r7                                         /* save 
sector length */
+start_read:
+       movs    r4, #SPIFLASH_READ                      /* read cmd */
+       send_cmd_addr                                           /* send cmd and 
address */
+       nop                                                                     
/* switch to input */
+       ldr             r2, port_pin_miso                       /* MISO port 
address */
+       ldr             r3, port_pin_miso+4                     /* MISO pin 
bitmask */
+       ldr             r5, bits_no                                     /* get 
bit numbers */
+       lsrs    r5, r5, #8                                      /* MISO bit no. 
in lowest byte */
+read_loop:
+       bl              shift_in_byte                           /* read byte 
from flash */
+       movs    r7, #0xFF                                       /* fill bits 
8-15 */
+       lsls    r7, #8                                          /* with ones */
+       orrs    r4, r4, r7                                      /* copy ones to 
left of read byte */
+       mov             r7, r9                                          /* 
pointer to result */
+       ldr             r6, [r7, #8]                            /* get previous 
result */
+       ands    r6, r4                                          /* and read 
byte to result */
+       lsls    r4, r4, #8                                      /* shift result 
into higher byte */
+       orrs    r6, r6, r4                                      /* or read byte 
to result */
+       str             r6, [r7, #8]                            /* save updated 
result */
+       mov             r7, r12                                         /* get 
address offset */
+       adds    r7, r7, #1                                      /* increment 
address */
+       mov             r12, r7                                         /* save 
address offset */
+       mov             r6, r11                                         /* get 
count */
+       subs    r6, r6, #1                                      /* decrement 
count */
+       mov             r11, r6                                         /* save 
count */
+       beq             sector_end                                      /* stop 
if sector completed */
+       mov             r6, r10                                         /* get 
page size mask */
+       tst             r6, r7                                          /* page 
end ? */
+       b               read_loop                                       /* if 
not, then next byte */
+page_end:
+       bl              deselect                                        /* 
finish this sector read */
+       b               start_read                                      /* then 
next page */
+sector_end:
+       bl              deselect                                        /* 
finish this sector read */
+       mov             r7, r9                                          /* 
pointer to result */
+       mov             r6, r11                                         /* get 
remaining count */
+       str             r6, [r7, #4]                            /* store 
remaining (zero) count */
+       adds    r7, r7, #12                                     /* three words 
*/
+       mov             r9, r7                                          /* save 
update pointer */
+       mov             r7, r8                                          /* get 
sector count */
+       subs    r7, r7, #1                                      /* decrement 
count */
+       mov             r8, r7                                          /* save 
updated count */
+       bne             sector_start                            /* next sector 
if not finished */
+       b               exit
+
+       deselect
+       shift_in_byte
+       shift_out_byte
+
+exit:
+       .align  2                                                       /* 
align to word, bkpt is 4 words */
+       bkpt    #0                                                      /* 
before code end for exit_point */
+       .align  2                                                       /* 
align to word */
+
+       param_block
+       .equ    buffer, .
diff --git a/contrib/loaders/flash/msoftspi.inc 
b/contrib/loaders/flash/msoftspi.inc
new file mode 100644
index 0000000..bb66618
--- /dev/null
+++ b/contrib/loaders/flash/msoftspi.inc
@@ -0,0 +1,143 @@
+#include "../../../src/flash/nor/spi.h"
+
+/* set NCS */
+       .macro deselect
+deselect:
+       ldr             r6, port_pin_ncs                        /* NCS port 
address */
+       ldr             r7, [r6]                                        /* load 
port data */
+       ldr             r6, port_pin_ncs+4                      /* NCS pin 
bitmask */
+       orrs    r7, r7, r6                                      /* set NCS bit 
*/
+       ldr             r6, port_pin_ncs                        /* NCS port 
address */
+       str             r7, [r6]                                        /* 
store new contents */
+       nop                                                                     
/* switch to output */
+       bx              lr                                                      
/* return */
+       .endm
+
+/* entry point, initialize registers */
+       .macro  setup_regs
+       subs    r0, #1                                          /* decrement 
count */
+       mov             r11, r0                                         /* save 
count */
+       mov             r12, r2                                         /* save 
address offset */
+       subs    r1, #1                                          /* create page 
size mask */
+       lsls    r3, r3, #30                                     /* flags into 
bit 30 and 31 */
+       orrs    r1, r1, r3                                      /* copy flags 
in page size mask */
+       mov             r10, r1                                         /* save 
mask and flags */
+       ldr             r0, port_pin_sclk                       /* load SCLK 
port address */
+       ldr             r1, port_pin_sclk+4                     /* load SCLK 
pin bitmask */
+       bl              deselect                                        /* for 
a clean start */
+       .endm
+
+/* send cmd and following 2-, 3- or 4-byte address to flash */
+       .macro  send_cmd_addr
+       bl              shift_out_byte                          /* send cmd */
+       mov             r7, r10                                         /* get 
3/4-byte flags */
+       lsls    r7, r7, #1                                      /* test for 
4-byte address */
+       bcc             addr_0_23                                       /* skip 
if 3-byte address */
+       mov             r4, r12                                         /* get 
address offset */
+       lsrs    r4, r4, #24                                     /* addr bits 
31-24 */
+       bl              shift_out_next                          /* send addr 
byte */
+addr_0_23:
+       mov             r7, r10                                         /* get 
3/4-byte flags */
+       lsls    r7, r7, #2                                      /* test for 
3-byte address */
+       bcc             addr_0_15                                       /* skip 
if 2-byte address */
+       mov             r4, r12                                         /* get 
address offset */
+       lsrs    r4, r4, #16                                     /* addr bits 
23-16 */
+       bl              shift_out_next                          /* send addr 
byte */
+addr_0_15:
+       mov             r4, r12                                         /* get 
address offset */
+       lsrs    r4, r4, #8                                      /* addr bits 
15-8 */
+       bl              shift_out_next                          /* send addr 
byte */
+       mov             r4, r12                                         /* get 
address offset */
+       lsrs    r4, r4, #0                                      /* addr bits 
7-0 */
+       bl              shift_out_next                          /* send addr 
byte */
+       .endm
+
+/* shift in one byte from MISO pin
+ * r4: data byte (out)
+ * r5: bit no of MISO pin (lowest byte)
+ * clobbered: r2, r3, r6, r7 */
+       .macro  shift_in_byte
+shift_in_byte:
+       movs    r4, #1                                          /* set bit 0 
only */
+       lsls    r4, r4, #24                                     /* into bit 24 
*/
+       movs    r7, #0                                          /* clear temp 
result */
+       ldr             r6, [r0]                                        /* load 
SCLK port data */
+shift_in_loop:
+       orrs    r4, r4, r7                                      /* insert new 
bit into result */
+       orrs    r6, r6, r1                                      /* set SCLK bit 
*/
+       str             r6, [r0]                                        /* 
store new SCLK port data */
+       ldr             r7, [r2]                                        /* load 
MISO port data */
+       ands    r7, r7, r3                                      /* mask all but 
MISO bit */
+       rors    r7, r7, r5                                      /* shift new 
bit into bit 0 */
+       bics    r6, r6, r1                                      /* clear SCLK 
bit */
+       str             r6, [r0]                                        /* 
store new SCLK port data */
+       lsls    r4, r4, #1                                      /* shift result 
left one */
+       bcc             shift_in_loop                           /* again if not 
finished */
+       orrs    r4, r4, r7                                      /* insert last 
bit into result */
+       bx              lr                                                      
/* return */
+       .endm
+
+/* tail of shift_out_byte, MOSI port address parametrized */
+       .macro  shift_tail      mosi
+       lsls    r4, r4, #1                                      /* original bit 
7 into C */
+shift_out_loop_\@:
+       bics    r6, r6, r1                                      /* clear SCLK 
bit */
+       sbcs    r5, r5, r5                                      /* fill all 
bits with ~C */
+       ands    r5, r5, r3                                      /* set/clear 
bit at MOSI position */
+       str             r6, [r0]                                        /* 
store new SCLK port data */
+       orrs    \mosi, \mosi, r3                        /* set MOSI bit */
+       bics    \mosi, \mosi, r5                        /* insert new MOSI bit 
*/
+       str             \mosi, [r2]                                     /* 
store new MOSI port data */
+       orrs    r6, r6, r1                                      /* set SCLK bit 
*/
+       lsls    r4, r4, #1                                      /* shift next 
bit into C */
+       str             r6, [r0]                                        /* 
store new SCLK port data */
+       bne             shift_out_loop_\@                       /* again if not 
finished */
+       str             r6, [r0]                                        /* 
store new SCLK port data */
+       bics    r6, r6, r1                                      /* clear SCLK 
bit */
+       str             r6, [r0]                                        /* 
store new SCLK port data */
+       bx              lr                                                      
/* return */
+       .endm
+
+/* clear NCS, shift lowest byte of word out via MOSI
+ * depending on whether MOSI and SCLK are located on same port,
+ * algorithm is slightly different
+ *
+ * r4: data word (in)
+ * clobbered: r2, r3, r5, r6, r7 */
+       .macro  shift_out_byte
+shift_out_byte:
+       ldr             r2, port_pin_ncs                        /* NCS port 
address */
+       ldr             r3, port_pin_ncs+4                      /* NCS pin 
bitmask */
+       ldr             r7, [r2]                                        /* load 
port data */
+       bics    r7, r7, r3                                      /* clear NCS 
bit */
+       str             r7, [r2]                                        /* 
store new contents */
+       ldr             r2, port_pin_mosi                       /* MOSI port 
address */
+       ldr             r3, port_pin_mosi+4                     /* MOSI pin 
bitmask */
+shift_out_next:
+       lsls    r4, r4, #1                                      /* insert '1' 
bit just */
+       adds    r4, r4, #1                                      /* right of 
data byte */
+       lsls    r4, r4, #23                                     /* original bit 
7 into bit 31 */
+       ldr             r6, [r0]                                        /* load 
SCLK port data */
+       bics    r6, r6, r1                                      /* clear SCLK 
bit */
+       cmp             r0, r2                                          /* SCLK 
and MOSI on same port? */
+       beq             shift_out_eq
+shift_out_ne:
+       ldr             r7, [r2]                                        /* load 
MOSI port data */
+       shift_tail      mosi=r7                                 /* SCLK, MOSI 
on different ports */
+shift_out_eq:
+       shift_tail      mosi=r6                                 /* SCLk, MOSI 
on same port */
+       .endm
+
+/* parameter and buffer allocation */
+       .macro  param_block
+port_pin_ncs:
+       .space  8                                                       /* port 
address (output reg) and mask for NCS pin */
+port_pin_sclk:
+       .space  8                                                       /* port 
address (output reg) and mask for SCLK pin */
+port_pin_miso:
+       .space  8                                                       /* port 
address (input reg) and mask for MISO pin  */
+port_pin_mosi:
+       .space  8                                                       /* port 
address (output reg) and mask for MOSI pin */
+bits_no:
+       .space  4                                                       /* bit 
numbers of NCS, SCLK, MISO, MOSI pins */
+       .endm
diff --git a/contrib/loaders/flash/msoftspi_read.S 
b/contrib/loaders/flash/msoftspi_read.S
new file mode 100644
index 0000000..3f05a14
--- /dev/null
+++ b/contrib/loaders/flash/msoftspi_read.S
@@ -0,0 +1,104 @@
+/***************************************************************************
+ *   Copyright (C) 2017 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.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+       .text
+       .syntax unified
+       .cpu cortex-m0
+       .thumb
+       .thumb_func
+
+/* To assemble:
+ * arm-none-eabi-gcc -Wa,-adlmn contrib/loaders/flash/msoftspi_read.S > 
msoftspi_read.lst
+ *
+ * To generate binary file:
+ * arm-none-eabi-objcopy -O binary msoftspi_read.o msoftspi_read.bin
+ *
+ * To generate C array definition:
+ * xxd -i msoftspi_read.bin
+ */
+
+/* Params:
+ * r0 - total count (bytes), status (out)
+ * r1 - flash page size
+ * r2 - address offset into flash
+ * r3 - 2/3/4-byte (0x0: 2 byte, 0x1: 3 byte, 0x3: 4-byte)
+ * r8 - fifo start
+ * r9 - fifo end + 1
+ *
+ * Clobbered:
+ * r4 - r7, r10 - r12 tmp */
+
+#include "msoftspi.inc"
+
+start:
+       setup_regs                                                      /* 
initialize registers */
+start_read:
+       movs    r4, #SPIFLASH_READ                      /* read cmd */
+       send_cmd_addr                                           /* send cmd and 
address */
+       nop                                                                     
/* switch to input */
+       ldr             r2, port_pin_miso                       /* MISO port 
address */
+       ldr             r3, port_pin_miso+4                     /* MISO pin 
bitmask */
+       ldr             r5, bits_no                                     /* get 
bit numbers */
+       lsrs    r5, r5, #8                                      /* MISO bit no. 
in lowest byte */
+read_loop:
+       bl              shift_in_byte                           /* read byte 
from flash */
+       ldr             r6, wp                                          /* get 
wp */
+       strb    r4, [r6]                                        /* write next 
byte */
+       adds    r6, r6, #1                                      /* increment wp 
*/
+       cmp             r6, r9                                          /* wp 
beyond end? */
+       blo             read_wait                                       /* if 
no, then ok */
+       mov             r6, r8                                          /* else 
wrap around */
+read_wait:
+       ldr             r7, rp                                          /* get 
rp */
+       cmp             r7, #0                                          /* if 
rp equals 0 */
+       beq             exit                                            /* then 
abort */
+       cmp             r6, r7                                          /* 
check if fifo full */
+       beq             read_wait                                       /* wait 
until not full */
+       adr             r7, wp                                          /* get 
address of wp */
+       str             r6, [r7]                                        /* save 
updated wp */
+       mov             r7, r12                                         /* get 
address offset */
+       adds    r7, r7, #1                                      /* increment 
address */
+       mov             r12, r7                                         /* save 
address offset */
+       mov             r6, r11                                         /* get 
count */
+       subs    r6, r6, #1                                      /* decrement 
count */
+       mov             r11, r6                                         /* save 
count */
+       bmi             exit                                            /* stop 
if no data left */
+       mov             r6, r10                                         /* get 
page size mask */
+       tst             r6, r7                                          /* page 
end ? */
+       b               read_loop                                       /* if 
not, then next byte */
+page_end:
+       bl              deselect                                        /* 
finish this page read */
+       b               start_read                                      /* then 
next page */
+
+       deselect
+       shift_in_byte
+       shift_out_byte
+
+exit:
+       bl              deselect                                        /* 
finish this read cmd */
+       mov             r0, r11                                         /* get 
count */
+       adds    r0, r0, #1                                      /* correct 
count */
+       mov             r2, r12                                         /* 
restore offset */
+       .align  2                                                       /* 
align to word, bkpt is 11 words */
+       bkpt    #0                                                      /* 
before code end for exit_point */
+       .align  2                                                       /* 
align to word */
+
+       param_block
+       .equ wp, .                                                      /* wp, 
uint32_t */
+       .equ rp, wp + 4                                         /* rp, uint32_t 
*/
+       .equ buffer, rp + 4                                     /* buffer 
follows right away */
diff --git a/contrib/loaders/flash/msoftspi_write.S 
b/contrib/loaders/flash/msoftspi_write.S
new file mode 100644
index 0000000..56bab5e
--- /dev/null
+++ b/contrib/loaders/flash/msoftspi_write.S
@@ -0,0 +1,118 @@
+/***************************************************************************
+ *   Copyright (C) 2017 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.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+       .text
+       .syntax unified
+       .cpu cortex-m0
+       .thumb
+       .thumb_func
+
+/* To assemble:
+ * arm-none-eabi-gcc -Wa,-adlmn contrib/loaders/flash/msoftspi_write.S > 
msoftspi_write.lst
+ *
+ * To generate binary file:
+ * arm-none-eabi-objcopy -O binary msoftspi_write.o msoftspi_write.bin
+ *
+ * To generate C array definition:
+ * xxd -i msoftspi_write.bin
+ */
+
+/* Params:
+ * r0 - total count (bytes), status (out)
+ * r1 - flash page size
+ * r2 - address offset into flash
+ * r3 - 2/3/4-byte (0x0: 2 byte, 0x1: 3 byte, 0x3: 4-byte)
+ * r8 - fifo start
+ * r9 - fifo end + 1
+ *
+ * Clobbered:
+ * r4 - r7, r10 - r12 tmp */
+
+#include "msoftspi.inc"
+
+start:
+       setup_regs                                                      /* 
initialize registers */
+wip_loop:
+       movs    r4, #SPIFLASH_READ_STATUS       /* read status reg cmd */
+       bl              shift_out_byte                          /* send command 
*/
+       nop                                                                     
/* switch to input */
+       ldr             r2, port_pin_miso                       /* MISO port 
address */
+       ldr             r3, port_pin_miso+4                     /* MISO pin 
bitmask */
+       ldr             r5, bits_no                                     /* get 
bit numbers */
+       lsrs    r5, r5, #8                                      /* MISO bit no. 
in lowest byte */
+       bl              shift_in_byte                           /* read status 
byte */
+       bl              deselect                                        /* end 
cmd */
+       lsrs    r4, r4, #(SPIFLASH_BSY+1)       /* if flash busy, */
+       bcs             wip_loop                                        /* then 
poll again */
+       mov             r7, r11                                         /* get 
residual count */
+       tst             r7, r7                                          /* test 
residual count */
+       bpl             start_write                                     /* if 
negative, then finished */
+       b               exit
+start_write:
+       movs    r4, #SPIFLASH_WRITE_ENABLE      /* write enable cmd */
+       bl              shift_out_byte                          /* send cmd */
+       bl              deselect                                        /* end 
cmd */
+       movs    r4, #SPIFLASH_PAGE_PROGRAM      /* page program cmd */
+       send_cmd_addr                                           /* send cmd and 
address */
+write_loop:
+       ldr             r7, wp                                          /* get 
wp */
+       cmp             r7, #0                                          /* if 
wp equals 0 */
+       beq             exit                                            /* then 
abort */
+       ldr             r6, rp                                          /* get 
rp */
+       cmp             r6, r7                                          /* 
check if fifo empty */
+       beq             write_loop                                      /* wait 
till not empty */
+       ldrb    r4, [r6, #0]                            /* read next byte */
+       bl              shift_out_next                          /* send byte to 
flash */
+       ldr             r6, rp                                          /* get 
rp */
+       adds    r6, r6, #1                                      /* increment 
internal rp */
+       cmp             r6, r9                                          /* 
internal rp beyond end? */
+       blo             write_loop1                                     /* if 
no, then ok */
+       mov             r6, r8                                          /* else 
wrap around */
+write_loop1:
+       adr             r7, rp                                          /* get 
address of rp */
+       str             r6, [r7]                                        /* save 
updated rp */
+       mov             r7, r12                                         /* get 
address offset */
+       adds    r7, r7, #1                                      /* increment 
address */
+       mov             r12, r7                                         /* save 
address offset */
+       mov             r6, r11                                         /* get 
count */
+       subs    r6, r6, #1                                      /* decrement 
count */
+       mov             r11, r6                                         /* save 
updated count */
+       bmi             page_end                                        /* stop 
if no data left */
+       mov             r6, r10                                         /* get 
page size mask */
+       tst             r6, r7                                          /* page 
end ? */
+       bne             write_loop                                      /* if 
not, then next byte */
+page_end:
+       bl              deselect                                        /* 
finish this page write */
+       b               wip_loop                                        /* then 
next page */
+
+       deselect
+       shift_in_byte
+       shift_out_byte
+
+exit:
+       mov             r0, r11                                         /* get 
count */
+       adds    r0, r0, #1                                      /* correct 
count */
+       mov             r1, r12                                         /* get 
address offset */
+       .align  2                                                       /* 
align to word, bkpt is 11 words */
+       bkpt    #0                                                      /* 
before code end for exit_point */
+       .align  2                                                       /* 
align to word */
+
+       param_block
+       .equ wp, .                                                      /* wp, 
uint32_t */
+       .equ rp, wp + 4                                         /* rp, uint32_t 
*/
+       .equ buffer, rp + 4                                     /* buffer 
follows right away */
diff --git a/doc/openocd.texi b/doc/openocd.texi
index 5ebfaac..ee58474 100644
--- a/doc/openocd.texi
+++ b/doc/openocd.texi
@@ -5073,6 +5073,64 @@ flash bank flash2 ath79 0x20000000 0 0 0 $_TARGETNAME cs2
 
 @end deffn
 
+@deffn {Flash Driver} msoftspi
+@cindex Generic Cortex-M Bitbanging SPI Interface
+@cindex msoftspi
+
+This driver supports common serial SPI flash chips for any little-endian
+ARM-Cortex-M CPU connected via GPIO pins with memory mapped and word accessible
+output and input registers. No special bit set / clear facility is required.
+
+Setup requires the @var{base} parameter in order to identify
+the memory bank and four @var{*_addr}, @var{*_bit} pairs specifying
+word address and bit number of the GPIO ports of NCS (out), SCLK (out),
+MISO (in), MOSI pins (out). Other parameters are ignored:
+
+base 0 0 0 $_TARGETNAME ncs_addr ncs_bit sclk_addr sclk_bit miso_addr miso_bit 
mosi_addr mosi_bit
+
+@var{base} is used in write_image etc. to map a certain part of the CPU 
address space
+to this flash, but is arbitrary otherwise. Chip is autodetected via SPI 
command and
+matched against hardcoded list of types.
+
+The driver automatically uses 2-, 3-, or 4-byte addresses according to 
capacity.
+However, for most flash chips 4-byte address mode must be enabled separately,
+e. g. by "Enter 4-Byte Mode" command or setting a special control bit.
+
+GPIO clocks must be enabled, pins configured as GPIO output or input pins,
+respectively, in e. g. reset handler. If flash is connected with four I/O lines
+for QPI mode, the lines not used in SPI mode (HOLD, WP) must be taken into
+account, too. Non-volatile settings like QPI mode or block protection have
+to be disabled.
+
+SPI cycle time depends on CPU and peripheral clocks. Bitbanging might seem
+to be quite slow, but page programming time and data transfer via debug
+interface largely dominate anyway.
+
+Example for STM32F746G discovery board (PB6, PB2, PD12, PD11):
+
+@example
+flash bank_id $_FLASHNAME msoftspi 0x90000000 0 0 0 $_TARGETNAME
+    0x40020414 6 0x40020414 2 0x40020C10 12 0x40020C14 11
+@end example
+
+There are three specific commands
+@deffn Command {msoftspi mass_erase} bank_id
+Performs a mass erase.
+@end deffn
+
+@deffn Command {msoftspi setid} bank_id name total_size page_size 
mass_erase_cmd sector_size sector_erase_cmd
+Set flash parameters: @var{name} human readable string, @var{total_size} size 
in bytes,
+@var{page_size} is write page size. @var{mass_erase_cmd}, @var{sector_size} 
and @var{sector_erase_cmd}
+are optional. Required if chip id not hardcoded yet and e. g. for EEPROMs 
which don't support an id
+command at all.
+@end deffn
+
+@deffn Command {msoftspi spicmd} bank_id resp_num cmd_byte ...
+Sends command @var{cmd_byte} and following bytes and reads @var{resp_num} 
bytes afterwards.
+@end deffn
+
+@end deffn
+
 @subsection Internal Flash (Microcontrollers)
 
 @deffn {Flash Driver} aduc702x
diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am
index 5179a7c..978b532 100644
--- a/src/flash/nor/Makefile.am
+++ b/src/flash/nor/Makefile.am
@@ -34,6 +34,7 @@ NOR_DRIVERS = \
        %D%/lpcspifi.c \
        %D%/mdr.c \
        %D%/mrvlqspi.c \
+       %D%/msoftspi.c \
        %D%/niietcm4.c \
        %D%/non_cfi.c \
        %D%/nrf51.c \
diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c
index 5502ce5..f8d505c 100644
--- a/src/flash/nor/drivers.c
+++ b/src/flash/nor/drivers.c
@@ -47,6 +47,7 @@ extern struct flash_driver lpc2900_flash;
 extern struct flash_driver lpcspifi_flash;
 extern struct flash_driver mdr_flash;
 extern struct flash_driver mrvlqspi_flash;
+extern struct flash_driver msoftspi_flash;
 extern struct flash_driver niietcm4_flash;
 extern struct flash_driver nrf51_flash;
 extern struct flash_driver numicro_flash;
@@ -101,6 +102,7 @@ static struct flash_driver *flash_drivers[] = {
        &lpcspifi_flash,
        &mdr_flash,
        &mrvlqspi_flash,
+       &msoftspi_flash,
        &niietcm4_flash,
        &nrf51_flash,
        &numicro_flash,
diff --git a/src/flash/nor/msoftspi.c b/src/flash/nor/msoftspi.c
new file mode 100644
index 0000000..7fac25f
--- /dev/null
+++ b/src/flash/nor/msoftspi.c
@@ -0,0 +1,1429 @@
+/***************************************************************************
+ *   Copyright (C) 2010 by Antonio Borneo <[email protected]>,     *
+ *        2017 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.                              
                   *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "imp.h"
+#include "spi.h"
+#include <helper/time_support.h>
+#include <target/algorithm.h>
+#include <target/armv7m.h>
+
+#define CLR_PORT_BIT(data, pin)                                                
                                        \
+{                                                                              
                                                                \
+       (data) &= ~(msoftspi_info->pin.mask);                                   
                        \
+       retval = target_write_u32(target, msoftspi_info->pin.addr, data);       
\
+}
+
+#define SET_PORT_BIT(data, pin)                                                
                                        \
+{                                                                              
                                                                \
+       (data) |= (msoftspi_info->pin.mask);                                    
                        \
+       retval = target_write_u32(target, msoftspi_info->pin.addr, data);       
\
+}
+
+/* bit 1: address byte 4 with bits 24-31 required
+ * bit 0: address byte 3 with bits 16-23 required */
+#define ADDR_BYTES \
+       (((msoftspi_info->dev.size_in_bytes > (1<<24)) ? 0x2 : 0x00) | \
+        ((msoftspi_info->dev.size_in_bytes > (1<<16)) ? 0x1 : 0x00))
+
+/* convert uint32_t into 4 uint8_t in target (i. e. little endian)
+ * byte order, re-inventing the wheel ... */
+static inline uint32_t h_to_le_32(uint32_t val)
+{
+       union {
+               uint32_t word;
+               uint8_t byte[sizeof(uint32_t)];
+       } res;
+
+       res.byte[0] = val & 0xFF;
+       res.byte[1] = (val>>8) & 0xFF;
+       res.byte[2] = (val>>16) & 0xFF;
+       res.byte[3] = (val>>24) & 0xFF;
+
+       return res.word;
+}
+
+/* timeout in ms */
+#define MSOFTSPI_CMD_TIMEOUT           (100)
+#define MSOFTSPI_PROBE_TIMEOUT         (100)
+#define MSOFTSPI_MAX_TIMEOUT           (2000)
+#define MSOFTSPI_MASS_ERASE_TIMEOUT    (400000)
+
+typedef struct {
+       uint32_t addr;
+       uint32_t mask;
+} port_pin;
+
+struct msoftspi_flash_bank {
+       int probed;
+       uint32_t bank_num;
+       char devname[32];
+       struct flash_device dev;
+       port_pin ncs;
+       port_pin sclk;
+       port_pin miso;
+       port_pin mosi;
+       uint32_t bits_no;
+};
+
+struct sector_info {
+       uint32_t offset;
+       uint32_t size;
+       uint32_t result;
+};
+
+FLASH_BANK_COMMAND_HANDLER(msoftspi_flash_bank_command)
+{
+       struct msoftspi_flash_bank *msoftspi_info;
+       uint8_t bit_no;
+
+       LOG_DEBUG("%s", __func__);
+
+       if (CMD_ARGC < 14)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       msoftspi_info = malloc(sizeof(struct msoftspi_flash_bank));
+       if (msoftspi_info == NULL) {
+               LOG_ERROR("not enough memory");
+               return ERROR_FAIL;
+       }
+
+       memset(&msoftspi_info->dev, 0, sizeof(msoftspi_info->dev));
+       bank->driver_priv = msoftspi_info;
+       msoftspi_info->probed = 0;
+
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[6], msoftspi_info->ncs.addr);
+       COMMAND_PARSE_NUMBER(u8, CMD_ARGV[7], bit_no);
+       if (bit_no < 32) {
+               msoftspi_info->bits_no = ((msoftspi_info->bits_no & 
~(0xFF<<24)) | (bit_no<<24));
+               msoftspi_info->ncs.mask = 1<<bit_no;
+       } else {
+               command_print(CMD_CTX, "msoftspi: NCS bit number in 0 ... 31");
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[8], msoftspi_info->sclk.addr);
+       COMMAND_PARSE_NUMBER(u8, CMD_ARGV[9], bit_no);
+       if (bit_no < 32) {
+               msoftspi_info->bits_no = ((msoftspi_info->bits_no & 
~(0xFF<<16)) | (bit_no<<16));
+               msoftspi_info->sclk.mask = 1<<bit_no;
+       } else {
+               command_print(CMD_CTX, "msoftspi: SCLK bit number in 0 ... 31");
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[10], msoftspi_info->miso.addr);
+       COMMAND_PARSE_NUMBER(u8, CMD_ARGV[11], bit_no);
+       if (bit_no < 32) {
+               msoftspi_info->bits_no = ((msoftspi_info->bits_no & ~(0xFF<<8)) 
| (bit_no<<8));
+               msoftspi_info->miso.mask = 1<<bit_no;
+       } else {
+               command_print(CMD_CTX, "msoftspi: MISO bit number in 0 ... 31");
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[12], msoftspi_info->mosi.addr);
+       COMMAND_PARSE_NUMBER(u8, CMD_ARGV[13], bit_no);
+       if (bit_no < 32) {
+               msoftspi_info->bits_no = ((msoftspi_info->bits_no & ~(0xFF<<0)) 
| (bit_no<<0));
+               msoftspi_info->mosi.mask = 1<<bit_no;
+       } else {
+               command_print(CMD_CTX, "msoftspi: MOSI bit number must be in 0 
... 31");
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       LOG_DEBUG("NCS (out):  0x%" PRIx32 ", 0x%" PRIx32, 
msoftspi_info->ncs.addr, msoftspi_info->ncs.mask);
+       LOG_DEBUG("SCLK (out): 0x%" PRIx32 ", 0x%" PRIx32, 
msoftspi_info->sclk.addr, msoftspi_info->sclk.mask);
+       LOG_DEBUG("MISO (in):  0x%" PRIx32 ", 0x%" PRIx32, 
msoftspi_info->miso.addr, msoftspi_info->miso.mask);
+       LOG_DEBUG("MOSI (out): 0x%" PRIx32 ", 0x%" PRIx32, 
msoftspi_info->mosi.addr, msoftspi_info->mosi.mask);
+
+       if (msoftspi_info->sclk.addr == msoftspi_info->mosi.addr)
+               LOG_INFO("SCLK and MOSI located on same port - should work 
anyway");
+       return ERROR_OK;
+}
+
+/* Send and receive one byte via SPI */
+/* bits 7 down to 0 are shifted out, MSB first */
+static int msoftspi_shift_out(struct flash_bank *bank, uint32_t word)
+{
+       struct target *target = bank->target;
+       struct msoftspi_flash_bank *msoftspi_info = bank->driver_priv;
+       uint32_t port_sclk, port_mosi;
+       int k, retval;
+
+       LOG_DEBUG("0x%08" PRIx32, word);
+
+       retval = target_read_u32(target, msoftspi_info->mosi.addr, &port_sclk);
+       if (retval != ERROR_OK)
+               return retval;
+       retval = target_read_u32(target, msoftspi_info->mosi.addr, &port_mosi);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* shift bit 7 into bit 31 */
+       word <<= 24;
+
+       for (k = 0; k < 8; k++) {
+               /* shift out data bit, msb first */
+               if (word & (1<<31))
+                       SET_PORT_BIT(port_mosi, mosi)
+               else
+                       CLR_PORT_BIT(port_mosi, mosi);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               /* set SCLK */
+               if (msoftspi_info->sclk.addr == msoftspi_info->mosi.addr)
+                       port_sclk = port_mosi;
+               SET_PORT_BIT(port_sclk, sclk);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               /* shift next bit into bit 31 */
+               word <<= 1;
+
+               /* clear SCLK */
+               CLR_PORT_BIT(port_sclk, sclk);
+               if (retval != ERROR_OK)
+                       return retval;
+       }
+
+       return ERROR_OK;
+}
+
+static int msoftspi_shift_in(struct flash_bank *bank, uint32_t *word)
+{
+       struct target *target = bank->target;
+       struct msoftspi_flash_bank *msoftspi_info = bank->driver_priv;
+       uint32_t port_sclk, port_miso;
+       int k, retval;
+
+       retval = target_read_u32(target, msoftspi_info->mosi.addr, &port_sclk);
+       if (retval != ERROR_OK)
+               return retval;
+
+       for (k = 0; k < 8; k++) {
+               /* set SCLK */
+               SET_PORT_BIT(port_sclk, sclk);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               /* shift in data bit, msb first */
+               retval = target_read_u32(target, msoftspi_info->miso.addr, 
&port_miso);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               *word <<= 1;
+               (port_miso & msoftspi_info->miso.mask) && (*word |= 0x1);
+
+               /* clear SCLK */
+               CLR_PORT_BIT(port_sclk, sclk);
+               if (retval != ERROR_OK)
+                       return retval;
+       }
+
+       LOG_DEBUG("0x%08" PRIx32, *word);
+       return ERROR_OK;
+}
+
+/* Read the status register of the external SPI flash chip. */
+static int read_status_reg(struct flash_bank *bank, uint32_t *status)
+{
+       struct target *target = bank->target;
+       struct msoftspi_flash_bank *msoftspi_info = bank->driver_priv;
+       uint32_t port;
+       int retval, success;
+
+       success = ERROR_FAIL;
+
+       /* set NCS */
+       retval = target_read_u32(target, msoftspi_info->ncs.addr, &port);
+       if (retval != ERROR_OK)
+               goto err;
+       SET_PORT_BIT(port, ncs);
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* clear NCS */
+       CLR_PORT_BIT(port, ncs);
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* send command byte */
+       retval = msoftspi_shift_out(bank, SPIFLASH_READ_STATUS);
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* set to input */
+
+       /* get result byte */
+       success = msoftspi_shift_in(bank, status);
+       *status &= 0xFF;
+
+err:
+       /* set NCS */
+       retval = target_read_u32(target, msoftspi_info->ncs.addr, &port);
+       SET_PORT_BIT(port, ncs);
+
+       /* set to output */
+
+       return success;
+}
+
+/* Check for WIP (write in progress) bit in status register */
+/* 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 */
+               retval = read_status_reg(bank, &status);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               if ((status & SPIFLASH_BSY_BIT) == 0)
+                       return ERROR_OK;
+               alive_sleep(25);
+       } while (timeval_ms() < endtime);
+
+       LOG_ERROR("timeout");
+       return ERROR_FAIL;
+}
+
+/* Send "write enable" command to SPI flash chip */
+static int msoftspi_write_enable(struct flash_bank *bank)
+{
+       struct target *target = bank->target;
+       struct msoftspi_flash_bank *msoftspi_info = bank->driver_priv;
+       uint32_t port, status;
+       int retval;
+
+       /* clear NCS */
+       retval = target_read_u32(target, msoftspi_info->ncs.addr, &port);
+       if (retval != ERROR_OK)
+               goto err;
+       CLR_PORT_BIT(port, ncs);
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* send write enable command */
+       retval = msoftspi_shift_out(bank, SPIFLASH_WRITE_ENABLE);
+
+err:
+       /* set NCS */
+       SET_PORT_BIT(port, ncs);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* Read flash status register */
+       retval = read_status_reg(bank, &status);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* check write enabled */
+       if ((status & SPIFLASH_WE_BIT) == 0) {
+               LOG_ERROR("Cannot enable write to flash. Status=0x%08" PRIx32, 
status);
+               return ERROR_FAIL;
+       }
+
+       return ERROR_OK;
+}
+
+/* Erase a single sector */
+static int msoftspi_erase_sector(struct flash_bank *bank, int sector)
+{
+       struct target *target = bank->target;
+       struct msoftspi_flash_bank *msoftspi_info = bank->driver_priv;
+       uint32_t addr = bank->sectors[sector].offset;
+       uint32_t port, data;
+       int retval;
+
+       if (msoftspi_info->dev.erase_cmd == 0x00) {
+               LOG_ERROR("No sector erase available");
+               return ERROR_FAIL;
+       }
+
+       retval = msoftspi_write_enable(bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       if (bank->sectors[sector].is_protected) {
+               LOG_ERROR("Flash sector %d protected", sector);
+               return ERROR_FAIL;
+       } else
+               bank->sectors[sector].is_erased = -1;
+
+       /* Send Sector Erase command */
+       /* clear NCS */
+       retval = target_read_u32(target, msoftspi_info->ncs.addr, &port);
+       if (retval != ERROR_OK)
+               goto err;
+       CLR_PORT_BIT(port, ncs);
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* send command byte */
+       retval = msoftspi_shift_out(bank, msoftspi_info->dev.erase_cmd);
+       if (retval != ERROR_OK)
+               goto err;
+
+       if (ADDR_BYTES & 0x2) {
+               /* bits 24-31 */
+               retval = msoftspi_shift_out(bank, addr >> 24);
+               if (retval != ERROR_OK)
+                       goto err;
+       }
+
+       if (ADDR_BYTES & 0x1) {
+               /* bits 16-23 */
+               retval = msoftspi_shift_out(bank, addr >> 16);
+               if (retval != ERROR_OK)
+                       goto err;
+       }
+
+       /* bits 8-15 */
+       retval = msoftspi_shift_out(bank, addr >> 8);
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* bits 0-7 */
+       retval = msoftspi_shift_out(bank, addr >> 0);
+       if (retval != ERROR_OK)
+               goto err;
+
+err:
+       /* set NCS */
+       retval = target_read_u32(target, msoftspi_info->ncs.addr, &port);
+       SET_PORT_BIT(port, ncs);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* read flash status register */
+       retval = read_status_reg(bank, &data);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* check for command in progress for flash */
+       if ((data & SPIFLASH_WE_BIT) == 0) {
+               LOG_DEBUG("Sector erase not accepted by flash or already 
completed. Status=0x%08" PRIx32, data);
+               /* return ERROR_FAIL; */
+       }
+
+       /* poll WIP for end of self timed Sector Erase cycle */
+       retval = wait_till_ready(bank, MSOFTSPI_MAX_TIMEOUT);
+
+       /* erasure takes a long time, so some sort of progress message is a 
good idea */
+       LOG_DEBUG("sector %4d erased", sector);
+
+       return retval;
+}
+
+/* Erase range of sectors */
+static int msoftspi_erase(struct flash_bank *bank, int first, int last)
+{
+       struct target *target = bank->target;
+       struct msoftspi_flash_bank *msoftspi_info = bank->driver_priv;
+       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 (!(msoftspi_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;
+       }
+
+       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 = msoftspi_erase_sector(bank, sector);
+               if (retval != ERROR_OK)
+                       break;
+               keep_alive();
+       }
+
+       if (retval != ERROR_OK)
+               LOG_ERROR("Flash sector_erase failed on sector %d", sector);
+
+       return retval;
+}
+
+/* Check whether flash is blank */
+static int msoftspi_blank_check(struct flash_bank *bank)
+{
+       struct target *target = bank->target;
+       struct msoftspi_flash_bank *msoftspi_info = bank->driver_priv;
+       struct duration bench;
+       struct reg_param reg_params[3];
+       struct armv7m_algorithm armv7m_info;
+       struct working_area *erase_check_algorithm;
+       struct sector_info erase_check_info;
+       uint32_t buffer_size, exit_point, result;
+       int num_sectors, sector, index, count, retval;
+       const uint32_t erased = 0x00FF;
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       if (!(msoftspi_info->probed)) {
+               LOG_ERROR("Flash bank not probed");
+               return ERROR_FLASH_BANK_NOT_PROBED;
+       }
+
+       /* see contrib/loaders/erase_check/msoftspi_erase_check.S for src */
+       static const uint8_t msoftspi_erase_check_code[] = {
+               0x80, 0x46, 0x57, 0xa0, 0x81, 0x46, 0x01, 0x38, 0x83, 0x46, 
0x94, 0x46,
+               0x01, 0x39, 0x9b, 0x07, 0x19, 0x43, 0x8a, 0x46, 0x4b, 0x48, 
0x4c, 0x49,
+               0x00, 0xf0, 0x46, 0xf8, 0x4d, 0x46, 0xc0, 0xcd, 0xb4, 0x46, 
0xbb, 0x46,
+               0x03, 0x24, 0x00, 0xf0, 0x57, 0xf8, 0x57, 0x46, 0x7f, 0x00, 
0x03, 0xd3,
+               0x64, 0x46, 0x24, 0x0e, 0x00, 0xf0, 0x57, 0xf8, 0x57, 0x46, 
0xbf, 0x00,
+               0x03, 0xd3, 0x64, 0x46, 0x24, 0x0c, 0x00, 0xf0, 0x50, 0xf8, 
0x64, 0x46,
+               0x24, 0x0a, 0x00, 0xf0, 0x4c, 0xf8, 0x64, 0x46, 0x24, 0x00, 
0x00, 0xf0,
+               0x48, 0xf8, 0xc0, 0x46, 0x3c, 0x4a, 0x3d, 0x4b, 0x3f, 0x4d, 
0x2d, 0x0a,
+               0x00, 0xf0, 0x2a, 0xf8, 0xff, 0x27, 0x3f, 0x02, 0x3c, 0x43, 
0x4f, 0x46,
+               0xbe, 0x68, 0x26, 0x40, 0x24, 0x02, 0x26, 0x43, 0xbe, 0x60, 
0x67, 0x46,
+               0x01, 0x37, 0xbc, 0x46, 0x5e, 0x46, 0x01, 0x3e, 0xb3, 0x46, 
0x05, 0xd0,
+               0x56, 0x46, 0x3e, 0x42, 0xea, 0xe7, 0x00, 0xf0, 0x0d, 0xf8, 
0xc9, 0xe7,
+               0x00, 0xf0, 0x0a, 0xf8, 0x4f, 0x46, 0x5e, 0x46, 0x7e, 0x60, 
0x0c, 0x37,
+               0xb9, 0x46, 0x47, 0x46, 0x01, 0x3f, 0xb8, 0x46, 0xba, 0xd1, 
0x46, 0xe0,
+               0x24, 0x4e, 0x37, 0x68, 0x24, 0x4e, 0x37, 0x43, 0x22, 0x4e, 
0x37, 0x60,
+               0xc0, 0x46, 0x70, 0x47, 0x01, 0x24, 0x24, 0x06, 0x00, 0x27, 
0x06, 0x68,
+               0x3c, 0x43, 0x0e, 0x43, 0x06, 0x60, 0x17, 0x68, 0x1f, 0x40, 
0xef, 0x41,
+               0x8e, 0x43, 0x06, 0x60, 0x64, 0x00, 0xf5, 0xd3, 0x3c, 0x43, 
0x70, 0x47,
+               0x18, 0x4a, 0x19, 0x4b, 0x17, 0x68, 0x9f, 0x43, 0x17, 0x60, 
0x1c, 0x4a,
+               0x1c, 0x4b, 0x64, 0x00, 0x01, 0x34, 0xe4, 0x05, 0x06, 0x68, 
0x8e, 0x43,
+               0x90, 0x42, 0x10, 0xd0, 0x17, 0x68, 0x64, 0x00, 0x8e, 0x43, 
0xad, 0x41,
+               0x1d, 0x40, 0x06, 0x60, 0x1f, 0x43, 0xaf, 0x43, 0x17, 0x60, 
0x0e, 0x43,
+               0x64, 0x00, 0x06, 0x60, 0xf4, 0xd1, 0x06, 0x60, 0x8e, 0x43, 
0x06, 0x60,
+               0x70, 0x47, 0x64, 0x00, 0x8e, 0x43, 0xad, 0x41, 0x1d, 0x40, 
0x06, 0x60,
+               0x1e, 0x43, 0xae, 0x43, 0x16, 0x60, 0x0e, 0x43, 0x64, 0x00, 
0x06, 0x60,
+               0xf4, 0xd1, 0x06, 0x60, 0x8e, 0x43, 0x06, 0x60, 0x70, 0x47, 
0xc0, 0x46,
+               0x00, 0xbe, 0xc0, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00
+       };
+
+       /* this will overlay the last 9 words of msoftspi_*_code in target */
+       uint32_t port_buffer[] = {
+               h_to_le_32(msoftspi_info->ncs.addr), 
h_to_le_32(msoftspi_info->ncs.mask),
+               h_to_le_32(msoftspi_info->sclk.addr), 
h_to_le_32(msoftspi_info->sclk.mask),
+               h_to_le_32(msoftspi_info->miso.addr), 
h_to_le_32(msoftspi_info->miso.mask),
+               h_to_le_32(msoftspi_info->mosi.addr), 
h_to_le_32(msoftspi_info->mosi.mask),
+               h_to_le_32(msoftspi_info->bits_no)
+       };
+
+       num_sectors = bank->num_sectors;
+       while (buffer_size = sizeof(msoftspi_erase_check_code) + num_sectors * 
sizeof(erase_check_info),
+               target_alloc_working_area_try(target, buffer_size, 
&erase_check_algorithm) != ERROR_OK) {
+               num_sectors /= 2;
+               if (num_sectors <= 2) {
+                       LOG_WARNING("not enough working area, can't do SPI 
blank check");
+                       return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+               }
+       }
+
+       /* prepare check code, excluding port_buffer */
+       retval = target_write_buffer(target, erase_check_algorithm->address,
+               sizeof(msoftspi_erase_check_code) - sizeof(port_buffer), 
msoftspi_erase_check_code);
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* prepare port_buffer values */
+       retval = target_write_buffer(target, erase_check_algorithm->address
+               + sizeof(msoftspi_erase_check_code) - sizeof(port_buffer),
+               sizeof(port_buffer), (uint8_t *) port_buffer);
+       if (retval != ERROR_OK)
+               goto err;
+
+       duration_start(&bench);
+
+       /* after breakpoint instruction (halfword) one nop (halfword) and
+        * port_buffer till end of code */
+       exit_point = erase_check_algorithm->address + 
sizeof(msoftspi_erase_check_code)
+               - sizeof(uint32_t) - sizeof(port_buffer);
+
+       init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT);    /* sector count 
*/
+       init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);    /* flash 
page_size */
+       init_reg_param(&reg_params[2], "r3", 32, PARAM_OUT);    /* 2/3/4-byte 
address mode */
+
+       sector = 0;
+       while (sector < bank->num_sectors) {
+               /* at most num_sectors sectors to handle in one run */
+               count = bank->num_sectors - sector;
+               if (count > num_sectors)
+                       count = num_sectors;
+
+               for (index = 0; index < count; index++) {
+                       erase_check_info.offset = 
h_to_le_32(bank->sectors[sector + index].offset);
+                       erase_check_info.size = h_to_le_32(bank->sectors[sector 
+ index].size);
+                       erase_check_info.result = h_to_le_32(erased);
+
+                       retval = target_write_buffer(target, 
erase_check_algorithm->address
+                               + sizeof(msoftspi_erase_check_code) + index * 
sizeof(erase_check_info),
+                                       sizeof(erase_check_info), (uint8_t *) 
&erase_check_info);
+                       if (retval != ERROR_OK)
+                               goto err;
+               }
+
+               buf_set_u32(reg_params[0].value, 0, 32, count);
+               buf_set_u32(reg_params[1].value, 0, 32, 
msoftspi_info->dev.pagesize);
+               buf_set_u32(reg_params[2].value, 0, 32, ADDR_BYTES);
+
+               armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
+               armv7m_info.core_mode = ARM_MODE_THREAD;
+
+               LOG_DEBUG("checking sectors %d to %d", sector, sector + count - 
1);
+               /* check a block of sectors */
+               retval = target_run_algorithm(target,
+                       0, NULL,
+                       3, reg_params,
+                       erase_check_algorithm->address, exit_point,
+                       count * MSOFTSPI_MAX_TIMEOUT,
+                       &armv7m_info);
+               if (retval != ERROR_OK)
+                       break;
+
+               for (index = 0; index < count; index++) {
+                       retval = target_read_buffer(target, 
erase_check_algorithm->address
+                               + sizeof(msoftspi_erase_check_code) + index * 
sizeof(erase_check_info),
+                                       sizeof(erase_check_info), (uint8_t *) 
&erase_check_info);
+                       if (retval != ERROR_OK)
+                               goto err;
+
+                       if ((erase_check_info.offset != 
h_to_le_32(bank->sectors[sector + index].offset)) ||
+                               (erase_check_info.size != 0)) {
+                               LOG_ERROR("corrupted blank check info");
+                               goto err;
+                       }
+
+                       result = h_to_le_32(erase_check_info.result);
+                       bank->sectors[sector + index].is_erased = ((result & 
0xFF) == 0xFF);
+                       LOG_DEBUG("Flash sector %d checked: %04x", sector + 
index, result & 0xFFFF);
+               }
+               keep_alive();
+               sector += count;
+       }
+
+       destroy_reg_param(&reg_params[0]);
+       destroy_reg_param(&reg_params[1]);
+       destroy_reg_param(&reg_params[2]);
+
+       duration_measure(&bench);
+       LOG_INFO("msoftspi blank checked in"
+                       " %fs (%0.3f KiB/s)", duration_elapsed(&bench),
+                       duration_kbps(&bench, bank->size));
+
+err:
+       target_free_working_area(target, erase_check_algorithm);
+
+       return retval;
+}
+
+static int msoftspi_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;
+}
+
+/* Read a block of data from flash or write a block of data to flash */
+static int msoftspi_read_write_block(struct flash_bank *bank, uint8_t *buffer,
+               uint32_t offset, uint32_t count, int write)
+{
+       struct target *target = bank->target;
+       struct msoftspi_flash_bank *msoftspi_info = bank->driver_priv;
+       struct reg_param reg_params[6];
+       struct armv7m_algorithm armv7m_info;
+       struct working_area *write_algorithm;
+       static const uint8_t *code;
+       uint32_t page_size, fifo_start, fifo_size, buffer_size;
+       uint32_t exit_point, remaining;
+       int code_size, retval = ERROR_OK;
+
+       LOG_DEBUG("%s: offset=0x%08" PRIx32 " len=0x%08" PRIx32,
+               __func__, offset, count);
+
+       /* see contrib/loaders/flash/msoftspi_read.S for src */
+       static const uint8_t msoftspi_read_code[] = {
+               0x01, 0x38, 0x83, 0x46, 0x94, 0x46, 0x01, 0x39, 0x9b, 0x07, 
0x19, 0x43,
+               0x8a, 0x46, 0x48, 0x48, 0x48, 0x49, 0x00, 0xf0, 0x3a, 0xf8, 
0x03, 0x24,
+               0x00, 0xf0, 0x4f, 0xf8, 0x57, 0x46, 0x7f, 0x00, 0x03, 0xd3, 
0x64, 0x46,
+               0x24, 0x0e, 0x00, 0xf0, 0x4f, 0xf8, 0x57, 0x46, 0xbf, 0x00, 
0x03, 0xd3,
+               0x64, 0x46, 0x24, 0x0c, 0x00, 0xf0, 0x48, 0xf8, 0x64, 0x46, 
0x24, 0x0a,
+               0x00, 0xf0, 0x44, 0xf8, 0x64, 0x46, 0x24, 0x00, 0x00, 0xf0, 
0x40, 0xf8,
+               0xc0, 0x46, 0x3b, 0x4a, 0x3b, 0x4b, 0x3e, 0x4d, 0x2d, 0x0a, 
0x00, 0xf0,
+               0x22, 0xf8, 0x3d, 0x4e, 0x34, 0x70, 0x01, 0x36, 0x4e, 0x45, 
0x00, 0xd3,
+               0x46, 0x46, 0x3b, 0x4f, 0x00, 0x2f, 0x57, 0xd0, 0xbe, 0x42, 
0xfa, 0xd0,
+               0x37, 0xa7, 0x3e, 0x60, 0x67, 0x46, 0x01, 0x37, 0xbc, 0x46, 
0x5e, 0x46,
+               0x01, 0x3e, 0xb3, 0x46, 0x4c, 0xd4, 0x56, 0x46, 0x3e, 0x42, 
0xe6, 0xe7,
+               0x00, 0xf0, 0x01, 0xf8, 0xc5, 0xe7, 0x27, 0x4e, 0x37, 0x68, 
0x27, 0x4e,
+               0x37, 0x43, 0x25, 0x4e, 0x37, 0x60, 0xc0, 0x46, 0x70, 0x47, 
0x01, 0x24,
+               0x24, 0x06, 0x00, 0x27, 0x06, 0x68, 0x3c, 0x43, 0x0e, 0x43, 
0x06, 0x60,
+               0x17, 0x68, 0x1f, 0x40, 0xef, 0x41, 0x8e, 0x43, 0x06, 0x60, 
0x64, 0x00,
+               0xf5, 0xd3, 0x3c, 0x43, 0x70, 0x47, 0x1b, 0x4a, 0x1b, 0x4b, 
0x17, 0x68,
+               0x9f, 0x43, 0x17, 0x60, 0x1e, 0x4a, 0x1f, 0x4b, 0x64, 0x00, 
0x01, 0x34,
+               0xe4, 0x05, 0x06, 0x68, 0x8e, 0x43, 0x90, 0x42, 0x10, 0xd0, 
0x17, 0x68,
+               0x64, 0x00, 0x8e, 0x43, 0xad, 0x41, 0x1d, 0x40, 0x06, 0x60, 
0x1f, 0x43,
+               0xaf, 0x43, 0x17, 0x60, 0x0e, 0x43, 0x64, 0x00, 0x06, 0x60, 
0xf4, 0xd1,
+               0x06, 0x60, 0x8e, 0x43, 0x06, 0x60, 0x70, 0x47, 0x64, 0x00, 
0x8e, 0x43,
+               0xad, 0x41, 0x1d, 0x40, 0x06, 0x60, 0x1e, 0x43, 0xae, 0x43, 
0x16, 0x60,
+               0x0e, 0x43, 0x64, 0x00, 0x06, 0x60, 0xf4, 0xd1, 0x06, 0x60, 
0x8e, 0x43,
+               0x06, 0x60, 0x70, 0x47, 0xff, 0xf7, 0xb7, 0xff, 0x58, 0x46, 
0x01, 0x30,
+               0x62, 0x46, 0xc0, 0x46, 0x00, 0xbe, 0xc0, 0x46, 0x00, 0x00, 
0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+       };
+
+       /* see contrib/loaders/flash/msoftspi_write.S for src */
+       static const uint8_t msoftspi_write_code[] = {
+               0x01, 0x38, 0x83, 0x46, 0x94, 0x46, 0x01, 0x39, 0x9b, 0x07, 
0x19, 0x43,
+               0x8a, 0x46, 0x50, 0x48, 0x50, 0x49, 0x00, 0xf0, 0x4d, 0xf8, 
0x05, 0x24,
+               0x00, 0xf0, 0x62, 0xf8, 0xc0, 0x46, 0x4e, 0x4a, 0x4e, 0x4b, 
0x51, 0x4d,
+               0x2d, 0x0a, 0x00, 0xf0, 0x4b, 0xf8, 0x00, 0xf0, 0x41, 0xf8, 
0x64, 0x08,
+               0xf1, 0xd2, 0x5f, 0x46, 0x3f, 0x42, 0x00, 0xd5, 0x81, 0xe0, 
0x06, 0x24,
+               0x00, 0xf0, 0x50, 0xf8, 0x00, 0xf0, 0x36, 0xf8, 0x02, 0x24, 
0x00, 0xf0,
+               0x4b, 0xf8, 0x57, 0x46, 0x7f, 0x00, 0x03, 0xd3, 0x64, 0x46, 
0x24, 0x0e,
+               0x00, 0xf0, 0x4b, 0xf8, 0x57, 0x46, 0xbf, 0x00, 0x03, 0xd3, 
0x64, 0x46,
+               0x24, 0x0c, 0x00, 0xf0, 0x44, 0xf8, 0x64, 0x46, 0x24, 0x0a, 
0x00, 0xf0,
+               0x40, 0xf8, 0x64, 0x46, 0x24, 0x00, 0x00, 0xf0, 0x3c, 0xf8, 
0x3d, 0x4f,
+               0x00, 0x2f, 0x60, 0xd0, 0x3c, 0x4e, 0xbe, 0x42, 0xf9, 0xd0, 
0x34, 0x78,
+               0x00, 0xf0, 0x33, 0xf8, 0x39, 0x4e, 0x01, 0x36, 0x4e, 0x45, 
0x00, 0xd3,
+               0x46, 0x46, 0x37, 0xa7, 0x3e, 0x60, 0x67, 0x46, 0x01, 0x37, 
0xbc, 0x46,
+               0x5e, 0x46, 0x01, 0x3e, 0xb3, 0x46, 0x02, 0xd4, 0x56, 0x46, 
0x3e, 0x42,
+               0xe5, 0xd1, 0x00, 0xf0, 0x01, 0xf8, 0xb2, 0xe7, 0x25, 0x4e, 
0x37, 0x68,
+               0x25, 0x4e, 0x37, 0x43, 0x23, 0x4e, 0x37, 0x60, 0xc0, 0x46, 
0x70, 0x47,
+               0x01, 0x24, 0x24, 0x06, 0x00, 0x27, 0x06, 0x68, 0x3c, 0x43, 
0x0e, 0x43,
+               0x06, 0x60, 0x17, 0x68, 0x1f, 0x40, 0xef, 0x41, 0x8e, 0x43, 
0x06, 0x60,
+               0x64, 0x00, 0xf5, 0xd3, 0x3c, 0x43, 0x70, 0x47, 0x19, 0x4a, 
0x1a, 0x4b,
+               0x17, 0x68, 0x9f, 0x43, 0x17, 0x60, 0x1d, 0x4a, 0x1d, 0x4b, 
0x64, 0x00,
+               0x01, 0x34, 0xe4, 0x05, 0x06, 0x68, 0x8e, 0x43, 0x90, 0x42, 
0x10, 0xd0,
+               0x17, 0x68, 0x64, 0x00, 0x8e, 0x43, 0xad, 0x41, 0x1d, 0x40, 
0x06, 0x60,
+               0x1f, 0x43, 0xaf, 0x43, 0x17, 0x60, 0x0e, 0x43, 0x64, 0x00, 
0x06, 0x60,
+               0xf4, 0xd1, 0x06, 0x60, 0x8e, 0x43, 0x06, 0x60, 0x70, 0x47, 
0x64, 0x00,
+               0x8e, 0x43, 0xad, 0x41, 0x1d, 0x40, 0x06, 0x60, 0x1e, 0x43, 
0xae, 0x43,
+               0x16, 0x60, 0x0e, 0x43, 0x64, 0x00, 0x06, 0x60, 0xf4, 0xd1, 
0x06, 0x60,
+               0x8e, 0x43, 0x06, 0x60, 0x70, 0x47, 0x58, 0x46, 0x01, 0x30, 
0x61, 0x46,
+               0x00, 0xbe, 0xc0, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00
+       };
+
+       code = write ? msoftspi_write_code : msoftspi_read_code;
+       code_size = write ? sizeof(msoftspi_write_code) : 
sizeof(msoftspi_read_code);
+
+       /* this will overlay the last 9 words of msoftspi_*_code in target */
+       uint32_t port_buffer[] = {
+               h_to_le_32(msoftspi_info->ncs.addr), 
h_to_le_32(msoftspi_info->ncs.mask),
+               h_to_le_32(msoftspi_info->sclk.addr), 
h_to_le_32(msoftspi_info->sclk.mask),
+               h_to_le_32(msoftspi_info->miso.addr), 
h_to_le_32(msoftspi_info->miso.mask),
+               h_to_le_32(msoftspi_info->mosi.addr), 
h_to_le_32(msoftspi_info->mosi.mask),
+               h_to_le_32(msoftspi_info->bits_no)
+       };
+
+       /* memory buffer, we assume sectorsize to be a power of 2 times 
page_size */
+       page_size = msoftspi_info->dev.pagesize;
+       fifo_size = msoftspi_info->dev.sectorsize;
+       while (buffer_size = code_size + 2 * sizeof(uint32_t) + fifo_size,
+                       target_alloc_working_area_try(target, buffer_size, 
&write_algorithm) != ERROR_OK) {
+               fifo_size /= 2;
+               if (fifo_size < page_size) {
+                       /* we already allocated the reading/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 SPI %s",
+                               write ? "page writes" : "reads");
+                       return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+               }
+       };
+
+       /* prepare read/write code, excluding port_buffer */
+       retval = target_write_buffer(target, write_algorithm->address,
+               code_size - sizeof(port_buffer), code);
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* prepare port_buffer values */
+       retval = target_write_buffer(target, write_algorithm->address
+               + code_size - sizeof(port_buffer),
+               sizeof(port_buffer), (uint8_t *) port_buffer);
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* target buffer starts right after flash_write_code, i. e.
+        * wp and rp are implicitly included in buffer!!! */
+       fifo_start = write_algorithm->address + code_size + 2 * 
sizeof(uint32_t);
+
+       init_reg_param(&reg_params[0], "r0", 32, PARAM_IN_OUT); /* count (in), 
status (out) */
+       init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);    /* flash 
page_size */
+       init_reg_param(&reg_params[2], "r2", 32, PARAM_IN_OUT); /* offset into 
flash address */
+       init_reg_param(&reg_params[3], "r3", 32, PARAM_OUT);    /* 2/3/4-byte 
address mode */
+       init_reg_param(&reg_params[4], "r8", 32, PARAM_OUT);    /* fifo start */
+       init_reg_param(&reg_params[5], "r9", 32, PARAM_OUT);    /* fifo end + 1 
*/
+
+       buf_set_u32(reg_params[0].value, 0, 32, count);
+       buf_set_u32(reg_params[1].value, 0, 32,
+               write ? page_size : msoftspi_info->dev.sectorsize);
+       buf_set_u32(reg_params[2].value, 0, 32, offset);
+       buf_set_u32(reg_params[3].value, 0, 32, ADDR_BYTES);
+       buf_set_u32(reg_params[4].value, 0, 32, fifo_start);
+       buf_set_u32(reg_params[5].value, 0, 32, fifo_start + fifo_size);
+
+       armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
+       armv7m_info.core_mode = ARM_MODE_THREAD;
+
+       /* after breakpoint instruction (halfword) one nop (halfword) and
+        * port_buffer till end of code */
+       exit_point = write_algorithm->address + code_size
+               - sizeof(uint32_t) - sizeof(port_buffer);
+
+       if (write) {
+               retval = target_run_flash_async_algorithm(target, buffer, 
count, 1,
+                               0, NULL,
+                               6, reg_params,
+                               write_algorithm->address + code_size,
+                               fifo_size + 2 * sizeof(uint32_t),
+                               write_algorithm->address, exit_point,
+                               &armv7m_info);
+       } else {
+               retval = target_run_read_async_algorithm(target, buffer, count, 
1,
+                               0, NULL,
+                               6, reg_params,
+                               write_algorithm->address + code_size,
+                               fifo_size + 2 * sizeof(uint32_t),
+                               write_algorithm->address, exit_point,
+                               &armv7m_info);
+       }
+
+       remaining = buf_get_u32(reg_params[0].value, 0, 32);
+       if ((retval == ERROR_OK) && remaining)
+               retval = ERROR_FLASH_OPERATION_FAILED;
+       if (retval != ERROR_OK) {
+               offset = buf_get_u32(reg_params[2].value, 0, 32);
+               LOG_ERROR("flash %s failed at address 0x%" PRIx32 ", remaining 
0x%" PRIx32,
+                       write ? "write" : "read", offset, remaining);
+       }
+
+       destroy_reg_param(&reg_params[0]);
+       destroy_reg_param(&reg_params[1]);
+       destroy_reg_param(&reg_params[2]);
+       destroy_reg_param(&reg_params[3]);
+       destroy_reg_param(&reg_params[4]);
+       destroy_reg_param(&reg_params[5]);
+
+err:
+       target_free_working_area(target, write_algorithm);
+
+       return retval;
+}
+
+static int msoftspi_write(struct flash_bank *bank, const uint8_t *buffer,
+       uint32_t offset, uint32_t count)
+{
+       struct target *target = bank->target;
+       struct msoftspi_flash_bank *msoftspi_info = bank->driver_priv;
+       int sector;
+
+       LOG_DEBUG("%s: offset=0x%08" PRIx32 " count=0x%08" PRIx32,
+               __func__, offset, count);
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       if (offset + count > msoftspi_info->dev.size_in_bytes) {
+               LOG_WARNING("Write beyond end of flash. Extra data discarded.");
+               count = msoftspi_info->dev.size_in_bytes - offset;
+       }
+
+       /* check sector protection */
+       for (sector = 0; sector < bank->num_sectors; sector++) {
+               /* Start offset in or before this sector? */
+               /* End offset in or behind this sector? */
+               if ((offset < (bank->sectors[sector].offset + 
bank->sectors[sector].size))
+                       && ((offset + count - 1) >= 
bank->sectors[sector].offset)) {
+                       if (bank->sectors[sector].is_protected) {
+                               LOG_ERROR("Flash sector %d protected", sector);
+                               return ERROR_FAIL;
+                       } else
+                               bank->sectors[sector].is_erased = -1;
+               }
+       }
+
+       return msoftspi_read_write_block(bank, (uint8_t *) buffer, offset, 
count, 1);
+}
+
+static int msoftspi_read(struct flash_bank *bank, uint8_t *buffer,
+       uint32_t offset, uint32_t count)
+{
+       struct target *target = bank->target;
+       struct msoftspi_flash_bank *msoftspi_info = bank->driver_priv;
+
+       LOG_DEBUG("%s: offset=0x%08" PRIx32 " count=0x%08" PRIx32,
+               __func__, offset, count);
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       if (!(msoftspi_info->probed)) {
+               LOG_ERROR("Flash bank not probed");
+               return ERROR_FLASH_BANK_NOT_PROBED;
+       }
+
+       if (offset + count > msoftspi_info->dev.size_in_bytes) {
+               LOG_WARNING("Read beyond end of flash. Extra data to be 
ignored.");
+               count = msoftspi_info->dev.size_in_bytes - offset;
+       }
+
+       return msoftspi_read_write_block(bank, buffer, offset, count, 0);
+}
+
+/* Return ID of flash device */
+static int read_flash_id(struct flash_bank *bank, uint32_t *id)
+{
+       struct target *target = bank->target;
+       struct msoftspi_flash_bank *msoftspi_info = bank->driver_priv;
+       uint32_t port, data;
+       int k, retval, success;
+
+       success = ERROR_FAIL;
+
+       if ((target->state != TARGET_HALTED) && (target->state != 
TARGET_RESET)) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       /* clear SCLK */
+       retval = target_read_u32(target, msoftspi_info->sclk.addr, &port);
+       if (retval != ERROR_OK)
+               goto err;
+       port &= ~msoftspi_info->sclk.mask;
+       retval = target_write_u32(target, msoftspi_info->sclk.addr, port);
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* set NCS */
+       retval = target_read_u32(target, msoftspi_info->ncs.addr, &port);
+       if (retval != ERROR_OK)
+               goto err;
+       SET_PORT_BIT(port, ncs);
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* poll WIP */
+       retval = wait_till_ready(bank, MSOFTSPI_PROBE_TIMEOUT);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* clear NCS */
+       CLR_PORT_BIT(port, ncs);
+       retval = target_write_u32(target, msoftspi_info->ncs.addr, port);
+       if (retval != ERROR_OK)
+               goto err;
+
+       success = msoftspi_shift_out(bank, SPIFLASH_READ_ID);
+       if (success != ERROR_OK)
+               goto err;
+
+       /* set to input */
+
+       for (k = 0; k < 3; k++) {
+               success = msoftspi_shift_in(bank, &data);
+               if (success != ERROR_OK)
+                       goto err;
+       }
+
+err:
+       /* set NCS */
+       retval = target_read_u32(target, msoftspi_info->ncs.addr, &port);
+       SET_PORT_BIT(port, ncs);
+
+       /* set to output */
+
+       /* three bytes received, placed in bits 0 to 23, byte reversed */
+       *id = ((data & 0xFF) << 16) | (data & 0xFF00) | ((data & 0xFF0000) >> 
16);
+
+       if ((*id == 0x000000) || (*id == 0xFFFFFF)) {
+               LOG_INFO("No response from flash");
+               success = ERROR_TARGET_NOT_EXAMINED;
+       }
+
+       return success;
+}
+
+/* Read id from flash chip  */
+static int msoftspi_probe(struct flash_bank *bank)
+{
+       struct msoftspi_flash_bank *msoftspi_info = bank->driver_priv;
+       struct flash_sector *sectors;
+       const struct flash_device *p;
+       uint32_t id = 0;
+       int retval;
+
+       if (msoftspi_info->probed)
+               free(bank->sectors);
+       msoftspi_info->probed = 0;
+
+       /* read and decode flash ID */
+       retval = read_flash_id(bank, &id);
+       LOG_DEBUG("id 0x%06" PRIx32, id);
+       if (retval == ERROR_TARGET_NOT_EXAMINED) {
+               /* no id retrieved, so id must be set manually */
+               LOG_INFO("No id - set flash parameters manually");
+               return ERROR_OK;
+       }
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* identify flash */
+       msoftspi_info->dev.name = NULL;
+       for (p = flash_devices; id && p->name ; p++) {
+               if (p->device_id == id) {
+                       memcpy(&msoftspi_info->dev, p, 
sizeof(msoftspi_info->dev));
+                       LOG_INFO("flash \'%s\' id = 0x%06" PRIx32
+                                "\nflash size = %lukbytes",
+                                p->name, id, p->size_in_bytes>>10);
+                       break;
+               }
+       }
+
+       if (id && !p->name) {
+               LOG_ERROR("Unknown flash device id = 0x%06" PRIx32, id);
+               return ERROR_FAIL;
+       }
+
+       /* set correct size value */
+       bank->size = msoftspi_info->dev.size_in_bytes;
+
+       /* create and fill sectors array */
+       bank->num_sectors =
+               msoftspi_info->dev.size_in_bytes / 
msoftspi_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 * 
(msoftspi_info->dev.sectorsize);
+               sectors[sector].size = msoftspi_info->dev.sectorsize;
+               sectors[sector].is_erased = -1;
+               sectors[sector].is_protected = 0;
+       }
+
+       bank->sectors = sectors;
+       msoftspi_info->probed = 1;
+
+       return ERROR_OK;
+}
+
+static int msoftspi_auto_probe(struct flash_bank *bank)
+{
+       struct msoftspi_flash_bank *msoftspi_info = bank->driver_priv;
+
+       if (msoftspi_info->probed)
+               return ERROR_OK;
+       return msoftspi_probe(bank);
+}
+
+static int msoftspi_protect_check(struct flash_bank *bank)
+{
+       /* nothing to do. Protection is only handled in SW. */
+       return ERROR_OK;
+}
+
+static int get_msoftspi_info(struct flash_bank *bank, char *buf, int buf_size)
+{
+       struct msoftspi_flash_bank *msoftspi_info = bank->driver_priv;
+
+       if (!(msoftspi_info->probed)) {
+               snprintf(buf, buf_size, "\nmsoftspi flash bank not probed 
yet\n");
+               return ERROR_OK;
+       }
+
+       snprintf(buf, buf_size, "flash \'%s\' device id = 0x%06" PRIx32
+                       "\nflash size = %dkBytes\npage size = %d"
+                       ", mass_erase = 0x%02x, sector_erase = 0x%02x",
+                       msoftspi_info->dev.name, msoftspi_info->dev.device_id,
+                       bank->size>>10, msoftspi_info->dev.pagesize,
+                       msoftspi_info->dev.chip_erase_cmd, 
msoftspi_info->dev.erase_cmd);
+
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(msoftspi_handle_mass_erase_command)
+{
+       struct target *target = NULL;
+       struct flash_bank *bank;
+       struct msoftspi_flash_bank *msoftspi_info;
+       struct duration bench;
+       uint32_t port, data;
+       int retval, sector;
+
+       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;
+
+       msoftspi_info = bank->driver_priv;
+       target = bank->target;
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       if (!(msoftspi_info->probed)) {
+               LOG_ERROR("Flash bank not probed");
+               return ERROR_FLASH_BANK_NOT_PROBED;
+       }
+
+       if (msoftspi_info->dev.chip_erase_cmd == 0x00) {
+               LOG_ERROR("No mass erase available");
+               return ERROR_FAIL;
+       }
+
+       for (sector = 0; sector < bank->num_sectors; sector++) {
+               if (bank->sectors[sector].is_protected) {
+                       LOG_ERROR("Flash sector %d protected", sector);
+                       return ERROR_FAIL;
+               }
+       }
+
+       duration_start(&bench);
+
+       retval = msoftspi_write_enable(bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* Send Mass Erase command */
+       /* clear NCS */
+       retval = target_read_u32(target, msoftspi_info->ncs.addr, &port);
+       if (retval != ERROR_OK)
+               goto err;
+       CLR_PORT_BIT(port, ncs);
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* send command byte */
+       retval = msoftspi_shift_out(bank, msoftspi_info->dev.chip_erase_cmd);
+       if (retval != ERROR_OK)
+               goto err;
+
+err:
+       /* set NCS */
+       retval = target_read_u32(target, msoftspi_info->ncs.addr, &port);
+       SET_PORT_BIT(port, ncs);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* read flash status register */
+       retval = read_status_reg(bank, &data);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* check for command in progress for flash */
+       if ((data & SPIFLASH_WE_BIT) == 0) {
+               LOG_ERROR("Mass erase command not accepted by flash. 
Status=0x%08" PRIx32, data);
+               return ERROR_FAIL;
+       }
+
+       /* poll WIP for end of self timed Sector Erase cycle */
+       retval = wait_till_ready(bank, MSOFTSPI_MASS_ERASE_TIMEOUT);
+
+       duration_measure(&bench);
+       if (retval == ERROR_OK) {
+               /* set all sectors as erased */
+               for (sector = 0; sector < bank->num_sectors; sector++)
+                       bank->sectors[sector].is_erased = 1;
+
+               command_print(CMD_CTX, "msoftspi mass erase completed in"
+                       " %fs (%0.3f KiB/s)", duration_elapsed(&bench),
+                       duration_kbps(&bench, bank->size));
+       } else {
+               command_print(CMD_CTX, "msoftspi mass erase failed after %fs",
+                       duration_elapsed(&bench));
+       }
+
+       return retval;
+}
+
+static int log2u(unsigned long word)
+{
+       int result;
+
+       for (result = 0; (unsigned int) result < sizeof(unsigned long) * 8; 
result++)
+               if (word == (1UL<<result))
+                       return result;
+
+       return -1;
+}
+
+COMMAND_HANDLER(msoftspi_handle_setid)
+{
+       struct flash_bank *bank = NULL;
+       struct msoftspi_flash_bank *msoftspi_info = NULL;
+       struct flash_sector *sectors = NULL;
+       int retval;
+
+       LOG_DEBUG("%s", __func__);
+
+       if (CMD_ARGC < 2 || CMD_ARGC > 7)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
+       if (ERROR_OK != retval)
+               return retval;
+       msoftspi_info = bank->driver_priv;
+
+       /* invalidate all old info */
+       if (msoftspi_info->probed)
+               free(bank->sectors);
+       msoftspi_info->probed = 0;
+       msoftspi_info->dev.name = NULL;
+       msoftspi_info->dev.device_id = 0;
+
+       strncpy(msoftspi_info->devname, CMD_ARGV[1], 
sizeof(msoftspi_info->devname) - 1);
+       msoftspi_info->devname[sizeof(msoftspi_info->devname) - 1] = '\0';
+
+       COMMAND_PARSE_NUMBER(u64, CMD_ARGV[2], 
msoftspi_info->dev.size_in_bytes);
+       if (log2u(msoftspi_info->dev.size_in_bytes) < 8) {
+               command_print(CMD_CTX, "msoftspi: device size must be 2^n with 
n >= 8");
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[3], msoftspi_info->dev.pagesize);
+       if ((log2u(msoftspi_info->dev.pagesize) > 
log2u(msoftspi_info->dev.size_in_bytes)) ||
+               (log2u(msoftspi_info->dev.pagesize) < 0)) {
+               command_print(CMD_CTX, "msoftspi: page size must be 2^n and <= 
device size");
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       if (CMD_ARGC > 4)
+               COMMAND_PARSE_NUMBER(u8, CMD_ARGV[4], 
msoftspi_info->dev.chip_erase_cmd);
+       else
+               msoftspi_info->dev.chip_erase_cmd = 0x00;
+
+       if (CMD_ARGC > 5) {
+               COMMAND_PARSE_NUMBER(u64, CMD_ARGV[5], 
msoftspi_info->dev.sectorsize);
+               if ((log2u(msoftspi_info->dev.sectorsize) > 
log2u(msoftspi_info->dev.size_in_bytes)) ||
+                       (log2u(msoftspi_info->dev.sectorsize) < 0)) {
+                       command_print(CMD_CTX, "msoftspi: sector size must be 
2^n and <= device size");
+                       return ERROR_COMMAND_SYNTAX_ERROR;
+               }
+
+               if (CMD_ARGC > 6)
+                       COMMAND_PARSE_NUMBER(u8, CMD_ARGV[6], 
msoftspi_info->dev.erase_cmd);
+               else
+                       return ERROR_COMMAND_SYNTAX_ERROR;
+       } else {
+               /* no sector size / sector erase cmd given, treat whole bank as 
a single sector */
+               msoftspi_info->dev.erase_cmd = 0x00;
+               msoftspi_info->dev.sectorsize = 
msoftspi_info->dev.size_in_bytes;
+       }
+
+       /* set correct size value */
+       bank->size = msoftspi_info->dev.size_in_bytes;
+
+       /* create and fill sectors array */
+       bank->num_sectors =
+               msoftspi_info->dev.size_in_bytes / 
msoftspi_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 * 
(msoftspi_info->dev.sectorsize);
+               sectors[sector].size = msoftspi_info->dev.sectorsize;
+               sectors[sector].is_erased = -1;
+               sectors[sector].is_protected = 0;
+       }
+
+       bank->sectors = sectors;
+       msoftspi_info->dev.name = msoftspi_info->devname;
+       msoftspi_info->probed = 1;
+
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(msoftspi_handle_spicmd)
+{
+       struct target *target = NULL;
+       struct flash_bank *bank;
+       struct msoftspi_flash_bank *msoftspi_info;
+       uint32_t port, data;
+       uint8_t num_bytes, cmd_byte;
+       const int max = 16;
+       unsigned int count;
+       char temp[4], output[(1 + max + 256) * 3 + 4];
+       int retval;
+
+       LOG_DEBUG("%s", __func__);
+
+       if (CMD_ARGC < 3 || CMD_ARGC > max + 3)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
+       if (ERROR_OK != retval)
+               return retval;
+
+       msoftspi_info = bank->driver_priv;
+       target = bank->target;
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       COMMAND_PARSE_NUMBER(u8, CMD_ARGV[1], num_bytes);
+       COMMAND_PARSE_NUMBER(u8, CMD_ARGV[2], cmd_byte);
+
+       /* clear NCS */
+       retval = target_read_u32(target, msoftspi_info->ncs.addr, &port);
+       if (retval != ERROR_OK)
+               goto err;
+       CLR_PORT_BIT(port, ncs);
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* send command byte */
+       snprintf(output, sizeof(output), "spicmd: %02x ", cmd_byte & 0xFF);
+       retval = msoftspi_shift_out(bank, cmd_byte);
+       if (retval != ERROR_OK)
+               goto err;
+
+       /* send additional bytes */
+       for (count = 3; count < CMD_ARGC; count++) {
+               COMMAND_PARSE_NUMBER(u8, CMD_ARGV[count], cmd_byte);
+               snprintf(temp, sizeof(temp), "%02x ", cmd_byte & 0xFF);
+               retval = msoftspi_shift_out(bank, cmd_byte);
+               if (retval != ERROR_OK)
+                       goto err;
+               strncat(output, temp, sizeof(output));
+       }
+
+       /* set to input */
+       strncat(output, "-> ", sizeof(output));
+
+       for ( ; num_bytes > 0; num_bytes--)     {
+               retval = msoftspi_shift_in(bank, &data);
+               if (retval != ERROR_OK)
+                       goto err;
+               snprintf(temp, sizeof(temp), "%02x ", data & 0xFF);
+               strncat(output, temp, sizeof(output));
+       }
+       command_print(CMD_CTX, "%s", output);
+
+err:
+       /* set NCS */
+       retval = target_read_u32(target, msoftspi_info->ncs.addr, &port);
+       SET_PORT_BIT(port, ncs);
+
+       /* set to output */
+
+       return retval;
+}
+
+static const struct command_registration msoftspi_exec_command_handlers[] = {
+       {
+               .name = "mass_erase",
+               .handler = msoftspi_handle_mass_erase_command,
+               .mode = COMMAND_EXEC,
+               .usage = "bank_id",
+               .help = "Mass erase entire flash device.",
+       },
+       {
+               .name = "setid",
+               .handler = msoftspi_handle_setid,
+               .mode = COMMAND_EXEC,
+               .usage = "bank_id flash_id",
+               .help = "Set id of flash chip to flash_id (uint24)",
+       },
+       {
+               .name = "spicmd",
+               .handler = msoftspi_handle_spicmd,
+               .mode = COMMAND_EXEC,
+               .usage = "bank_id num_resp cmd_byte ...",
+               .help = "Send low-level command cmd_byte and following bytes, 
read num_bytes.",
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration msoftspi_command_handlers[] = {
+       {
+               .name = "msoftspi",
+               .mode = COMMAND_ANY,
+               .help = "msoftspi flash command group",
+               .usage = "",
+               .chain = msoftspi_exec_command_handlers,
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+struct flash_driver msoftspi_flash = {
+       .name = "msoftspi",
+       .commands = msoftspi_command_handlers,
+       .flash_bank_command = msoftspi_flash_bank_command,
+       .erase = msoftspi_erase,
+       .protect = msoftspi_protect,
+       .write = msoftspi_write,
+       .read = msoftspi_read,
+       .probe = msoftspi_probe,
+       .auto_probe = msoftspi_auto_probe,
+       .erase_check = msoftspi_blank_check,
+       .protect_check = msoftspi_protect_check,
+       .info = get_msoftspi_info,
+};
diff --git a/src/flash/nor/spi.c b/src/flash/nor/spi.c
index fe1be86..62f1868 100644
--- a/src/flash/nor/spi.c
+++ b/src/flash/nor/spi.c
@@ -54,6 +54,7 @@ const struct flash_device flash_devices[] = {
        FLASH_ID("sp s25fl164k",    0xd8, 0xc7, 0x00174001, 0x100, 0x10000, 
0x800000),
        FLASH_ID("sp s25fl128",     0xd8, 0xc7, 0x00182001, 0x100, 0x10000, 
0x1000000),
        FLASH_ID("sp s25fl256",     0xd8, 0xc7, 0x00190201, 0x100, 0x10000, 
0x2000000),
+       FLASH_ID("sp s25fl512s",    0xd8, 0xc7, 0x00200201, 0x200, 0x40000, 
0x4000000),
        FLASH_ID("atmel 25f512",    0x52, 0xc7, 0x0065001f, 0x80,  0x8000,  
0x10000),
        FLASH_ID("atmel 25f1024",   0x52, 0x62, 0x0060001f, 0x100, 0x8000,  
0x20000),
        FLASH_ID("atmel 25f2048",   0x52, 0x62, 0x0063001f, 0x100, 0x10000, 
0x40000),
@@ -79,9 +80,10 @@ const struct flash_device flash_devices[] = {
        FLASH_ID("win w25q32dw",    0xd8, 0xc7, 0x001660ef, 0x100, 0x10000, 
0x400000),
        FLASH_ID("win w25q64cv",    0xd8, 0xc7, 0x001740ef, 0x100, 0x10000, 
0x800000),
        FLASH_ID("win w25q128fv",   0xd8, 0xc7, 0x001840ef, 0x100, 0x10000, 
0x1000000),
+       FLASH_ID("win w25q256fv",   0xd8, 0xc7, 0x001940ef, 0x100, 0x10000, 
0x2000000),
        FLASH_ID("gd gd25q20",      0x20, 0xc7, 0x00c84012, 0x100, 0x1000,  
0x80000),
        FLASH_ID("gd gd25q16c",     0xd8, 0xc7, 0x001540c8, 0x100, 0x10000, 
0x200000),
        FLASH_ID("gd gd25q32c",     0xd8, 0xc7, 0x001640c8, 0x100, 0x10000, 
0x400000),
        FLASH_ID("gd gd25q128c",    0xd8, 0xc7, 0x001840c8, 0x100, 0x10000, 
0x1000000),
-       FLASH_ID(NULL,              0,    0,       0,          0,     0,       
0)
+       FLASH_ID(NULL,              0,    0,    0,          0,     0,       0)
 };
diff --git a/src/flash/nor/spi.h b/src/flash/nor/spi.h
index a184998..b3d8778 100644
--- a/src/flash/nor/spi.h
+++ b/src/flash/nor/spi.h
@@ -22,6 +22,8 @@
 #ifndef OPENOCD_FLASH_NOR_SPI_H
 #define OPENOCD_FLASH_NOR_SPI_H
 
+#ifndef __ASSEMBLER__
+
 /* data structure to maintain flash ids from different vendors */
 struct flash_device {
        char *name;
@@ -46,9 +48,13 @@ struct flash_device {
 
 extern const struct flash_device flash_devices[];
 
+#endif
+
 /* fields in SPI flash status register */
-#define SPIFLASH_BSY_BIT               0x00000001 /* WIP Bit of SPI SR on SMI 
SR */
-#define SPIFLASH_WE_BIT                        0x00000002 /* WEL Bit of SPI SR 
on SMI SR */
+#define        SPIFLASH_BSY                    0
+#define SPIFLASH_BSY_BIT               (1 << SPIFLASH_BSY) /* WIP Bit of SPI 
SR on SMI SR */
+#define        SPIFLASH_WE                             1
+#define SPIFLASH_WE_BIT                        (1 << SPIFLASH_WE)      /* WEL 
Bit of SPI SR on SMI SR */
 
 /* SPI Flash Commands */
 #define SPIFLASH_READ_ID               0x9F /* Read Flash Identification */
diff --git a/src/target/target.c b/src/target/target.c
index e04ecc4..9501f1a 100644
--- a/src/target/target.c
+++ b/src/target/target.c
@@ -1038,6 +1038,148 @@ int target_run_flash_async_algorithm(struct target 
*target,
        return retval;
 }
 
+int target_run_read_async_algorithm(struct target *target,
+               uint8_t *buffer, uint32_t count, int block_size,
+               int num_mem_params, struct mem_param *mem_params,
+               int num_reg_params, struct reg_param *reg_params,
+               uint32_t buffer_start, uint32_t buffer_size,
+               uint32_t entry_point, uint32_t exit_point, void *arch_info)
+{
+       int retval;
+       int timeout = 0;
+
+       const uint8_t *buffer_orig = buffer;
+
+       /* Set up working area. First word is write pointer, second word is 
read pointer,
+        * rest is fifo data area. */
+       uint32_t wp_addr = buffer_start;
+       uint32_t rp_addr = buffer_start + 4;
+       uint32_t fifo_start_addr = buffer_start + 8;
+       uint32_t fifo_end_addr = buffer_start + buffer_size;
+
+       uint32_t wp = fifo_start_addr;
+       uint32_t rp = fifo_start_addr;
+
+       /* validate block_size is 2^n */
+       assert(!block_size || !(block_size & (block_size - 1)));
+
+       retval = target_write_u32(target, wp_addr, wp);
+       if (retval != ERROR_OK)
+               return retval;
+       retval = target_write_u32(target, rp_addr, rp);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* Start up algorithm on target */
+       retval = target_start_algorithm(target, num_mem_params, mem_params,
+                       num_reg_params, reg_params,
+                       entry_point,
+                       exit_point,
+                       arch_info);
+
+       if (retval != ERROR_OK) {
+               LOG_ERROR("error starting target flash read algorithm");
+               return retval;
+       }
+
+       while (count > 0) {
+               retval = target_read_u32(target, wp_addr, &wp);
+               if (retval != ERROR_OK) {
+                       LOG_ERROR("failed to get write pointer");
+                       break;
+               }
+
+               LOG_DEBUG("offs 0x%zx count 0x%" PRIx32 " wp 0x%" PRIx32 " rp 
0x%" PRIx32,
+                       (size_t) (buffer - buffer_orig), count, wp, rp);
+
+               if (wp == 0) {
+                       LOG_ERROR("flash read algorithm aborted by target");
+                       retval = ERROR_FLASH_OPERATION_FAILED;
+                       break;
+               }
+
+               if (((wp - fifo_start_addr) & (block_size - 1)) || wp < 
fifo_start_addr || wp >= fifo_end_addr) {
+                       LOG_ERROR("corrupted fifo write pointer 0x%" PRIx32, 
wp);
+                       break;
+               }
+
+               /* Count the number of bytes available in the fifo without
+                * crossing the wrap around. */
+               uint32_t thisrun_bytes;
+               if (wp > rp)
+                       thisrun_bytes = wp - rp;
+               else
+                       thisrun_bytes = fifo_end_addr - rp;
+
+               if (thisrun_bytes == 0) {
+                       /* Throttle polling a bit if transfer is (much) faster 
than flash
+                        * reading. The exact delay shouldn't matter as long as 
it's
+                        * less than buffer size / flash speed. This is very 
unlikely to
+                        * run when using high latency connections such as USB. 
*/
+                       alive_sleep(10);
+
+                       /* to stop an infinite loop on some targets check and 
increment a timeout
+                        * this issue was observed on a stellaris using the new 
ICDI interface */
+                       if (timeout++ >= 500) {
+                               LOG_ERROR("timeout waiting for algorithm, a 
target reset is recommended");
+                               return ERROR_FLASH_OPERATION_FAILED;
+                       }
+                       continue;
+               }
+
+               /* reset our timeout */
+               timeout = 0;
+
+               /* Limit to the amount of data we actually want to read */
+               if (thisrun_bytes > count * block_size)
+                       thisrun_bytes = count * block_size;
+
+               /* Read data from fifo */
+               retval = target_read_buffer(target, rp, thisrun_bytes, buffer);
+               if (retval != ERROR_OK)
+                       break;
+
+               /* Update counters and wrap write pointer */
+               buffer += thisrun_bytes;
+               count -= thisrun_bytes / block_size;
+               rp += thisrun_bytes;
+               if (rp >= fifo_end_addr)
+                       rp = fifo_start_addr;
+
+               /* Store updated write pointer to target */
+               retval = target_write_u32(target, rp_addr, rp);
+               if (retval != ERROR_OK)
+                       break;
+       }
+
+       if (retval != ERROR_OK) {
+               /* abort flash write algorithm on target */
+               target_write_u32(target, rp_addr, 0);
+       }
+
+       int retval2 = target_wait_algorithm(target, num_mem_params, mem_params,
+                       num_reg_params, reg_params,
+                       exit_point,
+                       10000,
+                       arch_info);
+
+       if (retval2 != ERROR_OK) {
+               LOG_ERROR("error waiting for target flash write algorithm");
+               retval = retval2;
+       }
+
+       if (retval == ERROR_OK) {
+               /* check if algorithm set wp = 0 after fifo writer loop 
finished */
+               retval = target_read_u32(target, wp_addr, &wp);
+               if (retval == ERROR_OK && wp == 0) {
+                       LOG_ERROR("flash read algorithm aborted by target");
+                       retval = ERROR_FLASH_OPERATION_FAILED;
+               }
+       }
+
+       return retval;
+}
+
 int target_read_memory(struct target *target,
                target_addr_t address, uint32_t size, uint32_t count, uint8_t 
*buffer)
 {
diff --git a/src/target/target.h b/src/target/target.h
index 53f9e26..98d29f7 100644
--- a/src/target/target.h
+++ b/src/target/target.h
@@ -526,6 +526,18 @@ int target_run_flash_async_algorithm(struct target *target,
                void *arch_info);
 
 /**
+ * This routine is a wrapper for asynchronous algorithms.
+ *
+ */
+int target_run_read_async_algorithm(struct target *target,
+               uint8_t *buffer, uint32_t count, int block_size,
+               int num_mem_params, struct mem_param *mem_params,
+               int num_reg_params, struct reg_param *reg_params,
+               uint32_t buffer_start, uint32_t buffer_size,
+               uint32_t entry_point, uint32_t exit_point,
+               void *arch_info);
+
+/**
  * Read @a count items of @a size bytes from the memory of @a target at
  * the @a address given.
  *
diff --git a/tcl/board/msoftspi_examples.cfg b/tcl/board/msoftspi_examples.cfg
new file mode 100644
index 0000000..83077b4
--- /dev/null
+++ b/tcl/board/msoftspi_examples.cfg
@@ -0,0 +1,119 @@
+--------------------------------------------------------------------------------
+for STM32F746G-disco:
+--------------------------------------------------------------------------------
+
+set _MSOFTSPINAME $_CHIPNAME.msoftspi
+flash bank $_MSOFTSPINAME msoftspi 0x90000000 0 0 0 $_TARGETNAME 0x40020414 6 
0x40020414 2 0x40020C10 12 0x40020C14 11
+
+$_TARGETNAME configure -event reset-init {
+       mmw 0x40023830 0x000007FF 0             ;# RCC_AHB1ENR |= GPIOA-GPIOK 
(enable clocks)
+
+       mww 0x40023C00 0x00000006               ;# 6 WS for 192 MHz HCLK
+       sleep 1
+       mww 0x40023804 0x24003008               ;# 192MHz: PLLM=8, PLLN=192, 
PLLP=2
+       mww 0x40023808 0x00009400               ;# APB1: /4, APB2: /2
+       mmw 0x40023800 0x01000000 0x00000000    ;# PLL on
+       sleep 1
+       mmw 0x40023808 0x00000002 0x00000000    ;# switch to PLL
+
+       adapter_khz 4000
+
+       # PB2: CLK, PB6: BK1_nCS, PD13: BK1_IO3, PE2: BK1_IO2, PD12: BK1_IO1 
(MISO), PD11: BK1_IO0 (MOSI)
+       mmw 0x40020400 0x00001010 0x00002020    ;# PB6, PB2 output
+       mmw 0x40020404 0x00000000 0x00000044    ;# PB6, PB2 push-pull
+       mmw 0x40020408 0x00003030 0x00000000    ;# very high speed
+       mmw 0x4002040C 0x00000000 0x00003030    ;# PB6, PB2 no pull-up
+
+       mmw 0x40020C00 0x00400000 0x03800000    ;# PD13 input, PD12 input, PD11 
output
+       mmw 0x40020C04 0x00000000 0x08000000    ;# PD11 push-pull
+       mmw 0x40020C08 0x03C00000 0x00000000    ;# PD12, PD11 very high speed
+       mmw 0x40020C0C 0x05000000 0x0A000000    ;# PD13, PD12 pull-up, PD11 no 
pull-up
+
+       mmw 0x40021000 0x00000000 0x00000300    ;# PE2 input
+       mmw 0x40021004 0x00000000 0x00000000    ;#
+       mmw 0x40021008 0x00000000 0x00000000    ;# very high speed
+       mmw 0x4002100C 0x05000000 0x0A000000    ;# PE2 pull-up
+}
+
+--------------------------------------------------------------------------------
+for STM32F746G-disco:
+--------------------------------------------------------------------------------
+
+set _MSOFTSPINAME $_CHIPNAME.msoftspi
+flash bank $_MSOFTSPINAME msoftspi 0x90000000 0 0 0 $_TARGETNAME 0x40020414 6 
0x40020414 2 0x40020810 10 0x40020814 9
+
+$_TARGETNAME configure -event reset-init {
+       mmw 0x40023830 0x000007FF 0     ;# RCC_AHB1ENR |= GPIOA-GPIOK (enable 
clocks)
+       mmw 0x40023838 0x00000002 0     ;# RCC_AHB3ENR |= QSPIEN (enable clock)
+       sleep 1
+
+       # if demo firmware is installed, it will switch the SPI flash to QPI 
mode,
+       # which will remain active until power-cycling
+       # hence QPI mode must be turned off ...
+
+       # PB2: CLK, PB6: BK1_nCS, PD13: BK1_IO3, PE2: BK1_IO2, PC10: BK1_IO1, 
PC9: BK1_IO0
+       mmw 0x40020400 0x00002020 0x00001010    ;# PB6, PB2 alternate
+       mmw 0x40020408 0x00003030 0x00000000    ;# high speed
+       mmw 0x40020420 0x0A000900 0x05000600    ;# AF10, AF9
+
+       mmw 0x40020800 0x00280000 0x00140000    ;# PC10, PC9 alternate
+       mmw 0x40020808 0x003C0000 0x00000000    ;# high speed
+       mmw 0x40020824 0x00000990 0x00000660    ;# AF9, AF9
+
+       mmw 0x40020C00 0x08000000 0x04000000    ;# PD13 alternate
+       mmw 0x40020C08 0x0C000000 0x00000000    ;# high speed
+       mmw 0x40020C24 0x00900000 0x00600000    ;# AF9
+
+       mmw 0x40021000 0x00000020 0x00000010    ;# PE2 alternate
+       mmw 0x40021008 0x00000030 0x00000000    ;# high speed
+       mmw 0x40021020 0x00000900 0x00000600    ;# AF9
+
+       mww 0xA0001000 0x01500318               ;# QUADSPI_CR: PRESCALER=1, 
APMS=1, FTHRES=3, SSHIFT=1, TCEN=1
+       mww 0xA0001004 0x00190100               ;# QUADSPI_DCR: FSIZE=0x19, 
CSHT=0x01, CKMODE=0
+       mmw 0xA0001000 0x00000001 0             ;# QUADSPI_CR: EN=1
+
+       # 1-line spi mode
+       mww 0xA0001014 0x000003F5               ;# QUADSPI_CCR: FMODE=0x0, 
DMODE=0x0, DCYC=0x0, ADSIZE=0x0, ADMODE=0x0, IMODE=0x3, INSTR=RS
+       sleep 1
+
+       # 4-byte address mode
+       mww 0xA0001014 0x000001B7               ;# QUADSPI_CCR: FMODE=0x0, 
DMODE=0x0, DCYC=0x0, ADSIZE=0x0, ADMODE=0x0, IMODE=0x1, INSTR=EN
+       sleep 1
+
+       # memory-mapped read mode with 4-byte addresses
+       mww 0xA0001014 0x0D003503               ;# QUADSPI_CCR: FMODE=0x3, 
DMODE=0x1, DCYC=0x0, ADSIZE=0x3, ADMODE=0x1, IMODE=0x1, INSTR=RE
+
+
+       mww 0x40023C00 0x00000006               ;# 6 WS for 192 MHz HCLK
+       sleep 1
+       mww 0x40023804 0x24003008               ;# 192MHz: PLLM=8, PLLN=192, 
PLLP=2
+       mww 0x40023808 0x00009400               ;# APB1: /4, APB2: /2
+       mmw 0x40023800 0x01000000 0x00000000    ;# PLL on
+       sleep 1
+       mmw 0x40023808 0x00000002 0x00000000    ;# switch to PLL
+
+       # PB2: CLK, PB6: BK1_nCS, PD13: BK1_IO3, PE2: BK1_IO2, PC10: BK1_IO1 
(MISO), PC9: BK1_IO0 (MOSI)
+       mmw 0x40020400 0x00001010 0x00002020    ;# PB6, PB2 output
+       mmw 0x40020404 0x00000000 0x00000044    ;# PB6, PB2 push-pull
+       mmw 0x40020408 0x00003030 0x00000000    ;# very high speed
+       mmw 0x4002040C 0x00000000 0x00003030    ;# PB6, PB2 no pull-up
+
+       mmw 0x40020800 0x00040000 0x00380000    ;# PC10 input, PC9 output
+       mmw 0x40020804 0x00000000 0x00000200    ;# PC9 push-pull
+       mmw 0x40020808 0x000C0000 0x00000000    ;# very high speed
+       mmw 0x4002080C 0x00100000 0x002C0000    ;# PC10 pull-up, PC9 no-pullup
+
+       mmw 0x40020C00 0x00000000 0x0C000000    ;# PD13 input
+       mmw 0x40020C04 0x00000000 0x00000000    ;#
+       mmw 0x40020C08 0x00000000 0x00000000    ;# very high speed
+       mmw 0x40020C0C 0x04000000 0x08000000    ;# PD13 pull-up
+
+       mmw 0x40021000 0x00000000 0x00000030    ;# PE2 input
+       mmw 0x40021004 0x00000000 0x00000000    ;#
+       mmw 0x40021008 0x00000000 0x00000000    ;# very high speed
+       mmw 0x4002100C 0x00000010 0x00000020    ;# PE2 pull-up
+
+       sleep 1
+       msoftspi spicmd 1 0 0xB7                ;# EN4B: switch to 4-byte 
address mode
+       msoftspi spicmd 1 1 0x15                ;# RDCR: read configuration 
register
+}

-- 

------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
OpenOCD-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/openocd-devel

Reply via email to