uboot/drivers/remoteproc/pru_rproc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * PRU-RTU remoteproc driver for various SoCs
   4 *
   5 * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
   6 *      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};
 462