19952f691SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
230058677SRob Herring /*
330058677SRob Herring * Copyright 2012 Calxeda, Inc.
430058677SRob Herring */
530058677SRob Herring #include <linux/types.h>
630058677SRob Herring #include <linux/err.h>
730058677SRob Herring #include <linux/delay.h>
830058677SRob Herring #include <linux/export.h>
930058677SRob Herring #include <linux/io.h>
1030058677SRob Herring #include <linux/interrupt.h>
1130058677SRob Herring #include <linux/completion.h>
1230058677SRob Herring #include <linux/mutex.h>
1330058677SRob Herring #include <linux/notifier.h>
1430058677SRob Herring #include <linux/spinlock.h>
1530058677SRob Herring #include <linux/device.h>
1630058677SRob Herring #include <linux/amba/bus.h>
1730058677SRob Herring
18f2fc42b6SSuman Anna #include <linux/pl320-ipc.h>
1930058677SRob Herring
2030058677SRob Herring #define IPCMxSOURCE(m) ((m) * 0x40)
2130058677SRob Herring #define IPCMxDSET(m) (((m) * 0x40) + 0x004)
2230058677SRob Herring #define IPCMxDCLEAR(m) (((m) * 0x40) + 0x008)
2330058677SRob Herring #define IPCMxDSTATUS(m) (((m) * 0x40) + 0x00C)
2430058677SRob Herring #define IPCMxMODE(m) (((m) * 0x40) + 0x010)
2530058677SRob Herring #define IPCMxMSET(m) (((m) * 0x40) + 0x014)
2630058677SRob Herring #define IPCMxMCLEAR(m) (((m) * 0x40) + 0x018)
2730058677SRob Herring #define IPCMxMSTATUS(m) (((m) * 0x40) + 0x01C)
2830058677SRob Herring #define IPCMxSEND(m) (((m) * 0x40) + 0x020)
2930058677SRob Herring #define IPCMxDR(m, dr) (((m) * 0x40) + ((dr) * 4) + 0x024)
3030058677SRob Herring
3130058677SRob Herring #define IPCMMIS(irq) (((irq) * 8) + 0x800)
3230058677SRob Herring #define IPCMRIS(irq) (((irq) * 8) + 0x804)
3330058677SRob Herring
3430058677SRob Herring #define MBOX_MASK(n) (1 << (n))
3530058677SRob Herring #define IPC_TX_MBOX 1
3630058677SRob Herring #define IPC_RX_MBOX 2
3730058677SRob Herring
3830058677SRob Herring #define CHAN_MASK(n) (1 << (n))
3930058677SRob Herring #define A9_SOURCE 1
4030058677SRob Herring #define M3_SOURCE 0
4130058677SRob Herring
4230058677SRob Herring static void __iomem *ipc_base;
4330058677SRob Herring static int ipc_irq;
4430058677SRob Herring static DEFINE_MUTEX(ipc_m1_lock);
4530058677SRob Herring static DECLARE_COMPLETION(ipc_completion);
4630058677SRob Herring static ATOMIC_NOTIFIER_HEAD(ipc_notifier);
4730058677SRob Herring
set_destination(int source,int mbox)4830058677SRob Herring static inline void set_destination(int source, int mbox)
4930058677SRob Herring {
509ac3e85aSBen Dooks writel_relaxed(CHAN_MASK(source), ipc_base + IPCMxDSET(mbox));
519ac3e85aSBen Dooks writel_relaxed(CHAN_MASK(source), ipc_base + IPCMxMSET(mbox));
5230058677SRob Herring }
5330058677SRob Herring
clear_destination(int source,int mbox)5430058677SRob Herring static inline void clear_destination(int source, int mbox)
5530058677SRob Herring {
569ac3e85aSBen Dooks writel_relaxed(CHAN_MASK(source), ipc_base + IPCMxDCLEAR(mbox));
579ac3e85aSBen Dooks writel_relaxed(CHAN_MASK(source), ipc_base + IPCMxMCLEAR(mbox));
5830058677SRob Herring }
5930058677SRob Herring
__ipc_send(int mbox,u32 * data)6030058677SRob Herring static void __ipc_send(int mbox, u32 *data)
6130058677SRob Herring {
6230058677SRob Herring int i;
6330058677SRob Herring for (i = 0; i < 7; i++)
649ac3e85aSBen Dooks writel_relaxed(data[i], ipc_base + IPCMxDR(mbox, i));
659ac3e85aSBen Dooks writel_relaxed(0x1, ipc_base + IPCMxSEND(mbox));
6630058677SRob Herring }
6730058677SRob Herring
__ipc_rcv(int mbox,u32 * data)6830058677SRob Herring static u32 __ipc_rcv(int mbox, u32 *data)
6930058677SRob Herring {
7030058677SRob Herring int i;
7130058677SRob Herring for (i = 0; i < 7; i++)
729ac3e85aSBen Dooks data[i] = readl_relaxed(ipc_base + IPCMxDR(mbox, i));
7330058677SRob Herring return data[1];
7430058677SRob Herring }
7530058677SRob Herring
76*9d2e8b93STom Saeger /* blocking implementation from the A9 side, not usable in interrupts! */
pl320_ipc_transmit(u32 * data)7730058677SRob Herring int pl320_ipc_transmit(u32 *data)
7830058677SRob Herring {
7930058677SRob Herring int ret;
8030058677SRob Herring
8130058677SRob Herring mutex_lock(&ipc_m1_lock);
8230058677SRob Herring
8330058677SRob Herring init_completion(&ipc_completion);
8430058677SRob Herring __ipc_send(IPC_TX_MBOX, data);
8530058677SRob Herring ret = wait_for_completion_timeout(&ipc_completion,
8630058677SRob Herring msecs_to_jiffies(1000));
8730058677SRob Herring if (ret == 0) {
8830058677SRob Herring ret = -ETIMEDOUT;
8930058677SRob Herring goto out;
9030058677SRob Herring }
9130058677SRob Herring
9230058677SRob Herring ret = __ipc_rcv(IPC_TX_MBOX, data);
9330058677SRob Herring out:
9430058677SRob Herring mutex_unlock(&ipc_m1_lock);
9530058677SRob Herring return ret;
9630058677SRob Herring }
9730058677SRob Herring EXPORT_SYMBOL_GPL(pl320_ipc_transmit);
9830058677SRob Herring
ipc_handler(int irq,void * dev)9930058677SRob Herring static irqreturn_t ipc_handler(int irq, void *dev)
10030058677SRob Herring {
10130058677SRob Herring u32 irq_stat;
10230058677SRob Herring u32 data[7];
10330058677SRob Herring
1049ac3e85aSBen Dooks irq_stat = readl_relaxed(ipc_base + IPCMMIS(1));
10530058677SRob Herring if (irq_stat & MBOX_MASK(IPC_TX_MBOX)) {
1069ac3e85aSBen Dooks writel_relaxed(0, ipc_base + IPCMxSEND(IPC_TX_MBOX));
10730058677SRob Herring complete(&ipc_completion);
10830058677SRob Herring }
10930058677SRob Herring if (irq_stat & MBOX_MASK(IPC_RX_MBOX)) {
11030058677SRob Herring __ipc_rcv(IPC_RX_MBOX, data);
11130058677SRob Herring atomic_notifier_call_chain(&ipc_notifier, data[0], data + 1);
1129ac3e85aSBen Dooks writel_relaxed(2, ipc_base + IPCMxSEND(IPC_RX_MBOX));
11330058677SRob Herring }
11430058677SRob Herring
11530058677SRob Herring return IRQ_HANDLED;
11630058677SRob Herring }
11730058677SRob Herring
pl320_ipc_register_notifier(struct notifier_block * nb)11830058677SRob Herring int pl320_ipc_register_notifier(struct notifier_block *nb)
11930058677SRob Herring {
12030058677SRob Herring return atomic_notifier_chain_register(&ipc_notifier, nb);
12130058677SRob Herring }
12230058677SRob Herring EXPORT_SYMBOL_GPL(pl320_ipc_register_notifier);
12330058677SRob Herring
pl320_ipc_unregister_notifier(struct notifier_block * nb)12430058677SRob Herring int pl320_ipc_unregister_notifier(struct notifier_block *nb)
12530058677SRob Herring {
12630058677SRob Herring return atomic_notifier_chain_unregister(&ipc_notifier, nb);
12730058677SRob Herring }
12830058677SRob Herring EXPORT_SYMBOL_GPL(pl320_ipc_unregister_notifier);
12930058677SRob Herring
pl320_probe(struct amba_device * adev,const struct amba_id * id)130091930a2SMark Langsdorf static int pl320_probe(struct amba_device *adev, const struct amba_id *id)
13130058677SRob Herring {
13230058677SRob Herring int ret;
13330058677SRob Herring
13430058677SRob Herring ipc_base = ioremap(adev->res.start, resource_size(&adev->res));
13530058677SRob Herring if (ipc_base == NULL)
13630058677SRob Herring return -ENOMEM;
13730058677SRob Herring
1389ac3e85aSBen Dooks writel_relaxed(0, ipc_base + IPCMxSEND(IPC_TX_MBOX));
13930058677SRob Herring
14030058677SRob Herring ipc_irq = adev->irq[0];
14130058677SRob Herring ret = request_irq(ipc_irq, ipc_handler, 0, dev_name(&adev->dev), NULL);
14230058677SRob Herring if (ret < 0)
14330058677SRob Herring goto err;
14430058677SRob Herring
14530058677SRob Herring /* Init slow mailbox */
1469ac3e85aSBen Dooks writel_relaxed(CHAN_MASK(A9_SOURCE),
14730058677SRob Herring ipc_base + IPCMxSOURCE(IPC_TX_MBOX));
1489ac3e85aSBen Dooks writel_relaxed(CHAN_MASK(M3_SOURCE),
14930058677SRob Herring ipc_base + IPCMxDSET(IPC_TX_MBOX));
1509ac3e85aSBen Dooks writel_relaxed(CHAN_MASK(M3_SOURCE) | CHAN_MASK(A9_SOURCE),
15130058677SRob Herring ipc_base + IPCMxMSET(IPC_TX_MBOX));
15230058677SRob Herring
15330058677SRob Herring /* Init receive mailbox */
1549ac3e85aSBen Dooks writel_relaxed(CHAN_MASK(M3_SOURCE),
15530058677SRob Herring ipc_base + IPCMxSOURCE(IPC_RX_MBOX));
1569ac3e85aSBen Dooks writel_relaxed(CHAN_MASK(A9_SOURCE),
15730058677SRob Herring ipc_base + IPCMxDSET(IPC_RX_MBOX));
1589ac3e85aSBen Dooks writel_relaxed(CHAN_MASK(M3_SOURCE) | CHAN_MASK(A9_SOURCE),
15930058677SRob Herring ipc_base + IPCMxMSET(IPC_RX_MBOX));
16030058677SRob Herring
16130058677SRob Herring return 0;
16230058677SRob Herring err:
16330058677SRob Herring iounmap(ipc_base);
16430058677SRob Herring return ret;
16530058677SRob Herring }
16630058677SRob Herring
16730058677SRob Herring static struct amba_id pl320_ids[] = {
16830058677SRob Herring {
16930058677SRob Herring .id = 0x00041320,
17030058677SRob Herring .mask = 0x000fffff,
17130058677SRob Herring },
17230058677SRob Herring { 0, 0 },
17330058677SRob Herring };
17430058677SRob Herring
17530058677SRob Herring static struct amba_driver pl320_driver = {
17630058677SRob Herring .drv = {
17730058677SRob Herring .name = "pl320",
17830058677SRob Herring },
17930058677SRob Herring .id_table = pl320_ids,
18030058677SRob Herring .probe = pl320_probe,
18130058677SRob Herring };
18230058677SRob Herring
ipc_init(void)18330058677SRob Herring static int __init ipc_init(void)
18430058677SRob Herring {
18530058677SRob Herring return amba_driver_register(&pl320_driver);
18630058677SRob Herring }
18789f08f64SPaul Gortmaker subsys_initcall(ipc_init);
188