1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Copyright 2018 NXP |
4 | * Author: Dong Aisheng <aisheng.dong@nxp.com> |
5 | * |
6 | * Implementation of the SCU IPC functions using MUs (client side). |
7 | * |
8 | */ |
9 | |
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.h> |
20 | #include <linux/of_platform.h> |
21 | #include <linux/platform_device.h> |
22 | |
23 | #define SCU_MU_CHAN_NUM 8 |
24 | #define MAX_RX_TIMEOUT (msecs_to_jiffies(3000)) |
25 | |
26 | struct imx_sc_chan { |
27 | struct imx_sc_ipc *sc_ipc; |
28 | |
29 | struct mbox_client cl; |
30 | struct mbox_chan *ch; |
31 | int idx; |
32 | struct completion tx_done; |
33 | }; |
34 | |
35 | struct imx_sc_ipc { |
36 | /* SCU uses 4 Tx and 4 Rx channels */ |
37 | struct imx_sc_chan chans[SCU_MU_CHAN_NUM]; |
38 | struct device *dev; |
39 | struct mutex lock; |
40 | struct completion done; |
41 | bool fast_ipc; |
42 | |
43 | /* temporarily store the SCU msg */ |
44 | u32 *msg; |
45 | u8 rx_size; |
46 | u8 count; |
47 | }; |
48 | |
49 | /* |
50 | * This type is used to indicate error response for most functions. |
51 | */ |
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 */ |
65 | IMX_SC_ERR_LAST |
66 | }; |
67 | |
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 */ |
81 | }; |
82 | |
83 | static struct imx_sc_ipc *imx_sc_ipc_handle; |
84 | |
85 | static inline int imx_sc_to_linux_errno(int errno) |
86 | { |
87 | if (errno >= IMX_SC_ERR_NONE && errno < IMX_SC_ERR_LAST) |
88 | return imx_sc_linux_errmap[errno]; |
89 | return -EIO; |
90 | } |
91 | |
92 | /* |
93 | * Get the default handle used by SCU |
94 | */ |
95 | int imx_scu_get_handle(struct imx_sc_ipc **ipc) |
96 | { |
97 | if (!imx_sc_ipc_handle) |
98 | return -EPROBE_DEFER; |
99 | |
100 | *ipc = imx_sc_ipc_handle; |
101 | return 0; |
102 | } |
103 | EXPORT_SYMBOL(imx_scu_get_handle); |
104 | |
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) |
107 | { |
108 | struct imx_sc_chan *sc_chan = container_of(cl, struct imx_sc_chan, cl); |
109 | |
110 | complete(&sc_chan->tx_done); |
111 | } |
112 | |
113 | static void imx_scu_rx_callback(struct mbox_client *c, void *msg) |
114 | { |
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; |
118 | u32 *data = msg; |
119 | int i; |
120 | |
121 | if (!sc_ipc->msg) { |
122 | dev_warn(sc_ipc->dev, "unexpected rx idx %d 0x%08x, ignore!\n" , |
123 | sc_chan->idx, *data); |
124 | return; |
125 | } |
126 | |
127 | if (sc_ipc->fast_ipc) { |
128 | hdr = msg; |
129 | sc_ipc->rx_size = hdr->size; |
130 | sc_ipc->msg[0] = *data++; |
131 | |
132 | for (i = 1; i < sc_ipc->rx_size; i++) |
133 | sc_ipc->msg[i] = *data++; |
134 | |
135 | complete(&sc_ipc->done); |
136 | |
137 | return; |
138 | } |
139 | |
140 | if (sc_chan->idx == 0) { |
141 | hdr = msg; |
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" , |
146 | sc_ipc->rx_size); |
147 | } |
148 | |
149 | sc_ipc->msg[sc_chan->idx] = *data; |
150 | sc_ipc->count++; |
151 | |
152 | dev_dbg(sc_ipc->dev, "mu %u msg %u 0x%x\n" , sc_chan->idx, |
153 | sc_ipc->count, *data); |
154 | |
155 | if ((sc_ipc->rx_size != 0) && (sc_ipc->count == sc_ipc->rx_size)) |
156 | complete(&sc_ipc->done); |
157 | } |
158 | |
159 | static int imx_scu_ipc_write(struct imx_sc_ipc *sc_ipc, void *msg) |
160 | { |
161 | struct imx_sc_rpc_msg hdr = *(struct imx_sc_rpc_msg *)msg; |
162 | struct imx_sc_chan *sc_chan; |
163 | u32 *data = msg; |
164 | int ret; |
165 | int size; |
166 | int i; |
167 | |
168 | /* Check size */ |
169 | if (hdr.size > IMX_SC_RPC_MAX_MSG) |
170 | return -EINVAL; |
171 | |
172 | dev_dbg(sc_ipc->dev, "RPC SVC %u FUNC %u SIZE %u\n" , hdr.svc, |
173 | hdr.func, hdr.size); |
174 | |
175 | size = sc_ipc->fast_ipc ? 1 : hdr.size; |
176 | for (i = 0; i < size; i++) { |
177 | sc_chan = &sc_ipc->chans[i % 4]; |
178 | |
179 | /* |
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. |
184 | * |
185 | * Wait for tx_done before every send to ensure that no |
186 | * queueing happens at the mailbox channel level. |
187 | */ |
188 | if (!sc_ipc->fast_ipc) { |
189 | wait_for_completion(&sc_chan->tx_done); |
190 | reinit_completion(x: &sc_chan->tx_done); |
191 | } |
192 | |
193 | ret = mbox_send_message(chan: sc_chan->ch, mssg: &data[i]); |
194 | if (ret < 0) |
195 | return ret; |
196 | } |
197 | |
198 | return 0; |
199 | } |
200 | |
201 | /* |
202 | * RPC command/response |
203 | */ |
204 | int imx_scu_call_rpc(struct imx_sc_ipc *sc_ipc, void *msg, bool have_resp) |
205 | { |
206 | uint8_t saved_svc, saved_func; |
207 | struct imx_sc_rpc_msg *hdr; |
208 | int ret; |
209 | |
210 | if (WARN_ON(!sc_ipc || !msg)) |
211 | return -EINVAL; |
212 | |
213 | mutex_lock(&sc_ipc->lock); |
214 | reinit_completion(x: &sc_ipc->done); |
215 | |
216 | if (have_resp) { |
217 | sc_ipc->msg = msg; |
218 | saved_svc = ((struct imx_sc_rpc_msg *)msg)->svc; |
219 | saved_func = ((struct imx_sc_rpc_msg *)msg)->func; |
220 | } |
221 | sc_ipc->count = 0; |
222 | ret = imx_scu_ipc_write(sc_ipc, msg); |
223 | if (ret < 0) { |
224 | dev_err(sc_ipc->dev, "RPC send msg failed: %d\n" , ret); |
225 | goto out; |
226 | } |
227 | |
228 | if (have_resp) { |
229 | if (!wait_for_completion_timeout(x: &sc_ipc->done, |
230 | MAX_RX_TIMEOUT)) { |
231 | dev_err(sc_ipc->dev, "RPC send msg timeout\n" ); |
232 | mutex_unlock(lock: &sc_ipc->lock); |
233 | return -ETIMEDOUT; |
234 | } |
235 | |
236 | /* response status is stored in hdr->func field */ |
237 | hdr = msg; |
238 | ret = hdr->func; |
239 | /* |
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. |
244 | */ |
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)) |
248 | ret = 0; |
249 | } |
250 | |
251 | out: |
252 | sc_ipc->msg = NULL; |
253 | mutex_unlock(lock: &sc_ipc->lock); |
254 | |
255 | dev_dbg(sc_ipc->dev, "RPC SVC done\n" ); |
256 | |
257 | return imx_sc_to_linux_errno(errno: ret); |
258 | } |
259 | EXPORT_SYMBOL(imx_scu_call_rpc); |
260 | |
261 | static int imx_scu_probe(struct platform_device *pdev) |
262 | { |
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; |
267 | char *chan_name; |
268 | struct of_phandle_args args; |
269 | int num_channel; |
270 | int ret; |
271 | int i; |
272 | |
273 | sc_ipc = devm_kzalloc(dev, size: sizeof(*sc_ipc), GFP_KERNEL); |
274 | if (!sc_ipc) |
275 | return -ENOMEM; |
276 | |
277 | ret = of_parse_phandle_with_args(np: pdev->dev.of_node, list_name: "mboxes" , |
278 | cells_name: "#mbox-cells" , index: 0, out_args: &args); |
279 | if (ret) |
280 | return ret; |
281 | |
282 | sc_ipc->fast_ipc = of_device_is_compatible(device: args.np, "fsl,imx8-mu-scu" ); |
283 | |
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, fmt: "tx%d" , i); |
288 | else |
289 | chan_name = kasprintf(GFP_KERNEL, fmt: "rx%d" , |
290 | i - num_channel / 2); |
291 | |
292 | if (!chan_name) |
293 | return -ENOMEM; |
294 | |
295 | sc_chan = &sc_ipc->chans[i]; |
296 | cl = &sc_chan->cl; |
297 | cl->dev = dev; |
298 | cl->tx_block = false; |
299 | cl->knows_txdone = true; |
300 | cl->rx_callback = imx_scu_rx_callback; |
301 | |
302 | if (!sc_ipc->fast_ipc) { |
303 | /* Initial tx_done completion as "done" */ |
304 | cl->tx_done = imx_scu_tx_done; |
305 | init_completion(x: &sc_chan->tx_done); |
306 | complete(&sc_chan->tx_done); |
307 | } |
308 | |
309 | sc_chan->sc_ipc = sc_ipc; |
310 | sc_chan->idx = i % (num_channel / 2); |
311 | sc_chan->ch = mbox_request_channel_byname(cl, name: chan_name); |
312 | if (IS_ERR(ptr: sc_chan->ch)) { |
313 | ret = PTR_ERR(ptr: sc_chan->ch); |
314 | dev_err_probe(dev, err: ret, fmt: "Failed to request mbox chan %s\n" , |
315 | chan_name); |
316 | kfree(objp: chan_name); |
317 | return ret; |
318 | } |
319 | |
320 | dev_dbg(dev, "request mbox chan %s\n" , chan_name); |
321 | /* chan_name is not used anymore by framework */ |
322 | kfree(objp: chan_name); |
323 | } |
324 | |
325 | sc_ipc->dev = dev; |
326 | mutex_init(&sc_ipc->lock); |
327 | init_completion(x: &sc_ipc->done); |
328 | |
329 | imx_sc_ipc_handle = sc_ipc; |
330 | |
331 | ret = imx_scu_soc_init(dev); |
332 | if (ret) |
333 | dev_warn(dev, "failed to initialize SoC info: %d\n" , ret); |
334 | |
335 | ret = imx_scu_enable_general_irq_channel(dev); |
336 | if (ret) |
337 | dev_warn(dev, |
338 | "failed to enable general irq channel: %d\n" , ret); |
339 | |
340 | dev_info(dev, "NXP i.MX SCU Initialized\n" ); |
341 | |
342 | return devm_of_platform_populate(dev); |
343 | } |
344 | |
345 | static const struct of_device_id imx_scu_match[] = { |
346 | { .compatible = "fsl,imx-scu" , }, |
347 | { /* Sentinel */ } |
348 | }; |
349 | |
350 | static struct platform_driver imx_scu_driver = { |
351 | .driver = { |
352 | .name = "imx-scu" , |
353 | .of_match_table = imx_scu_match, |
354 | }, |
355 | .probe = imx_scu_probe, |
356 | }; |
357 | |
358 | static int __init imx_scu_driver_init(void) |
359 | { |
360 | return platform_driver_register(&imx_scu_driver); |
361 | } |
362 | subsys_initcall_sync(imx_scu_driver_init); |
363 | |
364 | MODULE_AUTHOR("Dong Aisheng <aisheng.dong@nxp.com>" ); |
365 | MODULE_DESCRIPTION("IMX SCU firmware protocol driver" ); |
366 | MODULE_LICENSE("GPL v2" ); |
367 | |