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

Reply via email to