diff mbox series

[v2,13/16] nvmem: add support for cell lookups from machine code

Message ID 20180907100750.14564-14-brgl@bgdev.pl (mailing list archive)
State Superseded, archived
Headers show
Series nvmem: rework of the subsystem for non-DT users | expand

Commit Message

Bartosz Golaszewski Sept. 7, 2018, 10:07 a.m. UTC
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>

Add a way for machine code users to associate devices with nvmem cells.

Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
 drivers/nvmem/core.c          | 143 +++++++++++++++++++++++++++-------
 include/linux/nvmem-machine.h |  16 ++++
 2 files changed, 132 insertions(+), 27 deletions(-)

Comments

Srinivas Kandagatla Sept. 10, 2018, 7:32 a.m. UTC | #1
On 07/09/18 11:07, Bartosz Golaszewski wrote:
> From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
> 
> Add a way for machine code users to associate devices with nvmem cells.
> 
> Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
> ---
>   drivers/nvmem/core.c          | 143 +++++++++++++++++++++++++++-------
>   include/linux/nvmem-machine.h |  16 ++++
>   2 files changed, 132 insertions(+), 27 deletions(-)
> 
> diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
> index da7a9d5beb33..9e2f9c993a07 100644
> --- a/drivers/nvmem/core.c
> +++ b/drivers/nvmem/core.c
> @@ -62,6 +62,9 @@ static DEFINE_IDA(nvmem_ida);
>   static DEFINE_MUTEX(nvmem_cell_mutex);
>   static LIST_HEAD(nvmem_cell_tables);
>   
> +static DEFINE_MUTEX(nvmem_lookup_mutex);
> +static LIST_HEAD(nvmem_lookup_list);
> +
>   static BLOCKING_NOTIFIER_HEAD(nvmem_notifier);
>   
>   #ifdef CONFIG_DEBUG_LOCK_ALLOC
> @@ -285,6 +288,18 @@ static struct nvmem_device *of_nvmem_find(struct device_node *nvmem_np)
>   	return to_nvmem_device(d);
>   }
>   
> +static struct nvmem_device *nvmem_find(const char *name)
> +{
> +	struct device *d;
> +
> +	d = bus_find_device_by_name(&nvmem_bus_type, NULL, name);
> +
> +	if (!d)
> +		return NULL;
> +
> +	return to_nvmem_device(d);
> +}
> +
This is removed and added back in same patch, you should consider 
positioning the caller if possible to avoid any un-necessary changes.

>   static void nvmem_cell_drop(struct nvmem_cell *cell)
>   {
>   	mutex_lock(&nvmem_mutex);
> @@ -421,6 +436,21 @@ nvmem_find_cell_by_index(struct nvmem_device *nvmem, int index)
>   	return cell;
>   }
>   
> +static struct nvmem_cell *
> +nvmem_cell_get_from_lookup(struct device *dev, const char *con_id)
> +{
> +	struct nvmem_cell *cell = ERR_PTR(-ENOENT);
> +	struct nvmem_cell_lookup *lookup;
> +	struct nvmem_device *nvmem;
> +	const char *dev_id;
> +
> +	if (!dev)
> +		return ERR_PTR(-EINVAL);
> +
> +	dev_id = dev_name(dev);
> +
> +	mutex_lock(&nvmem_lookup_mutex);
> +
> +	list_for_each_entry(lookup, &nvmem_lookup_list, node) {
> +		if ((strcmp(lookup->dev_id, dev_id) == 0) &&
> +		    (strcmp(lookup->con_id, con_id) == 0)) {
> +			/* This is the right entry. */
> +			nvmem = __nvmem_device_get(NULL, lookup->nvmem_name);
> +			if (!nvmem) {
> +				/* Provider may not be registered yet. */
> +				cell = ERR_PTR(-EPROBE_DEFER);
> +				goto out;
> +			}
> +
> +			cell = nvmem_find_cell_by_name(nvmem,
> +						       lookup->cell_name);
> +			if (!cell)
> +				goto out;
Here nvmem refcount has already increased, you should probably fix this!
> +		}
> +	}
> +
> +out:
> +	mutex_unlock(&nvmem_lookup_mutex);
> +	return cell;
> +}

