]>
Commit | Line | Data |
---|---|---|
2bb70056 OR |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Copyright (c) 2018 Pengutronix, Oleksij Rempel <o.rempel@pengutronix.de> | |
a5cb407a | 4 | * Copyright 2022 NXP, Peng Fan <peng.fan@nxp.com> |
2bb70056 OR |
5 | */ |
6 | ||
7 | #include <linux/clk.h> | |
0a67003b | 8 | #include <linux/firmware/imx/ipc.h> |
97961f78 | 9 | #include <linux/firmware/imx/s4.h> |
2bb70056 OR |
10 | #include <linux/interrupt.h> |
11 | #include <linux/io.h> | |
0a67003b | 12 | #include <linux/iopoll.h> |
11dac1d3 | 13 | #include <linux/jiffies.h> |
2bb70056 OR |
14 | #include <linux/kernel.h> |
15 | #include <linux/mailbox_controller.h> | |
16 | #include <linux/module.h> | |
17 | #include <linux/of_device.h> | |
676f23ea | 18 | #include <linux/pm_runtime.h> |
892cb524 | 19 | #include <linux/suspend.h> |
2bb70056 OR |
20 | #include <linux/slab.h> |
21 | ||
2bb70056 | 22 | #define IMX_MU_CHANS 16 |
0a67003b PF |
23 | /* TX0/RX0/RXDB[0-3] */ |
24 | #define IMX_MU_SCU_CHANS 6 | |
97961f78 PF |
25 | /* TX0/RX0 */ |
26 | #define IMX_MU_S4_CHANS 2 | |
2bb70056 OR |
27 | #define IMX_MU_CHAN_NAME_SIZE 20 |
28 | ||
11dac1d3 FL |
29 | #define IMX_MU_SECO_TX_TOUT (msecs_to_jiffies(3000)) |
30 | #define IMX_MU_SECO_RX_TOUT (msecs_to_jiffies(3000)) | |
31 | ||
a5cb407a | 32 | /* Please not change TX & RX */ |
2bb70056 | 33 | enum imx_mu_chan_type { |
a5cb407a PF |
34 | IMX_MU_TYPE_TX = 0, /* Tx */ |
35 | IMX_MU_TYPE_RX = 1, /* Rx */ | |
36 | IMX_MU_TYPE_TXDB = 2, /* Tx doorbell */ | |
37 | IMX_MU_TYPE_RXDB = 3, /* Rx doorbell */ | |
2bb70056 OR |
38 | }; |
39 | ||
f689a7cf | 40 | enum imx_mu_xcr { |
4f0b776e | 41 | IMX_MU_GIER, |
f689a7cf PF |
42 | IMX_MU_GCR, |
43 | IMX_MU_TCR, | |
44 | IMX_MU_RCR, | |
45 | IMX_MU_xCR_MAX, | |
46 | }; | |
47 | ||
48 | enum imx_mu_xsr { | |
49 | IMX_MU_SR, | |
50 | IMX_MU_GSR, | |
51 | IMX_MU_TSR, | |
52 | IMX_MU_RSR, | |
53 | }; | |
54 | ||
0a67003b PF |
55 | struct imx_sc_rpc_msg_max { |
56 | struct imx_sc_rpc_msg hdr; | |
11dac1d3 | 57 | u32 data[30]; |
0a67003b PF |
58 | }; |
59 | ||
97961f78 PF |
60 | struct imx_s4_rpc_msg_max { |
61 | struct imx_s4_rpc_msg hdr; | |
62 | u32 data[254]; | |
63 | }; | |
64 | ||
2bb70056 OR |
65 | struct imx_mu_con_priv { |
66 | unsigned int idx; | |
67 | char irq_desc[IMX_MU_CHAN_NAME_SIZE]; | |
68 | enum imx_mu_chan_type type; | |
69 | struct mbox_chan *chan; | |
70 | struct tasklet_struct txdb_tasklet; | |
71 | }; | |
72 | ||
73 | struct imx_mu_priv { | |
74 | struct device *dev; | |
75 | void __iomem *base; | |
97961f78 | 76 | void *msg; |
2bb70056 OR |
77 | spinlock_t xcr_lock; /* control register lock */ |
78 | ||
79 | struct mbox_controller mbox; | |
80 | struct mbox_chan mbox_chans[IMX_MU_CHANS]; | |
81 | ||
82 | struct imx_mu_con_priv con_priv[IMX_MU_CHANS]; | |
c6c6bc6e | 83 | const struct imx_mu_dcfg *dcfg; |
2bb70056 | 84 | struct clk *clk; |
cfd162f6 | 85 | int irq[IMX_MU_CHANS]; |
892cb524 | 86 | bool suspend; |
2bb70056 | 87 | |
f689a7cf | 88 | u32 xcr[4]; |
ba5f9fa0 | 89 | |
2bb70056 OR |
90 | bool side_b; |
91 | }; | |
92 | ||
4f0b776e PF |
93 | enum imx_mu_type { |
94 | IMX_MU_V1, | |
97961f78 PF |
95 | IMX_MU_V2 = BIT(1), |
96 | IMX_MU_V2_S4 = BIT(15), | |
a5cb407a | 97 | IMX_MU_V2_IRQ = BIT(16), |
4f0b776e PF |
98 | }; |
99 | ||
63b38357 PF |
100 | struct imx_mu_dcfg { |
101 | int (*tx)(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp, void *data); | |
102 | int (*rx)(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp); | |
315d2e56 | 103 | int (*rxdb)(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp); |
63b38357 | 104 | void (*init)(struct imx_mu_priv *priv); |
4f0b776e | 105 | enum imx_mu_type type; |
32f7443d PF |
106 | u32 xTR; /* Transmit Register0 */ |
107 | u32 xRR; /* Receive Register0 */ | |
f689a7cf PF |
108 | u32 xSR[4]; /* Status Registers */ |
109 | u32 xCR[4]; /* Control Registers */ | |
c6c6bc6e RZ |
110 | }; |
111 | ||
97961f78 PF |
112 | #define IMX_MU_xSR_GIPn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(28 + (3 - (x)))) |
113 | #define IMX_MU_xSR_RFn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(24 + (3 - (x)))) | |
114 | #define IMX_MU_xSR_TEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(20 + (3 - (x)))) | |
4f0b776e PF |
115 | |
116 | /* General Purpose Interrupt Enable */ | |
97961f78 | 117 | #define IMX_MU_xCR_GIEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(28 + (3 - (x)))) |
4f0b776e | 118 | /* Receive Interrupt Enable */ |
97961f78 | 119 | #define IMX_MU_xCR_RIEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(24 + (3 - (x)))) |
4f0b776e | 120 | /* Transmit Interrupt Enable */ |
97961f78 | 121 | #define IMX_MU_xCR_TIEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(20 + (3 - (x)))) |
4f0b776e | 122 | /* General Purpose Interrupt Request */ |
97961f78 | 123 | #define IMX_MU_xCR_GIRn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(16 + (3 - (x)))) |
4f0b776e PF |
124 | |
125 | ||
2bb70056 OR |
126 | static struct imx_mu_priv *to_imx_mu_priv(struct mbox_controller *mbox) |
127 | { | |
128 | return container_of(mbox, struct imx_mu_priv, mbox); | |
129 | } | |
130 | ||
131 | static void imx_mu_write(struct imx_mu_priv *priv, u32 val, u32 offs) | |
132 | { | |
133 | iowrite32(val, priv->base + offs); | |
134 | } | |
135 | ||
136 | static u32 imx_mu_read(struct imx_mu_priv *priv, u32 offs) | |
137 | { | |
138 | return ioread32(priv->base + offs); | |
139 | } | |
140 | ||
11dac1d3 FL |
141 | static int imx_mu_tx_waiting_write(struct imx_mu_priv *priv, u32 val, u32 idx) |
142 | { | |
143 | u64 timeout_time = get_jiffies_64() + IMX_MU_SECO_TX_TOUT; | |
144 | u32 status; | |
145 | u32 can_write; | |
146 | ||
147 | dev_dbg(priv->dev, "Trying to write %.8x to idx %d\n", val, idx); | |
148 | ||
149 | do { | |
150 | status = imx_mu_read(priv, priv->dcfg->xSR[IMX_MU_TSR]); | |
151 | can_write = status & IMX_MU_xSR_TEn(priv->dcfg->type, idx % 4); | |
152 | } while (!can_write && time_is_after_jiffies64(timeout_time)); | |
153 | ||
154 | if (!can_write) { | |
155 | dev_err(priv->dev, "timeout trying to write %.8x at %d(%.8x)\n", | |
156 | val, idx, status); | |
157 | return -ETIME; | |
158 | } | |
159 | ||
160 | imx_mu_write(priv, val, priv->dcfg->xTR + (idx % 4) * 4); | |
161 | ||
162 | return 0; | |
163 | } | |
164 | ||
165 | static int imx_mu_rx_waiting_read(struct imx_mu_priv *priv, u32 *val, u32 idx) | |
166 | { | |
167 | u64 timeout_time = get_jiffies_64() + IMX_MU_SECO_RX_TOUT; | |
168 | u32 status; | |
169 | u32 can_read; | |
170 | ||
171 | dev_dbg(priv->dev, "Trying to read from idx %d\n", idx); | |
172 | ||
173 | do { | |
174 | status = imx_mu_read(priv, priv->dcfg->xSR[IMX_MU_RSR]); | |
175 | can_read = status & IMX_MU_xSR_RFn(priv->dcfg->type, idx % 4); | |
176 | } while (!can_read && time_is_after_jiffies64(timeout_time)); | |
177 | ||
178 | if (!can_read) { | |
179 | dev_err(priv->dev, "timeout trying to read idx %d (%.8x)\n", | |
180 | idx, status); | |
181 | return -ETIME; | |
182 | } | |
183 | ||
184 | *val = imx_mu_read(priv, priv->dcfg->xRR + (idx % 4) * 4); | |
185 | dev_dbg(priv->dev, "Read %.8x\n", *val); | |
186 | ||
187 | return 0; | |
188 | } | |
189 | ||
f689a7cf | 190 | static u32 imx_mu_xcr_rmw(struct imx_mu_priv *priv, enum imx_mu_xcr type, u32 set, u32 clr) |
2bb70056 OR |
191 | { |
192 | unsigned long flags; | |
193 | u32 val; | |
194 | ||
195 | spin_lock_irqsave(&priv->xcr_lock, flags); | |
f689a7cf | 196 | val = imx_mu_read(priv, priv->dcfg->xCR[type]); |
2bb70056 OR |
197 | val &= ~clr; |
198 | val |= set; | |
f689a7cf | 199 | imx_mu_write(priv, val, priv->dcfg->xCR[type]); |
2bb70056 OR |
200 | spin_unlock_irqrestore(&priv->xcr_lock, flags); |
201 | ||
202 | return val; | |
203 | } | |
204 | ||
63b38357 PF |
205 | static int imx_mu_generic_tx(struct imx_mu_priv *priv, |
206 | struct imx_mu_con_priv *cp, | |
207 | void *data) | |
208 | { | |
209 | u32 *arg = data; | |
210 | ||
211 | switch (cp->type) { | |
212 | case IMX_MU_TYPE_TX: | |
32f7443d | 213 | imx_mu_write(priv, *arg, priv->dcfg->xTR + cp->idx * 4); |
4f0b776e | 214 | imx_mu_xcr_rmw(priv, IMX_MU_TCR, IMX_MU_xCR_TIEn(priv->dcfg->type, cp->idx), 0); |
63b38357 PF |
215 | break; |
216 | case IMX_MU_TYPE_TXDB: | |
4f0b776e | 217 | imx_mu_xcr_rmw(priv, IMX_MU_GCR, IMX_MU_xCR_GIRn(priv->dcfg->type, cp->idx), 0); |
63b38357 PF |
218 | tasklet_schedule(&cp->txdb_tasklet); |
219 | break; | |
220 | default: | |
221 | dev_warn_ratelimited(priv->dev, "Send data on wrong channel type: %d\n", cp->type); | |
222 | return -EINVAL; | |
223 | } | |
224 | ||
225 | return 0; | |
226 | } | |
227 | ||
228 | static int imx_mu_generic_rx(struct imx_mu_priv *priv, | |
229 | struct imx_mu_con_priv *cp) | |
230 | { | |
231 | u32 dat; | |
232 | ||
32f7443d | 233 | dat = imx_mu_read(priv, priv->dcfg->xRR + (cp->idx) * 4); |
63b38357 PF |
234 | mbox_chan_received_data(cp->chan, (void *)&dat); |
235 | ||
236 | return 0; | |
237 | } | |
238 | ||
315d2e56 PF |
239 | static int imx_mu_generic_rxdb(struct imx_mu_priv *priv, |
240 | struct imx_mu_con_priv *cp) | |
241 | { | |
242 | imx_mu_write(priv, IMX_MU_xSR_GIPn(priv->dcfg->type, cp->idx), | |
243 | priv->dcfg->xSR[IMX_MU_GSR]); | |
244 | mbox_chan_received_data(cp->chan, NULL); | |
245 | ||
246 | return 0; | |
247 | } | |
248 | ||
97961f78 | 249 | static int imx_mu_specific_tx(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp, void *data) |
0a67003b | 250 | { |
0a67003b PF |
251 | u32 *arg = data; |
252 | int i, ret; | |
253 | u32 xsr; | |
97961f78 PF |
254 | u32 size, max_size, num_tr; |
255 | ||
256 | if (priv->dcfg->type & IMX_MU_V2_S4) { | |
257 | size = ((struct imx_s4_rpc_msg_max *)data)->hdr.size; | |
258 | max_size = sizeof(struct imx_s4_rpc_msg_max); | |
259 | num_tr = 8; | |
260 | } else { | |
261 | size = ((struct imx_sc_rpc_msg_max *)data)->hdr.size; | |
262 | max_size = sizeof(struct imx_sc_rpc_msg_max); | |
263 | num_tr = 4; | |
264 | } | |
0a67003b PF |
265 | |
266 | switch (cp->type) { | |
267 | case IMX_MU_TYPE_TX: | |
9d8ca628 PF |
268 | /* |
269 | * msg->hdr.size specifies the number of u32 words while | |
270 | * sizeof yields bytes. | |
271 | */ | |
272 | ||
97961f78 | 273 | if (size > max_size / 4) { |
0a67003b PF |
274 | /* |
275 | * The real message size can be different to | |
97961f78 | 276 | * struct imx_sc_rpc_msg_max/imx_s4_rpc_msg_max size |
0a67003b | 277 | */ |
97961f78 | 278 | dev_err(priv->dev, "Maximal message size (%u bytes) exceeded on TX; got: %i bytes\n", max_size, size << 2); |
0a67003b PF |
279 | return -EINVAL; |
280 | } | |
281 | ||
97961f78 PF |
282 | for (i = 0; i < num_tr && i < size; i++) |
283 | imx_mu_write(priv, *arg++, priv->dcfg->xTR + (i % num_tr) * 4); | |
284 | for (; i < size; i++) { | |
f689a7cf | 285 | ret = readl_poll_timeout(priv->base + priv->dcfg->xSR[IMX_MU_TSR], |
0a67003b | 286 | xsr, |
97961f78 | 287 | xsr & IMX_MU_xSR_TEn(priv->dcfg->type, i % num_tr), |
81a9d3b9 | 288 | 0, 5 * USEC_PER_SEC); |
0a67003b PF |
289 | if (ret) { |
290 | dev_err(priv->dev, "Send data index: %d timeout\n", i); | |
291 | return ret; | |
292 | } | |
97961f78 | 293 | imx_mu_write(priv, *arg++, priv->dcfg->xTR + (i % num_tr) * 4); |
0a67003b PF |
294 | } |
295 | ||
4f0b776e | 296 | imx_mu_xcr_rmw(priv, IMX_MU_TCR, IMX_MU_xCR_TIEn(priv->dcfg->type, cp->idx), 0); |
0a67003b PF |
297 | break; |
298 | default: | |
299 | dev_warn_ratelimited(priv->dev, "Send data on wrong channel type: %d\n", cp->type); | |
300 | return -EINVAL; | |
301 | } | |
302 | ||
303 | return 0; | |
304 | } | |
305 | ||
97961f78 | 306 | static int imx_mu_specific_rx(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp) |
0a67003b | 307 | { |
97961f78 | 308 | u32 *data; |
0a67003b PF |
309 | int i, ret; |
310 | u32 xsr; | |
97961f78 PF |
311 | u32 size, max_size; |
312 | ||
313 | data = (u32 *)priv->msg; | |
0a67003b | 314 | |
4f0b776e | 315 | imx_mu_xcr_rmw(priv, IMX_MU_RCR, 0, IMX_MU_xCR_RIEn(priv->dcfg->type, 0)); |
32f7443d | 316 | *data++ = imx_mu_read(priv, priv->dcfg->xRR); |
0a67003b | 317 | |
97961f78 PF |
318 | if (priv->dcfg->type & IMX_MU_V2_S4) { |
319 | size = ((struct imx_s4_rpc_msg_max *)priv->msg)->hdr.size; | |
320 | max_size = sizeof(struct imx_s4_rpc_msg_max); | |
321 | } else { | |
322 | size = ((struct imx_sc_rpc_msg_max *)priv->msg)->hdr.size; | |
323 | max_size = sizeof(struct imx_sc_rpc_msg_max); | |
324 | } | |
325 | ||
326 | if (size > max_size / 4) { | |
327 | dev_err(priv->dev, "Maximal message size (%u bytes) exceeded on RX; got: %i bytes\n", max_size, size << 2); | |
0a67003b PF |
328 | return -EINVAL; |
329 | } | |
330 | ||
97961f78 | 331 | for (i = 1; i < size; i++) { |
f689a7cf | 332 | ret = readl_poll_timeout(priv->base + priv->dcfg->xSR[IMX_MU_RSR], xsr, |
81a9d3b9 RV |
333 | xsr & IMX_MU_xSR_RFn(priv->dcfg->type, i % 4), 0, |
334 | 5 * USEC_PER_SEC); | |
0a67003b PF |
335 | if (ret) { |
336 | dev_err(priv->dev, "timeout read idx %d\n", i); | |
337 | return ret; | |
338 | } | |
32f7443d | 339 | *data++ = imx_mu_read(priv, priv->dcfg->xRR + (i % 4) * 4); |
0a67003b PF |
340 | } |
341 | ||
4f0b776e | 342 | imx_mu_xcr_rmw(priv, IMX_MU_RCR, IMX_MU_xCR_RIEn(priv->dcfg->type, 0), 0); |
97961f78 | 343 | mbox_chan_received_data(cp->chan, (void *)priv->msg); |
0a67003b PF |
344 | |
345 | return 0; | |
346 | } | |
347 | ||
11dac1d3 FL |
348 | static int imx_mu_seco_tx(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp, |
349 | void *data) | |
350 | { | |
351 | struct imx_sc_rpc_msg_max *msg = data; | |
352 | u32 *arg = data; | |
353 | u32 byte_size; | |
354 | int err; | |
355 | int i; | |
356 | ||
357 | dev_dbg(priv->dev, "Sending message\n"); | |
358 | ||
359 | switch (cp->type) { | |
360 | case IMX_MU_TYPE_TXDB: | |
361 | byte_size = msg->hdr.size * sizeof(u32); | |
362 | if (byte_size > sizeof(*msg)) { | |
363 | /* | |
364 | * The real message size can be different to | |
365 | * struct imx_sc_rpc_msg_max size | |
366 | */ | |
367 | dev_err(priv->dev, | |
368 | "Exceed max msg size (%zu) on TX, got: %i\n", | |
369 | sizeof(*msg), byte_size); | |
370 | return -EINVAL; | |
371 | } | |
372 | ||
373 | print_hex_dump_debug("from client ", DUMP_PREFIX_OFFSET, 4, 4, | |
374 | data, byte_size, false); | |
375 | ||
376 | /* Send first word */ | |
377 | dev_dbg(priv->dev, "Sending header\n"); | |
378 | imx_mu_write(priv, *arg++, priv->dcfg->xTR); | |
379 | ||
380 | /* Send signaling */ | |
381 | dev_dbg(priv->dev, "Sending signaling\n"); | |
382 | imx_mu_xcr_rmw(priv, IMX_MU_GCR, | |
383 | IMX_MU_xCR_GIRn(priv->dcfg->type, cp->idx), 0); | |
384 | ||
385 | /* Send words to fill the mailbox */ | |
386 | for (i = 1; i < 4 && i < msg->hdr.size; i++) { | |
387 | dev_dbg(priv->dev, "Sending word %d\n", i); | |
388 | imx_mu_write(priv, *arg++, | |
389 | priv->dcfg->xTR + (i % 4) * 4); | |
390 | } | |
391 | ||
392 | /* Send rest of message waiting for remote read */ | |
393 | for (; i < msg->hdr.size; i++) { | |
394 | dev_dbg(priv->dev, "Sending word %d\n", i); | |
395 | err = imx_mu_tx_waiting_write(priv, *arg++, i); | |
396 | if (err) { | |
397 | dev_err(priv->dev, "Timeout tx %d\n", i); | |
398 | return err; | |
399 | } | |
400 | } | |
401 | ||
402 | /* Simulate hack for mbox framework */ | |
403 | tasklet_schedule(&cp->txdb_tasklet); | |
404 | ||
405 | break; | |
406 | default: | |
407 | dev_warn_ratelimited(priv->dev, | |
408 | "Send data on wrong channel type: %d\n", | |
409 | cp->type); | |
410 | return -EINVAL; | |
411 | } | |
412 | ||
413 | return 0; | |
414 | } | |
415 | ||
416 | static int imx_mu_seco_rxdb(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp) | |
417 | { | |
418 | struct imx_sc_rpc_msg_max msg; | |
419 | u32 *data = (u32 *)&msg; | |
420 | u32 byte_size; | |
421 | int err = 0; | |
422 | int i; | |
423 | ||
424 | dev_dbg(priv->dev, "Receiving message\n"); | |
425 | ||
426 | /* Read header */ | |
427 | dev_dbg(priv->dev, "Receiving header\n"); | |
428 | *data++ = imx_mu_read(priv, priv->dcfg->xRR); | |
429 | byte_size = msg.hdr.size * sizeof(u32); | |
430 | if (byte_size > sizeof(msg)) { | |
431 | dev_err(priv->dev, "Exceed max msg size (%zu) on RX, got: %i\n", | |
432 | sizeof(msg), byte_size); | |
433 | err = -EINVAL; | |
434 | goto error; | |
435 | } | |
436 | ||
437 | /* Read message waiting they are written */ | |
438 | for (i = 1; i < msg.hdr.size; i++) { | |
439 | dev_dbg(priv->dev, "Receiving word %d\n", i); | |
440 | err = imx_mu_rx_waiting_read(priv, data++, i); | |
441 | if (err) { | |
442 | dev_err(priv->dev, "Timeout rx %d\n", i); | |
443 | goto error; | |
444 | } | |
445 | } | |
446 | ||
447 | /* Clear GIP */ | |
448 | imx_mu_write(priv, IMX_MU_xSR_GIPn(priv->dcfg->type, cp->idx), | |
449 | priv->dcfg->xSR[IMX_MU_GSR]); | |
450 | ||
451 | print_hex_dump_debug("to client ", DUMP_PREFIX_OFFSET, 4, 4, | |
452 | &msg, byte_size, false); | |
453 | ||
454 | /* send data to client */ | |
455 | dev_dbg(priv->dev, "Sending message to client\n"); | |
456 | mbox_chan_received_data(cp->chan, (void *)&msg); | |
457 | ||
458 | goto exit; | |
459 | ||
460 | error: | |
461 | mbox_chan_received_data(cp->chan, ERR_PTR(err)); | |
462 | ||
463 | exit: | |
464 | return err; | |
465 | } | |
466 | ||
2bb70056 OR |
467 | static void imx_mu_txdb_tasklet(unsigned long data) |
468 | { | |
469 | struct imx_mu_con_priv *cp = (struct imx_mu_con_priv *)data; | |
470 | ||
471 | mbox_chan_txdone(cp->chan, 0); | |
472 | } | |
473 | ||
474 | static irqreturn_t imx_mu_isr(int irq, void *p) | |
475 | { | |
476 | struct mbox_chan *chan = p; | |
477 | struct imx_mu_priv *priv = to_imx_mu_priv(chan->mbox); | |
478 | struct imx_mu_con_priv *cp = chan->con_priv; | |
63b38357 | 479 | u32 val, ctrl; |
2bb70056 | 480 | |
2bb70056 OR |
481 | switch (cp->type) { |
482 | case IMX_MU_TYPE_TX: | |
f689a7cf PF |
483 | ctrl = imx_mu_read(priv, priv->dcfg->xCR[IMX_MU_TCR]); |
484 | val = imx_mu_read(priv, priv->dcfg->xSR[IMX_MU_TSR]); | |
4f0b776e PF |
485 | val &= IMX_MU_xSR_TEn(priv->dcfg->type, cp->idx) & |
486 | (ctrl & IMX_MU_xCR_TIEn(priv->dcfg->type, cp->idx)); | |
2bb70056 OR |
487 | break; |
488 | case IMX_MU_TYPE_RX: | |
f689a7cf PF |
489 | ctrl = imx_mu_read(priv, priv->dcfg->xCR[IMX_MU_RCR]); |
490 | val = imx_mu_read(priv, priv->dcfg->xSR[IMX_MU_RSR]); | |
4f0b776e PF |
491 | val &= IMX_MU_xSR_RFn(priv->dcfg->type, cp->idx) & |
492 | (ctrl & IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx)); | |
2bb70056 OR |
493 | break; |
494 | case IMX_MU_TYPE_RXDB: | |
4f0b776e | 495 | ctrl = imx_mu_read(priv, priv->dcfg->xCR[IMX_MU_GIER]); |
f689a7cf | 496 | val = imx_mu_read(priv, priv->dcfg->xSR[IMX_MU_GSR]); |
4f0b776e PF |
497 | val &= IMX_MU_xSR_GIPn(priv->dcfg->type, cp->idx) & |
498 | (ctrl & IMX_MU_xCR_GIEn(priv->dcfg->type, cp->idx)); | |
2bb70056 OR |
499 | break; |
500 | default: | |
e80a7e7e NC |
501 | dev_warn_ratelimited(priv->dev, "Unhandled channel type %d\n", |
502 | cp->type); | |
503 | return IRQ_NONE; | |
2bb70056 OR |
504 | } |
505 | ||
506 | if (!val) | |
507 | return IRQ_NONE; | |
508 | ||
4f0b776e PF |
509 | if ((val == IMX_MU_xSR_TEn(priv->dcfg->type, cp->idx)) && |
510 | (cp->type == IMX_MU_TYPE_TX)) { | |
511 | imx_mu_xcr_rmw(priv, IMX_MU_TCR, 0, IMX_MU_xCR_TIEn(priv->dcfg->type, cp->idx)); | |
2bb70056 | 512 | mbox_chan_txdone(chan, 0); |
4f0b776e PF |
513 | } else if ((val == IMX_MU_xSR_RFn(priv->dcfg->type, cp->idx)) && |
514 | (cp->type == IMX_MU_TYPE_RX)) { | |
63b38357 | 515 | priv->dcfg->rx(priv, cp); |
4f0b776e PF |
516 | } else if ((val == IMX_MU_xSR_GIPn(priv->dcfg->type, cp->idx)) && |
517 | (cp->type == IMX_MU_TYPE_RXDB)) { | |
315d2e56 | 518 | priv->dcfg->rxdb(priv, cp); |
2bb70056 OR |
519 | } else { |
520 | dev_warn_ratelimited(priv->dev, "Not handled interrupt\n"); | |
521 | return IRQ_NONE; | |
522 | } | |
523 | ||
892cb524 RG |
524 | if (priv->suspend) |
525 | pm_system_wakeup(); | |
526 | ||
2bb70056 OR |
527 | return IRQ_HANDLED; |
528 | } | |
529 | ||
530 | static int imx_mu_send_data(struct mbox_chan *chan, void *data) | |
531 | { | |
532 | struct imx_mu_priv *priv = to_imx_mu_priv(chan->mbox); | |
533 | struct imx_mu_con_priv *cp = chan->con_priv; | |
2bb70056 | 534 | |
63b38357 | 535 | return priv->dcfg->tx(priv, cp, data); |
2bb70056 OR |
536 | } |
537 | ||
538 | static int imx_mu_startup(struct mbox_chan *chan) | |
539 | { | |
540 | struct imx_mu_priv *priv = to_imx_mu_priv(chan->mbox); | |
541 | struct imx_mu_con_priv *cp = chan->con_priv; | |
a5cb407a | 542 | unsigned long irq_flag = 0; |
2bb70056 OR |
543 | int ret; |
544 | ||
676f23ea | 545 | pm_runtime_get_sync(priv->dev); |
2bb70056 OR |
546 | if (cp->type == IMX_MU_TYPE_TXDB) { |
547 | /* Tx doorbell don't have ACK support */ | |
548 | tasklet_init(&cp->txdb_tasklet, imx_mu_txdb_tasklet, | |
549 | (unsigned long)cp); | |
550 | return 0; | |
551 | } | |
552 | ||
b7b2796b AH |
553 | /* IPC MU should be with IRQF_NO_SUSPEND set */ |
554 | if (!priv->dev->pm_domain) | |
555 | irq_flag |= IRQF_NO_SUSPEND; | |
556 | ||
a5cb407a PF |
557 | if (!(priv->dcfg->type & IMX_MU_V2_IRQ)) |
558 | irq_flag |= IRQF_SHARED; | |
559 | ||
560 | ret = request_irq(priv->irq[cp->type], imx_mu_isr, irq_flag, cp->irq_desc, chan); | |
2bb70056 | 561 | if (ret) { |
a5cb407a | 562 | dev_err(priv->dev, "Unable to acquire IRQ %d\n", priv->irq[cp->type]); |
2bb70056 OR |
563 | return ret; |
564 | } | |
565 | ||
566 | switch (cp->type) { | |
567 | case IMX_MU_TYPE_RX: | |
4f0b776e | 568 | imx_mu_xcr_rmw(priv, IMX_MU_RCR, IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx), 0); |
2bb70056 OR |
569 | break; |
570 | case IMX_MU_TYPE_RXDB: | |
4f0b776e | 571 | imx_mu_xcr_rmw(priv, IMX_MU_GIER, IMX_MU_xCR_GIEn(priv->dcfg->type, cp->idx), 0); |
2bb70056 OR |
572 | break; |
573 | default: | |
574 | break; | |
575 | } | |
576 | ||
577 | return 0; | |
578 | } | |
579 | ||
580 | static void imx_mu_shutdown(struct mbox_chan *chan) | |
581 | { | |
582 | struct imx_mu_priv *priv = to_imx_mu_priv(chan->mbox); | |
583 | struct imx_mu_con_priv *cp = chan->con_priv; | |
584 | ||
bf159d15 | 585 | if (cp->type == IMX_MU_TYPE_TXDB) { |
2bb70056 | 586 | tasklet_kill(&cp->txdb_tasklet); |
676f23ea | 587 | pm_runtime_put_sync(priv->dev); |
bf159d15 DB |
588 | return; |
589 | } | |
2bb70056 | 590 | |
5f0af07e DB |
591 | switch (cp->type) { |
592 | case IMX_MU_TYPE_TX: | |
4f0b776e | 593 | imx_mu_xcr_rmw(priv, IMX_MU_TCR, 0, IMX_MU_xCR_TIEn(priv->dcfg->type, cp->idx)); |
5f0af07e DB |
594 | break; |
595 | case IMX_MU_TYPE_RX: | |
4f0b776e | 596 | imx_mu_xcr_rmw(priv, IMX_MU_RCR, 0, IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx)); |
5f0af07e DB |
597 | break; |
598 | case IMX_MU_TYPE_RXDB: | |
4f0b776e | 599 | imx_mu_xcr_rmw(priv, IMX_MU_GIER, 0, IMX_MU_xCR_GIEn(priv->dcfg->type, cp->idx)); |
5f0af07e DB |
600 | break; |
601 | default: | |
602 | break; | |
603 | } | |
2bb70056 | 604 | |
a5cb407a | 605 | free_irq(priv->irq[cp->type], chan); |
676f23ea | 606 | pm_runtime_put_sync(priv->dev); |
2bb70056 OR |
607 | } |
608 | ||
609 | static const struct mbox_chan_ops imx_mu_ops = { | |
610 | .send_data = imx_mu_send_data, | |
611 | .startup = imx_mu_startup, | |
612 | .shutdown = imx_mu_shutdown, | |
613 | }; | |
614 | ||
97961f78 PF |
615 | static struct mbox_chan *imx_mu_specific_xlate(struct mbox_controller *mbox, |
616 | const struct of_phandle_args *sp) | |
0a67003b PF |
617 | { |
618 | u32 type, idx, chan; | |
619 | ||
620 | if (sp->args_count != 2) { | |
621 | dev_err(mbox->dev, "Invalid argument count %d\n", sp->args_count); | |
622 | return ERR_PTR(-EINVAL); | |
623 | } | |
624 | ||
625 | type = sp->args[0]; /* channel type */ | |
626 | idx = sp->args[1]; /* index */ | |
627 | ||
628 | switch (type) { | |
629 | case IMX_MU_TYPE_TX: | |
630 | case IMX_MU_TYPE_RX: | |
631 | if (idx != 0) | |
632 | dev_err(mbox->dev, "Invalid chan idx: %d\n", idx); | |
633 | chan = type; | |
634 | break; | |
635 | case IMX_MU_TYPE_RXDB: | |
636 | chan = 2 + idx; | |
637 | break; | |
638 | default: | |
639 | dev_err(mbox->dev, "Invalid chan type: %d\n", type); | |
1b3a347b | 640 | return ERR_PTR(-EINVAL); |
0a67003b PF |
641 | } |
642 | ||
643 | if (chan >= mbox->num_chans) { | |
644 | dev_err(mbox->dev, "Not supported channel number: %d. (type: %d, idx: %d)\n", chan, type, idx); | |
645 | return ERR_PTR(-EINVAL); | |
646 | } | |
647 | ||
648 | return &mbox->chans[chan]; | |
649 | } | |
650 | ||
2bb70056 OR |
651 | static struct mbox_chan * imx_mu_xlate(struct mbox_controller *mbox, |
652 | const struct of_phandle_args *sp) | |
653 | { | |
654 | u32 type, idx, chan; | |
655 | ||
656 | if (sp->args_count != 2) { | |
657 | dev_err(mbox->dev, "Invalid argument count %d\n", sp->args_count); | |
658 | return ERR_PTR(-EINVAL); | |
659 | } | |
660 | ||
661 | type = sp->args[0]; /* channel type */ | |
662 | idx = sp->args[1]; /* index */ | |
663 | chan = type * 4 + idx; | |
664 | ||
665 | if (chan >= mbox->num_chans) { | |
666 | dev_err(mbox->dev, "Not supported channel number: %d. (type: %d, idx: %d)\n", chan, type, idx); | |
667 | return ERR_PTR(-EINVAL); | |
668 | } | |
669 | ||
670 | return &mbox->chans[chan]; | |
671 | } | |
672 | ||
11dac1d3 FL |
673 | static struct mbox_chan *imx_mu_seco_xlate(struct mbox_controller *mbox, |
674 | const struct of_phandle_args *sp) | |
675 | { | |
676 | u32 type; | |
677 | ||
678 | if (sp->args_count < 1) { | |
679 | dev_err(mbox->dev, "Invalid argument count %d\n", sp->args_count); | |
680 | return ERR_PTR(-EINVAL); | |
681 | } | |
682 | ||
683 | type = sp->args[0]; /* channel type */ | |
684 | ||
685 | /* Only supports TXDB and RXDB */ | |
686 | if (type == IMX_MU_TYPE_TX || type == IMX_MU_TYPE_RX) { | |
687 | dev_err(mbox->dev, "Invalid type: %d\n", type); | |
688 | return ERR_PTR(-EINVAL); | |
689 | } | |
690 | ||
691 | return imx_mu_xlate(mbox, sp); | |
692 | } | |
693 | ||
2bb70056 OR |
694 | static void imx_mu_init_generic(struct imx_mu_priv *priv) |
695 | { | |
63b38357 PF |
696 | unsigned int i; |
697 | ||
698 | for (i = 0; i < IMX_MU_CHANS; i++) { | |
699 | struct imx_mu_con_priv *cp = &priv->con_priv[i]; | |
700 | ||
701 | cp->idx = i % 4; | |
702 | cp->type = i >> 2; | |
703 | cp->chan = &priv->mbox_chans[i]; | |
704 | priv->mbox_chans[i].con_priv = cp; | |
705 | snprintf(cp->irq_desc, sizeof(cp->irq_desc), | |
706 | "imx_mu_chan[%i-%i]", cp->type, cp->idx); | |
707 | } | |
708 | ||
709 | priv->mbox.num_chans = IMX_MU_CHANS; | |
710 | priv->mbox.of_xlate = imx_mu_xlate; | |
711 | ||
2bb70056 OR |
712 | if (priv->side_b) |
713 | return; | |
714 | ||
715 | /* Set default MU configuration */ | |
f689a7cf PF |
716 | for (i = 0; i < IMX_MU_xCR_MAX; i++) |
717 | imx_mu_write(priv, 0, priv->dcfg->xCR[i]); | |
2bb70056 OR |
718 | } |
719 | ||
97961f78 | 720 | static void imx_mu_init_specific(struct imx_mu_priv *priv) |
0a67003b PF |
721 | { |
722 | unsigned int i; | |
97961f78 | 723 | int num_chans = priv->dcfg->type & IMX_MU_V2_S4 ? IMX_MU_S4_CHANS : IMX_MU_SCU_CHANS; |
0a67003b | 724 | |
97961f78 | 725 | for (i = 0; i < num_chans; i++) { |
0a67003b PF |
726 | struct imx_mu_con_priv *cp = &priv->con_priv[i]; |
727 | ||
728 | cp->idx = i < 2 ? 0 : i - 2; | |
729 | cp->type = i < 2 ? i : IMX_MU_TYPE_RXDB; | |
730 | cp->chan = &priv->mbox_chans[i]; | |
731 | priv->mbox_chans[i].con_priv = cp; | |
732 | snprintf(cp->irq_desc, sizeof(cp->irq_desc), | |
733 | "imx_mu_chan[%i-%i]", cp->type, cp->idx); | |
734 | } | |
735 | ||
97961f78 PF |
736 | priv->mbox.num_chans = num_chans; |
737 | priv->mbox.of_xlate = imx_mu_specific_xlate; | |
0a67003b PF |
738 | |
739 | /* Set default MU configuration */ | |
f689a7cf PF |
740 | for (i = 0; i < IMX_MU_xCR_MAX; i++) |
741 | imx_mu_write(priv, 0, priv->dcfg->xCR[i]); | |
0a67003b PF |
742 | } |
743 | ||
11dac1d3 FL |
744 | static void imx_mu_init_seco(struct imx_mu_priv *priv) |
745 | { | |
746 | imx_mu_init_generic(priv); | |
747 | priv->mbox.of_xlate = imx_mu_seco_xlate; | |
748 | } | |
749 | ||
2bb70056 OR |
750 | static int imx_mu_probe(struct platform_device *pdev) |
751 | { | |
752 | struct device *dev = &pdev->dev; | |
753 | struct device_node *np = dev->of_node; | |
2bb70056 | 754 | struct imx_mu_priv *priv; |
c6c6bc6e | 755 | const struct imx_mu_dcfg *dcfg; |
a5cb407a | 756 | int i, ret; |
97961f78 | 757 | u32 size; |
2bb70056 OR |
758 | |
759 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | |
760 | if (!priv) | |
761 | return -ENOMEM; | |
762 | ||
763 | priv->dev = dev; | |
764 | ||
0c40e631 | 765 | priv->base = devm_platform_ioremap_resource(pdev, 0); |
2bb70056 OR |
766 | if (IS_ERR(priv->base)) |
767 | return PTR_ERR(priv->base); | |
768 | ||
c6c6bc6e RZ |
769 | dcfg = of_device_get_match_data(dev); |
770 | if (!dcfg) | |
771 | return -EINVAL; | |
772 | priv->dcfg = dcfg; | |
a5cb407a PF |
773 | if (priv->dcfg->type & IMX_MU_V2_IRQ) { |
774 | priv->irq[IMX_MU_TYPE_TX] = platform_get_irq_byname(pdev, "tx"); | |
775 | if (priv->irq[IMX_MU_TYPE_TX] < 0) | |
776 | return priv->irq[IMX_MU_TYPE_TX]; | |
777 | priv->irq[IMX_MU_TYPE_RX] = platform_get_irq_byname(pdev, "rx"); | |
778 | if (priv->irq[IMX_MU_TYPE_RX] < 0) | |
779 | return priv->irq[IMX_MU_TYPE_RX]; | |
780 | } else { | |
781 | ret = platform_get_irq(pdev, 0); | |
782 | if (ret < 0) | |
783 | return ret; | |
784 | ||
785 | for (i = 0; i < IMX_MU_CHANS; i++) | |
786 | priv->irq[i] = ret; | |
787 | } | |
c6c6bc6e | 788 | |
97961f78 PF |
789 | if (priv->dcfg->type & IMX_MU_V2_S4) |
790 | size = sizeof(struct imx_s4_rpc_msg_max); | |
791 | else | |
792 | size = sizeof(struct imx_sc_rpc_msg_max); | |
793 | ||
794 | priv->msg = devm_kzalloc(dev, size, GFP_KERNEL); | |
05d06f37 DC |
795 | if (!priv->msg) |
796 | return -ENOMEM; | |
97961f78 | 797 | |
2bb70056 OR |
798 | priv->clk = devm_clk_get(dev, NULL); |
799 | if (IS_ERR(priv->clk)) { | |
800 | if (PTR_ERR(priv->clk) != -ENOENT) | |
801 | return PTR_ERR(priv->clk); | |
802 | ||
803 | priv->clk = NULL; | |
804 | } | |
805 | ||
806 | ret = clk_prepare_enable(priv->clk); | |
807 | if (ret) { | |
808 | dev_err(dev, "Failed to enable clock\n"); | |
809 | return ret; | |
810 | } | |
811 | ||
2bb70056 OR |
812 | priv->side_b = of_property_read_bool(np, "fsl,mu-side-b"); |
813 | ||
63b38357 PF |
814 | priv->dcfg->init(priv); |
815 | ||
2bb70056 OR |
816 | spin_lock_init(&priv->xcr_lock); |
817 | ||
818 | priv->mbox.dev = dev; | |
819 | priv->mbox.ops = &imx_mu_ops; | |
820 | priv->mbox.chans = priv->mbox_chans; | |
2bb70056 OR |
821 | priv->mbox.txdone_irq = true; |
822 | ||
823 | platform_set_drvdata(pdev, priv); | |
824 | ||
676f23ea | 825 | ret = devm_mbox_controller_register(dev, &priv->mbox); |
47303f94 FE |
826 | if (ret) { |
827 | clk_disable_unprepare(priv->clk); | |
676f23ea | 828 | return ret; |
47303f94 | 829 | } |
676f23ea AH |
830 | |
831 | pm_runtime_enable(dev); | |
832 | ||
504ff5b0 | 833 | ret = pm_runtime_resume_and_get(dev); |
834 | if (ret < 0) | |
676f23ea | 835 | goto disable_runtime_pm; |
676f23ea AH |
836 | |
837 | ret = pm_runtime_put_sync(dev); | |
838 | if (ret < 0) | |
839 | goto disable_runtime_pm; | |
840 | ||
bb2b2624 AH |
841 | clk_disable_unprepare(priv->clk); |
842 | ||
676f23ea AH |
843 | return 0; |
844 | ||
845 | disable_runtime_pm: | |
846 | pm_runtime_disable(dev); | |
bb2b2624 | 847 | clk_disable_unprepare(priv->clk); |
676f23ea | 848 | return ret; |
2bb70056 OR |
849 | } |
850 | ||
851 | static int imx_mu_remove(struct platform_device *pdev) | |
852 | { | |
853 | struct imx_mu_priv *priv = platform_get_drvdata(pdev); | |
854 | ||
676f23ea | 855 | pm_runtime_disable(priv->dev); |
2bb70056 OR |
856 | |
857 | return 0; | |
858 | } | |
859 | ||
63b38357 PF |
860 | static const struct imx_mu_dcfg imx_mu_cfg_imx6sx = { |
861 | .tx = imx_mu_generic_tx, | |
862 | .rx = imx_mu_generic_rx, | |
315d2e56 | 863 | .rxdb = imx_mu_generic_rxdb, |
63b38357 | 864 | .init = imx_mu_init_generic, |
32f7443d PF |
865 | .xTR = 0x0, |
866 | .xRR = 0x10, | |
f689a7cf PF |
867 | .xSR = {0x20, 0x20, 0x20, 0x20}, |
868 | .xCR = {0x24, 0x24, 0x24, 0x24}, | |
63b38357 PF |
869 | }; |
870 | ||
871 | static const struct imx_mu_dcfg imx_mu_cfg_imx7ulp = { | |
872 | .tx = imx_mu_generic_tx, | |
873 | .rx = imx_mu_generic_rx, | |
315d2e56 | 874 | .rxdb = imx_mu_generic_rxdb, |
63b38357 | 875 | .init = imx_mu_init_generic, |
32f7443d PF |
876 | .xTR = 0x20, |
877 | .xRR = 0x40, | |
f689a7cf PF |
878 | .xSR = {0x60, 0x60, 0x60, 0x60}, |
879 | .xCR = {0x64, 0x64, 0x64, 0x64}, | |
63b38357 PF |
880 | }; |
881 | ||
4f0b776e PF |
882 | static const struct imx_mu_dcfg imx_mu_cfg_imx8ulp = { |
883 | .tx = imx_mu_generic_tx, | |
884 | .rx = imx_mu_generic_rx, | |
315d2e56 | 885 | .rxdb = imx_mu_generic_rxdb, |
4f0b776e PF |
886 | .init = imx_mu_init_generic, |
887 | .type = IMX_MU_V2, | |
888 | .xTR = 0x200, | |
889 | .xRR = 0x280, | |
890 | .xSR = {0xC, 0x118, 0x124, 0x12C}, | |
891 | .xCR = {0x110, 0x114, 0x120, 0x128}, | |
892 | }; | |
893 | ||
97961f78 PF |
894 | static const struct imx_mu_dcfg imx_mu_cfg_imx8ulp_s4 = { |
895 | .tx = imx_mu_specific_tx, | |
896 | .rx = imx_mu_specific_rx, | |
897 | .init = imx_mu_init_specific, | |
898 | .type = IMX_MU_V2 | IMX_MU_V2_S4, | |
899 | .xTR = 0x200, | |
900 | .xRR = 0x280, | |
901 | .xSR = {0xC, 0x118, 0x124, 0x12C}, | |
902 | .xCR = {0x110, 0x114, 0x120, 0x128}, | |
903 | }; | |
904 | ||
0184cc20 PF |
905 | static const struct imx_mu_dcfg imx_mu_cfg_imx93_s4 = { |
906 | .tx = imx_mu_specific_tx, | |
907 | .rx = imx_mu_specific_rx, | |
908 | .init = imx_mu_init_specific, | |
909 | .type = IMX_MU_V2 | IMX_MU_V2_S4 | IMX_MU_V2_IRQ, | |
910 | .xTR = 0x200, | |
911 | .xRR = 0x280, | |
912 | .xSR = {0xC, 0x118, 0x124, 0x12C}, | |
913 | .xCR = {0x110, 0x114, 0x120, 0x128}, | |
914 | }; | |
915 | ||
0a67003b | 916 | static const struct imx_mu_dcfg imx_mu_cfg_imx8_scu = { |
97961f78 PF |
917 | .tx = imx_mu_specific_tx, |
918 | .rx = imx_mu_specific_rx, | |
919 | .init = imx_mu_init_specific, | |
315d2e56 | 920 | .rxdb = imx_mu_generic_rxdb, |
4f0b776e PF |
921 | .xTR = 0x0, |
922 | .xRR = 0x10, | |
f689a7cf PF |
923 | .xSR = {0x20, 0x20, 0x20, 0x20}, |
924 | .xCR = {0x24, 0x24, 0x24, 0x24}, | |
0a67003b PF |
925 | }; |
926 | ||
11dac1d3 FL |
927 | static const struct imx_mu_dcfg imx_mu_cfg_imx8_seco = { |
928 | .tx = imx_mu_seco_tx, | |
929 | .rx = imx_mu_generic_rx, | |
930 | .rxdb = imx_mu_seco_rxdb, | |
931 | .init = imx_mu_init_seco, | |
932 | .xTR = 0x0, | |
933 | .xRR = 0x10, | |
934 | .xSR = {0x20, 0x20, 0x20, 0x20}, | |
935 | .xCR = {0x24, 0x24, 0x24, 0x24}, | |
936 | }; | |
937 | ||
2bb70056 | 938 | static const struct of_device_id imx_mu_dt_ids[] = { |
c6c6bc6e RZ |
939 | { .compatible = "fsl,imx7ulp-mu", .data = &imx_mu_cfg_imx7ulp }, |
940 | { .compatible = "fsl,imx6sx-mu", .data = &imx_mu_cfg_imx6sx }, | |
4f0b776e | 941 | { .compatible = "fsl,imx8ulp-mu", .data = &imx_mu_cfg_imx8ulp }, |
97961f78 | 942 | { .compatible = "fsl,imx8ulp-mu-s4", .data = &imx_mu_cfg_imx8ulp_s4 }, |
0184cc20 | 943 | { .compatible = "fsl,imx93-mu-s4", .data = &imx_mu_cfg_imx93_s4 }, |
0a67003b | 944 | { .compatible = "fsl,imx8-mu-scu", .data = &imx_mu_cfg_imx8_scu }, |
11dac1d3 | 945 | { .compatible = "fsl,imx8-mu-seco", .data = &imx_mu_cfg_imx8_seco }, |
2bb70056 OR |
946 | { }, |
947 | }; | |
948 | MODULE_DEVICE_TABLE(of, imx_mu_dt_ids); | |
949 | ||
03b70130 | 950 | static int __maybe_unused imx_mu_suspend_noirq(struct device *dev) |
ba5f9fa0 DA |
951 | { |
952 | struct imx_mu_priv *priv = dev_get_drvdata(dev); | |
f689a7cf | 953 | int i; |
ba5f9fa0 | 954 | |
f689a7cf PF |
955 | if (!priv->clk) { |
956 | for (i = 0; i < IMX_MU_xCR_MAX; i++) | |
957 | priv->xcr[i] = imx_mu_read(priv, priv->dcfg->xCR[i]); | |
958 | } | |
ba5f9fa0 | 959 | |
892cb524 RG |
960 | priv->suspend = true; |
961 | ||
ba5f9fa0 DA |
962 | return 0; |
963 | } | |
964 | ||
03b70130 | 965 | static int __maybe_unused imx_mu_resume_noirq(struct device *dev) |
ba5f9fa0 DA |
966 | { |
967 | struct imx_mu_priv *priv = dev_get_drvdata(dev); | |
f689a7cf | 968 | int i; |
ba5f9fa0 DA |
969 | |
970 | /* | |
971 | * ONLY restore MU when context lost, the TIE could | |
972 | * be set during noirq resume as there is MU data | |
973 | * communication going on, and restore the saved | |
974 | * value will overwrite the TIE and cause MU data | |
975 | * send failed, may lead to system freeze. This issue | |
976 | * is observed by testing freeze mode suspend. | |
977 | */ | |
8219efd0 | 978 | if (!priv->clk && !imx_mu_read(priv, priv->dcfg->xCR[0])) { |
f689a7cf PF |
979 | for (i = 0; i < IMX_MU_xCR_MAX; i++) |
980 | imx_mu_write(priv, priv->xcr[i], priv->dcfg->xCR[i]); | |
981 | } | |
ba5f9fa0 | 982 | |
892cb524 RG |
983 | priv->suspend = false; |
984 | ||
ba5f9fa0 DA |
985 | return 0; |
986 | } | |
987 | ||
03b70130 | 988 | static int __maybe_unused imx_mu_runtime_suspend(struct device *dev) |
bb2b2624 AH |
989 | { |
990 | struct imx_mu_priv *priv = dev_get_drvdata(dev); | |
991 | ||
992 | clk_disable_unprepare(priv->clk); | |
993 | ||
994 | return 0; | |
995 | } | |
996 | ||
03b70130 | 997 | static int __maybe_unused imx_mu_runtime_resume(struct device *dev) |
bb2b2624 AH |
998 | { |
999 | struct imx_mu_priv *priv = dev_get_drvdata(dev); | |
1000 | int ret; | |
1001 | ||
1002 | ret = clk_prepare_enable(priv->clk); | |
1003 | if (ret) | |
1004 | dev_err(dev, "failed to enable clock\n"); | |
1005 | ||
1006 | return ret; | |
1007 | } | |
1008 | ||
ba5f9fa0 DA |
1009 | static const struct dev_pm_ops imx_mu_pm_ops = { |
1010 | SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(imx_mu_suspend_noirq, | |
1011 | imx_mu_resume_noirq) | |
bb2b2624 AH |
1012 | SET_RUNTIME_PM_OPS(imx_mu_runtime_suspend, |
1013 | imx_mu_runtime_resume, NULL) | |
ba5f9fa0 DA |
1014 | }; |
1015 | ||
2bb70056 OR |
1016 | static struct platform_driver imx_mu_driver = { |
1017 | .probe = imx_mu_probe, | |
1018 | .remove = imx_mu_remove, | |
1019 | .driver = { | |
1020 | .name = "imx_mu", | |
1021 | .of_match_table = imx_mu_dt_ids, | |
ba5f9fa0 | 1022 | .pm = &imx_mu_pm_ops, |
2bb70056 OR |
1023 | }, |
1024 | }; | |
1025 | module_platform_driver(imx_mu_driver); | |
1026 | ||
1027 | MODULE_AUTHOR("Oleksij Rempel <o.rempel@pengutronix.de>"); | |
1028 | MODULE_DESCRIPTION("Message Unit driver for i.MX"); | |
1029 | MODULE_LICENSE("GPL v2"); |