Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
a0ff4aa6 OR |
2 | /* |
3 | * Copyright (c) 2017 Pengutronix, Oleksij Rempel <kernel@pengutronix.de> | |
a0ff4aa6 OR |
4 | */ |
5 | ||
fcd382b2 | 6 | #include <dt-bindings/firmware/imx/rsrc.h> |
79806d32 | 7 | #include <linux/arm-smccc.h> |
a0ff4aa6 OR |
8 | #include <linux/clk.h> |
9 | #include <linux/err.h> | |
5e50aef2 | 10 | #include <linux/firmware/imx/sci.h> |
a0ff4aa6 OR |
11 | #include <linux/interrupt.h> |
12 | #include <linux/kernel.h> | |
2df70620 | 13 | #include <linux/mailbox_client.h> |
a0ff4aa6 OR |
14 | #include <linux/mfd/syscon.h> |
15 | #include <linux/module.h> | |
3440d8da | 16 | #include <linux/of.h> |
a0ff4aa6 | 17 | #include <linux/of_address.h> |
b29b4249 | 18 | #include <linux/of_reserved_mem.h> |
a0ff4aa6 | 19 | #include <linux/platform_device.h> |
c94ea666 | 20 | #include <linux/pm_domain.h> |
a0ff4aa6 OR |
21 | #include <linux/regmap.h> |
22 | #include <linux/remoteproc.h> | |
2df70620 PF |
23 | #include <linux/workqueue.h> |
24 | ||
ebcd5d51 | 25 | #include "imx_rproc.h" |
2df70620 | 26 | #include "remoteproc_internal.h" |
a0ff4aa6 OR |
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) | |
da879769 PF |
40 | #define IMX7D_M4_STOP (IMX7D_ENABLE_M4 | IMX7D_SW_M4C_RST | \ |
41 | IMX7D_SW_M4C_NON_SCLR_RST) | |
a0ff4aa6 | 42 | |
49f80a7a MV |
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 | ||
a0ff4aa6 OR |
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) | |
da879769 PF |
58 | #define IMX6SX_M4_STOP (IMX6SX_ENABLE_M4 | IMX6SX_SW_M4C_RST | \ |
59 | IMX6SX_SW_M4C_NON_SCLR_RST) | |
a0ff4aa6 OR |
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 | ||
f638a197 | 64 | #define IMX_RPROC_MEM_MAX 32 |
a0ff4aa6 | 65 | |
79806d32 PF |
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 | ||
5e50aef2 PF |
71 | #define IMX_SC_IRQ_GROUP_REBOOTED 5 |
72 | ||
a0ff4aa6 OR |
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 | ||
fcd382b2 | 85 | /* att flags: lower 16 bits specifying core, higher 16 bits for flags */ |
a0ff4aa6 | 86 | /* M4 own area. Can be mapped at probe */ |
fcd382b2 PF |
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)) | |
a0ff4aa6 | 92 | |
99b142cf PF |
93 | static int imx_rproc_xtr_mbox_init(struct rproc *rproc); |
94 | static void imx_rproc_free_mbox(struct rproc *rproc); | |
c94ea666 | 95 | |
a0ff4aa6 OR |
96 | struct imx_rproc { |
97 | struct device *dev; | |
98 | struct regmap *regmap; | |
49f80a7a | 99 | struct regmap *gpr; |
a0ff4aa6 OR |
100 | struct rproc *rproc; |
101 | const struct imx_rproc_dcfg *dcfg; | |
f638a197 | 102 | struct imx_rproc_mem mem[IMX_RPROC_MEM_MAX]; |
a0ff4aa6 | 103 | struct clk *clk; |
2df70620 PF |
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; | |
5e4c1243 | 109 | void __iomem *rsc_table; |
5e50aef2 PF |
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 */ | |
c94ea666 | 114 | u32 entry; /* cpu start address */ |
fcd382b2 | 115 | u32 core_index; |
3f6905fb | 116 | struct dev_pm_domain_list *pd_list; |
a0ff4aa6 OR |
117 | }; |
118 | ||
9222fabf PF |
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 | ||
ee18f271 PF |
141 | { 0xC0000000, 0xC0000000, 0x10000000, 0 }, |
142 | { 0xD0000000, 0xC0000000, 0x10000000, 0 }, | |
9222fabf PF |
143 | }; |
144 | ||
fcd382b2 PF |
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 | ||
5e50aef2 PF |
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 | ||
79806d32 PF |
170 | static const struct imx_rproc_att imx_rproc_att_imx8mn[] = { |
171 | /* dev addr , sys addr , size , flags */ | |
172 | /* ITCM */ | |
91bb2663 | 173 | { 0x00000000, 0x007E0000, 0x00020000, ATT_OWN | ATT_IOMEM }, |
79806d32 PF |
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 */ | |
91bb2663 | 187 | { 0x20000000, 0x00800000, 0x00020000, ATT_OWN | ATT_IOMEM }, |
79806d32 PF |
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 | ||
4ab8f960 PF |
200 | static const struct imx_rproc_att imx_rproc_att_imx8mq[] = { |
201 | /* dev addr , sys addr , size , flags */ | |
202 | /* TCML - alias */ | |
91bb2663 | 203 | { 0x00000000, 0x007e0000, 0x00020000, ATT_IOMEM}, |
4ab8f960 PF |
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 */ | |
91bb2663 | 215 | { 0x1FFE0000, 0x007E0000, 0x00020000, ATT_OWN | ATT_IOMEM}, |
4ab8f960 | 216 | /* TCMU */ |
91bb2663 | 217 | { 0x20000000, 0x00800000, 0x00020000, ATT_OWN | ATT_IOMEM}, |
4ab8f960 PF |
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 | ||
d59eedc0 PF |
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 | ||
c8a1a56d PF |
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 | ||
a0ff4aa6 OR |
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) */ | |
91bb2663 | 255 | { 0x1FFF8000, 0x007F8000, 0x00008000, ATT_OWN | ATT_IOMEM }, |
a0ff4aa6 OR |
256 | /* DDR (Code) - alias, first part of DDR (Data) */ |
257 | { 0x10000000, 0x80000000, 0x0FFF0000, 0 }, | |
258 | ||
259 | /* TCMU (Data) */ | |
91bb2663 | 260 | { 0x20000000, 0x00800000, 0x00008000, ATT_OWN | ATT_IOMEM }, |
a0ff4aa6 OR |
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 */ | |
91bb2663 | 274 | { 0x00000000, 0x007F8000, 0x00008000, ATT_IOMEM }, |
a0ff4aa6 OR |
275 | /* OCRAM_S (Code) */ |
276 | { 0x00180000, 0x008F8000, 0x00004000, 0 }, | |
277 | /* OCRAM_S (Code) - alias */ | |
278 | { 0x00180000, 0x008FC000, 0x00004000, 0 }, | |
279 | /* TCML (Code) */ | |
91bb2663 | 280 | { 0x1FFF8000, 0x007F8000, 0x00008000, ATT_OWN | ATT_IOMEM }, |
a0ff4aa6 OR |
281 | /* DDR (Code) - alias, first part of DDR (Data) */ |
282 | { 0x10000000, 0x80000000, 0x0FFF8000, 0 }, | |
283 | ||
284 | /* TCMU (Data) */ | |
91bb2663 | 285 | { 0x20000000, 0x00800000, 0x00008000, ATT_OWN | ATT_IOMEM }, |
a0ff4aa6 OR |
286 | /* OCRAM_S (Data) - alias? */ |
287 | { 0x208F8000, 0x008F8000, 0x00004000, 0 }, | |
288 | /* DDR (Data) */ | |
289 | { 0x80000000, 0x80000000, 0x60000000, 0 }, | |
290 | }; | |
291 | ||
49f80a7a MV |
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 | ||
79806d32 PF |
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 | ||
4ab8f960 PF |
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), | |
52bda8d3 | 317 | .method = IMX_RPROC_MMIO, |
4ab8f960 PF |
318 | }; |
319 | ||
fcd382b2 PF |
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 | ||
5e50aef2 PF |
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 | ||
d59eedc0 PF |
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 | ||
c8a1a56d PF |
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 | ||
a0ff4aa6 OR |
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), | |
52bda8d3 | 351 | .method = IMX_RPROC_MMIO, |
a0ff4aa6 OR |
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), | |
52bda8d3 | 361 | .method = IMX_RPROC_MMIO, |
a0ff4aa6 OR |
362 | }; |
363 | ||
9222fabf PF |
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 | ||
a0ff4aa6 OR |
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; | |
79806d32 | 375 | struct arm_smccc_res res; |
a0ff4aa6 OR |
376 | int ret; |
377 | ||
99b142cf PF |
378 | ret = imx_rproc_xtr_mbox_init(rproc); |
379 | if (ret) | |
380 | return ret; | |
381 | ||
79806d32 PF |
382 | switch (dcfg->method) { |
383 | case IMX_RPROC_MMIO: | |
49f80a7a MV |
384 | if (priv->gpr) { |
385 | ret = regmap_clear_bits(priv->gpr, dcfg->gpr_reg, | |
386 | dcfg->gpr_wait); | |
387 | } else { | |
388 | ret = regmap_update_bits(priv->regmap, dcfg->src_reg, | |
389 | dcfg->src_mask, | |
390 | dcfg->src_start); | |
391 | } | |
79806d32 PF |
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; | |
c94ea666 PF |
397 | case IMX_RPROC_SCU_API: |
398 | ret = imx_sc_pm_cpu_start(priv->ipc_handle, priv->rsrc_id, true, priv->entry); | |
399 | break; | |
79806d32 PF |
400 | default: |
401 | return -EOPNOTSUPP; | |
402 | } | |
403 | ||
a0ff4aa6 | 404 | if (ret) |
79806d32 | 405 | dev_err(dev, "Failed to enable remote core!\n"); |
a0ff4aa6 OR |
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; | |
79806d32 | 415 | struct arm_smccc_res res; |
a0ff4aa6 OR |
416 | int ret; |
417 | ||
79806d32 PF |
418 | switch (dcfg->method) { |
419 | case IMX_RPROC_MMIO: | |
49f80a7a MV |
420 | if (priv->gpr) { |
421 | ret = regmap_set_bits(priv->gpr, dcfg->gpr_reg, | |
422 | dcfg->gpr_wait); | |
423 | if (ret) { | |
424 | dev_err(priv->dev, | |
425 | "Failed to quiescence M4 platform!\n"); | |
426 | return ret; | |
427 | } | |
428 | } | |
429 | ||
79806d32 PF |
430 | ret = regmap_update_bits(priv->regmap, dcfg->src_reg, dcfg->src_mask, |
431 | 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; | |
c94ea666 PF |
439 | case IMX_RPROC_SCU_API: |
440 | ret = imx_sc_pm_cpu_start(priv->ipc_handle, priv->rsrc_id, false, priv->entry); | |
441 | break; | |
79806d32 | 442 | default: |
c8a1a56d | 443 | return -EOPNOTSUPP; |
79806d32 | 444 | } |
c8a1a56d | 445 | |
a0ff4aa6 | 446 | if (ret) |
79806d32 | 447 | dev_err(dev, "Failed to stop remote core\n"); |
99b142cf PF |
448 | else |
449 | imx_rproc_free_mbox(rproc); | |
a0ff4aa6 OR |
450 | |
451 | return ret; | |
452 | } | |
453 | ||
454 | static int imx_rproc_da_to_sys(struct imx_rproc *priv, u64 da, | |
91bb2663 | 455 | size_t len, u64 *sys, bool *is_iomem) |
a0ff4aa6 OR |
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 | ||
fcd382b2 PF |
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 | ||
a0ff4aa6 OR |
475 | if (da >= att->da && da + len < att->da + att->size) { |
476 | unsigned int offset = da - att->da; | |
477 | ||
478 | *sys = att->sa + offset; | |
91bb2663 DA |
479 | if (is_iomem) |
480 | *is_iomem = att->flags & ATT_IOMEM; | |
a0ff4aa6 OR |
481 | return 0; |
482 | } | |
483 | } | |
484 | ||
9ce3bf22 | 485 | dev_warn(priv->dev, "Translation failed: da = 0x%llx len = 0x%zx\n", |
a0ff4aa6 OR |
486 | da, len); |
487 | return -ENOENT; | |
488 | } | |
489 | ||
40df0a91 | 490 | static void *imx_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem) |
a0ff4aa6 OR |
491 | { |
492 | struct imx_rproc *priv = rproc->priv; | |
493 | void *va = NULL; | |
494 | u64 sys; | |
495 | int i; | |
496 | ||
9ce3bf22 | 497 | if (len == 0) |
a0ff4aa6 OR |
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 | */ | |
91bb2663 | 504 | if (imx_rproc_da_to_sys(priv, da, len, &sys, is_iomem)) |
a0ff4aa6 OR |
505 | return NULL; |
506 | ||
f638a197 | 507 | for (i = 0; i < IMX_RPROC_MEM_MAX; i++) { |
a0ff4aa6 OR |
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 | ||
9ce3bf22 CL |
517 | dev_dbg(&rproc->dev, "da = 0x%llx len = 0x%zx va = 0x%p\n", |
518 | da, len, va); | |
a0ff4aa6 OR |
519 | |
520 | return va; | |
521 | } | |
522 | ||
b29b4249 PF |
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(mem->dma, mem->len); | |
531 | if (IS_ERR_OR_NULL(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(mem->va); | |
548 | ||
549 | return 0; | |
550 | } | |
551 | ||
10a3d407 | 552 | static int imx_rproc_prepare(struct rproc *rproc) |
b29b4249 PF |
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, np, "memory-region", NULL, 0); | |
563 | while (of_phandle_iterator_next(&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 | ||
58b7c856 PF |
571 | if (!strcmp(it.node->name, "rsc-table")) |
572 | continue; | |
573 | ||
b29b4249 PF |
574 | rmem = of_reserved_mem_lookup(it.node); |
575 | if (!rmem) { | |
5ef074e8 | 576 | of_node_put(it.node); |
b29b4249 PF |
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(priv->dev, NULL, (dma_addr_t)rmem->base, rmem->size, da, | |
586 | imx_rproc_mem_alloc, imx_rproc_mem_release, | |
587 | it.node->name); | |
588 | ||
5ef074e8 | 589 | if (mem) { |
b29b4249 | 590 | rproc_coredump_add_segment(rproc, da, rmem->size); |
5ef074e8 MP |
591 | } else { |
592 | of_node_put(it.node); | |
b29b4249 | 593 | return -ENOMEM; |
5ef074e8 | 594 | } |
b29b4249 PF |
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 | { | |
10a3d407 | 604 | int ret; |
b29b4249 PF |
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 | ||
2df70620 PF |
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(priv->tx_ch, (void *)&mmsg); | |
631 | if (err < 0) | |
632 | dev_err(priv->dev, "%s: failed (%d, err:%d)\n", | |
633 | __func__, vqid, err); | |
634 | } | |
635 | ||
5e4c1243 PF |
636 | static int imx_rproc_attach(struct rproc *rproc) |
637 | { | |
99b142cf PF |
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(priv->ipc_handle, priv->rsrc_id)) | |
650 | return -EOPNOTSUPP; | |
651 | ||
652 | imx_rproc_free_mbox(rproc); | |
653 | ||
5e4c1243 PF |
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 | ||
a0ff4aa6 | 669 | static const struct rproc_ops imx_rproc_ops = { |
10a3d407 | 670 | .prepare = imx_rproc_prepare, |
5e4c1243 | 671 | .attach = imx_rproc_attach, |
99b142cf | 672 | .detach = imx_rproc_detach, |
a0ff4aa6 OR |
673 | .start = imx_rproc_start, |
674 | .stop = imx_rproc_stop, | |
2df70620 | 675 | .kick = imx_rproc_kick, |
a0ff4aa6 | 676 | .da_to_va = imx_rproc_da_to_va, |
b29b4249 PF |
677 | .load = rproc_elf_load_segments, |
678 | .parse_fw = imx_rproc_parse_fw, | |
679 | .find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table, | |
5e4c1243 | 680 | .get_loaded_rsc_table = imx_rproc_get_loaded_rsc_table, |
b29b4249 PF |
681 | .sanity_check = rproc_elf_sanity_check, |
682 | .get_boot_addr = rproc_elf_get_boot_addr, | |
a0ff4aa6 OR |
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 | ||
f638a197 | 700 | if (b >= IMX_RPROC_MEM_MAX) |
a0ff4aa6 OR |
701 | break; |
702 | ||
91bb2663 DA |
703 | if (att->flags & ATT_IOMEM) |
704 | priv->mem[b].cpu_addr = devm_ioremap(&pdev->dev, | |
705 | att->sa, att->size); | |
706 | else | |
707 | priv->mem[b].cpu_addr = devm_ioremap_wc(&pdev->dev, | |
708 | att->sa, att->size); | |
68a39a3e | 709 | if (!priv->mem[b].cpu_addr) { |
1896b3d8 | 710 | dev_err(dev, "failed to remap %#x bytes from %#x\n", att->size, att->sa); |
68a39a3e | 711 | return -ENOMEM; |
a0ff4aa6 OR |
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, "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, "memory-region", a); | |
afe670e2 | 729 | /* Not map vdevbuffer, vdevring region */ |
61afafe8 ML |
730 | if (!strncmp(node->name, "vdev", strlen("vdev"))) { |
731 | of_node_put(node); | |
8f2d8961 | 732 | continue; |
61afafe8 | 733 | } |
a0ff4aa6 | 734 | err = of_address_to_resource(node, 0, &res); |
61afafe8 | 735 | of_node_put(node); |
a0ff4aa6 OR |
736 | if (err) { |
737 | dev_err(dev, "unable to resolve memory region\n"); | |
738 | return err; | |
739 | } | |
740 | ||
f638a197 | 741 | if (b >= IMX_RPROC_MEM_MAX) |
a0ff4aa6 OR |
742 | break; |
743 | ||
ecadcc47 | 744 | /* Not use resource version, because we might share region */ |
28d5554b | 745 | priv->mem[b].cpu_addr = devm_ioremap_wc(&pdev->dev, res.start, resource_size(&res)); |
18cda801 | 746 | if (!priv->mem[b].cpu_addr) { |
1896b3d8 | 747 | dev_err(dev, "failed to remap %pr\n", &res); |
18cda801 | 748 | return -ENOMEM; |
a0ff4aa6 OR |
749 | } |
750 | priv->mem[b].sys_addr = res.start; | |
751 | priv->mem[b].size = resource_size(&res); | |
e90547d5 | 752 | if (!strcmp(node->name, "rsc-table")) |
5e4c1243 | 753 | priv->rsc_table = priv->mem[b].cpu_addr; |
a0ff4aa6 OR |
754 | b++; |
755 | } | |
756 | ||
757 | return 0; | |
758 | } | |
759 | ||
0b6c3bc8 PF |
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, id); | |
765 | ||
766 | return 0; | |
767 | } | |
768 | ||
2df70620 PF |
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); | |
0b6c3bc8 | 773 | struct rproc *rproc = priv->rproc; |
2df70620 | 774 | |
0b6c3bc8 | 775 | idr_for_each(&rproc->notifyids, imx_rproc_notified_idr_cb, rproc); |
2df70620 PF |
776 | } |
777 | ||
778 | static void imx_rproc_rx_callback(struct mbox_client *cl, void *msg) | |
779 | { | |
780 | struct rproc *rproc = dev_get_drvdata(cl->dev); | |
781 | struct imx_rproc *priv = rproc->priv; | |
782 | ||
783 | queue_work(priv->workqueue, &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; | |
2df70620 | 791 | |
99b142cf PF |
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 | ||
2df70620 PF |
804 | if (!of_get_property(dev->of_node, "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, "tx"); | |
a1c3611d CJ |
815 | if (IS_ERR(priv->tx_ch)) |
816 | return dev_err_probe(cl->dev, PTR_ERR(priv->tx_ch), | |
817 | "failed to request tx mailbox channel\n"); | |
2df70620 PF |
818 | |
819 | priv->rx_ch = mbox_request_channel_byname(cl, "rx"); | |
820 | if (IS_ERR(priv->rx_ch)) { | |
821 | mbox_free_channel(priv->tx_ch); | |
a1c3611d CJ |
822 | return dev_err_probe(cl->dev, PTR_ERR(priv->rx_ch), |
823 | "failed to request rx mailbox channel\n"); | |
2df70620 PF |
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 | ||
99b142cf PF |
833 | if (priv->tx_ch) { |
834 | mbox_free_channel(priv->tx_ch); | |
835 | priv->tx_ch = NULL; | |
836 | } | |
837 | ||
838 | if (priv->rx_ch) { | |
839 | mbox_free_channel(priv->rx_ch); | |
840 | priv->rx_ch = NULL; | |
841 | } | |
2df70620 PF |
842 | } |
843 | ||
5e50aef2 PF |
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 | ||
c94ea666 | 852 | if (imx_sc_rm_is_resource_owned(priv->ipc_handle, priv->rsrc_id)) { |
3f6905fb | 853 | dev_pm_domain_detach_list(priv->pd_list); |
5e50aef2 | 854 | return; |
c94ea666 | 855 | } |
5e50aef2 PF |
856 | |
857 | imx_scu_irq_group_enable(IMX_SC_IRQ_GROUP_REBOOTED, BIT(priv->rproc_pt), false); | |
858 | imx_scu_irq_unregister_notifier(&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(priv->rproc, RPROC_WATCHDOG); | |
871 | ||
872 | pr_info("Partition%d reset!\n", priv->rproc_pt); | |
873 | ||
874 | return 0; | |
875 | } | |
876 | ||
c94ea666 PF |
877 | static int imx_rproc_attach_pd(struct imx_rproc *priv) |
878 | { | |
879 | struct device *dev = priv->dev; | |
3f6905fb UH |
880 | int ret; |
881 | struct dev_pm_domain_attach_data pd_data = { | |
882 | .pd_flags = PD_FLAG_DEV_LINK_ON, | |
883 | }; | |
c94ea666 PF |
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 | */ | |
3f6905fb | 889 | if (dev->pm_domain) |
c94ea666 PF |
890 | return 0; |
891 | ||
3f6905fb UH |
892 | ret = dev_pm_domain_attach_list(dev, &pd_data, &priv->pd_list); |
893 | return ret < 0 ? ret : 0; | |
c94ea666 PF |
894 | } |
895 | ||
5e4c1243 PF |
896 | static int imx_rproc_detect_mode(struct imx_rproc *priv) |
897 | { | |
c8a1a56d | 898 | struct regmap_config config = { .name = "imx-rproc" }; |
5e4c1243 PF |
899 | const struct imx_rproc_dcfg *dcfg = priv->dcfg; |
900 | struct device *dev = priv->dev; | |
c8a1a56d | 901 | struct regmap *regmap; |
79806d32 | 902 | struct arm_smccc_res res; |
5e4c1243 PF |
903 | int ret; |
904 | u32 val; | |
5e50aef2 | 905 | u8 pt; |
5e4c1243 | 906 | |
c8a1a56d PF |
907 | switch (dcfg->method) { |
908 | case IMX_RPROC_NONE: | |
909 | priv->rproc->state = RPROC_DETACHED; | |
910 | return 0; | |
79806d32 PF |
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; | |
5e50aef2 PF |
916 | case IMX_RPROC_SCU_API: |
917 | ret = imx_scu_get_handle(&priv->ipc_handle); | |
918 | if (ret) | |
919 | return ret; | |
920 | ret = of_property_read_u32(dev->of_node, "fsl,resource-id", &priv->rsrc_id); | |
921 | if (ret) { | |
922 | dev_err(dev, "No fsl,resource-id property\n"); | |
923 | return ret; | |
924 | } | |
925 | ||
fcd382b2 PF |
926 | if (priv->rsrc_id == IMX_SC_R_M4_1_PID0) |
927 | priv->core_index = 1; | |
928 | else | |
929 | priv->core_index = 0; | |
930 | ||
5e50aef2 PF |
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 | */ | |
c94ea666 PF |
935 | if (imx_sc_rm_is_resource_owned(priv->ipc_handle, priv->rsrc_id)) { |
936 | if (of_property_read_u32(dev->of_node, "fsl,entry-address", &priv->entry)) | |
937 | return -EINVAL; | |
938 | ||
939 | return imx_rproc_attach_pd(priv); | |
940 | } | |
5e50aef2 PF |
941 | |
942 | priv->rproc->state = RPROC_DETACHED; | |
6eed169c PF |
943 | priv->rproc->recovery_disabled = false; |
944 | rproc_set_feature(priv->rproc, RPROC_FEAT_ATTACH_ON_RECOVERY); | |
5e50aef2 PF |
945 | |
946 | /* Get partition id and enable irq in SCFW */ | |
947 | ret = imx_sc_rm_get_resource_owner(priv->ipc_handle, priv->rsrc_id, &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(&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 | true); | |
964 | if (ret) { | |
965 | imx_scu_irq_unregister_notifier(&priv->rproc_nb); | |
966 | dev_err(dev, "Enable irq failed, %d\n", ret); | |
967 | return ret; | |
968 | } | |
969 | ||
970 | return 0; | |
c8a1a56d PF |
971 | default: |
972 | break; | |
973 | } | |
974 | ||
49f80a7a MV |
975 | priv->gpr = syscon_regmap_lookup_by_phandle(dev->of_node, "fsl,iomuxc-gpr"); |
976 | if (IS_ERR(priv->gpr)) | |
977 | priv->gpr = NULL; | |
978 | ||
c8a1a56d PF |
979 | regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "syscon"); |
980 | if (IS_ERR(regmap)) { | |
981 | dev_err(dev, "failed to find syscon\n"); | |
982 | return PTR_ERR(regmap); | |
983 | } | |
984 | ||
985 | priv->regmap = regmap; | |
986 | regmap_attach_dev(dev, regmap, &config); | |
987 | ||
49f80a7a MV |
988 | if (priv->gpr) { |
989 | ret = regmap_read(priv->gpr, dcfg->gpr_reg, &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(priv->rproc); | |
997 | return 0; | |
998 | } | |
999 | } | |
1000 | ||
c8a1a56d | 1001 | ret = regmap_read(regmap, dcfg->src_reg, &val); |
5e4c1243 PF |
1002 | if (ret) { |
1003 | dev_err(dev, "Failed to read src\n"); | |
1004 | return ret; | |
1005 | } | |
1006 | ||
da879769 | 1007 | if ((val & dcfg->src_mask) != dcfg->src_stop) |
5e4c1243 PF |
1008 | priv->rproc->state = RPROC_DETACHED; |
1009 | ||
1010 | return 0; | |
1011 | } | |
1012 | ||
cc0316c1 PF |
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(priv->clk)) { | |
1025 | dev_err(dev, "Failed to get clock\n"); | |
1026 | return PTR_ERR(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(priv->clk); | |
1034 | if (ret) { | |
1035 | dev_err(dev, "Failed to enable clock\n"); | |
1036 | return ret; | |
1037 | } | |
1038 | ||
1039 | return 0; | |
1040 | } | |
1041 | ||
a0ff4aa6 OR |
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; | |
a0ff4aa6 | 1048 | const struct imx_rproc_dcfg *dcfg; |
a0ff4aa6 OR |
1049 | int ret; |
1050 | ||
a0ff4aa6 OR |
1051 | /* set some other name then imx */ |
1052 | rproc = rproc_alloc(dev, "imx-rproc", &imx_rproc_ops, | |
1053 | NULL, sizeof(*priv)); | |
99a31adf CJ |
1054 | if (!rproc) |
1055 | return -ENOMEM; | |
a0ff4aa6 OR |
1056 | |
1057 | dcfg = of_device_get_match_data(dev); | |
de6f83f8 CJ |
1058 | if (!dcfg) { |
1059 | ret = -EINVAL; | |
1060 | goto err_put_rproc; | |
1061 | } | |
a0ff4aa6 OR |
1062 | |
1063 | priv = rproc->priv; | |
1064 | priv->rproc = rproc; | |
a0ff4aa6 OR |
1065 | priv->dcfg = dcfg; |
1066 | priv->dev = dev; | |
1067 | ||
1068 | dev_set_drvdata(dev, rproc); | |
2df70620 PF |
1069 | priv->workqueue = create_workqueue(dev_name(dev)); |
1070 | if (!priv->workqueue) { | |
1071 | dev_err(dev, "cannot create workqueue\n"); | |
1072 | ret = -ENOMEM; | |
1073 | goto err_put_rproc; | |
1074 | } | |
1075 | ||
1076 | ret = imx_rproc_xtr_mbox_init(rproc); | |
1077 | if (ret) | |
1078 | goto err_put_wkq; | |
a0ff4aa6 OR |
1079 | |
1080 | ret = imx_rproc_addr_init(priv, pdev); | |
1081 | if (ret) { | |
16a3c637 | 1082 | dev_err(dev, "failed on imx_rproc_addr_init\n"); |
2df70620 | 1083 | goto err_put_mbox; |
a0ff4aa6 OR |
1084 | } |
1085 | ||
5e4c1243 PF |
1086 | ret = imx_rproc_detect_mode(priv); |
1087 | if (ret) | |
1088 | goto err_put_mbox; | |
1089 | ||
cc0316c1 PF |
1090 | ret = imx_rproc_clk_enable(priv); |
1091 | if (ret) | |
5e50aef2 | 1092 | goto err_put_scu; |
a0ff4aa6 | 1093 | |
2df70620 PF |
1094 | INIT_WORK(&priv->rproc_work, imx_rproc_vq_work); |
1095 | ||
e13d1a43 PF |
1096 | if (rproc->state != RPROC_DETACHED) |
1097 | rproc->auto_boot = of_property_read_bool(np, "fsl,auto-boot"); | |
1098 | ||
a0ff4aa6 OR |
1099 | ret = rproc_add(rproc); |
1100 | if (ret) { | |
1101 | dev_err(dev, "rproc_add failed\n"); | |
1102 | goto err_put_clk; | |
1103 | } | |
1104 | ||
99a31adf | 1105 | return 0; |
a0ff4aa6 OR |
1106 | |
1107 | err_put_clk: | |
1108 | clk_disable_unprepare(priv->clk); | |
5e50aef2 PF |
1109 | err_put_scu: |
1110 | imx_rproc_put_scu(rproc); | |
2df70620 PF |
1111 | err_put_mbox: |
1112 | imx_rproc_free_mbox(rproc); | |
1113 | err_put_wkq: | |
1114 | destroy_workqueue(priv->workqueue); | |
a0ff4aa6 OR |
1115 | err_put_rproc: |
1116 | rproc_free(rproc); | |
99a31adf | 1117 | |
a0ff4aa6 OR |
1118 | return ret; |
1119 | } | |
1120 | ||
94ea6edd | 1121 | static void imx_rproc_remove(struct platform_device *pdev) |
a0ff4aa6 OR |
1122 | { |
1123 | struct rproc *rproc = platform_get_drvdata(pdev); | |
1124 | struct imx_rproc *priv = rproc->priv; | |
1125 | ||
1126 | clk_disable_unprepare(priv->clk); | |
1127 | rproc_del(rproc); | |
5e50aef2 | 1128 | imx_rproc_put_scu(rproc); |
2df70620 | 1129 | imx_rproc_free_mbox(rproc); |
4da96175 | 1130 | destroy_workqueue(priv->workqueue); |
a0ff4aa6 | 1131 | rproc_free(rproc); |
a0ff4aa6 OR |
1132 | } |
1133 | ||
1134 | static const struct of_device_id imx_rproc_of_match[] = { | |
c8a1a56d | 1135 | { .compatible = "fsl,imx7ulp-cm4", .data = &imx_rproc_cfg_imx7ulp }, |
a0ff4aa6 OR |
1136 | { .compatible = "fsl,imx7d-cm4", .data = &imx_rproc_cfg_imx7d }, |
1137 | { .compatible = "fsl,imx6sx-cm4", .data = &imx_rproc_cfg_imx6sx }, | |
4ab8f960 PF |
1138 | { .compatible = "fsl,imx8mq-cm4", .data = &imx_rproc_cfg_imx8mq }, |
1139 | { .compatible = "fsl,imx8mm-cm4", .data = &imx_rproc_cfg_imx8mq }, | |
79806d32 PF |
1140 | { .compatible = "fsl,imx8mn-cm7", .data = &imx_rproc_cfg_imx8mn }, |
1141 | { .compatible = "fsl,imx8mp-cm7", .data = &imx_rproc_cfg_imx8mn }, | |
49f80a7a MV |
1142 | { .compatible = "fsl,imx8mn-cm7-mmio", .data = &imx_rproc_cfg_imx8mn_mmio }, |
1143 | { .compatible = "fsl,imx8mp-cm7-mmio", .data = &imx_rproc_cfg_imx8mn_mmio }, | |
5e50aef2 | 1144 | { .compatible = "fsl,imx8qxp-cm4", .data = &imx_rproc_cfg_imx8qxp }, |
fcd382b2 | 1145 | { .compatible = "fsl,imx8qm-cm4", .data = &imx_rproc_cfg_imx8qm }, |
d59eedc0 | 1146 | { .compatible = "fsl,imx8ulp-cm33", .data = &imx_rproc_cfg_imx8ulp }, |
9222fabf | 1147 | { .compatible = "fsl,imx93-cm33", .data = &imx_rproc_cfg_imx93 }, |
a0ff4aa6 OR |
1148 | {}, |
1149 | }; | |
1150 | MODULE_DEVICE_TABLE(of, imx_rproc_of_match); | |
1151 | ||
1152 | static struct platform_driver imx_rproc_driver = { | |
1153 | .probe = imx_rproc_probe, | |
94ea6edd | 1154 | .remove_new = imx_rproc_remove, |
a0ff4aa6 OR |
1155 | .driver = { |
1156 | .name = "imx-rproc", | |
1157 | .of_match_table = imx_rproc_of_match, | |
1158 | }, | |
1159 | }; | |
1160 | ||
1161 | module_platform_driver(imx_rproc_driver); | |
1162 | ||
1163 | MODULE_LICENSE("GPL v2"); | |
4ab8f960 | 1164 | MODULE_DESCRIPTION("i.MX remote processor control driver"); |
a0ff4aa6 | 1165 | MODULE_AUTHOR("Oleksij Rempel <o.rempel@pengutronix.de>"); |