...

> diff --git a/include/linux/nvmem-machine.h b/include/linux/nvmem-machine.h

Should be part of nvmem-consumer.h.

> index 1e199dfaacab..7859c08934d5 100644
> --- a/include/linux/nvmem-machine.h
> +++ b/include/linux/nvmem-machine.h
> @@ -26,16 +26,32 @@ struct nvmem_cell_table {
>   	struct list_head	node;
>   };
>   
> +struct nvmem_cell_lookup {
> +	const char		*nvmem_name;
> +	const char		*cell_name;
> +	const char		*dev_id;
> +	const char		*con_id;
> +	struct list_head	node;
> +};

Consider adding kerneldoc to this structure.

> +
>   #if IS_ENABLED(CONFIG_NVMEM)
>   
>   void nvmem_add_cell_table(struct nvmem_cell_table *table);
>   void nvmem_del_cell_table(struct nvmem_cell_table *table);
>   
> +void nvmem_add_cell_lookups(struct nvmem_cell_lookup *entries, size_t nentries);
> +void nvmem_del_cell_lookups(struct nvmem_cell_lookup *entries, size_t nentries);
> +
>   #else /* CONFIG_NVMEM */
>   
>   static inline void nvmem_add_cell_table(struct nvmem_cell_table *table) {}
>   static inline void nvmem_del_cell_table(struct nvmem_cell_table *table) {}
>   
> +static inline void
> +nvmem_add_cell_lookups(struct nvmem_cell_lookup *entries, size_t nentries) {}
> +static inline void
> +nvmem_del_cell_lookups(struct nvmem_cell_lookup *entries, size_t nentries) {}
> +
>   #endif /* CONFIG_NVMEM */
>   
>   #endif  /* ifndef _LINUX_NVMEM_MACHINE_H */
>
Bartosz Golaszewski Sept. 10, 2018, 8:17 a.m. UTC | #2
2018-09-10 9:32 GMT+02:00 Srinivas Kandagatla <srinivas.kandagatla@linaro.org>:
>
>
> On 07/09/18 11:07, Bartosz Golaszewski wrote:
>>
>> From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
>>
>> Add a way for machine code users to associate devices with nvmem cells.
>>
>> Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
>> ---
>>   drivers/nvmem/core.c          | 143 +++++++++++++++++++++++++++-------
>>   include/linux/nvmem-machine.h |  16 ++++
>>   2 files changed, 132 insertions(+), 27 deletions(-)
>>
>> diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
>> index da7a9d5beb33..9e2f9c993a07 100644
>> --- a/drivers/nvmem/core.c
>> +++ b/drivers/nvmem/core.c
>> @@ -62,6 +62,9 @@ static DEFINE_IDA(nvmem_ida);
>>   static DEFINE_MUTEX(nvmem_cell_mutex);
>>   static LIST_HEAD(nvmem_cell_tables);
>>   +static DEFINE_MUTEX(nvmem_lookup_mutex);
>> +static LIST_HEAD(nvmem_lookup_list);
>> +
>>   static BLOCKING_NOTIFIER_HEAD(nvmem_notifier);
>>     #ifdef CONFIG_DEBUG_LOCK_ALLOC
>> @@ -285,6 +288,18 @@ static struct nvmem_device *of_nvmem_find(struct
>> device_node *nvmem_np)
>>         return to_nvmem_device(d);
>>   }
>>   +static struct nvmem_device *nvmem_find(const char *name)
>> +{
>> +       struct device *d;
>> +
>> +       d = bus_find_device_by_name(&nvmem_bus_type, NULL, name);
>> +
>> +       if (!d)
>> +               return NULL;
>> +
>> +       return to_nvmem_device(d);
>> +}
>> +
>
> This is removed and added back in same patch, you should consider
> positioning the caller if possible to avoid any un-necessary changes.
>
>>   static void nvmem_cell_drop(struct nvmem_cell *cell)
>>   {
>>         mutex_lock(&nvmem_mutex);
>> @@ -421,6 +436,21 @@ nvmem_find_cell_by_index(struct nvmem_device *nvmem,
>> int index)
>>         return cell;
>>   }
>>   +static struct nvmem_cell *
>> +nvmem_cell_get_from_lookup(struct device *dev, const char *con_id)
>> +{
>> +       struct nvmem_cell *cell = ERR_PTR(-ENOENT);
>> +       struct nvmem_cell_lookup *lookup;
>> +       struct nvmem_device *nvmem;
>> +       const char *dev_id;
>> +
>> +       if (!dev)
>> +               return ERR_PTR(-EINVAL);
>> +
>> +       dev_id = dev_name(dev);
>> +
>> +       mutex_lock(&nvmem_lookup_mutex);
>> +
>> +       list_for_each_entry(lookup, &nvmem_lookup_list, node) {
>> +               if ((strcmp(lookup->dev_id, dev_id) == 0) &&
>> +                   (strcmp(lookup->con_id, con_id) == 0)) {
>> +                       /* This is the right entry. */
>> +                       nvmem = __nvmem_device_get(NULL,
>> lookup->nvmem_name);
>> +                       if (!nvmem) {
>> +                               /* Provider may not be registered yet. */
>> +                               cell = ERR_PTR(-EPROBE_DEFER);
>> +                               goto out;
>> +                       }
>> +
>> +                       cell = nvmem_find_cell_by_name(nvmem,
>> +                                                      lookup->cell_name);
>> +                       if (!cell)
>> +                               goto out;
>
> Here nvmem refcount has already increased, you should probably fix this!

Indeed.

>>
>> +               }
>> +       }
>> +
>> +out:
>> +       mutex_unlock(&nvmem_lookup_mutex);
>> +       return cell;
>> +}
>
>
> ...
>
>> diff --git a/include/linux/nvmem-machine.h b/include/linux/nvmem-machine.h
>
>
> Should be part of nvmem-consumer.h.
>

