// SPDX-License-Identifier: GPL-2.0-only
/*
* PRU-ICSS remoteproc driver for various TI SoCs
*
* Copyright (C) 2014-2022 Texas Instruments Incorporated - https://www.ti.com/
*
* Author(s):
* Suman Anna <s-anna@ti.com>
* Andrew F. Davis <afd@ti.com>
* Grzegorz Jaszczyk <grzegorz.jaszczyk@linaro.org> for Texas Instruments
* Puranjay Mohan <p-mohan@ti.com>
* Md Danish Anwar <danishanwar@ti.com>
*/
#include <linux/bitops.h>
#include <linux/debugfs.h>
#include <linux/irqdomain.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/remoteproc/pruss.h>
#include <linux/pruss_driver.h>
#include <linux/remoteproc.h>
#include "remoteproc_internal.h"
#include "remoteproc_elf_helpers.h"
#include "pru_rproc.h"
/* PRU_ICSS_PRU_CTRL registers */
#define PRU_CTRL_CTRL 0x0000
#define PRU_CTRL_STS 0x0004
#define PRU_CTRL_WAKEUP_EN 0x0008
#define PRU_CTRL_CYCLE 0x000C
#define PRU_CTRL_STALL 0x0010
#define PRU_CTRL_CTBIR0 0x0020
#define PRU_CTRL_CTBIR1 0x0024
#define PRU_CTRL_CTPPR0 0x0028
#define PRU_CTRL_CTPPR1 0x002C
/* CTRL register bit-fields */
#define CTRL_CTRL_SOFT_RST_N BIT(0)
#define CTRL_CTRL_EN BIT(1)
#define CTRL_CTRL_SLEEPING BIT(2)
#define CTRL_CTRL_CTR_EN BIT(3)
#define CTRL_CTRL_SINGLE_STEP BIT(8)
#define CTRL_CTRL_RUNSTATE BIT(15)
/* PRU_ICSS_PRU_DEBUG registers */
#define PRU_DEBUG_GPREG(x) (0x0000 + (x) * 4)
#define PRU_DEBUG_CT_REG(x) (0x0080 + (x) * 4)
/* PRU/RTU/Tx_PRU Core IRAM address masks */
#define PRU_IRAM_ADDR_MASK 0x3ffff
#define PRU0_IRAM_ADDR_MASK 0x34000
#define PRU1_IRAM_ADDR_MASK 0x38000
#define RTU0_IRAM_ADDR_MASK 0x4000
#define RTU1_IRAM_ADDR_MASK 0x6000
#define TX_PRU0_IRAM_ADDR_MASK 0xa000
#define TX_PRU1_IRAM_ADDR_MASK 0xc000
/* PRU device addresses for various type of PRU RAMs */
#define PRU_IRAM_DA 0 /* Instruction RAM */
#define PRU_PDRAM_DA 0 /* Primary Data RAM */
#define PRU_SDRAM_DA 0x2000 /* Secondary Data RAM */
#define PRU_SHRDRAM_DA 0x10000 /* Shared Data RAM */
#define MAX_PRU_SYS_EVENTS 160
/**
* enum pru_iomem - PRU core memory/register range identifiers
*
* @PRU_IOMEM_IRAM: PRU Instruction RAM range
* @PRU_IOMEM_CTRL: PRU Control register range
* @PRU_IOMEM_DEBUG: PRU Debug register range
* @PRU_IOMEM_MAX: just keep this one at the end
*/
enum pru_iomem {
PRU_IOMEM_IRAM = 0,
PRU_IOMEM_CTRL,
PRU_IOMEM_DEBUG,
PRU_IOMEM_MAX,
};
/**
* struct pru_private_data - device data for a PRU core
* @type: type of the PRU core (PRU, RTU, Tx_PRU)
* @is_k3: flag used to identify the need for special load handling
*/
struct pru_private_data {
enum pru_type type;
unsigned int is_k3 : 1;
};
/**
* struct pru_rproc - PRU remoteproc structure
* @id: id of the PRU core within the PRUSS
* @dev: PRU core device pointer
* @pruss: back-reference to parent PRUSS structure
* @rproc: remoteproc pointer for this PRU core
* @data: PRU core specific data
* @mem_regions: data for each of the PRU memory regions
* @client_np: client device node
* @lock: mutex to protect client usage
* @fw_name: name of firmware image used during loading
* @mapped_irq: virtual interrupt numbers of created fw specific mapping
* @pru_interrupt_map: pointer to interrupt mapping description (firmware)
* @pru_interrupt_map_sz: pru_interrupt_map size
* @rmw_lock: lock for read, modify, write operations on registers
* @dbg_single_step: debug state variable to set PRU into single step mode
* @dbg_continuous: debug state variable to restore PRU execution mode
* @evt_count: number of mapped events
* @gpmux_save: saved value for gpmux config
*/
struct pru_rproc {
int id;
struct device *dev;
struct pruss *pruss;
struct rproc *rproc;
const struct pru_private_data *data;
struct pruss_mem_region mem_regions[PRU_IOMEM_MAX];
struct device_node *client_np;
struct mutex lock;
const char *fw_name;
unsigned int *mapped_irq;
struct pru_irq_rsc *pru_interrupt_map;
size_t pru_interrupt_map_sz;
spinlock_t rmw_lock;
u32 dbg_single_step;
u32 dbg_continuous;
u8 evt_count;
u8 gpmux_save;
};
static inline u32 pru_control_read_reg(struct pru_rproc *pru, unsigned int reg)
{
return readl_relaxed(pru->mem_regions[PRU_IOMEM_CTRL].va + reg);
}
static inline
void pru_control_write_reg(struct pru_rproc *pru, unsigned int reg, u32 val)
{
writel_relaxed(val, pru->mem_regions[PRU_IOMEM_CTRL].va + reg);
}
static inline
void pru_control_set_reg(struct pru_rproc *pru, unsigned int reg,
u32 mask, u32 set)
{
u32 val;
unsigned long flags;
spin_lock_irqsave(&pru->rmw_lock, flags);
val = pru_control_read_reg(pru, reg);
val &= ~mask;
val |= (set & mask);
pru_control_write_reg(pru, reg, val);
spin_unlock_irqrestore(&pru->rmw_lock, flags);
}
/**
* pru_rproc_set_firmware() - set firmware for a PRU core
* @rproc: the rproc instance of the PRU
* @fw_name: the new firmware name, or NULL if default is desired
*
* Return: 0 on success, or errno in error case.
*/
static int pru_rproc_set_firmware(struct rproc *rproc, const char *fw_name)
{
struct pru_rproc *pru = rproc->priv;
if (!fw_name)
fw_name = pru->fw_name;
return rproc_set_firmware(rproc, fw_name);
}
static struct rproc *__pru_rproc_get(struct device_node *np, int index)
{
struct rproc *rproc;
phandle rproc_phandle;
int ret;
ret = of_property_read_u32_index(np, "ti,prus", index, &rproc_phandle);
if (ret)
return ERR_PTR(ret);
rproc = rproc_get_by_phandle(rproc_phandle);
if (!rproc) {
ret = -EPROBE_DEFER;
return ERR_PTR(ret);
}
/* make sure it is PRU rproc */
if (!is_pru_rproc(rproc