[PATCH v2 2/2] ARM: i.MX8M: convert the machine init to the soc driver

Marco Felsch m.felsch at pengutronix.de
Tue Jan 16 09:10:26 PST 2024


Convert the i.MX8M machine init code to the previously introduced soc
framework. The soc driver was mostly copied from Linux with slightly
adaptions for barebox. To the soc driver is called during the
postcore_initcall to keep the level aligned with the previous imx_init().

The ocotp clock must keept running else the ARM-SMCCC stuck for calls
where the TF-A tries to access the ocotp.

A sample output of the new introduced soc0 device:
|
|   barebox at FSL i.MX8MM EVKB:/ devinfo soc0
|   Bus: soc
|   Parameters:
|     family: Freescale i.MX (type: string)
|     machine: FSL i.MX8MM EVKB (type: string)
|     revision: 1.0 (type: string)
|     serial_number: 15182A09DAB5B3C9 (type: string)
|     soc_id: i.MX8MM (type: string)

Signed-off-by: Marco Felsch <m.felsch at pengutronix.de>
---
Changelog:
v2:
 - make use of xasprintf()
 - check of_compatible early and return no error if it does not match

 arch/arm/mach-imx/Kconfig   |   1 +
 arch/arm/mach-imx/imx.c     |  12 +-
 arch/arm/mach-imx/imx8m.c   | 137 +----------------
 drivers/soc/imx/Makefile    |   1 +
 drivers/soc/imx/soc-imx8m.c | 296 ++++++++++++++++++++++++++++++++++++
 5 files changed, 307 insertions(+), 140 deletions(-)
 create mode 100644 drivers/soc/imx/soc-imx8m.c

diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig
index 5429b80b0fdf..3aa551fd5334 100644
--- a/arch/arm/mach-imx/Kconfig
+++ b/arch/arm/mach-imx/Kconfig
@@ -157,6 +157,7 @@ config ARCH_IMX8M
 	select IMX8M_DRAM
 	select PBL_VERIFY_PIGGY if HABV4
 	select ARM_USE_COMPRESSED_DTB
+	select SOC_BUS
 	imply FSL_CAAM_RNG_PBL_INIT if HAVE_OPTEE
 
 config ARCH_IMX8MM
diff --git a/arch/arm/mach-imx/imx.c b/arch/arm/mach-imx/imx.c
index c97e566e0c56..f3491c6df7fa 100644
--- a/arch/arm/mach-imx/imx.c
+++ b/arch/arm/mach-imx/imx.c
@@ -95,6 +95,10 @@ static int imx_init(void)
 			return 0;
 	}
 
+	/*
+	 * Don't add new SoCs to this list, instead use the new
+	 * soc framework (see soc-imx8m.c).
+	 */
 	if (cpu_is_mx1())
 		ret = imx1_init();
 	else if (cpu_is_mx21())
@@ -118,13 +122,13 @@ static int imx_init(void)
 	else if (cpu_is_mx7())
 		ret = imx7_init();
 	else if (cpu_is_mx8mm())
-		ret = imx8mm_init();
+		ret = 0;
 	else if (cpu_is_mx8mn())
-		ret = imx8mn_init();
+		ret = 0;
 	else if (cpu_is_mx8mp())
-		ret = imx8mp_init();
+		ret = 0;
 	else if (cpu_is_mx8mq())
-		ret = imx8mq_init();
+		ret = 0;
 	else if (cpu_is_mx93())
 		ret = imx93_init();
 	else if (cpu_is_vf610())
diff --git a/arch/arm/mach-imx/imx8m.c b/arch/arm/mach-imx/imx8m.c
index 73b420b38697..52e42ee9ef63 100644
--- a/arch/arm/mach-imx/imx8m.c
+++ b/arch/arm/mach-imx/imx8m.c
@@ -2,28 +2,16 @@
 
 #include <init.h>
 #include <common.h>
-#include <asm/optee.h>
 #include <linux/sizes.h>
 #include <io.h>
-#include <pm_domain.h>
 #include <asm/syscounter.h>
 #include <asm/system.h>
-#include <asm-generic/memory_layout.h>
 #include <mach/imx/generic.h>
