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