1 | From: Patrice Chotard <patrice.chotard@foss.st.com> | ||
---|---|---|---|
2 | |||
3 | This series adds SPI NOR support for STM32MP25 SoCs from STMicroelectronics. | 1 | This series adds SPI NOR support for STM32MP25 SoCs from STMicroelectronics. |
4 | 2 | ||
5 | On STM32MP25 SoCs family, an Octo Memory Manager block manages the muxing, | 3 | On STM32MP25 SoCs family, an Octo Memory Manager block manages the muxing, |
6 | the memory area split, the chip select override and the time constraint | 4 | the memory area split, the chip select override and the time constraint |
7 | between its 2 Octo SPI children. | 5 | between its 2 Octo SPI children. |
8 | 6 | ||
9 | Due to these depedencies, this series adds support for: | 7 | Due to these depedencies, this series adds support for: |
10 | - Octo Memory Manager driver. | 8 | - Octo Memory Manager driver. |
11 | - Octo SPI driver. | 9 | - Octo SPI driver. |
12 | - yaml schema for Octo Memory Manager and Octo SPI drivers. | 10 | - yaml schema for Octo Memory Manager and Octo SPI drivers. |
13 | 11 | ||
14 | The device tree files adds Octo Memory Manager and its 2 associated Octo | 12 | The device tree files adds Octo Memory Manager and its 2 associated Octo |
15 | SPI chidren in stm32mp251.dtsi and adds SPI NOR support in stm32mp257f-ev1 | 13 | SPI chidren in stm32mp251.dtsi and adds SPI NOR support in stm32mp257f-ev1 |
16 | board. | 14 | board. |
15 | |||
16 | Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com> | ||
17 | 17 | ||
18 | Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com> | 18 | Changes in v7: |
19 | - update OMM's dt-bindings by updating : | ||
20 | - clock-names and reset-names properties. | ||
21 | - spi unit-address node. | ||
22 | - example. | ||
23 | - update stm32mp251.dtsi to match with OMM's bindings update. | ||
24 | - update stm32mp257f-ev1.dts to match with OMM's bindings update. | ||
25 | - Link to v6: https://lore.kernel.org/r/20250321-upstream_ospi_v6-v6-0-37bbcab43439@foss.st.com | ||
26 | |||
27 | Changes in v6: | ||
28 | - Update MAINTAINERS file. | ||
29 | - Remove previous patch 1/8 and 2/8, merged by Mark Brown in spi git tree. | ||
30 | - Fix Signed-off-by order for patch 3. | ||
31 | - OMM driver: | ||
32 | - Add dev_err_probe() in error path. | ||
33 | - Rename stm32_omm_enable_child_clock() to stm32_omm_toggle_child_clock(). | ||
34 | - Reorder initialised/non-initialized variable in stm32_omm_configure() | ||
35 | and stm32_omm_probe(). | ||
36 | - Move pm_runtime_disable() calls from stm32_omm_configure() to | ||
37 | stm32_omm_probe(). | ||
38 | - Update children's clocks and reset management. | ||
39 | - Use of_platform_populate() to probe children. | ||
40 | - Add missing pm_runtime_disable(). | ||
41 | - Remove useless stm32_omm_check_access's first parameter. | ||
42 | - Update OMM's dt-bindings by adding OSPI's clocks and resets. | ||
43 | - Update stm32mp251.dtsi by adding OSPI's clock and reset in OMM's node. | ||
19 | 44 | ||
20 | Changes in v5: | 45 | Changes in v5: |
21 | - Add Reviewed-by Krzysztof Kozlowski for patch 1 and 3. | 46 | - Add Reviewed-by Krzysztof Kozlowski for patch 1 and 3. |
22 | 47 | ||
23 | Changes in v4: | 48 | Changes in v4: |
24 | - Add default value requested by Krzysztof for st,omm-req2ack-ns, | 49 | - Add default value requested by Krzysztof for st,omm-req2ack-ns, |
25 | st,omm-cssel-ovr and st,omm-mux properties in st,stm32mp25-omm.yaml | 50 | st,omm-cssel-ovr and st,omm-mux properties in st,stm32mp25-omm.yaml |
26 | - Remove constraint in free form test for st,omm-mux property. | 51 | - Remove constraint in free form test for st,omm-mux property. |
27 | - Fix drivers/memory/Kconfig by replacing TEST_COMPILE_ by COMPILE_TEST. | 52 | - Fix drivers/memory/Kconfig by replacing TEST_COMPILE_ by COMPILE_TEST. |
28 | - Fix SPDX-License-Identifier for stm32-omm.c. | 53 | - Fix SPDX-License-Identifier for stm32-omm.c. |
29 | - Fix Kernel test robot by fixing dev_err() format in stm32-omm.c. | 54 | - Fix Kernel test robot by fixing dev_err() format in stm32-omm.c. |
30 | - Add missing pm_runtime_disable() in the error handling path in | 55 | - Add missing pm_runtime_disable() in the error handling path in |
31 | stm32-omm.c. | 56 | stm32-omm.c. |
32 | - Replace an int by an unsigned int in stm32-omm.c | 57 | - Replace an int by an unsigned int in stm32-omm.c |
33 | - Remove uneeded "," after terminator in stm32-omm.c. | 58 | - Remove uneeded "," after terminator in stm32-omm.c. |
34 | - Update cover letter description to explain dependecies between | 59 | - Update cover letter description to explain dependecies between |
35 | Octo Memory Manager and its 2 Octo SPI children. | 60 | Octo Memory Manager and its 2 Octo SPI children. |
36 | 61 | ||
37 | Changes in v3: | 62 | Changes in v3: |
38 | - Squash defconfig patches 8 and 9. | 63 | - Squash defconfig patches 8 and 9. |
39 | - Update STM32 Octo Memory Manager controller bindings. | 64 | - Update STM32 Octo Memory Manager controller bindings. |
40 | - Rename st,stm32-omm.yaml to st,stm32mp25-omm.yaml. | 65 | - Rename st,stm32-omm.yaml to st,stm32mp25-omm.yaml. |
41 | - Update STM32 OSPI controller bindings. | 66 | - Update STM32 OSPI controller bindings. |
42 | - Reorder DT properties in .dtsi and .dts files. | 67 | - Reorder DT properties in .dtsi and .dts files. |
43 | - Replace devm_reset_control_get_optional() by | 68 | - Replace devm_reset_control_get_optional() by |
44 | devm_reset_control_get_optional_exclusive() in stm32_omm.c. | 69 | devm_reset_control_get_optional_exclusive() in stm32_omm.c. |
45 | - Reintroduce region-memory-names management in stm32_omm.c. | 70 | - Reintroduce region-memory-names management in stm32_omm.c. |
46 | - Rename stm32_ospi_tx_poll() and stm32_ospi_tx() to respectively to | 71 | - Rename stm32_ospi_tx_poll() and stm32_ospi_tx() to respectively to |
47 | stm32_ospi_poll() and stm32_ospi_xfer() in spi-stm32-ospi.c. | 72 | stm32_ospi_poll() and stm32_ospi_xfer() in spi-stm32-ospi.c. |
48 | - Set SPI_CONTROLLER_HALF_DUPLEX in controller flags in spi-stm32-ospi.c. | 73 | - Set SPI_CONTROLLER_HALF_DUPLEX in controller flags in spi-stm32-ospi.c. |
49 | 74 | ||
50 | Changes in v2: | 75 | Changes in v2: |
51 | - Move STM32 Octo Memory Manager controller driver and bindings from | 76 | - Move STM32 Octo Memory Manager controller driver and bindings from |
52 | misc to memory-controllers. | 77 | misc to memory-controllers. |
53 | - Update STM32 OSPI controller bindings. | 78 | - Update STM32 OSPI controller bindings. |
54 | - Update STM32 Octo Memory Manager controller bindings. | 79 | - Update STM32 Octo Memory Manager controller bindings. |
55 | - Update STM32 Octo Memory Manager driver to match bindings update. | 80 | - Update STM32 Octo Memory Manager driver to match bindings update. |
56 | - Update DT to match bindings update. | 81 | - Update DT to match bindings update. |
57 | 82 | ||
83 | Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com> | ||
84 | --- | ||
85 | Patrice Chotard (7): | ||
86 | MAINTAINERS: add entry for STM32 OCTO MEMORY MANAGER driver | ||
87 | dt-bindings: memory-controllers: Add STM32 Octo Memory Manager controller | ||
88 | memory: Add STM32 Octo Memory Manager driver | ||
89 | arm64: dts: st: Add OMM node on stm32mp251 | ||
90 | arm64: dts: st: Add ospi port1 pinctrl entries in stm32mp25-pinctrl.dtsi | ||
91 | arm64: dts: st: Add SPI NOR flash support on stm32mp257f-ev1 board | ||
92 | arm64: defconfig: Enable STM32 Octo Memory Manager and OcstoSPI driver | ||
58 | 93 | ||
59 | Patrice Chotard (8): | 94 | .../memory-controllers/st,stm32mp25-omm.yaml | 227 ++++++++++ |
60 | dt-bindings: spi: Add STM32 OSPI controller | 95 | MAINTAINERS | 6 + |
61 | spi: stm32: Add OSPI driver | 96 | arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi | 51 +++ |
62 | dt-bindings: memory-controllers: Add STM32 Octo Memory Manager | 97 | arch/arm64/boot/dts/st/stm32mp251.dtsi | 54 +++ |
63 | controller | 98 | arch/arm64/boot/dts/st/stm32mp257f-ev1.dts | 32 ++ |
64 | memory: Add STM32 Octo Memory Manager driver | 99 | arch/arm64/configs/defconfig | 2 + |
65 | arm64: dts: st: Add OMM node on stm32mp251 | 100 | drivers/memory/Kconfig | 17 + |
66 | arm64: dts: st: Add ospi port1 pinctrl entries in | 101 | drivers/memory/Makefile | 1 + |
67 | stm32mp25-pinctrl.dtsi | 102 | drivers/memory/stm32_omm.c | 474 +++++++++++++++++++++ |
68 | arm64: dts: st: Add SPI NOR flash support on stm32mp257f-ev1 board | 103 | 9 files changed, 864 insertions(+) |
69 | arm64: defconfig: Enable STM32 Octo Memory Manager and OcstoSPI driver | 104 | --- |
105 | base-commit: 88424abd55ab36c3565898a656589a0a25ecd92f | ||
106 | change-id: 20250320-upstream_ospi_v6-d432a8172105 | ||
70 | 107 | ||
71 | .../memory-controllers/st,stm32mp25-omm.yaml | 204 ++++ | 108 | Best regards, |
72 | .../bindings/spi/st,stm32mp25-ospi.yaml | 105 ++ | ||
73 | arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi | 51 + | ||
74 | arch/arm64/boot/dts/st/stm32mp251.dtsi | 48 + | ||
75 | arch/arm64/boot/dts/st/stm32mp257f-ev1.dts | 32 + | ||
76 | arch/arm64/configs/defconfig | 2 + | ||
77 | drivers/memory/Kconfig | 17 + | ||
78 | drivers/memory/Makefile | 1 + | ||
79 | drivers/memory/stm32_omm.c | 522 ++++++++ | ||
80 | drivers/spi/Kconfig | 10 + | ||
81 | drivers/spi/Makefile | 1 + | ||
82 | drivers/spi/spi-stm32-ospi.c | 1065 +++++++++++++++++ | ||
83 | 12 files changed, 2058 insertions(+) | ||
84 | create mode 100644 Documentation/devicetree/bindings/memory-controllers/st,stm32mp25-omm.yaml | ||
85 | create mode 100644 Documentation/devicetree/bindings/spi/st,stm32mp25-ospi.yaml | ||
86 | create mode 100644 drivers/memory/stm32_omm.c | ||
87 | create mode 100644 drivers/spi/spi-stm32-ospi.c | ||
88 | |||
89 | -- | 109 | -- |
90 | 2.25.1 | 110 | Patrice Chotard <patrice.chotard@foss.st.com> | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Patrice Chotard <patrice.chotard@foss.st.com> | ||
2 | 1 | ||
3 | Add device tree bindings for the STM32 OSPI controller. | ||
4 | |||
5 | Main features of the Octo-SPI controller : | ||
6 | - support sNOR / sNAND / HyperRAM™ and HyperFlash™ devices. | ||
7 | - Three functional modes: indirect, automatic-status polling, | ||
8 | memory-mapped. | ||
9 | - Up to 4 Gbytes of external memory can be addressed in indirect | ||
10 | mode (per physical port and per CS), and up to 256 Mbytes in | ||
11 | memory-mapped mode (combined for both physical ports and per CS). | ||
12 | - Single-, dual-, quad-, and octal-SPI communication. | ||
13 | - Dual-quad communication. | ||
14 | - Single data rate (SDR) and double transfer rate (DTR). | ||
15 | - Maximum target frequency is 133 MHz for SDR and 133 MHz for DTR. | ||
16 | - Data strobe support. | ||
17 | - DMA channel for indirect mode. | ||
18 | - Double CS mapping that allows two external flash devices to be | ||
19 | addressed with a single OCTOSPI controller mapped on a single | ||
20 | OCTOSPI port. | ||
21 | |||
22 | Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com> | ||
23 | Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> | ||
24 | --- | ||
25 | .../bindings/spi/st,stm32mp25-ospi.yaml | 105 ++++++++++++++++++ | ||
26 | 1 file changed, 105 insertions(+) | ||
27 | create mode 100644 Documentation/devicetree/bindings/spi/st,stm32mp25-ospi.yaml | ||
28 | |||
29 | diff --git a/Documentation/devicetree/bindings/spi/st,stm32mp25-ospi.yaml b/Documentation/devicetree/bindings/spi/st,stm32mp25-ospi.yaml | ||
30 | new file mode 100644 | ||
31 | index XXXXXXX..XXXXXXX | ||
32 | --- /dev/null | ||
33 | +++ b/Documentation/devicetree/bindings/spi/st,stm32mp25-ospi.yaml | ||
34 | @@ -XXX,XX +XXX,XX @@ | ||
35 | +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) | ||
36 | +%YAML 1.2 | ||
37 | +--- | ||
38 | +$id: http://devicetree.org/schemas/spi/st,stm32mp25-ospi.yaml# | ||
39 | +$schema: http://devicetree.org/meta-schemas/core.yaml# | ||
40 | + | ||
41 | +title: STMicroelectronics STM32 Octal Serial Peripheral Interface (OSPI) | ||
42 | + | ||
43 | +maintainers: | ||
44 | + - Patrice Chotard <patrice.chotard@foss.st.com> | ||
45 | + | ||
46 | +allOf: | ||
47 | + - $ref: spi-controller.yaml# | ||
48 | + | ||
49 | +properties: | ||
50 | + compatible: | ||
51 | + const: st,stm32mp25-ospi | ||
52 | + | ||
53 | + reg: | ||
54 | + maxItems: 1 | ||
55 | + | ||
56 | + memory-region: | ||
57 | + description: | ||
58 | + Memory region to be used for memory-map read access. | ||
59 | + In memory-mapped mode, read access are performed from the memory | ||
60 | + device using the direct mapping. | ||
61 | + maxItems: 1 | ||
62 | + | ||
63 | + clocks: | ||
64 | + maxItems: 1 | ||
65 | + | ||
66 | + interrupts: | ||
67 | + maxItems: 1 | ||
68 | + | ||
69 | + resets: | ||
70 | + items: | ||
71 | + - description: phandle to OSPI block reset | ||
72 | + - description: phandle to delay block reset | ||
73 | + | ||
74 | + dmas: | ||
75 | + maxItems: 2 | ||
76 | + | ||
77 | + dma-names: | ||
78 | + items: | ||
79 | + - const: tx | ||
80 | + - const: rx | ||
81 | + | ||
82 | + st,syscfg-dlyb: | ||
83 | + description: configure OCTOSPI delay block. | ||
84 | + $ref: /schemas/types.yaml#/definitions/phandle-array | ||
85 | + items: | ||
86 | + - description: phandle to syscfg | ||
87 | + - description: register offset within syscfg | ||
88 | + | ||
89 | + access-controllers: | ||
90 | + description: phandle to the rifsc device to check access right | ||
91 | + and in some cases, an additional phandle to the rcc device for | ||
92 | + secure clock control. | ||
93 | + items: | ||
94 | + - description: phandle to bus controller | ||
95 | + - description: phandle to clock controller | ||
96 | + minItems: 1 | ||
97 | + | ||
98 | + power-domains: | ||
99 | + maxItems: 1 | ||
100 | + | ||
101 | +required: | ||
102 | + - compatible | ||
103 | + - reg | ||
104 | + - clocks | ||
105 | + - interrupts | ||
106 | + - st,syscfg-dlyb | ||
107 | + | ||
108 | +unevaluatedProperties: false | ||
109 | + | ||
110 | +examples: | ||
111 | + - | | ||
112 | + #include <dt-bindings/clock/st,stm32mp25-rcc.h> | ||
113 | + #include <dt-bindings/interrupt-controller/arm-gic.h> | ||
114 | + #include <dt-bindings/reset/st,stm32mp25-rcc.h> | ||
115 | + | ||
116 | + spi@40430000 { | ||
117 | + compatible = "st,stm32mp25-ospi"; | ||
118 | + reg = <0x40430000 0x400>; | ||
119 | + memory-region = <&mm_ospi1>; | ||
120 | + interrupts = <GIC_SPI 163 IRQ_TYPE_LEVEL_HIGH>; | ||
121 | + dmas = <&hpdma 2 0x62 0x00003121 0x0>, | ||
122 | + <&hpdma 2 0x42 0x00003112 0x0>; | ||
123 | + dma-names = "tx", "rx"; | ||
124 | + clocks = <&scmi_clk CK_SCMI_OSPI1>; | ||
125 | + resets = <&scmi_reset RST_SCMI_OSPI1>, <&scmi_reset RST_SCMI_OSPI1DLL>; | ||
126 | + access-controllers = <&rifsc 74>; | ||
127 | + power-domains = <&CLUSTER_PD>; | ||
128 | + st,syscfg-dlyb = <&syscfg 0x1000>; | ||
129 | + | ||
130 | + #address-cells = <1>; | ||
131 | + #size-cells = <0>; | ||
132 | + | ||
133 | + flash@0 { | ||
134 | + compatible = "jedec,spi-nor"; | ||
135 | + reg = <0>; | ||
136 | + spi-rx-bus-width = <4>; | ||
137 | + spi-max-frequency = <108000000>; | ||
138 | + }; | ||
139 | + }; | ||
140 | -- | ||
141 | 2.25.1 | ||
142 | diff view generated by jsdifflib |
1 | From: Patrice Chotard <patrice.chotard@foss.st.com> | 1 | Add myself as STM32 OCTO MEMORY MANAGER maintainer. |
---|---|---|---|
2 | |||
3 | Add STM32 OSPI driver, it supports : | ||
4 | - support sNOR / sNAND devices. | ||
5 | - Three functional modes: indirect, automatic-status polling, | ||
6 | memory-mapped. | ||
7 | - Single-, dual-, quad-, and octal-SPI communication. | ||
8 | - Dual-quad communication. | ||
9 | - Single data rate (SDR). | ||
10 | - DMA channel for indirect mode. | ||
11 | 2 | ||
12 | Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com> | 3 | Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com> |
13 | --- | 4 | --- |
14 | drivers/spi/Kconfig | 10 + | 5 | MAINTAINERS | 6 ++++++ |
15 | drivers/spi/Makefile | 1 + | 6 | 1 file changed, 6 insertions(+) |
16 | drivers/spi/spi-stm32-ospi.c | 1065 ++++++++++++++++++++++++++++++++++ | ||
17 | 3 files changed, 1076 insertions(+) | ||
18 | create mode 100644 drivers/spi/spi-stm32-ospi.c | ||
19 | 7 | ||
20 | diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig | 8 | diff --git a/MAINTAINERS b/MAINTAINERS |
21 | index XXXXXXX..XXXXXXX 100644 | 9 | index XXXXXXX..XXXXXXX 100644 |
22 | --- a/drivers/spi/Kconfig | 10 | --- a/MAINTAINERS |
23 | +++ b/drivers/spi/Kconfig | 11 | +++ b/MAINTAINERS |
24 | @@ -XXX,XX +XXX,XX @@ config SPI_STM32 | 12 | @@ -XXX,XX +XXX,XX @@ L: linux-i2c@vger.kernel.org |
25 | is not available, the driver automatically falls back to | 13 | S: Maintained |
26 | PIO mode. | 14 | F: drivers/i2c/busses/i2c-stm32* |
27 | 15 | ||
28 | +config SPI_STM32_OSPI | 16 | +ST STM32 OCTO MEMORY MANAGER |
29 | + tristate "STMicroelectronics STM32 OCTO SPI controller" | 17 | +M: Patrice Chotard <patrice.chotard@foss.st.com> |
30 | + depends on ARCH_STM32 || COMPILE_TEST | 18 | +S: Maintained |
31 | + depends on OF | 19 | +F: Documentation/devicetree/bindings/memory-controllers/st,stm32mp25-omm.yaml |
32 | + depends on SPI_MEM | 20 | +F: drivers/memory/stm32_omm.c |
33 | + help | ||
34 | + This enables support for the Octo SPI controller in master mode. | ||
35 | + This driver does not support generic SPI. The implementation only | ||
36 | + supports spi-mem interface. | ||
37 | + | 21 | + |
38 | config SPI_STM32_QSPI | 22 | ST STM32 SPI DRIVER |
39 | tristate "STMicroelectronics STM32 QUAD SPI controller" | 23 | M: Alain Volmat <alain.volmat@foss.st.com> |
40 | depends on ARCH_STM32 || COMPILE_TEST | 24 | L: linux-spi@vger.kernel.org |
41 | diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile | 25 | |
42 | index XXXXXXX..XXXXXXX 100644 | ||
43 | --- a/drivers/spi/Makefile | ||
44 | +++ b/drivers/spi/Makefile | ||
45 | @@ -XXX,XX +XXX,XX @@ obj-$(CONFIG_SPI_SN_F_OSPI) += spi-sn-f-ospi.o | ||
46 | obj-$(CONFIG_SPI_SPRD) += spi-sprd.o | ||
47 | obj-$(CONFIG_SPI_SPRD_ADI) += spi-sprd-adi.o | ||
48 | obj-$(CONFIG_SPI_STM32) += spi-stm32.o | ||
49 | +obj-$(CONFIG_SPI_STM32_OSPI) += spi-stm32-ospi.o | ||
50 | obj-$(CONFIG_SPI_STM32_QSPI) += spi-stm32-qspi.o | ||
51 | obj-$(CONFIG_SPI_ST_SSC4) += spi-st-ssc4.o | ||
52 | obj-$(CONFIG_SPI_SUN4I) += spi-sun4i.o | ||
53 | diff --git a/drivers/spi/spi-stm32-ospi.c b/drivers/spi/spi-stm32-ospi.c | ||
54 | new file mode 100644 | ||
55 | index XXXXXXX..XXXXXXX | ||
56 | --- /dev/null | ||
57 | +++ b/drivers/spi/spi-stm32-ospi.c | ||
58 | @@ -XXX,XX +XXX,XX @@ | ||
59 | +// SPDX-License-Identifier: GPL-2.0 | ||
60 | +/* | ||
61 | + * Copyright (C) STMicroelectronics 2025 - All Rights Reserved | ||
62 | + */ | ||
63 | + | ||
64 | +#include <linux/bitfield.h> | ||
65 | +#include <linux/clk.h> | ||
66 | +#include <linux/delay.h> | ||
67 | +#include <linux/dma-mapping.h> | ||
68 | +#include <linux/dmaengine.h> | ||
69 | +#include <linux/err.h> | ||
70 | +#include <linux/errno.h> | ||
71 | +#include <linux/interrupt.h> | ||
72 | +#include <linux/io.h> | ||
73 | +#include <linux/iopoll.h> | ||
74 | +#include <linux/mfd/syscon.h> | ||
75 | +#include <linux/module.h> | ||
76 | +#include <linux/mutex.h> | ||
77 | +#include <linux/of.h> | ||
78 | +#include <linux/of_address.h> | ||
79 | +#include <linux/of_device.h> | ||
80 | +#include <linux/of_gpio.h> | ||
81 | +#include <linux/of_reserved_mem.h> | ||
82 | +#include <linux/pinctrl/consumer.h> | ||
83 | +#include <linux/platform_device.h> | ||
84 | +#include <linux/pm_runtime.h> | ||
85 | +#include <linux/reset.h> | ||
86 | +#include <linux/sizes.h> | ||
87 | +#include <linux/spi/spi-mem.h> | ||
88 | +#include <linux/types.h> | ||
89 | + | ||
90 | +#define OSPI_CR 0x00 | ||
91 | +#define CR_EN BIT(0) | ||
92 | +#define CR_ABORT BIT(1) | ||
93 | +#define CR_DMAEN BIT(2) | ||
94 | +#define CR_FTHRES_SHIFT 8 | ||
95 | +#define CR_TEIE BIT(16) | ||
96 | +#define CR_TCIE BIT(17) | ||
97 | +#define CR_SMIE BIT(19) | ||
98 | +#define CR_APMS BIT(22) | ||
99 | +#define CR_CSSEL BIT(24) | ||
100 | +#define CR_FMODE_MASK GENMASK(29, 28) | ||
101 | +#define CR_FMODE_INDW (0U) | ||
102 | +#define CR_FMODE_INDR (1U) | ||
103 | +#define CR_FMODE_APM (2U) | ||
104 | +#define CR_FMODE_MM (3U) | ||
105 | + | ||
106 | +#define OSPI_DCR1 0x08 | ||
107 | +#define DCR1_DLYBYP BIT(3) | ||
108 | +#define DCR1_DEVSIZE_MASK GENMASK(20, 16) | ||
109 | +#define DCR1_MTYP_MASK GENMASK(26, 24) | ||
110 | +#define DCR1_MTYP_MX_MODE 1 | ||
111 | +#define DCR1_MTYP_HP_MEMMODE 4 | ||
112 | + | ||
113 | +#define OSPI_DCR2 0x0c | ||
114 | +#define DCR2_PRESC_MASK GENMASK(7, 0) | ||
115 | + | ||
116 | +#define OSPI_SR 0x20 | ||
117 | +#define SR_TEF BIT(0) | ||
118 | +#define SR_TCF BIT(1) | ||
119 | +#define SR_FTF BIT(2) | ||
120 | +#define SR_SMF BIT(3) | ||
121 | +#define SR_BUSY BIT(5) | ||
122 | + | ||
123 | +#define OSPI_FCR 0x24 | ||
124 | +#define FCR_CTEF BIT(0) | ||
125 | +#define FCR_CTCF BIT(1) | ||
126 | +#define FCR_CSMF BIT(3) | ||
127 | + | ||
128 | +#define OSPI_DLR 0x40 | ||
129 | +#define OSPI_AR 0x48 | ||
130 | +#define OSPI_DR 0x50 | ||
131 | +#define OSPI_PSMKR 0x80 | ||
132 | +#define OSPI_PSMAR 0x88 | ||
133 | + | ||
134 | +#define OSPI_CCR 0x100 | ||
135 | +#define CCR_IMODE_MASK GENMASK(2, 0) | ||
136 | +#define CCR_IDTR BIT(3) | ||
137 | +#define CCR_ISIZE_MASK GENMASK(5, 4) | ||
138 | +#define CCR_ADMODE_MASK GENMASK(10, 8) | ||
139 | +#define CCR_ADMODE_8LINES 4 | ||
140 | +#define CCR_ADDTR BIT(11) | ||
141 | +#define CCR_ADSIZE_MASK GENMASK(13, 12) | ||
142 | +#define CCR_ADSIZE_32BITS 3 | ||
143 | +#define CCR_DMODE_MASK GENMASK(26, 24) | ||
144 | +#define CCR_DMODE_8LINES 4 | ||
145 | +#define CCR_DQSE BIT(29) | ||
146 | +#define CCR_DDTR BIT(27) | ||
147 | +#define CCR_BUSWIDTH_0 0x0 | ||
148 | +#define CCR_BUSWIDTH_1 0x1 | ||
149 | +#define CCR_BUSWIDTH_2 0x2 | ||
150 | +#define CCR_BUSWIDTH_4 0x3 | ||
151 | +#define CCR_BUSWIDTH_8 0x4 | ||
152 | + | ||
153 | +#define OSPI_TCR 0x108 | ||
154 | +#define TCR_DCYC_MASK GENMASK(4, 0) | ||
155 | +#define TCR_DHQC BIT(28) | ||
156 | +#define TCR_SSHIFT BIT(30) | ||
157 | + | ||
158 | +#define OSPI_IR 0x110 | ||
159 | + | ||
160 | +#define STM32_OSPI_MAX_MMAP_SZ SZ_256M | ||
161 | +#define STM32_OSPI_MAX_NORCHIP 2 | ||
162 | + | ||
163 | +#define STM32_FIFO_TIMEOUT_US 30000 | ||
164 | +#define STM32_ABT_TIMEOUT_US 100000 | ||
165 | +#define STM32_COMP_TIMEOUT_MS 5000 | ||
166 | +#define STM32_BUSY_TIMEOUT_US 100000 | ||
167 | + | ||
168 | + | ||
169 | +#define STM32_AUTOSUSPEND_DELAY -1 | ||
170 | + | ||
171 | +struct stm32_ospi { | ||
172 | + struct device *dev; | ||
173 | + struct spi_controller *ctrl; | ||
174 | + struct clk *clk; | ||
175 | + struct reset_control *rstc; | ||
176 | + | ||
177 | + struct completion data_completion; | ||
178 | + struct completion match_completion; | ||
179 | + | ||
180 | + struct dma_chan *dma_chtx; | ||
181 | + struct dma_chan *dma_chrx; | ||
182 | + struct completion dma_completion; | ||
183 | + | ||
184 | + void __iomem *regs_base; | ||
185 | + void __iomem *mm_base; | ||
186 | + phys_addr_t regs_phys_base; | ||
187 | + resource_size_t mm_size; | ||
188 | + u32 clk_rate; | ||
189 | + u32 fmode; | ||
190 | + u32 cr_reg; | ||
191 | + u32 dcr_reg; | ||
192 | + u32 flash_presc[STM32_OSPI_MAX_NORCHIP]; | ||
193 | + int irq; | ||
194 | + unsigned long status_timeout; | ||
195 | + | ||
196 | + /* | ||
197 | + * To protect device configuration, could be different between | ||
198 | + * 2 flash access | ||
199 | + */ | ||
200 | + struct mutex lock; | ||
201 | +}; | ||
202 | + | ||
203 | +static void stm32_ospi_read_fifo(u8 *val, void __iomem *addr) | ||
204 | +{ | ||
205 | + *val = readb_relaxed(addr); | ||
206 | +} | ||
207 | + | ||
208 | +static void stm32_ospi_write_fifo(u8 *val, void __iomem *addr) | ||
209 | +{ | ||
210 | + writeb_relaxed(*val, addr); | ||
211 | +} | ||
212 | + | ||
213 | +static int stm32_ospi_abort(struct stm32_ospi *ospi) | ||
214 | +{ | ||
215 | + void __iomem *regs_base = ospi->regs_base; | ||
216 | + u32 cr; | ||
217 | + int timeout; | ||
218 | + | ||
219 | + cr = readl_relaxed(regs_base + OSPI_CR) | CR_ABORT; | ||
220 | + writel_relaxed(cr, regs_base + OSPI_CR); | ||
221 | + | ||
222 | + /* wait clear of abort bit by hw */ | ||
223 | + timeout = readl_relaxed_poll_timeout_atomic(regs_base + OSPI_CR, | ||
224 | + cr, !(cr & CR_ABORT), 1, | ||
225 | + STM32_ABT_TIMEOUT_US); | ||
226 | + | ||
227 | + if (timeout) | ||
228 | + dev_err(ospi->dev, "%s abort timeout:%d\n", __func__, timeout); | ||
229 | + | ||
230 | + return timeout; | ||
231 | +} | ||
232 | + | ||
233 | +static int stm32_ospi_poll(struct stm32_ospi *ospi, u8 *buf, u32 len, bool read) | ||
234 | +{ | ||
235 | + void __iomem *regs_base = ospi->regs_base; | ||
236 | + void (*fifo)(u8 *val, void __iomem *addr); | ||
237 | + u32 sr; | ||
238 | + int ret; | ||
239 | + | ||
240 | + if (read) | ||
241 | + fifo = stm32_ospi_read_fifo; | ||
242 | + else | ||
243 | + fifo = stm32_ospi_write_fifo; | ||
244 | + | ||
245 | + while (len--) { | ||
246 | + ret = readl_relaxed_poll_timeout_atomic(regs_base + OSPI_SR, | ||
247 | + sr, sr & SR_FTF, 1, | ||
248 | + STM32_FIFO_TIMEOUT_US); | ||
249 | + if (ret) { | ||
250 | + dev_err(ospi->dev, "fifo timeout (len:%d stat:%#x)\n", | ||
251 | + len, sr); | ||
252 | + return ret; | ||
253 | + } | ||
254 | + fifo(buf++, regs_base + OSPI_DR); | ||
255 | + } | ||
256 | + | ||
257 | + return 0; | ||
258 | +} | ||
259 | + | ||
260 | +static int stm32_ospi_wait_nobusy(struct stm32_ospi *ospi) | ||
261 | +{ | ||
262 | + u32 sr; | ||
263 | + | ||
264 | + return readl_relaxed_poll_timeout_atomic(ospi->regs_base + OSPI_SR, | ||
265 | + sr, !(sr & SR_BUSY), 1, | ||
266 | + STM32_BUSY_TIMEOUT_US); | ||
267 | +} | ||
268 | + | ||
269 | +static int stm32_ospi_wait_cmd(struct stm32_ospi *ospi) | ||
270 | +{ | ||
271 | + void __iomem *regs_base = ospi->regs_base; | ||
272 | + u32 cr, sr; | ||
273 | + int err = 0; | ||
274 | + | ||
275 | + if ((readl_relaxed(regs_base + OSPI_SR) & SR_TCF) || | ||
276 | + ospi->fmode == CR_FMODE_APM) | ||
277 | + goto out; | ||
278 | + | ||
279 | + reinit_completion(&ospi->data_completion); | ||
280 | + cr = readl_relaxed(regs_base + OSPI_CR); | ||
281 | + writel_relaxed(cr | CR_TCIE | CR_TEIE, regs_base + OSPI_CR); | ||
282 | + | ||
283 | + if (!wait_for_completion_timeout(&ospi->data_completion, | ||
284 | + msecs_to_jiffies(STM32_COMP_TIMEOUT_MS))) | ||
285 | + err = -ETIMEDOUT; | ||
286 | + | ||
287 | + sr = readl_relaxed(regs_base + OSPI_SR); | ||
288 | + if (sr & SR_TCF) | ||
289 | + /* avoid false timeout */ | ||
290 | + err = 0; | ||
291 | + if (sr & SR_TEF) | ||
292 | + err = -EIO; | ||
293 | + | ||
294 | +out: | ||
295 | + /* clear flags */ | ||
296 | + writel_relaxed(FCR_CTCF | FCR_CTEF, regs_base + OSPI_FCR); | ||
297 | + | ||
298 | + if (!err) | ||
299 | + err = stm32_ospi_wait_nobusy(ospi); | ||
300 | + | ||
301 | + return err; | ||
302 | +} | ||
303 | + | ||
304 | +static void stm32_ospi_dma_callback(void *arg) | ||
305 | +{ | ||
306 | + struct completion *dma_completion = arg; | ||
307 | + | ||
308 | + complete(dma_completion); | ||
309 | +} | ||
310 | + | ||
311 | +static irqreturn_t stm32_ospi_irq(int irq, void *dev_id) | ||
312 | +{ | ||
313 | + struct stm32_ospi *ospi = (struct stm32_ospi *)dev_id; | ||
314 | + void __iomem *regs_base = ospi->regs_base; | ||
315 | + u32 cr, sr; | ||
316 | + | ||
317 | + cr = readl_relaxed(regs_base + OSPI_CR); | ||
318 | + sr = readl_relaxed(regs_base + OSPI_SR); | ||
319 | + | ||
320 | + if (cr & CR_SMIE && sr & SR_SMF) { | ||
321 | + /* disable irq */ | ||
322 | + cr &= ~CR_SMIE; | ||
323 | + writel_relaxed(cr, regs_base + OSPI_CR); | ||
324 | + complete(&ospi->match_completion); | ||
325 | + | ||
326 | + return IRQ_HANDLED; | ||
327 | + } | ||
328 | + | ||
329 | + if (sr & (SR_TEF | SR_TCF)) { | ||
330 | + /* disable irq */ | ||
331 | + cr &= ~CR_TCIE & ~CR_TEIE; | ||
332 | + writel_relaxed(cr, regs_base + OSPI_CR); | ||
333 | + complete(&ospi->data_completion); | ||
334 | + } | ||
335 | + | ||
336 | + return IRQ_HANDLED; | ||
337 | +} | ||
338 | + | ||
339 | +static void stm32_ospi_dma_setup(struct stm32_ospi *ospi, | ||
340 | + struct dma_slave_config *dma_cfg) | ||
341 | +{ | ||
342 | + if (dma_cfg && ospi->dma_chrx) { | ||
343 | + if (dmaengine_slave_config(ospi->dma_chrx, dma_cfg)) { | ||
344 | + dev_err(ospi->dev, "dma rx config failed\n"); | ||
345 | + dma_release_channel(ospi->dma_chrx); | ||
346 | + ospi->dma_chrx = NULL; | ||
347 | + } | ||
348 | + } | ||
349 | + | ||
350 | + if (dma_cfg && ospi->dma_chtx) { | ||
351 | + if (dmaengine_slave_config(ospi->dma_chtx, dma_cfg)) { | ||
352 | + dev_err(ospi->dev, "dma tx config failed\n"); | ||
353 | + dma_release_channel(ospi->dma_chtx); | ||
354 | + ospi->dma_chtx = NULL; | ||
355 | + } | ||
356 | + } | ||
357 | + | ||
358 | + init_completion(&ospi->dma_completion); | ||
359 | +} | ||
360 | + | ||
361 | +static int stm32_ospi_tx_mm(struct stm32_ospi *ospi, | ||
362 | + const struct spi_mem_op *op) | ||
363 | +{ | ||
364 | + memcpy_fromio(op->data.buf.in, ospi->mm_base + op->addr.val, | ||
365 | + op->data.nbytes); | ||
366 | + return 0; | ||
367 | +} | ||
368 | + | ||
369 | +static int stm32_ospi_tx_dma(struct stm32_ospi *ospi, | ||
370 | + const struct spi_mem_op *op) | ||
371 | +{ | ||
372 | + struct dma_async_tx_descriptor *desc; | ||
373 | + void __iomem *regs_base = ospi->regs_base; | ||
374 | + enum dma_transfer_direction dma_dir; | ||
375 | + struct dma_chan *dma_ch; | ||
376 | + struct sg_table sgt; | ||
377 | + dma_cookie_t cookie; | ||
378 | + u32 cr, t_out; | ||
379 | + int err; | ||
380 | + | ||
381 | + if (op->data.dir == SPI_MEM_DATA_IN) { | ||
382 | + dma_dir = DMA_DEV_TO_MEM; | ||
383 | + dma_ch = ospi->dma_chrx; | ||
384 | + } else { | ||
385 | + dma_dir = DMA_MEM_TO_DEV; | ||
386 | + dma_ch = ospi->dma_chtx; | ||
387 | + } | ||
388 | + | ||
389 | + /* | ||
390 | + * Spi_map_buf return -EINVAL if the buffer is not DMA-able | ||
391 | + * (DMA-able: in vmalloc | kmap | virt_addr_valid) | ||
392 | + */ | ||
393 | + err = spi_controller_dma_map_mem_op_data(ospi->ctrl, op, &sgt); | ||
394 | + if (err) | ||
395 | + return err; | ||
396 | + | ||
397 | + desc = dmaengine_prep_slave_sg(dma_ch, sgt.sgl, sgt.nents, | ||
398 | + dma_dir, DMA_PREP_INTERRUPT); | ||
399 | + if (!desc) { | ||
400 | + err = -ENOMEM; | ||
401 | + goto out_unmap; | ||
402 | + } | ||
403 | + | ||
404 | + cr = readl_relaxed(regs_base + OSPI_CR); | ||
405 | + | ||
406 | + reinit_completion(&ospi->dma_completion); | ||
407 | + desc->callback = stm32_ospi_dma_callback; | ||
408 | + desc->callback_param = &ospi->dma_completion; | ||
409 | + cookie = dmaengine_submit(desc); | ||
410 | + err = dma_submit_error(cookie); | ||
411 | + if (err) | ||
412 | + goto out; | ||
413 | + | ||
414 | + dma_async_issue_pending(dma_ch); | ||
415 | + | ||
416 | + writel_relaxed(cr | CR_DMAEN, regs_base + OSPI_CR); | ||
417 | + | ||
418 | + t_out = sgt.nents * STM32_COMP_TIMEOUT_MS; | ||
419 | + if (!wait_for_completion_timeout(&ospi->dma_completion, | ||
420 | + msecs_to_jiffies(t_out))) | ||
421 | + err = -ETIMEDOUT; | ||
422 | + | ||
423 | + if (err) | ||
424 | + dmaengine_terminate_all(dma_ch); | ||
425 | + | ||
426 | +out: | ||
427 | + writel_relaxed(cr & ~CR_DMAEN, regs_base + OSPI_CR); | ||
428 | +out_unmap: | ||
429 | + spi_controller_dma_unmap_mem_op_data(ospi->ctrl, op, &sgt); | ||
430 | + | ||
431 | + return err; | ||
432 | +} | ||
433 | + | ||
434 | +static int stm32_ospi_xfer(struct stm32_ospi *ospi, const struct spi_mem_op *op) | ||
435 | +{ | ||
436 | + u8 *buf; | ||
437 | + | ||
438 | + if (!op->data.nbytes) | ||
439 | + return 0; | ||
440 | + | ||
441 | + if (ospi->fmode == CR_FMODE_MM) | ||
442 | + return stm32_ospi_tx_mm(ospi, op); | ||
443 | + else if (((op->data.dir == SPI_MEM_DATA_IN && ospi->dma_chrx) || | ||
444 | + (op->data.dir == SPI_MEM_DATA_OUT && ospi->dma_chtx)) && | ||
445 | + op->data.nbytes > 8) | ||
446 | + if (!stm32_ospi_tx_dma(ospi, op)) | ||
447 | + return 0; | ||
448 | + | ||
449 | + if (op->data.dir == SPI_MEM_DATA_IN) | ||
450 | + buf = op->data.buf.in; | ||
451 | + else | ||
452 | + buf = (u8 *)op->data.buf.out; | ||
453 | + | ||
454 | + return stm32_ospi_poll(ospi, buf, op->data.nbytes, | ||
455 | + op->data.dir == SPI_MEM_DATA_IN); | ||
456 | +} | ||
457 | + | ||
458 | +static int stm32_ospi_wait_poll_status(struct stm32_ospi *ospi, | ||
459 | + const struct spi_mem_op *op) | ||
460 | +{ | ||
461 | + void __iomem *regs_base = ospi->regs_base; | ||
462 | + u32 cr; | ||
463 | + | ||
464 | + reinit_completion(&ospi->match_completion); | ||
465 | + cr = readl_relaxed(regs_base + OSPI_CR); | ||
466 | + writel_relaxed(cr | CR_SMIE, regs_base + OSPI_CR); | ||
467 | + | ||
468 | + if (!wait_for_completion_timeout(&ospi->match_completion, | ||
469 | + msecs_to_jiffies(ospi->status_timeout))) { | ||
470 | + u32 sr = readl_relaxed(regs_base + OSPI_SR); | ||
471 | + | ||
472 | + /* Avoid false timeout */ | ||
473 | + if (!(sr & SR_SMF)) | ||
474 | + return -ETIMEDOUT; | ||
475 | + } | ||
476 | + | ||
477 | + writel_relaxed(FCR_CSMF, regs_base + OSPI_FCR); | ||
478 | + | ||
479 | + return 0; | ||
480 | +} | ||
481 | + | ||
482 | +static int stm32_ospi_get_mode(u8 buswidth) | ||
483 | +{ | ||
484 | + switch (buswidth) { | ||
485 | + case 8: | ||
486 | + return CCR_BUSWIDTH_8; | ||
487 | + case 4: | ||
488 | + return CCR_BUSWIDTH_4; | ||
489 | + default: | ||
490 | + return buswidth; | ||
491 | + } | ||
492 | +} | ||
493 | + | ||
494 | +static int stm32_ospi_send(struct spi_device *spi, const struct spi_mem_op *op) | ||
495 | +{ | ||
496 | + struct stm32_ospi *ospi = spi_controller_get_devdata(spi->controller); | ||
497 | + void __iomem *regs_base = ospi->regs_base; | ||
498 | + u32 ccr, cr, dcr2, tcr; | ||
499 | + int timeout, err = 0, err_poll_status = 0; | ||
500 | + u8 cs = spi->chip_select[ffs(spi->cs_index_mask) - 1]; | ||
501 | + | ||
502 | + dev_dbg(ospi->dev, "cmd:%#x mode:%d.%d.%d.%d addr:%#llx len:%#x\n", | ||
503 | + op->cmd.opcode, op->cmd.buswidth, op->addr.buswidth, | ||
504 | + op->dummy.buswidth, op->data.buswidth, | ||
505 | + op->addr.val, op->data.nbytes); | ||
506 | + | ||
507 | + cr = readl_relaxed(ospi->regs_base + OSPI_CR); | ||
508 | + cr &= ~CR_CSSEL; | ||
509 | + cr |= FIELD_PREP(CR_CSSEL, cs); | ||
510 | + cr &= ~CR_FMODE_MASK; | ||
511 | + cr |= FIELD_PREP(CR_FMODE_MASK, ospi->fmode); | ||
512 | + writel_relaxed(cr, regs_base + OSPI_CR); | ||
513 | + | ||
514 | + if (op->data.nbytes) | ||
515 | + writel_relaxed(op->data.nbytes - 1, regs_base + OSPI_DLR); | ||
516 | + | ||
517 | + /* set prescaler */ | ||
518 | + dcr2 = readl_relaxed(regs_base + OSPI_DCR2); | ||
519 | + dcr2 |= FIELD_PREP(DCR2_PRESC_MASK, ospi->flash_presc[cs]); | ||
520 | + writel_relaxed(dcr2, regs_base + OSPI_DCR2); | ||
521 | + | ||
522 | + ccr = FIELD_PREP(CCR_IMODE_MASK, stm32_ospi_get_mode(op->cmd.buswidth)); | ||
523 | + | ||
524 | + if (op->addr.nbytes) { | ||
525 | + ccr |= FIELD_PREP(CCR_ADMODE_MASK, | ||
526 | + stm32_ospi_get_mode(op->addr.buswidth)); | ||
527 | + ccr |= FIELD_PREP(CCR_ADSIZE_MASK, op->addr.nbytes - 1); | ||
528 | + } | ||
529 | + | ||
530 | + tcr = TCR_SSHIFT; | ||
531 | + if (op->dummy.buswidth && op->dummy.nbytes) { | ||
532 | + tcr |= FIELD_PREP(TCR_DCYC_MASK, | ||
533 | + op->dummy.nbytes * 8 / op->dummy.buswidth); | ||
534 | + } | ||
535 | + writel_relaxed(tcr, regs_base + OSPI_TCR); | ||
536 | + | ||
537 | + if (op->data.nbytes) { | ||
538 | + ccr |= FIELD_PREP(CCR_DMODE_MASK, | ||
539 | + stm32_ospi_get_mode(op->data.buswidth)); | ||
540 | + } | ||
541 | + | ||
542 | + writel_relaxed(ccr, regs_base + OSPI_CCR); | ||
543 | + | ||
544 | + /* set instruction, must be set after ccr register update */ | ||
545 | + writel_relaxed(op->cmd.opcode, regs_base + OSPI_IR); | ||
546 | + | ||
547 | + if (op->addr.nbytes && ospi->fmode != CR_FMODE_MM) | ||
548 | + writel_relaxed(op->addr.val, regs_base + OSPI_AR); | ||
549 | + | ||
550 | + if (ospi->fmode == CR_FMODE_APM) | ||
551 | + err_poll_status = stm32_ospi_wait_poll_status(ospi, op); | ||
552 | + | ||
553 | + err = stm32_ospi_xfer(ospi, op); | ||
554 | + | ||
555 | + /* | ||
556 | + * Abort in: | ||
557 | + * -error case | ||
558 | + * -read memory map: prefetching must be stopped if we read the last | ||
559 | + * byte of device (device size - fifo size). like device size is not | ||
560 | + * knows, the prefetching is always stop. | ||
561 | + */ | ||
562 | + if (err || err_poll_status || ospi->fmode == CR_FMODE_MM) | ||
563 | + goto abort; | ||
564 | + | ||
565 | + /* Wait end of tx in indirect mode */ | ||
566 | + err = stm32_ospi_wait_cmd(ospi); | ||
567 | + if (err) | ||
568 | + goto abort; | ||
569 | + | ||
570 | + return 0; | ||
571 | + | ||
572 | +abort: | ||
573 | + timeout = stm32_ospi_abort(ospi); | ||
574 | + writel_relaxed(FCR_CTCF | FCR_CSMF, regs_base + OSPI_FCR); | ||
575 | + | ||
576 | + if (err || err_poll_status || timeout) | ||
577 | + dev_err(ospi->dev, "%s err:%d err_poll_status:%d abort timeout:%d\n", | ||
578 | + __func__, err, err_poll_status, timeout); | ||
579 | + | ||
580 | + return err; | ||
581 | +} | ||
582 | + | ||
583 | +static int stm32_ospi_poll_status(struct spi_mem *mem, | ||
584 | + const struct spi_mem_op *op, | ||
585 | + u16 mask, u16 match, | ||
586 | + unsigned long initial_delay_us, | ||
587 | + unsigned long polling_rate_us, | ||
588 | + unsigned long timeout_ms) | ||
589 | +{ | ||
590 | + struct stm32_ospi *ospi = spi_controller_get_devdata(mem->spi->controller); | ||
591 | + void __iomem *regs_base = ospi->regs_base; | ||
592 | + int ret; | ||
593 | + | ||
594 | + ret = pm_runtime_resume_and_get(ospi->dev); | ||
595 | + if (ret < 0) | ||
596 | + return ret; | ||
597 | + | ||
598 | + mutex_lock(&ospi->lock); | ||
599 | + | ||
600 | + writel_relaxed(mask, regs_base + OSPI_PSMKR); | ||
601 | + writel_relaxed(match, regs_base + OSPI_PSMAR); | ||
602 | + ospi->fmode = CR_FMODE_APM; | ||
603 | + ospi->status_timeout = timeout_ms; | ||
604 | + | ||
605 | + ret = stm32_ospi_send(mem->spi, op); | ||
606 | + mutex_unlock(&ospi->lock); | ||
607 | + | ||
608 | + pm_runtime_mark_last_busy(ospi->dev); | ||
609 | + pm_runtime_put_autosuspend(ospi->dev); | ||
610 | + | ||
611 | + return ret; | ||
612 | +} | ||
613 | + | ||
614 | +static int stm32_ospi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) | ||
615 | +{ | ||
616 | + struct stm32_ospi *ospi = spi_controller_get_devdata(mem->spi->controller); | ||
617 | + int ret; | ||
618 | + | ||
619 | + ret = pm_runtime_resume_and_get(ospi->dev); | ||
620 | + if (ret < 0) | ||
621 | + return ret; | ||
622 | + | ||
623 | + mutex_lock(&ospi->lock); | ||
624 | + if (op->data.dir == SPI_MEM_DATA_IN && op->data.nbytes) | ||
625 | + ospi->fmode = CR_FMODE_INDR; | ||
626 | + else | ||
627 | + ospi->fmode = CR_FMODE_INDW; | ||
628 | + | ||
629 | + ret = stm32_ospi_send(mem->spi, op); | ||
630 | + mutex_unlock(&ospi->lock); | ||
631 | + | ||
632 | + pm_runtime_mark_last_busy(ospi->dev); | ||
633 | + pm_runtime_put_autosuspend(ospi->dev); | ||
634 | + | ||
635 | + return ret; | ||
636 | +} | ||
637 | + | ||
638 | +static int stm32_ospi_dirmap_create(struct spi_mem_dirmap_desc *desc) | ||
639 | +{ | ||
640 | + struct stm32_ospi *ospi = spi_controller_get_devdata(desc->mem->spi->controller); | ||
641 | + | ||
642 | + if (desc->info.op_tmpl.data.dir == SPI_MEM_DATA_OUT) | ||
643 | + return -EOPNOTSUPP; | ||
644 | + | ||
645 | + /* Should never happen, as mm_base == null is an error probe exit condition */ | ||
646 | + if (!ospi->mm_base && desc->info.op_tmpl.data.dir == SPI_MEM_DATA_IN) | ||
647 | + return -EOPNOTSUPP; | ||
648 | + | ||
649 | + if (!ospi->mm_size) | ||
650 | + return -EOPNOTSUPP; | ||
651 | + | ||
652 | + return 0; | ||
653 | +} | ||
654 | + | ||
655 | +static ssize_t stm32_ospi_dirmap_read(struct spi_mem_dirmap_desc *desc, | ||
656 | + u64 offs, size_t len, void *buf) | ||
657 | +{ | ||
658 | + struct stm32_ospi *ospi = spi_controller_get_devdata(desc->mem->spi->controller); | ||
659 | + struct spi_mem_op op; | ||
660 | + u32 addr_max; | ||
661 | + int ret; | ||
662 | + | ||
663 | + ret = pm_runtime_resume_and_get(ospi->dev); | ||
664 | + if (ret < 0) | ||
665 | + return ret; | ||
666 | + | ||
667 | + mutex_lock(&ospi->lock); | ||
668 | + /* | ||
669 | + * Make a local copy of desc op_tmpl and complete dirmap rdesc | ||
670 | + * spi_mem_op template with offs, len and *buf in order to get | ||
671 | + * all needed transfer information into struct spi_mem_op | ||
672 | + */ | ||
673 | + memcpy(&op, &desc->info.op_tmpl, sizeof(struct spi_mem_op)); | ||
674 | + dev_dbg(ospi->dev, "%s len = 0x%zx offs = 0x%llx buf = 0x%p\n", __func__, len, offs, buf); | ||
675 | + | ||
676 | + op.data.nbytes = len; | ||
677 | + op.addr.val = desc->info.offset + offs; | ||
678 | + op.data.buf.in = buf; | ||
679 | + | ||
680 | + addr_max = op.addr.val + op.data.nbytes + 1; | ||
681 | + if (addr_max < ospi->mm_size && op.addr.buswidth) | ||
682 | + ospi->fmode = CR_FMODE_MM; | ||
683 | + else | ||
684 | + ospi->fmode = CR_FMODE_INDR; | ||
685 | + | ||
686 | + ret = stm32_ospi_send(desc->mem->spi, &op); | ||
687 | + mutex_unlock(&ospi->lock); | ||
688 | + | ||
689 | + pm_runtime_mark_last_busy(ospi->dev); | ||
690 | + pm_runtime_put_autosuspend(ospi->dev); | ||
691 | + | ||
692 | + return ret ?: len; | ||
693 | +} | ||
694 | + | ||
695 | +static int stm32_ospi_transfer_one_message(struct spi_controller *ctrl, | ||
696 | + struct spi_message *msg) | ||
697 | +{ | ||
698 | + struct stm32_ospi *ospi = spi_controller_get_devdata(ctrl); | ||
699 | + struct spi_transfer *transfer; | ||
700 | + struct spi_device *spi = msg->spi; | ||
701 | + struct spi_mem_op op; | ||
702 | + struct gpio_desc *cs_gpiod = spi->cs_gpiod[ffs(spi->cs_index_mask) - 1]; | ||
703 | + int ret = 0; | ||
704 | + | ||
705 | + if (!cs_gpiod) | ||
706 | + return -EOPNOTSUPP; | ||
707 | + | ||
708 | + ret = pm_runtime_resume_and_get(ospi->dev); | ||
709 | + if (ret < 0) | ||
710 | + return ret; | ||
711 | + | ||
712 | + mutex_lock(&ospi->lock); | ||
713 | + | ||
714 | + gpiod_set_value_cansleep(cs_gpiod, true); | ||
715 | + | ||
716 | + list_for_each_entry(transfer, &msg->transfers, transfer_list) { | ||
717 | + u8 dummy_bytes = 0; | ||
718 | + | ||
719 | + memset(&op, 0, sizeof(op)); | ||
720 | + | ||
721 | + dev_dbg(ospi->dev, "tx_buf:%p tx_nbits:%d rx_buf:%p rx_nbits:%d len:%d dummy_data:%d\n", | ||
722 | + transfer->tx_buf, transfer->tx_nbits, | ||
723 | + transfer->rx_buf, transfer->rx_nbits, | ||
724 | + transfer->len, transfer->dummy_data); | ||
725 | + | ||
726 | + /* | ||
727 | + * OSPI hardware supports dummy bytes transfer. | ||
728 | + * If current transfer is dummy byte, merge it with the next | ||
729 | + * transfer in order to take into account OSPI block constraint | ||
730 | + */ | ||
731 | + if (transfer->dummy_data) { | ||
732 | + op.dummy.buswidth = transfer->tx_nbits; | ||
733 | + op.dummy.nbytes = transfer->len; | ||
734 | + dummy_bytes = transfer->len; | ||
735 | + | ||
736 | + /* If happens, means that message is not correctly built */ | ||
737 | + if (list_is_last(&transfer->transfer_list, &msg->transfers)) { | ||
738 | + ret = -EINVAL; | ||
739 | + goto end_of_transfer; | ||
740 | + } | ||
741 | + | ||
742 | + transfer = list_next_entry(transfer, transfer_list); | ||
743 | + } | ||
744 | + | ||
745 | + op.data.nbytes = transfer->len; | ||
746 | + | ||
747 | + if (transfer->rx_buf) { | ||
748 | + ospi->fmode = CR_FMODE_INDR; | ||
749 | + op.data.buswidth = transfer->rx_nbits; | ||
750 | + op.data.dir = SPI_MEM_DATA_IN; | ||
751 | + op.data.buf.in = transfer->rx_buf; | ||
752 | + } else { | ||
753 | + ospi->fmode = CR_FMODE_INDW; | ||
754 | + op.data.buswidth = transfer->tx_nbits; | ||
755 | + op.data.dir = SPI_MEM_DATA_OUT; | ||
756 | + op.data.buf.out = transfer->tx_buf; | ||
757 | + } | ||
758 | + | ||
759 | + ret = stm32_ospi_send(spi, &op); | ||
760 | + if (ret) | ||
761 | + goto end_of_transfer; | ||
762 | + | ||
763 | + msg->actual_length += transfer->len + dummy_bytes; | ||
764 | + } | ||
765 | + | ||
766 | +end_of_transfer: | ||
767 | + gpiod_set_value_cansleep(cs_gpiod, false); | ||
768 | + | ||
769 | + mutex_unlock(&ospi->lock); | ||
770 | + | ||
771 | + msg->status = ret; | ||
772 | + spi_finalize_current_message(ctrl); | ||
773 | + | ||
774 | + pm_runtime_mark_last_busy(ospi->dev); | ||
775 | + pm_runtime_put_autosuspend(ospi->dev); | ||
776 | + | ||
777 | + return ret; | ||
778 | +} | ||
779 | + | ||
780 | +static int stm32_ospi_setup(struct spi_device *spi) | ||
781 | +{ | ||
782 | + struct spi_controller *ctrl = spi->controller; | ||
783 | + struct stm32_ospi *ospi = spi_controller_get_devdata(ctrl); | ||
784 | + void __iomem *regs_base = ospi->regs_base; | ||
785 | + int ret; | ||
786 | + u8 cs = spi->chip_select[ffs(spi->cs_index_mask) - 1]; | ||
787 | + | ||
788 | + if (ctrl->busy) | ||
789 | + return -EBUSY; | ||
790 | + | ||
791 | + if (!spi->max_speed_hz) | ||
792 | + return -EINVAL; | ||
793 | + | ||
794 | + ret = pm_runtime_resume_and_get(ospi->dev); | ||
795 | + if (ret < 0) | ||
796 | + return ret; | ||
797 | + | ||
798 | + ospi->flash_presc[cs] = DIV_ROUND_UP(ospi->clk_rate, spi->max_speed_hz) - 1; | ||
799 | + | ||
800 | + mutex_lock(&ospi->lock); | ||
801 | + | ||
802 | + ospi->cr_reg = CR_APMS | 3 << CR_FTHRES_SHIFT | CR_EN; | ||
803 | + writel_relaxed(ospi->cr_reg, regs_base + OSPI_CR); | ||
804 | + | ||
805 | + /* set dcr fsize to max address */ | ||
806 | + ospi->dcr_reg = DCR1_DEVSIZE_MASK | DCR1_DLYBYP; | ||
807 | + writel_relaxed(ospi->dcr_reg, regs_base + OSPI_DCR1); | ||
808 | + | ||
809 | + mutex_unlock(&ospi->lock); | ||
810 | + | ||
811 | + pm_runtime_mark_last_busy(ospi->dev); | ||
812 | + pm_runtime_put_autosuspend(ospi->dev); | ||
813 | + | ||
814 | + return 0; | ||
815 | +} | ||
816 | + | ||
817 | +/* | ||
818 | + * No special host constraint, so use default spi_mem_default_supports_op | ||
819 | + * to check supported mode. | ||
820 | + */ | ||
821 | +static const struct spi_controller_mem_ops stm32_ospi_mem_ops = { | ||
822 | + .exec_op = stm32_ospi_exec_op, | ||
823 | + .dirmap_create = stm32_ospi_dirmap_create, | ||
824 | + .dirmap_read = stm32_ospi_dirmap_read, | ||
825 | + .poll_status = stm32_ospi_poll_status, | ||
826 | +}; | ||
827 | + | ||
828 | +static int stm32_ospi_get_resources(struct platform_device *pdev) | ||
829 | +{ | ||
830 | + struct device *dev = &pdev->dev; | ||
831 | + struct stm32_ospi *ospi = platform_get_drvdata(pdev); | ||
832 | + struct resource *res; | ||
833 | + struct reserved_mem *rmem = NULL; | ||
834 | + struct device_node *node; | ||
835 | + int ret; | ||
836 | + | ||
837 | + ospi->regs_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); | ||
838 | + if (IS_ERR(ospi->regs_base)) | ||
839 | + return PTR_ERR(ospi->regs_base); | ||
840 | + | ||
841 | + ospi->regs_phys_base = res->start; | ||
842 | + | ||
843 | + ospi->clk = devm_clk_get(dev, NULL); | ||
844 | + if (IS_ERR(ospi->clk)) | ||
845 | + return dev_err_probe(dev, PTR_ERR(ospi->clk), | ||
846 | + "Can't get clock\n"); | ||
847 | + | ||
848 | + ospi->clk_rate = clk_get_rate(ospi->clk); | ||
849 | + if (!ospi->clk_rate) { | ||
850 | + dev_err(dev, "Invalid clock rate\n"); | ||
851 | + return -EINVAL; | ||
852 | + } | ||
853 | + | ||
854 | + ospi->irq = platform_get_irq(pdev, 0); | ||
855 | + if (ospi->irq < 0) { | ||
856 | + dev_err(dev, "Can't get irq %d\n", ospi->irq); | ||
857 | + return ospi->irq; | ||
858 | + } | ||
859 | + | ||
860 | + ret = devm_request_irq(dev, ospi->irq, stm32_ospi_irq, 0, | ||
861 | + dev_name(dev), ospi); | ||
862 | + if (ret) { | ||
863 | + dev_err(dev, "Failed to request irq\n"); | ||
864 | + return ret; | ||
865 | + } | ||
866 | + | ||
867 | + ospi->rstc = devm_reset_control_array_get_optional_exclusive(dev); | ||
868 | + if (IS_ERR(ospi->rstc)) | ||
869 | + return dev_err_probe(dev, PTR_ERR(ospi->rstc), | ||
870 | + "Can't get reset\n"); | ||
871 | + | ||
872 | + ospi->dma_chrx = dma_request_chan(dev, "rx"); | ||
873 | + if (IS_ERR(ospi->dma_chrx)) { | ||
874 | + ret = PTR_ERR(ospi->dma_chrx); | ||
875 | + ospi->dma_chrx = NULL; | ||
876 | + if (ret == -EPROBE_DEFER) | ||
877 | + goto err_dma; | ||
878 | + } | ||
879 | + | ||
880 | + ospi->dma_chtx = dma_request_chan(dev, "tx"); | ||
881 | + if (IS_ERR(ospi->dma_chtx)) { | ||
882 | + ret = PTR_ERR(ospi->dma_chtx); | ||
883 | + ospi->dma_chtx = NULL; | ||
884 | + if (ret == -EPROBE_DEFER) | ||
885 | + goto err_dma; | ||
886 | + } | ||
887 | + | ||
888 | + node = of_parse_phandle(dev->of_node, "memory-region", 0); | ||
889 | + if (node) | ||
890 | + rmem = of_reserved_mem_lookup(node); | ||
891 | + of_node_put(node); | ||
892 | + | ||
893 | + if (rmem) { | ||
894 | + ospi->mm_size = rmem->size; | ||
895 | + ospi->mm_base = devm_ioremap(dev, rmem->base, rmem->size); | ||
896 | + if (IS_ERR(ospi->mm_base)) { | ||
897 | + dev_err(dev, "unable to map memory region: %pa+%pa\n", | ||
898 | + &rmem->base, &rmem->size); | ||
899 | + ret = PTR_ERR(ospi->mm_base); | ||
900 | + goto err_dma; | ||
901 | + } | ||
902 | + | ||
903 | + if (ospi->mm_size > STM32_OSPI_MAX_MMAP_SZ) { | ||
904 | + dev_err(dev, "Memory map size outsize bounds\n"); | ||
905 | + ret = -EINVAL; | ||
906 | + goto err_dma; | ||
907 | + } | ||
908 | + } else { | ||
909 | + dev_info(dev, "No memory-map region found\n"); | ||
910 | + } | ||
911 | + | ||
912 | + init_completion(&ospi->data_completion); | ||
913 | + init_completion(&ospi->match_completion); | ||
914 | + | ||
915 | + return 0; | ||
916 | + | ||
917 | +err_dma: | ||
918 | + dev_info(dev, "Can't get all resources (%d)\n", ret); | ||
919 | + | ||
920 | + if (ospi->dma_chtx) | ||
921 | + dma_release_channel(ospi->dma_chtx); | ||
922 | + if (ospi->dma_chrx) | ||
923 | + dma_release_channel(ospi->dma_chrx); | ||
924 | + | ||
925 | + return ret; | ||
926 | +}; | ||
927 | + | ||
928 | +static int stm32_ospi_probe(struct platform_device *pdev) | ||
929 | +{ | ||
930 | + struct device *dev = &pdev->dev; | ||
931 | + struct spi_controller *ctrl; | ||
932 | + struct stm32_ospi *ospi; | ||
933 | + struct dma_slave_config dma_cfg; | ||
934 | + struct device_node *child; | ||
935 | + int ret; | ||
936 | + u8 spi_flash_count = 0; | ||
937 | + | ||
938 | + /* | ||
939 | + * Flash subnodes sanity check: | ||
940 | + * 1 or 2 spi-nand/spi-nor flashes => supported | ||
941 | + * All other flash node configuration => not supported | ||
942 | + */ | ||
943 | + for_each_available_child_of_node(dev->of_node, child) { | ||
944 | + if (of_device_is_compatible(child, "jedec,spi-nor") || | ||
945 | + of_device_is_compatible(child, "spi-nand")) | ||
946 | + spi_flash_count++; | ||
947 | + } | ||
948 | + | ||
949 | + if (spi_flash_count == 0 || spi_flash_count > 2) { | ||
950 | + dev_err(dev, "Incorrect DT flash node\n"); | ||
951 | + return -ENODEV; | ||
952 | + } | ||
953 | + | ||
954 | + ctrl = devm_spi_alloc_host(dev, sizeof(*ospi)); | ||
955 | + if (!ctrl) | ||
956 | + return -ENOMEM; | ||
957 | + | ||
958 | + ospi = spi_controller_get_devdata(ctrl); | ||
959 | + ospi->ctrl = ctrl; | ||
960 | + | ||
961 | + ospi->dev = &pdev->dev; | ||
962 | + platform_set_drvdata(pdev, ospi); | ||
963 | + | ||
964 | + ret = stm32_ospi_get_resources(pdev); | ||
965 | + if (ret) | ||
966 | + return ret; | ||
967 | + | ||
968 | + memset(&dma_cfg, 0, sizeof(dma_cfg)); | ||
969 | + dma_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; | ||
970 | + dma_cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; | ||
971 | + dma_cfg.src_addr = ospi->regs_phys_base + OSPI_DR; | ||
972 | + dma_cfg.dst_addr = ospi->regs_phys_base + OSPI_DR; | ||
973 | + dma_cfg.src_maxburst = 4; | ||
974 | + dma_cfg.dst_maxburst = 4; | ||
975 | + stm32_ospi_dma_setup(ospi, &dma_cfg); | ||
976 | + | ||
977 | + mutex_init(&ospi->lock); | ||
978 | + | ||
979 | + ctrl->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD | | ||
980 | + SPI_TX_DUAL | SPI_TX_QUAD | | ||
981 | + SPI_TX_OCTAL | SPI_RX_OCTAL; | ||
982 | + ctrl->flags = SPI_CONTROLLER_HALF_DUPLEX; | ||
983 | + ctrl->setup = stm32_ospi_setup; | ||
984 | + ctrl->bus_num = -1; | ||
985 | + ctrl->mem_ops = &stm32_ospi_mem_ops; | ||
986 | + ctrl->use_gpio_descriptors = true; | ||
987 | + ctrl->transfer_one_message = stm32_ospi_transfer_one_message; | ||
988 | + ctrl->num_chipselect = STM32_OSPI_MAX_NORCHIP; | ||
989 | + ctrl->dev.of_node = dev->of_node; | ||
990 | + | ||
991 | + pm_runtime_enable(ospi->dev); | ||
992 | + pm_runtime_set_autosuspend_delay(ospi->dev, STM32_AUTOSUSPEND_DELAY); | ||
993 | + pm_runtime_use_autosuspend(ospi->dev); | ||
994 | + | ||
995 | + ret = pm_runtime_resume_and_get(ospi->dev); | ||
996 | + if (ret < 0) | ||
997 | + goto err_pm_enable; | ||
998 | + | ||
999 | + if (ospi->rstc) { | ||
1000 | + reset_control_assert(ospi->rstc); | ||
1001 | + udelay(2); | ||
1002 | + reset_control_deassert(ospi->rstc); | ||
1003 | + } | ||
1004 | + | ||
1005 | + ret = spi_register_controller(ctrl); | ||
1006 | + if (ret) { | ||
1007 | + /* Disable ospi */ | ||
1008 | + writel_relaxed(0, ospi->regs_base + OSPI_CR); | ||
1009 | + goto err_pm_resume; | ||
1010 | + } | ||
1011 | + | ||
1012 | + pm_runtime_mark_last_busy(ospi->dev); | ||
1013 | + pm_runtime_put_autosuspend(ospi->dev); | ||
1014 | + | ||
1015 | + return 0; | ||
1016 | + | ||
1017 | +err_pm_resume: | ||
1018 | + pm_runtime_put_sync_suspend(ospi->dev); | ||
1019 | + | ||
1020 | +err_pm_enable: | ||
1021 | + pm_runtime_force_suspend(ospi->dev); | ||
1022 | + mutex_destroy(&ospi->lock); | ||
1023 | + | ||
1024 | + return ret; | ||
1025 | +} | ||
1026 | + | ||
1027 | +static void stm32_ospi_remove(struct platform_device *pdev) | ||
1028 | +{ | ||
1029 | + struct stm32_ospi *ospi = platform_get_drvdata(pdev); | ||
1030 | + int ret; | ||
1031 | + | ||
1032 | + ret = pm_runtime_resume_and_get(ospi->dev); | ||
1033 | + if (ret < 0) | ||
1034 | + return; | ||
1035 | + | ||
1036 | + spi_unregister_controller(ospi->ctrl); | ||
1037 | + /* Disable ospi */ | ||
1038 | + writel_relaxed(0, ospi->regs_base + OSPI_CR); | ||
1039 | + mutex_destroy(&ospi->lock); | ||
1040 | + | ||
1041 | + if (ospi->dma_chtx) | ||
1042 | + dma_release_channel(ospi->dma_chtx); | ||
1043 | + if (ospi->dma_chrx) | ||
1044 | + dma_release_channel(ospi->dma_chrx); | ||
1045 | + | ||
1046 | + pm_runtime_put_sync_suspend(ospi->dev); | ||
1047 | + pm_runtime_force_suspend(ospi->dev); | ||
1048 | +} | ||
1049 | + | ||
1050 | +static int __maybe_unused stm32_ospi_suspend(struct device *dev) | ||
1051 | +{ | ||
1052 | + struct stm32_ospi *ospi = dev_get_drvdata(dev); | ||
1053 | + | ||
1054 | + pinctrl_pm_select_sleep_state(dev); | ||
1055 | + | ||
1056 | + return pm_runtime_force_suspend(ospi->dev); | ||
1057 | +} | ||
1058 | + | ||
1059 | +static int __maybe_unused stm32_ospi_resume(struct device *dev) | ||
1060 | +{ | ||
1061 | + struct stm32_ospi *ospi = dev_get_drvdata(dev); | ||
1062 | + void __iomem *regs_base = ospi->regs_base; | ||
1063 | + int ret; | ||
1064 | + | ||
1065 | + ret = pm_runtime_force_resume(ospi->dev); | ||
1066 | + if (ret < 0) | ||
1067 | + return ret; | ||
1068 | + | ||
1069 | + pinctrl_pm_select_default_state(dev); | ||
1070 | + | ||
1071 | + ret = pm_runtime_resume_and_get(ospi->dev); | ||
1072 | + if (ret < 0) | ||
1073 | + return ret; | ||
1074 | + | ||
1075 | + writel_relaxed(ospi->cr_reg, regs_base + OSPI_CR); | ||
1076 | + writel_relaxed(ospi->dcr_reg, regs_base + OSPI_DCR1); | ||
1077 | + pm_runtime_mark_last_busy(ospi->dev); | ||
1078 | + pm_runtime_put_autosuspend(ospi->dev); | ||
1079 | + | ||
1080 | + return 0; | ||
1081 | +} | ||
1082 | + | ||
1083 | +static int __maybe_unused stm32_ospi_runtime_suspend(struct device *dev) | ||
1084 | +{ | ||
1085 | + struct stm32_ospi *ospi = dev_get_drvdata(dev); | ||
1086 | + | ||
1087 | + clk_disable_unprepare(ospi->clk); | ||
1088 | + | ||
1089 | + return 0; | ||
1090 | +} | ||
1091 | + | ||
1092 | +static int __maybe_unused stm32_ospi_runtime_resume(struct device *dev) | ||
1093 | +{ | ||
1094 | + struct stm32_ospi *ospi = dev_get_drvdata(dev); | ||
1095 | + | ||
1096 | + return clk_prepare_enable(ospi->clk); | ||
1097 | +} | ||
1098 | + | ||
1099 | +static const struct dev_pm_ops stm32_ospi_pm_ops = { | ||
1100 | + SET_SYSTEM_SLEEP_PM_OPS(stm32_ospi_suspend, stm32_ospi_resume) | ||
1101 | + SET_RUNTIME_PM_OPS(stm32_ospi_runtime_suspend, | ||
1102 | + stm32_ospi_runtime_resume, NULL) | ||
1103 | +}; | ||
1104 | + | ||
1105 | +static const struct of_device_id stm32_ospi_of_match[] = { | ||
1106 | + { .compatible = "st,stm32mp25-ospi" }, | ||
1107 | + {}, | ||
1108 | +}; | ||
1109 | +MODULE_DEVICE_TABLE(of, stm32_ospi_of_match); | ||
1110 | + | ||
1111 | +static struct platform_driver stm32_ospi_driver = { | ||
1112 | + .probe = stm32_ospi_probe, | ||
1113 | + .remove = stm32_ospi_remove, | ||
1114 | + .driver = { | ||
1115 | + .name = "stm32-ospi", | ||
1116 | + .pm = &stm32_ospi_pm_ops, | ||
1117 | + .of_match_table = stm32_ospi_of_match, | ||
1118 | + }, | ||
1119 | +}; | ||
1120 | +module_platform_driver(stm32_ospi_driver); | ||
1121 | + | ||
1122 | +MODULE_DESCRIPTION("STMicroelectronics STM32 OCTO SPI driver"); | ||
1123 | +MODULE_LICENSE("GPL"); | ||
1124 | -- | 26 | -- |
1125 | 2.25.1 | 27 | 2.25.1 | diff view generated by jsdifflib |
1 | From: Patrice Chotard <patrice.chotard@foss.st.com> | ||
---|---|---|---|
2 | |||
3 | Add bindings for STM32 Octo Memory Manager (OMM) controller. | 1 | Add bindings for STM32 Octo Memory Manager (OMM) controller. |
4 | 2 | ||
5 | OMM manages: | 3 | OMM manages: |
6 | - the muxing between 2 OSPI busses and 2 output ports. | 4 | - the muxing between 2 OSPI busses and 2 output ports. |
7 | There are 4 possible muxing configurations: | 5 | There are 4 possible muxing configurations: |
... | ... | ||
14 | - the split of the memory area shared between the 2 OSPI instances. | 12 | - the split of the memory area shared between the 2 OSPI instances. |
15 | - chip select selection override. | 13 | - chip select selection override. |
16 | - the time between 2 transactions in multiplexed mode. | 14 | - the time between 2 transactions in multiplexed mode. |
17 | 15 | ||
18 | Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com> | 16 | Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com> |
19 | Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> | ||
20 | --- | 17 | --- |
21 | .../memory-controllers/st,stm32mp25-omm.yaml | 204 ++++++++++++++++++ | 18 | .../memory-controllers/st,stm32mp25-omm.yaml | 227 +++++++++++++++++++++ |
22 | 1 file changed, 204 insertions(+) | 19 | 1 file changed, 227 insertions(+) |
23 | create mode 100644 Documentation/devicetree/bindings/memory-controllers/st,stm32mp25-omm.yaml | ||
24 | 20 | ||
25 | diff --git a/Documentation/devicetree/bindings/memory-controllers/st,stm32mp25-omm.yaml b/Documentation/devicetree/bindings/memory-controllers/st,stm32mp25-omm.yaml | 21 | diff --git a/Documentation/devicetree/bindings/memory-controllers/st,stm32mp25-omm.yaml b/Documentation/devicetree/bindings/memory-controllers/st,stm32mp25-omm.yaml |
26 | new file mode 100644 | 22 | new file mode 100644 |
27 | index XXXXXXX..XXXXXXX | 23 | index XXXXXXX..XXXXXXX |
28 | --- /dev/null | 24 | --- /dev/null |
... | ... | ||
57 | + "#size-cells": | 53 | + "#size-cells": |
58 | + const: 1 | 54 | + const: 1 |
59 | + | 55 | + |
60 | + ranges: | 56 | + ranges: |
61 | + description: | | 57 | + description: | |
62 | + Reflects the memory layout with four integer values per OSPI instance. | 58 | + Reflects the memory layout per OSPI instance. |
63 | + Format: | 59 | + Format: |
64 | + <chip-select> 0 <registers base address> <size> | 60 | + <chip-select> 0 <registers base address> <size> |
65 | + minItems: 2 | 61 | + minItems: 2 |
66 | + maxItems: 2 | 62 | + maxItems: 2 |
67 | + | 63 | + |
... | ... | ||
74 | + items: | 70 | + items: |
75 | + - const: regs | 71 | + - const: regs |
76 | + - const: memory_map | 72 | + - const: memory_map |
77 | + | 73 | + |
78 | + memory-region: | 74 | + memory-region: |
79 | + description: | | 75 | + description: |
80 | + Memory region shared between the 2 OCTOSPI instance. | 76 | + Memory region shared between the 2 OCTOSPI instance. |
81 | + One or two phandle to a node describing a memory mapped region | 77 | + One or two phandle to a node describing a memory mapped region |
82 | + depending of child number. | 78 | + depending of child number. |
83 | + minItems: 1 | 79 | + minItems: 1 |
84 | + maxItems: 2 | 80 | + maxItems: 2 |
85 | + | 81 | + |
86 | + memory-region-names: | 82 | + memory-region-names: |
87 | + description: | | 83 | + description: |
88 | + OCTOSPI instance's name to which memory region is associated | 84 | + Identify to which OSPI instance the memory region belongs to. |
89 | + items: | 85 | + items: |
90 | + enum: [ospi1, ospi2] | 86 | + enum: [ospi1, ospi2] |
91 | + minItems: 1 | 87 | + minItems: 1 |
92 | + maxItems: 2 | 88 | + maxItems: 2 |
93 | + | 89 | + |
94 | + clocks: | 90 | + clocks: |
95 | + maxItems: 1 | 91 | + minItems: 3 |
92 | + maxItems: 3 | ||
93 | + | ||
94 | + clock-names: | ||
95 | + items: | ||
96 | + - const: omm | ||
97 | + - const: ospi1 | ||
98 | + - const: ospi2 | ||
96 | + | 99 | + |
97 | + resets: | 100 | + resets: |
98 | + maxItems: 1 | 101 | + minItems: 3 |
102 | + maxItems: 3 | ||
103 | + | ||
104 | + reset-names: | ||
105 | + items: | ||
106 | + - const: omm | ||
107 | + - const: ospi1 | ||
108 | + - const: ospi2 | ||
99 | + | 109 | + |
100 | + access-controllers: | 110 | + access-controllers: |
101 | + maxItems: 1 | 111 | + maxItems: 1 |
102 | + | 112 | + |
103 | + st,syscfg-amcr: | 113 | + st,syscfg-amcr: |
... | ... | ||
116 | + - description: phandle to syscfg | 126 | + - description: phandle to syscfg |
117 | + - description: register offset within syscfg | 127 | + - description: register offset within syscfg |
118 | + - description: register bitmask for memory split | 128 | + - description: register bitmask for memory split |
119 | + | 129 | + |
120 | + st,omm-req2ack-ns: | 130 | + st,omm-req2ack-ns: |
121 | + description: | | 131 | + description: |
122 | + In multiplexed mode (MUXEN = 1), this field defines the time in | 132 | + In multiplexed mode (MUXEN = 1), this field defines the time in |
123 | + nanoseconds between two transactions. | 133 | + nanoseconds between two transactions. |
124 | + default: 0 | 134 | + default: 0 |
125 | + | 135 | + |
126 | + st,omm-cssel-ovr: | 136 | + st,omm-cssel-ovr: |
... | ... | ||
149 | + | 159 | + |
150 | + power-domains: | 160 | + power-domains: |
151 | + maxItems: 1 | 161 | + maxItems: 1 |
152 | + | 162 | + |
153 | +patternProperties: | 163 | +patternProperties: |
154 | + ^spi@[a-f0-9]+$: | 164 | + ^spi@[0-9]: |
155 | + type: object | 165 | + type: object |
156 | + $ref: /schemas/spi/st,stm32mp25-ospi.yaml# | 166 | + $ref: /schemas/spi/st,stm32mp25-ospi.yaml# |
157 | + description: Required spi child node | 167 | + description: Required spi child node |
158 | + | 168 | + |
159 | +required: | 169 | +required: |
160 | + - compatible | 170 | + - compatible |
161 | + - reg | 171 | + - reg |
162 | + - "#address-cells" | 172 | + - "#address-cells" |
163 | + - "#size-cells" | 173 | + - "#size-cells" |
164 | + - clocks | 174 | + - clocks |
175 | + - clock-names | ||
176 | + - resets | ||
177 | + - reset-names | ||
165 | + - st,syscfg-amcr | 178 | + - st,syscfg-amcr |
166 | + - ranges | 179 | + - ranges |
167 | + | 180 | + |
168 | +additionalProperties: false | 181 | +additionalProperties: false |
169 | + | 182 | + |
... | ... | ||
185 | + &ospi_port1_cs0_pins_a>; | 198 | + &ospi_port1_cs0_pins_a>; |
186 | + pinctrl-1 = <&ospi_port1_clk_sleep_pins_a | 199 | + pinctrl-1 = <&ospi_port1_clk_sleep_pins_a |
187 | + &ospi_port1_io03_sleep_pins_a | 200 | + &ospi_port1_io03_sleep_pins_a |
188 | + &ospi_port1_cs0_sleep_pins_a>; | 201 | + &ospi_port1_cs0_sleep_pins_a>; |
189 | + pinctrl-names = "default", "sleep"; | 202 | + pinctrl-names = "default", "sleep"; |
190 | + clocks = <&rcc CK_BUS_OSPIIOM>; | 203 | + clocks = <&rcc CK_BUS_OSPIIOM>, |
191 | + resets = <&rcc OSPIIOM_R>; | 204 | + <&scmi_clk CK_SCMI_OSPI1>, |
205 | + <&scmi_clk CK_SCMI_OSPI2>; | ||
206 | + clock-names = "omm", "ospi1", "ospi2"; | ||
207 | + resets = <&rcc OSPIIOM_R>, | ||
208 | + <&scmi_reset RST_SCMI_OSPI1>, | ||
209 | + <&scmi_reset RST_SCMI_OSPI2>; | ||
210 | + reset-names = "omm", "ospi1", "ospi2"; | ||
192 | + access-controllers = <&rifsc 111>; | 211 | + access-controllers = <&rifsc 111>; |
193 | + power-domains = <&CLUSTER_PD>; | 212 | + power-domains = <&CLUSTER_PD>; |
194 | + #address-cells = <2>; | 213 | + #address-cells = <2>; |
195 | + #size-cells = <1>; | 214 | + #size-cells = <1>; |
196 | + st,syscfg-amcr = <&syscfg 0x2c00 0x7>; | 215 | + st,syscfg-amcr = <&syscfg 0x2c00 0x7>; |
197 | + st,omm-req2ack-ns = <0>; | 216 | + st,omm-req2ack-ns = <0>; |
198 | + st,omm-mux = <0>; | 217 | + st,omm-mux = <0>; |
199 | + st,omm-cssel-ovr = <0>; | 218 | + st,omm-cssel-ovr = <0>; |
200 | + | 219 | + |
201 | + spi@40430000 { | 220 | + spi@0 { |
202 | + compatible = "st,stm32mp25-ospi"; | 221 | + compatible = "st,stm32mp25-ospi"; |
203 | + reg = <0 0 0x400>; | 222 | + reg = <0 0 0x400>; |
204 | + memory-region = <&mm_ospi1>; | 223 | + memory-region = <&mm_ospi1>; |
205 | + interrupts = <GIC_SPI 163 IRQ_TYPE_LEVEL_HIGH>; | 224 | + interrupts = <GIC_SPI 163 IRQ_TYPE_LEVEL_HIGH>; |
206 | + dmas = <&hpdma 2 0x62 0x00003121 0x0>, | 225 | + dmas = <&hpdma 2 0x62 0x00003121 0x0>, |
... | ... | ||
213 | + #address-cells = <1>; | 232 | + #address-cells = <1>; |
214 | + #size-cells = <0>; | 233 | + #size-cells = <0>; |
215 | + st,syscfg-dlyb = <&syscfg 0x1000>; | 234 | + st,syscfg-dlyb = <&syscfg 0x1000>; |
216 | + }; | 235 | + }; |
217 | + | 236 | + |
218 | + spi@40440000 { | 237 | + spi@1 { |
219 | + compatible = "st,stm32mp25-ospi"; | 238 | + compatible = "st,stm32mp25-ospi"; |
220 | + reg = <1 0 0x400>; | 239 | + reg = <1 0 0x400>; |
221 | + memory-region = <&mm_ospi1>; | 240 | + memory-region = <&mm_ospi1>; |
222 | + interrupts = <GIC_SPI 164 IRQ_TYPE_LEVEL_HIGH>; | 241 | + interrupts = <GIC_SPI 164 IRQ_TYPE_LEVEL_HIGH>; |
223 | + dmas = <&hpdma 3 0x62 0x00003121 0x0>, | 242 | + dmas = <&hpdma 3 0x62 0x00003121 0x0>, |
... | ... | ||
230 | + #address-cells = <1>; | 249 | + #address-cells = <1>; |
231 | + #size-cells = <0>; | 250 | + #size-cells = <0>; |
232 | + st,syscfg-dlyb = <&syscfg 0x1000>; | 251 | + st,syscfg-dlyb = <&syscfg 0x1000>; |
233 | + }; | 252 | + }; |
234 | + }; | 253 | + }; |
254 | |||
235 | -- | 255 | -- |
236 | 2.25.1 | 256 | 2.25.1 | diff view generated by jsdifflib |
1 | From: Patrice Chotard <patrice.chotard@foss.st.com> | ||
---|---|---|---|
2 | |||
3 | Octo Memory Manager driver (OMM) manages: | 1 | Octo Memory Manager driver (OMM) manages: |
4 | - the muxing between 2 OSPI busses and 2 output ports. | 2 | - the muxing between 2 OSPI busses and 2 output ports. |
5 | There are 4 possible muxing configurations: | 3 | There are 4 possible muxing configurations: |
6 | - direct mode (no multiplexing): OSPI1 output is on port 1 and OSPI2 | 4 | - direct mode (no multiplexing): OSPI1 output is on port 1 and OSPI2 |
7 | output is on port 2 | 5 | output is on port 2 |
... | ... | ||
12 | - the split of the memory area shared between the 2 OSPI instances. | 10 | - the split of the memory area shared between the 2 OSPI instances. |
13 | - chip select selection override. | 11 | - chip select selection override. |
14 | - the time between 2 transactions in multiplexed mode. | 12 | - the time between 2 transactions in multiplexed mode. |
15 | - check firewall access. | 13 | - check firewall access. |
16 | 14 | ||
15 | Signed-off-by: Christophe Kerello <christophe.kerello@foss.st.com> | ||
17 | Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com> | 16 | Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com> |
18 | Signed-off-by: Christophe Kerello <christophe.kerello@foss.st.com> | ||
19 | --- | 17 | --- |
20 | drivers/memory/Kconfig | 17 ++ | 18 | drivers/memory/Kconfig | 17 ++ |
21 | drivers/memory/Makefile | 1 + | 19 | drivers/memory/Makefile | 1 + |
22 | drivers/memory/stm32_omm.c | 522 +++++++++++++++++++++++++++++++++++++ | 20 | drivers/memory/stm32_omm.c | 474 +++++++++++++++++++++++++++++++++++++++++++++ |
23 | 3 files changed, 540 insertions(+) | 21 | 3 files changed, 492 insertions(+) |
24 | create mode 100644 drivers/memory/stm32_omm.c | ||
25 | 22 | ||
26 | diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig | 23 | diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig |
27 | index XXXXXXX..XXXXXXX 100644 | 24 | index XXXXXXX..XXXXXXX 100644 |
28 | --- a/drivers/memory/Kconfig | 25 | --- a/drivers/memory/Kconfig |
29 | +++ b/drivers/memory/Kconfig | 26 | +++ b/drivers/memory/Kconfig |
... | ... | ||
95 | +#define CR_CSSEL_OVR_EN BIT(4) | 92 | +#define CR_CSSEL_OVR_EN BIT(4) |
96 | +#define CR_CSSEL_OVR_MASK GENMASK(6, 5) | 93 | +#define CR_CSSEL_OVR_MASK GENMASK(6, 5) |
97 | +#define CR_REQ2ACK_MASK GENMASK(23, 16) | 94 | +#define CR_REQ2ACK_MASK GENMASK(23, 16) |
98 | + | 95 | + |
99 | +#define OMM_CHILD_NB 2 | 96 | +#define OMM_CHILD_NB 2 |
100 | + | 97 | +#define OMM_CLK_NB 3 |
101 | +struct ospi_child { | ||
102 | + struct device *dev; | ||
103 | + struct device_node *node; | ||
104 | + struct clk *clk; | ||
105 | +}; | ||
106 | + | 98 | + |
107 | +struct stm32_omm { | 99 | +struct stm32_omm { |
108 | + struct ospi_child child[OMM_CHILD_NB]; | ||
109 | + struct resource *mm_res; | 100 | + struct resource *mm_res; |
110 | + struct clk *clk; | 101 | + struct clk_bulk_data clk_bulk[OMM_CLK_NB]; |
111 | + void __iomem *io_base; | 102 | + void __iomem *io_base; |
112 | + u32 cr; | 103 | + u32 cr; |
113 | + u8 nb_child; | 104 | + u8 nb_child; |
114 | + bool restore_omm; | 105 | + bool restore_omm; |
115 | +}; | 106 | +}; |
116 | + | 107 | + |
117 | +static int stm32_omm_set_amcr(struct device *dev, bool set) | 108 | +static int stm32_omm_set_amcr(struct device *dev, bool set) |
118 | +{ | 109 | +{ |
119 | + struct stm32_omm *omm = dev_get_drvdata(dev); | 110 | + struct stm32_omm *omm = dev_get_drvdata(dev); |
111 | + resource_size_t mm_ospi2_size = 0; | ||
112 | + static const char * const mm_name[] = { "ospi1", "ospi2" }; | ||
120 | + struct regmap *syscfg_regmap; | 113 | + struct regmap *syscfg_regmap; |
121 | + struct device_node *node; | 114 | + struct device_node *node; |
122 | + struct resource res, res1; | 115 | + struct resource res, res1; |
123 | + resource_size_t mm_ospi2_size = 0; | ||
124 | + static const char * const mm_name[] = { "ospi1", "ospi2" }; | ||
125 | + u32 amcr_base, amcr_mask; | 116 | + u32 amcr_base, amcr_mask; |
126 | + int ret, idx; | 117 | + int ret, idx; |
127 | + unsigned int i, amcr, read_amcr; | 118 | + unsigned int i, amcr, read_amcr; |
128 | + | 119 | + |
129 | + for (i = 0; i < omm->nb_child; i++) { | 120 | + for (i = 0; i < omm->nb_child; i++) { |
... | ... | ||
169 | + } | 160 | + } |
170 | + } | 161 | + } |
171 | + } | 162 | + } |
172 | + | 163 | + |
173 | + syscfg_regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "st,syscfg-amcr"); | 164 | + syscfg_regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "st,syscfg-amcr"); |
174 | + if (IS_ERR(syscfg_regmap)) { | 165 | + if (IS_ERR(syscfg_regmap)) |
175 | + dev_err(dev, "Failed to get st,syscfg-amcr property\n"); | 166 | + return dev_err_probe(dev, PTR_ERR(syscfg_regmap), |
176 | + return PTR_ERR(syscfg_regmap); | 167 | + "Failed to get st,syscfg-amcr property\n"); |
177 | + } | ||
178 | + | 168 | + |
179 | + ret = of_property_read_u32_index(dev->of_node, "st,syscfg-amcr", 1, | 169 | + ret = of_property_read_u32_index(dev->of_node, "st,syscfg-amcr", 1, |
180 | + &amcr_base); | 170 | + &amcr_base); |
181 | + if (ret) | 171 | + if (ret) |
182 | + return ret; | 172 | + return ret; |
... | ... | ||
201 | + } | 191 | + } |
202 | + | 192 | + |
203 | + return ret; | 193 | + return ret; |
204 | +} | 194 | +} |
205 | + | 195 | + |
206 | +static int stm32_omm_enable_child_clock(struct device *dev, bool enable) | 196 | +static int stm32_omm_toggle_child_clock(struct device *dev, bool enable) |
207 | +{ | 197 | +{ |
208 | + /* As there is only 2 children, remember first child in case of error */ | 198 | + /* As there is only 2 children, remember first child in case of error */ |
209 | + struct clk *first_child_clk = NULL; | 199 | + struct clk *first_child_clk = NULL; |
210 | + struct stm32_omm *omm = dev_get_drvdata(dev); | 200 | + struct stm32_omm *omm = dev_get_drvdata(dev); |
211 | + u8 i; | 201 | + u8 i; |
212 | + int ret; | 202 | + int ret; |
213 | + | 203 | + |
214 | + for (i = 0; i < omm->nb_child; i++) { | 204 | + for (i = 0; i < omm->nb_child; i++) { |
215 | + if (enable) { | 205 | + if (enable) { |
216 | + ret = clk_prepare_enable(omm->child[i].clk); | 206 | + ret = clk_prepare_enable(omm->clk_bulk[i + 1].clk); |
217 | + if (ret) { | 207 | + if (ret) { |
218 | + if (first_child_clk) | 208 | + if (first_child_clk) |
219 | + clk_disable_unprepare(first_child_clk); | 209 | + clk_disable_unprepare(first_child_clk); |
220 | + | 210 | + |
221 | + dev_err(dev, "Can not enable clock\n"); | 211 | + dev_err(dev, "Can not enable clock\n"); |
222 | + return ret; | 212 | + return ret; |
223 | + } | 213 | + } |
224 | + } else { | 214 | + } else { |
225 | + clk_disable_unprepare(omm->child[i].clk); | 215 | + clk_disable_unprepare(omm->clk_bulk[i + 1].clk); |
226 | + } | 216 | + } |
227 | + | 217 | + |
228 | + first_child_clk = omm->child[i].clk; | 218 | + first_child_clk = omm->clk_bulk[i + 1].clk; |
229 | + } | 219 | + } |
230 | + | 220 | + |
231 | + return 0; | 221 | + return 0; |
232 | +} | 222 | +} |
233 | + | 223 | + |
234 | +static int stm32_omm_configure(struct device *dev) | 224 | +static int stm32_omm_disable_child(struct device *dev) |
235 | +{ | 225 | +{ |
236 | + struct stm32_omm *omm = dev_get_drvdata(dev); | 226 | + static const char * const resets_name[] = {"ospi1", "ospi2"}; |
237 | + struct reset_control *rstc; | 227 | + struct stm32_omm *omm = dev_get_drvdata(dev); |
238 | + unsigned long clk_rate, clk_rate_max = 0; | 228 | + struct reset_control *reset; |
239 | + int ret; | 229 | + int ret; |
240 | + u8 i; | 230 | + u8 i; |
231 | + | ||
232 | + ret = stm32_omm_toggle_child_clock(dev, true); | ||
233 | + if (!ret) | ||
234 | + return ret; | ||
235 | + | ||
236 | + for (i = 0; i < omm->nb_child; i++) { | ||
237 | + reset = reset_control_get_exclusive(dev, resets_name[i]); | ||
238 | + if (IS_ERR(reset)) { | ||
239 | + dev_err(dev, "Can't get %s reset\n", resets_name[i]); | ||
240 | + return PTR_ERR(reset); | ||
241 | + }; | ||
242 | + | ||
243 | + /* reset OSPI to ensure CR_EN bit is set to 0 */ | ||
244 | + reset_control_assert(reset); | ||
245 | + udelay(2); | ||
246 | + reset_control_deassert(reset); | ||
247 | + | ||
248 | + reset_control_put(reset); | ||
249 | + } | ||
250 | + | ||
251 | + return stm32_omm_toggle_child_clock(dev, false); | ||
252 | +} | ||
253 | + | ||
254 | +static int stm32_omm_configure(struct device *dev) | ||
255 | +{ | ||
256 | + static const char * const clocks_name[] = {"omm", "ospi1", "ospi2"}; | ||
257 | + struct stm32_omm *omm = dev_get_drvdata(dev); | ||
258 | + unsigned long clk_rate_max = 0; | ||
241 | + u32 mux = 0; | 259 | + u32 mux = 0; |
242 | + u32 cssel_ovr = 0; | 260 | + u32 cssel_ovr = 0; |
243 | + u32 req2ack = 0; | 261 | + u32 req2ack = 0; |
244 | + | 262 | + struct reset_control *rstc; |
245 | + omm->clk = devm_clk_get(dev, NULL); | 263 | + unsigned long clk_rate; |
246 | + if (IS_ERR(omm->clk)) { | 264 | + int ret; |
247 | + dev_err(dev, "Failed to get OMM clock (%ld)\n", | 265 | + u8 i; |
248 | + PTR_ERR(omm->clk)); | 266 | + |
249 | + | 267 | + for (i = 0; i < OMM_CLK_NB; i++) |
250 | + return PTR_ERR(omm->clk); | 268 | + omm->clk_bulk[i].id = clocks_name[i]; |
251 | + } | 269 | + |
270 | + /* retrieve OMM, OSPI1 and OSPI2 clocks */ | ||
271 | + ret = devm_clk_bulk_get(dev, OMM_CLK_NB, omm->clk_bulk); | ||
272 | + if (ret) | ||
273 | + return dev_err_probe(dev, ret, "Failed to get OMM/OSPI's clocks\n"); | ||
274 | + | ||
275 | + /* Ensure both OSPI instance are disabled before configuring OMM */ | ||
276 | + ret = stm32_omm_disable_child(dev); | ||
277 | + if (ret) | ||
278 | + return ret; | ||
252 | + | 279 | + |
253 | + ret = pm_runtime_resume_and_get(dev); | 280 | + ret = pm_runtime_resume_and_get(dev); |
254 | + if (ret < 0) | 281 | + if (ret < 0) |
255 | + return ret; | 282 | + return ret; |
256 | + | 283 | + |
257 | + /* parse children's clock */ | 284 | + /* parse children's clock */ |
258 | + for (i = 0; i < omm->nb_child; i++) { | 285 | + for (i = 1; i <= omm->nb_child; i++) { |
259 | + clk_rate = clk_get_rate(omm->child[i].clk); | 286 | + clk_rate = clk_get_rate(omm->clk_bulk[i].clk); |
260 | + if (!clk_rate) { | 287 | + if (!clk_rate) { |
261 | + dev_err(dev, "Invalid clock rate\n"); | 288 | + dev_err(dev, "Invalid clock rate\n"); |
262 | + pm_runtime_disable(dev); | ||
263 | + goto err_clk_disable; | 289 | + goto err_clk_disable; |
264 | + } | 290 | + } |
265 | + | 291 | + |
266 | + if (clk_rate > clk_rate_max) | 292 | + if (clk_rate > clk_rate_max) |
267 | + clk_rate_max = clk_rate; | 293 | + clk_rate_max = clk_rate; |
268 | + } | 294 | + } |
269 | + | 295 | + |
270 | + rstc = devm_reset_control_get_optional_exclusive(dev, NULL); | 296 | + rstc = devm_reset_control_get_exclusive(dev, "omm"); |
271 | + if (IS_ERR(rstc)) { | 297 | + if (IS_ERR(rstc)) |
272 | + ret = dev_err_probe(dev, PTR_ERR(rstc), "reset get failed\n"); | 298 | + return dev_err_probe(dev, PTR_ERR(rstc), "reset get failed\n"); |
273 | + pm_runtime_disable(dev); | ||
274 | + goto err_clk_disable; | ||
275 | + } | ||
276 | + | 299 | + |
277 | + reset_control_assert(rstc); | 300 | + reset_control_assert(rstc); |
278 | + udelay(2); | 301 | + udelay(2); |
279 | + reset_control_deassert(rstc); | 302 | + reset_control_deassert(rstc); |
280 | + | 303 | + |
... | ... | ||
299 | + | 322 | + |
300 | + /* | 323 | + /* |
301 | + * If the mux is enabled, the 2 OSPI clocks have to be | 324 | + * If the mux is enabled, the 2 OSPI clocks have to be |
302 | + * always enabled | 325 | + * always enabled |
303 | + */ | 326 | + */ |
304 | + ret = stm32_omm_enable_child_clock(dev, true); | 327 | + ret = stm32_omm_toggle_child_clock(dev, true); |
305 | + if (ret) { | 328 | + if (ret) |
306 | + pm_runtime_disable(dev); | ||
307 | + goto err_clk_disable; | 329 | + goto err_clk_disable; |
308 | + } | ||
309 | + } | 330 | + } |
310 | + | 331 | + |
311 | + omm->cr &= ~CR_MUXENMODE_MASK; | 332 | + omm->cr &= ~CR_MUXENMODE_MASK; |
312 | + omm->cr |= FIELD_PREP(CR_MUXENMODE_MASK, mux); | 333 | + omm->cr |= FIELD_PREP(CR_MUXENMODE_MASK, mux); |
313 | + } | 334 | + } |
... | ... | ||
329 | + pm_runtime_put_sync_suspend(dev); | 350 | + pm_runtime_put_sync_suspend(dev); |
330 | + | 351 | + |
331 | + return ret; | 352 | + return ret; |
332 | +} | 353 | +} |
333 | + | 354 | + |
334 | +static int stm32_omm_check_access(struct device *dev, struct device_node *np) | 355 | +static int stm32_omm_check_access(struct device_node *np) |
335 | +{ | 356 | +{ |
336 | + struct stm32_firewall firewall; | 357 | + struct stm32_firewall firewall; |
337 | + int ret; | 358 | + int ret; |
338 | + | 359 | + |
339 | + ret = stm32_firewall_get_firewall(np, &firewall, 1); | 360 | + ret = stm32_firewall_get_firewall(np, &firewall, 1); |
340 | + if (ret) | 361 | + if (ret) |
341 | + return ret; | 362 | + return ret; |
342 | + | 363 | + |
343 | + return stm32_firewall_grant_access(&firewall); | 364 | + return stm32_firewall_grant_access(&firewall); |
344 | +} | 365 | +} |
345 | + | 366 | + |
346 | +static int stm32_omm_disable_child(struct device *dev) | 367 | +static int stm32_omm_probe(struct platform_device *pdev) |
347 | +{ | 368 | +{ |
348 | + struct stm32_omm *omm = dev_get_drvdata(dev); | 369 | + struct device *dev = &pdev->dev; |
349 | + struct reset_control *reset; | 370 | + u8 child_access_granted = 0; |
371 | + struct stm32_omm *omm; | ||
350 | + int ret; | 372 | + int ret; |
351 | + u8 i; | ||
352 | + | ||
353 | + for (i = 0; i < omm->nb_child; i++) { | ||
354 | + ret = clk_prepare_enable(omm->child[i].clk); | ||
355 | + if (ret) { | ||
356 | + dev_err(dev, "Can not enable clock\n"); | ||
357 | + return ret; | ||
358 | + } | ||
359 | + | ||
360 | + reset = of_reset_control_get_exclusive(omm->child[i].node, 0); | ||
361 | + if (IS_ERR(reset)) { | ||
362 | + dev_err(dev, "Can't get child reset\n"); | ||
363 | + return PTR_ERR(reset); | ||
364 | + }; | ||
365 | + | ||
366 | + /* reset OSPI to ensure CR_EN bit is set to 0 */ | ||
367 | + reset_control_assert(reset); | ||
368 | + udelay(2); | ||
369 | + reset_control_deassert(reset); | ||
370 | + | ||
371 | + reset_control_put(reset); | ||
372 | + clk_disable_unprepare(omm->child[i].clk); | ||
373 | + } | ||
374 | + | ||
375 | + return 0; | ||
376 | +} | ||
377 | + | ||
378 | +static int stm32_omm_probe(struct platform_device *pdev) | ||
379 | +{ | ||
380 | + struct platform_device *vdev; | ||
381 | + struct device *dev = &pdev->dev; | ||
382 | + struct stm32_omm *omm; | ||
383 | + struct clk *clk; | ||
384 | + int ret; | ||
385 | + u8 child_access_granted = 0; | ||
386 | + u8 i, j; | ||
387 | + bool child_access[OMM_CHILD_NB]; | ||
388 | + | 373 | + |
389 | + omm = devm_kzalloc(dev, sizeof(*omm), GFP_KERNEL); | 374 | + omm = devm_kzalloc(dev, sizeof(*omm), GFP_KERNEL); |
390 | + if (!omm) | 375 | + if (!omm) |
391 | + return -ENOMEM; | 376 | + return -ENOMEM; |
392 | + | 377 | + |
... | ... | ||
400 | + | 385 | + |
401 | + /* check child's access */ | 386 | + /* check child's access */ |
402 | + for_each_child_of_node_scoped(dev->of_node, child) { | 387 | + for_each_child_of_node_scoped(dev->of_node, child) { |
403 | + if (omm->nb_child >= OMM_CHILD_NB) { | 388 | + if (omm->nb_child >= OMM_CHILD_NB) { |
404 | + dev_err(dev, "Bad DT, found too much children\n"); | 389 | + dev_err(dev, "Bad DT, found too much children\n"); |
405 | + ret = -E2BIG; | 390 | + return -E2BIG; |
406 | + goto err_clk_release; | 391 | + } |
407 | + } | 392 | + |
408 | + | 393 | + if (!of_device_is_compatible(child, "st,stm32mp25-omi")) |
409 | + if (!of_device_is_compatible(child, "st,stm32mp25-ospi")) { | 394 | + return -EINVAL; |
410 | + ret = -EINVAL; | 395 | + |
411 | + goto err_clk_release; | 396 | + ret = stm32_omm_check_access(child); |
412 | + } | ||
413 | + | ||
414 | + ret = stm32_omm_check_access(dev, child); | ||
415 | + if (ret < 0 && ret != -EACCES) | 397 | + if (ret < 0 && ret != -EACCES) |
416 | + goto err_clk_release; | 398 | + return ret; |
417 | + | 399 | + |
418 | + child_access[omm->nb_child] = false; | 400 | + if (!ret) |
419 | + if (!ret) { | ||
420 | + child_access_granted++; | 401 | + child_access_granted++; |
421 | + child_access[omm->nb_child] = true; | 402 | + |
422 | + } | ||
423 | + | ||
424 | + omm->child[omm->nb_child].node = child; | ||
425 | + | ||
426 | + clk = of_clk_get(child, 0); | ||
427 | + if (IS_ERR(clk)) { | ||
428 | + dev_err(dev, "Can't get child clock\n"); | ||
429 | + ret = PTR_ERR(clk); | ||
430 | + goto err_clk_release; | ||
431 | + }; | ||
432 | + | ||
433 | + omm->child[omm->nb_child].clk = clk; | ||
434 | + omm->nb_child++; | 403 | + omm->nb_child++; |
435 | + } | 404 | + } |
436 | + | 405 | + |
437 | + if (omm->nb_child != OMM_CHILD_NB) { | 406 | + if (omm->nb_child != OMM_CHILD_NB) |
438 | + ret = -EINVAL; | 407 | + return -EINVAL; |
439 | + goto err_clk_release; | ||
440 | + } | ||
441 | + | 408 | + |
442 | + platform_set_drvdata(pdev, omm); | 409 | + platform_set_drvdata(pdev, omm); |
443 | + | 410 | + |
444 | + pm_runtime_enable(dev); | 411 | + pm_runtime_enable(dev); |
445 | + | 412 | + |
446 | + /* check if OMM's resource access is granted */ | 413 | + /* check if OMM's resource access is granted */ |
447 | + ret = stm32_omm_check_access(dev, dev->of_node); | 414 | + ret = stm32_omm_check_access(dev->of_node); |
448 | + if (ret < 0 && ret != -EACCES) | 415 | + if (ret < 0 && ret != -EACCES) |
449 | + goto err_clk_release; | 416 | + goto error; |
450 | + | 417 | + |
451 | + if (!ret && child_access_granted == OMM_CHILD_NB) { | 418 | + if (!ret && child_access_granted == OMM_CHILD_NB) { |
452 | + /* Ensure both OSPI instance are disabled before configuring OMM */ | ||
453 | + ret = stm32_omm_disable_child(dev); | ||
454 | + if (ret) | ||
455 | + goto err_clk_release; | ||
456 | + | ||
457 | + ret = stm32_omm_configure(dev); | 419 | + ret = stm32_omm_configure(dev); |
458 | + if (ret) | 420 | + if (ret) |
459 | + goto err_clk_release; | 421 | + goto error; |
460 | + } else { | 422 | + } else { |
461 | + dev_dbg(dev, "Octo Memory Manager resource's access not granted\n"); | 423 | + dev_dbg(dev, "Octo Memory Manager resource's access not granted\n"); |
462 | + /* | 424 | + /* |
463 | + * AMCR can't be set, so check if current value is coherent | 425 | + * AMCR can't be set, so check if current value is coherent |
464 | + * with memory-map areas defined in DT | 426 | + * with memory-map areas defined in DT |
465 | + */ | 427 | + */ |
466 | + ret = stm32_omm_set_amcr(dev, false); | 428 | + ret = stm32_omm_set_amcr(dev, false); |
467 | + if (ret) | 429 | + if (ret) |
468 | + goto err_clk_release; | 430 | + goto error; |
469 | + } | 431 | + } |
470 | + | 432 | + |
471 | + /* for each child, if resource access is granted and status "okay", probe it */ | 433 | + ret = of_platform_populate(dev->of_node, NULL, NULL, dev); |
472 | + for (i = 0; i < omm->nb_child; i++) { | 434 | + if (ret) { |
473 | + if (!child_access[i] || !of_device_is_available(omm->child[i].node)) | 435 | + dev_err(dev, "Failed to create Octo Memory Manager child\n"); |
474 | + continue; | 436 | + of_platform_depopulate(dev); |
475 | + | 437 | + ret = -EINVAL; |
476 | + vdev = of_platform_device_create(omm->child[i].node, NULL, NULL); | 438 | + goto error; |
477 | + if (!vdev) { | 439 | + } |
478 | + dev_err(dev, "Failed to create Octo Memory Manager child\n"); | ||
479 | + for (j = i; j > 0; --j) { | ||
480 | + if (omm->child[j].dev) | ||
481 | + of_platform_device_destroy(omm->child[j].dev, NULL); | ||
482 | + } | ||
483 | + | ||
484 | + ret = -EINVAL; | ||
485 | + goto err_clk_release; | ||
486 | + } | ||
487 | + omm->child[i].dev = &vdev->dev; | ||
488 | + } | ||
489 | + | ||
490 | +err_clk_release: | ||
491 | + for (i = 0; i < omm->nb_child; i++) | ||
492 | + clk_put(omm->child[i].clk); | ||
493 | + | 440 | + |
494 | + return ret; | 441 | + return ret; |
442 | + | ||
443 | +error: | ||
444 | + pm_runtime_disable(dev); | ||
445 | + | ||
446 | + return ret; | ||
447 | + | ||
495 | +} | 448 | +} |
496 | + | 449 | + |
497 | +static void stm32_omm_remove(struct platform_device *pdev) | 450 | +static void stm32_omm_remove(struct platform_device *pdev) |
498 | +{ | 451 | +{ |
499 | + struct stm32_omm *omm = platform_get_drvdata(pdev); | 452 | + struct stm32_omm *omm = platform_get_drvdata(pdev); |
500 | + int i; | 453 | + |
501 | + | 454 | + of_platform_depopulate(&pdev->dev); |
502 | + for (i = 0; i < omm->nb_child; i++) | ||
503 | + if (omm->child[i].dev) | ||
504 | + of_platform_device_destroy(omm->child[i].dev, NULL); | ||
505 | + | ||
506 | + if (omm->cr & CR_MUXEN) | 455 | + if (omm->cr & CR_MUXEN) |
507 | + stm32_omm_enable_child_clock(&pdev->dev, false); | 456 | + stm32_omm_toggle_child_clock(&pdev->dev, false); |
508 | + | 457 | + |
509 | + pm_runtime_disable(&pdev->dev); | 458 | + pm_runtime_disable(&pdev->dev); |
510 | +} | 459 | +} |
511 | + | 460 | + |
512 | +static const struct of_device_id stm32_omm_of_match[] = { | 461 | +static const struct of_device_id stm32_omm_of_match[] = { |
... | ... | ||
517 | + | 466 | + |
518 | +static int __maybe_unused stm32_omm_runtime_suspend(struct device *dev) | 467 | +static int __maybe_unused stm32_omm_runtime_suspend(struct device *dev) |
519 | +{ | 468 | +{ |
520 | + struct stm32_omm *omm = dev_get_drvdata(dev); | 469 | + struct stm32_omm *omm = dev_get_drvdata(dev); |
521 | + | 470 | + |
522 | + clk_disable_unprepare(omm->clk); | 471 | + clk_disable_unprepare(omm->clk_bulk[0].clk); |
523 | + | 472 | + |
524 | + return 0; | 473 | + return 0; |
525 | +} | 474 | +} |
526 | + | 475 | + |
527 | +static int __maybe_unused stm32_omm_runtime_resume(struct device *dev) | 476 | +static int __maybe_unused stm32_omm_runtime_resume(struct device *dev) |
528 | +{ | 477 | +{ |
529 | + struct stm32_omm *omm = dev_get_drvdata(dev); | 478 | + struct stm32_omm *omm = dev_get_drvdata(dev); |
530 | + | 479 | + |
531 | + return clk_prepare_enable(omm->clk); | 480 | + return clk_prepare_enable(omm->clk_bulk[0].clk); |
532 | +} | 481 | +} |
533 | + | 482 | + |
534 | +static int __maybe_unused stm32_omm_suspend(struct device *dev) | 483 | +static int __maybe_unused stm32_omm_suspend(struct device *dev) |
535 | +{ | 484 | +{ |
536 | + struct stm32_omm *omm = dev_get_drvdata(dev); | 485 | + struct stm32_omm *omm = dev_get_drvdata(dev); |
537 | + | 486 | + |
538 | + if (omm->restore_omm && omm->cr & CR_MUXEN) | 487 | + if (omm->restore_omm && omm->cr & CR_MUXEN) |
539 | + stm32_omm_enable_child_clock(dev, false); | 488 | + stm32_omm_toggle_child_clock(dev, false); |
540 | + | 489 | + |
541 | + return pinctrl_pm_select_sleep_state(dev); | 490 | + return pinctrl_pm_select_sleep_state(dev); |
542 | +} | 491 | +} |
543 | + | 492 | + |
544 | +static int __maybe_unused stm32_omm_resume(struct device *dev) | 493 | +static int __maybe_unused stm32_omm_resume(struct device *dev) |
... | ... | ||
565 | + pm_runtime_put_sync_suspend(dev); | 514 | + pm_runtime_put_sync_suspend(dev); |
566 | + if (ret) | 515 | + if (ret) |
567 | + return ret; | 516 | + return ret; |
568 | + | 517 | + |
569 | + if (omm->cr & CR_MUXEN) | 518 | + if (omm->cr & CR_MUXEN) |
570 | + ret = stm32_omm_enable_child_clock(dev, true); | 519 | + ret = stm32_omm_toggle_child_clock(dev, true); |
571 | + | 520 | + |
572 | + return ret; | 521 | + return ret; |
573 | +} | 522 | +} |
574 | + | 523 | + |
575 | +static const struct dev_pm_ops stm32_omm_pm_ops = { | 524 | +static const struct dev_pm_ops stm32_omm_pm_ops = { |
... | ... | ||
589 | +}; | 538 | +}; |
590 | +module_platform_driver(stm32_omm_driver); | 539 | +module_platform_driver(stm32_omm_driver); |
591 | + | 540 | + |
592 | +MODULE_DESCRIPTION("STMicroelectronics Octo Memory Manager driver"); | 541 | +MODULE_DESCRIPTION("STMicroelectronics Octo Memory Manager driver"); |
593 | +MODULE_LICENSE("GPL"); | 542 | +MODULE_LICENSE("GPL"); |
543 | |||
594 | -- | 544 | -- |
595 | 2.25.1 | 545 | 2.25.1 | diff view generated by jsdifflib |
1 | From: Patrice Chotard <patrice.chotard@foss.st.com> | ||
---|---|---|---|
2 | |||
3 | Add Octo Memory Manager (OMM) entry on stm32mp251 and its two | 1 | Add Octo Memory Manager (OMM) entry on stm32mp251 and its two |
4 | OSPI instance. | 2 | OSPI instance. |
5 | 3 | ||
6 | Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com> | 4 | Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com> |
7 | --- | 5 | --- |
8 | arch/arm64/boot/dts/st/stm32mp251.dtsi | 48 ++++++++++++++++++++++++++ | 6 | arch/arm64/boot/dts/st/stm32mp251.dtsi | 54 ++++++++++++++++++++++++++++++++++ |
9 | 1 file changed, 48 insertions(+) | 7 | 1 file changed, 54 insertions(+) |
10 | 8 | ||
11 | diff --git a/arch/arm64/boot/dts/st/stm32mp251.dtsi b/arch/arm64/boot/dts/st/stm32mp251.dtsi | 9 | diff --git a/arch/arm64/boot/dts/st/stm32mp251.dtsi b/arch/arm64/boot/dts/st/stm32mp251.dtsi |
12 | index XXXXXXX..XXXXXXX 100644 | 10 | index XXXXXXX..XXXXXXX 100644 |
13 | --- a/arch/arm64/boot/dts/st/stm32mp251.dtsi | 11 | --- a/arch/arm64/boot/dts/st/stm32mp251.dtsi |
14 | +++ b/arch/arm64/boot/dts/st/stm32mp251.dtsi | 12 | +++ b/arch/arm64/boot/dts/st/stm32mp251.dtsi |
... | ... | ||
20 | + compatible = "st,stm32mp25-omm"; | 18 | + compatible = "st,stm32mp25-omm"; |
21 | + reg = <0x40500000 0x400>, <0x60000000 0x10000000>; | 19 | + reg = <0x40500000 0x400>, <0x60000000 0x10000000>; |
22 | + reg-names = "regs", "memory_map"; | 20 | + reg-names = "regs", "memory_map"; |
23 | + ranges = <0 0 0x40430000 0x400>, | 21 | + ranges = <0 0 0x40430000 0x400>, |
24 | + <1 0 0x40440000 0x400>; | 22 | + <1 0 0x40440000 0x400>; |
25 | + clocks = <&rcc CK_BUS_OSPIIOM>; | 23 | + clocks = <&rcc CK_BUS_OSPIIOM>, |
26 | + resets = <&rcc OSPIIOM_R>; | 24 | + <&scmi_clk CK_SCMI_OSPI1>, |
25 | + <&scmi_clk CK_SCMI_OSPI2>; | ||
26 | + clock-names = "omm", "ospi1", "ospi2"; | ||
27 | + resets = <&rcc OSPIIOM_R>, | ||
28 | + <&scmi_reset RST_SCMI_OSPI1>, | ||
29 | + <&scmi_reset RST_SCMI_OSPI2>; | ||
30 | + reset-names = "omm", "ospi1", "ospi2"; | ||
27 | + access-controllers = <&rifsc 111>; | 31 | + access-controllers = <&rifsc 111>; |
28 | + power-domains = <&CLUSTER_PD>; | 32 | + power-domains = <&CLUSTER_PD>; |
29 | + #address-cells = <2>; | 33 | + #address-cells = <2>; |
30 | + #size-cells = <1>; | 34 | + #size-cells = <1>; |
31 | + st,syscfg-amcr = <&syscfg 0x2c00 0x7>; | 35 | + st,syscfg-amcr = <&syscfg 0x2c00 0x7>; |
32 | + status = "disabled"; | 36 | + status = "disabled"; |
33 | + | 37 | + |
34 | + ospi1: spi@40430000 { | 38 | + ospi1: spi@0 { |
35 | + compatible = "st,stm32mp25-ospi"; | 39 | + compatible = "st,stm32mp25-ospi"; |
36 | + reg = <0 0 0x400>; | 40 | + reg = <0 0 0x400>; |
37 | + interrupts = <GIC_SPI 163 IRQ_TYPE_LEVEL_HIGH>; | 41 | + interrupts = <GIC_SPI 163 IRQ_TYPE_LEVEL_HIGH>; |
38 | + dmas = <&hpdma 2 0x62 0x00003121>, | 42 | + dmas = <&hpdma 2 0x62 0x00003121>, |
39 | + <&hpdma 2 0x42 0x00003112>; | 43 | + <&hpdma 2 0x42 0x00003112>; |
... | ... | ||
45 | + power-domains = <&CLUSTER_PD>; | 49 | + power-domains = <&CLUSTER_PD>; |
46 | + st,syscfg-dlyb = <&syscfg 0x1000>; | 50 | + st,syscfg-dlyb = <&syscfg 0x1000>; |
47 | + status = "disabled"; | 51 | + status = "disabled"; |
48 | + }; | 52 | + }; |
49 | + | 53 | + |
50 | + ospi2: spi@40440000 { | 54 | + ospi2: spi@1 { |
51 | + compatible = "st,stm32mp25-ospi"; | 55 | + compatible = "st,stm32mp25-ospi"; |
52 | + reg = <1 0 0x400>; | 56 | + reg = <1 0 0x400>; |
53 | + interrupts = <GIC_SPI 164 IRQ_TYPE_LEVEL_HIGH>; | 57 | + interrupts = <GIC_SPI 164 IRQ_TYPE_LEVEL_HIGH>; |
54 | + dmas = <&hpdma 3 0x62 0x00003121>, | 58 | + dmas = <&hpdma 3 0x62 0x00003121>, |
55 | + <&hpdma 3 0x42 0x00003112>; | 59 | + <&hpdma 3 0x42 0x00003112>; |
... | ... | ||
65 | + }; | 69 | + }; |
66 | + | 70 | + |
67 | spi8: spi@46020000 { | 71 | spi8: spi@46020000 { |
68 | #address-cells = <1>; | 72 | #address-cells = <1>; |
69 | #size-cells = <0>; | 73 | #size-cells = <0>; |
74 | |||
70 | -- | 75 | -- |
71 | 2.25.1 | 76 | 2.25.1 | diff view generated by jsdifflib |
1 | From: Patrice Chotard <patrice.chotard@foss.st.com> | ||
---|---|---|---|
2 | |||
3 | Add pinctrl entry related to OSPI's port1 in stm32mp25-pinctrl.dtsi | 1 | Add pinctrl entry related to OSPI's port1 in stm32mp25-pinctrl.dtsi |
4 | 2 | ||
5 | Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com> | 3 | Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com> |
6 | --- | 4 | --- |
7 | arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi | 51 +++++++++++++++++++ | 5 | arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi | 51 +++++++++++++++++++++++++++ |
8 | 1 file changed, 51 insertions(+) | 6 | 1 file changed, 51 insertions(+) |
9 | 7 | ||
10 | diff --git a/arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi b/arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi | 8 | diff --git a/arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi b/arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi |
11 | index XXXXXXX..XXXXXXX 100644 | 9 | index XXXXXXX..XXXXXXX 100644 |
12 | --- a/arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi | 10 | --- a/arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi |
... | ... | ||
67 | + }; | 65 | + }; |
68 | + | 66 | + |
69 | sdmmc1_b4_od_pins_a: sdmmc1-b4-od-0 { | 67 | sdmmc1_b4_od_pins_a: sdmmc1-b4-od-0 { |
70 | pins1 { | 68 | pins1 { |
71 | pinmux = <STM32_PINMUX('E', 4, AF10)>, /* SDMMC1_D0 */ | 69 | pinmux = <STM32_PINMUX('E', 4, AF10)>, /* SDMMC1_D0 */ |
70 | |||
72 | -- | 71 | -- |
73 | 2.25.1 | 72 | 2.25.1 | diff view generated by jsdifflib |
1 | From: Patrice Chotard <patrice.chotard@foss.st.com> | ||
---|---|---|---|
2 | |||
3 | Add SPI NOR flash nor support on stm32mp257f-ev1 board. | 1 | Add SPI NOR flash nor support on stm32mp257f-ev1 board. |
4 | 2 | ||
5 | Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com> | 3 | Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com> |
6 | --- | 4 | --- |
7 | arch/arm64/boot/dts/st/stm32mp257f-ev1.dts | 32 ++++++++++++++++++++++ | 5 | arch/arm64/boot/dts/st/stm32mp257f-ev1.dts | 32 ++++++++++++++++++++++++++++++ |
8 | 1 file changed, 32 insertions(+) | 6 | 1 file changed, 32 insertions(+) |
9 | 7 | ||
10 | diff --git a/arch/arm64/boot/dts/st/stm32mp257f-ev1.dts b/arch/arm64/boot/dts/st/stm32mp257f-ev1.dts | 8 | diff --git a/arch/arm64/boot/dts/st/stm32mp257f-ev1.dts b/arch/arm64/boot/dts/st/stm32mp257f-ev1.dts |
11 | index XXXXXXX..XXXXXXX 100644 | 9 | index XXXXXXX..XXXXXXX 100644 |
12 | --- a/arch/arm64/boot/dts/st/stm32mp257f-ev1.dts | 10 | --- a/arch/arm64/boot/dts/st/stm32mp257f-ev1.dts |
... | ... | ||
36 | + &ospi_port1_io03_sleep_pins_a | 34 | + &ospi_port1_io03_sleep_pins_a |
37 | + &ospi_port1_cs0_sleep_pins_a>; | 35 | + &ospi_port1_cs0_sleep_pins_a>; |
38 | + pinctrl-names = "default", "sleep"; | 36 | + pinctrl-names = "default", "sleep"; |
39 | + status = "okay"; | 37 | + status = "okay"; |
40 | + | 38 | + |
41 | + spi@40430000 { | 39 | + spi@0 { |
42 | + #address-cells = <1>; | 40 | + #address-cells = <1>; |
43 | + #size-cells = <0>; | 41 | + #size-cells = <0>; |
44 | + memory-region = <&mm_ospi1>; | 42 | + memory-region = <&mm_ospi1>; |
45 | + status = "okay"; | 43 | + status = "okay"; |
46 | + | 44 | + |
... | ... | ||
55 | +}; | 53 | +}; |
56 | + | 54 | + |
57 | &rtc { | 55 | &rtc { |
58 | status = "okay"; | 56 | status = "okay"; |
59 | }; | 57 | }; |
58 | |||
60 | -- | 59 | -- |
61 | 2.25.1 | 60 | 2.25.1 | diff view generated by jsdifflib |
1 | From: Patrice Chotard <patrice.chotard@foss.st.com> | ||
---|---|---|---|
2 | |||
3 | Enable STM32 OctoSPI driver. | 1 | Enable STM32 OctoSPI driver. |
4 | Enable STM32 Octo Memory Manager (OMM) driver which is needed | 2 | Enable STM32 Octo Memory Manager (OMM) driver which is needed |
5 | for OSPI usage on STM32MP257F-EV1 board. | 3 | for OSPI usage on STM32MP257F-EV1 board. |
6 | 4 | ||
7 | Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com> | 5 | Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com> |
... | ... | ||
27 | CONFIG_RENESAS_RPCIF=m | 25 | CONFIG_RENESAS_RPCIF=m |
28 | +CONFIG_STM32_OMM=m | 26 | +CONFIG_STM32_OMM=m |
29 | CONFIG_IIO=y | 27 | CONFIG_IIO=y |
30 | CONFIG_EXYNOS_ADC=y | 28 | CONFIG_EXYNOS_ADC=y |
31 | CONFIG_IMX8QXP_ADC=m | 29 | CONFIG_IMX8QXP_ADC=m |
30 | |||
32 | -- | 31 | -- |
33 | 2.25.1 | 32 | 2.25.1 | diff view generated by jsdifflib |