-#include <mach/imx/revision.h>
-#include <mach/imx/imx8mq.h>
 #include <mach/imx/imx8m-ccm-regs.h>
-#include <mach/imx/reset-reason.h>
-#include <mach/imx/ocotp.h>
-#include <mach/imx/imx8mp-regs.h>
-#include <mach/imx/imx8mq-regs.h>
-#include <mach/imx/scratch.h>
-#include <mach/imx/tzasc.h>
 #include <soc/imx8m/clk-early.h>
-#include <tee/optee.h>
 
+#include <linux/bitfield.h>
 #include <linux/iopoll.h>
-#include <linux/arm-smccc.h>
 
 #define IMX_SIP_BUILDINFO			0xC2000003
 #define IMX_SIP_BUILDINFO_GET_COMMITHASH	0x00
@@ -51,129 +39,6 @@ void imx8m_ccgr_clock_disable(int index)
 	       ccm + IMX8M_CCM_CCGRn_CLR(index));
 }
 
-u64 imx8m_uid(void)
-{
-	return imx_ocotp_read_uid(IOMEM(MX8M_OCOTP_BASE_ADDR));
-}
-
-static int imx8m_init(const char *cputypestr)
-{
-	void __iomem *src = IOMEM(MX8M_SRC_BASE_ADDR);
-
-	genpd_activate();
-
-	/*
-	 * Reset reasons seem to be identical to that of i.MX7
-	 */
-	imx_set_reset_reason(src + IMX7_SRC_SRSR, imx7_reset_reasons);
-	pr_info("%s unique ID: %llx\n", cputypestr, imx8m_uid());
-
-	if (IS_ENABLED(CONFIG_PBL_OPTEE) && tzc380_is_enabled()) {
-		static struct of_optee_fixup_data optee_fixup_data = {
-			.shm_size = OPTEE_SHM_SIZE,
-			.method = "smc",
-		};
-
-		optee_set_membase(imx8m_scratch_get_optee_hdr());
-		of_optee_fixup(of_get_root_node(), &optee_fixup_data);
-		of_register_fixup(of_optee_fixup, &optee_fixup_data);
-	}
-
-	return 0;
-}
-
-int imx8mm_init(void)
-{
-	void __iomem *anatop = IOMEM(MX8M_ANATOP_BASE_ADDR);
-	uint32_t type = FIELD_GET(DIGPROG_MAJOR,
-				  readl(anatop + MX8MM_ANATOP_DIGPROG));
-	const char *cputypestr;
-
-	imx8mm_boot_save_loc();
-
-	switch (type) {
-	case IMX8M_CPUTYPE_IMX8MM:
-		cputypestr = "i.MX8MM";
-		break;
-	default:
-		cputypestr = "unknown i.MX8M";
-		break;
-	};
-
-	imx_set_silicon_revision(cputypestr, imx8mm_cpu_revision());
-
-	return imx8m_init(cputypestr);
-}
-
-int imx8mn_init(void)
-{
-	void __iomem *anatop = IOMEM(MX8M_ANATOP_BASE_ADDR);
-	uint32_t type = FIELD_GET(DIGPROG_MAJOR,
-				  readl(anatop + MX8MN_ANATOP_DIGPROG));
-	const char *cputypestr;
-
-	imx8mn_boot_save_loc();
-
-	switch (type) {
-	case IMX8M_CPUTYPE_IMX8MN:
-		cputypestr = "i.MX8MN";
-		break;
-	default:
-		cputypestr = "unknown i.MX8M";
-		break;
-	};
-
-	imx_set_silicon_revision(cputypestr, imx8mn_cpu_revision());
-
-	return imx8m_init(cputypestr);
-}
-
-int imx8mp_init(void)
-{
-	void __iomem *anatop = IOMEM(MX8MP_ANATOP_BASE_ADDR);
-	uint32_t type = FIELD_GET(DIGPROG_MAJOR,
-				  readl(anatop + MX8MP_ANATOP_DIGPROG));
-	const char *cputypestr;
-
-	imx8mp_boot_save_loc();
-
-	switch (type) {
-	case IMX8M_CPUTYPE_IMX8MP:
-		cputypestr = "i.MX8MP";
-		break;
-	default:
-		cputypestr = "unknown i.MX8M";
-		break;
-	};
-
-	imx_set_silicon_revision(cputypestr, imx8mp_cpu_revision());
-
-	return imx8m_init(cputypestr);
-}
-
-int imx8mq_init(void)
-{
-	void __iomem *anatop = IOMEM(MX8M_ANATOP_BASE_ADDR);
-	uint32_t type = FIELD_GET(DIGPROG_MAJOR,
-				  readl(anatop + MX8MQ_ANATOP_DIGPROG));
-	const char *cputypestr;
-
-	imx8mq_boot_save_loc();
-
-	switch (type) {
-	case IMX8M_CPUTYPE_IMX8MQ:
-		cputypestr = "i.MX8MQ";
-		break;
-	default:
-		cputypestr = "unknown i.MX8M";
-		break;
-	};
-
-	imx_set_silicon_revision(cputypestr, imx8mq_cpu_revision());
-
-	return imx8m_init(cputypestr);
-}
-
 #define INTPLL_DIV20_CLKE_MASK                  BIT(27)
 #define INTPLL_DIV10_CLKE_MASK                  BIT(25)
 #define INTPLL_DIV8_CLKE_MASK                   BIT(23)