If anything, this should probably go to nvmem-provider.h. But I like
the gpiolib way of putting machine-specific code into a separate
header. Most systems are not interested in these definitions anyway.
IMO this is a valid use case where creating a new header makes sense.

Bart

>> index 1e199dfaacab..7859c08934d5 100644
>> --- a/include/linux/nvmem-machine.h
>> +++ b/include/linux/nvmem-machine.h
>> @@ -26,16 +26,32 @@ struct nvmem_cell_table {
>>         struct list_head        node;
>>   };
>>   +struct nvmem_cell_lookup {
>> +       const char              *nvmem_name;
>> +       const char              *cell_name;
>> +       const char              *dev_id;
>> +       const char              *con_id;
>> +       struct list_head        node;
>> +};
>
>
> Consider adding kerneldoc to this structure.
>
>
>> +
>>   #if IS_ENABLED(CONFIG_NVMEM)
>>     void nvmem_add_cell_table(struct nvmem_cell_table *table);
>>   void nvmem_del_cell_table(struct nvmem_cell_table *table);
>>   +void nvmem_add_cell_lookups(struct nvmem_cell_lookup *entries, size_t
>> nentries);
>> +void nvmem_del_cell_lookups(struct nvmem_cell_lookup *entries, size_t
>> nentries);
>> +
>>   #else /* CONFIG_NVMEM */
>>     static inline void nvmem_add_cell_table(struct nvmem_cell_table
>> *table) {}
>>   static inline void nvmem_del_cell_table(struct nvmem_cell_table *table)
>> {}
>>   +static inline void
>> +nvmem_add_cell_lookups(struct nvmem_cell_lookup *entries, size_t
>> nentries) {}
>> +static inline void
>> +nvmem_del_cell_lookups(struct nvmem_cell_lookup *entries, size_t
>> nentries) {}
>> +
>>   #endif /* CONFIG_NVMEM */
>>     #endif  /* ifndef _LINUX_NVMEM_MACHINE_H */
>>
>
Boris Brezillon Sept. 10, 2018, 8:23 a.m. UTC | #3
On Mon, 10 Sep 2018 10:17:48 +0200
Bartosz Golaszewski <brgl@bgdev.pl> wrote:

