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