diff options
author | Andreas Bolsch <hyphen0break@gmail.com> | 2016-12-21 10:35:58 +0100 |
---|---|---|
committer | Tomas Vanek <vanekt@fbl.cz> | 2020-11-08 22:46:00 +0000 |
commit | e44539d66c8929679321704768125df9ba7d5f67 (patch) | |
tree | 245a9cc6b5fd9b734ae47bcacf568f8fb33e61c2 /contrib | |
parent | 475f42051e13d64bc4d1960306ad1d2ea3c7962a (diff) | |
download | riscv-openocd-e44539d66c8929679321704768125df9ba7d5f67.zip riscv-openocd-e44539d66c8929679321704768125df9ba7d5f67.tar.gz riscv-openocd-e44539d66c8929679321704768125df9ba7d5f67.tar.bz2 |
Flash, FRAM and EEPROM driver for STM32 QUAD-/OCTOSPI interface
- write speed up to 150 kByte/s on STM32F469I-disco (due to
SWD clock and USB connection), up to 1 MByte/s on Nucleo-F767ZI
with external STLink-V3 or Nucleo-G474RE with two W25Q256FV in
dual 4-line mode or STM32H73BI-Disco in octal mode
- tested with STM32L476G-disco (64MBit flash, 3-byte addr),
STM32F412G-Disco, STM32F469I-Disco, STM32F746G-Disco, and
STM32L476G-Disco (all 128Mbit flash, 3-byte addr),
STM32F723E-Disco, STM32F769I-Disco (512Mbit flash, 4-byte addr)
STM32L4R9I-Disco, STM32L4P5G-Disco (512MBit octo-flash, DTR, 4-byte addr)
STM32H745I-Disco, STM32H747I-Disco (two 512MBit flash, 4-byte addr)
STM32H73BI-Disco, STM32H735G-Disco (512MBit octo-flash, DTR, 4-byte addr)
- suitable cfg for Discovery boards included
- limited parsing of SFDP data if flash device not hardcoded
(tested only in single/quad mode as most devices either don't
support SFDP at all or have empty(!) SFDP memory)
- 'set' command for auto detection override (e. g. for EEPROMs)
- 'cmd' command for arbitrary SPI commands (reconfiguration, testing etc.)
- makefile for creation of binary loader files
- tcl/board/stm32f469discovery.cfg superseded by stm32f469i-disco.cfg
- tcl/board/stm32f7discovery.cfg removed as name is ambiguous
(superseded by stm32f746g-disco.cfg vs. stm32f769i-disco.cfg)
- dual 4-line mode tested on Nucleo-F767ZI, Nucleo-H743ZI and Nucleo-H7A3ZI-Q
with two W25Q256FV, and on Nucleo-L496ZP-P and Nucleo-L4R5ZI
with two W25Q128FV, sample cfg files included and on STM32H745I-Disco,
STM32H747I-Disco, STM32H750B-Disco
- read/verify/erase_check uses indirect read mode to work around silicon bug in
H7, L4+ and MP1 memory mapped mode (last bytes not readable, accessing last
bytes causes debug interface to hang)
- octospi supported only in single/dual 1-line, 2-line, 4-line
and single 8-line modes, (not in hyper flash mode)
Requirements:
GPIOs must be initialized appropriately, and SPI flash chip be configured
appropriately (1-line ..., QPI, 4-byte addresses ...). This is board/chip
specific, cf. included cfg files. The driver infers most parameters from
current setting in CR, CCR, ... registers.
Change-Id: I54858fbbe8758c3a5fe58812e93f5f39514704f8
Signed-off-by: Andreas Bolsch <hyphen0break@gmail.com>
Reviewed-on: http://openocd.zylin.com/4321
Tested-by: jenkins
Reviewed-by: Tarek BOCHKATI <tarek.bouchkati@gmail.com>
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-by: Christopher Head <chead@zaber.com>
Diffstat (limited to 'contrib')
18 files changed, 1910 insertions, 0 deletions
diff --git a/contrib/loaders/flash/stmqspi/Makefile b/contrib/loaders/flash/stmqspi/Makefile new file mode 100644 index 0000000..810c7e8 --- /dev/null +++ b/contrib/loaders/flash/stmqspi/Makefile @@ -0,0 +1,34 @@ +BIN2C = ../../../../src/helper/bin2char.sh + +SRCS=stmqspi_erase_check.S stmqspi_crc32.S stmqspi_read.S stmqspi_write.S \ + stmoctospi_erase_check.S stmoctospi_crc32.S stmoctospi_read.S stmoctospi_write.S +OBJS=$(patsubst %.S,%.inc,$(SRCS)) + +CROSS_COMPILE ?= arm-none-eabi- + +CC=$(CROSS_COMPILE)gcc +OBJCOPY=$(CROSS_COMPILE)objcopy +OBJDUMP=$(CROSS_COMPILE)objdump +LD=$(CROSS_COMPILE)ld + +all: $(OBJS) + +%.o: %.S Makefile + $(CC) -Wall -Werror -Wa,-adhlmn -o $@ -c $< > $(@:.o=.lst) + @enscript -Easm -T 4 -G -o - $(@:.o=.lst) | ps2pdf - $(@:.o=.pdf) || true + +%.elf: %.o + $(LD) -s -defsym=_start=0 -o $@ $< + +%.bin: %.elf + $(OBJCOPY) -S -O binary $< $@ + +%.inc: %.bin + $(BIN2C) < $< > $@ + +clean: + -rm -f *.o *.elf *.lst *.pdf *.bin *.inc + +.PHONY: all clean + +.INTERMEDIATE: $(patsubst %.S,%.o,$(SRCS)) $(patsubst %.S,%.elf,$(SRCS)) $(patsubst %.S,%.bin,$(SRCS)) diff --git a/contrib/loaders/flash/stmqspi/gpio_conf_stm32.pl b/contrib/loaders/flash/stmqspi/gpio_conf_stm32.pl new file mode 100755 index 0000000..b753864 --- /dev/null +++ b/contrib/loaders/flash/stmqspi/gpio_conf_stm32.pl @@ -0,0 +1,679 @@ +#!/usr/bin/perl +# +# Helper for generating GPIO setup for STM32F0, F4, F7, H7, L0, L1, L4, L4+ +# and F1 (for 'stmqspi' and 'cmspi' drivers). +# +# Each pin is configured by "PortAndBit:Conf:Speed" +# 'PortAndBit' specifies Port and bit number +# 'Conf' is one of 'AFx' (alternate), 'P' (output), 'IN' (input), +# (each optionally by 'P' (push-pull) or 'O' (open-drain)), +# (all optionally followed by 'UP' (pull-up), or 'DO' (pull-down)) +# 'Speed' is one of 'L' (low), 'M' (medium), 'H' (high), 'V' (very high) +# +# Port configuration can be given on command line as a single string (pins separated by commas) +# or via CubeMX generated file. The latter must consist of the quadspi.c / octospi.c and the +# corresponding header. The precise spelling in these files doesn't seem to be consistent, though ... +# +# Pins have to be ordered this way: +# - I2C: SDA, SCL +# - SPI (1 line): NCS, CLK, IO1/MISO, IO0/MOSI +# - DPI (2 lines): NCS, CLK, IO1/MISO, IO0/MOSI +# - QPI (4 lines): NCS, CLK, IO3/NHOLD, IO2/NWP, IO1/MISO, IO0/MOSI +# For dual flash: BK_1 first, then BK_2. If single NCS for both, omit NCS in BK_2 +# For octal flash: NCS, CLK, DQS, IO7 down to IO0 + +use strict; +use Getopt::Std; + +my $GPIO_BASE; +my $Conf; +my $STM32F1 = 0; + +# "Blue-Pill stm32f103cbt6 board w/ cmspi +#$STM32F1 = 1; +#$GPIO_BASE = 0x40010800; +#$Conf = "PB12:PP:M, PB13:PP:V, PB14:INUP:V, PB15:INUP:V"; +#$Conf = "PB12:PP:M, PB13:PP:V, PB14:INUP:V, PB01:INUP:V"; + +#$STM32F1 = 1; +#$GPIO_BASE = 0x40010800; +#$Conf = "PB07:INUP:V, PB06:INUP:V"; + +# mini-stm32f030f4p6 board w/ cmspi +#$GPIO_BASE = 0x48000000; +#$Conf = "PB01:PP:V, PA05:PP:V, PA06:INUP:V, PA07:INUP:V"; + +# stm32f407vet6 board w/ cmspi +#$GPIO_BASE = 0x40020000; +#$Conf = "PB00:PP:M, PB03:PP:V, PB04:INUP:V, PB05:INUP:V"; + +# stm32f412g-disco quad +#$GPIO_BASE = 0x40020000; +#$Conf = "PB02:AF09:V, PF09:AF10:V, PF08:AF10:V, PF07:AF09:V, PF06:AF09:V, PG06:AF10:V"; + +# stm32f413h-disco +#$GPIO_BASE = 0x40020000; +#$Conf = "PB02:AF09:V, PD13:AF09:V, PE02:AF09:V, PF09:AF10:V, PF08:AF10:V, PG06:AF10:V"; + +# stm32f469i-disco quad +#$GPIO_BASE = 0x40020000; +#$Conf = "PB06:AF10:V, PF10:AF09:V, PF09:AF10:V, PF08:AF10:V, PF07:AF09:V, PF06:AF09:V"; +# w/ cmspi +#$Conf = "PB06:PP:M, PF10:PP:V, PF06:INUP:V, PF07:INUP:V, PF09:INUP:V, PF08:INUP:V"; + +# stm32f723e-disco quad +#$GPIO_BASE = 0x40020000; +#$Conf = "PB06:AF10:V, PB02:AF09:V, PC10:AF09:V, PC09:AF09:V, PD13:AF09:V, PE02:AF09:V"; + +# stm32f746g-disco quad +#$GPIO_BASE = 0x40020000; +#Conf = "PB06:AF10:V, PB02:AF09:V, PD13:AF09:V, PE02:AF09:V, PD12:AF09:V, PD11:AF09:V"; +# w/ cmspi +#$Conf = "PB06:PP:M, PB02:PP:V, PD13:INUP:V, PE02:INUP:V, PD12:INUP:V, PD11:INUP:V"; + +# stm32f769i-disco quad +#$GPIO_BASE = 0x40020000; +#$Conf = "PB06:AF10:V, PB02:AF09:V, PC10:AF09:V, PC09:AF09:V, PD13:AF09:V, PE02:AF09:V"; +# w/ cmspi +#$Conf = "PB06:PP:M, PB02:PP:V, PD13:INUP:V, PE02:INUP:V, PC10:INUP:V, PC09:INUP:V, "; + +# b-l475e-iot01a quad +#$GPIO_BASE = 0x48000000; +#$Conf = "PE15:AF10:V, PE14:AF10:V, PE13:AF10:V, PE12:AF10:V, PE11:AF10:V, PE10:AF10:V"; + +# stm32l476g-disco quad +#$GPIO_BASE = 0x48000000; +#$Conf = "PE15:AF10:V, PE14:AF10:V, PE13:AF10:V, PE12:AF10:V, PE11:AF10:V, PE10:AF10:V"; + +# stm32l496g-disco quad +#$GPIO_BASE = 0x48000000; +#$Conf = "PA07:AF10:V, PA06:AF10:V, PA03:AF10:V, PB11:AF10:V, PB01:AF10:V, PB00:AF10:V"; + +# stm32l4r9i-disco octal +#$GPIO_BASE = 0x48000000; +#$Conf = "PG15:AF05:V, PG12:AF05:V, PG10:AF05:V, PG09:AF05:V, PH10:AF05:V, PH09:AF05:V, " +# . "PH08:AF05:V, PI11:AF05:V, PI10:AF05:V, PI09:AF05:V, PI06:AF05:V"; + +# stm32l4p5g-disco octal/octal +#$GPIO_BASE = 0x48000000; +#$Conf = "PA07:AF10:V, PA06:AF10:V, PC03:AF10:V, PD07:AF10:V, PD05:AF10:V, PD04:AF10:V, " +# . "PE13:AF10:V, PE12:AF10:V, PE11:AF10:V, PE10:AF10:V, PG06:AF03:V"; +#$Conf = "PF12:AF05:V, PF04:AF05:V, PF03:AF05:V, PF02:AF05:V, PF01:AF05:V, PF00:AF05:V, " +# . "PG12:AF05:V, PG10:AF05:V, PG09:AF05:V, PG01:AF05:V, PG00:AF05:V"; + +# nucleo-f767zi dual quad +#$GPIO_BASE = 0x40020000; +#$Conf = "PB06:AF10:V, PB02:AF09:V, PC11:AF09:V, PD13:AF09:V, PE02:AF09:V, PD12:AF09:V, " +# . "PD11:AF09:V, PE10:AF10:V, PE09:AF10:V, PE08:AF10:V, PE07:AF10:V"; +# w/ cmspi +#$Conf = "PB10:PPUP:M, PB02:PPUP:V, PD13:INPUP:V, PE02:INPUP:V, PD12:INPUP:V, PD11:INPUP:V"; +#$Conf = "PC11:PPUP:M, PB02:PPUP:V, PE10:INPUP:V, PE09:INPUP:V, PE08:INPUP:V, PE07:INPUP:V"; + +# nucleo-h743zi dual quad +#$GPIO_BASE = 0x58020000; +#$Conf = "PB10:AF09:V, PB02:AF09:V, PC11:AF09:V, PD13:AF09:V, PE02:AF09:V, PD12:AF09:V, " +# . "PD11:AF09:V, PE10:AF10:V, PE09:AF10:V, PE08:AF10:V, PE07:AF10:V"; +# w/ cmspi +#$Conf = "PB10:PPUP:M, PB02:PPUP:V, PD13:INPUP:V, PE02:INPUP:V, PD12:INPUP:V, PD11:INPUP:V"; +#$Conf = "PC11:PPUP:M, PB02:PPUP:V, PE10:INPUP:V, PE09:INPUP:V, PE08:INPUP:V, PE07:INPUP:V"; + +# nucleo-h7a3zi dual quad +#$GPIO_BASE = 0x58020000; +#$Conf = "PB10:AF09:V, PB02:AF09:V, PD13:AF09:V, PE02:AF09:V, PD12:AF09:V, PD11:AF09:V, " +# . "PC11:AF09:V, PE10:AF10:V, PD06:AF10:V, PE08:AF10:V, PE07:AF10:V"; +# w/ cmspi +#$Conf = "PB10:PPUP:M, PB02:PPUP:V, PD13:INPUP:V, PE02:INPUP:V, PD12:INPUP:V, PD11:INPUP:V"; +#$Conf = "PC11:PPUP:M, PB02:PPUP:V, PE10:INPUP:V, PD06:INPUP:V, PE08:INPUP:V, PE07:INPUP:V"; + +# nucleo-l4r5zi one dual quad single NCS +#$GPIO_BASE = 0x48000000; +#$Conf = "PA02:AF10:V, PE10:AF10:V, PD07:AF10:V, PD06:AF10:V, PD05:AF10:V, PD04:AF10:V, " +# . "PE15:AF10:V, PE14:AF10:V, PE13:AF10:V, PE12:AF10:V"; +# w/ cmspi +#$Conf = "PA02:PPUP:M, PE10:PPUP:V, PD07:INPDO:V, PD06:INPDO:V, PD05:INPDO:V, PD04:INPDO:V"; +#$Conf = "PA02:PPUP:M, PE10:PPUP:V, PE15:INPDO:V, PE14:INPDO:V, PE13:INPDO:V, PE12:INPDO:V"; + +# nucleo-l552ze-q dual quad with single NCS +#$GPIO_BASE = 0x42020000; +#$Conf = "PA02:AF10:V, PE10:AF10:V, PD07:AF10:V, PD06:AF10:V, PD05:AF10:V, PD04:AF10:V, " +# . "PE15:AF10:V, PE14:AF10:V, PE13:AF10:V, PE12:AF10:V"; +# w/ cmspi +#$Conf = "PA02:PPUP:M, PE10:PPUP:V, PD07:INPDO:V, PD06:INPDO:V, PD05:INPDO:V, PD04:INPDO:V"; +#$Conf = "PA02:PPUP:M, PE10:PPUP:V, PE15:INPDO:V, PE14:INPDO:V, PE13:INPDO:V, PE12:INPDO:V"; + +# nucleo-g071rb dual quad +#$GPIO_BASE = 0x50000000; +#$Conf = "PA00:PPUP:H, PA04:PPUP:V, PB03:INPUP:V, PA10:INPUP:V, PB11:INPUP:H, PB01:INPUP:H"; +#$Conf = "PA01:PPUP:H, PA04:PPUP:V, PA08:INPUP:V, PB14:INPUP:V, PB04:INPUP:V, PB05:INPUP:V"; + +# nucleo-g474re dual quad with single NCS +#$GPIO_BASE = 0x48000000; +#$Conf = "PB11:AF10:H, PB10:AF10:V, PA06:AF10:V, PA07:AF10:V, PB00:AF10:V, PB01:AF10:V, " +# . "PC04:AF10:V, PC03:AF10:V, PC02:AF10:V, PC01:AF10:V"; +# w/ cmspi +#$Conf = "PB11:PPUP:H, PB10:PPUP:V, PA06:INPUP:V, PA07:INPUP:V, PB00:INPUP:V, PB01:INPUP:V"; +#$Conf = "PB11:PPUP:H, PB10:PPUP:V, PC04:INPUP:V, PC03:INPUP:V, PC02:INPUP:V, PC01:INPUP:V"; + +# stm32h745i-disco dual quad with single NCS +#$GPIO_BASE = 0x58020000; +#$Conf = "PG06:AF10:H, PF10:AF09:V, PF06:AF09:V, PF07:AF09:V, PF09:AF10:V, PD11:AF09:V, " +# . "PG14:AF09:H, PG09:AF09:V, PH03:AF09:V, PH02:AF09:V"; + +# stm32h747i-disco dual quad with single NCS +#GPIO_BASE = 0x58020000; +#$Conf = "PG06:AF10:H, PB02:AF09:V, PF06:AF09:V, PF07:AF09:V, PF09:AF10:V, PD11:AF09:V, " +# . "PG14:AF09:H, PG09:AF09:V, PH03:AF09:V, PH02:AF09:V"; + +# stm32h7b3i-disco octal +#$GPIO_BASE = 0x58020000; +#$Conf = "PG06:AF10:V, PB02:AF09:V, PC05:AF10:V, PD07:AF10:V, PG09:AF09:V, PH03:AF09:V, PC01:AF10:V, " +# . "PF06:AF10:V, PF07:AF10:V, PF09:AF10:V, PD11:AF09:V"; + +# stm32h735g-disco octal +#$GPIO_BASE = 0x58020000; +#$Conf = "PG06:AF10:V, PF10:AF09:V, PB02:AF10:V, PD07:AF10:V, PG09:AF09:V, PD05:AF10:V, PD04:AF10:V, " +# . "PD13:AF09:V, PE02:AF09:V, PD12:AF09:V, PD11:AF09:V"; + +# stm32l562e-disco octal +#$GPIO_BASE = 0x42020000; +#$Conf = "PA02:AF10:V, PA03:AF10:V, PB02:AF10:V, PC00:AF03:V, PC03:AF10:V, PC02:AF10:V, PC01:AF10:V, " +# . "PA06:AF10:V, PA07:AF10:V, PB00:AF10:V, PB01:AF10:V"; + +&getopts('b:c:f:t'); +if ($Getopt::Std::opt_b eq '') +{ + if ($GPIO_BASE eq '') + { + die("usage: $0 [ -1 ] -b io_base [ -c port_configuration ] [ -f conf_file ]"); + } +} +else +{ + $GPIO_BASE = eval $Getopt::Std::opt_b; +} + +if ($Getopt::Std::opt_c eq '') +{ + if (($Conf eq '') && ($Getopt::Std::opt_f eq '')) + { + die("usage: $0 [ -b io_base ] ( -c port_configuration | -f conf_file )"); + } +}# +else +{ + $Conf = $Getopt::Std::opt_c . ','; +} + +$STM32F1 = $Getopt::Std::opt_t; + +my $Sep = "\t"; +my $Form = "${Sep}mmw 0x%08X 0x%08X 0x%08X\t;# "; + +my $GPIO_OFFS; +my $GPIO_CRL; +my $GPIO_CRH; +my $GPIO_MODER; +my $GPIO_OTYPER; +my $GPIO_OSPEEDR; +my $GPIO_PUPDR; +my $GPIO_IDR; +my $GPIO_ODR; +my $GPIO_AFRL; +my $GPIO_AFRH; + +if ($STM32F1) +{ + # offsets for F1 devices + $GPIO_OFFS = 0x400; + $GPIO_CRL = 0x00; + $GPIO_CRH = 0x04; + $GPIO_IDR = 0x08; + $GPIO_ODR = 0x0C; +} +else +{ + # these offsets are identical on all F0, F4, F7, H7, L4, L4+ devices up to now + $GPIO_OFFS = 0x400; + $GPIO_MODER = 0x00; + $GPIO_OTYPER = 0x04; + $GPIO_OSPEEDR = 0x08; + $GPIO_PUPDR = 0x0C; + $GPIO_IDR = 0x10; + $GPIO_ODR = 0x14; + $GPIO_AFRL = 0x20; + $GPIO_AFRH = 0x24; +} + +my @Out = ( { }, { }, { }, { }, { }, { }, { }, { }, { }, { }, { } ); +my @Port = ( ); +my $Exor; +my %Conf; +my $Pins = "${Sep}#"; + +my $pins; +my $altn; +my %defs; + +if ($Getopt::Std::opt_f ne '') +{ + open(CONF_FILE, '<', $Getopt::Std::opt_f) || die("can't open $Getopt::Std::opt_f"); + while (my $line = <CONF_FILE>) + { + if ($line =~ /^\s*#define\s+.?(QSPI|QUAD_?SPI|OCTOSPI[^_]*)\w+_(Port|Pin)\s/) + { + if ($line =~ /#define\s+(\w+)\s+(\w+)/) + { + $defs{$1} = $2; + } + else + { + die($line); + } + } + elsif ($line =~ /^\s*(P[A-Z])([0-9]+)\s*-+>\s+.?(QSPI|QUAD_?SPI|OCTO_?SPI[^_]*)_(\w+)/) + { + $Conf{$4} = sprintf("%s%02d", $1, $2); + } + elsif ($line =~ /^\s*GPIO_InitStruct.Pin\s*=\s*([^;]+\w)/) + { + $pins = $1; + while ($line !~ /;/) + { + $line = <CONF_FILE>; + $line =~ /^\s*([^;]+\w)/; + $pins .= $1; + } + } + elsif ($line =~ /^\s*GPIO_InitStruct.Alternate\s*=\s*GPIO_AF([0-9]+)/) + { + $altn = $1; + } + elsif ($line =~ /^\s*HAL_GPIO_Init\s*\(\s*(\w+)\s*,/) + { + my $port = $1; + if ($port =~ /GPIO([A-Z])/) + { + $port = $1; + } + elsif (exists($defs{$port})) + { + $defs{$port} =~ /GPIO([A-Z])/; + $port = $1; + } + else + { + printf("\n"); + next; + } + my @pin = split(/\s*\|\s*/, $pins); + foreach my $pin (@pin) + { + my $bit; + if (exists($defs{$pin})) + { + $defs{$pin} =~ /GPIO_PIN_([0-9]+)/; + $bit = $1; + } + else + { + $pin =~ /GPIO_PIN_([0-9]+)/; + $bit = $1; + } + $Conf .= sprintf("P%s%02d:AF%02d:V, ", $port, $bit, $altn); + } + $pins = ''; + $altn = 0; + } + } + close(CONF_FILE); +} +else +{ + my @names = ( ); + my @conf = split(/\s*,\s*/, $Conf); + + if (@conf == 2) + { + push(@names, 'SDA', 'SCL'); + } else { + if (@conf == 3) + { + push(@names, 'NCS', 'CLK', 'IO0/DIO'); + } + elsif (@conf == 4) + { + push(@names, 'NCS', 'CLK','IO1/MISO', 'IO0/MOSI'); + } + elsif (@conf == 6) + { + push(@names, 'NCS', 'CLK', 'IO3/NHOLD', 'IO2/NWP', 'IO1/MISO', 'IO0/MOSI'); + } + elsif (@conf == 10) + { + push(@names, 'NCS', 'CLK', 'BK_1_IO3/NHOLD', 'BK1_IO2/NWP', 'BK1_IO1/MISO', 'BK1_IO0/MOSI'); + push(@names, 'BK_2_IO3/NHOLD', 'BK2_IO2/NWP', 'BK2_IO1/MISO', 'BK2_IO0/MOSI'); + } + elsif (@conf == 11) + { + push(@names, 'BK_1_NCS', 'CLK', 'BK_1_IO3/NHOLD', 'BK1_IO2/NWP', 'BK1_IO1/MISO', 'BK1_IO0/MOSI'); + push(@names, 'BK_2_NCS', 'BK_2_IO3/NHOLD', 'BK2_IO2/NWP', 'BK2_IO1/MISO', 'BK2_IO0/MOSI'); + } + else + { + die("invalid config"); + } + } + + for (my $index = 0; $index < @conf; $index++) + { + uc($conf[$index]) =~ /^P([A-K])([0-9]+):\s*([A-Z0-9]+):(L|M|H|V)$/; + $Pins .= sprintf(" %s: P%s%02d,", $names[$index], $1, $2); + } + chop($Pins); +} + +if (exists $Conf{'BK1_IO0'}) +{ + # QuadSPI on F4, F7, H7 + my $line; + for my $i ('NCS', 'BK1_NCS', 'CLK', 'BK1_IO3', 'BK1_IO2', 'BK1_IO1', 'BK1_IO0') + { + (exists $Conf{$i}) && ($Pins .= sprintf(" %s: %s,", $Conf{$i}, $i)); + } +} + +if (exists $Conf{'BK2_IO0'}) +{ + # QuadSPI on F4, F7, H7 + my $line; + for my $i ('NCS', 'BK2_NCS', 'CLK', 'BK2_IO3', 'BK2_IO2', 'BK2_IO1', 'BK2_IO0') + { + (exists $Conf{$i}) && ($Pins .= sprintf(" %s: %s,", $Conf{$i}, $i)); + } +} + +if (exists $Conf{'P1_IO0'}) +{ + # OctoSPI on L4+, L5, H7 + my $line; + for my $i ('P1_NCS', 'P1_CLK', 'P1_DQS', 'P1_IO7', 'P1_IO6', 'P1_IO5', 'P1_IO4', + 'P1_IO3', 'P1_IO2', 'P1_IO1', 'P1_IO0') + { + (exists $Conf{$i}) && ($Pins .= sprintf(" %s: %s,", $Conf{$i}, $i)); + } +} + +if (exists $Conf{'P2_IO0'}) +{ + # OctoSPI on L4+, H7 + my $line; + for my $i ('P2_NCS', 'P2_CLK', 'P2_DQS', 'P2_IO7', 'P2_IO6', 'P2_IO5', 'P2_IO4', + 'P2_IO3', 'P2_IO2', 'P2_IO1', 'P2_IO0') + { + (exists $Conf{$i}) && ($Pins .= sprintf(" %s: %s,", $Conf{$i}, $i)); + } +} + +my @Col = ( ); +my @conf = split(/\s*,\s*/, $Conf); + +if (@conf == 3) +{ + splice(@conf, 2, 0, 'NONE', 'NONE', 'NONE'); +} +elsif (@conf == 4) +{ + splice(@conf, 2, 0, 'NONE', 'NONE'); +} + +foreach my $line (@conf) +{ + $line = uc($line); + $line =~ /^P([A-K])([0-9]+):\s*([A-Z0-9]+):(L|M|H|V)$/; + my $port = $1; + my $pin = $2; + my $conf = $3; + my $speed = $4; + + my $MODER = 0x0; + my $OTYPER = 0x0; + my $OSPEEDR = 0x0; + my $PUPDR = 0x0; + my $AFR = 0x0; + my $num = ord(${port}) - ord('A'); + my $out = $Out[$num]; + + (exists $$out{'DEF'}) || ($$out{'DEF'} = 0); + + if ($conf eq '') + { + if ($line ne 'NONE') + { + printf(STDERR "invalid conf %s\n", $line); + } + next; + } + elsif ($conf =~ /^AF([0-9]+)(|P|O)(|UP|DO)$/) + { + if ($STM32F1) + { + printf(STDERR "no alternate %s for F1 family\n", $line); + next; + } + if (($1 < 0) || ($1 > 15)) + { + printf(STDERR "invalid alternate %s\n", $line); + next; + } + $MODER = 0x2; + $AFR = $1; + if ($pin <= 7) + { + $$out{'AFRL_H'} |= ($AFR << (${pin} << 2)); + $$out{'AFRL_L'} |= (($AFR ^ 0xF) << (${pin} << 2)); + } + else + { + $$out{'AFRH_H'} |= ($AFR << ((${pin} - 8) << 2)); + $$out{'AFRH_L'} |= (($AFR ^ 0xF) << ((${pin} - 8) << 2)); + } + if ($2 ne '') { + $OTYPER = ($1 eq 'O') ? 0x1 : 0x0; + $$out{'OTYPER_H'} |= ($OTYPER << $pin); + $$out{'OTYPER_L'} |= (($OTYPER ^ 0x1) << $pin); + } + $PUPDR = ($3 eq 'UP') ? 0x1 : (($3 eq 'DO') ? 0x2 : 0x0); + $$out{'PUPDR_H'} |= ($PUPDR << (${pin} << 1)); + $$out{'PUPDR_L'} |= (($PUPDR ^0x3) << (${pin} << 1)); + $conf = sprintf("AF%02d%s%s", $AFR, $2, $3); + } + elsif ($conf =~ /^IN(|P|O)(|UP|DO)$/) + { + if ($STM32F1) + { + $MODER = ($1 eq '') ? 0x4 : 0x8; + ($2 eq 'UP') && ($$out{'PUPDR_H'} |= (1 << ${pin})); + ($2 eq 'DO') && ($$out{'PUPDR_L'} |= (1 << ${pin})); + } + else + { + $MODER = 0x0; + if ($1 ne '') + { + $OTYPER = ($1 eq 'O') ? 0x1 : 0x0; + $$out{'OTYPER_H'} |= ($OTYPER << $pin); + $$out{'OTYPER_L'} |= (($OTYPER ^ 0x1) << $pin); + } + $PUPDR = ($2 eq 'UP') ? 0x1 : (($2 eq 'DO') ? 0x2 : 0x0); + $$out{'PUPDR_H'} |= ($PUPDR << (${pin} << 1)); + $$out{'PUPDR_L'} |= (($PUPDR ^0x3) << (${pin} << 1)); + } + ($2 eq 'UP') && ($$out{'ODR_H'} |= (1 << ${pin})); + ($2 eq 'DO') && ($$out{'ODR_L'} |= (1 << ${pin})); + } + elsif ($conf =~ /^P(P|O)(|UP|DO)$/) + { + if ($STM32F1) + { + $MODER = ($1 eq 'O') ? 0x4 : 0x0; + $MODER |= (($speed eq 'V') ? 0x03 : (($speed eq 'L') ? 0x2 : 0x1)); + if ($2 ne '') + { + printf(STDERR "WARNING: no output w/ pull-up/pull-down for F1 family %s\n", $line); + } + } + else + { + $MODER = 0x1; + $OTYPER = ($1 eq 'O') ? 0x1 : 0x0; + $$out{'OTYPER_H'} |= ($OTYPER << $pin); + $$out{'OTYPER_L'} |= (($OTYPER ^ 0x1) << $pin); + $PUPDR = ($2 eq 'UP') ? 0x1 : (($2 eq 'DO') ? 0x2 : 0x0); + $$out{'PUPDR_H'} |= ($PUPDR << ($pin << 1)); + $$out{'PUPDR_L'} |= (($PUPDR ^ 0x3) << ($pin << 1)); + } + ($2 eq 'UP') && ($$out{'ODR_H'} |= (1 << ${pin})); + ($2 eq 'DO') && ($$out{'ODR_L'} |= (1 << ${pin})); + } + else + { + printf(STDERR "invalid conf %s\n", $line); + next; + } + + if ($$out{'DEF'} & (1<< ${pin})) + { + printf(STDERR "redefinition: %s\n", $line); + } + + if ($STM32F1) + { + if ($pin >= 8) + { + $$out{'CRH_H'} |= ($MODER << (($pin & 0x7) << 2)); + $$out{'CRH_L'} |= (($MODER ^ 0xF) << (($pin & 0x7) << 2)); + } + else + { + $$out{'CRL_H'} |= ($MODER << (($pin & 0x7) << 2)); + $$out{'CRL_L'} |= (($MODER ^ 0xF) << (($pin & 0x7) << 2)); + } + + $Exor = sprintf("0x%08X %2d", ${GPIO_BASE} + (ord($port) - ord('A')) * ${GPIO_OFFS} + ${GPIO_ODR}, $pin); + my $exor = 0xB << (($pin & 0x7) << 2); + (($MODER & 0x3) == 0x0) && ($Exor .= sprintf(" 0x%03X 0x%03X 0x%08X", + ((($pin >= 8) ? ${GPIO_CRH} : ${GPIO_CRL})-${GPIO_ODR}) & 0x3FF, + ((($pin >= 8) ? ${GPIO_CRH} : ${GPIO_CRL})-${GPIO_ODR}) & 0x3FF, $exor)); + } + else + { + $$out{'DEF'} |= (1 << ${pin}); + $$out{'MODER_H'} |= ($MODER << (${pin} << 1)); + $$out{'MODER_L'} |= (($MODER ^ 0x3) << (${pin} << 1)); + + $OSPEEDR = (($speed eq 'V') ? 0x3 : (($speed eq 'H') ? 0x2 : (($speed eq 'M') ? 0x1 : 0x0))); + $$out{'OSPEEDR_H'} |= ($OSPEEDR << (${pin} << 1)); + $$out{'OSPEEDR_L'} |= (($OSPEEDR ^ 0x3) << (${pin} << 1)); + + $Exor = sprintf("0x%08X %2d", ${GPIO_BASE} + (ord($port) - ord('A')) * ${GPIO_OFFS} + ${GPIO_ODR}, $pin); + my $exor = (0x1 << ($pin << 1)); + ($MODER == 0x0) && ($Exor .= sprintf(" 0x%03X 0x%03X 0x%08X", (${GPIO_MODER}-${GPIO_ODR}) & 0x3FF, + (${GPIO_MODER}-${GPIO_ODR}) & 0x3FF, $exor)); + } + + push(@{$Port[$num]}, sprintf("P%s%02d:%s:%s", $port, $pin, $conf, $speed)); + push(@Col, $Exor); +} + +my $Col = sprintf("${Sep}0x%03X ", (${GPIO_IDR}-${GPIO_ODR}) & 0x3FF); +for (my $i = 0; $i < @Col; $i++) +{ + if (($i != 0) && (($i % 2) == 0)) + { + (($i + 1) < @Col) && ($Col .= "\\\n${Sep}"); + } + $Col .= sprintf("%s ", $Col[$i]); +} +printf("%s\n", $Col); + +my @Col = ( ); +my $Set; +for (my $i = 0; $i < @Out; $i++) +{ + my $out = $Out[$i]; + my $addr = ${GPIO_BASE} + $i * ${GPIO_OFFS}; + my $count = 0; + + if ($STM32F1) + { + if (($$out{'CRH_H'} | $$out{'CRH_L'} | $$out{'CRL_H'} | $$out{'CRL_L'} | + $$out{'PUPDR_H'} | $$out{'PUPDR_L'}) != 0) + { + push(@Col, sort({ $b cmp $a } @{$Port[$i]})); + + $Set .= sprintf("\n%s# Port %s: %s\n", ${Sep}, chr($i + ord('A')), + join(", ", sort({ $b cmp $a } @{$Port[$i]}))); + + (($$out{'CRL_H'} | $$out{'CRL_L'}) != 0) && + ($Set .= sprintf("${Form}CRL\n", $addr + ${GPIO_CRL}, $$out{'CRL_H'}, $$out{'CRL_L'})); + + (($$out{'CRH_H'} | $$out{'CRH_L'}) != 0) && + ($Set .= sprintf("${Form}CRH\n", $addr + ${GPIO_CRH}, $$out{'CRH_H'}, $$out{'CRH_L'})); + + (($$out{'ODR_H'} | $$out{'ODR_L'}) != 0) && + ($Set .= sprintf("${Form}ODR/PUPDR\n", $addr + ${GPIO_ODR}, $$out{'ODR_H'}, $$out{'ODR_L'})); + } + } + else + { + if (($$out{'MODER_H'} | $$out{'MODER_L'} | + $$out{'OTYPER_H'} | $$out{'OTYPER_L'} | + $$out{'OSPEEDR_H'} | $$out{'OSPEEDR_L'} | + $$out{'PUPDR_H'} | $$out{'PUPDR_L'} | + $$out{'ODR_H'} | $$out{'ODR_L'} | + $$out{'AFRL_H'} | $$out{'AFRL_L'} | + $$out{'AFRH_H'} | $$out{'AFRH_L'}) != 0) + { + push(@Col, sort({ $b cmp $a } @{$Port[$i]})); + + $Set .= sprintf("%s# Port %s: %s\n", ${Sep}, chr($i + ord('A')), + join(", ", sort({ $b cmp $a } @{$Port[$i]}))); + + (($$out{'MODER_H'} | $$out{'MODER_L'}) != 0) && + ($Set .= sprintf("${Form}MODER\n", $addr + ${GPIO_MODER}, $$out{'MODER_H'}, $$out{'MODER_L'})); + + (($$out{'OTYPER_H'} | $$out{'OTYPER_L'}) != 0) && + ($Set .= sprintf("${Form}OTYPER\n", $addr + ${GPIO_OTYPER}, $$out{'OTYPER_H'}, $$out{'OTYPER_L'})); + + (($$out{'OSPEEDR_H'} | $$out{'OSPEEDR_L'}) != 0) && + ($Set .= sprintf("${Form}OSPEEDR\n", $addr + ${GPIO_OSPEEDR}, $$out{'OSPEEDR_H'}, $$out{'OSPEEDR_L'})); + + (($$out{'PUPDR_H'} | $$out{'PUPDR_L'}) != 0) && + ($Set .= sprintf("${Form}PUPDR\n", $addr + ${GPIO_PUPDR}, $$out{'PUPDR_H'}, $$out{'PUPDR_L'})); + + (($$out{'ODR_H'} | $$out{'ODR_L'}) != 0) && + ($Set .= sprintf("${Form}ODR\n", $addr + ${GPIO_ODR}, $$out{'ODR_H'}, $$out{'ODR_L'})); + + (($$out{'AFRL_H'} | $$out{'AFRL_L'}) != 0) && + ($Set .= sprintf("${Form}AFRL\n", $addr + ${GPIO_AFRL}, $$out{'AFRL_H'}, $$out{'AFRL_L'})); + + (($$out{'AFRH_H'} | $$out{'AFRH_L'}) != 0) && + ($Set .= sprintf("${Form}AFRH\n", $addr + ${GPIO_AFRH}, $$out{'AFRH_H'}, $$out{'AFRH_L'})); + } + } +} + +my $Col = ''; +for (my $i = 0; $i < @Col; $i++) +{ + if (($i % 6) == 0) + { + chop($Col); + (($i + 1) < @Col) && ($Col .= "\n${Sep}#"); + } + $Col .= sprintf(" %s,", $Col[$i]); +} +chop($Col); +#printf("\n%s\n", $Pins); +printf("%s\n", $Col); +printf("%s\n", $Set); diff --git a/contrib/loaders/flash/stmqspi/stmoctospi_crc32.S b/contrib/loaders/flash/stmqspi/stmoctospi_crc32.S new file mode 100644 index 0000000..941ea42 --- /dev/null +++ b/contrib/loaders/flash/stmqspi/stmoctospi_crc32.S @@ -0,0 +1,123 @@ +/*************************************************************************** + * Copyright (C) 2019 by Andreas Bolsch * + * andreas.bolsch@mni.thm.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + + .text + .syntax unified + .cpu cortex-m0 + .thumb + .thumb_func + +/* Params: + * r0 - total count (bytes), crc32 (out) + * r1 - flash page size + * r2 - address offset into flash + * r3 - OCTOSPI io_base + + * Clobbered: + * r4 - tmp + * r5 - address of OCTOSPI_DR + * r6 - address of OCTOSPI_CCR + * r7 - tmp + */ + +#include "../../../../src/flash/nor/stmqspi.h" + +#define OCTOSPI_CCR_CCR (OCTOSPI_CCR - OCTOSPI_CCR) +#define OCTOSPI_TCR_CCR (OCTOSPI_TCR - OCTOSPI_CCR) +#define OCTOSPI_IR_CCR (OCTOSPI_IR - OCTOSPI_CCR) + + .macro octospi_abort + movs r5, #(1<<SPI_ABORT) /* abort bit mask */ + ldr r7, [r3, #OCTOSPI_CR] /* get OCTOSPI CR register */ + orrs r7, r7, r5 /* set abort bit */ + str r7, [r3, #OCTOSPI_CR] /* store new CR register */ + .endm + + .macro wait_busy +0: + ldr r7, [r3, #OCTOSPI_SR] /* load status */ + lsrs r7, r7, #(SPI_BUSY+1) /* shift BUSY into C */ + bcs 0b /* loop until BUSY cleared */ + movs r7, #(1<<SPI_TCF) /* TCF bitmask */ + str r7, [r3, #OCTOSPI_FCR] /* clear TCF flag */ + .endm + +start: + subs r0, r0, #1 /* decrement count for DLR */ + subs r1, r1, #1 /* page size mask and for DLR */ + movs r4, #0x00 /* initialize crc */ + mvns r4, r4 /* to 0xFFFFFFFF */ +start_read: + octospi_abort /* start in clean state */ + movs r5, #OCTOSPI_DR /* load OCTOSPI_DR address offset */ + adds r5, r5, r3 /* address of OCTOSPI_DR */ + movs r6, #OCTOSPI_CCR-OCTOSPI_DR /* load OCTOSPI_CCR address offset */ + adds r6, r6, r5 /* address of OCTOSPI_CCR */ + wait_busy + ldr r7, cr_page_read /* indirect read mode */ + str r7, [r3, #OCTOSPI_CR] /* set mode */ + mov r7, r2 /* get current start address */ + orrs r7, r7, r1 /* end of current page */ + subs r7, r7, r2 /* count-1 to end of page */ + cmp r7, r0 /* if this count <= remaining */ + bls write_dlr /* then read to end of page */ + mov r7, r0 /* else read all remaining */ +write_dlr: + str r7, [r3, #OCTOSPI_DLR] /* size-1 in DLR register */ + ldr r7, ccr_page_read /* CCR for read */ + str r7, [r6, #OCTOSPI_CCR_CCR] /* initiate transfer */ + ldr r7, tcr_page_read /* TCR for read */ + str r7, [r6, #OCTOSPI_TCR_CCR] /* instruction */ + ldr r7, ir_page_read /* IR for read */ + str r7, [r6, #OCTOSPI_IR_CCR] /* instruction */ + str r2, [r3, #OCTOSPI_AR] /* store SPI start address */ + ldr r6, =0x04C11DB7 /* CRC32 polynomial */ +read_loop: + ldrb r7, [r5] /* read next byte from DR */ + lsls r7, r7, #24 /* shift into msb */ + eors r4, r4, r7 + .rept 8 /* unrolled bit loop */ + asrs r7, r4, #31 /* copy bit 31 into bits 0 to 31 */ + ands r7, r7, r6 /* r7 neg. -> CRC32XOR, pos. -> 0x0 */ + lsls r4, r4, #1 /* shift result */ + eors r4, r4, r7 /* eor by CRC32XOR or 0x0 */ + .endr + adds r2, r2, #1 /* increment address */ + subs r0, r0, #1 /* decrement (count-1) */ + bmi exit /* stop if no data left */ + tst r2, r1 /* page end ? */ + bne read_loop /* if not, then next byte */ +page_end: + bal start_read /* then next page */ + .pool + +exit: + mvns r0, r4 /* invert to get final result */ + octospi_abort /* to idle state */ + .align 2 /* align to word, bkpt is 4 words */ + bkpt #0 /* before code end for exit_point */ + .align 2 /* align to word */ + +cr_page_read: + .space 4 /* OCTOSPI_CR value for read command */ +ccr_page_read: + .space 4 /* OCTOSPI_CCR value for read command */ +tcr_page_read: + .space 4 /* OCTOSPI_TCR value for read command */ +ir_page_read: + .space 4 /* OCTOSPI_IR value for read command */ diff --git a/contrib/loaders/flash/stmqspi/stmoctospi_crc32.inc b/contrib/loaders/flash/stmqspi/stmoctospi_crc32.inc new file mode 100644 index 0000000..afc6168 --- /dev/null +++ b/contrib/loaders/flash/stmqspi/stmoctospi_crc32.inc @@ -0,0 +1,13 @@ +/* Autogenerated with ../../../../src/helper/bin2char.sh */ +0x01,0x38,0x01,0x39,0x00,0x24,0xe4,0x43,0x02,0x25,0x1f,0x68,0x2f,0x43,0x1f,0x60, +0x50,0x25,0xed,0x18,0xb0,0x26,0x76,0x19,0x1f,0x6a,0xbf,0x09,0xfc,0xd2,0x02,0x27, +0x5f,0x62,0x22,0x4f,0x1f,0x60,0x17,0x46,0x0f,0x43,0xbf,0x1a,0x87,0x42,0x00,0xd9, +0x07,0x46,0x1f,0x64,0x1e,0x4f,0x37,0x60,0x1e,0x4f,0xb7,0x60,0x1e,0x4f,0x37,0x61, +0x9a,0x64,0x15,0x4e,0x2f,0x78,0x3f,0x06,0x7c,0x40,0xe7,0x17,0x37,0x40,0x64,0x00, +0x7c,0x40,0xe7,0x17,0x37,0x40,0x64,0x00,0x7c,0x40,0xe7,0x17,0x37,0x40,0x64,0x00, +0x7c,0x40,0xe7,0x17,0x37,0x40,0x64,0x00,0x7c,0x40,0xe7,0x17,0x37,0x40,0x64,0x00, +0x7c,0x40,0xe7,0x17,0x37,0x40,0x64,0x00,0x7c,0x40,0xe7,0x17,0x37,0x40,0x64,0x00, +0x7c,0x40,0xe7,0x17,0x37,0x40,0x64,0x00,0x7c,0x40,0x01,0x32,0x01,0x38,0x05,0xd4, +0x0a,0x42,0xd7,0xd1,0xb8,0xe7,0x00,0x00,0xb7,0x1d,0xc1,0x04,0xe0,0x43,0x02,0x25, +0x1f,0x68,0x2f,0x43,0x1f,0x60,0xc0,0x46,0x00,0xbe,0xc0,0x46,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, diff --git a/contrib/loaders/flash/stmqspi/stmoctospi_erase_check.S b/contrib/loaders/flash/stmqspi/stmoctospi_erase_check.S new file mode 100644 index 0000000..3af82d4 --- /dev/null +++ b/contrib/loaders/flash/stmqspi/stmoctospi_erase_check.S @@ -0,0 +1,108 @@ +/*************************************************************************** + * Copyright (C) 2019 by Andreas Bolsch * + * andreas.bolsch@mni.thm.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + + .text + .syntax unified + .cpu cortex-m0 + .thumb + .thumb_func + +/* Params: + * r0 - sector count + * r1 - QSPI io_base + + * Clobbered: + * r2 - r7 tmp */ + +#include "../../../../src/flash/nor/stmqspi.h" + +#define OCTOSPI_CCR_CCR (OCTOSPI_CCR - OCTOSPI_CCR) +#define OCTOSPI_TCR_CCR (OCTOSPI_TCR - OCTOSPI_CCR) +#define OCTOSPI_IR_CCR (OCTOSPI_IR - OCTOSPI_CCR) + + .macro octospi_abort + movs r5, #(1<<SPI_ABORT) /* abort bit mask */ + ldr r7, [r1, #OCTOSPI_CR] /* get OCTOSPI_CR register */ + orrs r7, r7, r5 /* set abort bit */ + str r7, [r1, #OCTOSPI_CR] /* store new CR register */ + .endm + + .macro wait_busy +0: + ldr r7, [r1, #OCTOSPI_SR] /* load status */ + lsrs r7, r7, #(SPI_BUSY+1) /* shift BUSY into C */ + bcs 0b /* loop until BUSY cleared */ + movs r7, #(1<<SPI_TCF) /* TCF bitmask */ + str r7, [r1, #OCTOSPI_FCR] /* clear TCF flag */ + .endm + +start: + adr r2, buffer /* pointer to start of buffer */ + movs r3, #OCTOSPI_DR /* load OCTOSPI_DR address offset */ + adds r3, r3, r1 /* address of OCTOSPI_DR */ +sector_start: + octospi_abort /* start in clean state */ + movs r6, #OCTOSPI_CCR-OCTOSPI_DR /* load OCTOSPI_CCR address offset */ + adds r6, r6, r3 /* address of OCTOSPI_CCR */ + wait_busy + ldr r7, cr_page_read /* indirect read mode */ + str r7, [r1, #OCTOSPI_CR] /* set mode */ + ldmia r2!, {r4, r5} /* load address offset, length */ + subs r2, r2, #4 /* point to length */ + subs r5, r5, #1 /* decrement sector length for DLR */ + str r5, [r1, #OCTOSPI_DLR] /* size-1 in DLR register */ + ldr r7, ccr_page_read /* CCR for read */ + str r7, [r6, #OCTOSPI_CCR_CCR] /* initiate transfer */ + ldr r7, tcr_page_read /* TCR for read */ + str r7, [r6, #OCTOSPI_TCR_CCR] /* instruction */ + ldr r7, ir_page_read /* IR for read */ + str r7, [r6, #OCTOSPI_IR_CCR] /* instruction */ + str r4, [r1, #OCTOSPI_AR] /* store SPI start address */ + ldr r6, [r2, #4] /* load initial value */ +read_loop: + ldrb r4, [r3, #0] /* read next byte from DR */ + movs r7, #0xFF /* fill bits 8-15 */ + lsls r7, r7, #8 /* with ones */ + orrs r4, r4, r7 /* copy ones to left of read byte */ + ands r6, r6, r4 /* and read byte to result */ + lsls r4, r4, #8 /* shift result into higher byte */ + orrs r6, r6, r4 /* or read byte to result */ + subs r5, r5, #1 /* decrement byte (count-1) */ + bpl read_loop /* again if sector not completed */ + adds r5, r5, #1 /* increment count due to the -1 */ + stmia r2!, {r5, r6} /* save final count and result for sector */ + subs r0, r0, #1 /* decrement sector count */ + bne sector_start /* next sector? */ + octospi_abort /* to idle state */ + +exit: + .align 2 /* align to word, bkpt is 4 words */ + bkpt #0 /* before code end for exit_point */ + .align 2 /* align to word */ + +cr_page_read: + .space 4 /* OCTOSPI_CR value for read command */ +ccr_page_read: + .space 4 /* OCTOSPI_CCR value for read command */ +tcr_page_read: + .space 4 /* OCTOSPI_TCR value for read command */ +ir_page_read: + .space 4 /* OCTOSPI_IR value for read command */ + + .equ buffer, . + diff --git a/contrib/loaders/flash/stmqspi/stmoctospi_erase_check.inc b/contrib/loaders/flash/stmqspi/stmoctospi_erase_check.inc new file mode 100644 index 0000000..c0e124a --- /dev/null +++ b/contrib/loaders/flash/stmqspi/stmoctospi_erase_check.inc @@ -0,0 +1,8 @@ +/* Autogenerated with ../../../../src/helper/bin2char.sh */ +0x1b,0xa2,0x50,0x23,0x5b,0x18,0x02,0x25,0x0f,0x68,0x2f,0x43,0x0f,0x60,0xb0,0x26, +0xf6,0x18,0x0f,0x6a,0xbf,0x09,0xfc,0xd2,0x02,0x27,0x4f,0x62,0x10,0x4f,0x0f,0x60, +0x30,0xca,0x04,0x3a,0x01,0x3d,0x0d,0x64,0x0e,0x4f,0x37,0x60,0x0e,0x4f,0xb7,0x60, +0x0e,0x4f,0x37,0x61,0x8c,0x64,0x56,0x68,0x1c,0x78,0xff,0x27,0x3f,0x02,0x3c,0x43, +0x26,0x40,0x24,0x02,0x26,0x43,0x01,0x3d,0xf6,0xd5,0x01,0x35,0x60,0xc2,0x01,0x38, +0xd9,0xd1,0x02,0x25,0x0f,0x68,0x2f,0x43,0x0f,0x60,0xc0,0x46,0x00,0xbe,0xc0,0x46, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, diff --git a/contrib/loaders/flash/stmqspi/stmoctospi_read.S b/contrib/loaders/flash/stmqspi/stmoctospi_read.S new file mode 100644 index 0000000..fb5ff1f --- /dev/null +++ b/contrib/loaders/flash/stmqspi/stmoctospi_read.S @@ -0,0 +1,142 @@ +/*************************************************************************** + * Copyright (C) 2019 by Andreas Bolsch * + * andreas.bolsch@mni.thm.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + + .text + .syntax unified + .cpu cortex-m0 + .thumb + .thumb_func + +/* Params: + * r0 - total count (bytes), remaining bytes (out, 0 means successful) + * r1 - flash page size + * r2 - address offset into flash + * r3 - OCTOSPI io_base + * r8 - fifo start + * r9 - fifo end + 1 + + * Clobbered: + * r4 - wp + * r5 - address of OCTOSPI_DR + * r6 - address of OCTOSPI_CCR + * r7 - tmp + */ + +#include "../../../../src/flash/nor/stmqspi.h" + +#define OCTOSPI_CCR_CCR (OCTOSPI_CCR - OCTOSPI_CCR) +#define OCTOSPI_TCR_CCR (OCTOSPI_TCR - OCTOSPI_CCR) +#define OCTOSPI_IR_CCR (OCTOSPI_IR - OCTOSPI_CCR) + + .macro octospi_abort + movs r5, #(1<<SPI_ABORT) /* abort bit mask */ + ldr r7, [r3, #OCTOSPI_CR] /* get OCTOSPI CR register */ + orrs r7, r7, r5 /* set abort bit */ + str r7, [r3, #OCTOSPI_CR] /* store new CR register */ + .endm + + .macro wait_busy +0: + ldr r7, [r3, #OCTOSPI_SR] /* load status */ + lsrs r7, r7, #(SPI_BUSY+1) /* shift BUSY into C */ + bcs 0b /* loop until BUSY cleared */ + movs r7, #(1<<SPI_TCF) /* TCF bitmask */ + str r7, [r3, #OCTOSPI_FCR] /* clear TCF flag */ + .endm + +start: + subs r0, r0, #1 /* decrement count for DLR */ + subs r1, r1, #1 /* page size mask and for DLR */ + ldr r4, wp /* load wp */ +start_read: + octospi_abort /* start in clean state */ + movs r5, #OCTOSPI_DR /* load OCTOSPI_DR address offset */ + adds r5, r5, r3 /* address of OCTOSPI_DR */ + movs r6, #OCTOSPI_CCR-OCTOSPI_DR /* load OCTOSPI_CCR address offset */ + adds r6, r6, r5 /* address of OCTOSPI_CCR */ + wait_busy + ldr r7, cr_page_read /* indirect read mode */ + str r7, [r3, #OCTOSPI_CR] /* set mode */ + mov r7, r2 /* get current start address */ + orrs r7, r7, r1 /* end of current page */ + subs r7, r7, r2 /* count-1 to end of page */ + cmp r7, r0 /* if this count <= remaining */ + bls write_dlr /* then write to end of page */ + mov r7, r0 /* else write all remaining */ +write_dlr: + str r7, [r3, #OCTOSPI_DLR] /* size-1 in DLR register */ + ldr r7, ccr_page_read /* CCR for read */ + str r7, [r6, #OCTOSPI_CCR_CCR] /* initiate transfer */ + ldr r7, tcr_page_read /* TCR for read */ + str r7, [r6, #OCTOSPI_TCR_CCR] /* instruction */ + ldr r7, ir_page_read /* IR for read */ + str r7, [r6, #OCTOSPI_IR_CCR] /* instruction */ + str r2, [r3, #OCTOSPI_AR] /* store SPI start address */ +read_loop: + ldrb r7, [r5] /* read next byte from DR */ + strb r7, [r4, #0] /* write next byte */ + adds r4, r4, #1 /* increment internal wp */ + cmp r4, r9 /* internal wp beyond end? */ + blo wait_fifo /* if no, then ok */ + mov r4, r8 /* else wrap around */ +wait_fifo: + ldr r7, rp /* get rp */ + cmp r7, #0 /* if rp equals 0 */ + beq exit /* then abort */ + cmp r4, r7 /* check if fifo full */ + beq wait_fifo /* wait until not full */ + adr r7, wp /* get address of wp */ + str r4, [r7] /* store updated wp */ + adds r2, r2, #1 /* increment address */ + subs r0, r0, #1 /* decrement (count-1) */ + bmi exit /* stop if no data left */ + tst r2, r1 /* page end ? */ + bne read_loop /* if not, then next byte */ +page_end: + bal start_read /* then next page */ + +exit: + adds r0, r0, #1 /* increment count due to the -1 */ + octospi_abort /* to idle state */ + + .align 2 /* align to word, bkpt is 4 words */ + bkpt #0 /* before code end for exit_point */ + .align 2 /* align to word */ + + .space 4 /* not used */ + .space 4 /* not used */ + .space 4 /* not used */ + .space 4 /* not used */ + + .space 4 /* not used */ + .space 4 /* not used */ + .space 4 /* not used */ + .space 4 /* not used */ + +cr_page_read: + .space 4 /* OCTOSPI_CR value for read command */ +ccr_page_read: + .space 4 /* OCTOSPI_CCR value for read command */ +tcr_page_read: + .space 4 /* OCTOSPI_TCR value for read command */ +ir_page_read: + .space 4 /* OCTOSPI_IR value for read command */ + + .equ wp, . /* wp, uint32_t */ + .equ rp, wp + 4 /* rp, uint32_t */ + .equ buffer, rp + 4 /* buffer follows right away */ diff --git a/contrib/loaders/flash/stmqspi/stmoctospi_read.inc b/contrib/loaders/flash/stmqspi/stmoctospi_read.inc new file mode 100644 index 0000000..583f316 --- /dev/null +++ b/contrib/loaders/flash/stmqspi/stmoctospi_read.inc @@ -0,0 +1,12 @@ +/* Autogenerated with ../../../../src/helper/bin2char.sh */ +0x01,0x38,0x01,0x39,0x27,0x4c,0x02,0x25,0x1f,0x68,0x2f,0x43,0x1f,0x60,0x50,0x25, +0xed,0x18,0xb0,0x26,0x76,0x19,0x1f,0x6a,0xbf,0x09,0xfc,0xd2,0x02,0x27,0x5f,0x62, +0x1c,0x4f,0x1f,0x60,0x17,0x46,0x0f,0x43,0xbf,0x1a,0x87,0x42,0x00,0xd9,0x07,0x46, +0x1f,0x64,0x19,0x4f,0x37,0x60,0x19,0x4f,0xb7,0x60,0x19,0x4f,0x37,0x61,0x9a,0x64, +0x2f,0x78,0x27,0x70,0x01,0x34,0x4c,0x45,0x00,0xd3,0x44,0x46,0x16,0x4f,0x00,0x2f, +0x09,0xd0,0xbc,0x42,0xfa,0xd0,0x13,0xa7,0x3c,0x60,0x01,0x32,0x01,0x38,0x02,0xd4, +0x0a,0x42,0xed,0xd1,0xcf,0xe7,0x01,0x30,0x02,0x25,0x1f,0x68,0x2f,0x43,0x1f,0x60, +0x00,0xbe,0xc0,0x46,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00, diff --git a/contrib/loaders/flash/stmqspi/stmoctospi_write.S b/contrib/loaders/flash/stmqspi/stmoctospi_write.S new file mode 100644 index 0000000..867a024 --- /dev/null +++ b/contrib/loaders/flash/stmqspi/stmoctospi_write.S @@ -0,0 +1,219 @@ +/*************************************************************************** + * Copyright (C) 2018 by Andreas Bolsch * + * andreas.bolsch@mni.thm.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + + .text + .syntax unified + .cpu cortex-m0 + .thumb + .thumb_func + +/* Params: + * r0 - total count (bytes), remaining bytes (out, 0 means successful) + * r1 - flash page size + * r2 - address offset into flash + * r3 - OCTOSPI io_base + * r8 - fifo start + * r9 - fifo end + 1 + + * Clobbered: + * r4 - rp + * r5 - address of OCTOSPI_DR + * r6 - address of OCTOSPI_CCR + * r7 - tmp + * r10 - single 0x0 / dual 0x1 + */ + +#include "../../../../src/flash/nor/stmqspi.h" + +#define OCTOSPI_CCR_CCR (OCTOSPI_CCR - OCTOSPI_CCR) +#define OCTOSPI_TCR_CCR (OCTOSPI_TCR - OCTOSPI_CCR) +#define OCTOSPI_IR_CCR (OCTOSPI_IR - OCTOSPI_CCR) + + .macro octospi_abort + movs r5, #(1<<SPI_ABORT) /* abort bit mask */ + ldr r7, [r3, #OCTOSPI_CR] /* get OCTOSPI CR register */ + orrs r7, r7, r5 /* set abort bit */ + str r7, [r3, #OCTOSPI_CR] /* store new CR register */ + .endm + + .macro wait_busy +0: + ldr r7, [r3, #OCTOSPI_SR] /* load status */ + lsrs r7, r7, #(SPI_BUSY+1) /* shift BUSY into C */ + bcs 0b /* loop until BUSY cleared */ + movs r7, #(1<<SPI_TCF) /* TCF bitmask */ + str r7, [r3, #OCTOSPI_FCR] /* clear TCF flag */ + .endm + +start: + subs r0, r0, #1 /* decrement count for DLR */ + subs r1, r1, #1 /* page size mask and for DLR */ + ldr r4, rp /* load rp */ + ldr r7, [r3, #OCTOSPI_CR] /* get OCTOSPI_CR register */ + lsls r7, r7, #(31-SPI_DUAL_FLASH) /* clear higher order bits */ + lsrs r7, r7, #31 /* DUAL_FLASH bit into bit 0 */ + mov r10, r7 /* save in r10 */ +wip_loop: + octospi_abort /* start in clean state */ + movs r5, #OCTOSPI_DR /* load OCTOSPI_DR address offset */ + adds r5, r5, r3 /* address of OCTOSPI_DR */ + movs r6, #OCTOSPI_CCR-OCTOSPI_DR /* load OCTOSPI_CCR address offset */ + adds r6, r6, r5 /* address of OCTOSPI_CCR */ + wait_busy + ldr r7, cr_read_status /* indirect read mode */ + str r7, [r3, #OCTOSPI_CR] /* set mode */ + mov r7, r10 /* get dual bit */ + str r7, [r3, #OCTOSPI_DLR] /* one or two (for dual) bytes */ + ldr r7, ccr_read_status /* CCR for status read */ + str r7, [r6, #OCTOSPI_CCR_CCR] /* initiate status read */ + ldr r7, tcr_read_status /* TCR for status read */ + str r7, [r6, #OCTOSPI_TCR_CCR] /* instruction */ + ldr r7, ir_read_status /* IR for status read */ + str r7, [r6, #OCTOSPI_IR_CCR] /* instruction */ + movs r7, #0 /* dummy address */ + str r7, [r3, #OCTOSPI_AR] /* into AR (for 8-line mode) */ + ldrb r7, [r5] /* get first status register */ + lsrs r7, r7, #(SPIFLASH_BSY+1) /* if first flash busy, */ + bcs wip_loop /* then poll again */ + mov r7, r10 /* get dual bit */ + tst r7, r7 /* dual mode ? */ + beq write_enable /* not dual, then ok */ + ldrb r7, [r5] /* get second status register */ + lsrs r7, r7, #(SPIFLASH_BSY+1) /* if second flash busy, */ + bcs wip_loop /* then poll again */ +write_enable: + tst r0, r0 /* test residual count */ + bmi exit /* if negative, then finished */ + wait_busy + ldr r7, cr_write_enable /* indirect write mode */ + str r7, [r3, #OCTOSPI_CR] /* set mode */ + ldr r7, ccr_write_enable /* CCR for write enable */ + str r7, [r6, #OCTOSPI_CCR_CCR] /* initiate write enable */ + ldr r7, tcr_write_enable /* TCR for write enable */ + str r7, [r6, #OCTOSPI_TCR_CCR] /* write enable instruction */ + ldr r7, ir_write_enable /* IR for write enable */ + str r7, [r6, #OCTOSPI_IR_CCR] /* instruction */ + movs r7, #0 /* silicon bug in L5? dummy write */ + str r7, [r3, #OCTOSPI_AR] /* into AR resolves issue */ + wait_busy + ldr r7, cr_read_status /* indirect read mode */ + str r7, [r3, #OCTOSPI_CR] /* set mode */ + mov r7, r10 /* get dual count */ + str r7, [r3, #OCTOSPI_DLR] /* one or two (for dual) bytes */ + ldr r7, ccr_read_status /* CCR for status read */ + str r7, [r6, #OCTOSPI_CCR_CCR] /* initiate status read */ + ldr r7, tcr_read_status /* TCR for status read */ + str r7, [r6, #OCTOSPI_TCR_CCR] /* instruction */ + ldr r7, ir_read_status /* IR for status read */ + str r7, [r6, #OCTOSPI_IR_CCR] /* instruction */ + movs r7, #0 /* dummy address */ + str r7, [r3, #OCTOSPI_AR] /* into AR (for 8-line mode) */ + ldrb r7, [r5] /* get first status register */ + lsrs r7, r7, #(SPIFLASH_WE+1) /* if first flash not */ + bcc error /* write enabled, then error */ + mov r7, r10 /* get dual bit */ + tst r7, r7 /* dual mode ? */ + beq start_write /* not dual, then ok */ + ldrb r7, [r5] /* get second status register */ + lsrs r7, r7, #(SPIFLASH_WE+1) /* if second flash not */ + bcc error /* write enabled, then error */ +start_write: + wait_busy + ldr r7, cr_page_write /* indirect write mode */ + str r7, [r3, #OCTOSPI_CR] /* set mode */ + mov r7, r2 /* get current start address */ + orrs r7, r7, r1 /* end of current page */ + subs r7, r7, r2 /* count-1 to end of page */ + cmp r7, r0 /* if this count <= remaining */ + bls write_dlr /* then write to end of page */ + mov r7, r0 /* else write all remaining */ +write_dlr: + str r7, [r3, #OCTOSPI_DLR] /* size-1 in DLR register */ + ldr r7, ccr_page_write /* CCR for page write */ + str r7, [r6, #OCTOSPI_CCR_CCR] /* initiate transfer */ + ldr r7, tcr_page_write /* TCR for page write */ + str r7, [r6, #OCTOSPI_TCR_CCR] /* instruction */ + ldr r7, ir_page_write /* IR for page write */ + str r7, [r6, #OCTOSPI_IR_CCR] /* instruction */ + str r2, [r3, #OCTOSPI_AR] /* store SPI start address */ +write_loop: + ldr r7, wp /* get wp */ + cmp r7, #0 /* if wp equals 0 */ + beq exit /* then abort */ + cmp r4, r7 /* check if fifo empty */ + beq write_loop /* wait until not empty */ + ldrb r7, [r4, #0] /* read next byte */ + strb r7, [r5] /* write next byte to DR */ + adds r4, r4, #1 /* increment internal rp */ + cmp r4, r9 /* internal rp beyond end? */ + blo upd_write /* if no, then ok */ + mov r4, r8 /* else wrap around */ +upd_write: + adr r7, rp /* get address of rp */ + str r4, [r7] /* store updated rp */ + adds r2, r2, #1 /* increment address */ + subs r0, r0, #1 /* decrement (count-1) */ + bmi page_end /* stop if no data left */ + tst r2, r1 /* page end ? */ + bne write_loop /* if not, then next byte */ +page_end: + ldr r7, [r3, #OCTOSPI_SR] /* load status */ + lsrs r7, r7, #(SPI_TCF+1) /* shift TCF into C */ + bcc page_end /* loop until TCF set */ + bal wip_loop /* then next page */ + +error: + movs r0, #0 /* return 0xFFFFFFFF */ + subs r0, r0, #2 /* for error */ +exit: + adds r0, r0, #1 /* increment count due to the -1 */ + octospi_abort /* to idle state */ + .align 2 /* align to word, bkpt is 4 words */ + bkpt #0 /* before code end for exit_point */ + .align 2 /* align to word */ + +cr_read_status: + .space 4 /* OCTOSPI_CR value for READ_STATUS command */ +ccr_read_status: + .space 4 /* OCTOSPI_CCR value for READ_STATUS command */ +tcr_read_status: + .space 4 /* OCTOSPI_TCR value for READ_STATUS command */ +ir_read_status: + .space 4 /* OCTOSPI_IR value for READ_STATUS command */ + +cr_write_enable: + .space 4 /* OCTOSPI_CR value for WRITE_ENABLE command */ +ccr_write_enable: + .space 4 /* OCTOSPI_CCR value for WRITE_ENABLE command */ +tcr_write_enable: + .space 4 /* OCTOSPI_TCR value for WRITE_ENABLE command */ +ir_write_enable: + .space 4 /* OCTOSPI_IR value for WRITE_ENABLE command */ + +cr_page_write: + .space 4 /* OCTOSPI_CR value for PAGE_PROG command */ +ccr_page_write: + .space 4 /* OCTOSPI_CCR value for PAGE_PROG command */ +tcr_page_write: + .space 4 /* OCTOSPI_TCR value for PAGE_PROG command */ +ir_page_write: + .space 4 /* OCTOSPI_IR value for PAGE_PROG command */ + + .equ wp, . /* wp, uint32_t */ + .equ rp, wp + 4 /* rp, uint32_t */ + .equ buffer, rp + 4 /* buffer follows right away */ diff --git a/contrib/loaders/flash/stmqspi/stmoctospi_write.inc b/contrib/loaders/flash/stmqspi/stmoctospi_write.inc new file mode 100644 index 0000000..81781a2 --- /dev/null +++ b/contrib/loaders/flash/stmqspi/stmoctospi_write.inc @@ -0,0 +1,21 @@ +/* Autogenerated with ../../../../src/helper/bin2char.sh */ +0x01,0x38,0x01,0x39,0x4f,0x4c,0x1f,0x68,0x7f,0x06,0xff,0x0f,0xba,0x46,0x02,0x25, +0x1f,0x68,0x2f,0x43,0x1f,0x60,0x50,0x25,0xed,0x18,0xb0,0x26,0x76,0x19,0x1f,0x6a, +0xbf,0x09,0xfc,0xd2,0x02,0x27,0x5f,0x62,0x39,0x4f,0x1f,0x60,0x57,0x46,0x1f,0x64, +0x38,0x4f,0x37,0x60,0x38,0x4f,0xb7,0x60,0x38,0x4f,0x37,0x61,0x00,0x27,0x9f,0x64, +0x2f,0x78,0x7f,0x08,0xe3,0xd2,0x57,0x46,0x3f,0x42,0x02,0xd0,0x2f,0x78,0x7f,0x08, +0xdd,0xd2,0x00,0x42,0x55,0xd4,0x1f,0x6a,0xbf,0x09,0xfc,0xd2,0x02,0x27,0x5f,0x62, +0x2f,0x4f,0x1f,0x60,0x2f,0x4f,0x37,0x60,0x2f,0x4f,0xb7,0x60,0x2f,0x4f,0x37,0x61, +0x00,0x27,0x9f,0x64,0x1f,0x6a,0xbf,0x09,0xfc,0xd2,0x02,0x27,0x5f,0x62,0x24,0x4f, +0x1f,0x60,0x57,0x46,0x1f,0x64,0x23,0x4f,0x37,0x60,0x23,0x4f,0xb7,0x60,0x23,0x4f, +0x37,0x61,0x00,0x27,0x9f,0x64,0x2f,0x78,0xbf,0x08,0x30,0xd3,0x57,0x46,0x3f,0x42, +0x02,0xd0,0x2f,0x78,0xbf,0x08,0x2a,0xd3,0x1f,0x6a,0xbf,0x09,0xfc,0xd2,0x02,0x27, +0x5f,0x62,0x1f,0x4f,0x1f,0x60,0x17,0x46,0x0f,0x43,0xbf,0x1a,0x87,0x42,0x00,0xd9, +0x07,0x46,0x1f,0x64,0x1b,0x4f,0x37,0x60,0x1b,0x4f,0xb7,0x60,0x1b,0x4f,0x37,0x61, +0x9a,0x64,0x1b,0x4f,0x00,0x2f,0x14,0xd0,0xbc,0x42,0xfa,0xd0,0x27,0x78,0x2f,0x70, +0x01,0x34,0x4c,0x45,0x00,0xd3,0x44,0x46,0x16,0xa7,0x3c,0x60,0x01,0x32,0x01,0x38, +0x01,0xd4,0x0a,0x42,0xed,0xd1,0x1f,0x6a,0xbf,0x08,0xfc,0xd3,0x87,0xe7,0x00,0x20, +0x02,0x38,0x01,0x30,0x02,0x25,0x1f,0x68,0x2f,0x43,0x1f,0x60,0x00,0xbe,0xc0,0x46, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, diff --git a/contrib/loaders/flash/stmqspi/stmqspi_crc32.S b/contrib/loaders/flash/stmqspi/stmqspi_crc32.S new file mode 100644 index 0000000..bfb2662 --- /dev/null +++ b/contrib/loaders/flash/stmqspi/stmqspi_crc32.S @@ -0,0 +1,108 @@ +/*************************************************************************** + * Copyright (C) 2019 by Andreas Bolsch * + * andreas.bolsch@mni.thm.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + + .text + .syntax unified + .cpu cortex-m0 + .thumb + .thumb_func + +/* Params: + * r0 - total count (bytes), crc32 (out) + * r1 - flash page size + * r2 - address offset into flash + * r3 - QSPI io_base + + * Clobbered: + * r4 - rp + * r5 - address of QSPI_DR + * r7 - tmp + */ + +#include "../../../../src/flash/nor/stmqspi.h" + + .macro qspi_abort + movs r5, #(1<<SPI_ABORT) /* abort bit mask */ + ldr r7, [r3, #QSPI_CR] /* get QSPI_CR register */ + orrs r7, r7, r5 /* set abort bit */ + str r7, [r3, #QSPI_CR] /* store new CR register */ + .endm + + .macro wait_busy +0: + ldr r7, [r3, #QSPI_SR] /* load status */ + lsrs r7, r7, #(SPI_BUSY+1) /* shift BUSY into C */ + bcs 0b /* loop until BUSY cleared */ + movs r7, #(1<<SPI_TCF) /* TCF bitmask */ + str r7, [r3, #QSPI_FCR] /* clear TCF flag */ + .endm + +start: + subs r0, r0, #1 /* decrement count for DLR */ + subs r1, r1, #1 /* page size mask and for DLR */ + movs r4, #0x00 /* initialize crc */ + mvns r4, r4 /* to 0xFFFFFFFF */ +start_read: + qspi_abort /* start in clean state */ + movs r5, #QSPI_DR /* load QSPI_DR address offset */ + adds r5, r5, r3 /* address of QSPI_DR */ + wait_busy + mov r7, r2 /* get current start address */ + orrs r7, r7, r1 /* end of current page */ + subs r7, r7, r2 /* count-1 to end of page */ + cmp r7, r0 /* if this count <= remaining */ + bls write_dlr /* then read to end of page */ + mov r7, r0 /* else read all remaining */ +write_dlr: + str r7, [r3, #QSPI_DLR] /* size-1 in DLR register */ + ldr r7, ccr_page_read /* CCR for page read */ + str r7, [r3, #QSPI_CCR] /* initiate transfer */ + str r2, [r3, #QSPI_AR] /* store SPI start address */ + ldr r7, [r3, #QSPI_SR] /* wait for command startup */ + ldr r6, =0x04C11DB7 /* CRC32 polynomial */ +read_loop: + ldrb r7, [r5] /* read next byte from DR */ + lsls r7, r7, #24 /* shift into msb */ + eors r4, r4, r7 + .rept 8 /* unrolled bit loop */ + asrs r7, r4, #31 /* copy bit 31 into bits 0 to 31 */ + ands r7, r7, r6 /* r7 neg. -> CRC32XOR, pos. -> 0x0 */ + lsls r4, r4, #1 /* shift result */ + eors r4, r4, r7 /* eor by CRC32XOR or 0x0 */ + .endr + adds r2, r2, #1 /* increment address */ + subs r0, r0, #1 /* decrement (count-1) */ + bmi exit /* stop if no data left */ + tst r2, r1 /* page end ? */ + bne read_loop /* if not, then next byte */ +page_end: + bal start_read /* then next page */ + .pool + +exit: + mvns r0, r4 /* invert to get final result */ + qspi_abort /* to idle state */ + .align 2 /* align to word, bkpt is 4 words */ + bkpt #0 /* before code end for exit_point */ + .align 2 /* align to word */ + + .space 4 /* not used */ +ccr_page_read: + .space 4 /* QSPI_CCR value for read command */ + .space 4 /* not used */ + .space 4 /* not used */ diff --git a/contrib/loaders/flash/stmqspi/stmqspi_crc32.inc b/contrib/loaders/flash/stmqspi/stmqspi_crc32.inc new file mode 100644 index 0000000..b532a48 --- /dev/null +++ b/contrib/loaders/flash/stmqspi/stmqspi_crc32.inc @@ -0,0 +1,12 @@ +/* Autogenerated with ../../../../src/helper/bin2char.sh */ +0x01,0x38,0x01,0x39,0x00,0x24,0xe4,0x43,0x02,0x25,0x1f,0x68,0x2f,0x43,0x1f,0x60, +0x20,0x25,0xed,0x18,0x9f,0x68,0xbf,0x09,0xfc,0xd2,0x02,0x27,0xdf,0x60,0x17,0x46, +0x0f,0x43,0xbf,0x1a,0x87,0x42,0x00,0xd9,0x07,0x46,0x1f,0x61,0x1c,0x4f,0x5f,0x61, +0x9a,0x61,0x9f,0x68,0x14,0x4e,0x2f,0x78,0x3f,0x06,0x7c,0x40,0xe7,0x17,0x37,0x40, +0x64,0x00,0x7c,0x40,0xe7,0x17,0x37,0x40,0x64,0x00,0x7c,0x40,0xe7,0x17,0x37,0x40, +0x64,0x00,0x7c,0x40,0xe7,0x17,0x37,0x40,0x64,0x00,0x7c,0x40,0xe7,0x17,0x37,0x40, +0x64,0x00,0x7c,0x40,0xe7,0x17,0x37,0x40,0x64,0x00,0x7c,0x40,0xe7,0x17,0x37,0x40, +0x64,0x00,0x7c,0x40,0xe7,0x17,0x37,0x40,0x64,0x00,0x7c,0x40,0x01,0x32,0x01,0x38, +0x04,0xd4,0x0a,0x42,0xd7,0xd1,0xbf,0xe7,0xb7,0x1d,0xc1,0x04,0xe0,0x43,0x02,0x25, +0x1f,0x68,0x2f,0x43,0x1f,0x60,0xc0,0x46,0x00,0xbe,0xc0,0x46,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, diff --git a/contrib/loaders/flash/stmqspi/stmqspi_erase_check.S b/contrib/loaders/flash/stmqspi/stmqspi_erase_check.S new file mode 100644 index 0000000..d011103 --- /dev/null +++ b/contrib/loaders/flash/stmqspi/stmqspi_erase_check.S @@ -0,0 +1,91 @@ +/*************************************************************************** + * Copyright (C) 2019 by Andreas Bolsch * + * andreas.bolsch@mni.thm.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + + .text + .syntax unified + .cpu cortex-m0 + .thumb + .thumb_func + +/* Params: + * r0 - sector count + * r1 - QSPI io_base + + * Clobbered: + * r2 - r7 tmp */ + +#include "../../../../src/flash/nor/stmqspi.h" + + .macro qspi_abort + movs r4, #(1<<SPI_ABORT) /* abort bit mask */ + ldr r7, [r1, #QSPI_CR] /* get QSPI_CR register */ + orrs r7, r7, r4 /* set abort bit */ + str r7, [r1, #QSPI_CR] /* store new CR register */ + .endm + + .macro wait_busy +0: + ldr r7, [r1, #QSPI_SR] /* load status */ + lsrs r7, r7, #(SPI_BUSY+1) /* shift BUSY into C */ + bcs 0b /* loop until BUSY cleared */ + movs r7, #(1<<SPI_TCF) /* TCF bitmask */ + str r7, [r1, #QSPI_FCR] /* clear TCF flag */ + .endm + +start: + adr r2, buffer /* pointer to start of buffer */ + movs r3, #QSPI_DR /* load QSPI_DR address offset */ + add r3, r3, r1 /* address of QSPI_DR */ +sector_start: + qspi_abort /* start in clean state */ + ldmia r2!, {r4, r5, r6} /* load address offset, length, initial value */ + subs r2, r2, #8 /* point to length */ + subs r5, r5, #1 /* decrement sector length for DLR */ + wait_busy + str r5, [r1, #QSPI_DLR] /* size-1 in DLR register */ + ldr r7, ccr_page_read /* CCR for page read */ + str r7, [r1, #QSPI_CCR] /* initiate transfer */ + str r4, [r1, #QSPI_AR] /* store SPI start address */ + ldr r7, [r1, #QSPI_SR] /* wait for command startup */ +read_loop: + ldrb r4, [r3] /* read next byte from DR */ + movs r7, #0xFF /* fill bits 8-15 */ + lsls r7, r7, #8 /* with ones */ + orrs r4, r4, r7 /* copy ones to left of read byte */ + ands r6, r6, r4 /* and read byte to result */ + lsls r4, r4, #8 /* shift result into higher byte */ + orrs r6, r6, r4 /* or read byte to result */ + subs r5, r5, #1 /* decrement byte (count-1) */ + bpl read_loop /* again if sector not completed */ + adds r5, r5, #1 /* increment count due to the -1 */ + stmia r2!, {r5, r6} /* save final count and result for sector */ + subs r0, r0, #1 /* decrement sector count */ + bne sector_start /* next sector? */ + qspi_abort /* to idle state */ + .align 2 /* align to word, bkpt is 4 words */ + bkpt #0 /* before code end for exit_point */ + .align 2 /* align to word */ + + .space 4 /* not used */ +ccr_page_read: + .space 4 /* QSPI_CCR value for read command */ + .space 4 /* not used */ + .space 4 /* not used */ + + .equ buffer, . + diff --git a/contrib/loaders/flash/stmqspi/stmqspi_erase_check.inc b/contrib/loaders/flash/stmqspi/stmqspi_erase_check.inc new file mode 100644 index 0000000..3bf7898 --- /dev/null +++ b/contrib/loaders/flash/stmqspi/stmqspi_erase_check.inc @@ -0,0 +1,7 @@ +/* Autogenerated with ../../../../src/helper/bin2char.sh */ +0x17,0xa2,0x20,0x23,0x0b,0x44,0x02,0x24,0x0f,0x68,0x27,0x43,0x0f,0x60,0x70,0xca, +0x08,0x3a,0x01,0x3d,0x8f,0x68,0xbf,0x09,0xfc,0xd2,0x02,0x27,0xcf,0x60,0x0d,0x61, +0x0c,0x4f,0x4f,0x61,0x8c,0x61,0x8f,0x68,0x1c,0x78,0xff,0x27,0x3f,0x02,0x3c,0x43, +0x26,0x40,0x24,0x02,0x26,0x43,0x01,0x3d,0xf6,0xd5,0x01,0x35,0x60,0xc2,0x01,0x38, +0xe1,0xd1,0x02,0x24,0x0f,0x68,0x27,0x43,0x0f,0x60,0xc0,0x46,0x00,0xbe,0xc0,0x46, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, diff --git a/contrib/loaders/flash/stmqspi/stmqspi_read.S b/contrib/loaders/flash/stmqspi/stmqspi_read.S new file mode 100644 index 0000000..b84d4eb --- /dev/null +++ b/contrib/loaders/flash/stmqspi/stmqspi_read.S @@ -0,0 +1,127 @@ +/*************************************************************************** + * Copyright (C) 2019 by Andreas Bolsch * + * andreas.bolsch@mni.thm.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + + .text + .syntax unified + .cpu cortex-m0 + .thumb + .thumb_func + +/* Params: + * r0 - total count (bytes), remaining bytes (out, 0 means successful) + * r1 - flash page size + * r2 - address offset into flash + * r3 - QSPI io_base + * r8 - fifo start + * r9 - fifo end + 1 + + * Clobbered: + * r4 - wp + * r5 - address of QSPI_DR + * r7 - tmp + */ + +#include "../../../../src/flash/nor/stmqspi.h" + + .macro qspi_abort + movs r5, #(1<<SPI_ABORT) /* abort bit mask */ + ldr r7, [r3, #QSPI_CR] /* get QSPI_CR register */ + orrs r7, r7, r5 /* set abort bit */ + str r7, [r3, #QSPI_CR] /* store new CR register */ + .endm + + .macro wait_busy +0: + ldr r7, [r3, #QSPI_SR] /* load status */ + lsrs r7, r7, #(SPI_BUSY+1) /* shift BUSY into C */ + bcs 0b /* loop until BUSY cleared */ + movs r7, #(1<<SPI_TCF) /* TCF bitmask */ + str r7, [r3, #QSPI_FCR] /* clear TCF flag */ + .endm + +start: + subs r0, r0, #1 /* decrement count for DLR */ + subs r1, r1, #1 /* page size mask and for DLR */ + ldr r4, wp /* load wp */ +start_read: + qspi_abort /* start in clean state */ + movs r5, #QSPI_DR /* load QSPI_DR address offset */ + adds r5, r5, r3 /* address of QSPI_DR */ + wait_busy + mov r7, r2 /* get current start address */ + orrs r7, r7, r1 /* end of current page */ + subs r7, r7, r2 /* count-1 to end of page */ + cmp r7, r0 /* if this count <= remaining */ + bls write_dlr /* then read to end of page */ + mov r7, r0 /* else read all remaining */ +write_dlr: + str r7, [r3, #QSPI_DLR] /* size-1 in DLR register */ + ldr r7, ccr_page_read /* CCR for page read */ + str r7, [r3, #QSPI_CCR] /* initiate transfer */ + str r2, [r3, #QSPI_AR] /* store SPI start address */ + ldr r7, [r3, #QSPI_SR] /* wait for command startup */ +read_loop: + ldrb r7, [r5] /* read next byte from DR */ + strb r7, [r4, #0] /* write next byte */ + adds r4, r4, #1 /* increment internal wp */ + cmp r4, r9 /* internal wp beyond end? */ + blo wait_fifo /* if no, then ok */ + mov r4, r8 /* else wrap around */ +wait_fifo: + ldr r7, rp /* get rp */ + cmp r7, #0 /* if rp equals 0 */ + beq exit /* then abort */ + cmp r4, r7 /* check if fifo full */ + beq wait_fifo /* wait until not full */ + adr r7, wp /* get address of wp */ + str r4, [r7] /* store updated wp */ + adds r2, r2, #1 /* increment address */ + subs r0, r0, #1 /* decrement (count-1) */ + bmi exit /* stop if no data left */ + tst r2, r1 /* page end ? */ + bne read_loop /* if not, then next byte */ +page_end: + bal start_read /* then next page */ + +exit: + adds r0, r0, #1 /* increment count due to the -1 */ + qspi_abort /* to idle state */ + + .align 2 /* align to word, bkpt is 4 words */ + bkpt #0 /* before code end for exit_point */ + .align 2 /* align to word */ + + .space 4 /* not used */ + .space 4 /* not used */ + .space 4 /* not used */ + .space 4 /* not used */ + + .space 4 /* not used */ + .space 4 /* not used */ + .space 4 /* not used */ + .space 4 /* not used */ + + .space 4 /* not used */ +ccr_page_read: + .space 4 /* QSPI_CCR value for read command */ + .space 4 /* not used */ + .space 4 /* not used */ + + .equ wp, . /* wp, uint32_t */ + .equ rp, wp + 4 /* rp, uint32_t */ + .equ buffer, rp + 4 /* buffer follows right away */ diff --git a/contrib/loaders/flash/stmqspi/stmqspi_read.inc b/contrib/loaders/flash/stmqspi/stmqspi_read.inc new file mode 100644 index 0000000..934b9b1 --- /dev/null +++ b/contrib/loaders/flash/stmqspi/stmqspi_read.inc @@ -0,0 +1,11 @@ +/* Autogenerated with ../../../../src/helper/bin2char.sh */ +0x01,0x38,0x01,0x39,0x24,0x4c,0x02,0x25,0x1f,0x68,0x2f,0x43,0x1f,0x60,0x20,0x25, +0xed,0x18,0x9f,0x68,0xbf,0x09,0xfc,0xd2,0x02,0x27,0xdf,0x60,0x17,0x46,0x0f,0x43, +0xbf,0x1a,0x87,0x42,0x00,0xd9,0x07,0x46,0x1f,0x61,0x18,0x4f,0x5f,0x61,0x9a,0x61, +0x9f,0x68,0x2f,0x78,0x27,0x70,0x01,0x34,0x4c,0x45,0x00,0xd3,0x44,0x46,0x17,0x4f, +0x00,0x2f,0x09,0xd0,0xbc,0x42,0xfa,0xd0,0x13,0xa7,0x3c,0x60,0x01,0x32,0x01,0x38, +0x02,0xd4,0x0a,0x42,0xed,0xd1,0xd6,0xe7,0x01,0x30,0x02,0x25,0x1f,0x68,0x2f,0x43, +0x1f,0x60,0xc0,0x46,0x00,0xbe,0xc0,0x46,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, diff --git a/contrib/loaders/flash/stmqspi/stmqspi_write.S b/contrib/loaders/flash/stmqspi/stmqspi_write.S new file mode 100644 index 0000000..40953ac --- /dev/null +++ b/contrib/loaders/flash/stmqspi/stmqspi_write.S @@ -0,0 +1,177 @@ +/*************************************************************************** + * Copyright (C) 2016 - 2018 by Andreas Bolsch * + * andreas.bolsch@mni.thm.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + + .text + .syntax unified + .cpu cortex-m0 + .thumb + .thumb_func + +/* Params: + * r0 - total count (bytes), remaining bytes (out, 0 means successful) + * r1 - flash page size + * r2 - address offset into flash + * r3 - QSPI io_base + * r8 - fifo start + * r9 - fifo end + 1 + + * Clobbered: + * r4 - rp + * r5 - address of QSPI_DR + * r7 - tmp + * r10 - single 0x0 / dual 0x1 + */ + +#include "../../../../src/flash/nor/stmqspi.h" + + .macro qspi_abort + movs r5, #(1<<SPI_ABORT) /* abort bit mask */ + ldr r7, [r3, #QSPI_CR] /* get QSPI_CR register */ + orrs r7, r7, r5 /* set abort bit */ + str r7, [r3, #QSPI_CR] /* store new CR register */ + .endm + + .macro wait_busy +0: + ldr r7, [r3, #QSPI_SR] /* load status */ + lsrs r7, r7, #(SPI_BUSY+1) /* shift BUSY into C */ + bcs 0b /* loop until BUSY cleared */ + movs r7, #(1<<SPI_TCF) /* TCF bitmask */ + str r7, [r3, #QSPI_FCR] /* clear TCF flag */ + .endm + +start: + subs r0, r0, #1 /* decrement count for DLR */ + subs r1, r1, #1 /* page size mask and for DLR */ + ldr r4, rp /* load rp */ + ldr r7, [r3, #QSPI_CR] /* get QSPI_CR register */ + lsls r7, r7, #(31-SPI_DUAL_FLASH) /* clear higher order bits */ + lsrs r7, r7, #31 /* DUAL_FLASH bit into bit 0 */ + mov r10, r7 /* save in r10 */ +wip_loop: + qspi_abort /* start in clean state */ + movs r5, #QSPI_DR /* load QSPI_DR address offset */ + adds r5, r5, r3 /* address of QSPI_DR */ + wait_busy + mov r7, r10 /* get dual bit */ + str r7, [r3, #QSPI_DLR] /* one or two (for dual) bytes */ + ldr r7, ccr_read_status /* CCR for status read */ + str r7, [r3, #QSPI_CCR] /* initiate status read */ + ldr r7, [r3, #QSPI_SR] /* wait for command startup */ + ldrb r7, [r5] /* get first status register */ + lsrs r7, r7, #(SPIFLASH_BSY+1) /* if first flash busy, */ + bcs wip_loop /* then poll again */ + mov r7, r10 /* get dual bit */ + tst r7, r7 /* dual mode ? */ + beq write_enable /* not dual, then ok */ + ldrb r7, [r5] /* get second status register */ + lsrs r7, r7, #(SPIFLASH_BSY+1) /* if second flash busy, */ + bcs wip_loop /* then poll again */ +write_enable: + tst r0, r0 /* test residual count */ + bmi exit /* if negative, then finished */ + wait_busy + ldr r7, ccr_write_enable /* CCR for write enable */ + str r7, [r3, #QSPI_CCR] /* initiate write enable */ + wait_busy + mov r7, r10 /* get dual bit */ + str r7, [r3, #QSPI_DLR] /* one or two (for dual) bytes */ + ldr r7, ccr_read_status /* CCR for status read */ + str r7, [r3, #QSPI_CCR] /* initiate status read */ + ldr r7, [r3, #QSPI_SR] /* wait for command startup */ + ldrb r7, [r5] /* get first status register */ + lsrs r7, r7, #(SPIFLASH_WE+1) /* if first flash not */ + bcc error /* write enabled, then error */ + mov r7, r10 /* get dual bit */ + tst r7, r7 /* dual mode ? */ + beq start_write /* not dual, then ok */ + ldrb r7, [r5] /* get second status register */ + lsrs r7, r7, #(SPIFLASH_WE+1) /* if second flash not */ + bcc error /* write enabled, then error */ +start_write: + wait_busy + mov r7, r2 /* get current start address */ + orrs r7, r7, r1 /* end of current page */ + subs r7, r7, r2 /* count-1 to end of page */ + cmp r7, r0 /* if this count <= remaining */ + bls write_dlr /* then write to end of page */ + mov r7, r0 /* else write all remaining */ +write_dlr: + str r7, [r3, #QSPI_DLR] /* size-1 in DLR register */ + ldr r7, ccr_page_write /* CCR for page write */ + str r7, [r3, #QSPI_CCR] /* initiate transfer */ + str r2, [r3, #QSPI_AR] /* store SPI start address */ + ldr r7, [r3, #QSPI_SR] /* wait for command startup */ +write_loop: + ldr r7, wp /* get wp */ + cmp r7, #0 /* if wp equals 0 */ + beq exit /* then abort */ + cmp r4, r7 /* check if fifo empty */ + beq write_loop /* wait until not empty */ + ldrb r7, [r4, #0] /* read next byte */ + strb r7, [r5] /* write next byte to DR */ + adds r4, r4, #1 /* increment internal rp */ + cmp r4, r9 /* internal rp beyond end? */ + blo upd_write /* if no, then ok */ + mov r4, r8 /* else wrap around */ +upd_write: + adr r7, rp /* get address of rp */ + str r4, [r7] /* store updated rp */ + adds r2, r2, #1 /* increment address */ + subs r0, r0, #1 /* decrement (count-1) */ + bmi page_end /* stop if no data left */ + tst r2, r1 /* page end ? */ + bne write_loop /* if not, then next byte */ +page_end: + ldr r7, [r3, #QSPI_SR] /* load status */ + lsrs r7, r7, #(SPI_TCF+1) /* shift TCF into C */ + bcc page_end /* loop until TCF set */ + bal wip_loop /* then next page */ + +error: + movs r0, #0 /* return 0xFFFFFFFF */ + subs r0, r0, #2 /* for error */ +exit: + adds r0, r0, #1 /* increment count due to the -1 */ + qspi_abort /* to idle state */ + + .align 2 /* align to word, bkpt is 4 words */ + bkpt #0 /* before code end for exit_point */ + .align 2 /* align to word */ + + .space 4 /* not used */ +ccr_read_status: + .space 4 /* QSPI_CCR value for READ_STATUS command */ + .space 4 /* not used */ + .space 4 /* not used */ + + .space 4 /* not used */ +ccr_write_enable: + .space 4 /* QSPI_CCR value for WRITE_ENABLE command */ + .space 4 /* not used */ + .space 4 /* not used */ + + .space 4 /* not used */ +ccr_page_write: + .space 4 /* QSPI_CCR value for PAGE_PROG command */ + .space 4 /* not used */ + .space 4 /* not used */ + + .equ wp, . /* wp, uint32_t */ + .equ rp, wp + 4 /* rp, uint32_t */ + .equ buffer, rp + 4 /* buffer follows right away */ diff --git a/contrib/loaders/flash/stmqspi/stmqspi_write.inc b/contrib/loaders/flash/stmqspi/stmqspi_write.inc new file mode 100644 index 0000000..6c063c8 --- /dev/null +++ b/contrib/loaders/flash/stmqspi/stmqspi_write.inc @@ -0,0 +1,18 @@ +/* Autogenerated with ../../../../src/helper/bin2char.sh */ +0x01,0x38,0x01,0x39,0x41,0x4c,0x1f,0x68,0x7f,0x06,0xff,0x0f,0xba,0x46,0x02,0x25, +0x1f,0x68,0x2f,0x43,0x1f,0x60,0x20,0x25,0xed,0x18,0x9f,0x68,0xbf,0x09,0xfc,0xd2, +0x02,0x27,0xdf,0x60,0x57,0x46,0x1f,0x61,0x2c,0x4f,0x5f,0x61,0x9f,0x68,0x2f,0x78, +0x7f,0x08,0xec,0xd2,0x57,0x46,0x3f,0x42,0x02,0xd0,0x2f,0x78,0x7f,0x08,0xe6,0xd2, +0x00,0x42,0x41,0xd4,0x9f,0x68,0xbf,0x09,0xfc,0xd2,0x02,0x27,0xdf,0x60,0x27,0x4f, +0x5f,0x61,0x9f,0x68,0xbf,0x09,0xfc,0xd2,0x02,0x27,0xdf,0x60,0x57,0x46,0x1f,0x61, +0x1e,0x4f,0x5f,0x61,0x9f,0x68,0x2f,0x78,0xbf,0x08,0x2b,0xd3,0x57,0x46,0x3f,0x42, +0x02,0xd0,0x2f,0x78,0xbf,0x08,0x25,0xd3,0x9f,0x68,0xbf,0x09,0xfc,0xd2,0x02,0x27, +0xdf,0x60,0x17,0x46,0x0f,0x43,0xbf,0x1a,0x87,0x42,0x00,0xd9,0x07,0x46,0x1f,0x61, +0x1a,0x4f,0x5f,0x61,0x9a,0x61,0x9f,0x68,0x1b,0x4f,0x00,0x2f,0x14,0xd0,0xbc,0x42, +0xfa,0xd0,0x27,0x78,0x2f,0x70,0x01,0x34,0x4c,0x45,0x00,0xd3,0x44,0x46,0x17,0xa7, +0x3c,0x60,0x01,0x32,0x01,0x38,0x01,0xd4,0x0a,0x42,0xed,0xd1,0x9f,0x68,0xbf,0x08, +0xfc,0xd3,0xa4,0xe7,0x00,0x20,0x02,0x38,0x01,0x30,0x02,0x25,0x1f,0x68,0x2f,0x43, +0x1f,0x60,0xc0,0x46,0x00,0xbe,0xc0,0x46,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |