1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * ST's Remote Processor Control Driver |
4 | * |
5 | * Copyright (C) 2015 STMicroelectronics - All Rights Reserved |
6 | * |
7 | * Author: Ludovic Barre <ludovic.barre@st.com> |
8 | */ |
9 | |
10 | #include <linux/clk.h> |
11 | #include <linux/dma-mapping.h> |
12 | #include <linux/err.h> |
13 | #include <linux/interrupt.h> |
14 | #include <linux/kernel.h> |
15 | #include <linux/mailbox_client.h> |
16 | #include <linux/mfd/syscon.h> |
17 | #include <linux/module.h> |
18 | #include <linux/of.h> |
19 | #include <linux/of_reserved_mem.h> |
20 | #include <linux/platform_device.h> |
21 | #include <linux/property.h> |
22 | #include <linux/regmap.h> |
23 | #include <linux/remoteproc.h> |
24 | #include <linux/reset.h> |
25 | |
26 | #include "remoteproc_internal.h" |
27 | |
28 | #define ST_RPROC_VQ0 0 |
29 | #define ST_RPROC_VQ1 1 |
30 | #define ST_RPROC_MAX_VRING 2 |
31 | |
32 | #define MBOX_RX 0 |
33 | #define MBOX_TX 1 |
34 | #define MBOX_MAX 2 |
35 | |
36 | struct st_rproc_config { |
37 | bool sw_reset; |
38 | bool pwr_reset; |
39 | unsigned long bootaddr_mask; |
40 | }; |
41 | |
42 | struct st_rproc { |
43 | struct st_rproc_config *config; |
44 | struct reset_control *sw_reset; |
45 | struct reset_control *pwr_reset; |
46 | struct clk *clk; |
47 | u32 clk_rate; |
48 | struct regmap *boot_base; |
49 | u32 boot_offset; |
50 | struct mbox_chan *mbox_chan[ST_RPROC_MAX_VRING * MBOX_MAX]; |
51 | struct mbox_client mbox_client_vq0; |
52 | struct mbox_client mbox_client_vq1; |
53 | }; |
54 | |
55 | static void st_rproc_mbox_callback(struct device *dev, u32 msg) |
56 | { |
57 | struct rproc *rproc = dev_get_drvdata(dev); |
58 | |
59 | if (rproc_vq_interrupt(rproc, vq_id: msg) == IRQ_NONE) |
60 | dev_dbg(dev, "no message was found in vqid %d\n" , msg); |
61 | } |
62 | |
63 | static |
64 | void st_rproc_mbox_callback_vq0(struct mbox_client *mbox_client, void *data) |
65 | { |
66 | st_rproc_mbox_callback(dev: mbox_client->dev, msg: 0); |
67 | } |
68 | |
69 | static |
70 | void st_rproc_mbox_callback_vq1(struct mbox_client *mbox_client, void *data) |
71 | { |
72 | st_rproc_mbox_callback(dev: mbox_client->dev, msg: 1); |
73 | } |
74 | |
75 | static void st_rproc_kick(struct rproc *rproc, int vqid) |
76 | { |
77 | struct st_rproc *ddata = rproc->priv; |
78 | struct device *dev = rproc->dev.parent; |
79 | int ret; |
80 | |
81 | /* send the index of the triggered virtqueue in the mailbox payload */ |
82 | if (WARN_ON(vqid >= ST_RPROC_MAX_VRING)) |
83 | return; |
84 | |
85 | ret = mbox_send_message(chan: ddata->mbox_chan[vqid * MBOX_MAX + MBOX_TX], |
86 | mssg: (void *)&vqid); |
87 | if (ret < 0) |
88 | dev_err(dev, "failed to send message via mbox: %d\n" , ret); |
89 | } |
90 | |
91 | static int st_rproc_mem_alloc(struct rproc *rproc, |
92 | struct rproc_mem_entry *mem) |
93 | { |
94 | struct device *dev = rproc->dev.parent; |
95 | void *va; |
96 | |
97 | va = ioremap_wc(offset: mem->dma, size: mem->len); |
98 | if (!va) { |
99 | dev_err(dev, "Unable to map memory region: %pa+%zx\n" , |
100 | &mem->dma, mem->len); |
101 | return -ENOMEM; |
102 | } |
103 | |
104 | /* Update memory entry va */ |
105 | mem->va = va; |
106 | |
107 | return 0; |
108 | } |
109 | |
110 | static int st_rproc_mem_release(struct rproc *rproc, |
111 | struct rproc_mem_entry *mem) |
112 | { |
113 | iounmap(addr: mem->va); |
114 | |
115 | return 0; |
116 | } |
117 | |
118 | static int st_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) |
119 | { |
120 | struct device *dev = rproc->dev.parent; |
121 | struct device_node *np = dev->of_node; |
122 | struct rproc_mem_entry *mem; |
123 | struct reserved_mem *rmem; |
124 | struct of_phandle_iterator it; |
125 | int index = 0; |
126 | |
127 | of_phandle_iterator_init(it: &it, np, list_name: "memory-region" , NULL, cell_count: 0); |
128 | while (of_phandle_iterator_next(it: &it) == 0) { |
129 | rmem = of_reserved_mem_lookup(np: it.node); |
130 | if (!rmem) { |
131 | of_node_put(node: it.node); |
132 | dev_err(dev, "unable to acquire memory-region\n" ); |
133 | return -EINVAL; |
134 | } |
135 | |
136 | /* No need to map vdev buffer */ |
137 | if (strcmp(it.node->name, "vdev0buffer" )) { |
138 | /* Register memory region */ |
139 | mem = rproc_mem_entry_init(dev, NULL, |
140 | dma: (dma_addr_t)rmem->base, |
141 | len: rmem->size, da: rmem->base, |
142 | alloc: st_rproc_mem_alloc, |
143 | release: st_rproc_mem_release, |
144 | name: it.node->name); |
145 | } else { |
146 | /* Register reserved memory for vdev buffer allocation */ |
147 | mem = rproc_of_resm_mem_entry_init(dev, of_resm_idx: index, |
148 | len: rmem->size, |
149 | da: rmem->base, |
150 | name: it.node->name); |
151 | } |
152 | |
153 | if (!mem) { |
154 | of_node_put(node: it.node); |
155 | return -ENOMEM; |
156 | } |
157 | |
158 | rproc_add_carveout(rproc, mem); |
159 | index++; |
160 | } |
161 | |
162 | return rproc_elf_load_rsc_table(rproc, fw); |
163 | } |
164 | |
165 | static int st_rproc_start(struct rproc *rproc) |
166 | { |
167 | struct st_rproc *ddata = rproc->priv; |
168 | int err; |
169 | |
170 | regmap_update_bits(map: ddata->boot_base, reg: ddata->boot_offset, |
171 | mask: ddata->config->bootaddr_mask, val: rproc->bootaddr); |
172 | |
173 | err = clk_enable(clk: ddata->clk); |
174 | if (err) { |
175 | dev_err(&rproc->dev, "Failed to enable clock\n" ); |
176 | return err; |
177 | } |
178 | |
179 | if (ddata->config->sw_reset) { |
180 | err = reset_control_deassert(rstc: ddata->sw_reset); |
181 | if (err) { |
182 | dev_err(&rproc->dev, "Failed to deassert S/W Reset\n" ); |
183 | goto sw_reset_fail; |
184 | } |
185 | } |
186 | |
187 | if (ddata->config->pwr_reset) { |
188 | err = reset_control_deassert(rstc: ddata->pwr_reset); |
189 | if (err) { |
190 | dev_err(&rproc->dev, "Failed to deassert Power Reset\n" ); |
191 | goto pwr_reset_fail; |
192 | } |
193 | } |
194 | |
195 | dev_info(&rproc->dev, "Started from 0x%llx\n" , rproc->bootaddr); |
196 | |
197 | return 0; |
198 | |
199 | |
200 | pwr_reset_fail: |
201 | if (ddata->config->pwr_reset) |
202 | reset_control_assert(rstc: ddata->sw_reset); |
203 | sw_reset_fail: |
204 | clk_disable(clk: ddata->clk); |
205 | |
206 | return err; |
207 | } |
208 | |
209 | static int st_rproc_stop(struct rproc *rproc) |
210 | { |
211 | struct st_rproc *ddata = rproc->priv; |
212 | int sw_err = 0, pwr_err = 0; |
213 | |
214 | if (ddata->config->sw_reset) { |
215 | sw_err = reset_control_assert(rstc: ddata->sw_reset); |
216 | if (sw_err) |
217 | dev_err(&rproc->dev, "Failed to assert S/W Reset\n" ); |
218 | } |
219 | |
220 | if (ddata->config->pwr_reset) { |
221 | pwr_err = reset_control_assert(rstc: ddata->pwr_reset); |
222 | if (pwr_err) |
223 | dev_err(&rproc->dev, "Failed to assert Power Reset\n" ); |
224 | } |
225 | |
226 | clk_disable(clk: ddata->clk); |
227 | |
228 | return sw_err ?: pwr_err; |
229 | } |
230 | |
231 | static const struct rproc_ops st_rproc_ops = { |
232 | .kick = st_rproc_kick, |
233 | .start = st_rproc_start, |
234 | .stop = st_rproc_stop, |
235 | .parse_fw = st_rproc_parse_fw, |
236 | .load = rproc_elf_load_segments, |
237 | .find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table, |
238 | .sanity_check = rproc_elf_sanity_check, |
239 | .get_boot_addr = rproc_elf_get_boot_addr, |
240 | }; |
241 | |
242 | /* |
243 | * Fetch state of the processor: 0 is off, 1 is on. |
244 | */ |
245 | static int st_rproc_state(struct platform_device *pdev) |
246 | { |
247 | struct rproc *rproc = platform_get_drvdata(pdev); |
248 | struct st_rproc *ddata = rproc->priv; |
249 | int reset_sw = 0, reset_pwr = 0; |
250 | |
251 | if (ddata->config->sw_reset) |
252 | reset_sw = reset_control_status(rstc: ddata->sw_reset); |
253 | |
254 | if (ddata->config->pwr_reset) |
255 | reset_pwr = reset_control_status(rstc: ddata->pwr_reset); |
256 | |
257 | if (reset_sw < 0 || reset_pwr < 0) |
258 | return -EINVAL; |
259 | |
260 | return !reset_sw && !reset_pwr; |
261 | } |
262 | |
263 | static const struct st_rproc_config st40_rproc_cfg = { |
264 | .sw_reset = true, |
265 | .pwr_reset = true, |
266 | .bootaddr_mask = GENMASK(28, 1), |
267 | }; |
268 | |
269 | static const struct st_rproc_config st231_rproc_cfg = { |
270 | .sw_reset = true, |
271 | .pwr_reset = false, |
272 | .bootaddr_mask = GENMASK(31, 6), |
273 | }; |
274 | |
275 | static const struct of_device_id st_rproc_match[] = { |
276 | { .compatible = "st,st40-rproc" , .data = &st40_rproc_cfg }, |
277 | { .compatible = "st,st231-rproc" , .data = &st231_rproc_cfg }, |
278 | {}, |
279 | }; |
280 | MODULE_DEVICE_TABLE(of, st_rproc_match); |
281 | |
282 | static int st_rproc_parse_dt(struct platform_device *pdev) |
283 | { |
284 | struct device *dev = &pdev->dev; |
285 | struct rproc *rproc = platform_get_drvdata(pdev); |
286 | struct st_rproc *ddata = rproc->priv; |
287 | struct device_node *np = dev->of_node; |
288 | int err; |
289 | |
290 | if (ddata->config->sw_reset) { |
291 | ddata->sw_reset = devm_reset_control_get_exclusive(dev, |
292 | id: "sw_reset" ); |
293 | if (IS_ERR(ptr: ddata->sw_reset)) { |
294 | dev_err(dev, "Failed to get S/W Reset\n" ); |
295 | return PTR_ERR(ptr: ddata->sw_reset); |
296 | } |
297 | } |
298 | |
299 | if (ddata->config->pwr_reset) { |
300 | ddata->pwr_reset = devm_reset_control_get_exclusive(dev, |
301 | id: "pwr_reset" ); |
302 | if (IS_ERR(ptr: ddata->pwr_reset)) { |
303 | dev_err(dev, "Failed to get Power Reset\n" ); |
304 | return PTR_ERR(ptr: ddata->pwr_reset); |
305 | } |
306 | } |
307 | |
308 | ddata->clk = devm_clk_get(dev, NULL); |
309 | if (IS_ERR(ptr: ddata->clk)) { |
310 | dev_err(dev, "Failed to get clock\n" ); |
311 | return PTR_ERR(ptr: ddata->clk); |
312 | } |
313 | |
314 | err = of_property_read_u32(np, propname: "clock-frequency" , out_value: &ddata->clk_rate); |
315 | if (err) { |
316 | dev_err(dev, "failed to get clock frequency\n" ); |
317 | return err; |
318 | } |
319 | |
320 | ddata->boot_base = syscon_regmap_lookup_by_phandle(np, property: "st,syscfg" ); |
321 | if (IS_ERR(ptr: ddata->boot_base)) { |
322 | dev_err(dev, "Boot base not found\n" ); |
323 | return PTR_ERR(ptr: ddata->boot_base); |
324 | } |
325 | |
326 | err = of_property_read_u32_index(np, propname: "st,syscfg" , index: 1, |
327 | out_value: &ddata->boot_offset); |
328 | if (err) { |
329 | dev_err(dev, "Boot offset not found\n" ); |
330 | return -EINVAL; |
331 | } |
332 | |
333 | err = clk_prepare(clk: ddata->clk); |
334 | if (err) |
335 | dev_err(dev, "failed to get clock\n" ); |
336 | |
337 | return err; |
338 | } |
339 | |
340 | static int st_rproc_probe(struct platform_device *pdev) |
341 | { |
342 | struct device *dev = &pdev->dev; |
343 | struct st_rproc *ddata; |
344 | struct device_node *np = dev->of_node; |
345 | struct rproc *rproc; |
346 | struct mbox_chan *chan; |
347 | int enabled; |
348 | int ret, i; |
349 | |
350 | rproc = rproc_alloc(dev, name: np->name, ops: &st_rproc_ops, NULL, len: sizeof(*ddata)); |
351 | if (!rproc) |
352 | return -ENOMEM; |
353 | |
354 | rproc->has_iommu = false; |
355 | ddata = rproc->priv; |
356 | ddata->config = (struct st_rproc_config *)device_get_match_data(dev); |
357 | if (!ddata->config) { |
358 | ret = -ENODEV; |
359 | goto free_rproc; |
360 | } |
361 | |
362 | platform_set_drvdata(pdev, data: rproc); |
363 | |
364 | ret = st_rproc_parse_dt(pdev); |
365 | if (ret) |
366 | goto free_rproc; |
367 | |
368 | enabled = st_rproc_state(pdev); |
369 | if (enabled < 0) { |
370 | ret = enabled; |
371 | goto free_clk; |
372 | } |
373 | |
374 | if (enabled) { |
375 | atomic_inc(v: &rproc->power); |
376 | rproc->state = RPROC_RUNNING; |
377 | } else { |
378 | clk_set_rate(clk: ddata->clk, rate: ddata->clk_rate); |
379 | } |
380 | |
381 | if (of_property_present(np, propname: "mbox-names" )) { |
382 | ddata->mbox_client_vq0.dev = dev; |
383 | ddata->mbox_client_vq0.tx_done = NULL; |
384 | ddata->mbox_client_vq0.tx_block = false; |
385 | ddata->mbox_client_vq0.knows_txdone = false; |
386 | ddata->mbox_client_vq0.rx_callback = st_rproc_mbox_callback_vq0; |
387 | |
388 | ddata->mbox_client_vq1.dev = dev; |
389 | ddata->mbox_client_vq1.tx_done = NULL; |
390 | ddata->mbox_client_vq1.tx_block = false; |
391 | ddata->mbox_client_vq1.knows_txdone = false; |
392 | ddata->mbox_client_vq1.rx_callback = st_rproc_mbox_callback_vq1; |
393 | |
394 | /* |
395 | * To control a co-processor without IPC mechanism. |
396 | * This driver can be used without mbox and rpmsg. |
397 | */ |
398 | chan = mbox_request_channel_byname(cl: &ddata->mbox_client_vq0, name: "vq0_rx" ); |
399 | if (IS_ERR(ptr: chan)) { |
400 | dev_err(&rproc->dev, "failed to request mbox chan 0\n" ); |
401 | ret = PTR_ERR(ptr: chan); |
402 | goto free_clk; |
403 | } |
404 | ddata->mbox_chan[ST_RPROC_VQ0 * MBOX_MAX + MBOX_RX] = chan; |
405 | |
406 | chan = mbox_request_channel_byname(cl: &ddata->mbox_client_vq0, name: "vq0_tx" ); |
407 | if (IS_ERR(ptr: chan)) { |
408 | dev_err(&rproc->dev, "failed to request mbox chan 0\n" ); |
409 | ret = PTR_ERR(ptr: chan); |
410 | goto free_mbox; |
411 | } |
412 | ddata->mbox_chan[ST_RPROC_VQ0 * MBOX_MAX + MBOX_TX] = chan; |
413 | |
414 | chan = mbox_request_channel_byname(cl: &ddata->mbox_client_vq1, name: "vq1_rx" ); |
415 | if (IS_ERR(ptr: chan)) { |
416 | dev_err(&rproc->dev, "failed to request mbox chan 1\n" ); |
417 | ret = PTR_ERR(ptr: chan); |
418 | goto free_mbox; |
419 | } |
420 | ddata->mbox_chan[ST_RPROC_VQ1 * MBOX_MAX + MBOX_RX] = chan; |
421 | |
422 | chan = mbox_request_channel_byname(cl: &ddata->mbox_client_vq1, name: "vq1_tx" ); |
423 | if (IS_ERR(ptr: chan)) { |
424 | dev_err(&rproc->dev, "failed to request mbox chan 1\n" ); |
425 | ret = PTR_ERR(ptr: chan); |
426 | goto free_mbox; |
427 | } |
428 | ddata->mbox_chan[ST_RPROC_VQ1 * MBOX_MAX + MBOX_TX] = chan; |
429 | } |
430 | |
431 | ret = rproc_add(rproc); |
432 | if (ret) |
433 | goto free_mbox; |
434 | |
435 | return 0; |
436 | |
437 | free_mbox: |
438 | for (i = 0; i < ST_RPROC_MAX_VRING * MBOX_MAX; i++) |
439 | mbox_free_channel(chan: ddata->mbox_chan[i]); |
440 | free_clk: |
441 | clk_unprepare(clk: ddata->clk); |
442 | free_rproc: |
443 | rproc_free(rproc); |
444 | return ret; |
445 | } |
446 | |
447 | static void st_rproc_remove(struct platform_device *pdev) |
448 | { |
449 | struct rproc *rproc = platform_get_drvdata(pdev); |
450 | struct st_rproc *ddata = rproc->priv; |
451 | int i; |
452 | |
453 | rproc_del(rproc); |
454 | |
455 | clk_disable_unprepare(clk: ddata->clk); |
456 | |
457 | for (i = 0; i < ST_RPROC_MAX_VRING * MBOX_MAX; i++) |
458 | mbox_free_channel(chan: ddata->mbox_chan[i]); |
459 | |
460 | rproc_free(rproc); |
461 | } |
462 | |
463 | static struct platform_driver st_rproc_driver = { |
464 | .probe = st_rproc_probe, |
465 | .remove_new = st_rproc_remove, |
466 | .driver = { |
467 | .name = "st-rproc" , |
468 | .of_match_table = of_match_ptr(st_rproc_match), |
469 | }, |
470 | }; |
471 | module_platform_driver(st_rproc_driver); |
472 | |
473 | MODULE_DESCRIPTION("ST Remote Processor Control Driver" ); |
474 | MODULE_AUTHOR("Ludovic Barre <ludovic.barre@st.com>" ); |
475 | MODULE_LICENSE("GPL v2" ); |
476 | |