Skip to content

Commit 2af6e6d

Browse files
committed
nvmem: Introduce new subsystem for Non-Volatile Memory layer
This commit adds the NVMEM subsystem with a basic implementation for use with EEPROM devices. Signed-off-by: Pieter De Gendt <[email protected]>
1 parent 4e4cbc5 commit 2af6e6d

File tree

6 files changed

+393
-0
lines changed

6 files changed

+393
-0
lines changed

include/zephyr/nvmem.h

Lines changed: 330 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,330 @@
1+
/*
2+
* Copyright (c) 2025 Basalte bv
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
/**
7+
* @file
8+
* @brief Main header file for NVMEM API.
9+
* @ingroup nvmem_interface
10+
*/
11+
12+
#ifndef ZEPHYR_INCLUDE_NVMEM_H_
13+
#define ZEPHYR_INCLUDE_NVMEM_H_
14+
15+
/**
16+
* @brief Interfaces for NVMEM cells.
17+
* @defgroup nvmem_interface NVMEM
18+
* @since 4.3
19+
* @version 0.1.0
20+
* @ingroup io_interfaces
21+
* @{
22+
*/
23+
24+
#include <sys/types.h>
25+
#include <zephyr/device.h>
26+
#include <zephyr/devicetree.h>
27+
#include <zephyr/devicetree/nvmem.h>
28+
29+
#ifdef __cplusplus
30+
extern "C" {
31+
#endif
32+
33+
/**
34+
* @brief Non-Volatile Memory cell representation.
35+
*/
36+
struct nvmem_cell {
37+
/** NVMEM parent controller device instance. */
38+
const struct device *dev;
39+
/** Offset of the NVMEM cell relative to the parent controller's base address */
40+
off_t offset;
41+
/** Size of the NVMEM cell */
42+
size_t size;
43+
/** Indicator if the NVMEM cell is read-write or read-only */
44+
bool read_only;
45+
};
46+
47+
/**
48+
* @brief Static initializer for a struct nvmem_cell.
49+
*
50+
* This returns a static initializer for a struct nvmem_cell given a devicetree
51+
* node identifier and a name.
52+
*
53+
* Example devicetree fragment:
54+
*
55+
* @code{.dts}
56+
* mac_eeprom: mac_eeprom@2 {
57+
* nvmem-layout {
58+
* compatible = "fixed-layout";
59+
* #address-cells = <1>;
60+
* #size-cells = <1>;
61+
* mac_address: mac_address@fa {
62+
* reg = <0xfa 0x06>;
63+
* #nvmem-cell-cells = <0>;
64+
* };
65+
* };
66+
* };
67+
*
68+
* eth: ethernet {
69+
* nvmem-cells = <&mac_address>;
70+
* nvmem-cell-names = "mac-address";
71+
* };
72+
* @endcode
73+
*
74+
* Example usage:
75+
*
76+
* @code{.c}
77+
* const struct nvmem_cell cell =
78+
* NVMEM_CELL_GET_BY_NAME(DT_NODELABEL(eth), mac_address);
79+
*
80+
* // Initializes 'cell' to:
81+
* // {
82+
* // .dev = DEVICE_DT_GET(DT_NODELABEL(mac_eeprom)),
83+
* // .offset = 0xfa,
84+
* // .size = 6,
85+
* // .read_only = false,
86+
* // }
87+
* @endcode
88+
*
89+
* @param node_id Devicetree node identifier.
90+
* @param name Lowercase-and-underscores name of an nvmem-cells element as defined by
91+
* the node's nvmem-cell-names property.
92+
*
93+
* @return Static initializer for a struct nvmem_cell for the property.
94+
*
95+
* @see NVMEM_CELL_INST_GET_BY_NAME
96+
*/
97+
#define NVMEM_CELL_GET_BY_NAME(node_id, name) \
98+
{ \
99+
.dev = DEVICE_DT_GET(DT_MTD_FROM_NVMEM_CELL(DT_NVMEM_CELL_BY_NAME(node_id, name))),\
100+
.offset = DT_REG_ADDR(DT_NVMEM_CELL_BY_NAME(node_id, name)), \
101+
.size = DT_REG_SIZE(DT_NVMEM_CELL_BY_NAME(node_id, name)), \
102+
.read_only = DT_PROP(DT_NVMEM_CELL_BY_NAME(node_id, name), read_only), \
103+
}
104+
105+
/**
106+
* @brief Static initializer for a struct nvmem_cell from a DT_DRV_COMPAT
107+
* instance.
108+
*
109+
* @param inst DT_DRV_COMPAT instance number
110+
* @param name Lowercase-and-underscores name of an nvmem-cells element as defined by
111+
* the node's nvmem-cell-names property.
112+
*
113+
* @return Static initializer for a struct nvmem_cell for the property.
114+
*
115+
* @see NVMEM_CELL_GET_BY_NAME
116+
*/
117+
#define NVMEM_CELL_INST_GET_BY_NAME(inst, name) NVMEM_CELL_GET_BY_NAME(DT_DRV_INST(inst), name)
118+
119+
/**
120+
* @brief Like NVMEM_CELL_GET_BY_NAME(), with a fallback to a default value.
121+
*
122+
* If the devicetree node identifier 'node_id' refers to a node with a property
123+
* 'nvmem-cells', this expands to <tt>NVMEM_CELL_GET_BY_NAME(node_id, name)</tt>. The
124+
* @p default_value parameter is not expanded in this case. Otherwise, this
125+
* expands to @p default_value.
126+
*
127+
* @param node_id Devicetree node identifier.
128+
* @param name Lowercase-and-underscores name of an nvmem-cells element as defined by
129+
* the node's nvmem-cell-names property.
130+
* @param default_value Fallback value to expand to.
131+
*
132+
* @return Static initializer for a struct nvmem_cell for the property,
133+
* or @p default_value if the node or property do not exist.
134+
*
135+
* @see NVMEM_CELL_INST_GET_BY_NAME_OR
136+
*/
137+
#define NVMEM_CELL_GET_BY_NAME_OR(node_id, name, default_value) \
138+
COND_CODE_1(DT_NODE_HAS_PROP(node_id, nvmem_cells), \
139+
(NVMEM_CELL_GET_BY_NAME(node_id, name)), \
140+
(default_value))
141+
142+
/**
143+
* @brief Like NVMEM_CELL_INST_GET_BY_NAME(), with a fallback to a default
144+
* value.
145+
*
146+
* @param inst DT_DRV_COMPAT instance number
147+
* @param name Lowercase-and-underscores name of an nvmem-cells element as defined by
148+
* the node's nvmem-cell-names property.
149+
* @param default_value Fallback value to expand to.
150+
*
151+
* @return Static initializer for a struct nvmem_cell for the property,
152+
* or @p default_value if the node or property do not exist.
153+
*
154+
* @see NVMEM_CELL_GET_BY_NAME_OR
155+
*/
156+
#define NVMEM_CELL_INST_GET_BY_NAME_OR(inst, name, default_value) \
157+
NVMEM_CELL_GET_BY_NAME_OR(DT_DRV_INST(inst), name, default_value)
158+
159+
/**
160+
* @brief Static initializer for a struct nvmem_cell.
161+
*
162+
* This returns a static initializer for a struct nvmem_cell given a devicetree
163+
* node identifier and an index.
164+
*
165+
* Example devicetree fragment:
166+
*
167+
* @code{.dts}
168+
* mac_eeprom: mac_eeprom@2 {
169+
* nvmem-layout {
170+
* compatible = "fixed-layout";
171+
* #address-cells = <1>;
172+
* #size-cells = <1>;
173+
* mac_address: mac_address@fa {
174+
* reg = <0xfa 0x06>;
175+
* #nvmem-cell-cells = <0>;
176+
* };
177+
* };
178+
* };
179+
*
180+
* eth: ethernet {
181+
* nvmem-cells = <&mac_address>;
182+
* nvmem-cell-names = "mac-address";
183+
* };
184+
* @endcode
185+
*
186+
* Example usage:
187+
*
188+
* @code{.c}
189+
* const struct nvmem_cell cell =
190+
* NVMEM_CELL_GET_BY_IDX(DT_NODELABEL(eth), 0);
191+
*
192+
* // Initializes 'cell' to:
193+
* // {
194+
* // .dev = DEVICE_DT_GET(DT_NODELABEL(mac_eeprom)),
195+
* // .offset = 0xfa,
196+
* // .size = 6,
197+
* // .read_only = false,
198+
* // }
199+
* @endcode
200+
*
201+
* @param node_id Devicetree node identifier.
202+
* @param idx Logical index into 'nvmem-cells' property.
203+
*
204+
* @return Static initializer for a struct nvmem_cell for the property.
205+
*
206+
* @see NVMEM_CELL_INST_GET_BY_IDX
207+
*/
208+
#define NVMEM_CELL_GET_BY_IDX(node_id, idx) \
209+
{ \
210+
.dev = DEVICE_DT_GET(DT_MTD_FROM_NVMEM_CELL(DT_NVMEM_CELL_BY_IDX(node_id, idx))), \
211+
.offset = DT_REG_ADDR(DT_NVMEM_CELL_BY_IDX(node_id, idx)), \
212+
.size = DT_REG_SIZE(DT_NVMEM_CELL_BY_IDX(node_id, idx)), \
213+
.read_only = DT_PROP(DT_NVMEM_CELL_BY_IDX(node_id, idx), read_only), \
214+
}
215+
216+
/**
217+
* @brief Static initializer for a struct nvmem_cell from a DT_DRV_COMPAT
218+
* instance.
219+
*
220+
* @param inst DT_DRV_COMPAT instance number
221+
* @param idx Logical index into 'nvmem-cells' property.
222+
*
223+
* @return Static initializer for a struct nvmem_cell for the property.
224+
*
225+
* @see NVMEM_CELL_GET_BY_IDX
226+
*/
227+
#define NVMEM_CELL_INST_GET_BY_IDX(inst, idx) NVMEM_CELL_GET_BY_IDX(DT_DRV_INST(inst), idx)
228+
229+
/**
230+
* @brief Like NVMEM_CELL_GET_BY_IDX(), with a fallback to a default value.
231+
*
232+
* If the devicetree node identifier 'node_id' refers to a node with a property
233+
* 'nvmem-cells', this expands to <tt>NVMEM_CELL_GET_BY_IDX(node_id, idx)</tt>. The
234+
* @p default_value parameter is not expanded in this case. Otherwise, this
235+
* expands to @p default_value.
236+
*
237+
* @param node_id Devicetree node identifier.
238+
* @param idx Logical index into 'nvmem-cells' property.
239+
* @param default_value Fallback value to expand to.
240+
*
241+
* @return Static initializer for a struct nvmem_cell for the property,
242+
* or @p default_value if the node or property do not exist.
243+
*
244+
* @see NVMEM_CELL_INST_GET_BY_IDX_OR
245+
*/
246+
#define NVMEM_CELL_GET_BY_IDX_OR(node_id, idx, default_value) \
247+
COND_CODE_1(DT_NODE_HAS_PROP(node_id, nvmem_cells), \
248+
(NVMEM_CELL_GET_BY_IDX(node_id, idx)), \
249+
(default_value))
250+
251+
/**
252+
* @brief Like NVMEM_CELL_INST_GET_BY_IDX(), with a fallback to a default
253+
* value.
254+
*
255+
* @param inst DT_DRV_COMPAT instance number
256+
* @param idx Logical index into 'nvmem-cells' property.
257+
* @param default_value Fallback value to expand to.
258+
*
259+
* @return Static initializer for a struct nvmem_cell for the property,
260+
* or @p default_value if the node or property do not exist.
261+
*
262+
* @see NVMEM_CELL_GET_BY_IDX_OR
263+
*/
264+
#define NVMEM_CELL_INST_GET_BY_IDX_OR(inst, idx, default_value) \
265+
NVMEM_CELL_GET_BY_IDX_OR(DT_DRV_INST(inst), idx, default_value)
266+
267+
#if defined(CONFIG_NVMEM) || defined(__DOXYGEN__)
268+
269+
/**
270+
* @brief Read data from an NVMEM cell.
271+
*
272+
* @param cell The NVMEM cell.
273+
* @param data Buffer to store read data.
274+
* @param off The offset to start reading from.
275+
* @param len Number of bytes to read.
276+
*
277+
* @kconfig_dep{CONFIG_NVMEM}
278+
*
279+
* @return 0 on success, negative errno code on failure.
280+
*/
281+
int nvmem_cell_read(const struct nvmem_cell *cell, void *data, off_t off, size_t len);
282+
283+
/**
284+
* @brief Write data to an NVMEM cell.
285+
*
286+
* @param cell The NVMEM cell.
287+
* @param data Buffer with data to write.
288+
* @param off The offset to start writing to.
289+
* @param len Number of bytes to write.
290+
*
291+
* @kconfig_dep{CONFIG_NVMEM}
292+
*
293+
* @return 0 on success, negative errno code on failure.
294+
*/
295+
int nvmem_cell_write(const struct nvmem_cell *cell, const void *data, off_t off, size_t len);
296+
297+
#else /* defined(CONFIG_NVMEM) || defined(__DOXYGEN__) */
298+
299+
static inline int nvmem_cell_read(const struct nvmem_cell *cell, void *data, off_t off, size_t len)
300+
{
301+
ARG_UNUSED(cell);
302+
ARG_UNUSED(data);
303+
ARG_UNUSED(off);
304+
ARG_UNUSED(len);
305+
306+
return -ENOTSUP;
307+
}
308+
309+
static inline int nvmem_cell_write(const struct nvmem_cell *cell, const void *data, off_t off,
310+
size_t len)
311+
{
312+
ARG_UNUSED(cell);
313+
ARG_UNUSED(data);
314+
ARG_UNUSED(off);
315+
ARG_UNUSED(len);
316+
317+
return -ENOTSUP;
318+
}
319+
320+
#endif /* defined(CONFIG_NVMEM) || defined(__DOXYGEN__) */
321+
322+
#ifdef __cplusplus
323+
}
324+
#endif
325+
326+
/**
327+
* @}
328+
*/
329+
330+
#endif /* ZEPHYR_INCLUDE_NVMEM_H_ */