> 2018-09-10 9:32 GMT+02:00 Srinivas Kandagatla <srinivas.kandagatla@linaro.org>:
> >
> >
> > On 07/09/18 11:07, Bartosz Golaszewski wrote:  
> >>
> >> From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
> >>
> >> Add a way for machine code users to associate devices with nvmem cells.
> >>
> >> Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
> >> ---
> >>   drivers/nvmem/core.c          | 143 +++++++++++++++++++++++++++-------
> >>   include/linux/nvmem-machine.h |  16 ++++
> >>   2 files changed, 132 insertions(+), 27 deletions(-)
> >>
> >> diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
> >> index da7a9d5beb33..9e2f9c993a07 100644
> >> --- a/drivers/nvmem/core.c
> >> +++ b/drivers/nvmem/core.c
> >> @@ -62,6 +62,9 @@ static DEFINE_IDA(nvmem_ida);
> >>   static DEFINE_MUTEX(nvmem_cell_mutex);
> >>   static LIST_HEAD(nvmem_cell_tables);
> >>   +static DEFINE_MUTEX(nvmem_lookup_mutex);
> >> +static LIST_HEAD(nvmem_lookup_list);
> >> +
> >>   static BLOCKING_NOTIFIER_HEAD(nvmem_notifier);
> >>     #ifdef CONFIG_DEBUG_LOCK_ALLOC
> >> @@ -285,6 +288,18 @@ static struct nvmem_device *of_nvmem_find(struct
> >> device_node *nvmem_np)
> >>         return to_nvmem_device(d);
> >>   }
> >>   +static struct nvmem_device *nvmem_find(const char *name)
> >> +{
> >> +       struct device *d;
> >> +
> >> +       d = bus_find_device_by_name(&nvmem_bus_type, NULL, name);
> >> +
> >> +       if (!d)
> >> +               return NULL;
> >> +
> >> +       return to_nvmem_device(d);
> >> +}
> >> +  
> >
> > This is removed and added back in same patch, you should consider
> > positioning the caller if possible to avoid any un-necessary changes.
> >  
> >>   static void nvmem_cell_drop(struct nvmem_cell *cell)
> >>   {
> >>         mutex_lock(&nvmem_mutex);
> >> @@ -421,6 +436,21 @@ nvmem_find_cell_by_index(struct nvmem_device *nvmem,
> >> int index)
> >>         return cell;
> >>   }
> >>   +static struct nvmem_cell *
> >> +nvmem_cell_get_from_lookup(struct device *dev, const char *con_id)
> >> +{
> >> +       struct nvmem_cell *cell = ERR_PTR(-ENOENT);
> >> +       struct nvmem_cell_lookup *lookup;
> >> +       struct nvmem_device *nvmem;
> >> +       const char *dev_id;
> >> +
> >> +       if (!dev)
> >> +               return ERR_PTR(-EINVAL);
> >> +
> >> +       dev_id = dev_name(dev);
> >> +
> >> +       mutex_lock(&nvmem_lookup_mutex);
> >> +
> >> +       list_for_each_entry(lookup, &nvmem_lookup_list, node) {
> >> +               if ((strcmp(lookup->dev_id, dev_id) == 0) &&
> >> +                   (strcmp(lookup->con_id, con_id) == 0)) {
> >> +                       /* This is the right entry. */
> >> +                       nvmem = __nvmem_device_get(NULL,
> >> lookup->nvmem_name);
> >> +                       if (!nvmem) {
> >> +                               /* Provider may not be registered yet. */
> >> +                               cell = ERR_PTR(-EPROBE_DEFER);
> >> +                               goto out;
> >> +                       }
> >> +
> >> +                       cell = nvmem_find_cell_by_name(nvmem,
> >> +                                                      lookup->cell_name);
> >> +                       if (!cell)
> >> +                               goto out;  
> >
> > Here nvmem refcount has already increased, you should probably fix this!  
> 
> Indeed.
> 
> >>
> >> +               }
> >> +       }
> >> +
> >> +out:
> >> +       mutex_unlock(&nvmem_lookup_mutex);
> >> +       return cell;
> >> +}  
> >
> >
> > ...
> >  
> >> diff --git a/include/linux/nvmem-machine.h b/include/linux/nvmem-machine.h  
> >
> >
> > Should be part of nvmem-consumer.h.
> >  
> 
> If anything, this should probably go to nvmem-provider.h.

