xref: /openbmc/linux/drivers/mailbox/imx-mailbox.c (revision 8219efd0)
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>
122bb70056SOleksij Rempel #include <linux/kernel.h>
132bb70056SOleksij Rempel #include <linux/mailbox_controller.h>
142bb70056SOleksij Rempel #include <linux/module.h>
152bb70056SOleksij Rempel #include <linux/of_device.h>
16676f23eaSAnson Huang #include <linux/pm_runtime.h>
17892cb524SRobin Gong #include <linux/suspend.h>
182bb70056SOleksij Rempel #include <linux/slab.h>
192bb70056SOleksij Rempel 
202bb70056SOleksij Rempel #define IMX_MU_CHANS		16
210a67003bSPeng Fan /* TX0/RX0/RXDB[0-3] */
220a67003bSPeng Fan #define IMX_MU_SCU_CHANS	6
2397961f78SPeng Fan /* TX0/RX0 */
2497961f78SPeng Fan #define IMX_MU_S4_CHANS		2
252bb70056SOleksij Rempel #define IMX_MU_CHAN_NAME_SIZE	20
262bb70056SOleksij Rempel 
272bb70056SOleksij Rempel enum imx_mu_chan_type {
282bb70056SOleksij Rempel 	IMX_MU_TYPE_TX,		/* Tx */
292bb70056SOleksij Rempel 	IMX_MU_TYPE_RX,		/* Rx */
302bb70056SOleksij Rempel 	IMX_MU_TYPE_TXDB,	/* Tx doorbell */
312bb70056SOleksij Rempel 	IMX_MU_TYPE_RXDB,	/* Rx doorbell */
322bb70056SOleksij Rempel };
332bb70056SOleksij Rempel 
34f689a7cfSPeng Fan enum imx_mu_xcr {
354f0b776eSPeng Fan 	IMX_MU_GIER,
36f689a7cfSPeng Fan 	IMX_MU_GCR,
37f689a7cfSPeng Fan 	IMX_MU_TCR,
38f689a7cfSPeng Fan 	IMX_MU_RCR,
39f689a7cfSPeng Fan 	IMX_MU_xCR_MAX,
40f689a7cfSPeng Fan };
41f689a7cfSPeng Fan 
42f689a7cfSPeng Fan enum imx_mu_xsr {
43f689a7cfSPeng Fan 	IMX_MU_SR,
44f689a7cfSPeng Fan 	IMX_MU_GSR,
45f689a7cfSPeng Fan 	IMX_MU_TSR,
46f689a7cfSPeng Fan 	IMX_MU_RSR,
47f689a7cfSPeng Fan };
48f689a7cfSPeng Fan 
490a67003bSPeng Fan struct imx_sc_rpc_msg_max {
500a67003bSPeng Fan 	struct imx_sc_rpc_msg hdr;
510a67003bSPeng Fan 	u32 data[7];
520a67003bSPeng Fan };
530a67003bSPeng Fan 
5497961f78SPeng Fan struct imx_s4_rpc_msg_max {
5597961f78SPeng Fan 	struct imx_s4_rpc_msg hdr;
5697961f78SPeng Fan 	u32 data[254];
5797961f78SPeng Fan };
5897961f78SPeng Fan 
592bb70056SOleksij Rempel struct imx_mu_con_priv {
602bb70056SOleksij Rempel 	unsigned int		idx;
612bb70056SOleksij Rempel 	char			irq_desc[IMX_MU_CHAN_NAME_SIZE];
622bb70056SOleksij Rempel 	enum imx_mu_chan_type	type;
632bb70056SOleksij Rempel 	struct mbox_chan	*chan;
642bb70056SOleksij Rempel 	struct tasklet_struct	txdb_tasklet;
652bb70056SOleksij Rempel };
662bb70056SOleksij Rempel 
672bb70056SOleksij Rempel struct imx_mu_priv {
682bb70056SOleksij Rempel 	struct device		*dev;
692bb70056SOleksij Rempel 	void __iomem		*base;
7097961f78SPeng Fan 	void			*msg;
712bb70056SOleksij Rempel 	spinlock_t		xcr_lock; /* control register lock */
722bb70056SOleksij Rempel 
732bb70056SOleksij Rempel 	struct mbox_controller	mbox;
742bb70056SOleksij Rempel 	struct mbox_chan	mbox_chans[IMX_MU_CHANS];
752bb70056SOleksij Rempel 
762bb70056SOleksij Rempel 	struct imx_mu_con_priv  con_priv[IMX_MU_CHANS];
77c6c6bc6eSRichard Zhu 	const struct imx_mu_dcfg	*dcfg;
782bb70056SOleksij Rempel 	struct clk		*clk;
792bb70056SOleksij Rempel 	int			irq;
80892cb524SRobin Gong 	bool			suspend;
812bb70056SOleksij Rempel 
82f689a7cfSPeng Fan 	u32 xcr[4];
83ba5f9fa0SDong Aisheng 
842bb70056SOleksij Rempel 	bool			side_b;
852bb70056SOleksij Rempel };
862bb70056SOleksij Rempel 
874f0b776eSPeng Fan enum imx_mu_type {
884f0b776eSPeng Fan 	IMX_MU_V1,
8997961f78SPeng Fan 	IMX_MU_V2 = BIT(1),
9097961f78SPeng Fan 	IMX_MU_V2_S4 = BIT(15),
914f0b776eSPeng Fan };
924f0b776eSPeng Fan 
9363b38357SPeng Fan struct imx_mu_dcfg {
9463b38357SPeng Fan 	int (*tx)(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp, void *data);
9563b38357SPeng Fan 	int (*rx)(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp);
9663b38357SPeng Fan 	void (*init)(struct imx_mu_priv *priv);
974f0b776eSPeng Fan 	enum imx_mu_type type;
9832f7443dSPeng Fan 	u32	xTR;		/* Transmit Register0 */
9932f7443dSPeng Fan 	u32	xRR;		/* Receive Register0 */
100f689a7cfSPeng Fan 	u32	xSR[4];		/* Status Registers */
101f689a7cfSPeng Fan 	u32	xCR[4];		/* Control Registers */
102c6c6bc6eSRichard Zhu };
103c6c6bc6eSRichard Zhu 
10497961f78SPeng Fan #define IMX_MU_xSR_GIPn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(28 + (3 - (x))))
10597961f78SPeng Fan #define IMX_MU_xSR_RFn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(24 + (3 - (x))))
10697961f78SPeng Fan #define IMX_MU_xSR_TEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(20 + (3 - (x))))
1074f0b776eSPeng Fan 
1084f0b776eSPeng Fan /* General Purpose Interrupt Enable */
10997961f78SPeng Fan #define IMX_MU_xCR_GIEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(28 + (3 - (x))))
1104f0b776eSPeng Fan /* Receive Interrupt Enable */
11197961f78SPeng Fan #define IMX_MU_xCR_RIEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(24 + (3 - (x))))
1124f0b776eSPeng Fan /* Transmit Interrupt Enable */
11397961f78SPeng Fan #define IMX_MU_xCR_TIEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(20 + (3 - (x))))
1144f0b776eSPeng Fan /* General Purpose Interrupt Request */
11597961f78SPeng Fan #define IMX_MU_xCR_GIRn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(16 + (3 - (x))))
1164f0b776eSPeng Fan 
1174f0b776eSPeng Fan 
1182bb70056SOleksij Rempel static struct imx_mu_priv *to_imx_mu_priv(struct mbox_controller *mbox)
1192bb70056SOleksij Rempel {
1202bb70056SOleksij Rempel 	return container_of(mbox, struct imx_mu_priv, mbox);
1212bb70056SOleksij Rempel }
1222bb70056SOleksij Rempel 
1232bb70056SOleksij Rempel static void imx_mu_write(struct imx_mu_priv *priv, u32 val, u32 offs)
1242bb70056SOleksij Rempel {
1252bb70056SOleksij Rempel 	iowrite32(val, priv->base + offs);
1262bb70056SOleksij Rempel }
1272bb70056SOleksij Rempel 
1282bb70056SOleksij Rempel static u32 imx_mu_read(struct imx_mu_priv *priv, u32 offs)
1292bb70056SOleksij Rempel {
1302bb70056SOleksij Rempel 	return ioread32(priv->base + offs);
1312bb70056SOleksij Rempel }
1322bb70056SOleksij Rempel 
133f689a7cfSPeng Fan static u32 imx_mu_xcr_rmw(struct imx_mu_priv *priv, enum imx_mu_xcr type, u32 set, u32 clr)
1342bb70056SOleksij Rempel {
1352bb70056SOleksij Rempel 	unsigned long flags;
1362bb70056SOleksij Rempel 	u32 val;
1372bb70056SOleksij Rempel 
1382bb70056SOleksij Rempel 	spin_lock_irqsave(&priv->xcr_lock, flags);
139f689a7cfSPeng Fan 	val = imx_mu_read(priv, priv->dcfg->xCR[type]);
1402bb70056SOleksij Rempel 	val &= ~clr;
1412bb70056SOleksij Rempel 	val |= set;
142f689a7cfSPeng Fan 	imx_mu_write(priv, val, priv->dcfg->xCR[type]);
1432bb70056SOleksij Rempel 	spin_unlock_irqrestore(&priv->xcr_lock, flags);
1442bb70056SOleksij Rempel 
1452bb70056SOleksij Rempel 	return val;
1462bb70056SOleksij Rempel }
1472bb70056SOleksij Rempel 
14863b38357SPeng Fan static int imx_mu_generic_tx(struct imx_mu_priv *priv,
14963b38357SPeng Fan 			     struct imx_mu_con_priv *cp,
15063b38357SPeng Fan 			     void *data)
15163b38357SPeng Fan {
15263b38357SPeng Fan 	u32 *arg = data;
15363b38357SPeng Fan 
15463b38357SPeng Fan 	switch (cp->type) {
15563b38357SPeng Fan 	case IMX_MU_TYPE_TX:
15632f7443dSPeng Fan 		imx_mu_write(priv, *arg, priv->dcfg->xTR + cp->idx * 4);
1574f0b776eSPeng Fan 		imx_mu_xcr_rmw(priv, IMX_MU_TCR, IMX_MU_xCR_TIEn(priv->dcfg->type, cp->idx), 0);
15863b38357SPeng Fan 		break;
15963b38357SPeng Fan 	case IMX_MU_TYPE_TXDB:
1604f0b776eSPeng Fan 		imx_mu_xcr_rmw(priv, IMX_MU_GCR, IMX_MU_xCR_GIRn(priv->dcfg->type, cp->idx), 0);
16163b38357SPeng Fan 		tasklet_schedule(&cp->txdb_tasklet);
16263b38357SPeng Fan 		break;
16363b38357SPeng Fan 	default:
16463b38357SPeng Fan 		dev_warn_ratelimited(priv->dev, "Send data on wrong channel type: %d\n", cp->type);
16563b38357SPeng Fan 		return -EINVAL;
16663b38357SPeng Fan 	}
16763b38357SPeng Fan 
16863b38357SPeng Fan 	return 0;
16963b38357SPeng Fan }
17063b38357SPeng Fan 
17163b38357SPeng Fan static int imx_mu_generic_rx(struct imx_mu_priv *priv,
17263b38357SPeng Fan 			     struct imx_mu_con_priv *cp)
17363b38357SPeng Fan {
17463b38357SPeng Fan 	u32 dat;
17563b38357SPeng Fan 
17632f7443dSPeng Fan 	dat = imx_mu_read(priv, priv->dcfg->xRR + (cp->idx) * 4);
17763b38357SPeng Fan 	mbox_chan_received_data(cp->chan, (void *)&dat);
17863b38357SPeng Fan 
17963b38357SPeng Fan 	return 0;
18063b38357SPeng Fan }
18163b38357SPeng Fan 
18297961f78SPeng Fan static int imx_mu_specific_tx(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp, void *data)
1830a67003bSPeng Fan {
1840a67003bSPeng Fan 	u32 *arg = data;
1850a67003bSPeng Fan 	int i, ret;
1860a67003bSPeng Fan 	u32 xsr;
18797961f78SPeng Fan 	u32 size, max_size, num_tr;
18897961f78SPeng Fan 
18997961f78SPeng Fan 	if (priv->dcfg->type & IMX_MU_V2_S4) {
19097961f78SPeng Fan 		size = ((struct imx_s4_rpc_msg_max *)data)->hdr.size;
19197961f78SPeng Fan 		max_size = sizeof(struct imx_s4_rpc_msg_max);
19297961f78SPeng Fan 		num_tr = 8;
19397961f78SPeng Fan 	} else {
19497961f78SPeng Fan 		size = ((struct imx_sc_rpc_msg_max *)data)->hdr.size;
19597961f78SPeng Fan 		max_size = sizeof(struct imx_sc_rpc_msg_max);
19697961f78SPeng Fan 		num_tr = 4;
19797961f78SPeng Fan 	}
1980a67003bSPeng Fan 
1990a67003bSPeng Fan 	switch (cp->type) {
2000a67003bSPeng Fan 	case IMX_MU_TYPE_TX:
2019d8ca628SPeng Fan 		/*
2029d8ca628SPeng Fan 		 * msg->hdr.size specifies the number of u32 words while
2039d8ca628SPeng Fan 		 * sizeof yields bytes.
2049d8ca628SPeng Fan 		 */
2059d8ca628SPeng Fan 
20697961f78SPeng Fan 		if (size > max_size / 4) {
2070a67003bSPeng Fan 			/*
2080a67003bSPeng Fan 			 * The real message size can be different to
20997961f78SPeng Fan 			 * struct imx_sc_rpc_msg_max/imx_s4_rpc_msg_max size
2100a67003bSPeng Fan 			 */
21197961f78SPeng Fan 			dev_err(priv->dev, "Maximal message size (%u bytes) exceeded on TX; got: %i bytes\n", max_size, size << 2);
2120a67003bSPeng Fan 			return -EINVAL;
2130a67003bSPeng Fan 		}
2140a67003bSPeng Fan 
21597961f78SPeng Fan 		for (i = 0; i < num_tr && i < size; i++)
21697961f78SPeng Fan 			imx_mu_write(priv, *arg++, priv->dcfg->xTR + (i % num_tr) * 4);
21797961f78SPeng Fan 		for (; i < size; i++) {
218f689a7cfSPeng Fan 			ret = readl_poll_timeout(priv->base + priv->dcfg->xSR[IMX_MU_TSR],
2190a67003bSPeng Fan 						 xsr,
22097961f78SPeng Fan 						 xsr & IMX_MU_xSR_TEn(priv->dcfg->type, i % num_tr),
2210a67003bSPeng Fan 						 0, 100);
2220a67003bSPeng Fan 			if (ret) {
2230a67003bSPeng Fan 				dev_err(priv->dev, "Send data index: %d timeout\n", i);
2240a67003bSPeng Fan 				return ret;
2250a67003bSPeng Fan 			}
22697961f78SPeng Fan 			imx_mu_write(priv, *arg++, priv->dcfg->xTR + (i % num_tr) * 4);
2270a67003bSPeng Fan 		}
2280a67003bSPeng Fan 
2294f0b776eSPeng Fan 		imx_mu_xcr_rmw(priv, IMX_MU_TCR, IMX_MU_xCR_TIEn(priv->dcfg->type, cp->idx), 0);
2300a67003bSPeng Fan 		break;
2310a67003bSPeng Fan 	default:
2320a67003bSPeng Fan 		dev_warn_ratelimited(priv->dev, "Send data on wrong channel type: %d\n", cp->type);
2330a67003bSPeng Fan 		return -EINVAL;
2340a67003bSPeng Fan 	}
2350a67003bSPeng Fan 
2360a67003bSPeng Fan 	return 0;
2370a67003bSPeng Fan }
2380a67003bSPeng Fan 
23997961f78SPeng Fan static int imx_mu_specific_rx(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp)
2400a67003bSPeng Fan {
24197961f78SPeng Fan 	u32 *data;
2420a67003bSPeng Fan 	int i, ret;
2430a67003bSPeng Fan 	u32 xsr;
24497961f78SPeng Fan 	u32 size, max_size;
24597961f78SPeng Fan 
24697961f78SPeng Fan 	data = (u32 *)priv->msg;
2470a67003bSPeng Fan 
2484f0b776eSPeng Fan 	imx_mu_xcr_rmw(priv, IMX_MU_RCR, 0, IMX_MU_xCR_RIEn(priv->dcfg->type, 0));
24932f7443dSPeng Fan 	*data++ = imx_mu_read(priv, priv->dcfg->xRR);
2500a67003bSPeng Fan 
25197961f78SPeng Fan 	if (priv->dcfg->type & IMX_MU_V2_S4) {
25297961f78SPeng Fan 		size = ((struct imx_s4_rpc_msg_max *)priv->msg)->hdr.size;
25397961f78SPeng Fan 		max_size = sizeof(struct imx_s4_rpc_msg_max);
25497961f78SPeng Fan 	} else {
25597961f78SPeng Fan 		size = ((struct imx_sc_rpc_msg_max *)priv->msg)->hdr.size;
25697961f78SPeng Fan 		max_size = sizeof(struct imx_sc_rpc_msg_max);
25797961f78SPeng Fan 	}
25897961f78SPeng Fan 
25997961f78SPeng Fan 	if (size > max_size / 4) {
26097961f78SPeng Fan 		dev_err(priv->dev, "Maximal message size (%u bytes) exceeded on RX; got: %i bytes\n", max_size, size << 2);
2610a67003bSPeng Fan 		return -EINVAL;
2620a67003bSPeng Fan 	}
2630a67003bSPeng Fan 
26497961f78SPeng Fan 	for (i = 1; i < size; i++) {
265f689a7cfSPeng Fan 		ret = readl_poll_timeout(priv->base + priv->dcfg->xSR[IMX_MU_RSR], xsr,
2664f0b776eSPeng Fan 					 xsr & IMX_MU_xSR_RFn(priv->dcfg->type, i % 4), 0, 100);
2670a67003bSPeng Fan 		if (ret) {
2680a67003bSPeng Fan 			dev_err(priv->dev, "timeout read idx %d\n", i);
2690a67003bSPeng Fan 			return ret;
2700a67003bSPeng Fan 		}
27132f7443dSPeng Fan 		*data++ = imx_mu_read(priv, priv->dcfg->xRR + (i % 4) * 4);
2720a67003bSPeng Fan 	}
2730a67003bSPeng Fan 
2744f0b776eSPeng Fan 	imx_mu_xcr_rmw(priv, IMX_MU_RCR, IMX_MU_xCR_RIEn(priv->dcfg->type, 0), 0);
27597961f78SPeng Fan 	mbox_chan_received_data(cp->chan, (void *)priv->msg);
2760a67003bSPeng Fan 
2770a67003bSPeng Fan 	return 0;
2780a67003bSPeng Fan }
2790a67003bSPeng Fan 
2802bb70056SOleksij Rempel static void imx_mu_txdb_tasklet(unsigned long data)
2812bb70056SOleksij Rempel {
2822bb70056SOleksij Rempel 	struct imx_mu_con_priv *cp = (struct imx_mu_con_priv *)data;
2832bb70056SOleksij Rempel 
2842bb70056SOleksij Rempel 	mbox_chan_txdone(cp->chan, 0);
2852bb70056SOleksij Rempel }
2862bb70056SOleksij Rempel 
2872bb70056SOleksij Rempel static irqreturn_t imx_mu_isr(int irq, void *p)
2882bb70056SOleksij Rempel {
2892bb70056SOleksij Rempel 	struct mbox_chan *chan = p;
2902bb70056SOleksij Rempel 	struct imx_mu_priv *priv = to_imx_mu_priv(chan->mbox);
2912bb70056SOleksij Rempel 	struct imx_mu_con_priv *cp = chan->con_priv;
29263b38357SPeng Fan 	u32 val, ctrl;
2932bb70056SOleksij Rempel 
2942bb70056SOleksij Rempel 	switch (cp->type) {
2952bb70056SOleksij Rempel 	case IMX_MU_TYPE_TX:
296f689a7cfSPeng Fan 		ctrl = imx_mu_read(priv, priv->dcfg->xCR[IMX_MU_TCR]);
297f689a7cfSPeng Fan 		val = imx_mu_read(priv, priv->dcfg->xSR[IMX_MU_TSR]);
2984f0b776eSPeng Fan 		val &= IMX_MU_xSR_TEn(priv->dcfg->type, cp->idx) &
2994f0b776eSPeng Fan 			(ctrl & IMX_MU_xCR_TIEn(priv->dcfg->type, cp->idx));
3002bb70056SOleksij Rempel 		break;
3012bb70056SOleksij Rempel 	case IMX_MU_TYPE_RX:
302f689a7cfSPeng Fan 		ctrl = imx_mu_read(priv, priv->dcfg->xCR[IMX_MU_RCR]);
303f689a7cfSPeng Fan 		val = imx_mu_read(priv, priv->dcfg->xSR[IMX_MU_RSR]);
3044f0b776eSPeng Fan 		val &= IMX_MU_xSR_RFn(priv->dcfg->type, cp->idx) &
3054f0b776eSPeng Fan 			(ctrl & IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx));
3062bb70056SOleksij Rempel 		break;
3072bb70056SOleksij Rempel 	case IMX_MU_TYPE_RXDB:
3084f0b776eSPeng Fan 		ctrl = imx_mu_read(priv, priv->dcfg->xCR[IMX_MU_GIER]);
309f689a7cfSPeng Fan 		val = imx_mu_read(priv, priv->dcfg->xSR[IMX_MU_GSR]);
3104f0b776eSPeng Fan 		val &= IMX_MU_xSR_GIPn(priv->dcfg->type, cp->idx) &
3114f0b776eSPeng Fan 			(ctrl & IMX_MU_xCR_GIEn(priv->dcfg->type, cp->idx));
3122bb70056SOleksij Rempel 		break;
3132bb70056SOleksij Rempel 	default:
314e80a7e7eSNathan Chancellor 		dev_warn_ratelimited(priv->dev, "Unhandled channel type %d\n",
315e80a7e7eSNathan Chancellor 				     cp->type);
316e80a7e7eSNathan Chancellor 		return IRQ_NONE;
3172bb70056SOleksij Rempel 	}
3182bb70056SOleksij Rempel 
3192bb70056SOleksij Rempel 	if (!val)
3202bb70056SOleksij Rempel 		return IRQ_NONE;
3212bb70056SOleksij Rempel 
3224f0b776eSPeng Fan 	if ((val == IMX_MU_xSR_TEn(priv->dcfg->type, cp->idx)) &&
3234f0b776eSPeng Fan 	    (cp->type == IMX_MU_TYPE_TX)) {
3244f0b776eSPeng Fan 		imx_mu_xcr_rmw(priv, IMX_MU_TCR, 0, IMX_MU_xCR_TIEn(priv->dcfg->type, cp->idx));
3252bb70056SOleksij Rempel 		mbox_chan_txdone(chan, 0);
3264f0b776eSPeng Fan 	} else if ((val == IMX_MU_xSR_RFn(priv->dcfg->type, cp->idx)) &&
3274f0b776eSPeng Fan 		   (cp->type == IMX_MU_TYPE_RX)) {
32863b38357SPeng Fan 		priv->dcfg->rx(priv, cp);
3294f0b776eSPeng Fan 	} else if ((val == IMX_MU_xSR_GIPn(priv->dcfg->type, cp->idx)) &&
3304f0b776eSPeng Fan 		   (cp->type == IMX_MU_TYPE_RXDB)) {
3314f0b776eSPeng Fan 		imx_mu_write(priv, IMX_MU_xSR_GIPn(priv->dcfg->type, cp->idx),
3324f0b776eSPeng Fan 			     priv->dcfg->xSR[IMX_MU_GSR]);
3332bb70056SOleksij Rempel 		mbox_chan_received_data(chan, NULL);
3342bb70056SOleksij Rempel 	} else {
3352bb70056SOleksij Rempel 		dev_warn_ratelimited(priv->dev, "Not handled interrupt\n");
3362bb70056SOleksij Rempel 		return IRQ_NONE;
3372bb70056SOleksij Rempel 	}
3382bb70056SOleksij Rempel 
339892cb524SRobin Gong 	if (priv->suspend)
340892cb524SRobin Gong 		pm_system_wakeup();
341892cb524SRobin Gong 
3422bb70056SOleksij Rempel 	return IRQ_HANDLED;
3432bb70056SOleksij Rempel }
3442bb70056SOleksij Rempel 
3452bb70056SOleksij Rempel static int imx_mu_send_data(struct mbox_chan *chan, void *data)
3462bb70056SOleksij Rempel {
3472bb70056SOleksij Rempel 	struct imx_mu_priv *priv = to_imx_mu_priv(chan->mbox);
3482bb70056SOleksij Rempel 	struct imx_mu_con_priv *cp = chan->con_priv;
3492bb70056SOleksij Rempel 
35063b38357SPeng Fan 	return priv->dcfg->tx(priv, cp, data);
3512bb70056SOleksij Rempel }
3522bb70056SOleksij Rempel 
3532bb70056SOleksij Rempel static int imx_mu_startup(struct mbox_chan *chan)
3542bb70056SOleksij Rempel {
3552bb70056SOleksij Rempel 	struct imx_mu_priv *priv = to_imx_mu_priv(chan->mbox);
3562bb70056SOleksij Rempel 	struct imx_mu_con_priv *cp = chan->con_priv;
357b7b2796bSAnson Huang 	unsigned long irq_flag = IRQF_SHARED;
3582bb70056SOleksij Rempel 	int ret;
3592bb70056SOleksij Rempel 
360676f23eaSAnson Huang 	pm_runtime_get_sync(priv->dev);
3612bb70056SOleksij Rempel 	if (cp->type == IMX_MU_TYPE_TXDB) {
3622bb70056SOleksij Rempel 		/* Tx doorbell don't have ACK support */
3632bb70056SOleksij Rempel 		tasklet_init(&cp->txdb_tasklet, imx_mu_txdb_tasklet,
3642bb70056SOleksij Rempel 			     (unsigned long)cp);
3652bb70056SOleksij Rempel 		return 0;
3662bb70056SOleksij Rempel 	}
3672bb70056SOleksij Rempel 
368b7b2796bSAnson Huang 	/* IPC MU should be with IRQF_NO_SUSPEND set */
369b7b2796bSAnson Huang 	if (!priv->dev->pm_domain)
370b7b2796bSAnson Huang 		irq_flag |= IRQF_NO_SUSPEND;
371b7b2796bSAnson Huang 
372b7b2796bSAnson Huang 	ret = request_irq(priv->irq, imx_mu_isr, irq_flag,
373b7b2796bSAnson Huang 			  cp->irq_desc, chan);
3742bb70056SOleksij Rempel 	if (ret) {
3752bb70056SOleksij Rempel 		dev_err(priv->dev,
3762bb70056SOleksij Rempel 			"Unable to acquire IRQ %d\n", priv->irq);
3772bb70056SOleksij Rempel 		return ret;
3782bb70056SOleksij Rempel 	}
3792bb70056SOleksij Rempel 
3802bb70056SOleksij Rempel 	switch (cp->type) {
3812bb70056SOleksij Rempel 	case IMX_MU_TYPE_RX:
3824f0b776eSPeng Fan 		imx_mu_xcr_rmw(priv, IMX_MU_RCR, IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx), 0);
3832bb70056SOleksij Rempel 		break;
3842bb70056SOleksij Rempel 	case IMX_MU_TYPE_RXDB:
3854f0b776eSPeng Fan 		imx_mu_xcr_rmw(priv, IMX_MU_GIER, IMX_MU_xCR_GIEn(priv->dcfg->type, cp->idx), 0);
3862bb70056SOleksij Rempel 		break;
3872bb70056SOleksij Rempel 	default:
3882bb70056SOleksij Rempel 		break;
3892bb70056SOleksij Rempel 	}
3902bb70056SOleksij Rempel 
3912bb70056SOleksij Rempel 	return 0;
3922bb70056SOleksij Rempel }
3932bb70056SOleksij Rempel 
3942bb70056SOleksij Rempel static void imx_mu_shutdown(struct mbox_chan *chan)
3952bb70056SOleksij Rempel {
3962bb70056SOleksij Rempel 	struct imx_mu_priv *priv = to_imx_mu_priv(chan->mbox);
3972bb70056SOleksij Rempel 	struct imx_mu_con_priv *cp = chan->con_priv;
3982bb70056SOleksij Rempel 
399bf159d15SDaniel Baluta 	if (cp->type == IMX_MU_TYPE_TXDB) {
4002bb70056SOleksij Rempel 		tasklet_kill(&cp->txdb_tasklet);
401676f23eaSAnson Huang 		pm_runtime_put_sync(priv->dev);
402bf159d15SDaniel Baluta 		return;
403bf159d15SDaniel Baluta 	}
4042bb70056SOleksij Rempel 
4055f0af07eSDaniel Baluta 	switch (cp->type) {
4065f0af07eSDaniel Baluta 	case IMX_MU_TYPE_TX:
4074f0b776eSPeng Fan 		imx_mu_xcr_rmw(priv, IMX_MU_TCR, 0, IMX_MU_xCR_TIEn(priv->dcfg->type, cp->idx));
4085f0af07eSDaniel Baluta 		break;
4095f0af07eSDaniel Baluta 	case IMX_MU_TYPE_RX:
4104f0b776eSPeng Fan 		imx_mu_xcr_rmw(priv, IMX_MU_RCR, 0, IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx));
4115f0af07eSDaniel Baluta 		break;
4125f0af07eSDaniel Baluta 	case IMX_MU_TYPE_RXDB:
4134f0b776eSPeng Fan 		imx_mu_xcr_rmw(priv, IMX_MU_GIER, 0, IMX_MU_xCR_GIEn(priv->dcfg->type, cp->idx));
4145f0af07eSDaniel Baluta 		break;
4155f0af07eSDaniel Baluta 	default:
4165f0af07eSDaniel Baluta 		break;
4175f0af07eSDaniel Baluta 	}
4182bb70056SOleksij Rempel 
4192bb70056SOleksij Rempel 	free_irq(priv->irq, chan);
420676f23eaSAnson Huang 	pm_runtime_put_sync(priv->dev);
4212bb70056SOleksij Rempel }
4222bb70056SOleksij Rempel 
4232bb70056SOleksij Rempel static const struct mbox_chan_ops imx_mu_ops = {
4242bb70056SOleksij Rempel 	.send_data = imx_mu_send_data,
4252bb70056SOleksij Rempel 	.startup = imx_mu_startup,
4262bb70056SOleksij Rempel 	.shutdown = imx_mu_shutdown,
4272bb70056SOleksij Rempel };
4282bb70056SOleksij Rempel 
42997961f78SPeng Fan static struct mbox_chan *imx_mu_specific_xlate(struct mbox_controller *mbox,
4300a67003bSPeng Fan 					       const struct of_phandle_args *sp)
4310a67003bSPeng Fan {
4320a67003bSPeng Fan 	u32 type, idx, chan;
4330a67003bSPeng Fan 
4340a67003bSPeng Fan 	if (sp->args_count != 2) {
4350a67003bSPeng Fan 		dev_err(mbox->dev, "Invalid argument count %d\n", sp->args_count);
4360a67003bSPeng Fan 		return ERR_PTR(-EINVAL);
4370a67003bSPeng Fan 	}
4380a67003bSPeng Fan 
4390a67003bSPeng Fan 	type = sp->args[0]; /* channel type */
4400a67003bSPeng Fan 	idx = sp->args[1]; /* index */
4410a67003bSPeng Fan 
4420a67003bSPeng Fan 	switch (type) {
4430a67003bSPeng Fan 	case IMX_MU_TYPE_TX:
4440a67003bSPeng Fan 	case IMX_MU_TYPE_RX:
4450a67003bSPeng Fan 		if (idx != 0)
4460a67003bSPeng Fan 			dev_err(mbox->dev, "Invalid chan idx: %d\n", idx);
4470a67003bSPeng Fan 		chan = type;
4480a67003bSPeng Fan 		break;
4490a67003bSPeng Fan 	case IMX_MU_TYPE_RXDB:
4500a67003bSPeng Fan 		chan = 2 + idx;
4510a67003bSPeng Fan 		break;
4520a67003bSPeng Fan 	default:
4530a67003bSPeng Fan 		dev_err(mbox->dev, "Invalid chan type: %d\n", type);
4541b3a347bSDan Carpenter 		return ERR_PTR(-EINVAL);
4550a67003bSPeng Fan 	}
4560a67003bSPeng Fan 
4570a67003bSPeng Fan 	if (chan >= mbox->num_chans) {
4580a67003bSPeng Fan 		dev_err(mbox->dev, "Not supported channel number: %d. (type: %d, idx: %d)\n", chan, type, idx);
4590a67003bSPeng Fan 		return ERR_PTR(-EINVAL);
4600a67003bSPeng Fan 	}
4610a67003bSPeng Fan 
4620a67003bSPeng Fan 	return &mbox->chans[chan];
4630a67003bSPeng Fan }
4640a67003bSPeng Fan 
4652bb70056SOleksij Rempel static struct mbox_chan * imx_mu_xlate(struct mbox_controller *mbox,
4662bb70056SOleksij Rempel 				       const struct of_phandle_args *sp)
4672bb70056SOleksij Rempel {
4682bb70056SOleksij Rempel 	u32 type, idx, chan;
4692bb70056SOleksij Rempel 
4702bb70056SOleksij Rempel 	if (sp->args_count != 2) {
4712bb70056SOleksij Rempel 		dev_err(mbox->dev, "Invalid argument count %d\n", sp->args_count);
4722bb70056SOleksij Rempel 		return ERR_PTR(-EINVAL);
4732bb70056SOleksij Rempel 	}
4742bb70056SOleksij Rempel 
4752bb70056SOleksij Rempel 	type = sp->args[0]; /* channel type */
4762bb70056SOleksij Rempel 	idx = sp->args[1]; /* index */
4772bb70056SOleksij Rempel 	chan = type * 4 + idx;
4782bb70056SOleksij Rempel 
4792bb70056SOleksij Rempel 	if (chan >= mbox->num_chans) {
4802bb70056SOleksij Rempel 		dev_err(mbox->dev, "Not supported channel number: %d. (type: %d, idx: %d)\n", chan, type, idx);
4812bb70056SOleksij Rempel 		return ERR_PTR(-EINVAL);
4822bb70056SOleksij Rempel 	}
4832bb70056SOleksij Rempel 
4842bb70056SOleksij Rempel 	return &mbox->chans[chan];
4852bb70056SOleksij Rempel }
4862bb70056SOleksij Rempel 
4872bb70056SOleksij Rempel static void imx_mu_init_generic(struct imx_mu_priv *priv)
4882bb70056SOleksij Rempel {
48963b38357SPeng Fan 	unsigned int i;
49063b38357SPeng Fan 
49163b38357SPeng Fan 	for (i = 0; i < IMX_MU_CHANS; i++) {
49263b38357SPeng Fan 		struct imx_mu_con_priv *cp = &priv->con_priv[i];
49363b38357SPeng Fan 
49463b38357SPeng Fan 		cp->idx = i % 4;
49563b38357SPeng Fan 		cp->type = i >> 2;
49663b38357SPeng Fan 		cp->chan = &priv->mbox_chans[i];
49763b38357SPeng Fan 		priv->mbox_chans[i].con_priv = cp;
49863b38357SPeng Fan 		snprintf(cp->irq_desc, sizeof(cp->irq_desc),
49963b38357SPeng Fan 			 "imx_mu_chan[%i-%i]", cp->type, cp->idx);
50063b38357SPeng Fan 	}
50163b38357SPeng Fan 
50263b38357SPeng Fan 	priv->mbox.num_chans = IMX_MU_CHANS;
50363b38357SPeng Fan 	priv->mbox.of_xlate = imx_mu_xlate;
50463b38357SPeng Fan 
5052bb70056SOleksij Rempel 	if (priv->side_b)
5062bb70056SOleksij Rempel 		return;
5072bb70056SOleksij Rempel 
5082bb70056SOleksij Rempel 	/* Set default MU configuration */
509f689a7cfSPeng Fan 	for (i = 0; i < IMX_MU_xCR_MAX; i++)
510f689a7cfSPeng Fan 		imx_mu_write(priv, 0, priv->dcfg->xCR[i]);
5112bb70056SOleksij Rempel }
5122bb70056SOleksij Rempel 
51397961f78SPeng Fan static void imx_mu_init_specific(struct imx_mu_priv *priv)
5140a67003bSPeng Fan {
5150a67003bSPeng Fan 	unsigned int i;
51697961f78SPeng Fan 	int num_chans = priv->dcfg->type & IMX_MU_V2_S4 ? IMX_MU_S4_CHANS : IMX_MU_SCU_CHANS;
5170a67003bSPeng Fan 
51897961f78SPeng Fan 	for (i = 0; i < num_chans; i++) {
5190a67003bSPeng Fan 		struct imx_mu_con_priv *cp = &priv->con_priv[i];
5200a67003bSPeng Fan 
5210a67003bSPeng Fan 		cp->idx = i < 2 ? 0 : i - 2;
5220a67003bSPeng Fan 		cp->type = i < 2 ? i : IMX_MU_TYPE_RXDB;
5230a67003bSPeng Fan 		cp->chan = &priv->mbox_chans[i];
5240a67003bSPeng Fan 		priv->mbox_chans[i].con_priv = cp;
5250a67003bSPeng Fan 		snprintf(cp->irq_desc, sizeof(cp->irq_desc),
5260a67003bSPeng Fan 			 "imx_mu_chan[%i-%i]", cp->type, cp->idx);
5270a67003bSPeng Fan 	}
5280a67003bSPeng Fan 
52997961f78SPeng Fan 	priv->mbox.num_chans = num_chans;
53097961f78SPeng Fan 	priv->mbox.of_xlate = imx_mu_specific_xlate;
5310a67003bSPeng Fan 
5320a67003bSPeng Fan 	/* Set default MU configuration */
533f689a7cfSPeng Fan 	for (i = 0; i < IMX_MU_xCR_MAX; i++)
534f689a7cfSPeng Fan 		imx_mu_write(priv, 0, priv->dcfg->xCR[i]);
5350a67003bSPeng Fan }
5360a67003bSPeng Fan 
5372bb70056SOleksij Rempel static int imx_mu_probe(struct platform_device *pdev)
5382bb70056SOleksij Rempel {
5392bb70056SOleksij Rempel 	struct device *dev = &pdev->dev;
5402bb70056SOleksij Rempel 	struct device_node *np = dev->of_node;
5412bb70056SOleksij Rempel 	struct imx_mu_priv *priv;
542c6c6bc6eSRichard Zhu 	const struct imx_mu_dcfg *dcfg;
5432bb70056SOleksij Rempel 	int ret;
54497961f78SPeng Fan 	u32 size;
5452bb70056SOleksij Rempel 
5462bb70056SOleksij Rempel 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
5472bb70056SOleksij Rempel 	if (!priv)
5482bb70056SOleksij Rempel 		return -ENOMEM;
5492bb70056SOleksij Rempel 
5502bb70056SOleksij Rempel 	priv->dev = dev;
5512bb70056SOleksij Rempel 
5520c40e631SAnson Huang 	priv->base = devm_platform_ioremap_resource(pdev, 0);
5532bb70056SOleksij Rempel 	if (IS_ERR(priv->base))
5542bb70056SOleksij Rempel 		return PTR_ERR(priv->base);
5552bb70056SOleksij Rempel 
5562bb70056SOleksij Rempel 	priv->irq = platform_get_irq(pdev, 0);
5572bb70056SOleksij Rempel 	if (priv->irq < 0)
5582bb70056SOleksij Rempel 		return priv->irq;
5592bb70056SOleksij Rempel 
560c6c6bc6eSRichard Zhu 	dcfg = of_device_get_match_data(dev);
561c6c6bc6eSRichard Zhu 	if (!dcfg)
562c6c6bc6eSRichard Zhu 		return -EINVAL;
563c6c6bc6eSRichard Zhu 	priv->dcfg = dcfg;
564c6c6bc6eSRichard Zhu 
56597961f78SPeng Fan 	if (priv->dcfg->type & IMX_MU_V2_S4)
56697961f78SPeng Fan 		size = sizeof(struct imx_s4_rpc_msg_max);
56797961f78SPeng Fan 	else
56897961f78SPeng Fan 		size = sizeof(struct imx_sc_rpc_msg_max);
56997961f78SPeng Fan 
57097961f78SPeng Fan 	priv->msg = devm_kzalloc(dev, size, GFP_KERNEL);
57105d06f37SDan Carpenter 	if (!priv->msg)
57205d06f37SDan Carpenter 		return -ENOMEM;
57397961f78SPeng Fan 
5742bb70056SOleksij Rempel 	priv->clk = devm_clk_get(dev, NULL);
5752bb70056SOleksij Rempel 	if (IS_ERR(priv->clk)) {
5762bb70056SOleksij Rempel 		if (PTR_ERR(priv->clk) != -ENOENT)
5772bb70056SOleksij Rempel 			return PTR_ERR(priv->clk);
5782bb70056SOleksij Rempel 
5792bb70056SOleksij Rempel 		priv->clk = NULL;
5802bb70056SOleksij Rempel 	}
5812bb70056SOleksij Rempel 
5822bb70056SOleksij Rempel 	ret = clk_prepare_enable(priv->clk);
5832bb70056SOleksij Rempel 	if (ret) {
5842bb70056SOleksij Rempel 		dev_err(dev, "Failed to enable clock\n");
5852bb70056SOleksij Rempel 		return ret;
5862bb70056SOleksij Rempel 	}
5872bb70056SOleksij Rempel 
5882bb70056SOleksij Rempel 	priv->side_b = of_property_read_bool(np, "fsl,mu-side-b");
5892bb70056SOleksij Rempel 
59063b38357SPeng Fan 	priv->dcfg->init(priv);
59163b38357SPeng Fan 
5922bb70056SOleksij Rempel 	spin_lock_init(&priv->xcr_lock);
5932bb70056SOleksij Rempel 
5942bb70056SOleksij Rempel 	priv->mbox.dev = dev;
5952bb70056SOleksij Rempel 	priv->mbox.ops = &imx_mu_ops;
5962bb70056SOleksij Rempel 	priv->mbox.chans = priv->mbox_chans;
5972bb70056SOleksij Rempel 	priv->mbox.txdone_irq = true;
5982bb70056SOleksij Rempel 
5992bb70056SOleksij Rempel 	platform_set_drvdata(pdev, priv);
6002bb70056SOleksij Rempel 
601676f23eaSAnson Huang 	ret = devm_mbox_controller_register(dev, &priv->mbox);
60247303f94SFabio Estevam 	if (ret) {
60347303f94SFabio Estevam 		clk_disable_unprepare(priv->clk);
604676f23eaSAnson Huang 		return ret;
60547303f94SFabio Estevam 	}
606676f23eaSAnson Huang 
607676f23eaSAnson Huang 	pm_runtime_enable(dev);
608676f23eaSAnson Huang 
609676f23eaSAnson Huang 	ret = pm_runtime_get_sync(dev);
610676f23eaSAnson Huang 	if (ret < 0) {
611676f23eaSAnson Huang 		pm_runtime_put_noidle(dev);
612676f23eaSAnson Huang 		goto disable_runtime_pm;
613676f23eaSAnson Huang 	}
614676f23eaSAnson Huang 
615676f23eaSAnson Huang 	ret = pm_runtime_put_sync(dev);
616676f23eaSAnson Huang 	if (ret < 0)
617676f23eaSAnson Huang 		goto disable_runtime_pm;
618676f23eaSAnson Huang 
619bb2b2624SAnson Huang 	clk_disable_unprepare(priv->clk);
620bb2b2624SAnson Huang 
621676f23eaSAnson Huang 	return 0;
622676f23eaSAnson Huang 
623676f23eaSAnson Huang disable_runtime_pm:
624676f23eaSAnson Huang 	pm_runtime_disable(dev);
625bb2b2624SAnson Huang 	clk_disable_unprepare(priv->clk);
626676f23eaSAnson Huang 	return ret;
6272bb70056SOleksij Rempel }
6282bb70056SOleksij Rempel 
6292bb70056SOleksij Rempel static int imx_mu_remove(struct platform_device *pdev)
6302bb70056SOleksij Rempel {
6312bb70056SOleksij Rempel 	struct imx_mu_priv *priv = platform_get_drvdata(pdev);
6322bb70056SOleksij Rempel 
633676f23eaSAnson Huang 	pm_runtime_disable(priv->dev);
6342bb70056SOleksij Rempel 
6352bb70056SOleksij Rempel 	return 0;
6362bb70056SOleksij Rempel }
6372bb70056SOleksij Rempel 
63863b38357SPeng Fan static const struct imx_mu_dcfg imx_mu_cfg_imx6sx = {
63963b38357SPeng Fan 	.tx	= imx_mu_generic_tx,
64063b38357SPeng Fan 	.rx	= imx_mu_generic_rx,
64163b38357SPeng Fan 	.init	= imx_mu_init_generic,
64232f7443dSPeng Fan 	.xTR	= 0x0,
64332f7443dSPeng Fan 	.xRR	= 0x10,
644f689a7cfSPeng Fan 	.xSR	= {0x20, 0x20, 0x20, 0x20},
645f689a7cfSPeng Fan 	.xCR	= {0x24, 0x24, 0x24, 0x24},
64663b38357SPeng Fan };
64763b38357SPeng Fan 
64863b38357SPeng Fan static const struct imx_mu_dcfg imx_mu_cfg_imx7ulp = {
64963b38357SPeng Fan 	.tx	= imx_mu_generic_tx,
65063b38357SPeng Fan 	.rx	= imx_mu_generic_rx,
65163b38357SPeng Fan 	.init	= imx_mu_init_generic,
65232f7443dSPeng Fan 	.xTR	= 0x20,
65332f7443dSPeng Fan 	.xRR	= 0x40,
654f689a7cfSPeng Fan 	.xSR	= {0x60, 0x60, 0x60, 0x60},
655f689a7cfSPeng Fan 	.xCR	= {0x64, 0x64, 0x64, 0x64},
65663b38357SPeng Fan };
65763b38357SPeng Fan 
6584f0b776eSPeng Fan static const struct imx_mu_dcfg imx_mu_cfg_imx8ulp = {
6594f0b776eSPeng Fan 	.tx	= imx_mu_generic_tx,
6604f0b776eSPeng Fan 	.rx	= imx_mu_generic_rx,
6614f0b776eSPeng Fan 	.init	= imx_mu_init_generic,
6624f0b776eSPeng Fan 	.type	= IMX_MU_V2,
6634f0b776eSPeng Fan 	.xTR	= 0x200,
6644f0b776eSPeng Fan 	.xRR	= 0x280,
6654f0b776eSPeng Fan 	.xSR	= {0xC, 0x118, 0x124, 0x12C},
6664f0b776eSPeng Fan 	.xCR	= {0x110, 0x114, 0x120, 0x128},
6674f0b776eSPeng Fan };
6684f0b776eSPeng Fan 
66997961f78SPeng Fan static const struct imx_mu_dcfg imx_mu_cfg_imx8ulp_s4 = {
67097961f78SPeng Fan 	.tx	= imx_mu_specific_tx,
67197961f78SPeng Fan 	.rx	= imx_mu_specific_rx,
67297961f78SPeng Fan 	.init	= imx_mu_init_specific,
67397961f78SPeng Fan 	.type	= IMX_MU_V2 | IMX_MU_V2_S4,
67497961f78SPeng Fan 	.xTR	= 0x200,
67597961f78SPeng Fan 	.xRR	= 0x280,
67697961f78SPeng Fan 	.xSR	= {0xC, 0x118, 0x124, 0x12C},
67797961f78SPeng Fan 	.xCR	= {0x110, 0x114, 0x120, 0x128},
67897961f78SPeng Fan };
67997961f78SPeng Fan 
6800a67003bSPeng Fan static const struct imx_mu_dcfg imx_mu_cfg_imx8_scu = {
68197961f78SPeng Fan 	.tx	= imx_mu_specific_tx,
68297961f78SPeng Fan 	.rx	= imx_mu_specific_rx,
68397961f78SPeng Fan 	.init	= imx_mu_init_specific,
6844f0b776eSPeng Fan 	.xTR	= 0x0,
6854f0b776eSPeng Fan 	.xRR	= 0x10,
686f689a7cfSPeng Fan 	.xSR	= {0x20, 0x20, 0x20, 0x20},
687f689a7cfSPeng Fan 	.xCR	= {0x24, 0x24, 0x24, 0x24},
6880a67003bSPeng Fan };
6890a67003bSPeng Fan 
6902bb70056SOleksij Rempel static const struct of_device_id imx_mu_dt_ids[] = {
691c6c6bc6eSRichard Zhu 	{ .compatible = "fsl,imx7ulp-mu", .data = &imx_mu_cfg_imx7ulp },
692c6c6bc6eSRichard Zhu 	{ .compatible = "fsl,imx6sx-mu", .data = &imx_mu_cfg_imx6sx },
6934f0b776eSPeng Fan 	{ .compatible = "fsl,imx8ulp-mu", .data = &imx_mu_cfg_imx8ulp },
69497961f78SPeng Fan 	{ .compatible = "fsl,imx8ulp-mu-s4", .data = &imx_mu_cfg_imx8ulp_s4 },
6950a67003bSPeng Fan 	{ .compatible = "fsl,imx8-mu-scu", .data = &imx_mu_cfg_imx8_scu },
6962bb70056SOleksij Rempel 	{ },
6972bb70056SOleksij Rempel };
6982bb70056SOleksij Rempel MODULE_DEVICE_TABLE(of, imx_mu_dt_ids);
6992bb70056SOleksij Rempel 
70003b70130SNathan Chancellor static int __maybe_unused imx_mu_suspend_noirq(struct device *dev)
701ba5f9fa0SDong Aisheng {
702ba5f9fa0SDong Aisheng 	struct imx_mu_priv *priv = dev_get_drvdata(dev);
703f689a7cfSPeng Fan 	int i;
704ba5f9fa0SDong Aisheng 
705f689a7cfSPeng Fan 	if (!priv->clk) {
706f689a7cfSPeng Fan 		for (i = 0; i < IMX_MU_xCR_MAX; i++)
707f689a7cfSPeng Fan 			priv->xcr[i] = imx_mu_read(priv, priv->dcfg->xCR[i]);
708f689a7cfSPeng Fan 	}
709ba5f9fa0SDong Aisheng 
710892cb524SRobin Gong 	priv->suspend = true;
711892cb524SRobin Gong 
712ba5f9fa0SDong Aisheng 	return 0;
713ba5f9fa0SDong Aisheng }
714ba5f9fa0SDong Aisheng 
71503b70130SNathan Chancellor static int __maybe_unused imx_mu_resume_noirq(struct device *dev)
716ba5f9fa0SDong Aisheng {
717ba5f9fa0SDong Aisheng 	struct imx_mu_priv *priv = dev_get_drvdata(dev);
718f689a7cfSPeng Fan 	int i;
719ba5f9fa0SDong Aisheng 
720ba5f9fa0SDong Aisheng 	/*
721ba5f9fa0SDong Aisheng 	 * ONLY restore MU when context lost, the TIE could
722ba5f9fa0SDong Aisheng 	 * be set during noirq resume as there is MU data
723ba5f9fa0SDong Aisheng 	 * communication going on, and restore the saved
724ba5f9fa0SDong Aisheng 	 * value will overwrite the TIE and cause MU data
725ba5f9fa0SDong Aisheng 	 * send failed, may lead to system freeze. This issue
726ba5f9fa0SDong Aisheng 	 * is observed by testing freeze mode suspend.
727ba5f9fa0SDong Aisheng 	 */
728*8219efd0SRobin Gong 	if (!priv->clk && !imx_mu_read(priv, priv->dcfg->xCR[0])) {
729f689a7cfSPeng Fan 		for (i = 0; i < IMX_MU_xCR_MAX; i++)
730f689a7cfSPeng Fan 			imx_mu_write(priv, priv->xcr[i], priv->dcfg->xCR[i]);
731f689a7cfSPeng Fan 	}
732ba5f9fa0SDong Aisheng 
733892cb524SRobin Gong 	priv->suspend = false;
734892cb524SRobin Gong 
735ba5f9fa0SDong Aisheng 	return 0;
736ba5f9fa0SDong Aisheng }
737ba5f9fa0SDong Aisheng 
73803b70130SNathan Chancellor static int __maybe_unused imx_mu_runtime_suspend(struct device *dev)
739bb2b2624SAnson Huang {
740bb2b2624SAnson Huang 	struct imx_mu_priv *priv = dev_get_drvdata(dev);
741bb2b2624SAnson Huang 
742bb2b2624SAnson Huang 	clk_disable_unprepare(priv->clk);
743bb2b2624SAnson Huang 
744bb2b2624SAnson Huang 	return 0;
745bb2b2624SAnson Huang }
746bb2b2624SAnson Huang 
74703b70130SNathan Chancellor static int __maybe_unused imx_mu_runtime_resume(struct device *dev)
748bb2b2624SAnson Huang {
749bb2b2624SAnson Huang 	struct imx_mu_priv *priv = dev_get_drvdata(dev);
750bb2b2624SAnson Huang 	int ret;
751bb2b2624SAnson Huang 
752bb2b2624SAnson Huang 	ret = clk_prepare_enable(priv->clk);
753bb2b2624SAnson Huang 	if (ret)
754bb2b2624SAnson Huang 		dev_err(dev, "failed to enable clock\n");
755bb2b2624SAnson Huang 
756bb2b2624SAnson Huang 	return ret;
757bb2b2624SAnson Huang }
758bb2b2624SAnson Huang 
759ba5f9fa0SDong Aisheng static const struct dev_pm_ops imx_mu_pm_ops = {
760ba5f9fa0SDong Aisheng 	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(imx_mu_suspend_noirq,
761ba5f9fa0SDong Aisheng 				      imx_mu_resume_noirq)
762bb2b2624SAnson Huang 	SET_RUNTIME_PM_OPS(imx_mu_runtime_suspend,
763bb2b2624SAnson Huang 			   imx_mu_runtime_resume, NULL)
764ba5f9fa0SDong Aisheng };
765ba5f9fa0SDong Aisheng 
7662bb70056SOleksij Rempel static struct platform_driver imx_mu_driver = {
7672bb70056SOleksij Rempel 	.probe		= imx_mu_probe,
7682bb70056SOleksij Rempel 	.remove		= imx_mu_remove,
7692bb70056SOleksij Rempel 	.driver = {
7702bb70056SOleksij Rempel 		.name	= "imx_mu",
7712bb70056SOleksij Rempel 		.of_match_table = imx_mu_dt_ids,
772ba5f9fa0SDong Aisheng 		.pm = &imx_mu_pm_ops,
7732bb70056SOleksij Rempel 	},
7742bb70056SOleksij Rempel };
7752bb70056SOleksij Rempel module_platform_driver(imx_mu_driver);
7762bb70056SOleksij Rempel 
7772bb70056SOleksij Rempel MODULE_AUTHOR("Oleksij Rempel <o.rempel@pengutronix.de>");
7782bb70056SOleksij Rempel MODULE_DESCRIPTION("Message Unit driver for i.MX");
7792bb70056SOleksij Rempel MODULE_LICENSE("GPL v2");
780