* [PATCH v4 1/8] dt-bindings: spi: Add STM32 OSPI controller
2025-02-18 12:59 [PATCH v4 0/8] Add STM32MP25 SPI NOR support patrice.chotard
@ 2025-02-18 12:59 ` patrice.chotard
2025-02-18 12:59 ` [PATCH v4 2/8] spi: stm32: Add OSPI driver patrice.chotard
` (8 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: patrice.chotard @ 2025-02-18 12:59 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] 12+ messages in thread* [PATCH v4 2/8] spi: stm32: Add OSPI driver
2025-02-18 12:59 [PATCH v4 0/8] Add STM32MP25 SPI NOR support patrice.chotard
2025-02-18 12:59 ` [PATCH v4 1/8] dt-bindings: spi: Add STM32 OSPI controller patrice.chotard
@ 2025-02-18 12:59 ` patrice.chotard
2025-02-18 12:59 ` [PATCH v4 3/8] dt-bindings: memory-controllers: Add STM32 Octo Memory Manager controller patrice.chotard
` (7 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: patrice.chotard @ 2025-02-18 12:59 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] 12+ messages in thread* [PATCH v4 3/8] dt-bindings: memory-controllers: Add STM32 Octo Memory Manager controller
2025-02-18 12:59 [PATCH v4 0/8] Add STM32MP25 SPI NOR support patrice.chotard
2025-02-18 12:59 ` [PATCH v4 1/8] dt-bindings: spi: Add STM32 OSPI controller patrice.chotard
2025-02-18 12:59 ` [PATCH v4 2/8] spi: stm32: Add OSPI driver patrice.chotard
@ 2025-02-18 12:59 ` patrice.chotard
2025-02-18 12:59 ` [PATCH v4 4/8] memory: Add STM32 Octo Memory Manager driver patrice.chotard
` (6 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: patrice.chotard @ 2025-02-18 12:59 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 | 204 ++++++++++++++++++
1 file changed, 204 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..316e16ce238e
--- /dev/null
+++ b/Documentation/devicetree/bindings/memory-controllers/st,stm32mp25-omm.yaml
@@ -0,0 +1,204 @@
+# 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: 0
+
+ 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: 0
+
+ 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
+ - 1: mux OCTOSPI1 and OCTOSPI2 to port 1
+ - 2: swapped mode
+ - 3: mux OCTOSPI1 and OCTOSPI2 to port 2
+ minimum: 0
+ maximum: 3
+ default: 0
+
+ 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] 12+ messages in thread* [PATCH v4 4/8] memory: Add STM32 Octo Memory Manager driver
2025-02-18 12:59 [PATCH v4 0/8] Add STM32MP25 SPI NOR support patrice.chotard
` (2 preceding siblings ...)
2025-02-18 12:59 ` [PATCH v4 3/8] dt-bindings: memory-controllers: Add STM32 Octo Memory Manager controller patrice.chotard
@ 2025-02-18 12:59 ` patrice.chotard
2025-02-18 12:59 ` [PATCH v4 5/8] arm64: dts: st: Add OMM node on stm32mp251 patrice.chotard
` (5 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: patrice.chotard @ 2025-02-18 12:59 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 | 522 +++++++++++++++++++++++++++++++++++++
3 files changed, 540 insertions(+)
create mode 100644 drivers/memory/stm32_omm.c
diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig
index c82d8d8a16ea..3a0703fbfee7 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 || 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"
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..8f7f475769e7
--- /dev/null
+++ b/drivers/memory/stm32_omm.c
@@ -0,0 +1,522 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) STMicroelectronics 2025 - All Rights Reserved
+ * Author(s): Patrice Chotard <patrice.chotard@foss.st.com> for STMicroelectronics.
+ */
+
+#include <linux/bitfield.h>
+#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, idx;
+ unsigned int i, 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, "%pR doesn't fit inside %pR\n", &res, omm->mm_res);
+
+ 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, "%pR overlaps %pR\n", &res1, &res);
+
+ 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");
+ pm_runtime_disable(dev);
+ 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");
+ pm_runtime_disable(dev);
+ 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) {
+ pm_runtime_disable(dev);
+ 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] 12+ messages in thread* [PATCH v4 5/8] arm64: dts: st: Add OMM node on stm32mp251
2025-02-18 12:59 [PATCH v4 0/8] Add STM32MP25 SPI NOR support patrice.chotard
` (3 preceding siblings ...)
2025-02-18 12:59 ` [PATCH v4 4/8] memory: Add STM32 Octo Memory Manager driver patrice.chotard
@ 2025-02-18 12:59 ` patrice.chotard
2025-02-18 12:59 ` [PATCH v4 6/8] arm64: dts: st: Add ospi port1 pinctrl entries in stm32mp25-pinctrl.dtsi patrice.chotard
` (4 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: patrice.chotard @ 2025-02-18 12:59 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] 12+ messages in thread* [PATCH v4 6/8] arm64: dts: st: Add ospi port1 pinctrl entries in stm32mp25-pinctrl.dtsi
2025-02-18 12:59 [PATCH v4 0/8] Add STM32MP25 SPI NOR support patrice.chotard
` (4 preceding siblings ...)
2025-02-18 12:59 ` [PATCH v4 5/8] arm64: dts: st: Add OMM node on stm32mp251 patrice.chotard
@ 2025-02-18 12:59 ` patrice.chotard
2025-02-18 12:59 ` [PATCH v4 7/8] arm64: dts: st: Add SPI NOR flash support on stm32mp257f-ev1 board patrice.chotard
` (3 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: patrice.chotard @ 2025-02-18 12:59 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] 12+ messages in thread* [PATCH v4 7/8] arm64: dts: st: Add SPI NOR flash support on stm32mp257f-ev1 board
2025-02-18 12:59 [PATCH v4 0/8] Add STM32MP25 SPI NOR support patrice.chotard
` (5 preceding siblings ...)
2025-02-18 12:59 ` [PATCH v4 6/8] arm64: dts: st: Add ospi port1 pinctrl entries in stm32mp25-pinctrl.dtsi patrice.chotard
@ 2025-02-18 12:59 ` patrice.chotard
2025-02-18 13:00 ` [PATCH v4 8/8] arm64: defconfig: Enable STM32 Octo Memory Manager and OcstoSPI driver patrice.chotard
` (2 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: patrice.chotard @ 2025-02-18 12:59 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] 12+ messages in thread* [PATCH v4 8/8] arm64: defconfig: Enable STM32 Octo Memory Manager and OcstoSPI driver
2025-02-18 12:59 [PATCH v4 0/8] Add STM32MP25 SPI NOR support patrice.chotard
` (6 preceding siblings ...)
2025-02-18 12:59 ` [PATCH v4 7/8] arm64: dts: st: Add SPI NOR flash support on stm32mp257f-ev1 board patrice.chotard
@ 2025-02-18 13:00 ` patrice.chotard
2025-02-18 21:21 ` [PATCH v4 0/8] Add STM32MP25 SPI NOR support Rob Herring
2025-02-19 6:55 ` Krzysztof Kozlowski
9 siblings, 0 replies; 12+ messages in thread
From: patrice.chotard @ 2025-02-18 13:00 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] 12+ messages in thread* Re: [PATCH v4 0/8] Add STM32MP25 SPI NOR support
2025-02-18 12:59 [PATCH v4 0/8] Add STM32MP25 SPI NOR support patrice.chotard
` (7 preceding siblings ...)
2025-02-18 13:00 ` [PATCH v4 8/8] arm64: defconfig: Enable STM32 Octo Memory Manager and OcstoSPI driver patrice.chotard
@ 2025-02-18 21:21 ` Rob Herring
2025-02-19 7:52 ` Patrice CHOTARD
2025-02-19 6:55 ` Krzysztof Kozlowski
9 siblings, 1 reply; 12+ messages in thread
From: Rob Herring @ 2025-02-18 21:21 UTC (permalink / raw)
To: patrice.chotard
Cc: Mark Brown, 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 Tue, Feb 18, 2025 at 01:59:52PM +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.
>
> On STM32MP25 SoCs family, an Octo Memory Manager block manages the muxing,
> the memory area split, the chip select override and the time constraint
> between its 2 Octo SPI children.
>
> Due to these depedencies, this series adds support for:
> - Octo Memory Manager driver.
> - Octo SPI driver.
> - yaml schema for Octo Memory Manager and Octo SPI drivers.
>
> The device tree files adds Octo Memory Manager and its 2 associated Octo
> SPI chidren in stm32mp251.dtsi and adds SPI NOR support in stm32mp257f-ev1
> board.
>
> Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com>
>
> Changes in v4:
> - Add default value requested by Krzysztof for st,omm-req2ack-ns,
> st,omm-cssel-ovr and st,omm-mux properties in st,stm32mp25-omm.yaml
> - Remove constraint in free form test for st,omm-mux property.
> - Fix drivers/memory/Kconfig by replacing TEST_COMPILE_ by COMPILE_TEST.
> - Fix SPDX-License-Identifier for stm32-omm.c.
> - Fix Kernel test robot by fixing dev_err() format in stm32-omm.c.
> - Add missing pm_runtime_disable() in the error handling path in
> stm32-omm.c.
> - Replace an int by an unsigned int in stm32-omm.c
> - Remove uneeded "," after terminator in stm32-omm.c.
> - Update cover letter description to explain dependecies between
> Octo Memory Manager and its 2 Octo SPI children.
> - Add Reviewed-by Krzysztof Kozlowski for patch 1 and 3.
No, you didn't.
^ permalink raw reply [flat|nested] 12+ messages in thread* Re: [PATCH v4 0/8] Add STM32MP25 SPI NOR support
2025-02-18 21:21 ` [PATCH v4 0/8] Add STM32MP25 SPI NOR support Rob Herring
@ 2025-02-19 7:52 ` Patrice CHOTARD
0 siblings, 0 replies; 12+ messages in thread
From: Patrice CHOTARD @ 2025-02-19 7:52 UTC (permalink / raw)
To: Rob Herring
Cc: Mark Brown, 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/18/25 22:21, Rob Herring wrote:
> On Tue, Feb 18, 2025 at 01:59:52PM +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.
>>
>> On STM32MP25 SoCs family, an Octo Memory Manager block manages the muxing,
>> the memory area split, the chip select override and the time constraint
>> between its 2 Octo SPI children.
>>
>> Due to these depedencies, this series adds support for:
>> - Octo Memory Manager driver.
>> - Octo SPI driver.
>> - yaml schema for Octo Memory Manager and Octo SPI drivers.
>>
>> The device tree files adds Octo Memory Manager and its 2 associated Octo
>> SPI chidren in stm32mp251.dtsi and adds SPI NOR support in stm32mp257f-ev1
>> board.
>>
>> Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com>
>>
>> Changes in v4:
>> - Add default value requested by Krzysztof for st,omm-req2ack-ns,
>> st,omm-cssel-ovr and st,omm-mux properties in st,stm32mp25-omm.yaml
>> - Remove constraint in free form test for st,omm-mux property.
>> - Fix drivers/memory/Kconfig by replacing TEST_COMPILE_ by COMPILE_TEST.
>> - Fix SPDX-License-Identifier for stm32-omm.c.
>> - Fix Kernel test robot by fixing dev_err() format in stm32-omm.c.
>> - Add missing pm_runtime_disable() in the error handling path in
>> stm32-omm.c.
>> - Replace an int by an unsigned int in stm32-omm.c
>> - Remove uneeded "," after terminator in stm32-omm.c.
>> - Update cover letter description to explain dependecies between
>> Octo Memory Manager and its 2 Octo SPI children.
>> - Add Reviewed-by Krzysztof Kozlowski for patch 1 and 3.
>
> No, you didn't.
Argh, sorry, i will send a v5
Patrice
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v4 0/8] Add STM32MP25 SPI NOR support
2025-02-18 12:59 [PATCH v4 0/8] Add STM32MP25 SPI NOR support patrice.chotard
` (8 preceding siblings ...)
2025-02-18 21:21 ` [PATCH v4 0/8] Add STM32MP25 SPI NOR support Rob Herring
@ 2025-02-19 6:55 ` Krzysztof Kozlowski
9 siblings, 0 replies; 12+ messages in thread
From: Krzysztof Kozlowski @ 2025-02-19 6:55 UTC (permalink / raw)
To: patrice.chotard, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Alexandre Torgue, Philipp Zabel, Maxime Coquelin,
Greg Kroah-Hartman, Arnd Bergmann, Catalin Marinas, Will Deacon
Cc: linux-spi, devicetree, linux-stm32, linux-arm-kernel,
linux-kernel, christophe.kerello
On 18/02/2025 13:59, 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.
>
> On STM32MP25 SoCs family, an Octo Memory Manager block manages the muxing,
> the memory area split, the chip select override and the time constraint
> between its 2 Octo SPI children.
>
> Due to these depedencies, this series adds support for:
> - Octo Memory Manager driver.
> - Octo SPI driver.
> - yaml schema for Octo Memory Manager and Octo SPI drivers.
>
> The device tree files adds Octo Memory Manager and its 2 associated Octo
> SPI chidren in stm32mp251.dtsi and adds SPI NOR support in stm32mp257f-ev1
> board.
>
> Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com>
>
> Changes in v4:
> - Add default value requested by Krzysztof for st,omm-req2ack-ns,
> st,omm-cssel-ovr and st,omm-mux properties in st,stm32mp25-omm.yaml
> - Remove constraint in free form test for st,omm-mux property.
> - Fix drivers/memory/Kconfig by replacing TEST_COMPILE_ by COMPILE_TEST.
> - Fix SPDX-License-Identifier for stm32-omm.c.
> - Fix Kernel test robot by fixing dev_err() format in stm32-omm.c.
> - Add missing pm_runtime_disable() in the error handling path in
> stm32-omm.c.
> - Replace an int by an unsigned int in stm32-omm.c
> - Remove uneeded "," after terminator in stm32-omm.c.
> - Update cover letter description to explain dependecies between
> Octo Memory Manager and its 2 Octo SPI children.
> - Add Reviewed-by Krzysztof Kozlowski for patch 1 and 3.
If only people could start using b4, like we ask them...
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 12+ messages in thread