blob: 86acd1b98c70d9d4be2f08071479cfb4e5c12460 [file] [log] [blame]
Nathan Barrett-Morrison8f5030c2025-02-26 12:30:32 -05001// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * (C) Copyright 2022 - Analog Devices, Inc.
4 *
5 * Written and/or maintained by Timesys Corporation
6 *
7 * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
8 * Contact: Greg Malysa <greg.malysa@timesys.com>
9 *
10 * Analog Devices SC5xx remoteproc driver for loading code onto SHARC cores
11 */
12
13#include <dm.h>
14#include <regmap.h>
15#include <remoteproc.h>
16#include <syscon.h>
17#include <dm/device_compat.h>
18#include <linux/delay.h>
19#include <linux/io.h>
20
21/* Register offsets */
22#ifdef CONFIG_SC58X
23#define ADI_RCU_REG_CTL 0x00
24#define ADI_RCU_REG_STAT 0x04
25#define ADI_RCU_REG_CRCTL 0x08
26#define ADI_RCU_REG_CRSTAT 0x0c
27#define ADI_RCU_REG_SIDIS 0x10
28#define ADI_RCU_REG_SISTAT 0x14
29#define ADI_RCU_REG_BCODE 0x1c
30#define ADI_RCU_REG_SVECT0 0x20
31#define ADI_RCU_REG_SVECT1 0x24
32#define ADI_RCU_REG_SVECT2 0x28
33#define ADI_RCU_REG_MSG 0x60
34#define ADI_RCU_REG_MSG_SET 0x64
35#define ADI_RCU_REG_MSG_CLR 0x68
36#else
37#define ADI_RCU_REG_CTL 0x00
38#define ADI_RCU_REG_STAT 0x04
39#define ADI_RCU_REG_CRCTL 0x08
40#define ADI_RCU_REG_CRSTAT 0x0c
41#define ADI_RCU_REG_SRRQSTAT 0x18
42#define ADI_RCU_REG_SIDIS 0x1c
43#define ADI_RCU_REG_SISTAT 0x20
44#define ADI_RCU_REG_SVECT_LCK 0x24
45#define ADI_RCU_REG_BCODE 0x28
46#define ADI_RCU_REG_SVECT0 0x2c
47#define ADI_RCU_REG_SVECT1 0x30
48#define ADI_RCU_REG_SVECT2 0x34
49#define ADI_RCU_REG_MSG 0x6c
50#define ADI_RCU_REG_MSG_SET 0x70
51#define ADI_RCU_REG_MSG_CLR 0x74
52#endif /* CONFIG_SC58X */
53
54/* Register bit definitions */
55#define ADI_RCU_CTL_SYSRST BIT(0)
56
57/* Bit values for the RCU0_MSG register */
58#define RCU0_MSG_C0IDLE 0x00000100 /* Core 0 Idle */
59#define RCU0_MSG_C1IDLE 0x00000200 /* Core 1 Idle */
60#define RCU0_MSG_C2IDLE 0x00000400 /* Core 2 Idle */
61#define RCU0_MSG_CRR0 0x00001000 /* Core 0 reset request */
62#define RCU0_MSG_CRR1 0x00002000 /* Core 1 reset request */
63#define RCU0_MSG_CRR2 0x00004000 /* Core 2 reset request */
64#define RCU0_MSG_C1ACTIVATE 0x00080000 /* Core 1 Activated */
65#define RCU0_MSG_C2ACTIVATE 0x00100000 /* Core 2 Activated */
66
67struct sc5xx_rproc_data {
68 /* Address to load to svect when rebooting core */
69 u32 load_addr;
70
71 /* RCU parameters */
72 struct regmap *rcu;
73 u32 svect_offset;
74 u32 coreid;
75};
76
77struct block_code_flag {
78 u32 bcode:4, /* 0-3 */
79 bflag_save:1, /* 4 */
80 bflag_aux:1, /* 5 */
81 breserved:1, /* 6 */
82 bflag_forward:1, /* 7 */
83 bflag_fill:1, /* 8 */
84 bflag_quickboot:1, /* 9 */
85 bflag_callback:1, /* 10 */
86 bflag_init:1, /* 11 */
87 bflag_ignore:1, /* 12 */
88 bflag_indirect:1, /* 13 */
89 bflag_first:1, /* 14 */
90 bflag_final:1, /* 15 */
91 bhdrchk:8, /* 16-23 */
92 bhdrsign:8; /* 0xAD, 0xAC or 0xAB */
93};
94
95struct ldr_hdr {
96 struct block_code_flag bcode_flag;
97 u32 target_addr;
98 u32 byte_count;
99 u32 argument;
100};
101
102static int is_final(struct ldr_hdr *hdr)
103{
104 return hdr->bcode_flag.bflag_final;
105}
106
107static int is_empty(struct ldr_hdr *hdr)
108{
109 return hdr->bcode_flag.bflag_ignore || (hdr->byte_count == 0);
110}
111
112static int adi_valid_firmware(struct ldr_hdr *adi_ldr_hdr)
113{
114 if (!adi_ldr_hdr->byte_count &&
115 (adi_ldr_hdr->bcode_flag.bhdrsign == 0xAD ||
116 adi_ldr_hdr->bcode_flag.bhdrsign == 0xAC ||
117 adi_ldr_hdr->bcode_flag.bhdrsign == 0xAB))
118 return 1;
119
120 return 0;
121}
122
123static int sharc_load(struct udevice *dev, ulong addr, ulong size)
124{
125 struct sc5xx_rproc_data *priv = dev_get_priv(dev);
126 size_t offset;
127 u8 *buf = (u8 *)addr;
128 struct ldr_hdr *ldr = (struct ldr_hdr *)addr;
129 struct ldr_hdr *block_hdr;
130 struct ldr_hdr *next_hdr;
131
132 if (!adi_valid_firmware(ldr)) {
133 dev_err(dev, "Firmware at 0x%lx does not appear to be an LDR image\n", addr);
134 dev_err(dev, "Note: Signed firmware is not currently supported\n");
135 return -EINVAL;
136 }
137
138 do {
139 block_hdr = (struct ldr_hdr *)buf;
140 offset = sizeof(struct ldr_hdr) + (block_hdr->bcode_flag.bflag_fill ?
141 0 : block_hdr->byte_count);
142 next_hdr = (struct ldr_hdr *)(buf + offset);
143
144 if (block_hdr->bcode_flag.bflag_first)
145 priv->load_addr = (unsigned long)block_hdr->target_addr;
146
147 if (!is_empty(block_hdr)) {
148 if (block_hdr->bcode_flag.bflag_fill) {
149 memset_io((void *)(phys_addr_t)block_hdr->target_addr,
150 block_hdr->argument,
151 block_hdr->byte_count);
152 } else {
153 memcpy_toio((void *)(phys_addr_t)block_hdr->target_addr,
154 buf + sizeof(struct ldr_hdr),
155 block_hdr->byte_count);
156 }
157 }
158
159 if (is_final(block_hdr))
160 break;
161
162 buf += offset;
163 } while (1);
164
165 return 0;
166}
167
168static void sharc_reset(struct sc5xx_rproc_data *priv)
169{
170 u32 coreid = priv->coreid;
171 u32 val;
172
173 /* First put core in reset.
174 * Clear CRSTAT bit for given coreid.
175 */
176 regmap_write(priv->rcu, ADI_RCU_REG_CRSTAT, 1 << coreid);
177
178 /* Set SIDIS to disable the system interface */
179 regmap_read(priv->rcu, ADI_RCU_REG_SIDIS, &val);
180 regmap_write(priv->rcu, ADI_RCU_REG_SIDIS, val | (1 << (coreid - 1)));
181
182 /*
183 * Wait for access to coreX have been disabled and all the pending
184 * transactions have completed
185 */
186 udelay(50);
187
188 /* Set CRCTL bit to put core in reset */
189 regmap_read(priv->rcu, ADI_RCU_REG_CRCTL, &val);
190 regmap_write(priv->rcu, ADI_RCU_REG_CRCTL, val | (1 << coreid));
191
192 /* Poll until Core is in reset */
193 while (!(regmap_read(priv->rcu, ADI_RCU_REG_CRSTAT, &val), val & (1 << coreid)))
194 ;
195
196 /* Clear SIDIS to reenable the system interface */
197 regmap_read(priv->rcu, ADI_RCU_REG_SIDIS, &val);
198 regmap_write(priv->rcu, ADI_RCU_REG_SIDIS, val & ~(1 << (coreid - 1)));
199
200 udelay(50);
201
202 /* Take Core out of reset */
203 regmap_read(priv->rcu, ADI_RCU_REG_CRCTL, &val);
204 regmap_write(priv->rcu, ADI_RCU_REG_CRCTL, val & ~(1 << coreid));
205
206 /* Wait for done */
207 udelay(50);
208}
209
210static int sharc_start(struct udevice *dev)
211{
212 struct sc5xx_rproc_data *priv = dev_get_priv(dev);
213
214 /* Write load address to appropriate SVECT for core */
215 regmap_write(priv->rcu, priv->svect_offset, priv->load_addr);
216
217 sharc_reset(priv);
218
219 /* Clear the IDLE bit when start the SHARC core */
220 regmap_write(priv->rcu, ADI_RCU_REG_MSG_CLR, RCU0_MSG_C0IDLE << priv->coreid);
221
222 /* Notify CCES */
223 regmap_write(priv->rcu, ADI_RCU_REG_MSG_SET, RCU0_MSG_C1ACTIVATE << (priv->coreid - 1));
224 return 0;
225}
226
227static const struct dm_rproc_ops sc5xx_ops = {
228 .load = sharc_load,
229 .start = sharc_start,
230};
231
232static int sc5xx_probe(struct udevice *dev)
233{
234 struct sc5xx_rproc_data *priv = dev_get_priv(dev);
235 u32 coreid;
236
237 if (dev_read_u32(dev, "coreid", &coreid)) {
238 dev_err(dev, "Missing property coreid\n");
239 return -ENOENT;
240 }
241
242 priv->coreid = coreid;
243 switch (coreid) {
244 case 1:
245 priv->svect_offset = ADI_RCU_REG_SVECT1;
246 break;
247 case 2:
248 priv->svect_offset = ADI_RCU_REG_SVECT2;
249 break;
250 default:
251 dev_err(dev, "Invalid value %d for coreid, must be 1 or 2\n", coreid);
252 return -EINVAL;
253 }
254
255 priv->rcu = syscon_regmap_lookup_by_phandle(dev, "adi,rcu");
256 if (IS_ERR(priv->rcu))
257 return PTR_ERR(priv->rcu);
258
259 dev_err(dev, "sc5xx remoteproc core %d available\n", priv->coreid);
260
261 return 0;
262}
263
264static const struct udevice_id sc5xx_ids[] = {
265 { .compatible = "adi,sc5xx-rproc" },
266 { }
267};
268
269U_BOOT_DRIVER(adi_sc5xx_rproc) = {
270 .name = "adi_sc5xx_rproc",
271 .of_match = sc5xx_ids,
272 .id = UCLASS_REMOTEPROC,
273 .ops = &sc5xx_ops,
274 .probe = sc5xx_probe,
275 .priv_auto = sizeof(struct sc5xx_rproc_data),
276 .flags = 0,
277};