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