Well, if we get rid of nvmem-machine.h, the cell-lookup stuff
should go in nvmem-consumer.h not nvmem-provider.h. On the other hand,
everything that is related to cell creation should be placed in
nvmem-provider.h.

> But I like
> the gpiolib way of putting machine-specific code into a separate
> header. Most systems are not interested in these definitions anyway.
> IMO this is a valid use case where creating a new header makes sense.
Srinivas Kandagatla Sept. 10, 2018, 8:55 a.m. UTC | #4
On 10/09/18 09:23, Boris Brezillon wrote:
> Well, if we get rid of nvmem-machine.h, the cell-lookup stuff
> should go in nvmem-consumer.h not nvmem-provider.h. On the other hand,
> everything that is related to cell creation should be placed in
> nvmem-provider.h.
Yes, this is how it should be!

--srini
Bartosz Golaszewski Sept. 10, 2018, 9:45 a.m. UTC | #5
2018-09-10 10:55 GMT+02:00 Srinivas Kandagatla <srinivas.kandagatla@linaro.org>:
>
>
> On 10/09/18 09:23, Boris Brezillon wrote:
>>
>> Well, if we get rid of nvmem-machine.h, the cell-lookup stuff
>> should go in nvmem-consumer.h not nvmem-provider.h. On the other hand,
>> everything that is related to cell creation should be placed in
>> nvmem-provider.h.
>
> Yes, this is how it should be!
>

Any actual reason for not putting these definitions into a separate
'machine' header? This approach is currently used by gpio, pinctrl,
iio and regulator framework because most systems use either DT or ACPI
and don't need to pull in any stuff aimed at board files.

Bart
Boris Brezillon Sept. 10, 2018, 9:49 a.m. UTC | #6
On Mon, 10 Sep 2018 11:45:48 +0200
Bartosz Golaszewski <brgl@bgdev.pl> wrote:

> 2018-09-10 10:55 GMT+02:00 Srinivas Kandagatla <srinivas.kandagatla@linaro.org>:
> >
> >
> > On 10/09/18 09:23, Boris Brezillon wrote:  
> >>
> >> Well, if we get rid of nvmem-machine.h, the cell-lookup stuff
> >> should go in nvmem-consumer.h not nvmem-provider.h. On the other hand,
> >> everything that is related to cell creation should be placed in
> >> nvmem-provider.h.  
> >
> > Yes, this is how it should be!
> >  
> 
> Any actual reason for not putting these definitions into a separate
> 'machine' header? This approach is currently used by gpio, pinctrl,
> iio and regulator framework because most systems use either DT or ACPI
> and don't need to pull in any stuff aimed at board files.

