Skip to content

Commit 44753d5

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 e91896b commit 44753d5

File tree

6 files changed

+426
-0
lines changed

6 files changed

+426
-0
lines changed

include/zephyr/nvmem.h

Lines changed: 357 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,357 @@
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.
52+
*
53+
* @note This is a helper macro for other NVMEM_CELL_GET macros to initialize the
54+
* nvmem_cell struct.
55+
*
56+
* Example devicetree fragment:
57+
*
58+
* @code{.dts}
59+
* mac_eeprom: mac_eeprom@2 {
60+
* nvmem-layout {
61+
* compatible = "fixed-layout";
62+
* #address-cells = <1>;
63+
* #size-cells = <1>;
64+
*
65+
* mac_address: mac_address@fa {
66+
* reg = <0xfa 0x06>;
67+
* #nvmem-cell-cells = <0>;
68+
* };
69+
* };
70+
* };
71+
* @endcode
72+
*
73+
* Example usage:
74+
*
75+
* @code{.c}
76+
* const struct nvmem_cell cell = NVMEM_CELL_INIT(DT_NODELABEL(mac_address));
77+
*
78+
* // Initializes 'cell' to:
79+
* // {
80+
* // .dev = DEVICE_DT_GET(DT_NODELABEL(mac_eeprom)),
81+
* // .offset = 0xfa,
82+
* // .size = 6,
83+
* // .read_only = false,
84+
* // }
85+
* @endcode
86+
*
87+
* @param node_id Devicetree node identifier.
88+
*
89+
* @return Static initializer for a struct nvmem_cell
90+
*/
91+
#define NVMEM_CELL_INIT(node_id) \
92+
{ \
93+
.dev = DEVICE_DT_GET(DT_MTD_FROM_NVMEM_CELL(node_id)), \
94+
.offset = DT_REG_ADDR(node_id), \
95+
.size = DT_REG_SIZE(node_id), \
96+
.read_only = DT_PROP(node_id, read_only), \
97+
}
98+
99+
/**
100+
* @brief Static initializer for a struct nvmem_cell.
101+
*
102+
* This returns a static initializer for a struct nvmem_cell given a devicetree
103+
* node identifier and a name.
104+
*
105+
* Example devicetree fragment:
106+
*
107+
* @code{.dts}
108+
* mac_eeprom: mac_eeprom@2 {
109+
* nvmem-layout {
110+
* compatible = "fixed-layout";
111+
* #address-cells = <1>;
112+
* #size-cells = <1>;
113+
*
114+
* mac_address: mac_address@fa {
115+
* reg = <0xfa 0x06>;
116+
* #nvmem-cell-cells = <0>;
117+
* };
118+
* };
119+
* };
120+
*
121+
* eth: ethernet {
122+
* nvmem-cells = <&mac_address>;
123+
* nvmem-cell-names = "mac-address";
124+
* };
125+
* @endcode
126+
*
127+
* Example usage:
128+
*
129+
* @code{.c}
130+
* const struct nvmem_cell cell =
131+
* NVMEM_CELL_GET_BY_NAME(DT_NODELABEL(eth), mac_address);
132+
*
133+
* // Initializes 'cell' to:
134+
* // {
135+
* // .dev = DEVICE_DT_GET(DT_NODELABEL(mac_eeprom)),
136+
* // .offset = 0xfa,
137+
* // .size = 6,
138+
* // .read_only = false,
139+
* // }
140+
* @endcode
141+
*
142+
* @param node_id Devicetree node identifier.
143+
* @param name Lowercase-and-underscores name of an nvmem-cells element as defined by
144+
* the node's nvmem-cell-names property.
145+
*
146+
* @return Static initializer for a struct nvmem_cell for the property.
147+
*
148+
* @see NVMEM_CELL_INST_GET_BY_NAME
149+
*/
150+
#define NVMEM_CELL_GET_BY_NAME(node_id, name) NVMEM_CELL_INIT(DT_NVMEM_CELL_BY_NAME(node_id, name))
151+
152+
/**
153+
* @brief Static initializer for a struct nvmem_cell from a DT_DRV_COMPAT
154+
* instance.
155+
*
156+
* @param inst DT_DRV_COMPAT instance number
157+
* @param name Lowercase-and-underscores name of an nvmem-cells element as defined by
158+
* the node's nvmem-cell-names property.
159+
*
160+
* @return Static initializer for a struct nvmem_cell for the property.
161+
*
162+
* @see NVMEM_CELL_GET_BY_NAME
163+
*/
164+
#define NVMEM_CELL_INST_GET_BY_NAME(inst, name) NVMEM_CELL_GET_BY_NAME(DT_DRV_INST(inst), name)
165+
166+
/**
167+
* @brief Like NVMEM_CELL_GET_BY_NAME(), with a fallback to a default value.
168+
*
169+
* If the devicetree node identifier 'node_id' refers to a node with a property
170+
* 'nvmem-cells', this expands to <tt>NVMEM_CELL_GET_BY_NAME(node_id, name)</tt>. The
171+
* @p default_value parameter is not expanded in this case. Otherwise, this
172+
* expands to @p default_value.
173+
*
174+
* @param node_id Devicetree node identifier.
175+
* @param name Lowercase-and-underscores name of an nvmem-cells element as defined by
176+
* the node's nvmem-cell-names property.
177+
* @param default_value Fallback value to expand to.
178+
*
179+
* @return Static initializer for a struct nvmem_cell for the property,
180+
* or @p default_value if the node or property do not exist.
181+
*
182+
* @see NVMEM_CELL_INST_GET_BY_NAME_OR
183+
*/
184+
#define NVMEM_CELL_GET_BY_NAME_OR(node_id, name, default_value) \
185+
COND_CODE_1(DT_NODE_HAS_PROP(node_id, nvmem_cells), \
186+
(NVMEM_CELL_GET_BY_NAME(node_id, name)), \
187+
(default_value))
188+
189+
/**
190+
* @brief Like NVMEM_CELL_INST_GET_BY_NAME(), with a fallback to a default
191+
* value.
192+
*
193+
* @param inst DT_DRV_COMPAT instance number
194+
* @param name Lowercase-and-underscores name of an nvmem-cells element as defined by
195+
* the node's nvmem-cell-names property.
196+
* @param default_value Fallback value to expand to.
197+
*
198+
* @return Static initializer for a struct nvmem_cell for the property,
199+
* or @p default_value if the node or property do not exist.
200+
*
201+
* @see NVMEM_CELL_GET_BY_NAME_OR
202+
*/
203+
#define NVMEM_CELL_INST_GET_BY_NAME_OR(inst, name, default_value) \
204+
NVMEM_CELL_GET_BY_NAME_OR(DT_DRV_INST(inst), name, default_value)
205+
206+
/**
207+
* @brief Static initializer for a struct nvmem_cell.
208+
*
209+
* This returns a static initializer for a struct nvmem_cell given a devicetree
210+
* node identifier and an index.
211+
*
212+
* Example devicetree fragment:
213+
*
214+
* @code{.dts}
215+
* mac_eeprom: mac_eeprom@2 {
216+
* nvmem-layout {
217+
* compatible = "fixed-layout";
218+
* #address-cells = <1>;
219+
* #size-cells = <1>;
220+
*
221+
* mac_address: mac_address@fa {
222+
* reg = <0xfa 0x06>;
223+
* #nvmem-cell-cells = <0>;
224+
* };
225+
* };
226+
* };
227+
*
228+
* eth: ethernet {
229+
* nvmem-cells = <&mac_address>;
230+
* nvmem-cell-names = "mac-address";
231+
* };
232+
* @endcode
233+
*
234+
* Example usage:
235+
*
236+
* @code{.c}
237+
* const struct nvmem_cell cell =
238+
* NVMEM_CELL_GET_BY_IDX(DT_NODELABEL(eth), 0);
239+
*
240+
* // Initializes 'cell' to:
241+
* // {
242+
* // .dev = DEVICE_DT_GET(DT_NODELABEL(mac_eeprom)),
243+
* // .offset = 0xfa,
244+
* // .size = 6,
245+
* // .read_only = false,
246+
* // }
247+
* @endcode
248+
*
249+
* @param node_id Devicetree node identifier.
250+
* @param idx Logical index into 'nvmem-cells' property.
251+
*
252+
* @return Static initializer for a struct nvmem_cell for the property.
253+
*
254+
* @see NVMEM_CELL_INST_GET_BY_IDX
255+
*/
256+
#define NVMEM_CELL_GET_BY_IDX(node_id, idx) NVMEM_CELL_INIT(DT_NVMEM_CELL_BY_IDX(node_id, idx))
257+
258+
/**
259+
* @brief Static initializer for a struct nvmem_cell from a DT_DRV_COMPAT
260+
* instance.
261+
*
262+
* @param inst DT_DRV_COMPAT instance number
263+
* @param idx Logical index into 'nvmem-cells' property.
264+
*
265+
* @return Static initializer for a struct nvmem_cell for the property.
266+
*
267+
* @see NVMEM_CELL_GET_BY_IDX
268+
*/
269+
#define NVMEM_CELL_INST_GET_BY_IDX(inst, idx) NVMEM_CELL_GET_BY_IDX(DT_DRV_INST(inst), idx)
270+
271+
/**
272+
* @brief Like NVMEM_CELL_GET_BY_IDX(), with a fallback to a default value.
273+
*
274+
* If the devicetree node identifier 'node_id' refers to a node with a property
275+
* 'nvmem-cells', this expands to <tt>NVMEM_CELL_GET_BY_IDX(node_id, idx)</tt>. The
276+
* @p default_value parameter is not expanded in this case. Otherwise, this
277+
* expands to @p default_value.
278+
*
279+
* @param node_id Devicetree node identifier.
280+
* @param idx Logical index into 'nvmem-cells' property.
281+
* @param default_value Fallback value to expand to.
282+
*
283+
* @return Static initializer for a struct nvmem_cell for the property,
284+
* or @p default_value if the node or property do not exist.
285+
*
286+
* @see NVMEM_CELL_INST_GET_BY_IDX_OR
287+
*/
288+
#define NVMEM_CELL_GET_BY_IDX_OR(node_id, idx, default_value) \
289+
COND_CODE_1(DT_NODE_HAS_PROP(node_id, nvmem_cells), \
290+
(NVMEM_CELL_GET_BY_IDX(node_id, idx)), \
291+
(default_value))
292+
293+
/**
294+
* @brief Like NVMEM_CELL_INST_GET_BY_IDX(), with a fallback to a default
295+
* value.
296+
*
297+
* @param inst DT_DRV_COMPAT instance number
298+
* @param idx Logical index into 'nvmem-cells' property.
299+
* @param default_value Fallback value to expand to.
300+
*
301+
* @return Static initializer for a struct nvmem_cell for the property,
302+
* or @p default_value if the node or property do not exist.
303+
*
304+
* @see NVMEM_CELL_GET_BY_IDX_OR
305+
*/
306+
#define NVMEM_CELL_INST_GET_BY_IDX_OR(inst, idx, default_value) \
307+
NVMEM_CELL_GET_BY_IDX_OR(DT_DRV_INST(inst), idx, default_value)
308+
309+
/**
310+
* @brief Read data from an NVMEM cell.
311+
*
312+
* @param cell The NVMEM cell.
313+
* @param data Buffer to store read data.
314+
* @param off The offset to start reading from.
315+
* @param len Number of bytes to read.
316+
*
317+
* @kconfig_dep{CONFIG_NVMEM}
318+
*
319+
* @return 0 on success, negative errno code on failure.
320+
*/
321+
int nvmem_cell_read(const struct nvmem_cell *cell, void *data, off_t off, size_t len);
322+
323+
/**
324+
* @brief Write data to an NVMEM cell.
325+
*
326+
* @param cell The NVMEM cell.
327+
* @param data Buffer with data to write.
328+
* @param off The offset to start writing to.
329+
* @param len Number of bytes to write.
330+
*
331+
* @kconfig_dep{CONFIG_NVMEM}
332+
*
333+
* @return 0 on success, negative errno code on failure.
334+
*/
335+
int nvmem_cell_write(const struct nvmem_cell *cell, const void *data, off_t off, size_t len);
336+
337+
/**
338+
* @brief Validate that the NVMEM cell is ready.
339+
*
340+
* @param cell The NVMEM cell.
341+
*
342+
* @return true if the NVMEM cell is ready for use and false otherwise.
343+
*/
344+
static inline bool nvmem_cell_is_ready(const struct nvmem_cell *cell)
345+
{
346+
return cell != NULL && device_is_ready(cell->dev);
347+
}
348+
349+
#ifdef __cplusplus
350+
}
351+
#endif
352+
353+
/**
354+
* @}
355+
*/
356+
357+
#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)