kernel: backport NVMEM patches queued for the v6.4

They add NVMEM layouts support. It allows handling NVMEM content
independently of NVMEM device access.

Skip U-Boot env data patch for now as it break our downstream MAC hacks.

Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
This commit is contained in:
Rafał Miłecki 2023-04-06 09:15:49 +02:00
parent b67ba02fc8
commit 323072f3a6
46 changed files with 3960 additions and 0 deletions

View File

@ -0,0 +1,35 @@
From bcd1fe07def0f070eb5f31594620aaee6f81d31a Mon Sep 17 00:00:00 2001
From: Nick Alcock <nick.alcock@oracle.com>
Date: Tue, 4 Apr 2023 18:21:11 +0100
Subject: [PATCH] nvmem: xilinx: zynqmp: make modular
This driver has a MODULE_LICENSE but is not tristate so cannot be
built as a module, unlike all its peers: make it modular to match.
Signed-off-by: Nick Alcock <nick.alcock@oracle.com>
Suggested-by: Michal Simek <michal.simek@amd.com>
Cc: Luis Chamberlain <mcgrof@kernel.org>
Cc: linux-modules@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: Hitomi Hasegawa <hasegawa-hitomi@fujitsu.com>
Cc: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Cc: Michal Simek <michal.simek@xilinx.com>
Cc: linux-arm-kernel@lists.infradead.org
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-4-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/nvmem/Kconfig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/drivers/nvmem/Kconfig
+++ b/drivers/nvmem/Kconfig
@@ -368,7 +368,7 @@ config NVMEM_VF610_OCOTP
be called nvmem-vf610-ocotp.
config NVMEM_ZYNQMP
- bool "Xilinx ZYNQMP SoC nvmem firmware support"
+ tristate "Xilinx ZYNQMP SoC nvmem firmware support"
depends on ARCH_ZYNQMP
help
This is a driver to access hardware related data like

View File

