mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson
synced 2025-08-26 13:47:26 +00:00
mailbox: add Microchip IPC support
Add a mailbox controller driver for the Microchip Inter-processor Communication (IPC), which is used to send and receive data between processors. The driver uses the RISC-V Supervisor Binary Interface (SBI) to communicate with software running in machine mode (M-mode) to access the IPC hardware block. Additional details on the Microchip vendor extension and the IPC function IDs described in the driver can be found in the following documentation: https://github.com/linux4microchip/microchip-sbi-ecall-extension This SBI interface in this driver is compatible with the Mi-V Inter-hart Communication (IHC) IP. Transmitting and receiving data through the mailbox framework is done through struct mchp_ipc_msg. Signed-off-by: Valentina Fernandez <valentina.fernandezalanis@microchip.com> Signed-off-by: Jassi Brar <jassisinghbrar@gmail.com>
This commit is contained in:
parent
af33bd58c2
commit
e4b1d67e71
@ -178,6 +178,19 @@ config POLARFIRE_SOC_MAILBOX
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config MCHP_SBI_IPC_MBOX
|
||||
tristate "Microchip Inter-processor Communication (IPC) SBI driver"
|
||||
depends on RISCV_SBI || COMPILE_TEST
|
||||
depends on ARCH_MICROCHIP
|
||||
help
|
||||
Mailbox implementation for Microchip devices with an
|
||||
Inter-process communication (IPC) controller.
|
||||
|
||||
To compile this driver as a module, choose M here. the
|
||||
module will be called mailbox-mchp-ipc-sbi.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config QCOM_APCS_IPC
|
||||
tristate "Qualcomm APCS IPC driver"
|
||||
depends on ARCH_QCOM || COMPILE_TEST
|
||||
|
@ -45,6 +45,8 @@ obj-$(CONFIG_BCM_FLEXRM_MBOX) += bcm-flexrm-mailbox.o
|
||||
|
||||
obj-$(CONFIG_POLARFIRE_SOC_MAILBOX) += mailbox-mpfs.o
|
||||
|
||||
obj-$(CONFIG_MCHP_SBI_IPC_MBOX) += mailbox-mchp-ipc-sbi.o
|
||||
|
||||
obj-$(CONFIG_QCOM_APCS_IPC) += qcom-apcs-ipc-mailbox.o
|
||||
|
||||
obj-$(CONFIG_TEGRA_HSP_MBOX) += tegra-hsp.o
|
||||
|
504
drivers/mailbox/mailbox-mchp-ipc-sbi.c
Normal file
504
drivers/mailbox/mailbox-mchp-ipc-sbi.c
Normal file
@ -0,0 +1,504 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Microchip Inter-Processor communication (IPC) driver
|
||||
*
|
||||
* Copyright (c) 2021 - 2024 Microchip Technology Inc. All rights reserved.
|
||||
*
|
||||
* Author: Valentina Fernandez <valentina.fernandezalanis@microchip.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mailbox/mchp-ipc.h>
|
||||
#include <asm/sbi.h>
|
||||
#include <asm/vendorid_list.h>
|
||||
|
||||
#define IRQ_STATUS_BITS 12
|
||||
#define NUM_CHANS_PER_CLUSTER 5
|
||||
#define IPC_DMA_BIT_MASK 32
|
||||
#define SBI_EXT_MICROCHIP_TECHNOLOGY (SBI_EXT_VENDOR_START | \
|
||||
MICROCHIP_VENDOR_ID)
|
||||
|
||||
enum {
|
||||
SBI_EXT_IPC_PROBE = 0x100,
|
||||
SBI_EXT_IPC_CH_INIT,
|
||||
SBI_EXT_IPC_SEND,
|
||||
SBI_EXT_IPC_RECEIVE,
|
||||
SBI_EXT_IPC_STATUS,
|
||||
};
|
||||
|
||||
enum ipc_hw {
|
||||
MIV_IHC,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mchp_ipc_mbox_info - IPC probe message format
|
||||
*
|
||||
* @hw_type: IPC implementation available in the hardware
|
||||
* @num_channels: number of IPC channels available in the hardware
|
||||
*
|
||||
* Used to retrieve information on the IPC implementation
|
||||
* using the SBI_EXT_IPC_PROBE SBI function id.
|
||||
*/
|
||||
struct mchp_ipc_mbox_info {
|
||||
enum ipc_hw hw_type;
|
||||
u8 num_channels;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mchp_ipc_init - IPC channel init message format
|
||||
*
|
||||
* @max_msg_size: maxmimum message size in bytes of a given channel
|
||||
*
|
||||
* struct used by the SBI_EXT_IPC_CH_INIT SBI function id to get
|
||||
* the max message size in bytes of the initialized channel.
|
||||
*/
|
||||
struct mchp_ipc_init {
|
||||
u16 max_msg_size;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mchp_ipc_status - IPC status message format
|
||||
*
|
||||
* @status: interrupt status for all channels associated to a cluster
|
||||
* @cluster: specifies the cluster instance that originated an irq
|
||||
*
|
||||
* struct used by the SBI_EXT_IPC_STATUS SBI function id to get
|
||||
* the message present and message clear interrupt status for all the
|
||||
* channels associated to a cluster.
|
||||
*/
|
||||
struct mchp_ipc_status {
|
||||
u32 status;
|
||||
u8 cluster;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mchp_ipc_sbi_msg - IPC SBI payload message
|
||||
*
|
||||
* @buf_addr: physical address where the received data should be copied to
|
||||
* @size: maximum size(in bytes) that can be stored in the buffer pointed to by `buf`
|
||||
* @irq_type: mask representing the irq types that triggered an irq
|
||||
*
|
||||
* struct used by the SBI_EXT_IPC_SEND/SBI_EXT_IPC_RECEIVE SBI function
|
||||
* ids to send/receive a message from an associated processor using
|
||||
* the IPC.
|
||||
*/
|
||||
struct mchp_ipc_sbi_msg {
|
||||
u64 buf_addr;
|
||||
u16 size;
|
||||
u8 irq_type;
|
||||
};
|
||||
|
||||
struct mchp_ipc_cluster_cfg {
|
||||
void *buf_base;
|
||||
phys_addr_t buf_base_addr;
|
||||
int irq;
|
||||
};
|
||||
|
||||
struct mchp_ipc_sbi_mbox {
|
||||
struct device *dev;
|
||||
struct mbox_chan *chans;
|
||||
struct mchp_ipc_cluster_cfg *cluster_cfg;
|
||||
void *buf_base;
|
||||
unsigned long buf_base_addr;
|
||||
struct mbox_controller controller;
|
||||
enum ipc_hw hw_type;
|
||||
};
|
||||
|
||||
static int mchp_ipc_sbi_chan_send(u32 command, u32 channel, unsigned long address)
|
||||
{
|
||||
struct sbiret ret;
|
||||
|
||||
ret = sbi_ecall(SBI_EXT_MICROCHIP_TECHNOLOGY, command, channel,
|
||||
address, 0, 0, 0, 0);
|
||||
|
||||
if (ret.error)
|
||||
return sbi_err_map_linux_errno(ret.error);
|
||||
else
|
||||
return ret.value;
|
||||
}
|
||||
|
||||
static int mchp_ipc_sbi_send(u32 command, unsigned long address)
|
||||
{
|
||||
struct sbiret ret;
|
||||
|
||||
ret = sbi_ecall(SBI_EXT_MICROCHIP_TECHNOLOGY, command, address,
|
||||
0, 0, 0, 0, 0);
|
||||
|
||||
if (ret.error)
|
||||
return sbi_err_map_linux_errno(ret.error);
|
||||
else
|
||||
return ret.value;
|
||||
}
|
||||
|
||||
static struct mchp_ipc_sbi_mbox *to_mchp_ipc_mbox(struct mbox_controller *mbox)
|
||||
{
|
||||
return container_of(mbox, struct mchp_ipc_sbi_mbox, controller);
|
||||
}
|
||||
|
||||
static inline void mchp_ipc_prepare_receive_req(struct mbox_chan *chan)
|
||||
{
|
||||
struct mchp_ipc_sbi_chan *chan_info = (struct mchp_ipc_sbi_chan *)chan->con_priv;
|
||||
struct mchp_ipc_sbi_msg request;
|
||||
|
||||
request.buf_addr = chan_info->msg_buf_rx_addr;
|
||||
request.size = chan_info->max_msg_size;
|
||||
memcpy(chan_info->buf_base_rx, &request, sizeof(struct mchp_ipc_sbi_msg));
|
||||
}
|
||||
|
||||
static inline void mchp_ipc_process_received_data(struct mbox_chan *chan,
|
||||
struct mchp_ipc_msg *ipc_msg)
|
||||
{
|
||||
struct mchp_ipc_sbi_chan *chan_info = (struct mchp_ipc_sbi_chan *)chan->con_priv;
|
||||
struct mchp_ipc_sbi_msg sbi_msg;
|
||||
|
||||
memcpy(&sbi_msg, chan_info->buf_base_rx, sizeof(struct mchp_ipc_sbi_msg));
|
||||
ipc_msg->buf = (u32 *)chan_info->msg_buf_rx;
|
||||
ipc_msg->size = sbi_msg.size;
|
||||
}
|
||||
|
||||
static irqreturn_t mchp_ipc_cluster_aggr_isr(int irq, void *data)
|
||||
{
|
||||
struct mbox_chan *chan;
|
||||
struct mchp_ipc_sbi_chan *chan_info;
|
||||
struct mchp_ipc_sbi_mbox *ipc = (struct mchp_ipc_sbi_mbox *)data;
|
||||
struct mchp_ipc_msg ipc_msg;
|
||||
struct mchp_ipc_status status_msg;
|
||||
int ret;
|
||||
unsigned long hartid;
|
||||
u32 i, chan_index, chan_id;
|
||||
|
||||
/* Find out the hart that originated the irq */
|
||||
for_each_online_cpu(i) {
|
||||
hartid = cpuid_to_hartid_map(i);
|
||||
if (irq == ipc->cluster_cfg[hartid].irq)
|
||||
break;
|
||||
}
|
||||
|
||||
status_msg.cluster = hartid;
|
||||
memcpy(ipc->cluster_cfg[hartid].buf_base, &status_msg, sizeof(struct mchp_ipc_status));
|
||||
|
||||
ret = mchp_ipc_sbi_send(SBI_EXT_IPC_STATUS, ipc->cluster_cfg[hartid].buf_base_addr);
|
||||
if (ret < 0) {
|
||||
dev_err_ratelimited(ipc->dev, "could not get IHC irq status ret=%d\n", ret);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
memcpy(&status_msg, ipc->cluster_cfg[hartid].buf_base, sizeof(struct mchp_ipc_status));
|
||||
|
||||
/*
|
||||
* Iterate over each bit set in the IHC interrupt status register (IRQ_STATUS) to identify
|
||||
* the channel(s) that have a message to be processed/acknowledged.
|
||||
* The bits are organized in alternating format, where each pair of bits represents
|
||||
* the status of the message present and message clear interrupts for each cluster/hart
|
||||
* (from hart 0 to hart 5). Each cluster can have up to 5 fixed channels associated.
|
||||
*/
|
||||
|
||||
for_each_set_bit(i, (unsigned long *)&status_msg.status, IRQ_STATUS_BITS) {
|
||||
/* Find out the destination hart that triggered the interrupt */
|
||||
chan_index = i / 2;
|
||||
|
||||
/*
|
||||
* The IP has no loopback channels, so we need to decrement the index when
|
||||
* the target hart has a greater index than our own
|
||||
*/
|
||||
if (chan_index >= status_msg.cluster)
|
||||
chan_index--;
|
||||
|
||||
/*
|
||||
* Calculate the channel id given the hart and channel index. Channel IDs
|
||||
* are unique across all clusters of an IPC, and iterate contiguously
|
||||
* across all clusters.
|
||||
*/
|
||||
chan_id = status_msg.cluster * (NUM_CHANS_PER_CLUSTER + chan_index);
|
||||
|
||||
chan = &ipc->chans[chan_id];
|
||||
chan_info = (struct mchp_ipc_sbi_chan *)chan->con_priv;
|
||||
|
||||
if (i % 2 == 0) {
|
||||
mchp_ipc_prepare_receive_req(chan);
|
||||
ret = mchp_ipc_sbi_chan_send(SBI_EXT_IPC_RECEIVE, chan_id,
|
||||
chan_info->buf_base_rx_addr);
|
||||
if (ret < 0)
|
||||
continue;
|
||||
|
||||
mchp_ipc_process_received_data(chan, &ipc_msg);
|
||||
mbox_chan_received_data(&ipc->chans[chan_id], (void *)&ipc_msg);
|
||||
|
||||
} else {
|
||||
ret = mchp_ipc_sbi_chan_send(SBI_EXT_IPC_RECEIVE, chan_id,
|
||||
chan_info->buf_base_rx_addr);
|
||||
mbox_chan_txdone(&ipc->chans[chan_id], ret);
|
||||
}
|
||||
}
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int mchp_ipc_send_data(struct mbox_chan *chan, void *data)
|
||||
{
|
||||
struct mchp_ipc_sbi_chan *chan_info = (struct mchp_ipc_sbi_chan *)chan->con_priv;
|
||||
const struct mchp_ipc_msg *msg = data;
|
||||
struct mchp_ipc_sbi_msg sbi_payload;
|
||||
|
||||
memcpy(chan_info->msg_buf_tx, msg->buf, msg->size);
|
||||
sbi_payload.buf_addr = chan_info->msg_buf_tx_addr;
|
||||
sbi_payload.size = msg->size;
|
||||
memcpy(chan_info->buf_base_tx, &sbi_payload, sizeof(sbi_payload));
|
||||
|
||||
return mchp_ipc_sbi_chan_send(SBI_EXT_IPC_SEND, chan_info->id, chan_info->buf_base_tx_addr);
|
||||
}
|
||||
|
||||
static int mchp_ipc_startup(struct mbox_chan *chan)
|
||||
{
|
||||
struct mchp_ipc_sbi_chan *chan_info = (struct mchp_ipc_sbi_chan *)chan->con_priv;
|
||||
struct mchp_ipc_sbi_mbox *ipc = to_mchp_ipc_mbox(chan->mbox);
|
||||
struct mchp_ipc_init ch_init_msg;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* The TX base buffer is used to transmit two types of messages:
|
||||
* - struct mchp_ipc_init to initialize the channel
|
||||
* - struct mchp_ipc_sbi_msg to transmit user data/payload
|
||||
* Ensure the TX buffer size is large enough to accommodate either message type.
|
||||
*/
|
||||
size_t max_size = max(sizeof(struct mchp_ipc_init), sizeof(struct mchp_ipc_sbi_msg));
|
||||
|
||||
chan_info->buf_base_tx = kmalloc(max_size, GFP_KERNEL);
|
||||
if (!chan_info->buf_base_tx) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
chan_info->buf_base_tx_addr = __pa(chan_info->buf_base_tx);
|
||||
|
||||
chan_info->buf_base_rx = kmalloc(max_size, GFP_KERNEL);
|
||||
if (!chan_info->buf_base_rx) {
|
||||
ret = -ENOMEM;
|
||||
goto fail_free_buf_base_tx;
|
||||
}
|
||||
|
||||
chan_info->buf_base_rx_addr = __pa(chan_info->buf_base_rx);
|
||||
|
||||
ret = mchp_ipc_sbi_chan_send(SBI_EXT_IPC_CH_INIT, chan_info->id,
|
||||
chan_info->buf_base_tx_addr);
|
||||
if (ret < 0) {
|
||||
dev_err(ipc->dev, "channel %u init failed\n", chan_info->id);
|
||||
goto fail_free_buf_base_rx;
|
||||
}
|
||||
|
||||
memcpy(&ch_init_msg, chan_info->buf_base_tx, sizeof(struct mchp_ipc_init));
|
||||
chan_info->max_msg_size = ch_init_msg.max_msg_size;
|
||||
|
||||
chan_info->msg_buf_tx = kmalloc(chan_info->max_msg_size, GFP_KERNEL);
|
||||
if (!chan_info->msg_buf_tx) {
|
||||
ret = -ENOMEM;
|
||||
goto fail_free_buf_base_rx;
|
||||
}
|
||||
|
||||
chan_info->msg_buf_tx_addr = __pa(chan_info->msg_buf_tx);
|
||||
|
||||
chan_info->msg_buf_rx = kmalloc(chan_info->max_msg_size, GFP_KERNEL);
|
||||
if (!chan_info->msg_buf_rx) {
|
||||
ret = -ENOMEM;
|
||||
goto fail_free_buf_msg_tx;
|
||||
}
|
||||
|
||||
chan_info->msg_buf_rx_addr = __pa(chan_info->msg_buf_rx);
|
||||
|
||||
switch (ipc->hw_type) {
|
||||
case MIV_IHC:
|
||||
return 0;
|
||||
default:
|
||||
goto fail_free_buf_msg_rx;
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
dev_err(ipc->dev, "failed to register interrupt(s)\n");
|
||||
goto fail_free_buf_msg_rx;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
fail_free_buf_msg_rx:
|
||||
kfree(chan_info->msg_buf_rx);
|
||||
fail_free_buf_msg_tx:
|
||||
kfree(chan_info->msg_buf_tx);
|
||||
fail_free_buf_base_rx:
|
||||
kfree(chan_info->buf_base_rx);
|
||||
fail_free_buf_base_tx:
|
||||
kfree(chan_info->buf_base_tx);
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void mchp_ipc_shutdown(struct mbox_chan *chan)
|
||||
{
|
||||
struct mchp_ipc_sbi_chan *chan_info = (struct mchp_ipc_sbi_chan *)chan->con_priv;
|
||||
|
||||
kfree(chan_info->buf_base_tx);
|
||||
kfree(chan_info->buf_base_rx);
|
||||
kfree(chan_info->msg_buf_tx);
|
||||
kfree(chan_info->msg_buf_rx);
|
||||
}
|
||||
|
||||
static const struct mbox_chan_ops mchp_ipc_ops = {
|
||||
.startup = mchp_ipc_startup,
|
||||
.send_data = mchp_ipc_send_data,
|
||||
.shutdown = mchp_ipc_shutdown,
|
||||
};
|
||||
|
||||
static struct mbox_chan *mchp_ipc_mbox_xlate(struct mbox_controller *controller,
|
||||
const struct of_phandle_args *spec)
|
||||
{
|
||||
struct mchp_ipc_sbi_mbox *ipc = to_mchp_ipc_mbox(controller);
|
||||
unsigned int chan_id = spec->args[0];
|
||||
|
||||
if (chan_id >= ipc->controller.num_chans) {
|
||||
dev_err(ipc->dev, "invalid channel id %d\n", chan_id);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
return &ipc->chans[chan_id];
|
||||
}
|
||||
|
||||
static int mchp_ipc_get_cluster_aggr_irq(struct mchp_ipc_sbi_mbox *ipc)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(ipc->dev);
|
||||
char *irq_name;
|
||||
int cpuid, ret;
|
||||
unsigned long hartid;
|
||||
bool irq_found = false;
|
||||
|
||||
for_each_online_cpu(cpuid) {
|
||||
hartid = cpuid_to_hartid_map(cpuid);
|
||||
irq_name = devm_kasprintf(ipc->dev, GFP_KERNEL, "hart-%lu", hartid);
|
||||
ret = platform_get_irq_byname_optional(pdev, irq_name);
|
||||
if (ret <= 0)
|
||||
continue;
|
||||
|
||||
ipc->cluster_cfg[hartid].irq = ret;
|
||||
ret = devm_request_irq(ipc->dev, ipc->cluster_cfg[hartid].irq,
|
||||
mchp_ipc_cluster_aggr_isr, IRQF_SHARED,
|
||||
"miv-ihc-irq", ipc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ipc->cluster_cfg[hartid].buf_base = devm_kmalloc(ipc->dev,
|
||||
sizeof(struct mchp_ipc_status),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!ipc->cluster_cfg[hartid].buf_base)
|
||||
return -ENOMEM;
|
||||
|
||||
ipc->cluster_cfg[hartid].buf_base_addr = __pa(ipc->cluster_cfg[hartid].buf_base);
|
||||
|
||||
irq_found = true;
|
||||
}
|
||||
|
||||
return irq_found;
|
||||
}
|
||||
|
||||
static int mchp_ipc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct mchp_ipc_mbox_info ipc_info;
|
||||
struct mchp_ipc_sbi_mbox *ipc;
|
||||
struct mchp_ipc_sbi_chan *priv;
|
||||
bool irq_avail = false;
|
||||
int ret;
|
||||
u32 chan_id;
|
||||
|
||||
ret = sbi_probe_extension(SBI_EXT_MICROCHIP_TECHNOLOGY);
|
||||
if (ret <= 0)
|
||||
return dev_err_probe(dev, ret, "Microchip SBI extension not detected\n");
|
||||
|
||||
ipc = devm_kzalloc(dev, sizeof(*ipc), GFP_KERNEL);
|
||||
if (!ipc)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, ipc);
|
||||
|
||||
ipc->buf_base = devm_kmalloc(dev, sizeof(struct mchp_ipc_mbox_info), GFP_KERNEL);
|
||||
if (!ipc->buf_base)
|
||||
return -ENOMEM;
|
||||
|
||||
ipc->buf_base_addr = __pa(ipc->buf_base);
|
||||
|
||||
ret = mchp_ipc_sbi_send(SBI_EXT_IPC_PROBE, ipc->buf_base_addr);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(dev, ret, "could not probe IPC SBI service\n");
|
||||
|
||||
memcpy(&ipc_info, ipc->buf_base, sizeof(struct mchp_ipc_mbox_info));
|
||||
ipc->controller.num_chans = ipc_info.num_channels;
|
||||
ipc->hw_type = ipc_info.hw_type;
|
||||
|
||||
ipc->chans = devm_kcalloc(dev, ipc->controller.num_chans, sizeof(*ipc->chans), GFP_KERNEL);
|
||||
if (!ipc->chans)
|
||||
return -ENOMEM;
|
||||
|
||||
ipc->dev = dev;
|
||||
ipc->controller.txdone_irq = true;
|
||||
ipc->controller.dev = ipc->dev;
|
||||
ipc->controller.ops = &mchp_ipc_ops;
|
||||
ipc->controller.chans = ipc->chans;
|
||||
ipc->controller.of_xlate = mchp_ipc_mbox_xlate;
|
||||
|
||||
for (chan_id = 0; chan_id < ipc->controller.num_chans; chan_id++) {
|
||||
priv = devm_kmalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
ipc->chans[chan_id].con_priv = priv;
|
||||
priv->id = chan_id;
|
||||
}
|
||||
|
||||
if (ipc->hw_type == MIV_IHC) {
|
||||
ipc->cluster_cfg = devm_kcalloc(dev, num_online_cpus(),
|
||||
sizeof(struct mchp_ipc_cluster_cfg),
|
||||
GFP_KERNEL);
|
||||
if (!ipc->cluster_cfg)
|
||||
return -ENOMEM;
|
||||
|
||||
if (mchp_ipc_get_cluster_aggr_irq(ipc))
|
||||
irq_avail = true;
|
||||
}
|
||||
|
||||
if (!irq_avail)
|
||||
return dev_err_probe(dev, -ENODEV, "missing interrupt property\n");
|
||||
|
||||
ret = devm_mbox_controller_register(dev, &ipc->controller);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"Inter-Processor communication (IPC) registration failed\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id mchp_ipc_of_match[] = {
|
||||
{.compatible = "microchip,sbi-ipc", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mchp_ipc_of_match);
|
||||
|
||||
static struct platform_driver mchp_ipc_driver = {
|
||||
.driver = {
|
||||
.name = "microchip_ipc",
|
||||
.of_match_table = mchp_ipc_of_match,
|
||||
},
|
||||
.probe = mchp_ipc_probe,
|
||||
};
|
||||
|
||||
module_platform_driver(mchp_ipc_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Valentina Fernandez <valentina.fernandezalanis@microchip.com>");
|
||||
MODULE_DESCRIPTION("Microchip Inter-Processor Communication (IPC) driver");
|
33
include/linux/mailbox/mchp-ipc.h
Normal file
33
include/linux/mailbox/mchp-ipc.h
Normal file
@ -0,0 +1,33 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
*Copyright (c) 2024 Microchip Technology Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_MCHP_IPC_H_
|
||||
#define _LINUX_MCHP_IPC_H_
|
||||
|
||||
#include <linux/mailbox_controller.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
struct mchp_ipc_msg {
|
||||
u32 *buf;
|
||||
u16 size;
|
||||
};
|
||||
|
||||
struct mchp_ipc_sbi_chan {
|
||||
void *buf_base_tx;
|
||||
void *buf_base_rx;
|
||||
void *msg_buf_tx;
|
||||
void *msg_buf_rx;
|
||||
phys_addr_t buf_base_tx_addr;
|
||||
phys_addr_t buf_base_rx_addr;
|
||||
phys_addr_t msg_buf_tx_addr;
|
||||
phys_addr_t msg_buf_rx_addr;
|
||||
int chan_aggregated_irq;
|
||||
int mp_irq;
|
||||
int mc_irq;
|
||||
u32 id;
|
||||
u32 max_msg_size;
|
||||
};
|
||||
|
||||
#endif /* _LINUX_MCHP_IPC_H_ */
|
Loading…
Reference in New Issue
Block a user