Commit | Line | Data |
---|---|---|
9952f691 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
30058677 RH |
2 | /* |
3 | * Copyright 2012 Calxeda, Inc. | |
30058677 RH |
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 | ||
f2fc42b6 | 18 | #include <linux/pl320-ipc.h> |
30058677 RH |
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 | { | |
9ac3e85a BD |
50 | writel_relaxed(CHAN_MASK(source), ipc_base + IPCMxDSET(mbox)); |
51 | writel_relaxed(CHAN_MASK(source), ipc_base + IPCMxMSET(mbox)); | |
30058677 RH |
52 | } |
53 | ||
54 | static inline void clear_destination(int source, int mbox) | |
55 | { | |
9ac3e85a BD |
56 | writel_relaxed(CHAN_MASK(source), ipc_base + IPCMxDCLEAR(mbox)); |
57 | writel_relaxed(CHAN_MASK(source), ipc_base + IPCMxMCLEAR(mbox)); | |
30058677 RH |
58 | } |
59 | ||
60 | static void __ipc_send(int mbox, u32 *data) | |
61 | { | |
62 | int i; | |
63 | for (i = 0; i < 7; i++) | |
9ac3e85a BD |
64 | writel_relaxed(data[i], ipc_base + IPCMxDR(mbox, i)); |
65 | writel_relaxed(0x1, ipc_base + IPCMxSEND(mbox)); | |
30058677 RH |
66 | } |
67 | ||
68 | static u32 __ipc_rcv(int mbox, u32 *data) | |
69 | { | |
70 | int i; | |
71 | for (i = 0; i < 7; i++) | |
9ac3e85a | 72 | data[i] = readl_relaxed(ipc_base + IPCMxDR(mbox, i)); |
30058677 RH |
73 | return data[1]; |
74 | } | |
75 | ||
9d2e8b93 | 76 | /* blocking implementation from the A9 side, not usable in interrupts! */ |
30058677 RH |
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 | ||
9ac3e85a | 104 | irq_stat = readl_relaxed(ipc_base + IPCMMIS(1)); |
30058677 | 105 | if (irq_stat & MBOX_MASK(IPC_TX_MBOX)) { |
9ac3e85a | 106 | writel_relaxed(0, ipc_base + IPCMxSEND(IPC_TX_MBOX)); |
30058677 RH |
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); | |
9ac3e85a | 112 | writel_relaxed(2, ipc_base + IPCMxSEND(IPC_RX_MBOX)); |
30058677 RH |
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 | ||
091930a2 | 130 | static int pl320_probe(struct amba_device *adev, const struct amba_id *id) |
30058677 RH |
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 | ||
9ac3e85a | 138 | writel_relaxed(0, ipc_base + IPCMxSEND(IPC_TX_MBOX)); |
30058677 RH |
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 */ | |
9ac3e85a BD |
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)); | |
30058677 RH |
152 | |
153 | /* Init receive mailbox */ | |
9ac3e85a BD |
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)); | |
30058677 RH |
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 | } | |
89f08f64 | 187 | subsys_initcall(ipc_init); |