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