* [PATCH v3 0/3] Add STM32 TAMP nvram driver support
@ 2026-10-16 15:26 Patrice Chotard
2026-10-16 15:26 ` [PATCH v3 1/3] dt-bindings: nvmem: add STM32MPX TAMP backup registers Patrice Chotard
` (2 more replies)
0 siblings, 3 replies; 8+ messages in thread
From: Patrice Chotard @ 2026-10-16 15:26 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Maxime Coquelin,
Alexandre Torgue, Christophe Roullier, Srinivas Kandagatla
Cc: Simeon Marijon, linux-stm32, devicetree, linux-arm-kernel,
linux-kernel, Patrice Chotard, Gatien Chevallier
Propose bindings and driver to enable TAMP nvram driver
support for STM32MP SoC's family.
It allows to expose TAMP backup registers as nvmem cells.
Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com>
---
Changes in v3:
- Fix issue found by sashiko-bot:
- rely on the already resolved priv->data->nb_zones instead of
of_device_is_compatible() to set priv->bkpreg_access
- Fix max_register of stm32mp1_tamp_nvram_regmap_cfg and
stm32mp2_tamp_nvram_regmap_cfg
- Update zones_end type to int*
- Replace both memcpy by put_unaligned_le32 and get_unaligned_le32
- Link to v2: https://patch.msgid.link/20260930-upstream_nvram_reworked-v2-0-cfda749ee0b7@foss.st.com
Changes in v2:
- Update commit title of nvmem dt-bindings's patch.
- Add required compatible for patternProperties in stm32/st,stm32-syscon.yaml.
- Fix issue found by sashiko-bot:
- Add freeing compartment_owner.
- Replace regmap_read/regmap_write by regmap_update_bits in stm32_tamp_nvram_write_byte().
- Update error return in stm32_tamp_nvram_read_byte(), stm32_tamp_nvram_write_byte(),
stm32_tamp_nvram_read() and stm32_tamp_nvram_write() to return the actual regmap_read() /
regmap_write() error instead of -EIO.
- Add freeing field and update error return values for stm32_tamp_nvram_get_backup_zones().
- Update error return for stm32_tamp_nvram_probe().
- Link to v1: https://patch.msgid.link/20260910-upstream_nvram_reworked-v1-0-521c16175cef@foss.st.com
To: Simeon Marijon <simeon.marijon@foss.st.com>
To: Patrice Chotard <patrice.chotard@foss.st.com>
To: Srinivas Kandagatla <srini@kernel.org>
To: Rob Herring <robh@kernel.org>
To: Krzysztof Kozlowski <krzk+dt@kernel.org>
To: Conor Dooley <conor+dt@kernel.org>
To: Maxime Coquelin <mcoquelin.stm32@gmail.com>
To: Alexandre Torgue <alexandre.torgue@foss.st.com>
To: Christophe Roullier <christophe.roullier@foss.st.com>
Cc: linux-stm32@st-md-mailman.stormreply.com
Cc: devicetree@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-kernel@vger.kernel.org
---
Patrice Chotard (1):
dt-bindings: arm: stm32: allow TAMP nvram child node for syscon
Simeon Marijon (2):
dt-bindings: nvmem: add STM32MPX TAMP backup registers
nvmem: stm32: add TAMP nvmem driver
.../bindings/arm/stm32/st,stm32-syscon.yaml | 30 +
.../bindings/nvmem/st,stm32-tamp-nvram.yaml | 57 ++
MAINTAINERS | 8 +
drivers/nvmem/Kconfig | 10 +
drivers/nvmem/Makefile | 2 +
drivers/nvmem/stm32-tamp-nvram.c | 741 +++++++++++++++++++++
6 files changed, 848 insertions(+)
---
base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
change-id: 20260909-upstream_nvram_reworked-f84a4b37817c
Best regards,
--
Patrice Chotard <patrice.chotard@foss.st.com>
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH v3 1/3] dt-bindings: nvmem: add STM32MPX TAMP backup registers 2026-10-16 15:26 [PATCH v3 0/3] Add STM32 TAMP nvram driver support Patrice Chotard @ 2026-10-16 15:26 ` Patrice Chotard 2026-10-16 15:26 ` [PATCH v3 2/3] dt-bindings: arm: stm32: allow TAMP nvram child node for syscon Patrice Chotard 2026-10-16 15:26 ` [PATCH v3 3/3] nvmem: stm32: add TAMP nvmem driver Patrice Chotard 2 siblings, 0 replies; 8+ messages in thread From: Patrice Chotard @ 2026-10-16 15:26 UTC (permalink / raw) To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Maxime Coquelin, Alexandre Torgue, Christophe Roullier, Srinivas Kandagatla Cc: Simeon Marijon, linux-stm32, devicetree, linux-arm-kernel, linux-kernel, Patrice Chotard From: Simeon Marijon <simeon.marijon@foss.st.com> Adds the binding documentation for TAMP backup registers. The backup registers is a part of the TAMP device of STM32 MPU. Signed-off-by: Simeon Marijon <simeon.marijon@foss.st.com> Reviewed-by: Rob Herring (Arm) <robh@kernel.org> Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com> --- .../bindings/nvmem/st,stm32-tamp-nvram.yaml | 57 ++++++++++++++++++++++ MAINTAINERS | 7 +++ 2 files changed, 64 insertions(+) diff --git a/Documentation/devicetree/bindings/nvmem/st,stm32-tamp-nvram.yaml b/Documentation/devicetree/bindings/nvmem/st,stm32-tamp-nvram.yaml new file mode 100644 index 000000000000..4af3b88db566 --- /dev/null +++ b/Documentation/devicetree/bindings/nvmem/st,stm32-tamp-nvram.yaml @@ -0,0 +1,57 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/nvmem/st,stm32-tamp-nvram.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: STMicroelectronics STM32 backup registers as NVMEM + +description: | + This represents STM32 backup registers area presented as nvmem cells. + This contains various information (e.g. boot mode passed to bootloaders). + +maintainers: + - Simeon Marijon <simeon.marijon@foss.st.com> + - Patrice Chotard <patrice.chotard@foss.st.com> + +allOf: + - $ref: nvmem.yaml# + +properties: + compatible: + enum: + - st,stm32mp15-tamp-nvram + - st,stm32mp25-tamp-nvram + + reg: + description: + TAMP Registers bank location and size. + maxItems: 1 + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + nvram: nvram@5c00a100 { + reg = <0x5c00a100 0x80>; + compatible = "st,stm32mp15-tamp-nvram"; + + nvmem-layout { + compatible = "fixed-layout"; + #address-cells = <1>; + #size-cells = <1>; + + /* Data cells */ + wakeup_sec: tamp-bkp@0 { + reg = <0x0 0x4>; + }; + boot_mode: tamp-bkp@50 { + /*see boot mode selection feature*/ + reg = <0x50 0x4>; + }; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 3a19da74d00c..b634c037c229 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -26261,6 +26261,13 @@ F: Documentation/arch/arm/stm32/stm32-dma-mdma-chaining.rst F: Documentation/devicetree/bindings/dma/stm32/ F: drivers/dma/stm32/ +STM32 TAMP DRIVERS +M: Simeon Marijon <simeon.marijon@foss.st.com> +M: Patrice Chotard <patrice.chotard@foss.st.com> +L: linux-stm32@st-md-mailman.stormreply.com (moderated for non-subscribers) +S: Maintained +F: Documentation/devicetree/bindings/nvmem/st,stm32-tamp-nvram.yaml + STM32 TIMER/LPTIMER DRIVERS M: Fabrice Gasnier <fabrice.gasnier@foss.st.com> S: Maintained -- 2.43.0 ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v3 2/3] dt-bindings: arm: stm32: allow TAMP nvram child node for syscon 2026-10-16 15:26 [PATCH v3 0/3] Add STM32 TAMP nvram driver support Patrice Chotard 2026-10-16 15:26 ` [PATCH v3 1/3] dt-bindings: nvmem: add STM32MPX TAMP backup registers Patrice Chotard @ 2026-10-16 15:26 ` Patrice Chotard 2026-10-17 17:52 ` Krzysztof Kozlowski 2026-10-16 15:26 ` [PATCH v3 3/3] nvmem: stm32: add TAMP nvmem driver Patrice Chotard 2 siblings, 1 reply; 8+ messages in thread From: Patrice Chotard @ 2026-10-16 15:26 UTC (permalink / raw) To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Maxime Coquelin, Alexandre Torgue, Christophe Roullier, Srinivas Kandagatla Cc: Simeon Marijon, linux-stm32, devicetree, linux-arm-kernel, linux-kernel, Patrice Chotard The STM32 TAMP node (compatible "st,stm32-tamp", "syscon", "simple-mfd") exposes its backup registers through an nvram child node described by nvmem/st,stm32-tamp-nvram.yaml. That requires the TAMP node to carry "#address-cells", "#size-cells", "ranges" and an "nvram@..." child. st,stm32-syscon.yaml uses "additionalProperties: false" and only allowed reg/clocks/#clock-cells, so wiring the nvram provider into the DTSI made "make dtbs_check" fail on every STM32MP TAMP node: tamp@5c00a000 (st,stm32-tamp): '#address-cells', '#size-cells', 'nvram@5c00a100', 'ranges' do not match any of the regexes: '^pinctrl-[0-9]+$' Allow the addressing properties and the nvram child, gated on the st,stm32-tamp compatible so the other syscon variants stay strict. "#size-cells" may be 1 or 2: the STM32MP21 soc bus uses two size cells (an empty "ranges" requires the node to match its parent) while the other STM32MP SoCs use one. Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com> --- .../bindings/arm/stm32/st,stm32-syscon.yaml | 30 ++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/stm32/st,stm32-syscon.yaml b/Documentation/devicetree/bindings/arm/stm32/st,stm32-syscon.yaml index 95d2319afe23..1aeb54867db2 100644 --- a/Documentation/devicetree/bindings/arm/stm32/st,stm32-syscon.yaml +++ b/Documentation/devicetree/bindings/arm/stm32/st,stm32-syscon.yaml @@ -39,6 +39,23 @@ properties: "#clock-cells": const: 0 + "#address-cells": + const: 1 + + "#size-cells": + enum: [1, 2] + + ranges: true + +patternProperties: + "^nvram@[0-9a-f]+$": + type: object + description: + TAMP backup registers exposed as an NVMEM provider. See + nvmem/st,stm32-tamp-nvram.yaml. + required: + - compatible + required: - compatible - reg @@ -61,6 +78,19 @@ allOf: then: required: - "#clock-cells" + - if: + not: + properties: + compatible: + contains: + const: st,stm32-tamp + then: + properties: + "#address-cells": false + "#size-cells": false + ranges: false + patternProperties: + "^nvram@[0-9a-f]+$": false additionalProperties: false -- 2.43.0 ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v3 2/3] dt-bindings: arm: stm32: allow TAMP nvram child node for syscon 2026-10-16 15:26 ` [PATCH v3 2/3] dt-bindings: arm: stm32: allow TAMP nvram child node for syscon Patrice Chotard @ 2026-10-17 17:52 ` Krzysztof Kozlowski 2026-11-04 13:43 ` Patrice CHOTARD 0 siblings, 1 reply; 8+ messages in thread From: Krzysztof Kozlowski @ 2026-10-17 17:52 UTC (permalink / raw) To: Patrice Chotard Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Maxime Coquelin, Alexandre Torgue, Christophe Roullier, Srinivas Kandagatla, Simeon Marijon, linux-stm32, devicetree, linux-arm-kernel, linux-kernel On Fri, Oct 16, 2026 at 05:26:04PM +0200, Patrice Chotard wrote: > The STM32 TAMP node (compatible "st,stm32-tamp", "syscon", "simple-mfd") > exposes its backup registers through an nvram child node described by > nvmem/st,stm32-tamp-nvram.yaml. That requires the TAMP node to carry > "#address-cells", "#size-cells", "ranges" and an "nvram@..." child. > > st,stm32-syscon.yaml uses "additionalProperties: false" and only allowed > reg/clocks/#clock-cells, so wiring the nvram provider into the DTSI made > "make dtbs_check" fail on every STM32MP TAMP node: > > tamp@5c00a000 (st,stm32-tamp): '#address-cells', '#size-cells', > 'nvram@5c00a100', 'ranges' do not match any of the regexes: > '^pinctrl-[0-9]+$' There are no children of tamp@5c00a000 in arch/arm/boot/dts/st/stm32mp151.dtsi. How is it possible to reproduce this warning? Are you using some other patches bugs as a reason of change in bindings? > > Allow the addressing properties and the nvram child, gated on the > st,stm32-tamp compatible so the other syscon variants stay strict. > "#size-cells" may be 1 or 2: the STM32MP21 soc bus uses two size cells > (an empty "ranges" requires the node to match its parent) while the > other STM32MP SoCs use one. > > Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com> > --- > .../bindings/arm/stm32/st,stm32-syscon.yaml | 30 ++++++++++++++++++++++ > 1 file changed, 30 insertions(+) > > diff --git a/Documentation/devicetree/bindings/arm/stm32/st,stm32-syscon.yaml b/Documentation/devicetree/bindings/arm/stm32/st,stm32-syscon.yaml > index 95d2319afe23..1aeb54867db2 100644 > --- a/Documentation/devicetree/bindings/arm/stm32/st,stm32-syscon.yaml > +++ b/Documentation/devicetree/bindings/arm/stm32/st,stm32-syscon.yaml > @@ -39,6 +39,23 @@ properties: > "#clock-cells": > const: 0 > > + "#address-cells": > + const: 1 > + > + "#size-cells": > + enum: [1, 2] > + > + ranges: true You work on some weeks old tree. > + > +patternProperties: > + "^nvram@[0-9a-f]+$": > + type: object additionalProperties true with ref or false with a compatible, just like every other binding. See also display/msm for some examples. > + description: > + TAMP backup registers exposed as an NVMEM provider. See > + nvmem/st,stm32-tamp-nvram.yaml. > + required: > + - compatible Best regards, Krzysztof ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v3 2/3] dt-bindings: arm: stm32: allow TAMP nvram child node for syscon 2026-10-17 17:52 ` Krzysztof Kozlowski @ 2026-11-04 13:43 ` Patrice CHOTARD 0 siblings, 0 replies; 8+ messages in thread From: Patrice CHOTARD @ 2026-11-04 13:43 UTC (permalink / raw) To: Krzysztof Kozlowski Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Maxime Coquelin, Alexandre Torgue, Christophe Roullier, Srinivas Kandagatla, Simeon Marijon, linux-stm32, devicetree, linux-arm-kernel, linux-kernel On 10/17/26 19:52, Krzysztof Kozlowski wrote: > On Fri, Oct 16, 2026 at 05:26:04PM +0200, Patrice Chotard wrote: >> The STM32 TAMP node (compatible "st,stm32-tamp", "syscon", "simple-mfd") >> exposes its backup registers through an nvram child node described by >> nvmem/st,stm32-tamp-nvram.yaml. That requires the TAMP node to carry >> "#address-cells", "#size-cells", "ranges" and an "nvram@..." child. >> >> st,stm32-syscon.yaml uses "additionalProperties: false" and only allowed >> reg/clocks/#clock-cells, so wiring the nvram provider into the DTSI made >> "make dtbs_check" fail on every STM32MP TAMP node: >> >> tamp@5c00a000 (st,stm32-tamp): '#address-cells', '#size-cells', >> 'nvram@5c00a100', 'ranges' do not match any of the regexes: >> '^pinctrl-[0-9]+$' > > There are no children of tamp@5c00a000 in > arch/arm/boot/dts/st/stm32mp151.dtsi. How is it possible to reproduce > this warning? > > Are you using some other patches bugs as a reason of change in bindings? Hi Krzysztof yes, in this patch [1] arch/arm/boot/dts/st/stm32mp151.dtsi is updated that's why bindings need this change. See the complete series [2]. [1] https://patchwork.kernel.org/project/linux-arm-kernel/patch/20260930-upstream_tamp_nvram_dts_part-v2-2-bd73158ce46b@foss.st.com/ [2] https://patchwork.kernel.org/series/1176665/ > >> >> Allow the addressing properties and the nvram child, gated on the >> st,stm32-tamp compatible so the other syscon variants stay strict. >> "#size-cells" may be 1 or 2: the STM32MP21 soc bus uses two size cells >> (an empty "ranges" requires the node to match its parent) while the >> other STM32MP SoCs use one. >> >> Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com> >> --- >> .../bindings/arm/stm32/st,stm32-syscon.yaml | 30 ++++++++++++++++++++++ >> 1 file changed, 30 insertions(+) >> >> diff --git a/Documentation/devicetree/bindings/arm/stm32/st,stm32-syscon.yaml b/Documentation/devicetree/bindings/arm/stm32/st,stm32-syscon.yaml >> index 95d2319afe23..1aeb54867db2 100644 >> --- a/Documentation/devicetree/bindings/arm/stm32/st,stm32-syscon.yaml >> +++ b/Documentation/devicetree/bindings/arm/stm32/st,stm32-syscon.yaml >> @@ -39,6 +39,23 @@ properties: >> "#clock-cells": >> const: 0 >> >> + "#address-cells": >> + const: 1 >> + >> + "#size-cells": >> + enum: [1, 2] >> + >> + ranges: true > > You work on some weeks old tree. Indeed, this series need to be rebased on top of v7.4-rc1 Thanks Patrice > >> + >> +patternProperties: >> + "^nvram@[0-9a-f]+$": >> + type: object > > additionalProperties true with ref or false with a compatible, just like > every other binding. See also display/msm for some examples. > >> + description: >> + TAMP backup registers exposed as an NVMEM provider. See >> + nvmem/st,stm32-tamp-nvram.yaml. >> + required: >> + - compatible > > Best regards, > Krzysztof > ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v3 3/3] nvmem: stm32: add TAMP nvmem driver 2026-10-16 15:26 [PATCH v3 0/3] Add STM32 TAMP nvram driver support Patrice Chotard 2026-10-16 15:26 ` [PATCH v3 1/3] dt-bindings: nvmem: add STM32MPX TAMP backup registers Patrice Chotard 2026-10-16 15:26 ` [PATCH v3 2/3] dt-bindings: arm: stm32: allow TAMP nvram child node for syscon Patrice Chotard @ 2026-10-16 15:26 ` Patrice Chotard 2026-10-17 17:57 ` Krzysztof Kozlowski 2 siblings, 1 reply; 8+ messages in thread From: Patrice Chotard @ 2026-10-16 15:26 UTC (permalink / raw) To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Maxime Coquelin, Alexandre Torgue, Christophe Roullier, Srinivas Kandagatla Cc: Simeon Marijon, linux-stm32, devicetree, linux-arm-kernel, linux-kernel, Patrice Chotard, Gatien Chevallier From: Simeon Marijon <simeon.marijon@foss.st.com> TAMP backup registers will be exposed as nvmem cells. Each registers ([0..127] for STM32MP2, [0..31] for STM32MP1) could be exposed as nvmem cells under the nvram node in device tree. Signed-off-by: Simeon Marijon <simeon.marijon@foss.st.com> Signed-off-by: Gatien Chevallier <gatien.chevallier@foss.st.com> Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com> --- MAINTAINERS | 1 + drivers/nvmem/Kconfig | 10 + drivers/nvmem/Makefile | 2 + drivers/nvmem/stm32-tamp-nvram.c | 741 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 754 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index b634c037c229..63c69f8a93f0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -26267,6 +26267,7 @@ M: Patrice Chotard <patrice.chotard@foss.st.com> L: linux-stm32@st-md-mailman.stormreply.com (moderated for non-subscribers) S: Maintained F: Documentation/devicetree/bindings/nvmem/st,stm32-tamp-nvram.yaml +F: drivers/nvmem/stm32-tamp-nvram.c STM32 TIMER/LPTIMER DRIVERS M: Fabrice Gasnier <fabrice.gasnier@foss.st.com> diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index c36c2a4c2a0b..1d71d0097bc2 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -504,6 +504,16 @@ config NVMEM_STM32_ROMEM This driver can also be built as a module. If so, the module will be called nvmem-stm32-romem. +config NVMEM_STM32_TAMP + tristate "STMicroelectronics STM32 TAMP backup registers support" + depends on ARCH_STM32 || COMPILE_TEST + help + Say y here to enable access for STMicroelectronics STM32 + TAMP backup registers. + + This driver can also be built as a module. If so, the module + will be called nvmem_stm32_tamp_nvram + config NVMEM_SUNPLUS_OCOTP tristate "Sunplus SoC OTP support" depends on SOC_SP7021 || COMPILE_TEST diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile index 2bbfb9ff1885..2e9c55d1855a 100644 --- a/drivers/nvmem/Makefile +++ b/drivers/nvmem/Makefile @@ -86,6 +86,8 @@ nvmem_sprd_efuse-y := sprd-efuse.o obj-$(CONFIG_NVMEM_STM32_ROMEM) += nvmem_stm32_romem.o nvmem_stm32_romem-y := stm32-romem.o nvmem_stm32_romem-$(CONFIG_NVMEM_STM32_BSEC_OPTEE_TA) += stm32-bsec-optee-ta.o +obj-$(CONFIG_NVMEM_STM32_TAMP) += nvmem_stm32_tamp_nvram.o +nvmem_stm32_tamp_nvram-y := stm32-tamp-nvram.o obj-$(CONFIG_NVMEM_SUNPLUS_OCOTP) += nvmem_sunplus_ocotp.o nvmem_sunplus_ocotp-y := sunplus-ocotp.o obj-$(CONFIG_NVMEM_SUNXI_SID) += nvmem_sunxi_sid.o diff --git a/drivers/nvmem/stm32-tamp-nvram.c b/drivers/nvmem/stm32-tamp-nvram.c new file mode 100644 index 000000000000..a4b599fc0b37 --- /dev/null +++ b/drivers/nvmem/stm32-tamp-nvram.c @@ -0,0 +1,741 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * STM32 Tamp backup registers access driver + * + * Copyright (C) 2026, STMicroelectronics - All Rights Reserved + * Author: Simeon Marijon <simeon.marijon@foss.st.com> for STMicroelectronics. + */ + +#include <linux/align.h> +#include <linux/bits.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/mfd/syscon.h> +#include <linux/nvmem-provider.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/unaligned.h> + +#define RIF_CID1 0x1 +#define CURRENT_CID RIF_CID1 +#define NB_ZONES_STM32MP1 3 +#define NB_ZONES_STM32MP2 7 +#define NB_REGS_STM32MP1 32 +#define NB_REGS_STM32MP2 128 + +#define _TAMP_SECCFGR 0x20U +#define _TAMP_BKPRIFR(x) (0x70U + 0x4U * ((x) - 1)) +#define _TAMP_RXCIDCFGR(x) (0x80U + 0x4U * ((x))) + +#define BKPREG_PROTECTION_ZONE_1 0 +#define BKPREG_PROTECTION_ZONE_2 1 +#define BKPREG_PROTECTION_ZONE_3 2 + +#define BKPREG_PROTECTION_ZONE_1_RIF1 0 +#define BKPREG_PROTECTION_ZONE_1_RIF2 1 +#define BKPREG_PROTECTION_ZONE_2_RIF1 2 +#define BKPREG_PROTECTION_ZONE_2_RIF2 3 +#define BKPREG_PROTECTION_ZONE_3_RIF1 4 +#define BKPREG_PROTECTION_ZONE_3_RIF0 5 +#define BKPREG_PROTECTION_ZONE_3_RIF2 6 +#define NB_COMPARTMENT_STM32MP2 3 + +enum stm32_tamp_bkpreg_access { + BKP_READ_WRITE, + BKP_READ, + BKP_NO +}; + +struct stm32_tamp_nvram_priv { + struct nvmem_config cfg; + const struct stm32_tamp_nvram_plat *data; + int *idx_bkpreg_zones_end; + struct device *dev; + struct regmap *config_regmap; + struct regmap *bkpregs_regmap; + enum stm32_tamp_bkpreg_access *bkpreg_access; +}; + +struct stm32_tamp_nvram_plat { + const unsigned int nb_zones; + const unsigned int nb_regs; + const struct regmap_config *bkpregs_regmap_cfg; + const struct reg_field *config_reg_fields; +}; + +static const struct reg_field stm32mp1_tamp_nvram_zone_cfg_fields[NB_ZONES_STM32MP1 - 1] = { + [BKPREG_PROTECTION_ZONE_1] = REG_FIELD(_TAMP_SECCFGR, 0, 7), + [BKPREG_PROTECTION_ZONE_2] = REG_FIELD(_TAMP_SECCFGR, 16, 23), +}; + +static const struct reg_field stm32mp25_tamp_nvram_zone_cfg_fields[NB_ZONES_STM32MP2 - 1] = { + [BKPREG_PROTECTION_ZONE_1_RIF1] = REG_FIELD(_TAMP_BKPRIFR(1), 0, 7), + [BKPREG_PROTECTION_ZONE_1_RIF2] = REG_FIELD(_TAMP_SECCFGR, 0, 7), + [BKPREG_PROTECTION_ZONE_2_RIF1] = REG_FIELD(_TAMP_BKPRIFR(2), 0, 7), + [BKPREG_PROTECTION_ZONE_2_RIF2] = REG_FIELD(_TAMP_SECCFGR, 16, 23), + [BKPREG_PROTECTION_ZONE_3_RIF1] = REG_FIELD(_TAMP_BKPRIFR(3), 0, 7), + [BKPREG_PROTECTION_ZONE_3_RIF0] = REG_FIELD(_TAMP_BKPRIFR(3), 16, 23), +}; + +static const struct reg_field stm32mp25_tamp_nvram_rxcidcfg_cfen_fields[NB_COMPARTMENT_STM32MP2] = { + REG_FIELD(_TAMP_RXCIDCFGR(0), 0, 0), + REG_FIELD(_TAMP_RXCIDCFGR(1), 0, 0), + REG_FIELD(_TAMP_RXCIDCFGR(2), 0, 0), +}; + +static const struct reg_field stm32mp25_tamp_nvram_rxcidcfg_fields[NB_COMPARTMENT_STM32MP2] = { + REG_FIELD(_TAMP_RXCIDCFGR(0), 4, 6), + REG_FIELD(_TAMP_RXCIDCFGR(1), 4, 6), + REG_FIELD(_TAMP_RXCIDCFGR(2), 4, 6), +}; + +static enum stm32_tamp_bkpreg_access stm32mp1_tamp_bkpreg_access[NB_ZONES_STM32MP1] = { + [BKPREG_PROTECTION_ZONE_1] = BKP_NO, + [BKPREG_PROTECTION_ZONE_2] = BKP_READ, + [BKPREG_PROTECTION_ZONE_3] = BKP_READ_WRITE, +}; + +static int stm32_tamp_is_compartment_isolation_enabled_mp2x(struct stm32_tamp_nvram_priv *priv) +{ + int nb_compartment_enabled = 0; + struct device *dev = priv->dev; + u32 cfen; + struct regmap_field *cfen_field; + + for (int i = 0; i < NB_COMPARTMENT_STM32MP2; i++) { + cfen_field = devm_regmap_field_alloc(dev, + priv->config_regmap, + stm32mp25_tamp_nvram_rxcidcfg_cfen_fields[i]); + if (IS_ERR(cfen_field)) { + dev_err(dev, "Can't allocate field for reading configuration\n"); + return PTR_ERR(cfen_field); + } + + if (regmap_field_read(cfen_field, &cfen)) { + dev_err(dev, "Can't read field for registers zones\n"); + devm_regmap_field_free(dev, cfen_field); + return -EINVAL; + } + + nb_compartment_enabled += cfen; + devm_regmap_field_free(dev, cfen_field); + } + + if (!nb_compartment_enabled) + return 0; + else if (nb_compartment_enabled == NB_COMPARTMENT_STM32MP2) + return 1; + else + return -EINVAL; +} + +static bool *stm32_tamp_get_compartment_owner_mp2x(struct stm32_tamp_nvram_priv *priv) +{ + struct device *dev = priv->dev; + struct regmap_field *cid_field; + u32 cid_per_zone; + int isolation_enabled; + bool *compartment_owner; + + isolation_enabled = stm32_tamp_is_compartment_isolation_enabled_mp2x(priv); + if (isolation_enabled < 0) + return ERR_PTR(-EINVAL); + + compartment_owner = kcalloc(NB_COMPARTMENT_STM32MP2, + sizeof(*compartment_owner), + GFP_KERNEL); + + if (!compartment_owner) + return ERR_PTR(-ENOMEM); + + for (int i = 0; i < NB_COMPARTMENT_STM32MP2; i++) { + if (isolation_enabled) { + cid_field = devm_regmap_field_alloc(dev, + priv->config_regmap, + stm32mp25_tamp_nvram_rxcidcfg_fields[i] + ); + if (IS_ERR(cid_field)) { + kfree(compartment_owner); + return ERR_PTR(-ENOMEM); + } + + if (regmap_field_read(cid_field, &cid_per_zone)) { + dev_err(dev, "Can't read field for registers zones\n"); + devm_regmap_field_free(dev, cid_field); + kfree(compartment_owner); + return ERR_PTR(-EINVAL); + } + + if (cid_per_zone == CURRENT_CID) + compartment_owner[i] = true; + else + compartment_owner[i] = false; + + devm_regmap_field_free(dev, cid_field); + } else { + compartment_owner[i] = true; + } + } + + return compartment_owner; +} + +static enum stm32_tamp_bkpreg_access *stm32_tamp_get_access_rights_mp2x(struct stm32_tamp_nvram_priv + *priv) +{ + struct device *dev = priv->dev; + unsigned int nb_zones = priv->data->nb_zones; + bool *compartment_owner; + enum stm32_tamp_bkpreg_access *bkpreg_access; + + compartment_owner = stm32_tamp_get_compartment_owner_mp2x(priv); + if (IS_ERR(compartment_owner)) + return ERR_PTR(-ENODEV); + + bkpreg_access = devm_kcalloc(dev, + NB_ZONES_STM32MP2, + sizeof(*bkpreg_access), + GFP_KERNEL); + + if (!bkpreg_access) { + kfree(compartment_owner); + return ERR_PTR(-ENOMEM); + } + + for (int protection_zone_idx = 0; protection_zone_idx < nb_zones; + protection_zone_idx++) { + switch (protection_zone_idx) { + case BKPREG_PROTECTION_ZONE_1_RIF1: + bkpreg_access[protection_zone_idx] = BKP_NO; + break; + case BKPREG_PROTECTION_ZONE_1_RIF2: + bkpreg_access[protection_zone_idx] = BKP_NO; + break; + case BKPREG_PROTECTION_ZONE_2_RIF1: + if (compartment_owner[1] || compartment_owner[2]) + bkpreg_access[protection_zone_idx] = BKP_READ; + else + bkpreg_access[protection_zone_idx] = BKP_NO; + break; + case BKPREG_PROTECTION_ZONE_2_RIF2: + if (compartment_owner[1] || compartment_owner[2]) + bkpreg_access[protection_zone_idx] = BKP_READ; + else + bkpreg_access[protection_zone_idx] = BKP_NO; + break; + case BKPREG_PROTECTION_ZONE_3_RIF1: + if (compartment_owner[1]) + bkpreg_access[protection_zone_idx] = BKP_READ_WRITE; + else if (compartment_owner[0] || compartment_owner[2]) + bkpreg_access[protection_zone_idx] = BKP_READ; + else + bkpreg_access[protection_zone_idx] = BKP_NO; + break; + case BKPREG_PROTECTION_ZONE_3_RIF0: + if (compartment_owner[0]) + bkpreg_access[protection_zone_idx] = BKP_READ_WRITE; + else if (compartment_owner[1] || compartment_owner[2]) + bkpreg_access[protection_zone_idx] = BKP_READ; + else + bkpreg_access[protection_zone_idx] = BKP_NO; + break; + case BKPREG_PROTECTION_ZONE_3_RIF2: + if (compartment_owner[2]) + bkpreg_access[protection_zone_idx] = BKP_READ_WRITE; + else if (compartment_owner[0] || compartment_owner[1]) + bkpreg_access[protection_zone_idx] = BKP_READ; + else + bkpreg_access[protection_zone_idx] = BKP_NO; + break; + default: + devm_kfree(dev, bkpreg_access); + kfree(compartment_owner); + return ERR_PTR(-ENODEV); + } + } + + kfree(compartment_owner); + + return bkpreg_access; +} + +static int stm32_tamp_nvram_bkpreg_get_zone(struct stm32_tamp_nvram_priv *priv, int reg) +{ + int *idx_bkpreg_zones_end = priv->idx_bkpreg_zones_end; + int nb_zones = priv->data->nb_zones; + int protection_zone_idx; + + if (reg < 0) + return -1; /* negative reg is the boundary of an empty zone */ + + for (protection_zone_idx = 0; protection_zone_idx < nb_zones; protection_zone_idx++) { + if (reg <= idx_bkpreg_zones_end[protection_zone_idx]) + break; + } + + if (protection_zone_idx >= nb_zones) + return -1; /* the reg is not a part of any zone */ + + return protection_zone_idx; +} + +static bool stm32_tamp_nvram_rights(struct stm32_tamp_nvram_priv *priv, int reg, bool read_only) +{ + struct device *dev = priv->dev; + int protection_zone_idx = stm32_tamp_nvram_bkpreg_get_zone(priv, reg); + + if (protection_zone_idx < 0) + return false; + + switch (priv->bkpreg_access[protection_zone_idx]) { + case BKP_READ_WRITE: + return true; + case BKP_READ: + return read_only; + case BKP_NO: + return false; + default: + dev_err(dev, "Can't get access rights for the zone\n"); + return false; + } + + return false; +} + +static bool stm32_tamp_nvram_readable_bkpreg(struct device *dev, unsigned int reg) +{ + struct stm32_tamp_nvram_priv *priv = dev_get_drvdata(dev); + + if (!priv) + return false; + + if (!stm32_tamp_nvram_rights(priv, ALIGN_DOWN(reg, sizeof(u32)) / sizeof(u32), true)) + return false; + + return true; +} + +static bool stm32_tamp_nvram_writeable_bkpreg(struct device *dev, unsigned int reg) +{ + struct stm32_tamp_nvram_priv *priv = dev_get_drvdata(dev); + + if (!priv) + return false; + + if (!stm32_tamp_nvram_rights(priv, ALIGN_DOWN(reg, sizeof(u32)) / sizeof(u32), false)) + return false; + + return true; +} + +static const struct regmap_config stm32mp1_tamp_nvram_regmap_cfg = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .readable_reg = stm32_tamp_nvram_readable_bkpreg, + .writeable_reg = stm32_tamp_nvram_writeable_bkpreg, + .max_register = (NB_REGS_STM32MP1 - 1) * sizeof(u32), /* 0x7C */ +}; + +static const struct regmap_config stm32mp2_tamp_nvram_regmap_cfg = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .readable_reg = stm32_tamp_nvram_readable_bkpreg, + .writeable_reg = stm32_tamp_nvram_writeable_bkpreg, + .max_register = (NB_REGS_STM32MP2 - 1) * sizeof(u32), /* 0x1FC */ +}; + +static const struct stm32_tamp_nvram_plat stm32mp1_tamp_nvram = { + .nb_zones = NB_ZONES_STM32MP1, + .nb_regs = NB_REGS_STM32MP1, + .bkpregs_regmap_cfg = &stm32mp1_tamp_nvram_regmap_cfg, + .config_reg_fields = stm32mp1_tamp_nvram_zone_cfg_fields, +}; + +static const struct stm32_tamp_nvram_plat stm32mp25_tamp_nvram = { + .nb_zones = NB_ZONES_STM32MP2, + .nb_regs = NB_REGS_STM32MP2, + .bkpregs_regmap_cfg = &stm32mp2_tamp_nvram_regmap_cfg, + .config_reg_fields = stm32mp25_tamp_nvram_zone_cfg_fields, +}; + +static int stm32_tamp_nvram_write_byte(struct stm32_tamp_nvram_priv *priv, u32 offset, u8 byte) +{ + int offset_aligned = ALIGN_DOWN(offset, sizeof(u32)); + unsigned int shift = (offset - offset_aligned) * 8; + u32 reg_idx = offset_aligned / sizeof(u32); + + if (!stm32_tamp_nvram_rights(priv, reg_idx, false)) + return -EACCES; + + return regmap_update_bits(priv->bkpregs_regmap, offset_aligned, + 0xFFUL << shift, (u32)byte << shift); +} + +static int stm32_tamp_nvram_read_byte(struct stm32_tamp_nvram_priv *priv, u32 offset, u8 *byte) +{ + int offset_aligned = ALIGN_DOWN(offset, sizeof(u32)); + int byte_in_word = offset - offset_aligned; + u32 read_value; + u32 reg_idx = offset_aligned / sizeof(u32); + int ret; + + if (!stm32_tamp_nvram_rights(priv, reg_idx, true)) + return -EACCES; + + ret = regmap_read(priv->bkpregs_regmap, offset_aligned, &read_value); + if (ret) + return ret; + + *byte = (read_value >> (byte_in_word * 8)) & 0xFF; + + return 0; +} + +static int stm32_tamp_nvram_read(void *context, unsigned int offset, void *buf, size_t bytes) +{ + struct stm32_tamp_nvram_priv *priv = context; + struct device *dev = priv->dev; + u32 temp_u32; + u32 reg_idx; + int i, ret; + size_t total = offset + bytes; + u8 byte; + u8 *buf_u8 = buf; + + i = offset; + while (i < total) { + reg_idx = i / sizeof(u32); + if (i + sizeof(u32) <= total && IS_ALIGNED(i, sizeof(u32))) { + if (!stm32_tamp_nvram_rights(priv, reg_idx, true)) { + dev_dbg(dev, "Backup register %u is not allowed to be read\n", + reg_idx); + temp_u32 = 0; + } else { + ret = regmap_read(priv->bkpregs_regmap, i, &temp_u32); + if (ret) + return ret; + } + put_unaligned_le32(temp_u32, buf_u8); + buf_u8 += sizeof(u32); + i += sizeof(u32); + } else { + ret = stm32_tamp_nvram_read_byte(priv, i, &byte); + if (ret == -EACCES) { + dev_dbg(dev, "Backup register %u is not allowed to be read\n", + reg_idx); + byte = 0; + } else if (ret) { + return ret; + } + + *buf_u8 = byte; + i++; + buf_u8++; + } + } + + return 0; +} + +static int stm32_tamp_nvram_write(void *context, unsigned int offset, void *buf, size_t bytes) +{ + struct stm32_tamp_nvram_priv *priv = context; + struct device *dev = priv->dev; + u32 temp_u32; + u32 reg_idx; + int i, ret; + size_t total = offset + bytes; + u8 *buf_u8 = (u8 *)buf; + + i = offset; + while (i < total) { + reg_idx = i / sizeof(u32); + if (i + sizeof(u32) <= total && IS_ALIGNED(i, sizeof(u32))) { + if (stm32_tamp_nvram_rights(priv, reg_idx, false)) { + temp_u32 = get_unaligned_le32(buf_u8); + ret = regmap_write(priv->bkpregs_regmap, i, temp_u32); + if (ret) + return ret; + } else { + dev_dbg(dev, "Backup register %u is not allowed to be written\n", + reg_idx); + } + buf_u8 += sizeof(u32); + i += sizeof(u32); + } else { + ret = stm32_tamp_nvram_write_byte(priv, i, *buf_u8); + if (ret == -EACCES) { + dev_dbg(dev, "Backup register %u is not allowed to be written\n", + reg_idx); + } else if (ret) { + return ret; + } + + i++; + buf_u8++; + } + } + + return 0; +} + +static int *stm32_tamp_nvram_get_backup_zones(struct stm32_tamp_nvram_priv *priv) +{ + struct device *dev = priv->dev; + struct regmap *tamp_regmap = priv->config_regmap; + u32 offset_field; + int nb_zones = priv->data->nb_zones; + int zone_idx; + int *idx_bkpreg_zones_end; + + idx_bkpreg_zones_end = devm_kcalloc(dev, + nb_zones, + sizeof(*idx_bkpreg_zones_end), + GFP_KERNEL); + if (!idx_bkpreg_zones_end) { + dev_err(dev, "Can't allocate registers zones\n"); + return ERR_PTR(-ENOMEM); + } + + /* Get the n-1 frontiers of zone within the tamp configuration registers */ + for (zone_idx = 0; zone_idx < nb_zones - 1; zone_idx++) { + const struct reg_field reg_field = priv->data->config_reg_fields[zone_idx]; + struct regmap_field *field; + int ret; + + field = devm_regmap_field_alloc(dev, tamp_regmap, reg_field); + if (IS_ERR(field)) { + dev_err(dev, "Can't allocate registers zones\n"); + devm_kfree(dev, idx_bkpreg_zones_end); + return ERR_CAST(field); + } + + ret = regmap_field_read(field, &offset_field); + devm_regmap_field_free(dev, field); + if (ret) { + dev_err(dev, "Can't read field for registers zones\n"); + devm_kfree(dev, idx_bkpreg_zones_end); + return ERR_PTR(ret); + } + + idx_bkpreg_zones_end[zone_idx] = offset_field - 1; + } + + /* The last zone end is defined by the number of registers in TAMP */ + idx_bkpreg_zones_end[zone_idx] = priv->data->nb_regs - 1; + + return idx_bkpreg_zones_end; +} + +static const struct of_device_id stm32_tamp_nvram_of_match[] = { + { .compatible = "st,stm32mp15-tamp-nvram", + .data = &stm32mp1_tamp_nvram }, + { .compatible = "st,stm32mp25-tamp-nvram", + .data = &stm32mp25_tamp_nvram }, + {}, +}; + +static int stm32_tamp_nvram_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct stm32_tamp_nvram_priv *priv; + struct resource *res; + struct device_node *of_node; + const struct of_device_id *of_id; + void __iomem *base_addr; + int *zones_end; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = &pdev->dev; + + of_node = dev_of_node(dev); + if (!of_node) { + dev_err(dev, "Can't get of node of the device\n"); + return -ENODEV; + } + + of_id = of_match_node(stm32_tamp_nvram_of_match, of_node); + if (!of_id) { + dev_err(dev, "Can't get of_id of the device\n"); + return -ENODEV; + } + + priv->data = (struct stm32_tamp_nvram_plat *)of_id->data; + if (!priv->data) { + dev_err(dev, "Can't get platform data of the node\n"); + return -ENODEV; + } + + priv->config_regmap = syscon_node_to_regmap(dev->parent->of_node); + if (IS_ERR(priv->config_regmap)) + return dev_err_probe(dev, PTR_ERR(priv->config_regmap), + "Can't get TAMP syscon regmap\n"); + + priv->idx_bkpreg_zones_end = stm32_tamp_nvram_get_backup_zones(priv); + if (IS_ERR(priv->idx_bkpreg_zones_end)) + return dev_err_probe(dev, PTR_ERR(priv->idx_bkpreg_zones_end), + "Can't determine protection zone\n"); + + zones_end = priv->idx_bkpreg_zones_end; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + if (!res) { + dev_err(dev, "Can't get resource\n"); + return -ENODEV; + } + + base_addr = devm_ioremap_resource(dev, res); + if (IS_ERR(base_addr)) { + dev_err(dev, "Can't remap resource\n"); + return PTR_ERR(base_addr); + } + + priv->cfg.name = "stm32-tamp-nvram"; + priv->cfg.word_size = 1; + priv->cfg.stride = 1; + priv->cfg.dev = dev; + priv->cfg.priv = priv; + priv->cfg.owner = THIS_MODULE; + priv->cfg.type = NVMEM_TYPE_BATTERY_BACKED; + priv->cfg.size = resource_size(res); + priv->cfg.reg_read = stm32_tamp_nvram_read; + priv->cfg.reg_write = stm32_tamp_nvram_write; + + if (priv->data->nb_zones == NB_ZONES_STM32MP2) { + priv->bkpreg_access = stm32_tamp_get_access_rights_mp2x(priv); + + if (IS_ERR(priv->bkpreg_access)) + return PTR_ERR(priv->bkpreg_access); + + dev_dbg(dev, "\n" + "Zone 1-RIF1 %3d - %3d %c%c\n" + "Zone 1-RIF2 %3d - %3d %c%c\n" + "Zone 2-RIF1 %3d - %3d %c%c\n" + "Zone 2-RIF2 %3d - %3d %c%c\n" + "Zone 3-RIF1 %3d - %3d %c%c\n" + "Zone 3-RIF0 %3d - %3d %c%c\n" + "Zone 3-RIF2 %3d - %3d %c%c\n", + 0, + zones_end[BKPREG_PROTECTION_ZONE_1_RIF1], + stm32_tamp_nvram_rights(priv, + zones_end[BKPREG_PROTECTION_ZONE_1_RIF1], + true) ? 'R' : '-', + stm32_tamp_nvram_rights(priv, + zones_end[BKPREG_PROTECTION_ZONE_1_RIF1], + false) ? 'W' : '-', + zones_end[BKPREG_PROTECTION_ZONE_1_RIF1] + 1, + zones_end[BKPREG_PROTECTION_ZONE_1_RIF2], + stm32_tamp_nvram_rights(priv, + zones_end[BKPREG_PROTECTION_ZONE_1_RIF2], + true) ? 'R' : '-', + stm32_tamp_nvram_rights(priv, + zones_end[BKPREG_PROTECTION_ZONE_1_RIF2], + false) ? 'W' : '-', + zones_end[BKPREG_PROTECTION_ZONE_1_RIF2] + 1, + zones_end[BKPREG_PROTECTION_ZONE_2_RIF1], + stm32_tamp_nvram_rights(priv, + zones_end[BKPREG_PROTECTION_ZONE_2_RIF1], + true) ? 'R' : '-', + stm32_tamp_nvram_rights(priv, + zones_end[BKPREG_PROTECTION_ZONE_2_RIF1], + false) ? 'W' : '-', + zones_end[BKPREG_PROTECTION_ZONE_2_RIF1] + 1, + zones_end[BKPREG_PROTECTION_ZONE_2_RIF2], + stm32_tamp_nvram_rights(priv, + zones_end[BKPREG_PROTECTION_ZONE_2_RIF2], + true) ? 'R' : '-', + stm32_tamp_nvram_rights(priv, + zones_end[BKPREG_PROTECTION_ZONE_2_RIF2], + false) ? 'W' : '-', + zones_end[BKPREG_PROTECTION_ZONE_2_RIF2] + 1, + zones_end[BKPREG_PROTECTION_ZONE_3_RIF1], + stm32_tamp_nvram_rights(priv, + zones_end[BKPREG_PROTECTION_ZONE_3_RIF1], + true) ? 'R' : '-', + stm32_tamp_nvram_rights(priv, + zones_end[BKPREG_PROTECTION_ZONE_3_RIF1], + false) ? 'W' : '-', + zones_end[BKPREG_PROTECTION_ZONE_3_RIF1] + 1, + zones_end[BKPREG_PROTECTION_ZONE_3_RIF0], + stm32_tamp_nvram_rights(priv, + zones_end[BKPREG_PROTECTION_ZONE_3_RIF0], + true) ? 'R' : '-', + stm32_tamp_nvram_rights(priv, + zones_end[BKPREG_PROTECTION_ZONE_3_RIF0], + false) ? 'W' : '-', + zones_end[BKPREG_PROTECTION_ZONE_3_RIF0] + 1, + zones_end[BKPREG_PROTECTION_ZONE_3_RIF2], + stm32_tamp_nvram_rights(priv, + zones_end[BKPREG_PROTECTION_ZONE_3_RIF2], + true) ? 'R' : '-', + stm32_tamp_nvram_rights(priv, + zones_end[BKPREG_PROTECTION_ZONE_3_RIF2], + false) ? 'W' : '-' + ); + } else if (priv->data->nb_zones == NB_ZONES_STM32MP1) { + priv->bkpreg_access = stm32mp1_tamp_bkpreg_access; + + dev_dbg(dev, "\n" + "Zone 1 %3d - %3d %c%c\n" + "Zone 2 %3d - %3d %c%c\n" + "Zone 3 %3d - %3d %c%c\n", + 0, + zones_end[BKPREG_PROTECTION_ZONE_1], + stm32_tamp_nvram_rights(priv, + zones_end[BKPREG_PROTECTION_ZONE_1], + true) ? 'R' : '-', + stm32_tamp_nvram_rights(priv, + zones_end[BKPREG_PROTECTION_ZONE_1], + false) ? 'W' : '-', + zones_end[BKPREG_PROTECTION_ZONE_1] + 1, + zones_end[BKPREG_PROTECTION_ZONE_2], + stm32_tamp_nvram_rights(priv, + zones_end[BKPREG_PROTECTION_ZONE_2], + true) ? 'R' : '-', + stm32_tamp_nvram_rights(priv, + zones_end[BKPREG_PROTECTION_ZONE_2], + false) ? 'W' : '-', + zones_end[BKPREG_PROTECTION_ZONE_2] + 1, + zones_end[BKPREG_PROTECTION_ZONE_3], + stm32_tamp_nvram_rights(priv, + zones_end[BKPREG_PROTECTION_ZONE_3], + true) ? 'R' : '-', + stm32_tamp_nvram_rights(priv, + zones_end[BKPREG_PROTECTION_ZONE_3], + false) ? 'W' : '-'); + } + + platform_set_drvdata(pdev, priv); + priv->bkpregs_regmap = devm_regmap_init_mmio(dev, base_addr, + priv->data->bkpregs_regmap_cfg); + if (IS_ERR(priv->bkpregs_regmap)) { + dev_err(dev, "failed to init regmap: %ld\n", PTR_ERR(priv->bkpregs_regmap)); + return PTR_ERR(priv->bkpregs_regmap); + } + + return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &priv->cfg)); +} + +MODULE_DEVICE_TABLE(of, stm32_tamp_nvram_of_match); + +static struct platform_driver stm32_tamp_nvram_driver = { + .probe = stm32_tamp_nvram_probe, + .driver = { + .name = "stm32-tamp-nvram", + .of_match_table = stm32_tamp_nvram_of_match, + }, +}; + +module_platform_driver(stm32_tamp_nvram_driver) +MODULE_AUTHOR("Simeon Marijon <simeon.marijon@foss.st.com>"); +MODULE_DESCRIPTION("STMicroelectronics STM32 NVMEM TAMP Backup registers"); +MODULE_LICENSE("GPL"); -- 2.43.0 ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v3 3/3] nvmem: stm32: add TAMP nvmem driver 2026-10-16 15:26 ` [PATCH v3 3/3] nvmem: stm32: add TAMP nvmem driver Patrice Chotard @ 2026-10-17 17:57 ` Krzysztof Kozlowski 2026-11-04 13:43 ` Patrice CHOTARD 0 siblings, 1 reply; 8+ messages in thread From: Krzysztof Kozlowski @ 2026-10-17 17:57 UTC (permalink / raw) To: Patrice Chotard Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Maxime Coquelin, Alexandre Torgue, Christophe Roullier, Srinivas Kandagatla, Simeon Marijon, linux-stm32, devicetree, linux-arm-kernel, linux-kernel, Gatien Chevallier On Fri, Oct 16, 2026 at 05:26:05PM +0200, Patrice Chotard wrote: > +static int stm32_tamp_nvram_probe(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct stm32_tamp_nvram_priv *priv; > + struct resource *res; > + struct device_node *of_node; > + const struct of_device_id *of_id; > + void __iomem *base_addr; > + int *zones_end; > + > + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); > + if (!priv) > + return -ENOMEM; > + > + priv->dev = &pdev->dev; > + > + of_node = dev_of_node(dev); > + if (!of_node) { > + dev_err(dev, "Can't get of node of the device\n"); > + return -ENODEV; Entire dev_of_node and if() look not necessary. > + } > + > + of_id = of_match_node(stm32_tamp_nvram_of_match, of_node); > + if (!of_id) { > + dev_err(dev, "Can't get of_id of the device\n"); > + return -ENODEV; > + } > + > + priv->data = (struct stm32_tamp_nvram_plat *)of_id->data; Use wrapper - device_get_match_data or something similar (don't remember exactly). > + if (!priv->data) { > + dev_err(dev, "Can't get platform data of the node\n"); > + return -ENODEV; > + } > + > + priv->config_regmap = syscon_node_to_regmap(dev->parent->of_node); > + if (IS_ERR(priv->config_regmap)) > + return dev_err_probe(dev, PTR_ERR(priv->config_regmap), > + "Can't get TAMP syscon regmap\n"); > + > + priv->idx_bkpreg_zones_end = stm32_tamp_nvram_get_backup_zones(priv); > + if (IS_ERR(priv->idx_bkpreg_zones_end)) > + return dev_err_probe(dev, PTR_ERR(priv->idx_bkpreg_zones_end), > + "Can't determine protection zone\n"); > + > + zones_end = priv->idx_bkpreg_zones_end; > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + Drop blank line > + if (!res) { > + dev_err(dev, "Can't get resource\n"); > + return -ENODEV; > + } > + > + base_addr = devm_ioremap_resource(dev, res); You cannot use wrapper over these two? And I am a bit confused - device takes syscon from the parent, so how does it map also another memory? > + if (IS_ERR(base_addr)) { > + dev_err(dev, "Can't remap resource\n"); > + return PTR_ERR(base_addr); > + } > + > + priv->cfg.name = "stm32-tamp-nvram"; > + priv->cfg.word_size = 1; > + priv->cfg.stride = 1; > + priv->cfg.dev = dev; > + priv->cfg.priv = priv; > + priv->cfg.owner = THIS_MODULE; > + priv->cfg.type = NVMEM_TYPE_BATTERY_BACKED; > + priv->cfg.size = resource_size(res); > + priv->cfg.reg_read = stm32_tamp_nvram_read; > + priv->cfg.reg_write = stm32_tamp_nvram_write; > + > + if (priv->data->nb_zones == NB_ZONES_STM32MP2) { > + priv->bkpreg_access = stm32_tamp_get_access_rights_mp2x(priv); > + > + if (IS_ERR(priv->bkpreg_access)) > + return PTR_ERR(priv->bkpreg_access); > + > + dev_dbg(dev, "\n" > + "Zone 1-RIF1 %3d - %3d %c%c\n" > + "Zone 1-RIF2 %3d - %3d %c%c\n" > + "Zone 2-RIF1 %3d - %3d %c%c\n" > + "Zone 2-RIF2 %3d - %3d %c%c\n" > + "Zone 3-RIF1 %3d - %3d %c%c\n" > + "Zone 3-RIF0 %3d - %3d %c%c\n" > + "Zone 3-RIF2 %3d - %3d %c%c\n", > + 0, > + zones_end[BKPREG_PROTECTION_ZONE_1_RIF1], > + stm32_tamp_nvram_rights(priv, > + zones_end[BKPREG_PROTECTION_ZONE_1_RIF1], > + true) ? 'R' : '-', > + stm32_tamp_nvram_rights(priv, > + zones_end[BKPREG_PROTECTION_ZONE_1_RIF1], > + false) ? 'W' : '-', > + zones_end[BKPREG_PROTECTION_ZONE_1_RIF1] + 1, > + zones_end[BKPREG_PROTECTION_ZONE_1_RIF2], > + stm32_tamp_nvram_rights(priv, > + zones_end[BKPREG_PROTECTION_ZONE_1_RIF2], > + true) ? 'R' : '-', > + stm32_tamp_nvram_rights(priv, > + zones_end[BKPREG_PROTECTION_ZONE_1_RIF2], > + false) ? 'W' : '-', > + zones_end[BKPREG_PROTECTION_ZONE_1_RIF2] + 1, > + zones_end[BKPREG_PROTECTION_ZONE_2_RIF1], > + stm32_tamp_nvram_rights(priv, > + zones_end[BKPREG_PROTECTION_ZONE_2_RIF1], > + true) ? 'R' : '-', > + stm32_tamp_nvram_rights(priv, > + zones_end[BKPREG_PROTECTION_ZONE_2_RIF1], > + false) ? 'W' : '-', > + zones_end[BKPREG_PROTECTION_ZONE_2_RIF1] + 1, > + zones_end[BKPREG_PROTECTION_ZONE_2_RIF2], > + stm32_tamp_nvram_rights(priv, > + zones_end[BKPREG_PROTECTION_ZONE_2_RIF2], > + true) ? 'R' : '-', > + stm32_tamp_nvram_rights(priv, > + zones_end[BKPREG_PROTECTION_ZONE_2_RIF2], > + false) ? 'W' : '-', > + zones_end[BKPREG_PROTECTION_ZONE_2_RIF2] + 1, > + zones_end[BKPREG_PROTECTION_ZONE_3_RIF1], > + stm32_tamp_nvram_rights(priv, > + zones_end[BKPREG_PROTECTION_ZONE_3_RIF1], > + true) ? 'R' : '-', > + stm32_tamp_nvram_rights(priv, > + zones_end[BKPREG_PROTECTION_ZONE_3_RIF1], > + false) ? 'W' : '-', > + zones_end[BKPREG_PROTECTION_ZONE_3_RIF1] + 1, > + zones_end[BKPREG_PROTECTION_ZONE_3_RIF0], > + stm32_tamp_nvram_rights(priv, > + zones_end[BKPREG_PROTECTION_ZONE_3_RIF0], > + true) ? 'R' : '-', > + stm32_tamp_nvram_rights(priv, > + zones_end[BKPREG_PROTECTION_ZONE_3_RIF0], > + false) ? 'W' : '-', > + zones_end[BKPREG_PROTECTION_ZONE_3_RIF0] + 1, > + zones_end[BKPREG_PROTECTION_ZONE_3_RIF2], > + stm32_tamp_nvram_rights(priv, > + zones_end[BKPREG_PROTECTION_ZONE_3_RIF2], > + true) ? 'R' : '-', > + stm32_tamp_nvram_rights(priv, > + zones_end[BKPREG_PROTECTION_ZONE_3_RIF2], > + false) ? 'W' : '-' > + ); > + } else if (priv->data->nb_zones == NB_ZONES_STM32MP1) { > + priv->bkpreg_access = stm32mp1_tamp_bkpreg_access; > + > + dev_dbg(dev, "\n" > + "Zone 1 %3d - %3d %c%c\n" > + "Zone 2 %3d - %3d %c%c\n" > + "Zone 3 %3d - %3d %c%c\n", > + 0, > + zones_end[BKPREG_PROTECTION_ZONE_1], > + stm32_tamp_nvram_rights(priv, > + zones_end[BKPREG_PROTECTION_ZONE_1], > + true) ? 'R' : '-', > + stm32_tamp_nvram_rights(priv, > + zones_end[BKPREG_PROTECTION_ZONE_1], > + false) ? 'W' : '-', > + zones_end[BKPREG_PROTECTION_ZONE_1] + 1, > + zones_end[BKPREG_PROTECTION_ZONE_2], > + stm32_tamp_nvram_rights(priv, > + zones_end[BKPREG_PROTECTION_ZONE_2], > + true) ? 'R' : '-', > + stm32_tamp_nvram_rights(priv, > + zones_end[BKPREG_PROTECTION_ZONE_2], > + false) ? 'W' : '-', > + zones_end[BKPREG_PROTECTION_ZONE_2] + 1, > + zones_end[BKPREG_PROTECTION_ZONE_3], > + stm32_tamp_nvram_rights(priv, > + zones_end[BKPREG_PROTECTION_ZONE_3], > + true) ? 'R' : '-', > + stm32_tamp_nvram_rights(priv, > + zones_end[BKPREG_PROTECTION_ZONE_3], > + false) ? 'W' : '-'); > + } > + > + platform_set_drvdata(pdev, priv); > + priv->bkpregs_regmap = devm_regmap_init_mmio(dev, base_addr, > + priv->data->bkpregs_regmap_cfg); > + if (IS_ERR(priv->bkpregs_regmap)) { > + dev_err(dev, "failed to init regmap: %ld\n", PTR_ERR(priv->bkpregs_regmap)); > + return PTR_ERR(priv->bkpregs_regmap); return dev_err_probe Best regards, Krzysztof ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v3 3/3] nvmem: stm32: add TAMP nvmem driver 2026-10-17 17:57 ` Krzysztof Kozlowski @ 2026-11-04 13:43 ` Patrice CHOTARD 0 siblings, 0 replies; 8+ messages in thread From: Patrice CHOTARD @ 2026-11-04 13:43 UTC (permalink / raw) To: Krzysztof Kozlowski Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Maxime Coquelin, Alexandre Torgue, Christophe Roullier, Srinivas Kandagatla, Simeon Marijon, linux-stm32, devicetree, linux-arm-kernel, linux-kernel, Gatien Chevallier On 10/17/26 19:57, Krzysztof Kozlowski wrote: > On Fri, Oct 16, 2026 at 05:26:05PM +0200, Patrice Chotard wrote: >> +static int stm32_tamp_nvram_probe(struct platform_device *pdev) >> +{ >> + struct device *dev = &pdev->dev; >> + struct stm32_tamp_nvram_priv *priv; >> + struct resource *res; >> + struct device_node *of_node; >> + const struct of_device_id *of_id; >> + void __iomem *base_addr; >> + int *zones_end; >> + >> + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); >> + if (!priv) >> + return -ENOMEM; >> + >> + priv->dev = &pdev->dev; >> + >> + of_node = dev_of_node(dev); >> + if (!of_node) { >> + dev_err(dev, "Can't get of node of the device\n"); >> + return -ENODEV; > > Entire dev_of_node and if() look not necessary. Ok > >> + } >> + >> + of_id = of_match_node(stm32_tamp_nvram_of_match, of_node); >> + if (!of_id) { >> + dev_err(dev, "Can't get of_id of the device\n"); >> + return -ENODEV; >> + } >> + >> + priv->data = (struct stm32_tamp_nvram_plat *)of_id->data; > > Use wrapper - device_get_match_data or something similar (don't remember > exactly). Ok> >> + if (!priv->data) { >> + dev_err(dev, "Can't get platform data of the node\n"); >> + return -ENODEV; >> + } >> + >> + priv->config_regmap = syscon_node_to_regmap(dev->parent->of_node); >> + if (IS_ERR(priv->config_regmap)) >> + return dev_err_probe(dev, PTR_ERR(priv->config_regmap), >> + "Can't get TAMP syscon regmap\n"); >> + >> + priv->idx_bkpreg_zones_end = stm32_tamp_nvram_get_backup_zones(priv); >> + if (IS_ERR(priv->idx_bkpreg_zones_end)) >> + return dev_err_probe(dev, PTR_ERR(priv->idx_bkpreg_zones_end), >> + "Can't determine protection zone\n"); >> + >> + zones_end = priv->idx_bkpreg_zones_end; >> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >> + > > Drop blank line Ok> >> + if (!res) { >> + dev_err(dev, "Can't get resource\n"); >> + return -ENODEV; >> + } >> + >> + base_addr = devm_ioremap_resource(dev, res); > > You cannot use wrapper over these two? And I am a bit confused - device > takes syscon from the parent, so how does it map also another memory? This part will be reworked. On patch 2 of DT series [1], sashiko made a similar remark regarding the overlapping parent syscon's memory region. I will address this point in next revision. [1] https://patchwork.kernel.org/project/linux-arm-kernel/patch/20260930-upstream_tamp_nvram_dts_part-v2-2-bd73158ce46b@foss.st.com/ > >> + if (IS_ERR(base_addr)) { >> + dev_err(dev, "Can't remap resource\n"); >> + return PTR_ERR(base_addr); >> + } >> + >> + priv->cfg.name = "stm32-tamp-nvram"; >> + priv->cfg.word_size = 1; >> + priv->cfg.stride = 1; >> + priv->cfg.dev = dev; >> + priv->cfg.priv = priv; >> + priv->cfg.owner = THIS_MODULE; >> + priv->cfg.type = NVMEM_TYPE_BATTERY_BACKED; >> + priv->cfg.size = resource_size(res); >> + priv->cfg.reg_read = stm32_tamp_nvram_read; >> + priv->cfg.reg_write = stm32_tamp_nvram_write; >> + >> + if (priv->data->nb_zones == NB_ZONES_STM32MP2) { >> + priv->bkpreg_access = stm32_tamp_get_access_rights_mp2x(priv); >> + >> + if (IS_ERR(priv->bkpreg_access)) >> + return PTR_ERR(priv->bkpreg_access); >> + >> + dev_dbg(dev, "\n" >> + "Zone 1-RIF1 %3d - %3d %c%c\n" >> + "Zone 1-RIF2 %3d - %3d %c%c\n" >> + "Zone 2-RIF1 %3d - %3d %c%c\n" >> + "Zone 2-RIF2 %3d - %3d %c%c\n" >> + "Zone 3-RIF1 %3d - %3d %c%c\n" >> + "Zone 3-RIF0 %3d - %3d %c%c\n" >> + "Zone 3-RIF2 %3d - %3d %c%c\n", >> + 0, >> + zones_end[BKPREG_PROTECTION_ZONE_1_RIF1], >> + stm32_tamp_nvram_rights(priv, >> + zones_end[BKPREG_PROTECTION_ZONE_1_RIF1], >> + true) ? 'R' : '-', >> + stm32_tamp_nvram_rights(priv, >> + zones_end[BKPREG_PROTECTION_ZONE_1_RIF1], >> + false) ? 'W' : '-', >> + zones_end[BKPREG_PROTECTION_ZONE_1_RIF1] + 1, >> + zones_end[BKPREG_PROTECTION_ZONE_1_RIF2], >> + stm32_tamp_nvram_rights(priv, >> + zones_end[BKPREG_PROTECTION_ZONE_1_RIF2], >> + true) ? 'R' : '-', >> + stm32_tamp_nvram_rights(priv, >> + zones_end[BKPREG_PROTECTION_ZONE_1_RIF2], >> + false) ? 'W' : '-', >> + zones_end[BKPREG_PROTECTION_ZONE_1_RIF2] + 1, >> + zones_end[BKPREG_PROTECTION_ZONE_2_RIF1], >> + stm32_tamp_nvram_rights(priv, >> + zones_end[BKPREG_PROTECTION_ZONE_2_RIF1], >> + true) ? 'R' : '-', >> + stm32_tamp_nvram_rights(priv, >> + zones_end[BKPREG_PROTECTION_ZONE_2_RIF1], >> + false) ? 'W' : '-', >> + zones_end[BKPREG_PROTECTION_ZONE_2_RIF1] + 1, >> + zones_end[BKPREG_PROTECTION_ZONE_2_RIF2], >> + stm32_tamp_nvram_rights(priv, >> + zones_end[BKPREG_PROTECTION_ZONE_2_RIF2], >> + true) ? 'R' : '-', >> + stm32_tamp_nvram_rights(priv, >> + zones_end[BKPREG_PROTECTION_ZONE_2_RIF2], >> + false) ? 'W' : '-', >> + zones_end[BKPREG_PROTECTION_ZONE_2_RIF2] + 1, >> + zones_end[BKPREG_PROTECTION_ZONE_3_RIF1], >> + stm32_tamp_nvram_rights(priv, >> + zones_end[BKPREG_PROTECTION_ZONE_3_RIF1], >> + true) ? 'R' : '-', >> + stm32_tamp_nvram_rights(priv, >> + zones_end[BKPREG_PROTECTION_ZONE_3_RIF1], >> + false) ? 'W' : '-', >> + zones_end[BKPREG_PROTECTION_ZONE_3_RIF1] + 1, >> + zones_end[BKPREG_PROTECTION_ZONE_3_RIF0], >> + stm32_tamp_nvram_rights(priv, >> + zones_end[BKPREG_PROTECTION_ZONE_3_RIF0], >> + true) ? 'R' : '-', >> + stm32_tamp_nvram_rights(priv, >> + zones_end[BKPREG_PROTECTION_ZONE_3_RIF0], >> + false) ? 'W' : '-', >> + zones_end[BKPREG_PROTECTION_ZONE_3_RIF0] + 1, >> + zones_end[BKPREG_PROTECTION_ZONE_3_RIF2], >> + stm32_tamp_nvram_rights(priv, >> + zones_end[BKPREG_PROTECTION_ZONE_3_RIF2], >> + true) ? 'R' : '-', >> + stm32_tamp_nvram_rights(priv, >> + zones_end[BKPREG_PROTECTION_ZONE_3_RIF2], >> + false) ? 'W' : '-' >> + ); >> + } else if (priv->data->nb_zones == NB_ZONES_STM32MP1) { >> + priv->bkpreg_access = stm32mp1_tamp_bkpreg_access; >> + >> + dev_dbg(dev, "\n" >> + "Zone 1 %3d - %3d %c%c\n" >> + "Zone 2 %3d - %3d %c%c\n" >> + "Zone 3 %3d - %3d %c%c\n", >> + 0, >> + zones_end[BKPREG_PROTECTION_ZONE_1], >> + stm32_tamp_nvram_rights(priv, >> + zones_end[BKPREG_PROTECTION_ZONE_1], >> + true) ? 'R' : '-', >> + stm32_tamp_nvram_rights(priv, >> + zones_end[BKPREG_PROTECTION_ZONE_1], >> + false) ? 'W' : '-', >> + zones_end[BKPREG_PROTECTION_ZONE_1] + 1, >> + zones_end[BKPREG_PROTECTION_ZONE_2], >> + stm32_tamp_nvram_rights(priv, >> + zones_end[BKPREG_PROTECTION_ZONE_2], >> + true) ? 'R' : '-', >> + stm32_tamp_nvram_rights(priv, >> + zones_end[BKPREG_PROTECTION_ZONE_2], >> + false) ? 'W' : '-', >> + zones_end[BKPREG_PROTECTION_ZONE_2] + 1, >> + zones_end[BKPREG_PROTECTION_ZONE_3], >> + stm32_tamp_nvram_rights(priv, >> + zones_end[BKPREG_PROTECTION_ZONE_3], >> + true) ? 'R' : '-', >> + stm32_tamp_nvram_rights(priv, >> + zones_end[BKPREG_PROTECTION_ZONE_3], >> + false) ? 'W' : '-'); >> + } >> + >> + platform_set_drvdata(pdev, priv); >> + priv->bkpregs_regmap = devm_regmap_init_mmio(dev, base_addr, >> + priv->data->bkpregs_regmap_cfg); >> + if (IS_ERR(priv->bkpregs_regmap)) { >> + dev_err(dev, "failed to init regmap: %ld\n", PTR_ERR(priv->bkpregs_regmap)); >> + return PTR_ERR(priv->bkpregs_regmap); > > return dev_err_probe Ok Thanks Patrice > > > Best regards, > Krzysztof > ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-11-04 13:43 UTC | newest] Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-10-16 15:26 [PATCH v3 0/3] Add STM32 TAMP nvram driver support Patrice Chotard 2026-10-16 15:26 ` [PATCH v3 1/3] dt-bindings: nvmem: add STM32MPX TAMP backup registers Patrice Chotard 2026-10-16 15:26 ` [PATCH v3 2/3] dt-bindings: arm: stm32: allow TAMP nvram child node for syscon Patrice Chotard 2026-10-17 17:52 ` Krzysztof Kozlowski 2026-11-04 13:43 ` Patrice CHOTARD 2026-10-16 15:26 ` [PATCH v3 3/3] nvmem: stm32: add TAMP nvmem driver Patrice Chotard 2026-10-17 17:57 ` Krzysztof Kozlowski 2026-11-04 13:43 ` Patrice CHOTARD
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®