blob: fb1dd5089b3dc81f9932e8315f9ad6a88c2fff91 [file] [log] [blame]
/*
* Copyright (c) 2019 MediaTek Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/mailbox_controller.h>
#include <linux/mailbox_client.h>
#include <linux/mailbox/mtk-rpmsg-mailbox.h>
#include "../misc/mediatek/include/mt-plat/mtk-mbox.h"
static struct mtk_mbox_chan *to_mtk_mbox_chan(struct mbox_chan *chan)
{
if (!chan || !chan->con_priv)
return NULL;
return (struct mtk_mbox_chan *)chan->con_priv;
}
int mtk_mbox_send_ipi(struct mtk_mbox_chan *mchan, void *data)
{
struct mtk_mbox_device *mbdev;
struct mtk_ipi_msg *msg;
unsigned int status;
int ret;
if (WARN_ON(!data)) {
pr_notice("mbox fw:%u warning\n", mchan->mbox);
return -EINVAL;
}
mbdev = mchan->mbdev;
msg = (struct mtk_ipi_msg *)data;
status = mtk_mbox_check_send_irq(mbdev, mchan->mbox,
mchan->send_pin_index);
if (status != 0) {
mchan->ipimsg = data;
return -EBUSY;
}
ret = mtk_mbox_write_hd(mbdev, mchan->mbox, mchan->send_slot, msg);
if (ret != MBOX_DONE)
return -EIO;
/*
* Ensure that all writes to SRAM are committed before sending the
* interrupt to mbox.
*/
mb();
ret = mtk_mbox_trigger_irq(mbdev, mchan->mbox,
0x1 << mchan->send_pin_index);
if (ret != MBOX_DONE)
pr_notice("mbox fw:%u irq fail\n", mchan->mbox);
return ret;
}
bool mtk_mbox_tx_done(struct mtk_mbox_chan *mchan)
{
struct mtk_mbox_device *mbdev;
struct mbox_chan *chan;
unsigned int status;
unsigned long flags;
chan = mchan->chan;
mbdev = mchan->mbdev;
spin_lock_irqsave(&chan->lock, flags);
status = mtk_mbox_check_send_irq(mbdev, mchan->mbox,
mchan->send_pin_index);
spin_unlock_irqrestore(&chan->lock, flags);
return status ? false : true;
}
int mtk_mbox_start(struct mtk_mbox_chan *mchan)
{
return 0;
}
void mtk_mbox_down(struct mtk_mbox_chan *mchan)
{
}
int mtk_mbox_send_data(struct mbox_chan *chan, void *data)
{
struct mtk_mbox_chan *mchan = to_mtk_mbox_chan(chan);
if (mchan)
return mchan->ops->mtk_send_ipi(mchan, data);
return false;
}
bool mtk_mbox_last_tx_done(struct mbox_chan *chan)
{
struct mtk_mbox_chan *mchan = to_mtk_mbox_chan(chan);
if (mchan)
return mchan->ops->mtk_tx_done(mchan);
return false;
}
int mtk_mbox_startup(struct mbox_chan *chan)
{
struct mtk_mbox_chan *mchan = to_mtk_mbox_chan(chan);
if (mchan)
return mchan->ops->mtk_startup(mchan);
return 0;
}
void mtk_mbox_shutdown(struct mbox_chan *chan)
{
struct mtk_mbox_chan *mchan = to_mtk_mbox_chan(chan);
if (mchan)
mchan->ops->mtk_shutdown(mchan);
}
static const struct mbox_chan_ops mtk_ipi_mbox_chan_ops = {
.send_data = mtk_mbox_send_data,
.last_tx_done = mtk_mbox_last_tx_done,
.startup = mtk_mbox_startup,
.shutdown = mtk_mbox_shutdown,
};
static struct mtk_mbox_operations mtk_mbox_ops = {
.mtk_send_ipi = mtk_mbox_send_ipi,
.mtk_tx_done = mtk_mbox_tx_done,
.mtk_startup = mtk_mbox_start,
.mtk_shutdown = mtk_mbox_down,
};
static struct mbox_chan *mtk_rpmsg_mbox_xlate(struct mbox_controller *mbox,
const struct of_phandle_args *sp)
{
int chan_id = sp->args[0];
if (chan_id >= mbox->num_chans)
return ERR_PTR(-EINVAL);
return &mbox->chans[chan_id];
}
int mtk_mbox_chan_create(struct mbox_controller *mboxctrl,
struct mtk_mbox_device *mbdev, int num)
{
struct mtk_mbox_chan *mchan;
struct mtk_mbox_pin_send *msend;
struct mbox_chan *chan;
unsigned int i, j, count;
mchan = kcalloc(num, sizeof(struct mtk_mbox_chan), GFP_KERNEL);
if (!mchan)
return -ENOMEM;
chan = kcalloc(num, sizeof(struct mbox_chan), GFP_KERNEL);
if (!chan) {
kfree(mchan);
return -ENOMEM;
}
count = mbdev->send_count;
for (i = 0; i < num; i++) {
chan[i].con_priv = &mchan[i];
mchan[i].chan = &chan[i];
mchan[i].mbdev = mbdev;
mchan[i].ops = &mtk_mbox_ops;
for (j = 0; j < count; j++) {
msend = &(mbdev->pin_send_table[j]);
if (i == msend->chan_id) {
mchan[i].mbox = msend->mbox;
mchan[i].send_slot = msend->offset;
mchan[i].send_slot_size = msend->msg_size;
mchan[i].send_pin_index = msend->pin_index;
mchan[i].send_pin_offset = j;
}
}
}
mboxctrl->ops = &mtk_ipi_mbox_chan_ops;
mboxctrl->chans = chan;
mboxctrl->of_xlate = mtk_rpmsg_mbox_xlate;
return 0;
}