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