// SPDX-License-Identifier: GPL-2.0
/*
* ZynqMP R5 Remote Processor driver
*
*/
#include <dt-bindings/power/xlnx-zynqmp-power.h>
#include <linux/dma-mapping.h>
#include <linux/firmware/xlnx-zynqmp.h>
#include <linux/kernel.h>
#include <linux/mailbox_client.h>
#include <linux/mailbox/zynqmp-ipi-message.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/of_reserved_mem.h>
#include <linux/platform_device.h>
#include <linux/remoteproc.h>
#include "remoteproc_internal.h"
/* IPI buffer MAX length */
#define IPI_BUF_LEN_MAX 32U
/* RX mailbox client buffer max length */
#define MBOX_CLIENT_BUF_MAX (IPI_BUF_LEN_MAX + \
sizeof(struct zynqmp_ipi_message))
/*
* settings for RPU cluster mode which
* reflects possible values of xlnx,cluster-mode dt-property
*/
enum zynqmp_r5_cluster_mode {
SPLIT_MODE = 0, /* When cores run as separate processor */
LOCKSTEP_MODE = 1, /* cores execute same code in lockstep,clk-for-clk */
SINGLE_CPU_MODE = 2, /* core0 is held in reset and only core1 runs */
};
/**
* struct mem_bank_data - Memory Bank description
*
* @addr: Start address of memory bank
* @da: device address
* @size: Size of Memory bank
* @pm_domain_id: Power-domains id of memory bank for firmware to turn on/off
* @bank_name: name of the bank for remoteproc framework
*/
struct mem_bank_data {
phys_addr_t addr;
u32 da;
size_t size;
u32 pm_domain_id;
char *bank_name;
};
/**
* struct mbox_info
*
* @rx_mc_buf: to copy data from mailbox rx channel
* @tx_mc_buf: to copy data to mailbox tx channel
* @r5_core: this mailbox's corresponding r5_core pointer
* @mbox_work: schedule work after receiving data from mailbox
* @mbox_cl: mailbox client
* @tx_chan: mailbox tx channel
* @rx_chan: mailbox rx channel
*/
struct mbox_info {
unsigned char rx_mc_buf[MBOX_CLIENT_BUF_MAX];
unsigned char tx_mc_buf[MBOX_CLIENT_BUF_MAX];
struct zynqmp_r5_core *r5_core;
struct work_struct mbox_work;
struct mbox_client mbox_cl;
struct mbox_chan *tx_chan;
struct mbox_chan *rx_chan;
};
/*
* Hardcoded TCM bank values. This will be removed once TCM bindings are
* accepted for system-dt specifications and upstreamed in linux kernel
*/
static const struct mem_bank_data zynqmp_tcm_banks_split[] = {
{0xffe00000UL, 0x0, 0x10000UL, PD_R5_0_ATCM, "atcm0"}, /* TCM 64KB each */
{0xffe20000UL, 0x20000, 0x10000UL, PD_R5_0_BTCM, "btcm0"},
{0xffe90000UL, 0x0, 0x10000UL, PD_R5_1_ATCM, "atcm1"},
{0xffeb0000UL, 0x20000, 0x10000UL, PD_R5_1_BTCM, "btcm1"},
};
/* In lockstep mode cluster combines each 64KB TCM and makes 128KB TCM */
static const struct mem_bank_data zynqmp_tcm_banks_lockstep[] = {
{0xffe00000UL, 0x0, 0x20000UL, PD_R5_0_ATCM, "atcm0"}, /* TCM 128KB each */
{0xffe20000UL, 0x20000, 0x20000UL, PD_R5_0_BTCM, "btcm0"},
{0, 0, 0, PD_R5_1_ATCM, ""},
{0, 0, 0, PD_R5_1_BTCM, ""},
};
/**
* struct zynqmp_r5_core
*
* @dev: device of RPU instance
* @np: device node of RPU instance
* @tcm_bank_count: number TCM banks accessible to this RPU
* @tcm_banks: array of each TCM bank data
* @rproc: rproc handle
* @pm_domain_id: RPU CPU power domain id
* @ipi: pointer to mailbox information
*/
struct zynqmp_r5_core {
struct device *dev;
struct device_node *np;
int tcm_bank_count;
struct mem_bank_data **tcm_banks;
struct rproc *rproc;
u32 pm_domain_id;
struct mbox_info *ipi;
};
/**
* struct zynqmp_r5_cluster
*
* @dev: r5f subsystem cluster device node
* @mode: cluster mode of type zynqmp_r5_cluster_mode
* @core_count: number of r5 cores used for this cluster mode
* @r5_cores: Array of pointers pointing to r5 core
*/
struct zynqmp_r5_cluster {
struct device *dev;
enum zynqmp_r5_cluster_mode mode;
int core_count;
struct zynqmp_r5_core **r5_cores;
};
/**
* event_notified_idr_cb() - callback for vq_interrupt per notifyid
* @id: rproc->notify id
* @ptr: pointer to idr private data
* @data: data passed to idr_for_each callback
*
* Pass notification to remoteproc virtio
*
* Return: 0. having return is to satisfy the idr_for_each() function
* pointer input argument requirement.
**/
static int event_notified_idr_cb(int id, void *ptr, void *data)
{
struct rproc *rproc = data;
if (rproc_vq_interrupt(rproc, id) == IRQ_NONE)
dev_dbg(&rproc->dev, "data not found for vqid=%d\n",