xref: /linux/drivers/mailbox/axiado-mailbox.c (revision 8bfab832ad6905ba70e69bad87a78f6d90cce64a)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) 2021-2026 Axiado Corporation (or its affiliates).
4  */
5 
6 #include <linux/atomic.h>
7 #include <linux/interrupt.h>
8 #include <linux/io.h>
9 #include <linux/bitfield.h>
10 #include <linux/debugfs.h>
11 #include <linux/iopoll.h>
12 #include <linux/mailbox_controller.h>
13 #include <linux/math.h>
14 #include <linux/module.h>
15 #include <linux/of.h>
16 #include <linux/platform_device.h>
17 #include <linux/property.h>
18 #include <linux/slab.h>
19 #include <linux/unaligned.h>
20 
21 #define AXIADO_MBOX_TX_CHANS		8	/* 0-7 */
22 #define AXIADO_MBOX_RX_CHANS		8	/* 8-15 */
23 #define AXIADO_MBOX_CHAN_STRIDE	0x4
24 #define AXIADO_MBOX_TX_REG_STRIDE	0x40
25 #define AXIADO_MBOX_RX_REG_STRIDE	0x30
26 #define AXIADO_MBOX_RX_POLL_US		10
27 #define AXIADO_MBOX_RX_TIMEOUT_US	1000
28 
29 /* Mailbox CSR bit definitions */
30 #define AXIADO_MBOX_CSR_EMPTY		BIT(0) /* 1 = FIFO empty; 0 = data available */
31 #define AXIADO_MBOX_CSR_OVERFLOW	BIT(2) /* write 1 to clear (W1C) */
32 #define AXIADO_MBOX_CSR_UNDERFLOW	BIT(3) /* write 1 to clear (W1C) */
33 #define AXIADO_MBOX_CSR_FLUSH		BIT(4) /* W1TRG flush; startup/shutdown */
34 #define AXIADO_MBOX_CSR_LEVEL		GENMASK(11, 5)
35 
36 #define AXIADO_MBOX_CSR_ERRORS		(AXIADO_MBOX_CSR_OVERFLOW | \
37 					 AXIADO_MBOX_CSR_UNDERFLOW)
38 
39 struct axiado_mbox_data {
40 	u8 num_chans;
41 	u16 msg_size;
42 };
43 
44 struct axiado_channel_data {
45 	void __iomem *mbox_reg;
46 	void __iomem *csr_reg;
47 	void *rx_buffer;
48 	u8 channel_num;
49 	int irq;
50 	struct mbox_chan *chan;
51 	bool active;
52 	atomic_t fifo_errors;
53 };
54 
55 /* mailbox side */
56 struct axiado_mbox {
57 	struct mbox_controller mbox;
58 	const struct axiado_mbox_data *drv_data;
59 	void __iomem *tx_base;
60 	void __iomem *rx_base;
61 	struct dentry *debugfs_dir;
62 };
63 
64 /*
65  * Checks OVERFLOW/UNDERFLOW, logs and W1C-clears them if set. Shared by the
66  * TX and RX paths; RX additionally flushes the FIFO afterwards to resync
67  * framing, which TX must not do since it could discard data the peer has
68  * not read yet.
69  */
axiado_mbox_clear_fifo_errors(struct axiado_channel_data * priv)70 static bool axiado_mbox_clear_fifo_errors(struct axiado_channel_data *priv)
71 {
72 	struct device *dev = priv->chan->mbox->dev;
73 	u32 errors;
74 
75 	errors = readl(priv->csr_reg) & AXIADO_MBOX_CSR_ERRORS;
76 	if (!errors)
77 		return false;
78 
79 	writel(errors, priv->csr_reg);
80 	atomic_inc(&priv->fifo_errors);
81 
82 	dev_warn_ratelimited(dev, "Channel %u FIFO error: %#x\n",
83 			     priv->channel_num, errors);
84 
85 	return true;
86 }
87 
axiado_mbox_send_data(struct mbox_chan * chan,void * data)88 static int axiado_mbox_send_data(struct mbox_chan *chan, void *data)
89 {
90 	struct axiado_mbox *mb = dev_get_drvdata(chan->mbox->dev);
91 	struct axiado_channel_data *priv = chan->con_priv;
92 	unsigned int idx = priv->channel_num;
93 	u8 tail[sizeof(u32)] = { 0 };
94 	const u8 *buf = data;
95 	unsigned int tail_len;
96 	unsigned int offset;
97 	u32 msg_len;
98 
99 	if (!data)
100 		return -EINVAL;
101 
102 	/*
103 	 * The Axiado mailbox message ABI stores the total message length,
104 	 * including this length word, as a little-endian byte count in the
105 	 * first word of every message.
106 	 */
107 	msg_len = get_unaligned_le32(data);
108 	if (msg_len < sizeof(u32) || msg_len > mb->drv_data->msg_size)
109 		return -EINVAL;
110 
111 	/* Only touch the hardware after validating the message. */
112 	axiado_mbox_clear_fifo_errors(priv);
113 
114 	if (!(readl(priv->csr_reg) & AXIADO_MBOX_CSR_EMPTY)) {
115 		dev_warn_ratelimited(mb->mbox.dev, "Channel %u is busy\n", idx);
116 		return -EBUSY;
117 	}
118 
119 	for (offset = 0; offset + sizeof(u32) <= msg_len;
120 	     offset += sizeof(u32))
121 		writel(get_unaligned_le32(buf + offset), priv->mbox_reg);
122 
123 	tail_len = msg_len - offset;
124 	if (tail_len) {
125 		memcpy(tail, buf + offset, tail_len);
126 		writel(get_unaligned_le32(tail), priv->mbox_reg);
127 	}
128 
129 	dev_dbg(mb->mbox.dev, "%s: Ch-%u sent\n", __func__, idx);
130 
131 	return 0;
132 }
133 
axiado_rx_thread(int irq,void * dev_id)134 static irqreturn_t axiado_rx_thread(int irq, void *dev_id)
135 {
136 	struct axiado_channel_data *priv = dev_id;
137 	struct mbox_chan *chan = priv->chan;
138 	struct axiado_mbox *mb = dev_get_drvdata(chan->mbox->dev);
139 	u8 *buf = priv->rx_buffer;
140 	unsigned int num_words;
141 	unsigned int remaining;
142 	unsigned int i;
143 	u32 msg_len;
144 	u32 word;
145 	u32 csr;
146 	int ret;
147 
148 	/*
149 	 * Clear and log/count any pending errors, but don't discard data
150 	 * on their account alone: a rejected overflow write doesn't
151 	 * corrupt what was already safely queued ahead of it, so let the
152 	 * length-based read below decide whether what's here is usable.
153 	 */
154 	axiado_mbox_clear_fifo_errors(priv);
155 
156 	csr = readl(priv->csr_reg);
157 	if (csr & AXIADO_MBOX_CSR_EMPTY)
158 		return IRQ_NONE;
159 
160 	/*
161 	 * The first word contains the total message length in bytes,
162 	 * including the length word itself.
163 	 */
164 	word = readl(priv->mbox_reg);
165 	msg_len = word;
166 	put_unaligned_le32(word, buf);
167 
168 	if (msg_len < sizeof(u32) || msg_len > mb->drv_data->msg_size)
169 		goto invalid_message;
170 
171 	num_words = DIV_ROUND_UP(msg_len, sizeof(u32));
172 	remaining = num_words - 1;
173 
174 	/*
175 	 * The not-empty interrupt may occur as soon as the first DW enters
176 	 * the FIFO. Wait until all remaining DWs of this message arrive.
177 	 */
178 	if (remaining) {
179 		ret = readl_poll_timeout(priv->csr_reg, csr,
180 					 (csr & AXIADO_MBOX_CSR_ERRORS) ||
181 					  FIELD_GET(AXIADO_MBOX_CSR_LEVEL, csr) >=
182 					  remaining,
183 					  AXIADO_MBOX_RX_POLL_US,
184 					  AXIADO_MBOX_RX_TIMEOUT_US);
185 		if (ret)
186 			goto incomplete_message;
187 
188 		axiado_mbox_clear_fifo_errors(priv);
189 	}
190 
191 	for (i = 1; i < num_words; i++) {
192 		word = readl(priv->mbox_reg);
193 		put_unaligned_le32(word, buf + i * sizeof(u32));
194 	}
195 
196 	axiado_mbox_clear_fifo_errors(priv);
197 
198 	if (READ_ONCE(priv->active))
199 		mbox_chan_received_data(chan, priv->rx_buffer);
200 
201 	return IRQ_HANDLED;
202 
203 incomplete_message:
204 	dev_warn_ratelimited(chan->mbox->dev,
205 			     "Channel %u received an incomplete message\n",
206 			     priv->channel_num);
207 
208 invalid_message:
209 	writel(AXIADO_MBOX_CSR_FLUSH, priv->csr_reg);
210 
211 	return IRQ_HANDLED;
212 }
213 
axiado_mbox_startup(struct mbox_chan * chan)214 static int axiado_mbox_startup(struct mbox_chan *chan)
215 {
216 	struct axiado_channel_data *priv = chan->con_priv;
217 
218 	/*
219 	 * Only flush on a genuine error. A blind flush here would discard
220 	 * a message the peer legitimately sent before this side started
221 	 * up (e.g. during normal boot sequencing), which is not corrupt
222 	 * and does not need resyncing.
223 	 */
224 	if (axiado_mbox_clear_fifo_errors(priv))
225 		writel(AXIADO_MBOX_CSR_FLUSH, priv->csr_reg);
226 
227 	WRITE_ONCE(priv->active, true);
228 
229 	if (priv->channel_num >= AXIADO_MBOX_TX_CHANS)
230 		enable_irq(priv->irq);
231 
232 	return 0;
233 }
234 
axiado_mbox_shutdown(struct mbox_chan * chan)235 static void axiado_mbox_shutdown(struct mbox_chan *chan)
236 {
237 	struct axiado_channel_data *priv = chan->con_priv;
238 
239 	WRITE_ONCE(priv->active, false);
240 
241 	if (priv->channel_num >= AXIADO_MBOX_TX_CHANS)
242 		disable_irq(priv->irq);
243 
244 	if (axiado_mbox_clear_fifo_errors(priv))
245 		writel(AXIADO_MBOX_CSR_FLUSH, priv->csr_reg);
246 }
247 
axiado_mbox_last_tx_done(struct mbox_chan * chan)248 static bool axiado_mbox_last_tx_done(struct mbox_chan *chan)
249 {
250 	struct axiado_channel_data *priv = chan->con_priv;
251 
252 	return !!(readl(priv->csr_reg) & AXIADO_MBOX_CSR_EMPTY);
253 }
254 
255 static const struct mbox_chan_ops axiado_mbox_chan_ops = {
256 	.send_data	= axiado_mbox_send_data,
257 	.startup	= axiado_mbox_startup,
258 	.shutdown	= axiado_mbox_shutdown,
259 	.last_tx_done	= axiado_mbox_last_tx_done,
260 };
261 
axiado_mbox_debugfs_remove(void * dentry)262 static void axiado_mbox_debugfs_remove(void *dentry)
263 {
264 	debugfs_remove_recursive(dentry);
265 }
266 
axiado_mbox_probe(struct platform_device * pdev)267 static int axiado_mbox_probe(struct platform_device *pdev)
268 {
269 	const struct axiado_mbox_data *drv_data;
270 	struct axiado_channel_data *ch_data;
271 	struct device *dev = &pdev->dev;
272 	struct axiado_mbox *mb;
273 	unsigned int irq_idx = 0;
274 	unsigned int rx_chan;
275 	unsigned int i;
276 	int ret;
277 
278 	drv_data = device_get_match_data(dev);
279 	if (!drv_data)
280 		return -ENODEV;
281 
282 	mb = devm_kzalloc(dev, sizeof(*mb), GFP_KERNEL);
283 	if (!mb)
284 		return -ENOMEM;
285 
286 	mb->mbox.dev = dev;
287 	mb->mbox.num_chans = drv_data->num_chans;
288 
289 	mb->mbox.chans = devm_kcalloc(&pdev->dev,
290 				      drv_data->num_chans,
291 				      sizeof(*mb->mbox.chans),
292 				      GFP_KERNEL);
293 	if (!mb->mbox.chans)
294 		return -ENOMEM;
295 
296 	mb->tx_base = devm_platform_ioremap_resource_byname(pdev, "tx");
297 	if (IS_ERR(mb->tx_base))
298 		return PTR_ERR(mb->tx_base);
299 
300 	mb->rx_base = devm_platform_ioremap_resource_byname(pdev, "rx");
301 	if (IS_ERR(mb->rx_base))
302 		return PTR_ERR(mb->rx_base);
303 
304 	ch_data = devm_kcalloc(&pdev->dev,
305 			       drv_data->num_chans,
306 			       sizeof(*ch_data),
307 			       GFP_KERNEL);
308 	if (!ch_data)
309 		return -ENOMEM;
310 
311 	mb->debugfs_dir = debugfs_create_dir(dev_name(dev), NULL);
312 	ret = devm_add_action_or_reset(dev, axiado_mbox_debugfs_remove,
313 				       mb->debugfs_dir);
314 	if (ret)
315 		return ret;
316 
317 	for (i = 0; i < drv_data->num_chans; i++) {
318 		char name[16];
319 
320 		ch_data[i].channel_num = i;
321 		ch_data[i].chan = &mb->mbox.chans[i];
322 
323 		mb->mbox.chans[i].con_priv = &ch_data[i];
324 
325 		snprintf(name, sizeof(name), "chan%u-errors", i);
326 		debugfs_create_atomic_t(name, 0444, mb->debugfs_dir,
327 					&ch_data[i].fifo_errors);
328 
329 		if (i < AXIADO_MBOX_TX_CHANS) {
330 			ch_data[i].mbox_reg = mb->tx_base + (i * AXIADO_MBOX_CHAN_STRIDE);
331 			ch_data[i].csr_reg = mb->tx_base + AXIADO_MBOX_TX_REG_STRIDE +
332 					       (i * AXIADO_MBOX_CHAN_STRIDE);
333 			ch_data[i].irq = -1;
334 			continue;
335 		}
336 
337 		ch_data[i].rx_buffer = devm_kzalloc(dev, drv_data->msg_size,
338 						    GFP_KERNEL);
339 		if (!ch_data[i].rx_buffer)
340 			return -ENOMEM;
341 
342 		rx_chan = i - AXIADO_MBOX_TX_CHANS;
343 		ch_data[i].mbox_reg = mb->rx_base +
344 				       (rx_chan * AXIADO_MBOX_CHAN_STRIDE);
345 		ch_data[i].csr_reg = mb->rx_base + AXIADO_MBOX_RX_REG_STRIDE +
346 				      (rx_chan * AXIADO_MBOX_CHAN_STRIDE);
347 		ch_data[i].irq = platform_get_irq(pdev, irq_idx++);
348 		if (ch_data[i].irq < 0)
349 			return dev_err_probe(dev, ch_data[i].irq,
350 					     "Failed to get IRQ for channel %u\n", i);
351 
352 		ret = devm_request_threaded_irq(dev, ch_data[i].irq, NULL,
353 						axiado_rx_thread,
354 						IRQF_ONESHOT | IRQF_NO_AUTOEN,
355 						dev_name(dev), &ch_data[i]);
356 		if (ret)
357 			return dev_err_probe(dev, ret,
358 					     "Failed to request IRQ for channel %u\n", i);
359 
360 		dev_dbg(dev, "RX chan %u -> irq %d\n", i, ch_data[i].irq);
361 	}
362 
363 	platform_set_drvdata(pdev, mb);
364 	mb->drv_data = drv_data;
365 	mb->mbox.ops = &axiado_mbox_chan_ops;
366 	mb->mbox.txdone_irq = false;
367 	mb->mbox.txdone_poll = true;
368 	mb->mbox.txpoll_period = 5;
369 
370 	return devm_mbox_controller_register(dev, &mb->mbox);
371 }
372 
373 static const struct axiado_mbox_data axiado_drv_data = {
374 	.num_chans = AXIADO_MBOX_TX_CHANS + AXIADO_MBOX_RX_CHANS,
375 	.msg_size = 256,
376 };
377 
378 static const struct of_device_id axiado_mbox_of_match[] = {
379 	{ .compatible = "axiado,ax3005-mailbox", .data = &axiado_drv_data },
380 	{ }
381 };
382 MODULE_DEVICE_TABLE(of, axiado_mbox_of_match);
383 
384 static struct platform_driver axiado_mbox_driver = {
385 	.driver = {
386 		.name = "axiado-mailbox",
387 		.of_match_table = axiado_mbox_of_match,
388 	},
389 	.probe = axiado_mbox_probe,
390 };
391 module_platform_driver(axiado_mbox_driver);
392 
393 MODULE_AUTHOR("Axiado Corporation");
394 MODULE_DESCRIPTION("Axiado Mailbox driver");
395 MODULE_LICENSE("GPL");
396