blob: 9aec138637bc08fc00bc424df4f318a973265024 [file] [log] [blame]
Keerthyf9982752021-06-22 12:04:28 +05301// SPDX-License-Identifier: GPL-2.0
2/*
3 * PRU-RTU remoteproc driver for various SoCs
4 *
Nishanth Menoneaa39c62023-11-01 15:56:03 -05005 * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/
Keerthyf9982752021-06-22 12:04:28 +05306 * Keerthy <j-keerthy@ti.com>
7 */
8
Keerthyf9982752021-06-22 12:04:28 +05309#include <dm.h>
10#include <elf.h>
11#include <dm/of_access.h>
12#include <remoteproc.h>
13#include <errno.h>
14#include <clk.h>
15#include <reset.h>
16#include <regmap.h>
17#include <syscon.h>
18#include <asm/io.h>
19#include <power-domain.h>
20#include <linux/pruss_driver.h>
21#include <dm/device_compat.h>
22
23/* PRU_ICSS_PRU_CTRL registers */
24#define PRU_CTRL_CTRL 0x0000
25#define PRU_CTRL_STS 0x0004
26#define PRU_CTRL_WAKEUP_EN 0x0008
27#define PRU_CTRL_CYCLE 0x000C
28#define PRU_CTRL_STALL 0x0010
29#define PRU_CTRL_CTBIR0 0x0020
30#define PRU_CTRL_CTBIR1 0x0024
31#define PRU_CTRL_CTPPR0 0x0028
32#define PRU_CTRL_CTPPR1 0x002C
33
34/* CTRL register bit-fields */
35#define CTRL_CTRL_SOFT_RST_N BIT(0)
36#define CTRL_CTRL_EN BIT(1)
37#define CTRL_CTRL_SLEEPING BIT(2)
38#define CTRL_CTRL_CTR_EN BIT(3)
39#define CTRL_CTRL_SINGLE_STEP BIT(8)
40#define CTRL_CTRL_RUNSTATE BIT(15)
41
42#define RPROC_FLAGS_SHIFT 16
43#define RPROC_FLAGS_NONE 0
44#define RPROC_FLAGS_ELF_PHDR BIT(0 + RPROC_FLAGS_SHIFT)
45#define RPROC_FLAGS_ELF_SHDR BIT(1 + RPROC_FLAGS_SHIFT)
46
47/**
48 * enum pru_mem - PRU core memory range identifiers
49 */
50enum pru_mem {
51 PRU_MEM_IRAM = 0,
52 PRU_MEM_CTRL,
53 PRU_MEM_DEBUG,
54 PRU_MEM_MAX,
55};
56
57struct pru_privdata {
58 phys_addr_t pru_iram;
59 phys_addr_t pru_ctrl;
60 phys_addr_t pru_debug;
61 fdt_size_t pru_iramsz;
62 fdt_size_t pru_ctrlsz;
63 fdt_size_t pru_debugsz;
64 const char *fw_name;
65 u32 iram_da;
66 u32 pdram_da;
67 u32 sdram_da;
68 u32 shrdram_da;
69 u32 bootaddr;
70 int id;
71 struct pruss *prusspriv;
72};
73
74static inline u32 pru_control_read_reg(struct pru_privdata *pru, unsigned int reg)
75{
76 return readl(pru->pru_ctrl + reg);
77}
78
79static inline
80void pru_control_write_reg(struct pru_privdata *pru, unsigned int reg, u32 val)
81{
82 writel(val, pru->pru_ctrl + reg);
83}
84
85static inline
86void pru_control_set_reg(struct pru_privdata *pru, unsigned int reg,
87 u32 mask, u32 set)
88{
89 u32 val;
90
91 val = pru_control_read_reg(pru, reg);
92 val &= ~mask;
93 val |= (set & mask);
94 pru_control_write_reg(pru, reg, val);
95}
96
97/**
98 * pru_rproc_set_ctable() - set the constant table index for the PRU
99 * @rproc: the rproc instance of the PRU
100 * @c: constant table index to set
101 * @addr: physical address to set it to
102 */
103static int pru_rproc_set_ctable(struct pru_privdata *pru, enum pru_ctable_idx c, u32 addr)
104{
105 unsigned int reg;
106 u32 mask, set;
107 u16 idx;
108 u16 idx_mask;
109
110 /* pointer is 16 bit and index is 8-bit so mask out the rest */
111 idx_mask = (c >= PRU_C28) ? 0xFFFF : 0xFF;
112
113 /* ctable uses bit 8 and upwards only */
114 idx = (addr >> 8) & idx_mask;
115
116 /* configurable ctable (i.e. C24) starts at PRU_CTRL_CTBIR0 */
117 reg = PRU_CTRL_CTBIR0 + 4 * (c >> 1);
118 mask = idx_mask << (16 * (c & 1));
119 set = idx << (16 * (c & 1));
120
121 pru_control_set_reg(pru, reg, mask, set);
122
123 return 0;
124}
125
126/**
127 * pru_start() - start the pru processor
128 * @dev: corresponding k3 remote processor device
129 *
130 * Return: 0 if all goes good, else appropriate error message.
131 */
132static int pru_start(struct udevice *dev)
133{
134 struct pru_privdata *priv;
135 int val = 0;
136
137 priv = dev_get_priv(dev);
138
139 pru_rproc_set_ctable(priv, PRU_C28, 0x100 << 8);
140
141 val = CTRL_CTRL_EN | ((priv->bootaddr >> 2) << 16);
142 writel(val, priv->pru_ctrl + PRU_CTRL_CTRL);
143
144 return 0;
145}
146
147/**
148 * pru_stop() - Stop pru processor
149 * @dev: corresponding k3 remote processor device
150 *
151 * Return: 0 if all goes good, else appropriate error message.
152 */
153static int pru_stop(struct udevice *dev)
154{
155 struct pru_privdata *priv;
156 int val = 0;
157
158 priv = dev_get_priv(dev);
159
160 val = readl(priv->pru_ctrl + PRU_CTRL_CTRL);
161 val &= ~CTRL_CTRL_EN;
162 writel(val, priv->pru_ctrl + PRU_CTRL_CTRL);
163
164 return 0;
165}
166
167/**
168 * pru_init() - Initialize the remote processor
169 * @dev: rproc device pointer
170 *
171 * Return: 0 if all went ok, else return appropriate error
172 */
173static int pru_init(struct udevice *dev)
174{
175 return 0;
176}
177
178/*
179 * Convert PRU device address (data spaces only) to kernel virtual address
180 *
181 * Each PRU has access to all data memories within the PRUSS, accessible at
182 * different ranges. So, look through both its primary and secondary Data
183 * RAMs as well as any shared Data RAM to convert a PRU device address to
184 * kernel virtual address. Data RAM0 is primary Data RAM for PRU0 and Data
185 * RAM1 is primary Data RAM for PRU1.
186 */
187static void *pru_d_da_to_pa(struct pru_privdata *priv, u32 da, int len)
188{
189 u32 offset;
190 void *pa = NULL;
191 phys_addr_t dram0, dram1, shrdram2;
192 u32 dram0sz, dram1sz, shrdram2sz;
193
194 if (len <= 0)
195 return NULL;
196
197 dram0 = priv->prusspriv->mem_regions[PRUSS_MEM_DRAM0].pa;
198 dram1 = priv->prusspriv->mem_regions[PRUSS_MEM_DRAM1].pa;
199 shrdram2 = priv->prusspriv->mem_regions[PRUSS_MEM_SHRD_RAM2].pa;
200 dram0sz = priv->prusspriv->mem_regions[PRUSS_MEM_DRAM0].size;
201 dram1sz = priv->prusspriv->mem_regions[PRUSS_MEM_DRAM1].size;
202 shrdram2sz = priv->prusspriv->mem_regions[PRUSS_MEM_SHRD_RAM2].size;
203
204 /* PRU1 has its local RAM addresses reversed */
205 if (priv->id == 1) {
206 dram1 = dram0;
207 dram1sz = dram0sz;
208
209 dram0 = priv->prusspriv->mem_regions[PRUSS_MEM_DRAM1].pa;
210 dram0sz = priv->prusspriv->mem_regions[PRUSS_MEM_DRAM1].size;
211 }
212
213 if (da >= priv->pdram_da && da + len <= priv->pdram_da + dram0sz) {
214 offset = da - priv->pdram_da;
215 pa = (__force void *)(dram0 + offset);
216 } else if (da >= priv->sdram_da &&
217 da + len <= priv->sdram_da + dram1sz) {
218 offset = da - priv->sdram_da;
219 pa = (__force void *)(dram1 + offset);
220 } else if (da >= priv->shrdram_da &&
221 da + len <= priv->shrdram_da + shrdram2sz) {
222 offset = da - priv->shrdram_da;
223 pa = (__force void *)(shrdram2 + offset);
224 }
225
226 return pa;
227}
228
229/*
230 * Convert PRU device address (instruction space) to kernel virtual address
231 *
232 * A PRU does not have an unified address space. Each PRU has its very own
233 * private Instruction RAM, and its device address is identical to that of
234 * its primary Data RAM device address.
235 */
236static void *pru_i_da_to_pa(struct pru_privdata *priv, u32 da, int len)
237{
238 u32 offset;
239 void *pa = NULL;
240
241 if (len <= 0)
242 return NULL;
243
244 if (da >= priv->iram_da &&
245 da + len <= priv->iram_da + priv->pru_iramsz) {
246 offset = da - priv->iram_da;
247 pa = (__force void *)(priv->pru_iram + offset);
248 }
249
250 return pa;
251}
252
253/* PRU-specific address translator */
254static void *pru_da_to_pa(struct pru_privdata *priv, u64 da, int len, u32 flags)
255{
256 void *pa;
257 u32 exec_flag;
258
259 exec_flag = ((flags & RPROC_FLAGS_ELF_SHDR) ? flags & SHF_EXECINSTR :
260 ((flags & RPROC_FLAGS_ELF_PHDR) ? flags & PF_X : 0));
261
262 if (exec_flag)
263 pa = pru_i_da_to_pa(priv, da, len);
264 else
265 pa = pru_d_da_to_pa(priv, da, len);
266
267 return pa;
268}
269
270/*
271 * Custom memory copy implementation for ICSSG PRU/RTU Cores
272 *
273 * The ICSSG PRU/RTU cores have a memory copying issue with IRAM memories, that
274 * is not seen on previous generation SoCs. The data is reflected properly in
275 * the IRAM memories only for integer (4-byte) copies. Any unaligned copies
276 * result in all the other pre-existing bytes zeroed out within that 4-byte
277 * boundary, thereby resulting in wrong text/code in the IRAMs. Also, the
278 * IRAM memory port interface does not allow any 8-byte copies (as commonly
279 * used by ARM64 memcpy implementation) and throws an exception. The DRAM
280 * memory ports do not show this behavior. Use this custom copying function
281 * to properly load the PRU/RTU firmware images on all memories for simplicity.
282 *
283 * TODO: Improve the function to deal with additional corner cases like
284 * unaligned copy sizes or sub-integer trailing bytes when the need arises.
285 */
286static int pru_rproc_memcpy(void *dest, void *src, size_t count)
287{
288 const int *s = src;
289 int *d = dest;
290 int size = count / 4;
291 int *tmp_src = NULL;
292
293 /* limited to 4-byte aligned addresses and copy sizes */
294 if ((long)dest % 4 || count % 4)
295 return -EINVAL;
296
297 /* src offsets in ELF firmware image can be non-aligned */
298 if ((long)src % 4) {
299 tmp_src = malloc(count);
300 if (!tmp_src)
301 return -ENOMEM;
302
303 memcpy(tmp_src, src, count);
304 s = tmp_src;
305 }
306
307 while (size--)
308 *d++ = *s++;
309
310 kfree(tmp_src);
311
312 return 0;
313}
314
315/**
316 * pru_load() - Load pru firmware
317 * @dev: corresponding k3 remote processor device
318 * @addr: Address on the RAM from which firmware is to be loaded
319 * @size: Size of the pru firmware in bytes
320 *
321 * Return: 0 if all goes good, else appropriate error message.
322 */
323static int pru_load(struct udevice *dev, ulong addr, ulong size)
324{
325 struct pru_privdata *priv;
326 Elf32_Ehdr *ehdr;
327 Elf32_Phdr *phdr;
328 int i, ret = 0;
329
330 priv = dev_get_priv(dev);
331
332 ehdr = (Elf32_Ehdr *)addr;
333 phdr = (Elf32_Phdr *)(addr + ehdr->e_phoff);
334
335 /* go through the available ELF segments */
336 for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
337 u32 da = phdr->p_paddr;
338 u32 memsz = phdr->p_memsz;
339 u32 filesz = phdr->p_filesz;
340 u32 offset = phdr->p_offset;
341 void *ptr;
342
343 if (phdr->p_type != PT_LOAD)
344 continue;
345
346 dev_dbg(dev, "phdr: type %d da 0x%x memsz 0x%x filesz 0x%x\n",
347 phdr->p_type, da, memsz, filesz);
348
349 if (filesz > memsz) {
350 dev_dbg(dev, "bad phdr filesz 0x%x memsz 0x%x\n",
351 filesz, memsz);
352 ret = -EINVAL;
353 break;
354 }
355
356 if (offset + filesz > size) {
357 dev_dbg(dev, "truncated fw: need 0x%x avail 0x%zx\n",
358 offset + filesz, size);
359 ret = -EINVAL;
360 break;
361 }
362
363 /* grab the kernel address for this device address */
364 ptr = pru_da_to_pa(priv, da, memsz,
365 RPROC_FLAGS_ELF_PHDR | phdr->p_flags);
366 if (!ptr) {
367 dev_dbg(dev, "bad phdr da 0x%x mem 0x%x\n", da, memsz);
368 ret = -EINVAL;
369 break;
370 }
371
372 /* skip the memzero logic performed by remoteproc ELF loader */
373 if (!phdr->p_filesz)
374 continue;
375
376 ret = pru_rproc_memcpy(ptr,
377 (void *)addr + phdr->p_offset, filesz);
378 if (ret) {
379 dev_dbg(dev, "PRU custom memory copy failed for da 0x%x memsz 0x%x\n",
380 da, memsz);
381 break;
382 }
383 }
384
385 priv->bootaddr = ehdr->e_entry;
386
387 return ret;
388}
389
390static const struct dm_rproc_ops pru_ops = {
391 .init = pru_init,
392 .start = pru_start,
393 .stop = pru_stop,
394 .load = pru_load,
395};
396
397static void pru_set_id(struct pru_privdata *priv, struct udevice *dev)
398{
399 u32 mask2 = 0x38000;
400
Tom Rinidec7ea02024-05-20 13:35:03 -0600401 if (device_is_compatible(dev, "ti,am654-rtu") ||
402 device_is_compatible(dev, "ti,am642-rtu"))
Keerthyf9982752021-06-22 12:04:28 +0530403 mask2 = 0x6000;
404
Tom Rinidec7ea02024-05-20 13:35:03 -0600405 if (device_is_compatible(dev, "ti,am654-tx-pru") ||
406 device_is_compatible(dev, "ti,am642-tx-pru"))
Keerthyf9982752021-06-22 12:04:28 +0530407 mask2 = 0xc000;
408
409 if ((priv->pru_iram & mask2) == mask2)
410 priv->id = 1;
411 else
412 priv->id = 0;
413}
414
415/**
416 * pru_probe() - Basic probe
417 * @dev: corresponding k3 remote processor device
418 *
419 * Return: 0 if all goes good, else appropriate error message.
420 */
421static int pru_probe(struct udevice *dev)
422{
423 struct pru_privdata *priv;
424 ofnode node;
425
426 node = dev_ofnode(dev);
427
428 priv = dev_get_priv(dev);
429 priv->prusspriv = dev_get_priv(dev->parent);
430
431 priv->pru_iram = devfdt_get_addr_size_index(dev, PRU_MEM_IRAM,
432 &priv->pru_iramsz);
433 priv->pru_ctrl = devfdt_get_addr_size_index(dev, PRU_MEM_CTRL,
434 &priv->pru_ctrlsz);
435 priv->pru_debug = devfdt_get_addr_size_index(dev, PRU_MEM_DEBUG,
436 &priv->pru_debugsz);
437
438 priv->iram_da = 0;
439 priv->pdram_da = 0;
440 priv->sdram_da = 0x2000;
441 priv->shrdram_da = 0x10000;
442
443 pru_set_id(priv, dev);
444
445 return 0;
446}
447
448static const struct udevice_id pru_ids[] = {
449 { .compatible = "ti,am654-pru"},
450 { .compatible = "ti,am654-rtu"},
451 { .compatible = "ti,am654-tx-pru" },
Tom Rinidec7ea02024-05-20 13:35:03 -0600452 { .compatible = "ti,am642-pru"},
453 { .compatible = "ti,am642-rtu"},
454 { .compatible = "ti,am642-tx-pru" },
Keerthyf9982752021-06-22 12:04:28 +0530455 {}
456};
457
458U_BOOT_DRIVER(pru) = {
459 .name = "pru",
460 .of_match = pru_ids,
461 .id = UCLASS_REMOTEPROC,
462 .ops = &pru_ops,
463 .probe = pru_probe,
464 .priv_auto = sizeof(struct pru_privdata),
465};