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> 17*892cb524SRobin 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; 80*892cb524SRobin 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 339*892cb524SRobin Gong if (priv->suspend) 340*892cb524SRobin Gong pm_system_wakeup(); 341*892cb524SRobin 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 710*892cb524SRobin Gong priv->suspend = true; 711*892cb524SRobin 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 */ 728f689a7cfSPeng Fan if (!imx_mu_read(priv, priv->dcfg->xCR[0]) && !priv->clk) { 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 733*892cb524SRobin Gong priv->suspend = false; 734*892cb524SRobin 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