blob: 1a50cd3289bc40de23f23630f2b591fba2241dbb [file] [log] [blame]
Marek Vasut15388792024-12-20 01:02:14 +01001// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
2/*
3 * Copyright (C) 2024 Renesas Electronics Corp.
4 */
5
6#include <asm/io.h>
7#include <dm.h>
8#include <dm/device-internal.h>
9#include <dm/lists.h>
10#include <errno.h>
11#include <hang.h>
12#include <linux/iopoll.h>
13#include <linux/sizes.h>
14#include <malloc.h>
15#include <remoteproc.h>
16
17/* R-Car V4H/V4M contain 3 clusters / 3 cores */
18#define RCAR4_CR52_CORES 3
19
20/* Reset Control Register for Cortex-R52 #n */
21#define APMU_CRRSTCTRL(n) (0x304 + ((n) * 0x40))
22#define APMU_CRRSTCTRL_CR52RST BIT(0)
23
24/* Base Address Register for Cortex-R52 #n */
25#define APMU_CRBARP(n) (0x33c + ((n) * 0x40))
26#define APMU_CRBARP_CR_VLD_BARP BIT(0)
27#define APMU_CRBARP_CR_BAREN_VALID BIT(4)
28#define APMU_CRBARP_CR_RBAR_MASK 0xfffc0000
29#define APMU_CRBARP_CR_RBAR_ALIGN 0x40000
30
31/**
32 * struct renesas_apmu_rproc_privdata - remote processor private data
33 * @regs: controller registers
34 * @core_id: CPU core id
35 * @trampoline: jump trampoline code
36 */
37struct renesas_apmu_rproc_privdata {
38 void __iomem *regs;
39 ulong core_id;
40 u32 *trampoline;
41};
42
43/*
44 * CRBARP address is aligned to 0x40000 / 256 kiB , this trampoline
45 * allows arbitrary address alignment at instruction granularity.
46 */
47static const u32 renesas_apmu_rproc_trampoline[4] = {
48 0xe59f0004, /* ldr r0, [pc, #4] */
49 0xe1a0f000, /* mov pc, r0 */
50 0xeafffffe, /* 1: b 1b */
51 0xabcd1234 /* jump target (rewritten on load) */
52};
53
54/**
55 * renesas_apmu_rproc_load() - Load the remote processor
56 * @dev: corresponding remote processor device
57 * @addr: Address in memory where image is stored
58 * @size: Size in bytes of the image
59 *
60 * Return: 0 if all went ok, else corresponding -ve error
61 */
62static int renesas_apmu_rproc_load(struct udevice *dev, ulong addr, ulong size)
63{
64 struct renesas_apmu_rproc_privdata *priv = dev_get_priv(dev);
65 u32 trampolineaddr = (u32)(uintptr_t)(priv->trampoline);
66
67 priv->trampoline[3] = addr;
68 flush_dcache_range(trampolineaddr,
69 trampolineaddr +
70 sizeof(renesas_apmu_rproc_trampoline));
Marek Vasut81486a32025-01-29 22:54:50 +010071 invalidate_dcache_range(trampolineaddr,
72 trampolineaddr +
73 sizeof(renesas_apmu_rproc_trampoline));
74 flush_dcache_range(addr, addr + size);
75 invalidate_dcache_range(addr, addr + size);
76 asm volatile("dsb sy\n");
77 asm volatile("isb sy\n");
Marek Vasut15388792024-12-20 01:02:14 +010078
79 /* CR52 boot address set */
80 writel(trampolineaddr | APMU_CRBARP_CR_VLD_BARP,
81 priv->regs + APMU_CRBARP(priv->core_id));
82 writel(trampolineaddr | APMU_CRBARP_CR_VLD_BARP | APMU_CRBARP_CR_BAREN_VALID,
83 priv->regs + APMU_CRBARP(priv->core_id));
84
85 return 0;
86}
87
88/**
89 * renesas_apmu_rproc_start() - Start the remote processor
90 * @dev: corresponding remote processor device
91 *
92 * Return: 0 if all went ok, else corresponding -ve error
93 */
94static int renesas_apmu_rproc_start(struct udevice *dev)
95{
96 struct renesas_apmu_rproc_privdata *priv = dev_get_priv(dev);
97
98 /* Clear APMU_CRRSTCTRL_CR52RST, the only bit in this register */
99 writel(0, priv->regs + APMU_CRRSTCTRL(priv->core_id));
100
101 return 0;
102}
103
104/**
105 * renesas_apmu_rproc_stop() - Stop the remote processor
106 * @dev: corresponding remote processor device
107 *
108 * Return: 0 if all went ok, else corresponding -ve error
109 */
110static int renesas_apmu_rproc_stop(struct udevice *dev)
111{
112 struct renesas_apmu_rproc_privdata *priv = dev_get_priv(dev);
113
114 /* Set APMU_CRRSTCTRL_CR52RST, the only bit in this register */
115 writel(APMU_CRRSTCTRL_CR52RST,
116 priv->regs + APMU_CRRSTCTRL(priv->core_id));
117
118 return 0;
119}
120
121/**
122 * renesas_apmu_rproc_reset() - Reset the remote processor
123 * @dev: corresponding remote processor device
124 *
125 * Return: 0 if all went ok, else corresponding -ve error
126 */
127static int renesas_apmu_rproc_reset(struct udevice *dev)
128{
129 renesas_apmu_rproc_stop(dev);
130 renesas_apmu_rproc_start(dev);
131 return 0;
132}
133
134/**
135 * renesas_apmu_rproc_is_running() - Is the remote processor running
136 * @dev: corresponding remote processor device
137 *
138 * Return: 0 if the remote processor is running, 1 otherwise
139 */
140static int renesas_apmu_rproc_is_running(struct udevice *dev)
141{
142 struct renesas_apmu_rproc_privdata *priv = dev_get_priv(dev);
143
144 return readl(priv->regs + APMU_CRRSTCTRL(priv->core_id)) &
145 APMU_CRRSTCTRL_CR52RST;
146}
147
148/**
149 * renesas_apmu_rproc_init() - Initialize the remote processor CRBAR registers
150 * @dev: corresponding remote processor device
151 *
152 * Return: 0 if all went ok, else corresponding -ve error
153 */
154static int renesas_apmu_rproc_init(struct udevice *dev)
155{
156 struct renesas_apmu_rproc_privdata *priv = dev_get_priv(dev);
157
158 /* If the core is running already, do nothing. */
159 if (renesas_apmu_rproc_is_running(dev))
160 return 0;
161
162 /* Clear and invalidate CRBARP content */
163 writel(0, priv->regs + APMU_CRBARP(priv->core_id));
164
165 return 0;
166}
167
168/**
169 * renesas_apmu_rproc_device_to_virt() - Convert device address to virtual address
170 * @dev: corresponding remote processor device
171 * @da: device address
172 * @size: Size of the memory region @da is pointing to
173 *
174 * Return: converted virtual address
175 */
176static void *renesas_apmu_rproc_device_to_virt(struct udevice *dev, ulong da,
177 ulong size)
178{
179 /*
180 * The Cortex R52 and A76 share the same address space,
181 * this operation is a no-op.
182 */
183 return (void *)da;
184}
185
186static const struct dm_rproc_ops renesas_apmu_rproc_ops = {
187 .init = renesas_apmu_rproc_init,
188 .load = renesas_apmu_rproc_load,
189 .start = renesas_apmu_rproc_start,
190 .stop = renesas_apmu_rproc_stop,
191 .reset = renesas_apmu_rproc_reset,
192 .is_running = renesas_apmu_rproc_is_running,
193 .device_to_virt = renesas_apmu_rproc_device_to_virt,
194};
195
196/**
197 * renesas_apmu_rproc_of_to_plat() - Convert OF data to platform data
198 * @dev: corresponding remote processor device
199 *
200 * Return: 0 if all went ok, else corresponding -ve error
201 */
202static int renesas_apmu_rproc_of_to_plat(struct udevice *dev)
203{
204 struct renesas_apmu_rproc_privdata *priv = dev_get_priv(dev);
205
206 priv->core_id = dev_get_driver_data(dev);
207
208 priv->regs = dev_read_addr_ptr(dev);
209 if (!priv->regs)
210 return -EINVAL;
211
212 priv->trampoline = memalign(APMU_CRBARP_CR_RBAR_ALIGN,
213 sizeof(renesas_apmu_rproc_trampoline));
214 if (!priv->trampoline)
215 return -ENOMEM;
216
217 memcpy(priv->trampoline, renesas_apmu_rproc_trampoline,
218 sizeof(renesas_apmu_rproc_trampoline));
219
220 return 0;
221}
222
223U_BOOT_DRIVER(renesas_apmu_cr52) = {
224 .name = "rcar-apmu-cr52",
225 .id = UCLASS_REMOTEPROC,
226 .ops = &renesas_apmu_rproc_ops,
227 .of_to_plat = renesas_apmu_rproc_of_to_plat,
228 .priv_auto = sizeof(struct renesas_apmu_rproc_privdata),
229};
230
231/**
232 * renesas_apmu_rproc_bind() - Bind rproc driver to each core control
233 * @dev: corresponding remote processor parent device
234 *
235 * Return: 0 if all went ok, else corresponding -ve error
236 */
237static int renesas_apmu_rproc_bind(struct udevice *parent)
238{
239 const ulong cr52cores = RCAR4_CR52_CORES;
240 ofnode pnode = dev_ofnode(parent);
241 struct udevice *cdev;
242 struct driver *cdrv;
243 char name[32];
244 ulong i;
245 int ret;
246
247 cdrv = lists_driver_lookup_name("rcar-apmu-cr52");
248 if (!cdrv)
249 return -ENOENT;
250
251 for (i = 0; i < cr52cores; i++) {
252 snprintf(name, sizeof(name), "rcar-apmu-cr52.%ld", i);
253 ret = device_bind_with_driver_data(parent, cdrv, strdup(name),
254 i, pnode, &cdev);
255 if (ret)
256 return ret;
257 }
258
259 return 0;
260}
261
262static const struct udevice_id renesas_apmu_rproc_ids[] = {
263 { .compatible = "renesas,r8a779g0-cr52" },
264 { .compatible = "renesas,r8a779h0-cr52" },
265 { }
266};
267
268U_BOOT_DRIVER(renesas_apmu_rproc) = {
269 .name = "rcar-apmu-rproc",
270 .of_match = renesas_apmu_rproc_ids,
271 .id = UCLASS_NOP,
272 .bind = renesas_apmu_rproc_bind,
273};