1d4ce2de7SSuman Anna // SPDX-License-Identifier: GPL-2.0-only 2d4ce2de7SSuman Anna /* 3d4ce2de7SSuman Anna * PRU-ICSS remoteproc driver for various TI SoCs 4d4ce2de7SSuman Anna * 5919e8942SMD Danish Anwar * Copyright (C) 2014-2022 Texas Instruments Incorporated - https://www.ti.com/ 6d4ce2de7SSuman Anna * 7d4ce2de7SSuman Anna * Author(s): 8d4ce2de7SSuman Anna * Suman Anna <s-anna@ti.com> 9d4ce2de7SSuman Anna * Andrew F. Davis <afd@ti.com> 10d4ce2de7SSuman Anna * Grzegorz Jaszczyk <grzegorz.jaszczyk@linaro.org> for Texas Instruments 11919e8942SMD Danish Anwar * Puranjay Mohan <p-mohan@ti.com> 12919e8942SMD Danish Anwar * Md Danish Anwar <danishanwar@ti.com> 13d4ce2de7SSuman Anna */ 14d4ce2de7SSuman Anna 15d4ce2de7SSuman Anna #include <linux/bitops.h> 1620ad1de0SSuman Anna #include <linux/debugfs.h> 17c75c9fdaSGrzegorz Jaszczyk #include <linux/irqdomain.h> 18d4ce2de7SSuman Anna #include <linux/module.h> 19*3440d8daSRob Herring #include <linux/of.h> 20c75c9fdaSGrzegorz Jaszczyk #include <linux/of_irq.h> 21*3440d8daSRob Herring #include <linux/platform_device.h> 229b9ad70fSMD Danish Anwar #include <linux/remoteproc/pruss.h> 23d4ce2de7SSuman Anna #include <linux/pruss_driver.h> 24d4ce2de7SSuman Anna #include <linux/remoteproc.h> 25d4ce2de7SSuman Anna 26d4ce2de7SSuman Anna #include "remoteproc_internal.h" 27d4ce2de7SSuman Anna #include "remoteproc_elf_helpers.h" 28c75c9fdaSGrzegorz Jaszczyk #include "pru_rproc.h" 29d4ce2de7SSuman Anna 30d4ce2de7SSuman Anna /* PRU_ICSS_PRU_CTRL registers */ 31d4ce2de7SSuman Anna #define PRU_CTRL_CTRL 0x0000 32d4ce2de7SSuman Anna #define PRU_CTRL_STS 0x0004 3320ad1de0SSuman Anna #define PRU_CTRL_WAKEUP_EN 0x0008 3420ad1de0SSuman Anna #define PRU_CTRL_CYCLE 0x000C 3520ad1de0SSuman Anna #define PRU_CTRL_STALL 0x0010 3620ad1de0SSuman Anna #define PRU_CTRL_CTBIR0 0x0020 3720ad1de0SSuman Anna #define PRU_CTRL_CTBIR1 0x0024 3820ad1de0SSuman Anna #define PRU_CTRL_CTPPR0 0x0028 3920ad1de0SSuman Anna #define PRU_CTRL_CTPPR1 0x002C 40d4ce2de7SSuman Anna 41d4ce2de7SSuman Anna /* CTRL register bit-fields */ 42d4ce2de7SSuman Anna #define CTRL_CTRL_SOFT_RST_N BIT(0) 43d4ce2de7SSuman Anna #define CTRL_CTRL_EN BIT(1) 44d4ce2de7SSuman Anna #define CTRL_CTRL_SLEEPING BIT(2) 45d4ce2de7SSuman Anna #define CTRL_CTRL_CTR_EN BIT(3) 46d4ce2de7SSuman Anna #define CTRL_CTRL_SINGLE_STEP BIT(8) 47d4ce2de7SSuman Anna #define CTRL_CTRL_RUNSTATE BIT(15) 48d4ce2de7SSuman Anna 4920ad1de0SSuman Anna /* PRU_ICSS_PRU_DEBUG registers */ 5020ad1de0SSuman Anna #define PRU_DEBUG_GPREG(x) (0x0000 + (x) * 4) 5120ad1de0SSuman Anna #define PRU_DEBUG_CT_REG(x) (0x0080 + (x) * 4) 5220ad1de0SSuman Anna 531d39f4d1SSuman Anna /* PRU/RTU/Tx_PRU Core IRAM address masks */ 54d4ce2de7SSuman Anna #define PRU_IRAM_ADDR_MASK 0x3ffff 55d4ce2de7SSuman Anna #define PRU0_IRAM_ADDR_MASK 0x34000 56d4ce2de7SSuman Anna #define PRU1_IRAM_ADDR_MASK 0x38000 571d39f4d1SSuman Anna #define RTU0_IRAM_ADDR_MASK 0x4000 581d39f4d1SSuman Anna #define RTU1_IRAM_ADDR_MASK 0x6000 591d39f4d1SSuman Anna #define TX_PRU0_IRAM_ADDR_MASK 0xa000 601d39f4d1SSuman Anna #define TX_PRU1_IRAM_ADDR_MASK 0xc000 61d4ce2de7SSuman Anna 62d4ce2de7SSuman Anna /* PRU device addresses for various type of PRU RAMs */ 63d4ce2de7SSuman Anna #define PRU_IRAM_DA 0 /* Instruction RAM */ 64d4ce2de7SSuman Anna #define PRU_PDRAM_DA 0 /* Primary Data RAM */ 65d4ce2de7SSuman Anna #define PRU_SDRAM_DA 0x2000 /* Secondary Data RAM */ 66d4ce2de7SSuman Anna #define PRU_SHRDRAM_DA 0x10000 /* Shared Data RAM */ 67d4ce2de7SSuman Anna 68c75c9fdaSGrzegorz Jaszczyk #define MAX_PRU_SYS_EVENTS 160 69c75c9fdaSGrzegorz Jaszczyk 70d4ce2de7SSuman Anna /** 71d4ce2de7SSuman Anna * enum pru_iomem - PRU core memory/register range identifiers 72d4ce2de7SSuman Anna * 73d4ce2de7SSuman Anna * @PRU_IOMEM_IRAM: PRU Instruction RAM range 74d4ce2de7SSuman Anna * @PRU_IOMEM_CTRL: PRU Control register range 75d4ce2de7SSuman Anna * @PRU_IOMEM_DEBUG: PRU Debug register range 76d4ce2de7SSuman Anna * @PRU_IOMEM_MAX: just keep this one at the end 77d4ce2de7SSuman Anna */ 78d4ce2de7SSuman Anna enum pru_iomem { 79d4ce2de7SSuman Anna PRU_IOMEM_IRAM = 0, 80d4ce2de7SSuman Anna PRU_IOMEM_CTRL, 81d4ce2de7SSuman Anna PRU_IOMEM_DEBUG, 82d4ce2de7SSuman Anna PRU_IOMEM_MAX, 83d4ce2de7SSuman Anna }; 84d4ce2de7SSuman Anna 85d4ce2de7SSuman Anna /** 861d39f4d1SSuman Anna * struct pru_private_data - device data for a PRU core 871d39f4d1SSuman Anna * @type: type of the PRU core (PRU, RTU, Tx_PRU) 881d39f4d1SSuman Anna * @is_k3: flag used to identify the need for special load handling 891d39f4d1SSuman Anna */ 901d39f4d1SSuman Anna struct pru_private_data { 911d39f4d1SSuman Anna enum pru_type type; 921d39f4d1SSuman Anna unsigned int is_k3 : 1; 931d39f4d1SSuman Anna }; 941d39f4d1SSuman Anna 951d39f4d1SSuman Anna /** 96d4ce2de7SSuman Anna * struct pru_rproc - PRU remoteproc structure 97d4ce2de7SSuman Anna * @id: id of the PRU core within the PRUSS 98d4ce2de7SSuman Anna * @dev: PRU core device pointer 99d4ce2de7SSuman Anna * @pruss: back-reference to parent PRUSS structure 100d4ce2de7SSuman Anna * @rproc: remoteproc pointer for this PRU core 1011d39f4d1SSuman Anna * @data: PRU core specific data 102d4ce2de7SSuman Anna * @mem_regions: data for each of the PRU memory regions 103919e8942SMD Danish Anwar * @client_np: client device node 104919e8942SMD Danish Anwar * @lock: mutex to protect client usage 105d4ce2de7SSuman Anna * @fw_name: name of firmware image used during loading 106c75c9fdaSGrzegorz Jaszczyk * @mapped_irq: virtual interrupt numbers of created fw specific mapping 107c75c9fdaSGrzegorz Jaszczyk * @pru_interrupt_map: pointer to interrupt mapping description (firmware) 108c75c9fdaSGrzegorz Jaszczyk * @pru_interrupt_map_sz: pru_interrupt_map size 10910285340SRoger Quadros * @rmw_lock: lock for read, modify, write operations on registers 11020ad1de0SSuman Anna * @dbg_single_step: debug state variable to set PRU into single step mode 11120ad1de0SSuman Anna * @dbg_continuous: debug state variable to restore PRU execution mode 112c75c9fdaSGrzegorz Jaszczyk * @evt_count: number of mapped events 113d4ce2de7SSuman Anna */ 114d4ce2de7SSuman Anna struct pru_rproc { 115d4ce2de7SSuman Anna int id; 116d4ce2de7SSuman Anna struct device *dev; 117d4ce2de7SSuman Anna struct pruss *pruss; 118d4ce2de7SSuman Anna struct rproc *rproc; 1191d39f4d1SSuman Anna const struct pru_private_data *data; 120d4ce2de7SSuman Anna struct pruss_mem_region mem_regions[PRU_IOMEM_MAX]; 121919e8942SMD Danish Anwar struct device_node *client_np; 122919e8942SMD Danish Anwar struct mutex lock; 123d4ce2de7SSuman Anna const char *fw_name; 124c75c9fdaSGrzegorz Jaszczyk unsigned int *mapped_irq; 125c75c9fdaSGrzegorz Jaszczyk struct pru_irq_rsc *pru_interrupt_map; 126c75c9fdaSGrzegorz Jaszczyk size_t pru_interrupt_map_sz; 12710285340SRoger Quadros spinlock_t rmw_lock; 12820ad1de0SSuman Anna u32 dbg_single_step; 12920ad1de0SSuman Anna u32 dbg_continuous; 130c75c9fdaSGrzegorz Jaszczyk u8 evt_count; 131d4ce2de7SSuman Anna }; 132d4ce2de7SSuman Anna 133d4ce2de7SSuman Anna static inline u32 pru_control_read_reg(struct pru_rproc *pru, unsigned int reg) 134d4ce2de7SSuman Anna { 135d4ce2de7SSuman Anna return readl_relaxed(pru->mem_regions[PRU_IOMEM_CTRL].va + reg); 136d4ce2de7SSuman Anna } 137d4ce2de7SSuman Anna 138d4ce2de7SSuman Anna static inline 139d4ce2de7SSuman Anna void pru_control_write_reg(struct pru_rproc *pru, unsigned int reg, u32 val) 140d4ce2de7SSuman Anna { 141d4ce2de7SSuman Anna writel_relaxed(val, pru->mem_regions[PRU_IOMEM_CTRL].va + reg); 142d4ce2de7SSuman Anna } 143d4ce2de7SSuman Anna 14410285340SRoger Quadros static inline 14510285340SRoger Quadros void pru_control_set_reg(struct pru_rproc *pru, unsigned int reg, 14610285340SRoger Quadros u32 mask, u32 set) 14710285340SRoger Quadros { 14810285340SRoger Quadros u32 val; 14910285340SRoger Quadros unsigned long flags; 15010285340SRoger Quadros 15110285340SRoger Quadros spin_lock_irqsave(&pru->rmw_lock, flags); 15210285340SRoger Quadros 15310285340SRoger Quadros val = pru_control_read_reg(pru, reg); 15410285340SRoger Quadros val &= ~mask; 15510285340SRoger Quadros val |= (set & mask); 15610285340SRoger Quadros pru_control_write_reg(pru, reg, val); 15710285340SRoger Quadros 15810285340SRoger Quadros spin_unlock_irqrestore(&pru->rmw_lock, flags); 15910285340SRoger Quadros } 16010285340SRoger Quadros 161133f30d3STero Kristo /** 162133f30d3STero Kristo * pru_rproc_set_firmware() - set firmware for a PRU core 163133f30d3STero Kristo * @rproc: the rproc instance of the PRU 164133f30d3STero Kristo * @fw_name: the new firmware name, or NULL if default is desired 165133f30d3STero Kristo * 166133f30d3STero Kristo * Return: 0 on success, or errno in error case. 167133f30d3STero Kristo */ 168133f30d3STero Kristo static int pru_rproc_set_firmware(struct rproc *rproc, const char *fw_name) 169133f30d3STero Kristo { 170133f30d3STero Kristo struct pru_rproc *pru = rproc->priv; 171133f30d3STero Kristo 172133f30d3STero Kristo if (!fw_name) 173133f30d3STero Kristo fw_name = pru->fw_name; 174133f30d3STero Kristo 175133f30d3STero Kristo return rproc_set_firmware(rproc, fw_name); 176133f30d3STero Kristo } 177133f30d3STero Kristo 178919e8942SMD Danish Anwar static struct rproc *__pru_rproc_get(struct device_node *np, int index) 179919e8942SMD Danish Anwar { 180919e8942SMD Danish Anwar struct rproc *rproc; 181919e8942SMD Danish Anwar phandle rproc_phandle; 182919e8942SMD Danish Anwar int ret; 183919e8942SMD Danish Anwar 184919e8942SMD Danish Anwar ret = of_property_read_u32_index(np, "ti,prus", index, &rproc_phandle); 185919e8942SMD Danish Anwar if (ret) 186919e8942SMD Danish Anwar return ERR_PTR(ret); 187919e8942SMD Danish Anwar 188919e8942SMD Danish Anwar rproc = rproc_get_by_phandle(rproc_phandle); 189919e8942SMD Danish Anwar if (!rproc) { 190919e8942SMD Danish Anwar ret = -EPROBE_DEFER; 191919e8942SMD Danish Anwar return ERR_PTR(ret); 192919e8942SMD Danish Anwar } 193919e8942SMD Danish Anwar 194919e8942SMD Danish Anwar /* make sure it is PRU rproc */ 195919e8942SMD Danish Anwar if (!is_pru_rproc(rproc->dev.parent)) { 196919e8942SMD Danish Anwar rproc_put(rproc); 197919e8942SMD Danish Anwar return ERR_PTR(-ENODEV); 198919e8942SMD Danish Anwar } 199919e8942SMD Danish Anwar 200919e8942SMD Danish Anwar return rproc; 201919e8942SMD Danish Anwar } 202919e8942SMD Danish Anwar 203919e8942SMD Danish Anwar /** 204919e8942SMD Danish Anwar * pru_rproc_get() - get the PRU rproc instance from a device node 205919e8942SMD Danish Anwar * @np: the user/client device node 206919e8942SMD Danish Anwar * @index: index to use for the ti,prus property 207919e8942SMD Danish Anwar * @pru_id: optional pointer to return the PRU remoteproc processor id 208919e8942SMD Danish Anwar * 209919e8942SMD Danish Anwar * This function looks through a client device node's "ti,prus" property at 210919e8942SMD Danish Anwar * index @index and returns the rproc handle for a valid PRU remote processor if 211919e8942SMD Danish Anwar * found. The function allows only one user to own the PRU rproc resource at a 212919e8942SMD Danish Anwar * time. Caller must call pru_rproc_put() when done with using the rproc, not 213919e8942SMD Danish Anwar * required if the function returns a failure. 214919e8942SMD Danish Anwar * 215919e8942SMD Danish Anwar * When optional @pru_id pointer is passed the PRU remoteproc processor id is 216919e8942SMD Danish Anwar * returned. 217919e8942SMD Danish Anwar * 218919e8942SMD Danish Anwar * Return: rproc handle on success, and an ERR_PTR on failure using one 219919e8942SMD Danish Anwar * of the following error values 220919e8942SMD Danish Anwar * -ENODEV if device is not found 221919e8942SMD Danish Anwar * -EBUSY if PRU is already acquired by anyone 222919e8942SMD Danish Anwar * -EPROBE_DEFER is PRU device is not probed yet 223919e8942SMD Danish Anwar */ 224919e8942SMD Danish Anwar struct rproc *pru_rproc_get(struct device_node *np, int index, 225919e8942SMD Danish Anwar enum pruss_pru_id *pru_id) 226919e8942SMD Danish Anwar { 227919e8942SMD Danish Anwar struct rproc *rproc; 228919e8942SMD Danish Anwar struct pru_rproc *pru; 229919e8942SMD Danish Anwar struct device *dev; 230133f30d3STero Kristo const char *fw_name; 231919e8942SMD Danish Anwar int ret; 232919e8942SMD Danish Anwar 233919e8942SMD Danish Anwar rproc = __pru_rproc_get(np, index); 234919e8942SMD Danish Anwar if (IS_ERR(rproc)) 235919e8942SMD Danish Anwar return rproc; 236919e8942SMD Danish Anwar 237919e8942SMD Danish Anwar pru = rproc->priv; 238919e8942SMD Danish Anwar dev = &rproc->dev; 239919e8942SMD Danish Anwar 240919e8942SMD Danish Anwar mutex_lock(&pru->lock); 241919e8942SMD Danish Anwar 242919e8942SMD Danish Anwar if (pru->client_np) { 243919e8942SMD Danish Anwar mutex_unlock(&pru->lock); 244919e8942SMD Danish Anwar ret = -EBUSY; 245919e8942SMD Danish Anwar goto err_no_rproc_handle; 246919e8942SMD Danish Anwar } 247919e8942SMD Danish Anwar 248919e8942SMD Danish Anwar pru->client_np = np; 2492da812ffSSuman Anna rproc->sysfs_read_only = true; 250919e8942SMD Danish Anwar 251919e8942SMD Danish Anwar mutex_unlock(&pru->lock); 252919e8942SMD Danish Anwar 253919e8942SMD Danish Anwar if (pru_id) 254919e8942SMD Danish Anwar *pru_id = pru->id; 255919e8942SMD Danish Anwar 256133f30d3STero Kristo ret = of_property_read_string_index(np, "firmware-name", index, 257133f30d3STero Kristo &fw_name); 258133f30d3STero Kristo if (!ret) { 259133f30d3STero Kristo ret = pru_rproc_set_firmware(rproc, fw_name); 260133f30d3STero Kristo if (ret) { 261133f30d3STero Kristo dev_err(dev, "failed to set firmware: %d\n", ret); 262133f30d3STero Kristo goto err; 263133f30d3STero Kristo } 264133f30d3STero Kristo } 265133f30d3STero Kristo 266919e8942SMD Danish Anwar return rproc; 267919e8942SMD Danish Anwar 268919e8942SMD Danish Anwar err_no_rproc_handle: 269919e8942SMD Danish Anwar rproc_put(rproc); 270919e8942SMD Danish Anwar return ERR_PTR(ret); 271133f30d3STero Kristo 272133f30d3STero Kristo err: 273133f30d3STero Kristo pru_rproc_put(rproc); 274133f30d3STero Kristo return ERR_PTR(ret); 275919e8942SMD Danish Anwar } 276919e8942SMD Danish Anwar EXPORT_SYMBOL_GPL(pru_rproc_get); 277919e8942SMD Danish Anwar 278919e8942SMD Danish Anwar /** 279919e8942SMD Danish Anwar * pru_rproc_put() - release the PRU rproc resource 280919e8942SMD Danish Anwar * @rproc: the rproc resource to release 281919e8942SMD Danish Anwar * 282919e8942SMD Danish Anwar * Releases the PRU rproc resource and makes it available to other 283919e8942SMD Danish Anwar * users. 284919e8942SMD Danish Anwar */ 285919e8942SMD Danish Anwar void pru_rproc_put(struct rproc *rproc) 286919e8942SMD Danish Anwar { 287919e8942SMD Danish Anwar struct pru_rproc *pru; 288919e8942SMD Danish Anwar 289919e8942SMD Danish Anwar if (IS_ERR_OR_NULL(rproc) || !is_pru_rproc(rproc->dev.parent)) 290919e8942SMD Danish Anwar return; 291919e8942SMD Danish Anwar 292919e8942SMD Danish Anwar pru = rproc->priv; 293919e8942SMD Danish Anwar 294133f30d3STero Kristo pru_rproc_set_firmware(rproc, NULL); 295133f30d3STero Kristo 296919e8942SMD Danish Anwar mutex_lock(&pru->lock); 297919e8942SMD Danish Anwar 298919e8942SMD Danish Anwar if (!pru->client_np) { 299919e8942SMD Danish Anwar mutex_unlock(&pru->lock); 300919e8942SMD Danish Anwar return; 301919e8942SMD Danish Anwar } 302919e8942SMD Danish Anwar 303919e8942SMD Danish Anwar pru->client_np = NULL; 3042da812ffSSuman Anna rproc->sysfs_read_only = false; 305919e8942SMD Danish Anwar mutex_unlock(&pru->lock); 306919e8942SMD Danish Anwar 307919e8942SMD Danish Anwar rproc_put(rproc); 308919e8942SMD Danish Anwar } 309919e8942SMD Danish Anwar EXPORT_SYMBOL_GPL(pru_rproc_put); 310919e8942SMD Danish Anwar 31110285340SRoger Quadros /** 31210285340SRoger Quadros * pru_rproc_set_ctable() - set the constant table index for the PRU 31310285340SRoger Quadros * @rproc: the rproc instance of the PRU 31410285340SRoger Quadros * @c: constant table index to set 31510285340SRoger Quadros * @addr: physical address to set it to 31610285340SRoger Quadros * 31710285340SRoger Quadros * Return: 0 on success, or errno in error case. 31810285340SRoger Quadros */ 31910285340SRoger Quadros int pru_rproc_set_ctable(struct rproc *rproc, enum pru_ctable_idx c, u32 addr) 32010285340SRoger Quadros { 32110285340SRoger Quadros struct pru_rproc *pru = rproc->priv; 32210285340SRoger Quadros unsigned int reg; 32310285340SRoger Quadros u32 mask, set; 32410285340SRoger Quadros u16 idx; 32510285340SRoger Quadros u16 idx_mask; 32610285340SRoger Quadros 32710285340SRoger Quadros if (IS_ERR_OR_NULL(rproc)) 32810285340SRoger Quadros return -EINVAL; 32910285340SRoger Quadros 33010285340SRoger Quadros if (!rproc->dev.parent || !is_pru_rproc(rproc->dev.parent)) 33110285340SRoger Quadros return -ENODEV; 33210285340SRoger Quadros 33310285340SRoger Quadros /* pointer is 16 bit and index is 8-bit so mask out the rest */ 33410285340SRoger Quadros idx_mask = (c >= PRU_C28) ? 0xFFFF : 0xFF; 33510285340SRoger Quadros 33610285340SRoger Quadros /* ctable uses bit 8 and upwards only */ 33710285340SRoger Quadros idx = (addr >> 8) & idx_mask; 33810285340SRoger Quadros 33910285340SRoger Quadros /* configurable ctable (i.e. C24) starts at PRU_CTRL_CTBIR0 */ 34010285340SRoger Quadros reg = PRU_CTRL_CTBIR0 + 4 * (c >> 1); 34110285340SRoger Quadros mask = idx_mask << (16 * (c & 1)); 34210285340SRoger Quadros set = idx << (16 * (c & 1)); 34310285340SRoger Quadros 34410285340SRoger Quadros pru_control_set_reg(pru, reg, mask, set); 34510285340SRoger Quadros 34610285340SRoger Quadros return 0; 34710285340SRoger Quadros } 34810285340SRoger Quadros EXPORT_SYMBOL_GPL(pru_rproc_set_ctable); 34910285340SRoger Quadros 35020ad1de0SSuman Anna static inline u32 pru_debug_read_reg(struct pru_rproc *pru, unsigned int reg) 35120ad1de0SSuman Anna { 35220ad1de0SSuman Anna return readl_relaxed(pru->mem_regions[PRU_IOMEM_DEBUG].va + reg); 35320ad1de0SSuman Anna } 35420ad1de0SSuman Anna 35520ad1de0SSuman Anna static int regs_show(struct seq_file *s, void *data) 35620ad1de0SSuman Anna { 35720ad1de0SSuman Anna struct rproc *rproc = s->private; 35820ad1de0SSuman Anna struct pru_rproc *pru = rproc->priv; 35920ad1de0SSuman Anna int i, nregs = 32; 36020ad1de0SSuman Anna u32 pru_sts; 36120ad1de0SSuman Anna int pru_is_running; 36220ad1de0SSuman Anna 36320ad1de0SSuman Anna seq_puts(s, "============== Control Registers ==============\n"); 36420ad1de0SSuman Anna seq_printf(s, "CTRL := 0x%08x\n", 36520ad1de0SSuman Anna pru_control_read_reg(pru, PRU_CTRL_CTRL)); 36620ad1de0SSuman Anna pru_sts = pru_control_read_reg(pru, PRU_CTRL_STS); 36720ad1de0SSuman Anna seq_printf(s, "STS (PC) := 0x%08x (0x%08x)\n", pru_sts, pru_sts << 2); 36820ad1de0SSuman Anna seq_printf(s, "WAKEUP_EN := 0x%08x\n", 36920ad1de0SSuman Anna pru_control_read_reg(pru, PRU_CTRL_WAKEUP_EN)); 37020ad1de0SSuman Anna seq_printf(s, "CYCLE := 0x%08x\n", 37120ad1de0SSuman Anna pru_control_read_reg(pru, PRU_CTRL_CYCLE)); 37220ad1de0SSuman Anna seq_printf(s, "STALL := 0x%08x\n", 37320ad1de0SSuman Anna pru_control_read_reg(pru, PRU_CTRL_STALL)); 37420ad1de0SSuman Anna seq_printf(s, "CTBIR0 := 0x%08x\n", 37520ad1de0SSuman Anna pru_control_read_reg(pru, PRU_CTRL_CTBIR0)); 37620ad1de0SSuman Anna seq_printf(s, "CTBIR1 := 0x%08x\n", 37720ad1de0SSuman Anna pru_control_read_reg(pru, PRU_CTRL_CTBIR1)); 37820ad1de0SSuman Anna seq_printf(s, "CTPPR0 := 0x%08x\n", 37920ad1de0SSuman Anna pru_control_read_reg(pru, PRU_CTRL_CTPPR0)); 38020ad1de0SSuman Anna seq_printf(s, "CTPPR1 := 0x%08x\n", 38120ad1de0SSuman Anna pru_control_read_reg(pru, PRU_CTRL_CTPPR1)); 38220ad1de0SSuman Anna 38320ad1de0SSuman Anna seq_puts(s, "=============== Debug Registers ===============\n"); 38420ad1de0SSuman Anna pru_is_running = pru_control_read_reg(pru, PRU_CTRL_CTRL) & 38520ad1de0SSuman Anna CTRL_CTRL_RUNSTATE; 38620ad1de0SSuman Anna if (pru_is_running) { 38720ad1de0SSuman Anna seq_puts(s, "PRU is executing, cannot print/access debug registers.\n"); 38820ad1de0SSuman Anna return 0; 38920ad1de0SSuman Anna } 39020ad1de0SSuman Anna 39120ad1de0SSuman Anna for (i = 0; i < nregs; i++) { 39220ad1de0SSuman Anna seq_printf(s, "GPREG%-2d := 0x%08x\tCT_REG%-2d := 0x%08x\n", 39320ad1de0SSuman Anna i, pru_debug_read_reg(pru, PRU_DEBUG_GPREG(i)), 39420ad1de0SSuman Anna i, pru_debug_read_reg(pru, PRU_DEBUG_CT_REG(i))); 39520ad1de0SSuman Anna } 39620ad1de0SSuman Anna 39720ad1de0SSuman Anna return 0; 39820ad1de0SSuman Anna } 39920ad1de0SSuman Anna DEFINE_SHOW_ATTRIBUTE(regs); 40020ad1de0SSuman Anna 40120ad1de0SSuman Anna /* 40220ad1de0SSuman Anna * Control PRU single-step mode 40320ad1de0SSuman Anna * 40420ad1de0SSuman Anna * This is a debug helper function used for controlling the single-step 40520ad1de0SSuman Anna * mode of the PRU. The PRU Debug registers are not accessible when the 40620ad1de0SSuman Anna * PRU is in RUNNING state. 40720ad1de0SSuman Anna * 40820ad1de0SSuman Anna * Writing a non-zero value sets the PRU into single-step mode irrespective 40920ad1de0SSuman Anna * of its previous state. The PRU mode is saved only on the first set into 41020ad1de0SSuman Anna * a single-step mode. Writing a zero value will restore the PRU into its 41120ad1de0SSuman Anna * original mode. 41220ad1de0SSuman Anna */ 41320ad1de0SSuman Anna static int pru_rproc_debug_ss_set(void *data, u64 val) 41420ad1de0SSuman Anna { 41520ad1de0SSuman Anna struct rproc *rproc = data; 41620ad1de0SSuman Anna struct pru_rproc *pru = rproc->priv; 41720ad1de0SSuman Anna u32 reg_val; 41820ad1de0SSuman Anna 41920ad1de0SSuman Anna val = val ? 1 : 0; 42020ad1de0SSuman Anna if (!val && !pru->dbg_single_step) 42120ad1de0SSuman Anna return 0; 42220ad1de0SSuman Anna 42320ad1de0SSuman Anna reg_val = pru_control_read_reg(pru, PRU_CTRL_CTRL); 42420ad1de0SSuman Anna 42520ad1de0SSuman Anna if (val && !pru->dbg_single_step) 42620ad1de0SSuman Anna pru->dbg_continuous = reg_val; 42720ad1de0SSuman Anna 42820ad1de0SSuman Anna if (val) 42920ad1de0SSuman Anna reg_val |= CTRL_CTRL_SINGLE_STEP | CTRL_CTRL_EN; 43020ad1de0SSuman Anna else 43120ad1de0SSuman Anna reg_val = pru->dbg_continuous; 43220ad1de0SSuman Anna 43320ad1de0SSuman Anna pru->dbg_single_step = val; 43420ad1de0SSuman Anna pru_control_write_reg(pru, PRU_CTRL_CTRL, reg_val); 43520ad1de0SSuman Anna 43620ad1de0SSuman Anna return 0; 43720ad1de0SSuman Anna } 43820ad1de0SSuman Anna 43920ad1de0SSuman Anna static int pru_rproc_debug_ss_get(void *data, u64 *val) 44020ad1de0SSuman Anna { 44120ad1de0SSuman Anna struct rproc *rproc = data; 44220ad1de0SSuman Anna struct pru_rproc *pru = rproc->priv; 44320ad1de0SSuman Anna 44420ad1de0SSuman Anna *val = pru->dbg_single_step; 44520ad1de0SSuman Anna 44620ad1de0SSuman Anna return 0; 44720ad1de0SSuman Anna } 448780a980eSYang Li DEFINE_DEBUGFS_ATTRIBUTE(pru_rproc_debug_ss_fops, pru_rproc_debug_ss_get, 44920ad1de0SSuman Anna pru_rproc_debug_ss_set, "%llu\n"); 45020ad1de0SSuman Anna 45120ad1de0SSuman Anna /* 45220ad1de0SSuman Anna * Create PRU-specific debugfs entries 45320ad1de0SSuman Anna * 45420ad1de0SSuman Anna * The entries are created only if the parent remoteproc debugfs directory 45520ad1de0SSuman Anna * exists, and will be cleaned up by the remoteproc core. 45620ad1de0SSuman Anna */ 45720ad1de0SSuman Anna static void pru_rproc_create_debug_entries(struct rproc *rproc) 45820ad1de0SSuman Anna { 45920ad1de0SSuman Anna if (!rproc->dbg_dir) 46020ad1de0SSuman Anna return; 46120ad1de0SSuman Anna 46220ad1de0SSuman Anna debugfs_create_file("regs", 0400, rproc->dbg_dir, 46320ad1de0SSuman Anna rproc, ®s_fops); 46420ad1de0SSuman Anna debugfs_create_file("single_step", 0600, rproc->dbg_dir, 46520ad1de0SSuman Anna rproc, &pru_rproc_debug_ss_fops); 46620ad1de0SSuman Anna } 46720ad1de0SSuman Anna 468c75c9fdaSGrzegorz Jaszczyk static void pru_dispose_irq_mapping(struct pru_rproc *pru) 469c75c9fdaSGrzegorz Jaszczyk { 470880a66e0SSuman Anna if (!pru->mapped_irq) 471880a66e0SSuman Anna return; 472880a66e0SSuman Anna 473880a66e0SSuman Anna while (pru->evt_count) { 474880a66e0SSuman Anna pru->evt_count--; 475c75c9fdaSGrzegorz Jaszczyk if (pru->mapped_irq[pru->evt_count] > 0) 476c75c9fdaSGrzegorz Jaszczyk irq_dispose_mapping(pru->mapped_irq[pru->evt_count]); 477c75c9fdaSGrzegorz Jaszczyk } 478c75c9fdaSGrzegorz Jaszczyk 479c75c9fdaSGrzegorz Jaszczyk kfree(pru->mapped_irq); 480880a66e0SSuman Anna pru->mapped_irq = NULL; 481c75c9fdaSGrzegorz Jaszczyk } 482c75c9fdaSGrzegorz Jaszczyk 483c75c9fdaSGrzegorz Jaszczyk /* 484c75c9fdaSGrzegorz Jaszczyk * Parse the custom PRU interrupt map resource and configure the INTC 485c75c9fdaSGrzegorz Jaszczyk * appropriately. 486c75c9fdaSGrzegorz Jaszczyk */ 487c75c9fdaSGrzegorz Jaszczyk static int pru_handle_intrmap(struct rproc *rproc) 488c75c9fdaSGrzegorz Jaszczyk { 489c75c9fdaSGrzegorz Jaszczyk struct device *dev = rproc->dev.parent; 490c75c9fdaSGrzegorz Jaszczyk struct pru_rproc *pru = rproc->priv; 491c75c9fdaSGrzegorz Jaszczyk struct pru_irq_rsc *rsc = pru->pru_interrupt_map; 492c75c9fdaSGrzegorz Jaszczyk struct irq_fwspec fwspec; 4936d1f2803SSuman Anna struct device_node *parent, *irq_parent; 494c75c9fdaSGrzegorz Jaszczyk int i, ret = 0; 495c75c9fdaSGrzegorz Jaszczyk 496c75c9fdaSGrzegorz Jaszczyk /* not having pru_interrupt_map is not an error */ 497c75c9fdaSGrzegorz Jaszczyk if (!rsc) 498c75c9fdaSGrzegorz Jaszczyk return 0; 499c75c9fdaSGrzegorz Jaszczyk 500c75c9fdaSGrzegorz Jaszczyk /* currently supporting only type 0 */ 501c75c9fdaSGrzegorz Jaszczyk if (rsc->type != 0) { 502c75c9fdaSGrzegorz Jaszczyk dev_err(dev, "unsupported rsc type: %d\n", rsc->type); 503c75c9fdaSGrzegorz Jaszczyk return -EINVAL; 504c75c9fdaSGrzegorz Jaszczyk } 505c75c9fdaSGrzegorz Jaszczyk 506c75c9fdaSGrzegorz Jaszczyk if (rsc->num_evts > MAX_PRU_SYS_EVENTS) 507c75c9fdaSGrzegorz Jaszczyk return -EINVAL; 508c75c9fdaSGrzegorz Jaszczyk 509c75c9fdaSGrzegorz Jaszczyk if (sizeof(*rsc) + rsc->num_evts * sizeof(struct pruss_int_map) != 510c75c9fdaSGrzegorz Jaszczyk pru->pru_interrupt_map_sz) 511c75c9fdaSGrzegorz Jaszczyk return -EINVAL; 512c75c9fdaSGrzegorz Jaszczyk 513c75c9fdaSGrzegorz Jaszczyk pru->evt_count = rsc->num_evts; 514c75c9fdaSGrzegorz Jaszczyk pru->mapped_irq = kcalloc(pru->evt_count, sizeof(unsigned int), 515c75c9fdaSGrzegorz Jaszczyk GFP_KERNEL); 516880a66e0SSuman Anna if (!pru->mapped_irq) { 517880a66e0SSuman Anna pru->evt_count = 0; 518c75c9fdaSGrzegorz Jaszczyk return -ENOMEM; 519880a66e0SSuman Anna } 520c75c9fdaSGrzegorz Jaszczyk 521c75c9fdaSGrzegorz Jaszczyk /* 522c75c9fdaSGrzegorz Jaszczyk * parse and fill in system event to interrupt channel and 5236d1f2803SSuman Anna * channel-to-host mapping. The interrupt controller to be used 5246d1f2803SSuman Anna * for these mappings for a given PRU remoteproc is always its 5256d1f2803SSuman Anna * corresponding sibling PRUSS INTC node. 526c75c9fdaSGrzegorz Jaszczyk */ 5276d1f2803SSuman Anna parent = of_get_parent(dev_of_node(pru->dev)); 528880a66e0SSuman Anna if (!parent) { 529880a66e0SSuman Anna kfree(pru->mapped_irq); 530880a66e0SSuman Anna pru->mapped_irq = NULL; 531880a66e0SSuman Anna pru->evt_count = 0; 5326d1f2803SSuman Anna return -ENODEV; 533880a66e0SSuman Anna } 5346d1f2803SSuman Anna 5356d1f2803SSuman Anna irq_parent = of_get_child_by_name(parent, "interrupt-controller"); 5366d1f2803SSuman Anna of_node_put(parent); 537c75c9fdaSGrzegorz Jaszczyk if (!irq_parent) { 538c75c9fdaSGrzegorz Jaszczyk kfree(pru->mapped_irq); 539880a66e0SSuman Anna pru->mapped_irq = NULL; 540880a66e0SSuman Anna pru->evt_count = 0; 541c75c9fdaSGrzegorz Jaszczyk return -ENODEV; 542c75c9fdaSGrzegorz Jaszczyk } 543c75c9fdaSGrzegorz Jaszczyk 544c75c9fdaSGrzegorz Jaszczyk fwspec.fwnode = of_node_to_fwnode(irq_parent); 545c75c9fdaSGrzegorz Jaszczyk fwspec.param_count = 3; 546c75c9fdaSGrzegorz Jaszczyk for (i = 0; i < pru->evt_count; i++) { 547c75c9fdaSGrzegorz Jaszczyk fwspec.param[0] = rsc->pru_intc_map[i].event; 548c75c9fdaSGrzegorz Jaszczyk fwspec.param[1] = rsc->pru_intc_map[i].chnl; 549c75c9fdaSGrzegorz Jaszczyk fwspec.param[2] = rsc->pru_intc_map[i].host; 550c75c9fdaSGrzegorz Jaszczyk 551c75c9fdaSGrzegorz Jaszczyk dev_dbg(dev, "mapping%d: event %d, chnl %d, host %d\n", 552c75c9fdaSGrzegorz Jaszczyk i, fwspec.param[0], fwspec.param[1], fwspec.param[2]); 553c75c9fdaSGrzegorz Jaszczyk 554c75c9fdaSGrzegorz Jaszczyk pru->mapped_irq[i] = irq_create_fwspec_mapping(&fwspec); 555c75c9fdaSGrzegorz Jaszczyk if (!pru->mapped_irq[i]) { 5561fe72bcfSSuman Anna dev_err(dev, "failed to get virq for fw mapping %d: event %d chnl %d host %d\n", 5571fe72bcfSSuman Anna i, fwspec.param[0], fwspec.param[1], 5581fe72bcfSSuman Anna fwspec.param[2]); 5591fe72bcfSSuman Anna ret = -EINVAL; 560c75c9fdaSGrzegorz Jaszczyk goto map_fail; 561c75c9fdaSGrzegorz Jaszczyk } 562c75c9fdaSGrzegorz Jaszczyk } 5636d1f2803SSuman Anna of_node_put(irq_parent); 564c75c9fdaSGrzegorz Jaszczyk 565c75c9fdaSGrzegorz Jaszczyk return ret; 566c75c9fdaSGrzegorz Jaszczyk 567c75c9fdaSGrzegorz Jaszczyk map_fail: 568c75c9fdaSGrzegorz Jaszczyk pru_dispose_irq_mapping(pru); 5696d1f2803SSuman Anna of_node_put(irq_parent); 570c75c9fdaSGrzegorz Jaszczyk 571c75c9fdaSGrzegorz Jaszczyk return ret; 572c75c9fdaSGrzegorz Jaszczyk } 573c75c9fdaSGrzegorz Jaszczyk 574d4ce2de7SSuman Anna static int pru_rproc_start(struct rproc *rproc) 575d4ce2de7SSuman Anna { 576d4ce2de7SSuman Anna struct device *dev = &rproc->dev; 577d4ce2de7SSuman Anna struct pru_rproc *pru = rproc->priv; 5781d39f4d1SSuman Anna const char *names[PRU_TYPE_MAX] = { "PRU", "RTU", "Tx_PRU" }; 579d4ce2de7SSuman Anna u32 val; 580c75c9fdaSGrzegorz Jaszczyk int ret; 581d4ce2de7SSuman Anna 5821d39f4d1SSuman Anna dev_dbg(dev, "starting %s%d: entry-point = 0x%llx\n", 5831d39f4d1SSuman Anna names[pru->data->type], pru->id, (rproc->bootaddr >> 2)); 584d4ce2de7SSuman Anna 585c75c9fdaSGrzegorz Jaszczyk ret = pru_handle_intrmap(rproc); 586c75c9fdaSGrzegorz Jaszczyk /* 587c75c9fdaSGrzegorz Jaszczyk * reset references to pru interrupt map - they will stop being valid 588c75c9fdaSGrzegorz Jaszczyk * after rproc_start returns 589c75c9fdaSGrzegorz Jaszczyk */ 590c75c9fdaSGrzegorz Jaszczyk pru->pru_interrupt_map = NULL; 591c75c9fdaSGrzegorz Jaszczyk pru->pru_interrupt_map_sz = 0; 592c75c9fdaSGrzegorz Jaszczyk if (ret) 593c75c9fdaSGrzegorz Jaszczyk return ret; 594c75c9fdaSGrzegorz Jaszczyk 595d4ce2de7SSuman Anna val = CTRL_CTRL_EN | ((rproc->bootaddr >> 2) << 16); 596d4ce2de7SSuman Anna pru_control_write_reg(pru, PRU_CTRL_CTRL, val); 597d4ce2de7SSuman Anna 598d4ce2de7SSuman Anna return 0; 599d4ce2de7SSuman Anna } 600d4ce2de7SSuman Anna 601d4ce2de7SSuman Anna static int pru_rproc_stop(struct rproc *rproc) 602d4ce2de7SSuman Anna { 603d4ce2de7SSuman Anna struct device *dev = &rproc->dev; 604d4ce2de7SSuman Anna struct pru_rproc *pru = rproc->priv; 6051d39f4d1SSuman Anna const char *names[PRU_TYPE_MAX] = { "PRU", "RTU", "Tx_PRU" }; 606d4ce2de7SSuman Anna u32 val; 607d4ce2de7SSuman Anna 6081d39f4d1SSuman Anna dev_dbg(dev, "stopping %s%d\n", names[pru->data->type], pru->id); 609d4ce2de7SSuman Anna 610d4ce2de7SSuman Anna val = pru_control_read_reg(pru, PRU_CTRL_CTRL); 611d4ce2de7SSuman Anna val &= ~CTRL_CTRL_EN; 612d4ce2de7SSuman Anna pru_control_write_reg(pru, PRU_CTRL_CTRL, val); 613d4ce2de7SSuman Anna 614c75c9fdaSGrzegorz Jaszczyk /* dispose irq mapping - new firmware can provide new mapping */ 615c75c9fdaSGrzegorz Jaszczyk pru_dispose_irq_mapping(pru); 616c75c9fdaSGrzegorz Jaszczyk 617d4ce2de7SSuman Anna return 0; 618d4ce2de7SSuman Anna } 619d4ce2de7SSuman Anna 620d4ce2de7SSuman Anna /* 621d4ce2de7SSuman Anna * Convert PRU device address (data spaces only) to kernel virtual address. 622d4ce2de7SSuman Anna * 623d4ce2de7SSuman Anna * Each PRU has access to all data memories within the PRUSS, accessible at 624d4ce2de7SSuman Anna * different ranges. So, look through both its primary and secondary Data 625d4ce2de7SSuman Anna * RAMs as well as any shared Data RAM to convert a PRU device address to 626d4ce2de7SSuman Anna * kernel virtual address. Data RAM0 is primary Data RAM for PRU0 and Data 627d4ce2de7SSuman Anna * RAM1 is primary Data RAM for PRU1. 628d4ce2de7SSuman Anna */ 629d4ce2de7SSuman Anna static void *pru_d_da_to_va(struct pru_rproc *pru, u32 da, size_t len) 630d4ce2de7SSuman Anna { 631d4ce2de7SSuman Anna struct pruss_mem_region dram0, dram1, shrd_ram; 632d4ce2de7SSuman Anna struct pruss *pruss = pru->pruss; 633d4ce2de7SSuman Anna u32 offset; 634d4ce2de7SSuman Anna void *va = NULL; 635d4ce2de7SSuman Anna 636d4ce2de7SSuman Anna if (len == 0) 637d4ce2de7SSuman Anna return NULL; 638d4ce2de7SSuman Anna 639d4ce2de7SSuman Anna dram0 = pruss->mem_regions[PRUSS_MEM_DRAM0]; 640d4ce2de7SSuman Anna dram1 = pruss->mem_regions[PRUSS_MEM_DRAM1]; 641d4ce2de7SSuman Anna /* PRU1 has its local RAM addresses reversed */ 6429b9ad70fSMD Danish Anwar if (pru->id == PRUSS_PRU1) 643d4ce2de7SSuman Anna swap(dram0, dram1); 644d4ce2de7SSuman Anna shrd_ram = pruss->mem_regions[PRUSS_MEM_SHRD_RAM2]; 645d4ce2de7SSuman Anna 6463a87fc6fSSimon Horman if (da + len <= PRU_PDRAM_DA + dram0.size) { 647d4ce2de7SSuman Anna offset = da - PRU_PDRAM_DA; 648d4ce2de7SSuman Anna va = (__force void *)(dram0.va + offset); 649d4ce2de7SSuman Anna } else if (da >= PRU_SDRAM_DA && 650d4ce2de7SSuman Anna da + len <= PRU_SDRAM_DA + dram1.size) { 651d4ce2de7SSuman Anna offset = da - PRU_SDRAM_DA; 652d4ce2de7SSuman Anna va = (__force void *)(dram1.va + offset); 653d4ce2de7SSuman Anna } else if (da >= PRU_SHRDRAM_DA && 654d4ce2de7SSuman Anna da + len <= PRU_SHRDRAM_DA + shrd_ram.size) { 655d4ce2de7SSuman Anna offset = da - PRU_SHRDRAM_DA; 656d4ce2de7SSuman Anna va = (__force void *)(shrd_ram.va + offset); 657d4ce2de7SSuman Anna } 658d4ce2de7SSuman Anna 659d4ce2de7SSuman Anna return va; 660d4ce2de7SSuman Anna } 661d4ce2de7SSuman Anna 662d4ce2de7SSuman Anna /* 663d4ce2de7SSuman Anna * Convert PRU device address (instruction space) to kernel virtual address. 664d4ce2de7SSuman Anna * 665d4ce2de7SSuman Anna * A PRU does not have an unified address space. Each PRU has its very own 666d4ce2de7SSuman Anna * private Instruction RAM, and its device address is identical to that of 667d4ce2de7SSuman Anna * its primary Data RAM device address. 668d4ce2de7SSuman Anna */ 669d4ce2de7SSuman Anna static void *pru_i_da_to_va(struct pru_rproc *pru, u32 da, size_t len) 670d4ce2de7SSuman Anna { 671d4ce2de7SSuman Anna u32 offset; 672d4ce2de7SSuman Anna void *va = NULL; 673d4ce2de7SSuman Anna 674d4ce2de7SSuman Anna if (len == 0) 675d4ce2de7SSuman Anna return NULL; 676d4ce2de7SSuman Anna 677e6d9423dSDimitar Dimitrov /* 678e6d9423dSDimitar Dimitrov * GNU binutils do not support multiple address spaces. The GNU 679e6d9423dSDimitar Dimitrov * linker's default linker script places IRAM at an arbitrary high 680e6d9423dSDimitar Dimitrov * offset, in order to differentiate it from DRAM. Hence we need to 681e6d9423dSDimitar Dimitrov * strip the artificial offset in the IRAM addresses coming from the 682e6d9423dSDimitar Dimitrov * ELF file. 683e6d9423dSDimitar Dimitrov * 684e6d9423dSDimitar Dimitrov * The TI proprietary linker would never set those higher IRAM address 685e6d9423dSDimitar Dimitrov * bits anyway. PRU architecture limits the program counter to 16-bit 686e6d9423dSDimitar Dimitrov * word-address range. This in turn corresponds to 18-bit IRAM 687e6d9423dSDimitar Dimitrov * byte-address range for ELF. 688e6d9423dSDimitar Dimitrov * 689e6d9423dSDimitar Dimitrov * Two more bits are added just in case to make the final 20-bit mask. 690e6d9423dSDimitar Dimitrov * Idea is to have a safeguard in case TI decides to add banking 691e6d9423dSDimitar Dimitrov * in future SoCs. 692e6d9423dSDimitar Dimitrov */ 693e6d9423dSDimitar Dimitrov da &= 0xfffff; 694e6d9423dSDimitar Dimitrov 6953a87fc6fSSimon Horman if (da + len <= PRU_IRAM_DA + pru->mem_regions[PRU_IOMEM_IRAM].size) { 696d4ce2de7SSuman Anna offset = da - PRU_IRAM_DA; 697d4ce2de7SSuman Anna va = (__force void *)(pru->mem_regions[PRU_IOMEM_IRAM].va + 698d4ce2de7SSuman Anna offset); 699d4ce2de7SSuman Anna } 700d4ce2de7SSuman Anna 701d4ce2de7SSuman Anna return va; 702d4ce2de7SSuman Anna } 703d4ce2de7SSuman Anna 704d4ce2de7SSuman Anna /* 705d4ce2de7SSuman Anna * Provide address translations for only PRU Data RAMs through the remoteproc 706d4ce2de7SSuman Anna * core for any PRU client drivers. The PRU Instruction RAM access is restricted 707d4ce2de7SSuman Anna * only to the PRU loader code. 708d4ce2de7SSuman Anna */ 70940df0a91SPeng Fan static void *pru_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem) 710d4ce2de7SSuman Anna { 711d4ce2de7SSuman Anna struct pru_rproc *pru = rproc->priv; 712d4ce2de7SSuman Anna 713d4ce2de7SSuman Anna return pru_d_da_to_va(pru, da, len); 714d4ce2de7SSuman Anna } 715d4ce2de7SSuman Anna 716d4ce2de7SSuman Anna /* PRU-specific address translator used by PRU loader. */ 717d4ce2de7SSuman Anna static void *pru_da_to_va(struct rproc *rproc, u64 da, size_t len, bool is_iram) 718d4ce2de7SSuman Anna { 719d4ce2de7SSuman Anna struct pru_rproc *pru = rproc->priv; 720d4ce2de7SSuman Anna void *va; 721d4ce2de7SSuman Anna 722d4ce2de7SSuman Anna if (is_iram) 723d4ce2de7SSuman Anna va = pru_i_da_to_va(pru, da, len); 724d4ce2de7SSuman Anna else 725d4ce2de7SSuman Anna va = pru_d_da_to_va(pru, da, len); 726d4ce2de7SSuman Anna 727d4ce2de7SSuman Anna return va; 728d4ce2de7SSuman Anna } 729d4ce2de7SSuman Anna 730d4ce2de7SSuman Anna static struct rproc_ops pru_rproc_ops = { 731d4ce2de7SSuman Anna .start = pru_rproc_start, 732d4ce2de7SSuman Anna .stop = pru_rproc_stop, 733d4ce2de7SSuman Anna .da_to_va = pru_rproc_da_to_va, 734d4ce2de7SSuman Anna }; 735d4ce2de7SSuman Anna 7361d39f4d1SSuman Anna /* 7371d39f4d1SSuman Anna * Custom memory copy implementation for ICSSG PRU/RTU/Tx_PRU Cores 7381d39f4d1SSuman Anna * 7391d39f4d1SSuman Anna * The ICSSG PRU/RTU/Tx_PRU cores have a memory copying issue with IRAM 7401d39f4d1SSuman Anna * memories, that is not seen on previous generation SoCs. The data is reflected 7411d39f4d1SSuman Anna * properly in the IRAM memories only for integer (4-byte) copies. Any unaligned 7421d39f4d1SSuman Anna * copies result in all the other pre-existing bytes zeroed out within that 7431d39f4d1SSuman Anna * 4-byte boundary, thereby resulting in wrong text/code in the IRAMs. Also, the 7441d39f4d1SSuman Anna * IRAM memory port interface does not allow any 8-byte copies (as commonly used 7451d39f4d1SSuman Anna * by ARM64 memcpy implementation) and throws an exception. The DRAM memory 7461d39f4d1SSuman Anna * ports do not show this behavior. 7471d39f4d1SSuman Anna */ 7481d39f4d1SSuman Anna static int pru_rproc_memcpy(void *dest, const void *src, size_t count) 7491d39f4d1SSuman Anna { 7501d39f4d1SSuman Anna const u32 *s = src; 7511d39f4d1SSuman Anna u32 *d = dest; 7521d39f4d1SSuman Anna size_t size = count / 4; 7531d39f4d1SSuman Anna u32 *tmp_src = NULL; 7541d39f4d1SSuman Anna 7551d39f4d1SSuman Anna /* 7561d39f4d1SSuman Anna * TODO: relax limitation of 4-byte aligned dest addresses and copy 7571d39f4d1SSuman Anna * sizes 7581d39f4d1SSuman Anna */ 7591d39f4d1SSuman Anna if ((long)dest % 4 || count % 4) 7601d39f4d1SSuman Anna return -EINVAL; 7611d39f4d1SSuman Anna 7621d39f4d1SSuman Anna /* src offsets in ELF firmware image can be non-aligned */ 7631d39f4d1SSuman Anna if ((long)src % 4) { 7641d39f4d1SSuman Anna tmp_src = kmemdup(src, count, GFP_KERNEL); 7651d39f4d1SSuman Anna if (!tmp_src) 7661d39f4d1SSuman Anna return -ENOMEM; 7671d39f4d1SSuman Anna s = tmp_src; 7681d39f4d1SSuman Anna } 7691d39f4d1SSuman Anna 7701d39f4d1SSuman Anna while (size--) 7711d39f4d1SSuman Anna *d++ = *s++; 7721d39f4d1SSuman Anna 7731d39f4d1SSuman Anna kfree(tmp_src); 7741d39f4d1SSuman Anna 7751d39f4d1SSuman Anna return 0; 7761d39f4d1SSuman Anna } 7771d39f4d1SSuman Anna 778d4ce2de7SSuman Anna static int 779d4ce2de7SSuman Anna pru_rproc_load_elf_segments(struct rproc *rproc, const struct firmware *fw) 780d4ce2de7SSuman Anna { 7811d39f4d1SSuman Anna struct pru_rproc *pru = rproc->priv; 782d4ce2de7SSuman Anna struct device *dev = &rproc->dev; 783d4ce2de7SSuman Anna struct elf32_hdr *ehdr; 784d4ce2de7SSuman Anna struct elf32_phdr *phdr; 785d4ce2de7SSuman Anna int i, ret = 0; 786d4ce2de7SSuman Anna const u8 *elf_data = fw->data; 787d4ce2de7SSuman Anna 788d4ce2de7SSuman Anna ehdr = (struct elf32_hdr *)elf_data; 789d4ce2de7SSuman Anna phdr = (struct elf32_phdr *)(elf_data + ehdr->e_phoff); 790d4ce2de7SSuman Anna 791d4ce2de7SSuman Anna /* go through the available ELF segments */ 792d4ce2de7SSuman Anna for (i = 0; i < ehdr->e_phnum; i++, phdr++) { 793d4ce2de7SSuman Anna u32 da = phdr->p_paddr; 794d4ce2de7SSuman Anna u32 memsz = phdr->p_memsz; 795d4ce2de7SSuman Anna u32 filesz = phdr->p_filesz; 796d4ce2de7SSuman Anna u32 offset = phdr->p_offset; 797d4ce2de7SSuman Anna bool is_iram; 798d4ce2de7SSuman Anna void *ptr; 799d4ce2de7SSuman Anna 800d4ce2de7SSuman Anna if (phdr->p_type != PT_LOAD || !filesz) 801d4ce2de7SSuman Anna continue; 802d4ce2de7SSuman Anna 803d4ce2de7SSuman Anna dev_dbg(dev, "phdr: type %d da 0x%x memsz 0x%x filesz 0x%x\n", 804d4ce2de7SSuman Anna phdr->p_type, da, memsz, filesz); 805d4ce2de7SSuman Anna 806d4ce2de7SSuman Anna if (filesz > memsz) { 807d4ce2de7SSuman Anna dev_err(dev, "bad phdr filesz 0x%x memsz 0x%x\n", 808d4ce2de7SSuman Anna filesz, memsz); 809d4ce2de7SSuman Anna ret = -EINVAL; 810d4ce2de7SSuman Anna break; 811d4ce2de7SSuman Anna } 812d4ce2de7SSuman Anna 813d4ce2de7SSuman Anna if (offset + filesz > fw->size) { 814d4ce2de7SSuman Anna dev_err(dev, "truncated fw: need 0x%x avail 0x%zx\n", 815d4ce2de7SSuman Anna offset + filesz, fw->size); 816d4ce2de7SSuman Anna ret = -EINVAL; 817d4ce2de7SSuman Anna break; 818d4ce2de7SSuman Anna } 819d4ce2de7SSuman Anna 820d4ce2de7SSuman Anna /* grab the kernel address for this device address */ 821d4ce2de7SSuman Anna is_iram = phdr->p_flags & PF_X; 822d4ce2de7SSuman Anna ptr = pru_da_to_va(rproc, da, memsz, is_iram); 823d4ce2de7SSuman Anna if (!ptr) { 824d4ce2de7SSuman Anna dev_err(dev, "bad phdr da 0x%x mem 0x%x\n", da, memsz); 825d4ce2de7SSuman Anna ret = -EINVAL; 826d4ce2de7SSuman Anna break; 827d4ce2de7SSuman Anna } 828d4ce2de7SSuman Anna 8299afeefcfSSuman Anna if (pru->data->is_k3) { 8301d39f4d1SSuman Anna ret = pru_rproc_memcpy(ptr, elf_data + phdr->p_offset, 8311d39f4d1SSuman Anna filesz); 8321d39f4d1SSuman Anna if (ret) { 8331d39f4d1SSuman Anna dev_err(dev, "PRU memory copy failed for da 0x%x memsz 0x%x\n", 8341d39f4d1SSuman Anna da, memsz); 8351d39f4d1SSuman Anna break; 8361d39f4d1SSuman Anna } 8371d39f4d1SSuman Anna } else { 838d4ce2de7SSuman Anna memcpy(ptr, elf_data + phdr->p_offset, filesz); 8391d39f4d1SSuman Anna } 840d4ce2de7SSuman Anna 841d4ce2de7SSuman Anna /* skip the memzero logic performed by remoteproc ELF loader */ 842d4ce2de7SSuman Anna } 843d4ce2de7SSuman Anna 844d4ce2de7SSuman Anna return ret; 845d4ce2de7SSuman Anna } 846d4ce2de7SSuman Anna 847c75c9fdaSGrzegorz Jaszczyk static const void * 848c75c9fdaSGrzegorz Jaszczyk pru_rproc_find_interrupt_map(struct device *dev, const struct firmware *fw) 849c75c9fdaSGrzegorz Jaszczyk { 850c75c9fdaSGrzegorz Jaszczyk struct elf32_shdr *shdr, *name_table_shdr; 851c75c9fdaSGrzegorz Jaszczyk const char *name_table; 852c75c9fdaSGrzegorz Jaszczyk const u8 *elf_data = fw->data; 853c75c9fdaSGrzegorz Jaszczyk struct elf32_hdr *ehdr = (struct elf32_hdr *)elf_data; 854c75c9fdaSGrzegorz Jaszczyk u16 shnum = ehdr->e_shnum; 855c75c9fdaSGrzegorz Jaszczyk u16 shstrndx = ehdr->e_shstrndx; 856c75c9fdaSGrzegorz Jaszczyk int i; 857c75c9fdaSGrzegorz Jaszczyk 858c75c9fdaSGrzegorz Jaszczyk /* first, get the section header */ 859c75c9fdaSGrzegorz Jaszczyk shdr = (struct elf32_shdr *)(elf_data + ehdr->e_shoff); 860c75c9fdaSGrzegorz Jaszczyk /* compute name table section header entry in shdr array */ 861c75c9fdaSGrzegorz Jaszczyk name_table_shdr = shdr + shstrndx; 862c75c9fdaSGrzegorz Jaszczyk /* finally, compute the name table section address in elf */ 863c75c9fdaSGrzegorz Jaszczyk name_table = elf_data + name_table_shdr->sh_offset; 864c75c9fdaSGrzegorz Jaszczyk 865c75c9fdaSGrzegorz Jaszczyk for (i = 0; i < shnum; i++, shdr++) { 866c75c9fdaSGrzegorz Jaszczyk u32 size = shdr->sh_size; 867c75c9fdaSGrzegorz Jaszczyk u32 offset = shdr->sh_offset; 868c75c9fdaSGrzegorz Jaszczyk u32 name = shdr->sh_name; 869c75c9fdaSGrzegorz Jaszczyk 870c75c9fdaSGrzegorz Jaszczyk if (strcmp(name_table + name, ".pru_irq_map")) 871c75c9fdaSGrzegorz Jaszczyk continue; 872c75c9fdaSGrzegorz Jaszczyk 873c75c9fdaSGrzegorz Jaszczyk /* make sure we have the entire irq map */ 874c75c9fdaSGrzegorz Jaszczyk if (offset + size > fw->size || offset + size < size) { 875c75c9fdaSGrzegorz Jaszczyk dev_err(dev, ".pru_irq_map section truncated\n"); 876c75c9fdaSGrzegorz Jaszczyk return ERR_PTR(-EINVAL); 877c75c9fdaSGrzegorz Jaszczyk } 878c75c9fdaSGrzegorz Jaszczyk 879c75c9fdaSGrzegorz Jaszczyk /* make sure irq map has at least the header */ 880c75c9fdaSGrzegorz Jaszczyk if (sizeof(struct pru_irq_rsc) > size) { 881c75c9fdaSGrzegorz Jaszczyk dev_err(dev, "header-less .pru_irq_map section\n"); 882c75c9fdaSGrzegorz Jaszczyk return ERR_PTR(-EINVAL); 883c75c9fdaSGrzegorz Jaszczyk } 884c75c9fdaSGrzegorz Jaszczyk 885c75c9fdaSGrzegorz Jaszczyk return shdr; 886c75c9fdaSGrzegorz Jaszczyk } 887c75c9fdaSGrzegorz Jaszczyk 888c75c9fdaSGrzegorz Jaszczyk dev_dbg(dev, "no .pru_irq_map section found for this fw\n"); 889c75c9fdaSGrzegorz Jaszczyk 890c75c9fdaSGrzegorz Jaszczyk return NULL; 891c75c9fdaSGrzegorz Jaszczyk } 892c75c9fdaSGrzegorz Jaszczyk 893d4ce2de7SSuman Anna /* 894d4ce2de7SSuman Anna * Use a custom parse_fw callback function for dealing with PRU firmware 895d4ce2de7SSuman Anna * specific sections. 896c75c9fdaSGrzegorz Jaszczyk * 897c75c9fdaSGrzegorz Jaszczyk * The firmware blob can contain optional ELF sections: .resource_table section 898c75c9fdaSGrzegorz Jaszczyk * and .pru_irq_map one. The second one contains the PRUSS interrupt mapping 899c75c9fdaSGrzegorz Jaszczyk * description, which needs to be setup before powering on the PRU core. To 900c75c9fdaSGrzegorz Jaszczyk * avoid RAM wastage this ELF section is not mapped to any ELF segment (by the 901c75c9fdaSGrzegorz Jaszczyk * firmware linker) and therefore is not loaded to PRU memory. 902d4ce2de7SSuman Anna */ 903d4ce2de7SSuman Anna static int pru_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) 904d4ce2de7SSuman Anna { 905c75c9fdaSGrzegorz Jaszczyk struct device *dev = &rproc->dev; 906c75c9fdaSGrzegorz Jaszczyk struct pru_rproc *pru = rproc->priv; 907c75c9fdaSGrzegorz Jaszczyk const u8 *elf_data = fw->data; 908c75c9fdaSGrzegorz Jaszczyk const void *shdr; 909c75c9fdaSGrzegorz Jaszczyk u8 class = fw_elf_get_class(fw); 910c75c9fdaSGrzegorz Jaszczyk u64 sh_offset; 911d4ce2de7SSuman Anna int ret; 912d4ce2de7SSuman Anna 913d4ce2de7SSuman Anna /* load optional rsc table */ 914d4ce2de7SSuman Anna ret = rproc_elf_load_rsc_table(rproc, fw); 915d4ce2de7SSuman Anna if (ret == -EINVAL) 916d4ce2de7SSuman Anna dev_dbg(&rproc->dev, "no resource table found for this fw\n"); 917d4ce2de7SSuman Anna else if (ret) 918d4ce2de7SSuman Anna return ret; 919d4ce2de7SSuman Anna 920c75c9fdaSGrzegorz Jaszczyk /* find .pru_interrupt_map section, not having it is not an error */ 921c75c9fdaSGrzegorz Jaszczyk shdr = pru_rproc_find_interrupt_map(dev, fw); 922c75c9fdaSGrzegorz Jaszczyk if (IS_ERR(shdr)) 923c75c9fdaSGrzegorz Jaszczyk return PTR_ERR(shdr); 924c75c9fdaSGrzegorz Jaszczyk 925c75c9fdaSGrzegorz Jaszczyk if (!shdr) 926c75c9fdaSGrzegorz Jaszczyk return 0; 927c75c9fdaSGrzegorz Jaszczyk 928c75c9fdaSGrzegorz Jaszczyk /* preserve pointer to PRU interrupt map together with it size */ 929c75c9fdaSGrzegorz Jaszczyk sh_offset = elf_shdr_get_sh_offset(class, shdr); 930c75c9fdaSGrzegorz Jaszczyk pru->pru_interrupt_map = (struct pru_irq_rsc *)(elf_data + sh_offset); 931c75c9fdaSGrzegorz Jaszczyk pru->pru_interrupt_map_sz = elf_shdr_get_sh_size(class, shdr); 932c75c9fdaSGrzegorz Jaszczyk 933d4ce2de7SSuman Anna return 0; 934d4ce2de7SSuman Anna } 935d4ce2de7SSuman Anna 936d4ce2de7SSuman Anna /* 937d4ce2de7SSuman Anna * Compute PRU id based on the IRAM addresses. The PRU IRAMs are 938d4ce2de7SSuman Anna * always at a particular offset within the PRUSS address space. 939d4ce2de7SSuman Anna */ 940d4ce2de7SSuman Anna static int pru_rproc_set_id(struct pru_rproc *pru) 941d4ce2de7SSuman Anna { 942d4ce2de7SSuman Anna int ret = 0; 943d4ce2de7SSuman Anna 944d4ce2de7SSuman Anna switch (pru->mem_regions[PRU_IOMEM_IRAM].pa & PRU_IRAM_ADDR_MASK) { 9451d39f4d1SSuman Anna case TX_PRU0_IRAM_ADDR_MASK: 9461d39f4d1SSuman Anna fallthrough; 9471d39f4d1SSuman Anna case RTU0_IRAM_ADDR_MASK: 9481d39f4d1SSuman Anna fallthrough; 949d4ce2de7SSuman Anna case PRU0_IRAM_ADDR_MASK: 9509b9ad70fSMD Danish Anwar pru->id = PRUSS_PRU0; 951d4ce2de7SSuman Anna break; 9521d39f4d1SSuman Anna case TX_PRU1_IRAM_ADDR_MASK: 9531d39f4d1SSuman Anna fallthrough; 9541d39f4d1SSuman Anna case RTU1_IRAM_ADDR_MASK: 9551d39f4d1SSuman Anna fallthrough; 956d4ce2de7SSuman Anna case PRU1_IRAM_ADDR_MASK: 9579b9ad70fSMD Danish Anwar pru->id = PRUSS_PRU1; 958d4ce2de7SSuman Anna break; 959d4ce2de7SSuman Anna default: 960d4ce2de7SSuman Anna ret = -EINVAL; 961d4ce2de7SSuman Anna } 962d4ce2de7SSuman Anna 963d4ce2de7SSuman Anna return ret; 964d4ce2de7SSuman Anna } 965d4ce2de7SSuman Anna 966d4ce2de7SSuman Anna static int pru_rproc_probe(struct platform_device *pdev) 967d4ce2de7SSuman Anna { 968d4ce2de7SSuman Anna struct device *dev = &pdev->dev; 969d4ce2de7SSuman Anna struct device_node *np = dev->of_node; 970d4ce2de7SSuman Anna struct platform_device *ppdev = to_platform_device(dev->parent); 971d4ce2de7SSuman Anna struct pru_rproc *pru; 972d4ce2de7SSuman Anna const char *fw_name; 973d4ce2de7SSuman Anna struct rproc *rproc = NULL; 974d4ce2de7SSuman Anna struct resource *res; 975d4ce2de7SSuman Anna int i, ret; 9761d39f4d1SSuman Anna const struct pru_private_data *data; 977d4ce2de7SSuman Anna const char *mem_names[PRU_IOMEM_MAX] = { "iram", "control", "debug" }; 978d4ce2de7SSuman Anna 9791d39f4d1SSuman Anna data = of_device_get_match_data(&pdev->dev); 9801d39f4d1SSuman Anna if (!data) 9811d39f4d1SSuman Anna return -ENODEV; 9821d39f4d1SSuman Anna 983d4ce2de7SSuman Anna ret = of_property_read_string(np, "firmware-name", &fw_name); 984d4ce2de7SSuman Anna if (ret) { 985d4ce2de7SSuman Anna dev_err(dev, "unable to retrieve firmware-name %d\n", ret); 986d4ce2de7SSuman Anna return ret; 987d4ce2de7SSuman Anna } 988d4ce2de7SSuman Anna 989d4ce2de7SSuman Anna rproc = devm_rproc_alloc(dev, pdev->name, &pru_rproc_ops, fw_name, 990d4ce2de7SSuman Anna sizeof(*pru)); 991d4ce2de7SSuman Anna if (!rproc) { 992d4ce2de7SSuman Anna dev_err(dev, "rproc_alloc failed\n"); 993d4ce2de7SSuman Anna return -ENOMEM; 994d4ce2de7SSuman Anna } 995d4ce2de7SSuman Anna /* use a custom load function to deal with PRU-specific quirks */ 996d4ce2de7SSuman Anna rproc->ops->load = pru_rproc_load_elf_segments; 997d4ce2de7SSuman Anna 998d4ce2de7SSuman Anna /* use a custom parse function to deal with PRU-specific resources */ 999d4ce2de7SSuman Anna rproc->ops->parse_fw = pru_rproc_parse_fw; 1000d4ce2de7SSuman Anna 1001d4ce2de7SSuman Anna /* error recovery is not supported for PRUs */ 1002d4ce2de7SSuman Anna rproc->recovery_disabled = true; 1003d4ce2de7SSuman Anna 1004d4ce2de7SSuman Anna /* 1005d4ce2de7SSuman Anna * rproc_add will auto-boot the processor normally, but this is not 1006d4ce2de7SSuman Anna * desired with PRU client driven boot-flow methodology. A PRU 1007d4ce2de7SSuman Anna * application/client driver will boot the corresponding PRU 1008d4ce2de7SSuman Anna * remote-processor as part of its state machine either through the 1009d4ce2de7SSuman Anna * remoteproc sysfs interface or through the equivalent kernel API. 1010d4ce2de7SSuman Anna */ 1011d4ce2de7SSuman Anna rproc->auto_boot = false; 1012d4ce2de7SSuman Anna 1013d4ce2de7SSuman Anna pru = rproc->priv; 1014d4ce2de7SSuman Anna pru->dev = dev; 10151d39f4d1SSuman Anna pru->data = data; 1016d4ce2de7SSuman Anna pru->pruss = platform_get_drvdata(ppdev); 1017d4ce2de7SSuman Anna pru->rproc = rproc; 1018d4ce2de7SSuman Anna pru->fw_name = fw_name; 1019919e8942SMD Danish Anwar pru->client_np = NULL; 102010285340SRoger Quadros spin_lock_init(&pru->rmw_lock); 1021919e8942SMD Danish Anwar mutex_init(&pru->lock); 1022d4ce2de7SSuman Anna 1023d4ce2de7SSuman Anna for (i = 0; i < ARRAY_SIZE(mem_names); i++) { 1024d4ce2de7SSuman Anna res = platform_get_resource_byname(pdev, IORESOURCE_MEM, 1025d4ce2de7SSuman Anna mem_names[i]); 1026d4ce2de7SSuman Anna pru->mem_regions[i].va = devm_ioremap_resource(dev, res); 1027d4ce2de7SSuman Anna if (IS_ERR(pru->mem_regions[i].va)) { 1028d4ce2de7SSuman Anna dev_err(dev, "failed to parse and map memory resource %d %s\n", 1029d4ce2de7SSuman Anna i, mem_names[i]); 1030d4ce2de7SSuman Anna ret = PTR_ERR(pru->mem_regions[i].va); 1031d4ce2de7SSuman Anna return ret; 1032d4ce2de7SSuman Anna } 1033d4ce2de7SSuman Anna pru->mem_regions[i].pa = res->start; 1034d4ce2de7SSuman Anna pru->mem_regions[i].size = resource_size(res); 1035d4ce2de7SSuman Anna 1036d4ce2de7SSuman Anna dev_dbg(dev, "memory %8s: pa %pa size 0x%zx va %pK\n", 1037d4ce2de7SSuman Anna mem_names[i], &pru->mem_regions[i].pa, 1038d4ce2de7SSuman Anna pru->mem_regions[i].size, pru->mem_regions[i].va); 1039d4ce2de7SSuman Anna } 1040d4ce2de7SSuman Anna 1041d4ce2de7SSuman Anna ret = pru_rproc_set_id(pru); 1042d4ce2de7SSuman Anna if (ret < 0) 1043d4ce2de7SSuman Anna return ret; 1044d4ce2de7SSuman Anna 1045d4ce2de7SSuman Anna platform_set_drvdata(pdev, rproc); 1046d4ce2de7SSuman Anna 1047d4ce2de7SSuman Anna ret = devm_rproc_add(dev, pru->rproc); 1048d4ce2de7SSuman Anna if (ret) { 1049d4ce2de7SSuman Anna dev_err(dev, "rproc_add failed: %d\n", ret); 1050d4ce2de7SSuman Anna return ret; 1051d4ce2de7SSuman Anna } 1052d4ce2de7SSuman Anna 105320ad1de0SSuman Anna pru_rproc_create_debug_entries(rproc); 105420ad1de0SSuman Anna 1055d4ce2de7SSuman Anna dev_dbg(dev, "PRU rproc node %pOF probed successfully\n", np); 1056d4ce2de7SSuman Anna 1057d4ce2de7SSuman Anna return 0; 1058d4ce2de7SSuman Anna } 1059d4ce2de7SSuman Anna 1060d6b862fdSUwe Kleine-König static void pru_rproc_remove(struct platform_device *pdev) 1061d4ce2de7SSuman Anna { 1062d4ce2de7SSuman Anna struct device *dev = &pdev->dev; 1063d4ce2de7SSuman Anna struct rproc *rproc = platform_get_drvdata(pdev); 1064d4ce2de7SSuman Anna 1065d4ce2de7SSuman Anna dev_dbg(dev, "%s: removing rproc %s\n", __func__, rproc->name); 1066d4ce2de7SSuman Anna } 1067d4ce2de7SSuman Anna 10681d39f4d1SSuman Anna static const struct pru_private_data pru_data = { 10691d39f4d1SSuman Anna .type = PRU_TYPE_PRU, 10701d39f4d1SSuman Anna }; 10711d39f4d1SSuman Anna 10721d39f4d1SSuman Anna static const struct pru_private_data k3_pru_data = { 10731d39f4d1SSuman Anna .type = PRU_TYPE_PRU, 10741d39f4d1SSuman Anna .is_k3 = 1, 10751d39f4d1SSuman Anna }; 10761d39f4d1SSuman Anna 10771d39f4d1SSuman Anna static const struct pru_private_data k3_rtu_data = { 10781d39f4d1SSuman Anna .type = PRU_TYPE_RTU, 10791d39f4d1SSuman Anna .is_k3 = 1, 10801d39f4d1SSuman Anna }; 10811d39f4d1SSuman Anna 10821d39f4d1SSuman Anna static const struct pru_private_data k3_tx_pru_data = { 10831d39f4d1SSuman Anna .type = PRU_TYPE_TX_PRU, 10841d39f4d1SSuman Anna .is_k3 = 1, 10851d39f4d1SSuman Anna }; 10861d39f4d1SSuman Anna 1087d4ce2de7SSuman Anna static const struct of_device_id pru_rproc_match[] = { 10881d39f4d1SSuman Anna { .compatible = "ti,am3356-pru", .data = &pru_data }, 10891d39f4d1SSuman Anna { .compatible = "ti,am4376-pru", .data = &pru_data }, 10901d39f4d1SSuman Anna { .compatible = "ti,am5728-pru", .data = &pru_data }, 10910740ec08SSuman Anna { .compatible = "ti,am642-pru", .data = &k3_pru_data }, 10920740ec08SSuman Anna { .compatible = "ti,am642-rtu", .data = &k3_rtu_data }, 10930740ec08SSuman Anna { .compatible = "ti,am642-tx-pru", .data = &k3_tx_pru_data }, 10941d39f4d1SSuman Anna { .compatible = "ti,k2g-pru", .data = &pru_data }, 10951d39f4d1SSuman Anna { .compatible = "ti,am654-pru", .data = &k3_pru_data }, 10961d39f4d1SSuman Anna { .compatible = "ti,am654-rtu", .data = &k3_rtu_data }, 10971d39f4d1SSuman Anna { .compatible = "ti,am654-tx-pru", .data = &k3_tx_pru_data }, 1098b44786c9SSuman Anna { .compatible = "ti,j721e-pru", .data = &k3_pru_data }, 1099b44786c9SSuman Anna { .compatible = "ti,j721e-rtu", .data = &k3_rtu_data }, 1100b44786c9SSuman Anna { .compatible = "ti,j721e-tx-pru", .data = &k3_tx_pru_data }, 1101aa0cec24SKishon Vijay Abraham I { .compatible = "ti,am625-pru", .data = &k3_pru_data }, 1102d4ce2de7SSuman Anna {}, 1103d4ce2de7SSuman Anna }; 1104d4ce2de7SSuman Anna MODULE_DEVICE_TABLE(of, pru_rproc_match); 1105d4ce2de7SSuman Anna 1106d4ce2de7SSuman Anna static struct platform_driver pru_rproc_driver = { 1107d4ce2de7SSuman Anna .driver = { 1108919e8942SMD Danish Anwar .name = PRU_RPROC_DRVNAME, 1109d4ce2de7SSuman Anna .of_match_table = pru_rproc_match, 1110d4ce2de7SSuman Anna .suppress_bind_attrs = true, 1111d4ce2de7SSuman Anna }, 1112d4ce2de7SSuman Anna .probe = pru_rproc_probe, 1113d6b862fdSUwe Kleine-König .remove_new = pru_rproc_remove, 1114d4ce2de7SSuman Anna }; 1115d4ce2de7SSuman Anna module_platform_driver(pru_rproc_driver); 1116d4ce2de7SSuman Anna 1117d4ce2de7SSuman Anna MODULE_AUTHOR("Suman Anna <s-anna@ti.com>"); 1118d4ce2de7SSuman Anna MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>"); 1119d4ce2de7SSuman Anna MODULE_AUTHOR("Grzegorz Jaszczyk <grzegorz.jaszczyk@linaro.org>"); 1120919e8942SMD Danish Anwar MODULE_AUTHOR("Puranjay Mohan <p-mohan@ti.com>"); 1121919e8942SMD Danish Anwar MODULE_AUTHOR("Md Danish Anwar <danishanwar@ti.com>"); 1122d4ce2de7SSuman Anna MODULE_DESCRIPTION("PRU-ICSS Remote Processor Driver"); 1123d4ce2de7SSuman Anna MODULE_LICENSE("GPL v2"); 1124