xref: /linux/drivers/soc/imx/soc-imx9.c (revision 1fd1dc41724319406b0aff221a352a400b0ddfc5)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright 2024 NXP
4  */
5 
6 #include <linux/arm-smccc.h>
7 #include <linux/init.h>
8 #include <linux/module.h>
9 #include <linux/of.h>
10 #include <linux/platform_device.h>
11 #include <linux/slab.h>
12 #include <linux/sys_soc.h>
13 
14 #define IMX_SIP_GET_SOC_INFO	0xc2000006
15 #define SOC_ID(x)		(((x) & 0xFF) ? ((x) & 0xFFFF) >> 4 : ((x) & 0xFFFF) >> 8)
16 #define SOC_REV_MAJOR(x)	((((x) >> 28) & 0xF) - 0x9)
17 #define SOC_REV_MINOR(x)	(((x) >> 24) & 0xF)
18 
19 static int imx9_soc_probe(struct platform_device *pdev)
20 {
21 	struct device *dev = &pdev->dev;
22 	struct soc_device_attribute *attr;
23 	struct arm_smccc_res res;
24 	struct soc_device *sdev;
25 	u32 soc_id, rev_major, rev_minor;
26 	u64 uid127_64, uid63_0;
27 	int err;
28 
29 	attr = devm_kzalloc(dev, sizeof(*attr), GFP_KERNEL);
30 	if (!attr)
31 		return -ENOMEM;
32 
33 	err = of_property_read_string(of_root, "model", &attr->machine);
34 	if (err)
35 		return dev_err_probe(dev, err, "%s: missing model property\n", __func__);
36 
37 	attr->family = devm_kasprintf(dev, GFP_KERNEL, "Freescale i.MX");
38 
39 	/*
40 	 * Retrieve the soc id, rev & uid info:
41 	 * res.a1[31:16]: soc revision;
42 	 * res.a1[15:0]: soc id;
43 	 * res.a2: uid[127:64];
44 	 * res.a3: uid[63:0];
45 	 */
46 	arm_smccc_smc(IMX_SIP_GET_SOC_INFO, 0, 0, 0, 0, 0, 0, 0, &res);
47 	if (res.a0 != SMCCC_RET_SUCCESS)
48 		return dev_err_probe(dev, -EINVAL, "%s: SMC failed: 0x%lx\n", __func__, res.a0);
49 
50 	soc_id = SOC_ID(res.a1);
51 	rev_major = SOC_REV_MAJOR(res.a1);
52 	rev_minor = SOC_REV_MINOR(res.a1);
53 
54 	attr->soc_id = devm_kasprintf(dev, GFP_KERNEL, "i.MX%2x", soc_id);
55 	attr->revision = devm_kasprintf(dev, GFP_KERNEL, "%d.%d", rev_major, rev_minor);
56 
57 	uid127_64 = res.a2;
58 	uid63_0 = res.a3;
59 	attr->serial_number = devm_kasprintf(dev, GFP_KERNEL, "%016llx%016llx", uid127_64, uid63_0);
60 
61 	sdev = soc_device_register(attr);
62 	if (IS_ERR(sdev))
63 		return dev_err_probe(dev, PTR_ERR(sdev),
64 				     "%s failed to register SoC as a device\n", __func__);
65 
66 	return 0;
67 }
68 
69 static __maybe_unused const struct of_device_id imx9_soc_match[] = {
70 	{ .compatible = "fsl,imx93", },
71 	{ .compatible = "fsl,imx94", },
72 	{ .compatible = "fsl,imx95", },
73 	{ .compatible = "fsl,imx952", },
74 	{ }
75 };
76 
77 #define IMX_SOC_DRIVER	"imx9-soc"
78 
79 static struct platform_driver imx9_soc_driver = {
80 	.probe = imx9_soc_probe,
81 	.driver = {
82 		.name = IMX_SOC_DRIVER,
83 	},
84 };
85 
86 static int __init imx9_soc_init(void)
87 {
88 	int ret;
89 	struct platform_device *pdev;
90 
91 	/* No match means it is not an i.MX 9 series SoC, do nothing. */
92 	if (!of_match_node(imx9_soc_match, of_root))
93 		return 0;
94 
95 	ret = platform_driver_register(&imx9_soc_driver);
96 	if (ret) {
97 		pr_err("failed to register imx9_soc platform driver: %d\n", ret);
98 		return ret;
99 	}
100 
101 	pdev = platform_device_register_simple(IMX_SOC_DRIVER, -1, NULL, 0);
102 	if (IS_ERR(pdev)) {
103 		pr_err("failed to register imx9_soc platform device: %ld\n", PTR_ERR(pdev));
104 		platform_driver_unregister(&imx9_soc_driver);
105 		return PTR_ERR(pdev);
106 	}
107 
108 	return 0;
109 }
110 device_initcall(imx9_soc_init);
111 
112 MODULE_AUTHOR("NXP");
113 MODULE_DESCRIPTION("NXP i.MX9 SoC");
114 MODULE_LICENSE("GPL");
115