Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ARM / highbank: add support for pl320 IPC

The pl320 IPC allows for interprocessor communication between the
highbank A9 and the EnergyCore Management Engine. The pl320 implements
a straightforward mailbox protocol.

Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com>
Signed-off-by: Rob Herring <rob.herring@calxeda.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

authored by

Rob Herring and committed by
Rafael J. Wysocki
30058677 b5964708

+241
+2
arch/arm/mach-highbank/Kconfig
··· 11 11 select GENERIC_CLOCKEVENTS 12 12 select HAVE_ARM_SCU 13 13 select HAVE_SMP 14 + select MAILBOX 15 + select PL320_MBOX 14 16 select SPARSE_IRQ 15 17 select USE_OF
+2
drivers/Kconfig
··· 134 134 135 135 source "drivers/clocksource/Kconfig" 136 136 137 + source "drivers/mailbox/Kconfig" 138 + 137 139 source "drivers/iommu/Kconfig" 138 140 139 141 source "drivers/remoteproc/Kconfig"
+1
drivers/Makefile
··· 130 130 #common clk code 131 131 obj-y += clk/ 132 132 133 + obj-$(CONFIG_MAILBOX) += mailbox/ 133 134 obj-$(CONFIG_HWSPINLOCK) += hwspinlock/ 134 135 obj-$(CONFIG_NFC) += nfc/ 135 136 obj-$(CONFIG_IOMMU_SUPPORT) += iommu/
+19
drivers/mailbox/Kconfig
··· 1 + menuconfig MAILBOX 2 + bool "Mailbox Hardware Support" 3 + help 4 + Mailbox is a framework to control hardware communication between 5 + on-chip processors through queued messages and interrupt driven 6 + signals. Say Y if your platform supports hardware mailboxes. 7 + 8 + if MAILBOX 9 + config PL320_MBOX 10 + bool "ARM PL320 Mailbox" 11 + depends on ARM_AMBA 12 + help 13 + An implementation of the ARM PL320 Interprocessor Communication 14 + Mailbox (IPCM), tailored for the Calxeda Highbank. It is used to 15 + send short messages between Highbank's A9 cores and the EnergyCore 16 + Management Engine, primarily for cpufreq. Say Y here if you want 17 + to use the PL320 IPCM support. 18 + 19 + endif
+1
drivers/mailbox/Makefile
··· 1 + obj-$(CONFIG_PL320_MBOX) += pl320-ipc.o
+199
drivers/mailbox/pl320-ipc.c
··· 1 + /* 2 + * Copyright 2012 Calxeda, Inc. 3 + * 4 + * This program is free software; you can redistribute it and/or modify it 5 + * under the terms and conditions of the GNU General Public License, 6 + * version 2, as published by the Free Software Foundation. 7 + * 8 + * This program is distributed in the hope it will be useful, but WITHOUT 9 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 11 + * more details. 12 + * 13 + * You should have received a copy of the GNU General Public License along with 14 + * this program. If not, see <http://www.gnu.org/licenses/>. 15 + */ 16 + #include <linux/types.h> 17 + #include <linux/err.h> 18 + #include <linux/delay.h> 19 + #include <linux/export.h> 20 + #include <linux/io.h> 21 + #include <linux/interrupt.h> 22 + #include <linux/completion.h> 23 + #include <linux/mutex.h> 24 + #include <linux/notifier.h> 25 + #include <linux/spinlock.h> 26 + #include <linux/device.h> 27 + #include <linux/amba/bus.h> 28 + 29 + #include <linux/mailbox.h> 30 + 31 + #define IPCMxSOURCE(m) ((m) * 0x40) 32 + #define IPCMxDSET(m) (((m) * 0x40) + 0x004) 33 + #define IPCMxDCLEAR(m) (((m) * 0x40) + 0x008) 34 + #define IPCMxDSTATUS(m) (((m) * 0x40) + 0x00C) 35 + #define IPCMxMODE(m) (((m) * 0x40) + 0x010) 36 + #define IPCMxMSET(m) (((m) * 0x40) + 0x014) 37 + #define IPCMxMCLEAR(m) (((m) * 0x40) + 0x018) 38 + #define IPCMxMSTATUS(m) (((m) * 0x40) + 0x01C) 39 + #define IPCMxSEND(m) (((m) * 0x40) + 0x020) 40 + #define IPCMxDR(m, dr) (((m) * 0x40) + ((dr) * 4) + 0x024) 41 + 42 + #define IPCMMIS(irq) (((irq) * 8) + 0x800) 43 + #define IPCMRIS(irq) (((irq) * 8) + 0x804) 44 + 45 + #define MBOX_MASK(n) (1 << (n)) 46 + #define IPC_TX_MBOX 1 47 + #define IPC_RX_MBOX 2 48 + 49 + #define CHAN_MASK(n) (1 << (n)) 50 + #define A9_SOURCE 1 51 + #define M3_SOURCE 0 52 + 53 + static void __iomem *ipc_base; 54 + static int ipc_irq; 55 + static DEFINE_MUTEX(ipc_m1_lock); 56 + static DECLARE_COMPLETION(ipc_completion); 57 + static ATOMIC_NOTIFIER_HEAD(ipc_notifier); 58 + 59 + static inline void set_destination(int source, int mbox) 60 + { 61 + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxDSET(mbox)); 62 + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxMSET(mbox)); 63 + } 64 + 65 + static inline void clear_destination(int source, int mbox) 66 + { 67 + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxDCLEAR(mbox)); 68 + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxMCLEAR(mbox)); 69 + } 70 + 71 + static void __ipc_send(int mbox, u32 *data) 72 + { 73 + int i; 74 + for (i = 0; i < 7; i++) 75 + __raw_writel(data[i], ipc_base + IPCMxDR(mbox, i)); 76 + __raw_writel(0x1, ipc_base + IPCMxSEND(mbox)); 77 + } 78 + 79 + static u32 __ipc_rcv(int mbox, u32 *data) 80 + { 81 + int i; 82 + for (i = 0; i < 7; i++) 83 + data[i] = __raw_readl(ipc_base + IPCMxDR(mbox, i)); 84 + return data[1]; 85 + } 86 + 87 + /* blocking implmentation from the A9 side, not usuable in interrupts! */ 88 + int pl320_ipc_transmit(u32 *data) 89 + { 90 + int ret; 91 + 92 + mutex_lock(&ipc_m1_lock); 93 + 94 + init_completion(&ipc_completion); 95 + __ipc_send(IPC_TX_MBOX, data); 96 + ret = wait_for_completion_timeout(&ipc_completion, 97 + msecs_to_jiffies(1000)); 98 + if (ret == 0) { 99 + ret = -ETIMEDOUT; 100 + goto out; 101 + } 102 + 103 + ret = __ipc_rcv(IPC_TX_MBOX, data); 104 + out: 105 + mutex_unlock(&ipc_m1_lock); 106 + return ret; 107 + } 108 + EXPORT_SYMBOL_GPL(pl320_ipc_transmit); 109 + 110 + static irqreturn_t ipc_handler(int irq, void *dev) 111 + { 112 + u32 irq_stat; 113 + u32 data[7]; 114 + 115 + irq_stat = __raw_readl(ipc_base + IPCMMIS(1)); 116 + if (irq_stat & MBOX_MASK(IPC_TX_MBOX)) { 117 + __raw_writel(0, ipc_base + IPCMxSEND(IPC_TX_MBOX)); 118 + complete(&ipc_completion); 119 + } 120 + if (irq_stat & MBOX_MASK(IPC_RX_MBOX)) { 121 + __ipc_rcv(IPC_RX_MBOX, data); 122 + atomic_notifier_call_chain(&ipc_notifier, data[0], data + 1); 123 + __raw_writel(2, ipc_base + IPCMxSEND(IPC_RX_MBOX)); 124 + } 125 + 126 + return IRQ_HANDLED; 127 + } 128 + 129 + int pl320_ipc_register_notifier(struct notifier_block *nb) 130 + { 131 + return atomic_notifier_chain_register(&ipc_notifier, nb); 132 + } 133 + EXPORT_SYMBOL_GPL(pl320_ipc_register_notifier); 134 + 135 + int pl320_ipc_unregister_notifier(struct notifier_block *nb) 136 + { 137 + return atomic_notifier_chain_unregister(&ipc_notifier, nb); 138 + } 139 + EXPORT_SYMBOL_GPL(pl320_ipc_unregister_notifier); 140 + 141 + static int __init pl320_probe(struct amba_device *adev, 142 + const struct amba_id *id) 143 + { 144 + int ret; 145 + 146 + ipc_base = ioremap(adev->res.start, resource_size(&adev->res)); 147 + if (ipc_base == NULL) 148 + return -ENOMEM; 149 + 150 + __raw_writel(0, ipc_base + IPCMxSEND(IPC_TX_MBOX)); 151 + 152 + ipc_irq = adev->irq[0]; 153 + ret = request_irq(ipc_irq, ipc_handler, 0, dev_name(&adev->dev), NULL); 154 + if (ret < 0) 155 + goto err; 156 + 157 + /* Init slow mailbox */ 158 + __raw_writel(CHAN_MASK(A9_SOURCE), 159 + ipc_base + IPCMxSOURCE(IPC_TX_MBOX)); 160 + __raw_writel(CHAN_MASK(M3_SOURCE), 161 + ipc_base + IPCMxDSET(IPC_TX_MBOX)); 162 + __raw_writel(CHAN_MASK(M3_SOURCE) | CHAN_MASK(A9_SOURCE), 163 + ipc_base + IPCMxMSET(IPC_TX_MBOX)); 164 + 165 + /* Init receive mailbox */ 166 + __raw_writel(CHAN_MASK(M3_SOURCE), 167 + ipc_base + IPCMxSOURCE(IPC_RX_MBOX)); 168 + __raw_writel(CHAN_MASK(A9_SOURCE), 169 + ipc_base + IPCMxDSET(IPC_RX_MBOX)); 170 + __raw_writel(CHAN_MASK(M3_SOURCE) | CHAN_MASK(A9_SOURCE), 171 + ipc_base + IPCMxMSET(IPC_RX_MBOX)); 172 + 173 + return 0; 174 + err: 175 + iounmap(ipc_base); 176 + return ret; 177 + } 178 + 179 + static struct amba_id pl320_ids[] = { 180 + { 181 + .id = 0x00041320, 182 + .mask = 0x000fffff, 183 + }, 184 + { 0, 0 }, 185 + }; 186 + 187 + static struct amba_driver pl320_driver = { 188 + .drv = { 189 + .name = "pl320", 190 + }, 191 + .id_table = pl320_ids, 192 + .probe = pl320_probe, 193 + }; 194 + 195 + static int __init ipc_init(void) 196 + { 197 + return amba_driver_register(&pl320_driver); 198 + } 199 + module_init(ipc_init);
+17
include/linux/mailbox.h
··· 1 + /* 2 + * This program is free software; you can redistribute it and/or modify it 3 + * under the terms and conditions of the GNU General Public License, 4 + * version 2, as published by the Free Software Foundation. 5 + * 6 + * This program is distributed in the hope it will be useful, but WITHOUT 7 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 8 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 9 + * more details. 10 + * 11 + * You should have received a copy of the GNU General Public License along with 12 + * this program. If not, see <http://www.gnu.org/licenses/>. 13 + */ 14 + 15 + int pl320_ipc_transmit(u32 *data); 16 + int pl320_ipc_register_notifier(struct notifier_block *nb); 17 + int pl320_ipc_unregister_notifier(struct notifier_block *nb);