summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2024-02-21 07:43:45 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2024-02-21 07:43:45 +0100
commitb83407415e5a2d28a7876b936f1c682d62a0372a (patch)
treef5f9dd201feec42f7527f9f069aef56eca8c6aa2 /drivers
parent9fd550e388e2181a6cb9abe9ba06ae25897902fd (diff)
parent7eedf79ba92912455954ec178822f0a03e9c28e3 (diff)
downloadbarebox-b83407415e5a2d28a7876b936f1c682d62a0372a.tar.gz
barebox-b83407415e5a2d28a7876b936f1c682d62a0372a.tar.xz
Merge branch 'for-next/imx'
Diffstat (limited to 'drivers')
-rw-r--r--drivers/base/Kconfig3
-rw-r--r--drivers/base/Makefile1
-rw-r--r--drivers/base/soc.c123
-rw-r--r--drivers/ddr/imx/imx8m_ddr_init.c6
-rw-r--r--drivers/hab/hab.c137
-rw-r--r--drivers/hab/hab.h10
-rw-r--r--drivers/hab/habv3.c6
-rw-r--r--drivers/hab/habv4.c78
-rw-r--r--drivers/i2c/busses/Makefile2
-rw-r--r--drivers/i2c/busses/i2c-imx-lpi2c.c70
-rw-r--r--drivers/nvmem/imx-ocotp-ele.c22
-rw-r--r--drivers/nvmem/ocotp.c80
-rw-r--r--drivers/soc/imx/Makefile1
-rw-r--r--drivers/soc/imx/soc-imx8m.c296
14 files changed, 703 insertions, 132 deletions
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
index 612a84be33..21a4793cfa 100644
--- a/drivers/base/Kconfig
+++ b/drivers/base/Kconfig
@@ -6,4 +6,7 @@ config PM_GENERIC_DOMAINS
config FEATURE_CONTROLLER
bool "Feature controller support" if COMPILE_TEST || SANDBOX
+config SOC_BUS
+ bool
+
source "drivers/base/regmap/Kconfig"
diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index e8e354cdaa..acc53763da 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -7,3 +7,4 @@ obj-y += regmap/
obj-$(CONFIG_PM_GENERIC_DOMAINS) += power.o
obj-$(CONFIG_FEATURE_CONTROLLER) += featctrl.o
+obj-$(CONFIG_SOC_BUS) += soc.o
diff --git a/drivers/base/soc.c b/drivers/base/soc.c
new file mode 100644
index 0000000000..a481f8987b
--- /dev/null
+++ b/drivers/base/soc.c
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: 2024 Marco Felsch, Pengutronix
+/*
+ * Based on Linux drivers/base/soc.c:
+ * Copyright (C) ST-Ericsson SA 2011
+ */
+
+#include <common.h>
+#include <init.h>
+#include <of.h>
+
+#include <linux/slab.h>
+#include <linux/sys_soc.h>
+#include <linux/err.h>
+
+struct soc_device {
+ struct device dev;
+ struct soc_device_attribute *attr;
+};
+
+static struct bus_type soc_bus_type = {
+ .name = "soc",
+};
+static bool soc_bus_registered;
+
+static void soc_device_add_params(struct soc_device *soc_dev)
+{
+ struct soc_device_attribute *attr = soc_dev->attr;
+ struct device *dev = &soc_dev->dev;
+
+ if (attr->machine)
+ dev_add_param_string_fixed(dev, "machine", attr->machine);
+ if (attr->family)
+ dev_add_param_string_fixed(dev, "family", attr->family);
+ if (attr->revision)
+ dev_add_param_string_fixed(dev, "revision", attr->revision);
+ if (attr->serial_number)
+ dev_add_param_string_fixed(dev, "serial_number", attr->serial_number);
+ if (attr->soc_id)
+ dev_add_param_string_fixed(dev, "soc_id", attr->soc_id);
+}
+
+static void soc_device_get_machine(struct soc_device_attribute *soc_dev_attr)
+{
+ struct device_node *np;
+
+ if (soc_dev_attr->machine)
+ return;
+
+ np = of_find_node_by_path("/");
+ of_property_read_string(np, "model", &soc_dev_attr->machine);
+ of_node_put(np);
+}
+
+static struct soc_device_attribute *early_soc_dev_attr;
+
+struct soc_device *soc_device_register(struct soc_device_attribute *soc_dev_attr)
+{
+ struct soc_device *soc_dev;
+ int ret;
+
+ soc_device_get_machine(soc_dev_attr);
+
+ if (!soc_bus_registered) {
+ if (early_soc_dev_attr)
+ return ERR_PTR(-EBUSY);
+ early_soc_dev_attr = soc_dev_attr;
+ return NULL;
+ }
+
+ soc_dev = kzalloc(sizeof(*soc_dev), GFP_KERNEL);
+ if (!soc_dev) {
+ ret = -ENOMEM;
+ goto out1;
+ }
+
+ soc_dev->attr = soc_dev_attr;
+ soc_dev->dev.bus = &soc_bus_type;
+ soc_dev->dev.id = DEVICE_ID_DYNAMIC;
+
+ dev_set_name(&soc_dev->dev, "soc");
+
+ ret = device_register(&soc_dev->dev);
+ if (ret) {
+ put_device(&soc_dev->dev);
+ goto out2;
+ }
+
+ soc_device_add_params(soc_dev);
+
+ return soc_dev;
+
+out2:
+ kfree(soc_dev);
+out1:
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(soc_device_register);
+
+/* Ensure soc_dev->attr is freed after calling soc_device_unregister. */
+void soc_device_unregister(struct soc_device *soc_dev)
+{
+ device_unregister(&soc_dev->dev);
+ kfree(soc_dev);
+ early_soc_dev_attr = NULL;
+}
+EXPORT_SYMBOL_GPL(soc_device_unregister);
+
+static int __init soc_bus_register(void)
+{
+ int ret;
+
+ ret = bus_register(&soc_bus_type);
+ if (ret)
+ return ret;
+ soc_bus_registered = true;
+
+ if (early_soc_dev_attr)
+ return PTR_ERR(soc_device_register(early_soc_dev_attr));
+
+ return 0;
+}
+core_initcall(soc_bus_register);
diff --git a/drivers/ddr/imx/imx8m_ddr_init.c b/drivers/ddr/imx/imx8m_ddr_init.c
index 8b829645c0..d9a5d589f2 100644
--- a/drivers/ddr/imx/imx8m_ddr_init.c
+++ b/drivers/ddr/imx/imx8m_ddr_init.c
@@ -219,9 +219,9 @@ enum ddr_rate {
DDR_3720,
DDR_3200,
DDR_3000,
- DDR_2600, /* Unused */
+ DDR_2600,
DDR_2400,
- DDR_2376, /* Unused */
+ DDR_2376,
DDR_1600,
DDR_1000, /* Unused */
DDR_1066,
@@ -450,7 +450,9 @@ static void ddrphy_init_set_dfi_clk(struct dram_controller *dram, unsigned int d
case 3720: drate = DDR_3720; break;
case 3200: drate = DDR_3200; break;
case 3000: drate = DDR_3000; break;
+ case 2600: drate = DDR_2600; break;
case 2400: drate = DDR_2400; break;
+ case 2376: drate = DDR_2376; break;
case 1600: drate = DDR_1600; break;
case 1066: drate = DDR_1066; break;
case 667: drate = DDR_667; break;
diff --git a/drivers/hab/hab.c b/drivers/hab/hab.c
index 75a8cca71e..ed091058d8 100644
--- a/drivers/hab/hab.c
+++ b/drivers/hab/hab.c
@@ -13,6 +13,9 @@
#include <mach/imx/imx25-fusemap.h>
#include <mach/imx/ocotp.h>
#include <mach/imx/imx6-fusemap.h>
+#include <mach/imx/ele.h>
+
+#include "hab.h"
bool imx_hab_srk_hash_valid(const void *buf)
{
@@ -94,7 +97,7 @@ static int imx_hab_permanent_write_enable_iim(int enable)
return imx_iim_permanent_write(enable);
}
-static int imx_hab_lockdown_device_iim(void)
+static int imx_hab_lockdown_device_iim(unsigned flags)
{
return imx_iim_write_field(IMX25_IIM_HAB_TYPE, 3);
}
@@ -150,7 +153,7 @@ static int imx_hab_permanent_write_enable_ocotp(int enable)
return imx_ocotp_permanent_write(enable);
}
-static int imx6_hab_lockdown_device_ocotp(void)
+static int imx6_hab_lockdown_device_ocotp(unsigned flags)
{
int ret;
@@ -161,7 +164,7 @@ static int imx6_hab_lockdown_device_ocotp(void)
return imx_ocotp_write_field(OCOTP_SEC_CONFIG_1, 1);
}
-static int imx8m_hab_lockdown_device_ocotp(void)
+static int imx8m_hab_lockdown_device_ocotp(unsigned flags)
{
int ret;
@@ -204,8 +207,9 @@ struct imx_hab_ops {
int (*write_srk_hash)(const u8 *srk, unsigned flags);
int (*read_srk_hash)(u8 *srk);
int (*permanent_write_enable)(int enable);
- int (*lockdown_device)(void);
+ int (*lockdown_device)(unsigned flags);
int (*device_locked_down)(void);
+ int (*print_status)(void);
};
static struct imx_hab_ops imx_hab_ops_iim = {
@@ -214,6 +218,7 @@ static struct imx_hab_ops imx_hab_ops_iim = {
.lockdown_device = imx_hab_lockdown_device_iim,
.device_locked_down = imx_hab_device_locked_down_iim,
.permanent_write_enable = imx_hab_permanent_write_enable_iim,
+ .print_status = imx25_hab_print_status,
};
static struct imx_hab_ops imx6_hab_ops_ocotp = {
@@ -222,6 +227,7 @@ static struct imx_hab_ops imx6_hab_ops_ocotp = {
.lockdown_device = imx6_hab_lockdown_device_ocotp,
.device_locked_down = imx6_hab_device_locked_down_ocotp,
.permanent_write_enable = imx_hab_permanent_write_enable_ocotp,
+ .print_status = imx6_hab_print_status,
};
static struct imx_hab_ops imx8m_hab_ops_ocotp = {
@@ -230,6 +236,105 @@ static struct imx_hab_ops imx8m_hab_ops_ocotp = {
.lockdown_device = imx8m_hab_lockdown_device_ocotp,
.device_locked_down = imx8m_hab_device_locked_down_ocotp,
.permanent_write_enable = imx_hab_permanent_write_enable_ocotp,
+ .print_status = imx8m_hab_print_status,
+};
+
+static int imx_ahab_write_srk_hash(const u8 *__newsrk, unsigned flags)
+{
+ u32 *newsrk = (u32 *)__newsrk;
+ u32 resp;
+ int ret, i;
+
+ if (!(flags & IMX_SRK_HASH_WRITE_PERMANENT)) {
+ pr_err("Cannot write fuses temporarily\n");
+ return -EPERM;
+ }
+
+ for (i = 0; i < 32 / sizeof(u32); i++) {
+ ret = ele_write_fuse(0x80 + i, newsrk[i], false, &resp);
+ if (ret)
+ pr_err("Writing fuse index 0x%02x failed with %d, response 0x%08x\n",
+ i, ret, resp);
+ }
+
+ return 0;
+}
+
+static int imx_ahab_read_srk_hash(u8 *__srk)
+{
+ u32 *srk = (u32 *)__srk;
+ u32 resp;
+ int ret, i;
+
+ for (i = 0; i < SRK_HASH_SIZE / sizeof(uint32_t); i++) {
+ ret = ele_read_common_fuse(0x80 + i, &srk[i], &resp);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int imx_ahab_permanent_write_enable(int enable)
+{
+ return 0;
+}
+
+static int imx_ahab_lockdown_device(unsigned flags)
+{
+ unsigned int lc;
+ int ret;
+
+ if (!(flags & IMX_SRK_HASH_WRITE_PERMANENT)) {
+ pr_err("Cannot write fuses temporarily\n");
+ return -EPERM;
+ }
+
+ lc = imx93_ahab_read_lifecycle();
+ if (lc == ELE_LIFECYCLE_OEM_CLOSED) {
+ pr_info("already OEM closed\n");
+ return 0;
+ }
+
+ if (lc != ELE_LIFECYCLE_OEM_OPEN) {
+ pr_err("Current lifecycle is NOT OEM open, can't move to OEM closed\n");
+ return -EPERM;
+ }
+
+ ret = ele_forward_lifecycle(ELE_LIFECYCLE_OEM_CLOSED, NULL);
+ if (ret) {
+ printf("failed to forward lifecycle to OEM closed\n");
+ return ret;
+ }
+
+ printf("Change to OEM closed successfully\n");
+
+ return 0;
+}
+
+static int imx_ahab_device_locked_down(void)
+{
+ return imx93_ahab_read_lifecycle() != ELE_LIFECYCLE_OEM_OPEN;
+}
+
+static int imx_ahab_print_status(void)
+{
+ int ret;
+
+ ret = ele_print_events();
+ if (ret)
+ pr_err("Cannot read ELE events: %pe\n", ERR_PTR(ret));
+
+ return ret;
+}
+
+static struct imx_hab_ops imx93_ahab_ops = {
+ .write_srk_hash = imx_ahab_write_srk_hash,
+ .read_srk_hash = imx_ahab_read_srk_hash,
+ .lockdown_device = imx_ahab_lockdown_device,
+ .device_locked_down = imx_ahab_device_locked_down,
+ .permanent_write_enable = imx_ahab_permanent_write_enable,
+ .print_status = imx_ahab_print_status,
};
static struct imx_hab_ops *imx_get_hab_ops(void)
@@ -239,12 +344,14 @@ static struct imx_hab_ops *imx_get_hab_ops(void)
if (ops)
return ops;
- if (IS_ENABLED(CONFIG_HABV3) && (cpu_is_mx25() || cpu_is_mx35()))
+ if (IS_ENABLED(CONFIG_HABV3) && cpu_is_mx25())
ops = &imx_hab_ops_iim;
else if (IS_ENABLED(CONFIG_HABV4) && cpu_is_mx6())
ops = &imx6_hab_ops_ocotp;
else if (IS_ENABLED(CONFIG_HABV4) && cpu_is_mx8m())
ops = &imx8m_hab_ops_ocotp;
+ else if (IS_ENABLED(CONFIG_AHAB) && cpu_is_mx93())
+ ops = &imx93_ahab_ops;
else
return NULL;
@@ -366,7 +473,7 @@ int imx_hab_lockdown_device(unsigned flags)
return ret;
}
- ret = ops->lockdown_device();
+ ret = ops->lockdown_device(flags);
if (flags & IMX_SRK_HASH_WRITE_PERMANENT)
ops->permanent_write_enable(0);
@@ -383,3 +490,21 @@ int imx_hab_device_locked_down(void)
return ops->device_locked_down();
}
+
+int imx_hab_print_status(void)
+{
+ struct imx_hab_ops *ops = imx_get_hab_ops();
+
+ if (!ops)
+ return -ENOSYS;
+
+ return ops->print_status();
+}
+
+static int init_imx_hab_print_status(void)
+{
+ imx_hab_print_status();
+
+ return 0;
+}
+postmmu_initcall(init_imx_hab_print_status);
diff --git a/drivers/hab/hab.h b/drivers/hab/hab.h
new file mode 100644
index 0000000000..7be0e8386b
--- /dev/null
+++ b/drivers/hab/hab.h
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#ifndef __DRIVER_HAB_HAB_H
+#define __DRIVER_HAB_HAB_H
+
+int imx25_hab_print_status(void);
+int imx6_hab_print_status(void);
+int imx8m_hab_print_status(void);
+
+#endif /* __DRIVER_HAB_HAB_H */
diff --git a/drivers/hab/habv3.c b/drivers/hab/habv3.c
index 4818dae7d1..e28e9998d7 100644
--- a/drivers/hab/habv3.c
+++ b/drivers/hab/habv3.c
@@ -69,11 +69,7 @@ static int imx_habv3_get_status(uint32_t status)
return -EPERM;
}
-int imx25_hab_get_status(void)
+int imx25_hab_print_status(void)
{
- if (!cpu_is_mx25())
- return 0;
-
return imx_habv3_get_status(readl(IOMEM(0x780018d4)));
}
-postmmu_initcall(imx25_hab_get_status);
diff --git a/drivers/hab/habv4.c b/drivers/hab/habv4.c
index c60a658b89..a1d823ed25 100644
--- a/drivers/hab/habv4.c
+++ b/drivers/hab/habv4.c
@@ -20,7 +20,8 @@
#include <mach/imx/generic.h>
#include <mach/imx/imx8mq.h>
-#define HABV4_RVT_IMX28 0xffff8af8
+#include "hab.h"
+
#define HABV4_RVT_IMX6_OLD 0x00000094
#define HABV4_RVT_IMX6_NEW 0x00000098
#define HABV4_RVT_IMX6UL 0x00000100
@@ -147,9 +148,11 @@ struct hab_header {
typedef enum hab_status hab_loader_callback_fn(void **start, size_t *bytes, const void *boot_data);
typedef void hab_image_entry_fn(void);
-/* This table is constructed from the NXP manual "High Assurance Boot Version 4
- * Application Programming Interface Reference Manual", section 4.5 ROM vector
- * table. Revision 1.4 */
+/*
+ * This table is constructed from the NXP manual "High Assurance Boot
+ * Version 4 Application Programming Interface Reference Manual",
+ * section 4.5 ROM vector table. Revision 1.4
+ */
struct habv4_rvt {
struct hab_header header;
enum hab_status (*entry)(void);
@@ -169,9 +172,11 @@ struct habv4_rvt {
#define FSL_SIP_HAB 0xC2000007
-/* These values correspondent to the jump table found in the upstream TF-A
- * version 2.10 `imx_hab_handler`, not all HAB rom functions are supported yet.
- * */
+/*
+ * These values correspondent to the jump table found in the upstream
+ * TF-A version 2.10 `imx_hab_handler`, not all HAB rom functions are
+ * supported yet.
+ */
enum hab_sip_cmd {
FSL_SIP_HAB_AUTHENTICATE = 0x00,
FSL_SIP_HAB_ENTRY = 0x01,
@@ -643,7 +648,7 @@ static int habv4_get_status(const struct habv4_rvt *rvt)
return -EPERM;
}
-int imx6_hab_get_status(void)
+static int imx6_hab_get_status(void)
{
const struct habv4_rvt *rvt;
@@ -667,41 +672,19 @@ int imx6_hab_get_status(void)
return -EINVAL;
}
-static int imx8m_hab_get_status(void)
-{
- return habv4_get_status(&hab_smc_ops);
-}
-
-static int init_imx8m_hab_get_status(void)
+int imx8m_hab_print_status(void)
{
- if (!cpu_is_mx8m())
- /* can happen in multi-image builds and is not an error */
- return 0;
-
pr_info("ROM version: 0x%x\n", hab_sip_get_version());
- /*
- * Nobody will check the return value if there were HAB errors, but the
- * initcall will fail spectaculously with a strange error message.
- */
- imx8m_hab_get_status();
+ habv4_get_status(&hab_smc_ops);
return 0;
}
-postmmu_initcall(init_imx8m_hab_get_status);
-static int init_imx6_hab_get_status(void)
+int imx6_hab_print_status(void)
{
- if (!cpu_is_mx6())
- /* can happen in multi-image builds and is not an error */
- return 0;
-
remap_range(0x0, SZ_1M, MAP_CACHED);
- /*
- * Nobody will check the return value if there were HAB errors, but the
- * initcall will fail spectaculously with a strange error message.
- */
imx6_hab_get_status();
zero_page_faulting();
@@ -709,32 +692,3 @@ static int init_imx6_hab_get_status(void)
return 0;
}
-
-/*
- * Need to run before MMU setup because i.MX6 ROM code is mapped near 0x0,
- * which will no longer be accessible when the MMU sets the zero page to
- * faulting.
- */
-postmmu_initcall(init_imx6_hab_get_status);
-
-int imx28_hab_get_status(void)
-{
- const struct habv4_rvt *rvt = (void *)HABV4_RVT_IMX28;
-
- return habv4_get_status(rvt);
-}
-
-static int init_imx28_hab_get_status(void)
-{
- if (!cpu_is_mx28())
- /* can happen in multi-image builds and is not an error */
- return 0;
-
-
- /* nobody will check the return value if there were HAB errors, but the
- * initcall will fail spectaculously with a strange error message. */
- imx28_hab_get_status();
- return 0;
-}
-/* i.MX28 ROM code can be run after MMU setup to make use of caching */
-postmmu_initcall(init_imx28_hab_get_status);
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 48f9b5be04..b4225995c0 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -4,7 +4,7 @@ obj-$(CONFIG_I2C_BCM283X) += i2c-bcm283x.o
obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o
obj-$(CONFIG_I2C_IMX) += i2c-imx.o
lwl-$(CONFIG_I2C_IMX_EARLY) += i2c-imx-early.o
-obj-$(CONFIG_I2C_IMX_LPI2C) += i2c-imx-lpi2c.o
+obj-pbl-$(CONFIG_I2C_IMX_LPI2C) += i2c-imx-lpi2c.o
obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o
obj-$(CONFIG_I2C_OMAP) += i2c-omap.o
obj-$(CONFIG_I2C_TEGRA) += i2c-tegra.o
diff --git a/drivers/i2c/busses/i2c-imx-lpi2c.c b/drivers/i2c/busses/i2c-imx-lpi2c.c
index f28a445601..a7d52fb1b1 100644
--- a/drivers/i2c/busses/i2c-imx-lpi2c.c
+++ b/drivers/i2c/busses/i2c-imx-lpi2c.c
@@ -19,6 +19,7 @@
#include <pinctrl.h>
#include <of_gpio.h>
#include <of_device.h>
+#include <pbl/i2c.h>
#include <io.h>
#include <i2c/i2c.h>
@@ -93,6 +94,7 @@ enum lpi2c_imx_pincfg {
struct lpi2c_imx_struct {
struct i2c_adapter adapter;
+ struct pbl_i2c pbl_i2c;
int num_clks;
struct clk_bulk_data *clks;
void __iomem *base;
@@ -104,6 +106,7 @@ struct lpi2c_imx_struct {
unsigned int txfifosize;
unsigned int rxfifosize;
enum lpi2c_imx_mode mode;
+ unsigned long clk_rate;
};
static void lpi2c_imx_intctrl(struct lpi2c_imx_struct *lpi2c_imx,
@@ -115,7 +118,7 @@ static void lpi2c_imx_intctrl(struct lpi2c_imx_struct *lpi2c_imx,
static int lpi2c_imx_bus_busy(struct lpi2c_imx_struct *lpi2c_imx)
{
unsigned int temp;
- u64 start = get_time_ns();
+ unsigned int timeout = 500000;
while (1) {
temp = readl(lpi2c_imx->base + LPI2C_MSR);
@@ -129,7 +132,8 @@ static int lpi2c_imx_bus_busy(struct lpi2c_imx_struct *lpi2c_imx)
if (temp & (MSR_BBF | MSR_MBF))
break;
- if (is_timeout(start, 500 * MSECOND)) {
+ udelay(1);
+ if (!timeout--) {
dev_dbg(&lpi2c_imx->adapter.dev, "bus not work\n");
return -ETIMEDOUT;
}
@@ -176,7 +180,7 @@ static int lpi2c_imx_start(struct lpi2c_imx_struct *lpi2c_imx,
static void lpi2c_imx_stop(struct lpi2c_imx_struct *lpi2c_imx)
{
unsigned int temp;
- u64 start = get_time_ns();
+ unsigned int timeout = 500000;
writel(GEN_STOP << 8, lpi2c_imx->base + LPI2C_MTDR);
@@ -185,7 +189,8 @@ static void lpi2c_imx_stop(struct lpi2c_imx_struct *lpi2c_imx)
if (temp & MSR_SDF)
break;
- if (is_timeout(start, 500 * MSECOND)) {
+ udelay(1);
+ if (!timeout--) {
dev_dbg(&lpi2c_imx->adapter.dev, "stop timeout\n");
break;
}
@@ -197,23 +202,19 @@ static void lpi2c_imx_stop(struct lpi2c_imx_struct *lpi2c_imx)
static int lpi2c_imx_config(struct lpi2c_imx_struct *lpi2c_imx)
{
u8 prescale, filt, sethold, datavd;
- unsigned int clk_rate, clk_cycle, clkhi, clklo;
+ unsigned int clk_cycle, clkhi, clklo;
enum lpi2c_imx_pincfg pincfg;
unsigned int temp;
lpi2c_imx_set_mode(lpi2c_imx);
- clk_rate = clk_get_rate(lpi2c_imx->clks[0].clk);
- if (!clk_rate)
- return -EINVAL;
-
if (lpi2c_imx->mode == HS || lpi2c_imx->mode == ULTRA_FAST)
filt = 0;
else
filt = 2;
for (prescale = 0; prescale <= 7; prescale++) {
- clk_cycle = clk_rate / ((1 << prescale) * lpi2c_imx->bitrate)
+ clk_cycle = lpi2c_imx->clk_rate / ((1 << prescale) * lpi2c_imx->bitrate)
- 3 - (filt >> 1);
clkhi = DIV_ROUND_UP(clk_cycle, I2C_CLK_RATIO + 1);
clklo = clk_cycle - clkhi;
@@ -287,7 +288,7 @@ static int lpi2c_imx_master_disable(struct lpi2c_imx_struct *lpi2c_imx)
static int lpi2c_imx_txfifo_empty(struct lpi2c_imx_struct *lpi2c_imx)
{
u32 txcnt;
- u64 start = get_time_ns();
+ unsigned int timeout = 500000;
do {
txcnt = readl(lpi2c_imx->base + LPI2C_MFSR) & 0xff;
@@ -297,7 +298,8 @@ static int lpi2c_imx_txfifo_empty(struct lpi2c_imx_struct *lpi2c_imx)
return -EIO;
}
- if (is_timeout(start, 500 * MSECOND)) {
+ udelay(1);
+ if (!timeout--) {
dev_dbg(&lpi2c_imx->adapter.dev, "txfifo empty timeout\n");
return -ETIMEDOUT;
}
@@ -329,12 +331,13 @@ static void lpi2c_imx_set_rx_watermark(struct lpi2c_imx_struct *lpi2c_imx)
static int lpi2c_imx_write_txfifo(struct lpi2c_imx_struct *lpi2c_imx)
{
unsigned int data, remaining;
- uint64_t start = get_time_ns();
+ unsigned int timeout = 100000;;
do {
u32 cnt = readl(lpi2c_imx->base + LPI2C_MFSR) & 0xff;
if (cnt == lpi2c_imx->txfifosize) {
- if (is_timeout(start, 100 * MSECOND))
+ udelay(1);
+ if (!timeout--)
return -EIO;
continue;
}
@@ -352,12 +355,13 @@ static int lpi2c_imx_read_rxfifo(struct lpi2c_imx_struct *lpi2c_imx)
{
unsigned int remaining;
unsigned int data;
- uint64_t start = get_time_ns();
+ unsigned int timeout = 100000;;
do {
data = readl(lpi2c_imx->base + LPI2C_MRDR);
if (data & MRDR_RXEMPTY) {
- if (is_timeout(start, 100 * MSECOND))
+ udelay(1);
+ if (!timeout--)
return -EIO;
continue;
}
@@ -453,6 +457,36 @@ disable:
return (result < 0) ? result : num;
}
+#ifdef __PBL__
+
+static int lpi2c_pbl_imx_xfer(struct pbl_i2c *lpi2c, struct i2c_msg *msgs, int num)
+{
+ struct lpi2c_imx_struct *lpi2c_imx = container_of(lpi2c, struct lpi2c_imx_struct, pbl_i2c);
+
+ return lpi2c_imx_xfer(&lpi2c_imx->adapter, msgs, num);
+}
+
+struct pbl_i2c *imx93_i2c_early_init(void __iomem *regs)
+{
+ static struct lpi2c_imx_struct lpi2c;
+ u32 temp;
+
+ lpi2c.base = regs;
+
+ temp = readl(lpi2c.base + LPI2C_PARAM);
+ printf("%s: 0x%08x\n", __func__, temp);
+ lpi2c.txfifosize = 1 << (temp & 0x0f);
+ lpi2c.rxfifosize = 1 << ((temp >> 8) & 0x0f);
+ lpi2c.bitrate = 100000;
+ lpi2c.clk_rate = 24000000;
+
+ lpi2c.pbl_i2c.xfer = lpi2c_pbl_imx_xfer;
+
+ return &lpi2c.pbl_i2c;
+}
+
+#else
+
static const struct of_device_id lpi2c_imx_of_match[] = {
{ .compatible = "fsl,imx7ulp-lpi2c" },
{ },
@@ -493,6 +527,8 @@ static int lpi2c_imx_probe(struct device *dev)
if (ret)
return ret;
+ lpi2c_imx->clk_rate = clk_get_rate(lpi2c_imx->clks[0].clk);
+
temp = readl(lpi2c_imx->base + LPI2C_PARAM);
lpi2c_imx->txfifosize = 1 << (temp & 0x0f);
lpi2c_imx->rxfifosize = 1 << ((temp >> 8) & 0x0f);
@@ -513,6 +549,8 @@ static struct driver lpi2c_imx_driver = {
};
coredevice_platform_driver(lpi2c_imx_driver);
+#endif
+
MODULE_AUTHOR("Gao Pan <pandy.gao@nxp.com>");
MODULE_DESCRIPTION("I2C adapter driver for LPI2C bus");
MODULE_LICENSE("GPL");
diff --git a/drivers/nvmem/imx-ocotp-ele.c b/drivers/nvmem/imx-ocotp-ele.c
index b748a30b1f..9708d3f4bb 100644
--- a/drivers/nvmem/imx-ocotp-ele.c
+++ b/drivers/nvmem/imx-ocotp-ele.c
@@ -9,6 +9,10 @@
#include <linux/nvmem-provider.h>
#include <linux/regmap.h>
#include <mach/imx/ele.h>
+#include <machine_id.h>
+
+#define UNIQUE_ID_NUM 4
+#define OCOTP_UNIQUE_ID(n) (0xc0 + (n) * 4)
enum fuse_type {
FUSE_FSB = 1,
@@ -113,7 +117,20 @@ static int imx_ocotp_cell_pp(void *context, const char *id, unsigned int offset,
static struct regmap_bus imx_ocotp_regmap_bus = {
.reg_read = imx_ocotp_reg_read,
-};
+};
+
+static void imx_ocotp_set_unique_machine_id(struct imx_ocotp_priv *priv)
+{
+ uint32_t unique_id_parts[UNIQUE_ID_NUM];
+ int i;
+
+ for (i = 0; i < UNIQUE_ID_NUM; i++)
+ if (imx_ocotp_reg_read(priv, OCOTP_UNIQUE_ID(i),
+ &unique_id_parts[i]))
+ return;
+
+ machine_id_set_hashable(unique_id_parts, sizeof(unique_id_parts));
+}
static int imx_ele_ocotp_probe(struct device *dev)
{
@@ -145,6 +162,9 @@ static int imx_ele_ocotp_probe(struct device *dev)
if (IS_ERR(priv->map))
return PTR_ERR(priv->map);
+ if (IS_ENABLED(CONFIG_MACHINE_ID))
+ imx_ocotp_set_unique_machine_id(priv);
+
nvmem = nvmem_regmap_register_with_pp(priv->map, "imx-ocotp",
imx_ocotp_cell_pp);
if (IS_ERR(nvmem))
diff --git a/drivers/nvmem/ocotp.c b/drivers/nvmem/ocotp.c
index 28c121152e..c282efefa8 100644
--- a/drivers/nvmem/ocotp.c
+++ b/drivers/nvmem/ocotp.c
@@ -53,12 +53,12 @@
#define OCOTP_READ_CTRL 0x30
#define OCOTP_READ_FUSE_DATA 0x40
-#define MX7_OCOTP_DATA0 0x20
-#define MX7_OCOTP_DATA1 0x30
-#define MX7_OCOTP_DATA2 0x40
-#define MX7_OCOTP_DATA3 0x50
-#define MX7_OCOTP_READ_CTRL 0x60
-#define MX7_OCOTP_READ_FUSE_DATA0 0x70
+#define MX7_OCOTP_DATA0 0x20
+#define MX7_OCOTP_DATA1 0x30
+#define MX7_OCOTP_DATA2 0x40
+#define MX7_OCOTP_DATA3 0x50
+#define MX7_OCOTP_READ_CTRL 0x60
+#define MX7_OCOTP_READ_FUSE_DATA0 0x70
#define MX7_OCOTP_READ_FUSE_DATA1 0x80
#define MX7_OCOTP_READ_FUSE_DATA2 0x90
#define MX7_OCOTP_READ_FUSE_DATA3 0xA0
@@ -67,27 +67,29 @@
#define DEF_STROBE_PROG 10000 /* IPG clocks */
/* OCOTP Registers bits and masks */
-#define OCOTP_CTRL_ADDR GENMASK(7, 0)
-#define OCOTP_CTRL_BUSY BIT(8)
-#define OCOTP_CTRL_ERROR BIT(9)
-#define OCOTP_CTRL_RELOAD_SHADOWS BIT(10)
-#define OCOTP_CTRL_WR_UNLOCK GENMASK(31, 16)
-#define OCOTP_CTRL_WR_UNLOCK_KEY 0x3E77
-
-/* i.MX8MP OCOTP CTRL has a different layout. See RM Rev.1 06/2021 Section
- * 6.3.5.1.2.4 */
-#define OCOTP_CTRL_ADDR_8MP GENMASK(8, 0)
-#define OCOTP_CTRL_BUSY_8MP BIT(9)
-#define OCOTP_CTRL_ERROR_8MP BIT(10)
-#define OCOTP_CTRL_RELOAD_SHADOWS_8MP BIT(11)
-#define OCOTP_CTRL_WR_UNLOCK_8MP GENMASK(31, 16)
-
-#define OCOTP_TIMING_STROBE_READ GENMASK(21, 16)
-#define OCOTP_TIMING_RELAX GENMASK(15, 12)
-#define OCOTP_TIMING_STROBE_PROG GENMASK(11, 0)
-#define OCOTP_TIMING_WAIT GENMASK(27, 22)
-
-#define OCOTP_READ_CTRL_READ_FUSE BIT(1)
+#define OCOTP_CTRL_ADDR GENMASK(7, 0)
+#define OCOTP_CTRL_BUSY BIT(8)
+#define OCOTP_CTRL_ERROR BIT(9)
+#define OCOTP_CTRL_RELOAD_SHADOWS BIT(10)
+#define OCOTP_CTRL_WR_UNLOCK GENMASK(31, 16)
+#define OCOTP_CTRL_WR_UNLOCK_KEY 0x3E77
+
+/*
+ * i.MX8MP OCOTP CTRL has a different layout. See RM Rev.1 06/2021
+ * Section 6.3.5.1.2.4
+ */
+#define OCOTP_CTRL_ADDR_8MP GENMASK(8, 0)
+#define OCOTP_CTRL_BUSY_8MP BIT(9)
+#define OCOTP_CTRL_ERROR_8MP BIT(10)
+#define OCOTP_CTRL_RELOAD_SHADOWS_8MP BIT(11)
+#define OCOTP_CTRL_WR_UNLOCK_8MP GENMASK(31, 16)
+
+#define OCOTP_TIMING_STROBE_READ GENMASK(21, 16)
+#define OCOTP_TIMING_RELAX GENMASK(15, 12)
+#define OCOTP_TIMING_STROBE_PROG GENMASK(11, 0)
+#define OCOTP_TIMING_WAIT GENMASK(27, 22)
+
+#define OCOTP_READ_CTRL_READ_FUSE BIT(1)
#define OCOTP_OFFSET_TO_ADDR(o) (OCOTP_OFFSET_TO_INDEX(o) * 4)
@@ -273,11 +275,11 @@ static int imx6_ocotp_prepare(struct ocotp_priv *priv)
static int imx6_fuse_read_addr(struct ocotp_priv *priv, u32 addr, u32 *pdata)
{
+ const u32 bm_ctrl_error = priv->data->ctrl->bm_error;
+ const u32 bm_ctrl_addr = priv->data->ctrl->bm_addr;
+ const u32 bm_ctrl_wr_unlock = priv->data->ctrl->bm_wr_unlock;
u32 ctrl_reg;
int ret;
- u32 bm_ctrl_error = priv->data->ctrl->bm_error;
- u32 bm_ctrl_addr = priv->data->ctrl->bm_addr;
- u32 bm_ctrl_wr_unlock = priv->data->ctrl->bm_wr_unlock;
writel(bm_ctrl_error, priv->base + OCOTP_CTRL_CLR);
@@ -302,13 +304,13 @@ static int imx6_fuse_read_addr(struct ocotp_priv *priv, u32 addr, u32 *pdata)
static int imx7_fuse_read_addr(struct ocotp_priv *priv, u32 index, u32 *pdata)
{
+ const u32 bm_ctrl_error = priv->data->ctrl->bm_error;
+ const u32 bm_ctrl_addr = priv->data->ctrl->bm_addr;
+ const u32 bm_ctrl_wr_unlock = priv->data->ctrl->bm_wr_unlock;
u32 ctrl_reg;
u32 bank_addr;
u16 word;
int ret;
- u32 bm_ctrl_error = priv->data->ctrl->bm_error;
- u32 bm_ctrl_addr = priv->data->ctrl->bm_addr;
- u32 bm_ctrl_wr_unlock = priv->data->ctrl->bm_wr_unlock;
word = index & 0x3;
bank_addr = index >> 2;
@@ -389,10 +391,10 @@ static int imx_ocotp_reg_read(void *ctx, unsigned int reg, unsigned int *val)
static void imx_ocotp_clear_unlock(struct ocotp_priv *priv, u32 index)
{
+ const u32 bm_ctrl_error = priv->data->ctrl->bm_error;
+ const u32 bm_ctrl_addr = priv->data->ctrl->bm_addr;
+ const u32 bm_ctrl_wr_unlock = priv->data->ctrl->bm_wr_unlock;
u32 ctrl_reg;
- u32 bm_ctrl_error = priv->data->ctrl->bm_error;
- u32 bm_ctrl_addr = priv->data->ctrl->bm_addr;
- u32 bm_ctrl_wr_unlock = priv->data->ctrl->bm_wr_unlock;
writel(bm_ctrl_error, priv->base + OCOTP_CTRL_CLR);
@@ -406,8 +408,8 @@ static void imx_ocotp_clear_unlock(struct ocotp_priv *priv, u32 index)
static int imx6_fuse_blow_addr(struct ocotp_priv *priv, u32 index, u32 value)
{
+ const u32 bm_ctrl_error = priv->data->ctrl->bm_error;
int ret;
- u32 bm_ctrl_error = priv->data->ctrl->bm_error;
imx_ocotp_clear_unlock(priv, index);
@@ -472,7 +474,7 @@ static int imx7_fuse_blow_addr(struct ocotp_priv *priv, u32 index, u32 value)
static int imx6_ocotp_reload_shadow(struct ocotp_priv *priv)
{
- u32 bm_ctrl_reload_shadows = priv->data->ctrl->bm_reload_shadows;
+ const u32 bm_ctrl_reload_shadows = priv->data->ctrl->bm_reload_shadows;
dev_info(&priv->dev, "reloading shadow registers...\n");
writel(bm_ctrl_reload_shadows, priv->base + OCOTP_CTRL_SET);
@@ -484,8 +486,8 @@ static int imx6_ocotp_reload_shadow(struct ocotp_priv *priv)
static int imx6_ocotp_blow_one_u32(struct ocotp_priv *priv, u32 index, u32 data,
u32 *pfused_value)
{
+ const u32 bm_ctrl_error = priv->data->ctrl->bm_error;
int ret;
- u32 bm_ctrl_error = priv->data->ctrl->bm_error;
ret = imx6_ocotp_prepare(priv);
if (ret) {
diff --git a/drivers/soc/imx/Makefile b/drivers/soc/imx/Makefile
index 54d8002a58..65b2677f7a 100644
--- a/drivers/soc/imx/Makefile
+++ b/drivers/soc/imx/Makefile
@@ -1,2 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
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 0000000000..48b42a1b17
--- /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(imx_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");