352 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2013-2015 Fujitsu Semiconductor Ltd.4 * Copyright (C) 2015 Linaro Ltd.5 * Based on ARM MHU driver by Jassi Brar <jaswinder.singh@linaro.org>6 * Copyright (C) 2020 ARM Ltd.7 */8 9#include <linux/amba/bus.h>10#include <linux/device.h>11#include <linux/err.h>12#include <linux/interrupt.h>13#include <linux/io.h>14#include <linux/kernel.h>15#include <linux/mailbox_controller.h>16#include <linux/module.h>17#include <linux/of.h>18 19#define INTR_STAT_OFS 0x020#define INTR_SET_OFS 0x821#define INTR_CLR_OFS 0x1022 23#define MHU_LP_OFFSET 0x024#define MHU_HP_OFFSET 0x2025#define MHU_SEC_OFFSET 0x20026#define TX_REG_OFFSET 0x10027 28#define MHU_CHANS 3 /* Secure, Non-Secure High and Low Priority */29#define MHU_CHAN_MAX 20 /* Max channels to save on unused RAM */30#define MHU_NUM_DOORBELLS 3231 32struct mhu_db_link {33 unsigned int irq;34 void __iomem *tx_reg;35 void __iomem *rx_reg;36};37 38struct arm_mhu {39 void __iomem *base;40 struct mhu_db_link mlink[MHU_CHANS];41 struct mbox_controller mbox;42 struct device *dev;43};44 45/**46 * struct mhu_db_channel - ARM MHU Mailbox allocated channel information47 *48 * @mhu: Pointer to parent mailbox device49 * @pchan: Physical channel within which this doorbell resides in50 * @doorbell: doorbell number pertaining to this channel51 */52struct mhu_db_channel {53 struct arm_mhu *mhu;54 unsigned int pchan;55 unsigned int doorbell;56};57 58static inline struct mbox_chan *59mhu_db_mbox_to_channel(struct mbox_controller *mbox, unsigned int pchan,60 unsigned int doorbell)61{62 int i;63 struct mhu_db_channel *chan_info;64 65 for (i = 0; i < mbox->num_chans; i++) {66 chan_info = mbox->chans[i].con_priv;67 if (chan_info && chan_info->pchan == pchan &&68 chan_info->doorbell == doorbell)69 return &mbox->chans[i];70 }71 72 return NULL;73}74 75static void mhu_db_mbox_clear_irq(struct mbox_chan *chan)76{77 struct mhu_db_channel *chan_info = chan->con_priv;78 void __iomem *base = chan_info->mhu->mlink[chan_info->pchan].rx_reg;79 80 writel_relaxed(BIT(chan_info->doorbell), base + INTR_CLR_OFS);81}82 83static unsigned int mhu_db_mbox_irq_to_pchan_num(struct arm_mhu *mhu, int irq)84{85 unsigned int pchan;86 87 for (pchan = 0; pchan < MHU_CHANS; pchan++)88 if (mhu->mlink[pchan].irq == irq)89 break;90 return pchan;91}92 93static struct mbox_chan *94mhu_db_mbox_irq_to_channel(struct arm_mhu *mhu, unsigned int pchan)95{96 unsigned long bits;97 unsigned int doorbell;98 struct mbox_chan *chan = NULL;99 struct mbox_controller *mbox = &mhu->mbox;100 void __iomem *base = mhu->mlink[pchan].rx_reg;101 102 bits = readl_relaxed(base + INTR_STAT_OFS);103 if (!bits)104 /* No IRQs fired in specified physical channel */105 return NULL;106 107 /* An IRQ has fired, find the associated channel */108 for (doorbell = 0; bits; doorbell++) {109 if (!test_and_clear_bit(doorbell, &bits))110 continue;111 112 chan = mhu_db_mbox_to_channel(mbox, pchan, doorbell);113 if (chan)114 break;115 dev_err(mbox->dev,116 "Channel not registered: pchan: %d doorbell: %d\n",117 pchan, doorbell);118 }119 120 return chan;121}122 123static irqreturn_t mhu_db_mbox_rx_handler(int irq, void *data)124{125 struct mbox_chan *chan;126 struct arm_mhu *mhu = data;127 unsigned int pchan = mhu_db_mbox_irq_to_pchan_num(mhu, irq);128 129 while (NULL != (chan = mhu_db_mbox_irq_to_channel(mhu, pchan))) {130 mbox_chan_received_data(chan, NULL);131 mhu_db_mbox_clear_irq(chan);132 }133 134 return IRQ_HANDLED;135}136 137static bool mhu_db_last_tx_done(struct mbox_chan *chan)138{139 struct mhu_db_channel *chan_info = chan->con_priv;140 void __iomem *base = chan_info->mhu->mlink[chan_info->pchan].tx_reg;141 142 if (readl_relaxed(base + INTR_STAT_OFS) & BIT(chan_info->doorbell))143 return false;144 145 return true;146}147 148static int mhu_db_send_data(struct mbox_chan *chan, void *data)149{150 struct mhu_db_channel *chan_info = chan->con_priv;151 void __iomem *base = chan_info->mhu->mlink[chan_info->pchan].tx_reg;152 153 /* Send event to co-processor */154 writel_relaxed(BIT(chan_info->doorbell), base + INTR_SET_OFS);155 156 return 0;157}158 159static int mhu_db_startup(struct mbox_chan *chan)160{161 mhu_db_mbox_clear_irq(chan);162 return 0;163}164 165static void mhu_db_shutdown(struct mbox_chan *chan)166{167 struct mhu_db_channel *chan_info = chan->con_priv;168 struct mbox_controller *mbox = &chan_info->mhu->mbox;169 int i;170 171 for (i = 0; i < mbox->num_chans; i++)172 if (chan == &mbox->chans[i])173 break;174 175 if (mbox->num_chans == i) {176 dev_warn(mbox->dev, "Request to free non-existent channel\n");177 return;178 }179 180 /* Reset channel */181 mhu_db_mbox_clear_irq(chan);182 devm_kfree(mbox->dev, chan->con_priv);183 chan->con_priv = NULL;184}185 186static struct mbox_chan *mhu_db_mbox_xlate(struct mbox_controller *mbox,187 const struct of_phandle_args *spec)188{189 struct arm_mhu *mhu = dev_get_drvdata(mbox->dev);190 struct mhu_db_channel *chan_info;191 struct mbox_chan *chan;192 unsigned int pchan = spec->args[0];193 unsigned int doorbell = spec->args[1];194 int i;195 196 /* Bounds checking */197 if (pchan >= MHU_CHANS || doorbell >= MHU_NUM_DOORBELLS) {198 dev_err(mbox->dev,199 "Invalid channel requested pchan: %d doorbell: %d\n",200 pchan, doorbell);201 return ERR_PTR(-EINVAL);202 }203 204 /* Is requested channel free? */205 chan = mhu_db_mbox_to_channel(mbox, pchan, doorbell);206 if (chan) {207 dev_err(mbox->dev, "Channel in use: pchan: %d doorbell: %d\n",208 pchan, doorbell);209 return ERR_PTR(-EBUSY);210 }211 212 /* Find the first free slot */213 for (i = 0; i < mbox->num_chans; i++)214 if (!mbox->chans[i].con_priv)215 break;216 217 if (mbox->num_chans == i) {218 dev_err(mbox->dev, "No free channels left\n");219 return ERR_PTR(-EBUSY);220 }221 222 chan = &mbox->chans[i];223 224 chan_info = devm_kzalloc(mbox->dev, sizeof(*chan_info), GFP_KERNEL);225 if (!chan_info)226 return ERR_PTR(-ENOMEM);227 228 chan_info->mhu = mhu;229 chan_info->pchan = pchan;230 chan_info->doorbell = doorbell;231 232 chan->con_priv = chan_info;233 234 dev_dbg(mbox->dev, "mbox: created channel phys: %d doorbell: %d\n",235 pchan, doorbell);236 237 return chan;238}239 240static const struct mbox_chan_ops mhu_db_ops = {241 .send_data = mhu_db_send_data,242 .startup = mhu_db_startup,243 .shutdown = mhu_db_shutdown,244 .last_tx_done = mhu_db_last_tx_done,245};246 247static int mhu_db_probe(struct amba_device *adev, const struct amba_id *id)248{249 u32 cell_count;250 int i, err, max_chans;251 struct arm_mhu *mhu;252 struct mbox_chan *chans;253 struct device *dev = &adev->dev;254 struct device_node *np = dev->of_node;255 int mhu_reg[MHU_CHANS] = {256 MHU_LP_OFFSET, MHU_HP_OFFSET, MHU_SEC_OFFSET,257 };258 259 if (!of_device_is_compatible(np, "arm,mhu-doorbell"))260 return -ENODEV;261 262 err = of_property_read_u32(np, "#mbox-cells", &cell_count);263 if (err) {264 dev_err(dev, "failed to read #mbox-cells in '%pOF'\n", np);265 return err;266 }267 268 if (cell_count == 2) {269 max_chans = MHU_CHAN_MAX;270 } else {271 dev_err(dev, "incorrect value of #mbox-cells in '%pOF'\n", np);272 return -EINVAL;273 }274 275 mhu = devm_kzalloc(dev, sizeof(*mhu), GFP_KERNEL);276 if (!mhu)277 return -ENOMEM;278 279 mhu->base = devm_ioremap_resource(dev, &adev->res);280 if (IS_ERR(mhu->base))281 return PTR_ERR(mhu->base);282 283 chans = devm_kcalloc(dev, max_chans, sizeof(*chans), GFP_KERNEL);284 if (!chans)285 return -ENOMEM;286 287 mhu->dev = dev;288 mhu->mbox.dev = dev;289 mhu->mbox.chans = chans;290 mhu->mbox.num_chans = max_chans;291 mhu->mbox.txdone_irq = false;292 mhu->mbox.txdone_poll = true;293 mhu->mbox.txpoll_period = 1;294 295 mhu->mbox.of_xlate = mhu_db_mbox_xlate;296 amba_set_drvdata(adev, mhu);297 298 mhu->mbox.ops = &mhu_db_ops;299 300 err = devm_mbox_controller_register(dev, &mhu->mbox);301 if (err) {302 dev_err(dev, "Failed to register mailboxes %d\n", err);303 return err;304 }305 306 for (i = 0; i < MHU_CHANS; i++) {307 int irq = mhu->mlink[i].irq = adev->irq[i];308 309 if (irq <= 0) {310 dev_dbg(dev, "No IRQ found for Channel %d\n", i);311 continue;312 }313 314 mhu->mlink[i].rx_reg = mhu->base + mhu_reg[i];315 mhu->mlink[i].tx_reg = mhu->mlink[i].rx_reg + TX_REG_OFFSET;316 317 err = devm_request_threaded_irq(dev, irq, NULL,318 mhu_db_mbox_rx_handler,319 IRQF_ONESHOT, "mhu_db_link", mhu);320 if (err) {321 dev_err(dev, "Can't claim IRQ %d\n", irq);322 mbox_controller_unregister(&mhu->mbox);323 return err;324 }325 }326 327 dev_info(dev, "ARM MHU Doorbell mailbox registered\n");328 return 0;329}330 331static struct amba_id mhu_ids[] = {332 {333 .id = 0x1bb098,334 .mask = 0xffffff,335 },336 { 0, 0 },337};338MODULE_DEVICE_TABLE(amba, mhu_ids);339 340static struct amba_driver arm_mhu_db_driver = {341 .drv = {342 .name = "mhu-doorbell",343 },344 .id_table = mhu_ids,345 .probe = mhu_db_probe,346};347module_amba_driver(arm_mhu_db_driver);348 349MODULE_LICENSE("GPL v2");350MODULE_DESCRIPTION("ARM MHU Doorbell Driver");351MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");352