1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
|
// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
/*
* Copyright (C) 2026 Renesas Electronics Corp.
*/
#include <asm/io.h>
#include <dm.h>
#include <dm/device-internal.h>
#include <dm/lists.h>
#include <errno.h>
#include <hang.h>
#include <linux/iopoll.h>
#include <linux/sizes.h>
#include <malloc.h>
#include <remoteproc.h>
/* R-Car X5H contains 1 SCP core, 6 lockstep Cortex-R52 and 32 Cortex-A720AE cores. */
#define RCAR5_SCP_CORES 1
#define RCAR5_CR52_CORES 12
#define RCAR5_CA720_CORES 32
#define SCP_BASE 0xc1340000
#define SCP_CPUWAIT (SCP_BASE + 0x30)
#define SCP_CPUWAIT_WAIT BIT(0)
#define SCP_STCM 0xc1000000
struct scp_scmi_shmem {
u32 reserved0;
u32 status;
u64 reserved1;
u32 flags;
u32 length;
u32 message_header;
u8 payload[];
};
struct scp_scmi_pd_power_state_set_a2p {
u32 flags;
u32 domain_id;
u32 power_state;
u32 boot_addr;
};
/* The addresses in range 0x08000000..0x1fffffff are incremented by 0xa0000000 */
#define MFIS_COMMON_BASE 0xb89e1000
#define MFIS_SCP_WACNTR (MFIS_COMMON_BASE + 0x904)
/* The addresses in range 0x08000000..0x1fffffff are incremented by 0xa0000000 */
#define MFIS_SCP_BASE 0xb8840000
#define MFIS_SCP_REICR8 (MFIS_SCP_BASE + 0x28004)
#define MFIS_SCP_CODEVALUE 0xacc00000
#define MFIS_SCP_REG_MASK GENMASK(19, 0)
/**
* mfis_scp_unlock() - Release MFIS-SCP lock
* @lck: MFIS lock register
*/
static void mfis_scp_unlock(const u32 lck)
{
writel(MFIS_SCP_CODEVALUE | (lck & MFIS_SCP_REG_MASK), MFIS_SCP_WACNTR);
}
#define SCP_SCMI_SHMEM_AREA09 0xc1060800
#define SCP_SCMI_STATUS_MASK 0x3
#define SCP_SCMI_STATUS_FREE 0x1
/**
* scp_wait_fw_free() - Wait for SCP channel to be free for communication
*/
static void scp_wait_fw_free(void)
{
struct scp_scmi_shmem *shmem = (struct scp_scmi_shmem *)SCP_SCMI_SHMEM_AREA09;
while ((shmem->status & SCP_SCMI_STATUS_MASK) != SCP_SCMI_STATUS_FREE)
mdelay(1);
}
/**
* scp_send_interrupt() - Raise interrupt on SCP side
*/
static void scp_send_interrupt(void)
{
mfis_scp_unlock(MFIS_SCP_REICR8);
/* Send SCP IRQ */
writel(1, MFIS_SCP_REICR8);
}
/* SCMI power domain IDs */
#define SCMI_PD_CORE_RT_CORE00 117
#define SCMI_PD_CORE_AP_CORE00 129
/*
* FIXME: This is custom extension to the SCMI PD protocol:
* - Protocol 0x11 (PD)
* - Command 0x11 (POWER_STATE_SET_BOOTADDR - custom)
* This must be removed when proper upstream SCP port exists
*/
#define SCMI_PD_POWER_STATE_SET_BOOTADDR 0x4411
/**
* scp_cpu_core_start() - Boot CPU core by invoking SCP via SCMI
* @core: CPU core to boot
* @ep: Entry point
*/
static void scp_cpu_core_start(const u32 core, const u32 ep)
{
struct scp_scmi_shmem *shmem = (struct scp_scmi_shmem *)SCP_SCMI_SHMEM_AREA09;
struct scp_scmi_pd_power_state_set_a2p scmi_parameter = {
.flags = 1, /* Asynchronous power transition using APMU */
.domain_id = core,
.power_state = 0, /* Power on */
.boot_addr = ep,
};
u32 status;
/* Wait for SCP to be free, then set it busy */
scp_wait_fw_free();
shmem->status &= ~SCP_SCMI_STATUS_FREE;
/* Set up the message and copy it to SHMEM */
shmem->message_header = SCMI_PD_POWER_STATE_SET_BOOTADDR;
memcpy(shmem->payload, &scmi_parameter, sizeof(scmi_parameter));
shmem->length = sizeof(shmem->message_header) + sizeof(scmi_parameter);
/* Send message to SCP and wait for completion */
scp_send_interrupt();
scp_wait_fw_free();
/* Read back the result */
status = readl((uintptr_t)shmem->payload);
if (status)
printf("SCP POWER_STATE_SET failed, status=0x%x\n", status);
}
/**
* struct renesas_rsip_rproc_privdata - remote processor private data
* @core_id: CPU core id
* @ep: Entry point
*/
struct renesas_rsip_rproc_privdata {
ulong core_id;
ulong ep;
};
/**
* renesas_rsip_rproc_load() - Load the remote processor
* @dev: corresponding remote processor device
* @addr: Address in memory where image is stored
* @size: Size in bytes of the image
*
* Return: 0 if all went ok, else corresponding -ve error
*/
static int renesas_rsip_rproc_load(struct udevice *dev, ulong addr, ulong size)
{
struct renesas_rsip_rproc_privdata *priv = dev_get_priv(dev);
priv->ep = addr;
if (priv->core_id == 0) /* SCP */
memcpy((void *)SCP_STCM, (void *)addr, size);
return 0;
}
/**
* renesas_rsip_rproc_start() - Start the remote processor
* @dev: corresponding remote processor device
*
* Return: 0 if all went ok, else corresponding -ve error
*/
static int renesas_rsip_rproc_start(struct udevice *dev)
{
struct renesas_rsip_rproc_privdata *priv = dev_get_priv(dev);
int scmi_core;
if (priv->core_id == 0) {
/* SCP */
clrbits_le32(SCP_CPUWAIT, SCP_CPUWAIT_WAIT);
return 0;
} else if (priv->core_id >= RCAR5_SCP_CORES &&
priv->core_id < RCAR5_SCP_CORES + RCAR5_CR52_CORES) {
/* CR52 */
scmi_core = priv->core_id - RCAR5_SCP_CORES +
SCMI_PD_CORE_RT_CORE00;
} else if (priv->core_id >= RCAR5_SCP_CORES + RCAR5_CR52_CORES &&
priv->core_id < RCAR5_SCP_CORES + RCAR5_CR52_CORES + RCAR5_CA720_CORES) {
/* CA720 */
scmi_core = priv->core_id - RCAR5_SCP_CORES - RCAR5_CR52_CORES +
SCMI_PD_CORE_AP_CORE00;
}
scp_cpu_core_start(scmi_core, priv->ep);
return 0;
}
/**
* renesas_rsip_rproc_stop() - Stop the remote processor
* @dev: corresponding remote processor device
*
* Return: 0 if all went ok, else corresponding -ve error
*/
static int renesas_rsip_rproc_stop(struct udevice *dev)
{
struct renesas_rsip_rproc_privdata *priv = dev_get_priv(dev);
if (priv->core_id == 0) { /* SCP */
setbits_le32(SCP_CPUWAIT, SCP_CPUWAIT_WAIT);
return 0;
}
return 0;
}
/**
* renesas_rsip_rproc_reset() - Reset the remote processor
* @dev: corresponding remote processor device
*
* Return: 0 if all went ok, else corresponding -ve error
*/
static int renesas_rsip_rproc_reset(struct udevice *dev)
{
renesas_rsip_rproc_stop(dev);
renesas_rsip_rproc_start(dev);
return 0;
}
/**
* renesas_rsip_rproc_is_running() - Is the remote processor running
* @dev: corresponding remote processor device
*
* Return: 0 if the remote processor is running, 1 otherwise
*/
static int renesas_rsip_rproc_is_running(struct udevice *dev)
{
/* We assume the core is stopped. */
return 1;
}
/**
* renesas_rsip_rproc_init() - Initialize the remote processor
* @dev: corresponding remote processor device
*
* Return: 0 if all went ok, else corresponding -ve error
*/
static int renesas_rsip_rproc_init(struct udevice *dev)
{
return 0;
}
/**
* renesas_rsip_rproc_device_to_virt() - Convert device address to virtual address
* @dev: corresponding remote processor device
* @da: device address
* @size: Size of the memory region @da is pointing to
* @is_iomem: optional pointer filled in to indicate if @da is iomapped memory
*
* Return: converted virtual address
*/
static void *renesas_rsip_rproc_device_to_virt(struct udevice *dev, ulong da,
ulong size, bool *is_iomem)
{
return (void *)da;
}
static const struct dm_rproc_ops renesas_rsip_rproc_ops = {
.init = renesas_rsip_rproc_init,
.load = renesas_rsip_rproc_load,
.start = renesas_rsip_rproc_start,
.stop = renesas_rsip_rproc_stop,
.reset = renesas_rsip_rproc_reset,
.is_running = renesas_rsip_rproc_is_running,
.device_to_virt = renesas_rsip_rproc_device_to_virt,
};
/**
* renesas_rsip_rproc_of_to_plat() - Convert OF data to platform data
* @dev: corresponding remote processor device
*
* Return: 0 if all went ok, else corresponding -ve error
*/
static int renesas_rsip_rproc_of_to_plat(struct udevice *dev)
{
struct renesas_rsip_rproc_privdata *priv = dev_get_priv(dev);
priv->core_id = dev_get_driver_data(dev);
return 0;
}
U_BOOT_DRIVER(renesas_rsip_core) = {
.name = "rcar-rsip-core",
.id = UCLASS_REMOTEPROC,
.ops = &renesas_rsip_rproc_ops,
.of_to_plat = renesas_rsip_rproc_of_to_plat,
.priv_auto = sizeof(struct renesas_rsip_rproc_privdata),
};
/**
* renesas_rsip_rproc_bind() - Bind rproc driver to each core control
* @dev: corresponding remote processor parent device
*
* Return: 0 if all went ok, else corresponding -ve error
*/
static int renesas_rsip_rproc_bind(struct udevice *parent)
{
ofnode pnode = dev_ofnode(parent);
struct udevice *cdev;
struct driver *cdrv;
char name[32];
ulong i;
int ret;
cdrv = lists_driver_lookup_name("rcar-rsip-core");
if (!cdrv)
return -ENOENT;
/* Singleton SCP core is core 0 */
ret = device_bind_with_driver_data(parent, cdrv,
strdup("rcar-rsip-scp"),
0, pnode, &cdev);
if (ret)
return ret;
/* Cores 1..13 are Cortex-R52 */
for (i = 0; i < RCAR5_CR52_CORES; i++) {
snprintf(name, sizeof(name), "rcar-rsip-cr.%ld", i);
ret = device_bind_with_driver_data(parent, cdrv, strdup(name),
i + RCAR5_SCP_CORES, pnode,
&cdev);
if (ret)
return ret;
}
/* Cores 14..46 are Cortex-A720 */
for (i = 0; i < RCAR5_CA720_CORES; i++) {
snprintf(name, sizeof(name), "rcar-rsip-ca.%ld", i);
ret = device_bind_with_driver_data(parent, cdrv, strdup(name),
i + RCAR5_SCP_CORES + RCAR5_CR52_CORES,
pnode, &cdev);
if (ret)
return ret;
}
return 0;
}
static const struct udevice_id renesas_rsip_rproc_ids[] = {
{ .compatible = "renesas,r8a78000-rproc" },
{ }
};
U_BOOT_DRIVER(renesas_rsip_rproc) = {
.name = "rcar-rsip-rproc",
.of_match = renesas_rsip_rproc_ids,
.id = UCLASS_NOP,
.bind = renesas_rsip_rproc_bind,
};
|