linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/2] nvmem: add ONIE NVMEM cells provider
@ 2020-05-27 20:17 Vadym Kochan
  2020-05-27 20:17 ` [PATCH 1/2] nvmem: core: allow to register cells for existing device Vadym Kochan
  2020-05-27 20:17 ` [PATCH 2/2] nvmem: add ONIE NVMEM cells support Vadym Kochan
  0 siblings, 2 replies; 4+ messages in thread
From: Vadym Kochan @ 2020-05-27 20:17 UTC (permalink / raw)
  To: Srinivas Kandagatla, linux-kernel; +Cc: Taras Chornyi, Vadym Kochan

This series adds cells provider for the ONIE TLV attributes which are
stored on NVMEM device. It adds possibility to read the mac address (and
other info) by other drivers.

Driver needs to register NVMEM cells table for already registered
NVMEM device, this requires additional change to the core logic because
current logic only allows to add additional cells only before nvmem
device registration.

Vadym Kochan (2):
  nvmem: core: allow to register cells for existing device
  nvmem: add ONIE NVMEM cells support

 drivers/nvmem/Kconfig      |   9 ++
 drivers/nvmem/Makefile     |   3 +
 drivers/nvmem/core.c       |  61 ++++---
 drivers/nvmem/onie-cells.c | 324 +++++++++++++++++++++++++++++++++++++
 4 files changed, 375 insertions(+), 22 deletions(-)
 create mode 100644 drivers/nvmem/onie-cells.c

-- 
2.17.1


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 1/2] nvmem: core: allow to register cells for existing device
  2020-05-27 20:17 [PATCH 0/2] nvmem: add ONIE NVMEM cells provider Vadym Kochan
@ 2020-05-27 20:17 ` Vadym Kochan
  2020-05-27 20:17 ` [PATCH 2/2] nvmem: add ONIE NVMEM cells support Vadym Kochan
  1 sibling, 0 replies; 4+ messages in thread
From: Vadym Kochan @ 2020-05-27 20:17 UTC (permalink / raw)
  To: Srinivas Kandagatla, linux-kernel; +Cc: Taras Chornyi, Vadym Kochan

Current implementation does not allow to register nvmem cells for
existing device and requires that this will be done before device is
registered.

But there might a driver which provides only cells info which needs to
be added for already registered nvmem device.

Signed-off-by: Vadym Kochan <vadym.kochan@plvision.eu>
---
 drivers/nvmem/core.c | 59 +++++++++++++++++++++++++++-----------------
 1 file changed, 37 insertions(+), 22 deletions(-)

diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
index 05c6ae4b0b97..39c9df9ae9a0 100644
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -482,35 +482,43 @@ int nvmem_unregister_notifier(struct notifier_block *nb)
 }
 EXPORT_SYMBOL_GPL(nvmem_unregister_notifier);
 
