162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * PRU-ICSS remoteproc driver for various TI SoCs 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2014-2022 Texas Instruments Incorporated - https://www.ti.com/ 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author(s): 862306a36Sopenharmony_ci * Suman Anna <s-anna@ti.com> 962306a36Sopenharmony_ci * Andrew F. Davis <afd@ti.com> 1062306a36Sopenharmony_ci * Grzegorz Jaszczyk <grzegorz.jaszczyk@linaro.org> for Texas Instruments 1162306a36Sopenharmony_ci * Puranjay Mohan <p-mohan@ti.com> 1262306a36Sopenharmony_ci * Md Danish Anwar <danishanwar@ti.com> 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/bitops.h> 1662306a36Sopenharmony_ci#include <linux/debugfs.h> 1762306a36Sopenharmony_ci#include <linux/irqdomain.h> 1862306a36Sopenharmony_ci#include <linux/module.h> 1962306a36Sopenharmony_ci#include <linux/of.h> 2062306a36Sopenharmony_ci#include <linux/of_irq.h> 2162306a36Sopenharmony_ci#include <linux/platform_device.h> 2262306a36Sopenharmony_ci#include <linux/remoteproc/pruss.h> 2362306a36Sopenharmony_ci#include <linux/pruss_driver.h> 2462306a36Sopenharmony_ci#include <linux/remoteproc.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include "remoteproc_internal.h" 2762306a36Sopenharmony_ci#include "remoteproc_elf_helpers.h" 2862306a36Sopenharmony_ci#include "pru_rproc.h" 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* PRU_ICSS_PRU_CTRL registers */ 3162306a36Sopenharmony_ci#define PRU_CTRL_CTRL 0x0000 3262306a36Sopenharmony_ci#define PRU_CTRL_STS 0x0004 3362306a36Sopenharmony_ci#define PRU_CTRL_WAKEUP_EN 0x0008 3462306a36Sopenharmony_ci#define PRU_CTRL_CYCLE 0x000C 3562306a36Sopenharmony_ci#define PRU_CTRL_STALL 0x0010 3662306a36Sopenharmony_ci#define PRU_CTRL_CTBIR0 0x0020 3762306a36Sopenharmony_ci#define PRU_CTRL_CTBIR1 0x0024 3862306a36Sopenharmony_ci#define PRU_CTRL_CTPPR0 0x0028 3962306a36Sopenharmony_ci#define PRU_CTRL_CTPPR1 0x002C 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* CTRL register bit-fields */ 4262306a36Sopenharmony_ci#define CTRL_CTRL_SOFT_RST_N BIT(0) 4362306a36Sopenharmony_ci#define CTRL_CTRL_EN BIT(1) 4462306a36Sopenharmony_ci#define CTRL_CTRL_SLEEPING BIT(2) 4562306a36Sopenharmony_ci#define CTRL_CTRL_CTR_EN BIT(3) 4662306a36Sopenharmony_ci#define CTRL_CTRL_SINGLE_STEP BIT(8) 4762306a36Sopenharmony_ci#define CTRL_CTRL_RUNSTATE BIT(15) 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/* PRU_ICSS_PRU_DEBUG registers */ 5062306a36Sopenharmony_ci#define PRU_DEBUG_GPREG(x) (0x0000 + (x) * 4) 5162306a36Sopenharmony_ci#define PRU_DEBUG_CT_REG(x) (0x0080 + (x) * 4) 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/* PRU/RTU/Tx_PRU Core IRAM address masks */ 5462306a36Sopenharmony_ci#define PRU_IRAM_ADDR_MASK 0x3ffff 5562306a36Sopenharmony_ci#define PRU0_IRAM_ADDR_MASK 0x34000 5662306a36Sopenharmony_ci#define PRU1_IRAM_ADDR_MASK 0x38000 5762306a36Sopenharmony_ci#define RTU0_IRAM_ADDR_MASK 0x4000 5862306a36Sopenharmony_ci#define RTU1_IRAM_ADDR_MASK 0x6000 5962306a36Sopenharmony_ci#define TX_PRU0_IRAM_ADDR_MASK 0xa000 6062306a36Sopenharmony_ci#define TX_PRU1_IRAM_ADDR_MASK 0xc000 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/* PRU device addresses for various type of PRU RAMs */ 6362306a36Sopenharmony_ci#define PRU_IRAM_DA 0 /* Instruction RAM */ 6462306a36Sopenharmony_ci#define PRU_PDRAM_DA 0 /* Primary Data RAM */ 6562306a36Sopenharmony_ci#define PRU_SDRAM_DA 0x2000 /* Secondary Data RAM */ 6662306a36Sopenharmony_ci#define PRU_SHRDRAM_DA 0x10000 /* Shared Data RAM */ 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci#define MAX_PRU_SYS_EVENTS 160 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/** 7162306a36Sopenharmony_ci * enum pru_iomem - PRU core memory/register range identifiers 7262306a36Sopenharmony_ci * 7362306a36Sopenharmony_ci * @PRU_IOMEM_IRAM: PRU Instruction RAM range 7462306a36Sopenharmony_ci * @PRU_IOMEM_CTRL: PRU Control register range 7562306a36Sopenharmony_ci * @PRU_IOMEM_DEBUG: PRU Debug register range 7662306a36Sopenharmony_ci * @PRU_IOMEM_MAX: just keep this one at the end 7762306a36Sopenharmony_ci */ 7862306a36Sopenharmony_cienum pru_iomem { 7962306a36Sopenharmony_ci PRU_IOMEM_IRAM = 0, 8062306a36Sopenharmony_ci PRU_IOMEM_CTRL, 8162306a36Sopenharmony_ci PRU_IOMEM_DEBUG, 8262306a36Sopenharmony_ci PRU_IOMEM_MAX, 8362306a36Sopenharmony_ci}; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/** 8662306a36Sopenharmony_ci * struct pru_private_data - device data for a PRU core 8762306a36Sopenharmony_ci * @type: type of the PRU core (PRU, RTU, Tx_PRU) 8862306a36Sopenharmony_ci * @is_k3: flag used to identify the need for special load handling 8962306a36Sopenharmony_ci */ 9062306a36Sopenharmony_cistruct pru_private_data { 9162306a36Sopenharmony_ci enum pru_type type; 9262306a36Sopenharmony_ci unsigned int is_k3 : 1; 9362306a36Sopenharmony_ci}; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci/** 9662306a36Sopenharmony_ci * struct pru_rproc - PRU remoteproc structure 9762306a36Sopenharmony_ci * @id: id of the PRU core within the PRUSS 9862306a36Sopenharmony_ci * @dev: PRU core device pointer 9962306a36Sopenharmony_ci * @pruss: back-reference to parent PRUSS structure 10062306a36Sopenharmony_ci * @rproc: remoteproc pointer for this PRU core 10162306a36Sopenharmony_ci * @data: PRU core specific data 10262306a36Sopenharmony_ci * @mem_regions: data for each of the PRU memory regions 10362306a36Sopenharmony_ci * @client_np: client device node 10462306a36Sopenharmony_ci * @lock: mutex to protect client usage 10562306a36Sopenharmony_ci * @fw_name: name of firmware image used during loading 10662306a36Sopenharmony_ci * @mapped_irq: virtual interrupt numbers of created fw specific mapping 10762306a36Sopenharmony_ci * @pru_interrupt_map: pointer to interrupt mapping description (firmware) 10862306a36Sopenharmony_ci * @pru_interrupt_map_sz: pru_interrupt_map size 10962306a36Sopenharmony_ci * @rmw_lock: lock for read, modify, write operations on registers 11062306a36Sopenharmony_ci * @dbg_single_step: debug state variable to set PRU into single step mode 11162306a36Sopenharmony_ci * @dbg_continuous: debug state variable to restore PRU execution mode 11262306a36Sopenharmony_ci * @evt_count: number of mapped events 11362306a36Sopenharmony_ci * @gpmux_save: saved value for gpmux config 11462306a36Sopenharmony_ci */ 11562306a36Sopenharmony_cistruct pru_rproc { 11662306a36Sopenharmony_ci int id; 11762306a36Sopenharmony_ci struct device *dev; 11862306a36Sopenharmony_ci struct pruss *pruss; 11962306a36Sopenharmony_ci struct rproc *rproc; 12062306a36Sopenharmony_ci const struct pru_private_data *data; 12162306a36Sopenharmony_ci struct pruss_mem_region mem_regions[PRU_IOMEM_MAX]; 12262306a36Sopenharmony_ci struct device_node *client_np; 12362306a36Sopenharmony_ci struct mutex lock; 12462306a36Sopenharmony_ci const char *fw_name; 12562306a36Sopenharmony_ci unsigned int *mapped_irq; 12662306a36Sopenharmony_ci struct pru_irq_rsc *pru_interrupt_map; 12762306a36Sopenharmony_ci size_t pru_interrupt_map_sz; 12862306a36Sopenharmony_ci spinlock_t rmw_lock; 12962306a36Sopenharmony_ci u32 dbg_single_step; 13062306a36Sopenharmony_ci u32 dbg_continuous; 13162306a36Sopenharmony_ci u8 evt_count; 13262306a36Sopenharmony_ci u8 gpmux_save; 13362306a36Sopenharmony_ci}; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic inline u32 pru_control_read_reg(struct pru_rproc *pru, unsigned int reg) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci return readl_relaxed(pru->mem_regions[PRU_IOMEM_CTRL].va + reg); 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic inline 14162306a36Sopenharmony_civoid pru_control_write_reg(struct pru_rproc *pru, unsigned int reg, u32 val) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci writel_relaxed(val, pru->mem_regions[PRU_IOMEM_CTRL].va + reg); 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic inline 14762306a36Sopenharmony_civoid pru_control_set_reg(struct pru_rproc *pru, unsigned int reg, 14862306a36Sopenharmony_ci u32 mask, u32 set) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci u32 val; 15162306a36Sopenharmony_ci unsigned long flags; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci spin_lock_irqsave(&pru->rmw_lock, flags); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci val = pru_control_read_reg(pru, reg); 15662306a36Sopenharmony_ci val &= ~mask; 15762306a36Sopenharmony_ci val |= (set & mask); 15862306a36Sopenharmony_ci pru_control_write_reg(pru, reg, val); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci spin_unlock_irqrestore(&pru->rmw_lock, flags); 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci/** 16462306a36Sopenharmony_ci * pru_rproc_set_firmware() - set firmware for a PRU core 16562306a36Sopenharmony_ci * @rproc: the rproc instance of the PRU 16662306a36Sopenharmony_ci * @fw_name: the new firmware name, or NULL if default is desired 16762306a36Sopenharmony_ci * 16862306a36Sopenharmony_ci * Return: 0 on success, or errno in error case. 16962306a36Sopenharmony_ci */ 17062306a36Sopenharmony_cistatic int pru_rproc_set_firmware(struct rproc *rproc, const char *fw_name) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci struct pru_rproc *pru = rproc->priv; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (!fw_name) 17562306a36Sopenharmony_ci fw_name = pru->fw_name; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci return rproc_set_firmware(rproc, fw_name); 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic struct rproc *__pru_rproc_get(struct device_node *np, int index) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci struct rproc *rproc; 18362306a36Sopenharmony_ci phandle rproc_phandle; 18462306a36Sopenharmony_ci int ret; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci ret = of_property_read_u32_index(np, "ti,prus", index, &rproc_phandle); 18762306a36Sopenharmony_ci if (ret) 18862306a36Sopenharmony_ci return ERR_PTR(ret); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci rproc = rproc_get_by_phandle(rproc_phandle); 19162306a36Sopenharmony_ci if (!rproc) { 19262306a36Sopenharmony_ci ret = -EPROBE_DEFER; 19362306a36Sopenharmony_ci return ERR_PTR(ret); 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci /* make sure it is PRU rproc */ 19762306a36Sopenharmony_ci if (!is_pru_rproc(rproc->dev.parent)) { 19862306a36Sopenharmony_ci rproc_put(rproc); 19962306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci return rproc; 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci/** 20662306a36Sopenharmony_ci * pru_rproc_get() - get the PRU rproc instance from a device node 20762306a36Sopenharmony_ci * @np: the user/client device node 20862306a36Sopenharmony_ci * @index: index to use for the ti,prus property 20962306a36Sopenharmony_ci * @pru_id: optional pointer to return the PRU remoteproc processor id 21062306a36Sopenharmony_ci * 21162306a36Sopenharmony_ci * This function looks through a client device node's "ti,prus" property at 21262306a36Sopenharmony_ci * index @index and returns the rproc handle for a valid PRU remote processor if 21362306a36Sopenharmony_ci * found. The function allows only one user to own the PRU rproc resource at a 21462306a36Sopenharmony_ci * time. Caller must call pru_rproc_put() when done with using the rproc, not 21562306a36Sopenharmony_ci * required if the function returns a failure. 21662306a36Sopenharmony_ci * 21762306a36Sopenharmony_ci * When optional @pru_id pointer is passed the PRU remoteproc processor id is 21862306a36Sopenharmony_ci * returned. 21962306a36Sopenharmony_ci * 22062306a36Sopenharmony_ci * Return: rproc handle on success, and an ERR_PTR on failure using one 22162306a36Sopenharmony_ci * of the following error values 22262306a36Sopenharmony_ci * -ENODEV if device is not found 22362306a36Sopenharmony_ci * -EBUSY if PRU is already acquired by anyone 22462306a36Sopenharmony_ci * -EPROBE_DEFER is PRU device is not probed yet 22562306a36Sopenharmony_ci */ 22662306a36Sopenharmony_cistruct rproc *pru_rproc_get(struct device_node *np, int index, 22762306a36Sopenharmony_ci enum pruss_pru_id *pru_id) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci struct rproc *rproc; 23062306a36Sopenharmony_ci struct pru_rproc *pru; 23162306a36Sopenharmony_ci struct device *dev; 23262306a36Sopenharmony_ci const char *fw_name; 23362306a36Sopenharmony_ci int ret; 23462306a36Sopenharmony_ci u32 mux; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci rproc = __pru_rproc_get(np, index); 23762306a36Sopenharmony_ci if (IS_ERR(rproc)) 23862306a36Sopenharmony_ci return rproc; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci pru = rproc->priv; 24162306a36Sopenharmony_ci dev = &rproc->dev; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci mutex_lock(&pru->lock); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci if (pru->client_np) { 24662306a36Sopenharmony_ci mutex_unlock(&pru->lock); 24762306a36Sopenharmony_ci ret = -EBUSY; 24862306a36Sopenharmony_ci goto err_no_rproc_handle; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci pru->client_np = np; 25262306a36Sopenharmony_ci rproc->sysfs_read_only = true; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci mutex_unlock(&pru->lock); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (pru_id) 25762306a36Sopenharmony_ci *pru_id = pru->id; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci ret = pruss_cfg_get_gpmux(pru->pruss, pru->id, &pru->gpmux_save); 26062306a36Sopenharmony_ci if (ret) { 26162306a36Sopenharmony_ci dev_err(dev, "failed to get cfg gpmux: %d\n", ret); 26262306a36Sopenharmony_ci goto err; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci /* An error here is acceptable for backward compatibility */ 26662306a36Sopenharmony_ci ret = of_property_read_u32_index(np, "ti,pruss-gp-mux-sel", index, 26762306a36Sopenharmony_ci &mux); 26862306a36Sopenharmony_ci if (!ret) { 26962306a36Sopenharmony_ci ret = pruss_cfg_set_gpmux(pru->pruss, pru->id, mux); 27062306a36Sopenharmony_ci if (ret) { 27162306a36Sopenharmony_ci dev_err(dev, "failed to set cfg gpmux: %d\n", ret); 27262306a36Sopenharmony_ci goto err; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci ret = of_property_read_string_index(np, "firmware-name", index, 27762306a36Sopenharmony_ci &fw_name); 27862306a36Sopenharmony_ci if (!ret) { 27962306a36Sopenharmony_ci ret = pru_rproc_set_firmware(rproc, fw_name); 28062306a36Sopenharmony_ci if (ret) { 28162306a36Sopenharmony_ci dev_err(dev, "failed to set firmware: %d\n", ret); 28262306a36Sopenharmony_ci goto err; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci return rproc; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cierr_no_rproc_handle: 28962306a36Sopenharmony_ci rproc_put(rproc); 29062306a36Sopenharmony_ci return ERR_PTR(ret); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cierr: 29362306a36Sopenharmony_ci pru_rproc_put(rproc); 29462306a36Sopenharmony_ci return ERR_PTR(ret); 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pru_rproc_get); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci/** 29962306a36Sopenharmony_ci * pru_rproc_put() - release the PRU rproc resource 30062306a36Sopenharmony_ci * @rproc: the rproc resource to release 30162306a36Sopenharmony_ci * 30262306a36Sopenharmony_ci * Releases the PRU rproc resource and makes it available to other 30362306a36Sopenharmony_ci * users. 30462306a36Sopenharmony_ci */ 30562306a36Sopenharmony_civoid pru_rproc_put(struct rproc *rproc) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci struct pru_rproc *pru; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci if (IS_ERR_OR_NULL(rproc) || !is_pru_rproc(rproc->dev.parent)) 31062306a36Sopenharmony_ci return; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci pru = rproc->priv; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci pruss_cfg_set_gpmux(pru->pruss, pru->id, pru->gpmux_save); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci pru_rproc_set_firmware(rproc, NULL); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci mutex_lock(&pru->lock); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci if (!pru->client_np) { 32162306a36Sopenharmony_ci mutex_unlock(&pru->lock); 32262306a36Sopenharmony_ci return; 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci pru->client_np = NULL; 32662306a36Sopenharmony_ci rproc->sysfs_read_only = false; 32762306a36Sopenharmony_ci mutex_unlock(&pru->lock); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci rproc_put(rproc); 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pru_rproc_put); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci/** 33462306a36Sopenharmony_ci * pru_rproc_set_ctable() - set the constant table index for the PRU 33562306a36Sopenharmony_ci * @rproc: the rproc instance of the PRU 33662306a36Sopenharmony_ci * @c: constant table index to set 33762306a36Sopenharmony_ci * @addr: physical address to set it to 33862306a36Sopenharmony_ci * 33962306a36Sopenharmony_ci * Return: 0 on success, or errno in error case. 34062306a36Sopenharmony_ci */ 34162306a36Sopenharmony_ciint pru_rproc_set_ctable(struct rproc *rproc, enum pru_ctable_idx c, u32 addr) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci struct pru_rproc *pru = rproc->priv; 34462306a36Sopenharmony_ci unsigned int reg; 34562306a36Sopenharmony_ci u32 mask, set; 34662306a36Sopenharmony_ci u16 idx; 34762306a36Sopenharmony_ci u16 idx_mask; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci if (IS_ERR_OR_NULL(rproc)) 35062306a36Sopenharmony_ci return -EINVAL; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci if (!rproc->dev.parent || !is_pru_rproc(rproc->dev.parent)) 35362306a36Sopenharmony_ci return -ENODEV; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci /* pointer is 16 bit and index is 8-bit so mask out the rest */ 35662306a36Sopenharmony_ci idx_mask = (c >= PRU_C28) ? 0xFFFF : 0xFF; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci /* ctable uses bit 8 and upwards only */ 35962306a36Sopenharmony_ci idx = (addr >> 8) & idx_mask; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci /* configurable ctable (i.e. C24) starts at PRU_CTRL_CTBIR0 */ 36262306a36Sopenharmony_ci reg = PRU_CTRL_CTBIR0 + 4 * (c >> 1); 36362306a36Sopenharmony_ci mask = idx_mask << (16 * (c & 1)); 36462306a36Sopenharmony_ci set = idx << (16 * (c & 1)); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci pru_control_set_reg(pru, reg, mask, set); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci return 0; 36962306a36Sopenharmony_ci} 37062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pru_rproc_set_ctable); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic inline u32 pru_debug_read_reg(struct pru_rproc *pru, unsigned int reg) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci return readl_relaxed(pru->mem_regions[PRU_IOMEM_DEBUG].va + reg); 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cistatic int regs_show(struct seq_file *s, void *data) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci struct rproc *rproc = s->private; 38062306a36Sopenharmony_ci struct pru_rproc *pru = rproc->priv; 38162306a36Sopenharmony_ci int i, nregs = 32; 38262306a36Sopenharmony_ci u32 pru_sts; 38362306a36Sopenharmony_ci int pru_is_running; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci seq_puts(s, "============== Control Registers ==============\n"); 38662306a36Sopenharmony_ci seq_printf(s, "CTRL := 0x%08x\n", 38762306a36Sopenharmony_ci pru_control_read_reg(pru, PRU_CTRL_CTRL)); 38862306a36Sopenharmony_ci pru_sts = pru_control_read_reg(pru, PRU_CTRL_STS); 38962306a36Sopenharmony_ci seq_printf(s, "STS (PC) := 0x%08x (0x%08x)\n", pru_sts, pru_sts << 2); 39062306a36Sopenharmony_ci seq_printf(s, "WAKEUP_EN := 0x%08x\n", 39162306a36Sopenharmony_ci pru_control_read_reg(pru, PRU_CTRL_WAKEUP_EN)); 39262306a36Sopenharmony_ci seq_printf(s, "CYCLE := 0x%08x\n", 39362306a36Sopenharmony_ci pru_control_read_reg(pru, PRU_CTRL_CYCLE)); 39462306a36Sopenharmony_ci seq_printf(s, "STALL := 0x%08x\n", 39562306a36Sopenharmony_ci pru_control_read_reg(pru, PRU_CTRL_STALL)); 39662306a36Sopenharmony_ci seq_printf(s, "CTBIR0 := 0x%08x\n", 39762306a36Sopenharmony_ci pru_control_read_reg(pru, PRU_CTRL_CTBIR0)); 39862306a36Sopenharmony_ci seq_printf(s, "CTBIR1 := 0x%08x\n", 39962306a36Sopenharmony_ci pru_control_read_reg(pru, PRU_CTRL_CTBIR1)); 40062306a36Sopenharmony_ci seq_printf(s, "CTPPR0 := 0x%08x\n", 40162306a36Sopenharmony_ci pru_control_read_reg(pru, PRU_CTRL_CTPPR0)); 40262306a36Sopenharmony_ci seq_printf(s, "CTPPR1 := 0x%08x\n", 40362306a36Sopenharmony_ci pru_control_read_reg(pru, PRU_CTRL_CTPPR1)); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci seq_puts(s, "=============== Debug Registers ===============\n"); 40662306a36Sopenharmony_ci pru_is_running = pru_control_read_reg(pru, PRU_CTRL_CTRL) & 40762306a36Sopenharmony_ci CTRL_CTRL_RUNSTATE; 40862306a36Sopenharmony_ci if (pru_is_running) { 40962306a36Sopenharmony_ci seq_puts(s, "PRU is executing, cannot print/access debug registers.\n"); 41062306a36Sopenharmony_ci return 0; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci for (i = 0; i < nregs; i++) { 41462306a36Sopenharmony_ci seq_printf(s, "GPREG%-2d := 0x%08x\tCT_REG%-2d := 0x%08x\n", 41562306a36Sopenharmony_ci i, pru_debug_read_reg(pru, PRU_DEBUG_GPREG(i)), 41662306a36Sopenharmony_ci i, pru_debug_read_reg(pru, PRU_DEBUG_CT_REG(i))); 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci return 0; 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(regs); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci/* 42462306a36Sopenharmony_ci * Control PRU single-step mode 42562306a36Sopenharmony_ci * 42662306a36Sopenharmony_ci * This is a debug helper function used for controlling the single-step 42762306a36Sopenharmony_ci * mode of the PRU. The PRU Debug registers are not accessible when the 42862306a36Sopenharmony_ci * PRU is in RUNNING state. 42962306a36Sopenharmony_ci * 43062306a36Sopenharmony_ci * Writing a non-zero value sets the PRU into single-step mode irrespective 43162306a36Sopenharmony_ci * of its previous state. The PRU mode is saved only on the first set into 43262306a36Sopenharmony_ci * a single-step mode. Writing a zero value will restore the PRU into its 43362306a36Sopenharmony_ci * original mode. 43462306a36Sopenharmony_ci */ 43562306a36Sopenharmony_cistatic int pru_rproc_debug_ss_set(void *data, u64 val) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci struct rproc *rproc = data; 43862306a36Sopenharmony_ci struct pru_rproc *pru = rproc->priv; 43962306a36Sopenharmony_ci u32 reg_val; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci val = val ? 1 : 0; 44262306a36Sopenharmony_ci if (!val && !pru->dbg_single_step) 44362306a36Sopenharmony_ci return 0; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci reg_val = pru_control_read_reg(pru, PRU_CTRL_CTRL); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci if (val && !pru->dbg_single_step) 44862306a36Sopenharmony_ci pru->dbg_continuous = reg_val; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci if (val) 45162306a36Sopenharmony_ci reg_val |= CTRL_CTRL_SINGLE_STEP | CTRL_CTRL_EN; 45262306a36Sopenharmony_ci else 45362306a36Sopenharmony_ci reg_val = pru->dbg_continuous; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci pru->dbg_single_step = val; 45662306a36Sopenharmony_ci pru_control_write_reg(pru, PRU_CTRL_CTRL, reg_val); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci return 0; 45962306a36Sopenharmony_ci} 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_cistatic int pru_rproc_debug_ss_get(void *data, u64 *val) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci struct rproc *rproc = data; 46462306a36Sopenharmony_ci struct pru_rproc *pru = rproc->priv; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci *val = pru->dbg_single_step; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci return 0; 46962306a36Sopenharmony_ci} 47062306a36Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(pru_rproc_debug_ss_fops, pru_rproc_debug_ss_get, 47162306a36Sopenharmony_ci pru_rproc_debug_ss_set, "%llu\n"); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci/* 47462306a36Sopenharmony_ci * Create PRU-specific debugfs entries 47562306a36Sopenharmony_ci * 47662306a36Sopenharmony_ci * The entries are created only if the parent remoteproc debugfs directory 47762306a36Sopenharmony_ci * exists, and will be cleaned up by the remoteproc core. 47862306a36Sopenharmony_ci */ 47962306a36Sopenharmony_cistatic void pru_rproc_create_debug_entries(struct rproc *rproc) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci if (!rproc->dbg_dir) 48262306a36Sopenharmony_ci return; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci debugfs_create_file("regs", 0400, rproc->dbg_dir, 48562306a36Sopenharmony_ci rproc, ®s_fops); 48662306a36Sopenharmony_ci debugfs_create_file("single_step", 0600, rproc->dbg_dir, 48762306a36Sopenharmony_ci rproc, &pru_rproc_debug_ss_fops); 48862306a36Sopenharmony_ci} 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_cistatic void pru_dispose_irq_mapping(struct pru_rproc *pru) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci if (!pru->mapped_irq) 49362306a36Sopenharmony_ci return; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci while (pru->evt_count) { 49662306a36Sopenharmony_ci pru->evt_count--; 49762306a36Sopenharmony_ci if (pru->mapped_irq[pru->evt_count] > 0) 49862306a36Sopenharmony_ci irq_dispose_mapping(pru->mapped_irq[pru->evt_count]); 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci kfree(pru->mapped_irq); 50262306a36Sopenharmony_ci pru->mapped_irq = NULL; 50362306a36Sopenharmony_ci} 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci/* 50662306a36Sopenharmony_ci * Parse the custom PRU interrupt map resource and configure the INTC 50762306a36Sopenharmony_ci * appropriately. 50862306a36Sopenharmony_ci */ 50962306a36Sopenharmony_cistatic int pru_handle_intrmap(struct rproc *rproc) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci struct device *dev = rproc->dev.parent; 51262306a36Sopenharmony_ci struct pru_rproc *pru = rproc->priv; 51362306a36Sopenharmony_ci struct pru_irq_rsc *rsc = pru->pru_interrupt_map; 51462306a36Sopenharmony_ci struct irq_fwspec fwspec; 51562306a36Sopenharmony_ci struct device_node *parent, *irq_parent; 51662306a36Sopenharmony_ci int i, ret = 0; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci /* not having pru_interrupt_map is not an error */ 51962306a36Sopenharmony_ci if (!rsc) 52062306a36Sopenharmony_ci return 0; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci /* currently supporting only type 0 */ 52362306a36Sopenharmony_ci if (rsc->type != 0) { 52462306a36Sopenharmony_ci dev_err(dev, "unsupported rsc type: %d\n", rsc->type); 52562306a36Sopenharmony_ci return -EINVAL; 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci if (rsc->num_evts > MAX_PRU_SYS_EVENTS) 52962306a36Sopenharmony_ci return -EINVAL; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci if (sizeof(*rsc) + rsc->num_evts * sizeof(struct pruss_int_map) != 53262306a36Sopenharmony_ci pru->pru_interrupt_map_sz) 53362306a36Sopenharmony_ci return -EINVAL; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci pru->evt_count = rsc->num_evts; 53662306a36Sopenharmony_ci pru->mapped_irq = kcalloc(pru->evt_count, sizeof(unsigned int), 53762306a36Sopenharmony_ci GFP_KERNEL); 53862306a36Sopenharmony_ci if (!pru->mapped_irq) { 53962306a36Sopenharmony_ci pru->evt_count = 0; 54062306a36Sopenharmony_ci return -ENOMEM; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci /* 54462306a36Sopenharmony_ci * parse and fill in system event to interrupt channel and 54562306a36Sopenharmony_ci * channel-to-host mapping. The interrupt controller to be used 54662306a36Sopenharmony_ci * for these mappings for a given PRU remoteproc is always its 54762306a36Sopenharmony_ci * corresponding sibling PRUSS INTC node. 54862306a36Sopenharmony_ci */ 54962306a36Sopenharmony_ci parent = of_get_parent(dev_of_node(pru->dev)); 55062306a36Sopenharmony_ci if (!parent) { 55162306a36Sopenharmony_ci kfree(pru->mapped_irq); 55262306a36Sopenharmony_ci pru->mapped_irq = NULL; 55362306a36Sopenharmony_ci pru->evt_count = 0; 55462306a36Sopenharmony_ci return -ENODEV; 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci irq_parent = of_get_child_by_name(parent, "interrupt-controller"); 55862306a36Sopenharmony_ci of_node_put(parent); 55962306a36Sopenharmony_ci if (!irq_parent) { 56062306a36Sopenharmony_ci kfree(pru->mapped_irq); 56162306a36Sopenharmony_ci pru->mapped_irq = NULL; 56262306a36Sopenharmony_ci pru->evt_count = 0; 56362306a36Sopenharmony_ci return -ENODEV; 56462306a36Sopenharmony_ci } 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci fwspec.fwnode = of_node_to_fwnode(irq_parent); 56762306a36Sopenharmony_ci fwspec.param_count = 3; 56862306a36Sopenharmony_ci for (i = 0; i < pru->evt_count; i++) { 56962306a36Sopenharmony_ci fwspec.param[0] = rsc->pru_intc_map[i].event; 57062306a36Sopenharmony_ci fwspec.param[1] = rsc->pru_intc_map[i].chnl; 57162306a36Sopenharmony_ci fwspec.param[2] = rsc->pru_intc_map[i].host; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci dev_dbg(dev, "mapping%d: event %d, chnl %d, host %d\n", 57462306a36Sopenharmony_ci i, fwspec.param[0], fwspec.param[1], fwspec.param[2]); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci pru->mapped_irq[i] = irq_create_fwspec_mapping(&fwspec); 57762306a36Sopenharmony_ci if (!pru->mapped_irq[i]) { 57862306a36Sopenharmony_ci dev_err(dev, "failed to get virq for fw mapping %d: event %d chnl %d host %d\n", 57962306a36Sopenharmony_ci i, fwspec.param[0], fwspec.param[1], 58062306a36Sopenharmony_ci fwspec.param[2]); 58162306a36Sopenharmony_ci ret = -EINVAL; 58262306a36Sopenharmony_ci goto map_fail; 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci of_node_put(irq_parent); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci return ret; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_cimap_fail: 59062306a36Sopenharmony_ci pru_dispose_irq_mapping(pru); 59162306a36Sopenharmony_ci of_node_put(irq_parent); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci return ret; 59462306a36Sopenharmony_ci} 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_cistatic int pru_rproc_start(struct rproc *rproc) 59762306a36Sopenharmony_ci{ 59862306a36Sopenharmony_ci struct device *dev = &rproc->dev; 59962306a36Sopenharmony_ci struct pru_rproc *pru = rproc->priv; 60062306a36Sopenharmony_ci const char *names[PRU_TYPE_MAX] = { "PRU", "RTU", "Tx_PRU" }; 60162306a36Sopenharmony_ci u32 val; 60262306a36Sopenharmony_ci int ret; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci dev_dbg(dev, "starting %s%d: entry-point = 0x%llx\n", 60562306a36Sopenharmony_ci names[pru->data->type], pru->id, (rproc->bootaddr >> 2)); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci ret = pru_handle_intrmap(rproc); 60862306a36Sopenharmony_ci /* 60962306a36Sopenharmony_ci * reset references to pru interrupt map - they will stop being valid 61062306a36Sopenharmony_ci * after rproc_start returns 61162306a36Sopenharmony_ci */ 61262306a36Sopenharmony_ci pru->pru_interrupt_map = NULL; 61362306a36Sopenharmony_ci pru->pru_interrupt_map_sz = 0; 61462306a36Sopenharmony_ci if (ret) 61562306a36Sopenharmony_ci return ret; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci val = CTRL_CTRL_EN | ((rproc->bootaddr >> 2) << 16); 61862306a36Sopenharmony_ci pru_control_write_reg(pru, PRU_CTRL_CTRL, val); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci return 0; 62162306a36Sopenharmony_ci} 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_cistatic int pru_rproc_stop(struct rproc *rproc) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci struct device *dev = &rproc->dev; 62662306a36Sopenharmony_ci struct pru_rproc *pru = rproc->priv; 62762306a36Sopenharmony_ci const char *names[PRU_TYPE_MAX] = { "PRU", "RTU", "Tx_PRU" }; 62862306a36Sopenharmony_ci u32 val; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci dev_dbg(dev, "stopping %s%d\n", names[pru->data->type], pru->id); 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci val = pru_control_read_reg(pru, PRU_CTRL_CTRL); 63362306a36Sopenharmony_ci val &= ~CTRL_CTRL_EN; 63462306a36Sopenharmony_ci pru_control_write_reg(pru, PRU_CTRL_CTRL, val); 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci /* dispose irq mapping - new firmware can provide new mapping */ 63762306a36Sopenharmony_ci pru_dispose_irq_mapping(pru); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci return 0; 64062306a36Sopenharmony_ci} 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci/* 64362306a36Sopenharmony_ci * Convert PRU device address (data spaces only) to kernel virtual address. 64462306a36Sopenharmony_ci * 64562306a36Sopenharmony_ci * Each PRU has access to all data memories within the PRUSS, accessible at 64662306a36Sopenharmony_ci * different ranges. So, look through both its primary and secondary Data 64762306a36Sopenharmony_ci * RAMs as well as any shared Data RAM to convert a PRU device address to 64862306a36Sopenharmony_ci * kernel virtual address. Data RAM0 is primary Data RAM for PRU0 and Data 64962306a36Sopenharmony_ci * RAM1 is primary Data RAM for PRU1. 65062306a36Sopenharmony_ci */ 65162306a36Sopenharmony_cistatic void *pru_d_da_to_va(struct pru_rproc *pru, u32 da, size_t len) 65262306a36Sopenharmony_ci{ 65362306a36Sopenharmony_ci struct pruss_mem_region dram0, dram1, shrd_ram; 65462306a36Sopenharmony_ci struct pruss *pruss = pru->pruss; 65562306a36Sopenharmony_ci u32 offset; 65662306a36Sopenharmony_ci void *va = NULL; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci if (len == 0) 65962306a36Sopenharmony_ci return NULL; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci dram0 = pruss->mem_regions[PRUSS_MEM_DRAM0]; 66262306a36Sopenharmony_ci dram1 = pruss->mem_regions[PRUSS_MEM_DRAM1]; 66362306a36Sopenharmony_ci /* PRU1 has its local RAM addresses reversed */ 66462306a36Sopenharmony_ci if (pru->id == PRUSS_PRU1) 66562306a36Sopenharmony_ci swap(dram0, dram1); 66662306a36Sopenharmony_ci shrd_ram = pruss->mem_regions[PRUSS_MEM_SHRD_RAM2]; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci if (da + len <= PRU_PDRAM_DA + dram0.size) { 66962306a36Sopenharmony_ci offset = da - PRU_PDRAM_DA; 67062306a36Sopenharmony_ci va = (__force void *)(dram0.va + offset); 67162306a36Sopenharmony_ci } else if (da >= PRU_SDRAM_DA && 67262306a36Sopenharmony_ci da + len <= PRU_SDRAM_DA + dram1.size) { 67362306a36Sopenharmony_ci offset = da - PRU_SDRAM_DA; 67462306a36Sopenharmony_ci va = (__force void *)(dram1.va + offset); 67562306a36Sopenharmony_ci } else if (da >= PRU_SHRDRAM_DA && 67662306a36Sopenharmony_ci da + len <= PRU_SHRDRAM_DA + shrd_ram.size) { 67762306a36Sopenharmony_ci offset = da - PRU_SHRDRAM_DA; 67862306a36Sopenharmony_ci va = (__force void *)(shrd_ram.va + offset); 67962306a36Sopenharmony_ci } 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci return va; 68262306a36Sopenharmony_ci} 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci/* 68562306a36Sopenharmony_ci * Convert PRU device address (instruction space) to kernel virtual address. 68662306a36Sopenharmony_ci * 68762306a36Sopenharmony_ci * A PRU does not have an unified address space. Each PRU has its very own 68862306a36Sopenharmony_ci * private Instruction RAM, and its device address is identical to that of 68962306a36Sopenharmony_ci * its primary Data RAM device address. 69062306a36Sopenharmony_ci */ 69162306a36Sopenharmony_cistatic void *pru_i_da_to_va(struct pru_rproc *pru, u32 da, size_t len) 69262306a36Sopenharmony_ci{ 69362306a36Sopenharmony_ci u32 offset; 69462306a36Sopenharmony_ci void *va = NULL; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci if (len == 0) 69762306a36Sopenharmony_ci return NULL; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci /* 70062306a36Sopenharmony_ci * GNU binutils do not support multiple address spaces. The GNU 70162306a36Sopenharmony_ci * linker's default linker script places IRAM at an arbitrary high 70262306a36Sopenharmony_ci * offset, in order to differentiate it from DRAM. Hence we need to 70362306a36Sopenharmony_ci * strip the artificial offset in the IRAM addresses coming from the 70462306a36Sopenharmony_ci * ELF file. 70562306a36Sopenharmony_ci * 70662306a36Sopenharmony_ci * The TI proprietary linker would never set those higher IRAM address 70762306a36Sopenharmony_ci * bits anyway. PRU architecture limits the program counter to 16-bit 70862306a36Sopenharmony_ci * word-address range. This in turn corresponds to 18-bit IRAM 70962306a36Sopenharmony_ci * byte-address range for ELF. 71062306a36Sopenharmony_ci * 71162306a36Sopenharmony_ci * Two more bits are added just in case to make the final 20-bit mask. 71262306a36Sopenharmony_ci * Idea is to have a safeguard in case TI decides to add banking 71362306a36Sopenharmony_ci * in future SoCs. 71462306a36Sopenharmony_ci */ 71562306a36Sopenharmony_ci da &= 0xfffff; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci if (da + len <= PRU_IRAM_DA + pru->mem_regions[PRU_IOMEM_IRAM].size) { 71862306a36Sopenharmony_ci offset = da - PRU_IRAM_DA; 71962306a36Sopenharmony_ci va = (__force void *)(pru->mem_regions[PRU_IOMEM_IRAM].va + 72062306a36Sopenharmony_ci offset); 72162306a36Sopenharmony_ci } 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci return va; 72462306a36Sopenharmony_ci} 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci/* 72762306a36Sopenharmony_ci * Provide address translations for only PRU Data RAMs through the remoteproc 72862306a36Sopenharmony_ci * core for any PRU client drivers. The PRU Instruction RAM access is restricted 72962306a36Sopenharmony_ci * only to the PRU loader code. 73062306a36Sopenharmony_ci */ 73162306a36Sopenharmony_cistatic void *pru_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem) 73262306a36Sopenharmony_ci{ 73362306a36Sopenharmony_ci struct pru_rproc *pru = rproc->priv; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci return pru_d_da_to_va(pru, da, len); 73662306a36Sopenharmony_ci} 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci/* PRU-specific address translator used by PRU loader. */ 73962306a36Sopenharmony_cistatic void *pru_da_to_va(struct rproc *rproc, u64 da, size_t len, bool is_iram) 74062306a36Sopenharmony_ci{ 74162306a36Sopenharmony_ci struct pru_rproc *pru = rproc->priv; 74262306a36Sopenharmony_ci void *va; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci if (is_iram) 74562306a36Sopenharmony_ci va = pru_i_da_to_va(pru, da, len); 74662306a36Sopenharmony_ci else 74762306a36Sopenharmony_ci va = pru_d_da_to_va(pru, da, len); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci return va; 75062306a36Sopenharmony_ci} 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_cistatic struct rproc_ops pru_rproc_ops = { 75362306a36Sopenharmony_ci .start = pru_rproc_start, 75462306a36Sopenharmony_ci .stop = pru_rproc_stop, 75562306a36Sopenharmony_ci .da_to_va = pru_rproc_da_to_va, 75662306a36Sopenharmony_ci}; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci/* 75962306a36Sopenharmony_ci * Custom memory copy implementation for ICSSG PRU/RTU/Tx_PRU Cores 76062306a36Sopenharmony_ci * 76162306a36Sopenharmony_ci * The ICSSG PRU/RTU/Tx_PRU cores have a memory copying issue with IRAM 76262306a36Sopenharmony_ci * memories, that is not seen on previous generation SoCs. The data is reflected 76362306a36Sopenharmony_ci * properly in the IRAM memories only for integer (4-byte) copies. Any unaligned 76462306a36Sopenharmony_ci * copies result in all the other pre-existing bytes zeroed out within that 76562306a36Sopenharmony_ci * 4-byte boundary, thereby resulting in wrong text/code in the IRAMs. Also, the 76662306a36Sopenharmony_ci * IRAM memory port interface does not allow any 8-byte copies (as commonly used 76762306a36Sopenharmony_ci * by ARM64 memcpy implementation) and throws an exception. The DRAM memory 76862306a36Sopenharmony_ci * ports do not show this behavior. 76962306a36Sopenharmony_ci */ 77062306a36Sopenharmony_cistatic int pru_rproc_memcpy(void *dest, const void *src, size_t count) 77162306a36Sopenharmony_ci{ 77262306a36Sopenharmony_ci const u32 *s = src; 77362306a36Sopenharmony_ci u32 *d = dest; 77462306a36Sopenharmony_ci size_t size = count / 4; 77562306a36Sopenharmony_ci u32 *tmp_src = NULL; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci /* 77862306a36Sopenharmony_ci * TODO: relax limitation of 4-byte aligned dest addresses and copy 77962306a36Sopenharmony_ci * sizes 78062306a36Sopenharmony_ci */ 78162306a36Sopenharmony_ci if ((long)dest % 4 || count % 4) 78262306a36Sopenharmony_ci return -EINVAL; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci /* src offsets in ELF firmware image can be non-aligned */ 78562306a36Sopenharmony_ci if ((long)src % 4) { 78662306a36Sopenharmony_ci tmp_src = kmemdup(src, count, GFP_KERNEL); 78762306a36Sopenharmony_ci if (!tmp_src) 78862306a36Sopenharmony_ci return -ENOMEM; 78962306a36Sopenharmony_ci s = tmp_src; 79062306a36Sopenharmony_ci } 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci while (size--) 79362306a36Sopenharmony_ci *d++ = *s++; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci kfree(tmp_src); 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci return 0; 79862306a36Sopenharmony_ci} 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_cistatic int 80162306a36Sopenharmony_cipru_rproc_load_elf_segments(struct rproc *rproc, const struct firmware *fw) 80262306a36Sopenharmony_ci{ 80362306a36Sopenharmony_ci struct pru_rproc *pru = rproc->priv; 80462306a36Sopenharmony_ci struct device *dev = &rproc->dev; 80562306a36Sopenharmony_ci struct elf32_hdr *ehdr; 80662306a36Sopenharmony_ci struct elf32_phdr *phdr; 80762306a36Sopenharmony_ci int i, ret = 0; 80862306a36Sopenharmony_ci const u8 *elf_data = fw->data; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci ehdr = (struct elf32_hdr *)elf_data; 81162306a36Sopenharmony_ci phdr = (struct elf32_phdr *)(elf_data + ehdr->e_phoff); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci /* go through the available ELF segments */ 81462306a36Sopenharmony_ci for (i = 0; i < ehdr->e_phnum; i++, phdr++) { 81562306a36Sopenharmony_ci u32 da = phdr->p_paddr; 81662306a36Sopenharmony_ci u32 memsz = phdr->p_memsz; 81762306a36Sopenharmony_ci u32 filesz = phdr->p_filesz; 81862306a36Sopenharmony_ci u32 offset = phdr->p_offset; 81962306a36Sopenharmony_ci bool is_iram; 82062306a36Sopenharmony_ci void *ptr; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci if (phdr->p_type != PT_LOAD || !filesz) 82362306a36Sopenharmony_ci continue; 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci dev_dbg(dev, "phdr: type %d da 0x%x memsz 0x%x filesz 0x%x\n", 82662306a36Sopenharmony_ci phdr->p_type, da, memsz, filesz); 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci if (filesz > memsz) { 82962306a36Sopenharmony_ci dev_err(dev, "bad phdr filesz 0x%x memsz 0x%x\n", 83062306a36Sopenharmony_ci filesz, memsz); 83162306a36Sopenharmony_ci ret = -EINVAL; 83262306a36Sopenharmony_ci break; 83362306a36Sopenharmony_ci } 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci if (offset + filesz > fw->size) { 83662306a36Sopenharmony_ci dev_err(dev, "truncated fw: need 0x%x avail 0x%zx\n", 83762306a36Sopenharmony_ci offset + filesz, fw->size); 83862306a36Sopenharmony_ci ret = -EINVAL; 83962306a36Sopenharmony_ci break; 84062306a36Sopenharmony_ci } 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci /* grab the kernel address for this device address */ 84362306a36Sopenharmony_ci is_iram = phdr->p_flags & PF_X; 84462306a36Sopenharmony_ci ptr = pru_da_to_va(rproc, da, memsz, is_iram); 84562306a36Sopenharmony_ci if (!ptr) { 84662306a36Sopenharmony_ci dev_err(dev, "bad phdr da 0x%x mem 0x%x\n", da, memsz); 84762306a36Sopenharmony_ci ret = -EINVAL; 84862306a36Sopenharmony_ci break; 84962306a36Sopenharmony_ci } 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci if (pru->data->is_k3) { 85262306a36Sopenharmony_ci ret = pru_rproc_memcpy(ptr, elf_data + phdr->p_offset, 85362306a36Sopenharmony_ci filesz); 85462306a36Sopenharmony_ci if (ret) { 85562306a36Sopenharmony_ci dev_err(dev, "PRU memory copy failed for da 0x%x memsz 0x%x\n", 85662306a36Sopenharmony_ci da, memsz); 85762306a36Sopenharmony_ci break; 85862306a36Sopenharmony_ci } 85962306a36Sopenharmony_ci } else { 86062306a36Sopenharmony_ci memcpy(ptr, elf_data + phdr->p_offset, filesz); 86162306a36Sopenharmony_ci } 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci /* skip the memzero logic performed by remoteproc ELF loader */ 86462306a36Sopenharmony_ci } 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci return ret; 86762306a36Sopenharmony_ci} 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_cistatic const void * 87062306a36Sopenharmony_cipru_rproc_find_interrupt_map(struct device *dev, const struct firmware *fw) 87162306a36Sopenharmony_ci{ 87262306a36Sopenharmony_ci struct elf32_shdr *shdr, *name_table_shdr; 87362306a36Sopenharmony_ci const char *name_table; 87462306a36Sopenharmony_ci const u8 *elf_data = fw->data; 87562306a36Sopenharmony_ci struct elf32_hdr *ehdr = (struct elf32_hdr *)elf_data; 87662306a36Sopenharmony_ci u16 shnum = ehdr->e_shnum; 87762306a36Sopenharmony_ci u16 shstrndx = ehdr->e_shstrndx; 87862306a36Sopenharmony_ci int i; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci /* first, get the section header */ 88162306a36Sopenharmony_ci shdr = (struct elf32_shdr *)(elf_data + ehdr->e_shoff); 88262306a36Sopenharmony_ci /* compute name table section header entry in shdr array */ 88362306a36Sopenharmony_ci name_table_shdr = shdr + shstrndx; 88462306a36Sopenharmony_ci /* finally, compute the name table section address in elf */ 88562306a36Sopenharmony_ci name_table = elf_data + name_table_shdr->sh_offset; 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci for (i = 0; i < shnum; i++, shdr++) { 88862306a36Sopenharmony_ci u32 size = shdr->sh_size; 88962306a36Sopenharmony_ci u32 offset = shdr->sh_offset; 89062306a36Sopenharmony_ci u32 name = shdr->sh_name; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci if (strcmp(name_table + name, ".pru_irq_map")) 89362306a36Sopenharmony_ci continue; 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci /* make sure we have the entire irq map */ 89662306a36Sopenharmony_ci if (offset + size > fw->size || offset + size < size) { 89762306a36Sopenharmony_ci dev_err(dev, ".pru_irq_map section truncated\n"); 89862306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci /* make sure irq map has at least the header */ 90262306a36Sopenharmony_ci if (sizeof(struct pru_irq_rsc) > size) { 90362306a36Sopenharmony_ci dev_err(dev, "header-less .pru_irq_map section\n"); 90462306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 90562306a36Sopenharmony_ci } 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci return shdr; 90862306a36Sopenharmony_ci } 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci dev_dbg(dev, "no .pru_irq_map section found for this fw\n"); 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci return NULL; 91362306a36Sopenharmony_ci} 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci/* 91662306a36Sopenharmony_ci * Use a custom parse_fw callback function for dealing with PRU firmware 91762306a36Sopenharmony_ci * specific sections. 91862306a36Sopenharmony_ci * 91962306a36Sopenharmony_ci * The firmware blob can contain optional ELF sections: .resource_table section 92062306a36Sopenharmony_ci * and .pru_irq_map one. The second one contains the PRUSS interrupt mapping 92162306a36Sopenharmony_ci * description, which needs to be setup before powering on the PRU core. To 92262306a36Sopenharmony_ci * avoid RAM wastage this ELF section is not mapped to any ELF segment (by the 92362306a36Sopenharmony_ci * firmware linker) and therefore is not loaded to PRU memory. 92462306a36Sopenharmony_ci */ 92562306a36Sopenharmony_cistatic int pru_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) 92662306a36Sopenharmony_ci{ 92762306a36Sopenharmony_ci struct device *dev = &rproc->dev; 92862306a36Sopenharmony_ci struct pru_rproc *pru = rproc->priv; 92962306a36Sopenharmony_ci const u8 *elf_data = fw->data; 93062306a36Sopenharmony_ci const void *shdr; 93162306a36Sopenharmony_ci u8 class = fw_elf_get_class(fw); 93262306a36Sopenharmony_ci u64 sh_offset; 93362306a36Sopenharmony_ci int ret; 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci /* load optional rsc table */ 93662306a36Sopenharmony_ci ret = rproc_elf_load_rsc_table(rproc, fw); 93762306a36Sopenharmony_ci if (ret == -EINVAL) 93862306a36Sopenharmony_ci dev_dbg(&rproc->dev, "no resource table found for this fw\n"); 93962306a36Sopenharmony_ci else if (ret) 94062306a36Sopenharmony_ci return ret; 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci /* find .pru_interrupt_map section, not having it is not an error */ 94362306a36Sopenharmony_ci shdr = pru_rproc_find_interrupt_map(dev, fw); 94462306a36Sopenharmony_ci if (IS_ERR(shdr)) 94562306a36Sopenharmony_ci return PTR_ERR(shdr); 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci if (!shdr) 94862306a36Sopenharmony_ci return 0; 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci /* preserve pointer to PRU interrupt map together with it size */ 95162306a36Sopenharmony_ci sh_offset = elf_shdr_get_sh_offset(class, shdr); 95262306a36Sopenharmony_ci pru->pru_interrupt_map = (struct pru_irq_rsc *)(elf_data + sh_offset); 95362306a36Sopenharmony_ci pru->pru_interrupt_map_sz = elf_shdr_get_sh_size(class, shdr); 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci return 0; 95662306a36Sopenharmony_ci} 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci/* 95962306a36Sopenharmony_ci * Compute PRU id based on the IRAM addresses. The PRU IRAMs are 96062306a36Sopenharmony_ci * always at a particular offset within the PRUSS address space. 96162306a36Sopenharmony_ci */ 96262306a36Sopenharmony_cistatic int pru_rproc_set_id(struct pru_rproc *pru) 96362306a36Sopenharmony_ci{ 96462306a36Sopenharmony_ci int ret = 0; 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci switch (pru->mem_regions[PRU_IOMEM_IRAM].pa & PRU_IRAM_ADDR_MASK) { 96762306a36Sopenharmony_ci case TX_PRU0_IRAM_ADDR_MASK: 96862306a36Sopenharmony_ci fallthrough; 96962306a36Sopenharmony_ci case RTU0_IRAM_ADDR_MASK: 97062306a36Sopenharmony_ci fallthrough; 97162306a36Sopenharmony_ci case PRU0_IRAM_ADDR_MASK: 97262306a36Sopenharmony_ci pru->id = PRUSS_PRU0; 97362306a36Sopenharmony_ci break; 97462306a36Sopenharmony_ci case TX_PRU1_IRAM_ADDR_MASK: 97562306a36Sopenharmony_ci fallthrough; 97662306a36Sopenharmony_ci case RTU1_IRAM_ADDR_MASK: 97762306a36Sopenharmony_ci fallthrough; 97862306a36Sopenharmony_ci case PRU1_IRAM_ADDR_MASK: 97962306a36Sopenharmony_ci pru->id = PRUSS_PRU1; 98062306a36Sopenharmony_ci break; 98162306a36Sopenharmony_ci default: 98262306a36Sopenharmony_ci ret = -EINVAL; 98362306a36Sopenharmony_ci } 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci return ret; 98662306a36Sopenharmony_ci} 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_cistatic int pru_rproc_probe(struct platform_device *pdev) 98962306a36Sopenharmony_ci{ 99062306a36Sopenharmony_ci struct device *dev = &pdev->dev; 99162306a36Sopenharmony_ci struct device_node *np = dev->of_node; 99262306a36Sopenharmony_ci struct platform_device *ppdev = to_platform_device(dev->parent); 99362306a36Sopenharmony_ci struct pru_rproc *pru; 99462306a36Sopenharmony_ci const char *fw_name; 99562306a36Sopenharmony_ci struct rproc *rproc = NULL; 99662306a36Sopenharmony_ci struct resource *res; 99762306a36Sopenharmony_ci int i, ret; 99862306a36Sopenharmony_ci const struct pru_private_data *data; 99962306a36Sopenharmony_ci const char *mem_names[PRU_IOMEM_MAX] = { "iram", "control", "debug" }; 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci data = of_device_get_match_data(&pdev->dev); 100262306a36Sopenharmony_ci if (!data) 100362306a36Sopenharmony_ci return -ENODEV; 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci ret = of_property_read_string(np, "firmware-name", &fw_name); 100662306a36Sopenharmony_ci if (ret) { 100762306a36Sopenharmony_ci dev_err(dev, "unable to retrieve firmware-name %d\n", ret); 100862306a36Sopenharmony_ci return ret; 100962306a36Sopenharmony_ci } 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci rproc = devm_rproc_alloc(dev, pdev->name, &pru_rproc_ops, fw_name, 101262306a36Sopenharmony_ci sizeof(*pru)); 101362306a36Sopenharmony_ci if (!rproc) { 101462306a36Sopenharmony_ci dev_err(dev, "rproc_alloc failed\n"); 101562306a36Sopenharmony_ci return -ENOMEM; 101662306a36Sopenharmony_ci } 101762306a36Sopenharmony_ci /* use a custom load function to deal with PRU-specific quirks */ 101862306a36Sopenharmony_ci rproc->ops->load = pru_rproc_load_elf_segments; 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci /* use a custom parse function to deal with PRU-specific resources */ 102162306a36Sopenharmony_ci rproc->ops->parse_fw = pru_rproc_parse_fw; 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci /* error recovery is not supported for PRUs */ 102462306a36Sopenharmony_ci rproc->recovery_disabled = true; 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci /* 102762306a36Sopenharmony_ci * rproc_add will auto-boot the processor normally, but this is not 102862306a36Sopenharmony_ci * desired with PRU client driven boot-flow methodology. A PRU 102962306a36Sopenharmony_ci * application/client driver will boot the corresponding PRU 103062306a36Sopenharmony_ci * remote-processor as part of its state machine either through the 103162306a36Sopenharmony_ci * remoteproc sysfs interface or through the equivalent kernel API. 103262306a36Sopenharmony_ci */ 103362306a36Sopenharmony_ci rproc->auto_boot = false; 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci pru = rproc->priv; 103662306a36Sopenharmony_ci pru->dev = dev; 103762306a36Sopenharmony_ci pru->data = data; 103862306a36Sopenharmony_ci pru->pruss = platform_get_drvdata(ppdev); 103962306a36Sopenharmony_ci pru->rproc = rproc; 104062306a36Sopenharmony_ci pru->fw_name = fw_name; 104162306a36Sopenharmony_ci pru->client_np = NULL; 104262306a36Sopenharmony_ci spin_lock_init(&pru->rmw_lock); 104362306a36Sopenharmony_ci mutex_init(&pru->lock); 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(mem_names); i++) { 104662306a36Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, 104762306a36Sopenharmony_ci mem_names[i]); 104862306a36Sopenharmony_ci pru->mem_regions[i].va = devm_ioremap_resource(dev, res); 104962306a36Sopenharmony_ci if (IS_ERR(pru->mem_regions[i].va)) { 105062306a36Sopenharmony_ci dev_err(dev, "failed to parse and map memory resource %d %s\n", 105162306a36Sopenharmony_ci i, mem_names[i]); 105262306a36Sopenharmony_ci ret = PTR_ERR(pru->mem_regions[i].va); 105362306a36Sopenharmony_ci return ret; 105462306a36Sopenharmony_ci } 105562306a36Sopenharmony_ci pru->mem_regions[i].pa = res->start; 105662306a36Sopenharmony_ci pru->mem_regions[i].size = resource_size(res); 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci dev_dbg(dev, "memory %8s: pa %pa size 0x%zx va %pK\n", 105962306a36Sopenharmony_ci mem_names[i], &pru->mem_regions[i].pa, 106062306a36Sopenharmony_ci pru->mem_regions[i].size, pru->mem_regions[i].va); 106162306a36Sopenharmony_ci } 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci ret = pru_rproc_set_id(pru); 106462306a36Sopenharmony_ci if (ret < 0) 106562306a36Sopenharmony_ci return ret; 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci platform_set_drvdata(pdev, rproc); 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci ret = devm_rproc_add(dev, pru->rproc); 107062306a36Sopenharmony_ci if (ret) { 107162306a36Sopenharmony_ci dev_err(dev, "rproc_add failed: %d\n", ret); 107262306a36Sopenharmony_ci return ret; 107362306a36Sopenharmony_ci } 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci pru_rproc_create_debug_entries(rproc); 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci dev_dbg(dev, "PRU rproc node %pOF probed successfully\n", np); 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci return 0; 108062306a36Sopenharmony_ci} 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_cistatic void pru_rproc_remove(struct platform_device *pdev) 108362306a36Sopenharmony_ci{ 108462306a36Sopenharmony_ci struct device *dev = &pdev->dev; 108562306a36Sopenharmony_ci struct rproc *rproc = platform_get_drvdata(pdev); 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci dev_dbg(dev, "%s: removing rproc %s\n", __func__, rproc->name); 108862306a36Sopenharmony_ci} 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_cistatic const struct pru_private_data pru_data = { 109162306a36Sopenharmony_ci .type = PRU_TYPE_PRU, 109262306a36Sopenharmony_ci}; 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_cistatic const struct pru_private_data k3_pru_data = { 109562306a36Sopenharmony_ci .type = PRU_TYPE_PRU, 109662306a36Sopenharmony_ci .is_k3 = 1, 109762306a36Sopenharmony_ci}; 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_cistatic const struct pru_private_data k3_rtu_data = { 110062306a36Sopenharmony_ci .type = PRU_TYPE_RTU, 110162306a36Sopenharmony_ci .is_k3 = 1, 110262306a36Sopenharmony_ci}; 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_cistatic const struct pru_private_data k3_tx_pru_data = { 110562306a36Sopenharmony_ci .type = PRU_TYPE_TX_PRU, 110662306a36Sopenharmony_ci .is_k3 = 1, 110762306a36Sopenharmony_ci}; 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_cistatic const struct of_device_id pru_rproc_match[] = { 111062306a36Sopenharmony_ci { .compatible = "ti,am3356-pru", .data = &pru_data }, 111162306a36Sopenharmony_ci { .compatible = "ti,am4376-pru", .data = &pru_data }, 111262306a36Sopenharmony_ci { .compatible = "ti,am5728-pru", .data = &pru_data }, 111362306a36Sopenharmony_ci { .compatible = "ti,am642-pru", .data = &k3_pru_data }, 111462306a36Sopenharmony_ci { .compatible = "ti,am642-rtu", .data = &k3_rtu_data }, 111562306a36Sopenharmony_ci { .compatible = "ti,am642-tx-pru", .data = &k3_tx_pru_data }, 111662306a36Sopenharmony_ci { .compatible = "ti,k2g-pru", .data = &pru_data }, 111762306a36Sopenharmony_ci { .compatible = "ti,am654-pru", .data = &k3_pru_data }, 111862306a36Sopenharmony_ci { .compatible = "ti,am654-rtu", .data = &k3_rtu_data }, 111962306a36Sopenharmony_ci { .compatible = "ti,am654-tx-pru", .data = &k3_tx_pru_data }, 112062306a36Sopenharmony_ci { .compatible = "ti,j721e-pru", .data = &k3_pru_data }, 112162306a36Sopenharmony_ci { .compatible = "ti,j721e-rtu", .data = &k3_rtu_data }, 112262306a36Sopenharmony_ci { .compatible = "ti,j721e-tx-pru", .data = &k3_tx_pru_data }, 112362306a36Sopenharmony_ci { .compatible = "ti,am625-pru", .data = &k3_pru_data }, 112462306a36Sopenharmony_ci {}, 112562306a36Sopenharmony_ci}; 112662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, pru_rproc_match); 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_cistatic struct platform_driver pru_rproc_driver = { 112962306a36Sopenharmony_ci .driver = { 113062306a36Sopenharmony_ci .name = PRU_RPROC_DRVNAME, 113162306a36Sopenharmony_ci .of_match_table = pru_rproc_match, 113262306a36Sopenharmony_ci .suppress_bind_attrs = true, 113362306a36Sopenharmony_ci }, 113462306a36Sopenharmony_ci .probe = pru_rproc_probe, 113562306a36Sopenharmony_ci .remove_new = pru_rproc_remove, 113662306a36Sopenharmony_ci}; 113762306a36Sopenharmony_cimodule_platform_driver(pru_rproc_driver); 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ciMODULE_AUTHOR("Suman Anna <s-anna@ti.com>"); 114062306a36Sopenharmony_ciMODULE_AUTHOR("Andrew F. Davis <afd@ti.com>"); 114162306a36Sopenharmony_ciMODULE_AUTHOR("Grzegorz Jaszczyk <grzegorz.jaszczyk@linaro.org>"); 114262306a36Sopenharmony_ciMODULE_AUTHOR("Puranjay Mohan <p-mohan@ti.com>"); 114362306a36Sopenharmony_ciMODULE_AUTHOR("Md Danish Anwar <danishanwar@ti.com>"); 114462306a36Sopenharmony_ciMODULE_DESCRIPTION("PRU-ICSS Remote Processor Driver"); 114562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1146