From: Ahmad Fatoum <a.fatoum@pengutronix.de>
Date: Thu, 3 Mar 2022 11:22:11 +0100
Subject: [PATCH] boards: add decoder for LXA TLV v1 format

The LXA TLV format is stored onto the EEPROM of either the Baseboard or
Powerboard. Its contents are mainly for use in Linux userspace.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
Upstream-Status: Submitted [https://lore.kernel.org/all/20250411074045.2019372-1-a.fatoum@pengutronix.de/]
---
 common/boards/Kconfig                  |  5 ++
 common/boards/Makefile                 |  1 +
 common/boards/include/boards/lxa/tlv.h | 14 +++++
 common/boards/lxa/Makefile             |  2 +
 common/boards/lxa/factory-data.c       | 98 ++++++++++++++++++++++++++++++++++
 include/string.h                       |  2 +
 include/tlv/format.h                   |  2 +
 lib/string.c                           | 21 ++++++++
 8 files changed, 145 insertions(+)
 create mode 100644 common/boards/include/boards/lxa/tlv.h
 create mode 100644 common/boards/lxa/Makefile
 create mode 100644 common/boards/lxa/factory-data.c

diff --git a/common/boards/Kconfig b/common/boards/Kconfig
index 586a54d7ca13..74947316954b 100644
--- a/common/boards/Kconfig
+++ b/common/boards/Kconfig
@@ -19,3 +19,8 @@ config BOARD_WOLFVISION
 	bool
 	select AIODEV
 	select ROCKCHIP_SARADC
+
+config BOARD_LXA
+	bool "LXA common board support" if COMPILE_TEST
+	select TLV
+	select TLV_BAREBOX
diff --git a/common/boards/Makefile b/common/boards/Makefile
index 3f8ac57b2f82..058733522411 100644
--- a/common/boards/Makefile
+++ b/common/boards/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_BOARD_QEMU_VIRT)	+= qemu-virt/
 obj-$(CONFIG_BOARD_PHYTEC_SOM_DETECTION) += phytec/
 obj-$(CONFIG_BOARD_TQ) += tq/
 obj-$(CONFIG_BOARD_WOLFVISION) += wolfvision/
