Skip to content

Commit ee9d0b0

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 ee9d0b0

File tree

6 files changed

+418
-0
lines changed

6 files changed

+418
-0
lines changed

include/zephyr/nvmem.h

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