* [PATCH 1/9] dt-bindings: spi: Add STM32 OSPI controller
2025-01-22 14:10 [PATCH 0/9] Add STM32MP25 SPI NOR support patrice.chotard
@ 2025-01-22 14:10 ` patrice.chotard
2025-01-22 15:18 ` Krzysztof Kozlowski
2025-01-22 14:10 ` [PATCH 2/9] spi: stm32: Add OSPI driver patrice.chotard
` (7 subsequent siblings)
8 siblings, 1 reply; 16+ messages in thread
From: patrice.chotard @ 2025-01-22 14:10 UTC (permalink / raw)
To: Mark Brown, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Alexandre Torgue, Philipp Zabel, Maxime Coquelin,
Greg Kroah-Hartman, Arnd Bergmann, Catalin Marinas, Will Deacon
Cc: linux-spi, devicetree, linux-stm32, linux-arm-kernel,
linux-kernel, christophe.kerello, patrice.chotard
From: Patrice Chotard <patrice.chotard@foss.st.com>
Add device tree bindings for the STM32 OSPI controller.
Main features of the Octo-SPI controller :
- support sNOR / sNAND / HyperRAM™ and HyperFlash™ devices.
- Three functional modes: indirect, automatic-status polling,
memory-mapped.
- Up to 4 Gbytes of external memory can be addressed in indirect
mode (per physical port and per CS), and up to 256 Mbytes in
memory-mapped mode (combined for both physical ports and per CS).
- Single-, dual-, quad-, and octal-SPI communication.
- Dual-quad communication.
- Single data rate (SDR) and double transfer rate (DTR).
- Maximum target frequency is 133 MHz for SDR and 133 MHz for DTR.
- Data strobe support.
- DMA channel for indirect mode.
- Double CS mapping that allows two external flash devices to be
addressed with a single OCTOSPI controller mapped on a single
OCTOSPI port.
Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com>
---
.../bindings/spi/st,stm32-ospi.yaml | 109 ++++++++++++++++++
1 file changed, 109 insertions(+)
create mode 100644 Documentation/devicetree/bindings/spi/st,stm32-ospi.yaml
diff --git a/Documentation/devicetree/bindings/spi/st,stm32-ospi.yaml b/Documentation/devicetree/bindings/spi/st,stm32-ospi.yaml
new file mode 100644
index 000000000000..bf16252f85fa
--- /dev/null
+++ b/Documentation/devicetree/bindings/spi/st,stm32-ospi.yaml
@@ -0,0 +1,109 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/spi/st,stm32-ospi.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: STMicroelectronics STM32 Octal Serial Peripheral Interface (OSPI)
+
+maintainers:
+ - Patrice Chotard <patrice.chotard@foss.st.com>
+
+allOf:
+ - $ref: spi-controller.yaml#
+
+properties:
+ compatible:
+ const: st,stm32mp25-ospi
+
+ reg:
+ description: registers
+
+ "#address-cells":
+ const: 1
+
+ "#size-cells":
+ const: 0
+
+ memory-region:
+ maxItems: 1
+ description: Phandle to a node describing memory-map region to be used
+
+ clocks:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ resets:
+ maxItems: 2
+
+ dmas:
+ items:
+ - description: tx DMA channel
+ - description: rx DMA channel
+
+ dma-names:
+ items:
+ - const: tx
+ - const: rx
+
+ st,syscfg-dlyb:
+ description: |
+ Use to set the OSPI delay block within SYSCFG to:
+ Tune the phase of the RX sampling clock (or DQS) in order
+ to sample the data in their valid window.
+ Tune the phase of the TX launch clock in order to meet setup
+ and hold constraints of TX signals versus the memory clock.
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ items:
+ minItems: 2
+ maxItems: 2
+
+ access-controllers:
+ minItems: 1
+ maxItems: 2
+
+ power-domains:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - "#address-cells"
+ - "#size-cells"
+ - clocks
+ - interrupts
+ - st,syscfg-dlyb
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/st,stm32mp25-rcc.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/reset/st,stm32mp25-rcc.h>
+ spi@40430000 {
+ compatible = "st,stm32mp25-ospi";
+ reg = <0x40430000 0x400>;
+ memory-region = <&mm_ospi1>;
+ interrupts = <GIC_SPI 163 IRQ_TYPE_LEVEL_HIGH>;
+ dmas = <&hpdma 2 0x62 0x00003121 0x0>,
+ <&hpdma 2 0x42 0x00003112 0x0>;
+ dma-names = "tx", "rx";
+ clocks = <&scmi_clk CK_SCMI_OSPI1>;
+ resets = <&scmi_reset RST_SCMI_OSPI1>, <&scmi_reset RST_SCMI_OSPI1DLL>;
+ access-controllers = <&rifsc 74>;
+ power-domains = <&CLUSTER_PD>;
+ st,syscfg-dlyb = <&syscfg 0x1000>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ flash@0 {
+ compatible = "jedec,spi-nor";
+ reg = <0>;
+ spi-rx-bus-width = <4>;
+ spi-max-frequency = <108000000>;
+ };
+ };
--
2.25.1
^ permalink raw reply related [flat|nested] 16+ messages in thread* Re: [PATCH 1/9] dt-bindings: spi: Add STM32 OSPI controller
2025-01-22 14:10 ` [PATCH 1/9] dt-bindings: spi: Add STM32 OSPI controller patrice.chotard
@ 2025-01-22 15:18 ` Krzysztof Kozlowski
2025-01-24 15:53 ` Patrice CHOTARD
0 siblings, 1 reply; 16+ messages in thread
From: Krzysztof Kozlowski @ 2025-01-22 15:18 UTC (permalink / raw)
To: patrice.chotard, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Alexandre Torgue, Philipp Zabel, Maxime Coquelin,
Greg Kroah-Hartman, Arnd Bergmann, Catalin Marinas, Will Deacon
Cc: linux-spi, devicetree, linux-stm32, linux-arm-kernel,
linux-kernel, christophe.kerello
On 22/01/2025 15:10, patrice.chotard@foss.st.com wrote:
> ---
> .../bindings/spi/st,stm32-ospi.yaml | 109 ++++++++++++++++++
> 1 file changed, 109 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/spi/st,stm32-ospi.yaml
>
> diff --git a/Documentation/devicetree/bindings/spi/st,stm32-ospi.yaml b/Documentation/devicetree/bindings/spi/st,stm32-ospi.yaml
> new file mode 100644
> index 000000000000..bf16252f85fa
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/spi/st,stm32-ospi.yaml
Use compatible as filename.
> @@ -0,0 +1,109 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/spi/st,stm32-ospi.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: STMicroelectronics STM32 Octal Serial Peripheral Interface (OSPI)
> +
> +maintainers:
> + - Patrice Chotard <patrice.chotard@foss.st.com>
> +
> +allOf:
> + - $ref: spi-controller.yaml#
> +
> +properties:
> + compatible:
> + const: st,stm32mp25-ospi
> +
> + reg:
> + description: registers
That's not helping. Please take a look how other bindings do it.
maxItems instead or you need to list the items with meaningful description.
> +
> + "#address-cells":
> + const: 1
> +
> + "#size-cells":
> + const: 0
Drop *cells.
> +
> + memory-region:
> + maxItems: 1
> + description: Phandle to a node describing memory-map region to be used
Drop description, redundant. Say something useful - the purpose - or
just maxItems if purpose is obvious.
> +
> + clocks:
> + maxItems: 1
> +
> + interrupts:
> + maxItems: 1
> +
> + resets:
> + maxItems: 2
You need to list and describe the items.
> +
> + dmas:
> + items:
> + - description: tx DMA channel
> + - description: rx DMA channel
maxItems: 2 is enough, because names define what these are
> +
> + dma-names:
> + items:
> + - const: tx
> + - const: rx
> +
> + st,syscfg-dlyb:
> + description: |
> + Use to set the OSPI delay block within SYSCFG to:
Phandles to what? Describe also the destination device.
> + Tune the phase of the RX sampling clock (or DQS) in order
Unneeded indentation.
> + to sample the data in their valid window.
> + Tune the phase of the TX launch clock in order to meet setup
> + and hold constraints of TX signals versus the memory clock.
> + $ref: /schemas/types.yaml#/definitions/phandle-array
> + items:
> + minItems: 2
> + maxItems: 2
Your example has only one item, so probably you wanted one more items
with description. Now you miss one of matrix constraints.
git grep -C 8 phandle-array
(e.g. some sram or syscon examples)
> +
> + access-controllers:
> + minItems: 1
> + maxItems: 2
List the items.
> +
> + power-domains:
> + maxItems: 1
> +
> +required:
> + - compatible
> + - reg
> + - "#address-cells"
> + - "#size-cells"
Drop cells
> + - clocks
> + - interrupts
> + - st,syscfg-dlyb
> +
> +unevaluatedProperties: false
> +
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 1/9] dt-bindings: spi: Add STM32 OSPI controller
2025-01-22 15:18 ` Krzysztof Kozlowski
@ 2025-01-24 15:53 ` Patrice CHOTARD
0 siblings, 0 replies; 16+ messages in thread
From: Patrice CHOTARD @ 2025-01-24 15:53 UTC (permalink / raw)
To: Krzysztof Kozlowski, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Alexandre Torgue, Philipp Zabel, Maxime Coquelin,
Greg Kroah-Hartman, Arnd Bergmann, Catalin Marinas, Will Deacon
Cc: linux-spi, devicetree, linux-stm32, linux-arm-kernel,
linux-kernel, christophe.kerello
On 1/22/25 16:18, Krzysztof Kozlowski wrote:
> On 22/01/2025 15:10, patrice.chotard@foss.st.com wrote:
>> ---
>> .../bindings/spi/st,stm32-ospi.yaml | 109 ++++++++++++++++++
>> 1 file changed, 109 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/spi/st,stm32-ospi.yaml
>>
>> diff --git a/Documentation/devicetree/bindings/spi/st,stm32-ospi.yaml b/Documentation/devicetree/bindings/spi/st,stm32-ospi.yaml
>> new file mode 100644
>> index 000000000000..bf16252f85fa
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/spi/st,stm32-ospi.yaml
>
>
> Use compatible as filename.
Ok
>
>> @@ -0,0 +1,109 @@
>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/spi/st,stm32-ospi.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: STMicroelectronics STM32 Octal Serial Peripheral Interface (OSPI)
>> +
>> +maintainers:
>> + - Patrice Chotard <patrice.chotard@foss.st.com>
>> +
>> +allOf:
>> + - $ref: spi-controller.yaml#
>> +
>> +properties:
>> + compatible:
>> + const: st,stm32mp25-ospi
>> +
>> + reg:
>> + description: registers
>
> That's not helping. Please take a look how other bindings do it.
> maxItems instead or you need to list the items with meaningful description.
ok, will use maxItems
>
>> +
>> + "#address-cells":
>> + const: 1
>> +
>> + "#size-cells":
>> + const: 0
>
> Drop *cells.
ok
>> +
>> + memory-region:
>> + maxItems: 1
>> + description: Phandle to a node describing memory-map region to be used
>
> Drop description, redundant. Say something useful - the purpose - or
> just maxItems if purpose is obvious.
ok, i will add
- description: phandle to OSPI block reset
- description: phandle to delay block reset
>
>> +
>> + clocks:
>> + maxItems: 1
>> +
>> + interrupts:
>> + maxItems: 1
>> +
>> + resets:
>> + maxItems: 2
>
> You need to list and describe the items.
ok
>
>> +
>> + dmas:
>> + items:
>> + - description: tx DMA channel
>> + - description: rx DMA channel
>
> maxItems: 2 is enough, because names define what these are
ok
>
>> +
>> + dma-names:
>> + items:
>> + - const: tx
>> + - const: rx
>> +
>> + st,syscfg-dlyb:
>> + description: |
>> + Use to set the OSPI delay block within SYSCFG to:
>
> Phandles to what? Describe also the destination device.
>
>> + Tune the phase of the RX sampling clock (or DQS) in order
>
> Unneeded indentation.
>
>> + to sample the data in their valid window.
>> + Tune the phase of the TX launch clock in order to meet setup
>> + and hold constraints of TX signals versus the memory clock.
>> + $ref: /schemas/types.yaml#/definitions/phandle-array
>> + items:
>> + minItems: 2
>> + maxItems: 2
>
> Your example has only one item, so probably you wanted one more items
> with description. Now you miss one of matrix constraints.
My fault, only one item is required.
>
> git grep -C 8 phandle-array
> (e.g. some sram or syscon examples)
>
>
>
>> +
>> + access-controllers:
>> + minItems: 1
>> + maxItems: 2
>
> List the items.
i will update as following:
access-controllers:
- description: phandle to the rifsc device to check access right.
- description: phandle to the rcc device for secure clock control
minItems: 1
maxItems: 2
>
>> +
>> + power-domains:
>> + maxItems: 1
>> +
>> +required:
>> + - compatible
>> + - reg
>> + - "#address-cells"
>> + - "#size-cells"
>
> Drop cells
ok
>
>> + - clocks
>> + - interrupts
>> + - st,syscfg-dlyb
>> +
>> +unevaluatedProperties: false
>> +
>
>
>
> Best regards,
> Krzysztof
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH 2/9] spi: stm32: Add OSPI driver
2025-01-22 14:10 [PATCH 0/9] Add STM32MP25 SPI NOR support patrice.chotard
2025-01-22 14:10 ` [PATCH 1/9] dt-bindings: spi: Add STM32 OSPI controller patrice.chotard
@ 2025-01-22 14:10 ` patrice.chotard
2025-01-22 14:10 ` [PATCH 3/9] dt-bindings: misc: Add STM32 Octo Memory Manager controller patrice.chotard
` (6 subsequent siblings)
8 siblings, 0 replies; 16+ messages in thread
From: patrice.chotard @ 2025-01-22 14:10 UTC (permalink / raw)
To: Mark Brown, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Alexandre Torgue, Philipp Zabel, Maxime Coquelin,
Greg Kroah-Hartman, Arnd Bergmann, Catalin Marinas, Will Deacon
Cc: linux-spi, devicetree, linux-stm32, linux-arm-kernel,
linux-kernel, christophe.kerello, patrice.chotard
From: Patrice Chotard <patrice.chotard@foss.st.com>
Add STM32 OSPI driver, it supports :
- support sNOR / sNAND devices.
- Three functional modes: indirect, automatic-status polling,
memory-mapped.
- Single-, dual-, quad-, and octal-SPI communication.
- Dual-quad communication.
- Single data rate (SDR).
- DMA channel for indirect mode.
Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com>
---
drivers/spi/Kconfig | 10 +
drivers/spi/Makefile | 1 +
drivers/spi/spi-stm32-ospi.c | 1064 ++++++++++++++++++++++++++++++++++
3 files changed, 1075 insertions(+)
create mode 100644 drivers/spi/spi-stm32-ospi.c
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index ea8a31032927..256a3e23639a 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -1045,6 +1045,16 @@ config SPI_STM32
is not available, the driver automatically falls back to
PIO mode.
+config SPI_STM32_OSPI
+ tristate "STMicroelectronics STM32 OCTO SPI controller"
+ depends on ARCH_STM32 || COMPILE_TEST
+ depends on OF
+ depends on SPI_MEM
+ help
+ This enables support for the Octo SPI controller in master mode.
+ This driver does not support generic SPI. The implementation only
+ supports spi-mem interface.
+
config SPI_STM32_QSPI
tristate "STMicroelectronics STM32 QUAD SPI controller"
depends on ARCH_STM32 || COMPILE_TEST
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 9db7554c1864..65e0804ebb3a 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -138,6 +138,7 @@ obj-$(CONFIG_SPI_SPRD) += spi-sprd.o
obj-$(CONFIG_SPI_SPRD_ADI) += spi-sprd-adi.o
obj-$(CONFIG_SPI_STM32) += spi-stm32.o
obj-$(CONFIG_SPI_STM32_QSPI) += spi-stm32-qspi.o
+obj-$(CONFIG_SPI_STM32_OSPI) += spi-stm32-ospi.o
obj-$(CONFIG_SPI_ST_SSC4) += spi-st-ssc4.o
obj-$(CONFIG_SPI_SUN4I) += spi-sun4i.o
obj-$(CONFIG_SPI_SUN6I) += spi-sun6i.o
diff --git a/drivers/spi/spi-stm32-ospi.c b/drivers/spi/spi-stm32-ospi.c
new file mode 100644
index 000000000000..131266a7c5fe
--- /dev/null
+++ b/drivers/spi/spi-stm32-ospi.c
@@ -0,0 +1,1064 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) STMicroelectronics 2025 - All Rights Reserved
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+#include <linux/sizes.h>
+#include <linux/spi/spi-mem.h>
+#include <linux/types.h>
+
+#define OSPI_CR 0x00
+#define CR_EN BIT(0)
+#define CR_ABORT BIT(1)
+#define CR_DMAEN BIT(2)
+#define CR_FTHRES_SHIFT 8
+#define CR_TEIE BIT(16)
+#define CR_TCIE BIT(17)
+#define CR_SMIE BIT(19)
+#define CR_APMS BIT(22)
+#define CR_CSSEL BIT(24)
+#define CR_FMODE_MASK GENMASK(29, 28)
+#define CR_FMODE_INDW (0U)
+#define CR_FMODE_INDR (1U)
+#define CR_FMODE_APM (2U)
+#define CR_FMODE_MM (3U)
+
+#define OSPI_DCR1 0x08
+#define DCR1_DLYBYP BIT(3)
+#define DCR1_DEVSIZE_MASK GENMASK(20, 16)
+#define DCR1_MTYP_MASK GENMASK(26, 24)
+#define DCR1_MTYP_MX_MODE 1
+#define DCR1_MTYP_HP_MEMMODE 4
+
+#define OSPI_DCR2 0x0c
+#define DCR2_PRESC_MASK GENMASK(7, 0)
+
+#define OSPI_SR 0x20
+#define SR_TEF BIT(0)
+#define SR_TCF BIT(1)
+#define SR_FTF BIT(2)
+#define SR_SMF BIT(3)
+#define SR_BUSY BIT(5)
+
+#define OSPI_FCR 0x24
+#define FCR_CTEF BIT(0)
+#define FCR_CTCF BIT(1)
+#define FCR_CSMF BIT(3)
+
+#define OSPI_DLR 0x40
+#define OSPI_AR 0x48
+#define OSPI_DR 0x50
+#define OSPI_PSMKR 0x80
+#define OSPI_PSMAR 0x88
+
+#define OSPI_CCR 0x100
+#define CCR_IMODE_MASK GENMASK(2, 0)
+#define CCR_IDTR BIT(3)
+#define CCR_ISIZE_MASK GENMASK(5, 4)
+#define CCR_ADMODE_MASK GENMASK(10, 8)
+#define CCR_ADMODE_8LINES 4
+#define CCR_ADDTR BIT(11)
+#define CCR_ADSIZE_MASK GENMASK(13, 12)
+#define CCR_ADSIZE_32BITS 3
+#define CCR_DMODE_MASK GENMASK(26, 24)
+#define CCR_DMODE_8LINES 4
+#define CCR_DQSE BIT(29)
+#define CCR_DDTR BIT(27)
+#define CCR_BUSWIDTH_0 0x0
+#define CCR_BUSWIDTH_1 0x1
+#define CCR_BUSWIDTH_2 0x2
+#define CCR_BUSWIDTH_4 0x3
+#define CCR_BUSWIDTH_8 0x4
+
+#define OSPI_TCR 0x108
+#define TCR_DCYC_MASK GENMASK(4, 0)
+#define TCR_DHQC BIT(28)
+#define TCR_SSHIFT BIT(30)
+
+#define OSPI_IR 0x110
+
+#define STM32_OSPI_MAX_MMAP_SZ SZ_256M
+#define STM32_OSPI_MAX_NORCHIP 2
+
+#define STM32_FIFO_TIMEOUT_US 30000
+#define STM32_ABT_TIMEOUT_US 100000
+#define STM32_COMP_TIMEOUT_MS 5000
+#define STM32_BUSY_TIMEOUT_US 100000
+
+
+#define STM32_AUTOSUSPEND_DELAY -1
+
+struct stm32_ospi {
+ struct device *dev;
+ struct spi_controller *ctrl;
+ struct clk *clk;
+ struct reset_control *rstc;
+
+ struct completion data_completion;
+ struct completion match_completion;
+
+ struct dma_chan *dma_chtx;
+ struct dma_chan *dma_chrx;
+ struct completion dma_completion;
+
+ void __iomem *regs_base;
+ void __iomem *mm_base;
+ phys_addr_t regs_phys_base;
+ resource_size_t mm_size;
+ u32 clk_rate;
+ u32 fmode;
+ u32 cr_reg;
+ u32 dcr_reg;
+ u32 flash_presc[STM32_OSPI_MAX_NORCHIP];
+ int irq;
+ unsigned long status_timeout;
+
+ /*
+ * To protect device configuration, could be different between
+ * 2 flash access
+ */
+ struct mutex lock;
+};
+
+static void stm32_ospi_read_fifo(u8 *val, void __iomem *addr)
+{
+ *val = readb_relaxed(addr);
+}
+
+static void stm32_ospi_write_fifo(u8 *val, void __iomem *addr)
+{
+ writeb_relaxed(*val, addr);
+}
+
+static int stm32_ospi_abort(struct stm32_ospi *ospi)
+{
+ void __iomem *regs_base = ospi->regs_base;
+ u32 cr;
+ int timeout;
+
+ cr = readl_relaxed(regs_base + OSPI_CR) | CR_ABORT;
+ writel_relaxed(cr, regs_base + OSPI_CR);
+
+ /* wait clear of abort bit by hw */
+ timeout = readl_relaxed_poll_timeout_atomic(regs_base + OSPI_CR,
+ cr, !(cr & CR_ABORT), 1,
+ STM32_ABT_TIMEOUT_US);
+
+ if (timeout)
+ dev_err(ospi->dev, "%s abort timeout:%d\n", __func__, timeout);
+
+ return timeout;
+}
+
+static int stm32_ospi_tx_poll(struct stm32_ospi *ospi, u8 *buf, u32 len, bool read)
+{
+ void __iomem *regs_base = ospi->regs_base;
+ void (*tx_fifo)(u8 *val, void __iomem *addr);
+ u32 sr;
+ int ret;
+
+ if (read)
+ tx_fifo = stm32_ospi_read_fifo;
+ else
+ tx_fifo = stm32_ospi_write_fifo;
+
+ while (len--) {
+ ret = readl_relaxed_poll_timeout_atomic(regs_base + OSPI_SR,
+ sr, sr & SR_FTF, 1,
+ STM32_FIFO_TIMEOUT_US);
+ if (ret) {
+ dev_err(ospi->dev, "fifo timeout (len:%d stat:%#x)\n",
+ len, sr);
+ return ret;
+ }
+ tx_fifo(buf++, regs_base + OSPI_DR);
+ }
+
+ return 0;
+}
+
+static int stm32_ospi_wait_nobusy(struct stm32_ospi *ospi)
+{
+ u32 sr;
+
+ return readl_relaxed_poll_timeout_atomic(ospi->regs_base + OSPI_SR,
+ sr, !(sr & SR_BUSY), 1,
+ STM32_BUSY_TIMEOUT_US);
+}
+
+static int stm32_ospi_wait_cmd(struct stm32_ospi *ospi)
+{
+ void __iomem *regs_base = ospi->regs_base;
+ u32 cr, sr;
+ int err = 0;
+
+ if ((readl_relaxed(regs_base + OSPI_SR) & SR_TCF) ||
+ ospi->fmode == CR_FMODE_APM)
+ goto out;
+
+ reinit_completion(&ospi->data_completion);
+ cr = readl_relaxed(regs_base + OSPI_CR);
+ writel_relaxed(cr | CR_TCIE | CR_TEIE, regs_base + OSPI_CR);
+
+ if (!wait_for_completion_timeout(&ospi->data_completion,
+ msecs_to_jiffies(STM32_COMP_TIMEOUT_MS)))
+ err = -ETIMEDOUT;
+
+ sr = readl_relaxed(regs_base + OSPI_SR);
+ if (sr & SR_TCF)
+ /* avoid false timeout */
+ err = 0;
+ if (sr & SR_TEF)
+ err = -EIO;
+
+out:
+ /* clear flags */
+ writel_relaxed(FCR_CTCF | FCR_CTEF, regs_base + OSPI_FCR);
+
+ if (!err)
+ err = stm32_ospi_wait_nobusy(ospi);
+
+ return err;
+}
+
+static void stm32_ospi_dma_callback(void *arg)
+{
+ struct completion *dma_completion = arg;
+
+ complete(dma_completion);
+}
+
+static irqreturn_t stm32_ospi_irq(int irq, void *dev_id)
+{
+ struct stm32_ospi *ospi = (struct stm32_ospi *)dev_id;
+ void __iomem *regs_base = ospi->regs_base;
+ u32 cr, sr;
+
+ cr = readl_relaxed(regs_base + OSPI_CR);
+ sr = readl_relaxed(regs_base + OSPI_SR);
+
+ if (cr & CR_SMIE && sr & SR_SMF) {
+ /* disable irq */
+ cr &= ~CR_SMIE;
+ writel_relaxed(cr, regs_base + OSPI_CR);
+ complete(&ospi->match_completion);
+
+ return IRQ_HANDLED;
+ }
+
+ if (sr & (SR_TEF | SR_TCF)) {
+ /* disable irq */
+ cr &= ~CR_TCIE & ~CR_TEIE;
+ writel_relaxed(cr, regs_base + OSPI_CR);
+ complete(&ospi->data_completion);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void stm32_ospi_dma_setup(struct stm32_ospi *ospi,
+ struct dma_slave_config *dma_cfg)
+{
+ if (dma_cfg && ospi->dma_chrx) {
+ if (dmaengine_slave_config(ospi->dma_chrx, dma_cfg)) {
+ dev_err(ospi->dev, "dma rx config failed\n");
+ dma_release_channel(ospi->dma_chrx);
+ ospi->dma_chrx = NULL;
+ }
+ }
+
+ if (dma_cfg && ospi->dma_chtx) {
+ if (dmaengine_slave_config(ospi->dma_chtx, dma_cfg)) {
+ dev_err(ospi->dev, "dma tx config failed\n");
+ dma_release_channel(ospi->dma_chtx);
+ ospi->dma_chtx = NULL;
+ }
+ }
+
+ init_completion(&ospi->dma_completion);
+}
+
+static int stm32_ospi_tx_mm(struct stm32_ospi *ospi,
+ const struct spi_mem_op *op)
+{
+ memcpy_fromio(op->data.buf.in, ospi->mm_base + op->addr.val,
+ op->data.nbytes);
+ return 0;
+}
+
+static int stm32_ospi_tx_dma(struct stm32_ospi *ospi,
+ const struct spi_mem_op *op)
+{
+ struct dma_async_tx_descriptor *desc;
+ void __iomem *regs_base = ospi->regs_base;
+ enum dma_transfer_direction dma_dir;
+ struct dma_chan *dma_ch;
+ struct sg_table sgt;
+ dma_cookie_t cookie;
+ u32 cr, t_out;
+ int err;
+
+ if (op->data.dir == SPI_MEM_DATA_IN) {
+ dma_dir = DMA_DEV_TO_MEM;
+ dma_ch = ospi->dma_chrx;
+ } else {
+ dma_dir = DMA_MEM_TO_DEV;
+ dma_ch = ospi->dma_chtx;
+ }
+
+ /*
+ * Spi_map_buf return -EINVAL if the buffer is not DMA-able
+ * (DMA-able: in vmalloc | kmap | virt_addr_valid)
+ */
+ err = spi_controller_dma_map_mem_op_data(ospi->ctrl, op, &sgt);
+ if (err)
+ return err;
+
+ desc = dmaengine_prep_slave_sg(dma_ch, sgt.sgl, sgt.nents,
+ dma_dir, DMA_PREP_INTERRUPT);
+ if (!desc) {
+ err = -ENOMEM;
+ goto out_unmap;
+ }
+
+ cr = readl_relaxed(regs_base + OSPI_CR);
+
+ reinit_completion(&ospi->dma_completion);
+ desc->callback = stm32_ospi_dma_callback;
+ desc->callback_param = &ospi->dma_completion;
+ cookie = dmaengine_submit(desc);
+ err = dma_submit_error(cookie);
+ if (err)
+ goto out;
+
+ dma_async_issue_pending(dma_ch);
+
+ writel_relaxed(cr | CR_DMAEN, regs_base + OSPI_CR);
+
+ t_out = sgt.nents * STM32_COMP_TIMEOUT_MS;
+ if (!wait_for_completion_timeout(&ospi->dma_completion,
+ msecs_to_jiffies(t_out)))
+ err = -ETIMEDOUT;
+
+ if (err)
+ dmaengine_terminate_all(dma_ch);
+
+out:
+ writel_relaxed(cr & ~CR_DMAEN, regs_base + OSPI_CR);
+out_unmap:
+ spi_controller_dma_unmap_mem_op_data(ospi->ctrl, op, &sgt);
+
+ return err;
+}
+
+static int stm32_ospi_tx(struct stm32_ospi *ospi, const struct spi_mem_op *op)
+{
+ u8 *buf;
+
+ if (!op->data.nbytes)
+ return 0;
+
+ if (ospi->fmode == CR_FMODE_MM)
+ return stm32_ospi_tx_mm(ospi, op);
+ else if (((op->data.dir == SPI_MEM_DATA_IN && ospi->dma_chrx) ||
+ (op->data.dir == SPI_MEM_DATA_OUT && ospi->dma_chtx)) &&
+ op->data.nbytes > 8)
+ if (!stm32_ospi_tx_dma(ospi, op))
+ return 0;
+
+ if (op->data.dir == SPI_MEM_DATA_IN)
+ buf = op->data.buf.in;
+ else
+ buf = (u8 *)op->data.buf.out;
+
+ return stm32_ospi_tx_poll(ospi, buf, op->data.nbytes,
+ op->data.dir == SPI_MEM_DATA_IN);
+}
+
+static int stm32_ospi_wait_poll_status(struct stm32_ospi *ospi,
+ const struct spi_mem_op *op)
+{
+ void __iomem *regs_base = ospi->regs_base;
+ u32 cr;
+
+ reinit_completion(&ospi->match_completion);
+ cr = readl_relaxed(regs_base + OSPI_CR);
+ writel_relaxed(cr | CR_SMIE, regs_base + OSPI_CR);
+
+ if (!wait_for_completion_timeout(&ospi->match_completion,
+ msecs_to_jiffies(ospi->status_timeout))) {
+ u32 sr = readl_relaxed(regs_base + OSPI_SR);
+
+ /* Avoid false timeout */
+ if (!(sr & SR_SMF))
+ return -ETIMEDOUT;
+ }
+
+ writel_relaxed(FCR_CSMF, regs_base + OSPI_FCR);
+
+ return 0;
+}
+
+static int stm32_ospi_get_mode(u8 buswidth)
+{
+ switch (buswidth) {
+ case 8:
+ return CCR_BUSWIDTH_8;
+ case 4:
+ return CCR_BUSWIDTH_4;
+ default:
+ return buswidth;
+ }
+}
+
+static int stm32_ospi_send(struct spi_device *spi, const struct spi_mem_op *op)
+{
+ struct stm32_ospi *ospi = spi_controller_get_devdata(spi->controller);
+ void __iomem *regs_base = ospi->regs_base;
+ u32 ccr, cr, dcr2, tcr;
+ int timeout, err = 0, err_poll_status = 0;
+ u8 cs = spi->chip_select[ffs(spi->cs_index_mask) - 1];
+
+ dev_dbg(ospi->dev, "cmd:%#x mode:%d.%d.%d.%d addr:%#llx len:%#x\n",
+ op->cmd.opcode, op->cmd.buswidth, op->addr.buswidth,
+ op->dummy.buswidth, op->data.buswidth,
+ op->addr.val, op->data.nbytes);
+
+ cr = readl_relaxed(ospi->regs_base + OSPI_CR);
+ cr &= ~CR_CSSEL;
+ cr |= FIELD_PREP(CR_CSSEL, cs);
+ cr &= ~CR_FMODE_MASK;
+ cr |= FIELD_PREP(CR_FMODE_MASK, ospi->fmode);
+ writel_relaxed(cr, regs_base + OSPI_CR);
+
+ if (op->data.nbytes)
+ writel_relaxed(op->data.nbytes - 1, regs_base + OSPI_DLR);
+
+ /* set prescaler */
+ dcr2 = readl_relaxed(regs_base + OSPI_DCR2);
+ dcr2 |= FIELD_PREP(DCR2_PRESC_MASK, ospi->flash_presc[cs]);
+ writel_relaxed(dcr2, regs_base + OSPI_DCR2);
+
+ ccr = FIELD_PREP(CCR_IMODE_MASK, stm32_ospi_get_mode(op->cmd.buswidth));
+
+ if (op->addr.nbytes) {
+ ccr |= FIELD_PREP(CCR_ADMODE_MASK,
+ stm32_ospi_get_mode(op->addr.buswidth));
+ ccr |= FIELD_PREP(CCR_ADSIZE_MASK, op->addr.nbytes - 1);
+ }
+
+ tcr = TCR_SSHIFT;
+ if (op->dummy.buswidth && op->dummy.nbytes) {
+ tcr |= FIELD_PREP(TCR_DCYC_MASK,
+ op->dummy.nbytes * 8 / op->dummy.buswidth);
+ }
+ writel_relaxed(tcr, regs_base + OSPI_TCR);
+
+ if (op->data.nbytes) {
+ ccr |= FIELD_PREP(CCR_DMODE_MASK,
+ stm32_ospi_get_mode(op->data.buswidth));
+ }
+
+ writel_relaxed(ccr, regs_base + OSPI_CCR);
+
+ /* set instruction, must be set after ccr register update */
+ writel_relaxed(op->cmd.opcode, regs_base + OSPI_IR);
+
+ if (op->addr.nbytes && ospi->fmode != CR_FMODE_MM)
+ writel_relaxed(op->addr.val, regs_base + OSPI_AR);
+
+ if (ospi->fmode == CR_FMODE_APM)
+ err_poll_status = stm32_ospi_wait_poll_status(ospi, op);
+
+ err = stm32_ospi_tx(ospi, op);
+
+ /*
+ * Abort in:
+ * -error case
+ * -read memory map: prefetching must be stopped if we read the last
+ * byte of device (device size - fifo size). like device size is not
+ * knows, the prefetching is always stop.
+ */
+ if (err || err_poll_status || ospi->fmode == CR_FMODE_MM)
+ goto abort;
+
+ /* Wait end of tx in indirect mode */
+ err = stm32_ospi_wait_cmd(ospi);
+ if (err)
+ goto abort;
+
+ return 0;
+
+abort:
+ timeout = stm32_ospi_abort(ospi);
+ writel_relaxed(FCR_CTCF | FCR_CSMF, regs_base + OSPI_FCR);
+
+ if (err || err_poll_status || timeout)
+ dev_err(ospi->dev, "%s err:%d err_poll_status:%d abort timeout:%d\n",
+ __func__, err, err_poll_status, timeout);
+
+ return err;
+}
+
+static int stm32_ospi_poll_status(struct spi_mem *mem,
+ const struct spi_mem_op *op,
+ u16 mask, u16 match,
+ unsigned long initial_delay_us,
+ unsigned long polling_rate_us,
+ unsigned long timeout_ms)
+{
+ struct stm32_ospi *ospi = spi_controller_get_devdata(mem->spi->controller);
+ void __iomem *regs_base = ospi->regs_base;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(ospi->dev);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&ospi->lock);
+
+ writel_relaxed(mask, regs_base + OSPI_PSMKR);
+ writel_relaxed(match, regs_base + OSPI_PSMAR);
+ ospi->fmode = CR_FMODE_APM;
+ ospi->status_timeout = timeout_ms;
+
+ ret = stm32_ospi_send(mem->spi, op);
+ mutex_unlock(&ospi->lock);
+
+ pm_runtime_mark_last_busy(ospi->dev);
+ pm_runtime_put_autosuspend(ospi->dev);
+
+ return ret;
+}
+
+static int stm32_ospi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
+{
+ struct stm32_ospi *ospi = spi_controller_get_devdata(mem->spi->controller);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(ospi->dev);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&ospi->lock);
+ if (op->data.dir == SPI_MEM_DATA_IN && op->data.nbytes)
+ ospi->fmode = CR_FMODE_INDR;
+ else
+ ospi->fmode = CR_FMODE_INDW;
+
+ ret = stm32_ospi_send(mem->spi, op);
+ mutex_unlock(&ospi->lock);
+
+ pm_runtime_mark_last_busy(ospi->dev);
+ pm_runtime_put_autosuspend(ospi->dev);
+
+ return ret;
+}
+
+static int stm32_ospi_dirmap_create(struct spi_mem_dirmap_desc *desc)
+{
+ struct stm32_ospi *ospi = spi_controller_get_devdata(desc->mem->spi->controller);
+
+ if (desc->info.op_tmpl.data.dir == SPI_MEM_DATA_OUT)
+ return -EOPNOTSUPP;
+
+ /* Should never happen, as mm_base == null is an error probe exit condition */
+ if (!ospi->mm_base && desc->info.op_tmpl.data.dir == SPI_MEM_DATA_IN)
+ return -EOPNOTSUPP;
+
+ if (!ospi->mm_size)
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+static ssize_t stm32_ospi_dirmap_read(struct spi_mem_dirmap_desc *desc,
+ u64 offs, size_t len, void *buf)
+{
+ struct stm32_ospi *ospi = spi_controller_get_devdata(desc->mem->spi->controller);
+ struct spi_mem_op op;
+ u32 addr_max;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(ospi->dev);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&ospi->lock);
+ /*
+ * Make a local copy of desc op_tmpl and complete dirmap rdesc
+ * spi_mem_op template with offs, len and *buf in order to get
+ * all needed transfer information into struct spi_mem_op
+ */
+ memcpy(&op, &desc->info.op_tmpl, sizeof(struct spi_mem_op));
+ dev_dbg(ospi->dev, "%s len = 0x%zx offs = 0x%llx buf = 0x%p\n", __func__, len, offs, buf);
+
+ op.data.nbytes = len;
+ op.addr.val = desc->info.offset + offs;
+ op.data.buf.in = buf;
+
+ addr_max = op.addr.val + op.data.nbytes + 1;
+ if (addr_max < ospi->mm_size && op.addr.buswidth)
+ ospi->fmode = CR_FMODE_MM;
+ else
+ ospi->fmode = CR_FMODE_INDR;
+
+ ret = stm32_ospi_send(desc->mem->spi, &op);
+ mutex_unlock(&ospi->lock);
+
+ pm_runtime_mark_last_busy(ospi->dev);
+ pm_runtime_put_autosuspend(ospi->dev);
+
+ return ret ?: len;
+}
+
+static int stm32_ospi_transfer_one_message(struct spi_controller *ctrl,
+ struct spi_message *msg)
+{
+ struct stm32_ospi *ospi = spi_controller_get_devdata(ctrl);
+ struct spi_transfer *transfer;
+ struct spi_device *spi = msg->spi;
+ struct spi_mem_op op;
+ struct gpio_desc *cs_gpiod = spi->cs_gpiod[ffs(spi->cs_index_mask) - 1];
+ int ret = 0;
+
+ if (!cs_gpiod)
+ return -EOPNOTSUPP;
+
+ ret = pm_runtime_resume_and_get(ospi->dev);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&ospi->lock);
+
+ gpiod_set_value_cansleep(cs_gpiod, true);
+
+ list_for_each_entry(transfer, &msg->transfers, transfer_list) {
+ u8 dummy_bytes = 0;
+
+ memset(&op, 0, sizeof(op));
+
+ dev_dbg(ospi->dev, "tx_buf:%p tx_nbits:%d rx_buf:%p rx_nbits:%d len:%d dummy_data:%d\n",
+ transfer->tx_buf, transfer->tx_nbits,
+ transfer->rx_buf, transfer->rx_nbits,
+ transfer->len, transfer->dummy_data);
+
+ /*
+ * OSPI hardware supports dummy bytes transfer.
+ * If current transfer is dummy byte, merge it with the next
+ * transfer in order to take into account OSPI block constraint
+ */
+ if (transfer->dummy_data) {
+ op.dummy.buswidth = transfer->tx_nbits;
+ op.dummy.nbytes = transfer->len;
+ dummy_bytes = transfer->len;
+
+ /* If happens, means that message is not correctly built */
+ if (list_is_last(&transfer->transfer_list, &msg->transfers)) {
+ ret = -EINVAL;
+ goto end_of_transfer;
+ }
+
+ transfer = list_next_entry(transfer, transfer_list);
+ }
+
+ op.data.nbytes = transfer->len;
+
+ if (transfer->rx_buf) {
+ ospi->fmode = CR_FMODE_INDR;
+ op.data.buswidth = transfer->rx_nbits;
+ op.data.dir = SPI_MEM_DATA_IN;
+ op.data.buf.in = transfer->rx_buf;
+ } else {
+ ospi->fmode = CR_FMODE_INDW;
+ op.data.buswidth = transfer->tx_nbits;
+ op.data.dir = SPI_MEM_DATA_OUT;
+ op.data.buf.out = transfer->tx_buf;
+ }
+
+ ret = stm32_ospi_send(spi, &op);
+ if (ret)
+ goto end_of_transfer;
+
+ msg->actual_length += transfer->len + dummy_bytes;
+ }
+
+end_of_transfer:
+ gpiod_set_value_cansleep(cs_gpiod, false);
+
+ mutex_unlock(&ospi->lock);
+
+ msg->status = ret;
+ spi_finalize_current_message(ctrl);
+
+ pm_runtime_mark_last_busy(ospi->dev);
+ pm_runtime_put_autosuspend(ospi->dev);
+
+ return ret;
+}
+
+static int stm32_ospi_setup(struct spi_device *spi)
+{
+ struct spi_controller *ctrl = spi->controller;
+ struct stm32_ospi *ospi = spi_controller_get_devdata(ctrl);
+ void __iomem *regs_base = ospi->regs_base;
+ int ret;
+ u8 cs = spi->chip_select[ffs(spi->cs_index_mask) - 1];
+
+ if (ctrl->busy)
+ return -EBUSY;
+
+ if (!spi->max_speed_hz)
+ return -EINVAL;
+
+ ret = pm_runtime_resume_and_get(ospi->dev);
+ if (ret < 0)
+ return ret;
+
+ ospi->flash_presc[cs] = DIV_ROUND_UP(ospi->clk_rate, spi->max_speed_hz) - 1;
+
+ mutex_lock(&ospi->lock);
+
+ ospi->cr_reg = CR_APMS | 3 << CR_FTHRES_SHIFT | CR_EN;
+ writel_relaxed(ospi->cr_reg, regs_base + OSPI_CR);
+
+ /* set dcr fsize to max address */
+ ospi->dcr_reg = DCR1_DEVSIZE_MASK | DCR1_DLYBYP;
+ writel_relaxed(ospi->dcr_reg, regs_base + OSPI_DCR1);
+
+ mutex_unlock(&ospi->lock);
+
+ pm_runtime_mark_last_busy(ospi->dev);
+ pm_runtime_put_autosuspend(ospi->dev);
+
+ return 0;
+}
+
+/*
+ * No special host constraint, so use default spi_mem_default_supports_op
+ * to check supported mode.
+ */
+static const struct spi_controller_mem_ops stm32_ospi_mem_ops = {
+ .exec_op = stm32_ospi_exec_op,
+ .dirmap_create = stm32_ospi_dirmap_create,
+ .dirmap_read = stm32_ospi_dirmap_read,
+ .poll_status = stm32_ospi_poll_status,
+};
+
+static int stm32_ospi_get_resources(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct stm32_ospi *ospi = platform_get_drvdata(pdev);
+ struct resource *res;
+ struct reserved_mem *rmem = NULL;
+ struct device_node *node;
+ int ret;
+
+ ospi->regs_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(ospi->regs_base))
+ return PTR_ERR(ospi->regs_base);
+
+ ospi->regs_phys_base = res->start;
+
+ ospi->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(ospi->clk))
+ return dev_err_probe(dev, PTR_ERR(ospi->clk),
+ "Can't get clock\n");
+
+ ospi->clk_rate = clk_get_rate(ospi->clk);
+ if (!ospi->clk_rate) {
+ dev_err(dev, "Invalid clock rate\n");
+ return -EINVAL;
+ }
+
+ ospi->irq = platform_get_irq(pdev, 0);
+ if (ospi->irq < 0) {
+ dev_err(dev, "Can't get irq %d\n", ospi->irq);
+ return ospi->irq;
+ }
+
+ ret = devm_request_irq(dev, ospi->irq, stm32_ospi_irq, 0,
+ dev_name(dev), ospi);
+ if (ret) {
+ dev_err(dev, "Failed to request irq\n");
+ return ret;
+ }
+
+ ospi->rstc = devm_reset_control_array_get_optional_exclusive(dev);
+ if (IS_ERR(ospi->rstc))
+ return dev_err_probe(dev, PTR_ERR(ospi->rstc),
+ "Can't get reset\n");
+
+ ospi->dma_chrx = dma_request_chan(dev, "rx");
+ if (IS_ERR(ospi->dma_chrx)) {
+ ret = PTR_ERR(ospi->dma_chrx);
+ ospi->dma_chrx = NULL;
+ if (ret == -EPROBE_DEFER)
+ goto err_dma;
+ }
+
+ ospi->dma_chtx = dma_request_chan(dev, "tx");
+ if (IS_ERR(ospi->dma_chtx)) {
+ ret = PTR_ERR(ospi->dma_chtx);
+ ospi->dma_chtx = NULL;
+ if (ret == -EPROBE_DEFER)
+ goto err_dma;
+ }
+
+ node = of_parse_phandle(dev->of_node, "memory-region", 0);
+ if (node)
+ rmem = of_reserved_mem_lookup(node);
+ of_node_put(node);
+
+ if (rmem) {
+ ospi->mm_size = rmem->size;
+ ospi->mm_base = devm_ioremap(dev, rmem->base, rmem->size);
+ if (IS_ERR(ospi->mm_base)) {
+ dev_err(dev, "unable to map memory region: %pa+%pa\n",
+ &rmem->base, &rmem->size);
+ ret = PTR_ERR(ospi->mm_base);
+ goto err_dma;
+ }
+
+ if (ospi->mm_size > STM32_OSPI_MAX_MMAP_SZ) {
+ dev_err(dev, "Memory map size outsize bounds\n");
+ ret = -EINVAL;
+ goto err_dma;
+ }
+ } else {
+ dev_info(dev, "No memory-map region found\n");
+ }
+
+ init_completion(&ospi->data_completion);
+ init_completion(&ospi->match_completion);
+
+ return 0;
+
+err_dma:
+ dev_info(dev, "Can't get all resources (%d)\n", ret);
+
+ if (ospi->dma_chtx)
+ dma_release_channel(ospi->dma_chtx);
+ if (ospi->dma_chrx)
+ dma_release_channel(ospi->dma_chrx);
+
+ return ret;
+};
+
+static int stm32_ospi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct spi_controller *ctrl;
+ struct stm32_ospi *ospi;
+ struct dma_slave_config dma_cfg;
+ struct device_node *child;
+ int ret;
+ u8 spi_flash_count = 0;
+
+ /*
+ * Flash subnodes sanity check:
+ * 1 or 2 spi-nand/spi-nor flashes => supported
+ * All other flash node configuration => not supported
+ */
+ for_each_available_child_of_node(dev->of_node, child) {
+ if (of_device_is_compatible(child, "jedec,spi-nor") ||
+ of_device_is_compatible(child, "spi-nand"))
+ spi_flash_count++;
+ }
+
+ if (spi_flash_count == 0 || spi_flash_count > 2) {
+ dev_err(dev, "Incorrect DT flash node\n");
+ return -ENODEV;
+ }
+
+ ctrl = devm_spi_alloc_host(dev, sizeof(*ospi));
+ if (!ctrl)
+ return -ENOMEM;
+
+ ospi = spi_controller_get_devdata(ctrl);
+ ospi->ctrl = ctrl;
+
+ ospi->dev = &pdev->dev;
+ platform_set_drvdata(pdev, ospi);
+
+ ret = stm32_ospi_get_resources(pdev);
+ if (ret)
+ return ret;
+
+ memset(&dma_cfg, 0, sizeof(dma_cfg));
+ dma_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ dma_cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ dma_cfg.src_addr = ospi->regs_phys_base + OSPI_DR;
+ dma_cfg.dst_addr = ospi->regs_phys_base + OSPI_DR;
+ dma_cfg.src_maxburst = 4;
+ dma_cfg.dst_maxburst = 4;
+ stm32_ospi_dma_setup(ospi, &dma_cfg);
+
+ mutex_init(&ospi->lock);
+
+ ctrl->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD |
+ SPI_TX_DUAL | SPI_TX_QUAD |
+ SPI_TX_OCTAL | SPI_RX_OCTAL;
+ ctrl->setup = stm32_ospi_setup;
+ ctrl->bus_num = -1;
+ ctrl->mem_ops = &stm32_ospi_mem_ops;
+ ctrl->use_gpio_descriptors = true;
+ ctrl->transfer_one_message = stm32_ospi_transfer_one_message;
+ ctrl->num_chipselect = STM32_OSPI_MAX_NORCHIP;
+ ctrl->dev.of_node = dev->of_node;
+
+ pm_runtime_enable(ospi->dev);
+ pm_runtime_set_autosuspend_delay(ospi->dev, STM32_AUTOSUSPEND_DELAY);
+ pm_runtime_use_autosuspend(ospi->dev);
+
+ ret = pm_runtime_resume_and_get(ospi->dev);
+ if (ret < 0)
+ goto err_pm_enable;
+
+ if (ospi->rstc) {
+ reset_control_assert(ospi->rstc);
+ udelay(2);
+ reset_control_deassert(ospi->rstc);
+ }
+
+ ret = spi_register_controller(ctrl);
+ if (ret) {
+ /* Disable ospi */
+ writel_relaxed(0, ospi->regs_base + OSPI_CR);
+ goto err_pm_resume;
+ }
+
+ pm_runtime_mark_last_busy(ospi->dev);
+ pm_runtime_put_autosuspend(ospi->dev);
+
+ return 0;
+
+err_pm_resume:
+ pm_runtime_put_sync_suspend(ospi->dev);
+
+err_pm_enable:
+ pm_runtime_force_suspend(ospi->dev);
+ mutex_destroy(&ospi->lock);
+
+ return ret;
+}
+
+static void stm32_ospi_remove(struct platform_device *pdev)
+{
+ struct stm32_ospi *ospi = platform_get_drvdata(pdev);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(ospi->dev);
+ if (ret < 0)
+ return;
+
+ spi_unregister_controller(ospi->ctrl);
+ /* Disable ospi */
+ writel_relaxed(0, ospi->regs_base + OSPI_CR);
+ mutex_destroy(&ospi->lock);
+
+ if (ospi->dma_chtx)
+ dma_release_channel(ospi->dma_chtx);
+ if (ospi->dma_chrx)
+ dma_release_channel(ospi->dma_chrx);
+
+ pm_runtime_put_sync_suspend(ospi->dev);
+ pm_runtime_force_suspend(ospi->dev);
+}
+
+static int __maybe_unused stm32_ospi_suspend(struct device *dev)
+{
+ struct stm32_ospi *ospi = dev_get_drvdata(dev);
+
+ pinctrl_pm_select_sleep_state(dev);
+
+ return pm_runtime_force_suspend(ospi->dev);
+}
+
+static int __maybe_unused stm32_ospi_resume(struct device *dev)
+{
+ struct stm32_ospi *ospi = dev_get_drvdata(dev);
+ void __iomem *regs_base = ospi->regs_base;
+ int ret;
+
+ ret = pm_runtime_force_resume(ospi->dev);
+ if (ret < 0)
+ return ret;
+
+ pinctrl_pm_select_default_state(dev);
+
+ ret = pm_runtime_resume_and_get(ospi->dev);
+ if (ret < 0)
+ return ret;
+
+ writel_relaxed(ospi->cr_reg, regs_base + OSPI_CR);
+ writel_relaxed(ospi->dcr_reg, regs_base + OSPI_DCR1);
+ pm_runtime_mark_last_busy(ospi->dev);
+ pm_runtime_put_autosuspend(ospi->dev);
+
+ return 0;
+}
+
+static int __maybe_unused stm32_ospi_runtime_suspend(struct device *dev)
+{
+ struct stm32_ospi *ospi = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(ospi->clk);
+
+ return 0;
+}
+
+static int __maybe_unused stm32_ospi_runtime_resume(struct device *dev)
+{
+ struct stm32_ospi *ospi = dev_get_drvdata(dev);
+
+ return clk_prepare_enable(ospi->clk);
+}
+
+static const struct dev_pm_ops stm32_ospi_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(stm32_ospi_suspend, stm32_ospi_resume)
+ SET_RUNTIME_PM_OPS(stm32_ospi_runtime_suspend,
+ stm32_ospi_runtime_resume, NULL)
+};
+
+static const struct of_device_id stm32_ospi_of_match[] = {
+ { .compatible = "st,stm32mp25-ospi" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, stm32_ospi_of_match);
+
+static struct platform_driver stm32_ospi_driver = {
+ .probe = stm32_ospi_probe,
+ .remove = stm32_ospi_remove,
+ .driver = {
+ .name = "stm32-ospi",
+ .pm = &stm32_ospi_pm_ops,
+ .of_match_table = stm32_ospi_of_match,
+ },
+};
+module_platform_driver(stm32_ospi_driver);
+
+MODULE_DESCRIPTION("STMicroelectronics STM32 OCTO SPI driver");
+MODULE_LICENSE("GPL");
--
2.25.1
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH 3/9] dt-bindings: misc: Add STM32 Octo Memory Manager controller
2025-01-22 14:10 [PATCH 0/9] Add STM32MP25 SPI NOR support patrice.chotard
2025-01-22 14:10 ` [PATCH 1/9] dt-bindings: spi: Add STM32 OSPI controller patrice.chotard
2025-01-22 14:10 ` [PATCH 2/9] spi: stm32: Add OSPI driver patrice.chotard
@ 2025-01-22 14:10 ` patrice.chotard
2025-01-22 15:22 ` Krzysztof Kozlowski
2025-01-22 14:10 ` [PATCH 4/9] misc: Add STM32 Octo Memory Manager driver patrice.chotard
` (5 subsequent siblings)
8 siblings, 1 reply; 16+ messages in thread
From: patrice.chotard @ 2025-01-22 14:10 UTC (permalink / raw)
To: Mark Brown, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Alexandre Torgue, Philipp Zabel, Maxime Coquelin,
Greg Kroah-Hartman, Arnd Bergmann, Catalin Marinas, Will Deacon
Cc: linux-spi, devicetree, linux-stm32, linux-arm-kernel,
linux-kernel, christophe.kerello, patrice.chotard
From: Patrice Chotard <patrice.chotard@foss.st.com>
Add bindings for STM32 Octo Memory Manager (OMM) controller.
OMM manages:
- the muxing between 2 OSPI busses and 2 output ports.
There are 4 possible muxing configurations:
- direct mode (no multiplexing): OSPI1 output is on port 1 and OSPI2
output is on port 2
- OSPI1 and OSPI2 are multiplexed over the same output port 1
- swapped mode (no multiplexing), OSPI1 output is on port 2,
OSPI2 output is on port 1
- OSPI1 and OSPI2 are multiplexed over the same output port 2
- the split of the memory area shared between the 2 OSPI instances.
- chip select selection override.
- the time between 2 transactions in multiplexed mode.
Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com>
---
.../bindings/misc/st,stm32-omm.yaml | 194 ++++++++++++++++++
1 file changed, 194 insertions(+)
create mode 100644 Documentation/devicetree/bindings/misc/st,stm32-omm.yaml
diff --git a/Documentation/devicetree/bindings/misc/st,stm32-omm.yaml b/Documentation/devicetree/bindings/misc/st,stm32-omm.yaml
new file mode 100644
index 000000000000..ef8f5d2c526c
--- /dev/null
+++ b/Documentation/devicetree/bindings/misc/st,stm32-omm.yaml
@@ -0,0 +1,194 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/misc/st,stm32-omm.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: STM32 Octo Memory Manager (OMM)
+
+maintainers:
+ - Patrice Chotard <patrice.chotard@foss.st.com>
+
+description: |
+ The STM32 Octo Memory Manager is a low-level interface that enables an
+ efficient OCTOSPI pin assignment with a full I/O matrix (before alternate
+ function map) and multiplex of single/dual/quad/octal SPI interfaces over
+ the same bus. It Supports up to:
+ - Two single/dual/quad/octal SPI interfaces
+ - Two ports for pin assignment
+
+properties:
+ compatible:
+ const: st,stm32mp25-omm
+
+ "#address-cells":
+ const: 2
+
+ "#size-cells":
+ const: 1
+
+ ranges:
+ description: |
+ Reflects the memory layout with four integer values per OSPI instance.
+ Format:
+ <chip-select> 0 <registers base address> <size>
+
+ reg:
+ items:
+ - description: registers
+ - description: memory mapping
+
+ reg-names:
+ items:
+ - const: omm
+ - const: omm_mm
+
+ memory-region:
+ description: Phandle to a node describing memory-map region to be used.
+
+ memory-region-names:
+ minItems: 1
+ items:
+ - const: mm_ospi1
+ - const: mm_ospi2
+
+ clocks:
+ maxItems: 1
+
+ resets:
+ maxItems: 1
+
+ access-controllers:
+ minItems: 1
+ maxItems: 2
+
+ st,syscfg-amcr:
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ description: |
+ The Address Mapping Control Register (AMCR) is used to split the 256MB
+ memory map area shared between the 2 OSPI instance. The Octo Memory
+ Manager sets the AMCR depending of the memory-region configuration.
+ Format is phandle to syscfg / register offset within syscfg / memory split
+ bitmask.
+ The memory split bitmask description is:
+ - 000: OCTOSPI1 (256 Mbytes), OCTOSPI2 unmapped
+ - 001: OCTOSPI1 (192 Mbytes), OCTOSPI2 (64 Mbytes)
+ - 010: OCTOSPI1 (128 Mbytes), OCTOSPI2 (128 Mbytes)
+ - 011: OCTOSPI1 (64 Mbytes), OCTOSPI2 (192 Mbytes)
+ - 1xx: OCTOSPI1 unmapped, OCTOSPI2 (256 Mbytes)
+ items:
+ minItems: 3
+ maxItems: 3
+
+ st,omm-req2ack-ns:
+ description: |
+ In multiplexed mode (MUXEN = 1), this field defines the time in
+ nanoseconds between two transactions.
+
+ st,omm-cssel-ovr:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: |
+ Configure the chip select selector override for the 2 OCTOSPIs.
+ The 2 bits mask muxing description is:
+ -bit 0: Chip select selector override setting for OCTOSPI1
+ 0x0: the chip select signal from OCTOSPI1 is sent to NCS1
+ 0x1: the chip select signal from OCTOSPI1 is sent to NCS2
+ -bit 1: Chip select selector override setting for OCTOSPI2
+ 0x0: the chip select signal from OCTOSPI2 is sent to NCS1
+ 0x1: the chip select signal from OCTOSPI2 is sent to NCS2
+
+ st,omm-mux:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: |
+ Configure the muxing between the 2 OCTOSPIs busses and the 2 output ports.
+ The muxing 2 bits mask description is:
+ - 0x0: direct mode, default
+ - 0x1: mux OCTOSPI1 and OCTOSPI2 to port 1
+ - 0x2: swapped mode
+ - 0x3: mux OCTOSPI1 and OCTOSPI2 to port 2
+
+ power-domains:
+ maxItems: 1
+
+patternProperties:
+ "^spi@[a-f0-9]+$":
+ type: object
+ $ref: "/schemas/spi/st,stm32-ospi.yaml#"
+ description: Required spi child node
+
+required:
+ - compatible
+ - reg
+ - "#address-cells"
+ - "#size-cells"
+ - clocks
+ - st,syscfg-amcr
+ - ranges
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/st,stm32mp25-rcc.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/reset/st,stm32mp25-rcc.h>
+ ommanager@40500000 {
+ compatible = "st,stm32mp25-omm";
+ reg = <0x40500000 0x400>, <0x60000000 0x10000000>;
+ reg-names = "omm", "omm_mm";
+ memory-region = <&mm_ospi1>, <&mm_ospi2>;
+ memory-region-names = "mm_ospi1", "mm_ospi2";
+ pinctrl-names = "default", "sleep";
+ pinctrl-0 = <&ospi_port1_clk_pins_a
+ &ospi_port1_io03_pins_a
+ &ospi_port1_cs0_pins_a>;
+ pinctrl-1 = <&ospi_port1_clk_sleep_pins_a
+ &ospi_port1_io03_sleep_pins_a
+ &ospi_port1_cs0_sleep_pins_a>;
+ clocks = <&rcc CK_BUS_OSPIIOM>;
+ resets = <&rcc OSPIIOM_R>;
+ st,syscfg-amcr = <&syscfg 0x2c00 0x7>;
+ st,omm-req2ack-ns = <0x0>;
+ st,omm-mux = <0x0>;
+ st,omm-cssel-ovr = <0x0>;
+ access-controllers = <&rifsc 111>;
+ power-domains = <&CLUSTER_PD>;
+
+ #address-cells = <2>;
+ #size-cells = <1>;
+
+ ranges = <0 0 0x40430000 0x400>,
+ <1 0 0x40440000 0x400>;
+
+ spi@40430000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "st,stm32mp25-ospi";
+ reg = <0 0 0x400>;
+ interrupts = <GIC_SPI 163 IRQ_TYPE_LEVEL_HIGH>;
+ dmas = <&hpdma 2 0x62 0x00003121 0x0>,
+ <&hpdma 2 0x42 0x00003112 0x0>;
+ dma-names = "tx", "rx";
+ clocks = <&scmi_clk CK_SCMI_OSPI1>;
+ resets = <&scmi_reset RST_SCMI_OSPI1>, <&scmi_reset RST_SCMI_OSPI1DLL>;
+ access-controllers = <&rifsc 74>;
+ power-domains = <&CLUSTER_PD>;
+ st,syscfg-dlyb = <&syscfg 0x1000>;
+ };
+
+ spi@40440000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "st,stm32mp25-ospi";
+ reg = <1 0 0x400>;
+ interrupts = <GIC_SPI 164 IRQ_TYPE_LEVEL_HIGH>;
+ dmas = <&hpdma 3 0x62 0x00003121 0x0>,
+ <&hpdma 3 0x42 0x00003112 0x0>;
+ dma-names = "tx", "rx";
+ clocks = <&scmi_clk CK_KER_OSPI2>;
+ resets = <&scmi_reset RST_SCMI_OSPI2>, <&scmi_reset RST_SCMI_OSPI1DLL>;
+ access-controllers = <&rifsc 75>;
+ power-domains = <&CLUSTER_PD>;
+ st,syscfg-dlyb = <&syscfg 0x1000>;
+ };
+ };
--
2.25.1
^ permalink raw reply related [flat|nested] 16+ messages in thread* Re: [PATCH 3/9] dt-bindings: misc: Add STM32 Octo Memory Manager controller
2025-01-22 14:10 ` [PATCH 3/9] dt-bindings: misc: Add STM32 Octo Memory Manager controller patrice.chotard
@ 2025-01-22 15:22 ` Krzysztof Kozlowski
2025-01-24 15:53 ` Patrice CHOTARD
0 siblings, 1 reply; 16+ messages in thread
From: Krzysztof Kozlowski @ 2025-01-22 15:22 UTC (permalink / raw)
To: patrice.chotard, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Alexandre Torgue, Philipp Zabel, Maxime Coquelin,
Greg Kroah-Hartman, Arnd Bergmann, Catalin Marinas, Will Deacon
Cc: linux-spi, devicetree, linux-stm32, linux-arm-kernel,
linux-kernel, christophe.kerello
On 22/01/2025 15:10, patrice.chotard@foss.st.com wrote:
> From: Patrice Chotard <patrice.chotard@foss.st.com>
>
> Add bindings for STM32 Octo Memory Manager (OMM) controller.
>
> OMM manages:
> - the muxing between 2 OSPI busses and 2 output ports.
> There are 4 possible muxing configurations:
> - direct mode (no multiplexing): OSPI1 output is on port 1 and OSPI2
> output is on port 2
> - OSPI1 and OSPI2 are multiplexed over the same output port 1
> - swapped mode (no multiplexing), OSPI1 output is on port 2,
> OSPI2 output is on port 1
> - OSPI1 and OSPI2 are multiplexed over the same output port 2
> - the split of the memory area shared between the 2 OSPI instances.
> - chip select selection override.
> - the time between 2 transactions in multiplexed mode.
>
> Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com>
> ---
> .../bindings/misc/st,stm32-omm.yaml | 194 ++++++++++++++++++
> 1 file changed, 194 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/misc/st,stm32-omm.yaml
All my other comments apply.
Also:
This cannot be misc. Depending what this is, either dedicated subsystem
like memory or soc.
>
> diff --git a/Documentation/devicetree/bindings/misc/st,stm32-omm.yaml b/Documentation/devicetree/bindings/misc/st,stm32-omm.yaml
> new file mode 100644
> index 000000000000..ef8f5d2c526c
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/misc/st,stm32-omm.yaml
> @@ -0,0 +1,194 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/misc/st,stm32-omm.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: STM32 Octo Memory Manager (OMM)
> +
> +maintainers:
> + - Patrice Chotard <patrice.chotard@foss.st.com>
> +
> +description: |
> + The STM32 Octo Memory Manager is a low-level interface that enables an
> + efficient OCTOSPI pin assignment with a full I/O matrix (before alternate
> + function map) and multiplex of single/dual/quad/octal SPI interfaces over
> + the same bus. It Supports up to:
> + - Two single/dual/quad/octal SPI interfaces
> + - Two ports for pin assignment
> +
> +properties:
> + compatible:
> + const: st,stm32mp25-omm
> +
> + "#address-cells":
> + const: 2
> +
> + "#size-cells":
> + const: 1
> +
> + ranges:
> + description: |
> + Reflects the memory layout with four integer values per OSPI instance.
> + Format:
> + <chip-select> 0 <registers base address> <size>
> +
> + reg:
> + items:
> + - description: registers
Well, why here is entirely different syntax? Anyway, useless
description. Say something useful
> + - description: memory mapping
This is a bit better but still confusing. Memory mapping of what?
Virtual memory? This is vague to me.
> +
> + reg-names:
> + items:
> + - const: omm
> + - const: omm_mm
Not useful names. Drop prefixes and then you end up with empty first
entry :/
> +
> + memory-region:
> + description: Phandle to a node describing memory-map region to be used.
Constraints.
> +
> + memory-region-names:
> + minItems: 1
Nope, you just said one phandle?
> + items:
> + - const: mm_ospi1
> + - const: mm_ospi2
Drop redundant parts. If name is just 1 or 2, then just drop xxx-names
> +
> + clocks:
> + maxItems: 1
> +
> + resets:
> + maxItems: 1
> +
> + access-controllers:
> + minItems: 1
> + maxItems: 2
> +
> + st,syscfg-amcr:
> + $ref: /schemas/types.yaml#/definitions/phandle-array
> + description: |
> + The Address Mapping Control Register (AMCR) is used to split the 256MB
> + memory map area shared between the 2 OSPI instance. The Octo Memory
> + Manager sets the AMCR depending of the memory-region configuration.
> + Format is phandle to syscfg / register offset within syscfg / memory split
> + bitmask.
> + The memory split bitmask description is:
> + - 000: OCTOSPI1 (256 Mbytes), OCTOSPI2 unmapped
> + - 001: OCTOSPI1 (192 Mbytes), OCTOSPI2 (64 Mbytes)
> + - 010: OCTOSPI1 (128 Mbytes), OCTOSPI2 (128 Mbytes)
> + - 011: OCTOSPI1 (64 Mbytes), OCTOSPI2 (192 Mbytes)
> + - 1xx: OCTOSPI1 unmapped, OCTOSPI2 (256 Mbytes)
> + items:
> + minItems: 3
> + maxItems: 3
> +
> + st,omm-req2ack-ns:
> + description: |
> + In multiplexed mode (MUXEN = 1), this field defines the time in
> + nanoseconds between two transactions.
> +
> + st,omm-cssel-ovr:
> + $ref: /schemas/types.yaml#/definitions/uint32
> + description: |
> + Configure the chip select selector override for the 2 OCTOSPIs.
> + The 2 bits mask muxing description is:
> + -bit 0: Chip select selector override setting for OCTOSPI1
> + 0x0: the chip select signal from OCTOSPI1 is sent to NCS1
> + 0x1: the chip select signal from OCTOSPI1 is sent to NCS2
> + -bit 1: Chip select selector override setting for OCTOSPI2
> + 0x0: the chip select signal from OCTOSPI2 is sent to NCS1
> + 0x1: the chip select signal from OCTOSPI2 is sent to NCS2
> +
> + st,omm-mux:
> + $ref: /schemas/types.yaml#/definitions/uint32
> + description: |
> + Configure the muxing between the 2 OCTOSPIs busses and the 2 output ports.
> + The muxing 2 bits mask description is:
> + - 0x0: direct mode, default
> + - 0x1: mux OCTOSPI1 and OCTOSPI2 to port 1
> + - 0x2: swapped mode
> + - 0x3: mux OCTOSPI1 and OCTOSPI2 to port 2
> +
> + power-domains:
> + maxItems: 1
> +
> +patternProperties:
> + "^spi@[a-f0-9]+$":
> + type: object
> + $ref: "/schemas/spi/st,stm32-ospi.yaml#"
Drop quotes.
Look at your other $ref and keep things consistent.
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 3/9] dt-bindings: misc: Add STM32 Octo Memory Manager controller
2025-01-22 15:22 ` Krzysztof Kozlowski
@ 2025-01-24 15:53 ` Patrice CHOTARD
0 siblings, 0 replies; 16+ messages in thread
From: Patrice CHOTARD @ 2025-01-24 15:53 UTC (permalink / raw)
To: Krzysztof Kozlowski, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Alexandre Torgue, Philipp Zabel, Maxime Coquelin,
Greg Kroah-Hartman, Arnd Bergmann, Catalin Marinas, Will Deacon
Cc: linux-spi, devicetree, linux-stm32, linux-arm-kernel,
linux-kernel, christophe.kerello
On 1/22/25 16:22, Krzysztof Kozlowski wrote:
> On 22/01/2025 15:10, patrice.chotard@foss.st.com wrote:
>> From: Patrice Chotard <patrice.chotard@foss.st.com>
>>
>> Add bindings for STM32 Octo Memory Manager (OMM) controller.
>>
>> OMM manages:
>> - the muxing between 2 OSPI busses and 2 output ports.
>> There are 4 possible muxing configurations:
>> - direct mode (no multiplexing): OSPI1 output is on port 1 and OSPI2
>> output is on port 2
>> - OSPI1 and OSPI2 are multiplexed over the same output port 1
>> - swapped mode (no multiplexing), OSPI1 output is on port 2,
>> OSPI2 output is on port 1
>> - OSPI1 and OSPI2 are multiplexed over the same output port 2
>> - the split of the memory area shared between the 2 OSPI instances.
>> - chip select selection override.
>> - the time between 2 transactions in multiplexed mode.
>>
>> Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com>
>> ---
>> .../bindings/misc/st,stm32-omm.yaml | 194 ++++++++++++++++++
>> 1 file changed, 194 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/misc/st,stm32-omm.yaml
>
>
> All my other comments apply.
>
> Also:
> This cannot be misc. Depending what this is, either dedicated subsystem
> like memory or soc.
>
Ok i will migrate it to memory
>
>>
>> diff --git a/Documentation/devicetree/bindings/misc/st,stm32-omm.yaml b/Documentation/devicetree/bindings/misc/st,stm32-omm.yaml
>> new file mode 100644
>> index 000000000000..ef8f5d2c526c
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/misc/st,stm32-omm.yaml
>
>
>> @@ -0,0 +1,194 @@
>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/misc/st,stm32-omm.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: STM32 Octo Memory Manager (OMM)
>> +
>> +maintainers:
>> + - Patrice Chotard <patrice.chotard@foss.st.com>
>> +
>> +description: |
>> + The STM32 Octo Memory Manager is a low-level interface that enables an
>> + efficient OCTOSPI pin assignment with a full I/O matrix (before alternate
>> + function map) and multiplex of single/dual/quad/octal SPI interfaces over
>> + the same bus. It Supports up to:
>> + - Two single/dual/quad/octal SPI interfaces
>> + - Two ports for pin assignment
>> +
>> +properties:
>> + compatible:
>> + const: st,stm32mp25-omm
>> +
>> + "#address-cells":
>> + const: 2
>> +
>> + "#size-cells":
>> + const: 1
>> +
>> + ranges:
>> + description: |
>> + Reflects the memory layout with four integer values per OSPI instance.
>> + Format:
>> + <chip-select> 0 <registers base address> <size>
>> +
>> + reg:
>> + items:
>> + - description: registers
>
> Well, why here is entirely different syntax? Anyway, useless
> description. Say something useful
ok
>
>> + - description: memory mapping
>
> This is a bit better but still confusing. Memory mapping of what?
> Virtual memory? This is vague to me.
It's a memory map area, i will update to :
reg:
items:
- description: OMM registers
- description: OMM memory map area
>
>> +
>> + reg-names:
>> + items:
>> + - const: omm
>> + - const: omm_mm
>
> Not useful names. Drop prefixes and then you end up with empty first
> entry :/
Will replace by
reg-names:
items:
- const: regs
- const: memory_map
>
>> +
>> + memory-region:
>> + description: Phandle to a node describing memory-map region to be used.
>
> Constraints.
>
>> +
>> + memory-region-names:
>> + minItems: 1
>
> Nope, you just said one phandle?
I made an error, it's not mandatory and can be up to 2 phandle.
will update as following:
memory-region:
description: Phandle to a node describing memory-map region to be used.
maxItems: 2
>
>> + items:
>> + - const: mm_ospi1
>> + - const: mm_ospi2
>
> Drop redundant parts. If name is just 1 or 2, then just drop xxx-names
I will remove the memory-regions-names properties (a driver update is needed accordingly)
>
>> +
>> + clocks:
>> + maxItems: 1
>> +
>> + resets:
>> + maxItems: 1
>> +
>> + access-controllers:
>> + minItems: 1
>> + maxItems: 2
>> +
>> + st,syscfg-amcr:
>> + $ref: /schemas/types.yaml#/definitions/phandle-array
>> + description: |
>> + The Address Mapping Control Register (AMCR) is used to split the 256MB
>> + memory map area shared between the 2 OSPI instance. The Octo Memory
>> + Manager sets the AMCR depending of the memory-region configuration.
>> + Format is phandle to syscfg / register offset within syscfg / memory split
>> + bitmask.
>> + The memory split bitmask description is:
>> + - 000: OCTOSPI1 (256 Mbytes), OCTOSPI2 unmapped
>> + - 001: OCTOSPI1 (192 Mbytes), OCTOSPI2 (64 Mbytes)
>> + - 010: OCTOSPI1 (128 Mbytes), OCTOSPI2 (128 Mbytes)
>> + - 011: OCTOSPI1 (64 Mbytes), OCTOSPI2 (192 Mbytes)
>> + - 1xx: OCTOSPI1 unmapped, OCTOSPI2 (256 Mbytes)
>> + items:
>> + minItems: 3
>> + maxItems: 3
>> +
>> + st,omm-req2ack-ns:
>> + description: |
>> + In multiplexed mode (MUXEN = 1), this field defines the time in
>> + nanoseconds between two transactions.
>> +
>> + st,omm-cssel-ovr:
>> + $ref: /schemas/types.yaml#/definitions/uint32
>> + description: |
>> + Configure the chip select selector override for the 2 OCTOSPIs.
>> + The 2 bits mask muxing description is:
>> + -bit 0: Chip select selector override setting for OCTOSPI1
>> + 0x0: the chip select signal from OCTOSPI1 is sent to NCS1
>> + 0x1: the chip select signal from OCTOSPI1 is sent to NCS2
>> + -bit 1: Chip select selector override setting for OCTOSPI2
>> + 0x0: the chip select signal from OCTOSPI2 is sent to NCS1
>> + 0x1: the chip select signal from OCTOSPI2 is sent to NCS2
>> +
>> + st,omm-mux:
>> + $ref: /schemas/types.yaml#/definitions/uint32
>> + description: |
>> + Configure the muxing between the 2 OCTOSPIs busses and the 2 output ports.
>> + The muxing 2 bits mask description is:
>> + - 0x0: direct mode, default
>> + - 0x1: mux OCTOSPI1 and OCTOSPI2 to port 1
>> + - 0x2: swapped mode
>> + - 0x3: mux OCTOSPI1 and OCTOSPI2 to port 2
>> +
>> + power-domains:
>> + maxItems: 1
>> +
>> +patternProperties:
>> + "^spi@[a-f0-9]+$":
>> + type: object
>> + $ref: "/schemas/spi/st,stm32-ospi.yaml#"
>
> Drop quotes.
>
> Look at your other $ref and keep things consistent.
ok
Thanks
Patrice
>
> Best regards,
> Krzysztof
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH 4/9] misc: Add STM32 Octo Memory Manager driver
2025-01-22 14:10 [PATCH 0/9] Add STM32MP25 SPI NOR support patrice.chotard
` (2 preceding siblings ...)
2025-01-22 14:10 ` [PATCH 3/9] dt-bindings: misc: Add STM32 Octo Memory Manager controller patrice.chotard
@ 2025-01-22 14:10 ` patrice.chotard
2025-01-22 14:10 ` [PATCH 5/9] arm64: dts: st: Add OMM node on stm32mp251 patrice.chotard
` (4 subsequent siblings)
8 siblings, 0 replies; 16+ messages in thread
From: patrice.chotard @ 2025-01-22 14:10 UTC (permalink / raw)
To: Mark Brown, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Alexandre Torgue, Philipp Zabel, Maxime Coquelin,
Greg Kroah-Hartman, Arnd Bergmann, Catalin Marinas, Will Deacon
Cc: linux-spi, devicetree, linux-stm32, linux-arm-kernel,
linux-kernel, christophe.kerello, patrice.chotard
From: Patrice Chotard <patrice.chotard@foss.st.com>
Octo Memory Manager driver (OMM) manages:
- the muxing between 2 OSPI busses and 2 output ports.
There are 4 possible muxing configurations:
- direct mode (no multiplexing): OSPI1 output is on port 1 and OSPI2
output is on port 2
- OSPI1 and OSPI2 are multiplexed over the same output port 1
- swapped mode (no multiplexing), OSPI1 output is on port 2,
OSPI2 output is on port 1
- OSPI1 and OSPI2 are multiplexed over the same output port 2
- the split of the memory area shared between the 2 OSPI instances.
- chip select selection override.
- the time between 2 transactions in multiplexed mode.
- check firewall access.
Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com>
Signed-off-by: Christophe Kerello <christophe.kerello@foss.st.com>
---
drivers/misc/Kconfig | 17 ++
drivers/misc/Makefile | 1 +
drivers/misc/stm32_omm.c | 510 +++++++++++++++++++++++++++++++++++++++
3 files changed, 528 insertions(+)
create mode 100644 drivers/misc/stm32_omm.c
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 56bc72c7ce4a..a6be09505ee5 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -467,6 +467,23 @@ config DW_XDATA_PCIE
If unsure, say N.
+config STM32_OMM
+ bool "STM32 Octo Memory Manager"
+ depends on SPI_STM32_OSPI || TEST_COMPILE
+ help
+ This driver manages the muxing between the 2 OSPI busses and
+ the 2 output ports. There are 4 possible muxing configurations:
+ - direct mode (no multiplexing): OSPI1 output is on port 1 and OSPI2
+ output is on port 2
+ - OSPI1 and OSPI2 are multiplexed over the same output port 1
+ - swapped mode (no multiplexing), OSPI1 output is on port 2,
+ OSPI2 output is on port 1
+ - OSPI1 and OSPI2 are multiplexed over the same output port 2
+ It also manages :
+ - the split of the memory area shared between the 2 OSPI instances.
+ - chip select selection override.
+ - the time between 2 transactions in multiplexed mode.
+
config PCI_ENDPOINT_TEST
depends on PCI
select CRC32
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 545aad06d088..a20c1cb6e663 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -75,3 +75,4 @@ lan966x-pci-objs := lan966x_pci.o
lan966x-pci-objs += lan966x_pci.dtbo.o
obj-$(CONFIG_MCHP_LAN966X_PCI) += lan966x-pci.o
obj-y += keba/
+obj-$(CONFIG_STM32_OMM) += stm32_omm.o
diff --git a/drivers/misc/stm32_omm.c b/drivers/misc/stm32_omm.c
new file mode 100644
index 000000000000..3a064c4b30f1
--- /dev/null
+++ b/drivers/misc/stm32_omm.c
@@ -0,0 +1,510 @@
+// SPDX-License-Identifier: GPL
+/*
+ * Copyright (C) STMicroelectronics 2024 - All Rights Reserved
+ * Author(s): Patrice Chotard <patrice.chotard@foss.st.com> for STMicroelectronics.
+ */
+
+#include <linux/bus/stm32_firewall_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#define OMM_CR 0
+#define CR_MUXEN BIT(0)
+#define CR_MUXENMODE_MASK GENMASK(1, 0)
+#define CR_CSSEL_OVR_EN BIT(4)
+#define CR_CSSEL_OVR_MASK GENMASK(6, 5)
+#define CR_REQ2ACK_MASK GENMASK(23, 16)
+
+#define OMM_CHILD_NB 2
+
+struct ospi_child {
+ struct device *dev;
+ struct device_node *node;
+ struct clk *clk;
+ struct reset_control *reset;
+};
+
+struct stm32_omm {
+ struct ospi_child child[OMM_CHILD_NB];
+ struct resource *mm_res;
+ struct clk *clk;
+ void __iomem *io_base;
+ u32 cr;
+ u8 nb_child;
+ bool restore_omm;
+};
+
+static int stm32_omm_set_amcr(struct device *dev, bool set)
+{
+ struct stm32_omm *omm = dev_get_drvdata(dev);
+ struct regmap *syscfg_regmap;
+ struct device_node *node;
+ struct resource res, res1;
+ resource_size_t mm_ospi2_size = 0;
+ static const char * const mm_name[] = { "mm_ospi1", "mm_ospi2" };
+ u32 amcr_base, amcr_mask;
+ int ret, i, idx;
+ unsigned int amcr, read_amcr;
+
+ for (i = 0; i < omm->nb_child; i++) {
+ idx = of_property_match_string(dev->of_node,
+ "memory-region-names",
+ mm_name[i]);
+ if (idx < 0)
+ continue;
+
+ /* res1 only used on second loop iteration */
+ res1.start = res.start;
+ res1.end = res.end;
+
+ node = of_parse_phandle(dev->of_node, "memory-region", idx);
+ ret = of_address_to_resource(node, 0, &res);
+ if (ret) {
+ dev_err(dev, "unable to resolve memory region\n");
+ return ret;
+ }
+
+ /* check that memory region fits inside OMM memory map area */
+ if (!resource_contains(omm->mm_res, &res)) {
+ dev_err(dev, "%s doesn't fit inside OMM memory map area\n",
+ mm_name[i]);
+ dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n",
+ res.start, res.end,
+ omm->mm_res->start, omm->mm_res->end);
+
+ return -EFAULT;
+ }
+
+ if (i == 1) {
+ mm_ospi2_size = resource_size(&res);
+
+ /* check that OMM memory region 1 doesn't overlap memory region 2 */
+ if (resource_overlaps(&res, &res1)) {
+ dev_err(dev, "OMM memory-region %s overlaps memory region %s\n",
+ mm_name[0], mm_name[1]);
+ dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n",
+ res1.start, res1.end, res.start, res.end);
+
+ return -EFAULT;
+ }
+ }
+ }
+
+ syscfg_regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "st,syscfg-amcr");
+ if (IS_ERR(syscfg_regmap)) {
+ dev_err(dev, "Failed to get st,syscfg-amcr property\n");
+ return PTR_ERR(syscfg_regmap);
+ }
+
+ ret = of_property_read_u32_index(dev->of_node, "st,syscfg-amcr", 1,
+ &amcr_base);
+ if (ret)
+ return ret;
+
+ ret = of_property_read_u32_index(dev->of_node, "st,syscfg-amcr", 2,
+ &amcr_mask);
+ if (ret)
+ return ret;
+
+ amcr = mm_ospi2_size / SZ_64M;
+
+ if (set)
+ regmap_update_bits(syscfg_regmap, amcr_base, amcr_mask, amcr);
+
+ /* read AMCR and check coherency with memory-map areas defined in DT */
+ regmap_read(syscfg_regmap, amcr_base, &read_amcr);
+ read_amcr = read_amcr >> (ffs(amcr_mask) - 1);
+
+ if (amcr != read_amcr) {
+ dev_err(dev, "AMCR value not coherent with DT memory-map areas\n");
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int stm32_omm_enable_child_clock(struct device *dev, bool enable)
+{
+ /* As there is only 2 children, remember first child in case of error */
+ struct clk *first_child_clk = NULL;
+ struct stm32_omm *omm = dev_get_drvdata(dev);
+ u8 i;
+ int ret;
+
+ for (i = 0; i < omm->nb_child; i++) {
+ if (enable) {
+ ret = clk_prepare_enable(omm->child[i].clk);
+ if (ret) {
+ if (first_child_clk)
+ clk_disable_unprepare(first_child_clk);
+
+ dev_err(dev, "Can not enable clock\n");
+ return ret;
+ }
+ } else {
+ clk_disable_unprepare(omm->child[i].clk);
+ }
+
+ first_child_clk = omm->child[i].clk;
+ }
+
+ return 0;
+}
+
+static int stm32_omm_configure(struct device *dev)
+{
+ struct stm32_omm *omm = dev_get_drvdata(dev);
+ struct reset_control *rstc;
+ unsigned long clk_rate, clk_rate_max = 0;
+ int ret;
+ u8 i;
+ u32 mux = 0;
+ u32 cssel_ovr = 0;
+ u32 req2ack = 0;
+
+ omm->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(omm->clk)) {
+ dev_err(dev, "Failed to get OMM clock (%ld)\n",
+ PTR_ERR(omm->clk));
+
+ return PTR_ERR(omm->clk);
+ }
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ return ret;
+
+ /* parse children's clock */
+ for (i = 0; i < omm->nb_child; i++) {
+ clk_rate = clk_get_rate(omm->child[i].clk);
+ if (!clk_rate) {
+ dev_err(dev, "Invalid clock rate\n");
+ goto err_clk_disable;
+ }
+
+ if (clk_rate > clk_rate_max)
+ clk_rate_max = clk_rate;
+ }
+
+ rstc = devm_reset_control_get_optional(dev, NULL);
+ if (IS_ERR(rstc)) {
+ ret = dev_err_probe(dev, PTR_ERR(rstc), "reset get failed\n");
+ goto err_clk_disable;
+ }
+
+ reset_control_assert(rstc);
+ udelay(2);
+ reset_control_deassert(rstc);
+
+ omm->cr = readl_relaxed(omm->io_base + OMM_CR);
+ /* optional */
+ ret = of_property_read_u32(dev->of_node, "st,omm-mux", &mux);
+ if (!ret) {
+ if (mux & CR_MUXEN) {
+ ret = of_property_read_u32(dev->of_node, "st,omm-req2ack-ns",
+ &req2ack);
+ if (!ret && !req2ack) {
+ req2ack = DIV_ROUND_UP(req2ack, NSEC_PER_SEC / clk_rate_max) - 1;
+
+ if (req2ack > 256)
+ req2ack = 256;
+ }
+
+ req2ack = FIELD_PREP(CR_REQ2ACK_MASK, req2ack);
+
+ omm->cr &= ~CR_REQ2ACK_MASK;
+ omm->cr |= FIELD_PREP(CR_REQ2ACK_MASK, req2ack);
+
+ /*
+ * If the mux is enabled, the 2 OSPI clocks have to be
+ * always enabled
+ */
+ ret = stm32_omm_enable_child_clock(dev, true);
+ if (ret)
+ goto err_clk_disable;
+ }
+
+ omm->cr &= ~CR_MUXENMODE_MASK;
+ omm->cr |= FIELD_PREP(CR_MUXENMODE_MASK, mux);
+ }
+
+ /* optional */
+ ret = of_property_read_u32(dev->of_node, "st,omm-cssel-ovr", &cssel_ovr);
+ if (!ret) {
+ omm->cr &= ~CR_CSSEL_OVR_MASK;
+ omm->cr |= FIELD_PREP(CR_CSSEL_OVR_MASK, cssel_ovr);
+ omm->cr |= CR_CSSEL_OVR_EN;
+ }
+
+ omm->restore_omm = true;
+ writel_relaxed(omm->cr, omm->io_base + OMM_CR);
+
+ ret = stm32_omm_set_amcr(dev, true);
+
+err_clk_disable:
+ pm_runtime_put_sync_suspend(dev);
+
+ return ret;
+}
+
+static int stm32_omm_check_access(struct device *dev, struct device_node *np)
+{
+ struct stm32_firewall firewall;
+ int ret;
+
+ ret = stm32_firewall_get_firewall(np, &firewall, 1);
+ if (ret)
+ return ret;
+
+ return stm32_firewall_grant_access(&firewall);
+}
+
+static int stm32_omm_disable_child(struct device *dev)
+{
+ struct stm32_omm *omm = dev_get_drvdata(dev);
+ u8 i;
+ int ret;
+
+ for (i = 0; i < omm->nb_child; i++) {
+ ret = clk_prepare_enable(omm->child[i].clk);
+ if (ret) {
+ dev_err(dev, "Can not enable clock\n");
+ return ret;
+ }
+
+ /* reset OSPI to ensure CR_EN bit is set to 0 */
+ reset_control_assert(omm->child[i].reset);
+ udelay(2);
+ reset_control_deassert(omm->child[i].reset);
+
+ reset_control_put(omm->child[i].reset);
+ clk_disable_unprepare(omm->child[i].clk);
+ }
+
+ return 0;
+}
+
+static int stm32_omm_probe(struct platform_device *pdev)
+{
+ struct platform_device *vdev;
+ struct device *dev = &pdev->dev;
+ struct stm32_omm *omm;
+ int ret;
+ u8 child_access_granted = 0;
+ u8 i, j;
+ bool child_access[OMM_CHILD_NB];
+
+ omm = devm_kzalloc(dev, sizeof(*omm), GFP_KERNEL);
+ if (!omm)
+ return -ENOMEM;
+
+ omm->io_base = devm_platform_ioremap_resource_byname(pdev, "omm");
+ if (IS_ERR(omm->io_base))
+ return PTR_ERR(omm->io_base);
+
+ omm->mm_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "omm_mm");
+
+ /* check child's access */
+ for_each_child_of_node_scoped(dev->of_node, child) {
+ if (omm->nb_child >= OMM_CHILD_NB) {
+ dev_err(dev, "Bad DT, found too much children\n");
+ ret = -E2BIG;
+ goto err_clk_release;
+ }
+
+ if (!of_device_is_compatible(child, "st,stm32mp25-ospi")) {
+ ret = -EINVAL;
+ goto err_clk_release;
+ }
+
+ ret = stm32_omm_check_access(dev, child);
+ if (ret < 0 && ret != -EACCES)
+ goto err_clk_release;
+
+ child_access[omm->nb_child] = false;
+ if (!ret) {
+ child_access_granted++;
+ child_access[omm->nb_child] = true;
+ }
+
+ omm->child[omm->nb_child].node = child;
+ omm->child[omm->nb_child].clk = of_clk_get(child, 0);
+ omm->child[omm->nb_child].reset = of_reset_control_get_exclusive(child, 0);
+ omm->nb_child++;
+ }
+
+ if (omm->nb_child != OMM_CHILD_NB) {
+ ret = -EINVAL;
+ goto err_clk_release;
+ }
+
+ platform_set_drvdata(pdev, omm);
+
+ pm_runtime_enable(dev);
+
+ /* check if OMM's resource access is granted */
+ ret = stm32_omm_check_access(dev, dev->of_node);
+ if (ret < 0 && ret != -EACCES)
+ goto err_clk_release;
+
+ if (!ret && child_access_granted == OMM_CHILD_NB) {
+ /* Ensure both OSPI instance are disabled before configuring OMM */
+ ret = stm32_omm_disable_child(dev);
+ if (ret)
+ goto err_clk_release;
+
+ ret = stm32_omm_configure(dev);
+ if (ret)
+ goto err_clk_release;
+ } else {
+ dev_dbg(dev, "Octo Memory Manager resource's access not granted\n");
+ /*
+ * AMCR can't be set, so check if current value is coherent
+ * with memory-map areas defined in DT
+ */
+ ret = stm32_omm_set_amcr(dev, false);
+ if (ret)
+ goto err_clk_release;
+ }
+
+ /* for each child, if resource access is granted and status "okay", probe it */
+ for (i = 0; i < omm->nb_child; i++) {
+ if (!child_access[i] || !of_device_is_available(omm->child[i].node))
+ continue;
+
+ vdev = of_platform_device_create(omm->child[i].node, NULL, NULL);
+ if (!vdev) {
+ dev_err(dev, "Failed to create Octo Memory Manager child\n");
+ for (j = i; j > 0; --j) {
+ if (omm->child[j].dev)
+ of_platform_device_destroy(omm->child[j].dev, NULL);
+ }
+
+ ret = -EINVAL;
+ goto err_clk_release;
+ }
+ omm->child[i].dev = &vdev->dev;
+ }
+
+ return 0;
+
+err_clk_release:
+ for (i = 0; i < omm->nb_child; i++) {
+ clk_put(omm->child[i].clk);
+ reset_control_put(omm->child[i].reset);
+ }
+
+ return ret;
+}
+
+static void stm32_omm_remove(struct platform_device *pdev)
+{
+ struct stm32_omm *omm = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < omm->nb_child; i++)
+ if (omm->child[i].dev)
+ of_platform_device_destroy(omm->child[i].dev, NULL);
+
+ if (omm->cr & CR_MUXEN)
+ stm32_omm_enable_child_clock(&pdev->dev, false);
+
+ for (i = 0; i < omm->nb_child; i++) {
+ clk_put(omm->child[i].clk);
+ reset_control_put(omm->child[i].reset);
+ };
+
+ pm_runtime_disable(&pdev->dev);
+}
+
+static const struct of_device_id stm32_omm_of_match[] = {
+ { .compatible = "st,stm32mp25-omm", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, stm32_omm_of_match);
+
+static int __maybe_unused stm32_omm_runtime_suspend(struct device *dev)
+{
+ struct stm32_omm *omm = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(omm->clk);
+
+ return 0;
+}
+
+static int __maybe_unused stm32_omm_runtime_resume(struct device *dev)
+{
+ struct stm32_omm *omm = dev_get_drvdata(dev);
+
+ return clk_prepare_enable(omm->clk);
+}
+
+static int __maybe_unused stm32_omm_suspend(struct device *dev)
+{
+ struct stm32_omm *omm = dev_get_drvdata(dev);
+
+ if (omm->restore_omm && omm->cr & CR_MUXEN)
+ stm32_omm_enable_child_clock(dev, false);
+
+ return pinctrl_pm_select_sleep_state(dev);
+}
+
+static int __maybe_unused stm32_omm_resume(struct device *dev)
+{
+ struct stm32_omm *omm = dev_get_drvdata(dev);
+ int ret;
+
+ pinctrl_pm_select_default_state(dev);
+
+ if (!omm->restore_omm)
+ return 0;
+
+ /* Ensure both OSPI instance are disabled before configuring OMM */
+ ret = stm32_omm_disable_child(dev);
+ if (ret)
+ return ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ return ret;
+
+ writel_relaxed(omm->cr, omm->io_base + OMM_CR);
+ ret = stm32_omm_set_amcr(dev, true);
+ pm_runtime_put_sync_suspend(dev);
+ if (ret)
+ return ret;
+
+ if (omm->cr & CR_MUXEN)
+ ret = stm32_omm_enable_child_clock(dev, true);
+
+ return ret;
+}
+
+static const struct dev_pm_ops stm32_omm_pm_ops = {
+ SET_RUNTIME_PM_OPS(stm32_omm_runtime_suspend,
+ stm32_omm_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(stm32_omm_suspend, stm32_omm_resume)
+};
+
+static struct platform_driver stm32_omm_driver = {
+ .probe = stm32_omm_probe,
+ .remove = stm32_omm_remove,
+ .driver = {
+ .name = "stm32-omm",
+ .of_match_table = stm32_omm_of_match,
+ .pm = &stm32_omm_pm_ops,
+ },
+};
+module_platform_driver(stm32_omm_driver);
+
+MODULE_DESCRIPTION("STMicroelectronics Octo Memory Manager driver");
+MODULE_LICENSE("GPL");
--
2.25.1
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH 5/9] arm64: dts: st: Add OMM node on stm32mp251
2025-01-22 14:10 [PATCH 0/9] Add STM32MP25 SPI NOR support patrice.chotard
` (3 preceding siblings ...)
2025-01-22 14:10 ` [PATCH 4/9] misc: Add STM32 Octo Memory Manager driver patrice.chotard
@ 2025-01-22 14:10 ` patrice.chotard
2025-01-22 14:10 ` [PATCH 6/9] arm64: dts: st: Add ospi port1 pinctrl entries in stm32mp25-pinctrl.dtsi patrice.chotard
` (3 subsequent siblings)
8 siblings, 0 replies; 16+ messages in thread
From: patrice.chotard @ 2025-01-22 14:10 UTC (permalink / raw)
To: Mark Brown, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Alexandre Torgue, Philipp Zabel, Maxime Coquelin,
Greg Kroah-Hartman, Arnd Bergmann, Catalin Marinas, Will Deacon
Cc: linux-spi, devicetree, linux-stm32, linux-arm-kernel,
linux-kernel, christophe.kerello, patrice.chotard
From: Patrice Chotard <patrice.chotard@foss.st.com>
Add Octo Memory Manager (OMM) entry on stm32mp251 and its two
OSPI instance.
Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com>
---
arch/arm64/boot/dts/st/stm32mp251.dtsi | 48 ++++++++++++++++++++++++++
1 file changed, 48 insertions(+)
diff --git a/arch/arm64/boot/dts/st/stm32mp251.dtsi b/arch/arm64/boot/dts/st/stm32mp251.dtsi
index f3c6cdfd7008..7d23a0beb154 100644
--- a/arch/arm64/boot/dts/st/stm32mp251.dtsi
+++ b/arch/arm64/boot/dts/st/stm32mp251.dtsi
@@ -768,6 +768,54 @@ rng: rng@42020000 {
status = "disabled";
};
+ ommanager: ommanager@40500000 {
+ compatible = "st,stm32mp25-omm";
+ reg = <0x40500000 0x400>, <0x60000000 0x10000000>;
+ reg-names = "omm", "omm_mm";
+ ranges = <0 0 0x40430000 0x400>,
+ <1 0 0x40440000 0x400>;
+ clocks = <&rcc CK_BUS_OSPIIOM>;
+ resets = <&rcc OSPIIOM_R>;
+ #address-cells = <2>;
+ #size-cells = <1>;
+ st,syscfg-amcr = <&syscfg 0x2c00 0x7>;
+ access-controllers = <&rifsc 111>;
+ power-domains = <&CLUSTER_PD>;
+ status = "disabled";
+
+ ospi1: spi@40430000 {
+ compatible = "st,stm32mp25-ospi";
+ reg = <0 0 0x400>;
+ interrupts = <GIC_SPI 163 IRQ_TYPE_LEVEL_HIGH>;
+ dmas = <&hpdma 2 0x62 0x00003121>,
+ <&hpdma 2 0x42 0x00003112>;
+ dma-names = "tx", "rx";
+ st,syscfg-dlyb = <&syscfg 0x1000>;
+ clocks = <&scmi_clk CK_SCMI_OSPI1>;
+ resets = <&scmi_reset RST_SCMI_OSPI1>,
+ <&scmi_reset RST_SCMI_OSPI1DLL>;
+ access-controllers = <&rifsc 74>;
+ power-domains = <&CLUSTER_PD>;
+ status = "disabled";
+ };
+
+ ospi2: spi@40440000 {
+ compatible = "st,stm32mp25-ospi";
+ reg = <1 0 0x400>;
+ interrupts = <GIC_SPI 164 IRQ_TYPE_LEVEL_HIGH>;
+ dmas = <&hpdma 3 0x62 0x00003121>,
+ <&hpdma 3 0x42 0x00003112>;
+ dma-names = "tx", "rx";
+ st,syscfg-dlyb = <&syscfg 0x1400>;
+ clocks = <&scmi_clk CK_SCMI_OSPI2>;
+ resets = <&scmi_reset RST_SCMI_OSPI2>,
+ <&scmi_reset RST_SCMI_OSPI2DLL>;
+ access-controllers = <&rifsc 75>;
+ power-domains = <&CLUSTER_PD>;
+ status = "disabled";
+ };
+ };
+
spi8: spi@46020000 {
#address-cells = <1>;
#size-cells = <0>;
--
2.25.1
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH 6/9] arm64: dts: st: Add ospi port1 pinctrl entries in stm32mp25-pinctrl.dtsi
2025-01-22 14:10 [PATCH 0/9] Add STM32MP25 SPI NOR support patrice.chotard
` (4 preceding siblings ...)
2025-01-22 14:10 ` [PATCH 5/9] arm64: dts: st: Add OMM node on stm32mp251 patrice.chotard
@ 2025-01-22 14:10 ` patrice.chotard
2025-01-22 14:10 ` [PATCH 7/9] arm64: dts: st: Add SPI NOR flash support on stm32mp257f-ev1 board patrice.chotard
` (2 subsequent siblings)
8 siblings, 0 replies; 16+ messages in thread
From: patrice.chotard @ 2025-01-22 14:10 UTC (permalink / raw)
To: Mark Brown, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Alexandre Torgue, Philipp Zabel, Maxime Coquelin,
Greg Kroah-Hartman, Arnd Bergmann, Catalin Marinas, Will Deacon
Cc: linux-spi, devicetree, linux-stm32, linux-arm-kernel,
linux-kernel, christophe.kerello, patrice.chotard
From: Patrice Chotard <patrice.chotard@foss.st.com>
Add pinctrl entry related to OSPI's port1 in stm32mp25-pinctrl.dtsi
Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com>
---
arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi | 51 +++++++++++++++++++
1 file changed, 51 insertions(+)
diff --git a/arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi b/arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi
index 8fdd5f020425..cf5be316de26 100644
--- a/arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi
+++ b/arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi
@@ -101,6 +101,57 @@ pins2 {
};
};
+ ospi_port1_clk_pins_a: ospi-port1-clk-0 {
+ pins {
+ pinmux = <STM32_PINMUX('D', 0, AF10)>; /* OSPI1_CLK */
+ bias-disable;
+ drive-push-pull;
+ slew-rate = <2>;
+ };
+ };
+
+ ospi_port1_clk_sleep_pins_a: ospi-port1-clk-sleep-0 {
+ pins {
+ pinmux = <STM32_PINMUX('D', 0, ANALOG)>; /* OSPI1_CLK */
+ };
+ };
+
+ ospi_port1_cs0_pins_a: ospi-port1-cs0-0 {
+ pins {
+ pinmux = <STM32_PINMUX('D', 3, AF10)>; /* OSPI_NCS0 */
+ bias-pull-up;
+ drive-push-pull;
+ slew-rate = <0>;
+ };
+ };
+
+ ospi_port1_cs0_sleep_pins_a: ospi-port1-cs0-sleep-0 {
+ pins {
+ pinmux = <STM32_PINMUX('D', 3, ANALOG)>; /* OSPI_NCS0 */
+ };
+ };
+
+ ospi_port1_io03_pins_a: ospi-port1-io03-0 {
+ pins {
+ pinmux = <STM32_PINMUX('D', 4, AF10)>, /* OSPI_IO0 */
+ <STM32_PINMUX('D', 5, AF10)>, /* OSPI_IO1 */
+ <STM32_PINMUX('D', 6, AF10)>, /* OSPI_IO2 */
+ <STM32_PINMUX('D', 7, AF10)>; /* OSPI_IO3 */
+ bias-disable;
+ drive-push-pull;
+ slew-rate = <0>;
+ };
+ };
+
+ ospi_port1_io03_sleep_pins_a: ospi-port1-io03-sleep-0 {
+ pins {
+ pinmux = <STM32_PINMUX('D', 4, ANALOG)>, /* OSPI_IO0 */
+ <STM32_PINMUX('D', 5, ANALOG)>, /* OSPI_IO1 */
+ <STM32_PINMUX('D', 6, ANALOG)>, /* OSPI_IO2 */
+ <STM32_PINMUX('D', 7, ANALOG)>; /* OSPI_IO3 */
+ };
+ };
+
sdmmc1_b4_od_pins_a: sdmmc1-b4-od-0 {
pins1 {
pinmux = <STM32_PINMUX('E', 4, AF10)>, /* SDMMC1_D0 */
--
2.25.1
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH 7/9] arm64: dts: st: Add SPI NOR flash support on stm32mp257f-ev1 board
2025-01-22 14:10 [PATCH 0/9] Add STM32MP25 SPI NOR support patrice.chotard
` (5 preceding siblings ...)
2025-01-22 14:10 ` [PATCH 6/9] arm64: dts: st: Add ospi port1 pinctrl entries in stm32mp25-pinctrl.dtsi patrice.chotard
@ 2025-01-22 14:10 ` patrice.chotard
2025-01-22 14:10 ` [PATCH 8/9] arm64: defconfig: Enable STM32 Octo Memory Manager driver patrice.chotard
2025-01-22 14:10 ` [PATCH 9/9] arm64: defconfig: Enable STM32 OctoSPI driver patrice.chotard
8 siblings, 0 replies; 16+ messages in thread
From: patrice.chotard @ 2025-01-22 14:10 UTC (permalink / raw)
To: Mark Brown, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Alexandre Torgue, Philipp Zabel, Maxime Coquelin,
Greg Kroah-Hartman, Arnd Bergmann, Catalin Marinas, Will Deacon
Cc: linux-spi, devicetree, linux-stm32, linux-arm-kernel,
linux-kernel, christophe.kerello, patrice.chotard
From: Patrice Chotard <patrice.chotard@foss.st.com>
Add SPI NOR flash nor support on stm32mp257f-ev1 board.
Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com>
---
arch/arm64/boot/dts/st/stm32mp257f-ev1.dts | 33 ++++++++++++++++++++++
1 file changed, 33 insertions(+)
diff --git a/arch/arm64/boot/dts/st/stm32mp257f-ev1.dts b/arch/arm64/boot/dts/st/stm32mp257f-ev1.dts
index 1b88485a62a1..fe5577fff4d3 100644
--- a/arch/arm64/boot/dts/st/stm32mp257f-ev1.dts
+++ b/arch/arm64/boot/dts/st/stm32mp257f-ev1.dts
@@ -80,6 +80,11 @@ fw@80000000 {
reg = <0x0 0x80000000 0x0 0x4000000>;
no-map;
};
+
+ mm_ospi1: mm-ospi@60000000 {
+ reg = <0x0 0x60000000 0x0 0x10000000>;
+ no-map;
+ };
};
};
@@ -190,6 +195,34 @@ &i2c8 {
status = "disabled";
};
+&ommanager {
+ memory-region = <&mm_ospi1>;
+ memory-region-names = "mm_ospi1";
+ pinctrl-names = "default", "sleep";
+ pinctrl-0 = <&ospi_port1_clk_pins_a
+ &ospi_port1_io03_pins_a
+ &ospi_port1_cs0_pins_a>;
+ pinctrl-1 = <&ospi_port1_clk_sleep_pins_a
+ &ospi_port1_io03_sleep_pins_a
+ &ospi_port1_cs0_sleep_pins_a>;
+ status = "okay";
+
+ spi@40430000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ memory-region = <&mm_ospi1>;
+ status = "okay";
+
+ flash0: flash@0 {
+ compatible = "jedec,spi-nor";
+ reg = <0>;
+ spi-rx-bus-width = <4>;
+ spi-tx-bus-width = <4>;
+ spi-max-frequency = <50000000>;
+ };
+ };
+};
+
&rtc {
status = "okay";
};
--
2.25.1
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH 8/9] arm64: defconfig: Enable STM32 Octo Memory Manager driver
2025-01-22 14:10 [PATCH 0/9] Add STM32MP25 SPI NOR support patrice.chotard
` (6 preceding siblings ...)
2025-01-22 14:10 ` [PATCH 7/9] arm64: dts: st: Add SPI NOR flash support on stm32mp257f-ev1 board patrice.chotard
@ 2025-01-22 14:10 ` patrice.chotard
2025-01-22 15:23 ` Krzysztof Kozlowski
2025-01-22 14:10 ` [PATCH 9/9] arm64: defconfig: Enable STM32 OctoSPI driver patrice.chotard
8 siblings, 1 reply; 16+ messages in thread
From: patrice.chotard @ 2025-01-22 14:10 UTC (permalink / raw)
To: Mark Brown, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Alexandre Torgue, Philipp Zabel, Maxime Coquelin,
Greg Kroah-Hartman, Arnd Bergmann, Catalin Marinas, Will Deacon
Cc: linux-spi, devicetree, linux-stm32, linux-arm-kernel,
linux-kernel, christophe.kerello, patrice.chotard
From: Patrice Chotard <patrice.chotard@foss.st.com>
Enable STM32 Octo Memory Manager (OMM) driver which is needed
for OSPI usage on STM32 platforms.
Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com>
---
arch/arm64/configs/defconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
index 246a13412bf0..28df37d1baa6 100644
--- a/arch/arm64/configs/defconfig
+++ b/arch/arm64/configs/defconfig
@@ -294,6 +294,7 @@ CONFIG_BLK_DEV_NVME=m
CONFIG_QCOM_COINCELL=m
CONFIG_QCOM_FASTRPC=m
CONFIG_SRAM=y
+CONFIG_STM32_OMM=m
CONFIG_PCI_ENDPOINT_TEST=m
CONFIG_EEPROM_AT24=m
CONFIG_EEPROM_AT25=m
--
2.25.1
^ permalink raw reply related [flat|nested] 16+ messages in thread* Re: [PATCH 8/9] arm64: defconfig: Enable STM32 Octo Memory Manager driver
2025-01-22 14:10 ` [PATCH 8/9] arm64: defconfig: Enable STM32 Octo Memory Manager driver patrice.chotard
@ 2025-01-22 15:23 ` Krzysztof Kozlowski
2025-01-24 15:53 ` Patrice CHOTARD
0 siblings, 1 reply; 16+ messages in thread
From: Krzysztof Kozlowski @ 2025-01-22 15:23 UTC (permalink / raw)
To: patrice.chotard, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Alexandre Torgue, Philipp Zabel, Maxime Coquelin,
Greg Kroah-Hartman, Arnd Bergmann, Catalin Marinas, Will Deacon
Cc: linux-spi, devicetree, linux-stm32, linux-arm-kernel,
linux-kernel, christophe.kerello
On 22/01/2025 15:10, patrice.chotard@foss.st.com wrote:
> From: Patrice Chotard <patrice.chotard@foss.st.com>
>
> Enable STM32 Octo Memory Manager (OMM) driver which is needed
> for OSPI usage on STM32 platforms.
... And which upstream boards need it?
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 8/9] arm64: defconfig: Enable STM32 Octo Memory Manager driver
2025-01-22 15:23 ` Krzysztof Kozlowski
@ 2025-01-24 15:53 ` Patrice CHOTARD
0 siblings, 0 replies; 16+ messages in thread
From: Patrice CHOTARD @ 2025-01-24 15:53 UTC (permalink / raw)
To: Krzysztof Kozlowski, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Alexandre Torgue, Philipp Zabel, Maxime Coquelin,
Greg Kroah-Hartman, Arnd Bergmann, Catalin Marinas, Will Deacon
Cc: linux-spi, devicetree, linux-stm32, linux-arm-kernel,
linux-kernel, christophe.kerello
On 1/22/25 16:23, Krzysztof Kozlowski wrote:
> On 22/01/2025 15:10, patrice.chotard@foss.st.com wrote:
>> From: Patrice Chotard <patrice.chotard@foss.st.com>
>>
>> Enable STM32 Octo Memory Manager (OMM) driver which is needed
>> for OSPI usage on STM32 platforms.
> ... And which upstream boards need it?
Right, i will add that is needed for stm32mp257f-ev1 board
>
> Best regards,
> Krzysztof
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH 9/9] arm64: defconfig: Enable STM32 OctoSPI driver
2025-01-22 14:10 [PATCH 0/9] Add STM32MP25 SPI NOR support patrice.chotard
` (7 preceding siblings ...)
2025-01-22 14:10 ` [PATCH 8/9] arm64: defconfig: Enable STM32 Octo Memory Manager driver patrice.chotard
@ 2025-01-22 14:10 ` patrice.chotard
8 siblings, 0 replies; 16+ messages in thread
From: patrice.chotard @ 2025-01-22 14:10 UTC (permalink / raw)
To: Mark Brown, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Alexandre Torgue, Philipp Zabel, Maxime Coquelin,
Greg Kroah-Hartman, Arnd Bergmann, Catalin Marinas, Will Deacon
Cc: linux-spi, devicetree, linux-stm32, linux-arm-kernel,
linux-kernel, christophe.kerello, patrice.chotard
From: Patrice Chotard <patrice.chotard@foss.st.com>
Enable the STM32 OctoSPI driver.
Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com>
---
arch/arm64/configs/defconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
index 28df37d1baa6..dbd8ed3bbd7d 100644
--- a/arch/arm64/configs/defconfig
+++ b/arch/arm64/configs/defconfig
@@ -578,6 +578,7 @@ CONFIG_SPI_QUP=y
CONFIG_SPI_QCOM_GENI=m
CONFIG_SPI_S3C64XX=y
CONFIG_SPI_SH_MSIOF=m
+CONFIG_SPI_STM32_OSPI=m
CONFIG_SPI_SUN6I=y
CONFIG_SPI_TEGRA210_QUAD=m
CONFIG_SPI_TEGRA114=m
--
2.25.1
^ permalink raw reply related [flat|nested] 16+ messages in thread