162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2013-2015 Fujitsu Semiconductor Ltd. 462306a36Sopenharmony_ci * Copyright (C) 2015 Linaro Ltd. 562306a36Sopenharmony_ci * Author: Jassi Brar <jaswinder.singh@linaro.org> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/amba/bus.h> 962306a36Sopenharmony_ci#include <linux/device.h> 1062306a36Sopenharmony_ci#include <linux/err.h> 1162306a36Sopenharmony_ci#include <linux/interrupt.h> 1262306a36Sopenharmony_ci#include <linux/io.h> 1362306a36Sopenharmony_ci#include <linux/mailbox_controller.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/of.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define INTR_STAT_OFS 0x0 1862306a36Sopenharmony_ci#define INTR_SET_OFS 0x8 1962306a36Sopenharmony_ci#define INTR_CLR_OFS 0x10 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define MHU_LP_OFFSET 0x0 2262306a36Sopenharmony_ci#define MHU_HP_OFFSET 0x20 2362306a36Sopenharmony_ci#define MHU_SEC_OFFSET 0x200 2462306a36Sopenharmony_ci#define TX_REG_OFFSET 0x100 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define MHU_CHANS 3 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistruct mhu_link { 2962306a36Sopenharmony_ci unsigned irq; 3062306a36Sopenharmony_ci void __iomem *tx_reg; 3162306a36Sopenharmony_ci void __iomem *rx_reg; 3262306a36Sopenharmony_ci}; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistruct arm_mhu { 3562306a36Sopenharmony_ci void __iomem *base; 3662306a36Sopenharmony_ci struct mhu_link mlink[MHU_CHANS]; 3762306a36Sopenharmony_ci struct mbox_chan chan[MHU_CHANS]; 3862306a36Sopenharmony_ci struct mbox_controller mbox; 3962306a36Sopenharmony_ci}; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic irqreturn_t mhu_rx_interrupt(int irq, void *p) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci struct mbox_chan *chan = p; 4462306a36Sopenharmony_ci struct mhu_link *mlink = chan->con_priv; 4562306a36Sopenharmony_ci u32 val; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci val = readl_relaxed(mlink->rx_reg + INTR_STAT_OFS); 4862306a36Sopenharmony_ci if (!val) 4962306a36Sopenharmony_ci return IRQ_NONE; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci mbox_chan_received_data(chan, (void *)&val); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci writel_relaxed(val, mlink->rx_reg + INTR_CLR_OFS); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci return IRQ_HANDLED; 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic bool mhu_last_tx_done(struct mbox_chan *chan) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci struct mhu_link *mlink = chan->con_priv; 6162306a36Sopenharmony_ci u32 val = readl_relaxed(mlink->tx_reg + INTR_STAT_OFS); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci return (val == 0); 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic int mhu_send_data(struct mbox_chan *chan, void *data) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci struct mhu_link *mlink = chan->con_priv; 6962306a36Sopenharmony_ci u32 *arg = data; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci writel_relaxed(*arg, mlink->tx_reg + INTR_SET_OFS); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci return 0; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic int mhu_startup(struct mbox_chan *chan) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci struct mhu_link *mlink = chan->con_priv; 7962306a36Sopenharmony_ci u32 val; 8062306a36Sopenharmony_ci int ret; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci val = readl_relaxed(mlink->tx_reg + INTR_STAT_OFS); 8362306a36Sopenharmony_ci writel_relaxed(val, mlink->tx_reg + INTR_CLR_OFS); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci ret = request_irq(mlink->irq, mhu_rx_interrupt, 8662306a36Sopenharmony_ci IRQF_SHARED, "mhu_link", chan); 8762306a36Sopenharmony_ci if (ret) { 8862306a36Sopenharmony_ci dev_err(chan->mbox->dev, 8962306a36Sopenharmony_ci "Unable to acquire IRQ %d\n", mlink->irq); 9062306a36Sopenharmony_ci return ret; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci return 0; 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic void mhu_shutdown(struct mbox_chan *chan) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci struct mhu_link *mlink = chan->con_priv; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci free_irq(mlink->irq, chan); 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic const struct mbox_chan_ops mhu_ops = { 10462306a36Sopenharmony_ci .send_data = mhu_send_data, 10562306a36Sopenharmony_ci .startup = mhu_startup, 10662306a36Sopenharmony_ci .shutdown = mhu_shutdown, 10762306a36Sopenharmony_ci .last_tx_done = mhu_last_tx_done, 10862306a36Sopenharmony_ci}; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic int mhu_probe(struct amba_device *adev, const struct amba_id *id) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci int i, err; 11362306a36Sopenharmony_ci struct arm_mhu *mhu; 11462306a36Sopenharmony_ci struct device *dev = &adev->dev; 11562306a36Sopenharmony_ci int mhu_reg[MHU_CHANS] = {MHU_LP_OFFSET, MHU_HP_OFFSET, MHU_SEC_OFFSET}; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (!of_device_is_compatible(dev->of_node, "arm,mhu")) 11862306a36Sopenharmony_ci return -ENODEV; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* Allocate memory for device */ 12162306a36Sopenharmony_ci mhu = devm_kzalloc(dev, sizeof(*mhu), GFP_KERNEL); 12262306a36Sopenharmony_ci if (!mhu) 12362306a36Sopenharmony_ci return -ENOMEM; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci mhu->base = devm_ioremap_resource(dev, &adev->res); 12662306a36Sopenharmony_ci if (IS_ERR(mhu->base)) 12762306a36Sopenharmony_ci return PTR_ERR(mhu->base); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci for (i = 0; i < MHU_CHANS; i++) { 13062306a36Sopenharmony_ci mhu->chan[i].con_priv = &mhu->mlink[i]; 13162306a36Sopenharmony_ci mhu->mlink[i].irq = adev->irq[i]; 13262306a36Sopenharmony_ci mhu->mlink[i].rx_reg = mhu->base + mhu_reg[i]; 13362306a36Sopenharmony_ci mhu->mlink[i].tx_reg = mhu->mlink[i].rx_reg + TX_REG_OFFSET; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci mhu->mbox.dev = dev; 13762306a36Sopenharmony_ci mhu->mbox.chans = &mhu->chan[0]; 13862306a36Sopenharmony_ci mhu->mbox.num_chans = MHU_CHANS; 13962306a36Sopenharmony_ci mhu->mbox.ops = &mhu_ops; 14062306a36Sopenharmony_ci mhu->mbox.txdone_irq = false; 14162306a36Sopenharmony_ci mhu->mbox.txdone_poll = true; 14262306a36Sopenharmony_ci mhu->mbox.txpoll_period = 1; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci amba_set_drvdata(adev, mhu); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci err = devm_mbox_controller_register(dev, &mhu->mbox); 14762306a36Sopenharmony_ci if (err) { 14862306a36Sopenharmony_ci dev_err(dev, "Failed to register mailboxes %d\n", err); 14962306a36Sopenharmony_ci return err; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci dev_info(dev, "ARM MHU Mailbox registered\n"); 15362306a36Sopenharmony_ci return 0; 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic struct amba_id mhu_ids[] = { 15762306a36Sopenharmony_ci { 15862306a36Sopenharmony_ci .id = 0x1bb098, 15962306a36Sopenharmony_ci .mask = 0xffffff, 16062306a36Sopenharmony_ci }, 16162306a36Sopenharmony_ci { 0, 0 }, 16262306a36Sopenharmony_ci}; 16362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(amba, mhu_ids); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic struct amba_driver arm_mhu_driver = { 16662306a36Sopenharmony_ci .drv = { 16762306a36Sopenharmony_ci .name = "mhu", 16862306a36Sopenharmony_ci }, 16962306a36Sopenharmony_ci .id_table = mhu_ids, 17062306a36Sopenharmony_ci .probe = mhu_probe, 17162306a36Sopenharmony_ci}; 17262306a36Sopenharmony_cimodule_amba_driver(arm_mhu_driver); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 17562306a36Sopenharmony_ciMODULE_DESCRIPTION("ARM MHU Driver"); 17662306a36Sopenharmony_ciMODULE_AUTHOR("Jassi Brar <jassisinghbrar@gmail.com>"); 177