-static int nvmem_add_cells_from_table(struct nvmem_device *nvmem)
+static int __nvmem_add_cells_from_table(struct nvmem_device *nvmem,
+					struct nvmem_cell_table *table)
 {
 	const struct nvmem_cell_info *info;
-	struct nvmem_cell_table *table;
 	struct nvmem_cell *cell;
-	int rval = 0, i;
+	int err, i;
+
+	for (i = 0; i < table->ncells; i++) {
+		info = &table->cells[i];
+
+		cell = kzalloc(sizeof(*cell), GFP_KERNEL);
+		if (!cell)
+			return -ENOMEM;
+
+		err = nvmem_cell_info_to_nvmem_cell(nvmem, info, cell);
+		if (err) {
+			kfree(cell);
+			return err;
+		}
+
+		nvmem_cell_add(cell);
+	}
+
+	return 0;
+}
+
+static int nvmem_add_cells_from_table(struct nvmem_device *nvmem)
+{
+	struct nvmem_cell_table *table;
+	int rval = 0;
 
 	mutex_lock(&nvmem_cell_mutex);
 	list_for_each_entry(table, &nvmem_cell_tables, node) {
 		if (strcmp(nvmem_dev_name(nvmem), table->nvmem_name) == 0) {
-			for (i = 0; i < table->ncells; i++) {
-				info = &table->cells[i];
-
-				cell = kzalloc(sizeof(*cell), GFP_KERNEL);
-				if (!cell) {
-					rval = -ENOMEM;
-					goto out;
-				}
-
-				rval = nvmem_cell_info_to_nvmem_cell(nvmem,
-								     info,
-								     cell);
-				if (rval) {
-					kfree(cell);
-					goto out;
-				}
-
-				nvmem_cell_add(cell);
-			}
+			rval = __nvmem_add_cells_from_table(nvmem, table);
+			if (rval)
+				goto out;
 		}
 	}
 
@@ -1560,6 +1568,13 @@ EXPORT_SYMBOL_GPL(nvmem_device_write);
  */
 void nvmem_add_cell_table(struct nvmem_cell_table *table)
 {
+	const char *dev_name = table->nvmem_name;
+	struct nvmem_device *nvmem;
+
+	nvmem = __nvmem_device_get((void *)dev_name, device_match_name);
+	if (nvmem)
+		__nvmem_add_cells_from_table(nvmem, table);
+
 	mutex_lock(&nvmem_cell_mutex);
 	list_add_tail(&table->node, &nvmem_cell_tables);
 	mutex_unlock(&nvmem_cell_mutex);
-- 
2.17.1


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 2/2] nvmem: add ONIE NVMEM cells support
  2020-05-27 20:17 [PATCH 0/2] nvmem: add ONIE NVMEM cells provider Vadym Kochan
  2020-05-27 20:17 ` [PATCH 1/2] nvmem: core: allow to register cells for existing device Vadym Kochan
@ 2020-05-27 20:17 ` Vadym Kochan
  2020-05-29 20:52   ` kbuild test robot
  1 sibling, 1 reply; 4+ messages in thread
From: Vadym Kochan @ 2020-05-27 20:17 UTC (permalink / raw)
  To: Srinivas Kandagatla, linux-kernel; +Cc: Taras Chornyi, Vadym Kochan

ONIE is a small operating system, pre-installed on bare metal network
switches, that provides an environment for automated provisioning.

This system requires that NVMEM (EEPROM) device holds various system
information (mac address, platform name, etc) in a special TLV layout.

The driver registers ONIE TLV attributes as NVMEM cells which can be
accessed by other platform driver. Also it allows to use
of_get_mac_address() to retrieve mac address for the netdev.

Signed-off-by: Vadym Kochan <vadym.kochan@plvision.eu>
---
 drivers/nvmem/Kconfig      |   9 +
 drivers/nvmem/Makefile     |   3 +
 drivers/nvmem/onie-cells.c | 332 +++++++++++++++++++++++++++++++++++++
 3 files changed, 344 insertions(+)
 create mode 100644 drivers/nvmem/onie-cells.c

diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig
index d7b7f6d688e7..dd9298487992 100644
--- a/drivers/nvmem/Kconfig
+++ b/drivers/nvmem/Kconfig
@@ -273,4 +273,13 @@ config SPRD_EFUSE
 	  This driver can also be built as a module. If so, the module
 	  will be called nvmem-sprd-efuse.
 
+config NVMEM_ONIE_CELLS
+	tristate "ONIE TLV cells support"
+	help
+	  This is a driver to provide cells from ONIE TLV structure stored
+	  on NVME device.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called nvmem-onie-cells.
+
 endif
diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile
index a7c377218341..2199784a489f 100644
--- a/drivers/nvmem/Makefile
+++ b/drivers/nvmem/Makefile
@@ -55,3 +55,6 @@ obj-$(CONFIG_NVMEM_ZYNQMP)	+= nvmem_zynqmp_nvmem.o
 nvmem_zynqmp_nvmem-y		:= zynqmp_nvmem.o
 obj-$(CONFIG_SPRD_EFUSE)	+= nvmem_sprd_efuse.o
 nvmem_sprd_efuse-y		:= sprd-efuse.o
+
+obj-$(CONFIG_NVMEM_ONIE_CELLS)	+= nvmem-onie-cells.o
+nvmem-onie-cells-y		:= onie-cells.o
diff --git a/drivers/nvmem/onie-cells.c b/drivers/nvmem/onie-cells.c
new file mode 100644
index 000000000000..d5afd4a955db
--- /dev/null
+++ b/drivers/nvmem/onie-cells.c
@@ -0,0 +1,332 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * ONIE NVMEM cells provider
+ *
+ * Author: Vadym Kochan <vadym.kochan@plvision.eu>
+ */
+
+#define ONIE_NVMEM_DRVNAME	"onie-nvmem-cells"
+
+#define pr_fmt(fmt) ONIE_NVMEM_DRVNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/nvmem-provider.h>
+
+#define ONIE_NVMEM_TLV_MAX_LEN	2048
+
+#define ONIE_NVMEM_HDR_ID	"TlvInfo"
+
+struct onie_nvmem_hdr {
+	u8 id[8];
+	u8 version;
+	__be16 data_len;
+} __packed;
+
+struct onie_nvmem_tlv {
+	u8 type;
+	u8 len;
+	u8 val[0];
+} __packed;
+
+struct onie_nvmem_attr {
+	struct list_head head;
+	const char *name;
+	unsigned int offset;
+	unsigned int len;
+};
+
+struct onie_nvmem {
+	struct nvmem_device *nvmem;
+	unsigned int attr_count;
+	struct list_head attrs;
+
+	struct nvmem_cell_lookup *cell_lookup;
+	struct nvmem_cell_table cell_tbl;
+};
+
+static bool onie_nvmem_hdr_is_valid(struct onie_nvmem_hdr *hdr)
+{
+	if (memcmp(hdr->id, ONIE_NVMEM_HDR_ID, sizeof(hdr->id) != 0))
+		return false;
+	if (hdr->version != 0x1)
+		return false;
+
+	return true;
+}
+
+static void onie_nvmem_attrs_free(struct onie_nvmem *onie)
+{
+	struct onie_nvmem_attr *attr, *tmp;
+
+	list_for_each_entry_safe(attr, tmp, &onie->attrs, head) {
+		list_del(&attr->head);
+		kfree(attr);
+	}
+}
+
+static const char *onie_nvmem_attr_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 "platforn-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: return "unknown";
+	}
+}
+
+static int onie_nvmem_tlv_parse(struct onie_nvmem *onie, u8 *data, u16 len)
+{
+	unsigned int hlen = sizeof(struct onie_nvmem_hdr);
+	unsigned int offset = 0;
+	int err;
+
+	while (offset < len) {
+		struct onie_nvmem_attr *attr;
+		struct onie_nvmem_tlv *tlv;
+
+		tlv = (struct onie_nvmem_tlv *)(data + offset);
+
+		if (offset + tlv->len >= len) {
+			struct nvmem_device *nvmem = onie->nvmem;
+
+			pr_err("%s: TLV len is too big(0x%x) at 0x%x\n",
+			       nvmem_dev_name(nvmem), tlv->len, hlen + offset);
+
+			/* return success in case something was parsed */
+			return 0;
+		}
+
+		attr = kmalloc(sizeof(*attr), GFP_KERNEL);
+		if (!attr) {
+			err = -ENOMEM;
+			goto err_attr_alloc;
+		}
+
+		attr->name = onie_nvmem_attr_name(tlv->type);
+		/* skip 'type' and 'len' */
+		attr->offset = hlen + offset + 2;
+		attr->len = tlv->len;
+
+		list_add(&attr->head, &onie->attrs);
+		onie->attr_count++;
+
+		offset += sizeof(*tlv) + tlv->len;
+	}
+
+	return 0;
+
+err_attr_alloc:
+	onie_nvmem_attrs_free(onie);
+
+	return err;
+}
+
+static int onie_nvmem_decode(struct onie_nvmem *onie)
+{
+	struct nvmem_device *nvmem = onie->nvmem;
+	struct onie_nvmem_hdr hdr;
+	u8 *data;
+	u16 len;
+	int ret;
+
+	ret = nvmem_device_read(nvmem, 0, sizeof(hdr), &hdr);
+	if (ret < 0)
+		return ret;
+
+	if (!onie_nvmem_hdr_is_valid(&hdr)) {
+		pr_err("%s: invalid ONIE TLV header\n", nvmem_dev_name(nvmem));
+		ret = -EINVAL;
+		goto err_invalid;
+	}
+
+	len = be16_to_cpu(hdr.data_len);
+
+	if (len > ONIE_NVMEM_TLV_MAX_LEN)
+		len = ONIE_NVMEM_TLV_MAX_LEN;
+
+	data = kmalloc(len, GFP_KERNEL);
+	if (!data) {
+		ret = -ENOMEM;
+		goto err_kmalloc;
+	}
+
+	ret = nvmem_device_read(nvmem, sizeof(hdr), len, data);
+	if (ret < 0)
+		goto err_data_read;
+
+	ret = onie_nvmem_tlv_parse(onie, data, len);
+	if (ret)
+		goto err_info_parse;
+
+	kfree(data);
+
+	return 0;
+
+err_info_parse:
+err_data_read:
+	kfree(data);
+err_kmalloc:
+err_invalid:
+	return ret;
+}
+
+static int onie_nvmem_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct nvmem_cell_info *cells;
+	struct onie_nvmem_attr *attr;
+	struct nvmem_device *nvmem;
+	struct onie_nvmem *onie;
+	unsigned int ncells = 0;
+	int err;
+
+	nvmem = of_nvmem_device_get(np, NULL);
+	if (IS_ERR(nvmem))
+		return PTR_ERR(nvmem);
+
+	onie = kmalloc(sizeof(*onie), GFP_KERNEL);
+	if (!onie) {
+		err = -ENOMEM;
+		goto err_nvmem_alloc;
+	}
+
+	INIT_LIST_HEAD(&onie->attrs);
+	onie->attr_count = 0;
+	onie->nvmem = nvmem;
+
+	err = onie_nvmem_decode(onie);
+	if (err)
+		goto err_nvmem_decode;
+
+	if (!onie->attr_count) {
+		pr_err("%s: has no ONIE attributes\n", nvmem_dev_name(nvmem));
+		err = -EINVAL;
+		goto err_no_attrs;
+	}
+
+	cells = kmalloc_array(onie->attr_count, sizeof(*cells), GFP_KERNEL);
+	if (!cells) {
+		err = -ENOMEM;
+		goto err_cells_alloc;
+	}
+
+	onie->cell_lookup = kmalloc_array(onie->attr_count,
+					  sizeof(struct nvmem_cell_lookup),
+					  GFP_KERNEL);
+	if (!onie->cell_lookup) {
+		err = -ENOMEM;
+		goto err_lookup_alloc;
+	}
+
+	list_for_each_entry(attr, &onie->attrs, head) {
+		struct nvmem_cell_lookup *lookup;
+		struct nvmem_cell_info *cell;
+
+		cell = &cells[ncells];
+
+		lookup = &onie->cell_lookup[ncells];
+		lookup->con_id = NULL;
+
+		cell->offset = attr->offset;
+		cell->name = attr->name;
+		cell->bytes = attr->len;
+		cell->bit_offset = 0;
+		cell->nbits = 0;
+
+		lookup->nvmem_name = nvmem_dev_name(onie->nvmem);
+		lookup->dev_id = dev_name(dev);
+		lookup->cell_name = cell->name;
+		lookup->con_id = cell->name;
+
+		ncells++;
+	}
+
+	onie->cell_tbl.nvmem_name = nvmem_dev_name(onie->nvmem);
+	onie->cell_tbl.ncells = ncells;
+	onie->cell_tbl.cells = cells;
+
+	nvmem_add_cell_table(&onie->cell_tbl);
+	nvmem_add_cell_lookups(onie->cell_lookup, ncells);
+
+	dev_set_drvdata(dev, onie);
+
+	onie_nvmem_attrs_free(onie);
+
+	nvmem_device_put(nvmem);
+
+	return 0;
+
+err_lookup_alloc:
+	kfree(onie->cell_tbl.cells);
+err_cells_alloc:
+	onie_nvmem_attrs_free(onie);
+err_no_attrs:
+err_nvmem_decode:
+	kfree(onie);
+err_nvmem_alloc:
+	nvmem_device_put(nvmem);
+
+	return err;
+}
+
+static int onie_nvmem_remove(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct onie_nvmem *onie;
+
+	onie = dev_get_drvdata(dev);
+
+	nvmem_del_cell_lookups(onie->cell_lookup, onie->attr_count);
+	nvmem_del_cell_table(&onie->cell_tbl);
+
+	kfree(onie->cell_tbl.cells);
+	kfree(onie->cell_lookup);
+	kfree(onie);
+
+	return 0;
+}
+
+static const struct of_device_id onie_nvmem_match[] = {
+	{
+		.compatible = "onie-nvmem-cells",
+	},
+	{ }
+};
+MODULE_DEVICE_TABLE(of, onie_nvmem_match);
+
+static struct platform_driver onie_nvmem_driver = {
+	.probe = onie_nvmem_probe,
+	.remove = onie_nvmem_remove,
+	.driver = {
+		.name = ONIE_NVMEM_DRVNAME,
+		.of_match_table = onie_nvmem_match,
+	},
+};
+module_platform_driver(onie_nvmem_driver);
+
+MODULE_AUTHOR("Vadym Kochan <vadym.kochan@plvision.eu>");
+MODULE_DESCRIPTION("ONIE NVMEM cells driver");
+MODULE_LICENSE("GPL");
-- 
2.17.1


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH 2/2] nvmem: add ONIE NVMEM cells support
  2020-05-27 20:17 ` [PATCH 2/2] nvmem: add ONIE NVMEM cells support Vadym Kochan
@ 2020-05-29 20:52   ` kbuild test robot
  0 siblings, 0 replies; 4+ messages in thread
From: kbuild test robot @ 2020-05-29 20:52 UTC (permalink / raw)
  To: Vadym Kochan, Srinivas Kandagatla, linux-kernel
  Cc: kbuild-all, clang-built-linux, Taras Chornyi, Vadym Kochan

[-- Attachment #1: Type: text/plain, Size: 2484 bytes --]

Hi Vadym,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on linus/master]
[also build test WARNING on linux/master v5.7-rc7 next-20200529]
[if your patch is applied to the wrong git tree, please drop us a note to help
improve the system. BTW, we also suggest to use '--base' option to specify the
base tree in git format-patch, please see https://stackoverflow.com/a/37406982]

url:    https://github.com/0day-ci/linux/commits/Vadym-Kochan/nvmem-add-ONIE-NVMEM-cells-provider/20200528-041903
base:   https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git b0c3ba31be3e45a130e13b278cf3b90f69bda6f6
config: x86_64-randconfig-r024-20200529 (attached as .config)
compiler: clang version 11.0.0 (https://github.com/llvm/llvm-project 2d068e534f1671459e1b135852c1b3c10502e929)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # install x86_64 cross compiling tool for clang build
        # apt-get install binutils-x86-64-linux-gnu
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=x86_64 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kbuild test robot <lkp@intel.com>

All warnings (new ones prefixed by >>, old ones prefixed by <<):

>> drivers/nvmem/onie-cells.c:56:57: warning: size argument in 'memcmp' call is a comparison [-Wmemsize-comparison]
if (memcmp(hdr->id, ONIE_NVMEM_HDR_ID, sizeof(hdr->id) != 0))
~~~~~~~~~~~~~~~~^~~~
drivers/nvmem/onie-cells.c:56:6: note: did you mean to compare the result of 'memcmp' instead?
if (memcmp(hdr->id, ONIE_NVMEM_HDR_ID, sizeof(hdr->id) != 0))
^                                                      ~
)
drivers/nvmem/onie-cells.c:56:41: note: explicitly cast the argument to size_t to silence this warning
if (memcmp(hdr->id, ONIE_NVMEM_HDR_ID, sizeof(hdr->id) != 0))
^
(size_t)(           )
1 warning generated.

vim +/memcmp +56 drivers/nvmem/onie-cells.c

    53	
    54	static bool onie_nvmem_hdr_is_valid(struct onie_nvmem_hdr *hdr)
    55	{
  > 56		if (memcmp(hdr->id, ONIE_NVMEM_HDR_ID, sizeof(hdr->id) != 0))
    57			return false;
    58		if (hdr->version != 0x1)
    59			return false;
    60	
    61		return true;
    62	}
    63	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 34875 bytes --]

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2020-05-29 21:21 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-05-27 20:17 [PATCH 0/2] nvmem: add ONIE NVMEM cells provider Vadym Kochan
2020-05-27 20:17 ` [PATCH 1/2] nvmem: core: allow to register cells for existing device Vadym Kochan
2020-05-27 20:17 ` [PATCH 2/2] nvmem: add ONIE NVMEM cells support Vadym Kochan
2020-05-29 20:52   ` kbuild test robot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).