xref: /openbmc/linux/drivers/mailbox/imx-mailbox.c (revision 11dac1d3)
12bb70056SOleksij Rempel // SPDX-License-Identifier: GPL-2.0
22bb70056SOleksij Rempel /*
32bb70056SOleksij Rempel  * Copyright (c) 2018 Pengutronix, Oleksij Rempel <o.rempel@pengutronix.de>
42bb70056SOleksij Rempel  */
52bb70056SOleksij Rempel 
62bb70056SOleksij Rempel #include <linux/clk.h>
70a67003bSPeng Fan #include <linux/firmware/imx/ipc.h>
897961f78SPeng Fan #include <linux/firmware/imx/s4.h>
92bb70056SOleksij Rempel #include <linux/interrupt.h>
102bb70056SOleksij Rempel #include <linux/io.h>
110a67003bSPeng Fan #include <linux/iopoll.h>
12*11dac1d3SFranck LENORMAND #include <linux/jiffies.h>
132bb70056SOleksij Rempel #include <linux/kernel.h>
142bb70056SOleksij Rempel #include <linux/mailbox_controller.h>
152bb70056SOleksij Rempel #include <linux/module.h>
162bb70056SOleksij Rempel #include <linux/of_device.h>
17676f23eaSAnson Huang #include <linux/pm_runtime.h>
18892cb524SRobin Gong #include <linux/suspend.h>
192bb70056SOleksij Rempel #include <linux/slab.h>
202bb70056SOleksij Rempel 
212bb70056SOleksij Rempel #define IMX_MU_CHANS		16
220a67003bSPeng Fan /* TX0/RX0/RXDB[0-3] */
230a67003bSPeng Fan #define IMX_MU_SCU_CHANS	6
2497961f78SPeng Fan /* TX0/RX0 */
2597961f78SPeng Fan #define IMX_MU_S4_CHANS		2
262bb70056SOleksij Rempel #define IMX_MU_CHAN_NAME_SIZE	20
272bb70056SOleksij Rempel 
28*11dac1d3SFranck LENORMAND #define IMX_MU_SECO_TX_TOUT (msecs_to_jiffies(3000))
29*11dac1d3SFranck LENORMAND #define IMX_MU_SECO_RX_TOUT (msecs_to_jiffies(3000))
30*11dac1d3SFranck LENORMAND 
312bb70056SOleksij Rempel enum imx_mu_chan_type {
322bb70056SOleksij Rempel 	IMX_MU_TYPE_TX,		/* Tx */
332bb70056SOleksij Rempel 	IMX_MU_TYPE_RX,		/* Rx */
342bb70056SOleksij Rempel 	IMX_MU_TYPE_TXDB,	/* Tx doorbell */
352bb70056SOleksij Rempel 	IMX_MU_TYPE_RXDB,	/* Rx doorbell */
362bb70056SOleksij Rempel };
372bb70056SOleksij Rempel 
38f689a7cfSPeng Fan enum imx_mu_xcr {
394f0b776eSPeng Fan 	IMX_MU_GIER,
40f689a7cfSPeng Fan 	IMX_MU_GCR,
41f689a7cfSPeng Fan 	IMX_MU_TCR,
42f689a7cfSPeng Fan 	IMX_MU_RCR,
43f689a7cfSPeng Fan 	IMX_MU_xCR_MAX,
44f689a7cfSPeng Fan };
45f689a7cfSPeng Fan 
46f689a7cfSPeng Fan enum imx_mu_xsr {
47f689a7cfSPeng Fan 	IMX_MU_SR,
48f689a7cfSPeng Fan 	IMX_MU_GSR,
49f689a7cfSPeng Fan 	IMX_MU_TSR,
50f689a7cfSPeng Fan 	IMX_MU_RSR,
51f689a7cfSPeng Fan };
52f689a7cfSPeng Fan 
530a67003bSPeng Fan struct imx_sc_rpc_msg_max {
540a67003bSPeng Fan 	struct imx_sc_rpc_msg hdr;
55*11dac1d3SFranck LENORMAND 	u32 data[30];
560a67003bSPeng Fan };
570a67003bSPeng Fan 
5897961f78SPeng Fan struct imx_s4_rpc_msg_max {
5997961f78SPeng Fan 	struct imx_s4_rpc_msg hdr;
6097961f78SPeng Fan 	u32 data[254];
6197961f78SPeng Fan };
6297961f78SPeng Fan 
632bb70056SOleksij Rempel struct imx_mu_con_priv {
642bb70056SOleksij Rempel 	unsigned int		idx;
652bb70056SOleksij Rempel 	char			irq_desc[IMX_MU_CHAN_NAME_SIZE];
662bb70056SOleksij Rempel 	enum imx_mu_chan_type	type;
672bb70056SOleksij Rempel 	struct mbox_chan	*chan;
682bb70056SOleksij Rempel 	struct tasklet_struct	txdb_tasklet;
692bb70056SOleksij Rempel };
702bb70056SOleksij Rempel 
712bb70056SOleksij Rempel struct imx_mu_priv {
722bb70056SOleksij Rempel 	struct device		*dev;
732bb70056SOleksij Rempel 	void __iomem		*base;
7497961f78SPeng Fan 	void			*msg;
752bb70056SOleksij Rempel 	spinlock_t		xcr_lock; /* control register lock */
762bb70056SOleksij Rempel 
772bb70056SOleksij Rempel 	struct mbox_controller	mbox;
782bb70056SOleksij Rempel 	struct mbox_chan	mbox_chans[IMX_MU_CHANS];
792bb70056SOleksij Rempel 
802bb70056SOleksij Rempel 	struct imx_mu_con_priv  con_priv[IMX_MU_CHANS];
81c6c6bc6eSRichard Zhu 	const struct imx_mu_dcfg	*dcfg;
822bb70056SOleksij Rempel 	struct clk		*clk;
832bb70056SOleksij Rempel 	int			irq;
84892cb524SRobin Gong 	bool			suspend;
852bb70056SOleksij Rempel 
86f689a7cfSPeng Fan 	u32 xcr[4];
87ba5f9fa0SDong Aisheng 
882bb70056SOleksij Rempel 	bool			side_b;
892bb70056SOleksij Rempel };
902bb70056SOleksij Rempel 
914f0b776eSPeng Fan enum imx_mu_type {
924f0b776eSPeng Fan 	IMX_MU_V1,
9397961f78SPeng Fan 	IMX_MU_V2 = BIT(1),
9497961f78SPeng Fan 	IMX_MU_V2_S4 = BIT(15),
954f0b776eSPeng Fan };
964f0b776eSPeng Fan 
9763b38357SPeng Fan struct imx_mu_dcfg {
9863b38357SPeng Fan 	int (*tx)(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp, void *data);
9963b38357SPeng Fan 	int (*rx)(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp);
100315d2e56SPeng Fan 	int (*rxdb)(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp);
10163b38357SPeng Fan 	void (*init)(struct imx_mu_priv *priv);
1024f0b776eSPeng Fan 	enum imx_mu_type type;
10332f7443dSPeng Fan 	u32	xTR;		/* Transmit Register0 */
10432f7443dSPeng Fan 	u32	xRR;		/* Receive Register0 */
105f689a7cfSPeng Fan 	u32	xSR[4];		/* Status Registers */
106f689a7cfSPeng Fan 	u32	xCR[4];		/* Control Registers */
107c6c6bc6eSRichard Zhu };
108c6c6bc6eSRichard Zhu 
10997961f78SPeng Fan #define IMX_MU_xSR_GIPn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(28 + (3 - (x))))
11097961f78SPeng Fan #define IMX_MU_xSR_RFn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(24 + (3 - (x))))
11197961f78SPeng Fan #define IMX_MU_xSR_TEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(20 + (3 - (x))))
1124f0b776eSPeng Fan 
1134f0b776eSPeng Fan /* General Purpose Interrupt Enable */
11497961f78SPeng Fan #define IMX_MU_xCR_GIEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(28 + (3 - (x))))
1154f0b776eSPeng Fan /* Receive Interrupt Enable */
11697961f78SPeng Fan #define IMX_MU_xCR_RIEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(24 + (3 - (x))))
1174f0b776eSPeng Fan /* Transmit Interrupt Enable */
11897961f78SPeng Fan #define IMX_MU_xCR_TIEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(20 + (3 - (x))))
1194f0b776eSPeng Fan /* General Purpose Interrupt Request */
12097961f78SPeng Fan #define IMX_MU_xCR_GIRn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(16 + (3 - (x))))
1214f0b776eSPeng Fan 
1224f0b776eSPeng Fan 
1232bb70056SOleksij Rempel static struct imx_mu_priv *to_imx_mu_priv(struct mbox_controller *mbox)
1242bb70056SOleksij Rempel {
1252bb70056SOleksij Rempel 	return container_of(mbox, struct imx_mu_priv, mbox);
1262bb70056SOleksij Rempel }
1272bb70056SOleksij Rempel 
1282bb70056SOleksij Rempel static void imx_mu_write(struct imx_mu_priv *priv, u32 val, u32 offs)
1292bb70056SOleksij Rempel {
1302bb70056SOleksij Rempel 	iowrite32(val, priv->base + offs);
1312bb70056SOleksij Rempel }
1322bb70056SOleksij Rempel 
1332bb70056SOleksij Rempel static u32 imx_mu_read(struct imx_mu_priv *priv, u32 offs)
1342bb70056SOleksij Rempel {
1352bb70056SOleksij Rempel 	return ioread32(priv->base + offs);
1362bb70056SOleksij Rempel }
1372bb70056SOleksij Rempel 
138*11dac1d3SFranck LENORMAND static int imx_mu_tx_waiting_write(struct imx_mu_priv *priv, u32 val, u32 idx)
139*11dac1d3SFranck LENORMAND {
140*11dac1d3SFranck LENORMAND 	u64 timeout_time = get_jiffies_64() + IMX_MU_SECO_TX_TOUT;
141*11dac1d3SFranck LENORMAND 	u32 status;
142*11dac1d3SFranck LENORMAND 	u32 can_write;
143*11dac1d3SFranck LENORMAND 
144*11dac1d3SFranck LENORMAND 	dev_dbg(priv->dev, "Trying to write %.8x to idx %d\n", val, idx);
145*11dac1d3SFranck LENORMAND 
146*11dac1d3SFranck LENORMAND 	do {
147*11dac1d3SFranck LENORMAND 		status = imx_mu_read(priv, priv->dcfg->xSR[IMX_MU_TSR]);
148*11dac1d3SFranck LENORMAND 		can_write = status & IMX_MU_xSR_TEn(priv->dcfg->type, idx % 4);
149*11dac1d3SFranck LENORMAND 	} while (!can_write && time_is_after_jiffies64(timeout_time));
150*11dac1d3SFranck LENORMAND 
151*11dac1d3SFranck LENORMAND 	if (!can_write) {
152*11dac1d3SFranck LENORMAND 		dev_err(priv->dev, "timeout trying to write %.8x at %d(%.8x)\n",
153*11dac1d3SFranck LENORMAND 			val, idx, status);
154*11dac1d3SFranck LENORMAND 		return -ETIME;
155*11dac1d3SFranck LENORMAND 	}
156*11dac1d3SFranck LENORMAND 
157*11dac1d3SFranck LENORMAND 	imx_mu_write(priv, val, priv->dcfg->xTR + (idx % 4) * 4);
158*11dac1d3SFranck LENORMAND 
159*11dac1d3SFranck LENORMAND 	return 0;
160*11dac1d3SFranck LENORMAND }
161*11dac1d3SFranck LENORMAND 
162*11dac1d3SFranck LENORMAND static int imx_mu_rx_waiting_read(struct imx_mu_priv *priv, u32 *val, u32 idx)
163*11dac1d3SFranck LENORMAND {
164*11dac1d3SFranck LENORMAND 	u64 timeout_time = get_jiffies_64() + IMX_MU_SECO_RX_TOUT;
165*11dac1d3SFranck LENORMAND 	u32 status;
166*11dac1d3SFranck LENORMAND 	u32 can_read;
167*11dac1d3SFranck LENORMAND 
168*11dac1d3SFranck LENORMAND 	dev_dbg(priv->dev, "Trying to read from idx %d\n", idx);
169*11dac1d3SFranck LENORMAND 
170*11dac1d3SFranck LENORMAND 	do {
171*11dac1d3SFranck LENORMAND 		status = imx_mu_read(priv, priv->dcfg->xSR[IMX_MU_RSR]);
172*11dac1d3SFranck LENORMAND 		can_read = status & IMX_MU_xSR_RFn(priv->dcfg->type, idx % 4);
173*11dac1d3SFranck LENORMAND 	} while (!can_read && time_is_after_jiffies64(timeout_time));
174*11dac1d3SFranck LENORMAND 
175*11dac1d3SFranck LENORMAND 	if (!can_read) {
176*11dac1d3SFranck LENORMAND 		dev_err(priv->dev, "timeout trying to read idx %d (%.8x)\n",
177*11dac1d3SFranck LENORMAND 			idx, status);
178*11dac1d3SFranck LENORMAND 		return -ETIME;
179*11dac1d3SFranck LENORMAND 	}
180*11dac1d3SFranck LENORMAND 
181*11dac1d3SFranck LENORMAND 	*val = imx_mu_read(priv, priv->dcfg->xRR + (idx % 4) * 4);
182*11dac1d3SFranck LENORMAND 	dev_dbg(priv->dev, "Read %.8x\n", *val);
183*11dac1d3SFranck LENORMAND 
184*11dac1d3SFranck LENORMAND 	return 0;
185*11dac1d3SFranck LENORMAND }
186*11dac1d3SFranck LENORMAND 
187f689a7cfSPeng Fan static u32 imx_mu_xcr_rmw(struct imx_mu_priv *priv, enum imx_mu_xcr type, u32 set, u32 clr)
1882bb70056SOleksij Rempel {
1892bb70056SOleksij Rempel 	unsigned long flags;
1902bb70056SOleksij Rempel 	u32 val;
1912bb70056SOleksij Rempel 
1922bb70056SOleksij Rempel 	spin_lock_irqsave(&priv->xcr_lock, flags);
193f689a7cfSPeng Fan 	val = imx_mu_read(priv, priv->dcfg->xCR[type]);
1942bb70056SOleksij Rempel 	val &= ~clr;
1952bb70056SOleksij Rempel 	val |= set;
196f689a7cfSPeng Fan 	imx_mu_write(priv, val, priv->dcfg->xCR[type]);
1972bb70056SOleksij Rempel 	spin_unlock_irqrestore(&priv->xcr_lock, flags);
1982bb70056SOleksij Rempel 
1992bb70056SOleksij Rempel 	return val;
2002bb70056SOleksij Rempel }
2012bb70056SOleksij Rempel 
20263b38357SPeng Fan static int imx_mu_generic_tx(struct imx_mu_priv *priv,
20363b38357SPeng Fan 			     struct imx_mu_con_priv *cp,
20463b38357SPeng Fan 			     void *data)
20563b38357SPeng Fan {
20663b38357SPeng Fan 	u32 *arg = data;
20763b38357SPeng Fan 
20863b38357SPeng Fan 	switch (cp->type) {
20963b38357SPeng Fan 	case IMX_MU_TYPE_TX:
21032f7443dSPeng Fan 		imx_mu_write(priv, *arg, priv->dcfg->xTR + cp->idx * 4);
2114f0b776eSPeng Fan 		imx_mu_xcr_rmw(priv, IMX_MU_TCR, IMX_MU_xCR_TIEn(priv->dcfg->type, cp->idx), 0);
21263b38357SPeng Fan 		break;
21363b38357SPeng Fan 	case IMX_MU_TYPE_TXDB:
2144f0b776eSPeng Fan 		imx_mu_xcr_rmw(priv, IMX_MU_GCR, IMX_MU_xCR_GIRn(priv->dcfg->type, cp->idx), 0);
21563b38357SPeng Fan 		tasklet_schedule(&cp->txdb_tasklet);
21663b38357SPeng Fan 		break;
21763b38357SPeng Fan 	default:
21863b38357SPeng Fan 		dev_warn_ratelimited(priv->dev, "Send data on wrong channel type: %d\n", cp->type);
21963b38357SPeng Fan 		return -EINVAL;
22063b38357SPeng Fan 	}
22163b38357SPeng Fan 
22263b38357SPeng Fan 	return 0;
22363b38357SPeng Fan }
22463b38357SPeng Fan 
22563b38357SPeng Fan static int imx_mu_generic_rx(struct imx_mu_priv *priv,
22663b38357SPeng Fan 			     struct imx_mu_con_priv *cp)
22763b38357SPeng Fan {
22863b38357SPeng Fan 	u32 dat;
22963b38357SPeng Fan 
23032f7443dSPeng Fan 	dat = imx_mu_read(priv, priv->dcfg->xRR + (cp->idx) * 4);
23163b38357SPeng Fan 	mbox_chan_received_data(cp->chan, (void *)&dat);
23263b38357SPeng Fan 
23363b38357SPeng Fan 	return 0;
23463b38357SPeng Fan }
23563b38357SPeng Fan 
236315d2e56SPeng Fan static int imx_mu_generic_rxdb(struct imx_mu_priv *priv,
237315d2e56SPeng Fan 			       struct imx_mu_con_priv *cp)
238315d2e56SPeng Fan {
239315d2e56SPeng Fan 	imx_mu_write(priv, IMX_MU_xSR_GIPn(priv->dcfg->type, cp->idx),
240315d2e56SPeng Fan 		     priv->dcfg->xSR[IMX_MU_GSR]);
241315d2e56SPeng Fan 	mbox_chan_received_data(cp->chan, NULL);
242315d2e56SPeng Fan 
243315d2e56SPeng Fan 	return 0;
244315d2e56SPeng Fan }
245315d2e56SPeng Fan 
24697961f78SPeng Fan static int imx_mu_specific_tx(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp, void *data)
2470a67003bSPeng Fan {
2480a67003bSPeng Fan 	u32 *arg = data;
2490a67003bSPeng Fan 	int i, ret;
2500a67003bSPeng Fan 	u32 xsr;
25197961f78SPeng Fan 	u32 size, max_size, num_tr;
25297961f78SPeng Fan 
25397961f78SPeng Fan 	if (priv->dcfg->type & IMX_MU_V2_S4) {
25497961f78SPeng Fan 		size = ((struct imx_s4_rpc_msg_max *)data)->hdr.size;
25597961f78SPeng Fan 		max_size = sizeof(struct imx_s4_rpc_msg_max);
25697961f78SPeng Fan 		num_tr = 8;
25797961f78SPeng Fan 	} else {
25897961f78SPeng Fan 		size = ((struct imx_sc_rpc_msg_max *)data)->hdr.size;
25997961f78SPeng Fan 		max_size = sizeof(struct imx_sc_rpc_msg_max);
26097961f78SPeng Fan 		num_tr = 4;
26197961f78SPeng Fan 	}
2620a67003bSPeng Fan 
2630a67003bSPeng Fan 	switch (cp->type) {
2640a67003bSPeng Fan 	case IMX_MU_TYPE_TX:
2659d8ca628SPeng Fan 		/*
2669d8ca628SPeng Fan 		 * msg->hdr.size specifies the number of u32 words while
2679d8ca628SPeng Fan 		 * sizeof yields bytes.
2689d8ca628SPeng Fan 		 */
2699d8ca628SPeng Fan 
27097961f78SPeng Fan 		if (size > max_size / 4) {
2710a67003bSPeng Fan 			/*
2720a67003bSPeng Fan 			 * The real message size can be different to
27397961f78SPeng Fan 			 * struct imx_sc_rpc_msg_max/imx_s4_rpc_msg_max size
2740a67003bSPeng Fan 			 */
27597961f78SPeng Fan 			dev_err(priv->dev, "Maximal message size (%u bytes) exceeded on TX; got: %i bytes\n", max_size, size << 2);
2760a67003bSPeng Fan 			return -EINVAL;
2770a67003bSPeng Fan 		}
2780a67003bSPeng Fan 
27997961f78SPeng Fan 		for (i = 0; i < num_tr && i < size; i++)
28097961f78SPeng Fan 			imx_mu_write(priv, *arg++, priv->dcfg->xTR + (i % num_tr) * 4);
28197961f78SPeng Fan 		for (; i < size; i++) {
282f689a7cfSPeng Fan 			ret = readl_poll_timeout(priv->base + priv->dcfg->xSR[IMX_MU_TSR],
2830a67003bSPeng Fan 						 xsr,
28497961f78SPeng Fan 						 xsr & IMX_MU_xSR_TEn(priv->dcfg->type, i % num_tr),
28581a9d3b9SRanjani Vaidyanathan 						 0, 5 * USEC_PER_SEC);
2860a67003bSPeng Fan 			if (ret) {
2870a67003bSPeng Fan 				dev_err(priv->dev, "Send data index: %d timeout\n", i);
2880a67003bSPeng Fan 				return ret;
2890a67003bSPeng Fan 			}
29097961f78SPeng Fan 			imx_mu_write(priv, *arg++, priv->dcfg->xTR + (i % num_tr) * 4);
2910a67003bSPeng Fan 		}
2920a67003bSPeng Fan 
2934f0b776eSPeng Fan 		imx_mu_xcr_rmw(priv, IMX_MU_TCR, IMX_MU_xCR_TIEn(priv->dcfg->type, cp->idx), 0);
2940a67003bSPeng Fan 		break;
2950a67003bSPeng Fan 	default:
2960a67003bSPeng Fan 		dev_warn_ratelimited(priv->dev, "Send data on wrong channel type: %d\n", cp->type);
2970a67003bSPeng Fan 		return -EINVAL;
2980a67003bSPeng Fan 	}
2990a67003bSPeng Fan 
3000a67003bSPeng Fan 	return 0;
3010a67003bSPeng Fan }
3020a67003bSPeng Fan 
30397961f78SPeng Fan static int imx_mu_specific_rx(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp)
3040a67003bSPeng Fan {
30597961f78SPeng Fan 	u32 *data;
3060a67003bSPeng Fan 	int i, ret;
3070a67003bSPeng Fan 	u32 xsr;
30897961f78SPeng Fan 	u32 size, max_size;
30997961f78SPeng Fan 
31097961f78SPeng Fan 	data = (u32 *)priv->msg;
3110a67003bSPeng Fan 
3124f0b776eSPeng Fan 	imx_mu_xcr_rmw(priv, IMX_MU_RCR, 0, IMX_MU_xCR_RIEn(priv->dcfg->type, 0));
31332f7443dSPeng Fan 	*data++ = imx_mu_read(priv, priv->dcfg->xRR);
3140a67003bSPeng Fan 
31597961f78SPeng Fan 	if (priv->dcfg->type & IMX_MU_V2_S4) {
31697961f78SPeng Fan 		size = ((struct imx_s4_rpc_msg_max *)priv->msg)->hdr.size;
31797961f78SPeng Fan 		max_size = sizeof(struct imx_s4_rpc_msg_max);
31897961f78SPeng Fan 	} else {
31997961f78SPeng Fan 		size = ((struct imx_sc_rpc_msg_max *)priv->msg)->hdr.size;
32097961f78SPeng Fan 		max_size = sizeof(struct imx_sc_rpc_msg_max);
32197961f78SPeng Fan 	}
32297961f78SPeng Fan 
32397961f78SPeng Fan 	if (size > max_size / 4) {
32497961f78SPeng Fan 		dev_err(priv->dev, "Maximal message size (%u bytes) exceeded on RX; got: %i bytes\n", max_size, size << 2);
3250a67003bSPeng Fan 		return -EINVAL;
3260a67003bSPeng Fan 	}
3270a67003bSPeng Fan 
32897961f78SPeng Fan 	for (i = 1; i < size; i++) {
329f689a7cfSPeng Fan 		ret = readl_poll_timeout(priv->base + priv->dcfg->xSR[IMX_MU_RSR], xsr,
33081a9d3b9SRanjani Vaidyanathan 					 xsr & IMX_MU_xSR_RFn(priv->dcfg->type, i % 4), 0,
33181a9d3b9SRanjani Vaidyanathan 					 5 * USEC_PER_SEC);
3320a67003bSPeng Fan 		if (ret) {
3330a67003bSPeng Fan 			dev_err(priv->dev, "timeout read idx %d\n", i);
3340a67003bSPeng Fan 			return ret;
3350a67003bSPeng Fan 		}
33632f7443dSPeng Fan 		*data++ = imx_mu_read(priv, priv->dcfg->xRR + (i % 4) * 4);
3370a67003bSPeng Fan 	}
3380a67003bSPeng Fan 
3394f0b776eSPeng Fan 	imx_mu_xcr_rmw(priv, IMX_MU_RCR, IMX_MU_xCR_RIEn(priv->dcfg->type, 0), 0);
34097961f78SPeng Fan 	mbox_chan_received_data(cp->chan, (void *)priv->msg);
3410a67003bSPeng Fan 
3420a67003bSPeng Fan 	return 0;
3430a67003bSPeng Fan }
3440a67003bSPeng Fan 
345*11dac1d3SFranck LENORMAND static int imx_mu_seco_tx(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp,
346*11dac1d3SFranck LENORMAND 			  void *data)
347*11dac1d3SFranck LENORMAND {
348*11dac1d3SFranck LENORMAND 	struct imx_sc_rpc_msg_max *msg = data;
349*11dac1d3SFranck LENORMAND 	u32 *arg = data;
350*11dac1d3SFranck LENORMAND 	u32 byte_size;
351*11dac1d3SFranck LENORMAND 	int err;
352*11dac1d3SFranck LENORMAND 	int i;
353*11dac1d3SFranck LENORMAND 
354*11dac1d3SFranck LENORMAND 	dev_dbg(priv->dev, "Sending message\n");
355*11dac1d3SFranck LENORMAND 
356*11dac1d3SFranck LENORMAND 	switch (cp->type) {
357*11dac1d3SFranck LENORMAND 	case IMX_MU_TYPE_TXDB:
358*11dac1d3SFranck LENORMAND 		byte_size = msg->hdr.size * sizeof(u32);
359*11dac1d3SFranck LENORMAND 		if (byte_size > sizeof(*msg)) {
360*11dac1d3SFranck LENORMAND 			/*
361*11dac1d3SFranck LENORMAND 			 * The real message size can be different to
362*11dac1d3SFranck LENORMAND 			 * struct imx_sc_rpc_msg_max size
363*11dac1d3SFranck LENORMAND 			 */
364*11dac1d3SFranck LENORMAND 			dev_err(priv->dev,
365*11dac1d3SFranck LENORMAND 				"Exceed max msg size (%zu) on TX, got: %i\n",
366*11dac1d3SFranck LENORMAND 				sizeof(*msg), byte_size);
367*11dac1d3SFranck LENORMAND 			return -EINVAL;
368*11dac1d3SFranck LENORMAND 		}
369*11dac1d3SFranck LENORMAND 
370*11dac1d3SFranck LENORMAND 		print_hex_dump_debug("from client ", DUMP_PREFIX_OFFSET, 4, 4,
371*11dac1d3SFranck LENORMAND 				     data, byte_size, false);
372*11dac1d3SFranck LENORMAND 
373*11dac1d3SFranck LENORMAND 		/* Send first word */
374*11dac1d3SFranck LENORMAND 		dev_dbg(priv->dev, "Sending header\n");
375*11dac1d3SFranck LENORMAND 		imx_mu_write(priv, *arg++, priv->dcfg->xTR);
376*11dac1d3SFranck LENORMAND 
377*11dac1d3SFranck LENORMAND 		/* Send signaling */
378*11dac1d3SFranck LENORMAND 		dev_dbg(priv->dev, "Sending signaling\n");
379*11dac1d3SFranck LENORMAND 		imx_mu_xcr_rmw(priv, IMX_MU_GCR,
380*11dac1d3SFranck LENORMAND 			       IMX_MU_xCR_GIRn(priv->dcfg->type, cp->idx), 0);
381*11dac1d3SFranck LENORMAND 
382*11dac1d3SFranck LENORMAND 		/* Send words to fill the mailbox */
383*11dac1d3SFranck LENORMAND 		for (i = 1; i < 4 && i < msg->hdr.size; i++) {
384*11dac1d3SFranck LENORMAND 			dev_dbg(priv->dev, "Sending word %d\n", i);
385*11dac1d3SFranck LENORMAND 			imx_mu_write(priv, *arg++,
386*11dac1d3SFranck LENORMAND 				     priv->dcfg->xTR + (i % 4) * 4);
387*11dac1d3SFranck LENORMAND 		}
388*11dac1d3SFranck LENORMAND 
389*11dac1d3SFranck LENORMAND 		/* Send rest of message waiting for remote read */
390*11dac1d3SFranck LENORMAND 		for (; i < msg->hdr.size; i++) {
391*11dac1d3SFranck LENORMAND 			dev_dbg(priv->dev, "Sending word %d\n", i);
392*11dac1d3SFranck LENORMAND 			err = imx_mu_tx_waiting_write(priv, *arg++, i);
393*11dac1d3SFranck LENORMAND 			if (err) {
394*11dac1d3SFranck LENORMAND 				dev_err(priv->dev, "Timeout tx %d\n", i);
395*11dac1d3SFranck LENORMAND 				return err;
396*11dac1d3SFranck LENORMAND 			}
397*11dac1d3SFranck LENORMAND 		}
398*11dac1d3SFranck LENORMAND 
399*11dac1d3SFranck LENORMAND 		/* Simulate hack for mbox framework */
400*11dac1d3SFranck LENORMAND 		tasklet_schedule(&cp->txdb_tasklet);
401*11dac1d3SFranck LENORMAND 
402*11dac1d3SFranck LENORMAND 		break;
403*11dac1d3SFranck LENORMAND 	default:
404*11dac1d3SFranck LENORMAND 		dev_warn_ratelimited(priv->dev,
405*11dac1d3SFranck LENORMAND 				     "Send data on wrong channel type: %d\n",
406*11dac1d3SFranck LENORMAND 				     cp->type);
407*11dac1d3SFranck LENORMAND 		return -EINVAL;
408*11dac1d3SFranck LENORMAND 	}
409*11dac1d3SFranck LENORMAND 
410*11dac1d3SFranck LENORMAND 	return 0;
411*11dac1d3SFranck LENORMAND }
412*11dac1d3SFranck LENORMAND 
413*11dac1d3SFranck LENORMAND static int imx_mu_seco_rxdb(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp)
414*11dac1d3SFranck LENORMAND {
415*11dac1d3SFranck LENORMAND 	struct imx_sc_rpc_msg_max msg;
416*11dac1d3SFranck LENORMAND 	u32 *data = (u32 *)&msg;
417*11dac1d3SFranck LENORMAND 	u32 byte_size;
418*11dac1d3SFranck LENORMAND 	int err = 0;
419*11dac1d3SFranck LENORMAND 	int i;
420*11dac1d3SFranck LENORMAND 
421*11dac1d3SFranck LENORMAND 	dev_dbg(priv->dev, "Receiving message\n");
422*11dac1d3SFranck LENORMAND 
423*11dac1d3SFranck LENORMAND 	/* Read header */
424*11dac1d3SFranck LENORMAND 	dev_dbg(priv->dev, "Receiving header\n");
425*11dac1d3SFranck LENORMAND 	*data++ = imx_mu_read(priv, priv->dcfg->xRR);
426*11dac1d3SFranck LENORMAND 	byte_size = msg.hdr.size * sizeof(u32);
427*11dac1d3SFranck LENORMAND 	if (byte_size > sizeof(msg)) {
428*11dac1d3SFranck LENORMAND 		dev_err(priv->dev, "Exceed max msg size (%zu) on RX, got: %i\n",
429*11dac1d3SFranck LENORMAND 			sizeof(msg), byte_size);
430*11dac1d3SFranck LENORMAND 		err = -EINVAL;
431*11dac1d3SFranck LENORMAND 		goto error;
432*11dac1d3SFranck LENORMAND 	}
433*11dac1d3SFranck LENORMAND 
434*11dac1d3SFranck LENORMAND 	/* Read message waiting they are written */
435*11dac1d3SFranck LENORMAND 	for (i = 1; i < msg.hdr.size; i++) {
436*11dac1d3SFranck LENORMAND 		dev_dbg(priv->dev, "Receiving word %d\n", i);
437*11dac1d3SFranck LENORMAND 		err = imx_mu_rx_waiting_read(priv, data++, i);
438*11dac1d3SFranck LENORMAND 		if (err) {
439*11dac1d3SFranck LENORMAND 			dev_err(priv->dev, "Timeout rx %d\n", i);
440*11dac1d3SFranck LENORMAND 			goto error;
441*11dac1d3SFranck LENORMAND 		}
442*11dac1d3SFranck LENORMAND 	}
443*11dac1d3SFranck LENORMAND 
444*11dac1d3SFranck LENORMAND 	/* Clear GIP */
445*11dac1d3SFranck LENORMAND 	imx_mu_write(priv, IMX_MU_xSR_GIPn(priv->dcfg->type, cp->idx),
446*11dac1d3SFranck LENORMAND 		     priv->dcfg->xSR[IMX_MU_GSR]);
447*11dac1d3SFranck LENORMAND 
448*11dac1d3SFranck LENORMAND 	print_hex_dump_debug("to client ", DUMP_PREFIX_OFFSET, 4, 4,
449*11dac1d3SFranck LENORMAND 			     &msg, byte_size, false);
450*11dac1d3SFranck LENORMAND 
451*11dac1d3SFranck LENORMAND 	/* send data to client */
452*11dac1d3SFranck LENORMAND 	dev_dbg(priv->dev, "Sending message to client\n");
453*11dac1d3SFranck LENORMAND 	mbox_chan_received_data(cp->chan, (void *)&msg);
454*11dac1d3SFranck LENORMAND 
455*11dac1d3SFranck LENORMAND 	goto exit;
456*11dac1d3SFranck LENORMAND 
457*11dac1d3SFranck LENORMAND error:
458*11dac1d3SFranck LENORMAND 	mbox_chan_received_data(cp->chan, ERR_PTR(err));
459*11dac1d3SFranck LENORMAND 
460*11dac1d3SFranck LENORMAND exit:
461*11dac1d3SFranck LENORMAND 	return err;
462*11dac1d3SFranck LENORMAND }
463*11dac1d3SFranck LENORMAND 
4642bb70056SOleksij Rempel static void imx_mu_txdb_tasklet(unsigned long data)
4652bb70056SOleksij Rempel {
4662bb70056SOleksij Rempel 	struct imx_mu_con_priv *cp = (struct imx_mu_con_priv *)data;
4672bb70056SOleksij Rempel 
4682bb70056SOleksij Rempel 	mbox_chan_txdone(cp->chan, 0);
4692bb70056SOleksij Rempel }
4702bb70056SOleksij Rempel 
4712bb70056SOleksij Rempel static irqreturn_t imx_mu_isr(int irq, void *p)
4722bb70056SOleksij Rempel {
4732bb70056SOleksij Rempel 	struct mbox_chan *chan = p;
4742bb70056SOleksij Rempel 	struct imx_mu_priv *priv = to_imx_mu_priv(chan->mbox);
4752bb70056SOleksij Rempel 	struct imx_mu_con_priv *cp = chan->con_priv;
47663b38357SPeng Fan 	u32 val, ctrl;
4772bb70056SOleksij Rempel 
4782bb70056SOleksij Rempel 	switch (cp->type) {
4792bb70056SOleksij Rempel 	case IMX_MU_TYPE_TX:
480f689a7cfSPeng Fan 		ctrl = imx_mu_read(priv, priv->dcfg->xCR[IMX_MU_TCR]);
481f689a7cfSPeng Fan 		val = imx_mu_read(priv, priv->dcfg->xSR[IMX_MU_TSR]);
4824f0b776eSPeng Fan 		val &= IMX_MU_xSR_TEn(priv->dcfg->type, cp->idx) &
4834f0b776eSPeng Fan 			(ctrl & IMX_MU_xCR_TIEn(priv->dcfg->type, cp->idx));
4842bb70056SOleksij Rempel 		break;
4852bb70056SOleksij Rempel 	case IMX_MU_TYPE_RX:
486f689a7cfSPeng Fan 		ctrl = imx_mu_read(priv, priv->dcfg->xCR[IMX_MU_RCR]);
487f689a7cfSPeng Fan 		val = imx_mu_read(priv, priv->dcfg->xSR[IMX_MU_RSR]);
4884f0b776eSPeng Fan 		val &= IMX_MU_xSR_RFn(priv->dcfg->type, cp->idx) &
4894f0b776eSPeng Fan 			(ctrl & IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx));
4902bb70056SOleksij Rempel 		break;
4912bb70056SOleksij Rempel 	case IMX_MU_TYPE_RXDB:
4924f0b776eSPeng Fan 		ctrl = imx_mu_read(priv, priv->dcfg->xCR[IMX_MU_GIER]);
493f689a7cfSPeng Fan 		val = imx_mu_read(priv, priv->dcfg->xSR[IMX_MU_GSR]);
4944f0b776eSPeng Fan 		val &= IMX_MU_xSR_GIPn(priv->dcfg->type, cp->idx) &
4954f0b776eSPeng Fan 			(ctrl & IMX_MU_xCR_GIEn(priv->dcfg->type, cp->idx));
4962bb70056SOleksij Rempel 		break;
4972bb70056SOleksij Rempel 	default:
498e80a7e7eSNathan Chancellor 		dev_warn_ratelimited(priv->dev, "Unhandled channel type %d\n",
499e80a7e7eSNathan Chancellor 				     cp->type);
500e80a7e7eSNathan Chancellor 		return IRQ_NONE;
5012bb70056SOleksij Rempel 	}
5022bb70056SOleksij Rempel 
5032bb70056SOleksij Rempel 	if (!val)
5042bb70056SOleksij Rempel 		return IRQ_NONE;
5052bb70056SOleksij Rempel 
5064f0b776eSPeng Fan 	if ((val == IMX_MU_xSR_TEn(priv->dcfg->type, cp->idx)) &&
5074f0b776eSPeng Fan 	    (cp->type == IMX_MU_TYPE_TX)) {
5084f0b776eSPeng Fan 		imx_mu_xcr_rmw(priv, IMX_MU_TCR, 0, IMX_MU_xCR_TIEn(priv->dcfg->type, cp->idx));
5092bb70056SOleksij Rempel 		mbox_chan_txdone(chan, 0);
5104f0b776eSPeng Fan 	} else if ((val == IMX_MU_xSR_RFn(priv->dcfg->type, cp->idx)) &&
5114f0b776eSPeng Fan 		   (cp->type == IMX_MU_TYPE_RX)) {
51263b38357SPeng Fan 		priv->dcfg->rx(priv, cp);
5134f0b776eSPeng Fan 	} else if ((val == IMX_MU_xSR_GIPn(priv->dcfg->type, cp->idx)) &&
5144f0b776eSPeng Fan 		   (cp->type == IMX_MU_TYPE_RXDB)) {
515315d2e56SPeng Fan 		priv->dcfg->rxdb(priv, cp);
5162bb70056SOleksij Rempel 	} else {
5172bb70056SOleksij Rempel 		dev_warn_ratelimited(priv->dev, "Not handled interrupt\n");
5182bb70056SOleksij Rempel 		return IRQ_NONE;
5192bb70056SOleksij Rempel 	}
5202bb70056SOleksij Rempel 
521892cb524SRobin Gong 	if (priv->suspend)
522892cb524SRobin Gong 		pm_system_wakeup();
523892cb524SRobin Gong 
5242bb70056SOleksij Rempel 	return IRQ_HANDLED;
5252bb70056SOleksij Rempel }
5262bb70056SOleksij Rempel 
5272bb70056SOleksij Rempel static int imx_mu_send_data(struct mbox_chan *chan, void *data)
5282bb70056SOleksij Rempel {
5292bb70056SOleksij Rempel 	struct imx_mu_priv *priv = to_imx_mu_priv(chan->mbox);
5302bb70056SOleksij Rempel 	struct imx_mu_con_priv *cp = chan->con_priv;
5312bb70056SOleksij Rempel 
53263b38357SPeng Fan 	return priv->dcfg->tx(priv, cp, data);
5332bb70056SOleksij Rempel }
5342bb70056SOleksij Rempel 
5352bb70056SOleksij Rempel static int imx_mu_startup(struct mbox_chan *chan)
5362bb70056SOleksij Rempel {
5372bb70056SOleksij Rempel 	struct imx_mu_priv *priv = to_imx_mu_priv(chan->mbox);
5382bb70056SOleksij Rempel 	struct imx_mu_con_priv *cp = chan->con_priv;
539b7b2796bSAnson Huang 	unsigned long irq_flag = IRQF_SHARED;
5402bb70056SOleksij Rempel 	int ret;
5412bb70056SOleksij Rempel 
542676f23eaSAnson Huang 	pm_runtime_get_sync(priv->dev);
5432bb70056SOleksij Rempel 	if (cp->type == IMX_MU_TYPE_TXDB) {
5442bb70056SOleksij Rempel 		/* Tx doorbell don't have ACK support */
5452bb70056SOleksij Rempel 		tasklet_init(&cp->txdb_tasklet, imx_mu_txdb_tasklet,
5462bb70056SOleksij Rempel 			     (unsigned long)cp);
5472bb70056SOleksij Rempel 		return 0;
5482bb70056SOleksij Rempel 	}
5492bb70056SOleksij Rempel 
550b7b2796bSAnson Huang 	/* IPC MU should be with IRQF_NO_SUSPEND set */
551b7b2796bSAnson Huang 	if (!priv->dev->pm_domain)
552b7b2796bSAnson Huang 		irq_flag |= IRQF_NO_SUSPEND;
553b7b2796bSAnson Huang 
554b7b2796bSAnson Huang 	ret = request_irq(priv->irq, imx_mu_isr, irq_flag,
555b7b2796bSAnson Huang 			  cp->irq_desc, chan);
5562bb70056SOleksij Rempel 	if (ret) {
5572bb70056SOleksij Rempel 		dev_err(priv->dev,
5582bb70056SOleksij Rempel 			"Unable to acquire IRQ %d\n", priv->irq);
5592bb70056SOleksij Rempel 		return ret;
5602bb70056SOleksij Rempel 	}
5612bb70056SOleksij Rempel 
5622bb70056SOleksij Rempel 	switch (cp->type) {
5632bb70056SOleksij Rempel 	case IMX_MU_TYPE_RX:
5644f0b776eSPeng Fan 		imx_mu_xcr_rmw(priv, IMX_MU_RCR, IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx), 0);
5652bb70056SOleksij Rempel 		break;
5662bb70056SOleksij Rempel 	case IMX_MU_TYPE_RXDB:
5674f0b776eSPeng Fan 		imx_mu_xcr_rmw(priv, IMX_MU_GIER, IMX_MU_xCR_GIEn(priv->dcfg->type, cp->idx), 0);
5682bb70056SOleksij Rempel 		break;
5692bb70056SOleksij Rempel 	default:
5702bb70056SOleksij Rempel 		break;
5712bb70056SOleksij Rempel 	}
5722bb70056SOleksij Rempel 
5732bb70056SOleksij Rempel 	return 0;
5742bb70056SOleksij Rempel }
5752bb70056SOleksij Rempel 
5762bb70056SOleksij Rempel static void imx_mu_shutdown(struct mbox_chan *chan)
5772bb70056SOleksij Rempel {
5782bb70056SOleksij Rempel 	struct imx_mu_priv *priv = to_imx_mu_priv(chan->mbox);
5792bb70056SOleksij Rempel 	struct imx_mu_con_priv *cp = chan->con_priv;
5802bb70056SOleksij Rempel 
581bf159d15SDaniel Baluta 	if (cp->type == IMX_MU_TYPE_TXDB) {
5822bb70056SOleksij Rempel 		tasklet_kill(&cp->txdb_tasklet);
583676f23eaSAnson Huang 		pm_runtime_put_sync(priv->dev);
584bf159d15SDaniel Baluta 		return;
585bf159d15SDaniel Baluta 	}
5862bb70056SOleksij Rempel 
5875f0af07eSDaniel Baluta 	switch (cp->type) {
5885f0af07eSDaniel Baluta 	case IMX_MU_TYPE_TX:
5894f0b776eSPeng Fan 		imx_mu_xcr_rmw(priv, IMX_MU_TCR, 0, IMX_MU_xCR_TIEn(priv->dcfg->type, cp->idx));
5905f0af07eSDaniel Baluta 		break;
5915f0af07eSDaniel Baluta 	case IMX_MU_TYPE_RX:
5924f0b776eSPeng Fan 		imx_mu_xcr_rmw(priv, IMX_MU_RCR, 0, IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx));
5935f0af07eSDaniel Baluta 		break;
5945f0af07eSDaniel Baluta 	case IMX_MU_TYPE_RXDB:
5954f0b776eSPeng Fan 		imx_mu_xcr_rmw(priv, IMX_MU_GIER, 0, IMX_MU_xCR_GIEn(priv->dcfg->type, cp->idx));
5965f0af07eSDaniel Baluta 		break;
5975f0af07eSDaniel Baluta 	default:
5985f0af07eSDaniel Baluta 		break;
5995f0af07eSDaniel Baluta 	}
6002bb70056SOleksij Rempel 
6012bb70056SOleksij Rempel 	free_irq(priv->irq, chan);
602676f23eaSAnson Huang 	pm_runtime_put_sync(priv->dev);
6032bb70056SOleksij Rempel }
6042bb70056SOleksij Rempel 
6052bb70056SOleksij Rempel static const struct mbox_chan_ops imx_mu_ops = {
6062bb70056SOleksij Rempel 	.send_data = imx_mu_send_data,
6072bb70056SOleksij Rempel 	.startup = imx_mu_startup,
6082bb70056SOleksij Rempel 	.shutdown = imx_mu_shutdown,
6092bb70056SOleksij Rempel };
6102bb70056SOleksij Rempel 
61197961f78SPeng Fan static struct mbox_chan *imx_mu_specific_xlate(struct mbox_controller *mbox,
6120a67003bSPeng Fan 					       const struct of_phandle_args *sp)
6130a67003bSPeng Fan {
6140a67003bSPeng Fan 	u32 type, idx, chan;
6150a67003bSPeng Fan 
6160a67003bSPeng Fan 	if (sp->args_count != 2) {
6170a67003bSPeng Fan 		dev_err(mbox->dev, "Invalid argument count %d\n", sp->args_count);
6180a67003bSPeng Fan 		return ERR_PTR(-EINVAL);
6190a67003bSPeng Fan 	}
6200a67003bSPeng Fan 
6210a67003bSPeng Fan 	type = sp->args[0]; /* channel type */
6220a67003bSPeng Fan 	idx = sp->args[1]; /* index */
6230a67003bSPeng Fan 
6240a67003bSPeng Fan 	switch (type) {
6250a67003bSPeng Fan 	case IMX_MU_TYPE_TX:
6260a67003bSPeng Fan 	case IMX_MU_TYPE_RX:
6270a67003bSPeng Fan 		if (idx != 0)
6280a67003bSPeng Fan 			dev_err(mbox->dev, "Invalid chan idx: %d\n", idx);
6290a67003bSPeng Fan 		chan = type;
6300a67003bSPeng Fan 		break;
6310a67003bSPeng Fan 	case IMX_MU_TYPE_RXDB:
6320a67003bSPeng Fan 		chan = 2 + idx;
6330a67003bSPeng Fan 		break;
6340a67003bSPeng Fan 	default:
6350a67003bSPeng Fan 		dev_err(mbox->dev, "Invalid chan type: %d\n", type);
6361b3a347bSDan Carpenter 		return ERR_PTR(-EINVAL);
6370a67003bSPeng Fan 	}
6380a67003bSPeng Fan 
6390a67003bSPeng Fan 	if (chan >= mbox->num_chans) {
6400a67003bSPeng Fan 		dev_err(mbox->dev, "Not supported channel number: %d. (type: %d, idx: %d)\n", chan, type, idx);
6410a67003bSPeng Fan 		return ERR_PTR(-EINVAL);
6420a67003bSPeng Fan 	}
6430a67003bSPeng Fan 
6440a67003bSPeng Fan 	return &mbox->chans[chan];
6450a67003bSPeng Fan }
6460a67003bSPeng Fan 
6472bb70056SOleksij Rempel static struct mbox_chan * imx_mu_xlate(struct mbox_controller *mbox,
6482bb70056SOleksij Rempel 				       const struct of_phandle_args *sp)
6492bb70056SOleksij Rempel {
6502bb70056SOleksij Rempel 	u32 type, idx, chan;
6512bb70056SOleksij Rempel 
6522bb70056SOleksij Rempel 	if (sp->args_count != 2) {
6532bb70056SOleksij Rempel 		dev_err(mbox->dev, "Invalid argument count %d\n", sp->args_count);
6542bb70056SOleksij Rempel 		return ERR_PTR(-EINVAL);
6552bb70056SOleksij Rempel 	}
6562bb70056SOleksij Rempel 
6572bb70056SOleksij Rempel 	type = sp->args[0]; /* channel type */
6582bb70056SOleksij Rempel 	idx = sp->args[1]; /* index */
6592bb70056SOleksij Rempel 	chan = type * 4 + idx;
6602bb70056SOleksij Rempel 
6612bb70056SOleksij Rempel 	if (chan >= mbox->num_chans) {
6622bb70056SOleksij Rempel 		dev_err(mbox->dev, "Not supported channel number: %d. (type: %d, idx: %d)\n", chan, type, idx);
6632bb70056SOleksij Rempel 		return ERR_PTR(-EINVAL);
6642bb70056SOleksij Rempel 	}
6652bb70056SOleksij Rempel 
6662bb70056SOleksij Rempel 	return &mbox->chans[chan];
6672bb70056SOleksij Rempel }
6682bb70056SOleksij Rempel 
669*11dac1d3SFranck LENORMAND static struct mbox_chan *imx_mu_seco_xlate(struct mbox_controller *mbox,
670*11dac1d3SFranck LENORMAND 					   const struct of_phandle_args *sp)
671*11dac1d3SFranck LENORMAND {
672*11dac1d3SFranck LENORMAND 	u32 type;
673*11dac1d3SFranck LENORMAND 
674*11dac1d3SFranck LENORMAND 	if (sp->args_count < 1) {
675*11dac1d3SFranck LENORMAND 		dev_err(mbox->dev, "Invalid argument count %d\n", sp->args_count);
676*11dac1d3SFranck LENORMAND 		return ERR_PTR(-EINVAL);
677*11dac1d3SFranck LENORMAND 	}
678*11dac1d3SFranck LENORMAND 
679*11dac1d3SFranck LENORMAND 	type = sp->args[0]; /* channel type */
680*11dac1d3SFranck LENORMAND 
681*11dac1d3SFranck LENORMAND 	/* Only supports TXDB and RXDB */
682*11dac1d3SFranck LENORMAND 	if (type == IMX_MU_TYPE_TX || type == IMX_MU_TYPE_RX) {
683*11dac1d3SFranck LENORMAND 		dev_err(mbox->dev, "Invalid type: %d\n", type);
684*11dac1d3SFranck LENORMAND 		return ERR_PTR(-EINVAL);
685*11dac1d3SFranck LENORMAND 	}
686*11dac1d3SFranck LENORMAND 
687*11dac1d3SFranck LENORMAND 	return imx_mu_xlate(mbox, sp);
688*11dac1d3SFranck LENORMAND }
689*11dac1d3SFranck LENORMAND 
6902bb70056SOleksij Rempel static void imx_mu_init_generic(struct imx_mu_priv *priv)
6912bb70056SOleksij Rempel {
69263b38357SPeng Fan 	unsigned int i;
69363b38357SPeng Fan 
69463b38357SPeng Fan 	for (i = 0; i < IMX_MU_CHANS; i++) {
69563b38357SPeng Fan 		struct imx_mu_con_priv *cp = &priv->con_priv[i];
69663b38357SPeng Fan 
69763b38357SPeng Fan 		cp->idx = i % 4;
69863b38357SPeng Fan 		cp->type = i >> 2;
69963b38357SPeng Fan 		cp->chan = &priv->mbox_chans[i];
70063b38357SPeng Fan 		priv->mbox_chans[i].con_priv = cp;
70163b38357SPeng Fan 		snprintf(cp->irq_desc, sizeof(cp->irq_desc),
70263b38357SPeng Fan 			 "imx_mu_chan[%i-%i]", cp->type, cp->idx);
70363b38357SPeng Fan 	}
70463b38357SPeng Fan 
70563b38357SPeng Fan 	priv->mbox.num_chans = IMX_MU_CHANS;
70663b38357SPeng Fan 	priv->mbox.of_xlate = imx_mu_xlate;
70763b38357SPeng Fan 
7082bb70056SOleksij Rempel 	if (priv->side_b)
7092bb70056SOleksij Rempel 		return;
7102bb70056SOleksij Rempel 
7112bb70056SOleksij Rempel 	/* Set default MU configuration */
712f689a7cfSPeng Fan 	for (i = 0; i < IMX_MU_xCR_MAX; i++)
713f689a7cfSPeng Fan 		imx_mu_write(priv, 0, priv->dcfg->xCR[i]);
7142bb70056SOleksij Rempel }
7152bb70056SOleksij Rempel 
71697961f78SPeng Fan static void imx_mu_init_specific(struct imx_mu_priv *priv)
7170a67003bSPeng Fan {
7180a67003bSPeng Fan 	unsigned int i;
71997961f78SPeng Fan 	int num_chans = priv->dcfg->type & IMX_MU_V2_S4 ? IMX_MU_S4_CHANS : IMX_MU_SCU_CHANS;
7200a67003bSPeng Fan 
72197961f78SPeng Fan 	for (i = 0; i < num_chans; i++) {
7220a67003bSPeng Fan 		struct imx_mu_con_priv *cp = &priv->con_priv[i];
7230a67003bSPeng Fan 
7240a67003bSPeng Fan 		cp->idx = i < 2 ? 0 : i - 2;
7250a67003bSPeng Fan 		cp->type = i < 2 ? i : IMX_MU_TYPE_RXDB;
7260a67003bSPeng Fan 		cp->chan = &priv->mbox_chans[i];
7270a67003bSPeng Fan 		priv->mbox_chans[i].con_priv = cp;
7280a67003bSPeng Fan 		snprintf(cp->irq_desc, sizeof(cp->irq_desc),
7290a67003bSPeng Fan 			 "imx_mu_chan[%i-%i]", cp->type, cp->idx);
7300a67003bSPeng Fan 	}
7310a67003bSPeng Fan 
73297961f78SPeng Fan 	priv->mbox.num_chans = num_chans;
73397961f78SPeng Fan 	priv->mbox.of_xlate = imx_mu_specific_xlate;
7340a67003bSPeng Fan 
7350a67003bSPeng Fan 	/* Set default MU configuration */
736f689a7cfSPeng Fan 	for (i = 0; i < IMX_MU_xCR_MAX; i++)
737f689a7cfSPeng Fan 		imx_mu_write(priv, 0, priv->dcfg->xCR[i]);
7380a67003bSPeng Fan }
7390a67003bSPeng Fan 
740*11dac1d3SFranck LENORMAND static void imx_mu_init_seco(struct imx_mu_priv *priv)
741*11dac1d3SFranck LENORMAND {
742*11dac1d3SFranck LENORMAND 	imx_mu_init_generic(priv);
743*11dac1d3SFranck LENORMAND 	priv->mbox.of_xlate = imx_mu_seco_xlate;
744*11dac1d3SFranck LENORMAND }
745*11dac1d3SFranck LENORMAND 
7462bb70056SOleksij Rempel static int imx_mu_probe(struct platform_device *pdev)
7472bb70056SOleksij Rempel {
7482bb70056SOleksij Rempel 	struct device *dev = &pdev->dev;
7492bb70056SOleksij Rempel 	struct device_node *np = dev->of_node;
7502bb70056SOleksij Rempel 	struct imx_mu_priv *priv;
751c6c6bc6eSRichard Zhu 	const struct imx_mu_dcfg *dcfg;
7522bb70056SOleksij Rempel 	int ret;
75397961f78SPeng Fan 	u32 size;
7542bb70056SOleksij Rempel 
7552bb70056SOleksij Rempel 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
7562bb70056SOleksij Rempel 	if (!priv)
7572bb70056SOleksij Rempel 		return -ENOMEM;
7582bb70056SOleksij Rempel 
7592bb70056SOleksij Rempel 	priv->dev = dev;
7602bb70056SOleksij Rempel 
7610c40e631SAnson Huang 	priv->base = devm_platform_ioremap_resource(pdev, 0);
7622bb70056SOleksij Rempel 	if (IS_ERR(priv->base))
7632bb70056SOleksij Rempel 		return PTR_ERR(priv->base);
7642bb70056SOleksij Rempel 
7652bb70056SOleksij Rempel 	priv->irq = platform_get_irq(pdev, 0);
7662bb70056SOleksij Rempel 	if (priv->irq < 0)
7672bb70056SOleksij Rempel 		return priv->irq;
7682bb70056SOleksij Rempel 
769c6c6bc6eSRichard Zhu 	dcfg = of_device_get_match_data(dev);
770c6c6bc6eSRichard Zhu 	if (!dcfg)
771c6c6bc6eSRichard Zhu 		return -EINVAL;
772c6c6bc6eSRichard Zhu 	priv->dcfg = dcfg;
773c6c6bc6eSRichard Zhu 
77497961f78SPeng Fan 	if (priv->dcfg->type & IMX_MU_V2_S4)
77597961f78SPeng Fan 		size = sizeof(struct imx_s4_rpc_msg_max);
77697961f78SPeng Fan 	else
77797961f78SPeng Fan 		size = sizeof(struct imx_sc_rpc_msg_max);
77897961f78SPeng Fan 
77997961f78SPeng Fan 	priv->msg = devm_kzalloc(dev, size, GFP_KERNEL);
78005d06f37SDan Carpenter 	if (!priv->msg)
78105d06f37SDan Carpenter 		return -ENOMEM;
78297961f78SPeng Fan 
7832bb70056SOleksij Rempel 	priv->clk = devm_clk_get(dev, NULL);
7842bb70056SOleksij Rempel 	if (IS_ERR(priv->clk)) {
7852bb70056SOleksij Rempel 		if (PTR_ERR(priv->clk) != -ENOENT)
7862bb70056SOleksij Rempel 			return PTR_ERR(priv->clk);
7872bb70056SOleksij Rempel 
7882bb70056SOleksij Rempel 		priv->clk = NULL;
7892bb70056SOleksij Rempel 	}
7902bb70056SOleksij Rempel 
7912bb70056SOleksij Rempel 	ret = clk_prepare_enable(priv->clk);
7922bb70056SOleksij Rempel 	if (ret) {
7932bb70056SOleksij Rempel 		dev_err(dev, "Failed to enable clock\n");
7942bb70056SOleksij Rempel 		return ret;
7952bb70056SOleksij Rempel 	}
7962bb70056SOleksij Rempel 
7972bb70056SOleksij Rempel 	priv->side_b = of_property_read_bool(np, "fsl,mu-side-b");
7982bb70056SOleksij Rempel 
79963b38357SPeng Fan 	priv->dcfg->init(priv);
80063b38357SPeng Fan 
8012bb70056SOleksij Rempel 	spin_lock_init(&priv->xcr_lock);
8022bb70056SOleksij Rempel 
8032bb70056SOleksij Rempel 	priv->mbox.dev = dev;
8042bb70056SOleksij Rempel 	priv->mbox.ops = &imx_mu_ops;
8052bb70056SOleksij Rempel 	priv->mbox.chans = priv->mbox_chans;
8062bb70056SOleksij Rempel 	priv->mbox.txdone_irq = true;
8072bb70056SOleksij Rempel 
8082bb70056SOleksij Rempel 	platform_set_drvdata(pdev, priv);
8092bb70056SOleksij Rempel 
810676f23eaSAnson Huang 	ret = devm_mbox_controller_register(dev, &priv->mbox);
81147303f94SFabio Estevam 	if (ret) {
81247303f94SFabio Estevam 		clk_disable_unprepare(priv->clk);
813676f23eaSAnson Huang 		return ret;
81447303f94SFabio Estevam 	}
815676f23eaSAnson Huang 
816676f23eaSAnson Huang 	pm_runtime_enable(dev);
817676f23eaSAnson Huang 
818676f23eaSAnson Huang 	ret = pm_runtime_get_sync(dev);
819676f23eaSAnson Huang 	if (ret < 0) {
820676f23eaSAnson Huang 		pm_runtime_put_noidle(dev);
821676f23eaSAnson Huang 		goto disable_runtime_pm;
822676f23eaSAnson Huang 	}
823676f23eaSAnson Huang 
824676f23eaSAnson Huang 	ret = pm_runtime_put_sync(dev);
825676f23eaSAnson Huang 	if (ret < 0)
826676f23eaSAnson Huang 		goto disable_runtime_pm;
827676f23eaSAnson Huang 
828bb2b2624SAnson Huang 	clk_disable_unprepare(priv->clk);
829bb2b2624SAnson Huang 
830676f23eaSAnson Huang 	return 0;
831676f23eaSAnson Huang 
832676f23eaSAnson Huang disable_runtime_pm:
833676f23eaSAnson Huang 	pm_runtime_disable(dev);
834bb2b2624SAnson Huang 	clk_disable_unprepare(priv->clk);
835676f23eaSAnson Huang 	return ret;
8362bb70056SOleksij Rempel }
8372bb70056SOleksij Rempel 
8382bb70056SOleksij Rempel static int imx_mu_remove(struct platform_device *pdev)
8392bb70056SOleksij Rempel {
8402bb70056SOleksij Rempel 	struct imx_mu_priv *priv = platform_get_drvdata(pdev);
8412bb70056SOleksij Rempel 
842676f23eaSAnson Huang 	pm_runtime_disable(priv->dev);
8432bb70056SOleksij Rempel 
8442bb70056SOleksij Rempel 	return 0;
8452bb70056SOleksij Rempel }
8462bb70056SOleksij Rempel 
84763b38357SPeng Fan static const struct imx_mu_dcfg imx_mu_cfg_imx6sx = {
84863b38357SPeng Fan 	.tx	= imx_mu_generic_tx,
84963b38357SPeng Fan 	.rx	= imx_mu_generic_rx,
850315d2e56SPeng Fan 	.rxdb	= imx_mu_generic_rxdb,
85163b38357SPeng Fan 	.init	= imx_mu_init_generic,
85232f7443dSPeng Fan 	.xTR	= 0x0,
85332f7443dSPeng Fan 	.xRR	= 0x10,
854f689a7cfSPeng Fan 	.xSR	= {0x20, 0x20, 0x20, 0x20},
855f689a7cfSPeng Fan 	.xCR	= {0x24, 0x24, 0x24, 0x24},
85663b38357SPeng Fan };
85763b38357SPeng Fan 
85863b38357SPeng Fan static const struct imx_mu_dcfg imx_mu_cfg_imx7ulp = {
85963b38357SPeng Fan 	.tx	= imx_mu_generic_tx,
86063b38357SPeng Fan 	.rx	= imx_mu_generic_rx,
861315d2e56SPeng Fan 	.rxdb	= imx_mu_generic_rxdb,
86263b38357SPeng Fan 	.init	= imx_mu_init_generic,
86332f7443dSPeng Fan 	.xTR	= 0x20,
86432f7443dSPeng Fan 	.xRR	= 0x40,
865f689a7cfSPeng Fan 	.xSR	= {0x60, 0x60, 0x60, 0x60},
866f689a7cfSPeng Fan 	.xCR	= {0x64, 0x64, 0x64, 0x64},
86763b38357SPeng Fan };
86863b38357SPeng Fan 
8694f0b776eSPeng Fan static const struct imx_mu_dcfg imx_mu_cfg_imx8ulp = {
8704f0b776eSPeng Fan 	.tx	= imx_mu_generic_tx,
8714f0b776eSPeng Fan 	.rx	= imx_mu_generic_rx,
872315d2e56SPeng Fan 	.rxdb	= imx_mu_generic_rxdb,
8734f0b776eSPeng Fan 	.init	= imx_mu_init_generic,
874315d2e56SPeng Fan 	.rxdb	= imx_mu_generic_rxdb,
8754f0b776eSPeng Fan 	.type	= IMX_MU_V2,
8764f0b776eSPeng Fan 	.xTR	= 0x200,
8774f0b776eSPeng Fan 	.xRR	= 0x280,
8784f0b776eSPeng Fan 	.xSR	= {0xC, 0x118, 0x124, 0x12C},
8794f0b776eSPeng Fan 	.xCR	= {0x110, 0x114, 0x120, 0x128},
8804f0b776eSPeng Fan };
8814f0b776eSPeng Fan 
88297961f78SPeng Fan static const struct imx_mu_dcfg imx_mu_cfg_imx8ulp_s4 = {
88397961f78SPeng Fan 	.tx	= imx_mu_specific_tx,
88497961f78SPeng Fan 	.rx	= imx_mu_specific_rx,
88597961f78SPeng Fan 	.init	= imx_mu_init_specific,
88697961f78SPeng Fan 	.type	= IMX_MU_V2 | IMX_MU_V2_S4,
88797961f78SPeng Fan 	.xTR	= 0x200,
88897961f78SPeng Fan 	.xRR	= 0x280,
88997961f78SPeng Fan 	.xSR	= {0xC, 0x118, 0x124, 0x12C},
89097961f78SPeng Fan 	.xCR	= {0x110, 0x114, 0x120, 0x128},
89197961f78SPeng Fan };
89297961f78SPeng Fan 
8930a67003bSPeng Fan static const struct imx_mu_dcfg imx_mu_cfg_imx8_scu = {
89497961f78SPeng Fan 	.tx	= imx_mu_specific_tx,
89597961f78SPeng Fan 	.rx	= imx_mu_specific_rx,
89697961f78SPeng Fan 	.init	= imx_mu_init_specific,
897315d2e56SPeng Fan 	.rxdb	= imx_mu_generic_rxdb,
8984f0b776eSPeng Fan 	.xTR	= 0x0,
8994f0b776eSPeng Fan 	.xRR	= 0x10,
900f689a7cfSPeng Fan 	.xSR	= {0x20, 0x20, 0x20, 0x20},
901f689a7cfSPeng Fan 	.xCR	= {0x24, 0x24, 0x24, 0x24},
9020a67003bSPeng Fan };
9030a67003bSPeng Fan 
904*11dac1d3SFranck LENORMAND static const struct imx_mu_dcfg imx_mu_cfg_imx8_seco = {
905*11dac1d3SFranck LENORMAND 	.tx	= imx_mu_seco_tx,
906*11dac1d3SFranck LENORMAND 	.rx	= imx_mu_generic_rx,
907*11dac1d3SFranck LENORMAND 	.rxdb	= imx_mu_seco_rxdb,
908*11dac1d3SFranck LENORMAND 	.init	= imx_mu_init_seco,
909*11dac1d3SFranck LENORMAND 	.xTR	= 0x0,
910*11dac1d3SFranck LENORMAND 	.xRR	= 0x10,
911*11dac1d3SFranck LENORMAND 	.xSR	= {0x20, 0x20, 0x20, 0x20},
912*11dac1d3SFranck LENORMAND 	.xCR	= {0x24, 0x24, 0x24, 0x24},
913*11dac1d3SFranck LENORMAND };
914*11dac1d3SFranck LENORMAND 
9152bb70056SOleksij Rempel static const struct of_device_id imx_mu_dt_ids[] = {
916c6c6bc6eSRichard Zhu 	{ .compatible = "fsl,imx7ulp-mu", .data = &imx_mu_cfg_imx7ulp },
917c6c6bc6eSRichard Zhu 	{ .compatible = "fsl,imx6sx-mu", .data = &imx_mu_cfg_imx6sx },
9184f0b776eSPeng Fan 	{ .compatible = "fsl,imx8ulp-mu", .data = &imx_mu_cfg_imx8ulp },
91997961f78SPeng Fan 	{ .compatible = "fsl,imx8ulp-mu-s4", .data = &imx_mu_cfg_imx8ulp_s4 },
9200a67003bSPeng Fan 	{ .compatible = "fsl,imx8-mu-scu", .data = &imx_mu_cfg_imx8_scu },
921*11dac1d3SFranck LENORMAND 	{ .compatible = "fsl,imx8-mu-seco", .data = &imx_mu_cfg_imx8_seco },
9222bb70056SOleksij Rempel 	{ },
9232bb70056SOleksij Rempel };
9242bb70056SOleksij Rempel MODULE_DEVICE_TABLE(of, imx_mu_dt_ids);
9252bb70056SOleksij Rempel 
92603b70130SNathan Chancellor static int __maybe_unused imx_mu_suspend_noirq(struct device *dev)
927ba5f9fa0SDong Aisheng {
928ba5f9fa0SDong Aisheng 	struct imx_mu_priv *priv = dev_get_drvdata(dev);
929f689a7cfSPeng Fan 	int i;
930ba5f9fa0SDong Aisheng 
931f689a7cfSPeng Fan 	if (!priv->clk) {
932f689a7cfSPeng Fan 		for (i = 0; i < IMX_MU_xCR_MAX; i++)
933f689a7cfSPeng Fan 			priv->xcr[i] = imx_mu_read(priv, priv->dcfg->xCR[i]);
934f689a7cfSPeng Fan 	}
935ba5f9fa0SDong Aisheng 
936892cb524SRobin Gong 	priv->suspend = true;
937892cb524SRobin Gong 
938ba5f9fa0SDong Aisheng 	return 0;
939ba5f9fa0SDong Aisheng }
940ba5f9fa0SDong Aisheng 
94103b70130SNathan Chancellor static int __maybe_unused imx_mu_resume_noirq(struct device *dev)
942ba5f9fa0SDong Aisheng {
943ba5f9fa0SDong Aisheng 	struct imx_mu_priv *priv = dev_get_drvdata(dev);
944f689a7cfSPeng Fan 	int i;
945ba5f9fa0SDong Aisheng 
946ba5f9fa0SDong Aisheng 	/*
947ba5f9fa0SDong Aisheng 	 * ONLY restore MU when context lost, the TIE could
948ba5f9fa0SDong Aisheng 	 * be set during noirq resume as there is MU data
949ba5f9fa0SDong Aisheng 	 * communication going on, and restore the saved
950ba5f9fa0SDong Aisheng 	 * value will overwrite the TIE and cause MU data
951ba5f9fa0SDong Aisheng 	 * send failed, may lead to system freeze. This issue
952ba5f9fa0SDong Aisheng 	 * is observed by testing freeze mode suspend.
953ba5f9fa0SDong Aisheng 	 */
9548219efd0SRobin Gong 	if (!priv->clk && !imx_mu_read(priv, priv->dcfg->xCR[0])) {
955f689a7cfSPeng Fan 		for (i = 0; i < IMX_MU_xCR_MAX; i++)
956f689a7cfSPeng Fan 			imx_mu_write(priv, priv->xcr[i], priv->dcfg->xCR[i]);
957f689a7cfSPeng Fan 	}
958ba5f9fa0SDong Aisheng 
959892cb524SRobin Gong 	priv->suspend = false;
960892cb524SRobin Gong 
961ba5f9fa0SDong Aisheng 	return 0;
962ba5f9fa0SDong Aisheng }
963ba5f9fa0SDong Aisheng 
96403b70130SNathan Chancellor static int __maybe_unused imx_mu_runtime_suspend(struct device *dev)
965bb2b2624SAnson Huang {
966bb2b2624SAnson Huang 	struct imx_mu_priv *priv = dev_get_drvdata(dev);
967bb2b2624SAnson Huang 
968bb2b2624SAnson Huang 	clk_disable_unprepare(priv->clk);
969bb2b2624SAnson Huang 
970bb2b2624SAnson Huang 	return 0;
971bb2b2624SAnson Huang }
972bb2b2624SAnson Huang 
97303b70130SNathan Chancellor static int __maybe_unused imx_mu_runtime_resume(struct device *dev)
974bb2b2624SAnson Huang {
975bb2b2624SAnson Huang 	struct imx_mu_priv *priv = dev_get_drvdata(dev);
976bb2b2624SAnson Huang 	int ret;
977bb2b2624SAnson Huang 
978bb2b2624SAnson Huang 	ret = clk_prepare_enable(priv->clk);
979bb2b2624SAnson Huang 	if (ret)
980bb2b2624SAnson Huang 		dev_err(dev, "failed to enable clock\n");
981bb2b2624SAnson Huang 
982bb2b2624SAnson Huang 	return ret;
983bb2b2624SAnson Huang }
984bb2b2624SAnson Huang 
985ba5f9fa0SDong Aisheng static const struct dev_pm_ops imx_mu_pm_ops = {
986ba5f9fa0SDong Aisheng 	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(imx_mu_suspend_noirq,
987ba5f9fa0SDong Aisheng 				      imx_mu_resume_noirq)
988bb2b2624SAnson Huang 	SET_RUNTIME_PM_OPS(imx_mu_runtime_suspend,
989bb2b2624SAnson Huang 			   imx_mu_runtime_resume, NULL)
990ba5f9fa0SDong Aisheng };
991ba5f9fa0SDong Aisheng 
9922bb70056SOleksij Rempel static struct platform_driver imx_mu_driver = {
9932bb70056SOleksij Rempel 	.probe		= imx_mu_probe,
9942bb70056SOleksij Rempel 	.remove		= imx_mu_remove,
9952bb70056SOleksij Rempel 	.driver = {
9962bb70056SOleksij Rempel 		.name	= "imx_mu",
9972bb70056SOleksij Rempel 		.of_match_table = imx_mu_dt_ids,
998ba5f9fa0SDong Aisheng 		.pm = &imx_mu_pm_ops,
9992bb70056SOleksij Rempel 	},
10002bb70056SOleksij Rempel };
10012bb70056SOleksij Rempel module_platform_driver(imx_mu_driver);
10022bb70056SOleksij Rempel 
10032bb70056SOleksij Rempel MODULE_AUTHOR("Oleksij Rempel <o.rempel@pengutronix.de>");
10042bb70056SOleksij Rempel MODULE_DESCRIPTION("Message Unit driver for i.MX");
10052bb70056SOleksij Rempel MODULE_LICENSE("GPL v2");
1006