[PATCH v1 06/14] nvmem: core: introduce NVMEM layouts

Michael Walle michael at walle.cc
Tue Aug 30 07:24:42 PDT 2022


Am 2022-08-30 15:36, schrieb Srinivas Kandagatla:
> On 25/08/2022 22:44, Michael Walle wrote:
>> NVMEM layouts are used to generate NVMEM cells during runtime. Think 
>> of
>> an EEPROM with a well-defined conent. For now, the content can be
>> described by a device tree or a board file. But this only works if the
>> offsets and lengths are static and don't change. One could also argue
>> that putting the layout of the EEPROM in the device tree is the wrong
>> place. Instead, the device tree should just have a specific compatible
>> string.
>> 
>> Right now there are two use cases:
>>   (1) The NVMEM cell needs special processing. E.g. if it only 
>> specifies
>>       a base MAC address offset and you need to add an offset, or it
>>       needs to parse a MAC from ASCII format or some proprietary 
>> format.
>>       (Post processing of cells is added in a later commit).
>>   (2) u-boot environment parsing. The cells don't have a particular
>>       offset but it needs parsing the content to determine the offsets
>>       and length.
>> 
>> Signed-off-by: Michael Walle <michael at walle.cc>
>> ---
>>   drivers/nvmem/Kconfig          |  2 ++
>>   drivers/nvmem/Makefile         |  1 +
>>   drivers/nvmem/core.c           | 57 
>> ++++++++++++++++++++++++++++++++++
>>   drivers/nvmem/layouts/Kconfig  |  5 +++
>>   drivers/nvmem/layouts/Makefile |  4 +++
>>   include/linux/nvmem-provider.h | 38 +++++++++++++++++++++++
>>   6 files changed, 107 insertions(+)
>>   create mode 100644 drivers/nvmem/layouts/Kconfig
>>   create mode 100644 drivers/nvmem/layouts/Makefile
> 
> update to ./Documentation/driver-api/nvmem.rst would help others.

Sure. Didn't know about that one.

>> diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig
>> index bab8a29c9861..1416837b107b 100644
>> --- a/drivers/nvmem/Kconfig
>> +++ b/drivers/nvmem/Kconfig
>> @@ -357,4 +357,6 @@ config NVMEM_U_BOOT_ENV
>>     	  If compiled as module it will be called nvmem_u-boot-env.
>>   +source "drivers/nvmem/layouts/Kconfig"
>> +
>>   endif
>> diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile
>> index 399f9972d45b..cd5a5baa2f3a 100644
>> --- a/drivers/nvmem/Makefile
>> +++ b/drivers/nvmem/Makefile
>> @@ -5,6 +5,7 @@
>>     obj-$(CONFIG_NVMEM)		+= nvmem_core.o
>>   nvmem_core-y			:= core.o
>> +obj-y				+= layouts/
>>     # Devices
>>   obj-$(CONFIG_NVMEM_BCM_OCOTP)	+= nvmem-bcm-ocotp.o
>> diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
>> index 3dfd149374a8..5357fc378700 100644
>> --- a/drivers/nvmem/core.c
>> +++ b/drivers/nvmem/core.c
>> @@ -74,6 +74,9 @@ static LIST_HEAD(nvmem_lookup_list);
>>     static BLOCKING_NOTIFIER_HEAD(nvmem_notifier);
>>   +static DEFINE_SPINLOCK(nvmem_layout_lock);
>> +static LIST_HEAD(nvmem_layouts);
>> +
>>   static int __nvmem_reg_read(struct nvmem_device *nvmem, unsigned int 
>> offset,
>>   			    void *val, size_t bytes)
>>   {
>> @@ -744,6 +747,56 @@ static int nvmem_add_cells_from_of(struct 
>> nvmem_device *nvmem)
>>   	return 0;
>>   }
>>   +int nvmem_register_layout(struct nvmem_layout *layout)
>> +{
>> +	spin_lock(&nvmem_layout_lock);
>> +	list_add(&layout->node, &nvmem_layouts);
>> +	spin_unlock(&nvmem_layout_lock);
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(nvmem_register_layout);
> 
> we should provide nvmem_unregister_layout too, so that providers can
> add them if they can in there respective drivers.

Actually, that was the idea; that you can have layouts outside of 
layouts/.
I also had a nvmem_unregister_layout() but removed it because it was 
dead
code. Will re-add it again.

>> +
>> +static struct nvmem_layout *nvmem_get_compatible_layout(struct 
>> device_node *np)
>> +{
>> +	struct nvmem_layout *p, *ret = NULL;
>> +
>> +	spin_lock(&nvmem_layout_lock);
>> +
>> +	list_for_each_entry(p, &nvmem_layouts, node) {
>> +		if (of_match_node(p->of_match_table, np)) {
>> +			ret = p;
>> +			break;
>> +		}
>> +	}
>> +
>> +	spin_unlock(&nvmem_layout_lock);
>> +
>> +	return ret;
>> +}
>> +
>> +static int nvmem_add_cells_from_layout(struct nvmem_device *nvmem)
>> +{
>> +	struct nvmem_layout *layout;
>> +
>> +	layout = nvmem_get_compatible_layout(nvmem->dev.of_node);
>> +	if (layout)
>> +		layout->add_cells(&nvmem->dev, nvmem, layout);
> 
> access to add_cells can crash hear as we did not check it before
> adding in to list.
> Or
> we could relax add_cells callback for usecases like imx-octop.

good catch, will use layout && layout->add_cells.

>> +
>> +	return 0;
>> +}
>> +
>> +const void *nvmem_layout_get_match_data(struct nvmem_device *nvmem,
>> +					struct nvmem_layout *layout)
>> +{
>> +	const struct of_device_id *match;
>> +
>> +	match = of_match_node(layout->of_match_table, nvmem->dev.of_node);
>> +
>> +	return match ? match->data : NULL;
>> +}
>> +EXPORT_SYMBOL_GPL(nvmem_layout_get_match_data);
>> +
>>   /**
>>    * nvmem_register() - Register a nvmem device for given 
>> nvmem_config.
>>    * Also creates a binary entry in 
>> /sys/bus/nvmem/devices/dev-name/nvmem
>> @@ -872,6 +925,10 @@ struct nvmem_device *nvmem_register(const struct 
>> nvmem_config *config)
>>   	if (rval)
>>   		goto err_remove_cells;
>>   +	rval = nvmem_add_cells_from_layout(nvmem);
>> +	if (rval)
>> +		goto err_remove_cells;
>> +
>>   	blocking_notifier_call_chain(&nvmem_notifier, NVMEM_ADD, nvmem);
>>     	return nvmem;
>> diff --git a/drivers/nvmem/layouts/Kconfig 
>> b/drivers/nvmem/layouts/Kconfig
>> new file mode 100644
>> index 000000000000..9ad3911d1605
>> --- /dev/null
>> +++ b/drivers/nvmem/layouts/Kconfig
>> @@ -0,0 +1,5 @@
>> +# SPDX-License-Identifier: GPL-2.0
>> +
>> +menu "Layout Types"
>> +
>> +endmenu
>> diff --git a/drivers/nvmem/layouts/Makefile 
>> b/drivers/nvmem/layouts/Makefile
>> new file mode 100644
>> index 000000000000..6fdb3c60a4fa
>> --- /dev/null
>> +++ b/drivers/nvmem/layouts/Makefile
>> @@ -0,0 +1,4 @@
>> +# SPDX-License-Identifier: GPL-2.0
>> +#
>> +# Makefile for nvmem layouts.
>> +#
>> diff --git a/include/linux/nvmem-provider.h 
>> b/include/linux/nvmem-provider.h
>> index e710404959e7..323685841e9f 100644
>> --- a/include/linux/nvmem-provider.h
>> +++ b/include/linux/nvmem-provider.h
>> @@ -127,6 +127,28 @@ struct nvmem_cell_table {
>>   	struct list_head	node;
>>   };
>>   +/**
>> + * struct nvmem_layout - NVMEM layout definitions
>> + *
>> + * @name:		Layout name.
>> + * @of_match_table:	Open firmware match table.
>> + * @add_cells:		Will be called if a nvmem device is found which
>> + *			has this layout. The function will add layout
>> + *			specific cells with nvmem_add_one_cell().
>> + * @node:		List node.
>> + *
>> + * A nvmem device can hold a well defined structure which can just be
>> + * evaluated during runtime. For example a TLV list, or a list of 
>> "name=val"
>> + * pairs. A nvmem layout can parse the nvmem device and add 
>> appropriate
>> + * cells.
>> + */
>> +struct nvmem_layout {
>> +	const char *name;
>> +	const struct of_device_id *of_match_table;
> 
> looking at this, I think its doable to convert the existing
> cell_post_process callback to layouts by adding a layout specific
> callback here.

can you elaborate on that?

-michael



More information about the linux-arm-kernel mailing list