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 static const char *imx_scu_soc_name(u32 id) 82 { 83 switch (id) { 84 case 0x1: 85 return "i.MX8QM"; 86 case 0x2: 87 return "i.MX8QXP"; 88 case 0xe: 89 return "i.MX8DXL"; 90 default: 91 break; 92 } 93 94 return "NULL"; 95 } 96 97 int imx_scu_soc_init(struct device *dev) 98 { 99 struct soc_device_attribute *soc_dev_attr; 100 struct soc_device *soc_dev; 101 int id, ret; 102 u64 uid = 0; 103 u32 val; 104 105 ret = imx_scu_get_handle(&imx_sc_soc_ipc_handle); 106 if (ret) 107 return ret; 108 109 soc_dev_attr = devm_kzalloc(dev, sizeof(*soc_dev_attr), 110 GFP_KERNEL); 111 if (!soc_dev_attr) 112 return -ENOMEM; 113 114 soc_dev_attr->family = "Freescale i.MX"; 115 116 ret = of_property_read_string(of_root, 117 "model", 118 &soc_dev_attr->machine); 119 if (ret) 120 return ret; 121 122 id = imx_scu_soc_id(); 123 if (id < 0) 124 return -EINVAL; 125 126 ret = imx_scu_soc_uid(&uid); 127 if (ret < 0) 128 return -EINVAL; 129 130 /* format soc_id value passed from SCU firmware */ 131 val = id & 0x1f; 132 soc_dev_attr->soc_id = imx_scu_soc_name(val); 133 134 /* format revision value passed from SCU firmware */ 135 val = (id >> 5) & 0xf; 136 val = (((val >> 2) + 1) << 4) | (val & 0x3); 137 soc_dev_attr->revision = devm_kasprintf(dev, GFP_KERNEL, "%d.%d", 138 (val >> 4) & 0xf, val & 0xf); 139 if (!soc_dev_attr->revision) 140 return -ENOMEM; 141 142 soc_dev_attr->serial_number = devm_kasprintf(dev, GFP_KERNEL, 143 "%016llX", uid); 144 if (!soc_dev_attr->serial_number) 145 return -ENOMEM; 146 147 soc_dev = soc_device_register(soc_dev_attr); 148 if (IS_ERR(soc_dev)) 149 return PTR_ERR(soc_dev); 150 151 return 0; 152 } 153