xref: /openbmc/linux/drivers/mailbox/imx-mailbox.c (revision e80a7e7e)
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>
82bb70056SOleksij Rempel #include <linux/interrupt.h>
92bb70056SOleksij Rempel #include <linux/io.h>
100a67003bSPeng Fan #include <linux/iopoll.h>
112bb70056SOleksij Rempel #include <linux/kernel.h>
122bb70056SOleksij Rempel #include <linux/mailbox_controller.h>
132bb70056SOleksij Rempel #include <linux/module.h>
142bb70056SOleksij Rempel #include <linux/of_device.h>
15676f23eaSAnson Huang #include <linux/pm_runtime.h>
162bb70056SOleksij Rempel #include <linux/slab.h>
172bb70056SOleksij Rempel 
182bb70056SOleksij Rempel #define IMX_MU_CHANS		16
190a67003bSPeng Fan /* TX0/RX0/RXDB[0-3] */
200a67003bSPeng Fan #define IMX_MU_SCU_CHANS	6
212bb70056SOleksij Rempel #define IMX_MU_CHAN_NAME_SIZE	20
222bb70056SOleksij Rempel 
232bb70056SOleksij Rempel enum imx_mu_chan_type {
242bb70056SOleksij Rempel 	IMX_MU_TYPE_TX,		/* Tx */
252bb70056SOleksij Rempel 	IMX_MU_TYPE_RX,		/* Rx */
262bb70056SOleksij Rempel 	IMX_MU_TYPE_TXDB,	/* Tx doorbell */
272bb70056SOleksij Rempel 	IMX_MU_TYPE_RXDB,	/* Rx doorbell */
282bb70056SOleksij Rempel };
292bb70056SOleksij Rempel 
30f689a7cfSPeng Fan enum imx_mu_xcr {
314f0b776eSPeng Fan 	IMX_MU_GIER,
32f689a7cfSPeng Fan 	IMX_MU_GCR,
33f689a7cfSPeng Fan 	IMX_MU_TCR,
34f689a7cfSPeng Fan 	IMX_MU_RCR,
35f689a7cfSPeng Fan 	IMX_MU_xCR_MAX,
36f689a7cfSPeng Fan };
37f689a7cfSPeng Fan 
38f689a7cfSPeng Fan enum imx_mu_xsr {
39f689a7cfSPeng Fan 	IMX_MU_SR,
40f689a7cfSPeng Fan 	IMX_MU_GSR,
41f689a7cfSPeng Fan 	IMX_MU_TSR,
42f689a7cfSPeng Fan 	IMX_MU_RSR,
43f689a7cfSPeng Fan };
44f689a7cfSPeng Fan 
450a67003bSPeng Fan struct imx_sc_rpc_msg_max {
460a67003bSPeng Fan 	struct imx_sc_rpc_msg hdr;
470a67003bSPeng Fan 	u32 data[7];
480a67003bSPeng Fan };
490a67003bSPeng Fan 
502bb70056SOleksij Rempel struct imx_mu_con_priv {
512bb70056SOleksij Rempel 	unsigned int		idx;
522bb70056SOleksij Rempel 	char			irq_desc[IMX_MU_CHAN_NAME_SIZE];
532bb70056SOleksij Rempel 	enum imx_mu_chan_type	type;
542bb70056SOleksij Rempel 	struct mbox_chan	*chan;
552bb70056SOleksij Rempel 	struct tasklet_struct	txdb_tasklet;
562bb70056SOleksij Rempel };
572bb70056SOleksij Rempel 
582bb70056SOleksij Rempel struct imx_mu_priv {
592bb70056SOleksij Rempel 	struct device		*dev;
602bb70056SOleksij Rempel 	void __iomem		*base;
612bb70056SOleksij Rempel 	spinlock_t		xcr_lock; /* control register lock */
622bb70056SOleksij Rempel 
632bb70056SOleksij Rempel 	struct mbox_controller	mbox;
642bb70056SOleksij Rempel 	struct mbox_chan	mbox_chans[IMX_MU_CHANS];
652bb70056SOleksij Rempel 
662bb70056SOleksij Rempel 	struct imx_mu_con_priv  con_priv[IMX_MU_CHANS];
67c6c6bc6eSRichard Zhu 	const struct imx_mu_dcfg	*dcfg;
682bb70056SOleksij Rempel 	struct clk		*clk;
692bb70056SOleksij Rempel 	int			irq;
702bb70056SOleksij Rempel 
71f689a7cfSPeng Fan 	u32 xcr[4];
72ba5f9fa0SDong Aisheng 
732bb70056SOleksij Rempel 	bool			side_b;
742bb70056SOleksij Rempel };
752bb70056SOleksij Rempel 
764f0b776eSPeng Fan enum imx_mu_type {
774f0b776eSPeng Fan 	IMX_MU_V1,
784f0b776eSPeng Fan 	IMX_MU_V2,
794f0b776eSPeng Fan };
804f0b776eSPeng Fan 
8163b38357SPeng Fan struct imx_mu_dcfg {
8263b38357SPeng Fan 	int (*tx)(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp, void *data);
8363b38357SPeng Fan 	int (*rx)(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp);
8463b38357SPeng Fan 	void (*init)(struct imx_mu_priv *priv);
854f0b776eSPeng Fan 	enum imx_mu_type type;
8632f7443dSPeng Fan 	u32	xTR;		/* Transmit Register0 */
8732f7443dSPeng Fan 	u32	xRR;		/* Receive Register0 */
88f689a7cfSPeng Fan 	u32	xSR[4];		/* Status Registers */
89f689a7cfSPeng Fan 	u32	xCR[4];		/* Control Registers */
90c6c6bc6eSRichard Zhu };
91c6c6bc6eSRichard Zhu 
924f0b776eSPeng Fan #define IMX_MU_xSR_GIPn(type, x) (type == IMX_MU_V2 ? BIT(x) : BIT(28 + (3 - (x))))
934f0b776eSPeng Fan #define IMX_MU_xSR_RFn(type, x) (type == IMX_MU_V2 ? BIT(x) : BIT(24 + (3 - (x))))
944f0b776eSPeng Fan #define IMX_MU_xSR_TEn(type, x) (type == IMX_MU_V2 ? BIT(x) : BIT(20 + (3 - (x))))
954f0b776eSPeng Fan 
964f0b776eSPeng Fan /* General Purpose Interrupt Enable */
974f0b776eSPeng Fan #define IMX_MU_xCR_GIEn(type, x) (type == IMX_MU_V2 ? BIT(x) : BIT(28 + (3 - (x))))
984f0b776eSPeng Fan /* Receive Interrupt Enable */
994f0b776eSPeng Fan #define IMX_MU_xCR_RIEn(type, x) (type == IMX_MU_V2 ? BIT(x) : BIT(24 + (3 - (x))))
1004f0b776eSPeng Fan /* Transmit Interrupt Enable */
1014f0b776eSPeng Fan #define IMX_MU_xCR_TIEn(type, x) (type == IMX_MU_V2 ? BIT(x) : BIT(20 + (3 - (x))))
1024f0b776eSPeng Fan /* General Purpose Interrupt Request */
1034f0b776eSPeng Fan #define IMX_MU_xCR_GIRn(type, x) (type == IMX_MU_V2 ? BIT(x) : BIT(16 + (3 - (x))))
1044f0b776eSPeng Fan 
1054f0b776eSPeng Fan 
1062bb70056SOleksij Rempel static struct imx_mu_priv *to_imx_mu_priv(struct mbox_controller *mbox)
1072bb70056SOleksij Rempel {
1082bb70056SOleksij Rempel 	return container_of(mbox, struct imx_mu_priv, mbox);
1092bb70056SOleksij Rempel }
1102bb70056SOleksij Rempel 
1112bb70056SOleksij Rempel static void imx_mu_write(struct imx_mu_priv *priv, u32 val, u32 offs)
1122bb70056SOleksij Rempel {
1132bb70056SOleksij Rempel 	iowrite32(val, priv->base + offs);
1142bb70056SOleksij Rempel }
1152bb70056SOleksij Rempel 
1162bb70056SOleksij Rempel static u32 imx_mu_read(struct imx_mu_priv *priv, u32 offs)
1172bb70056SOleksij Rempel {
1182bb70056SOleksij Rempel 	return ioread32(priv->base + offs);
1192bb70056SOleksij Rempel }
1202bb70056SOleksij Rempel 
121f689a7cfSPeng Fan static u32 imx_mu_xcr_rmw(struct imx_mu_priv *priv, enum imx_mu_xcr type, u32 set, u32 clr)
1222bb70056SOleksij Rempel {
1232bb70056SOleksij Rempel 	unsigned long flags;
1242bb70056SOleksij Rempel 	u32 val;
1252bb70056SOleksij Rempel 
1262bb70056SOleksij Rempel 	spin_lock_irqsave(&priv->xcr_lock, flags);
127f689a7cfSPeng Fan 	val = imx_mu_read(priv, priv->dcfg->xCR[type]);
1282bb70056SOleksij Rempel 	val &= ~clr;
1292bb70056SOleksij Rempel 	val |= set;
130f689a7cfSPeng Fan 	imx_mu_write(priv, val, priv->dcfg->xCR[type]);
1312bb70056SOleksij Rempel 	spin_unlock_irqrestore(&priv->xcr_lock, flags);
1322bb70056SOleksij Rempel 
1332bb70056SOleksij Rempel 	return val;
1342bb70056SOleksij Rempel }
1352bb70056SOleksij Rempel 
13663b38357SPeng Fan static int imx_mu_generic_tx(struct imx_mu_priv *priv,
13763b38357SPeng Fan 			     struct imx_mu_con_priv *cp,
13863b38357SPeng Fan 			     void *data)
13963b38357SPeng Fan {
14063b38357SPeng Fan 	u32 *arg = data;
14163b38357SPeng Fan 
14263b38357SPeng Fan 	switch (cp->type) {
14363b38357SPeng Fan 	case IMX_MU_TYPE_TX:
14432f7443dSPeng Fan 		imx_mu_write(priv, *arg, priv->dcfg->xTR + cp->idx * 4);
1454f0b776eSPeng Fan 		imx_mu_xcr_rmw(priv, IMX_MU_TCR, IMX_MU_xCR_TIEn(priv->dcfg->type, cp->idx), 0);
14663b38357SPeng Fan 		break;
14763b38357SPeng Fan 	case IMX_MU_TYPE_TXDB:
1484f0b776eSPeng Fan 		imx_mu_xcr_rmw(priv, IMX_MU_GCR, IMX_MU_xCR_GIRn(priv->dcfg->type, cp->idx), 0);
14963b38357SPeng Fan 		tasklet_schedule(&cp->txdb_tasklet);
15063b38357SPeng Fan 		break;
15163b38357SPeng Fan 	default:
15263b38357SPeng Fan 		dev_warn_ratelimited(priv->dev, "Send data on wrong channel type: %d\n", cp->type);
15363b38357SPeng Fan 		return -EINVAL;
15463b38357SPeng Fan 	}
15563b38357SPeng Fan 
15663b38357SPeng Fan 	return 0;
15763b38357SPeng Fan }
15863b38357SPeng Fan 
15963b38357SPeng Fan static int imx_mu_generic_rx(struct imx_mu_priv *priv,
16063b38357SPeng Fan 			     struct imx_mu_con_priv *cp)
16163b38357SPeng Fan {
16263b38357SPeng Fan 	u32 dat;
16363b38357SPeng Fan 
16432f7443dSPeng Fan 	dat = imx_mu_read(priv, priv->dcfg->xRR + (cp->idx) * 4);
16563b38357SPeng Fan 	mbox_chan_received_data(cp->chan, (void *)&dat);
16663b38357SPeng Fan 
16763b38357SPeng Fan 	return 0;
16863b38357SPeng Fan }
16963b38357SPeng Fan 
1700a67003bSPeng Fan static int imx_mu_scu_tx(struct imx_mu_priv *priv,
1710a67003bSPeng Fan 			 struct imx_mu_con_priv *cp,
1720a67003bSPeng Fan 			 void *data)
1730a67003bSPeng Fan {
1740a67003bSPeng Fan 	struct imx_sc_rpc_msg_max *msg = data;
1750a67003bSPeng Fan 	u32 *arg = data;
1760a67003bSPeng Fan 	int i, ret;
1770a67003bSPeng Fan 	u32 xsr;
1780a67003bSPeng Fan 
1790a67003bSPeng Fan 	switch (cp->type) {
1800a67003bSPeng Fan 	case IMX_MU_TYPE_TX:
1819d8ca628SPeng Fan 		/*
1829d8ca628SPeng Fan 		 * msg->hdr.size specifies the number of u32 words while
1839d8ca628SPeng Fan 		 * sizeof yields bytes.
1849d8ca628SPeng Fan 		 */
1859d8ca628SPeng Fan 
1869d8ca628SPeng Fan 		if (msg->hdr.size > sizeof(*msg) / 4) {
1870a67003bSPeng Fan 			/*
1880a67003bSPeng Fan 			 * The real message size can be different to
1890a67003bSPeng Fan 			 * struct imx_sc_rpc_msg_max size
1900a67003bSPeng Fan 			 */
1919d8ca628SPeng Fan 			dev_err(priv->dev, "Maximal message size (%zu bytes) exceeded on TX; got: %i bytes\n", sizeof(*msg), msg->hdr.size << 2);
1920a67003bSPeng Fan 			return -EINVAL;
1930a67003bSPeng Fan 		}
1940a67003bSPeng Fan 
1950a67003bSPeng Fan 		for (i = 0; i < 4 && i < msg->hdr.size; i++)
19632f7443dSPeng Fan 			imx_mu_write(priv, *arg++, priv->dcfg->xTR + (i % 4) * 4);
1970a67003bSPeng Fan 		for (; i < msg->hdr.size; i++) {
198f689a7cfSPeng Fan 			ret = readl_poll_timeout(priv->base + priv->dcfg->xSR[IMX_MU_TSR],
1990a67003bSPeng Fan 						 xsr,
2004f0b776eSPeng Fan 						 xsr & IMX_MU_xSR_TEn(priv->dcfg->type, i % 4),
2010a67003bSPeng Fan 						 0, 100);
2020a67003bSPeng Fan 			if (ret) {
2030a67003bSPeng Fan 				dev_err(priv->dev, "Send data index: %d timeout\n", i);
2040a67003bSPeng Fan 				return ret;
2050a67003bSPeng Fan 			}
20632f7443dSPeng Fan 			imx_mu_write(priv, *arg++, priv->dcfg->xTR + (i % 4) * 4);
2070a67003bSPeng Fan 		}
2080a67003bSPeng Fan 
2094f0b776eSPeng Fan 		imx_mu_xcr_rmw(priv, IMX_MU_TCR, IMX_MU_xCR_TIEn(priv->dcfg->type, cp->idx), 0);
2100a67003bSPeng Fan 		break;
2110a67003bSPeng Fan 	default:
2120a67003bSPeng Fan 		dev_warn_ratelimited(priv->dev, "Send data on wrong channel type: %d\n", cp->type);
2130a67003bSPeng Fan 		return -EINVAL;
2140a67003bSPeng Fan 	}
2150a67003bSPeng Fan 
2160a67003bSPeng Fan 	return 0;
2170a67003bSPeng Fan }
2180a67003bSPeng Fan 
2190a67003bSPeng Fan static int imx_mu_scu_rx(struct imx_mu_priv *priv,
2200a67003bSPeng Fan 			 struct imx_mu_con_priv *cp)
2210a67003bSPeng Fan {
2220a67003bSPeng Fan 	struct imx_sc_rpc_msg_max msg;
2230a67003bSPeng Fan 	u32 *data = (u32 *)&msg;
2240a67003bSPeng Fan 	int i, ret;
2250a67003bSPeng Fan 	u32 xsr;
2260a67003bSPeng Fan 
2274f0b776eSPeng Fan 	imx_mu_xcr_rmw(priv, IMX_MU_RCR, 0, IMX_MU_xCR_RIEn(priv->dcfg->type, 0));
22832f7443dSPeng Fan 	*data++ = imx_mu_read(priv, priv->dcfg->xRR);
2290a67003bSPeng Fan 
2309d8ca628SPeng Fan 	if (msg.hdr.size > sizeof(msg) / 4) {
2319d8ca628SPeng Fan 		dev_err(priv->dev, "Maximal message size (%zu bytes) exceeded on RX; got: %i bytes\n", sizeof(msg), msg.hdr.size << 2);
2320a67003bSPeng Fan 		return -EINVAL;
2330a67003bSPeng Fan 	}
2340a67003bSPeng Fan 
2350a67003bSPeng Fan 	for (i = 1; i < msg.hdr.size; i++) {
236f689a7cfSPeng Fan 		ret = readl_poll_timeout(priv->base + priv->dcfg->xSR[IMX_MU_RSR], xsr,
2374f0b776eSPeng Fan 					 xsr & IMX_MU_xSR_RFn(priv->dcfg->type, i % 4), 0, 100);
2380a67003bSPeng Fan 		if (ret) {
2390a67003bSPeng Fan 			dev_err(priv->dev, "timeout read idx %d\n", i);
2400a67003bSPeng Fan 			return ret;
2410a67003bSPeng Fan 		}
24232f7443dSPeng Fan 		*data++ = imx_mu_read(priv, priv->dcfg->xRR + (i % 4) * 4);
2430a67003bSPeng Fan 	}
2440a67003bSPeng Fan 
2454f0b776eSPeng Fan 	imx_mu_xcr_rmw(priv, IMX_MU_RCR, IMX_MU_xCR_RIEn(priv->dcfg->type, 0), 0);
2460a67003bSPeng Fan 	mbox_chan_received_data(cp->chan, (void *)&msg);
2470a67003bSPeng Fan 
2480a67003bSPeng Fan 	return 0;
2490a67003bSPeng Fan }
2500a67003bSPeng Fan 
2512bb70056SOleksij Rempel static void imx_mu_txdb_tasklet(unsigned long data)
2522bb70056SOleksij Rempel {
2532bb70056SOleksij Rempel 	struct imx_mu_con_priv *cp = (struct imx_mu_con_priv *)data;
2542bb70056SOleksij Rempel 
2552bb70056SOleksij Rempel 	mbox_chan_txdone(cp->chan, 0);
2562bb70056SOleksij Rempel }
2572bb70056SOleksij Rempel 
2582bb70056SOleksij Rempel static irqreturn_t imx_mu_isr(int irq, void *p)
2592bb70056SOleksij Rempel {
2602bb70056SOleksij Rempel 	struct mbox_chan *chan = p;
2612bb70056SOleksij Rempel 	struct imx_mu_priv *priv = to_imx_mu_priv(chan->mbox);
2622bb70056SOleksij Rempel 	struct imx_mu_con_priv *cp = chan->con_priv;
26363b38357SPeng Fan 	u32 val, ctrl;
2642bb70056SOleksij Rempel 
2652bb70056SOleksij Rempel 	switch (cp->type) {
2662bb70056SOleksij Rempel 	case IMX_MU_TYPE_TX:
267f689a7cfSPeng Fan 		ctrl = imx_mu_read(priv, priv->dcfg->xCR[IMX_MU_TCR]);
268f689a7cfSPeng Fan 		val = imx_mu_read(priv, priv->dcfg->xSR[IMX_MU_TSR]);
2694f0b776eSPeng Fan 		val &= IMX_MU_xSR_TEn(priv->dcfg->type, cp->idx) &
2704f0b776eSPeng Fan 			(ctrl & IMX_MU_xCR_TIEn(priv->dcfg->type, cp->idx));
2712bb70056SOleksij Rempel 		break;
2722bb70056SOleksij Rempel 	case IMX_MU_TYPE_RX:
273f689a7cfSPeng Fan 		ctrl = imx_mu_read(priv, priv->dcfg->xCR[IMX_MU_RCR]);
274f689a7cfSPeng Fan 		val = imx_mu_read(priv, priv->dcfg->xSR[IMX_MU_RSR]);
2754f0b776eSPeng Fan 		val &= IMX_MU_xSR_RFn(priv->dcfg->type, cp->idx) &
2764f0b776eSPeng Fan 			(ctrl & IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx));
2772bb70056SOleksij Rempel 		break;
2782bb70056SOleksij Rempel 	case IMX_MU_TYPE_RXDB:
2794f0b776eSPeng Fan 		ctrl = imx_mu_read(priv, priv->dcfg->xCR[IMX_MU_GIER]);
280f689a7cfSPeng Fan 		val = imx_mu_read(priv, priv->dcfg->xSR[IMX_MU_GSR]);
2814f0b776eSPeng Fan 		val &= IMX_MU_xSR_GIPn(priv->dcfg->type, cp->idx) &
2824f0b776eSPeng Fan 			(ctrl & IMX_MU_xCR_GIEn(priv->dcfg->type, cp->idx));
2832bb70056SOleksij Rempel 		break;
2842bb70056SOleksij Rempel 	default:
285*e80a7e7eSNathan Chancellor 		dev_warn_ratelimited(priv->dev, "Unhandled channel type %d\n",
286*e80a7e7eSNathan Chancellor 				     cp->type);
287*e80a7e7eSNathan Chancellor 		return IRQ_NONE;
2882bb70056SOleksij Rempel 	}
2892bb70056SOleksij Rempel 
2902bb70056SOleksij Rempel 	if (!val)
2912bb70056SOleksij Rempel 		return IRQ_NONE;
2922bb70056SOleksij Rempel 
2934f0b776eSPeng Fan 	if ((val == IMX_MU_xSR_TEn(priv->dcfg->type, cp->idx)) &&
2944f0b776eSPeng Fan 	    (cp->type == IMX_MU_TYPE_TX)) {
2954f0b776eSPeng Fan 		imx_mu_xcr_rmw(priv, IMX_MU_TCR, 0, IMX_MU_xCR_TIEn(priv->dcfg->type, cp->idx));
2962bb70056SOleksij Rempel 		mbox_chan_txdone(chan, 0);
2974f0b776eSPeng Fan 	} else if ((val == IMX_MU_xSR_RFn(priv->dcfg->type, cp->idx)) &&
2984f0b776eSPeng Fan 		   (cp->type == IMX_MU_TYPE_RX)) {
29963b38357SPeng Fan 		priv->dcfg->rx(priv, cp);
3004f0b776eSPeng Fan 	} else if ((val == IMX_MU_xSR_GIPn(priv->dcfg->type, cp->idx)) &&
3014f0b776eSPeng Fan 		   (cp->type == IMX_MU_TYPE_RXDB)) {
3024f0b776eSPeng Fan 		imx_mu_write(priv, IMX_MU_xSR_GIPn(priv->dcfg->type, cp->idx),
3034f0b776eSPeng Fan 			     priv->dcfg->xSR[IMX_MU_GSR]);
3042bb70056SOleksij Rempel 		mbox_chan_received_data(chan, NULL);
3052bb70056SOleksij Rempel 	} else {
3062bb70056SOleksij Rempel 		dev_warn_ratelimited(priv->dev, "Not handled interrupt\n");
3072bb70056SOleksij Rempel 		return IRQ_NONE;
3082bb70056SOleksij Rempel 	}
3092bb70056SOleksij Rempel 
3102bb70056SOleksij Rempel 	return IRQ_HANDLED;
3112bb70056SOleksij Rempel }
3122bb70056SOleksij Rempel 
3132bb70056SOleksij Rempel static int imx_mu_send_data(struct mbox_chan *chan, void *data)
3142bb70056SOleksij Rempel {
3152bb70056SOleksij Rempel 	struct imx_mu_priv *priv = to_imx_mu_priv(chan->mbox);
3162bb70056SOleksij Rempel 	struct imx_mu_con_priv *cp = chan->con_priv;
3172bb70056SOleksij Rempel 
31863b38357SPeng Fan 	return priv->dcfg->tx(priv, cp, data);
3192bb70056SOleksij Rempel }
3202bb70056SOleksij Rempel 
3212bb70056SOleksij Rempel static int imx_mu_startup(struct mbox_chan *chan)
3222bb70056SOleksij Rempel {
3232bb70056SOleksij Rempel 	struct imx_mu_priv *priv = to_imx_mu_priv(chan->mbox);
3242bb70056SOleksij Rempel 	struct imx_mu_con_priv *cp = chan->con_priv;
325b7b2796bSAnson Huang 	unsigned long irq_flag = IRQF_SHARED;
3262bb70056SOleksij Rempel 	int ret;
3272bb70056SOleksij Rempel 
328676f23eaSAnson Huang 	pm_runtime_get_sync(priv->dev);
3292bb70056SOleksij Rempel 	if (cp->type == IMX_MU_TYPE_TXDB) {
3302bb70056SOleksij Rempel 		/* Tx doorbell don't have ACK support */
3312bb70056SOleksij Rempel 		tasklet_init(&cp->txdb_tasklet, imx_mu_txdb_tasklet,
3322bb70056SOleksij Rempel 			     (unsigned long)cp);
3332bb70056SOleksij Rempel 		return 0;
3342bb70056SOleksij Rempel 	}
3352bb70056SOleksij Rempel 
336b7b2796bSAnson Huang 	/* IPC MU should be with IRQF_NO_SUSPEND set */
337b7b2796bSAnson Huang 	if (!priv->dev->pm_domain)
338b7b2796bSAnson Huang 		irq_flag |= IRQF_NO_SUSPEND;
339b7b2796bSAnson Huang 
340b7b2796bSAnson Huang 	ret = request_irq(priv->irq, imx_mu_isr, irq_flag,
341b7b2796bSAnson Huang 			  cp->irq_desc, chan);
3422bb70056SOleksij Rempel 	if (ret) {
3432bb70056SOleksij Rempel 		dev_err(priv->dev,
3442bb70056SOleksij Rempel 			"Unable to acquire IRQ %d\n", priv->irq);
3452bb70056SOleksij Rempel 		return ret;
3462bb70056SOleksij Rempel 	}
3472bb70056SOleksij Rempel 
3482bb70056SOleksij Rempel 	switch (cp->type) {
3492bb70056SOleksij Rempel 	case IMX_MU_TYPE_RX:
3504f0b776eSPeng Fan 		imx_mu_xcr_rmw(priv, IMX_MU_RCR, IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx), 0);
3512bb70056SOleksij Rempel 		break;
3522bb70056SOleksij Rempel 	case IMX_MU_TYPE_RXDB:
3534f0b776eSPeng Fan 		imx_mu_xcr_rmw(priv, IMX_MU_GIER, IMX_MU_xCR_GIEn(priv->dcfg->type, cp->idx), 0);
3542bb70056SOleksij Rempel 		break;
3552bb70056SOleksij Rempel 	default:
3562bb70056SOleksij Rempel 		break;
3572bb70056SOleksij Rempel 	}
3582bb70056SOleksij Rempel 
3592bb70056SOleksij Rempel 	return 0;
3602bb70056SOleksij Rempel }
3612bb70056SOleksij Rempel 
3622bb70056SOleksij Rempel static void imx_mu_shutdown(struct mbox_chan *chan)
3632bb70056SOleksij Rempel {
3642bb70056SOleksij Rempel 	struct imx_mu_priv *priv = to_imx_mu_priv(chan->mbox);
3652bb70056SOleksij Rempel 	struct imx_mu_con_priv *cp = chan->con_priv;
3662bb70056SOleksij Rempel 
367bf159d15SDaniel Baluta 	if (cp->type == IMX_MU_TYPE_TXDB) {
3682bb70056SOleksij Rempel 		tasklet_kill(&cp->txdb_tasklet);
369676f23eaSAnson Huang 		pm_runtime_put_sync(priv->dev);
370bf159d15SDaniel Baluta 		return;
371bf159d15SDaniel Baluta 	}
3722bb70056SOleksij Rempel 
3735f0af07eSDaniel Baluta 	switch (cp->type) {
3745f0af07eSDaniel Baluta 	case IMX_MU_TYPE_TX:
3754f0b776eSPeng Fan 		imx_mu_xcr_rmw(priv, IMX_MU_TCR, 0, IMX_MU_xCR_TIEn(priv->dcfg->type, cp->idx));
3765f0af07eSDaniel Baluta 		break;
3775f0af07eSDaniel Baluta 	case IMX_MU_TYPE_RX:
3784f0b776eSPeng Fan 		imx_mu_xcr_rmw(priv, IMX_MU_RCR, 0, IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx));
3795f0af07eSDaniel Baluta 		break;
3805f0af07eSDaniel Baluta 	case IMX_MU_TYPE_RXDB:
3814f0b776eSPeng Fan 		imx_mu_xcr_rmw(priv, IMX_MU_GIER, 0, IMX_MU_xCR_GIEn(priv->dcfg->type, cp->idx));
3825f0af07eSDaniel Baluta 		break;
3835f0af07eSDaniel Baluta 	default:
3845f0af07eSDaniel Baluta 		break;
3855f0af07eSDaniel Baluta 	}
3862bb70056SOleksij Rempel 
3872bb70056SOleksij Rempel 	free_irq(priv->irq, chan);
388676f23eaSAnson Huang 	pm_runtime_put_sync(priv->dev);
3892bb70056SOleksij Rempel }
3902bb70056SOleksij Rempel 
3912bb70056SOleksij Rempel static const struct mbox_chan_ops imx_mu_ops = {
3922bb70056SOleksij Rempel 	.send_data = imx_mu_send_data,
3932bb70056SOleksij Rempel 	.startup = imx_mu_startup,
3942bb70056SOleksij Rempel 	.shutdown = imx_mu_shutdown,
3952bb70056SOleksij Rempel };
3962bb70056SOleksij Rempel 
3970a67003bSPeng Fan static struct mbox_chan *imx_mu_scu_xlate(struct mbox_controller *mbox,
3980a67003bSPeng Fan 					  const struct of_phandle_args *sp)
3990a67003bSPeng Fan {
4000a67003bSPeng Fan 	u32 type, idx, chan;
4010a67003bSPeng Fan 
4020a67003bSPeng Fan 	if (sp->args_count != 2) {
4030a67003bSPeng Fan 		dev_err(mbox->dev, "Invalid argument count %d\n", sp->args_count);
4040a67003bSPeng Fan 		return ERR_PTR(-EINVAL);
4050a67003bSPeng Fan 	}
4060a67003bSPeng Fan 
4070a67003bSPeng Fan 	type = sp->args[0]; /* channel type */
4080a67003bSPeng Fan 	idx = sp->args[1]; /* index */
4090a67003bSPeng Fan 
4100a67003bSPeng Fan 	switch (type) {
4110a67003bSPeng Fan 	case IMX_MU_TYPE_TX:
4120a67003bSPeng Fan 	case IMX_MU_TYPE_RX:
4130a67003bSPeng Fan 		if (idx != 0)
4140a67003bSPeng Fan 			dev_err(mbox->dev, "Invalid chan idx: %d\n", idx);
4150a67003bSPeng Fan 		chan = type;
4160a67003bSPeng Fan 		break;
4170a67003bSPeng Fan 	case IMX_MU_TYPE_RXDB:
4180a67003bSPeng Fan 		chan = 2 + idx;
4190a67003bSPeng Fan 		break;
4200a67003bSPeng Fan 	default:
4210a67003bSPeng Fan 		dev_err(mbox->dev, "Invalid chan type: %d\n", type);
4221b3a347bSDan Carpenter 		return ERR_PTR(-EINVAL);
4230a67003bSPeng Fan 	}
4240a67003bSPeng Fan 
4250a67003bSPeng Fan 	if (chan >= mbox->num_chans) {
4260a67003bSPeng Fan 		dev_err(mbox->dev, "Not supported channel number: %d. (type: %d, idx: %d)\n", chan, type, idx);
4270a67003bSPeng Fan 		return ERR_PTR(-EINVAL);
4280a67003bSPeng Fan 	}
4290a67003bSPeng Fan 
4300a67003bSPeng Fan 	return &mbox->chans[chan];
4310a67003bSPeng Fan }
4320a67003bSPeng Fan 
4332bb70056SOleksij Rempel static struct mbox_chan * imx_mu_xlate(struct mbox_controller *mbox,
4342bb70056SOleksij Rempel 				       const struct of_phandle_args *sp)
4352bb70056SOleksij Rempel {
4362bb70056SOleksij Rempel 	u32 type, idx, chan;
4372bb70056SOleksij Rempel 
4382bb70056SOleksij Rempel 	if (sp->args_count != 2) {
4392bb70056SOleksij Rempel 		dev_err(mbox->dev, "Invalid argument count %d\n", sp->args_count);
4402bb70056SOleksij Rempel 		return ERR_PTR(-EINVAL);
4412bb70056SOleksij Rempel 	}
4422bb70056SOleksij Rempel 
4432bb70056SOleksij Rempel 	type = sp->args[0]; /* channel type */
4442bb70056SOleksij Rempel 	idx = sp->args[1]; /* index */
4452bb70056SOleksij Rempel 	chan = type * 4 + idx;
4462bb70056SOleksij Rempel 
4472bb70056SOleksij Rempel 	if (chan >= mbox->num_chans) {
4482bb70056SOleksij Rempel 		dev_err(mbox->dev, "Not supported channel number: %d. (type: %d, idx: %d)\n", chan, type, idx);
4492bb70056SOleksij Rempel 		return ERR_PTR(-EINVAL);
4502bb70056SOleksij Rempel 	}
4512bb70056SOleksij Rempel 
4522bb70056SOleksij Rempel 	return &mbox->chans[chan];
4532bb70056SOleksij Rempel }
4542bb70056SOleksij Rempel 
4552bb70056SOleksij Rempel static void imx_mu_init_generic(struct imx_mu_priv *priv)
4562bb70056SOleksij Rempel {
45763b38357SPeng Fan 	unsigned int i;
45863b38357SPeng Fan 
45963b38357SPeng Fan 	for (i = 0; i < IMX_MU_CHANS; i++) {
46063b38357SPeng Fan 		struct imx_mu_con_priv *cp = &priv->con_priv[i];
46163b38357SPeng Fan 
46263b38357SPeng Fan 		cp->idx = i % 4;
46363b38357SPeng Fan 		cp->type = i >> 2;
46463b38357SPeng Fan 		cp->chan = &priv->mbox_chans[i];
46563b38357SPeng Fan 		priv->mbox_chans[i].con_priv = cp;
46663b38357SPeng Fan 		snprintf(cp->irq_desc, sizeof(cp->irq_desc),
46763b38357SPeng Fan 			 "imx_mu_chan[%i-%i]", cp->type, cp->idx);
46863b38357SPeng Fan 	}
46963b38357SPeng Fan 
47063b38357SPeng Fan 	priv->mbox.num_chans = IMX_MU_CHANS;
47163b38357SPeng Fan 	priv->mbox.of_xlate = imx_mu_xlate;
47263b38357SPeng Fan 
4732bb70056SOleksij Rempel 	if (priv->side_b)
4742bb70056SOleksij Rempel 		return;
4752bb70056SOleksij Rempel 
4762bb70056SOleksij Rempel 	/* Set default MU configuration */
477f689a7cfSPeng Fan 	for (i = 0; i < IMX_MU_xCR_MAX; i++)
478f689a7cfSPeng Fan 		imx_mu_write(priv, 0, priv->dcfg->xCR[i]);
4792bb70056SOleksij Rempel }
4802bb70056SOleksij Rempel 
4810a67003bSPeng Fan static void imx_mu_init_scu(struct imx_mu_priv *priv)
4820a67003bSPeng Fan {
4830a67003bSPeng Fan 	unsigned int i;
4840a67003bSPeng Fan 
4850a67003bSPeng Fan 	for (i = 0; i < IMX_MU_SCU_CHANS; i++) {
4860a67003bSPeng Fan 		struct imx_mu_con_priv *cp = &priv->con_priv[i];
4870a67003bSPeng Fan 
4880a67003bSPeng Fan 		cp->idx = i < 2 ? 0 : i - 2;
4890a67003bSPeng Fan 		cp->type = i < 2 ? i : IMX_MU_TYPE_RXDB;
4900a67003bSPeng Fan 		cp->chan = &priv->mbox_chans[i];
4910a67003bSPeng Fan 		priv->mbox_chans[i].con_priv = cp;
4920a67003bSPeng Fan 		snprintf(cp->irq_desc, sizeof(cp->irq_desc),
4930a67003bSPeng Fan 			 "imx_mu_chan[%i-%i]", cp->type, cp->idx);
4940a67003bSPeng Fan 	}
4950a67003bSPeng Fan 
4960a67003bSPeng Fan 	priv->mbox.num_chans = IMX_MU_SCU_CHANS;
4970a67003bSPeng Fan 	priv->mbox.of_xlate = imx_mu_scu_xlate;
4980a67003bSPeng Fan 
4990a67003bSPeng Fan 	/* Set default MU configuration */
500f689a7cfSPeng Fan 	for (i = 0; i < IMX_MU_xCR_MAX; i++)
501f689a7cfSPeng Fan 		imx_mu_write(priv, 0, priv->dcfg->xCR[i]);
5020a67003bSPeng Fan }
5030a67003bSPeng Fan 
5042bb70056SOleksij Rempel static int imx_mu_probe(struct platform_device *pdev)
5052bb70056SOleksij Rempel {
5062bb70056SOleksij Rempel 	struct device *dev = &pdev->dev;
5072bb70056SOleksij Rempel 	struct device_node *np = dev->of_node;
5082bb70056SOleksij Rempel 	struct imx_mu_priv *priv;
509c6c6bc6eSRichard Zhu 	const struct imx_mu_dcfg *dcfg;
5102bb70056SOleksij Rempel 	int ret;
5112bb70056SOleksij Rempel 
5122bb70056SOleksij Rempel 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
5132bb70056SOleksij Rempel 	if (!priv)
5142bb70056SOleksij Rempel 		return -ENOMEM;
5152bb70056SOleksij Rempel 
5162bb70056SOleksij Rempel 	priv->dev = dev;
5172bb70056SOleksij Rempel 
5180c40e631SAnson Huang 	priv->base = devm_platform_ioremap_resource(pdev, 0);
5192bb70056SOleksij Rempel 	if (IS_ERR(priv->base))
5202bb70056SOleksij Rempel 		return PTR_ERR(priv->base);
5212bb70056SOleksij Rempel 
5222bb70056SOleksij Rempel 	priv->irq = platform_get_irq(pdev, 0);
5232bb70056SOleksij Rempel 	if (priv->irq < 0)
5242bb70056SOleksij Rempel 		return priv->irq;
5252bb70056SOleksij Rempel 
526c6c6bc6eSRichard Zhu 	dcfg = of_device_get_match_data(dev);
527c6c6bc6eSRichard Zhu 	if (!dcfg)
528c6c6bc6eSRichard Zhu 		return -EINVAL;
529c6c6bc6eSRichard Zhu 	priv->dcfg = dcfg;
530c6c6bc6eSRichard Zhu 
5312bb70056SOleksij Rempel 	priv->clk = devm_clk_get(dev, NULL);
5322bb70056SOleksij Rempel 	if (IS_ERR(priv->clk)) {
5332bb70056SOleksij Rempel 		if (PTR_ERR(priv->clk) != -ENOENT)
5342bb70056SOleksij Rempel 			return PTR_ERR(priv->clk);
5352bb70056SOleksij Rempel 
5362bb70056SOleksij Rempel 		priv->clk = NULL;
5372bb70056SOleksij Rempel 	}
5382bb70056SOleksij Rempel 
5392bb70056SOleksij Rempel 	ret = clk_prepare_enable(priv->clk);
5402bb70056SOleksij Rempel 	if (ret) {
5412bb70056SOleksij Rempel 		dev_err(dev, "Failed to enable clock\n");
5422bb70056SOleksij Rempel 		return ret;
5432bb70056SOleksij Rempel 	}
5442bb70056SOleksij Rempel 
5452bb70056SOleksij Rempel 	priv->side_b = of_property_read_bool(np, "fsl,mu-side-b");
5462bb70056SOleksij Rempel 
54763b38357SPeng Fan 	priv->dcfg->init(priv);
54863b38357SPeng Fan 
5492bb70056SOleksij Rempel 	spin_lock_init(&priv->xcr_lock);
5502bb70056SOleksij Rempel 
5512bb70056SOleksij Rempel 	priv->mbox.dev = dev;
5522bb70056SOleksij Rempel 	priv->mbox.ops = &imx_mu_ops;
5532bb70056SOleksij Rempel 	priv->mbox.chans = priv->mbox_chans;
5542bb70056SOleksij Rempel 	priv->mbox.txdone_irq = true;
5552bb70056SOleksij Rempel 
5562bb70056SOleksij Rempel 	platform_set_drvdata(pdev, priv);
5572bb70056SOleksij Rempel 
558676f23eaSAnson Huang 	ret = devm_mbox_controller_register(dev, &priv->mbox);
55947303f94SFabio Estevam 	if (ret) {
56047303f94SFabio Estevam 		clk_disable_unprepare(priv->clk);
561676f23eaSAnson Huang 		return ret;
56247303f94SFabio Estevam 	}
563676f23eaSAnson Huang 
564676f23eaSAnson Huang 	pm_runtime_enable(dev);
565676f23eaSAnson Huang 
566676f23eaSAnson Huang 	ret = pm_runtime_get_sync(dev);
567676f23eaSAnson Huang 	if (ret < 0) {
568676f23eaSAnson Huang 		pm_runtime_put_noidle(dev);
569676f23eaSAnson Huang 		goto disable_runtime_pm;
570676f23eaSAnson Huang 	}
571676f23eaSAnson Huang 
572676f23eaSAnson Huang 	ret = pm_runtime_put_sync(dev);
573676f23eaSAnson Huang 	if (ret < 0)
574676f23eaSAnson Huang 		goto disable_runtime_pm;
575676f23eaSAnson Huang 
576bb2b2624SAnson Huang 	clk_disable_unprepare(priv->clk);
577bb2b2624SAnson Huang 
578676f23eaSAnson Huang 	return 0;
579676f23eaSAnson Huang 
580676f23eaSAnson Huang disable_runtime_pm:
581676f23eaSAnson Huang 	pm_runtime_disable(dev);
582bb2b2624SAnson Huang 	clk_disable_unprepare(priv->clk);
583676f23eaSAnson Huang 	return ret;
5842bb70056SOleksij Rempel }
5852bb70056SOleksij Rempel 
5862bb70056SOleksij Rempel static int imx_mu_remove(struct platform_device *pdev)
5872bb70056SOleksij Rempel {
5882bb70056SOleksij Rempel 	struct imx_mu_priv *priv = platform_get_drvdata(pdev);
5892bb70056SOleksij Rempel 
590676f23eaSAnson Huang 	pm_runtime_disable(priv->dev);
5912bb70056SOleksij Rempel 
5922bb70056SOleksij Rempel 	return 0;
5932bb70056SOleksij Rempel }
5942bb70056SOleksij Rempel 
59563b38357SPeng Fan static const struct imx_mu_dcfg imx_mu_cfg_imx6sx = {
59663b38357SPeng Fan 	.tx	= imx_mu_generic_tx,
59763b38357SPeng Fan 	.rx	= imx_mu_generic_rx,
59863b38357SPeng Fan 	.init	= imx_mu_init_generic,
59932f7443dSPeng Fan 	.xTR	= 0x0,
60032f7443dSPeng Fan 	.xRR	= 0x10,
601f689a7cfSPeng Fan 	.xSR	= {0x20, 0x20, 0x20, 0x20},
602f689a7cfSPeng Fan 	.xCR	= {0x24, 0x24, 0x24, 0x24},
60363b38357SPeng Fan };
60463b38357SPeng Fan 
60563b38357SPeng Fan static const struct imx_mu_dcfg imx_mu_cfg_imx7ulp = {
60663b38357SPeng Fan 	.tx	= imx_mu_generic_tx,
60763b38357SPeng Fan 	.rx	= imx_mu_generic_rx,
60863b38357SPeng Fan 	.init	= imx_mu_init_generic,
60932f7443dSPeng Fan 	.xTR	= 0x20,
61032f7443dSPeng Fan 	.xRR	= 0x40,
611f689a7cfSPeng Fan 	.xSR	= {0x60, 0x60, 0x60, 0x60},
612f689a7cfSPeng Fan 	.xCR	= {0x64, 0x64, 0x64, 0x64},
61363b38357SPeng Fan };
61463b38357SPeng Fan 
6154f0b776eSPeng Fan static const struct imx_mu_dcfg imx_mu_cfg_imx8ulp = {
6164f0b776eSPeng Fan 	.tx	= imx_mu_generic_tx,
6174f0b776eSPeng Fan 	.rx	= imx_mu_generic_rx,
6184f0b776eSPeng Fan 	.init	= imx_mu_init_generic,
6194f0b776eSPeng Fan 	.type	= IMX_MU_V2,
6204f0b776eSPeng Fan 	.xTR	= 0x200,
6214f0b776eSPeng Fan 	.xRR	= 0x280,
6224f0b776eSPeng Fan 	.xSR	= {0xC, 0x118, 0x124, 0x12C},
6234f0b776eSPeng Fan 	.xCR	= {0x110, 0x114, 0x120, 0x128},
6244f0b776eSPeng Fan };
6254f0b776eSPeng Fan 
6260a67003bSPeng Fan static const struct imx_mu_dcfg imx_mu_cfg_imx8_scu = {
6270a67003bSPeng Fan 	.tx	= imx_mu_scu_tx,
6280a67003bSPeng Fan 	.rx	= imx_mu_scu_rx,
6290a67003bSPeng Fan 	.init	= imx_mu_init_scu,
6304f0b776eSPeng Fan 	.xTR	= 0x0,
6314f0b776eSPeng Fan 	.xRR	= 0x10,
632f689a7cfSPeng Fan 	.xSR	= {0x20, 0x20, 0x20, 0x20},
633f689a7cfSPeng Fan 	.xCR	= {0x24, 0x24, 0x24, 0x24},
6340a67003bSPeng Fan };
6350a67003bSPeng Fan 
6362bb70056SOleksij Rempel static const struct of_device_id imx_mu_dt_ids[] = {
637c6c6bc6eSRichard Zhu 	{ .compatible = "fsl,imx7ulp-mu", .data = &imx_mu_cfg_imx7ulp },
638c6c6bc6eSRichard Zhu 	{ .compatible = "fsl,imx6sx-mu", .data = &imx_mu_cfg_imx6sx },
6394f0b776eSPeng Fan 	{ .compatible = "fsl,imx8ulp-mu", .data = &imx_mu_cfg_imx8ulp },
6400a67003bSPeng Fan 	{ .compatible = "fsl,imx8-mu-scu", .data = &imx_mu_cfg_imx8_scu },
6412bb70056SOleksij Rempel 	{ },
6422bb70056SOleksij Rempel };
6432bb70056SOleksij Rempel MODULE_DEVICE_TABLE(of, imx_mu_dt_ids);
6442bb70056SOleksij Rempel 
64503b70130SNathan Chancellor static int __maybe_unused imx_mu_suspend_noirq(struct device *dev)
646ba5f9fa0SDong Aisheng {
647ba5f9fa0SDong Aisheng 	struct imx_mu_priv *priv = dev_get_drvdata(dev);
648f689a7cfSPeng Fan 	int i;
649ba5f9fa0SDong Aisheng 
650f689a7cfSPeng Fan 	if (!priv->clk) {
651f689a7cfSPeng Fan 		for (i = 0; i < IMX_MU_xCR_MAX; i++)
652f689a7cfSPeng Fan 			priv->xcr[i] = imx_mu_read(priv, priv->dcfg->xCR[i]);
653f689a7cfSPeng Fan 	}
654ba5f9fa0SDong Aisheng 
655ba5f9fa0SDong Aisheng 	return 0;
656ba5f9fa0SDong Aisheng }
657ba5f9fa0SDong Aisheng 
65803b70130SNathan Chancellor static int __maybe_unused imx_mu_resume_noirq(struct device *dev)
659ba5f9fa0SDong Aisheng {
660ba5f9fa0SDong Aisheng 	struct imx_mu_priv *priv = dev_get_drvdata(dev);
661f689a7cfSPeng Fan 	int i;
662ba5f9fa0SDong Aisheng 
663ba5f9fa0SDong Aisheng 	/*
664ba5f9fa0SDong Aisheng 	 * ONLY restore MU when context lost, the TIE could
665ba5f9fa0SDong Aisheng 	 * be set during noirq resume as there is MU data
666ba5f9fa0SDong Aisheng 	 * communication going on, and restore the saved
667ba5f9fa0SDong Aisheng 	 * value will overwrite the TIE and cause MU data
668ba5f9fa0SDong Aisheng 	 * send failed, may lead to system freeze. This issue
669ba5f9fa0SDong Aisheng 	 * is observed by testing freeze mode suspend.
670ba5f9fa0SDong Aisheng 	 */
671f689a7cfSPeng Fan 	if (!imx_mu_read(priv, priv->dcfg->xCR[0]) && !priv->clk) {
672f689a7cfSPeng Fan 		for (i = 0; i < IMX_MU_xCR_MAX; i++)
673f689a7cfSPeng Fan 			imx_mu_write(priv, priv->xcr[i], priv->dcfg->xCR[i]);
674f689a7cfSPeng Fan 	}
675ba5f9fa0SDong Aisheng 
676ba5f9fa0SDong Aisheng 	return 0;
677ba5f9fa0SDong Aisheng }
678ba5f9fa0SDong Aisheng 
67903b70130SNathan Chancellor static int __maybe_unused imx_mu_runtime_suspend(struct device *dev)
680bb2b2624SAnson Huang {
681bb2b2624SAnson Huang 	struct imx_mu_priv *priv = dev_get_drvdata(dev);
682bb2b2624SAnson Huang 
683bb2b2624SAnson Huang 	clk_disable_unprepare(priv->clk);
684bb2b2624SAnson Huang 
685bb2b2624SAnson Huang 	return 0;
686bb2b2624SAnson Huang }
687bb2b2624SAnson Huang 
68803b70130SNathan Chancellor static int __maybe_unused imx_mu_runtime_resume(struct device *dev)
689bb2b2624SAnson Huang {
690bb2b2624SAnson Huang 	struct imx_mu_priv *priv = dev_get_drvdata(dev);
691bb2b2624SAnson Huang 	int ret;
692bb2b2624SAnson Huang 
693bb2b2624SAnson Huang 	ret = clk_prepare_enable(priv->clk);
694bb2b2624SAnson Huang 	if (ret)
695bb2b2624SAnson Huang 		dev_err(dev, "failed to enable clock\n");
696bb2b2624SAnson Huang 
697bb2b2624SAnson Huang 	return ret;
698bb2b2624SAnson Huang }
699bb2b2624SAnson Huang 
700ba5f9fa0SDong Aisheng static const struct dev_pm_ops imx_mu_pm_ops = {
701ba5f9fa0SDong Aisheng 	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(imx_mu_suspend_noirq,
702ba5f9fa0SDong Aisheng 				      imx_mu_resume_noirq)
703bb2b2624SAnson Huang 	SET_RUNTIME_PM_OPS(imx_mu_runtime_suspend,
704bb2b2624SAnson Huang 			   imx_mu_runtime_resume, NULL)
705ba5f9fa0SDong Aisheng };
706ba5f9fa0SDong Aisheng 
7072bb70056SOleksij Rempel static struct platform_driver imx_mu_driver = {
7082bb70056SOleksij Rempel 	.probe		= imx_mu_probe,
7092bb70056SOleksij Rempel 	.remove		= imx_mu_remove,
7102bb70056SOleksij Rempel 	.driver = {
7112bb70056SOleksij Rempel 		.name	= "imx_mu",
7122bb70056SOleksij Rempel 		.of_match_table = imx_mu_dt_ids,
713ba5f9fa0SDong Aisheng 		.pm = &imx_mu_pm_ops,
7142bb70056SOleksij Rempel 	},
7152bb70056SOleksij Rempel };
7162bb70056SOleksij Rempel module_platform_driver(imx_mu_driver);
7172bb70056SOleksij Rempel 
7182bb70056SOleksij Rempel MODULE_AUTHOR("Oleksij Rempel <o.rempel@pengutronix.de>");
7192bb70056SOleksij Rempel MODULE_DESCRIPTION("Message Unit driver for i.MX");
7202bb70056SOleksij Rempel MODULE_LICENSE("GPL v2");
721