[openwrt/openwrt] generic: platform/mikrotik: add NVMEM layout driver

LEDE Commits lede-commits at lists.infradead.org
Thu Jun 20 06:54:00 PDT 2024


robimarko pushed a commit to openwrt/openwrt.git, branch main:
https://git.openwrt.org/73a9f9f857947bc63fef1aefb7e2e4b906fb9d2b

commit 73a9f9f857947bc63fef1aefb7e2e4b906fb9d2b
Author: Robert Marko <robimarko at gmail.com>
AuthorDate: Sun Jun 9 12:14:25 2024 +0200

    generic: platform/mikrotik: add NVMEM layout driver
    
    Currently, information from MikroTik hard_config is only available via
    sysfs, meaning that we have to rely on userspace to for example setup MACs.
    
    So, lets provide a basic NVMEM layout based driver to expose the same cells
    as sysfs driver exposes.
    
    Do note that the we dont extract the WLAN caldata and BDF-s at this point.
    
    Reviewed-by: Thibaut VARÈNE <hacks at slashdirt.org>
    Link: https://github.com/openwrt/openwrt/pull/15665
    Signed-off-by: Robert Marko <robimarko at gmail.com>
---
 target/linux/ath79/mikrotik/config-default         |   1 +
 .../files/drivers/platform/mikrotik/Kconfig        |   6 +
 .../files/drivers/platform/mikrotik/Makefile       |   1 +
 .../files/drivers/platform/mikrotik/rb_nvmem.c     | 230 +++++++++++++++++++++
 target/linux/ipq40xx/mikrotik/config-default       |   1 +
 target/linux/ramips/mt7621/config-6.6              |   1 +
 6 files changed, 240 insertions(+)

diff --git a/target/linux/ath79/mikrotik/config-default b/target/linux/ath79/mikrotik/config-default
index 4325e1f69a..71b64b26ad 100644
--- a/target/linux/ath79/mikrotik/config-default
+++ b/target/linux/ath79/mikrotik/config-default
@@ -33,6 +33,7 @@ CONFIG_MTD_UBI_WL_THRESHOLD=4096
 CONFIG_NET_DEVLINK=y
 CONFIG_NET_DSA=y
 CONFIG_NET_SWITCHDEV=y
+# CONFIG_NVMEM_LAYOUT_MIKROTIK is not set
 CONFIG_PHYLINK=y
 CONFIG_PHY_AR7100_USB=y
 CONFIG_PHY_AR7200_USB=y
diff --git a/target/linux/generic/files/drivers/platform/mikrotik/Kconfig b/target/linux/generic/files/drivers/platform/mikrotik/Kconfig
index 1dc027815a..32ef8f29de 100644
--- a/target/linux/generic/files/drivers/platform/mikrotik/Kconfig
+++ b/target/linux/generic/files/drivers/platform/mikrotik/Kconfig
@@ -15,4 +15,10 @@ config MIKROTIK_RB_SYSFS
 	help
 	  This driver exposes RouterBoot configuration in sysfs.
 
+config NVMEM_LAYOUT_MIKROTIK
+	tristate "RouterBoot NVMEM layout support"
+	depends on NVMEM_LAYOUTS
+	help
+	  This driver exposes MikroTik hard_config via NVMEM layout.
+
 endif # MIKROTIK
diff --git a/target/linux/generic/files/drivers/platform/mikrotik/Makefile b/target/linux/generic/files/drivers/platform/mikrotik/Makefile
index a232e1a9e8..164b23b701 100644
--- a/target/linux/generic/files/drivers/platform/mikrotik/Makefile
+++ b/target/linux/generic/files/drivers/platform/mikrotik/Makefile
@@ -2,3 +2,4 @@
 # Makefile for MikroTik RouterBoard platform specific drivers
 #
 obj-$(CONFIG_MIKROTIK_RB_SYSFS)     += routerboot.o rb_hardconfig.o rb_softconfig.o
