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)
36 const struct vpu_mem_map
*map
;
41 static const struct vpu_mem_map vpu_mem_map
[] = {
42 { "tcsm0", 0x132b0000 },
43 { "tcsm1", 0xf4000000 },
44 { "sram", 0x132f0000 },
48 * struct vpu - Ingenic VPU remoteproc private structure
49 * @irq: interrupt number
50 * @clks: pointers to the VPU and AUX clocks
51 * @aux_base: raw pointer to the AUX interface registers
52 * @mem_info: array of struct vpu_mem_info, which contain the mapping info of
53 * each of the external memories
54 * @dev: private pointer to the device
58 struct clk_bulk_data clks
[2];
59 void __iomem
*aux_base
;
60 struct vpu_mem_info mem_info
[ARRAY_SIZE(vpu_mem_map
)];
64 static int ingenic_rproc_prepare(struct rproc
*rproc
)
66 struct vpu
*vpu
= rproc
->priv
;
69 /* The clocks must be enabled for the firmware to be loaded in TCSM */
70 ret
= clk_bulk_prepare_enable(ARRAY_SIZE(vpu
->clks
), vpu
->clks
);
72 dev_err(vpu
->dev
, "Unable to start clocks: %d\n", ret
);
77 static int ingenic_rproc_unprepare(struct rproc
*rproc
)
79 struct vpu
*vpu
= rproc
->priv
;
81 clk_bulk_disable_unprepare(ARRAY_SIZE(vpu
->clks
), vpu
->clks
);
86 static int ingenic_rproc_start(struct rproc
*rproc
)
88 struct vpu
*vpu
= rproc
->priv
;
93 /* Reset the AUX and enable message IRQ */
94 ctrl
= AUX_CTRL_NMI_RESETS
| AUX_CTRL_NMI
| AUX_CTRL_MSG_IRQ_EN
;
95 writel(ctrl
, vpu
->aux_base
+ REG_AUX_CTRL
);
100 static int ingenic_rproc_stop(struct rproc
*rproc
)
102 struct vpu
*vpu
= rproc
->priv
;
104 disable_irq(vpu
->irq
);
106 /* Keep AUX in reset mode */
107 writel(AUX_CTRL_SW_RESET
, vpu
->aux_base
+ REG_AUX_CTRL
);
112 static void ingenic_rproc_kick(struct rproc
*rproc
, int vqid
)
114 struct vpu
*vpu
= rproc
->priv
;
116 writel(vqid
, vpu
->aux_base
+ REG_CORE_MSG
);
119 static void *ingenic_rproc_da_to_va(struct rproc
*rproc
, u64 da
, size_t len
)
121 struct vpu
*vpu
= rproc
->priv
;
122 void __iomem
*va
= NULL
;
125 for (i
= 0; i
< ARRAY_SIZE(vpu_mem_map
); i
++) {
126 const struct vpu_mem_info
*info
= &vpu
->mem_info
[i
];
127 const struct vpu_mem_map
*map
= info
->map
;
129 if (da
>= map
->da
&& (da
+ len
) < (map
->da
+ info
->len
)) {
130 va
= info
->base
+ (da
- map
->da
);
135 return (__force
void *)va
;
138 static const struct rproc_ops ingenic_rproc_ops
= {
139 .prepare
= ingenic_rproc_prepare
,
140 .unprepare
= ingenic_rproc_unprepare
,
141 .start
= ingenic_rproc_start
,
142 .stop
= ingenic_rproc_stop
,
143 .kick
= ingenic_rproc_kick
,
144 .da_to_va
= ingenic_rproc_da_to_va
,
147 static irqreturn_t
vpu_interrupt(int irq
, void *data
)
149 struct rproc
*rproc
= data
;
150 struct vpu
*vpu
= rproc
->priv
;
153 vring
= readl(vpu
->aux_base
+ REG_AUX_MSG
);
155 /* Ack the interrupt */
156 writel(0, vpu
->aux_base
+ REG_AUX_MSG_ACK
);
158 return rproc_vq_interrupt(rproc
, vring
);
161 static int ingenic_rproc_probe(struct platform_device
*pdev
)
163 struct device
*dev
= &pdev
->dev
;
164 struct resource
*mem
;
170 rproc
= devm_rproc_alloc(dev
, "ingenic-vpu",
171 &ingenic_rproc_ops
, NULL
, sizeof(*vpu
));
176 vpu
->dev
= &pdev
->dev
;
177 platform_set_drvdata(pdev
, vpu
);
179 mem
= platform_get_resource_byname(pdev
, IORESOURCE_MEM
, "aux");
180 vpu
->aux_base
= devm_ioremap_resource(dev
, mem
);
181 if (IS_ERR(vpu
->aux_base
)) {
182 dev_err(dev
, "Failed to ioremap\n");
183 return PTR_ERR(vpu
->aux_base
);
186 for (i
= 0; i
< ARRAY_SIZE(vpu_mem_map
); i
++) {
187 mem
= platform_get_resource_byname(pdev
, IORESOURCE_MEM
,
188 vpu_mem_map
[i
].name
);
190 vpu
->mem_info
[i
].base
= devm_ioremap_resource(dev
, mem
);
191 if (IS_ERR(vpu
->mem_info
[i
].base
)) {
192 ret
= PTR_ERR(vpu
->mem_info
[i
].base
);
193 dev_err(dev
, "Failed to ioremap\n");
197 vpu
->mem_info
[i
].len
= resource_size(mem
);
198 vpu
->mem_info
[i
].map
= &vpu_mem_map
[i
];
201 vpu
->clks
[0].id
= "vpu";
202 vpu
->clks
[1].id
= "aux";
204 ret
= devm_clk_bulk_get(dev
, ARRAY_SIZE(vpu
->clks
), vpu
->clks
);
206 dev_err(dev
, "Failed to get clocks\n");
210 vpu
->irq
= platform_get_irq(pdev
, 0);
214 ret
= devm_request_irq(dev
, vpu
->irq
, vpu_interrupt
, 0, "VPU", rproc
);
216 dev_err(dev
, "Failed to request IRQ\n");
220 disable_irq(vpu
->irq
);
222 ret
= devm_rproc_add(dev
, rproc
);
224 dev_err(dev
, "Failed to register remote processor\n");
231 static const struct of_device_id ingenic_rproc_of_matches
[] = {
232 { .compatible
= "ingenic,jz4770-vpu-rproc", },
235 MODULE_DEVICE_TABLE(of
, ingenic_rproc_of_matches
);
237 static struct platform_driver ingenic_rproc_driver
= {
238 .probe
= ingenic_rproc_probe
,
240 .name
= "ingenic-vpu",
241 .of_match_table
= ingenic_rproc_of_matches
,
244 module_platform_driver(ingenic_rproc_driver
);
246 MODULE_LICENSE("GPL");
247 MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
248 MODULE_DESCRIPTION("Ingenic JZ47xx Remote Processor control driver");