I'm perfectly fine with the separate header file, all I'm saying is, if
Srinivas does not want nvmem-machine.h, definitions should be placed in
nvmem-provider.h or nvmem-consumer.h depending on who they're meant to
be used by (providers or consumers).
Srinivas Kandagatla Sept. 10, 2018, 9:50 a.m. UTC | #7
On 10/09/18 10:45, Bartosz Golaszewski wrote:
>> Yes, this is how it should be!
>>
> Any actual reason for not putting these definitions into a separate
> 'machine' header? This approach is currently used by gpio, pinctrl,
> iio and regulator framework because most systems use either DT or ACPI
> and don't need to pull in any stuff aimed at board files.

I don't want to create header files specific to usecase!
Lets keep it simple!

--srini
Bartosz Golaszewski Sept. 10, 2018, 11:26 a.m. UTC | #8
2018-09-10 11:50 GMT+02:00 Srinivas Kandagatla <srinivas.kandagatla@linaro.org>:
>
>
> On 10/09/18 10:45, Bartosz Golaszewski wrote:
>>>
>>> Yes, this is how it should be!
>>>
>> Any actual reason for not putting these definitions into a separate
>> 'machine' header? This approach is currently used by gpio, pinctrl,
>> iio and regulator framework because most systems use either DT or ACPI
>> and don't need to pull in any stuff aimed at board files.
>
>
> I don't want to create header files specific to usecase!
> Lets keep it simple!
>

I won't argue this point anymore, but I disagree. This is not specific
to a usecase but to a whole family of users that need to a) define
nvmem cells without knowing the provider and b) associate them with
consumers. Something normal providers and consumers are not bothered
by, thus a new header file would be in order.

But as you wish...

Bart
diff mbox series

Patch

diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
index da7a9d5beb33..9e2f9c993a07 100644
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -62,6 +62,9 @@  static DEFINE_IDA(nvmem_ida);
 static DEFINE_MUTEX(nvmem_cell_mutex);
 static LIST_HEAD(nvmem_cell_tables);
 
+static DEFINE_MUTEX(nvmem_lookup_mutex);
+static LIST_HEAD(nvmem_lookup_list);
+
 static BLOCKING_NOTIFIER_HEAD(nvmem_notifier);
 
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
@@ -285,6 +288,18 @@  static struct nvmem_device *of_nvmem_find(struct device_node *nvmem_np)
 	return to_nvmem_device(d);
 }
 
+static struct nvmem_device *nvmem_find(const char *name)
+{
+	struct device *d;
+
+	d = bus_find_device_by_name(&nvmem_bus_type, NULL, name);
+
+	if (!d)
+		return NULL;
+
+	return to_nvmem_device(d);
+}
+
 static void nvmem_cell_drop(struct nvmem_cell *cell)
 {
 	mutex_lock(&nvmem_mutex);
@@ -421,6 +436,21 @@  nvmem_find_cell_by_index(struct nvmem_device *nvmem, int index)
 	return cell;
 }
 
+static struct nvmem_cell *
+nvmem_find_cell_by_name(struct nvmem_device *nvmem, const char *cell_id)
+{
+	struct nvmem_cell *cell = NULL;
+
+	mutex_lock(&nvmem_mutex);
+	list_for_each_entry(cell, &nvmem->cells, node) {
+		if (strcmp(cell_id, cell->name) == 0)
+			break;
+	}
+	mutex_unlock(&nvmem_mutex);
+
+	return cell;
+}
+
 static int nvmem_add_cells_from_of(struct nvmem_device *nvmem)
 {
 	struct device_node *parent, *child;
@@ -691,22 +721,16 @@  int devm_nvmem_unregister(struct device *dev, struct nvmem_device *nvmem)
 }
 EXPORT_SYMBOL(devm_nvmem_unregister);
 
