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/types.h>
12 #include <linux/firmware/imx/ipc.h>
13 #include <linux/firmware/imx/sci.h>
14 #include <linux/interrupt.h>
15 #include <linux/irq.h>
16 #include <linux/kernel.h>
17 #include <linux/mailbox_client.h>
18 #include <linux/module.h>
19 #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(30))
27 struct imx_sc_ipc
*sc_ipc
;
29 struct mbox_client cl
;
35 /* SCU uses 4 Tx and 4 Rx channels */
36 struct imx_sc_chan chans
[SCU_MU_CHAN_NUM
];
39 struct completion done
;
41 /* temporarily store the SCU msg */
48 * This type is used to indicate error response for most functions.
50 enum imx_sc_error_codes
{
51 IMX_SC_ERR_NONE
= 0, /* Success */
52 IMX_SC_ERR_VERSION
= 1, /* Incompatible API version */
53 IMX_SC_ERR_CONFIG
= 2, /* Configuration error */
54 IMX_SC_ERR_PARM
= 3, /* Bad parameter */
55 IMX_SC_ERR_NOACCESS
= 4, /* Permission error (no access) */
56 IMX_SC_ERR_LOCKED
= 5, /* Permission error (locked) */
57 IMX_SC_ERR_UNAVAILABLE
= 6, /* Unavailable (out of resources) */
58 IMX_SC_ERR_NOTFOUND
= 7, /* Not found */
59 IMX_SC_ERR_NOPOWER
= 8, /* No power */
60 IMX_SC_ERR_IPC
= 9, /* Generic IPC error */
61 IMX_SC_ERR_BUSY
= 10, /* Resource is currently busy/active */
62 IMX_SC_ERR_FAIL
= 11, /* General I/O failure */
66 static int imx_sc_linux_errmap
[IMX_SC_ERR_LAST
] = {
67 0, /* IMX_SC_ERR_NONE */
68 -EINVAL
, /* IMX_SC_ERR_VERSION */
69 -EINVAL
, /* IMX_SC_ERR_CONFIG */
70 -EINVAL
, /* IMX_SC_ERR_PARM */
71 -EACCES
, /* IMX_SC_ERR_NOACCESS */
72 -EACCES
, /* IMX_SC_ERR_LOCKED */
73 -ERANGE
, /* IMX_SC_ERR_UNAVAILABLE */
74 -EEXIST
, /* IMX_SC_ERR_NOTFOUND */
75 -EPERM
, /* IMX_SC_ERR_NOPOWER */
76 -EPIPE
, /* IMX_SC_ERR_IPC */
77 -EBUSY
, /* IMX_SC_ERR_BUSY */
78 -EIO
, /* IMX_SC_ERR_FAIL */
81 static struct imx_sc_ipc
*imx_sc_ipc_handle
;
83 static inline int imx_sc_to_linux_errno(int errno
)
85 if (errno
>= IMX_SC_ERR_NONE
&& errno
< IMX_SC_ERR_LAST
)
86 return imx_sc_linux_errmap
[errno
];
91 * Get the default handle used by SCU
93 int imx_scu_get_handle(struct imx_sc_ipc
**ipc
)
95 if (!imx_sc_ipc_handle
)
98 *ipc
= imx_sc_ipc_handle
;
101 EXPORT_SYMBOL(imx_scu_get_handle
);
103 static void imx_scu_rx_callback(struct mbox_client
*c
, void *msg
)
105 struct imx_sc_chan
*sc_chan
= container_of(c
, struct imx_sc_chan
, cl
);
106 struct imx_sc_ipc
*sc_ipc
= sc_chan
->sc_ipc
;
107 struct imx_sc_rpc_msg
*hdr
;
111 dev_warn(sc_ipc
->dev
, "unexpected rx idx %d 0x%08x, ignore!\n",
112 sc_chan
->idx
, *data
);
116 if (sc_chan
->idx
== 0) {
118 sc_ipc
->rx_size
= hdr
->size
;
119 dev_dbg(sc_ipc
->dev
, "msg rx size %u\n", sc_ipc
->rx_size
);
120 if (sc_ipc
->rx_size
> 4)
121 dev_warn(sc_ipc
->dev
, "RPC does not support receiving over 4 words: %u\n",
125 sc_ipc
->msg
[sc_chan
->idx
] = *data
;
128 dev_dbg(sc_ipc
->dev
, "mu %u msg %u 0x%x\n", sc_chan
->idx
,
129 sc_ipc
->count
, *data
);
131 if ((sc_ipc
->rx_size
!= 0) && (sc_ipc
->count
== sc_ipc
->rx_size
))
132 complete(&sc_ipc
->done
);
135 static int imx_scu_ipc_write(struct imx_sc_ipc
*sc_ipc
, void *msg
)
137 struct imx_sc_rpc_msg
*hdr
= msg
;
138 struct imx_sc_chan
*sc_chan
;
144 if (hdr
->size
> IMX_SC_RPC_MAX_MSG
)
147 dev_dbg(sc_ipc
->dev
, "RPC SVC %u FUNC %u SIZE %u\n", hdr
->svc
,
148 hdr
->func
, hdr
->size
);
150 for (i
= 0; i
< hdr
->size
; i
++) {
151 sc_chan
= &sc_ipc
->chans
[i
% 4];
152 ret
= mbox_send_message(sc_chan
->ch
, &data
[i
]);
161 * RPC command/response
163 int imx_scu_call_rpc(struct imx_sc_ipc
*sc_ipc
, void *msg
, bool have_resp
)
165 uint8_t saved_svc
, saved_func
;
166 struct imx_sc_rpc_msg
*hdr
;
169 if (WARN_ON(!sc_ipc
|| !msg
))
172 mutex_lock(&sc_ipc
->lock
);
173 reinit_completion(&sc_ipc
->done
);
177 saved_svc
= ((struct imx_sc_rpc_msg
*)msg
)->svc
;
178 saved_func
= ((struct imx_sc_rpc_msg
*)msg
)->func
;
181 ret
= imx_scu_ipc_write(sc_ipc
, msg
);
183 dev_err(sc_ipc
->dev
, "RPC send msg failed: %d\n", ret
);
188 if (!wait_for_completion_timeout(&sc_ipc
->done
,
190 dev_err(sc_ipc
->dev
, "RPC send msg timeout\n");
191 mutex_unlock(&sc_ipc
->lock
);
195 /* response status is stored in hdr->func field */
199 * Some special SCU firmware APIs do NOT have return value
200 * in hdr->func, but they do have response data, those special
201 * APIs are defined as void function in SCU firmware, so they
202 * should be treated as return success always.
204 if ((saved_svc
== IMX_SC_RPC_SVC_MISC
) &&
205 (saved_func
== IMX_SC_MISC_FUNC_UNIQUE_ID
||
206 saved_func
== IMX_SC_MISC_FUNC_GET_BUTTON_STATUS
))
212 mutex_unlock(&sc_ipc
->lock
);
214 dev_dbg(sc_ipc
->dev
, "RPC SVC done\n");
216 return imx_sc_to_linux_errno(ret
);
218 EXPORT_SYMBOL(imx_scu_call_rpc
);
220 static int imx_scu_probe(struct platform_device
*pdev
)
222 struct device
*dev
= &pdev
->dev
;
223 struct imx_sc_ipc
*sc_ipc
;
224 struct imx_sc_chan
*sc_chan
;
225 struct mbox_client
*cl
;
230 sc_ipc
= devm_kzalloc(dev
, sizeof(*sc_ipc
), GFP_KERNEL
);
234 for (i
= 0; i
< SCU_MU_CHAN_NUM
; i
++) {
236 chan_name
= kasprintf(GFP_KERNEL
, "tx%d", i
);
238 chan_name
= kasprintf(GFP_KERNEL
, "rx%d", i
- 4);
243 sc_chan
= &sc_ipc
->chans
[i
];
246 cl
->tx_block
= false;
247 cl
->knows_txdone
= true;
248 cl
->rx_callback
= imx_scu_rx_callback
;
250 sc_chan
->sc_ipc
= sc_ipc
;
251 sc_chan
->idx
= i
% 4;
252 sc_chan
->ch
= mbox_request_channel_byname(cl
, chan_name
);
253 if (IS_ERR(sc_chan
->ch
)) {
254 ret
= PTR_ERR(sc_chan
->ch
);
255 if (ret
!= -EPROBE_DEFER
)
256 dev_err(dev
, "Failed to request mbox chan %s ret %d\n",
261 dev_dbg(dev
, "request mbox chan %s\n", chan_name
);
262 /* chan_name is not used anymore by framework */
267 mutex_init(&sc_ipc
->lock
);
268 init_completion(&sc_ipc
->done
);
270 imx_sc_ipc_handle
= sc_ipc
;
272 ret
= imx_scu_enable_general_irq_channel(dev
);
275 "failed to enable general irq channel: %d\n", ret
);
277 dev_info(dev
, "NXP i.MX SCU Initialized\n");
279 return devm_of_platform_populate(dev
);
282 static const struct of_device_id imx_scu_match
[] = {
283 { .compatible
= "fsl,imx-scu", },
287 static struct platform_driver imx_scu_driver
= {
290 .of_match_table
= imx_scu_match
,
292 .probe
= imx_scu_probe
,
294 builtin_platform_driver(imx_scu_driver
);
296 MODULE_AUTHOR("Dong Aisheng <aisheng.dong@nxp.com>");
297 MODULE_DESCRIPTION("IMX SCU firmware protocol driver");
298 MODULE_LICENSE("GPL v2");