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) & 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 soc_device_attribute *attr; 22 struct arm_smccc_res res; 23 struct soc_device *sdev; 24 u32 soc_id, rev_major, rev_minor; 25 u64 uid127_64, uid63_0; 26 int err; 27 28 attr = kzalloc(sizeof(*attr), GFP_KERNEL); 29 if (!attr) 30 return -ENOMEM; 31 32 err = of_property_read_string(of_root, "model", &attr->machine); 33 if (err) { 34 pr_err("%s: missing model property: %d\n", __func__, err); 35 goto attr; 36 } 37 38 attr->family = kasprintf(GFP_KERNEL, "Freescale i.MX"); 39 40 /* 41 * Retrieve the soc id, rev & uid info: 42 * res.a1[31:16]: soc revision; 43 * res.a1[15:0]: soc id; 44 * res.a2: uid[127:64]; 45 * res.a3: uid[63:0]; 46 */ 47 arm_smccc_smc(IMX_SIP_GET_SOC_INFO, 0, 0, 0, 0, 0, 0, 0, &res); 48 if (res.a0 != SMCCC_RET_SUCCESS) { 49 pr_err("%s: SMC failed: 0x%lx\n", __func__, res.a0); 50 err = -EINVAL; 51 goto family; 52 } 53 54 soc_id = SOC_ID(res.a1); 55 rev_major = SOC_REV_MAJOR(res.a1); 56 rev_minor = SOC_REV_MINOR(res.a1); 57 58 attr->soc_id = kasprintf(GFP_KERNEL, "i.MX%2x", soc_id); 59 attr->revision = kasprintf(GFP_KERNEL, "%d.%d", rev_major, rev_minor); 60 61 uid127_64 = res.a2; 62 uid63_0 = res.a3; 63 attr->serial_number = kasprintf(GFP_KERNEL, "%016llx%016llx", uid127_64, uid63_0); 64 65 sdev = soc_device_register(attr); 66 if (IS_ERR(sdev)) { 67 err = PTR_ERR(sdev); 68 pr_err("%s failed to register SoC as a device: %d\n", __func__, err); 69 goto serial_number; 70 } 71 72 return 0; 73 74 serial_number: 75 kfree(attr->serial_number); 76 kfree(attr->revision); 77 kfree(attr->soc_id); 78 family: 79 kfree(attr->family); 80 attr: 81 kfree(attr); 82 return err; 83 } 84 85 static __maybe_unused const struct of_device_id imx9_soc_match[] = { 86 { .compatible = "fsl,imx93", }, 87 { .compatible = "fsl,imx95", }, 88 { } 89 }; 90 91 #define IMX_SOC_DRIVER "imx9-soc" 92 93 static struct platform_driver imx9_soc_driver = { 94 .probe = imx9_soc_probe, 95 .driver = { 96 .name = IMX_SOC_DRIVER, 97 }, 98 }; 99 100 static int __init imx9_soc_init(void) 101 { 102 int ret; 103 struct platform_device *pdev; 104 105 /* No match means it is not an i.MX 9 series SoC, do nothing. */ 106 if (!of_match_node(imx9_soc_match, of_root)) 107 return 0; 108 109 ret = platform_driver_register(&imx9_soc_driver); 110 if (ret) { 111 pr_err("failed to register imx9_soc platform driver: %d\n", ret); 112 return ret; 113 } 114 115 pdev = platform_device_register_simple(IMX_SOC_DRIVER, -1, NULL, 0); 116 if (IS_ERR(pdev)) { 117 pr_err("failed to register imx9_soc platform device: %ld\n", PTR_ERR(pdev)); 118 platform_driver_unregister(&imx9_soc_driver); 119 return PTR_ERR(pdev); 120 } 121 122 return 0; 123 } 124 device_initcall(imx9_soc_init); 125 126 MODULE_AUTHOR("NXP"); 127 MODULE_DESCRIPTION("NXP i.MX9 SoC"); 128 MODULE_LICENSE("GPL"); 129