1 // SPDX-License-Identifier: GPL-2.0+
3 * Ingenic JZ47xx remoteproc driver
4 * Copyright 2019, Paul Cercueil <paul@crapouillou.net>
7 #include <linux/bitops.h>
10 #include <linux/interrupt.h>
12 #include <linux/module.h>
13 #include <linux/platform_device.h>
14 #include <linux/remoteproc.h>
16 #include "remoteproc_internal.h"
18 #define REG_AUX_CTRL 0x0
19 #define REG_AUX_MSG_ACK 0x10
20 #define REG_AUX_MSG 0x14
21 #define REG_CORE_MSG_ACK 0x18
22 #define REG_CORE_MSG 0x1C
24 #define AUX_CTRL_SLEEP BIT(31)
25 #define AUX_CTRL_MSG_IRQ_EN BIT(3)
26 #define AUX_CTRL_NMI_RESETS BIT(2)
27 #define AUX_CTRL_NMI BIT(1)
28 #define AUX_CTRL_SW_RESET BIT(0)
30 static bool auto_boot
;
31 module_param(auto_boot
, bool, 0400);
32 MODULE_PARM_DESC(auto_boot
,
33 "Auto-boot the remote processor [default=false]");
41 const struct vpu_mem_map
*map
;
46 static const struct vpu_mem_map vpu_mem_map
[] = {
47 { "tcsm0", 0x132b0000 },
48 { "tcsm1", 0xf4000000 },
49 { "sram", 0x132f0000 },
53 * struct vpu - Ingenic VPU remoteproc private structure
54 * @irq: interrupt number
55 * @clks: pointers to the VPU and AUX clocks
56 * @aux_base: raw pointer to the AUX interface registers
57 * @mem_info: array of struct vpu_mem_info, which contain the mapping info of
58 * each of the external memories
59 * @dev: private pointer to the device
63 struct clk_bulk_data clks
[2];
64 void __iomem
*aux_base
;
65 struct vpu_mem_info mem_info
[ARRAY_SIZE(vpu_mem_map
)];
69 static int ingenic_rproc_prepare(struct rproc
*rproc
)
71 struct vpu
*vpu
= rproc
->priv
;
74 /* The clocks must be enabled for the firmware to be loaded in TCSM */
75 ret
= clk_bulk_prepare_enable(ARRAY_SIZE(vpu
->clks
), vpu
->clks
);
77 dev_err(vpu
->dev
, "Unable to start clocks: %d\n", ret
);
82 static int ingenic_rproc_unprepare(struct rproc
*rproc
)
84 struct vpu
*vpu
= rproc
->priv
;
86 clk_bulk_disable_unprepare(ARRAY_SIZE(vpu
->clks
), vpu
->clks
);
91 static int ingenic_rproc_start(struct rproc
*rproc
)
93 struct vpu
*vpu
= rproc
->priv
;
98 /* Reset the AUX and enable message IRQ */
99 ctrl
= AUX_CTRL_NMI_RESETS
| AUX_CTRL_NMI
| AUX_CTRL_MSG_IRQ_EN
;
100 writel(ctrl
, vpu
->aux_base
+ REG_AUX_CTRL
);
105 static int ingenic_rproc_stop(struct rproc
*rproc
)
107 struct vpu
*vpu
= rproc
->priv
;
109 disable_irq(vpu
->irq
);
111 /* Keep AUX in reset mode */
112 writel(AUX_CTRL_SW_RESET
, vpu
->aux_base
+ REG_AUX_CTRL
);
117 static void ingenic_rproc_kick(struct rproc
*rproc
, int vqid
)
119 struct vpu
*vpu
= rproc
->priv
;
121 writel(vqid
, vpu
->aux_base
+ REG_CORE_MSG
);
124 static void *ingenic_rproc_da_to_va(struct rproc
*rproc
, u64 da
, size_t len
, bool *is_iomem
)
126 struct vpu
*vpu
= rproc
->priv
;
127 void __iomem
*va
= NULL
;
130 for (i
= 0; i
< ARRAY_SIZE(vpu_mem_map
); i
++) {
131 const struct vpu_mem_info
*info
= &vpu
->mem_info
[i
];
132 const struct vpu_mem_map
*map
= info
->map
;
134 if (da
>= map
->da
&& (da
+ len
) < (map
->da
+ info
->len
)) {
135 va
= info
->base
+ (da
- map
->da
);
140 return (__force
void *)va
;
143 static const struct rproc_ops ingenic_rproc_ops
= {
144 .prepare
= ingenic_rproc_prepare
,
145 .unprepare
= ingenic_rproc_unprepare
,
146 .start
= ingenic_rproc_start
,
147 .stop
= ingenic_rproc_stop
,
148 .kick
= ingenic_rproc_kick
,
149 .da_to_va
= ingenic_rproc_da_to_va
,
152 static irqreturn_t
vpu_interrupt(int irq
, void *data
)
154 struct rproc
*rproc
= data
;
155 struct vpu
*vpu
= rproc
->priv
;
158 vring
= readl(vpu
->aux_base
+ REG_AUX_MSG
);
160 /* Ack the interrupt */
161 writel(0, vpu
->aux_base
+ REG_AUX_MSG_ACK
);
163 return rproc_vq_interrupt(rproc
, vring
);
166 static int ingenic_rproc_probe(struct platform_device
*pdev
)
168 struct device
*dev
= &pdev
->dev
;
169 struct resource
*mem
;
175 rproc
= devm_rproc_alloc(dev
, "ingenic-vpu",
176 &ingenic_rproc_ops
, NULL
, sizeof(*vpu
));
180 rproc
->auto_boot
= auto_boot
;
183 vpu
->dev
= &pdev
->dev
;
184 platform_set_drvdata(pdev
, vpu
);
186 vpu
->aux_base
= devm_platform_ioremap_resource_byname(pdev
, "aux");
187 if (IS_ERR(vpu
->aux_base
)) {
188 dev_err(dev
, "Failed to ioremap\n");
189 return PTR_ERR(vpu
->aux_base
);
192 for (i
= 0; i
< ARRAY_SIZE(vpu_mem_map
); i
++) {
193 mem
= platform_get_resource_byname(pdev
, IORESOURCE_MEM
,
194 vpu_mem_map
[i
].name
);
196 vpu
->mem_info
[i
].base
= devm_ioremap_resource(dev
, mem
);
197 if (IS_ERR(vpu
->mem_info
[i
].base
)) {
198 ret
= PTR_ERR(vpu
->mem_info
[i
].base
);
199 dev_err(dev
, "Failed to ioremap\n");
203 vpu
->mem_info
[i
].len
= resource_size(mem
);
204 vpu
->mem_info
[i
].map
= &vpu_mem_map
[i
];
207 vpu
->clks
[0].id
= "vpu";
208 vpu
->clks
[1].id
= "aux";
210 ret
= devm_clk_bulk_get(dev
, ARRAY_SIZE(vpu
->clks
), vpu
->clks
);
212 dev_err(dev
, "Failed to get clocks\n");
216 vpu
->irq
= platform_get_irq(pdev
, 0);
220 ret
= devm_request_irq(dev
, vpu
->irq
, vpu_interrupt
, IRQF_NO_AUTOEN
,
223 dev_err(dev
, "Failed to request IRQ\n");
227 ret
= devm_rproc_add(dev
, rproc
);
229 dev_err(dev
, "Failed to register remote processor\n");
236 static const struct of_device_id ingenic_rproc_of_matches
[] = {
237 { .compatible
= "ingenic,jz4770-vpu-rproc", },
240 MODULE_DEVICE_TABLE(of
, ingenic_rproc_of_matches
);
242 static struct platform_driver ingenic_rproc_driver
= {
243 .probe
= ingenic_rproc_probe
,
245 .name
= "ingenic-vpu",
246 .of_match_table
= ingenic_rproc_of_matches
,
249 module_platform_driver(ingenic_rproc_driver
);
251 MODULE_LICENSE("GPL");
252 MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
253 MODULE_DESCRIPTION("Ingenic JZ47xx Remote Processor control driver");