+obj-$(CONFIG_NVMEM_LAYOUT_MIKROTIK)     += rb_nvmem.o
diff --git a/target/linux/generic/files/drivers/platform/mikrotik/rb_nvmem.c b/target/linux/generic/files/drivers/platform/mikrotik/rb_nvmem.c
new file mode 100644
index 0000000000..6f785ce7d1
--- /dev/null
+++ b/target/linux/generic/files/drivers/platform/mikrotik/rb_nvmem.c
@@ -0,0 +1,230 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * NVMEM layout driver for MikroTik Routerboard hard config cells
+ *
+ * Copyright (C) 2024 Robert Marko <robimarko at gmail.com>
+ * Based on the sysfs hard config driver by Thibaut VARÈNE <hacks+kernel at slashdirt.org>
+ * Comments documenting the format carried over from routerboot.c
+ */
+
+#include <linux/bitfield.h>
+#include <linux/etherdevice.h>
+#include <linux/mod_devicetable.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/nvmem-provider.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#include "rb_hardconfig.h"
+#include "routerboot.h"
+
+#define TLV_TAG_MASK 	GENMASK(15, 0)
+#define TLV_LEN_MASK 	GENMASK(31, 16)
+
+static const char *rb_tlv_cell_name(u16 tag)
+{
+	switch (tag) {
+	case RB_ID_FLASH_INFO:
+		return "flash-info";
+	case RB_ID_MAC_ADDRESS_PACK:
+		return "base-mac-address";
+	case RB_ID_BOARD_PRODUCT_CODE:
+		return "board-product-code";
+	case RB_ID_BIOS_VERSION:
+		return "booter-version";
+	case RB_ID_SERIAL_NUMBER:
+		return "board-serial";
+	case RB_ID_MEMORY_SIZE:
+		return "mem-size";
+	case RB_ID_MAC_ADDRESS_COUNT:
+		return "mac-count";
+	case RB_ID_HW_OPTIONS:
+		return "hw-options";
+	case RB_ID_WLAN_DATA:
+		return "wlan-data";
+	case RB_ID_BOARD_IDENTIFIER:
+		return "board-identifier";
+	case RB_ID_PRODUCT_NAME:
+		return "product-name";
+	case RB_ID_DEFCONF:
+		return "defconf";
+	case RB_ID_BOARD_REVISION:
+		return "board-revision";
+	default:
+		break;
+	}
+
+	return NULL;
+}
+
+static int rb_tlv_mac_read_cb(void *priv, const char *id, int index,
+			      unsigned int offset, void *buf,
+			      size_t bytes)
+{
+	if (index < 0)
+		return -EINVAL;
+
+	if (!is_valid_ether_addr(buf))
+		return -EINVAL;
+
+	eth_addr_add(buf, index);
+
+	return 0;
+}
+
+static nvmem_cell_post_process_t rb_tlv_read_cb(u16 tag)
+{
+	switch (tag) {
+	case RB_ID_MAC_ADDRESS_PACK:
+		return &rb_tlv_mac_read_cb;
+	default:
+		break;
+	}
+
+	return NULL;
+}
+
+static int rb_add_cells(struct device *dev, struct nvmem_device *nvmem,
+			const size_t data_len, u8 *data)
+{
+	u32 node, offset = sizeof(RB_MAGIC_HARD);
+	struct nvmem_cell_info cell = {};
+	struct device_node *layout;
+	u16 tlv_tag, tlv_len;
+	int ret;
+
+	layout = of_nvmem_layout_get_container(nvmem);
+	if (!layout)
+		return -ENOENT;
+
+	/*
+	 * Routerboot tag nodes are u32 values:
+	 * - The low nibble is the tag identification number,
+	 * - The high nibble is the tag payload length (node excluded) in bytes.
+	 * Tag nodes are CPU-endian.
+	 * Tag nodes are 32bit-aligned.
+	 *
+	 * The payload immediately follows the tag node.
+	 * Payload offset will always be aligned. while length may not end on 32bit
+	 * boundary (the only known case is when parsing ERD data).
+	 * The payload is CPU-endian when applicable.
+	 * Tag nodes are not ordered (by ID) on flash.
+	 */
+	while ((offset + sizeof(node)) <= data_len) {
+		node = *((const u32 *) (data + offset));
+		/* Tag list ends with null node */
+		if (!node)
+			break;
+
+		tlv_tag = FIELD_GET(TLV_TAG_MASK, node);
+		tlv_len = FIELD_GET(TLV_LEN_MASK, node);
+
+		offset += sizeof(node);
+		if (offset + tlv_len > data_len) {
+			dev_err(dev, "Out of bounds field (0x%x bytes at 0x%x)\n",
+			        tlv_len, offset);
+			break;
+		}
+
+		cell.name = rb_tlv_cell_name(tlv_tag);
+		if (!cell.name)
+			goto skip;
+
+		cell.offset = offset;
+		/*
+		 * MikroTik stores MAC-s with length of 8 bytes,
+		 * but kernel expects it to be ETH_ALEN (6 bytes),
+		 * so we need to make sure that is the case.
+		 */
+		if (tlv_tag == RB_ID_MAC_ADDRESS_PACK)
+			cell.bytes = ETH_ALEN;
+		else
+			cell.bytes = tlv_len;
+		cell.np = of_get_child_by_name(layout, cell.name);
+		cell.read_post_process = rb_tlv_read_cb(tlv_tag);
+
+		ret = nvmem_add_one_cell(nvmem, &cell);
+		if (ret) {
+			of_node_put(layout);
+			return ret;
+		}
+
+		/*
+		 * The only known situation where len may not end on 32bit
+		 * boundary is within ERD data. Since we're only extracting
+		 * one tag (the first and only one) from that data, we should
+		 * never need to forcefully ALIGN(). Do it anyway, this is not a
+		 * performance path.
+		 */
+skip:
+		offset += ALIGN(tlv_len, sizeof(offset));
+	}
+
+	of_node_put(layout);
+
+	return 0;
+}
+
+static int rb_parse_table(struct nvmem_layout *layout)
+{
+	struct nvmem_device *nvmem = layout->nvmem;
+	struct device *dev = &layout->dev;
+	size_t mtd_size;
+	u8 *data;
+	u32 hdr;
+	int ret;
+
+	ret = nvmem_device_read(nvmem, 0, sizeof(hdr), &hdr);
+	if (ret < 0)
+		return ret;
+
+	if (hdr != RB_MAGIC_HARD) {
+		dev_err(dev, "Invalid header\n");
+		return -EINVAL;
+	}
+
+	mtd_size = nvmem_dev_size(nvmem);
+
+	data = devm_kmalloc(dev, mtd_size, GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	ret = nvmem_device_read(nvmem, 0, mtd_size, data);
+	if (ret != mtd_size)
+		return ret;
+
+	return rb_add_cells(dev, nvmem, mtd_size, data);
+}
+
+static int rb_nvmem_probe(struct nvmem_layout *layout)
+{
+	layout->add_cells = rb_parse_table;
+
+	return nvmem_layout_register(layout);
+}
+
+static void rb_nvmem_remove(struct nvmem_layout *layout)
+{
+	nvmem_layout_unregister(layout);
+}
+
+static const struct of_device_id rb_nvmem_of_match_table[] = {
+	{ .compatible = "mikrotik,routerboot-nvmem", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, rb_nvmem_of_match_table);
+
+static struct nvmem_layout_driver rb_nvmem_layout = {
+	.probe = rb_nvmem_probe,
+	.remove = rb_nvmem_remove,
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = "rb_nvmem",
+		.of_match_table = rb_nvmem_of_match_table,
+	},
+};
+module_nvmem_layout_driver(rb_nvmem_layout);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Robert Marko <robimarko at gmail.com>");
+MODULE_DESCRIPTION("NVMEM layout driver for MikroTik Routerboard hard config cells");
diff --git a/target/linux/ipq40xx/mikrotik/config-default b/target/linux/ipq40xx/mikrotik/config-default
index 1268d2c810..ab470ecb41 100644
--- a/target/linux/ipq40xx/mikrotik/config-default
+++ b/target/linux/ipq40xx/mikrotik/config-default
@@ -3,3 +3,4 @@ CONFIG_MIKROTIK_RB_SYSFS=y
 CONFIG_MTD_ROUTERBOOT_PARTS=y
 CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE=y
 CONFIG_MTD_SPLIT_MINOR_FW=y
+# CONFIG_NVMEM_LAYOUT_MIKROTIK is not set
diff --git a/target/linux/ramips/mt7621/config-6.6 b/target/linux/ramips/mt7621/config-6.6
index e77ea238e7..219e61a467 100644
--- a/target/linux/ramips/mt7621/config-6.6
+++ b/target/linux/ramips/mt7621/config-6.6
@@ -128,6 +128,7 @@ CONFIG_MFD_SYSCON=y
 CONFIG_MIGRATION=y
 CONFIG_MIKROTIK=y
 CONFIG_MIKROTIK_RB_SYSFS=y
+# CONFIG_NVMEM_LAYOUT_MIKROTIK is not set
 CONFIG_MIPS=y
 CONFIG_MIPS_ASID_BITS=8
 CONFIG_MIPS_ASID_SHIFT=0




More information about the lede-commits mailing list