-static struct nvmem_device *__nvmem_device_get(struct device_node *np)
+static struct nvmem_device *
+__nvmem_device_get(struct device_node *np, const char *name)
 {
 	struct nvmem_device *nvmem = NULL;
 
-	if (!np)
-		return ERR_PTR(-EINVAL);
-
 	mutex_lock(&nvmem_mutex);
-
-	nvmem = of_nvmem_find(np);
-	if (!nvmem) {
-		mutex_unlock(&nvmem_mutex);
-		return ERR_PTR(-EPROBE_DEFER);
-	}
-
+	nvmem = np ? of_nvmem_find(np) : nvmem_find(name);
 	mutex_unlock(&nvmem_mutex);
+	if (!nvmem)
+		return ERR_PTR(-EPROBE_DEFER);
 
 	if (!try_module_get(nvmem->owner)) {
 		dev_err(&nvmem->dev,
@@ -726,18 +750,6 @@  static void __nvmem_device_put(struct nvmem_device *nvmem)
 	kref_put(&nvmem->refcnt, nvmem_device_release);
 }
 
-static struct nvmem_device *nvmem_find(const char *name)
-{
-	struct device *d;
-
-	d = bus_find_device_by_name(&nvmem_bus_type, NULL, name);
-
-	if (!d)
-		return NULL;
-
-	return to_nvmem_device(d);
-}
-
 #if IS_ENABLED(CONFIG_OF)
 /**
  * of_nvmem_device_get() - Get nvmem device from a given id
@@ -760,7 +772,7 @@  struct nvmem_device *of_nvmem_device_get(struct device_node *np, const char *id)
 	if (!nvmem_np)
 		return ERR_PTR(-EINVAL);
 
-	return __nvmem_device_get(nvmem_np);
+	return __nvmem_device_get(nvmem_np, NULL);
 }
 EXPORT_SYMBOL_GPL(of_nvmem_device_get);
 #endif
@@ -897,7 +909,7 @@  struct nvmem_cell *of_nvmem_cell_get(struct device_node *np,
 	if (!nvmem_np)
 		return ERR_PTR(-EINVAL);
 
-	nvmem = __nvmem_device_get(nvmem_np);
+	nvmem = __nvmem_device_get(nvmem_np, NULL);
 	of_node_put(nvmem_np);
 	if (IS_ERR(nvmem))
 		return ERR_CAST(nvmem);
@@ -913,6 +925,44 @@  struct nvmem_cell *of_nvmem_cell_get(struct device_node *np,
 EXPORT_SYMBOL_GPL(of_nvmem_cell_get);
 #endif
 
+static struct nvmem_cell *
+nvmem_cell_get_from_lookup(struct device *dev, const char *con_id)
+{
+	struct nvmem_cell *cell = ERR_PTR(-ENOENT);
+	struct nvmem_cell_lookup *lookup;
+	struct nvmem_device *nvmem;
+	const char *dev_id;
+
+	if (!dev)
+		return ERR_PTR(-EINVAL);
+
+	dev_id = dev_name(dev);
+
+	mutex_lock(&nvmem_lookup_mutex);
+
+	list_for_each_entry(lookup, &nvmem_lookup_list, node) {
+		if ((strcmp(lookup->dev_id, dev_id) == 0) &&
+		    (strcmp(lookup->con_id, con_id) == 0)) {
+			/* This is the right entry. */
+			nvmem = __nvmem_device_get(NULL, lookup->nvmem_name);
+			if (!nvmem) {
+				/* Provider may not be registered yet. */
+				cell = ERR_PTR(-EPROBE_DEFER);
+				goto out;
+			}
+
+			cell = nvmem_find_cell_by_name(nvmem,
+						       lookup->cell_name);
+			if (!cell)
+				goto out;
+		}
+	}
+
+out:
+	mutex_unlock(&nvmem_lookup_mutex);
+	return cell;
+}
+
 /**
  * nvmem_cell_get() - Get nvmem cell of device form a given cell name
  *
@@ -925,10 +975,14 @@  EXPORT_SYMBOL_GPL(of_nvmem_cell_get);
  */
 struct nvmem_cell *nvmem_cell_get(struct device *dev, const char *cell_id)
 {
-	if (!dev->of_node)
+	if (dev->of_node)
+		return of_nvmem_cell_get(dev->of_node, cell_id);
+
+	/* Only allow empty cell_id for DT systems. */
+	if (!cell_id)
 		return ERR_PTR(-EINVAL);
 
-	return of_nvmem_cell_get(dev->of_node, cell_id);
+	return nvmem_cell_get_from_lookup(dev, cell_id);
 }
 EXPORT_SYMBOL_GPL(nvmem_cell_get);
 
