blob: 42a88339612e6cebb97be207704320392339eb98 [file] [log] [blame]
Kristofer Jonsson116a6352020-08-20 17:25:23 +02001/*
2 * Copyright (C) 2013-2015 Fujitsu Semiconductor Ltd.
3 * Copyright (C) 2015 Linaro Ltd.
4 * Copyright (C) 2020 Arm Ltd.
5 * Author: Jassi Brar <jaswinder.singh@linaro.org>
6 *
7 * This program is free software and is provided to you under the terms of the
8 * GNU General Public License version 2 as published by the Free Software
9 * Foundation, and any use by you of this program is subject to the terms
10 * of such GNU licence.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you can access it online at
19 * http://www.gnu.org/licenses/gpl-2.0.html.
20 *
21 * SPDX-License-Identifier: GPL-2.0-only
22 */
23
24#include <linux/interrupt.h>
25#include <linux/spinlock.h>
26#include <linux/mutex.h>
27#include <linux/delay.h>
28#include <linux/slab.h>
29#include <linux/err.h>
30#include <linux/io.h>
31#include <linux/module.h>
32#include <linux/amba/bus.h>
33#include <linux/mailbox_controller.h>
34
35struct mhu_register_offsets {
36 uint8_t intr_stat_ofs;
37 uint8_t intr_set_ofs;
38 uint8_t intr_clr_ofs;
39};
40
41#define MHU_MAX_CHANS 3
42struct mhu_cfg {
43 uint32_t id;
44 uint8_t channels;
45 struct mhu_register_offsets offsets;
46 uint16_t tx_offset;
47 uint16_t rx_offset;
48 uint32_t channel_offsets[MHU_MAX_CHANS];
49};
50
51struct mhu_link {
52 unsigned irq;
53 void __iomem *tx_reg;
54 void __iomem *rx_reg;
55 struct mhu_register_offsets *offsets;
56};
57
58#define MHU_LP_OFFSET 0x0
59#define MHU_HP_OFFSET 0x20
60#define MHU_SEC_OFFSET 0x200
61static struct mhu_cfg mhu_cfgs[] = {
62 {
63 /* MHUv1 */
64 .id = 0x1bb098,
65 .channels = 3,
66 .offsets = {
67 .intr_stat_ofs = 0x0,
68 .intr_set_ofs = 0x8,
69 .intr_clr_ofs = 0x10,
70 },
71 .tx_offset = 0x100,
72 .rx_offset = 0x0,
73 .channel_offsets = {MHU_LP_OFFSET, MHU_HP_OFFSET, MHU_SEC_OFFSET}
74 },
75 {
76 /* MHU found on CoreLink SSE200 */
77 .id = 0x0bb856,
78 .channels = 1,
79 .offsets = {
80 .intr_stat_ofs = 0x0,
81 .intr_set_ofs = 0x4,
82 .intr_clr_ofs = 0x8,
83 },
84 .tx_offset = 0x0,
85 .rx_offset = 0x10,
86 .channel_offsets = {0}
87 }
88};
89
90struct arm_mhu {
91 void __iomem *base;
92 struct mhu_link mlink[MHU_MAX_CHANS];
93 struct mbox_chan chan[MHU_MAX_CHANS];
94 struct mbox_controller mbox;
95};
96
97static irqreturn_t mhu_rx_interrupt(int irq, void *p)
98{
99 struct mbox_chan *chan = p;
100 struct mhu_link *mlink = chan->con_priv;
101 u32 val;
102
103 val = readl_relaxed(mlink->rx_reg + mlink->offsets->intr_stat_ofs);
104 if (!val)
105 return IRQ_NONE;
106
107 mbox_chan_received_data(chan, (void *)&val);
108
109 writel_relaxed(val, mlink->rx_reg + mlink->offsets->intr_clr_ofs);
110
111 return IRQ_HANDLED;
112}
113
114static bool mhu_last_tx_done(struct mbox_chan *chan)
115{
116 struct mhu_link *mlink = chan->con_priv;
117 u32 val = readl_relaxed(mlink->tx_reg + mlink->offsets->intr_stat_ofs);
118
119 return (val == 0);
120}
121
122static int mhu_send_data(struct mbox_chan *chan, void *data)
123{
124 struct mhu_link *mlink = chan->con_priv;
125 u32 *arg = data;
126
127 writel_relaxed(*arg, mlink->tx_reg + mlink->offsets->intr_set_ofs);
128
129 return 0;
130}
131
132static int mhu_startup(struct mbox_chan *chan)
133{
134 struct mhu_link *mlink = chan->con_priv;
135 u32 val;
136 int ret;
137
138 val = readl_relaxed(mlink->tx_reg + mlink->offsets->intr_stat_ofs);
139 writel_relaxed(val, mlink->tx_reg + mlink->offsets->intr_clr_ofs);
140
141 ret = request_irq(mlink->irq, mhu_rx_interrupt,
142 IRQF_SHARED, "mhu_link", chan);
143 if (ret) {
144 dev_err(chan->mbox->dev,
145 "Unable to acquire IRQ %d\n", mlink->irq);
146 return ret;
147 }
148
149 return 0;
150}
151
152static void mhu_shutdown(struct mbox_chan *chan)
153{
154 struct mhu_link *mlink = chan->con_priv;
155
156 free_irq(mlink->irq, chan);
157}
158
159static const struct mbox_chan_ops mhu_ops = {
160 .send_data = mhu_send_data,
161 .startup = mhu_startup,
162 .shutdown = mhu_shutdown,
163 .last_tx_done = mhu_last_tx_done,
164};
165
166static int mhu_probe(struct amba_device *adev, const struct amba_id *id)
167{
168 int i, err;
169 struct arm_mhu *mhu;
170 struct device *dev = &adev->dev;
171 struct mhu_cfg *cfg = NULL;
172
173 /* Allocate memory for device */
174 mhu = devm_kzalloc(dev, sizeof(*mhu), GFP_KERNEL);
175 if (!mhu)
176 return -ENOMEM;
177
178 mhu->base = devm_ioremap_resource(dev, &adev->res);
179 if (IS_ERR(mhu->base)) {
180 dev_err(dev, "ioremap failed\n");
181 return PTR_ERR(mhu->base);
182 }
183
184 for (i = 0; i < ARRAY_SIZE(mhu_cfgs); i++) {
185 if ((mhu_cfgs[i].id & id->mask) == id->id) {
186 cfg = &mhu_cfgs[i];
187 break;
188 }
189 }
190
191 if (!cfg) {
192 dev_err(dev, "Failed to match id %x to configuration\n", id->id);
193 return -EINVAL;
194 }
195
196 for (i = 0; i < cfg->channels; i++) {
197 mhu->chan[i].con_priv = &mhu->mlink[i];
198 mhu->mlink[i].irq = adev->irq[i];
199 mhu->mlink[i].rx_reg = mhu->base + cfg->channel_offsets[i]
200 + cfg->rx_offset;
201 mhu->mlink[i].tx_reg = mhu->base + cfg->channel_offsets[i]
202 + cfg->tx_offset;
203 mhu->mlink[i].offsets = &cfg->offsets;
204 }
205
206 mhu->mbox.dev = dev;
207 mhu->mbox.chans = &mhu->chan[0];
208 mhu->mbox.num_chans = cfg->channels;
209 mhu->mbox.ops = &mhu_ops;
210 mhu->mbox.txdone_irq = false;
211 mhu->mbox.txdone_poll = true;
212 mhu->mbox.txpoll_period = 1;
213
214 amba_set_drvdata(adev, mhu);
215
216 err = mbox_controller_register(&mhu->mbox);
217 if (err) {
218 dev_err(dev, "Failed to register mailboxes %d\n", err);
219 return err;
220 }
221
222 dev_info(dev, "ARM MHU Mailbox registered\n");
223 return 0;
224}
225
226static int mhu_remove(struct amba_device *adev)
227{
228 struct arm_mhu *mhu = amba_get_drvdata(adev);
229
230 mbox_controller_unregister(&mhu->mbox);
231
232 return 0;
233}
234
235static struct amba_id mhu_ids[] = {
236 {
237 .id = 0x1bb098,
238 .mask = 0xffffff,
239 },
240 {
241 .id = 0x0bb856,
242 .mask = 0xffffff,
243 },
244 { 0, 0 },
245};
246MODULE_DEVICE_TABLE(amba, mhu_ids);
247
248static struct amba_driver arm_mhu_driver = {
249 .drv = {
250 /* Change name from "mhu" to "mhu_v1" to avoid conflict with
251 * upstream version of kernel module.
252 */
253 .name = "mhu_v1",
254 },
255 .id_table = mhu_ids,
256 .probe = mhu_probe,
257 .remove = mhu_remove,
258};
259module_amba_driver(arm_mhu_driver);
260
261MODULE_LICENSE("GPL v2");
262MODULE_DESCRIPTION("ARM MHU Driver");
263MODULE_AUTHOR("Jassi Brar <jassisinghbrar@gmail.com>");