[PATCH v5 1/5] misc: qcom: Add HW-Info aggregator driver

Aswin Murugan via U-Boot <[email protected]>
Newsgroups gmane.comp.boot-loaders.u-boot
Message-ID <[email protected]>
Add a generic driver that binds to a U-Boot-only "qcom,hwinfo" DT
node holding phandles to the existing IMEM/TCSR syscon nodes plus
per-SoC offsets. This provides a common interface for reading the
IMEM boot cookie (used to detect boot storage type) and the TCSR
SOC_HW_VERSION register.

Signed-off-by: Aswin Murugan <[email protected]>
---
 drivers/misc/Kconfig       |   9 +++
 drivers/misc/Makefile      |   1 +
 drivers/misc/qcom_hwinfo.c | 160 +++++++++++++++++++++++++++++++++++++
 include/qcom_hwinfo.h      | 109 +++++++++++++++++++++++++
 4 files changed, 279 insertions(+)
 create mode 100644 drivers/misc/qcom_hwinfo.c
 create mode 100644 include/qcom_hwinfo.h

diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index bde5c640de8..b75b2e4c70c 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -93,6 +93,15 @@ config QCOM_GENI
 	  for providing a common interface for various peripherals like UART, I2C, SPI,
 	  etc.
 
+config QCOM_HWINFO
+	bool "Qualcomm HW-Info aggregator driver"
+	depends on MISC
+	select SYSCON
+	select REGMAP
+	help
+	  Reads the IMEM boot cookie and TCSR HW version via a common
+	  "qcom,hwinfo" DT node.
+
 config ROCKCHIP_EFUSE
 	bool "Rockchip e-fuse support"
 	depends on MISC
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index e2170212e5a..2a7e3379d83 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -66,6 +66,7 @@ obj-$(CONFIG_QFW_SMBIOS) += qfw_smbios.o
 obj-$(CONFIG_SANDBOX) += qfw_sandbox.o
 endif
 obj-$(CONFIG_QCOM_GENI) += qcom_geni.o
+obj-$(CONFIG_$(PHASE_)QCOM_HWINFO) += qcom_hwinfo.o
 obj-$(CONFIG_$(PHASE_)ROCKCHIP_EFUSE) += rockchip-efuse.o
 obj-$(CONFIG_$(PHASE_)ROCKCHIP_OTP) += rockchip-otp.o
 obj-$(CONFIG_$(PHASE_)ROCKCHIP_IODOMAIN) += rockchip-io-domain.o