@@ -1300,6 +1354,41 @@  void nvmem_del_cell_table(struct nvmem_cell_table *table)
 }
 EXPORT_SYMBOL_GPL(nvmem_del_cell_table);
 
+/**
+ * nvmem_add_cell_lookups() - register a list of cell lookup entries
+ *
+ * @entries: array of cell lookup entries
+ * @nentries: number of cell lookup entries in the array
+ */
+void nvmem_add_cell_lookups(struct nvmem_cell_lookup *entries, size_t nentries)
+{
+	int i;
+
+	mutex_lock(&nvmem_lookup_mutex);
+	for (i = 0; i < nentries; i++)
+		list_add_tail(&entries[i].node, &nvmem_lookup_list);
+	mutex_unlock(&nvmem_lookup_mutex);
+}
+EXPORT_SYMBOL_GPL(nvmem_add_cell_lookups);
+
+/**
+ * nvmem_del_cell_lookups() - remove a list of previously added cell lookup
+ *                            entries
+ *
+ * @entries: array of cell lookup entries
+ * @nentries: number of cell lookup entries in the array
+ */
+void nvmem_del_cell_lookups(struct nvmem_cell_lookup *entries, size_t nentries)
+{
+	int i;
+
+	mutex_lock(&nvmem_lookup_mutex);
+	for (i = 0; i < nentries; i++)
+		list_del(&entries[i].node);
+	mutex_unlock(&nvmem_lookup_mutex);
+}
+EXPORT_SYMBOL_GPL(nvmem_del_cell_lookups);
+
 /**
  * nvmem_dev_name() - Get the name of a given nvmem device.
  *
diff --git a/include/linux/nvmem-machine.h b/include/linux/nvmem-machine.h
index 1e199dfaacab..7859c08934d5 100644
--- a/include/linux/nvmem-machine.h
+++ b/include/linux/nvmem-machine.h
@@ -26,16 +26,32 @@  struct nvmem_cell_table {
 	struct list_head	node;
 };
 
+struct nvmem_cell_lookup {
+	const char		*nvmem_name;
+	const char		*cell_name;
+	const char		*dev_id;
+	const char		*con_id;
+	struct list_head	node;
+};
+
 #if IS_ENABLED(CONFIG_NVMEM)
 
 void nvmem_add_cell_table(struct nvmem_cell_table *table);
 void nvmem_del_cell_table(struct nvmem_cell_table *table);
 
+void nvmem_add_cell_lookups(struct nvmem_cell_lookup *entries, size_t nentries);
+void nvmem_del_cell_lookups(struct nvmem_cell_lookup *entries, size_t nentries);
+
 #else /* CONFIG_NVMEM */
 
 static inline void nvmem_add_cell_table(struct nvmem_cell_table *table) {}
 static inline void nvmem_del_cell_table(struct nvmem_cell_table *table) {}
 
+static inline void
+nvmem_add_cell_lookups(struct nvmem_cell_lookup *entries, size_t nentries) {}
+static inline void
+nvmem_del_cell_lookups(struct nvmem_cell_lookup *entries, size_t nentries) {}
+
 #endif /* CONFIG_NVMEM */
 
 #endif  /* ifndef _LINUX_NVMEM_MACHINE_H */