Commit | Line | Data |
---|---|---|
15f5276a AH |
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 | #define IMX_SCU_SOC_DRIVER_NAME "imx-scu-soc" | |
14 | ||
15 | static struct imx_sc_ipc *soc_ipc_handle; | |
16 | ||
17 | struct imx_sc_msg_misc_get_soc_id { | |
18 | struct imx_sc_rpc_msg hdr; | |
19 | union { | |
20 | struct { | |
21 | u32 control; | |
22 | u16 resource; | |
23 | } __packed req; | |
24 | struct { | |
25 | u32 id; | |
26 | } resp; | |
27 | } data; | |
f10e58a5 | 28 | } __packed __aligned(4); |
15f5276a | 29 | |
73feb4d0 AH |
30 | struct imx_sc_msg_misc_get_soc_uid { |
31 | struct imx_sc_rpc_msg hdr; | |
32 | u32 uid_low; | |
33 | u32 uid_high; | |
34 | } __packed; | |
35 | ||
7ae399b7 | 36 | static int imx_scu_soc_uid(u64 *soc_uid) |
73feb4d0 AH |
37 | { |
38 | struct imx_sc_msg_misc_get_soc_uid msg; | |
39 | struct imx_sc_rpc_msg *hdr = &msg.hdr; | |
73feb4d0 AH |
40 | int ret; |
41 | ||
42 | hdr->ver = IMX_SC_RPC_VERSION; | |
43 | hdr->svc = IMX_SC_RPC_SVC_MISC; | |
44 | hdr->func = IMX_SC_MISC_FUNC_UNIQUE_ID; | |
45 | hdr->size = 1; | |
46 | ||
30ca9b04 | 47 | ret = imx_scu_call_rpc(soc_ipc_handle, &msg, true); |
73feb4d0 AH |
48 | if (ret) { |
49 | pr_err("%s: get soc uid failed, ret %d\n", __func__, ret); | |
50 | return ret; | |
51 | } | |
52 | ||
7ae399b7 AH |
53 | *soc_uid = msg.uid_high; |
54 | *soc_uid <<= 32; | |
55 | *soc_uid |= msg.uid_low; | |
73feb4d0 | 56 | |
7ae399b7 | 57 | return 0; |
73feb4d0 AH |
58 | } |
59 | ||
15f5276a AH |
60 | static int imx_scu_soc_id(void) |
61 | { | |
62 | struct imx_sc_msg_misc_get_soc_id msg; | |
63 | struct imx_sc_rpc_msg *hdr = &msg.hdr; | |
64 | int ret; | |
65 | ||
66 | hdr->ver = IMX_SC_RPC_VERSION; | |
67 | hdr->svc = IMX_SC_RPC_SVC_MISC; | |
68 | hdr->func = IMX_SC_MISC_FUNC_GET_CONTROL; | |
69 | hdr->size = 3; | |
70 | ||
71 | msg.data.req.control = IMX_SC_C_ID; | |
72 | msg.data.req.resource = IMX_SC_R_SYSTEM; | |
73 | ||
74 | ret = imx_scu_call_rpc(soc_ipc_handle, &msg, true); | |
75 | if (ret) { | |
76 | pr_err("%s: get soc info failed, ret %d\n", __func__, ret); | |
77 | return ret; | |
78 | } | |
79 | ||
80 | return msg.data.resp.id; | |
81 | } | |
82 | ||
83 | static int imx_scu_soc_probe(struct platform_device *pdev) | |
84 | { | |
85 | struct soc_device_attribute *soc_dev_attr; | |
86 | struct soc_device *soc_dev; | |
87 | int id, ret; | |
7ae399b7 | 88 | u64 uid = 0; |
15f5276a AH |
89 | u32 val; |
90 | ||
91 | ret = imx_scu_get_handle(&soc_ipc_handle); | |
92 | if (ret) | |
93 | return ret; | |
94 | ||
95 | soc_dev_attr = devm_kzalloc(&pdev->dev, sizeof(*soc_dev_attr), | |
96 | GFP_KERNEL); | |
97 | if (!soc_dev_attr) | |
98 | return -ENOMEM; | |
99 | ||
100 | soc_dev_attr->family = "Freescale i.MX"; | |
101 | ||
102 | ret = of_property_read_string(of_root, | |
103 | "model", | |
104 | &soc_dev_attr->machine); | |
105 | if (ret) | |
106 | return ret; | |
107 | ||
108 | id = imx_scu_soc_id(); | |
109 | if (id < 0) | |
110 | return -EINVAL; | |
111 | ||
7ae399b7 AH |
112 | ret = imx_scu_soc_uid(&uid); |
113 | if (ret < 0) | |
114 | return -EINVAL; | |
115 | ||
15f5276a AH |
116 | /* format soc_id value passed from SCU firmware */ |
117 | val = id & 0x1f; | |
118 | soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "0x%x", val); | |
119 | if (!soc_dev_attr->soc_id) | |
120 | return -ENOMEM; | |
121 | ||
122 | /* format revision value passed from SCU firmware */ | |
123 | val = (id >> 5) & 0xf; | |
124 | val = (((val >> 2) + 1) << 4) | (val & 0x3); | |
125 | soc_dev_attr->revision = kasprintf(GFP_KERNEL, | |
126 | "%d.%d", | |
127 | (val >> 4) & 0xf, | |
128 | val & 0xf); | |
129 | if (!soc_dev_attr->revision) { | |
130 | ret = -ENOMEM; | |
131 | goto free_soc_id; | |
132 | } | |
133 | ||
7ae399b7 AH |
134 | soc_dev_attr->serial_number = kasprintf(GFP_KERNEL, "%016llX", uid); |
135 | if (!soc_dev_attr->serial_number) { | |
136 | ret = -ENOMEM; | |
137 | goto free_revision; | |
138 | } | |
139 | ||
15f5276a AH |
140 | soc_dev = soc_device_register(soc_dev_attr); |
141 | if (IS_ERR(soc_dev)) { | |
142 | ret = PTR_ERR(soc_dev); | |
7ae399b7 | 143 | goto free_serial_number; |
15f5276a AH |
144 | } |
145 | ||
146 | return 0; | |
147 | ||
7ae399b7 AH |
148 | free_serial_number: |
149 | kfree(soc_dev_attr->serial_number); | |
15f5276a AH |
150 | free_revision: |
151 | kfree(soc_dev_attr->revision); | |
152 | free_soc_id: | |
153 | kfree(soc_dev_attr->soc_id); | |
154 | return ret; | |
155 | } | |
156 | ||
157 | static struct platform_driver imx_scu_soc_driver = { | |
158 | .driver = { | |
159 | .name = IMX_SCU_SOC_DRIVER_NAME, | |
160 | }, | |
161 | .probe = imx_scu_soc_probe, | |
162 | }; | |
163 | ||
164 | static int __init imx_scu_soc_init(void) | |
165 | { | |
166 | struct platform_device *pdev; | |
167 | struct device_node *np; | |
168 | int ret; | |
169 | ||
170 | np = of_find_compatible_node(NULL, NULL, "fsl,imx-scu"); | |
171 | if (!np) | |
172 | return -ENODEV; | |
173 | ||
174 | of_node_put(np); | |
175 | ||
176 | ret = platform_driver_register(&imx_scu_soc_driver); | |
177 | if (ret) | |
178 | return ret; | |
179 | ||
180 | pdev = platform_device_register_simple(IMX_SCU_SOC_DRIVER_NAME, | |
181 | -1, NULL, 0); | |
182 | if (IS_ERR(pdev)) | |
183 | platform_driver_unregister(&imx_scu_soc_driver); | |
184 | ||
185 | return PTR_ERR_OR_ZERO(pdev); | |
186 | } | |
187 | device_initcall(imx_scu_soc_init); |