* [PATCH v3 1/8] dt-bindings: spi: Add STM32 OSPI controller
2025-02-10 13:18 [PATCH v3 0/8] Add STM32MP25 SPI NOR support patrice.chotard
@ 2025-02-10 13:18 ` patrice.chotard
2025-02-13 7:57 ` Krzysztof Kozlowski
2025-02-17 9:17 ` Philipp Zabel
2025-02-10 13:18 ` [PATCH v3 2/8] spi: stm32: Add OSPI driver patrice.chotard
` (7 subsequent siblings)
8 siblings, 2 replies; 23+ messages in thread
From: patrice.chotard @ 2025-02-10 13:18 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,stm32mp25-ospi.yaml | 105 ++++++++++++++++++
1 file changed, 105 insertions(+)
create mode 100644 Documentation/devicetree/bindings/spi/st,stm32mp25-ospi.yaml
diff --git a/Documentation/devicetree/bindings/spi/st,stm32mp25-ospi.yaml b/Documentation/devicetree/bindings/spi/st,stm32mp25-ospi.yaml
new file mode 100644
index 000000000000..5f276f27dc4c
--- /dev/null
+++ b/Documentation/devicetree/bindings/spi/st,stm32mp25-ospi.yaml
@@ -0,0 +1,105 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/spi/st,stm32mp25-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:
+ maxItems: 1
+
+ memory-region:
+ description:
+ Memory region to be used for memory-map read access.
+ In memory-mapped mode, read access are performed from the memory
+ device using the direct mapping.
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ resets:
+ items:
+ - description: phandle to OSPI block reset
+ - description: phandle to delay block reset
+
+ dmas:
+ maxItems: 2
+
+ dma-names:
+ items:
+ - const: tx
+ - const: rx
+
+ st,syscfg-dlyb:
+ description: configure OCTOSPI delay block.
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ items:
+ - description: phandle to syscfg
+ - description: register offset within syscfg
+
+ access-controllers:
+ description: phandle to the rifsc device to check access right
+ and in some cases, an additional phandle to the rcc device for
+ secure clock control.
+ items:
+ - description: phandle to bus controller
+ - description: phandle to clock controller
+ minItems: 1
+
+ power-domains:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - 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] 23+ messages in thread* Re: [PATCH v3 1/8] dt-bindings: spi: Add STM32 OSPI controller
2025-02-10 13:18 ` [PATCH v3 1/8] dt-bindings: spi: Add STM32 OSPI controller patrice.chotard
@ 2025-02-13 7:57 ` Krzysztof Kozlowski
2025-02-17 9:17 ` Philipp Zabel
1 sibling, 0 replies; 23+ messages in thread
From: Krzysztof Kozlowski @ 2025-02-13 7:57 UTC (permalink / raw)
To: patrice.chotard
Cc: Mark Brown, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Alexandre Torgue, Philipp Zabel, Maxime Coquelin,
Greg Kroah-Hartman, Arnd Bergmann, Catalin Marinas, Will Deacon,
linux-spi, devicetree, linux-stm32, linux-arm-kernel,
linux-kernel, christophe.kerello
On Mon, Feb 10, 2025 at 02:18:19PM +0100, patrice.chotard@foss.st.com wrote:
> 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,
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v3 1/8] dt-bindings: spi: Add STM32 OSPI controller
2025-02-10 13:18 ` [PATCH v3 1/8] dt-bindings: spi: Add STM32 OSPI controller patrice.chotard
2025-02-13 7:57 ` Krzysztof Kozlowski
@ 2025-02-17 9:17 ` Philipp Zabel
2025-02-18 8:39 ` Patrice CHOTARD
1 sibling, 1 reply; 23+ messages in thread
From: Philipp Zabel @ 2025-02-17 9:17 UTC (permalink / raw)
To: patrice.chotard, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Alexandre Torgue, 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 Mo, 2025-02-10 at 14:18 +0100, patrice.chotard@foss.st.com wrote:
> 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,stm32mp25-ospi.yaml | 105 ++++++++++++++++++
> 1 file changed, 105 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/spi/st,stm32mp25-ospi.yaml
>
> diff --git a/Documentation/devicetree/bindings/spi/st,stm32mp25-ospi.yaml b/Documentation/devicetree/bindings/spi/st,stm32mp25-ospi.yaml
> new file mode 100644
> index 000000000000..5f276f27dc4c
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/spi/st,stm32mp25-ospi.yaml
> @@ -0,0 +1,105 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/spi/st,stm32mp25-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:
> + maxItems: 1
> +
> + memory-region:
> + description:
> + Memory region to be used for memory-map read access.
> + In memory-mapped mode, read access are performed from the memory
> + device using the direct mapping.
> + maxItems: 1
> +
> + clocks:
> + maxItems: 1
> +
> + interrupts:
> + maxItems: 1
> +
> + resets:
> + items:
> + - description: phandle to OSPI block reset
> + - description: phandle to delay block reset
Are you positive that these will only ever have to be reset together?
Otherwise I'd add a reset-names property just in case.
regards
Philipp
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v3 1/8] dt-bindings: spi: Add STM32 OSPI controller
2025-02-17 9:17 ` Philipp Zabel
@ 2025-02-18 8:39 ` Patrice CHOTARD
0 siblings, 0 replies; 23+ messages in thread
From: Patrice CHOTARD @ 2025-02-18 8:39 UTC (permalink / raw)
To: Philipp Zabel, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Alexandre Torgue, 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 2/17/25 10:17, Philipp Zabel wrote:
> On Mo, 2025-02-10 at 14:18 +0100, patrice.chotard@foss.st.com wrote:
>> 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,stm32mp25-ospi.yaml | 105 ++++++++++++++++++
>> 1 file changed, 105 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/spi/st,stm32mp25-ospi.yaml
>>
>> diff --git a/Documentation/devicetree/bindings/spi/st,stm32mp25-ospi.yaml b/Documentation/devicetree/bindings/spi/st,stm32mp25-ospi.yaml
>> new file mode 100644
>> index 000000000000..5f276f27dc4c
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/spi/st,stm32mp25-ospi.yaml
>> @@ -0,0 +1,105 @@
>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/spi/st,stm32mp25-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:
>> + maxItems: 1
>> +
>> + memory-region:
>> + description:
>> + Memory region to be used for memory-map read access.
>> + In memory-mapped mode, read access are performed from the memory
>> + device using the direct mapping.
>> + maxItems: 1
>> +
>> + clocks:
>> + maxItems: 1
>> +
>> + interrupts:
>> + maxItems: 1
>> +
>> + resets:
>> + items:
>> + - description: phandle to OSPI block reset
>> + - description: phandle to delay block reset
>
> Are you positive that these will only ever have to be reset together?
> Otherwise I'd add a reset-names property just in case.
Yes i confirm that these both reset are reset together.
Thanks
Patrice
>
> regards
> Philipp
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v3 2/8] spi: stm32: Add OSPI driver
2025-02-10 13:18 [PATCH v3 0/8] Add STM32MP25 SPI NOR support patrice.chotard
2025-02-10 13:18 ` [PATCH v3 1/8] dt-bindings: spi: Add STM32 OSPI controller patrice.chotard
@ 2025-02-10 13:18 ` patrice.chotard
2025-02-10 13:18 ` [PATCH v3 3/8] dt-bindings: memory-controllers: Add STM32 Octo Memory Manager controller patrice.chotard
` (6 subsequent siblings)
8 siblings, 0 replies; 23+ messages in thread
From: patrice.chotard @ 2025-02-10 13:18 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 | 1065 ++++++++++++++++++++++++++++++++++
3 files changed, 1076 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..41cbd6df048f 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -137,6 +137,7 @@ obj-$(CONFIG_SPI_SN_F_OSPI) += spi-sn-f-ospi.o
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_OSPI) += spi-stm32-ospi.o
obj-$(CONFIG_SPI_STM32_QSPI) += spi-stm32-qspi.o
obj-$(CONFIG_SPI_ST_SSC4) += spi-st-ssc4.o
obj-$(CONFIG_SPI_SUN4I) += spi-sun4i.o
diff --git a/drivers/spi/spi-stm32-ospi.c b/drivers/spi/spi-stm32-ospi.c
new file mode 100644
index 000000000000..8eadcb64f34a
--- /dev/null
+++ b/drivers/spi/spi-stm32-ospi.c
@@ -0,0 +1,1065 @@
+// 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_poll(struct stm32_ospi *ospi, u8 *buf, u32 len, bool read)
+{
+ void __iomem *regs_base = ospi->regs_base;
+ void (*fifo)(u8 *val, void __iomem *addr);
+ u32 sr;
+ int ret;
+
+ if (read)
+ fifo = stm32_ospi_read_fifo;
+ else
+ 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;
+ }
+ 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_xfer(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_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_xfer(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->flags = SPI_CONTROLLER_HALF_DUPLEX;
+ 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] 23+ messages in thread* [PATCH v3 3/8] dt-bindings: memory-controllers: Add STM32 Octo Memory Manager controller
2025-02-10 13:18 [PATCH v3 0/8] Add STM32MP25 SPI NOR support patrice.chotard
2025-02-10 13:18 ` [PATCH v3 1/8] dt-bindings: spi: Add STM32 OSPI controller patrice.chotard
2025-02-10 13:18 ` [PATCH v3 2/8] spi: stm32: Add OSPI driver patrice.chotard
@ 2025-02-10 13:18 ` patrice.chotard
2025-02-13 8:00 ` Krzysztof Kozlowski
2025-02-10 13:18 ` [PATCH v3 4/8] memory: Add STM32 Octo Memory Manager driver patrice.chotard
` (5 subsequent siblings)
8 siblings, 1 reply; 23+ messages in thread
From: patrice.chotard @ 2025-02-10 13:18 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>
---
.../memory-controllers/st,stm32mp25-omm.yaml | 201 ++++++++++++++++++
1 file changed, 201 insertions(+)
create mode 100644 Documentation/devicetree/bindings/memory-controllers/st,stm32mp25-omm.yaml
diff --git a/Documentation/devicetree/bindings/memory-controllers/st,stm32mp25-omm.yaml b/Documentation/devicetree/bindings/memory-controllers/st,stm32mp25-omm.yaml
new file mode 100644
index 000000000000..c897e6bf490d
--- /dev/null
+++ b/Documentation/devicetree/bindings/memory-controllers/st,stm32mp25-omm.yaml
@@ -0,0 +1,201 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/memory-controllers/st,stm32mp25-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>
+ minItems: 2
+ maxItems: 2
+
+ reg:
+ items:
+ - description: OMM registers
+ - description: OMM memory map area
+
+ reg-names:
+ items:
+ - const: regs
+ - const: memory_map
+
+ memory-region:
+ description: |
+ Memory region shared between the 2 OCTOSPI instance.
+ One or two phandle to a node describing a memory mapped region
+ depending of child number.
+ minItems: 1
+ maxItems: 2
+
+ memory-region-names:
+ description: |
+ OCTOSPI instance's name to which memory region is associated
+ items:
+ enum: [ospi1, ospi2]
+ minItems: 1
+ maxItems: 2
+
+ clocks:
+ maxItems: 1
+
+ resets:
+ maxItems: 1
+
+ access-controllers:
+ maxItems: 1
+
+ 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.
+ 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:
+ - description: phandle to syscfg
+ - description: register offset within syscfg
+ - description: register bitmask for memory split
+
+ 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.
+ - 0: OCTOSPI1 chip select send to NCS1 OCTOSPI2 chip select send to NCS1
+ - 1: OCTOSPI1 chip select send to NCS2 OCTOSPI2 chip select send to NCS1
+ - 2: OCTOSPI1 chip select send to NCS1 OCTOSPI2 chip select send to NCS2
+ - 3: OCTOSPI1 chip select send to NCS2 OCTOSPI2 chip select send to NCS2
+ minimum: 0
+ maximum: 3
+
+ st,omm-mux:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: |
+ Configure the muxing between the 2 OCTOSPIs busses and the 2 output ports.
+ - 0: direct mode, default
+ - 1: mux OCTOSPI1 and OCTOSPI2 to port 1
+ - 2: swapped mode
+ - 3: mux OCTOSPI1 and OCTOSPI2 to port 2
+ minimum: 0
+ maximum: 3
+
+ power-domains:
+ maxItems: 1
+
+patternProperties:
+ ^spi@[a-f0-9]+$:
+ type: object
+ $ref: /schemas/spi/st,stm32mp25-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 = "regs", "memory_map";
+ ranges = <0 0 0x40430000 0x400>,
+ <1 0 0x40440000 0x400>;
+ memory-region = <&mm_ospi1>, <&mm_ospi2>;
+ memory-region-names = "ospi1", "ospi2";
+ 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>;
+ pinctrl-names = "default", "sleep";
+ clocks = <&rcc CK_BUS_OSPIIOM>;
+ resets = <&rcc OSPIIOM_R>;
+ access-controllers = <&rifsc 111>;
+ power-domains = <&CLUSTER_PD>;
+ #address-cells = <2>;
+ #size-cells = <1>;
+ st,syscfg-amcr = <&syscfg 0x2c00 0x7>;
+ st,omm-req2ack-ns = <0>;
+ st,omm-mux = <0>;
+ st,omm-cssel-ovr = <0>;
+
+ spi@40430000 {
+ compatible = "st,stm32mp25-ospi";
+ reg = <0 0 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>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ st,syscfg-dlyb = <&syscfg 0x1000>;
+ };
+
+ spi@40440000 {
+ compatible = "st,stm32mp25-ospi";
+ reg = <1 0 0x400>;
+ memory-region = <&mm_ospi1>;
+ 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>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ st,syscfg-dlyb = <&syscfg 0x1000>;
+ };
+ };
--
2.25.1
^ permalink raw reply related [flat|nested] 23+ messages in thread* Re: [PATCH v3 3/8] dt-bindings: memory-controllers: Add STM32 Octo Memory Manager controller
2025-02-10 13:18 ` [PATCH v3 3/8] dt-bindings: memory-controllers: Add STM32 Octo Memory Manager controller patrice.chotard
@ 2025-02-13 8:00 ` Krzysztof Kozlowski
2025-02-18 9:58 ` Patrice CHOTARD
0 siblings, 1 reply; 23+ messages in thread
From: Krzysztof Kozlowski @ 2025-02-13 8:00 UTC (permalink / raw)
To: patrice.chotard
Cc: Mark Brown, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Alexandre Torgue, Philipp Zabel, Maxime Coquelin,
Greg Kroah-Hartman, Arnd Bergmann, Catalin Marinas, Will Deacon,
linux-spi, devicetree, linux-stm32, linux-arm-kernel,
linux-kernel, christophe.kerello
On Mon, Feb 10, 2025 at 02:18:21PM +0100, 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>
> ---
> .../memory-controllers/st,stm32mp25-omm.yaml | 201 ++++++++++++++++++
> 1 file changed, 201 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/memory-controllers/st,stm32mp25-omm.yaml
>
> diff --git a/Documentation/devicetree/bindings/memory-controllers/st,stm32mp25-omm.yaml b/Documentation/devicetree/bindings/memory-controllers/st,stm32mp25-omm.yaml
> new file mode 100644
> index 000000000000..c897e6bf490d
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/memory-controllers/st,stm32mp25-omm.yaml
> @@ -0,0 +1,201 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/memory-controllers/st,stm32mp25-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>
> + minItems: 2
> + maxItems: 2
> +
> + reg:
> + items:
> + - description: OMM registers
> + - description: OMM memory map area
> +
> + reg-names:
> + items:
> + - const: regs
> + - const: memory_map
> +
> + memory-region:
> + description: |
> + Memory region shared between the 2 OCTOSPI instance.
> + One or two phandle to a node describing a memory mapped region
> + depending of child number.
> + minItems: 1
> + maxItems: 2
> +
> + memory-region-names:
> + description: |
> + OCTOSPI instance's name to which memory region is associated
> + items:
> + enum: [ospi1, ospi2]
> + minItems: 1
> + maxItems: 2
> +
> + clocks:
> + maxItems: 1
> +
> + resets:
> + maxItems: 1
> +
> + access-controllers:
> + maxItems: 1
> +
> + 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.
> + 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:
> + - description: phandle to syscfg
> + - description: register offset within syscfg
> + - description: register bitmask for memory split
> +
> + st,omm-req2ack-ns:
> + description: |
> + In multiplexed mode (MUXEN = 1), this field defines the time in
> + nanoseconds between two transactions.
default: ?
> +
> + st,omm-cssel-ovr:
> + $ref: /schemas/types.yaml#/definitions/uint32
> + description: |
> + Configure the chip select selector override for the 2 OCTOSPIs.
> + - 0: OCTOSPI1 chip select send to NCS1 OCTOSPI2 chip select send to NCS1
> + - 1: OCTOSPI1 chip select send to NCS2 OCTOSPI2 chip select send to NCS1
> + - 2: OCTOSPI1 chip select send to NCS1 OCTOSPI2 chip select send to NCS2
> + - 3: OCTOSPI1 chip select send to NCS2 OCTOSPI2 chip select send to NCS2
> + minimum: 0
> + maximum: 3
default: ?
> +
> + st,omm-mux:
> + $ref: /schemas/types.yaml#/definitions/uint32
> + description: |
> + Configure the muxing between the 2 OCTOSPIs busses and the 2 output ports.
> + - 0: direct mode, default
Don't repeat constraints in free form text.
> + - 1: mux OCTOSPI1 and OCTOSPI2 to port 1
> + - 2: swapped mode
> + - 3: mux OCTOSPI1 and OCTOSPI2 to port 2
> + minimum: 0
> + maximum: 3
default: ?
None of them are required, so usually we expect defaults.
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v3 3/8] dt-bindings: memory-controllers: Add STM32 Octo Memory Manager controller
2025-02-13 8:00 ` Krzysztof Kozlowski
@ 2025-02-18 9:58 ` Patrice CHOTARD
0 siblings, 0 replies; 23+ messages in thread
From: Patrice CHOTARD @ 2025-02-18 9:58 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: Mark Brown, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Alexandre Torgue, Philipp Zabel, Maxime Coquelin,
Greg Kroah-Hartman, Arnd Bergmann, Catalin Marinas, Will Deacon,
linux-spi, devicetree, linux-stm32, linux-arm-kernel,
linux-kernel, christophe.kerello
On 2/13/25 09:00, Krzysztof Kozlowski wrote:
> On Mon, Feb 10, 2025 at 02:18:21PM +0100, 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>
>> ---
>> .../memory-controllers/st,stm32mp25-omm.yaml | 201 ++++++++++++++++++
>> 1 file changed, 201 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/memory-controllers/st,stm32mp25-omm.yaml
>>
>> diff --git a/Documentation/devicetree/bindings/memory-controllers/st,stm32mp25-omm.yaml b/Documentation/devicetree/bindings/memory-controllers/st,stm32mp25-omm.yaml
>> new file mode 100644
>> index 000000000000..c897e6bf490d
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/memory-controllers/st,stm32mp25-omm.yaml
>> @@ -0,0 +1,201 @@
>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/memory-controllers/st,stm32mp25-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>
>> + minItems: 2
>> + maxItems: 2
>> +
>> + reg:
>> + items:
>> + - description: OMM registers
>> + - description: OMM memory map area
>> +
>> + reg-names:
>> + items:
>> + - const: regs
>> + - const: memory_map
>> +
>> + memory-region:
>> + description: |
>> + Memory region shared between the 2 OCTOSPI instance.
>> + One or two phandle to a node describing a memory mapped region
>> + depending of child number.
>> + minItems: 1
>> + maxItems: 2
>> +
>> + memory-region-names:
>> + description: |
>> + OCTOSPI instance's name to which memory region is associated
>> + items:
>> + enum: [ospi1, ospi2]
>> + minItems: 1
>> + maxItems: 2
>> +
>> + clocks:
>> + maxItems: 1
>> +
>> + resets:
>> + maxItems: 1
>> +
>> + access-controllers:
>> + maxItems: 1
>> +
>> + 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.
>> + 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:
>> + - description: phandle to syscfg
>> + - description: register offset within syscfg
>> + - description: register bitmask for memory split
>> +
>> + st,omm-req2ack-ns:
>> + description: |
>> + In multiplexed mode (MUXEN = 1), this field defines the time in
>> + nanoseconds between two transactions.
>
> default: ?
Yes, i will add the default value
>
>> +
>> + st,omm-cssel-ovr:
>> + $ref: /schemas/types.yaml#/definitions/uint32
>> + description: |
>> + Configure the chip select selector override for the 2 OCTOSPIs.
>> + - 0: OCTOSPI1 chip select send to NCS1 OCTOSPI2 chip select send to NCS1
>> + - 1: OCTOSPI1 chip select send to NCS2 OCTOSPI2 chip select send to NCS1
>> + - 2: OCTOSPI1 chip select send to NCS1 OCTOSPI2 chip select send to NCS2
>> + - 3: OCTOSPI1 chip select send to NCS2 OCTOSPI2 chip select send to NCS2
>> + minimum: 0
>> + maximum: 3
>
> default: ?
ditto
>
>> +
>> + st,omm-mux:
>> + $ref: /schemas/types.yaml#/definitions/uint32
>> + description: |
>> + Configure the muxing between the 2 OCTOSPIs busses and the 2 output ports.
>> + - 0: direct mode, default
>
> Don't repeat constraints in free form text.
>
ok
>> + - 1: mux OCTOSPI1 and OCTOSPI2 to port 1
>> + - 2: swapped mode
>> + - 3: mux OCTOSPI1 and OCTOSPI2 to port 2
>> + minimum: 0
>> + maximum: 3
>
> default: ?
i will add the default value as well
Thanks
Patrice
>
> None of them are required, so usually we expect defaults.
>
> Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
>
> Best regards,
> Krzysztof
>
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v3 4/8] memory: Add STM32 Octo Memory Manager driver
2025-02-10 13:18 [PATCH v3 0/8] Add STM32MP25 SPI NOR support patrice.chotard
` (2 preceding siblings ...)
2025-02-10 13:18 ` [PATCH v3 3/8] dt-bindings: memory-controllers: Add STM32 Octo Memory Manager controller patrice.chotard
@ 2025-02-10 13:18 ` patrice.chotard
2025-02-11 17:04 ` kernel test robot
` (4 more replies)
2025-02-10 13:18 ` [PATCH v3 5/8] arm64: dts: st: Add OMM node on stm32mp251 patrice.chotard
` (4 subsequent siblings)
8 siblings, 5 replies; 23+ messages in thread
From: patrice.chotard @ 2025-02-10 13:18 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/memory/Kconfig | 17 ++
drivers/memory/Makefile | 1 +
drivers/memory/stm32_omm.c | 520 +++++++++++++++++++++++++++++++++++++
3 files changed, 538 insertions(+)
create mode 100644 drivers/memory/stm32_omm.c
diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig
index c82d8d8a16ea..80c44a6d5bdb 100644
--- a/drivers/memory/Kconfig
+++ b/drivers/memory/Kconfig
@@ -225,6 +225,23 @@ config STM32_FMC2_EBI
devices (like SRAM, ethernet adapters, FPGAs, LCD displays, ...) on
SOCs containing the FMC2 External Bus Interface.
+config STM32_OMM
+ tristate "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.
+
source "drivers/memory/samsung/Kconfig"
source "drivers/memory/tegra/Kconfig"
diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile
index d2e6ca9abbe0..c1959661bf63 100644
--- a/drivers/memory/Makefile
+++ b/drivers/memory/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_DA8XX_DDRCTL) += da8xx-ddrctl.o
obj-$(CONFIG_PL353_SMC) += pl353-smc.o
obj-$(CONFIG_RENESAS_RPCIF) += renesas-rpc-if.o
obj-$(CONFIG_STM32_FMC2_EBI) += stm32-fmc2-ebi.o
+obj-$(CONFIG_STM32_OMM) += stm32_omm.o
obj-$(CONFIG_SAMSUNG_MC) += samsung/
obj-$(CONFIG_TEGRA_MC) += tegra/
diff --git a/drivers/memory/stm32_omm.c b/drivers/memory/stm32_omm.c
new file mode 100644
index 000000000000..af69137bfba2
--- /dev/null
+++ b/drivers/memory/stm32_omm.c
@@ -0,0 +1,520 @@
+// 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 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[] = { "ospi1", "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);
+ if (!node)
+ continue;
+
+ 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_exclusive(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);
+ struct reset_control *reset;
+ int ret;
+ u8 i;
+
+ 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 = of_reset_control_get_exclusive(omm->child[i].node, 0);
+ if (IS_ERR(reset)) {
+ dev_err(dev, "Can't get child reset\n");
+ return PTR_ERR(reset);
+ };
+
+ /* reset OSPI to ensure CR_EN bit is set to 0 */
+ reset_control_assert(reset);
+ udelay(2);
+ reset_control_deassert(reset);
+
+ reset_control_put(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;
+ struct clk *clk;
+ 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, "regs");
+ if (IS_ERR(omm->io_base))
+ return PTR_ERR(omm->io_base);
+
+ omm->mm_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "memory_map");
+ if (IS_ERR(omm->mm_res))
+ return PTR_ERR(omm->mm_res);
+
+ /* 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;
+
+ clk = of_clk_get(child, 0);
+ if (IS_ERR(clk)) {
+ dev_err(dev, "Can't get child clock\n");
+ ret = PTR_ERR(clk);
+ goto err_clk_release;
+ };
+
+ omm->child[omm->nb_child].clk = clk;
+ 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;
+ }
+
+err_clk_release:
+ for (i = 0; i < omm->nb_child; i++)
+ clk_put(omm->child[i].clk);
+
+ 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);
+
+ 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] 23+ messages in thread* Re: [PATCH v3 4/8] memory: Add STM32 Octo Memory Manager driver
2025-02-10 13:18 ` [PATCH v3 4/8] memory: Add STM32 Octo Memory Manager driver patrice.chotard
@ 2025-02-11 17:04 ` kernel test robot
2025-02-11 17:56 ` kernel test robot
` (3 subsequent siblings)
4 siblings, 0 replies; 23+ messages in thread
From: kernel test robot @ 2025-02-11 17:04 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: oe-kbuild-all, linux-spi, devicetree, linux-stm32,
linux-arm-kernel, linux-kernel, christophe.kerello,
patrice.chotard
Hi,
kernel test robot noticed the following build warnings:
[auto build test WARNING on broonie-spi/for-next]
[also build test WARNING on atorgue-stm32/stm32-next krzk-mem-ctrl/for-next linus/master v6.14-rc2 next-20250210]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/patrice-chotard-foss-st-com/dt-bindings-spi-Add-STM32-OSPI-controller/20250210-212554
base: https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git for-next
patch link: https://lore.kernel.org/r/20250210131826.220318-5-patrice.chotard%40foss.st.com
patch subject: [PATCH v3 4/8] memory: Add STM32 Octo Memory Manager driver
config: m68k-allyesconfig (https://download.01.org/0day-ci/archive/20250212/202502120005.pznVoB0E-lkp@intel.com/config)
compiler: m68k-linux-gcc (GCC) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250212/202502120005.pznVoB0E-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202502120005.pznVoB0E-lkp@intel.com/
All warnings (new ones prefixed by >>):
In file included from include/linux/device.h:15,
from include/linux/platform_device.h:13,
from include/linux/bus/stm32_firewall_device.h:10,
from drivers/memory/stm32_omm.c:7:
drivers/memory/stm32_omm.c: In function 'stm32_omm_set_amcr':
>> drivers/memory/stm32_omm.c:82:38: warning: format '%llx' expects argument of type 'long long unsigned int', but argument 3 has type 'resource_size_t' {aka 'unsigned int'} [-Wformat=]
82 | dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n",
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/dev_printk.h:110:30: note: in definition of macro 'dev_printk_index_wrap'
110 | _p_func(dev, fmt, ##__VA_ARGS__); \
| ^~~
include/linux/dev_printk.h:154:56: note: in expansion of macro 'dev_fmt'
154 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__)
| ^~~~~~~
drivers/memory/stm32_omm.c:82:25: note: in expansion of macro 'dev_err'
82 | dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n",
| ^~~~~~~
drivers/memory/stm32_omm.c:82:45: note: format string is defined here
82 | dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n",
| ~~~^
| |
| long long unsigned int
| %x
drivers/memory/stm32_omm.c:82:38: warning: format '%llx' expects argument of type 'long long unsigned int', but argument 4 has type 'resource_size_t' {aka 'unsigned int'} [-Wformat=]
82 | dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n",
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/dev_printk.h:110:30: note: in definition of macro 'dev_printk_index_wrap'
110 | _p_func(dev, fmt, ##__VA_ARGS__); \
| ^~~
include/linux/dev_printk.h:154:56: note: in expansion of macro 'dev_fmt'
154 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__)
| ^~~~~~~
drivers/memory/stm32_omm.c:82:25: note: in expansion of macro 'dev_err'
82 | dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n",
| ^~~~~~~
drivers/memory/stm32_omm.c:82:52: note: format string is defined here
82 | dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n",
| ~~~^
| |
| long long unsigned int
| %x
drivers/memory/stm32_omm.c:82:38: warning: format '%llx' expects argument of type 'long long unsigned int', but argument 5 has type 'resource_size_t' {aka 'unsigned int'} [-Wformat=]
82 | dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n",
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/dev_printk.h:110:30: note: in definition of macro 'dev_printk_index_wrap'
110 | _p_func(dev, fmt, ##__VA_ARGS__); \
| ^~~
include/linux/dev_printk.h:154:56: note: in expansion of macro 'dev_fmt'
154 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__)
| ^~~~~~~
drivers/memory/stm32_omm.c:82:25: note: in expansion of macro 'dev_err'
82 | dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n",
| ^~~~~~~
drivers/memory/stm32_omm.c:82:80: note: format string is defined here
82 | dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n",
| ~~~^
| |
| long long unsigned int
| %x
drivers/memory/stm32_omm.c:82:38: warning: format '%llx' expects argument of type 'long long unsigned int', but argument 6 has type 'resource_size_t' {aka 'unsigned int'} [-Wformat=]
82 | dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n",
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/dev_printk.h:110:30: note: in definition of macro 'dev_printk_index_wrap'
110 | _p_func(dev, fmt, ##__VA_ARGS__); \
| ^~~
include/linux/dev_printk.h:154:56: note: in expansion of macro 'dev_fmt'
154 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__)
| ^~~~~~~
drivers/memory/stm32_omm.c:82:25: note: in expansion of macro 'dev_err'
82 | dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n",
| ^~~~~~~
drivers/memory/stm32_omm.c:82:87: note: format string is defined here
82 | dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n",
| ~~~^
| |
| long long unsigned int
| %x
drivers/memory/stm32_omm.c:96:46: warning: format '%llx' expects argument of type 'long long unsigned int', but argument 3 has type 'resource_size_t' {aka 'unsigned int'} [-Wformat=]
96 | dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n",
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/dev_printk.h:110:30: note: in definition of macro 'dev_printk_index_wrap'
110 | _p_func(dev, fmt, ##__VA_ARGS__); \
| ^~~
include/linux/dev_printk.h:154:56: note: in expansion of macro 'dev_fmt'
154 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__)
| ^~~~~~~
drivers/memory/stm32_omm.c:96:33: note: in expansion of macro 'dev_err'
96 | dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n",
| ^~~~~~~
drivers/memory/stm32_omm.c:96:53: note: format string is defined here
96 | dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n",
| ~~~^
| |
| long long unsigned int
| %x
drivers/memory/stm32_omm.c:96:46: warning: format '%llx' expects argument of type 'long long unsigned int', but argument 4 has type 'resource_size_t' {aka 'unsigned int'} [-Wformat=]
96 | dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n",
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/dev_printk.h:110:30: note: in definition of macro 'dev_printk_index_wrap'
110 | _p_func(dev, fmt, ##__VA_ARGS__); \
| ^~~
include/linux/dev_printk.h:154:56: note: in expansion of macro 'dev_fmt'
154 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__)
| ^~~~~~~
drivers/memory/stm32_omm.c:96:33: note: in expansion of macro 'dev_err'
96 | dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n",
vim +82 drivers/memory/stm32_omm.c
44
45 static int stm32_omm_set_amcr(struct device *dev, bool set)
46 {
47 struct stm32_omm *omm = dev_get_drvdata(dev);
48 struct regmap *syscfg_regmap;
49 struct device_node *node;
50 struct resource res, res1;
51 resource_size_t mm_ospi2_size = 0;
52 static const char * const mm_name[] = { "ospi1", "ospi2" };
53 u32 amcr_base, amcr_mask;
54 int ret, i, idx;
55 unsigned int amcr, read_amcr;
56
57 for (i = 0; i < omm->nb_child; i++) {
58 idx = of_property_match_string(dev->of_node,
59 "memory-region-names",
60 mm_name[i]);
61 if (idx < 0)
62 continue;
63
64 /* res1 only used on second loop iteration */
65 res1.start = res.start;
66 res1.end = res.end;
67
68 node = of_parse_phandle(dev->of_node, "memory-region", idx);
69 if (!node)
70 continue;
71
72 ret = of_address_to_resource(node, 0, &res);
73 if (ret) {
74 dev_err(dev, "unable to resolve memory region\n");
75 return ret;
76 }
77
78 /* check that memory region fits inside OMM memory map area */
79 if (!resource_contains(omm->mm_res, &res)) {
80 dev_err(dev, "%s doesn't fit inside OMM memory map area\n",
81 mm_name[i]);
> 82 dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n",
83 res.start, res.end,
84 omm->mm_res->start, omm->mm_res->end);
85
86 return -EFAULT;
87 }
88
89 if (i == 1) {
90 mm_ospi2_size = resource_size(&res);
91
92 /* check that OMM memory region 1 doesn't overlap memory region 2 */
93 if (resource_overlaps(&res, &res1)) {
94 dev_err(dev, "OMM memory-region %s overlaps memory region %s\n",
95 mm_name[0], mm_name[1]);
96 dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n",
97 res1.start, res1.end, res.start, res.end);
98
99 return -EFAULT;
100 }
101 }
102 }
103
104 syscfg_regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "st,syscfg-amcr");
105 if (IS_ERR(syscfg_regmap)) {
106 dev_err(dev, "Failed to get st,syscfg-amcr property\n");
107 return PTR_ERR(syscfg_regmap);
108 }
109
110 ret = of_property_read_u32_index(dev->of_node, "st,syscfg-amcr", 1,
111 &amcr_base);
112 if (ret)
113 return ret;
114
115 ret = of_property_read_u32_index(dev->of_node, "st,syscfg-amcr", 2,
116 &amcr_mask);
117 if (ret)
118 return ret;
119
120 amcr = mm_ospi2_size / SZ_64M;
121
122 if (set)
123 regmap_update_bits(syscfg_regmap, amcr_base, amcr_mask, amcr);
124
125 /* read AMCR and check coherency with memory-map areas defined in DT */
126 regmap_read(syscfg_regmap, amcr_base, &read_amcr);
127 read_amcr = read_amcr >> (ffs(amcr_mask) - 1);
128
129 if (amcr != read_amcr) {
130 dev_err(dev, "AMCR value not coherent with DT memory-map areas\n");
131 ret = -EINVAL;
132 }
133
134 return ret;
135 }
136
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 23+ messages in thread* Re: [PATCH v3 4/8] memory: Add STM32 Octo Memory Manager driver
2025-02-10 13:18 ` [PATCH v3 4/8] memory: Add STM32 Octo Memory Manager driver patrice.chotard
2025-02-11 17:04 ` kernel test robot
@ 2025-02-11 17:56 ` kernel test robot
2025-02-11 18:16 ` Christophe JAILLET
` (2 subsequent siblings)
4 siblings, 0 replies; 23+ messages in thread
From: kernel test robot @ 2025-02-11 17:56 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: llvm, oe-kbuild-all, linux-spi, devicetree, linux-stm32,
linux-arm-kernel, linux-kernel, christophe.kerello,
patrice.chotard
Hi,
kernel test robot noticed the following build errors:
[auto build test ERROR on broonie-spi/for-next]
[also build test ERROR on atorgue-stm32/stm32-next krzk-mem-ctrl/for-next linus/master v6.14-rc2 next-20250210]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/patrice-chotard-foss-st-com/dt-bindings-spi-Add-STM32-OSPI-controller/20250210-212554
base: https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git for-next
patch link: https://lore.kernel.org/r/20250210131826.220318-5-patrice.chotard%40foss.st.com
patch subject: [PATCH v3 4/8] memory: Add STM32 Octo Memory Manager driver
config: hexagon-allmodconfig (https://download.01.org/0day-ci/archive/20250212/202502120118.27fjrRqt-lkp@intel.com/config)
compiler: clang version 21.0.0git (https://github.com/llvm/llvm-project 6807164500e9920638e2ab0cdb4bf8321d24f8eb)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250212/202502120118.27fjrRqt-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202502120118.27fjrRqt-lkp@intel.com/
All error/warnings (new ones prefixed by >>):
>> drivers/memory/stm32_omm.c:83:5: warning: format specifies type 'unsigned long long' but the argument has type 'resource_size_t' (aka 'unsigned int') [-Wformat]
82 | dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n",
| ~~~~
| %x
83 | res.start, res.end,
| ^~~~~~~~~
include/linux/dev_printk.h:154:65: note: expanded from macro 'dev_err'
154 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__)
| ~~~ ^~~~~~~~~~~
include/linux/dev_printk.h:110:23: note: expanded from macro 'dev_printk_index_wrap'
110 | _p_func(dev, fmt, ##__VA_ARGS__); \
| ~~~ ^~~~~~~~~~~
drivers/memory/stm32_omm.c:83:16: warning: format specifies type 'unsigned long long' but the argument has type 'resource_size_t' (aka 'unsigned int') [-Wformat]
82 | dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n",
| ~~~~
| %x
83 | res.start, res.end,
| ^~~~~~~
include/linux/dev_printk.h:154:65: note: expanded from macro 'dev_err'
154 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__)
| ~~~ ^~~~~~~~~~~
include/linux/dev_printk.h:110:23: note: expanded from macro 'dev_printk_index_wrap'
110 | _p_func(dev, fmt, ##__VA_ARGS__); \
| ~~~ ^~~~~~~~~~~
drivers/memory/stm32_omm.c:84:5: warning: format specifies type 'unsigned long long' but the argument has type 'resource_size_t' (aka 'unsigned int') [-Wformat]
82 | dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n",
| ~~~~
| %x
83 | res.start, res.end,
84 | omm->mm_res->start, omm->mm_res->end);
| ^~~~~~~~~~~~~~~~~~
include/linux/dev_printk.h:154:65: note: expanded from macro 'dev_err'
154 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__)
| ~~~ ^~~~~~~~~~~
include/linux/dev_printk.h:110:23: note: expanded from macro 'dev_printk_index_wrap'
110 | _p_func(dev, fmt, ##__VA_ARGS__); \
| ~~~ ^~~~~~~~~~~
drivers/memory/stm32_omm.c:84:25: warning: format specifies type 'unsigned long long' but the argument has type 'resource_size_t' (aka 'unsigned int') [-Wformat]
82 | dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n",
| ~~~~
| %x
83 | res.start, res.end,
84 | omm->mm_res->start, omm->mm_res->end);
| ^~~~~~~~~~~~~~~~
include/linux/dev_printk.h:154:65: note: expanded from macro 'dev_err'
154 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__)
| ~~~ ^~~~~~~~~~~
include/linux/dev_printk.h:110:23: note: expanded from macro 'dev_printk_index_wrap'
110 | _p_func(dev, fmt, ##__VA_ARGS__); \
| ~~~ ^~~~~~~~~~~
drivers/memory/stm32_omm.c:97:6: warning: format specifies type 'unsigned long long' but the argument has type 'resource_size_t' (aka 'unsigned int') [-Wformat]
96 | dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n",
| ~~~~
| %x
97 | res1.start, res1.end, res.start, res.end);
| ^~~~~~~~~~
include/linux/dev_printk.h:154:65: note: expanded from macro 'dev_err'
154 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__)
| ~~~ ^~~~~~~~~~~
include/linux/dev_printk.h:110:23: note: expanded from macro 'dev_printk_index_wrap'
110 | _p_func(dev, fmt, ##__VA_ARGS__); \
| ~~~ ^~~~~~~~~~~
drivers/memory/stm32_omm.c:97:18: warning: format specifies type 'unsigned long long' but the argument has type 'resource_size_t' (aka 'unsigned int') [-Wformat]
96 | dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n",
| ~~~~
| %x
97 | res1.start, res1.end, res.start, res.end);
| ^~~~~~~~
include/linux/dev_printk.h:154:65: note: expanded from macro 'dev_err'
154 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__)
| ~~~ ^~~~~~~~~~~
include/linux/dev_printk.h:110:23: note: expanded from macro 'dev_printk_index_wrap'
110 | _p_func(dev, fmt, ##__VA_ARGS__); \
| ~~~ ^~~~~~~~~~~
drivers/memory/stm32_omm.c:97:28: warning: format specifies type 'unsigned long long' but the argument has type 'resource_size_t' (aka 'unsigned int') [-Wformat]
96 | dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n",
| ~~~~
| %x
97 | res1.start, res1.end, res.start, res.end);
| ^~~~~~~~~
include/linux/dev_printk.h:154:65: note: expanded from macro 'dev_err'
154 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__)
| ~~~ ^~~~~~~~~~~
include/linux/dev_printk.h:110:23: note: expanded from macro 'dev_printk_index_wrap'
110 | _p_func(dev, fmt, ##__VA_ARGS__); \
| ~~~ ^~~~~~~~~~~
drivers/memory/stm32_omm.c:97:39: warning: format specifies type 'unsigned long long' but the argument has type 'resource_size_t' (aka 'unsigned int') [-Wformat]
96 | dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n",
| ~~~~
| %x
97 | res1.start, res1.end, res.start, res.end);
| ^~~~~~~
include/linux/dev_printk.h:154:65: note: expanded from macro 'dev_err'
154 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__)
| ~~~ ^~~~~~~~~~~
include/linux/dev_printk.h:110:23: note: expanded from macro 'dev_printk_index_wrap'
110 | _p_func(dev, fmt, ##__VA_ARGS__); \
| ~~~ ^~~~~~~~~~~
>> drivers/memory/stm32_omm.c:224:14: error: call to undeclared function 'FIELD_PREP'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
224 | req2ack = FIELD_PREP(CR_REQ2ACK_MASK, req2ack);
| ^
drivers/memory/stm32_omm.c:239:14: error: call to undeclared function 'FIELD_PREP'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
239 | omm->cr |= FIELD_PREP(CR_MUXENMODE_MASK, mux);
| ^
drivers/memory/stm32_omm.c:246:14: error: call to undeclared function 'FIELD_PREP'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
246 | omm->cr |= FIELD_PREP(CR_CSSEL_OVR_MASK, cssel_ovr);
| ^
8 warnings and 3 errors generated.
vim +/FIELD_PREP +224 drivers/memory/stm32_omm.c
44
45 static int stm32_omm_set_amcr(struct device *dev, bool set)
46 {
47 struct stm32_omm *omm = dev_get_drvdata(dev);
48 struct regmap *syscfg_regmap;
49 struct device_node *node;
50 struct resource res, res1;
51 resource_size_t mm_ospi2_size = 0;
52 static const char * const mm_name[] = { "ospi1", "ospi2" };
53 u32 amcr_base, amcr_mask;
54 int ret, i, idx;
55 unsigned int amcr, read_amcr;
56
57 for (i = 0; i < omm->nb_child; i++) {
58 idx = of_property_match_string(dev->of_node,
59 "memory-region-names",
60 mm_name[i]);
61 if (idx < 0)
62 continue;
63
64 /* res1 only used on second loop iteration */
65 res1.start = res.start;
66 res1.end = res.end;
67
68 node = of_parse_phandle(dev->of_node, "memory-region", idx);
69 if (!node)
70 continue;
71
72 ret = of_address_to_resource(node, 0, &res);
73 if (ret) {
74 dev_err(dev, "unable to resolve memory region\n");
75 return ret;
76 }
77
78 /* check that memory region fits inside OMM memory map area */
79 if (!resource_contains(omm->mm_res, &res)) {
80 dev_err(dev, "%s doesn't fit inside OMM memory map area\n",
81 mm_name[i]);
82 dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n",
> 83 res.start, res.end,
84 omm->mm_res->start, omm->mm_res->end);
85
86 return -EFAULT;
87 }
88
89 if (i == 1) {
90 mm_ospi2_size = resource_size(&res);
91
92 /* check that OMM memory region 1 doesn't overlap memory region 2 */
93 if (resource_overlaps(&res, &res1)) {
94 dev_err(dev, "OMM memory-region %s overlaps memory region %s\n",
95 mm_name[0], mm_name[1]);
96 dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n",
97 res1.start, res1.end, res.start, res.end);
98
99 return -EFAULT;
100 }
101 }
102 }
103
104 syscfg_regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "st,syscfg-amcr");
105 if (IS_ERR(syscfg_regmap)) {
106 dev_err(dev, "Failed to get st,syscfg-amcr property\n");
107 return PTR_ERR(syscfg_regmap);
108 }
109
110 ret = of_property_read_u32_index(dev->of_node, "st,syscfg-amcr", 1,
111 &amcr_base);
112 if (ret)
113 return ret;
114
115 ret = of_property_read_u32_index(dev->of_node, "st,syscfg-amcr", 2,
116 &amcr_mask);
117 if (ret)
118 return ret;
119
120 amcr = mm_ospi2_size / SZ_64M;
121
122 if (set)
123 regmap_update_bits(syscfg_regmap, amcr_base, amcr_mask, amcr);
124
125 /* read AMCR and check coherency with memory-map areas defined in DT */
126 regmap_read(syscfg_regmap, amcr_base, &read_amcr);
127 read_amcr = read_amcr >> (ffs(amcr_mask) - 1);
128
129 if (amcr != read_amcr) {
130 dev_err(dev, "AMCR value not coherent with DT memory-map areas\n");
131 ret = -EINVAL;
132 }
133
134 return ret;
135 }
136
137 static int stm32_omm_enable_child_clock(struct device *dev, bool enable)
138 {
139 /* As there is only 2 children, remember first child in case of error */
140 struct clk *first_child_clk = NULL;
141 struct stm32_omm *omm = dev_get_drvdata(dev);
142 u8 i;
143 int ret;
144
145 for (i = 0; i < omm->nb_child; i++) {
146 if (enable) {
147 ret = clk_prepare_enable(omm->child[i].clk);
148 if (ret) {
149 if (first_child_clk)
150 clk_disable_unprepare(first_child_clk);
151
152 dev_err(dev, "Can not enable clock\n");
153 return ret;
154 }
155 } else {
156 clk_disable_unprepare(omm->child[i].clk);
157 }
158
159 first_child_clk = omm->child[i].clk;
160 }
161
162 return 0;
163 }
164
165 static int stm32_omm_configure(struct device *dev)
166 {
167 struct stm32_omm *omm = dev_get_drvdata(dev);
168 struct reset_control *rstc;
169 unsigned long clk_rate, clk_rate_max = 0;
170 int ret;
171 u8 i;
172 u32 mux = 0;
173 u32 cssel_ovr = 0;
174 u32 req2ack = 0;
175
176 omm->clk = devm_clk_get(dev, NULL);
177 if (IS_ERR(omm->clk)) {
178 dev_err(dev, "Failed to get OMM clock (%ld)\n",
179 PTR_ERR(omm->clk));
180
181 return PTR_ERR(omm->clk);
182 }
183
184 ret = pm_runtime_resume_and_get(dev);
185 if (ret < 0)
186 return ret;
187
188 /* parse children's clock */
189 for (i = 0; i < omm->nb_child; i++) {
190 clk_rate = clk_get_rate(omm->child[i].clk);
191 if (!clk_rate) {
192 dev_err(dev, "Invalid clock rate\n");
193 goto err_clk_disable;
194 }
195
196 if (clk_rate > clk_rate_max)
197 clk_rate_max = clk_rate;
198 }
199
200 rstc = devm_reset_control_get_optional_exclusive(dev, NULL);
201 if (IS_ERR(rstc)) {
202 ret = dev_err_probe(dev, PTR_ERR(rstc), "reset get failed\n");
203 goto err_clk_disable;
204 }
205
206 reset_control_assert(rstc);
207 udelay(2);
208 reset_control_deassert(rstc);
209
210 omm->cr = readl_relaxed(omm->io_base + OMM_CR);
211 /* optional */
212 ret = of_property_read_u32(dev->of_node, "st,omm-mux", &mux);
213 if (!ret) {
214 if (mux & CR_MUXEN) {
215 ret = of_property_read_u32(dev->of_node, "st,omm-req2ack-ns",
216 &req2ack);
217 if (!ret && !req2ack) {
218 req2ack = DIV_ROUND_UP(req2ack, NSEC_PER_SEC / clk_rate_max) - 1;
219
220 if (req2ack > 256)
221 req2ack = 256;
222 }
223
> 224 req2ack = FIELD_PREP(CR_REQ2ACK_MASK, req2ack);
225
226 omm->cr &= ~CR_REQ2ACK_MASK;
227 omm->cr |= FIELD_PREP(CR_REQ2ACK_MASK, req2ack);
228
229 /*
230 * If the mux is enabled, the 2 OSPI clocks have to be
231 * always enabled
232 */
233 ret = stm32_omm_enable_child_clock(dev, true);
234 if (ret)
235 goto err_clk_disable;
236 }
237
238 omm->cr &= ~CR_MUXENMODE_MASK;
239 omm->cr |= FIELD_PREP(CR_MUXENMODE_MASK, mux);
240 }
241
242 /* optional */
243 ret = of_property_read_u32(dev->of_node, "st,omm-cssel-ovr", &cssel_ovr);
244 if (!ret) {
245 omm->cr &= ~CR_CSSEL_OVR_MASK;
246 omm->cr |= FIELD_PREP(CR_CSSEL_OVR_MASK, cssel_ovr);
247 omm->cr |= CR_CSSEL_OVR_EN;
248 }
249
250 omm->restore_omm = true;
251 writel_relaxed(omm->cr, omm->io_base + OMM_CR);
252
253 ret = stm32_omm_set_amcr(dev, true);
254
255 err_clk_disable:
256 pm_runtime_put_sync_suspend(dev);
257
258 return ret;
259 }
260
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 23+ messages in thread* Re: [PATCH v3 4/8] memory: Add STM32 Octo Memory Manager driver
2025-02-10 13:18 ` [PATCH v3 4/8] memory: Add STM32 Octo Memory Manager driver patrice.chotard
2025-02-11 17:04 ` kernel test robot
2025-02-11 17:56 ` kernel test robot
@ 2025-02-11 18:16 ` Christophe JAILLET
2025-02-12 16:17 ` Patrice CHOTARD
2025-02-12 3:19 ` kernel test robot
2025-02-12 12:51 ` Geert Uytterhoeven
4 siblings, 1 reply; 23+ messages in thread
From: Christophe JAILLET @ 2025-02-11 18:16 UTC (permalink / raw)
To: patrice.chotard
Cc: alexandre.torgue, arnd, broonie, catalin.marinas,
christophe.kerello, conor+dt, devicetree, gregkh, krzk+dt,
linux-arm-kernel, linux-kernel, linux-spi, linux-stm32,
mcoquelin.stm32, p.zabel, robh, will
Le 10/02/2025 à 14:18,
patrice.chotard-rj0Iel/JR4NBDgjK7y7TUQ@public.gmane.org a écrit :
> From: Patrice Chotard <patrice.chotard-rj0Iel/JR4NBDgjK7y7TUQ@public.gmane.org>
>
> 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.
...
> diff --git a/drivers/memory/stm32_omm.c b/drivers/memory/stm32_omm.c
> new file mode 100644
> index 000000000000..af69137bfba2
> --- /dev/null
> +++ b/drivers/memory/stm32_omm.c
> @@ -0,0 +1,520 @@
> +// SPDX-License-Identifier: GPL
Not sure this SPDX-License-Identifier exists.
> +/*
> + * Copyright (C) STMicroelectronics 2024 - All Rights Reserved
...
> + 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;
Should we call, here and below, pm_runtime_disable() in the error
handling path, as done in the remove function?
> +
> + 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;
> + }
> +
> +err_clk_release:
> + for (i = 0; i < omm->nb_child; i++)
> + clk_put(omm->child[i].clk);
> +
> + 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);
> +
> + pm_runtime_disable(&pdev->dev);
Should we have:
for (i = 0; i < omm->nb_child; i++)
clk_put(omm->child[i].clk);
as done in the error handling path of the probe?
> +}
> +
> +static const struct of_device_id stm32_omm_of_match[] = {
> + { .compatible = "st,stm32mp25-omm", },
> + {},
Nitpick: Unneeded , after a terminator.
> +};
> +MODULE_DEVICE_TABLE(of, stm32_omm_of_match);
...
CJ
^ permalink raw reply [flat|nested] 23+ messages in thread* Re: [PATCH v3 4/8] memory: Add STM32 Octo Memory Manager driver
2025-02-11 18:16 ` Christophe JAILLET
@ 2025-02-12 16:17 ` Patrice CHOTARD
0 siblings, 0 replies; 23+ messages in thread
From: Patrice CHOTARD @ 2025-02-12 16:17 UTC (permalink / raw)
To: Christophe JAILLET
Cc: alexandre.torgue, arnd, broonie, catalin.marinas,
christophe.kerello, conor+dt, devicetree, gregkh, krzk+dt,
linux-arm-kernel, linux-kernel, linux-spi, linux-stm32,
mcoquelin.stm32, p.zabel, robh, will
On 2/11/25 19:16, Christophe JAILLET wrote:
> Le 10/02/2025 à 14:18, patrice.chotard-rj0Iel/JR4NBDgjK7y7TUQ@public.gmane.org a écrit :
>> From: Patrice Chotard <patrice.chotard-rj0Iel/JR4NBDgjK7y7TUQ@public.gmane.org>
>>
>> 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.
>
> ...
>
>> diff --git a/drivers/memory/stm32_omm.c b/drivers/memory/stm32_omm.c
>> new file mode 100644
>> index 000000000000..af69137bfba2
>> --- /dev/null
>> +++ b/drivers/memory/stm32_omm.c
>> @@ -0,0 +1,520 @@
>> +// SPDX-License-Identifier: GPL
>
> Not sure this SPDX-License-Identifier exists.
Right, i will fix that.
>
>> +/*
>> + * Copyright (C) STMicroelectronics 2024 - All Rights Reserved
>
> ...
>
>> + 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;
>
> Should we call, here and below, pm_runtime_disable() in the error handling path, as done in the remove function?
right, i will add it.
>
>> +
>> + 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;
>> + }
>> +
>> +err_clk_release:
>> + for (i = 0; i < omm->nb_child; i++)
>> + clk_put(omm->child[i].clk);
>> +
>> + 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);
>> +
>> + pm_runtime_disable(&pdev->dev);
>
> Should we have:
> for (i = 0; i < omm->nb_child; i++)
> clk_put(omm->child[i].clk);
> as done in the error handling path of the probe?
no need, as child's clock are always freed in stm32_omm_probe() in all cases.
>
>> +}
>> +
>> +static const struct of_device_id stm32_omm_of_match[] = {
>> + { .compatible = "st,stm32mp25-omm", },
>> + {},
>
> Nitpick: Unneeded , after a terminator.
ok
>
>> +};
>> +MODULE_DEVICE_TABLE(of, stm32_omm_of_match);
>
> ...
>
> CJ
>
>
>
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v3 4/8] memory: Add STM32 Octo Memory Manager driver
2025-02-10 13:18 ` [PATCH v3 4/8] memory: Add STM32 Octo Memory Manager driver patrice.chotard
` (2 preceding siblings ...)
2025-02-11 18:16 ` Christophe JAILLET
@ 2025-02-12 3:19 ` kernel test robot
2025-02-12 12:51 ` Geert Uytterhoeven
4 siblings, 0 replies; 23+ messages in thread
From: kernel test robot @ 2025-02-12 3:19 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: oe-kbuild-all, linux-spi, devicetree, linux-stm32,
linux-arm-kernel, linux-kernel, christophe.kerello,
patrice.chotard
Hi,
kernel test robot noticed the following build errors:
[auto build test ERROR on broonie-spi/for-next]
[also build test ERROR on atorgue-stm32/stm32-next krzk-mem-ctrl/for-next linus/master v6.14-rc2 next-20250210]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/patrice-chotard-foss-st-com/dt-bindings-spi-Add-STM32-OSPI-controller/20250210-212554
base: https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git for-next
patch link: https://lore.kernel.org/r/20250210131826.220318-5-patrice.chotard%40foss.st.com
patch subject: [PATCH v3 4/8] memory: Add STM32 Octo Memory Manager driver
config: m68k-allyesconfig (https://download.01.org/0day-ci/archive/20250212/202502121131.W1HsUg9j-lkp@intel.com/config)
compiler: m68k-linux-gcc (GCC) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250212/202502121131.W1HsUg9j-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202502121131.W1HsUg9j-lkp@intel.com/
All errors (new ones prefixed by >>):
drivers/memory/stm32_omm.c:82:25: note: in expansion of macro 'dev_err'
82 | dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n",
| ^~~~~~~
drivers/memory/stm32_omm.c:82:80: note: format string is defined here
82 | dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n",
| ~~~^
| |
| long long unsigned int
| %x
drivers/memory/stm32_omm.c:82:38: warning: format '%llx' expects argument of type 'long long unsigned int', but argument 6 has type 'resource_size_t' {aka 'unsigned int'} [-Wformat=]
82 | dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n",
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/dev_printk.h:110:30: note: in definition of macro 'dev_printk_index_wrap'
110 | _p_func(dev, fmt, ##__VA_ARGS__); \
| ^~~
include/linux/dev_printk.h:154:56: note: in expansion of macro 'dev_fmt'
154 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__)
| ^~~~~~~
drivers/memory/stm32_omm.c:82:25: note: in expansion of macro 'dev_err'
82 | dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n",
| ^~~~~~~
drivers/memory/stm32_omm.c:82:87: note: format string is defined here
82 | dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n",
| ~~~^
| |
| long long unsigned int
| %x
drivers/memory/stm32_omm.c:96:46: warning: format '%llx' expects argument of type 'long long unsigned int', but argument 3 has type 'resource_size_t' {aka 'unsigned int'} [-Wformat=]
96 | dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n",
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/dev_printk.h:110:30: note: in definition of macro 'dev_printk_index_wrap'
110 | _p_func(dev, fmt, ##__VA_ARGS__); \
| ^~~
include/linux/dev_printk.h:154:56: note: in expansion of macro 'dev_fmt'
154 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__)
| ^~~~~~~
drivers/memory/stm32_omm.c:96:33: note: in expansion of macro 'dev_err'
96 | dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n",
| ^~~~~~~
drivers/memory/stm32_omm.c:96:53: note: format string is defined here
96 | dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n",
| ~~~^
| |
| long long unsigned int
| %x
drivers/memory/stm32_omm.c:96:46: warning: format '%llx' expects argument of type 'long long unsigned int', but argument 4 has type 'resource_size_t' {aka 'unsigned int'} [-Wformat=]
96 | dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n",
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/dev_printk.h:110:30: note: in definition of macro 'dev_printk_index_wrap'
110 | _p_func(dev, fmt, ##__VA_ARGS__); \
| ^~~
include/linux/dev_printk.h:154:56: note: in expansion of macro 'dev_fmt'
154 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__)
| ^~~~~~~
drivers/memory/stm32_omm.c:96:33: note: in expansion of macro 'dev_err'
96 | dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n",
| ^~~~~~~
drivers/memory/stm32_omm.c:96:60: note: format string is defined here
96 | dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n",
| ~~~^
| |
| long long unsigned int
| %x
drivers/memory/stm32_omm.c:96:46: warning: format '%llx' expects argument of type 'long long unsigned int', but argument 5 has type 'resource_size_t' {aka 'unsigned int'} [-Wformat=]
96 | dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n",
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/dev_printk.h:110:30: note: in definition of macro 'dev_printk_index_wrap'
110 | _p_func(dev, fmt, ##__VA_ARGS__); \
| ^~~
include/linux/dev_printk.h:154:56: note: in expansion of macro 'dev_fmt'
154 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__)
| ^~~~~~~
drivers/memory/stm32_omm.c:96:33: note: in expansion of macro 'dev_err'
96 | dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n",
| ^~~~~~~
drivers/memory/stm32_omm.c:96:78: note: format string is defined here
96 | dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n",
| ~~~^
| |
| long long unsigned int
| %x
drivers/memory/stm32_omm.c:96:46: warning: format '%llx' expects argument of type 'long long unsigned int', but argument 6 has type 'resource_size_t' {aka 'unsigned int'} [-Wformat=]
96 | dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n",
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/dev_printk.h:110:30: note: in definition of macro 'dev_printk_index_wrap'
110 | _p_func(dev, fmt, ##__VA_ARGS__); \
| ^~~
include/linux/dev_printk.h:154:56: note: in expansion of macro 'dev_fmt'
154 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__)
| ^~~~~~~
drivers/memory/stm32_omm.c:96:33: note: in expansion of macro 'dev_err'
96 | dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n",
| ^~~~~~~
drivers/memory/stm32_omm.c:96:85: note: format string is defined here
96 | dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n",
| ~~~^
| |
| long long unsigned int
| %x
drivers/memory/stm32_omm.c: In function 'stm32_omm_configure':
>> drivers/memory/stm32_omm.c:224:35: error: implicit declaration of function 'FIELD_PREP' [-Wimplicit-function-declaration]
224 | req2ack = FIELD_PREP(CR_REQ2ACK_MASK, req2ack);
| ^~~~~~~~~~
vim +/FIELD_PREP +224 drivers/memory/stm32_omm.c
164
165 static int stm32_omm_configure(struct device *dev)
166 {
167 struct stm32_omm *omm = dev_get_drvdata(dev);
168 struct reset_control *rstc;
169 unsigned long clk_rate, clk_rate_max = 0;
170 int ret;
171 u8 i;
172 u32 mux = 0;
173 u32 cssel_ovr = 0;
174 u32 req2ack = 0;
175
176 omm->clk = devm_clk_get(dev, NULL);
177 if (IS_ERR(omm->clk)) {
178 dev_err(dev, "Failed to get OMM clock (%ld)\n",
179 PTR_ERR(omm->clk));
180
181 return PTR_ERR(omm->clk);
182 }
183
184 ret = pm_runtime_resume_and_get(dev);
185 if (ret < 0)
186 return ret;
187
188 /* parse children's clock */
189 for (i = 0; i < omm->nb_child; i++) {
190 clk_rate = clk_get_rate(omm->child[i].clk);
191 if (!clk_rate) {
192 dev_err(dev, "Invalid clock rate\n");
193 goto err_clk_disable;
194 }
195
196 if (clk_rate > clk_rate_max)
197 clk_rate_max = clk_rate;
198 }
199
200 rstc = devm_reset_control_get_optional_exclusive(dev, NULL);
201 if (IS_ERR(rstc)) {
202 ret = dev_err_probe(dev, PTR_ERR(rstc), "reset get failed\n");
203 goto err_clk_disable;
204 }
205
206 reset_control_assert(rstc);
207 udelay(2);
208 reset_control_deassert(rstc);
209
210 omm->cr = readl_relaxed(omm->io_base + OMM_CR);
211 /* optional */
212 ret = of_property_read_u32(dev->of_node, "st,omm-mux", &mux);
213 if (!ret) {
214 if (mux & CR_MUXEN) {
215 ret = of_property_read_u32(dev->of_node, "st,omm-req2ack-ns",
216 &req2ack);
217 if (!ret && !req2ack) {
218 req2ack = DIV_ROUND_UP(req2ack, NSEC_PER_SEC / clk_rate_max) - 1;
219
220 if (req2ack > 256)
221 req2ack = 256;
222 }
223
> 224 req2ack = FIELD_PREP(CR_REQ2ACK_MASK, req2ack);
225
226 omm->cr &= ~CR_REQ2ACK_MASK;
227 omm->cr |= FIELD_PREP(CR_REQ2ACK_MASK, req2ack);
228
229 /*
230 * If the mux is enabled, the 2 OSPI clocks have to be
231 * always enabled
232 */
233 ret = stm32_omm_enable_child_clock(dev, true);
234 if (ret)
235 goto err_clk_disable;
236 }
237
238 omm->cr &= ~CR_MUXENMODE_MASK;
239 omm->cr |= FIELD_PREP(CR_MUXENMODE_MASK, mux);
240 }
241
242 /* optional */
243 ret = of_property_read_u32(dev->of_node, "st,omm-cssel-ovr", &cssel_ovr);
244 if (!ret) {
245 omm->cr &= ~CR_CSSEL_OVR_MASK;
246 omm->cr |= FIELD_PREP(CR_CSSEL_OVR_MASK, cssel_ovr);
247 omm->cr |= CR_CSSEL_OVR_EN;
248 }
249
250 omm->restore_omm = true;
251 writel_relaxed(omm->cr, omm->io_base + OMM_CR);
252
253 ret = stm32_omm_set_amcr(dev, true);
254
255 err_clk_disable:
256 pm_runtime_put_sync_suspend(dev);
257
258 return ret;
259 }
260
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 23+ messages in thread* Re: [PATCH v3 4/8] memory: Add STM32 Octo Memory Manager driver
2025-02-10 13:18 ` [PATCH v3 4/8] memory: Add STM32 Octo Memory Manager driver patrice.chotard
` (3 preceding siblings ...)
2025-02-12 3:19 ` kernel test robot
@ 2025-02-12 12:51 ` Geert Uytterhoeven
2025-02-12 16:18 ` Patrice CHOTARD
4 siblings, 1 reply; 23+ messages in thread
From: Geert Uytterhoeven @ 2025-02-12 12:51 UTC (permalink / raw)
To: patrice.chotard
Cc: Mark Brown, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Alexandre Torgue, Philipp Zabel, Maxime Coquelin,
Greg Kroah-Hartman, Arnd Bergmann, Catalin Marinas, Will Deacon,
linux-spi, devicetree, linux-stm32, linux-arm-kernel,
linux-kernel, christophe.kerello
Hi Patrice,
On Mon, 10 Feb 2025 at 14:21, <patrice.chotard@foss.st.com> wrote:
> 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>
Thanks for your patch!
> --- a/drivers/memory/Kconfig
> +++ b/drivers/memory/Kconfig
> @@ -225,6 +225,23 @@ config STM32_FMC2_EBI
> devices (like SRAM, ethernet adapters, FPGAs, LCD displays, ...) on
> SOCs containing the FMC2 External Bus Interface.
>
> +config STM32_OMM
> + tristate "STM32 Octo Memory Manager"
> + depends on SPI_STM32_OSPI || TEST_COMPILE
COMPILE_TEST
> + 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.
> +
> source "drivers/memory/samsung/Kconfig"
> source "drivers/memory/tegra/Kconfig"
> --- /dev/null
> +++ b/drivers/memory/stm32_omm.c
> +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[] = { "ospi1", "ospi2" };
> + u32 amcr_base, amcr_mask;
> + int ret, i, idx;
unsigned int i
> + 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);
> + if (!node)
> + continue;
> +
> + 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);
As reported by the kernel test robot, this fails to build when
resource_size_t differs from unsigned long long. However, you can
easily print the full resource instead:
dev_err(dev, "%pR doesn't fit inside %pR\n", &res, omm->mm_res);
https://elixir.bootlin.com/linux/v6.13.2/source/Documentation/core-api/printk-formats.rst#L206
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply [flat|nested] 23+ messages in thread* Re: [PATCH v3 4/8] memory: Add STM32 Octo Memory Manager driver
2025-02-12 12:51 ` Geert Uytterhoeven
@ 2025-02-12 16:18 ` Patrice CHOTARD
0 siblings, 0 replies; 23+ messages in thread
From: Patrice CHOTARD @ 2025-02-12 16:18 UTC (permalink / raw)
To: Geert Uytterhoeven
Cc: Mark Brown, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Alexandre Torgue, Philipp Zabel, Maxime Coquelin,
Greg Kroah-Hartman, Arnd Bergmann, Catalin Marinas, Will Deacon,
linux-spi, devicetree, linux-stm32, linux-arm-kernel,
linux-kernel, christophe.kerello
On 2/12/25 13:51, Geert Uytterhoeven wrote:
> Hi Patrice,
>
> On Mon, 10 Feb 2025 at 14:21, <patrice.chotard@foss.st.com> wrote:
>> 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>
>
> Thanks for your patch!
>
>> --- a/drivers/memory/Kconfig
>> +++ b/drivers/memory/Kconfig
>> @@ -225,6 +225,23 @@ config STM32_FMC2_EBI
>> devices (like SRAM, ethernet adapters, FPGAs, LCD displays, ...) on
>> SOCs containing the FMC2 External Bus Interface.
>>
>> +config STM32_OMM
>> + tristate "STM32 Octo Memory Manager"
>> + depends on SPI_STM32_OSPI || TEST_COMPILE
>
> COMPILE_TEST
good catch
>
>> + 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.
>> +
>> source "drivers/memory/samsung/Kconfig"
>> source "drivers/memory/tegra/Kconfig"
>
>> --- /dev/null
>> +++ b/drivers/memory/stm32_omm.c
>
>> +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[] = { "ospi1", "ospi2" };
>> + u32 amcr_base, amcr_mask;
>> + int ret, i, idx;
>
> unsigned int i
ok
>
>> + 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);
>> + if (!node)
>> + continue;
>> +
>> + 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);
>
> As reported by the kernel test robot, this fails to build when
> resource_size_t differs from unsigned long long. However, you can
> easily print the full resource instead:
>
> dev_err(dev, "%pR doesn't fit inside %pR\n", &res, omm->mm_res);
>
> https://elixir.bootlin.com/linux/v6.13.2/source/Documentation/core-api/printk-formats.rst#L206
OK, thanks for this tips
Patrice
>
> Gr{oetje,eeting}s,
>
> Geert
>
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v3 5/8] arm64: dts: st: Add OMM node on stm32mp251
2025-02-10 13:18 [PATCH v3 0/8] Add STM32MP25 SPI NOR support patrice.chotard
` (3 preceding siblings ...)
2025-02-10 13:18 ` [PATCH v3 4/8] memory: Add STM32 Octo Memory Manager driver patrice.chotard
@ 2025-02-10 13:18 ` patrice.chotard
2025-02-10 13:18 ` [PATCH v3 6/8] arm64: dts: st: Add ospi port1 pinctrl entries in stm32mp25-pinctrl.dtsi patrice.chotard
` (3 subsequent siblings)
8 siblings, 0 replies; 23+ messages in thread
From: patrice.chotard @ 2025-02-10 13:18 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..2565236e369f 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 = "regs", "memory_map";
+ ranges = <0 0 0x40430000 0x400>,
+ <1 0 0x40440000 0x400>;
+ clocks = <&rcc CK_BUS_OSPIIOM>;
+ resets = <&rcc OSPIIOM_R>;
+ access-controllers = <&rifsc 111>;
+ power-domains = <&CLUSTER_PD>;
+ #address-cells = <2>;
+ #size-cells = <1>;
+ st,syscfg-amcr = <&syscfg 0x2c00 0x7>;
+ 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";
+ 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>;
+ 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";
+ 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>;
+ st,syscfg-dlyb = <&syscfg 0x1400>;
+ status = "disabled";
+ };
+ };
+
spi8: spi@46020000 {
#address-cells = <1>;
#size-cells = <0>;
--
2.25.1
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH v3 6/8] arm64: dts: st: Add ospi port1 pinctrl entries in stm32mp25-pinctrl.dtsi
2025-02-10 13:18 [PATCH v3 0/8] Add STM32MP25 SPI NOR support patrice.chotard
` (4 preceding siblings ...)
2025-02-10 13:18 ` [PATCH v3 5/8] arm64: dts: st: Add OMM node on stm32mp251 patrice.chotard
@ 2025-02-10 13:18 ` patrice.chotard
2025-02-10 13:18 ` [PATCH v3 7/8] arm64: dts: st: Add SPI NOR flash support on stm32mp257f-ev1 board patrice.chotard
` (2 subsequent siblings)
8 siblings, 0 replies; 23+ messages in thread
From: patrice.chotard @ 2025-02-10 13:18 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] 23+ messages in thread* [PATCH v3 7/8] arm64: dts: st: Add SPI NOR flash support on stm32mp257f-ev1 board
2025-02-10 13:18 [PATCH v3 0/8] Add STM32MP25 SPI NOR support patrice.chotard
` (5 preceding siblings ...)
2025-02-10 13:18 ` [PATCH v3 6/8] arm64: dts: st: Add ospi port1 pinctrl entries in stm32mp25-pinctrl.dtsi patrice.chotard
@ 2025-02-10 13:18 ` patrice.chotard
2025-02-10 13:18 ` [PATCH v3 8/8] arm64: defconfig: Enable STM32 Octo Memory Manager and OcstoSPI driver patrice.chotard
2025-02-13 7:56 ` [PATCH v3 0/8] Add STM32MP25 SPI NOR support Krzysztof Kozlowski
8 siblings, 0 replies; 23+ messages in thread
From: patrice.chotard @ 2025-02-10 13:18 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 | 32 ++++++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/arch/arm64/boot/dts/st/stm32mp257f-ev1.dts b/arch/arm64/boot/dts/st/stm32mp257f-ev1.dts
index 1b88485a62a1..76970c15e043 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,33 @@ &i2c8 {
status = "disabled";
};
+&ommanager {
+ memory-region = <&mm_ospi1>;
+ 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>;
+ pinctrl-names = "default", "sleep";
+ 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] 23+ messages in thread* [PATCH v3 8/8] arm64: defconfig: Enable STM32 Octo Memory Manager and OcstoSPI driver
2025-02-10 13:18 [PATCH v3 0/8] Add STM32MP25 SPI NOR support patrice.chotard
` (6 preceding siblings ...)
2025-02-10 13:18 ` [PATCH v3 7/8] arm64: dts: st: Add SPI NOR flash support on stm32mp257f-ev1 board patrice.chotard
@ 2025-02-10 13:18 ` patrice.chotard
2025-02-13 7:56 ` [PATCH v3 0/8] Add STM32MP25 SPI NOR support Krzysztof Kozlowski
8 siblings, 0 replies; 23+ messages in thread
From: patrice.chotard @ 2025-02-10 13:18 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 OctoSPI driver.
Enable STM32 Octo Memory Manager (OMM) driver which is needed
for OSPI usage on STM32MP257F-EV1 board.
Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com>
---
arch/arm64/configs/defconfig | 2 ++
1 file changed, 2 insertions(+)
diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
index 246a13412bf0..b089cf4b90a1 100644
--- a/arch/arm64/configs/defconfig
+++ b/arch/arm64/configs/defconfig
@@ -577,6 +577,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
@@ -1506,6 +1507,7 @@ CONFIG_EXTCON_USB_GPIO=y
CONFIG_EXTCON_USBC_CROS_EC=y
CONFIG_FSL_IFC=y
CONFIG_RENESAS_RPCIF=m
+CONFIG_STM32_OMM=m
CONFIG_IIO=y
CONFIG_EXYNOS_ADC=y
CONFIG_IMX8QXP_ADC=m
--
2.25.1
^ permalink raw reply related [flat|nested] 23+ messages in thread* Re: [PATCH v3 0/8] Add STM32MP25 SPI NOR support
2025-02-10 13:18 [PATCH v3 0/8] Add STM32MP25 SPI NOR support patrice.chotard
` (7 preceding siblings ...)
2025-02-10 13:18 ` [PATCH v3 8/8] arm64: defconfig: Enable STM32 Octo Memory Manager and OcstoSPI driver patrice.chotard
@ 2025-02-13 7:56 ` Krzysztof Kozlowski
2025-02-18 10:39 ` Patrice CHOTARD
8 siblings, 1 reply; 23+ messages in thread
From: Krzysztof Kozlowski @ 2025-02-13 7:56 UTC (permalink / raw)
To: patrice.chotard
Cc: Mark Brown, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Alexandre Torgue, Philipp Zabel, Maxime Coquelin,
Greg Kroah-Hartman, Arnd Bergmann, Catalin Marinas, Will Deacon,
linux-spi, devicetree, linux-stm32, linux-arm-kernel,
linux-kernel, christophe.kerello
On Mon, Feb 10, 2025 at 02:18:18PM +0100, patrice.chotard@foss.st.com wrote:
> From: Patrice Chotard <patrice.chotard@foss.st.com>
>
> This series adds SPI NOR support for STM32MP25 SoCs from STMicroelectronics,
> for that it adds support for:
> - Octo Memory Manager driver.
> - Octo SPI driver.
> - yaml schema for Octo Memory Manager and Octo SPI drivers.
>
You combined three different subsystems in one patchset and nothing here
explains why and what is the merging intention. Either split your work
or explain dependencies/merging.
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 23+ messages in thread* Re: [PATCH v3 0/8] Add STM32MP25 SPI NOR support
2025-02-13 7:56 ` [PATCH v3 0/8] Add STM32MP25 SPI NOR support Krzysztof Kozlowski
@ 2025-02-18 10:39 ` Patrice CHOTARD
0 siblings, 0 replies; 23+ messages in thread
From: Patrice CHOTARD @ 2025-02-18 10:39 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: Mark Brown, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Alexandre Torgue, Philipp Zabel, Maxime Coquelin,
Greg Kroah-Hartman, Arnd Bergmann, Catalin Marinas, Will Deacon,
linux-spi, devicetree, linux-stm32, linux-arm-kernel,
linux-kernel, christophe.kerello
On 2/13/25 08:56, Krzysztof Kozlowski wrote:
> On Mon, Feb 10, 2025 at 02:18:18PM +0100, patrice.chotard@foss.st.com wrote:
>> From: Patrice Chotard <patrice.chotard@foss.st.com>
>>
>> This series adds SPI NOR support for STM32MP25 SoCs from STMicroelectronics,
>> for that it adds support for:
>> - Octo Memory Manager driver.
>> - Octo SPI driver.
>> - yaml schema for Octo Memory Manager and Octo SPI drivers.
>>
>
> You combined three different subsystems in one patchset and nothing here
> explains why and what is the merging intention. Either split your work
> or explain dependencies/merging.
Ok i will add an explaination about OMM and OSPI relationship.
Thanks
Patrice
>
> Best regards,
> Krzysztof
>
^ permalink raw reply [flat|nested] 23+ messages in thread