ARM: imx: Add serial number support for i.MX6/7 SoCs
authorAnson Huang <Anson.Huang@nxp.com>
Mon, 28 Oct 2019 09:16:01 +0000 (17:16 +0800)
committerShawn Guo <shawnguo@kernel.org>
Mon, 28 Oct 2019 13:28:55 +0000 (21:28 +0800)
i.MX6/7 SoCs have a 64-bit SoC unique ID stored in OCOTP,
it can be used as SoC serial number, add this support for
i.MX6Q/6DL/6SL/6SX/6SLL/6UL/6ULL/6ULZ/7D, see below example
on i.MX6Q:

root@imx6qpdlsolox:~# cat /sys/devices/soc0/serial_number
240F31D4E1FDFCA7

Signed-off-by: Anson Huang <Anson.Huang@nxp.com>
Signed-off-by: Shawn Guo <shawnguo@kernel.org>
arch/arm/mach-imx/cpu.c

index 0b137ee..d811803 100644 (file)
@@ -1,15 +1,20 @@
 // SPDX-License-Identifier: GPL-2.0
 #include <linux/err.h>
+#include <linux/mfd/syscon.h>
 #include <linux/module.h>
 #include <linux/io.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
+#include <linux/regmap.h>
 #include <linux/slab.h>
 #include <linux/sys_soc.h>
 
 #include "hardware.h"
 #include "common.h"
 
+#define OCOTP_UID_H    0x420
+#define OCOTP_UID_L    0x410
+
 unsigned int __mxc_cpu_type;
 static unsigned int imx_soc_revision;
 
@@ -76,9 +81,13 @@ void __init imx_aips_allow_unprivileged_access(
 struct device * __init imx_soc_device_init(void)
 {
        struct soc_device_attribute *soc_dev_attr;
+       const char *ocotp_compat = NULL;
        struct soc_device *soc_dev;
        struct device_node *root;
+       struct regmap *ocotp;
        const char *soc_id;
+       u64 soc_uid = 0;
+       u32 val;
        int ret;
 
        soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
@@ -119,30 +128,39 @@ struct device * __init imx_soc_device_init(void)
                soc_id = "i.MX53";
                break;
        case MXC_CPU_IMX6SL:
+               ocotp_compat = "fsl,imx6sl-ocotp";
                soc_id = "i.MX6SL";
                break;
        case MXC_CPU_IMX6DL:
+               ocotp_compat = "fsl,imx6q-ocotp";
                soc_id = "i.MX6DL";
                break;
        case MXC_CPU_IMX6SX:
+               ocotp_compat = "fsl,imx6sx-ocotp";
                soc_id = "i.MX6SX";
                break;
        case MXC_CPU_IMX6Q:
+               ocotp_compat = "fsl,imx6q-ocotp";
                soc_id = "i.MX6Q";
                break;
        case MXC_CPU_IMX6UL:
+               ocotp_compat = "fsl,imx6ul-ocotp";
                soc_id = "i.MX6UL";
                break;
        case MXC_CPU_IMX6ULL:
+               ocotp_compat = "fsl,imx6ul-ocotp";
                soc_id = "i.MX6ULL";
                break;
        case MXC_CPU_IMX6ULZ:
+               ocotp_compat = "fsl,imx6ul-ocotp";
                soc_id = "i.MX6ULZ";
                break;
        case MXC_CPU_IMX6SLL:
+               ocotp_compat = "fsl,imx6sll-ocotp";
                soc_id = "i.MX6SLL";
                break;
        case MXC_CPU_IMX7D:
+               ocotp_compat = "fsl,imx7d-ocotp";
                soc_id = "i.MX7D";
                break;
        case MXC_CPU_IMX7ULP:
@@ -153,18 +171,36 @@ struct device * __init imx_soc_device_init(void)
        }
        soc_dev_attr->soc_id = soc_id;
 
+       if (ocotp_compat) {
+               ocotp = syscon_regmap_lookup_by_compatible(ocotp_compat);
+               if (IS_ERR(ocotp))
+                       pr_err("%s: failed to find %s regmap!\n", __func__, ocotp_compat);
+
+               regmap_read(ocotp, OCOTP_UID_H, &val);
+               soc_uid = val;
+               regmap_read(ocotp, OCOTP_UID_L, &val);
+               soc_uid <<= 32;
+               soc_uid |= val;
+       }
+
        soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%d.%d",
                                           (imx_soc_revision >> 4) & 0xf,
                                           imx_soc_revision & 0xf);
        if (!soc_dev_attr->revision)
                goto free_soc;
 
+       soc_dev_attr->serial_number = kasprintf(GFP_KERNEL, "%016llX", soc_uid);
+       if (!soc_dev_attr->serial_number)
+               goto free_rev;
+
        soc_dev = soc_device_register(soc_dev_attr);
        if (IS_ERR(soc_dev))
-               goto free_rev;
+               goto free_serial_number;
 
        return soc_device_to_device(soc_dev);
 
+free_serial_number:
+       kfree(soc_dev_attr->serial_number);
 free_rev:
        kfree(soc_dev_attr->revision);
 free_soc: