From 90751c4cfbe53d168810101ccf116538d09ad98a Mon Sep 17 00:00:00 2001 From: Thomas Altenbach Date: Sat, 5 Jul 2025 23:59:07 +0200 Subject: [PATCH 1/4] drivers: flash: stm32_qspi: Use sample shift also in single flash mode The QSPI 1/2 sample shift (SSHIFT) was only enabled in dual flash mode. This feature is useful to guarantee that data is ready at the sampling moment, even if the signals are a bit delayed due to PCB constraints. Therefore, it should be enabled when possible (only supported in STR mode). Signed-off-by: Thomas Altenbach --- drivers/flash/flash_stm32_qspi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/flash/flash_stm32_qspi.c b/drivers/flash/flash_stm32_qspi.c index 5ce9f08359298..1441dfc7aa4cc 100644 --- a/drivers/flash/flash_stm32_qspi.c +++ b/drivers/flash/flash_stm32_qspi.c @@ -1510,6 +1510,7 @@ static int flash_stm32_qspi_init(const struct device *dev) dev_data->hqspi.Init.ClockPrescaler = prescaler; /* Give a bit position from 0 to 31 to the HAL init minus 1 for the DCR1 reg */ dev_data->hqspi.Init.FlashSize = find_lsb_set(dev_cfg->flash_size) - 2; + dev_data->hqspi.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE; #if DT_PROP(DT_NODELABEL(quadspi), dual_flash) && defined(QUADSPI_CR_DFM) /* * When the DTS has , it means Dual Flash Mode @@ -1517,7 +1518,6 @@ static int flash_stm32_qspi_init(const struct device *dev) * else the magic nb is wrong (0x46465353) * That means that the Dual Flash config is set after the SFDP sequence */ - dev_data->hqspi.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE; dev_data->hqspi.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_3_CYCLE; dev_data->hqspi.Init.DualFlash = QSPI_DUALFLASH_DISABLE; /* Set Dual Flash Mode only on MemoryMapped */ From 056624287185be5ec54a582d82d0661bd5cf8971 Mon Sep 17 00:00:00 2001 From: Thomas Altenbach Date: Sun, 6 Jul 2025 01:28:13 +0200 Subject: [PATCH 2/4] dts: bindings: flash_controller: Add CS high time to stm32-qspi-nor The STM32 QUADSPI peripheral allows to configure, in clock cycles, the duration the chip select signal must stay high between each command sent to the flash memory controller (QUADSPI_DCR_CSHT). Currently, this value is set by the flash driver to 1 clock cycle in single flash mode and 3 clock cycles in dual flash mode. However, the minimal duration depends on the flash memory (typically 30-50 ns) and the number of clock cycles on the QSPI's clock frequency. So, adding this new property allows to select the value of CSHT to match the requirement of the flash memory used. Also note that in single flash mode, the current configuration is out-of-spec for most flash memories. A default value of 4 clock cycles is used. This is enough for all board configurations currently provided by Zephyr (flash memory needing up 50 ns and frequency up to 80 MHz). Signed-off-by: Thomas Altenbach --- dts/bindings/flash_controller/st,stm32-qspi-nor.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/dts/bindings/flash_controller/st,stm32-qspi-nor.yaml b/dts/bindings/flash_controller/st,stm32-qspi-nor.yaml index 1680610356b66..5d4e2ef562e0c 100644 --- a/dts/bindings/flash_controller/st,stm32-qspi-nor.yaml +++ b/dts/bindings/flash_controller/st,stm32-qspi-nor.yaml @@ -80,3 +80,10 @@ properties: type: int default: 8 description: The number of dummy-cycles required when reading jedec id + + cs-high-time: + type: int + default: 4 + description: | + Minimum number of QSPI clock cycles the chip select signal must remain + high between commands issued to the flash memory. From 8de251d1727ce66d950c62818be5743044bb2397 Mon Sep 17 00:00:00 2001 From: Thomas Altenbach Date: Sun, 6 Jul 2025 01:58:06 +0200 Subject: [PATCH 3/4] boards: Add cs-high-time for all STM32 boards using QSPI flash This commit set the value of the cs-high-time property for all STM32-based boards defined in Zephyr and having a QSPI flash memory. The value is chosen according to the flash memory requirements (often named 'tSHSL' in the datasheets) and the maximum QSPI frequency. Signed-off-by: Thomas Altenbach --- boards/alientek/pandora_stm32l475/pandora_stm32l475.dts | 1 + boards/arduino/giga_r1/arduino_giga_r1_stm32h747xx_m7.dts | 1 + .../arduino/nicla_vision/arduino_nicla_vision_stm32h747xx_m7.dts | 1 + boards/arduino/portenta_h7/arduino_portenta_h7-common.dtsi | 1 + boards/fanke/fk743m5_xih6/fk743m5_xih6.dts | 1 + boards/fanke/fk750m1_vbt6/fk750m1_vbt6.dts | 1 + boards/st/disco_l475_iot1/disco_l475_iot1.dts | 1 + boards/st/stm32f412g_disco/stm32f412g_disco.dts | 1 + boards/st/stm32f723e_disco/stm32f723e_disco.dts | 1 + boards/st/stm32f746g_disco/stm32f746g_disco.dts | 1 + boards/st/stm32f7508_dk/stm32f7508_dk.dts | 1 + boards/st/stm32f769i_disco/stm32f769i_disco.dts | 1 + boards/st/stm32h745i_disco/stm32h745i_disco_stm32h745xx_m7.dts | 1 + boards/st/stm32h747i_disco/stm32h747i_disco_stm32h747xx_m7.dts | 1 + boards/st/stm32h750b_dk/stm32h750b_dk.dts | 1 + boards/st/stm32h757i_eval/stm32h757i_eval_stm32h757xx_m7.dts | 1 + boards/st/stm32l496g_disco/stm32l496g_disco.dts | 1 + boards/vcc-gnd/yd_stm32h750vb/yd_stm32h750vb.dts | 1 + boards/weact/mini_stm32h743/mini_stm32h743.dts | 1 + samples/subsys/fs/littlefs/boards/nucleo_h743zi.overlay | 1 + 20 files changed, 20 insertions(+) diff --git a/boards/alientek/pandora_stm32l475/pandora_stm32l475.dts b/boards/alientek/pandora_stm32l475/pandora_stm32l475.dts index a8025833d941a..b0f43f21b6754 100644 --- a/boards/alientek/pandora_stm32l475/pandora_stm32l475.dts +++ b/boards/alientek/pandora_stm32l475/pandora_stm32l475.dts @@ -82,6 +82,7 @@ reg = <0>; size = ; /* 128 Mbits */ qspi-max-frequency = <80000000>; + cs-high-time = <4>; /* >= 50 ns */ jedec-id = [ef 40 18]; spi-bus-width = <4>; writeoc = "PP_1_1_4"; diff --git a/boards/arduino/giga_r1/arduino_giga_r1_stm32h747xx_m7.dts b/boards/arduino/giga_r1/arduino_giga_r1_stm32h747xx_m7.dts index 4feba7fb1f37d..78dc27882c136 100644 --- a/boards/arduino/giga_r1/arduino_giga_r1_stm32h747xx_m7.dts +++ b/boards/arduino/giga_r1/arduino_giga_r1_stm32h747xx_m7.dts @@ -178,6 +178,7 @@ reg = <0>; size = ; /* 128 Mbits */ qspi-max-frequency = <72000000>; + cs-high-time = <4>; /* >= 50 ns */ status = "okay"; partitions { diff --git a/boards/arduino/nicla_vision/arduino_nicla_vision_stm32h747xx_m7.dts b/boards/arduino/nicla_vision/arduino_nicla_vision_stm32h747xx_m7.dts index 0cc4ef217a189..bf2e4dce029ac 100644 --- a/boards/arduino/nicla_vision/arduino_nicla_vision_stm32h747xx_m7.dts +++ b/boards/arduino/nicla_vision/arduino_nicla_vision_stm32h747xx_m7.dts @@ -170,6 +170,7 @@ zephyr_i2c: &i2c1 { reg = <0>; size = ; /* 128 Mbits */ qspi-max-frequency = <72000000>; + cs-high-time = <4>; /* >= 50 ns */ status = "okay"; partitions { diff --git a/boards/arduino/portenta_h7/arduino_portenta_h7-common.dtsi b/boards/arduino/portenta_h7/arduino_portenta_h7-common.dtsi index b95a2e6799990..2887776f93b7b 100644 --- a/boards/arduino/portenta_h7/arduino_portenta_h7-common.dtsi +++ b/boards/arduino/portenta_h7/arduino_portenta_h7-common.dtsi @@ -184,6 +184,7 @@ zephyr_i2c: &i2c1 { reg = <0>; size = ; /* 128 Mbits */ qspi-max-frequency = < 40000000 >; + cs-high-time = <2>; /* >= 30 ns */ sfdp-bfp = [ e5 20 f1 ff ff ff ff 07 44 eb 08 6b 08 3b 04 bb fe ff ff ff ff ff 00 ff ff ff 44 eb 0c 20 0f 52 10 d8 00 ff 82 41 bd 00 81 e5 7b c6 44 03 67 38 diff --git a/boards/fanke/fk743m5_xih6/fk743m5_xih6.dts b/boards/fanke/fk743m5_xih6/fk743m5_xih6.dts index 798418d379f2b..799c8f01a1127 100644 --- a/boards/fanke/fk743m5_xih6/fk743m5_xih6.dts +++ b/boards/fanke/fk743m5_xih6/fk743m5_xih6.dts @@ -101,6 +101,7 @@ reg = <0>; size = ; /* 64 Mbits */ qspi-max-frequency = <40000000>; + cs-high-time = <2>; /* >= 50 ns */ status = "okay"; spi-bus-width = <4>; writeoc = "PP_1_1_4"; diff --git a/boards/fanke/fk750m1_vbt6/fk750m1_vbt6.dts b/boards/fanke/fk750m1_vbt6/fk750m1_vbt6.dts index c3c1b6418bfa6..6b2269d963f0f 100644 --- a/boards/fanke/fk750m1_vbt6/fk750m1_vbt6.dts +++ b/boards/fanke/fk750m1_vbt6/fk750m1_vbt6.dts @@ -123,6 +123,7 @@ reg = <0>; size = ; /* 64 Mbits */ qspi-max-frequency = <40000000>; + cs-high-time = <2>; /* >= 50 ns */ status = "okay"; spi-bus-width = <4>; writeoc = "PP_1_1_4"; diff --git a/boards/st/disco_l475_iot1/disco_l475_iot1.dts b/boards/st/disco_l475_iot1/disco_l475_iot1.dts index 6b30f7139654a..dc1ce05dbace8 100644 --- a/boards/st/disco_l475_iot1/disco_l475_iot1.dts +++ b/boards/st/disco_l475_iot1/disco_l475_iot1.dts @@ -330,6 +330,7 @@ zephyr_udc0: &usbotg_fs { reg = <0>; size = ; /* 64 Mbits */ qspi-max-frequency = <50000000>; + cs-high-time = <2>; /* >= 30 ns */ status = "okay"; partitions { diff --git a/boards/st/stm32f412g_disco/stm32f412g_disco.dts b/boards/st/stm32f412g_disco/stm32f412g_disco.dts index 35f48d21b3c3b..3f83c2fee3d1f 100644 --- a/boards/st/stm32f412g_disco/stm32f412g_disco.dts +++ b/boards/st/stm32f412g_disco/stm32f412g_disco.dts @@ -161,6 +161,7 @@ reg = <0>; size = ; /* 128 Mbits */ qspi-max-frequency = <72000000>; + cs-high-time = <4>; /* >= 50 ns */ status = "okay"; }; }; diff --git a/boards/st/stm32f723e_disco/stm32f723e_disco.dts b/boards/st/stm32f723e_disco/stm32f723e_disco.dts index 134ec57806e7c..624c7b101ea5c 100644 --- a/boards/st/stm32f723e_disco/stm32f723e_disco.dts +++ b/boards/st/stm32f723e_disco/stm32f723e_disco.dts @@ -132,6 +132,7 @@ reg = <0>; size = ; /* 512 Mbits */ qspi-max-frequency = <8000000>; + cs-high-time = <3>; /* >= 30 ns */ status = "okay"; spi-bus-width = <4>; writeoc = "PP_1_4_4"; diff --git a/boards/st/stm32f746g_disco/stm32f746g_disco.dts b/boards/st/stm32f746g_disco/stm32f746g_disco.dts index 3cde07f09c266..66f7f642e13c7 100644 --- a/boards/st/stm32f746g_disco/stm32f746g_disco.dts +++ b/boards/st/stm32f746g_disco/stm32f746g_disco.dts @@ -215,6 +215,7 @@ zephyr_udc0: &usbotg_fs { reg = <0>; size = ; /* 128 Mbits */ qspi-max-frequency = <72000000>; + cs-high-time = <4>; /* >= 50 ns */ status = "okay"; partitions { diff --git a/boards/st/stm32f7508_dk/stm32f7508_dk.dts b/boards/st/stm32f7508_dk/stm32f7508_dk.dts index 91cccf4521fa4..d0884a18248c7 100644 --- a/boards/st/stm32f7508_dk/stm32f7508_dk.dts +++ b/boards/st/stm32f7508_dk/stm32f7508_dk.dts @@ -202,6 +202,7 @@ zephyr_udc0: &usbotg_fs { reg = <0>; size = ; /* 128 Mbits */ qspi-max-frequency = <72000000>; + cs-high-time = <4>; /* >= 50 ns */ status = "okay"; partitions { diff --git a/boards/st/stm32f769i_disco/stm32f769i_disco.dts b/boards/st/stm32f769i_disco/stm32f769i_disco.dts index 822b8ebf900b4..0aa96890bc701 100644 --- a/boards/st/stm32f769i_disco/stm32f769i_disco.dts +++ b/boards/st/stm32f769i_disco/stm32f769i_disco.dts @@ -200,6 +200,7 @@ arduino_serial: &usart6 {}; reg = <0>; size = ; /* 512 Mbits */ qspi-max-frequency = ; + cs-high-time = <2>; /* >= 30 ns */ status = "okay"; partitions { diff --git a/boards/st/stm32h745i_disco/stm32h745i_disco_stm32h745xx_m7.dts b/boards/st/stm32h745i_disco/stm32h745i_disco_stm32h745xx_m7.dts index c7c49da4f4ebf..74aa4ca067538 100644 --- a/boards/st/stm32h745i_disco/stm32h745i_disco_stm32h745xx_m7.dts +++ b/boards/st/stm32h745i_disco/stm32h745i_disco_stm32h745xx_m7.dts @@ -185,6 +185,7 @@ reg = <0>; size = ; /* 512 Mbits */ qspi-max-frequency = <72000000>; + cs-high-time = <4>; /* >= 50 ns */ spi-bus-width = <4>; reset-cmd; status = "okay"; diff --git a/boards/st/stm32h747i_disco/stm32h747i_disco_stm32h747xx_m7.dts b/boards/st/stm32h747i_disco/stm32h747i_disco_stm32h747xx_m7.dts index 4ec0856b92e00..9582738a373fe 100644 --- a/boards/st/stm32h747i_disco/stm32h747i_disco_stm32h747xx_m7.dts +++ b/boards/st/stm32h747i_disco/stm32h747i_disco_stm32h747xx_m7.dts @@ -258,6 +258,7 @@ zephyr_udc0: &usbotg_hs { reg = <0>; size = ; /* 512 Mbits */ qspi-max-frequency = <72000000>; + cs-high-time = <4>; /* >= 50 ns */ spi-bus-width = <4>; reset-cmd; status = "okay"; diff --git a/boards/st/stm32h750b_dk/stm32h750b_dk.dts b/boards/st/stm32h750b_dk/stm32h750b_dk.dts index e664a0a663e3a..a29b0d4e6f13f 100644 --- a/boards/st/stm32h750b_dk/stm32h750b_dk.dts +++ b/boards/st/stm32h750b_dk/stm32h750b_dk.dts @@ -192,6 +192,7 @@ reg = <0>; size = ; /* 512 Mbits */ qspi-max-frequency = <72000000>; + cs-high-time = <4>; /* >= 50 ns */ spi-bus-width = <4>; reset-cmd; status = "okay"; diff --git a/boards/st/stm32h757i_eval/stm32h757i_eval_stm32h757xx_m7.dts b/boards/st/stm32h757i_eval/stm32h757i_eval_stm32h757xx_m7.dts index e2d6fddfe6cad..4d96d2c87cb82 100644 --- a/boards/st/stm32h757i_eval/stm32h757i_eval_stm32h757xx_m7.dts +++ b/boards/st/stm32h757i_eval/stm32h757i_eval_stm32h757xx_m7.dts @@ -273,6 +273,7 @@ zephyr_udc0: &usbotg_hs { reg = <0>; size = ; /* 512 Mbits */ qspi-max-frequency = <72000000>; + cs-high-time = <4>; /* >= 50 ns */ spi-bus-width = <4>; reset-cmd; status = "okay"; diff --git a/boards/st/stm32l496g_disco/stm32l496g_disco.dts b/boards/st/stm32l496g_disco/stm32l496g_disco.dts index a888fec55987a..ed7b419390a43 100644 --- a/boards/st/stm32l496g_disco/stm32l496g_disco.dts +++ b/boards/st/stm32l496g_disco/stm32l496g_disco.dts @@ -211,6 +211,7 @@ zephyr_udc0: &usbotg_fs { reg = <0>; size = ; /* 64 Mbits */ qspi-max-frequency = <8000000>; + cs-high-time = <3>; /* >= 30 ns */ status = "okay"; spi-bus-width = <4>; writeoc = "PP_1_4_4"; diff --git a/boards/vcc-gnd/yd_stm32h750vb/yd_stm32h750vb.dts b/boards/vcc-gnd/yd_stm32h750vb/yd_stm32h750vb.dts index 2e94300081851..6d39ef748cc91 100644 --- a/boards/vcc-gnd/yd_stm32h750vb/yd_stm32h750vb.dts +++ b/boards/vcc-gnd/yd_stm32h750vb/yd_stm32h750vb.dts @@ -119,6 +119,7 @@ reg = <0>; size = ; /* 128 Mbits */ qspi-max-frequency = <80000000>; + cs-high-time = <4>; /* >= 50 ns */ spi-bus-width = <4>; status = "okay"; diff --git a/boards/weact/mini_stm32h743/mini_stm32h743.dts b/boards/weact/mini_stm32h743/mini_stm32h743.dts index 7df87472d073c..b946a522d74bd 100644 --- a/boards/weact/mini_stm32h743/mini_stm32h743.dts +++ b/boards/weact/mini_stm32h743/mini_stm32h743.dts @@ -171,6 +171,7 @@ zephyr_udc0: &usbotg_fs { reg = <0>; size = ; /* 64 Mbits */ qspi-max-frequency = <40000000>; + cs-high-time = <2>; /* >= 50 ns */ status = "okay"; spi-bus-width = <4>; writeoc = "PP_1_1_4"; diff --git a/samples/subsys/fs/littlefs/boards/nucleo_h743zi.overlay b/samples/subsys/fs/littlefs/boards/nucleo_h743zi.overlay index 67fb25ae7ce7d..95dd5d3940327 100644 --- a/samples/subsys/fs/littlefs/boards/nucleo_h743zi.overlay +++ b/samples/subsys/fs/littlefs/boards/nucleo_h743zi.overlay @@ -47,6 +47,7 @@ reg = <0>; size = ; /* 256 Mbits */ qspi-max-frequency = <50000000>; + cs-high-time = <3>; /* >= 30 ns */ reset-gpios = <&gpiod 3 GPIO_ACTIVE_LOW>; reset-gpios-duration = <1>; spi-bus-width = <4>; From e3e71db7215fffde2a2322667657a63441a25ab5 Mon Sep 17 00:00:00 2001 From: Thomas Altenbach Date: Sun, 6 Jul 2025 00:00:11 +0200 Subject: [PATCH 4/4] drivers: flash: flash_stm32_qspi: Add support for cs-high-time This commit adds support for the new cs-high-time devicetree property. The QUADSPI_DCR_CSHT is now configured according to the value indicated in the devicetree, for both single and dual flash modes. Signed-off-by: Thomas Altenbach --- drivers/flash/flash_stm32_qspi.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/flash/flash_stm32_qspi.c b/drivers/flash/flash_stm32_qspi.c index 1441dfc7aa4cc..2c7ee4b6548f9 100644 --- a/drivers/flash/flash_stm32_qspi.c +++ b/drivers/flash/flash_stm32_qspi.c @@ -102,6 +102,7 @@ struct flash_stm32_qspi_config { irq_config_func_t irq_config; size_t flash_size; uint32_t max_frequency; + uint8_t cs_high_time; const struct pinctrl_dev_config *pcfg; #if STM32_QSPI_RESET_GPIO const struct gpio_dt_spec reset; @@ -1511,6 +1512,7 @@ static int flash_stm32_qspi_init(const struct device *dev) /* Give a bit position from 0 to 31 to the HAL init minus 1 for the DCR1 reg */ dev_data->hqspi.Init.FlashSize = find_lsb_set(dev_cfg->flash_size) - 2; dev_data->hqspi.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE; + dev_data->hqspi.Init.ChipSelectHighTime = dev_cfg->cs_high_time - 1; #if DT_PROP(DT_NODELABEL(quadspi), dual_flash) && defined(QUADSPI_CR_DFM) /* * When the DTS has , it means Dual Flash Mode @@ -1518,7 +1520,6 @@ static int flash_stm32_qspi_init(const struct device *dev) * else the magic nb is wrong (0x46465353) * That means that the Dual Flash config is set after the SFDP sequence */ - dev_data->hqspi.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_3_CYCLE; dev_data->hqspi.Init.DualFlash = QSPI_DUALFLASH_DISABLE; /* Set Dual Flash Mode only on MemoryMapped */ dev_data->hqspi.Init.FlashID = QSPI_FLASH_ID_1; @@ -1705,6 +1706,7 @@ static const struct flash_stm32_qspi_config flash_stm32_qspi_cfg = { .irq_config = flash_stm32_qspi_irq_config_func, .flash_size = (DT_INST_PROP(0, size) / 8) << STM32_QSPI_DOUBLE_FLASH, .max_frequency = DT_INST_PROP(0, qspi_max_frequency), + .cs_high_time = DT_INST_PROP(0, cs_high_time), .pcfg = PINCTRL_DT_DEV_CONFIG_GET(STM32_QSPI_NODE), #if STM32_QSPI_RESET_GPIO .reset = GPIO_DT_SPEC_INST_GET(0, reset_gpios),