diff --git a/drivers/soc/imx/Makefile b/drivers/soc/imx/Makefile
index bd1717b03883..9d9c2e88798c 100644
--- a/drivers/soc/imx/Makefile
+++ b/drivers/soc/imx/Makefile
@@ -1,3 +1,4 @@
 # SPDX-License-Identifier: GPL-2.0-only
 obj-$(CONFIG_IMX_GPCV2_PM_DOMAINS) += gpcv2.o
 obj-$(CONFIG_IMX8M_FEATCTRL) += imx8m-featctrl.o
+obj-$(CONFIG_ARCH_IMX8M) += soc-imx8m.o
diff --git a/drivers/soc/imx/soc-imx8m.c b/drivers/soc/imx/soc-imx8m.c
new file mode 100644
index 000000000000..c648b201b747
--- /dev/null
+++ b/drivers/soc/imx/soc-imx8m.c
@@ -0,0 +1,296 @@
+// SPDX-License-Identifier: GPL-2.0
+// SPDX-FileCopyrightText: 2024 Marco Felsch, Pengutronix
+/*
+ * Based on Linux drivers/soc/imx/soc-imx8m.c:
+ * Copyright 2019 NXP.
+ */
+
+#include <init.h>
+#include <of.h>
+#include <of_address.h>
+#include <pm_domain.h>
+
+#include <asm/optee.h>
+#include <asm-generic/memory_layout.h>
+
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/sys_soc.h>
+#include <linux/arm-smccc.h>
+#include <linux/clk.h>
+
+#include <mach/imx/generic.h>
+#include <mach/imx/imx8m-regs.h>
+#include <mach/imx/reset-reason.h>
+#include <mach/imx/revision.h>
+#include <mach/imx/scratch.h>
+#include <mach/imx/tzasc.h>
+
+#include <tee/optee.h>
+
+#define REV_B1				0x21
+
+#define IMX8MQ_SW_INFO_B1		0x40
+#define IMX8MQ_SW_MAGIC_B1		0xff0055aa
+
+#define IMX_SIP_GET_SOC_INFO		0xc2000006
+
+#define OCOTP_UID_LOW			0x410
+#define OCOTP_UID_HIGH			0x420
+
+#define IMX8MP_OCOTP_UID_OFFSET		0x10
+
+/* Same as ANADIG_DIGPROG_IMX7D */
+#define ANADIG_DIGPROG_IMX8MM	0x800
+
+struct imx8_soc_data {
+	char *name;
+	u32 (*soc_revision)(void);
+	void (*save_boot_loc)(void);
+};
+
+static u64 soc_uid;
+
+#ifdef CONFIG_HAVE_ARM_SMCCC
+static u32 imx8mq_soc_revision_from_atf(void)
+{
+	struct arm_smccc_res res;
+
+	arm_smccc_smc(IMX_SIP_GET_SOC_INFO, 0, 0, 0, 0, 0, 0, 0, &res);
+
+	if (res.a0 == SMCCC_RET_NOT_SUPPORTED)
+		return 0;
+	else
+		return res.a0 & 0xff;
+}
+#else
+static inline u32 imx8mq_soc_revision_from_atf(void) { return 0; };
+#endif
+
+static u32 __init imx8mq_soc_revision(void)
+{
+	struct device_node *np;
+	void __iomem *ocotp_base;
+	u32 magic;
+	u32 rev;
+	struct clk *clk;
+
+	np = of_find_compatible_node(NULL, NULL, "fsl,imx8mq-ocotp");
+	if (!np)
+		return 0;
+
+	ocotp_base = of_iomap(np, 0);
+	WARN_ON(!ocotp_base);
+	clk = of_clk_get_by_name(np, NULL);
+	if (IS_ERR(clk)) {
+		WARN_ON(IS_ERR(clk));
+		return 0;
+	}
+
+	clk_prepare_enable(clk);
+
+	/*
+	 * SOC revision on older imx8mq is not available in fuses so query
+	 * the value from ATF instead.
+	 */
+	rev = imx8mq_soc_revision_from_atf();
+	if (!rev) {
+		magic = readl_relaxed(ocotp_base + IMX8MQ_SW_INFO_B1);
+		if (magic == IMX8MQ_SW_MAGIC_B1)
+			rev = REV_B1;
+	}
+
+	soc_uid = readl_relaxed(ocotp_base + OCOTP_UID_HIGH);
+	soc_uid <<= 32;
+	soc_uid |= readl_relaxed(ocotp_base + OCOTP_UID_LOW);
+
+	/* Keep the OCOTP clk on for the TF-A else the CPU stuck */
+	of_node_put(np);
+
+	return rev;
+}
+
+static void __init imx8mm_soc_uid(void)
+{
+	void __iomem *ocotp_base;
+	struct device_node *np;
+	struct clk *clk;
+	u32 offset = of_machine_is_compatible("fsl,imx8mp") ?
+		     IMX8MP_OCOTP_UID_OFFSET : 0;
+
+	np = of_find_compatible_node(NULL, NULL, "fsl,imx8mm-ocotp");
+	if (!np)
+		return;
+
+	ocotp_base = of_iomap(np, 0);
+	WARN_ON(!ocotp_base);
+	clk = of_clk_get_by_name(np, NULL);
+	if (IS_ERR(clk)) {
+		WARN_ON(IS_ERR(clk));
+		return;
+	}
+
+	clk_prepare_enable(clk);
+
+	soc_uid = readl_relaxed(ocotp_base + OCOTP_UID_HIGH + offset);
+	soc_uid <<= 32;
+	soc_uid |= readl_relaxed(ocotp_base + OCOTP_UID_LOW + offset);
+
+	/* Keep the OCOTP clk on for the TF-A else the CPU stuck */
+	of_node_put(np);
+}
+
+static u32 __init imx8mm_soc_revision(void)
+{
+	struct device_node *np;
+	void __iomem *anatop_base;
+	u32 rev;
+
+	np = of_find_compatible_node(NULL, NULL, "fsl,imx8mm-anatop");
+	if (!np)
+		return 0;
+
+	anatop_base = of_iomap(np, 0);
+	WARN_ON(!anatop_base);
+
+	rev = readl_relaxed(anatop_base + ANADIG_DIGPROG_IMX8MM);
+
+	of_node_put(np);
+
+	imx8mm_soc_uid();
+
+	return rev;
+}
+
+static const struct imx8_soc_data imx8mq_soc_data = {
+	.name = "i.MX8MQ",
+	.soc_revision = imx8mq_soc_revision,
+	.save_boot_loc = imx8mq_boot_save_loc,
+};
+
+static const struct imx8_soc_data imx8mm_soc_data = {
+	.name = "i.MX8MM",
+	.soc_revision = imx8mm_soc_revision,
+	.save_boot_loc = imx8mm_boot_save_loc,
+};
+
+static const struct imx8_soc_data imx8mn_soc_data = {
+	.name = "i.MX8MN",
+	.soc_revision = imx8mm_soc_revision,
+	.save_boot_loc = imx8mn_boot_save_loc,
+};
+
+static const struct imx8_soc_data imx8mp_soc_data = {
+	.name = "i.MX8MP",
+	.soc_revision = imx8mm_soc_revision,
+	.save_boot_loc = imx8mp_boot_save_loc,
+};
+
+static __maybe_unused const struct of_device_id imx8_soc_match[] = {
+	{ .compatible = "fsl,imx8mq", .data = &imx8mq_soc_data, },
+	{ .compatible = "fsl,imx8mm", .data = &imx8mm_soc_data, },
+	{ .compatible = "fsl,imx8mn", .data = &imx8mn_soc_data, },
+	{ .compatible = "fsl,imx8mp", .data = &imx8mp_soc_data, },
+	{ }
+};
+
+static int imx8_soc_imx8m_init(struct soc_device_attribute *soc_dev_attr)
+{
+	void __iomem *src = IOMEM(MX8M_SRC_BASE_ADDR);
+	const char *uid = soc_dev_attr->serial_number;
+	const char *cputypestr = soc_dev_attr->soc_id;
+
+	genpd_activate();
+
+	/*
+	 * Reset reasons seem to be identical to that of i.MX7
+	 */
+	imx_set_reset_reason(src + IMX7_SRC_SRSR, imx7_reset_reasons);
+	pr_info("%s unique ID: %s\n", cputypestr, uid);
+
+	if (IS_ENABLED(CONFIG_PBL_OPTEE) && tzc380_is_enabled()) {
+		static struct of_optee_fixup_data optee_fixup_data = {
+			.shm_size = OPTEE_SHM_SIZE,
+			.method = "smc",
+		};
+
+		optee_set_membase(imx8m_scratch_get_optee_hdr());
+		of_optee_fixup(of_get_root_node(), &optee_fixup_data);
+		of_register_fixup(of_optee_fixup, &optee_fixup_data);
+	}
+
+	return 0;
+}
+
+#define imx8_revision(soc_rev) \
+	soc_rev ? \
+	xasprintf("%d.%d", (soc_rev >> 4) & 0xf,  soc_rev & 0xf) : \
+	"unknown"
+
+static int __init imx8_soc_init(void)
+{
+	struct device_node *of_root = of_get_root_node();
+	struct soc_device_attribute *soc_dev_attr;
+	struct soc_device *soc_dev;
+	const struct of_device_id *id;
+	u32 soc_rev = 0;
+	const struct imx8_soc_data *data;
+	int ret;
+
+	id = of_match_node(imx8_soc_match, of_root);
+	if (!id)
+		return 0;
+
+	soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
+	if (!soc_dev_attr)
+		return -ENOMEM;
+
+	soc_dev_attr->family = "Freescale i.MX";
+
+	ret = of_property_read_string(of_root, "model", &soc_dev_attr->machine);
+	if (ret)
+		goto free_soc;
+
+	data = id->data;
+	if (data) {
+		soc_dev_attr->soc_id = data->name;
+		if (data->soc_revision)
+			soc_rev = data->soc_revision();
+		if (data->save_boot_loc)
+			data->save_boot_loc();
+	}
+
+	soc_dev_attr->revision = imx8_revision(soc_rev);
+	if (!soc_dev_attr->revision) {
+		ret = -ENOMEM;
+		goto free_soc;
+	}
+
+	soc_dev_attr->serial_number = xasprintf("%016llX", soc_uid);
+	if (!soc_dev_attr->serial_number) {
+		ret = -ENOMEM;
+		goto free_rev;
+	}
+
+	soc_dev = soc_device_register(soc_dev_attr);
+	if (IS_ERR(soc_dev)) {
+		ret = PTR_ERR(soc_dev);
+		goto free_serial_number;
+	}
+
+	imx_set_silicon_revision(soc_dev_attr->soc_id, soc_rev);
+
+	return imx8_soc_imx8m_init(soc_dev_attr);
+
+free_serial_number:
+	kfree(soc_dev_attr->serial_number);
+free_rev:
+	if (strcmp(soc_dev_attr->revision, "unknown"))
+		kfree(soc_dev_attr->revision);
+free_soc:
+	kfree(soc_dev_attr);
+	return ret;
+}
+/* Aligned with imx_init() to not cause regressions */
+postcore_initcall(imx8_soc_init);
+MODULE_LICENSE("GPL");
-- 
2.39.2




More information about the barebox mailing list