subsys/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ add_subdirectory_ifdef(CONFIG_JWT jwt)
5353
add_subdirectory_ifdef(CONFIG_LLEXT llext)
5454
add_subdirectory_ifdef(CONFIG_MODEM_MODULES modem)
5555
add_subdirectory_ifdef(CONFIG_NETWORKING net)
56+
add_subdirectory_ifdef(CONFIG_NVMEM nvmem)
5657
add_subdirectory_ifdef(CONFIG_PROFILING profiling)
5758
add_subdirectory_ifdef(CONFIG_RETENTION retention)
5859
add_subdirectory_ifdef(CONFIG_SECURE_STORAGE secure_storage)

subsys/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ source "subsys/mgmt/Kconfig"
3535
source "subsys/modbus/Kconfig"
3636
source "subsys/modem/Kconfig"
3737
source "subsys/net/Kconfig"
38+
source "subsys/nvmem/Kconfig"
3839
source "subsys/pm/Kconfig"
3940
source "subsys/pmci/Kconfig"
4041
source "subsys/portability/Kconfig"

subsys/nvmem/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
zephyr_library()
4+
5+
zephyr_library_sources(nvmem.c)

subsys/nvmem/Kconfig

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Copyright (c) 2025 Basalte bv
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
menuconfig NVMEM
5+
bool "Non Volatile Memory (NVMEM)"
6+
7+
if NVMEM
8+
9+
config NVMEM_EEPROM
10+
bool "NVMEM support for EEPROM devices"
11+
default y
12+
depends on EEPROM
13+
14+
module = NVMEM
15+
module-str = nvmem
16+
source "subsys/logging/Kconfig.template.log_config"
17+
18+
endif # NVMEM

0 commit comments

Comments
 (0)