diff --git a/drivers/misc/qcom_hwinfo.c b/drivers/misc/qcom_hwinfo.c
new file mode 100644
index 00000000000..822968234d5
--- /dev/null
+++ b/drivers/misc/qcom_hwinfo.c
@@ -0,0 +1,160 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Qualcomm HW-Info aggregator driver
+ *
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ *
+ * Binds to a U-Boot-only DT node (compatible = "qcom,hwinfo") holding
+ * phandles to the existing IMEM/TCSR syscon nodes plus per-SoC offsets.
+ *
+ * Example DT node (arch/arm/dts/<board>-u-boot.dtsi):
+ *
+ *	qcom_hwinfo: qcom-hwinfo {
+ *		compatible = "qcom,hwinfo";
+ *		imem = <&sram>;
+ *		imem-boot-cookie-offset = <0x0>;
+ *		tcsr = <&tcsr>;
+ *		tcsr-hw-version-offset = <0x8000>;
+ *	};
+ */
+
+#include <dm.h>
+#include <log.h>
+#include <regmap.h>
+#include <syscon.h>
+#include <dm/device_compat.h>
+#include <linux/err.h>
+#include <qcom_hwinfo.h>
+
+struct qcom_hwinfo_priv {
+	struct regmap *imem_regmap;
+	u32 imem_cookie_off;
+	struct regmap *tcsr_regmap;
+	u32 tcsr_hwver_off;
+};
+
+int qcom_hwinfo_get_imem_cookie(struct udevice *dev,
+				const struct qcom_hwinfo_imem_cookie **cookie)
+{
+	struct qcom_hwinfo_priv *priv = dev_get_priv(dev);
+	const struct qcom_hwinfo_imem_cookie *c;
+	void *base;
+
+	if (!priv->imem_regmap)
+		return -ENODEV;
+
+	/* IMEM is plain mapped SRAM; safe to dereference directly. */
+	base = regmap_get_range(priv->imem_regmap, 0);
+	if (!base) {
+		dev_err(dev, "Failed to get IMEM regmap range\n");
+		return -ENODEV;
+	}
+
+	c = (const struct qcom_hwinfo_imem_cookie *)((u8 *)base + priv->imem_cookie_off);
+
+	if (c->shared_imem_magic != QCOM_HWINFO_IMEM_MAGIC_NUM ||
+	    c->shared_imem_version < QCOM_HWINFO_IMEM_VERSION_NUM) {
+		dev_warn(dev, "Invalid IMEM cookie magic=0x%x version=%u\n",
+			 c->shared_imem_magic, c->shared_imem_version);
+		return -EINVAL;
+	}
+
+	*cookie = c;
+
+	return 0;
+}
+
+int qcom_hwinfo_get_storage_type(struct udevice *dev, u32 *storage_type)
+{
+	const struct qcom_hwinfo_imem_cookie *cookie;
+	int ret;
+
+	ret = qcom_hwinfo_get_imem_cookie(dev, &cookie);
+	if (ret) {
+		dev_warn(dev, "Failed to get IMEM cookie: %d, defaulting to UFS\n", ret);
+		*storage_type = QCOM_HWINFO_STORAGE_UFS;
+		return 0;
+	}
+
+	switch (cookie->boot_device_type) {
+	case QCOM_HWINFO_UFS_FLASH:
+		*storage_type = QCOM_HWINFO_STORAGE_UFS;
+		break;
+	case QCOM_HWINFO_MMC_FLASH:
+	case QCOM_HWINFO_SDC_FLASH:
+		*storage_type = QCOM_HWINFO_STORAGE_EMMC;
+		break;
+	case QCOM_HWINFO_NAND_FLASH:
+		*storage_type = QCOM_HWINFO_STORAGE_NAND;
+		break;
+	default:
+		dev_warn(dev, "Unknown boot device type: %u, defaulting to UFS\n",
+			 cookie->boot_device_type);
+		*storage_type = QCOM_HWINFO_STORAGE_UFS;
+		break;
+	}
+
+	return 0;
+}
+
+int qcom_hwinfo_get_tcsr_hw_version(struct udevice *dev, u32 *hw_version)
+{
+	struct qcom_hwinfo_priv *priv = dev_get_priv(dev);
+	int ret;
+
+	if (!priv->tcsr_regmap)
+		return -ENODEV;
+
+	ret = regmap_read(priv->tcsr_regmap, priv->tcsr_hwver_off, hw_version);
+	if (ret) {
+		dev_err(dev, "Failed to read TCSR HW version: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int qcom_hwinfo_of_to_plat(struct udevice *dev)
+{
+	struct qcom_hwinfo_priv *priv = dev_get_priv(dev);
+
+	priv->imem_regmap = syscon_regmap_lookup_by_phandle(dev, "imem");
+	if (IS_ERR(priv->imem_regmap)) {
+		dev_warn(dev, "Unable to find 'imem' regmap (%ld)\n",
+			 PTR_ERR(priv->imem_regmap));
+		priv->imem_regmap = NULL;
+	} else {
+		priv->imem_cookie_off =
+			dev_read_u32_default(dev, "imem-boot-cookie-offset", 0);
+	}
+
+	priv->tcsr_regmap = syscon_regmap_lookup_by_phandle(dev, "tcsr");
+	if (IS_ERR(priv->tcsr_regmap)) {
+		dev_warn(dev, "Unable to find 'tcsr' regmap (%ld)\n",
+			 PTR_ERR(priv->tcsr_regmap));
+		priv->tcsr_regmap = NULL;
+	} else {
+		priv->tcsr_hwver_off =
+			dev_read_u32_default(dev, "tcsr-hw-version-offset", 0);
+	}
+
+	if (!priv->imem_regmap && !priv->tcsr_regmap) {
+		dev_err(dev, "Neither 'imem' nor 'tcsr' regmap found\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static const struct udevice_id qcom_hwinfo_ids[] = {
+	{ .compatible = "qcom,hwinfo" },
+	{ }
+};
+
+U_BOOT_DRIVER(qcom_hwinfo) = {
+	.name = "qcom_hwinfo",
+	.id = UCLASS_MISC,
+	.of_match = qcom_hwinfo_ids,
+	.of_to_plat = qcom_hwinfo_of_to_plat,
+	.priv_auto = sizeof(struct qcom_hwinfo_priv),
+};
diff --git a/include/qcom_hwinfo.h b/include/qcom_hwinfo.h
new file mode 100644
index 00000000000..994a7d0bb20
--- /dev/null
+++ b/include/qcom_hwinfo.h
@@ -0,0 +1,109 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Qualcomm HW-Info aggregator driver
+ *
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ *
+ * Reads boot-time hardware info (IMEM boot cookie, TCSR HW version) via
+ * syscon regions, using offsets from the "qcom,hwinfo" DT node.
+ */
+
+#ifndef __QCOM_HWINFO_H__
+#define __QCOM_HWINFO_H__
+
+#include <linux/types.h>
+
+struct udevice;
+
+/*
+ * Storage type values. Match enum mem_card_type in
+ * arch/arm/mach-snapdragon/qcom_fit_multidtb.h.
+ */
+enum qcom_hwinfo_storage_type {
+	QCOM_HWINFO_STORAGE_EMMC = 0,
+	QCOM_HWINFO_STORAGE_UFS = 1,
+	QCOM_HWINFO_STORAGE_NAND = 2,
+	QCOM_HWINFO_STORAGE_SDCARD = 3,
+	QCOM_HWINFO_STORAGE_UNKNOWN,
+};
+
+/* Boot shared IMEM cookie constants */
+#define QCOM_HWINFO_IMEM_MAGIC_NUM    0xc1f8db40
+#define QCOM_HWINFO_IMEM_VERSION_NUM  0x3
+
+/* Boot device types from shared IMEM */
+enum qcom_hwinfo_boot_media_type {
+	QCOM_HWINFO_NO_FLASH         = 0,
+	QCOM_HWINFO_NOR_FLASH        = 1,
+	QCOM_HWINFO_NAND_FLASH       = 2,
+	QCOM_HWINFO_ONENAND_FLASH    = 3,
+	QCOM_HWINFO_SDC_FLASH        = 4,
+	QCOM_HWINFO_MMC_FLASH        = 5,
+	QCOM_HWINFO_SPI_FLASH        = 6,
+	QCOM_HWINFO_PCIE_FLASHLESS   = 7,
+	QCOM_HWINFO_UFS_FLASH        = 8,
+	QCOM_HWINFO_RESERVED_0_FLASH = 9,
+	QCOM_HWINFO_RESERVED_1_FLASH = 10,
+	QCOM_HWINFO_USB_FLASHLESS    = 11,
+};
+
+/*
+ * Boot shared IMEM cookie layout, populated by the bootloader.
+ * Do NOT mark __packed - breaks natural alignment expected by firmware.
+ */
+struct qcom_hwinfo_imem_cookie {
+	u32 shared_imem_magic;
+	u32 shared_imem_version;
+	u64 etb_buf_addr;
+	u64 l2_cache_dump_buff_addr;
+	u32 a64_pointer_padding;
+	u32 uefi_ram_dump_magic;
+	u32 ddr_training_cookie;
+	u32 abnormal_reset_occurred;
+	u32 reset_status_register;
+	u32 rpm_sync_cookie;
+	u32 debug_config;
+	u64 boot_log_addr;
+	u32 boot_log_size;
+	u32 boot_fail_count;
+	u32 sbl1_error_type;
+	u32 uefi_image_magic;
+	u32 boot_device_type;
+	u64 boot_devtree_addr;
+	u64 boot_devtree_size;
+};
+
+/**
+ * qcom_hwinfo_get_imem_cookie() - Get a read-only pointer to the IMEM cookie
+ * @dev: qcom_hwinfo device
+ * @cookie: Returns a const pointer into the live IMEM region
+ *
+ * Validates magic/version before returning. On failure, @cookie is unset.
+ *
+ * Return: 0 on success, -ENODEV if imem regmap unavailable, -EINVAL on
+ *	   magic/version mismatch
+ */
+int qcom_hwinfo_get_imem_cookie(struct udevice *dev,
+				const struct qcom_hwinfo_imem_cookie **cookie);
+
+/**
+ * qcom_hwinfo_get_storage_type() - Detect boot storage type from IMEM cookie
+ * @dev: qcom_hwinfo device
+ * @storage_type: Returns detected storage type
+ *
+ * Wraps qcom_hwinfo_get_imem_cookie(); defaults to UFS on failure.
+ *
+ * Return: 0 always
+ */
+int qcom_hwinfo_get_storage_type(struct udevice *dev, u32 *storage_type);
+
+/**
+ * qcom_hwinfo_get_tcsr_hw_version() - Read the TCSR SOC_HW_VERSION register
+ * @dev: qcom_hwinfo device
+ * @hw_version: Returns the raw register value
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int qcom_hwinfo_get_tcsr_hw_version(struct udevice *dev, u32 *hw_version);
+
+#endif /* __QCOM_HWINFO_H__ */
-- 
2.34.1
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.