+obj-$(CONFIG_BOARD_LXA) += lxa/
diff --git a/common/boards/include/boards/lxa/tlv.h b/common/boards/include/boards/lxa/tlv.h
new file mode 100644
index 000000000000..7b4222de43d0
--- /dev/null
+++ b/common/boards/include/boards/lxa/tlv.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __BOARDS_LXA_TLV_H_
+#define __BOARDS_LXA_TLV_H_
+
+#ifdef CONFIG_TLV
+int lxa_tlv_v1_register(void);
+#else
+static inline int lxa_tlv_v1_register(void)
+{
+	return 0;
+}
+#endif
+
+#endif
diff --git a/common/boards/lxa/Makefile b/common/boards/lxa/Makefile
new file mode 100644
index 000000000000..29acfec3cb92
--- /dev/null
+++ b/common/boards/lxa/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_TLV) += factory-data.o
diff --git a/common/boards/lxa/factory-data.c b/common/boards/lxa/factory-data.c
new file mode 100644
index 000000000000..70ddbc734613
--- /dev/null
+++ b/common/boards/lxa/factory-data.c
@@ -0,0 +1,98 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <boards/lxa/tlv.h>
+#include <string.h>
+#include <tlv/tlv.h>
+#include <common.h>
+
+static int lxa_tlv_handle_serial(struct tlv_device *dev, struct tlv_mapping *map,
+				 u16 len, const u8 *val)
+{
+	const char *buf;
+	char *period;
+
+	buf = __tlv_format_str(dev, map, len, val);
+	if (!buf)
+		return -ENOMEM;
+
+	/* Strip /\d+\./ prefix to arrive at hostname without period */
+	period = memrchr(buf, '.', len);
+	barebox_set_serial_number(period ? period + 1 : buf);
+
+	return 0;
+}
+
+static int lxa_tlv_format_calib(struct tlv_device *dev, struct tlv_mapping *map,
+				u16 len, const u8 *val)
+{
+	return tlv_format_blob(dev, map, len, val);
+}
+
+static struct tlv_mapping lxa_tlv_baseboard_v1_mappings[] = {
+	/* vendor-specific override */
+	{ 0x0004, lxa_tlv_handle_serial, "serial-number"},
+	/* vendor-specific additions */
+	{ 0x8000, lxa_tlv_format_calib, "usb-host-curr" },
+	{ 0x8001, lxa_tlv_format_calib, "usb-host1-curr" },
+	{ 0x8002, lxa_tlv_format_calib, "usb-host2-curr" },
+	{ 0x8003, lxa_tlv_format_calib, "usb-host3-curr" },
+	{ 0x8010, lxa_tlv_format_calib, "out0-volt" },
+	{ 0x8011, lxa_tlv_format_calib, "out1-volt" },
+	{ 0x8020, lxa_tlv_format_calib, "iobus-curr" },
+	{ 0x8021, lxa_tlv_format_calib, "iobus-volt" },
+	{ /* sentintel */ },
+};
+
+static struct tlv_mapping *baseboard_mappings[] = {
+	lxa_tlv_baseboard_v1_mappings, barebox_tlv_v1_mappings, NULL
+};
+
+static struct of_device_id baseboard_matches[] = {
+	{ .compatible = "lxa,tlv-baseboard-v1" },
+	{ /* sentinel */ }
+};
+
+static struct tlv_decoder lxa_tlv_baseboard_v1 = {
+	.magic = TLV_MAGIC_LXA_BASEBOARD_V1,
+	.driver.name = "lxa-tlv-baseboard-v1",
+	.driver.of_compatible = baseboard_matches,
+	.mappings = baseboard_mappings,
+};
+
+static struct tlv_mapping lxa_tlv_powerboard_v1_mappings[] = {
+	{ 0x0003, tlv_format_dec, "factory-timestamp" },
+	{ 0x0005, tlv_format_dec, "modification" },
+	{ 0x0006, tlv_format_str, "featureset" },
+	{ 0x0007, tlv_format_str, "pcba-serial-number"},
+	{ 0x0008, tlv_format_str, "pcba-hardware-release"},
+	{ 0x9000, lxa_tlv_format_calib, "pwr-volt" },
+	{ 0x9001, lxa_tlv_format_calib, "pwr-curr" },
+	{ /* sentintel */ },
+};
+
+static struct tlv_mapping *powerboard_mappings[] = {
+	lxa_tlv_powerboard_v1_mappings, NULL
+};
+
+static struct of_device_id powerboard_matches[] = {
+	{ .compatible = "lxa,tlv-powerboard-v1" },
+	{ /* sentinel */ }
+};
+
+static struct tlv_decoder lxa_tlv_powerboard_v1 = {
+	.magic = TLV_MAGIC_LXA_POWERBOARD_V1,
+	.driver.name = "lxa-tlv-powerboard-v1",
+	.driver.of_compatible = powerboard_matches,
+	.mappings = powerboard_mappings,
+};
+
+int lxa_tlv_v1_register(void)
+{
+	int ret;
+
+	ret = tlv_register_decoder(&lxa_tlv_baseboard_v1);
+	if (ret)
+		return ret;
+
+	return tlv_register_decoder(&lxa_tlv_powerboard_v1);
+}
diff --git a/include/string.h b/include/string.h
index e56f4c77cfcb..b3d543262731 100644
--- a/include/string.h
+++ b/include/string.h
@@ -11,6 +11,8 @@ char *strsep_unescaped(char **, const char *);
 char *stpcpy(char *dest, const char *src);
 bool strends(const char *str, const char *postfix);
 
+void *memrchr(const void *s, int c, size_t n);
+
 void *__default_memset(void *, int, __kernel_size_t);
 void *__nokasan_default_memset(void *, int, __kernel_size_t);
 
diff --git a/include/tlv/format.h b/include/tlv/format.h
index a32ec917a434..33f5c30a4beb 100644
--- a/include/tlv/format.h
+++ b/include/tlv/format.h
@@ -22,6 +22,8 @@
 #include <linux/build_bug.h>
 
 #define TLV_MAGIC_BAREBOX_V1		0x61bb95f2
+#define TLV_MAGIC_LXA_BASEBOARD_V1	0xbc288dfe
+#define TLV_MAGIC_LXA_POWERBOARD_V1	0xc6895c21
 
 #define TLV_IS_VENDOR_SPECIFIC(val)	((*(u8 *)&(val) & 0x80) == 0x80)
 #define TLV_IS_GENERIC(val)		((*(u8 *)&(val) & 0x80) != 0x80)
diff --git a/lib/string.c b/lib/string.c
index 374f326143a7..f2f81144dbdd 100644
--- a/lib/string.c
+++ b/lib/string.c
@@ -791,6 +791,27 @@ void *memchr(const void *s, int c, size_t n)
 #endif
 EXPORT_SYMBOL(memchr);
 
+/**
+ * memrchr - Find last occurrence of character in an area of memory.
+ * @s: The memory area
+ * @c: The byte to search for
+ * @n: The size of the area.
+ *
+ * returns the address of the last occurrence of @c, or %NULL
+ * if @c is not found
+ */
+void *memrchr(const void *s, int c, size_t n)
+{
+	const unsigned char *p = s;
+	while (n-- > 0) {
+		if ((unsigned char)c == p[n]) {
+			return (void *)(p+n);
+		}
+	}
+	return NULL;
+}
+EXPORT_SYMBOL(memrchr);
+
 /**
  * skip_spaces - Removes leading whitespace from @str.
  * @str: The string to be stripped.
