Skip to content

Commit 191a857

Browse files
committed
nvmem: Introduce EEPROM NVMEM support
wip Signed-off-by: Pieter De Gendt <[email protected]>
1 parent fc96fec commit 191a857

File tree

8 files changed

+439
-1
lines changed

8 files changed

+439
-1
lines changed

dts/bindings/mtd/eeprom-base.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33

44
# Common fields for EEPROM devices
55

6-
include: base.yaml
6+
include:
7+
- base.yaml
8+
- nvmem-provider.yaml
79

810
properties:
911
size:
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
description: |
4+
Non-Volatile Memory (NVMEM) provider.
5+
6+
NVMEM provider refers to an entity that implements methods to
7+
initialize, read and write the non-volatile memory.
8+
9+
properties:
10+
"#address-cells":
11+
type: int
12+
description: |
13+
Number of cells required to represent a child node's
14+
reg property address.
15+
16+
"#size-cells":
17+
type: int
18+
description: |
19+
Number of cells required to represent a child node's size.
20+
21+
child-binding:
22+
description: |
23+
Each child node of the NVMEM provider node represents
24+
an individual NVMEM cell. These should usually
25+
look like this:
26+
27+
cell_nodelabel: cell@START_OFFSET {
28+
reg = <0xSTART_OFFSET 0xSIZE>;
29+
#nvmem-cell-cells = <0>;
30+
};
31+
32+
properties:
33+
reg:
34+
type: array
35+
required: true
36+
description: |
37+
This should be in the format <OFFSET SIZE>, where OFFSET
38+
is the offset of the NVMEM cell relative to the base
39+
address of the parent memory, and SIZE is the size of
40+
the cell in bytes.
41+
42+
read-only:
43+
type: boolean
44+
description: Set this property if the cell is read-only.
45+
46+
bits:
47+
type: array
48+
description: |
49+
Is pair of bit location and number of bits, which specifies
50+
offset in bit and number of bits within the address range
51+
specified by reg property. Offset takes values from 0-7.

include/zephyr/nvmem.h

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

0 commit comments

Comments
 (0)