/* * 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 #include #include #include #include #include #include #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; }