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
