Linux-libre 5.3.12-gnu
[librecmc/linux-libre.git] / drivers / soc / imx / soc-imx-scu.c
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;
28 } __packed;
29
30 static int imx_scu_soc_id(void)
31 {
32         struct imx_sc_msg_misc_get_soc_id msg;
33         struct imx_sc_rpc_msg *hdr = &msg.hdr;
34         int ret;
35
36         hdr->ver = IMX_SC_RPC_VERSION;
37         hdr->svc = IMX_SC_RPC_SVC_MISC;
38         hdr->func = IMX_SC_MISC_FUNC_GET_CONTROL;
39         hdr->size = 3;
40
41         msg.data.req.control = IMX_SC_C_ID;
42         msg.data.req.resource = IMX_SC_R_SYSTEM;
43
44         ret = imx_scu_call_rpc(soc_ipc_handle, &msg, true);
45         if (ret) {
46                 pr_err("%s: get soc info failed, ret %d\n", __func__, ret);
47                 return ret;
48         }
49
50         return msg.data.resp.id;
51 }
52
53 static int imx_scu_soc_probe(struct platform_device *pdev)
54 {
55         struct soc_device_attribute *soc_dev_attr;
56         struct soc_device *soc_dev;
57         int id, ret;
58         u32 val;
59
60         ret = imx_scu_get_handle(&soc_ipc_handle);
61         if (ret)
62                 return ret;
63
64         soc_dev_attr = devm_kzalloc(&pdev->dev, sizeof(*soc_dev_attr),
65                                     GFP_KERNEL);
66         if (!soc_dev_attr)
67                 return -ENOMEM;
68
69         soc_dev_attr->family = "Freescale i.MX";
70
71         ret = of_property_read_string(of_root,
72                                       "model",
73                                       &soc_dev_attr->machine);
74         if (ret)
75                 return ret;
76
77         id = imx_scu_soc_id();
78         if (id < 0)
79                 return -EINVAL;
80
81         /* format soc_id value passed from SCU firmware */
82         val = id & 0x1f;
83         soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "0x%x", val);
84         if (!soc_dev_attr->soc_id)
85                 return -ENOMEM;
86
87         /* format revision value passed from SCU firmware */
88         val = (id >> 5) & 0xf;
89         val = (((val >> 2) + 1) << 4) | (val & 0x3);
90         soc_dev_attr->revision = kasprintf(GFP_KERNEL,
91                                            "%d.%d",
92                                            (val >> 4) & 0xf,
93                                            val & 0xf);
94         if (!soc_dev_attr->revision) {
95                 ret = -ENOMEM;
96                 goto free_soc_id;
97         }
98
99         soc_dev = soc_device_register(soc_dev_attr);
100         if (IS_ERR(soc_dev)) {
101                 ret = PTR_ERR(soc_dev);
102                 goto free_revision;
103         }
104
105         return 0;
106
107 free_revision:
108         kfree(soc_dev_attr->revision);
109 free_soc_id:
110         kfree(soc_dev_attr->soc_id);
111         return ret;
112 }
113
114 static struct platform_driver imx_scu_soc_driver = {
115         .driver = {
116                 .name = IMX_SCU_SOC_DRIVER_NAME,
117         },
118         .probe = imx_scu_soc_probe,
119 };
120
121 static int __init imx_scu_soc_init(void)
122 {
123         struct platform_device *pdev;
124         struct device_node *np;
125         int ret;
126
127         np = of_find_compatible_node(NULL, NULL, "fsl,imx-scu");
128         if (!np)
129                 return -ENODEV;
130
131         of_node_put(np);
132
133         ret = platform_driver_register(&imx_scu_soc_driver);
134         if (ret)
135                 return ret;
136
137         pdev = platform_device_register_simple(IMX_SCU_SOC_DRIVER_NAME,
138                                                -1, NULL, 0);
139         if (IS_ERR(pdev))
140                 platform_driver_unregister(&imx_scu_soc_driver);
141
142         return PTR_ERR_OR_ZERO(pdev);
143 }
144 device_initcall(imx_scu_soc_init);