Introduce new API functions to allow looking up NVMEM devices and cells
by name, enhancing flexibility in cases where devicetree-based lookup
is not available.
Key changes:
- Added `nvmem_device_get_by_name()`: Enables retrieving an NVMEM device by
its name for systems where direct device reference is needed.
- Added `nvmem_cell_get_by_sysfs_name()`: Allows retrieving an NVMEM cell
based on its sysfs-style name (e.g., "cell@offset,bits"), making it
possible to identify cells dynamically.
- Introduced `nvmem_find_cell_entry_by_sysfs_name()`: A helper function
that constructs sysfs-like names and searches for matching cell entries.
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
---
changes v5:
- fix build we NVMEM=n
---
drivers/nvmem/core.c | 105 +++++++++++++++++++++++++++++++++
include/linux/nvmem-consumer.h | 18 ++++++
2 files changed, 123 insertions(+)
diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
index 59c295a11d86..d310fd8ca9c0 100644
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -1177,6 +1177,23 @@ struct nvmem_device *of_nvmem_device_get(struct device_node *np, const char *id)
EXPORT_SYMBOL_GPL(of_nvmem_device_get);
#endif
+/**
+ * nvmem_device_get_by_name - Look up an NVMEM device by its device name
+ * @name: String matching device name in the provider
+ *
+ * Return: A valid pointer to struct nvmem_device on success,
+ * or ERR_PTR(...) on failure. The caller must later call nvmem_device_put() to
+ * release the reference.
+ */
+struct nvmem_device *nvmem_device_get_by_name(const char *name)
+{
+ if (!name)
+ return ERR_PTR(-EINVAL);
+
+ return __nvmem_device_get((void *)name, device_match_name);
+}
+EXPORT_SYMBOL_GPL(nvmem_device_get_by_name);
+
/**
* nvmem_device_get() - Get nvmem device from a given id
*
@@ -1490,6 +1507,94 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *id)
EXPORT_SYMBOL_GPL(of_nvmem_cell_get);
#endif
+/**
+ * nvmem_find_cell_entry_by_sysfs_name - Find an NVMEM cell entry by its sysfs
+ * name.
+ * @nvmem: The nvmem_device pointer where the cell is located.
+ * @sysfs_name: The full sysfs cell name, e.g. "mycell@0x100,8".
+ *
+ * This function constructs the sysfs-like name for each cell and compares it
+ * to @sysfs_name. If a match is found, the matching nvmem_cell_entry pointer
+ * is returned. This is analogous to nvmem_find_cell_entry_by_name(), except
+ * it matches on the sysfs naming convention used in the device's attributes.
+ *
+ * Return: Pointer to the matching nvmem_cell_entry on success, or NULL if no
+ * match is found.
+ */
+static struct nvmem_cell_entry *
+nvmem_find_cell_entry_by_sysfs_name(struct nvmem_device *nvmem,
+ const char *sysfs_name)
+{
+ struct nvmem_cell_entry *entry;
+ char tmp[NVMEM_CELL_NAME_MAX];
+
+ mutex_lock(&nvmem_mutex);
+ list_for_each_entry(entry, &nvmem->cells, node) {
+ int len = snprintf(tmp, NVMEM_CELL_NAME_MAX, "%s@%x,%u",
+ entry->name, entry->offset,
+ entry->bit_offset);
+
+ if (len >= NVMEM_CELL_NAME_MAX) {
+ pr_warn("nvmem: cell name too long (max %zu bytes): %s\n",
+ NVMEM_CELL_NAME_MAX, sysfs_name);
+ continue;
+ }
+
+ if (len < 0) {
+ pr_warn("nvmem: error formatting cell name\n");
+ continue;
+ }
+
+ if (!strcmp(tmp, sysfs_name)) {
+ mutex_unlock(&nvmem_mutex);
+ return entry;
+ }
+ }
+
+ mutex_unlock(&nvmem_mutex);
+ return NULL;
+}
+
+/**
+ * nvmem_cell_get_by_sysfs_name - Retrieve an NVMEM cell using a sysfs-style
+ * name.
+ * @nvmem: Pointer to the `struct nvmem_device` containing the cell.
+ * @sysfs_name: The sysfs-style cell name, formatted as
+ * "<cell_name>@<offset>,<bits>".
+ *
+ * This function enables dynamic lookup of NVMEM cells via sysfs-style
+ * identifiers. It is useful when devicetree-based lookup is unavailable or when
+ * cells are managed dynamically.
+ *
+ * Example Usage:
+ * nvmem_cell_get_by_sysfs_name(nvmem, "mycell@0x100,8");
+ *
+ * Return: Pointer to `struct nvmem_cell` on success. On error, an ERR_PTR() is
+ * returned with the appropriate error code.
+ */
+struct nvmem_cell *nvmem_cell_get_by_sysfs_name(struct nvmem_device *nvmem,
+ const char *sysfs_name)
+{
+ struct nvmem_cell_entry *entry;
+ struct nvmem_cell *cell;
+
+ entry = nvmem_find_cell_entry_by_sysfs_name(nvmem, sysfs_name);
+ if (!entry)
+ return ERR_PTR(-ENOENT);
+
+ if (!try_module_get(nvmem->owner))
+ return ERR_PTR(-EINVAL);
+
+ kref_get(&nvmem->refcnt);
+
+ cell = nvmem_create_cell(entry, entry->name, 0);
+ if (IS_ERR(cell))
+ __nvmem_device_put(nvmem);
+
+ return cell;
+}
+EXPORT_SYMBOL_GPL(nvmem_cell_get_by_sysfs_name);
+
/**
* nvmem_cell_get() - Get nvmem cell of device form a given cell name
*
diff --git a/include/linux/nvmem-consumer.h b/include/linux/nvmem-consumer.h
index bcb0e17e415d..2b5e06f457b0 100644
--- a/include/linux/nvmem-consumer.h
+++ b/include/linux/nvmem-consumer.h
@@ -20,6 +20,10 @@ struct nvmem_cell;
struct nvmem_device;
struct nvmem_cell_info;
+#define NVMEM_CELL_NAME_MAX \
+ (sizeof("very_long_cell_name_with_some_extra_chars_12345678@0x12345678,128"))
+
+
/**
* struct nvmem_cell_lookup - cell lookup entry
*
@@ -52,6 +56,8 @@ enum {
/* Cell based interface */
struct nvmem_cell *nvmem_cell_get(struct device *dev, const char *id);
struct nvmem_cell *devm_nvmem_cell_get(struct device *dev, const char *id);
+struct nvmem_cell *nvmem_cell_get_by_sysfs_name(struct nvmem_device *nvmem,
+ const char *cell_name);
void nvmem_cell_put(struct nvmem_cell *cell);
void devm_nvmem_cell_put(struct device *dev, struct nvmem_cell *cell);
void *nvmem_cell_read(struct nvmem_cell *cell, size_t *len);
@@ -70,6 +76,7 @@ int nvmem_cell_read_variable_le_u64(struct device *dev, const char *cell_id,
struct nvmem_device *nvmem_device_get(struct device *dev, const char *name);
struct nvmem_device *devm_nvmem_device_get(struct device *dev,
const char *name);
+struct nvmem_device *nvmem_device_get_by_name(const char *name);
void nvmem_device_put(struct nvmem_device *nvmem);
void devm_nvmem_device_put(struct device *dev, struct nvmem_device *nvmem);
int nvmem_device_read(struct nvmem_device *nvmem, unsigned int offset,
@@ -109,6 +116,12 @@ static inline struct nvmem_cell *devm_nvmem_cell_get(struct device *dev,
return ERR_PTR(-EOPNOTSUPP);
}
+static inline struct nvmem_cell *
+nvmem_cell_get_by_sysfs_name(struct nvmem_device *nvmem, const char *cell_name)
+{
+ return ERR_PTR(-EOPNOTSUPP);
+}
+
static inline void devm_nvmem_cell_put(struct device *dev,
struct nvmem_cell *cell)
{
@@ -185,6 +198,11 @@ static inline struct nvmem_device *devm_nvmem_device_get(struct device *dev,
return ERR_PTR(-EOPNOTSUPP);
}
+static inline struct nvmem_device *nvmem_device_get_by_name(const char *name)
+{
+ return ERR_PTR(-EOPNOTSUPP);
+}
+
static inline void nvmem_device_put(struct nvmem_device *nvmem)
{
}
--
2.39.5
On Wed, Jun 18, 2025 at 02:02:53PM +0200, Oleksij Rempel wrote: > Introduce new API functions to allow looking up NVMEM devices and cells > by name, enhancing flexibility in cases where devicetree-based lookup > is not available. > > Key changes: > - Added `nvmem_device_get_by_name()`: Enables retrieving an NVMEM device by > its name for systems where direct device reference is needed. > - Added `nvmem_cell_get_by_sysfs_name()`: Allows retrieving an NVMEM cell > based on its sysfs-style name (e.g., "cell@offset,bits"), making it > possible to identify cells dynamically. > - Introduced `nvmem_find_cell_entry_by_sysfs_name()`: A helper function > that constructs sysfs-like names and searches for matching cell entries. > > Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de> > --- > changes v5: > - fix build we NVMEM=n > --- > drivers/nvmem/core.c | 105 +++++++++++++++++++++++++++++++++ > include/linux/nvmem-consumer.h | 18 ++++++ > 2 files changed, 123 insertions(+) > > diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c > index 59c295a11d86..d310fd8ca9c0 100644 > --- a/drivers/nvmem/core.c > +++ b/drivers/nvmem/core.c > @@ -1177,6 +1177,23 @@ struct nvmem_device *of_nvmem_device_get(struct device_node *np, const char *id) > EXPORT_SYMBOL_GPL(of_nvmem_device_get); > #endif > > +/** > + * nvmem_device_get_by_name - Look up an NVMEM device by its device name > + * @name: String matching device name in the provider > + * > + * Return: A valid pointer to struct nvmem_device on success, > + * or ERR_PTR(...) on failure. The caller must later call nvmem_device_put() to > + * release the reference. > + */ > +struct nvmem_device *nvmem_device_get_by_name(const char *name) > +{ > + if (!name) > + return ERR_PTR(-EINVAL); > + > + return __nvmem_device_get((void *)name, device_match_name); > +} > +EXPORT_SYMBOL_GPL(nvmem_device_get_by_name); > + > /** > * nvmem_device_get() - Get nvmem device from a given id > * > @@ -1490,6 +1507,94 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *id) > EXPORT_SYMBOL_GPL(of_nvmem_cell_get); > #endif > > +/** > + * nvmem_find_cell_entry_by_sysfs_name - Find an NVMEM cell entry by its sysfs > + * name. > + * @nvmem: The nvmem_device pointer where the cell is located. > + * @sysfs_name: The full sysfs cell name, e.g. "mycell@0x100,8". > + * > + * This function constructs the sysfs-like name for each cell and compares it > + * to @sysfs_name. If a match is found, the matching nvmem_cell_entry pointer > + * is returned. This is analogous to nvmem_find_cell_entry_by_name(), except > + * it matches on the sysfs naming convention used in the device's attributes. > + * > + * Return: Pointer to the matching nvmem_cell_entry on success, or NULL if no > + * match is found. > + */ > +static struct nvmem_cell_entry * > +nvmem_find_cell_entry_by_sysfs_name(struct nvmem_device *nvmem, > + const char *sysfs_name) > +{ > + struct nvmem_cell_entry *entry; > + char tmp[NVMEM_CELL_NAME_MAX]; That's a lot of bytes on the stack, are you sure? > + > + mutex_lock(&nvmem_mutex); Use a guard? > + list_for_each_entry(entry, &nvmem->cells, node) { > + int len = snprintf(tmp, NVMEM_CELL_NAME_MAX, "%s@%x,%u", You have a naming scheme here that you now need to keep in sync with a naming scheme else where. Why? How is that going to happen? sysfs names are NOT static, or deterministic, and will be totally random depending on the boot order and the phase of the moon. Attempting to look, within the kernel, at a sysfs path name is almost always the sign of something gone wrong. Please don't do this, it will only cause headaches and issues over time, trust me. > + entry->name, entry->offset, > + entry->bit_offset); > + > + if (len >= NVMEM_CELL_NAME_MAX) { > + pr_warn("nvmem: cell name too long (max %zu bytes): %s\n", > + NVMEM_CELL_NAME_MAX, sysfs_name); What can a user do about this? > + continue; Shouldn't you have errored out? > + } > + > + if (len < 0) { > + pr_warn("nvmem: error formatting cell name\n"); > + continue; No error? > + } > + > + if (!strcmp(tmp, sysfs_name)) { > + mutex_unlock(&nvmem_mutex); > + return entry; > + } > + } > + > + mutex_unlock(&nvmem_mutex); > + return NULL; > +} > + > +/** > + * nvmem_cell_get_by_sysfs_name - Retrieve an NVMEM cell using a sysfs-style > + * name. > + * @nvmem: Pointer to the `struct nvmem_device` containing the cell. > + * @sysfs_name: The sysfs-style cell name, formatted as > + * "<cell_name>@<offset>,<bits>". Again, who is keeping this naming scheme in sync? And what happens when the firmware changes it? Please don't. > + * > + * This function enables dynamic lookup of NVMEM cells via sysfs-style > + * identifiers. It is useful when devicetree-based lookup is unavailable or when > + * cells are managed dynamically. > + * > + * Example Usage: > + * nvmem_cell_get_by_sysfs_name(nvmem, "mycell@0x100,8"); > + * > + * Return: Pointer to `struct nvmem_cell` on success. On error, an ERR_PTR() is > + * returned with the appropriate error code. > + */ > +struct nvmem_cell *nvmem_cell_get_by_sysfs_name(struct nvmem_device *nvmem, > + const char *sysfs_name) > +{ > + struct nvmem_cell_entry *entry; > + struct nvmem_cell *cell; > + > + entry = nvmem_find_cell_entry_by_sysfs_name(nvmem, sysfs_name); > + if (!entry) > + return ERR_PTR(-ENOENT); > + > + if (!try_module_get(nvmem->owner)) > + return ERR_PTR(-EINVAL); Why are you messing with a module owner field, when no other nvmem call uses that? Who will decrement it? Are you sure you didn't just race with it being unloaded? module owner fields are almost always wrong these days. thanks, greg k-h
© 2016 - 2025 Red Hat, Inc.