1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright 2019 NXP. 4 */ 5 6 #include <dt-bindings/firmware/imx/rsrc.h> 7 #include <linux/firmware/imx/sci.h> 8 #include <linux/slab.h> 9 #include <linux/sys_soc.h> 10 #include <linux/platform_device.h> 11 #include <linux/of.h> 12 13 static struct imx_sc_ipc *imx_sc_soc_ipc_handle; 14 15 struct imx_sc_msg_misc_get_soc_id { 16 struct imx_sc_rpc_msg hdr; 17 union { 18 struct { 19 u32 control; 20 u16 resource; 21 } __packed req; 22 struct { 23 u32 id; 24 } resp; 25 } data; 26 } __packed __aligned(4); 27 28 struct imx_sc_msg_misc_get_soc_uid { 29 struct imx_sc_rpc_msg hdr; 30 u32 uid_low; 31 u32 uid_high; 32 } __packed; 33 34 static int imx_scu_soc_uid(u64 *soc_uid) 35 { 36 struct imx_sc_msg_misc_get_soc_uid msg; 37 struct imx_sc_rpc_msg *hdr = &msg.hdr; 38 int ret; 39 40 hdr->ver = IMX_SC_RPC_VERSION; 41 hdr->svc = IMX_SC_RPC_SVC_MISC; 42 hdr->func = IMX_SC_MISC_FUNC_UNIQUE_ID; 43 hdr->size = 1; 44 45 ret = imx_scu_call_rpc(imx_sc_soc_ipc_handle, &msg, true); 46 if (ret) { 47 pr_err("%s: get soc uid failed, ret %d\n", __func__, ret); 48 return ret; 49 } 50 51 *soc_uid = msg.uid_high; 52 *soc_uid <<= 32; 53 *soc_uid |= msg.uid_low; 54 55 return 0; 56 } 57 58 static int imx_scu_soc_id(void) 59 { 60 struct imx_sc_msg_misc_get_soc_id msg; 61 struct imx_sc_rpc_msg *hdr = &msg.hdr; 62 int ret; 63 64 hdr->ver = IMX_SC_RPC_VERSION; 65 hdr->svc = IMX_SC_RPC_SVC_MISC; 66 hdr->func = IMX_SC_MISC_FUNC_GET_CONTROL; 67 hdr->size = 3; 68 69 msg.data.req.control = IMX_SC_C_ID; 70 msg.data.req.resource = IMX_SC_R_SYSTEM; 71 72 ret = imx_scu_call_rpc(imx_sc_soc_ipc_handle, &msg, true); 73 if (ret) { 74 pr_err("%s: get soc info failed, ret %d\n", __func__, ret); 75 return ret; 76 } 77 78 return msg.data.resp.id; 79 } 80 81 int imx_scu_soc_init(struct device *dev) 82 { 83 struct soc_device_attribute *soc_dev_attr; 84 struct soc_device *soc_dev; 85 int id, ret; 86 u64 uid = 0; 87 u32 val; 88 89 ret = imx_scu_get_handle(&imx_sc_soc_ipc_handle); 90 if (ret) 91 return ret; 92 93 soc_dev_attr = devm_kzalloc(dev, sizeof(*soc_dev_attr), 94 GFP_KERNEL); 95 if (!soc_dev_attr) 96 return -ENOMEM; 97 98 soc_dev_attr->family = "Freescale i.MX"; 99 100 ret = of_property_read_string(of_root, 101 "model", 102 &soc_dev_attr->machine); 103 if (ret) 104 return ret; 105 106 id = imx_scu_soc_id(); 107 if (id < 0) 108 return -EINVAL; 109 110 ret = imx_scu_soc_uid(&uid); 111 if (ret < 0) 112 return -EINVAL; 113 114 /* format soc_id value passed from SCU firmware */ 115 val = id & 0x1f; 116 soc_dev_attr->soc_id = devm_kasprintf(dev, GFP_KERNEL, "0x%x", val); 117 if (!soc_dev_attr->soc_id) 118 return -ENOMEM; 119 120 /* format revision value passed from SCU firmware */ 121 val = (id >> 5) & 0xf; 122 val = (((val >> 2) + 1) << 4) | (val & 0x3); 123 soc_dev_attr->revision = devm_kasprintf(dev, GFP_KERNEL, "%d.%d", 124 (val >> 4) & 0xf, val & 0xf); 125 if (!soc_dev_attr->revision) 126 return -ENOMEM; 127 128 soc_dev_attr->serial_number = devm_kasprintf(dev, GFP_KERNEL, 129 "%016llX", uid); 130 if (!soc_dev_attr->serial_number) 131 return -ENOMEM; 132 133 soc_dev = soc_device_register(soc_dev_attr); 134 if (IS_ERR(soc_dev)) 135 return PTR_ERR(soc_dev); 136 137 return 0; 138 } 139