1 // SPDX-License-Identifier: GPL-2.0+
4 * Author: Dong Aisheng <aisheng.dong@nxp.com>
6 * Implementation of the SCU IPC functions using MUs (client side).
10 #include <linux/err.h>
11 #include <linux/firmware/imx/ipc.h>
12 #include <linux/firmware/imx/sci.h>
13 #include <linux/interrupt.h>
14 #include <linux/irq.h>
15 #include <linux/kernel.h>
16 #include <linux/mailbox_client.h>
17 #include <linux/module.h>
18 #include <linux/mutex.h>
20 #include <linux/of_platform.h>
21 #include <linux/platform_device.h>
23 #define SCU_MU_CHAN_NUM 8
24 #define MAX_RX_TIMEOUT (msecs_to_jiffies(3000))
27 struct imx_sc_ipc
*sc_ipc
;
29 struct mbox_client cl
;
32 struct completion tx_done
;
36 /* SCU uses 4 Tx and 4 Rx channels */
37 struct imx_sc_chan chans
[SCU_MU_CHAN_NUM
];
40 struct completion done
;
43 /* temporarily store the SCU msg */
50 * This type is used to indicate error response for most functions.
52 enum imx_sc_error_codes
{
53 IMX_SC_ERR_NONE
= 0, /* Success */
54 IMX_SC_ERR_VERSION
= 1, /* Incompatible API version */
55 IMX_SC_ERR_CONFIG
= 2, /* Configuration error */
56 IMX_SC_ERR_PARM
= 3, /* Bad parameter */
57 IMX_SC_ERR_NOACCESS
= 4, /* Permission error (no access) */
58 IMX_SC_ERR_LOCKED
= 5, /* Permission error (locked) */
59 IMX_SC_ERR_UNAVAILABLE
= 6, /* Unavailable (out of resources) */
60 IMX_SC_ERR_NOTFOUND
= 7, /* Not found */
61 IMX_SC_ERR_NOPOWER
= 8, /* No power */
62 IMX_SC_ERR_IPC
= 9, /* Generic IPC error */
63 IMX_SC_ERR_BUSY
= 10, /* Resource is currently busy/active */
64 IMX_SC_ERR_FAIL
= 11, /* General I/O failure */
68 static int imx_sc_linux_errmap
[IMX_SC_ERR_LAST
] = {
69 0, /* IMX_SC_ERR_NONE */
70 -EINVAL
, /* IMX_SC_ERR_VERSION */
71 -EINVAL
, /* IMX_SC_ERR_CONFIG */
72 -EINVAL
, /* IMX_SC_ERR_PARM */
73 -EACCES
, /* IMX_SC_ERR_NOACCESS */
74 -EACCES
, /* IMX_SC_ERR_LOCKED */
75 -ERANGE
, /* IMX_SC_ERR_UNAVAILABLE */
76 -EEXIST
, /* IMX_SC_ERR_NOTFOUND */
77 -EPERM
, /* IMX_SC_ERR_NOPOWER */
78 -EPIPE
, /* IMX_SC_ERR_IPC */
79 -EBUSY
, /* IMX_SC_ERR_BUSY */
80 -EIO
, /* IMX_SC_ERR_FAIL */
83 static struct imx_sc_ipc
*imx_sc_ipc_handle
;
85 static inline int imx_sc_to_linux_errno(int errno
)
87 if (errno
>= IMX_SC_ERR_NONE
&& errno
< IMX_SC_ERR_LAST
)
88 return imx_sc_linux_errmap
[errno
];
93 * Get the default handle used by SCU
95 int imx_scu_get_handle(struct imx_sc_ipc
**ipc
)
97 if (!imx_sc_ipc_handle
)
100 *ipc
= imx_sc_ipc_handle
;
103 EXPORT_SYMBOL(imx_scu_get_handle
);
105 /* Callback called when the word of a message is ack-ed, eg read by SCU */
106 static void imx_scu_tx_done(struct mbox_client
*cl
, void *mssg
, int r
)
108 struct imx_sc_chan
*sc_chan
= container_of(cl
, struct imx_sc_chan
, cl
);
110 complete(&sc_chan
->tx_done
);
113 static void imx_scu_rx_callback(struct mbox_client
*c
, void *msg
)
115 struct imx_sc_chan
*sc_chan
= container_of(c
, struct imx_sc_chan
, cl
);
116 struct imx_sc_ipc
*sc_ipc
= sc_chan
->sc_ipc
;
117 struct imx_sc_rpc_msg
*hdr
;
122 dev_warn(sc_ipc
->dev
, "unexpected rx idx %d 0x%08x, ignore!\n",
123 sc_chan
->idx
, *data
);
127 if (sc_ipc
->fast_ipc
) {
129 sc_ipc
->rx_size
= hdr
->size
;
130 sc_ipc
->msg
[0] = *data
++;
132 for (i
= 1; i
< sc_ipc
->rx_size
; i
++)
133 sc_ipc
->msg
[i
] = *data
++;
135 complete(&sc_ipc
->done
);
140 if (sc_chan
->idx
== 0) {
142 sc_ipc
->rx_size
= hdr
->size
;
143 dev_dbg(sc_ipc
->dev
, "msg rx size %u\n", sc_ipc
->rx_size
);
144 if (sc_ipc
->rx_size
> 4)
145 dev_warn(sc_ipc
->dev
, "RPC does not support receiving over 4 words: %u\n",
149 sc_ipc
->msg
[sc_chan
->idx
] = *data
;
152 dev_dbg(sc_ipc
->dev
, "mu %u msg %u 0x%x\n", sc_chan
->idx
,
153 sc_ipc
->count
, *data
);
155 if ((sc_ipc
->rx_size
!= 0) && (sc_ipc
->count
== sc_ipc
->rx_size
))
156 complete(&sc_ipc
->done
);
159 static int imx_scu_ipc_write(struct imx_sc_ipc
*sc_ipc
, void *msg
)
161 struct imx_sc_rpc_msg hdr
= *(struct imx_sc_rpc_msg
*)msg
;
162 struct imx_sc_chan
*sc_chan
;
169 if (hdr
.size
> IMX_SC_RPC_MAX_MSG
)
172 dev_dbg(sc_ipc
->dev
, "RPC SVC %u FUNC %u SIZE %u\n", hdr
.svc
,
175 size
= sc_ipc
->fast_ipc
? 1 : hdr
.size
;
176 for (i
= 0; i
< size
; i
++) {
177 sc_chan
= &sc_ipc
->chans
[i
% 4];
180 * SCU requires that all messages words are written
181 * sequentially but linux MU driver implements multiple
182 * independent channels for each register so ordering between
183 * different channels must be ensured by SCU API interface.
185 * Wait for tx_done before every send to ensure that no
186 * queueing happens at the mailbox channel level.
188 if (!sc_ipc
->fast_ipc
) {
189 wait_for_completion(&sc_chan
->tx_done
);
190 reinit_completion(&sc_chan
->tx_done
);
193 ret
= mbox_send_message(sc_chan
->ch
, &data
[i
]);
202 * RPC command/response
204 int imx_scu_call_rpc(struct imx_sc_ipc
*sc_ipc
, void *msg
, bool have_resp
)
206 uint8_t saved_svc
, saved_func
;
207 struct imx_sc_rpc_msg
*hdr
;
210 if (WARN_ON(!sc_ipc
|| !msg
))
213 mutex_lock(&sc_ipc
->lock
);
214 reinit_completion(&sc_ipc
->done
);
218 saved_svc
= ((struct imx_sc_rpc_msg
*)msg
)->svc
;
219 saved_func
= ((struct imx_sc_rpc_msg
*)msg
)->func
;
222 ret
= imx_scu_ipc_write(sc_ipc
, msg
);
224 dev_err(sc_ipc
->dev
, "RPC send msg failed: %d\n", ret
);
229 if (!wait_for_completion_timeout(&sc_ipc
->done
,
231 dev_err(sc_ipc
->dev
, "RPC send msg timeout\n");
232 mutex_unlock(&sc_ipc
->lock
);
236 /* response status is stored in hdr->func field */
240 * Some special SCU firmware APIs do NOT have return value
241 * in hdr->func, but they do have response data, those special
242 * APIs are defined as void function in SCU firmware, so they
243 * should be treated as return success always.
245 if ((saved_svc
== IMX_SC_RPC_SVC_MISC
) &&
246 (saved_func
== IMX_SC_MISC_FUNC_UNIQUE_ID
||
247 saved_func
== IMX_SC_MISC_FUNC_GET_BUTTON_STATUS
))
253 mutex_unlock(&sc_ipc
->lock
);
255 dev_dbg(sc_ipc
->dev
, "RPC SVC done\n");
257 return imx_sc_to_linux_errno(ret
);
259 EXPORT_SYMBOL(imx_scu_call_rpc
);
261 static int imx_scu_probe(struct platform_device
*pdev
)
263 struct device
*dev
= &pdev
->dev
;
264 struct imx_sc_ipc
*sc_ipc
;
265 struct imx_sc_chan
*sc_chan
;
266 struct mbox_client
*cl
;
268 struct of_phandle_args args
;
273 sc_ipc
= devm_kzalloc(dev
, sizeof(*sc_ipc
), GFP_KERNEL
);
277 ret
= of_parse_phandle_with_args(pdev
->dev
.of_node
, "mboxes",
278 "#mbox-cells", 0, &args
);
282 sc_ipc
->fast_ipc
= of_device_is_compatible(args
.np
, "fsl,imx8-mu-scu");
284 num_channel
= sc_ipc
->fast_ipc
? 2 : SCU_MU_CHAN_NUM
;
285 for (i
= 0; i
< num_channel
; i
++) {
286 if (i
< num_channel
/ 2)
287 chan_name
= kasprintf(GFP_KERNEL
, "tx%d", i
);
289 chan_name
= kasprintf(GFP_KERNEL
, "rx%d",
290 i
- num_channel
/ 2);
295 sc_chan
= &sc_ipc
->chans
[i
];
298 cl
->tx_block
= false;
299 cl
->knows_txdone
= true;
300 cl
->rx_callback
= imx_scu_rx_callback
;
302 if (!sc_ipc
->fast_ipc
) {
303 /* Initial tx_done completion as "done" */
304 cl
->tx_done
= imx_scu_tx_done
;
305 init_completion(&sc_chan
->tx_done
);
306 complete(&sc_chan
->tx_done
);
309 sc_chan
->sc_ipc
= sc_ipc
;
310 sc_chan
->idx
= i
% (num_channel
/ 2);
311 sc_chan
->ch
= mbox_request_channel_byname(cl
, chan_name
);
312 if (IS_ERR(sc_chan
->ch
)) {
313 ret
= PTR_ERR(sc_chan
->ch
);
314 dev_err_probe(dev
, ret
, "Failed to request mbox chan %s\n",
320 dev_dbg(dev
, "request mbox chan %s\n", chan_name
);
321 /* chan_name is not used anymore by framework */
326 mutex_init(&sc_ipc
->lock
);
327 init_completion(&sc_ipc
->done
);
329 imx_sc_ipc_handle
= sc_ipc
;
331 ret
= imx_scu_soc_init(dev
);
333 dev_warn(dev
, "failed to initialize SoC info: %d\n", ret
);
335 ret
= imx_scu_enable_general_irq_channel(dev
);
338 "failed to enable general irq channel: %d\n", ret
);
340 dev_info(dev
, "NXP i.MX SCU Initialized\n");
342 return devm_of_platform_populate(dev
);
345 static const struct of_device_id imx_scu_match
[] = {
346 { .compatible
= "fsl,imx-scu", },
350 static struct platform_driver imx_scu_driver
= {
353 .of_match_table
= imx_scu_match
,
355 .probe
= imx_scu_probe
,
358 static int __init
imx_scu_driver_init(void)
360 return platform_driver_register(&imx_scu_driver
);
362 subsys_initcall_sync(imx_scu_driver_init
);
364 MODULE_AUTHOR("Dong Aisheng <aisheng.dong@nxp.com>");
365 MODULE_DESCRIPTION("IMX SCU firmware protocol driver");
366 MODULE_LICENSE("GPL v2");