|
| 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"); |
0 commit comments