@ -0,0 +1,387 @@
From 266570f496b90dea8fda893c2cf7c28d63ae2bd9 Mon Sep 17 00:00:00 2001
From: Michael Walle <michael@walle.cc>
Date: Tue, 4 Apr 2023 18:21:21 +0100
Subject: [PATCH] nvmem: core: introduce NVMEM layouts
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.
Co-developed-by: Miquel Raynal <miquel.raynal@bootlin.com>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Signed-off-by: Michael Walle <michael@walle.cc>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-14-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
Documentation/driver-api/nvmem.rst | 15 ++++
drivers/nvmem/Kconfig | 4 +
drivers/nvmem/Makefile | 1 +
drivers/nvmem/core.c | 120 +++++++++++++++++++++++++++++
drivers/nvmem/layouts/Kconfig | 5 ++
drivers/nvmem/layouts/Makefile | 4 +
include/linux/nvmem-consumer.h | 7 ++
include/linux/nvmem-provider.h | 51 ++++++++++++
8 files changed, 207 insertions(+)
create mode 100644 drivers/nvmem/layouts/Kconfig
create mode 100644 drivers/nvmem/layouts/Makefile
--- a/Documentation/driver-api/nvmem.rst
+++ b/Documentation/driver-api/nvmem.rst
@@ -189,3 +189,18 @@ ex::
=====================
See Documentation/devicetree/bindings/nvmem/nvmem.txt
+
+8. NVMEM layouts
+================
+
+NVMEM layouts are yet another mechanism to create cells. With the device
+tree binding it is possible to specify simple cells by using an offset
+and a length. Sometimes, the cells doesn't have a static offset, but
+the content is still well defined, e.g. tag-length-values. In this case,
+the NVMEM device content has to be first parsed and the cells need to
+be added accordingly. Layouts let you read the content of the NVMEM device
+and let you add cells dynamically.
+
+Another use case for layouts is the post processing of cells. With layouts,
+it is possible to associate a custom post processing hook to a cell. It
+even possible to add this hook to cells not created by the layout itself.
--- a/drivers/nvmem/Kconfig
+++ b/drivers/nvmem/Kconfig
@@ -21,6 +21,10 @@ config NVMEM_SYSFS
This interface is mostly used by userspace applications to
read/write directly into nvmem.
+# Layouts
+
+source "drivers/nvmem/layouts/Kconfig"
+
# Devices
config NVMEM_APPLE_EFUSES
--- 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_APPLE_EFUSES) += nvmem-apple-efuses.o
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -40,6 +40,7 @@ struct nvmem_device {
nvmem_reg_write_t reg_write;
nvmem_cell_post_process_t cell_post_process;
struct gpio_desc *wp_gpio;
+ struct nvmem_layout *layout;
void *priv;
};
@@ -74,6 +75,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)
{
@@ -728,6 +732,101 @@ static int nvmem_add_cells_from_of(struc
return 0;
}
+int __nvmem_layout_register(struct nvmem_layout *layout, struct module *owner)
+{
+ layout->owner = owner;
+
+ spin_lock(&nvmem_layout_lock);
+ list_add(&layout->node, &nvmem_layouts);
+ spin_unlock(&nvmem_layout_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(__nvmem_layout_register);
+
+void nvmem_layout_unregister(struct nvmem_layout *layout)
+{
+ spin_lock(&nvmem_layout_lock);
+ list_del(&layout->node);
+ spin_unlock(&nvmem_layout_lock);
+}
+EXPORT_SYMBOL_GPL(nvmem_layout_unregister);
+
+static struct nvmem_layout *nvmem_layout_get(struct nvmem_device *nvmem)
+{
+ struct device_node *layout_np, *np = nvmem->dev.of_node;
+ struct nvmem_layout *l, *layout = NULL;
+
+ layout_np = of_get_child_by_name(np, "nvmem-layout");
+ if (!layout_np)
+ return NULL;
+
+ spin_lock(&nvmem_layout_lock);
+
+ list_for_each_entry(l, &nvmem_layouts, node) {
+ if (of_match_node(l->of_match_table, layout_np)) {
+ if (try_module_get(l->owner))
+ layout = l;
+
+ break;
+ }
+ }
+
+ spin_unlock(&nvmem_layout_lock);
+ of_node_put(layout_np);
+
+ return layout;
+}
+
+static void nvmem_layout_put(struct nvmem_layout *layout)
+{
+ if (layout)
+ module_put(layout->owner);
+}
+
+static int nvmem_add_cells_from_layout(struct nvmem_device *nvmem)
+{
+ struct nvmem_layout *layout = nvmem->layout;
+ int ret;
+
+ if (layout && layout->add_cells) {
+ ret = layout->add_cells(&nvmem->dev, nvmem, layout);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+#if IS_ENABLED(CONFIG_OF)
+/**
+ * of_nvmem_layout_get_container() - Get OF node to layout container.
+ *
+ * @nvmem: nvmem device.
+ *
+ * Return: a node pointer with refcount incremented or NULL if no
+ * container exists. Use of_node_put() on it when done.
+ */
+struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem)
+{
+ return of_get_child_by_name(nvmem->dev.of_node, "nvmem-layout");
+}
+EXPORT_SYMBOL_GPL(of_nvmem_layout_get_container);
+#endif
+
+const void *nvmem_layout_get_match_data(struct nvmem_device *nvmem,
+ struct nvmem_layout *layout)
+{
+ struct device_node __maybe_unused *layout_np;
+ const struct of_device_id *match;
+
+ layout_np = of_nvmem_layout_get_container(nvmem);
+ match = of_match_node(layout->of_match_table, layout_np);
+
+ 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
@@ -834,6 +933,12 @@ struct nvmem_device *nvmem_register(cons
goto err_put_device;
}
+ /*
+ * If the driver supplied a layout by config->layout, the module
+ * pointer will be NULL and nvmem_layout_put() will be a noop.
+ */
+ nvmem->layout = config->layout ?: nvmem_layout_get(nvmem);
+
if (config->cells) {
rval = nvmem_add_cells(nvmem, config->cells, config->ncells);
if (rval)
@@ -854,12 +959,17 @@ struct nvmem_device *nvmem_register(cons
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;
err_remove_cells:
nvmem_device_remove_all_cells(nvmem);
+ nvmem_layout_put(nvmem->layout);
if (config->compat)
nvmem_sysfs_remove_compat(nvmem, config);
err_put_device:
@@ -881,6 +991,7 @@ static void nvmem_device_release(struct
device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom);
nvmem_device_remove_all_cells(nvmem);
+ nvmem_layout_put(nvmem->layout);
device_unregister(&nvmem->dev);
}
@@ -1246,6 +1357,15 @@ struct nvmem_cell *of_nvmem_cell_get(str
return ERR_PTR(-EINVAL);
}
+ /* nvmem layouts produce cells within the nvmem-layout container */
+ if (of_node_name_eq(nvmem_np, "nvmem-layout")) {
+ nvmem_np = of_get_next_parent(nvmem_np);
+ if (!nvmem_np) {
+ of_node_put(cell_np);
+ return ERR_PTR(-EINVAL);
+ }
+ }
+
nvmem = __nvmem_device_get(nvmem_np, device_match_of_node);
of_node_put(nvmem_np);
if (IS_ERR(nvmem)) {
--- /dev/null
+++ b/drivers/nvmem/layouts/Kconfig
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+
+menu "Layout Types"
+
+endmenu
--- /dev/null
+++ b/drivers/nvmem/layouts/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for nvmem layouts.
+#
--- a/include/linux/nvmem-consumer.h
+++ b/include/linux/nvmem-consumer.h
@@ -225,6 +225,7 @@ struct nvmem_cell *of_nvmem_cell_get(str
const char *id);
struct nvmem_device *of_nvmem_device_get(struct device_node *np,
const char *name);
+struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem);
#else
static inline struct nvmem_cell *of_nvmem_cell_get(struct device_node *np,
const char *id)
@@ -237,6 +238,12 @@ static inline struct nvmem_device *of_nv
{
return ERR_PTR(-EOPNOTSUPP);
}
+
+static inline struct device_node *
+of_nvmem_layout_get_container(struct nvmem_device *nvmem)
+{
+ return ERR_PTR(-EOPNOTSUPP);
+}
#endif /* CONFIG_NVMEM && CONFIG_OF */
#endif /* ifndef _LINUX_NVMEM_CONSUMER_H */
--- a/include/linux/nvmem-provider.h
+++ b/include/linux/nvmem-provider.h
@@ -88,6 +88,7 @@ struct nvmem_cell_info {
* @stride: Minimum read/write access stride.
* @priv: User context passed to read/write callbacks.
* @ignore_wp: Write Protect pin is managed by the provider.
+ * @layout: Fixed layout associated with this nvmem device.
*
* Note: A default "nvmem<id>" name will be assigned to the device if
* no name is specified in its configuration. In such case "<id>" is
@@ -110,6 +111,7 @@ struct nvmem_config {
bool root_only;
struct device_node *of_node;
bool ignore_wp;
+ struct nvmem_layout *layout;
bool no_of_node;
nvmem_reg_read_t reg_read;
nvmem_reg_write_t reg_write;
@@ -142,6 +144,33 @@ 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().
+ * @owner: Pointer to struct module.
+ * @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;
+ int (*add_cells)(struct device *dev, struct nvmem_device *nvmem,
+ struct nvmem_layout *layout);
+
+ /* private */
+ struct module *owner;
+ struct list_head node;
+};
+
#if IS_ENABLED(CONFIG_NVMEM)
struct nvmem_device *nvmem_register(const struct nvmem_config *cfg);
@@ -156,6 +185,14 @@ void nvmem_del_cell_table(struct nvmem_c
int nvmem_add_one_cell(struct nvmem_device *nvmem,
const struct nvmem_cell_info *info);
+int __nvmem_layout_register(struct nvmem_layout *layout, struct module *owner);
+#define nvmem_layout_register(layout) \
+ __nvmem_layout_register(layout, THIS_MODULE)
+void nvmem_layout_unregister(struct nvmem_layout *layout);
+
+const void *nvmem_layout_get_match_data(struct nvmem_device *nvmem,
+ struct nvmem_layout *layout);
+
#else
static inline struct nvmem_device *nvmem_register(const struct nvmem_config *c)
@@ -179,5 +216,19 @@ static inline int nvmem_add_one_cell(str
return -EOPNOTSUPP;
}
+static inline int nvmem_layout_register(struct nvmem_layout *layout)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline void nvmem_layout_unregister(struct nvmem_layout *layout) {}
+
+static inline const void *
+nvmem_layout_get_match_data(struct nvmem_device *nvmem,
+ struct nvmem_layout *layout)
+{
+ return NULL;
+}
+
#endif /* CONFIG_NVMEM */
#endif /* ifndef _LINUX_NVMEM_PROVIDER_H */

View File

@ -0,0 +1,61 @@
From 6468a6f45148fb5e95c86b4efebf63f9abcd2137 Mon Sep 17 00:00:00 2001
From: Miquel Raynal <miquel.raynal@bootlin.com>
Date: Tue, 4 Apr 2023 18:21:22 +0100
Subject: [PATCH] nvmem: core: handle the absence of expected layouts
Make nvmem_layout_get() return -EPROBE_DEFER while the expected layout
is not available. This condition cannot be triggered today as nvmem
layout drivers are initialed as part of an early init call, but soon
these drivers will be converted into modules and be initialized with a
standard priority, so the unavailability of the drivers might become a
reality that must be taken care of.
Let's anticipate this by telling the caller the layout might not yet be
available. A probe deferral is requested in this case.
Please note this does not affect any nvmem device not using layouts,
because an early check against the "nvmem-layout" container presence
will return NULL in this case.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Tested-by: Michael Walle <michael@walle.cc>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-15-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/nvmem/core.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -755,7 +755,7 @@ EXPORT_SYMBOL_GPL(nvmem_layout_unregiste
static struct nvmem_layout *nvmem_layout_get(struct nvmem_device *nvmem)
{
struct device_node *layout_np, *np = nvmem->dev.of_node;
- struct nvmem_layout *l, *layout = NULL;
+ struct nvmem_layout *l, *layout = ERR_PTR(-EPROBE_DEFER);
layout_np = of_get_child_by_name(np, "nvmem-layout");
if (!layout_np)
@@ -938,6 +938,13 @@ struct nvmem_device *nvmem_register(cons
* pointer will be NULL and nvmem_layout_put() will be a noop.
*/
nvmem->layout = config->layout ?: nvmem_layout_get(nvmem);
+ if (IS_ERR(nvmem->layout)) {
+ rval = PTR_ERR(nvmem->layout);
+ nvmem->layout = NULL;
+
+ if (rval == -EPROBE_DEFER)
+ goto err_teardown_compat;
+ }
if (config->cells) {
rval = nvmem_add_cells(nvmem, config->cells, config->ncells);
@@ -970,6 +977,7 @@ struct nvmem_device *nvmem_register(cons
err_remove_cells:
nvmem_device_remove_all_cells(nvmem);
nvmem_layout_put(nvmem->layout);
+err_teardown_compat:
if (config->compat)
nvmem_sysfs_remove_compat(nvmem, config);
err_put_device:

View File

@ -0,0 +1,52 @@
From b1c37bec1ccfe5ccab72bc0ddc0dfa45c43e2de2 Mon Sep 17 00:00:00 2001
From: Miquel Raynal <miquel.raynal@bootlin.com>
Date: Tue, 4 Apr 2023 18:21:23 +0100
Subject: [PATCH] nvmem: core: request layout modules loading
When a storage device like an eeprom or an mtd device probes, it
registers an nvmem device if the nvmem subsystem has been enabled (bool
symbol). During nvmem registration, if the device is using layouts to
expose dynamic nvmem cells, the core will first try to get a reference
over the layout driver callbacks. In practice there is not relationship
that can be described between the storage driver and the nvmem
layout. So there is no way we can enforce both drivers will be built-in
or both will be modules. If the storage device driver is built-in but
the layout is built as a module, instead of badly failing with an
endless probe deferral loop, lets just make a modprobe call in case the
driver was made available in an initramfs with
of_device_node_request_module(), and offer a fully functional system to
the user.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Tested-by: Michael Walle <michael@walle.cc>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-16-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/nvmem/core.c | 8 ++++++++
1 file changed, 8 insertions(+)
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -17,6 +17,7 @@
#include <linux/nvmem-provider.h>
#include <linux/gpio/consumer.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/slab.h>
struct nvmem_device {
@@ -761,6 +762,13 @@ static struct nvmem_layout *nvmem_layout
if (!layout_np)
return NULL;
+ /*
+ * In case the nvmem device was built-in while the layout was built as a
+ * module, we shall manually request the layout driver loading otherwise
+ * we'll never have any match.
+ */
+ of_request_module(layout_np);
+
spin_lock(&nvmem_layout_lock);
list_for_each_entry(l, &nvmem_layouts, node) {

View File

@ -0,0 +1,86 @@
From 345ec382cd4b736c36e01f155d08c913b225b736 Mon Sep 17 00:00:00 2001
From: Michael Walle <michael@walle.cc>
Date: Tue, 4 Apr 2023 18:21:24 +0100
Subject: [PATCH] nvmem: core: add per-cell post processing
Instead of relying on the name the consumer is using for the cell, like
it is done for the nvmem .cell_post_process configuration parameter,
provide a per-cell post processing hook. This can then be populated by
the NVMEM provider (or the NVMEM layout) when adding the cell.
Signed-off-by: Michael Walle <michael@walle.cc>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-17-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/nvmem/core.c | 17 +++++++++++++++++
include/linux/nvmem-provider.h | 3 +++
2 files changed, 20 insertions(+)
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -54,6 +54,7 @@ struct nvmem_cell_entry {
int bytes;
int bit_offset;
int nbits;
+ nvmem_cell_post_process_t read_post_process;
struct device_node *np;
struct nvmem_device *nvmem;
struct list_head node;
@@ -470,6 +471,7 @@ static int nvmem_cell_info_to_nvmem_cell
cell->offset = info->offset;
cell->bytes = info->bytes;
cell->name = info->name;
+ cell->read_post_process = info->read_post_process;
cell->bit_offset = info->bit_offset;
cell->nbits = info->nbits;
@@ -1563,6 +1565,13 @@ static int __nvmem_cell_read(struct nvme
if (cell->bit_offset || cell->nbits)
nvmem_shift_read_buffer_in_place(cell, buf);
+ if (cell->read_post_process) {
+ rc = cell->read_post_process(nvmem->priv, id, index,
+ cell->offset, buf, cell->bytes);
+ if (rc)
+ return rc;
+ }
+
if (nvmem->cell_post_process) {
rc = nvmem->cell_post_process(nvmem->priv, id, index,
cell->offset, buf, cell->bytes);
@@ -1671,6 +1680,14 @@ static int __nvmem_cell_entry_write(stru
(cell->bit_offset == 0 && len != cell->bytes))
return -EINVAL;
+ /*
+ * Any cells which have a read_post_process hook are read-only because
+ * we cannot reverse the operation and it might affect other cells,
+ * too.
+ */
+ if (cell->read_post_process)
+ return -EINVAL;
+
if (cell->bit_offset || cell->nbits) {
buf = nvmem_cell_prepare_write_buffer(cell, buf, len);
if (IS_ERR(buf))
--- a/include/linux/nvmem-provider.h
+++ b/include/linux/nvmem-provider.h
@@ -54,6 +54,8 @@ struct nvmem_keepout {
* @bit_offset: Bit offset if cell is smaller than a byte.
* @nbits: Number of bits.
* @np: Optional device_node pointer.
+ * @read_post_process: Callback for optional post processing of cell data
+ * on reads.
*/
struct nvmem_cell_info {
const char *name;
@@ -62,6 +64,7 @@ struct nvmem_cell_info {
unsigned int bit_offset;
unsigned int nbits;
struct device_node *np;
+ nvmem_cell_post_process_t read_post_process;
};
/**

View File

@ -0,0 +1,59 @@
From de12c9691501ccba41a154c223869f82be4c12fd Mon Sep 17 00:00:00 2001
From: Michael Walle <michael@walle.cc>
Date: Tue, 4 Apr 2023 18:21:25 +0100
Subject: [PATCH] nvmem: core: allow to modify a cell before adding it
Provide a way to modify a cell before it will get added. This is useful
to attach a custom post processing hook via a layout.
Signed-off-by: Michael Walle <michael@walle.cc>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-18-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/nvmem/core.c | 4 ++++
include/linux/nvmem-provider.h | 5 +++++
2 files changed, 9 insertions(+)
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -695,6 +695,7 @@ static int nvmem_validate_keepouts(struc
static int nvmem_add_cells_from_of(struct nvmem_device *nvmem)
{
+ struct nvmem_layout *layout = nvmem->layout;
struct device *dev = &nvmem->dev;
struct device_node *child;
const __be32 *addr;
@@ -724,6 +725,9 @@ static int nvmem_add_cells_from_of(struc
info.np = of_node_get(child);
+ if (layout && layout->fixup_cell_info)
+ layout->fixup_cell_info(nvmem, layout, &info);
+
ret = nvmem_add_one_cell(nvmem, &info);
kfree(info.name);
if (ret) {
--- a/include/linux/nvmem-provider.h
+++ b/include/linux/nvmem-provider.h
@@ -155,6 +155,8 @@ struct nvmem_cell_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().
+ * @fixup_cell_info: Will be called before a cell is added. Can be
+ * used to modify the nvmem_cell_info.
* @owner: Pointer to struct module.
* @node: List node.
*
@@ -168,6 +170,9 @@ struct nvmem_layout {
const struct of_device_id *of_match_table;
int (*add_cells)(struct device *dev, struct nvmem_device *nvmem,
struct nvmem_layout *layout);
+ void (*fixup_cell_info)(struct nvmem_device *nvmem,
+ struct nvmem_layout *layout,
+ struct nvmem_cell_info *cell);
/* private */
struct module *owner;

View File

@ -0,0 +1,81 @@
From 6c56a82d7895a213a43182a5d01a21a906a79847 Mon Sep 17 00:00:00 2001
From: Michael Walle <michael@walle.cc>
Date: Tue, 4 Apr 2023 18:21:26 +0100
Subject: [PATCH] nvmem: imx-ocotp: replace global post processing with layouts
In preparation of retiring the global post processing hook change this
driver to use layouts. The layout will be supplied during registration
and will be used to add the post processing hook to all added cells.
Signed-off-by: Michael Walle <michael@walle.cc>
Tested-by: Michael Walle <michael@walle.cc> # on kontron-pitx-imx8m
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-19-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/nvmem/imx-ocotp.c | 30 +++++++++++++++++++-----------
1 file changed, 19 insertions(+), 11 deletions(-)
--- a/drivers/nvmem/imx-ocotp.c
+++ b/drivers/nvmem/imx-ocotp.c
@@ -225,18 +225,13 @@ read_end:
static int imx_ocotp_cell_pp(void *context, const char *id, int index,
unsigned int offset, void *data, size_t bytes)
{
- struct ocotp_priv *priv = context;
+ u8 *buf = data;
+ int i;
/* Deal with some post processing of nvmem cell data */
- if (id && !strcmp(id, "mac-address")) {
- if (priv->params->reverse_mac_address) {
- u8 *buf = data;
- int i;
-
- for (i = 0; i < bytes/2; i++)
- swap(buf[i], buf[bytes - i - 1]);
- }
- }
+ if (id && !strcmp(id, "mac-address"))
+ for (i = 0; i < bytes / 2; i++)
+ swap(buf[i], buf[bytes - i - 1]);
return 0;
}
@@ -488,7 +483,6 @@ static struct nvmem_config imx_ocotp_nvm
.stride = 1,
.reg_read = imx_ocotp_read,
.reg_write = imx_ocotp_write,
- .cell_post_process = imx_ocotp_cell_pp,
};
static const struct ocotp_params imx6q_params = {
@@ -595,6 +589,17 @@ static const struct of_device_id imx_oco
};
MODULE_DEVICE_TABLE(of, imx_ocotp_dt_ids);
+static void imx_ocotp_fixup_cell_info(struct nvmem_device *nvmem,
+ struct nvmem_layout *layout,
+ struct nvmem_cell_info *cell)
+{
+ cell->read_post_process = imx_ocotp_cell_pp;
+}
+
+struct nvmem_layout imx_ocotp_layout = {
+ .fixup_cell_info = imx_ocotp_fixup_cell_info,
+};
+
static int imx_ocotp_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -619,6 +624,9 @@ static int imx_ocotp_probe(struct platfo
imx_ocotp_nvmem_config.size = 4 * priv->params->nregs;
imx_ocotp_nvmem_config.dev = dev;
imx_ocotp_nvmem_config.priv = priv;
+ if (priv->params->reverse_mac_address)
+ imx_ocotp_nvmem_config.layout = &imx_ocotp_layout;
+
priv->config = &imx_ocotp_nvmem_config;
clk_prepare_enable(priv->clk);

View File

@ -0,0 +1,68 @@
From 011e40a166fdaa65fb9946b7cd91efec85b70dbb Mon Sep 17 00:00:00 2001
From: Michael Walle <michael@walle.cc>
Date: Tue, 4 Apr 2023 18:21:27 +0100
Subject: [PATCH] nvmem: cell: drop global cell_post_process
There are no users anymore for the global cell_post_process callback
anymore. New users should use proper nvmem layouts.
Signed-off-by: Michael Walle <michael@walle.cc>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-20-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/nvmem/core.c | 9 ---------
include/linux/nvmem-provider.h | 2 --
2 files changed, 11 deletions(-)
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -39,7 +39,6 @@ struct nvmem_device {
unsigned int nkeepout;
nvmem_reg_read_t reg_read;
nvmem_reg_write_t reg_write;
- nvmem_cell_post_process_t cell_post_process;
struct gpio_desc *wp_gpio;
struct nvmem_layout *layout;
void *priv;
@@ -903,7 +902,6 @@ struct nvmem_device *nvmem_register(cons
nvmem->type = config->type;
nvmem->reg_read = config->reg_read;
nvmem->reg_write = config->reg_write;
- nvmem->cell_post_process = config->cell_post_process;
nvmem->keepout = config->keepout;
nvmem->nkeepout = config->nkeepout;
if (config->of_node)
@@ -1575,13 +1573,6 @@ static int __nvmem_cell_read(struct nvme
if (rc)
return rc;
}
-
- if (nvmem->cell_post_process) {
- rc = nvmem->cell_post_process(nvmem->priv, id, index,
- cell->offset, buf, cell->bytes);
- if (rc)
- return rc;
- }
if (len)
*len = cell->bytes;
--- a/include/linux/nvmem-provider.h
+++ b/include/linux/nvmem-provider.h
@@ -85,7 +85,6 @@ struct nvmem_cell_info {
* @no_of_node: Device should not use the parent's of_node even if it's !NULL.
* @reg_read: Callback to read data.
* @reg_write: Callback to write data.
- * @cell_post_process: Callback for vendor specific post processing of cell data
* @size: Device size.
* @word_size: Minimum read/write access granularity.
* @stride: Minimum read/write access stride.
@@ -118,7 +117,6 @@ struct nvmem_config {
bool no_of_node;
nvmem_reg_read_t reg_read;
nvmem_reg_write_t reg_write;
- nvmem_cell_post_process_t cell_post_process;
int size;
int word_size;
int stride;

View File

@ -0,0 +1,76 @@
From 8a134fd9f9323f4c39ec27055b3d3723cfb5c1e9 Mon Sep 17 00:00:00 2001
From: Michael Walle <michael@walle.cc>
Date: Tue, 4 Apr 2023 18:21:28 +0100
Subject: [PATCH] nvmem: core: provide own priv pointer in post process
callback
It doesn't make any more sense to have a opaque pointer set up by the
nvmem device. Usually, the layout isn't associated with a particular
nvmem device. Instead, let the caller who set the post process callback
provide the priv pointer.
Signed-off-by: Michael Walle <michael@walle.cc>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-21-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/nvmem/core.c | 4 +++-
include/linux/nvmem-provider.h | 5 ++++-
2 files changed, 7 insertions(+), 2 deletions(-)
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -54,6 +54,7 @@ struct nvmem_cell_entry {
int bit_offset;
int nbits;
nvmem_cell_post_process_t read_post_process;
+ void *priv;
struct device_node *np;
struct nvmem_device *nvmem;
struct list_head node;
@@ -471,6 +472,7 @@ static int nvmem_cell_info_to_nvmem_cell
cell->bytes = info->bytes;
cell->name = info->name;
cell->read_post_process = info->read_post_process;
+ cell->priv = info->priv;
cell->bit_offset = info->bit_offset;
cell->nbits = info->nbits;
@@ -1568,7 +1570,7 @@ static int __nvmem_cell_read(struct nvme
nvmem_shift_read_buffer_in_place(cell, buf);
if (cell->read_post_process) {
- rc = cell->read_post_process(nvmem->priv, id, index,
+ rc = cell->read_post_process(cell->priv, id, index,
cell->offset, buf, cell->bytes);
if (rc)
return rc;
--- a/include/linux/nvmem-provider.h
+++ b/include/linux/nvmem-provider.h
@@ -20,7 +20,8 @@ typedef int (*nvmem_reg_write_t)(void *p
void *val, size_t bytes);
/* used for vendor specific post processing of cell data */
typedef int (*nvmem_cell_post_process_t)(void *priv, const char *id, int index,
- unsigned int offset, void *buf, size_t bytes);
+ unsigned int offset, void *buf,
+ size_t bytes);
enum nvmem_type {
NVMEM_TYPE_UNKNOWN = 0,
@@ -56,6 +57,7 @@ struct nvmem_keepout {
* @np: Optional device_node pointer.
* @read_post_process: Callback for optional post processing of cell data
* on reads.
+ * @priv: Opaque data passed to the read_post_process hook.
*/
struct nvmem_cell_info {
const char *name;
@@ -65,6 +67,7 @@ struct nvmem_cell_info {
unsigned int nbits;
struct device_node *np;
nvmem_cell_post_process_t read_post_process;
+ void *priv;
};
/**

View File

@ -0,0 +1,215 @@
From d9fae023fe86069750092fc1c2f3a73e2fb18512 Mon Sep 17 00:00:00 2001
From: Michael Walle <michael@walle.cc>
Date: Tue, 4 Apr 2023 18:21:29 +0100
Subject: [PATCH] nvmem: layouts: sl28vpd: Add new layout driver
This layout applies to the VPD of the Kontron sl28 boards. The VPD only
contains a base MAC address. Therefore, we have to add an individual
offset to it. This is done by taking the second argument of the nvmem
phandle into account. Also this let us checking the VPD version and the
checksum.
Signed-off-by: Michael Walle <michael@walle.cc>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-22-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/nvmem/layouts/Kconfig | 9 ++
drivers/nvmem/layouts/Makefile | 2 +
drivers/nvmem/layouts/sl28vpd.c | 165 ++++++++++++++++++++++++++++++++
3 files changed, 176 insertions(+)
create mode 100644 drivers/nvmem/layouts/sl28vpd.c
--- a/drivers/nvmem/layouts/Kconfig
+++ b/drivers/nvmem/layouts/Kconfig
@@ -2,4 +2,13 @@
menu "Layout Types"
+config NVMEM_LAYOUT_SL28_VPD
+ tristate "Kontron sl28 VPD layout support"
+ select CRC8
+ help
+ Say Y here if you want to support the VPD layout of the Kontron
+ SMARC-sAL28 boards.
+
+ If unsure, say N.
+
endmenu
--- a/drivers/nvmem/layouts/Makefile
+++ b/drivers/nvmem/layouts/Makefile
@@ -2,3 +2,5 @@
#
# Makefile for nvmem layouts.
#
+
+obj-$(CONFIG_NVMEM_LAYOUT_SL28_VPD) += sl28vpd.o
--- /dev/null
+++ b/drivers/nvmem/layouts/sl28vpd.c
@@ -0,0 +1,165 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/crc8.h>
+#include <linux/etherdevice.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/nvmem-provider.h>
+#include <linux/of.h>
+#include <uapi/linux/if_ether.h>
+
+#define SL28VPD_MAGIC 'V'
+
+struct sl28vpd_header {
+ u8 magic;
+ u8 version;
+} __packed;
+
+struct sl28vpd_v1 {
+ struct sl28vpd_header header;
+ char serial_number[15];
+ u8 base_mac_address[ETH_ALEN];
+ u8 crc8;
+} __packed;
+
+static int sl28vpd_mac_address_pp(void *priv, const char *id, int index,
+ unsigned int offset, void *buf,
+ size_t bytes)
+{
+ if (bytes != ETH_ALEN)
+ return -EINVAL;
+
+ if (index < 0)
+ return -EINVAL;
+
+ if (!is_valid_ether_addr(buf))
+ return -EINVAL;
+
+ eth_addr_add(buf, index);
+
+ return 0;
+}
+
+static const struct nvmem_cell_info sl28vpd_v1_entries[] = {
+ {
+ .name = "serial-number",
+ .offset = offsetof(struct sl28vpd_v1, serial_number),
+ .bytes = sizeof_field(struct sl28vpd_v1, serial_number),
+ },
+ {
+ .name = "base-mac-address",
+ .offset = offsetof(struct sl28vpd_v1, base_mac_address),
+ .bytes = sizeof_field(struct sl28vpd_v1, base_mac_address),
+ .read_post_process = sl28vpd_mac_address_pp,
+ },
+};
+
+static int sl28vpd_v1_check_crc(struct device *dev, struct nvmem_device *nvmem)
+{
+ struct sl28vpd_v1 data_v1;
+ u8 table[CRC8_TABLE_SIZE];
+ int ret;
+ u8 crc;
+
+ crc8_populate_msb(table, 0x07);
+
+ ret = nvmem_device_read(nvmem, 0, sizeof(data_v1), &data_v1);
+ if (ret < 0)
+ return ret;
+ else if (ret != sizeof(data_v1))
+ return -EIO;
+
+ crc = crc8(table, (void *)&data_v1, sizeof(data_v1) - 1, 0);
+
+ if (crc != data_v1.crc8) {
+ dev_err(dev,
+ "Checksum is invalid (got %02x, expected %02x).\n",
+ crc, data_v1.crc8);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sl28vpd_add_cells(struct device *dev, struct nvmem_device *nvmem,
+ struct nvmem_layout *layout)
+{
+ const struct nvmem_cell_info *pinfo;
+ struct nvmem_cell_info info = {0};
+ struct device_node *layout_np;
+ struct sl28vpd_header hdr;
+ int ret, i;
+
+ /* check header */
+ ret = nvmem_device_read(nvmem, 0, sizeof(hdr), &hdr);
+ if (ret < 0)
+ return ret;
+ else if (ret != sizeof(hdr))
+ return -EIO;
+
+ if (hdr.magic != SL28VPD_MAGIC) {
+ dev_err(dev, "Invalid magic value (%02x)\n", hdr.magic);
+ return -EINVAL;
+ }
+
+ if (hdr.version != 1) {
+ dev_err(dev, "Version %d is unsupported.\n", hdr.version);
+ return -EINVAL;
+ }
+
+ ret = sl28vpd_v1_check_crc(dev, nvmem);
+ if (ret)
+ return ret;
+
+ layout_np = of_nvmem_layout_get_container(nvmem);
+ if (!layout_np)
+ return -ENOENT;
+
+ for (i = 0; i < ARRAY_SIZE(sl28vpd_v1_entries); i++) {
+ pinfo = &sl28vpd_v1_entries[i];
+
+ info.name = pinfo->name;
+ info.offset = pinfo->offset;
+ info.bytes = pinfo->bytes;
+ info.read_post_process = pinfo->read_post_process;
+ info.np = of_get_child_by_name(layout_np, pinfo->name);
+
+ ret = nvmem_add_one_cell(nvmem, &info);
+ if (ret) {
+ of_node_put(layout_np);
+ return ret;
+ }
+ }
+
+ of_node_put(layout_np);
+
+ return 0;
+}
+
+static const struct of_device_id sl28vpd_of_match_table[] = {
+ { .compatible = "kontron,sl28-vpd" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, sl28vpd_of_match_table);
+
+struct nvmem_layout sl28vpd_layout = {
+ .name = "sl28-vpd",
+ .of_match_table = sl28vpd_of_match_table,
+ .add_cells = sl28vpd_add_cells,
+};
+
+static int __init sl28vpd_init(void)
+{
+ return nvmem_layout_register(&sl28vpd_layout);
+}
+
+static void __exit sl28vpd_exit(void)
+{
+ nvmem_layout_unregister(&sl28vpd_layout);
+}
+
+module_init(sl28vpd_init);
+module_exit(sl28vpd_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Michael Walle <michael@walle.cc>");
+MODULE_DESCRIPTION("NVMEM layout driver for the VPD of Kontron sl28 boards");

View File

@ -0,0 +1,306 @@
From d3c0d12f6474216bf386101e2449cc73e5c5b61d Mon Sep 17 00:00:00 2001
From: Miquel Raynal <miquel.raynal@bootlin.com>
Date: Tue, 4 Apr 2023 18:21:31 +0100
Subject: [PATCH] nvmem: layouts: onie-tlv: Add new layout driver
This layout applies on top of any non volatile storage device containing
an ONIE table factory flashed. This table follows the tlv
(type-length-value) organization described in the link below. We cannot
afford using regular parsers because the content of these tables is
manufacturer specific and must be dynamically discovered.
Link: https://opencomputeproject.github.io/onie/design-spec/hw_requirements.html
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-24-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/nvmem/layouts/Kconfig | 9 ++
drivers/nvmem/layouts/Makefile | 1 +
drivers/nvmem/layouts/onie-tlv.c | 257 +++++++++++++++++++++++++++++++
3 files changed, 267 insertions(+)
create mode 100644 drivers/nvmem/layouts/onie-tlv.c
--- a/drivers/nvmem/layouts/Kconfig
+++ b/drivers/nvmem/layouts/Kconfig
@@ -11,4 +11,13 @@ config NVMEM_LAYOUT_SL28_VPD
If unsure, say N.
+config NVMEM_LAYOUT_ONIE_TLV
+ tristate "ONIE tlv support"
+ select CRC32
+ help
+ Say Y here if you want to support the Open Compute Project ONIE
+ Type-Length-Value standard table.
+
+ If unsure, say N.
+
endmenu
--- a/drivers/nvmem/layouts/Makefile
+++ b/drivers/nvmem/layouts/Makefile
@@ -4,3 +4,4 @@
#
obj-$(CONFIG_NVMEM_LAYOUT_SL28_VPD) += sl28vpd.o
+obj-$(CONFIG_NVMEM_LAYOUT_ONIE_TLV) += onie-tlv.o
--- /dev/null
+++ b/drivers/nvmem/layouts/onie-tlv.c
@@ -0,0 +1,257 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ONIE tlv NVMEM cells provider
+ *
+ * Copyright (C) 2022 Open Compute Group ONIE
+ * Author: Miquel Raynal <miquel.raynal@bootlin.com>
+ * Based on the nvmem driver written by: Vadym Kochan <vadym.kochan@plvision.eu>
+ * Inspired by the first layout written by: Rafał Miłecki <rafal@milecki.pl>
+ */
+
+#include <linux/crc32.h>
+#include <linux/etherdevice.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/nvmem-provider.h>
+#include <linux/of.h>
+
+#define ONIE_TLV_MAX_LEN 2048
+#define ONIE_TLV_CRC_FIELD_SZ 6
+#define ONIE_TLV_CRC_SZ 4
+#define ONIE_TLV_HDR_ID "TlvInfo"
+
+struct onie_tlv_hdr {
+ u8 id[8];
+ u8 version;
+ __be16 data_len;
+} __packed;
+
+struct onie_tlv {
+ u8 type;
+ u8 len;
+} __packed;
+
+static const char *onie_tlv_cell_name(u8 type)
+{
+ switch (type) {
+ case 0x21:
+ return "product-name";
+ case 0x22:
+ return "part-number";
+ case 0x23:
+ return "serial-number";
+ case 0x24:
+ return "mac-address";
+ case 0x25:
+ return "manufacture-date";
+ case 0x26:
+ return "device-version";
+ case 0x27:
+ return "label-revision";
+ case 0x28:
+ return "platform-name";
+ case 0x29:
+ return "onie-version";
+ case 0x2A:
+ return "num-macs";
+ case 0x2B:
+ return "manufacturer";
+ case 0x2C:
+ return "country-code";
+ case 0x2D:
+ return "vendor";
+ case 0x2E:
+ return "diag-version";
+ case 0x2F:
+ return "service-tag";
+ case 0xFD:
+ return "vendor-extension";
+ case 0xFE:
+ return "crc32";
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+static int onie_tlv_mac_read_cb(void *priv, const char *id, int index,
+ unsigned int offset, void *buf,
+ size_t bytes)
+{
+ eth_addr_add(buf, index);
+
+ return 0;
+}
+
+static nvmem_cell_post_process_t onie_tlv_read_cb(u8 type, u8 *buf)
+{
+ switch (type) {
+ case 0x24:
+ return &onie_tlv_mac_read_cb;
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+static int onie_tlv_add_cells(struct device *dev, struct nvmem_device *nvmem,
+ size_t data_len, u8 *data)
+{
+ struct nvmem_cell_info cell = {};
+ struct device_node *layout;
+ struct onie_tlv tlv;
+ unsigned int hdr_len = sizeof(struct onie_tlv_hdr);
+ unsigned int offset = 0;
+ int ret;
+
+ layout = of_nvmem_layout_get_container(nvmem);
+ if (!layout)
+ return -ENOENT;
+
+ while (offset < data_len) {
+ memcpy(&tlv, data + offset, sizeof(tlv));
+ if (offset + tlv.len >= data_len) {
+ dev_err(dev, "Out of bounds field (0x%x bytes at 0x%x)\n",
+ tlv.len, hdr_len + offset);
+ break;
+ }
+
+ cell.name = onie_tlv_cell_name(tlv.type);
+ if (!cell.name)
+ continue;
+
+ cell.offset = hdr_len + offset + sizeof(tlv.type) + sizeof(tlv.len);
+ cell.bytes = tlv.len;
+ cell.np = of_get_child_by_name(layout, cell.name);
+ cell.read_post_process = onie_tlv_read_cb(tlv.type, data + offset + sizeof(tlv));
+
+ ret = nvmem_add_one_cell(nvmem, &cell);
+ if (ret) {
+ of_node_put(layout);
+ return ret;
+ }
+
+ offset += sizeof(tlv) + tlv.len;
+ }
+
+ of_node_put(layout);
+
+ return 0;
+}
+
+static bool onie_tlv_hdr_is_valid(struct device *dev, struct onie_tlv_hdr *hdr)
+{
+ if (memcmp(hdr->id, ONIE_TLV_HDR_ID, sizeof(hdr->id))) {
+ dev_err(dev, "Invalid header\n");
+ return false;
+ }
+
+ if (hdr->version != 0x1) {
+ dev_err(dev, "Invalid version number\n");
+ return false;
+ }
+
+ return true;
+}
+
+static bool onie_tlv_crc_is_valid(struct device *dev, size_t table_len, u8 *table)
+{
+ struct onie_tlv crc_hdr;
+ u32 read_crc, calc_crc;
+ __be32 crc_be;
+
+ memcpy(&crc_hdr, table + table_len - ONIE_TLV_CRC_FIELD_SZ, sizeof(crc_hdr));
+ if (crc_hdr.type != 0xfe || crc_hdr.len != ONIE_TLV_CRC_SZ) {
+ dev_err(dev, "Invalid CRC field\n");
+ return false;
+ }
+
+ /* The table contains a JAMCRC, which is XOR'ed compared to the original
+ * CRC32 implementation as known in the Ethernet world.
+ */
+ memcpy(&crc_be, table + table_len - ONIE_TLV_CRC_SZ, ONIE_TLV_CRC_SZ);
+ read_crc = be32_to_cpu(crc_be);
+ calc_crc = crc32(~0, table, table_len - ONIE_TLV_CRC_SZ) ^ 0xFFFFFFFF;
+ if (read_crc != calc_crc) {
+ dev_err(dev, "Invalid CRC read: 0x%08x, expected: 0x%08x\n",
+ read_crc, calc_crc);
+ return false;
+ }
+
+ return true;
+}
+
+static int onie_tlv_parse_table(struct device *dev, struct nvmem_device *nvmem,
+ struct nvmem_layout *layout)
+{
+ struct onie_tlv_hdr hdr;
+ size_t table_len, data_len, hdr_len;
+ u8 *table, *data;
+ int ret;
+
+ ret = nvmem_device_read(nvmem, 0, sizeof(hdr), &hdr);
+ if (ret < 0)
+ return ret;
+
+ if (!onie_tlv_hdr_is_valid(dev, &hdr)) {
+ dev_err(dev, "Invalid ONIE TLV header\n");
+ return -EINVAL;
+ }
+
+ hdr_len = sizeof(hdr.id) + sizeof(hdr.version) + sizeof(hdr.data_len);
+ data_len = be16_to_cpu(hdr.data_len);
+ table_len = hdr_len + data_len;
+ if (table_len > ONIE_TLV_MAX_LEN) {
+ dev_err(dev, "Invalid ONIE TLV data length\n");
+ return -EINVAL;
+ }
+
+ table = devm_kmalloc(dev, table_len, GFP_KERNEL);
+ if (!table)
+ return -ENOMEM;
+
+ ret = nvmem_device_read(nvmem, 0, table_len, table);
+ if (ret != table_len)
+ return ret;
+
+ if (!onie_tlv_crc_is_valid(dev, table_len, table))
+ return -EINVAL;
+
+ data = table + hdr_len;
+ ret = onie_tlv_add_cells(dev, nvmem, data_len, data);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const struct of_device_id onie_tlv_of_match_table[] = {
+ { .compatible = "onie,tlv-layout", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, onie_tlv_of_match_table);
+
+static struct nvmem_layout onie_tlv_layout = {
+ .name = "ONIE tlv layout",
+ .of_match_table = onie_tlv_of_match_table,
+ .add_cells = onie_tlv_parse_table,
+};
+
+static int __init onie_tlv_init(void)
+{
+ return nvmem_layout_register(&onie_tlv_layout);
+}
+
+static void __exit onie_tlv_exit(void)
+{
+ nvmem_layout_unregister(&onie_tlv_layout);
+}
+
+module_init(onie_tlv_init);
+module_exit(onie_tlv_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Miquel Raynal <miquel.raynal@bootlin.com>");
+MODULE_DESCRIPTION("NVMEM layout driver for Onie TLV table parsing");
+MODULE_ALIAS("NVMEM layout driver for Onie TLV table parsing");

View File

@ -0,0 +1,32 @@
From a4fb434ef96ace5af758ca2c52c3a3f8f3abc87c Mon Sep 17 00:00:00 2001
From: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Date: Tue, 4 Apr 2023 18:21:34 +0100
Subject: [PATCH] nvmem: stm32-romem: mark OF related data as maybe unused
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The driver can be compile tested with !CONFIG_OF making certain data
unused:
drivers/nvmem/stm32-romem.c:271:34: error: stm32_romem_of_match defined but not used [-Werror=unused-const-variable=]
Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-27-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/nvmem/stm32-romem.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/drivers/nvmem/stm32-romem.c
+++ b/drivers/nvmem/stm32-romem.c
@@ -268,7 +268,7 @@ static const struct stm32_romem_cfg stm3
.ta = true,
};
-static const struct of_device_id stm32_romem_of_match[] = {
+static const struct of_device_id stm32_romem_of_match[] __maybe_unused = {
{ .compatible = "st,stm32f4-otp", }, {
.compatible = "st,stm32mp15-bsec",
.data = (void *)&stm32mp15_bsec_cfg,

View File

@ -0,0 +1,120 @@
From de6e05097f7db066afb0ad4c88b730949f7b7749 Mon Sep 17 00:00:00 2001
From: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Date: Tue, 4 Apr 2023 18:21:35 +0100
Subject: [PATCH] nvmem: mtk-efuse: Support postprocessing for GPU speed
binning data
On some MediaTek SoCs GPU speed binning data is available for read
in the SoC's eFuse array but it has a format that is incompatible
with what the OPP API expects, as we read a number from 0 to 7 but
opp-supported-hw is expecting a bitmask to enable an OPP entry:
being what we read limited to 0-7, it's straightforward to simply
convert the value to BIT(value) as a post-processing action.
So, introduce post-processing support and enable it by evaluating
the newly introduced platform data's `uses_post_processing` member,
currently enabled only for MT8186.
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-28-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/nvmem/mtk-efuse.c | 53 +++++++++++++++++++++++++++++++++++++--
1 file changed, 51 insertions(+), 2 deletions(-)
--- a/drivers/nvmem/mtk-efuse.c
+++ b/drivers/nvmem/mtk-efuse.c
@@ -10,6 +10,11 @@
#include <linux/io.h>
#include <linux/nvmem-provider.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
+
+struct mtk_efuse_pdata {
+ bool uses_post_processing;
+};
struct mtk_efuse_priv {
void __iomem *base;
@@ -29,6 +34,37 @@ static int mtk_reg_read(void *context,
return 0;
}
+static int mtk_efuse_gpu_speedbin_pp(void *context, const char *id, int index,
+ unsigned int offset, void *data, size_t bytes)
+{
+ u8 *val = data;
+
+ if (val[0] < 8)
+ val[0] = BIT(val[0]);
+
+ return 0;
+}
+
+static void mtk_efuse_fixup_cell_info(struct nvmem_device *nvmem,
+ struct nvmem_layout *layout,
+ struct nvmem_cell_info *cell)
+{
+ size_t sz = strlen(cell->name);
+
+ /*
+ * On some SoCs, the GPU speedbin is not read as bitmask but as
+ * a number with range [0-7] (max 3 bits): post process to use
+ * it in OPP tables to describe supported-hw.
+ */
+ if (cell->nbits <= 3 &&
+ strncmp(cell->name, "gpu-speedbin", min(sz, strlen("gpu-speedbin"))) == 0)
+ cell->read_post_process = mtk_efuse_gpu_speedbin_pp;
+}
+
+static struct nvmem_layout mtk_efuse_layout = {
+ .fixup_cell_info = mtk_efuse_fixup_cell_info,
+};
+
static int mtk_efuse_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -36,6 +72,7 @@ static int mtk_efuse_probe(struct platfo
struct nvmem_device *nvmem;
struct nvmem_config econfig = {};
struct mtk_efuse_priv *priv;
+ const struct mtk_efuse_pdata *pdata;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -45,20 +82,32 @@ static int mtk_efuse_probe(struct platfo
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
+ pdata = device_get_match_data(dev);
econfig.stride = 1;
econfig.word_size = 1;
econfig.reg_read = mtk_reg_read;
econfig.size = resource_size(res);
econfig.priv = priv;
econfig.dev = dev;
+ if (pdata->uses_post_processing)
+ econfig.layout = &mtk_efuse_layout;
nvmem = devm_nvmem_register(dev, &econfig);
return PTR_ERR_OR_ZERO(nvmem);
}
+static const struct mtk_efuse_pdata mtk_mt8186_efuse_pdata = {
+ .uses_post_processing = true,
+};
+
+static const struct mtk_efuse_pdata mtk_efuse_pdata = {
+ .uses_post_processing = false,
+};
+
static const struct of_device_id mtk_efuse_of_match[] = {
- { .compatible = "mediatek,mt8173-efuse",},
- { .compatible = "mediatek,efuse",},
+ { .compatible = "mediatek,mt8173-efuse", .data = &mtk_efuse_pdata },
+ { .compatible = "mediatek,mt8186-efuse", .data = &mtk_mt8186_efuse_pdata },
+ { .compatible = "mediatek,efuse", .data = &mtk_efuse_pdata },
{/* sentinel */},
};
MODULE_DEVICE_TABLE(of, mtk_efuse_of_match);

View File

@ -0,0 +1,39 @@
From 1dc552fa33cf98af3e784dbc0500da93cae3b24a Mon Sep 17 00:00:00 2001
From: Yang Li <yang.lee@linux.alibaba.com>
Date: Tue, 4 Apr 2023 18:21:38 +0100
Subject: [PATCH] nvmem: bcm-ocotp: Use devm_platform_ioremap_resource()
According to commit 7945f929f1a7 ("drivers: provide
devm_platform_ioremap_resource()"), convert platform_get_resource(),
devm_ioremap_resource() to a single call to use
devm_platform_ioremap_resource(), as this is exactly what this function
does.
Signed-off-by: Yang Li <yang.lee@linux.alibaba.com>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-31-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/nvmem/bcm-ocotp.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
--- a/drivers/nvmem/bcm-ocotp.c
+++ b/drivers/nvmem/bcm-ocotp.c
@@ -254,7 +254,6 @@ MODULE_DEVICE_TABLE(acpi, bcm_otpc_acpi_
static int bcm_otpc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct resource *res;
struct otpc_priv *priv;
struct nvmem_device *nvmem;
int err;
@@ -269,8 +268,7 @@ static int bcm_otpc_probe(struct platfor
return -ENODEV;
/* Get OTP base address register. */
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- priv->base = devm_ioremap_resource(dev, res);
+ priv->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->base)) {
dev_err(dev, "unable to map I/O memory\n");
return PTR_ERR(priv->base);

View File

@ -0,0 +1,39 @@
From 649409990d2e93fac657be7c6960c28a2c601d65 Mon Sep 17 00:00:00 2001
From: Yang Li <yang.lee@linux.alibaba.com>
Date: Tue, 4 Apr 2023 18:21:39 +0100
Subject: [PATCH] nvmem: nintendo-otp: Use devm_platform_ioremap_resource()
According to commit 7945f929f1a7 ("drivers: provide
devm_platform_ioremap_resource()"), convert platform_get_resource(),
devm_ioremap_resource() to a single call to use
devm_platform_ioremap_resource(), as this is exactly what this function
does.
Signed-off-by: Yang Li <yang.lee@linux.alibaba.com>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-32-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/nvmem/nintendo-otp.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
--- a/drivers/nvmem/nintendo-otp.c
+++ b/drivers/nvmem/nintendo-otp.c
@@ -76,7 +76,6 @@ static int nintendo_otp_probe(struct pla
struct device *dev = &pdev->dev;
const struct of_device_id *of_id =
of_match_device(nintendo_otp_of_table, dev);
- struct resource *res;
struct nvmem_device *nvmem;
struct nintendo_otp_priv *priv;
@@ -92,8 +91,7 @@ static int nintendo_otp_probe(struct pla
if (!priv)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- priv->regs = devm_ioremap_resource(dev, res);
+ priv->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->regs))
return PTR_ERR(priv->regs);

View File

@ -0,0 +1,32 @@
From c2367aa60d5e34d48582362c6de34b4131d92be7 Mon Sep 17 00:00:00 2001
From: Yang Li <yang.lee@linux.alibaba.com>
Date: Tue, 4 Apr 2023 18:21:40 +0100
Subject: [PATCH] nvmem: vf610-ocotp: Use
devm_platform_get_and_ioremap_resource()
According to commit 890cc39a8799 ("drivers: provide
devm_platform_get_and_ioremap_resource()"), convert
platform_get_resource(), devm_ioremap_resource() to a single
call to devm_platform_get_and_ioremap_resource(), as this is exactly
what this function does.
Signed-off-by: Yang Li <yang.lee@linux.alibaba.com>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-33-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/nvmem/vf610-ocotp.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
--- a/drivers/nvmem/vf610-ocotp.c
+++ b/drivers/nvmem/vf610-ocotp.c
@@ -219,8 +219,7 @@ static int vf610_ocotp_probe(struct plat
if (!ocotp_dev)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- ocotp_dev->base = devm_ioremap_resource(dev, res);
+ ocotp_dev->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(ocotp_dev->base))
return PTR_ERR(ocotp_dev->base);

View File

@ -0,0 +1,115 @@
From 55d4980ce55b6bb4be66877de4dbec513911b988 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
Date: Tue, 4 Apr 2023 18:21:42 +0100
Subject: [PATCH] nvmem: core: support specifying both: cell raw data & post
read lengths
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Callback .read_post_process() is designed to modify raw cell content
before providing it to the consumer. So far we were dealing with
modifications that didn't affect cell size (length). In some cases
however cell content needs to be reformatted and resized.
It's required e.g. to provide properly formatted MAC address in case
it's stored in a non-binary format (e.g. using ASCII).
There were few discussions how to optimally handle that. Following
possible solutions were considered:
1. Allow .read_post_process() to realloc (resize) content buffer
2. Allow .read_post_process() to adjust (decrease) just buffer length
3. Register NVMEM cells using post-read sizes
The preferred solution was the last one. The problem is that simply
adjusting "bytes" in NVMEM providers would result in core code NOT
passing whole raw data to .read_post_process() callbacks. It means
callback functions couldn't do their job without somehow manually
reading original cell content on their own.
This patch deals with that by registering NVMEM cells with both lengths:
raw content one and post read one. It allows:
1. Core code to read whole raw cell content
2. Callbacks to return content they want
Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-35-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/nvmem/core.c | 11 +++++++----
include/linux/nvmem-provider.h | 2 ++
2 files changed, 9 insertions(+), 4 deletions(-)
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -50,6 +50,7 @@ struct nvmem_device {
struct nvmem_cell_entry {
const char *name;
int offset;
+ size_t raw_len;
int bytes;
int bit_offset;
int nbits;
@@ -469,6 +470,7 @@ static int nvmem_cell_info_to_nvmem_cell
{
cell->nvmem = nvmem;
cell->offset = info->offset;
+ cell->raw_len = info->raw_len ?: info->bytes;
cell->bytes = info->bytes;
cell->name = info->name;
cell->read_post_process = info->read_post_process;
@@ -1560,7 +1562,7 @@ static int __nvmem_cell_read(struct nvme
{
int rc;
- rc = nvmem_reg_read(nvmem, cell->offset, buf, cell->bytes);
+ rc = nvmem_reg_read(nvmem, cell->offset, buf, cell->raw_len);
if (rc)
return rc;
@@ -1571,7 +1573,7 @@ static int __nvmem_cell_read(struct nvme
if (cell->read_post_process) {
rc = cell->read_post_process(cell->priv, id, index,
- cell->offset, buf, cell->bytes);
+ cell->offset, buf, cell->raw_len);
if (rc)
return rc;
}
@@ -1594,14 +1596,15 @@ static int __nvmem_cell_read(struct nvme
*/
void *nvmem_cell_read(struct nvmem_cell *cell, size_t *len)
{
- struct nvmem_device *nvmem = cell->entry->nvmem;
+ struct nvmem_cell_entry *entry = cell->entry;
+ struct nvmem_device *nvmem = entry->nvmem;
u8 *buf;
int rc;
if (!nvmem)
return ERR_PTR(-EINVAL);
- buf = kzalloc(cell->entry->bytes, GFP_KERNEL);
+ buf = kzalloc(max_t(size_t, entry->raw_len, entry->bytes), GFP_KERNEL);
if (!buf)
return ERR_PTR(-ENOMEM);
--- a/include/linux/nvmem-provider.h
+++ b/include/linux/nvmem-provider.h
@@ -51,6 +51,7 @@ struct nvmem_keepout {
* struct nvmem_cell_info - NVMEM cell description
* @name: Name.
* @offset: Offset within the NVMEM device.
+ * @raw_len: Length of raw data (without post processing).
* @bytes: Length of the cell.
* @bit_offset: Bit offset if cell is smaller than a byte.
* @nbits: Number of bits.
@@ -62,6 +63,7 @@ struct nvmem_keepout {
struct nvmem_cell_info {
const char *name;
unsigned int offset;
+ size_t raw_len;
unsigned int bytes;
unsigned int bit_offset;
unsigned int nbits;

View File

@ -0,0 +1,42 @@
From 814c978f02db17f16e6aa2efa2a929372f06da09 Mon Sep 17 00:00:00 2001
From: Miquel Raynal <miquel.raynal@bootlin.com>
Date: Tue, 4 Apr 2023 18:21:44 +0100
Subject: [PATCH] nvmem: Add macro to register nvmem layout drivers
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Provide a module_nvmem_layout_driver() macro at the end of the
nvmem-provider.h header to reduce the boilerplate when registering nvmem
layout drivers.
Suggested-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Acked-by: Rafał Miłecki <rafal@milecki.pl>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-37-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
include/linux/nvmem-provider.h | 6 ++++++
1 file changed, 6 insertions(+)
--- a/include/linux/nvmem-provider.h
+++ b/include/linux/nvmem-provider.h
@@ -9,6 +9,7 @@
#ifndef _LINUX_NVMEM_PROVIDER_H
#define _LINUX_NVMEM_PROVIDER_H
+#include <linux/device/driver.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/gpio/consumer.h>
@@ -242,4 +243,9 @@ nvmem_layout_get_match_data(struct nvmem
}
#endif /* CONFIG_NVMEM */
+
+#define module_nvmem_layout_driver(__layout_driver) \
+ module_driver(__layout_driver, nvmem_layout_register, \
+ nvmem_layout_unregister)
+
#endif /* ifndef _LINUX_NVMEM_PROVIDER_H */

View File

@ -0,0 +1,39 @@
From 0abdf99fe0c86252ba274703425f8d543d7e7f0d Mon Sep 17 00:00:00 2001
From: Miquel Raynal <miquel.raynal@bootlin.com>
Date: Tue, 4 Apr 2023 18:21:45 +0100
Subject: [PATCH] nvmem: layouts: sl28vpd: Use module_nvmem_layout_driver()
Stop open-coding the module init/exit functions. Use the
module_nvmem_layout_driver() instead.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-38-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/nvmem/layouts/sl28vpd.c | 14 +-------------
1 file changed, 1 insertion(+), 13 deletions(-)
--- a/drivers/nvmem/layouts/sl28vpd.c
+++ b/drivers/nvmem/layouts/sl28vpd.c
@@ -146,19 +146,7 @@ struct nvmem_layout sl28vpd_layout = {
.of_match_table = sl28vpd_of_match_table,
.add_cells = sl28vpd_add_cells,
};
-
-static int __init sl28vpd_init(void)
-{
- return nvmem_layout_register(&sl28vpd_layout);
-}
-
-static void __exit sl28vpd_exit(void)
-{
- nvmem_layout_unregister(&sl28vpd_layout);
-}
-
-module_init(sl28vpd_init);
-module_exit(sl28vpd_exit);
+module_nvmem_layout_driver(sl28vpd_layout);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Michael Walle <michael@walle.cc>");

View File

@ -0,0 +1,39 @@
From d119eb38faab61125aaa4f63c74eef61585cf34c Mon Sep 17 00:00:00 2001
From: Miquel Raynal <miquel.raynal@bootlin.com>
Date: Tue, 4 Apr 2023 18:21:46 +0100
Subject: [PATCH] nvmem: layouts: onie-tlv: Use module_nvmem_layout_driver()
Stop open-coding the module init/exit functions. Use the
module_nvmem_layout_driver() instead.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-39-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/nvmem/layouts/onie-tlv.c | 14 +-------------
1 file changed, 1 insertion(+), 13 deletions(-)
--- a/drivers/nvmem/layouts/onie-tlv.c
+++ b/drivers/nvmem/layouts/onie-tlv.c
@@ -237,19 +237,7 @@ static struct nvmem_layout onie_tlv_layo
.of_match_table = onie_tlv_of_match_table,
.add_cells = onie_tlv_parse_table,
};
-
-static int __init onie_tlv_init(void)
-{
- return nvmem_layout_register(&onie_tlv_layout);
-}
-
-static void __exit onie_tlv_exit(void)
-{
- nvmem_layout_unregister(&onie_tlv_layout);
-}
-
-module_init(onie_tlv_init);
-module_exit(onie_tlv_exit);
+module_nvmem_layout_driver(onie_tlv_layout);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Miquel Raynal <miquel.raynal@bootlin.com>");

View File

@ -0,0 +1,24 @@
From 6b13e4b6a9a45028ac730e550380077df1845912 Mon Sep 17 00:00:00 2001
From: Miquel Raynal <miquel.raynal@bootlin.com>
Date: Tue, 4 Apr 2023 18:21:47 +0100
Subject: [PATCH] nvmem: layouts: onie-tlv: Drop wrong module alias
The MODULE_ALIAS macro is misused here as it carries the
description. There is currently no relevant alias to provide so let's
just drop it.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-40-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/nvmem/layouts/onie-tlv.c | 1 -
1 file changed, 1 deletion(-)
--- a/drivers/nvmem/layouts/onie-tlv.c
+++ b/drivers/nvmem/layouts/onie-tlv.c
@@ -242,4 +242,3 @@ module_nvmem_layout_driver(onie_tlv_layo
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Miquel Raynal <miquel.raynal@bootlin.com>");
MODULE_DESCRIPTION("NVMEM layout driver for Onie TLV table parsing");
-MODULE_ALIAS("NVMEM layout driver for Onie TLV table parsing");

View File

@ -0,0 +1,31 @@
From a8642cd11635a35a5f1dc31857887900d6610778 Mon Sep 17 00:00:00 2001
From: Tom Rix <trix@redhat.com>
Date: Tue, 4 Apr 2023 18:21:48 +0100
Subject: [PATCH] nvmem: layouts: sl28vpd: set varaiable sl28vpd_layout
storage-class-specifier to static
smatch reports
drivers/nvmem/layouts/sl28vpd.c:144:21: warning: symbol
'sl28vpd_layout' was not declared. Should it be static?
This variable is only used in one file so it should be static.
Signed-off-by: Tom Rix <trix@redhat.com>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-41-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/nvmem/layouts/sl28vpd.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/drivers/nvmem/layouts/sl28vpd.c
+++ b/drivers/nvmem/layouts/sl28vpd.c
@@ -141,7 +141,7 @@ static const struct of_device_id sl28vpd
};
MODULE_DEVICE_TABLE(of, sl28vpd_of_match_table);
-struct nvmem_layout sl28vpd_layout = {
+static struct nvmem_layout sl28vpd_layout = {
.name = "sl28-vpd",
.of_match_table = sl28vpd_of_match_table,
.add_cells = sl28vpd_add_cells,

View File

@ -0,0 +1,35 @@
From bcd1fe07def0f070eb5f31594620aaee6f81d31a Mon Sep 17 00:00:00 2001
From: Nick Alcock <nick.alcock@oracle.com>
Date: Tue, 4 Apr 2023 18:21:11 +0100
Subject: [PATCH] nvmem: xilinx: zynqmp: make modular
This driver has a MODULE_LICENSE but is not tristate so cannot be
built as a module, unlike all its peers: make it modular to match.
Signed-off-by: Nick Alcock <nick.alcock@oracle.com>
Suggested-by: Michal Simek <michal.simek@amd.com>
Cc: Luis Chamberlain <mcgrof@kernel.org>
Cc: linux-modules@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: Hitomi Hasegawa <hasegawa-hitomi@fujitsu.com>
Cc: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Cc: Michal Simek <michal.simek@xilinx.com>
Cc: linux-arm-kernel@lists.infradead.org
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-4-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/nvmem/Kconfig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/drivers/nvmem/Kconfig
+++ b/drivers/nvmem/Kconfig
@@ -368,7 +368,7 @@ config NVMEM_VF610_OCOTP
be called nvmem-vf610-ocotp.
config NVMEM_ZYNQMP
- bool "Xilinx ZYNQMP SoC nvmem firmware support"
+ tristate "Xilinx ZYNQMP SoC nvmem firmware support"
depends on ARCH_ZYNQMP
help
This is a driver to access hardware related data like

View File

@ -0,0 +1,387 @@
From 266570f496b90dea8fda893c2cf7c28d63ae2bd9 Mon Sep 17 00:00:00 2001
From: Michael Walle <michael@walle.cc>
Date: Tue, 4 Apr 2023 18:21:21 +0100
Subject: [PATCH] nvmem: core: introduce NVMEM layouts
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.
Co-developed-by: Miquel Raynal <miquel.raynal@bootlin.com>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Signed-off-by: Michael Walle <michael@walle.cc>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-14-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
Documentation/driver-api/nvmem.rst | 15 ++++
drivers/nvmem/Kconfig | 4 +
drivers/nvmem/Makefile | 1 +
drivers/nvmem/core.c | 120 +++++++++++++++++++++++++++++
drivers/nvmem/layouts/Kconfig | 5 ++
drivers/nvmem/layouts/Makefile | 4 +
include/linux/nvmem-consumer.h | 7 ++
include/linux/nvmem-provider.h | 51 ++++++++++++
8 files changed, 207 insertions(+)
create mode 100644 drivers/nvmem/layouts/Kconfig
create mode 100644 drivers/nvmem/layouts/Makefile
--- a/Documentation/driver-api/nvmem.rst
+++ b/Documentation/driver-api/nvmem.rst
@@ -189,3 +189,18 @@ ex::
=====================
See Documentation/devicetree/bindings/nvmem/nvmem.txt
+
+8. NVMEM layouts
+================
+
+NVMEM layouts are yet another mechanism to create cells. With the device
+tree binding it is possible to specify simple cells by using an offset
+and a length. Sometimes, the cells doesn't have a static offset, but
+the content is still well defined, e.g. tag-length-values. In this case,
+the NVMEM device content has to be first parsed and the cells need to
+be added accordingly. Layouts let you read the content of the NVMEM device
+and let you add cells dynamically.
+
+Another use case for layouts is the post processing of cells. With layouts,
+it is possible to associate a custom post processing hook to a cell. It
+even possible to add this hook to cells not created by the layout itself.
--- a/drivers/nvmem/Kconfig
+++ b/drivers/nvmem/Kconfig
@@ -21,6 +21,10 @@ config NVMEM_SYSFS
This interface is mostly used by userspace applications to
read/write directly into nvmem.
+# Layouts
+
+source "drivers/nvmem/layouts/Kconfig"
+
# Devices
config NVMEM_APPLE_EFUSES
--- 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_APPLE_EFUSES) += nvmem-apple-efuses.o
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -40,6 +40,7 @@ struct nvmem_device {
nvmem_reg_write_t reg_write;
nvmem_cell_post_process_t cell_post_process;
struct gpio_desc *wp_gpio;
+ struct nvmem_layout *layout;
void *priv;
};
@@ -74,6 +75,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)
{
@@ -728,6 +732,101 @@ static int nvmem_add_cells_from_of(struc
return 0;
}
+int __nvmem_layout_register(struct nvmem_layout *layout, struct module *owner)
+{
+ layout->owner = owner;
+
+ spin_lock(&nvmem_layout_lock);
+ list_add(&layout->node, &nvmem_layouts);
+ spin_unlock(&nvmem_layout_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(__nvmem_layout_register);
+
+void nvmem_layout_unregister(struct nvmem_layout *layout)
+{
+ spin_lock(&nvmem_layout_lock);
+ list_del(&layout->node);
+ spin_unlock(&nvmem_layout_lock);
+}
+EXPORT_SYMBOL_GPL(nvmem_layout_unregister);
+
+static struct nvmem_layout *nvmem_layout_get(struct nvmem_device *nvmem)
+{
+ struct device_node *layout_np, *np = nvmem->dev.of_node;
+ struct nvmem_layout *l, *layout = NULL;
+
+ layout_np = of_get_child_by_name(np, "nvmem-layout");
+ if (!layout_np)
+ return NULL;
+
+ spin_lock(&nvmem_layout_lock);
+
+ list_for_each_entry(l, &nvmem_layouts, node) {
+ if (of_match_node(l->of_match_table, layout_np)) {
+ if (try_module_get(l->owner))
+ layout = l;
+
+ break;
+ }
+ }
+
+ spin_unlock(&nvmem_layout_lock);
+ of_node_put(layout_np);
+
+ return layout;
+}
+
+static void nvmem_layout_put(struct nvmem_layout *layout)
+{
+ if (layout)
+ module_put(layout->owner);
+}
+
+static int nvmem_add_cells_from_layout(struct nvmem_device *nvmem)
+{
+ struct nvmem_layout *layout = nvmem->layout;
+ int ret;
+
+ if (layout && layout->add_cells) {
+ ret = layout->add_cells(&nvmem->dev, nvmem, layout);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+#if IS_ENABLED(CONFIG_OF)
+/**
+ * of_nvmem_layout_get_container() - Get OF node to layout container.
+ *
+ * @nvmem: nvmem device.
+ *
+ * Return: a node pointer with refcount incremented or NULL if no
+ * container exists. Use of_node_put() on it when done.
+ */
+struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem)
+{
+ return of_get_child_by_name(nvmem->dev.of_node, "nvmem-layout");
+}
+EXPORT_SYMBOL_GPL(of_nvmem_layout_get_container);
+#endif
+
+const void *nvmem_layout_get_match_data(struct nvmem_device *nvmem,
+ struct nvmem_layout *layout)
+{
+ struct device_node __maybe_unused *layout_np;
+ const struct of_device_id *match;
+
+ layout_np = of_nvmem_layout_get_container(nvmem);
+ match = of_match_node(layout->of_match_table, layout_np);
+
+ 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
@@ -834,6 +933,12 @@ struct nvmem_device *nvmem_register(cons
goto err_put_device;
}
+ /*
+ * If the driver supplied a layout by config->layout, the module
+ * pointer will be NULL and nvmem_layout_put() will be a noop.
+ */
+ nvmem->layout = config->layout ?: nvmem_layout_get(nvmem);
+
if (config->cells) {
rval = nvmem_add_cells(nvmem, config->cells, config->ncells);
if (rval)
@@ -854,12 +959,17 @@ struct nvmem_device *nvmem_register(cons
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;
err_remove_cells:
nvmem_device_remove_all_cells(nvmem);
+ nvmem_layout_put(nvmem->layout);
if (config->compat)
nvmem_sysfs_remove_compat(nvmem, config);
err_put_device:
@@ -881,6 +991,7 @@ static void nvmem_device_release(struct
device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom);
nvmem_device_remove_all_cells(nvmem);
+ nvmem_layout_put(nvmem->layout);
device_unregister(&nvmem->dev);
}
@@ -1246,6 +1357,15 @@ struct nvmem_cell *of_nvmem_cell_get(str
return ERR_PTR(-EINVAL);
}
+ /* nvmem layouts produce cells within the nvmem-layout container */
+ if (of_node_name_eq(nvmem_np, "nvmem-layout")) {
+ nvmem_np = of_get_next_parent(nvmem_np);
+ if (!nvmem_np) {
+ of_node_put(cell_np);
+ return ERR_PTR(-EINVAL);
+ }
+ }
+
nvmem = __nvmem_device_get(nvmem_np, device_match_of_node);
of_node_put(nvmem_np);
if (IS_ERR(nvmem)) {
--- /dev/null
+++ b/drivers/nvmem/layouts/Kconfig
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+
+menu "Layout Types"
+
+endmenu
--- /dev/null
+++ b/drivers/nvmem/layouts/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for nvmem layouts.
+#
--- a/include/linux/nvmem-consumer.h
+++ b/include/linux/nvmem-consumer.h
@@ -239,6 +239,7 @@ struct nvmem_cell *of_nvmem_cell_get(str
const char *id);
struct nvmem_device *of_nvmem_device_get(struct device_node *np,
const char *name);
+struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem);
#else
static inline struct nvmem_cell *of_nvmem_cell_get(struct device_node *np,
const char *id)
@@ -251,6 +252,12 @@ static inline struct nvmem_device *of_nv
{
return ERR_PTR(-EOPNOTSUPP);
}
+
+static inline struct device_node *
+of_nvmem_layout_get_container(struct nvmem_device *nvmem)
+{
+ return ERR_PTR(-EOPNOTSUPP);
+}
#endif /* CONFIG_NVMEM && CONFIG_OF */
#endif /* ifndef _LINUX_NVMEM_CONSUMER_H */
--- a/include/linux/nvmem-provider.h
+++ b/include/linux/nvmem-provider.h
@@ -88,6 +88,7 @@ struct nvmem_cell_info {
* @stride: Minimum read/write access stride.
* @priv: User context passed to read/write callbacks.
* @ignore_wp: Write Protect pin is managed by the provider.
+ * @layout: Fixed layout associated with this nvmem device.
*
* Note: A default "nvmem<id>" name will be assigned to the device if
* no name is specified in its configuration. In such case "<id>" is
@@ -109,6 +110,7 @@ struct nvmem_config {
bool read_only;
bool root_only;
bool ignore_wp;
+ struct nvmem_layout *layout;
struct device_node *of_node;
bool no_of_node;
nvmem_reg_read_t reg_read;
@@ -142,6 +144,33 @@ 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().
+ * @owner: Pointer to struct module.
+ * @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;
+ int (*add_cells)(struct device *dev, struct nvmem_device *nvmem,
+ struct nvmem_layout *layout);
+
+ /* private */
+ struct module *owner;
+ struct list_head node;
+};
+
#if IS_ENABLED(CONFIG_NVMEM)
struct nvmem_device *nvmem_register(const struct nvmem_config *cfg);
@@ -156,6 +185,14 @@ void nvmem_del_cell_table(struct nvmem_c
int nvmem_add_one_cell(struct nvmem_device *nvmem,
const struct nvmem_cell_info *info);
+int __nvmem_layout_register(struct nvmem_layout *layout, struct module *owner);
+#define nvmem_layout_register(layout) \
+ __nvmem_layout_register(layout, THIS_MODULE)
+void nvmem_layout_unregister(struct nvmem_layout *layout);
+
+const void *nvmem_layout_get_match_data(struct nvmem_device *nvmem,
+ struct nvmem_layout *layout);
+
#else
static inline struct nvmem_device *nvmem_register(const struct nvmem_config *c)
@@ -179,5 +216,19 @@ static inline int nvmem_add_one_cell(str
return -EOPNOTSUPP;
}
+static inline int nvmem_layout_register(struct nvmem_layout *layout)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline void nvmem_layout_unregister(struct nvmem_layout *layout) {}
+
+static inline const void *
+nvmem_layout_get_match_data(struct nvmem_device *nvmem,
+ struct nvmem_layout *layout)
+{
+ return NULL;
+}
+
#endif /* CONFIG_NVMEM */
#endif /* ifndef _LINUX_NVMEM_PROVIDER_H */

View File

@ -0,0 +1,61 @@
From 6468a6f45148fb5e95c86b4efebf63f9abcd2137 Mon Sep 17 00:00:00 2001
From: Miquel Raynal <miquel.raynal@bootlin.com>
Date: Tue, 4 Apr 2023 18:21:22 +0100
Subject: [PATCH] nvmem: core: handle the absence of expected layouts
Make nvmem_layout_get() return -EPROBE_DEFER while the expected layout
is not available. This condition cannot be triggered today as nvmem
layout drivers are initialed as part of an early init call, but soon
these drivers will be converted into modules and be initialized with a
standard priority, so the unavailability of the drivers might become a
reality that must be taken care of.
Let's anticipate this by telling the caller the layout might not yet be
available. A probe deferral is requested in this case.
Please note this does not affect any nvmem device not using layouts,
because an early check against the "nvmem-layout" container presence
will return NULL in this case.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Tested-by: Michael Walle <michael@walle.cc>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-15-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/nvmem/core.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -755,7 +755,7 @@ EXPORT_SYMBOL_GPL(nvmem_layout_unregiste
static struct nvmem_layout *nvmem_layout_get(struct nvmem_device *nvmem)
{
struct device_node *layout_np, *np = nvmem->dev.of_node;
- struct nvmem_layout *l, *layout = NULL;
+ struct nvmem_layout *l, *layout = ERR_PTR(-EPROBE_DEFER);
layout_np = of_get_child_by_name(np, "nvmem-layout");
if (!layout_np)
@@ -938,6 +938,13 @@ struct nvmem_device *nvmem_register(cons
* pointer will be NULL and nvmem_layout_put() will be a noop.
*/
nvmem->layout = config->layout ?: nvmem_layout_get(nvmem);
+ if (IS_ERR(nvmem->layout)) {
+ rval = PTR_ERR(nvmem->layout);
+ nvmem->layout = NULL;
+
+ if (rval == -EPROBE_DEFER)
+ goto err_teardown_compat;
+ }
if (config->cells) {
rval = nvmem_add_cells(nvmem, config->cells, config->ncells);
@@ -970,6 +977,7 @@ struct nvmem_device *nvmem_register(cons
err_remove_cells:
nvmem_device_remove_all_cells(nvmem);
nvmem_layout_put(nvmem->layout);
+err_teardown_compat:
if (config->compat)
nvmem_sysfs_remove_compat(nvmem, config);
err_put_device:

View File

@ -0,0 +1,52 @@
From b1c37bec1ccfe5ccab72bc0ddc0dfa45c43e2de2 Mon Sep 17 00:00:00 2001
From: Miquel Raynal <miquel.raynal@bootlin.com>
Date: Tue, 4 Apr 2023 18:21:23 +0100
Subject: [PATCH] nvmem: core: request layout modules loading
When a storage device like an eeprom or an mtd device probes, it
registers an nvmem device if the nvmem subsystem has been enabled (bool
symbol). During nvmem registration, if the device is using layouts to
expose dynamic nvmem cells, the core will first try to get a reference
over the layout driver callbacks. In practice there is not relationship
that can be described between the storage driver and the nvmem
layout. So there is no way we can enforce both drivers will be built-in
or both will be modules. If the storage device driver is built-in but
the layout is built as a module, instead of badly failing with an
endless probe deferral loop, lets just make a modprobe call in case the
driver was made available in an initramfs with
of_device_node_request_module(), and offer a fully functional system to
the user.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Tested-by: Michael Walle <michael@walle.cc>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-16-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/nvmem/core.c | 8 ++++++++
1 file changed, 8 insertions(+)
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -17,6 +17,7 @@
#include <linux/nvmem-provider.h>
#include <linux/gpio/consumer.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/slab.h>
struct nvmem_device {
@@ -761,6 +762,13 @@ static struct nvmem_layout *nvmem_layout
if (!layout_np)
return NULL;
+ /*
+ * In case the nvmem device was built-in while the layout was built as a
+ * module, we shall manually request the layout driver loading otherwise
+ * we'll never have any match.
+ */
+ of_request_module(layout_np);
+
spin_lock(&nvmem_layout_lock);
list_for_each_entry(l, &nvmem_layouts, node) {

View File

@ -0,0 +1,86 @@
From 345ec382cd4b736c36e01f155d08c913b225b736 Mon Sep 17 00:00:00 2001
From: Michael Walle <michael@walle.cc>
Date: Tue, 4 Apr 2023 18:21:24 +0100
Subject: [PATCH] nvmem: core: add per-cell post processing
Instead of relying on the name the consumer is using for the cell, like
it is done for the nvmem .cell_post_process configuration parameter,
provide a per-cell post processing hook. This can then be populated by
the NVMEM provider (or the NVMEM layout) when adding the cell.
Signed-off-by: Michael Walle <michael@walle.cc>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-17-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/nvmem/core.c | 17 +++++++++++++++++
include/linux/nvmem-provider.h | 3 +++
2 files changed, 20 insertions(+)
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -54,6 +54,7 @@ struct nvmem_cell_entry {
int bytes;
int bit_offset;
int nbits;
+ nvmem_cell_post_process_t read_post_process;
struct device_node *np;
struct nvmem_device *nvmem;
struct list_head node;
@@ -470,6 +471,7 @@ static int nvmem_cell_info_to_nvmem_cell
cell->offset = info->offset;
cell->bytes = info->bytes;
cell->name = info->name;
+ cell->read_post_process = info->read_post_process;
cell->bit_offset = info->bit_offset;
cell->nbits = info->nbits;
@@ -1563,6 +1565,13 @@ static int __nvmem_cell_read(struct nvme
if (cell->bit_offset || cell->nbits)
nvmem_shift_read_buffer_in_place(cell, buf);
+ if (cell->read_post_process) {
+ rc = cell->read_post_process(nvmem->priv, id, index,
+ cell->offset, buf, cell->bytes);
+ if (rc)
+ return rc;
+ }
+
if (nvmem->cell_post_process) {
rc = nvmem->cell_post_process(nvmem->priv, id, index,
cell->offset, buf, cell->bytes);
@@ -1671,6 +1680,14 @@ static int __nvmem_cell_entry_write(stru
(cell->bit_offset == 0 && len != cell->bytes))
return -EINVAL;
+ /*
+ * Any cells which have a read_post_process hook are read-only because
+ * we cannot reverse the operation and it might affect other cells,
+ * too.
+ */
+ if (cell->read_post_process)
+ return -EINVAL;
+
if (cell->bit_offset || cell->nbits) {
buf = nvmem_cell_prepare_write_buffer(cell, buf, len);
if (IS_ERR(buf))
--- a/include/linux/nvmem-provider.h
+++ b/include/linux/nvmem-provider.h
@@ -54,6 +54,8 @@ struct nvmem_keepout {
* @bit_offset: Bit offset if cell is smaller than a byte.
* @nbits: Number of bits.
* @np: Optional device_node pointer.
+ * @read_post_process: Callback for optional post processing of cell data
+ * on reads.
*/
struct nvmem_cell_info {
const char *name;
@@ -62,6 +64,7 @@ struct nvmem_cell_info {
unsigned int bit_offset;
unsigned int nbits;
struct device_node *np;
+ nvmem_cell_post_process_t read_post_process;
};
/**

View File

@ -0,0 +1,59 @@
From de12c9691501ccba41a154c223869f82be4c12fd Mon Sep 17 00:00:00 2001
From: Michael Walle <michael@walle.cc>
Date: Tue, 4 Apr 2023 18:21:25 +0100
Subject: [PATCH] nvmem: core: allow to modify a cell before adding it
Provide a way to modify a cell before it will get added. This is useful
to attach a custom post processing hook via a layout.
Signed-off-by: Michael Walle <michael@walle.cc>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-18-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/nvmem/core.c | 4 ++++
include/linux/nvmem-provider.h | 5 +++++
2 files changed, 9 insertions(+)
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -695,6 +695,7 @@ static int nvmem_validate_keepouts(struc
static int nvmem_add_cells_from_of(struct nvmem_device *nvmem)
{
+ struct nvmem_layout *layout = nvmem->layout;
struct device *dev = &nvmem->dev;
struct device_node *child;
const __be32 *addr;
@@ -724,6 +725,9 @@ static int nvmem_add_cells_from_of(struc
info.np = of_node_get(child);
+ if (layout && layout->fixup_cell_info)
+ layout->fixup_cell_info(nvmem, layout, &info);
+
ret = nvmem_add_one_cell(nvmem, &info);
kfree(info.name);
if (ret) {
--- a/include/linux/nvmem-provider.h
+++ b/include/linux/nvmem-provider.h
@@ -155,6 +155,8 @@ struct nvmem_cell_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().
+ * @fixup_cell_info: Will be called before a cell is added. Can be
+ * used to modify the nvmem_cell_info.
* @owner: Pointer to struct module.
* @node: List node.
*
@@ -168,6 +170,9 @@ struct nvmem_layout {
const struct of_device_id *of_match_table;
int (*add_cells)(struct device *dev, struct nvmem_device *nvmem,
struct nvmem_layout *layout);
+ void (*fixup_cell_info)(struct nvmem_device *nvmem,
+ struct nvmem_layout *layout,
+ struct nvmem_cell_info *cell);
/* private */
struct module *owner;

View File

@ -0,0 +1,81 @@
From 6c56a82d7895a213a43182a5d01a21a906a79847 Mon Sep 17 00:00:00 2001
From: Michael Walle <michael@walle.cc>
Date: Tue, 4 Apr 2023 18:21:26 +0100
Subject: [PATCH] nvmem: imx-ocotp: replace global post processing with layouts
In preparation of retiring the global post processing hook change this
driver to use layouts. The layout will be supplied during registration
and will be used to add the post processing hook to all added cells.
Signed-off-by: Michael Walle <michael@walle.cc>
Tested-by: Michael Walle <michael@walle.cc> # on kontron-pitx-imx8m
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-19-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/nvmem/imx-ocotp.c | 30 +++++++++++++++++++-----------
1 file changed, 19 insertions(+), 11 deletions(-)
--- a/drivers/nvmem/imx-ocotp.c
+++ b/drivers/nvmem/imx-ocotp.c
@@ -225,18 +225,13 @@ read_end:
static int imx_ocotp_cell_pp(void *context, const char *id, int index,
unsigned int offset, void *data, size_t bytes)
{
- struct ocotp_priv *priv = context;
+ u8 *buf = data;
+ int i;
/* Deal with some post processing of nvmem cell data */
- if (id && !strcmp(id, "mac-address")) {
- if (priv->params->reverse_mac_address) {
- u8 *buf = data;
- int i;
-
- for (i = 0; i < bytes/2; i++)
- swap(buf[i], buf[bytes - i - 1]);
- }
- }
+ if (id && !strcmp(id, "mac-address"))
+ for (i = 0; i < bytes / 2; i++)
+ swap(buf[i], buf[bytes - i - 1]);
return 0;
}
@@ -488,7 +483,6 @@ static struct nvmem_config imx_ocotp_nvm
.stride = 1,
.reg_read = imx_ocotp_read,
.reg_write = imx_ocotp_write,
- .cell_post_process = imx_ocotp_cell_pp,
};
static const struct ocotp_params imx6q_params = {
@@ -595,6 +589,17 @@ static const struct of_device_id imx_oco
};
MODULE_DEVICE_TABLE(of, imx_ocotp_dt_ids);
+static void imx_ocotp_fixup_cell_info(struct nvmem_device *nvmem,
+ struct nvmem_layout *layout,
+ struct nvmem_cell_info *cell)
+{
+ cell->read_post_process = imx_ocotp_cell_pp;
+}
+
+struct nvmem_layout imx_ocotp_layout = {
+ .fixup_cell_info = imx_ocotp_fixup_cell_info,
+};
+
static int imx_ocotp_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -619,6 +624,9 @@ static int imx_ocotp_probe(struct platfo
imx_ocotp_nvmem_config.size = 4 * priv->params->nregs;
imx_ocotp_nvmem_config.dev = dev;
imx_ocotp_nvmem_config.priv = priv;
+ if (priv->params->reverse_mac_address)
+ imx_ocotp_nvmem_config.layout = &imx_ocotp_layout;
+
priv->config = &imx_ocotp_nvmem_config;
clk_prepare_enable(priv->clk);

View File

@ -0,0 +1,68 @@
From 011e40a166fdaa65fb9946b7cd91efec85b70dbb Mon Sep 17 00:00:00 2001
From: Michael Walle <michael@walle.cc>
Date: Tue, 4 Apr 2023 18:21:27 +0100
Subject: [PATCH] nvmem: cell: drop global cell_post_process
There are no users anymore for the global cell_post_process callback
anymore. New users should use proper nvmem layouts.
Signed-off-by: Michael Walle <michael@walle.cc>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-20-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/nvmem/core.c | 9 ---------
include/linux/nvmem-provider.h | 2 --
2 files changed, 11 deletions(-)
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -39,7 +39,6 @@ struct nvmem_device {
unsigned int nkeepout;
nvmem_reg_read_t reg_read;
nvmem_reg_write_t reg_write;
- nvmem_cell_post_process_t cell_post_process;
struct gpio_desc *wp_gpio;
struct nvmem_layout *layout;
void *priv;
@@ -903,7 +902,6 @@ struct nvmem_device *nvmem_register(cons
nvmem->type = config->type;
nvmem->reg_read = config->reg_read;
nvmem->reg_write = config->reg_write;
- nvmem->cell_post_process = config->cell_post_process;
nvmem->keepout = config->keepout;
nvmem->nkeepout = config->nkeepout;
if (config->of_node)
@@ -1575,13 +1573,6 @@ static int __nvmem_cell_read(struct nvme
if (rc)
return rc;
}
-
- if (nvmem->cell_post_process) {
- rc = nvmem->cell_post_process(nvmem->priv, id, index,
- cell->offset, buf, cell->bytes);
- if (rc)
- return rc;
- }
if (len)
*len = cell->bytes;
--- a/include/linux/nvmem-provider.h
+++ b/include/linux/nvmem-provider.h
@@ -85,7 +85,6 @@ struct nvmem_cell_info {
* @no_of_node: Device should not use the parent's of_node even if it's !NULL.
* @reg_read: Callback to read data.
* @reg_write: Callback to write data.
- * @cell_post_process: Callback for vendor specific post processing of cell data
* @size: Device size.
* @word_size: Minimum read/write access granularity.
* @stride: Minimum read/write access stride.
@@ -118,7 +117,6 @@ struct nvmem_config {
bool no_of_node;
nvmem_reg_read_t reg_read;
nvmem_reg_write_t reg_write;
- nvmem_cell_post_process_t cell_post_process;
int size;
int word_size;
int stride;

View File

@ -0,0 +1,76 @@
From 8a134fd9f9323f4c39ec27055b3d3723cfb5c1e9 Mon Sep 17 00:00:00 2001
From: Michael Walle <michael@walle.cc>
Date: Tue, 4 Apr 2023 18:21:28 +0100
Subject: [PATCH] nvmem: core: provide own priv pointer in post process
callback
It doesn't make any more sense to have a opaque pointer set up by the
nvmem device. Usually, the layout isn't associated with a particular
nvmem device. Instead, let the caller who set the post process callback
provide the priv pointer.
Signed-off-by: Michael Walle <michael@walle.cc>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-21-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/nvmem/core.c | 4 +++-
include/linux/nvmem-provider.h | 5 ++++-
2 files changed, 7 insertions(+), 2 deletions(-)
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -54,6 +54,7 @@ struct nvmem_cell_entry {
int bit_offset;
int nbits;
nvmem_cell_post_process_t read_post_process;
+ void *priv;
struct device_node *np;
struct nvmem_device *nvmem;
struct list_head node;
@@ -471,6 +472,7 @@ static int nvmem_cell_info_to_nvmem_cell
cell->bytes = info->bytes;
cell->name = info->name;
cell->read_post_process = info->read_post_process;
+ cell->priv = info->priv;
cell->bit_offset = info->bit_offset;
cell->nbits = info->nbits;
@@ -1568,7 +1570,7 @@ static int __nvmem_cell_read(struct nvme
nvmem_shift_read_buffer_in_place(cell, buf);
if (cell->read_post_process) {
- rc = cell->read_post_process(nvmem->priv, id, index,
+ rc = cell->read_post_process(cell->priv, id, index,
cell->offset, buf, cell->bytes);
if (rc)
return rc;
--- a/include/linux/nvmem-provider.h
+++ b/include/linux/nvmem-provider.h
@@ -20,7 +20,8 @@ typedef int (*nvmem_reg_write_t)(void *p
void *val, size_t bytes);
/* used for vendor specific post processing of cell data */
typedef int (*nvmem_cell_post_process_t)(void *priv, const char *id, int index,
- unsigned int offset, void *buf, size_t bytes);
+ unsigned int offset, void *buf,
+ size_t bytes);
enum nvmem_type {
NVMEM_TYPE_UNKNOWN = 0,
@@ -56,6 +57,7 @@ struct nvmem_keepout {
* @np: Optional device_node pointer.
* @read_post_process: Callback for optional post processing of cell data
* on reads.
+ * @priv: Opaque data passed to the read_post_process hook.
*/
struct nvmem_cell_info {
const char *name;
@@ -65,6 +67,7 @@ struct nvmem_cell_info {
unsigned int nbits;
struct device_node *np;
nvmem_cell_post_process_t read_post_process;
+ void *priv;
};
/**

View File

@ -0,0 +1,215 @@
From d9fae023fe86069750092fc1c2f3a73e2fb18512 Mon Sep 17 00:00:00 2001
From: Michael Walle <michael@walle.cc>
Date: Tue, 4 Apr 2023 18:21:29 +0100
Subject: [PATCH] nvmem: layouts: sl28vpd: Add new layout driver
This layout applies to the VPD of the Kontron sl28 boards. The VPD only
contains a base MAC address. Therefore, we have to add an individual
offset to it. This is done by taking the second argument of the nvmem
phandle into account. Also this let us checking the VPD version and the
checksum.
Signed-off-by: Michael Walle <michael@walle.cc>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-22-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/nvmem/layouts/Kconfig | 9 ++
drivers/nvmem/layouts/Makefile | 2 +
drivers/nvmem/layouts/sl28vpd.c | 165 ++++++++++++++++++++++++++++++++
3 files changed, 176 insertions(+)
create mode 100644 drivers/nvmem/layouts/sl28vpd.c
--- a/drivers/nvmem/layouts/Kconfig
+++ b/drivers/nvmem/layouts/Kconfig
@@ -2,4 +2,13 @@
menu "Layout Types"
+config NVMEM_LAYOUT_SL28_VPD
+ tristate "Kontron sl28 VPD layout support"
+ select CRC8
+ help
+ Say Y here if you want to support the VPD layout of the Kontron
+ SMARC-sAL28 boards.
+
+ If unsure, say N.
+
endmenu
--- a/drivers/nvmem/layouts/Makefile
+++ b/drivers/nvmem/layouts/Makefile
@@ -2,3 +2,5 @@
#
# Makefile for nvmem layouts.
#
+
+obj-$(CONFIG_NVMEM_LAYOUT_SL28_VPD) += sl28vpd.o
--- /dev/null
+++ b/drivers/nvmem/layouts/sl28vpd.c
@@ -0,0 +1,165 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/crc8.h>
+#include <linux/etherdevice.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/nvmem-provider.h>
+#include <linux/of.h>
+#include <uapi/linux/if_ether.h>
+
+#define SL28VPD_MAGIC 'V'
+
+struct sl28vpd_header {
+ u8 magic;
+ u8 version;
+} __packed;
+
+struct sl28vpd_v1 {
+ struct sl28vpd_header header;
+ char serial_number[15];
+ u8 base_mac_address[ETH_ALEN];
+ u8 crc8;
+} __packed;
+
+static int sl28vpd_mac_address_pp(void *priv, const char *id, int index,
+ unsigned int offset, void *buf,
+ size_t bytes)
+{
+ if (bytes != ETH_ALEN)
+ return -EINVAL;
+
+ if (index < 0)
+ return -EINVAL;
+
+ if (!is_valid_ether_addr(buf))
+ return -EINVAL;
+
+ eth_addr_add(buf, index);
+
+ return 0;
+}
+
+static const struct nvmem_cell_info sl28vpd_v1_entries[] = {
+ {
+ .name = "serial-number",
+ .offset = offsetof(struct sl28vpd_v1, serial_number),
+ .bytes = sizeof_field(struct sl28vpd_v1, serial_number),
+ },
+ {
+ .name = "base-mac-address",
+ .offset = offsetof(struct sl28vpd_v1, base_mac_address),
+ .bytes = sizeof_field(struct sl28vpd_v1, base_mac_address),
+ .read_post_process = sl28vpd_mac_address_pp,
+ },
+};
+
+static int sl28vpd_v1_check_crc(struct device *dev, struct nvmem_device *nvmem)
+{
+ struct sl28vpd_v1 data_v1;
+ u8 table[CRC8_TABLE_SIZE];
+ int ret;
+ u8 crc;
+
+ crc8_populate_msb(table, 0x07);
+
+ ret = nvmem_device_read(nvmem, 0, sizeof(data_v1), &data_v1);
+ if (ret < 0)
+ return ret;
+ else if (ret != sizeof(data_v1))
+ return -EIO;
+
+ crc = crc8(table, (void *)&data_v1, sizeof(data_v1) - 1, 0);
+
+ if (crc != data_v1.crc8) {
+ dev_err(dev,
+ "Checksum is invalid (got %02x, expected %02x).\n",
+ crc, data_v1.crc8);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sl28vpd_add_cells(struct device *dev, struct nvmem_device *nvmem,
+ struct nvmem_layout *layout)
+{
+ const struct nvmem_cell_info *pinfo;
+ struct nvmem_cell_info info = {0};
+ struct device_node *layout_np;
+ struct sl28vpd_header hdr;
+ int ret, i;
+
+ /* check header */
+ ret = nvmem_device_read(nvmem, 0, sizeof(hdr), &hdr);
+ if (ret < 0)
+ return ret;
+ else if (ret != sizeof(hdr))
+ return -EIO;
+
+ if (hdr.magic != SL28VPD_MAGIC) {
+ dev_err(dev, "Invalid magic value (%02x)\n", hdr.magic);
+ return -EINVAL;
+ }
+
+ if (hdr.version != 1) {
+ dev_err(dev, "Version %d is unsupported.\n", hdr.version);
+ return -EINVAL;
+ }
+
+ ret = sl28vpd_v1_check_crc(dev, nvmem);
+ if (ret)
+ return ret;
+
+ layout_np = of_nvmem_layout_get_container(nvmem);
+ if (!layout_np)
+ return -ENOENT;
+
+ for (i = 0; i < ARRAY_SIZE(sl28vpd_v1_entries); i++) {
+ pinfo = &sl28vpd_v1_entries[i];
+
+ info.name = pinfo->name;
+ info.offset = pinfo->offset;
+ info.bytes = pinfo->bytes;
+ info.read_post_process = pinfo->read_post_process;
+ info.np = of_get_child_by_name(layout_np, pinfo->name);
+
+ ret = nvmem_add_one_cell(nvmem, &info);
+ if (ret) {
+ of_node_put(layout_np);
+ return ret;
+ }
+ }
+
+ of_node_put(layout_np);
+
+ return 0;
+}
+
+static const struct of_device_id sl28vpd_of_match_table[] = {
+ { .compatible = "kontron,sl28-vpd" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, sl28vpd_of_match_table);
+
+struct nvmem_layout sl28vpd_layout = {
+ .name = "sl28-vpd",
+ .of_match_table = sl28vpd_of_match_table,
+ .add_cells = sl28vpd_add_cells,
+};
+
+static int __init sl28vpd_init(void)
+{
+ return nvmem_layout_register(&sl28vpd_layout);
+}
+
+static void __exit sl28vpd_exit(void)
+{
+ nvmem_layout_unregister(&sl28vpd_layout);
+}
+
+module_init(sl28vpd_init);
+module_exit(sl28vpd_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Michael Walle <michael@walle.cc>");
+MODULE_DESCRIPTION("NVMEM layout driver for the VPD of Kontron sl28 boards");

View File

@ -0,0 +1,306 @@
From d3c0d12f6474216bf386101e2449cc73e5c5b61d Mon Sep 17 00:00:00 2001
From: Miquel Raynal <miquel.raynal@bootlin.com>
Date: Tue, 4 Apr 2023 18:21:31 +0100
Subject: [PATCH] nvmem: layouts: onie-tlv: Add new layout driver
This layout applies on top of any non volatile storage device containing
an ONIE table factory flashed. This table follows the tlv
(type-length-value) organization described in the link below. We cannot
afford using regular parsers because the content of these tables is
manufacturer specific and must be dynamically discovered.
Link: https://opencomputeproject.github.io/onie/design-spec/hw_requirements.html
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-24-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/nvmem/layouts/Kconfig | 9 ++
drivers/nvmem/layouts/Makefile | 1 +
drivers/nvmem/layouts/onie-tlv.c | 257 +++++++++++++++++++++++++++++++
3 files changed, 267 insertions(+)
create mode 100644 drivers/nvmem/layouts/onie-tlv.c
--- a/drivers/nvmem/layouts/Kconfig
+++ b/drivers/nvmem/layouts/Kconfig
@@ -11,4 +11,13 @@ config NVMEM_LAYOUT_SL28_VPD
If unsure, say N.
+config NVMEM_LAYOUT_ONIE_TLV
+ tristate "ONIE tlv support"
+ select CRC32
+ help
+ Say Y here if you want to support the Open Compute Project ONIE
+ Type-Length-Value standard table.
+
+ If unsure, say N.
+
endmenu
--- a/drivers/nvmem/layouts/Makefile
+++ b/drivers/nvmem/layouts/Makefile
@@ -4,3 +4,4 @@
#
obj-$(CONFIG_NVMEM_LAYOUT_SL28_VPD) += sl28vpd.o
+obj-$(CONFIG_NVMEM_LAYOUT_ONIE_TLV) += onie-tlv.o
--- /dev/null
+++ b/drivers/nvmem/layouts/onie-tlv.c
@@ -0,0 +1,257 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ONIE tlv NVMEM cells provider
+ *
+ * Copyright (C) 2022 Open Compute Group ONIE
+ * Author: Miquel Raynal <miquel.raynal@bootlin.com>
+ * Based on the nvmem driver written by: Vadym Kochan <vadym.kochan@plvision.eu>
+ * Inspired by the first layout written by: Rafał Miłecki <rafal@milecki.pl>
+ */
+
+#include <linux/crc32.h>
+#include <linux/etherdevice.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/nvmem-provider.h>
+#include <linux/of.h>
+
+#define ONIE_TLV_MAX_LEN 2048
+#define ONIE_TLV_CRC_FIELD_SZ 6
+#define ONIE_TLV_CRC_SZ 4
+#define ONIE_TLV_HDR_ID "TlvInfo"
+
+struct onie_tlv_hdr {
+ u8 id[8];
+ u8 version;
+ __be16 data_len;
+} __packed;
+
+struct onie_tlv {
+ u8 type;
+ u8 len;
+} __packed;
+
+static const char *onie_tlv_cell_name(u8 type)
+{
+ switch (type) {
+ case 0x21:
+ return "product-name";
+ case 0x22:
+ return "part-number";
+ case 0x23:
+ return "serial-number";
+ case 0x24:
+ return "mac-address";
+ case 0x25:
+ return "manufacture-date";
+ case 0x26:
+ return "device-version";
+ case 0x27:
+ return "label-revision";
+ case 0x28:
+ return "platform-name";
+ case 0x29:
+ return "onie-version";
+ case 0x2A:
+ return "num-macs";
+ case 0x2B:
+ return "manufacturer";
+ case 0x2C:
+ return "country-code";
+ case 0x2D:
+ return "vendor";
+ case 0x2E:
+ return "diag-version";
+ case 0x2F:
+ return "service-tag";
+ case 0xFD:
+ return "vendor-extension";
+ case 0xFE:
+ return "crc32";
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+static int onie_tlv_mac_read_cb(void *priv, const char *id, int index,
+ unsigned int offset, void *buf,
+ size_t bytes)
+{
+ eth_addr_add(buf, index);
+
+ return 0;
+}
+
+static nvmem_cell_post_process_t onie_tlv_read_cb(u8 type, u8 *buf)
+{
+ switch (type) {
+ case 0x24:
+ return &onie_tlv_mac_read_cb;
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+static int onie_tlv_add_cells(struct device *dev, struct nvmem_device *nvmem,
+ size_t data_len, u8 *data)
+{
+ struct nvmem_cell_info cell = {};
+ struct device_node *layout;
+ struct onie_tlv tlv;
+ unsigned int hdr_len = sizeof(struct onie_tlv_hdr);
+ unsigned int offset = 0;
+ int ret;
+
+ layout = of_nvmem_layout_get_container(nvmem);
+ if (!layout)
+ return -ENOENT;
+
+ while (offset < data_len) {
+ memcpy(&tlv, data + offset, sizeof(tlv));
+ if (offset + tlv.len >= data_len) {
+ dev_err(dev, "Out of bounds field (0x%x bytes at 0x%x)\n",
+ tlv.len, hdr_len + offset);
+ break;
+ }
+
+ cell.name = onie_tlv_cell_name(tlv.type);
+ if (!cell.name)
+ continue;
+
+ cell.offset = hdr_len + offset + sizeof(tlv.type) + sizeof(tlv.len);
+ cell.bytes = tlv.len;
+ cell.np = of_get_child_by_name(layout, cell.name);
+ cell.read_post_process = onie_tlv_read_cb(tlv.type, data + offset + sizeof(tlv));
+
+ ret = nvmem_add_one_cell(nvmem, &cell);
+ if (ret) {
+ of_node_put(layout);
+ return ret;
+ }
+
+ offset += sizeof(tlv) + tlv.len;
+ }
+
+ of_node_put(layout);
+
+ return 0;
+}
+
+static bool onie_tlv_hdr_is_valid(struct device *dev, struct onie_tlv_hdr *hdr)
+{
+ if (memcmp(hdr->id, ONIE_TLV_HDR_ID, sizeof(hdr->id))) {
+ dev_err(dev, "Invalid header\n");
+ return false;
+ }
+
+ if (hdr->version != 0x1) {
+ dev_err(dev, "Invalid version number\n");
+ return false;
+ }
+
+ return true;
+}
+
+static bool onie_tlv_crc_is_valid(struct device *dev, size_t table_len, u8 *table)
+{
+ struct onie_tlv crc_hdr;
+ u32 read_crc, calc_crc;
+ __be32 crc_be;
+
+ memcpy(&crc_hdr, table + table_len - ONIE_TLV_CRC_FIELD_SZ, sizeof(crc_hdr));
+ if (crc_hdr.type != 0xfe || crc_hdr.len != ONIE_TLV_CRC_SZ) {
+ dev_err(dev, "Invalid CRC field\n");
+ return false;
+ }
+
+ /* The table contains a JAMCRC, which is XOR'ed compared to the original
+ * CRC32 implementation as known in the Ethernet world.
+ */
+ memcpy(&crc_be, table + table_len - ONIE_TLV_CRC_SZ, ONIE_TLV_CRC_SZ);
+ read_crc = be32_to_cpu(crc_be);
+ calc_crc = crc32(~0, table, table_len - ONIE_TLV_CRC_SZ) ^ 0xFFFFFFFF;
+ if (read_crc != calc_crc) {
+ dev_err(dev, "Invalid CRC read: 0x%08x, expected: 0x%08x\n",
+ read_crc, calc_crc);
+ return false;
+ }
+
+ return true;
+}
+
+static int onie_tlv_parse_table(struct device *dev, struct nvmem_device *nvmem,
+ struct nvmem_layout *layout)
+{
+ struct onie_tlv_hdr hdr;
+ size_t table_len, data_len, hdr_len;
+ u8 *table, *data;
+ int ret;
+
+ ret = nvmem_device_read(nvmem, 0, sizeof(hdr), &hdr);
+ if (ret < 0)
+ return ret;
+
+ if (!onie_tlv_hdr_is_valid(dev, &hdr)) {
+ dev_err(dev, "Invalid ONIE TLV header\n");
+ return -EINVAL;
+ }
+
+ hdr_len = sizeof(hdr.id) + sizeof(hdr.version) + sizeof(hdr.data_len);
+ data_len = be16_to_cpu(hdr.data_len);
+ table_len = hdr_len + data_len;
+ if (table_len > ONIE_TLV_MAX_LEN) {
+ dev_err(dev, "Invalid ONIE TLV data length\n");
+ return -EINVAL;
+ }
+
+ table = devm_kmalloc(dev, table_len, GFP_KERNEL);
+ if (!table)
+ return -ENOMEM;
+
+ ret = nvmem_device_read(nvmem, 0, table_len, table);
+ if (ret != table_len)
+ return ret;
+
+ if (!onie_tlv_crc_is_valid(dev, table_len, table))
+ return -EINVAL;
+
+ data = table + hdr_len;
+ ret = onie_tlv_add_cells(dev, nvmem, data_len, data);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const struct of_device_id onie_tlv_of_match_table[] = {
+ { .compatible = "onie,tlv-layout", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, onie_tlv_of_match_table);
+
+static struct nvmem_layout onie_tlv_layout = {
+ .name = "ONIE tlv layout",
+ .of_match_table = onie_tlv_of_match_table,
+ .add_cells = onie_tlv_parse_table,
+};
+
+static int __init onie_tlv_init(void)
+{
+ return nvmem_layout_register(&onie_tlv_layout);
+}
+
+static void __exit onie_tlv_exit(void)
+{
+ nvmem_layout_unregister(&onie_tlv_layout);
+}
+
+module_init(onie_tlv_init);
+module_exit(onie_tlv_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Miquel Raynal <miquel.raynal@bootlin.com>");
+MODULE_DESCRIPTION("NVMEM layout driver for Onie TLV table parsing");
+MODULE_ALIAS("NVMEM layout driver for Onie TLV table parsing");

View File

@ -0,0 +1,32 @@
From a4fb434ef96ace5af758ca2c52c3a3f8f3abc87c Mon Sep 17 00:00:00 2001
From: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Date: Tue, 4 Apr 2023 18:21:34 +0100
Subject: [PATCH] nvmem: stm32-romem: mark OF related data as maybe unused
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The driver can be compile tested with !CONFIG_OF making certain data
unused:
drivers/nvmem/stm32-romem.c:271:34: error: stm32_romem_of_match defined but not used [-Werror=unused-const-variable=]
Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-27-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/nvmem/stm32-romem.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/drivers/nvmem/stm32-romem.c
+++ b/drivers/nvmem/stm32-romem.c
@@ -268,7 +268,7 @@ static const struct stm32_romem_cfg stm3
.ta = true,
};
-static const struct of_device_id stm32_romem_of_match[] = {
+static const struct of_device_id stm32_romem_of_match[] __maybe_unused = {
{ .compatible = "st,stm32f4-otp", }, {
.compatible = "st,stm32mp15-bsec",
.data = (void *)&stm32mp15_bsec_cfg,

View File

@ -0,0 +1,120 @@
From de6e05097f7db066afb0ad4c88b730949f7b7749 Mon Sep 17 00:00:00 2001
From: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Date: Tue, 4 Apr 2023 18:21:35 +0100
Subject: [PATCH] nvmem: mtk-efuse: Support postprocessing for GPU speed
binning data
On some MediaTek SoCs GPU speed binning data is available for read
in the SoC's eFuse array but it has a format that is incompatible
with what the OPP API expects, as we read a number from 0 to 7 but
opp-supported-hw is expecting a bitmask to enable an OPP entry:
being what we read limited to 0-7, it's straightforward to simply
convert the value to BIT(value) as a post-processing action.
So, introduce post-processing support and enable it by evaluating
the newly introduced platform data's `uses_post_processing` member,
currently enabled only for MT8186.
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-28-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/nvmem/mtk-efuse.c | 53 +++++++++++++++++++++++++++++++++++++--
1 file changed, 51 insertions(+), 2 deletions(-)
--- a/drivers/nvmem/mtk-efuse.c
+++ b/drivers/nvmem/mtk-efuse.c
@@ -10,6 +10,11 @@
#include <linux/io.h>
#include <linux/nvmem-provider.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
+
+struct mtk_efuse_pdata {
+ bool uses_post_processing;
+};
struct mtk_efuse_priv {
void __iomem *base;
@@ -29,6 +34,37 @@ static int mtk_reg_read(void *context,
return 0;
}
+static int mtk_efuse_gpu_speedbin_pp(void *context, const char *id, int index,
+ unsigned int offset, void *data, size_t bytes)
+{
+ u8 *val = data;
+
+ if (val[0] < 8)
+ val[0] = BIT(val[0]);
+
+ return 0;
+}
+
+static void mtk_efuse_fixup_cell_info(struct nvmem_device *nvmem,
+ struct nvmem_layout *layout,
+ struct nvmem_cell_info *cell)
+{
+ size_t sz = strlen(cell->name);
+
+ /*
+ * On some SoCs, the GPU speedbin is not read as bitmask but as
+ * a number with range [0-7] (max 3 bits): post process to use
+ * it in OPP tables to describe supported-hw.
+ */
+ if (cell->nbits <= 3 &&
+ strncmp(cell->name, "gpu-speedbin", min(sz, strlen("gpu-speedbin"))) == 0)
+ cell->read_post_process = mtk_efuse_gpu_speedbin_pp;
+}
+
+static struct nvmem_layout mtk_efuse_layout = {
+ .fixup_cell_info = mtk_efuse_fixup_cell_info,
+};
+
static int mtk_efuse_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -36,6 +72,7 @@ static int mtk_efuse_probe(struct platfo
struct nvmem_device *nvmem;
struct nvmem_config econfig = {};
struct mtk_efuse_priv *priv;
+ const struct mtk_efuse_pdata *pdata;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -45,20 +82,32 @@ static int mtk_efuse_probe(struct platfo
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
+ pdata = device_get_match_data(dev);
econfig.stride = 1;
econfig.word_size = 1;
econfig.reg_read = mtk_reg_read;
econfig.size = resource_size(res);
econfig.priv = priv;
econfig.dev = dev;
+ if (pdata->uses_post_processing)
+ econfig.layout = &mtk_efuse_layout;
nvmem = devm_nvmem_register(dev, &econfig);
return PTR_ERR_OR_ZERO(nvmem);
}
+static const struct mtk_efuse_pdata mtk_mt8186_efuse_pdata = {
+ .uses_post_processing = true,
+};
+
+static const struct mtk_efuse_pdata mtk_efuse_pdata = {
+ .uses_post_processing = false,
+};
+
static const struct of_device_id mtk_efuse_of_match[] = {
- { .compatible = "mediatek,mt8173-efuse",},
- { .compatible = "mediatek,efuse",},
+ { .compatible = "mediatek,mt8173-efuse", .data = &mtk_efuse_pdata },
+ { .compatible = "mediatek,mt8186-efuse", .data = &mtk_mt8186_efuse_pdata },
+ { .compatible = "mediatek,efuse", .data = &mtk_efuse_pdata },
{/* sentinel */},
};
MODULE_DEVICE_TABLE(of, mtk_efuse_of_match);

View File

@ -0,0 +1,39 @@
From 1dc552fa33cf98af3e784dbc0500da93cae3b24a Mon Sep 17 00:00:00 2001
From: Yang Li <yang.lee@linux.alibaba.com>
Date: Tue, 4 Apr 2023 18:21:38 +0100
Subject: [PATCH] nvmem: bcm-ocotp: Use devm_platform_ioremap_resource()
According to commit 7945f929f1a7 ("drivers: provide
devm_platform_ioremap_resource()"), convert platform_get_resource(),
devm_ioremap_resource() to a single call to use
devm_platform_ioremap_resource(), as this is exactly what this function
does.
Signed-off-by: Yang Li <yang.lee@linux.alibaba.com>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-31-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/nvmem/bcm-ocotp.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
--- a/drivers/nvmem/bcm-ocotp.c
+++ b/drivers/nvmem/bcm-ocotp.c
@@ -254,7 +254,6 @@ MODULE_DEVICE_TABLE(acpi, bcm_otpc_acpi_
static int bcm_otpc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct resource *res;
struct otpc_priv *priv;
struct nvmem_device *nvmem;
int err;
@@ -269,8 +268,7 @@ static int bcm_otpc_probe(struct platfor
return -ENODEV;
/* Get OTP base address register. */
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- priv->base = devm_ioremap_resource(dev, res);
+ priv->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->base)) {
dev_err(dev, "unable to map I/O memory\n");
return PTR_ERR(priv->base);

View File

@ -0,0 +1,39 @@
From 649409990d2e93fac657be7c6960c28a2c601d65 Mon Sep 17 00:00:00 2001
From: Yang Li <yang.lee@linux.alibaba.com>
Date: Tue, 4 Apr 2023 18:21:39 +0100
Subject: [PATCH] nvmem: nintendo-otp: Use devm_platform_ioremap_resource()
According to commit 7945f929f1a7 ("drivers: provide
devm_platform_ioremap_resource()"), convert platform_get_resource(),
devm_ioremap_resource() to a single call to use
devm_platform_ioremap_resource(), as this is exactly what this function
does.
Signed-off-by: Yang Li <yang.lee@linux.alibaba.com>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-32-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/nvmem/nintendo-otp.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
--- a/drivers/nvmem/nintendo-otp.c
+++ b/drivers/nvmem/nintendo-otp.c
@@ -76,7 +76,6 @@ static int nintendo_otp_probe(struct pla
struct device *dev = &pdev->dev;
const struct of_device_id *of_id =
of_match_device(nintendo_otp_of_table, dev);
- struct resource *res;
struct nvmem_device *nvmem;
struct nintendo_otp_priv *priv;
@@ -92,8 +91,7 @@ static int nintendo_otp_probe(struct pla
if (!priv)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- priv->regs = devm_ioremap_resource(dev, res);
+ priv->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->regs))
return PTR_ERR(priv->regs);

View File

@ -0,0 +1,32 @@
From c2367aa60d5e34d48582362c6de34b4131d92be7 Mon Sep 17 00:00:00 2001
From: Yang Li <yang.lee@linux.alibaba.com>
Date: Tue, 4 Apr 2023 18:21:40 +0100
Subject: [PATCH] nvmem: vf610-ocotp: Use
devm_platform_get_and_ioremap_resource()
According to commit 890cc39a8799 ("drivers: provide
devm_platform_get_and_ioremap_resource()"), convert
platform_get_resource(), devm_ioremap_resource() to a single
call to devm_platform_get_and_ioremap_resource(), as this is exactly
what this function does.
Signed-off-by: Yang Li <yang.lee@linux.alibaba.com>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-33-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/nvmem/vf610-ocotp.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
--- a/drivers/nvmem/vf610-ocotp.c
+++ b/drivers/nvmem/vf610-ocotp.c
@@ -219,8 +219,7 @@ static int vf610_ocotp_probe(struct plat
if (!ocotp_dev)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- ocotp_dev->base = devm_ioremap_resource(dev, res);
+ ocotp_dev->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(ocotp_dev->base))
return PTR_ERR(ocotp_dev->base);

View File

@ -0,0 +1,115 @@
From 55d4980ce55b6bb4be66877de4dbec513911b988 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
Date: Tue, 4 Apr 2023 18:21:42 +0100
Subject: [PATCH] nvmem: core: support specifying both: cell raw data & post
read lengths
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Callback .read_post_process() is designed to modify raw cell content
before providing it to the consumer. So far we were dealing with
modifications that didn't affect cell size (length). In some cases
however cell content needs to be reformatted and resized.
It's required e.g. to provide properly formatted MAC address in case
it's stored in a non-binary format (e.g. using ASCII).
There were few discussions how to optimally handle that. Following
possible solutions were considered:
1. Allow .read_post_process() to realloc (resize) content buffer
2. Allow .read_post_process() to adjust (decrease) just buffer length
3. Register NVMEM cells using post-read sizes
The preferred solution was the last one. The problem is that simply
adjusting "bytes" in NVMEM providers would result in core code NOT
passing whole raw data to .read_post_process() callbacks. It means
callback functions couldn't do their job without somehow manually
reading original cell content on their own.
This patch deals with that by registering NVMEM cells with both lengths:
raw content one and post read one. It allows:
1. Core code to read whole raw cell content
2. Callbacks to return content they want
Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-35-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/nvmem/core.c | 11 +++++++----
include/linux/nvmem-provider.h | 2 ++
2 files changed, 9 insertions(+), 4 deletions(-)
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -50,6 +50,7 @@ struct nvmem_device {
struct nvmem_cell_entry {
const char *name;
int offset;
+ size_t raw_len;
int bytes;
int bit_offset;
int nbits;
@@ -469,6 +470,7 @@ static int nvmem_cell_info_to_nvmem_cell
{
cell->nvmem = nvmem;
cell->offset = info->offset;
+ cell->raw_len = info->raw_len ?: info->bytes;
cell->bytes = info->bytes;
cell->name = info->name;
cell->read_post_process = info->read_post_process;
@@ -1560,7 +1562,7 @@ static int __nvmem_cell_read(struct nvme
{
int rc;
- rc = nvmem_reg_read(nvmem, cell->offset, buf, cell->bytes);
+ rc = nvmem_reg_read(nvmem, cell->offset, buf, cell->raw_len);
if (rc)
return rc;
@@ -1571,7 +1573,7 @@ static int __nvmem_cell_read(struct nvme
if (cell->read_post_process) {
rc = cell->read_post_process(cell->priv, id, index,
- cell->offset, buf, cell->bytes);
+ cell->offset, buf, cell->raw_len);
if (rc)
return rc;
}
@@ -1594,14 +1596,15 @@ static int __nvmem_cell_read(struct nvme
*/
void *nvmem_cell_read(struct nvmem_cell *cell, size_t *len)
{
- struct nvmem_device *nvmem = cell->entry->nvmem;
+ struct nvmem_cell_entry *entry = cell->entry;
+ struct nvmem_device *nvmem = entry->nvmem;
u8 *buf;
int rc;
if (!nvmem)
return ERR_PTR(-EINVAL);
- buf = kzalloc(cell->entry->bytes, GFP_KERNEL);
+ buf = kzalloc(max_t(size_t, entry->raw_len, entry->bytes), GFP_KERNEL);
if (!buf)
return ERR_PTR(-ENOMEM);
--- a/include/linux/nvmem-provider.h
+++ b/include/linux/nvmem-provider.h
@@ -51,6 +51,7 @@ struct nvmem_keepout {
* struct nvmem_cell_info - NVMEM cell description
* @name: Name.
* @offset: Offset within the NVMEM device.
+ * @raw_len: Length of raw data (without post processing).
* @bytes: Length of the cell.
* @bit_offset: Bit offset if cell is smaller than a byte.
* @nbits: Number of bits.
@@ -62,6 +63,7 @@ struct nvmem_keepout {
struct nvmem_cell_info {
const char *name;
unsigned int offset;
+ size_t raw_len;
unsigned int bytes;
unsigned int bit_offset;
unsigned int nbits;

View File

@ -0,0 +1,42 @@
From 814c978f02db17f16e6aa2efa2a929372f06da09 Mon Sep 17 00:00:00 2001
From: Miquel Raynal <miquel.raynal@bootlin.com>
Date: Tue, 4 Apr 2023 18:21:44 +0100
Subject: [PATCH] nvmem: Add macro to register nvmem layout drivers
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Provide a module_nvmem_layout_driver() macro at the end of the
nvmem-provider.h header to reduce the boilerplate when registering nvmem
layout drivers.
Suggested-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Acked-by: Rafał Miłecki <rafal@milecki.pl>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-37-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
include/linux/nvmem-provider.h | 6 ++++++
1 file changed, 6 insertions(+)
--- a/include/linux/nvmem-provider.h
+++ b/include/linux/nvmem-provider.h
@@ -9,6 +9,7 @@
#ifndef _LINUX_NVMEM_PROVIDER_H
#define _LINUX_NVMEM_PROVIDER_H
+#include <linux/device/driver.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/gpio/consumer.h>
@@ -242,4 +243,9 @@ nvmem_layout_get_match_data(struct nvmem
}
#endif /* CONFIG_NVMEM */
+
+#define module_nvmem_layout_driver(__layout_driver) \
+ module_driver(__layout_driver, nvmem_layout_register, \
+ nvmem_layout_unregister)
+
#endif /* ifndef _LINUX_NVMEM_PROVIDER_H */

View File

@ -0,0 +1,39 @@
From 0abdf99fe0c86252ba274703425f8d543d7e7f0d Mon Sep 17 00:00:00 2001
From: Miquel Raynal <miquel.raynal@bootlin.com>
Date: Tue, 4 Apr 2023 18:21:45 +0100
Subject: [PATCH] nvmem: layouts: sl28vpd: Use module_nvmem_layout_driver()
Stop open-coding the module init/exit functions. Use the
module_nvmem_layout_driver() instead.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-38-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/nvmem/layouts/sl28vpd.c | 14 +-------------
1 file changed, 1 insertion(+), 13 deletions(-)
--- a/drivers/nvmem/layouts/sl28vpd.c
+++ b/drivers/nvmem/layouts/sl28vpd.c
@@ -146,19 +146,7 @@ struct nvmem_layout sl28vpd_layout = {
.of_match_table = sl28vpd_of_match_table,
.add_cells = sl28vpd_add_cells,
};
-
-static int __init sl28vpd_init(void)
-{
- return nvmem_layout_register(&sl28vpd_layout);
-}
-
-static void __exit sl28vpd_exit(void)
-{
- nvmem_layout_unregister(&sl28vpd_layout);
-}
-
-module_init(sl28vpd_init);
-module_exit(sl28vpd_exit);
+module_nvmem_layout_driver(sl28vpd_layout);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Michael Walle <michael@walle.cc>");

View File

@ -0,0 +1,39 @@
From d119eb38faab61125aaa4f63c74eef61585cf34c Mon Sep 17 00:00:00 2001
From: Miquel Raynal <miquel.raynal@bootlin.com>
Date: Tue, 4 Apr 2023 18:21:46 +0100
Subject: [PATCH] nvmem: layouts: onie-tlv: Use module_nvmem_layout_driver()
Stop open-coding the module init/exit functions. Use the
module_nvmem_layout_driver() instead.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-39-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/nvmem/layouts/onie-tlv.c | 14 +-------------
1 file changed, 1 insertion(+), 13 deletions(-)
--- a/drivers/nvmem/layouts/onie-tlv.c
+++ b/drivers/nvmem/layouts/onie-tlv.c
@@ -237,19 +237,7 @@ static struct nvmem_layout onie_tlv_layo
.of_match_table = onie_tlv_of_match_table,
.add_cells = onie_tlv_parse_table,
};
-
-static int __init onie_tlv_init(void)
-{
- return nvmem_layout_register(&onie_tlv_layout);
-}
-
-static void __exit onie_tlv_exit(void)
-{
- nvmem_layout_unregister(&onie_tlv_layout);
-}
-
-module_init(onie_tlv_init);
-module_exit(onie_tlv_exit);
+module_nvmem_layout_driver(onie_tlv_layout);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Miquel Raynal <miquel.raynal@bootlin.com>");

View File

@ -0,0 +1,24 @@
From 6b13e4b6a9a45028ac730e550380077df1845912 Mon Sep 17 00:00:00 2001
From: Miquel Raynal <miquel.raynal@bootlin.com>
Date: Tue, 4 Apr 2023 18:21:47 +0100
Subject: [PATCH] nvmem: layouts: onie-tlv: Drop wrong module alias
The MODULE_ALIAS macro is misused here as it carries the
description. There is currently no relevant alias to provide so let's
just drop it.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-40-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/nvmem/layouts/onie-tlv.c | 1 -
1 file changed, 1 deletion(-)
--- a/drivers/nvmem/layouts/onie-tlv.c
+++ b/drivers/nvmem/layouts/onie-tlv.c
@@ -242,4 +242,3 @@ module_nvmem_layout_driver(onie_tlv_layo
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Miquel Raynal <miquel.raynal@bootlin.com>");
MODULE_DESCRIPTION("NVMEM layout driver for Onie TLV table parsing");
-MODULE_ALIAS("NVMEM layout driver for Onie TLV table parsing");

View File

@ -0,0 +1,31 @@
From a8642cd11635a35a5f1dc31857887900d6610778 Mon Sep 17 00:00:00 2001
From: Tom Rix <trix@redhat.com>
Date: Tue, 4 Apr 2023 18:21:48 +0100
Subject: [PATCH] nvmem: layouts: sl28vpd: set varaiable sl28vpd_layout
storage-class-specifier to static
smatch reports
drivers/nvmem/layouts/sl28vpd.c:144:21: warning: symbol
'sl28vpd_layout' was not declared. Should it be static?
This variable is only used in one file so it should be static.
Signed-off-by: Tom Rix <trix@redhat.com>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20230404172148.82422-41-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/nvmem/layouts/sl28vpd.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/drivers/nvmem/layouts/sl28vpd.c
+++ b/drivers/nvmem/layouts/sl28vpd.c
@@ -141,7 +141,7 @@ static const struct of_device_id sl28vpd
};
MODULE_DEVICE_TABLE(of, sl28vpd_of_match_table);
-struct nvmem_layout sl28vpd_layout = {
+static struct nvmem_layout sl28vpd_layout = {
.name = "sl28-vpd",
.of_match_table = sl28vpd_of_match_table,
.add_cells = sl28vpd_add_cells,

View File

@ -4242,6 +4242,8 @@ CONFIG_NMI_LOG_BUF_SHIFT=13
# CONFIG_NVMEM is not set
# CONFIG_NVMEM_BCM_OCOTP is not set
# CONFIG_NVMEM_IMX_OCOTP is not set
# CONFIG_NVMEM_LAYOUT_ONIE_TLV is not set
# CONFIG_NVMEM_LAYOUT_SL28_VPD is not set
# CONFIG_NVMEM_REBOOT_MODE is not set
# CONFIG_NVMEM_RMEM is not set
# CONFIG_NVMEM_SYSFS is not set

View File

@ -4442,6 +4442,8 @@ CONFIG_NMI_LOG_BUF_SHIFT=13
# CONFIG_NVMEM is not set
# CONFIG_NVMEM_BCM_OCOTP is not set
# CONFIG_NVMEM_IMX_OCOTP is not set
# CONFIG_NVMEM_LAYOUT_ONIE_TLV is not set
# CONFIG_NVMEM_LAYOUT_SL28_VPD is not set
# CONFIG_NVMEM_REBOOT_MODE is not set
# CONFIG_NVMEM_RMEM is not set
# CONFIG_NVMEM_SYSFS is not set