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