1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2017 Pengutronix, Oleksij Rempel <kernel@pengutronix.de> |
4 | */ |
5 | |
6 | #include <dt-bindings/firmware/imx/rsrc.h> |
7 | #include <linux/arm-smccc.h> |
8 | #include <linux/clk.h> |
9 | #include <linux/err.h> |
10 | #include <linux/firmware/imx/sci.h> |
11 | #include <linux/interrupt.h> |
12 | #include <linux/kernel.h> |
13 | #include <linux/mailbox_client.h> |
14 | #include <linux/mfd/syscon.h> |
15 | #include <linux/module.h> |
16 | #include <linux/of.h> |
17 | #include <linux/of_address.h> |
18 | #include <linux/of_reserved_mem.h> |
19 | #include <linux/platform_device.h> |
20 | #include <linux/pm_domain.h> |
21 | #include <linux/regmap.h> |
22 | #include <linux/remoteproc.h> |
23 | #include <linux/workqueue.h> |
24 | |
25 | #include "imx_rproc.h" |
26 | #include "remoteproc_internal.h" |
27 | |
28 | #define IMX7D_SRC_SCR 0x0C |
29 | #define IMX7D_ENABLE_M4 BIT(3) |
30 | #define IMX7D_SW_M4P_RST BIT(2) |
31 | #define IMX7D_SW_M4C_RST BIT(1) |
32 | #define IMX7D_SW_M4C_NON_SCLR_RST BIT(0) |
33 | |
34 | #define IMX7D_M4_RST_MASK (IMX7D_ENABLE_M4 | IMX7D_SW_M4P_RST \ |
35 | | IMX7D_SW_M4C_RST \ |
36 | | IMX7D_SW_M4C_NON_SCLR_RST) |
37 | |
38 | #define IMX7D_M4_START (IMX7D_ENABLE_M4 | IMX7D_SW_M4P_RST \ |
39 | | IMX7D_SW_M4C_RST) |
40 | #define IMX7D_M4_STOP (IMX7D_ENABLE_M4 | IMX7D_SW_M4C_RST | \ |
41 | IMX7D_SW_M4C_NON_SCLR_RST) |
42 | |
43 | #define IMX8M_M7_STOP (IMX7D_ENABLE_M4 | IMX7D_SW_M4C_RST) |
44 | #define IMX8M_M7_POLL IMX7D_ENABLE_M4 |
45 | |
46 | #define IMX8M_GPR22 0x58 |
47 | #define IMX8M_GPR22_CM7_CPUWAIT BIT(0) |
48 | |
49 | /* Address: 0x020D8000 */ |
50 | #define IMX6SX_SRC_SCR 0x00 |
51 | #define IMX6SX_ENABLE_M4 BIT(22) |
52 | #define IMX6SX_SW_M4P_RST BIT(12) |
53 | #define IMX6SX_SW_M4C_NON_SCLR_RST BIT(4) |
54 | #define IMX6SX_SW_M4C_RST BIT(3) |
55 | |
56 | #define IMX6SX_M4_START (IMX6SX_ENABLE_M4 | IMX6SX_SW_M4P_RST \ |
57 | | IMX6SX_SW_M4C_RST) |
58 | #define IMX6SX_M4_STOP (IMX6SX_ENABLE_M4 | IMX6SX_SW_M4C_RST | \ |
59 | IMX6SX_SW_M4C_NON_SCLR_RST) |
60 | #define IMX6SX_M4_RST_MASK (IMX6SX_ENABLE_M4 | IMX6SX_SW_M4P_RST \ |
61 | | IMX6SX_SW_M4C_NON_SCLR_RST \ |
62 | | IMX6SX_SW_M4C_RST) |
63 | |
64 | #define IMX_RPROC_MEM_MAX 32 |
65 | |
66 | #define IMX_SIP_RPROC 0xC2000005 |
67 | #define IMX_SIP_RPROC_START 0x00 |
68 | #define IMX_SIP_RPROC_STARTED 0x01 |
69 | #define IMX_SIP_RPROC_STOP 0x02 |
70 | |
71 | #define IMX_SC_IRQ_GROUP_REBOOTED 5 |
72 | |
73 | /** |
74 | * struct imx_rproc_mem - slim internal memory structure |
75 | * @cpu_addr: MPU virtual address of the memory region |
76 | * @sys_addr: Bus address used to access the memory region |
77 | * @size: Size of the memory region |
78 | */ |
79 | struct imx_rproc_mem { |
80 | void __iomem *cpu_addr; |
81 | phys_addr_t sys_addr; |
82 | size_t size; |
83 | }; |
84 | |
85 | /* att flags: lower 16 bits specifying core, higher 16 bits for flags */ |
86 | /* M4 own area. Can be mapped at probe */ |
87 | #define ATT_OWN BIT(31) |
88 | #define ATT_IOMEM BIT(30) |
89 | |
90 | #define ATT_CORE_MASK 0xffff |
91 | #define ATT_CORE(I) BIT((I)) |
92 | |
93 | static int imx_rproc_xtr_mbox_init(struct rproc *rproc); |
94 | static void imx_rproc_free_mbox(struct rproc *rproc); |
95 | |
96 | struct imx_rproc { |
97 | struct device *dev; |
98 | struct regmap *regmap; |
99 | struct regmap *gpr; |
100 | struct rproc *rproc; |
101 | const struct imx_rproc_dcfg *dcfg; |
102 | struct imx_rproc_mem mem[IMX_RPROC_MEM_MAX]; |
103 | struct clk *clk; |
104 | struct mbox_client cl; |
105 | struct mbox_chan *tx_ch; |
106 | struct mbox_chan *rx_ch; |
107 | struct work_struct rproc_work; |
108 | struct workqueue_struct *workqueue; |
109 | void __iomem *rsc_table; |
110 | struct imx_sc_ipc *ipc_handle; |
111 | struct notifier_block rproc_nb; |
112 | u32 rproc_pt; /* partition id */ |
113 | u32 rsrc_id; /* resource id */ |
114 | u32 entry; /* cpu start address */ |
115 | u32 core_index; |
116 | struct dev_pm_domain_list *pd_list; |
117 | }; |
118 | |
119 | static const struct imx_rproc_att imx_rproc_att_imx93[] = { |
120 | /* dev addr , sys addr , size , flags */ |
121 | /* TCM CODE NON-SECURE */ |
122 | { 0x0FFC0000, 0x201C0000, 0x00020000, ATT_OWN | ATT_IOMEM }, |
123 | { 0x0FFE0000, 0x201E0000, 0x00020000, ATT_OWN | ATT_IOMEM }, |
124 | |
125 | /* TCM CODE SECURE */ |
126 | { 0x1FFC0000, 0x201C0000, 0x00020000, ATT_OWN | ATT_IOMEM }, |
127 | { 0x1FFE0000, 0x201E0000, 0x00020000, ATT_OWN | ATT_IOMEM }, |
128 | |
129 | /* TCM SYS NON-SECURE*/ |
130 | { 0x20000000, 0x20200000, 0x00020000, ATT_OWN | ATT_IOMEM }, |
131 | { 0x20020000, 0x20220000, 0x00020000, ATT_OWN | ATT_IOMEM }, |
132 | |
133 | /* TCM SYS SECURE*/ |
134 | { 0x30000000, 0x20200000, 0x00020000, ATT_OWN | ATT_IOMEM }, |
135 | { 0x30020000, 0x20220000, 0x00020000, ATT_OWN | ATT_IOMEM }, |
136 | |
137 | /* DDR */ |
138 | { 0x80000000, 0x80000000, 0x10000000, 0 }, |
139 | { 0x90000000, 0x80000000, 0x10000000, 0 }, |
140 | |
141 | { 0xC0000000, 0xC0000000, 0x10000000, 0 }, |
142 | { 0xD0000000, 0xC0000000, 0x10000000, 0 }, |
143 | }; |
144 | |
145 | static const struct imx_rproc_att imx_rproc_att_imx8qm[] = { |
146 | /* dev addr , sys addr , size , flags */ |
147 | { 0x08000000, 0x08000000, 0x10000000, 0}, |
148 | /* TCML */ |
149 | { 0x1FFE0000, 0x34FE0000, 0x00020000, ATT_OWN | ATT_IOMEM | ATT_CORE(0)}, |
150 | { 0x1FFE0000, 0x38FE0000, 0x00020000, ATT_OWN | ATT_IOMEM | ATT_CORE(1)}, |
151 | /* TCMU */ |
152 | { 0x20000000, 0x35000000, 0x00020000, ATT_OWN | ATT_IOMEM | ATT_CORE(0)}, |
153 | { 0x20000000, 0x39000000, 0x00020000, ATT_OWN | ATT_IOMEM | ATT_CORE(1)}, |
154 | /* DDR (Data) */ |
155 | { 0x80000000, 0x80000000, 0x60000000, 0 }, |
156 | }; |
157 | |
158 | static const struct imx_rproc_att imx_rproc_att_imx8qxp[] = { |
159 | { 0x08000000, 0x08000000, 0x10000000, 0 }, |
160 | /* TCML/U */ |
161 | { 0x1FFE0000, 0x34FE0000, 0x00040000, ATT_OWN | ATT_IOMEM }, |
162 | /* OCRAM(Low 96KB) */ |
163 | { 0x21000000, 0x00100000, 0x00018000, 0 }, |
164 | /* OCRAM */ |
165 | { 0x21100000, 0x00100000, 0x00040000, 0 }, |
166 | /* DDR (Data) */ |
167 | { 0x80000000, 0x80000000, 0x60000000, 0 }, |
168 | }; |
169 | |
170 | static const struct imx_rproc_att imx_rproc_att_imx8mn[] = { |
171 | /* dev addr , sys addr , size , flags */ |
172 | /* ITCM */ |
173 | { 0x00000000, 0x007E0000, 0x00020000, ATT_OWN | ATT_IOMEM }, |
174 | /* OCRAM_S */ |
175 | { 0x00180000, 0x00180000, 0x00009000, 0 }, |
176 | /* OCRAM */ |
177 | { 0x00900000, 0x00900000, 0x00020000, 0 }, |
178 | /* OCRAM */ |
179 | { 0x00920000, 0x00920000, 0x00020000, 0 }, |
180 | /* OCRAM */ |
181 | { 0x00940000, 0x00940000, 0x00050000, 0 }, |
182 | /* QSPI Code - alias */ |
183 | { 0x08000000, 0x08000000, 0x08000000, 0 }, |
184 | /* DDR (Code) - alias */ |
185 | { 0x10000000, 0x40000000, 0x0FFE0000, 0 }, |
186 | /* DTCM */ |
187 | { 0x20000000, 0x00800000, 0x00020000, ATT_OWN | ATT_IOMEM }, |
188 | /* OCRAM_S - alias */ |
189 | { 0x20180000, 0x00180000, 0x00008000, ATT_OWN }, |
190 | /* OCRAM */ |
191 | { 0x20200000, 0x00900000, 0x00020000, ATT_OWN }, |
192 | /* OCRAM */ |
193 | { 0x20220000, 0x00920000, 0x00020000, ATT_OWN }, |
194 | /* OCRAM */ |
195 | { 0x20240000, 0x00940000, 0x00040000, ATT_OWN }, |
196 | /* DDR (Data) */ |
197 | { 0x40000000, 0x40000000, 0x80000000, 0 }, |
198 | }; |
199 | |
200 | static const struct imx_rproc_att imx_rproc_att_imx8mq[] = { |
201 | /* dev addr , sys addr , size , flags */ |
202 | /* TCML - alias */ |
203 | { 0x00000000, 0x007e0000, 0x00020000, ATT_IOMEM}, |
204 | /* OCRAM_S */ |
205 | { 0x00180000, 0x00180000, 0x00008000, 0 }, |
206 | /* OCRAM */ |
207 | { 0x00900000, 0x00900000, 0x00020000, 0 }, |
208 | /* OCRAM */ |
209 | { 0x00920000, 0x00920000, 0x00020000, 0 }, |
210 | /* QSPI Code - alias */ |
211 | { 0x08000000, 0x08000000, 0x08000000, 0 }, |
212 | /* DDR (Code) - alias */ |
213 | { 0x10000000, 0x80000000, 0x0FFE0000, 0 }, |
214 | /* TCML */ |
215 | { 0x1FFE0000, 0x007E0000, 0x00020000, ATT_OWN | ATT_IOMEM}, |
216 | /* TCMU */ |
217 | { 0x20000000, 0x00800000, 0x00020000, ATT_OWN | ATT_IOMEM}, |
218 | /* OCRAM_S */ |
219 | { 0x20180000, 0x00180000, 0x00008000, ATT_OWN }, |
220 | /* OCRAM */ |
221 | { 0x20200000, 0x00900000, 0x00020000, ATT_OWN }, |
222 | /* OCRAM */ |
223 | { 0x20220000, 0x00920000, 0x00020000, ATT_OWN }, |
224 | /* DDR (Data) */ |
225 | { 0x40000000, 0x40000000, 0x80000000, 0 }, |
226 | }; |
227 | |
228 | static const struct imx_rproc_att imx_rproc_att_imx8ulp[] = { |
229 | {0x1FFC0000, 0x1FFC0000, 0xC0000, ATT_OWN}, |
230 | {0x21000000, 0x21000000, 0x10000, ATT_OWN}, |
231 | {0x80000000, 0x80000000, 0x60000000, 0} |
232 | }; |
233 | |
234 | static const struct imx_rproc_att imx_rproc_att_imx7ulp[] = { |
235 | {0x1FFD0000, 0x1FFD0000, 0x30000, ATT_OWN}, |
236 | {0x20000000, 0x20000000, 0x10000, ATT_OWN}, |
237 | {0x2F000000, 0x2F000000, 0x20000, ATT_OWN}, |
238 | {0x2F020000, 0x2F020000, 0x20000, ATT_OWN}, |
239 | {0x60000000, 0x60000000, 0x40000000, 0} |
240 | }; |
241 | |
242 | static const struct imx_rproc_att imx_rproc_att_imx7d[] = { |
243 | /* dev addr , sys addr , size , flags */ |
244 | /* OCRAM_S (M4 Boot code) - alias */ |
245 | { 0x00000000, 0x00180000, 0x00008000, 0 }, |
246 | /* OCRAM_S (Code) */ |
247 | { 0x00180000, 0x00180000, 0x00008000, ATT_OWN }, |
248 | /* OCRAM (Code) - alias */ |
249 | { 0x00900000, 0x00900000, 0x00020000, 0 }, |
250 | /* OCRAM_EPDC (Code) - alias */ |
251 | { 0x00920000, 0x00920000, 0x00020000, 0 }, |
252 | /* OCRAM_PXP (Code) - alias */ |
253 | { 0x00940000, 0x00940000, 0x00008000, 0 }, |
254 | /* TCML (Code) */ |
255 | { 0x1FFF8000, 0x007F8000, 0x00008000, ATT_OWN | ATT_IOMEM }, |
256 | /* DDR (Code) - alias, first part of DDR (Data) */ |
257 | { 0x10000000, 0x80000000, 0x0FFF0000, 0 }, |
258 | |
259 | /* TCMU (Data) */ |
260 | { 0x20000000, 0x00800000, 0x00008000, ATT_OWN | ATT_IOMEM }, |
261 | /* OCRAM (Data) */ |
262 | { 0x20200000, 0x00900000, 0x00020000, 0 }, |
263 | /* OCRAM_EPDC (Data) */ |
264 | { 0x20220000, 0x00920000, 0x00020000, 0 }, |
265 | /* OCRAM_PXP (Data) */ |
266 | { 0x20240000, 0x00940000, 0x00008000, 0 }, |
267 | /* DDR (Data) */ |
268 | { 0x80000000, 0x80000000, 0x60000000, 0 }, |
269 | }; |
270 | |
271 | static const struct imx_rproc_att imx_rproc_att_imx6sx[] = { |
272 | /* dev addr , sys addr , size , flags */ |
273 | /* TCML (M4 Boot Code) - alias */ |
274 | { 0x00000000, 0x007F8000, 0x00008000, ATT_IOMEM }, |
275 | /* OCRAM_S (Code) */ |
276 | { 0x00180000, 0x008F8000, 0x00004000, 0 }, |
277 | /* OCRAM_S (Code) - alias */ |
278 | { 0x00180000, 0x008FC000, 0x00004000, 0 }, |
279 | /* TCML (Code) */ |
280 | { 0x1FFF8000, 0x007F8000, 0x00008000, ATT_OWN | ATT_IOMEM }, |
281 | /* DDR (Code) - alias, first part of DDR (Data) */ |
282 | { 0x10000000, 0x80000000, 0x0FFF8000, 0 }, |
283 | |
284 | /* TCMU (Data) */ |
285 | { 0x20000000, 0x00800000, 0x00008000, ATT_OWN | ATT_IOMEM }, |
286 | /* OCRAM_S (Data) - alias? */ |
287 | { 0x208F8000, 0x008F8000, 0x00004000, 0 }, |
288 | /* DDR (Data) */ |
289 | { 0x80000000, 0x80000000, 0x60000000, 0 }, |
290 | }; |
291 | |
292 | static const struct imx_rproc_dcfg imx_rproc_cfg_imx8mn_mmio = { |
293 | .src_reg = IMX7D_SRC_SCR, |
294 | .src_mask = IMX7D_M4_RST_MASK, |
295 | .src_start = IMX7D_M4_START, |
296 | .src_stop = IMX8M_M7_STOP, |
297 | .gpr_reg = IMX8M_GPR22, |
298 | .gpr_wait = IMX8M_GPR22_CM7_CPUWAIT, |
299 | .att = imx_rproc_att_imx8mn, |
300 | .att_size = ARRAY_SIZE(imx_rproc_att_imx8mn), |
301 | .method = IMX_RPROC_MMIO, |
302 | }; |
303 | |
304 | static const struct imx_rproc_dcfg imx_rproc_cfg_imx8mn = { |
305 | .att = imx_rproc_att_imx8mn, |
306 | .att_size = ARRAY_SIZE(imx_rproc_att_imx8mn), |
307 | .method = IMX_RPROC_SMC, |
308 | }; |
309 | |
310 | static const struct imx_rproc_dcfg imx_rproc_cfg_imx8mq = { |
311 | .src_reg = IMX7D_SRC_SCR, |
312 | .src_mask = IMX7D_M4_RST_MASK, |
313 | .src_start = IMX7D_M4_START, |
314 | .src_stop = IMX7D_M4_STOP, |
315 | .att = imx_rproc_att_imx8mq, |
316 | .att_size = ARRAY_SIZE(imx_rproc_att_imx8mq), |
317 | .method = IMX_RPROC_MMIO, |
318 | }; |
319 | |
320 | static const struct imx_rproc_dcfg imx_rproc_cfg_imx8qm = { |
321 | .att = imx_rproc_att_imx8qm, |
322 | .att_size = ARRAY_SIZE(imx_rproc_att_imx8qm), |
323 | .method = IMX_RPROC_SCU_API, |
324 | }; |
325 | |
326 | static const struct imx_rproc_dcfg imx_rproc_cfg_imx8qxp = { |
327 | .att = imx_rproc_att_imx8qxp, |
328 | .att_size = ARRAY_SIZE(imx_rproc_att_imx8qxp), |
329 | .method = IMX_RPROC_SCU_API, |
330 | }; |
331 | |
332 | static const struct imx_rproc_dcfg imx_rproc_cfg_imx8ulp = { |
333 | .att = imx_rproc_att_imx8ulp, |
334 | .att_size = ARRAY_SIZE(imx_rproc_att_imx8ulp), |
335 | .method = IMX_RPROC_NONE, |
336 | }; |
337 | |
338 | static const struct imx_rproc_dcfg imx_rproc_cfg_imx7ulp = { |
339 | .att = imx_rproc_att_imx7ulp, |
340 | .att_size = ARRAY_SIZE(imx_rproc_att_imx7ulp), |
341 | .method = IMX_RPROC_NONE, |
342 | }; |
343 | |
344 | static const struct imx_rproc_dcfg imx_rproc_cfg_imx7d = { |
345 | .src_reg = IMX7D_SRC_SCR, |
346 | .src_mask = IMX7D_M4_RST_MASK, |
347 | .src_start = IMX7D_M4_START, |
348 | .src_stop = IMX7D_M4_STOP, |
349 | .att = imx_rproc_att_imx7d, |
350 | .att_size = ARRAY_SIZE(imx_rproc_att_imx7d), |
351 | .method = IMX_RPROC_MMIO, |
352 | }; |
353 | |
354 | static const struct imx_rproc_dcfg imx_rproc_cfg_imx6sx = { |
355 | .src_reg = IMX6SX_SRC_SCR, |
356 | .src_mask = IMX6SX_M4_RST_MASK, |
357 | .src_start = IMX6SX_M4_START, |
358 | .src_stop = IMX6SX_M4_STOP, |
359 | .att = imx_rproc_att_imx6sx, |
360 | .att_size = ARRAY_SIZE(imx_rproc_att_imx6sx), |
361 | .method = IMX_RPROC_MMIO, |
362 | }; |
363 | |
364 | static const struct imx_rproc_dcfg imx_rproc_cfg_imx93 = { |
365 | .att = imx_rproc_att_imx93, |
366 | .att_size = ARRAY_SIZE(imx_rproc_att_imx93), |
367 | .method = IMX_RPROC_SMC, |
368 | }; |
369 | |
370 | static int imx_rproc_start(struct rproc *rproc) |
371 | { |
372 | struct imx_rproc *priv = rproc->priv; |
373 | const struct imx_rproc_dcfg *dcfg = priv->dcfg; |
374 | struct device *dev = priv->dev; |
375 | struct arm_smccc_res res; |
376 | int ret; |
377 | |
378 | ret = imx_rproc_xtr_mbox_init(rproc); |
379 | if (ret) |
380 | return ret; |
381 | |
382 | switch (dcfg->method) { |
383 | case IMX_RPROC_MMIO: |
384 | if (priv->gpr) { |
385 | ret = regmap_clear_bits(map: priv->gpr, reg: dcfg->gpr_reg, |
386 | bits: dcfg->gpr_wait); |
387 | } else { |
388 | ret = regmap_update_bits(map: priv->regmap, reg: dcfg->src_reg, |
389 | mask: dcfg->src_mask, |
390 | val: dcfg->src_start); |
391 | } |
392 | break; |
393 | case IMX_RPROC_SMC: |
394 | arm_smccc_smc(IMX_SIP_RPROC, IMX_SIP_RPROC_START, 0, 0, 0, 0, 0, 0, &res); |
395 | ret = res.a0; |
396 | break; |
397 | case IMX_RPROC_SCU_API: |
398 | ret = imx_sc_pm_cpu_start(ipc: priv->ipc_handle, resource: priv->rsrc_id, enable: true, phys_addr: priv->entry); |
399 | break; |
400 | default: |
401 | return -EOPNOTSUPP; |
402 | } |
403 | |
404 | if (ret) |
405 | dev_err(dev, "Failed to enable remote core!\n" ); |
406 | |
407 | return ret; |
408 | } |
409 | |
410 | static int imx_rproc_stop(struct rproc *rproc) |
411 | { |
412 | struct imx_rproc *priv = rproc->priv; |
413 | const struct imx_rproc_dcfg *dcfg = priv->dcfg; |
414 | struct device *dev = priv->dev; |
415 | struct arm_smccc_res res; |
416 | int ret; |
417 | |
418 | switch (dcfg->method) { |
419 | case IMX_RPROC_MMIO: |
420 | if (priv->gpr) { |
421 | ret = regmap_set_bits(map: priv->gpr, reg: dcfg->gpr_reg, |
422 | bits: dcfg->gpr_wait); |
423 | if (ret) { |
424 | dev_err(priv->dev, |
425 | "Failed to quiescence M4 platform!\n" ); |
426 | return ret; |
427 | } |
428 | } |
429 | |
430 | ret = regmap_update_bits(map: priv->regmap, reg: dcfg->src_reg, mask: dcfg->src_mask, |
431 | val: dcfg->src_stop); |
432 | break; |
433 | case IMX_RPROC_SMC: |
434 | arm_smccc_smc(IMX_SIP_RPROC, IMX_SIP_RPROC_STOP, 0, 0, 0, 0, 0, 0, &res); |
435 | ret = res.a0; |
436 | if (res.a1) |
437 | dev_info(dev, "Not in wfi, force stopped\n" ); |
438 | break; |
439 | case IMX_RPROC_SCU_API: |
440 | ret = imx_sc_pm_cpu_start(ipc: priv->ipc_handle, resource: priv->rsrc_id, enable: false, phys_addr: priv->entry); |
441 | break; |
442 | default: |
443 | return -EOPNOTSUPP; |
444 | } |
445 | |
446 | if (ret) |
447 | dev_err(dev, "Failed to stop remote core\n" ); |
448 | else |
449 | imx_rproc_free_mbox(rproc); |
450 | |
451 | return ret; |
452 | } |
453 | |
454 | static int imx_rproc_da_to_sys(struct imx_rproc *priv, u64 da, |
455 | size_t len, u64 *sys, bool *is_iomem) |
456 | { |
457 | const struct imx_rproc_dcfg *dcfg = priv->dcfg; |
458 | int i; |
459 | |
460 | /* parse address translation table */ |
461 | for (i = 0; i < dcfg->att_size; i++) { |
462 | const struct imx_rproc_att *att = &dcfg->att[i]; |
463 | |
464 | /* |
465 | * Ignore entries not belong to current core: |
466 | * i.MX8QM has dual general M4_[0,1] cores, M4_0's own entries |
467 | * has "ATT_CORE(0) & BIT(0)" true, M4_1's own entries has |
468 | * "ATT_CORE(1) & BIT(1)" true. |
469 | */ |
470 | if (att->flags & ATT_CORE_MASK) { |
471 | if (!((BIT(priv->core_index)) & (att->flags & ATT_CORE_MASK))) |
472 | continue; |
473 | } |
474 | |
475 | if (da >= att->da && da + len < att->da + att->size) { |
476 | unsigned int offset = da - att->da; |
477 | |
478 | *sys = att->sa + offset; |
479 | if (is_iomem) |
480 | *is_iomem = att->flags & ATT_IOMEM; |
481 | return 0; |
482 | } |
483 | } |
484 | |
485 | dev_warn(priv->dev, "Translation failed: da = 0x%llx len = 0x%zx\n" , |
486 | da, len); |
487 | return -ENOENT; |
488 | } |
489 | |
490 | static void *imx_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem) |
491 | { |
492 | struct imx_rproc *priv = rproc->priv; |
493 | void *va = NULL; |
494 | u64 sys; |
495 | int i; |
496 | |
497 | if (len == 0) |
498 | return NULL; |
499 | |
500 | /* |
501 | * On device side we have many aliases, so we need to convert device |
502 | * address (M4) to system bus address first. |
503 | */ |
504 | if (imx_rproc_da_to_sys(priv, da, len, sys: &sys, is_iomem)) |
505 | return NULL; |
506 | |
507 | for (i = 0; i < IMX_RPROC_MEM_MAX; i++) { |
508 | if (sys >= priv->mem[i].sys_addr && sys + len < |
509 | priv->mem[i].sys_addr + priv->mem[i].size) { |
510 | unsigned int offset = sys - priv->mem[i].sys_addr; |
511 | /* __force to make sparse happy with type conversion */ |
512 | va = (__force void *)(priv->mem[i].cpu_addr + offset); |
513 | break; |
514 | } |
515 | } |
516 | |
517 | dev_dbg(&rproc->dev, "da = 0x%llx len = 0x%zx va = 0x%p\n" , |
518 | da, len, va); |
519 | |
520 | return va; |
521 | } |
522 | |
523 | static int imx_rproc_mem_alloc(struct rproc *rproc, |
524 | struct rproc_mem_entry *mem) |
525 | { |
526 | struct device *dev = rproc->dev.parent; |
527 | void *va; |
528 | |
529 | dev_dbg(dev, "map memory: %p+%zx\n" , &mem->dma, mem->len); |
530 | va = ioremap_wc(offset: mem->dma, size: mem->len); |
531 | if (IS_ERR_OR_NULL(ptr: va)) { |
532 | dev_err(dev, "Unable to map memory region: %p+%zx\n" , |
533 | &mem->dma, mem->len); |
534 | return -ENOMEM; |
535 | } |
536 | |
537 | /* Update memory entry va */ |
538 | mem->va = va; |
539 | |
540 | return 0; |
541 | } |
542 | |
543 | static int imx_rproc_mem_release(struct rproc *rproc, |
544 | struct rproc_mem_entry *mem) |
545 | { |
546 | dev_dbg(rproc->dev.parent, "unmap memory: %pa\n" , &mem->dma); |
547 | iounmap(addr: mem->va); |
548 | |
549 | return 0; |
550 | } |
551 | |
552 | static int imx_rproc_prepare(struct rproc *rproc) |
553 | { |
554 | struct imx_rproc *priv = rproc->priv; |
555 | struct device_node *np = priv->dev->of_node; |
556 | struct of_phandle_iterator it; |
557 | struct rproc_mem_entry *mem; |
558 | struct reserved_mem *rmem; |
559 | u32 da; |
560 | |
561 | /* Register associated reserved memory regions */ |
562 | of_phandle_iterator_init(it: &it, np, list_name: "memory-region" , NULL, cell_count: 0); |
563 | while (of_phandle_iterator_next(it: &it) == 0) { |
564 | /* |
565 | * Ignore the first memory region which will be used vdev buffer. |
566 | * No need to do extra handlings, rproc_add_virtio_dev will handle it. |
567 | */ |
568 | if (!strcmp(it.node->name, "vdev0buffer" )) |
569 | continue; |
570 | |
571 | if (!strcmp(it.node->name, "rsc-table" )) |
572 | continue; |
573 | |
574 | rmem = of_reserved_mem_lookup(np: it.node); |
575 | if (!rmem) { |
576 | of_node_put(node: it.node); |
577 | dev_err(priv->dev, "unable to acquire memory-region\n" ); |
578 | return -EINVAL; |
579 | } |
580 | |
581 | /* No need to translate pa to da, i.MX use same map */ |
582 | da = rmem->base; |
583 | |
584 | /* Register memory region */ |
585 | mem = rproc_mem_entry_init(dev: priv->dev, NULL, dma: (dma_addr_t)rmem->base, len: rmem->size, da, |
586 | alloc: imx_rproc_mem_alloc, release: imx_rproc_mem_release, |
587 | name: it.node->name); |
588 | |
589 | if (mem) { |
590 | rproc_coredump_add_segment(rproc, da, size: rmem->size); |
591 | } else { |
592 | of_node_put(node: it.node); |
593 | return -ENOMEM; |
594 | } |
595 | |
596 | rproc_add_carveout(rproc, mem); |
597 | } |
598 | |
599 | return 0; |
600 | } |
601 | |
602 | static int imx_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) |
603 | { |
604 | int ret; |
605 | |
606 | ret = rproc_elf_load_rsc_table(rproc, fw); |
607 | if (ret) |
608 | dev_info(&rproc->dev, "No resource table in elf\n" ); |
609 | |
610 | return 0; |
611 | } |
612 | |
613 | static void imx_rproc_kick(struct rproc *rproc, int vqid) |
614 | { |
615 | struct imx_rproc *priv = rproc->priv; |
616 | int err; |
617 | __u32 mmsg; |
618 | |
619 | if (!priv->tx_ch) { |
620 | dev_err(priv->dev, "No initialized mbox tx channel\n" ); |
621 | return; |
622 | } |
623 | |
624 | /* |
625 | * Send the index of the triggered virtqueue as the mu payload. |
626 | * Let remote processor know which virtqueue is used. |
627 | */ |
628 | mmsg = vqid << 16; |
629 | |
630 | err = mbox_send_message(chan: priv->tx_ch, mssg: (void *)&mmsg); |
631 | if (err < 0) |
632 | dev_err(priv->dev, "%s: failed (%d, err:%d)\n" , |
633 | __func__, vqid, err); |
634 | } |
635 | |
636 | static int imx_rproc_attach(struct rproc *rproc) |
637 | { |
638 | return imx_rproc_xtr_mbox_init(rproc); |
639 | } |
640 | |
641 | static int imx_rproc_detach(struct rproc *rproc) |
642 | { |
643 | struct imx_rproc *priv = rproc->priv; |
644 | const struct imx_rproc_dcfg *dcfg = priv->dcfg; |
645 | |
646 | if (dcfg->method != IMX_RPROC_SCU_API) |
647 | return -EOPNOTSUPP; |
648 | |
649 | if (imx_sc_rm_is_resource_owned(ipc: priv->ipc_handle, resource: priv->rsrc_id)) |
650 | return -EOPNOTSUPP; |
651 | |
652 | imx_rproc_free_mbox(rproc); |
653 | |
654 | return 0; |
655 | } |
656 | |
657 | static struct resource_table *imx_rproc_get_loaded_rsc_table(struct rproc *rproc, size_t *table_sz) |
658 | { |
659 | struct imx_rproc *priv = rproc->priv; |
660 | |
661 | /* The resource table has already been mapped in imx_rproc_addr_init */ |
662 | if (!priv->rsc_table) |
663 | return NULL; |
664 | |
665 | *table_sz = SZ_1K; |
666 | return (struct resource_table *)priv->rsc_table; |
667 | } |
668 | |
669 | static const struct rproc_ops imx_rproc_ops = { |
670 | .prepare = imx_rproc_prepare, |
671 | .attach = imx_rproc_attach, |
672 | .detach = imx_rproc_detach, |
673 | .start = imx_rproc_start, |
674 | .stop = imx_rproc_stop, |
675 | .kick = imx_rproc_kick, |
676 | .da_to_va = imx_rproc_da_to_va, |
677 | .load = rproc_elf_load_segments, |
678 | .parse_fw = imx_rproc_parse_fw, |
679 | .find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table, |
680 | .get_loaded_rsc_table = imx_rproc_get_loaded_rsc_table, |
681 | .sanity_check = rproc_elf_sanity_check, |
682 | .get_boot_addr = rproc_elf_get_boot_addr, |
683 | }; |
684 | |
685 | static int imx_rproc_addr_init(struct imx_rproc *priv, |
686 | struct platform_device *pdev) |
687 | { |
688 | const struct imx_rproc_dcfg *dcfg = priv->dcfg; |
689 | struct device *dev = &pdev->dev; |
690 | struct device_node *np = dev->of_node; |
691 | int a, b = 0, err, nph; |
692 | |
693 | /* remap required addresses */ |
694 | for (a = 0; a < dcfg->att_size; a++) { |
695 | const struct imx_rproc_att *att = &dcfg->att[a]; |
696 | |
697 | if (!(att->flags & ATT_OWN)) |
698 | continue; |
699 | |
700 | if (b >= IMX_RPROC_MEM_MAX) |
701 | break; |
702 | |
703 | if (att->flags & ATT_IOMEM) |
704 | priv->mem[b].cpu_addr = devm_ioremap(dev: &pdev->dev, |
705 | offset: att->sa, size: att->size); |
706 | else |
707 | priv->mem[b].cpu_addr = devm_ioremap_wc(dev: &pdev->dev, |
708 | offset: att->sa, size: att->size); |
709 | if (!priv->mem[b].cpu_addr) { |
710 | dev_err(dev, "failed to remap %#x bytes from %#x\n" , att->size, att->sa); |
711 | return -ENOMEM; |
712 | } |
713 | priv->mem[b].sys_addr = att->sa; |
714 | priv->mem[b].size = att->size; |
715 | b++; |
716 | } |
717 | |
718 | /* memory-region is optional property */ |
719 | nph = of_count_phandle_with_args(np, list_name: "memory-region" , NULL); |
720 | if (nph <= 0) |
721 | return 0; |
722 | |
723 | /* remap optional addresses */ |
724 | for (a = 0; a < nph; a++) { |
725 | struct device_node *node; |
726 | struct resource res; |
727 | |
728 | node = of_parse_phandle(np, phandle_name: "memory-region" , index: a); |
729 | /* Not map vdevbuffer, vdevring region */ |
730 | if (!strncmp(node->name, "vdev" , strlen("vdev" ))) { |
731 | of_node_put(node); |
732 | continue; |
733 | } |
734 | err = of_address_to_resource(dev: node, index: 0, r: &res); |
735 | of_node_put(node); |
736 | if (err) { |
737 | dev_err(dev, "unable to resolve memory region\n" ); |
738 | return err; |
739 | } |
740 | |
741 | if (b >= IMX_RPROC_MEM_MAX) |
742 | break; |
743 | |
744 | /* Not use resource version, because we might share region */ |
745 | priv->mem[b].cpu_addr = devm_ioremap_wc(dev: &pdev->dev, offset: res.start, size: resource_size(res: &res)); |
746 | if (!priv->mem[b].cpu_addr) { |
747 | dev_err(dev, "failed to remap %pr\n" , &res); |
748 | return -ENOMEM; |
749 | } |
750 | priv->mem[b].sys_addr = res.start; |
751 | priv->mem[b].size = resource_size(res: &res); |
752 | if (!strcmp(node->name, "rsc-table" )) |
753 | priv->rsc_table = priv->mem[b].cpu_addr; |
754 | b++; |
755 | } |
756 | |
757 | return 0; |
758 | } |
759 | |
760 | static int imx_rproc_notified_idr_cb(int id, void *ptr, void *data) |
761 | { |
762 | struct rproc *rproc = data; |
763 | |
764 | rproc_vq_interrupt(rproc, vq_id: id); |
765 | |
766 | return 0; |
767 | } |
768 | |
769 | static void imx_rproc_vq_work(struct work_struct *work) |
770 | { |
771 | struct imx_rproc *priv = container_of(work, struct imx_rproc, |
772 | rproc_work); |
773 | struct rproc *rproc = priv->rproc; |
774 | |
775 | idr_for_each(&rproc->notifyids, fn: imx_rproc_notified_idr_cb, data: rproc); |
776 | } |
777 | |
778 | static void imx_rproc_rx_callback(struct mbox_client *cl, void *msg) |
779 | { |
780 | struct rproc *rproc = dev_get_drvdata(dev: cl->dev); |
781 | struct imx_rproc *priv = rproc->priv; |
782 | |
783 | queue_work(wq: priv->workqueue, work: &priv->rproc_work); |
784 | } |
785 | |
786 | static int imx_rproc_xtr_mbox_init(struct rproc *rproc) |
787 | { |
788 | struct imx_rproc *priv = rproc->priv; |
789 | struct device *dev = priv->dev; |
790 | struct mbox_client *cl; |
791 | |
792 | /* |
793 | * stop() and detach() will free the mbox channels, so need |
794 | * to request mbox channels in start() and attach(). |
795 | * |
796 | * Because start() and attach() not able to handle mbox defer |
797 | * probe, imx_rproc_xtr_mbox_init is also called in probe(). |
798 | * The check is to avoid request mbox again when start() or |
799 | * attach() after probe() returns success. |
800 | */ |
801 | if (priv->tx_ch && priv->rx_ch) |
802 | return 0; |
803 | |
804 | if (!of_get_property(node: dev->of_node, name: "mbox-names" , NULL)) |
805 | return 0; |
806 | |
807 | cl = &priv->cl; |
808 | cl->dev = dev; |
809 | cl->tx_block = true; |
810 | cl->tx_tout = 100; |
811 | cl->knows_txdone = false; |
812 | cl->rx_callback = imx_rproc_rx_callback; |
813 | |
814 | priv->tx_ch = mbox_request_channel_byname(cl, name: "tx" ); |
815 | if (IS_ERR(ptr: priv->tx_ch)) |
816 | return dev_err_probe(dev: cl->dev, err: PTR_ERR(ptr: priv->tx_ch), |
817 | fmt: "failed to request tx mailbox channel\n" ); |
818 | |
819 | priv->rx_ch = mbox_request_channel_byname(cl, name: "rx" ); |
820 | if (IS_ERR(ptr: priv->rx_ch)) { |
821 | mbox_free_channel(chan: priv->tx_ch); |
822 | return dev_err_probe(dev: cl->dev, err: PTR_ERR(ptr: priv->rx_ch), |
823 | fmt: "failed to request rx mailbox channel\n" ); |
824 | } |
825 | |
826 | return 0; |
827 | } |
828 | |
829 | static void imx_rproc_free_mbox(struct rproc *rproc) |
830 | { |
831 | struct imx_rproc *priv = rproc->priv; |
832 | |
833 | if (priv->tx_ch) { |
834 | mbox_free_channel(chan: priv->tx_ch); |
835 | priv->tx_ch = NULL; |
836 | } |
837 | |
838 | if (priv->rx_ch) { |
839 | mbox_free_channel(chan: priv->rx_ch); |
840 | priv->rx_ch = NULL; |
841 | } |
842 | } |
843 | |
844 | static void imx_rproc_put_scu(struct rproc *rproc) |
845 | { |
846 | struct imx_rproc *priv = rproc->priv; |
847 | const struct imx_rproc_dcfg *dcfg = priv->dcfg; |
848 | |
849 | if (dcfg->method != IMX_RPROC_SCU_API) |
850 | return; |
851 | |
852 | if (imx_sc_rm_is_resource_owned(ipc: priv->ipc_handle, resource: priv->rsrc_id)) { |
853 | dev_pm_domain_detach_list(list: priv->pd_list); |
854 | return; |
855 | } |
856 | |
857 | imx_scu_irq_group_enable(IMX_SC_IRQ_GROUP_REBOOTED, BIT(priv->rproc_pt), enable: false); |
858 | imx_scu_irq_unregister_notifier(nb: &priv->rproc_nb); |
859 | } |
860 | |
861 | static int imx_rproc_partition_notify(struct notifier_block *nb, |
862 | unsigned long event, void *group) |
863 | { |
864 | struct imx_rproc *priv = container_of(nb, struct imx_rproc, rproc_nb); |
865 | |
866 | /* Ignore other irqs */ |
867 | if (!((event & BIT(priv->rproc_pt)) && (*(u8 *)group == IMX_SC_IRQ_GROUP_REBOOTED))) |
868 | return 0; |
869 | |
870 | rproc_report_crash(rproc: priv->rproc, type: RPROC_WATCHDOG); |
871 | |
872 | pr_info("Partition%d reset!\n" , priv->rproc_pt); |
873 | |
874 | return 0; |
875 | } |
876 | |
877 | static int imx_rproc_attach_pd(struct imx_rproc *priv) |
878 | { |
879 | struct device *dev = priv->dev; |
880 | int ret; |
881 | struct dev_pm_domain_attach_data pd_data = { |
882 | .pd_flags = PD_FLAG_DEV_LINK_ON, |
883 | }; |
884 | |
885 | /* |
886 | * If there is only one power-domain entry, the platform driver framework |
887 | * will handle it, no need handle it in this driver. |
888 | */ |
889 | if (dev->pm_domain) |
890 | return 0; |
891 | |
892 | ret = dev_pm_domain_attach_list(dev, data: &pd_data, list: &priv->pd_list); |
893 | return ret < 0 ? ret : 0; |
894 | } |
895 | |
896 | static int imx_rproc_detect_mode(struct imx_rproc *priv) |
897 | { |
898 | struct regmap_config config = { .name = "imx-rproc" }; |
899 | const struct imx_rproc_dcfg *dcfg = priv->dcfg; |
900 | struct device *dev = priv->dev; |
901 | struct regmap *regmap; |
902 | struct arm_smccc_res res; |
903 | int ret; |
904 | u32 val; |
905 | u8 pt; |
906 | |
907 | switch (dcfg->method) { |
908 | case IMX_RPROC_NONE: |
909 | priv->rproc->state = RPROC_DETACHED; |
910 | return 0; |
911 | case IMX_RPROC_SMC: |
912 | arm_smccc_smc(IMX_SIP_RPROC, IMX_SIP_RPROC_STARTED, 0, 0, 0, 0, 0, 0, &res); |
913 | if (res.a0) |
914 | priv->rproc->state = RPROC_DETACHED; |
915 | return 0; |
916 | case IMX_RPROC_SCU_API: |
917 | ret = imx_scu_get_handle(ipc: &priv->ipc_handle); |
918 | if (ret) |
919 | return ret; |
920 | ret = of_property_read_u32(np: dev->of_node, propname: "fsl,resource-id" , out_value: &priv->rsrc_id); |
921 | if (ret) { |
922 | dev_err(dev, "No fsl,resource-id property\n" ); |
923 | return ret; |
924 | } |
925 | |
926 | if (priv->rsrc_id == IMX_SC_R_M4_1_PID0) |
927 | priv->core_index = 1; |
928 | else |
929 | priv->core_index = 0; |
930 | |
931 | /* |
932 | * If Mcore resource is not owned by Acore partition, It is kicked by ROM, |
933 | * and Linux could only do IPC with Mcore and nothing else. |
934 | */ |
935 | if (imx_sc_rm_is_resource_owned(ipc: priv->ipc_handle, resource: priv->rsrc_id)) { |
936 | if (of_property_read_u32(np: dev->of_node, propname: "fsl,entry-address" , out_value: &priv->entry)) |
937 | return -EINVAL; |
938 | |
939 | return imx_rproc_attach_pd(priv); |
940 | } |
941 | |
942 | priv->rproc->state = RPROC_DETACHED; |
943 | priv->rproc->recovery_disabled = false; |
944 | rproc_set_feature(rproc: priv->rproc, feature: RPROC_FEAT_ATTACH_ON_RECOVERY); |
945 | |
946 | /* Get partition id and enable irq in SCFW */ |
947 | ret = imx_sc_rm_get_resource_owner(ipc: priv->ipc_handle, resource: priv->rsrc_id, pt: &pt); |
948 | if (ret) { |
949 | dev_err(dev, "not able to get resource owner\n" ); |
950 | return ret; |
951 | } |
952 | |
953 | priv->rproc_pt = pt; |
954 | priv->rproc_nb.notifier_call = imx_rproc_partition_notify; |
955 | |
956 | ret = imx_scu_irq_register_notifier(nb: &priv->rproc_nb); |
957 | if (ret) { |
958 | dev_err(dev, "register scu notifier failed, %d\n" , ret); |
959 | return ret; |
960 | } |
961 | |
962 | ret = imx_scu_irq_group_enable(IMX_SC_IRQ_GROUP_REBOOTED, BIT(priv->rproc_pt), |
963 | enable: true); |
964 | if (ret) { |
965 | imx_scu_irq_unregister_notifier(nb: &priv->rproc_nb); |
966 | dev_err(dev, "Enable irq failed, %d\n" , ret); |
967 | return ret; |
968 | } |
969 | |
970 | return 0; |
971 | default: |
972 | break; |
973 | } |
974 | |
975 | priv->gpr = syscon_regmap_lookup_by_phandle(np: dev->of_node, property: "fsl,iomuxc-gpr" ); |
976 | if (IS_ERR(ptr: priv->gpr)) |
977 | priv->gpr = NULL; |
978 | |
979 | regmap = syscon_regmap_lookup_by_phandle(np: dev->of_node, property: "syscon" ); |
980 | if (IS_ERR(ptr: regmap)) { |
981 | dev_err(dev, "failed to find syscon\n" ); |
982 | return PTR_ERR(ptr: regmap); |
983 | } |
984 | |
985 | priv->regmap = regmap; |
986 | regmap_attach_dev(dev, map: regmap, config: &config); |
987 | |
988 | if (priv->gpr) { |
989 | ret = regmap_read(map: priv->gpr, reg: dcfg->gpr_reg, val: &val); |
990 | if (val & dcfg->gpr_wait) { |
991 | /* |
992 | * After cold boot, the CM indicates its in wait |
993 | * state, but not fully powered off. Power it off |
994 | * fully so firmware can be loaded into it. |
995 | */ |
996 | imx_rproc_stop(rproc: priv->rproc); |
997 | return 0; |
998 | } |
999 | } |
1000 | |
1001 | ret = regmap_read(map: regmap, reg: dcfg->src_reg, val: &val); |
1002 | if (ret) { |
1003 | dev_err(dev, "Failed to read src\n" ); |
1004 | return ret; |
1005 | } |
1006 | |
1007 | if ((val & dcfg->src_mask) != dcfg->src_stop) |
1008 | priv->rproc->state = RPROC_DETACHED; |
1009 | |
1010 | return 0; |
1011 | } |
1012 | |
1013 | static int imx_rproc_clk_enable(struct imx_rproc *priv) |
1014 | { |
1015 | const struct imx_rproc_dcfg *dcfg = priv->dcfg; |
1016 | struct device *dev = priv->dev; |
1017 | int ret; |
1018 | |
1019 | /* Remote core is not under control of Linux */ |
1020 | if (dcfg->method == IMX_RPROC_NONE) |
1021 | return 0; |
1022 | |
1023 | priv->clk = devm_clk_get(dev, NULL); |
1024 | if (IS_ERR(ptr: priv->clk)) { |
1025 | dev_err(dev, "Failed to get clock\n" ); |
1026 | return PTR_ERR(ptr: priv->clk); |
1027 | } |
1028 | |
1029 | /* |
1030 | * clk for M4 block including memory. Should be |
1031 | * enabled before .start for FW transfer. |
1032 | */ |
1033 | ret = clk_prepare_enable(clk: priv->clk); |
1034 | if (ret) { |
1035 | dev_err(dev, "Failed to enable clock\n" ); |
1036 | return ret; |
1037 | } |
1038 | |
1039 | return 0; |
1040 | } |
1041 | |
1042 | static int imx_rproc_probe(struct platform_device *pdev) |
1043 | { |
1044 | struct device *dev = &pdev->dev; |
1045 | struct device_node *np = dev->of_node; |
1046 | struct imx_rproc *priv; |
1047 | struct rproc *rproc; |
1048 | const struct imx_rproc_dcfg *dcfg; |
1049 | int ret; |
1050 | |
1051 | /* set some other name then imx */ |
1052 | rproc = devm_rproc_alloc(dev, name: "imx-rproc" , ops: &imx_rproc_ops, |
1053 | NULL, len: sizeof(*priv)); |
1054 | if (!rproc) |
1055 | return -ENOMEM; |
1056 | |
1057 | dcfg = of_device_get_match_data(dev); |
1058 | if (!dcfg) |
1059 | return -EINVAL; |
1060 | |
1061 | priv = rproc->priv; |
1062 | priv->rproc = rproc; |
1063 | priv->dcfg = dcfg; |
1064 | priv->dev = dev; |
1065 | |
1066 | dev_set_drvdata(dev, data: rproc); |
1067 | priv->workqueue = create_workqueue(dev_name(dev)); |
1068 | if (!priv->workqueue) { |
1069 | dev_err(dev, "cannot create workqueue\n" ); |
1070 | return -ENOMEM; |
1071 | } |
1072 | |
1073 | ret = imx_rproc_xtr_mbox_init(rproc); |
1074 | if (ret) |
1075 | goto err_put_wkq; |
1076 | |
1077 | ret = imx_rproc_addr_init(priv, pdev); |
1078 | if (ret) { |
1079 | dev_err(dev, "failed on imx_rproc_addr_init\n" ); |
1080 | goto err_put_mbox; |
1081 | } |
1082 | |
1083 | ret = imx_rproc_detect_mode(priv); |
1084 | if (ret) |
1085 | goto err_put_mbox; |
1086 | |
1087 | ret = imx_rproc_clk_enable(priv); |
1088 | if (ret) |
1089 | goto err_put_scu; |
1090 | |
1091 | INIT_WORK(&priv->rproc_work, imx_rproc_vq_work); |
1092 | |
1093 | if (rproc->state != RPROC_DETACHED) |
1094 | rproc->auto_boot = of_property_read_bool(np, propname: "fsl,auto-boot" ); |
1095 | |
1096 | ret = rproc_add(rproc); |
1097 | if (ret) { |
1098 | dev_err(dev, "rproc_add failed\n" ); |
1099 | goto err_put_clk; |
1100 | } |
1101 | |
1102 | return 0; |
1103 | |
1104 | err_put_clk: |
1105 | clk_disable_unprepare(clk: priv->clk); |
1106 | err_put_scu: |
1107 | imx_rproc_put_scu(rproc); |
1108 | err_put_mbox: |
1109 | imx_rproc_free_mbox(rproc); |
1110 | err_put_wkq: |
1111 | destroy_workqueue(wq: priv->workqueue); |
1112 | |
1113 | return ret; |
1114 | } |
1115 | |
1116 | static void imx_rproc_remove(struct platform_device *pdev) |
1117 | { |
1118 | struct rproc *rproc = platform_get_drvdata(pdev); |
1119 | struct imx_rproc *priv = rproc->priv; |
1120 | |
1121 | clk_disable_unprepare(clk: priv->clk); |
1122 | rproc_del(rproc); |
1123 | imx_rproc_put_scu(rproc); |
1124 | imx_rproc_free_mbox(rproc); |
1125 | destroy_workqueue(wq: priv->workqueue); |
1126 | } |
1127 | |
1128 | static const struct of_device_id imx_rproc_of_match[] = { |
1129 | { .compatible = "fsl,imx7ulp-cm4" , .data = &imx_rproc_cfg_imx7ulp }, |
1130 | { .compatible = "fsl,imx7d-cm4" , .data = &imx_rproc_cfg_imx7d }, |
1131 | { .compatible = "fsl,imx6sx-cm4" , .data = &imx_rproc_cfg_imx6sx }, |
1132 | { .compatible = "fsl,imx8mq-cm4" , .data = &imx_rproc_cfg_imx8mq }, |
1133 | { .compatible = "fsl,imx8mm-cm4" , .data = &imx_rproc_cfg_imx8mq }, |
1134 | { .compatible = "fsl,imx8mn-cm7" , .data = &imx_rproc_cfg_imx8mn }, |
1135 | { .compatible = "fsl,imx8mp-cm7" , .data = &imx_rproc_cfg_imx8mn }, |
1136 | { .compatible = "fsl,imx8mn-cm7-mmio" , .data = &imx_rproc_cfg_imx8mn_mmio }, |
1137 | { .compatible = "fsl,imx8mp-cm7-mmio" , .data = &imx_rproc_cfg_imx8mn_mmio }, |
1138 | { .compatible = "fsl,imx8qxp-cm4" , .data = &imx_rproc_cfg_imx8qxp }, |
1139 | { .compatible = "fsl,imx8qm-cm4" , .data = &imx_rproc_cfg_imx8qm }, |
1140 | { .compatible = "fsl,imx8ulp-cm33" , .data = &imx_rproc_cfg_imx8ulp }, |
1141 | { .compatible = "fsl,imx93-cm33" , .data = &imx_rproc_cfg_imx93 }, |
1142 | {}, |
1143 | }; |
1144 | MODULE_DEVICE_TABLE(of, imx_rproc_of_match); |
1145 | |
1146 | static struct platform_driver imx_rproc_driver = { |
1147 | .probe = imx_rproc_probe, |
1148 | .remove_new = imx_rproc_remove, |
1149 | .driver = { |
1150 | .name = "imx-rproc" , |
1151 | .of_match_table = imx_rproc_of_match, |
1152 | }, |
1153 | }; |
1154 | |
1155 | module_platform_driver(imx_rproc_driver); |
1156 | |
1157 | MODULE_LICENSE("GPL v2" ); |
1158 | MODULE_DESCRIPTION("i.MX remote processor control driver" ); |
1159 | MODULE_AUTHOR("Oleksij Rempel <o.rempel@pengutronix.de>" ); |
1160 | |