linux/drivers/mailbox/mailbox-xgene-slimpro.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * APM X-Gene SLIMpro MailBox Driver
   4 *
   5 * Copyright (c) 2015, Applied Micro Circuits Corporation
   6 * Author: Feng Kan fkan@apm.com
   7 */
   8#include <linux/acpi.h>
   9#include <linux/delay.h>
  10#include <linux/interrupt.h>
  11#include <linux/io.h>
  12#include <linux/mailbox_controller.h>
  13#include <linux/module.h>
  14#include <linux/of.h>
  15#include <linux/platform_device.h>
  16#include <linux/spinlock.h>
  17
  18#define MBOX_CON_NAME                   "slimpro-mbox"
  19#define MBOX_REG_SET_OFFSET             0x1000
  20#define MBOX_CNT                        8
  21#define MBOX_STATUS_AVAIL_MASK          BIT(16)
  22#define MBOX_STATUS_ACK_MASK            BIT(0)
  23
  24/* Configuration and Status Registers */
  25#define REG_DB_IN               0x00
  26#define REG_DB_DIN0             0x04
  27#define REG_DB_DIN1             0x08
  28#define REG_DB_OUT              0x10
  29#define REG_DB_DOUT0            0x14
  30#define REG_DB_DOUT1            0x18
  31#define REG_DB_STAT             0x20
  32#define REG_DB_STATMASK         0x24
  33
  34/**
  35 * X-Gene SlimPRO mailbox channel information
  36 *
  37 * @dev:        Device to which it is attached
  38 * @chan:       Pointer to mailbox communication channel
  39 * @reg:        Base address to access channel registers
  40 * @irq:        Interrupt number of the channel
  41 * @rx_msg:     Received message storage
  42 */
  43struct slimpro_mbox_chan {
  44        struct device           *dev;
  45        struct mbox_chan        *chan;
  46        void __iomem            *reg;
  47        int                     irq;
  48        u32                     rx_msg[3];
  49};
  50
  51/**
  52 * X-Gene SlimPRO Mailbox controller data
  53 *
  54 * X-Gene SlimPRO Mailbox controller has 8 commnunication channels.
  55 * Each channel has a separate IRQ number assgined to it.
  56 *
  57 * @mb_ctrl:    Representation of the commnunication channel controller
  58 * @mc:         Array of SlimPRO mailbox channels of the controller
  59 * @chans:      Array of mailbox communication channels
  60 *
  61 */
  62struct slimpro_mbox {
  63        struct mbox_controller          mb_ctrl;
  64        struct slimpro_mbox_chan        mc[MBOX_CNT];
  65        struct mbox_chan                chans[MBOX_CNT];
  66};
  67
  68static void mb_chan_send_msg(struct slimpro_mbox_chan *mb_chan, u32 *msg)
  69{
  70        writel(msg[1], mb_chan->reg + REG_DB_DOUT0);
  71        writel(msg[2], mb_chan->reg + REG_DB_DOUT1);
  72        writel(msg[0], mb_chan->reg + REG_DB_OUT);
  73}
  74
  75static void mb_chan_recv_msg(struct slimpro_mbox_chan *mb_chan)
  76{
  77        mb_chan->rx_msg[1] = readl(mb_chan->reg + REG_DB_DIN0);
  78        mb_chan->rx_msg[2] = readl(mb_chan->reg + REG_DB_DIN1);
  79        mb_chan->rx_msg[0] = readl(mb_chan->reg + REG_DB_IN);
  80}
  81
  82static int mb_chan_status_ack(struct slimpro_mbox_chan *mb_chan)
  83{
  84        u32 val = readl(mb_chan->reg + REG_DB_STAT);
  85
  86        if (val & MBOX_STATUS_ACK_MASK) {
  87                writel(MBOX_STATUS_ACK_MASK, mb_chan->reg + REG_DB_STAT);
  88                return 1;
  89        }
  90        return 0;
  91}
  92
  93static int mb_chan_status_avail(struct slimpro_mbox_chan *mb_chan)
  94{
  95        u32 val = readl(mb_chan->reg + REG_DB_STAT);
  96
  97        if (val & MBOX_STATUS_AVAIL_MASK) {
  98                mb_chan_recv_msg(mb_chan);
  99                writel(MBOX_STATUS_AVAIL_MASK, mb_chan->reg + REG_DB_STAT);
 100                return 1;
 101        }
 102        return 0;
 103}
 104
 105static irqreturn_t slimpro_mbox_irq(int irq, void *id)
 106{
 107        struct slimpro_mbox_chan *mb_chan = id;
 108
 109        if (mb_chan_status_ack(mb_chan))
 110                mbox_chan_txdone(mb_chan->chan, 0);
 111
 112        if (mb_chan_status_avail(mb_chan))
 113                mbox_chan_received_data(mb_chan->chan, mb_chan->rx_msg);
 114
 115        return IRQ_HANDLED;
 116}
 117
 118static int slimpro_mbox_send_data(struct mbox_chan *chan, void *msg)
 119{
 120        struct slimpro_mbox_chan *mb_chan = chan->con_priv;
 121
 122        mb_chan_send_msg(mb_chan, msg);
 123        return 0;
 124}
 125
 126static int slimpro_mbox_startup(struct mbox_chan *chan)
 127{
 128        struct slimpro_mbox_chan *mb_chan = chan->con_priv;
 129        int rc;
 130        u32 val;
 131
 132        rc = devm_request_irq(mb_chan->dev, mb_chan->irq, slimpro_mbox_irq, 0,
 133                              MBOX_CON_NAME, mb_chan);
 134        if (unlikely(rc)) {
 135                dev_err(mb_chan->dev, "failed to register mailbox interrupt %d\n",
 136                        mb_chan->irq);
 137                return rc;
 138        }
 139
 140        /* Enable HW interrupt */
 141        writel(MBOX_STATUS_ACK_MASK | MBOX_STATUS_AVAIL_MASK,
 142               mb_chan->reg + REG_DB_STAT);
 143        /* Unmask doorbell status interrupt */
 144        val = readl(mb_chan->reg + REG_DB_STATMASK);
 145        val &= ~(MBOX_STATUS_ACK_MASK | MBOX_STATUS_AVAIL_MASK);
 146        writel(val, mb_chan->reg + REG_DB_STATMASK);
 147
 148        return 0;
 149}
 150
 151static void slimpro_mbox_shutdown(struct mbox_chan *chan)
 152{
 153        struct slimpro_mbox_chan *mb_chan = chan->con_priv;
 154        u32 val;
 155
 156        /* Mask doorbell status interrupt */
 157        val = readl(mb_chan->reg + REG_DB_STATMASK);
 158        val |= (MBOX_STATUS_ACK_MASK | MBOX_STATUS_AVAIL_MASK);
 159        writel(val, mb_chan->reg + REG_DB_STATMASK);
 160
 161        devm_free_irq(mb_chan->dev, mb_chan->irq, mb_chan);
 162}
 163
 164static const struct mbox_chan_ops slimpro_mbox_ops = {
 165        .send_data = slimpro_mbox_send_data,
 166        .startup = slimpro_mbox_startup,
 167        .shutdown = slimpro_mbox_shutdown,
 168};
 169
 170static int slimpro_mbox_probe(struct platform_device *pdev)
 171{
 172        struct slimpro_mbox *ctx;
 173        struct resource *regs;
 174        void __iomem *mb_base;
 175        int rc;
 176        int i;
 177
 178        ctx = devm_kzalloc(&pdev->dev, sizeof(struct slimpro_mbox), GFP_KERNEL);
 179        if (!ctx)
 180                return -ENOMEM;
 181
 182        platform_set_drvdata(pdev, ctx);
 183
 184        regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 185        mb_base = devm_ioremap_resource(&pdev->dev, regs);
 186        if (IS_ERR(mb_base))
 187                return PTR_ERR(mb_base);
 188
 189        /* Setup mailbox links */
 190        for (i = 0; i < MBOX_CNT; i++) {
 191                ctx->mc[i].irq = platform_get_irq(pdev, i);
 192                if (ctx->mc[i].irq < 0) {
 193                        if (i == 0) {
 194                                dev_err(&pdev->dev, "no available IRQ\n");
 195                                return -EINVAL;
 196                        }
 197                        dev_info(&pdev->dev, "no IRQ for channel %d\n", i);
 198                        break;
 199                }
 200
 201                ctx->mc[i].dev = &pdev->dev;
 202                ctx->mc[i].reg = mb_base + i * MBOX_REG_SET_OFFSET;
 203                ctx->mc[i].chan = &ctx->chans[i];
 204                ctx->chans[i].con_priv = &ctx->mc[i];
 205        }
 206
 207        /* Setup mailbox controller */
 208        ctx->mb_ctrl.dev = &pdev->dev;
 209        ctx->mb_ctrl.chans = ctx->chans;
 210        ctx->mb_ctrl.txdone_irq = true;
 211        ctx->mb_ctrl.ops = &slimpro_mbox_ops;
 212        ctx->mb_ctrl.num_chans = i;
 213
 214        rc = devm_mbox_controller_register(&pdev->dev, &ctx->mb_ctrl);
 215        if (rc) {
 216                dev_err(&pdev->dev,
 217                        "APM X-Gene SLIMpro MailBox register failed:%d\n", rc);
 218                return rc;
 219        }
 220
 221        dev_info(&pdev->dev, "APM X-Gene SLIMpro MailBox registered\n");
 222        return 0;
 223}
 224
 225static const struct of_device_id slimpro_of_match[] = {
 226        {.compatible = "apm,xgene-slimpro-mbox" },
 227        { },
 228};
 229MODULE_DEVICE_TABLE(of, slimpro_of_match);
 230
 231#ifdef CONFIG_ACPI
 232static const struct acpi_device_id slimpro_acpi_ids[] = {
 233        {"APMC0D01", 0},
 234        {}
 235};
 236MODULE_DEVICE_TABLE(acpi, slimpro_acpi_ids);
 237#endif
 238
 239static struct platform_driver slimpro_mbox_driver = {
 240        .probe  = slimpro_mbox_probe,
 241        .driver = {
 242                .name = "xgene-slimpro-mbox",
 243                .of_match_table = of_match_ptr(slimpro_of_match),
 244                .acpi_match_table = ACPI_PTR(slimpro_acpi_ids)
 245        },
 246};
 247
 248static int __init slimpro_mbox_init(void)
 249{
 250        return platform_driver_register(&slimpro_mbox_driver);
 251}
 252
 253static void __exit slimpro_mbox_exit(void)
 254{
 255        platform_driver_unregister(&slimpro_mbox_driver);
 256}
 257
 258subsys_initcall(slimpro_mbox_init);
 259module_exit(slimpro_mbox_exit);
 260
 261MODULE_DESCRIPTION("APM X-Gene SLIMpro Mailbox Driver");
 262MODULE_LICENSE("GPL");
 263