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