root/drivers/mailbox/pl320-ipc.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. set_destination
  2. clear_destination
  3. __ipc_send
  4. __ipc_rcv
  5. pl320_ipc_transmit
  6. ipc_handler
  7. pl320_ipc_register_notifier
  8. pl320_ipc_unregister_notifier
  9. pl320_probe
  10. ipc_init

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * Copyright 2012 Calxeda, Inc.
   4  */
   5 #include <linux/types.h>
   6 #include <linux/err.h>
   7 #include <linux/delay.h>
   8 #include <linux/export.h>
   9 #include <linux/io.h>
  10 #include <linux/interrupt.h>
  11 #include <linux/completion.h>
  12 #include <linux/mutex.h>
  13 #include <linux/notifier.h>
  14 #include <linux/spinlock.h>
  15 #include <linux/device.h>
  16 #include <linux/amba/bus.h>
  17 
  18 #include <linux/pl320-ipc.h>
  19 
  20 #define IPCMxSOURCE(m)          ((m) * 0x40)
  21 #define IPCMxDSET(m)            (((m) * 0x40) + 0x004)
  22 #define IPCMxDCLEAR(m)          (((m) * 0x40) + 0x008)
  23 #define IPCMxDSTATUS(m)         (((m) * 0x40) + 0x00C)
  24 #define IPCMxMODE(m)            (((m) * 0x40) + 0x010)
  25 #define IPCMxMSET(m)            (((m) * 0x40) + 0x014)
  26 #define IPCMxMCLEAR(m)          (((m) * 0x40) + 0x018)
  27 #define IPCMxMSTATUS(m)         (((m) * 0x40) + 0x01C)
  28 #define IPCMxSEND(m)            (((m) * 0x40) + 0x020)
  29 #define IPCMxDR(m, dr)          (((m) * 0x40) + ((dr) * 4) + 0x024)
  30 
  31 #define IPCMMIS(irq)            (((irq) * 8) + 0x800)
  32 #define IPCMRIS(irq)            (((irq) * 8) + 0x804)
  33 
  34 #define MBOX_MASK(n)            (1 << (n))
  35 #define IPC_TX_MBOX             1
  36 #define IPC_RX_MBOX             2
  37 
  38 #define CHAN_MASK(n)            (1 << (n))
  39 #define A9_SOURCE               1
  40 #define M3_SOURCE               0
  41 
  42 static void __iomem *ipc_base;
  43 static int ipc_irq;
  44 static DEFINE_MUTEX(ipc_m1_lock);
  45 static DECLARE_COMPLETION(ipc_completion);
  46 static ATOMIC_NOTIFIER_HEAD(ipc_notifier);
  47 
  48 static inline void set_destination(int source, int mbox)
  49 {
  50         writel_relaxed(CHAN_MASK(source), ipc_base + IPCMxDSET(mbox));
  51         writel_relaxed(CHAN_MASK(source), ipc_base + IPCMxMSET(mbox));
  52 }
  53 
  54 static inline void clear_destination(int source, int mbox)
  55 {
  56         writel_relaxed(CHAN_MASK(source), ipc_base + IPCMxDCLEAR(mbox));
  57         writel_relaxed(CHAN_MASK(source), ipc_base + IPCMxMCLEAR(mbox));
  58 }
  59 
  60 static void __ipc_send(int mbox, u32 *data)
  61 {
  62         int i;
  63         for (i = 0; i < 7; i++)
  64                 writel_relaxed(data[i], ipc_base + IPCMxDR(mbox, i));
  65         writel_relaxed(0x1, ipc_base + IPCMxSEND(mbox));
  66 }
  67 
  68 static u32 __ipc_rcv(int mbox, u32 *data)
  69 {
  70         int i;
  71         for (i = 0; i < 7; i++)
  72                 data[i] = readl_relaxed(ipc_base + IPCMxDR(mbox, i));
  73         return data[1];
  74 }
  75 
  76 /* blocking implmentation from the A9 side, not usuable in interrupts! */
  77 int pl320_ipc_transmit(u32 *data)
  78 {
  79         int ret;
  80 
  81         mutex_lock(&ipc_m1_lock);
  82 
  83         init_completion(&ipc_completion);
  84         __ipc_send(IPC_TX_MBOX, data);
  85         ret = wait_for_completion_timeout(&ipc_completion,
  86                                           msecs_to_jiffies(1000));
  87         if (ret == 0) {
  88                 ret = -ETIMEDOUT;
  89                 goto out;
  90         }
  91 
  92         ret = __ipc_rcv(IPC_TX_MBOX, data);
  93 out:
  94         mutex_unlock(&ipc_m1_lock);
  95         return ret;
  96 }
  97 EXPORT_SYMBOL_GPL(pl320_ipc_transmit);
  98 
  99 static irqreturn_t ipc_handler(int irq, void *dev)
 100 {
 101         u32 irq_stat;
 102         u32 data[7];
 103 
 104         irq_stat = readl_relaxed(ipc_base + IPCMMIS(1));
 105         if (irq_stat & MBOX_MASK(IPC_TX_MBOX)) {
 106                 writel_relaxed(0, ipc_base + IPCMxSEND(IPC_TX_MBOX));
 107                 complete(&ipc_completion);
 108         }
 109         if (irq_stat & MBOX_MASK(IPC_RX_MBOX)) {
 110                 __ipc_rcv(IPC_RX_MBOX, data);
 111                 atomic_notifier_call_chain(&ipc_notifier, data[0], data + 1);
 112                 writel_relaxed(2, ipc_base + IPCMxSEND(IPC_RX_MBOX));
 113         }
 114 
 115         return IRQ_HANDLED;
 116 }
 117 
 118 int pl320_ipc_register_notifier(struct notifier_block *nb)
 119 {
 120         return atomic_notifier_chain_register(&ipc_notifier, nb);
 121 }
 122 EXPORT_SYMBOL_GPL(pl320_ipc_register_notifier);
 123 
 124 int pl320_ipc_unregister_notifier(struct notifier_block *nb)
 125 {
 126         return atomic_notifier_chain_unregister(&ipc_notifier, nb);
 127 }
 128 EXPORT_SYMBOL_GPL(pl320_ipc_unregister_notifier);
 129 
 130 static int pl320_probe(struct amba_device *adev, const struct amba_id *id)
 131 {
 132         int ret;
 133 
 134         ipc_base = ioremap(adev->res.start, resource_size(&adev->res));
 135         if (ipc_base == NULL)
 136                 return -ENOMEM;
 137 
 138         writel_relaxed(0, ipc_base + IPCMxSEND(IPC_TX_MBOX));
 139 
 140         ipc_irq = adev->irq[0];
 141         ret = request_irq(ipc_irq, ipc_handler, 0, dev_name(&adev->dev), NULL);
 142         if (ret < 0)
 143                 goto err;
 144 
 145         /* Init slow mailbox */
 146         writel_relaxed(CHAN_MASK(A9_SOURCE),
 147                        ipc_base + IPCMxSOURCE(IPC_TX_MBOX));
 148         writel_relaxed(CHAN_MASK(M3_SOURCE),
 149                        ipc_base + IPCMxDSET(IPC_TX_MBOX));
 150         writel_relaxed(CHAN_MASK(M3_SOURCE) | CHAN_MASK(A9_SOURCE),
 151                        ipc_base + IPCMxMSET(IPC_TX_MBOX));
 152 
 153         /* Init receive mailbox */
 154         writel_relaxed(CHAN_MASK(M3_SOURCE),
 155                        ipc_base + IPCMxSOURCE(IPC_RX_MBOX));
 156         writel_relaxed(CHAN_MASK(A9_SOURCE),
 157                        ipc_base + IPCMxDSET(IPC_RX_MBOX));
 158         writel_relaxed(CHAN_MASK(M3_SOURCE) | CHAN_MASK(A9_SOURCE),
 159                        ipc_base + IPCMxMSET(IPC_RX_MBOX));
 160 
 161         return 0;
 162 err:
 163         iounmap(ipc_base);
 164         return ret;
 165 }
 166 
 167 static struct amba_id pl320_ids[] = {
 168         {
 169                 .id     = 0x00041320,
 170                 .mask   = 0x000fffff,
 171         },
 172         { 0, 0 },
 173 };
 174 
 175 static struct amba_driver pl320_driver = {
 176         .drv = {
 177                 .name   = "pl320",
 178         },
 179         .id_table       = pl320_ids,
 180         .probe          = pl320_probe,
 181 };
 182 
 183 static int __init ipc_init(void)
 184 {
 185         return amba_driver_register(&pl320_driver);
 186 }
 187 subsys_initcall(ipc_init);

/* [<][>][^][v][top][bottom][index][help] */