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>
19 #include <linux/of_platform.h>
20 #include <linux/platform_device.h>
22 #define SCU_MU_CHAN_NUM 8
23 #define MAX_RX_TIMEOUT (msecs_to_jiffies(30))
26 struct imx_sc_ipc
*sc_ipc
;
28 struct mbox_client cl
;
31 struct completion tx_done
;
35 /* SCU uses 4 Tx and 4 Rx channels */
36 struct imx_sc_chan chans
[SCU_MU_CHAN_NUM
];
39 struct completion done
;
42 /* temporarily store the SCU msg */
49 * This type is used to indicate error response for most functions.
51 enum imx_sc_error_codes
{
52 IMX_SC_ERR_NONE
= 0, /* Success */
53 IMX_SC_ERR_VERSION
= 1, /* Incompatible API version */
54 IMX_SC_ERR_CONFIG
= 2, /* Configuration error */
55 IMX_SC_ERR_PARM
= 3, /* Bad parameter */
56 IMX_SC_ERR_NOACCESS
= 4, /* Permission error (no access) */
57 IMX_SC_ERR_LOCKED
= 5, /* Permission error (locked) */
58 IMX_SC_ERR_UNAVAILABLE
= 6, /* Unavailable (out of resources) */
59 IMX_SC_ERR_NOTFOUND
= 7, /* Not found */
60 IMX_SC_ERR_NOPOWER
= 8, /* No power */
61 IMX_SC_ERR_IPC
= 9, /* Generic IPC error */
62 IMX_SC_ERR_BUSY
= 10, /* Resource is currently busy/active */
63 IMX_SC_ERR_FAIL
= 11, /* General I/O failure */
67 static int imx_sc_linux_errmap
[IMX_SC_ERR_LAST
] = {
68 0, /* IMX_SC_ERR_NONE */
69 -EINVAL
, /* IMX_SC_ERR_VERSION */
70 -EINVAL
, /* IMX_SC_ERR_CONFIG */
71 -EINVAL
, /* IMX_SC_ERR_PARM */
72 -EACCES
, /* IMX_SC_ERR_NOACCESS */
73 -EACCES
, /* IMX_SC_ERR_LOCKED */
74 -ERANGE
, /* IMX_SC_ERR_UNAVAILABLE */
75 -EEXIST
, /* IMX_SC_ERR_NOTFOUND */
76 -EPERM
, /* IMX_SC_ERR_NOPOWER */
77 -EPIPE
, /* IMX_SC_ERR_IPC */
78 -EBUSY
, /* IMX_SC_ERR_BUSY */
79 -EIO
, /* IMX_SC_ERR_FAIL */
82 static struct imx_sc_ipc
*imx_sc_ipc_handle
;
84 static inline int imx_sc_to_linux_errno(int errno
)
86 if (errno
>= IMX_SC_ERR_NONE
&& errno
< IMX_SC_ERR_LAST
)
87 return imx_sc_linux_errmap
[errno
];
92 * Get the default handle used by SCU
94 int imx_scu_get_handle(struct imx_sc_ipc
**ipc
)
96 if (!imx_sc_ipc_handle
)
99 *ipc
= imx_sc_ipc_handle
;
102 EXPORT_SYMBOL(imx_scu_get_handle
);
104 /* Callback called when the word of a message is ack-ed, eg read by SCU */
105 static void imx_scu_tx_done(struct mbox_client
*cl
, void *mssg
, int r
)
107 struct imx_sc_chan
*sc_chan
= container_of(cl
, struct imx_sc_chan
, cl
);
109 complete(&sc_chan
->tx_done
);
112 static void imx_scu_rx_callback(struct mbox_client
*c
, void *msg
)
114 struct imx_sc_chan
*sc_chan
= container_of(c
, struct imx_sc_chan
, cl
);
115 struct imx_sc_ipc
*sc_ipc
= sc_chan
->sc_ipc
;
116 struct imx_sc_rpc_msg
*hdr
;
121 dev_warn(sc_ipc
->dev
, "unexpected rx idx %d 0x%08x, ignore!\n",
122 sc_chan
->idx
, *data
);
126 if (sc_ipc
->fast_ipc
) {
128 sc_ipc
->rx_size
= hdr
->size
;
129 sc_ipc
->msg
[0] = *data
++;
131 for (i
= 1; i
< sc_ipc
->rx_size
; i
++)
132 sc_ipc
->msg
[i
] = *data
++;
134 complete(&sc_ipc
->done
);
139 if (sc_chan
->idx
== 0) {
141 sc_ipc
->rx_size
= hdr
->size
;
142 dev_dbg(sc_ipc
->dev
, "msg rx size %u\n", sc_ipc
->rx_size
);
143 if (sc_ipc
->rx_size
> 4)
144 dev_warn(sc_ipc
->dev
, "RPC does not support receiving over 4 words: %u\n",
148 sc_ipc
->msg
[sc_chan
->idx
] = *data
;
151 dev_dbg(sc_ipc
->dev
, "mu %u msg %u 0x%x\n", sc_chan
->idx
,
152 sc_ipc
->count
, *data
);
154 if ((sc_ipc
->rx_size
!= 0) && (sc_ipc
->count
== sc_ipc
->rx_size
))
155 complete(&sc_ipc
->done
);
158 static int imx_scu_ipc_write(struct imx_sc_ipc
*sc_ipc
, void *msg
)
160 struct imx_sc_rpc_msg hdr
= *(struct imx_sc_rpc_msg
*)msg
;
161 struct imx_sc_chan
*sc_chan
;
168 if (hdr
.size
> IMX_SC_RPC_MAX_MSG
)
171 dev_dbg(sc_ipc
->dev
, "RPC SVC %u FUNC %u SIZE %u\n", hdr
.svc
,
174 size
= sc_ipc
->fast_ipc
? 1 : hdr
.size
;
175 for (i
= 0; i
< size
; i
++) {
176 sc_chan
= &sc_ipc
->chans
[i
% 4];
179 * SCU requires that all messages words are written
180 * sequentially but linux MU driver implements multiple
181 * independent channels for each register so ordering between
182 * different channels must be ensured by SCU API interface.
184 * Wait for tx_done before every send to ensure that no
185 * queueing happens at the mailbox channel level.
187 if (!sc_ipc
->fast_ipc
) {
188 wait_for_completion(&sc_chan
->tx_done
);
189 reinit_completion(&sc_chan
->tx_done
);
192 ret
= mbox_send_message(sc_chan
->ch
, &data
[i
]);
201 * RPC command/response
203 int imx_scu_call_rpc(struct imx_sc_ipc
*sc_ipc
, void *msg
, bool have_resp
)
205 uint8_t saved_svc
, saved_func
;
206 struct imx_sc_rpc_msg
*hdr
;
209 if (WARN_ON(!sc_ipc
|| !msg
))
212 mutex_lock(&sc_ipc
->lock
);
213 reinit_completion(&sc_ipc
->done
);
217 saved_svc
= ((struct imx_sc_rpc_msg
*)msg
)->svc
;
218 saved_func
= ((struct imx_sc_rpc_msg
*)msg
)->func
;
221 ret
= imx_scu_ipc_write(sc_ipc
, msg
);
223 dev_err(sc_ipc
->dev
, "RPC send msg failed: %d\n", ret
);
228 if (!wait_for_completion_timeout(&sc_ipc
->done
,
230 dev_err(sc_ipc
->dev
, "RPC send msg timeout\n");
231 mutex_unlock(&sc_ipc
->lock
);
235 /* response status is stored in hdr->func field */
239 * Some special SCU firmware APIs do NOT have return value
240 * in hdr->func, but they do have response data, those special
241 * APIs are defined as void function in SCU firmware, so they
242 * should be treated as return success always.
244 if ((saved_svc
== IMX_SC_RPC_SVC_MISC
) &&
245 (saved_func
== IMX_SC_MISC_FUNC_UNIQUE_ID
||
246 saved_func
== IMX_SC_MISC_FUNC_GET_BUTTON_STATUS
))
252 mutex_unlock(&sc_ipc
->lock
);
254 dev_dbg(sc_ipc
->dev
, "RPC SVC done\n");
256 return imx_sc_to_linux_errno(ret
);
258 EXPORT_SYMBOL(imx_scu_call_rpc
);
260 static int imx_scu_probe(struct platform_device
*pdev
)
262 struct device
*dev
= &pdev
->dev
;
263 struct imx_sc_ipc
*sc_ipc
;
264 struct imx_sc_chan
*sc_chan
;
265 struct mbox_client
*cl
;
267 struct of_phandle_args args
;
272 sc_ipc
= devm_kzalloc(dev
, sizeof(*sc_ipc
), GFP_KERNEL
);
276 ret
= of_parse_phandle_with_args(pdev
->dev
.of_node
, "mboxes",
277 "#mbox-cells", 0, &args
);
281 sc_ipc
->fast_ipc
= of_device_is_compatible(args
.np
, "fsl,imx8-mu-scu");
283 num_channel
= sc_ipc
->fast_ipc
? 2 : SCU_MU_CHAN_NUM
;
284 for (i
= 0; i
< num_channel
; i
++) {
285 if (i
< num_channel
/ 2)
286 chan_name
= kasprintf(GFP_KERNEL
, "tx%d", i
);
288 chan_name
= kasprintf(GFP_KERNEL
, "rx%d",
289 i
- num_channel
/ 2);
294 sc_chan
= &sc_ipc
->chans
[i
];
297 cl
->tx_block
= false;
298 cl
->knows_txdone
= true;
299 cl
->rx_callback
= imx_scu_rx_callback
;
301 if (!sc_ipc
->fast_ipc
) {
302 /* Initial tx_done completion as "done" */
303 cl
->tx_done
= imx_scu_tx_done
;
304 init_completion(&sc_chan
->tx_done
);
305 complete(&sc_chan
->tx_done
);
308 sc_chan
->sc_ipc
= sc_ipc
;
309 sc_chan
->idx
= i
% (num_channel
/ 2);
310 sc_chan
->ch
= mbox_request_channel_byname(cl
, chan_name
);
311 if (IS_ERR(sc_chan
->ch
)) {
312 ret
= PTR_ERR(sc_chan
->ch
);
313 if (ret
!= -EPROBE_DEFER
)
314 dev_err(dev
, "Failed to request mbox chan %s ret %d\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
,
357 builtin_platform_driver(imx_scu_driver
);
359 MODULE_AUTHOR("Dong Aisheng <aisheng.dong@nxp.com>");
360 MODULE_DESCRIPTION("IMX SCU firmware protocol driver");
361 MODULE_LICENSE("GPL v2");