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_xSR_GIPn(x) BIT(28 + (3 - (x))) 192bb70056SOleksij Rempel #define IMX_MU_xSR_RFn(x) BIT(24 + (3 - (x))) 202bb70056SOleksij Rempel #define IMX_MU_xSR_TEn(x) BIT(20 + (3 - (x))) 212bb70056SOleksij Rempel #define IMX_MU_xSR_BRDIP BIT(9) 222bb70056SOleksij Rempel 232bb70056SOleksij Rempel /* General Purpose Interrupt Enable */ 242bb70056SOleksij Rempel #define IMX_MU_xCR_GIEn(x) BIT(28 + (3 - (x))) 252bb70056SOleksij Rempel /* Receive Interrupt Enable */ 262bb70056SOleksij Rempel #define IMX_MU_xCR_RIEn(x) BIT(24 + (3 - (x))) 272bb70056SOleksij Rempel /* Transmit Interrupt Enable */ 282bb70056SOleksij Rempel #define IMX_MU_xCR_TIEn(x) BIT(20 + (3 - (x))) 292bb70056SOleksij Rempel /* General Purpose Interrupt Request */ 302bb70056SOleksij Rempel #define IMX_MU_xCR_GIRn(x) BIT(16 + (3 - (x))) 312bb70056SOleksij Rempel 322bb70056SOleksij Rempel #define IMX_MU_CHANS 16 330a67003bSPeng Fan /* TX0/RX0/RXDB[0-3] */ 340a67003bSPeng Fan #define IMX_MU_SCU_CHANS 6 352bb70056SOleksij Rempel #define IMX_MU_CHAN_NAME_SIZE 20 362bb70056SOleksij Rempel 372bb70056SOleksij Rempel enum imx_mu_chan_type { 382bb70056SOleksij Rempel IMX_MU_TYPE_TX, /* Tx */ 392bb70056SOleksij Rempel IMX_MU_TYPE_RX, /* Rx */ 402bb70056SOleksij Rempel IMX_MU_TYPE_TXDB, /* Tx doorbell */ 412bb70056SOleksij Rempel IMX_MU_TYPE_RXDB, /* Rx doorbell */ 422bb70056SOleksij Rempel }; 432bb70056SOleksij Rempel 440a67003bSPeng Fan struct imx_sc_rpc_msg_max { 450a67003bSPeng Fan struct imx_sc_rpc_msg hdr; 460a67003bSPeng Fan u32 data[7]; 470a67003bSPeng Fan }; 480a67003bSPeng Fan 492bb70056SOleksij Rempel struct imx_mu_con_priv { 502bb70056SOleksij Rempel unsigned int idx; 512bb70056SOleksij Rempel char irq_desc[IMX_MU_CHAN_NAME_SIZE]; 522bb70056SOleksij Rempel enum imx_mu_chan_type type; 532bb70056SOleksij Rempel struct mbox_chan *chan; 542bb70056SOleksij Rempel struct tasklet_struct txdb_tasklet; 552bb70056SOleksij Rempel }; 562bb70056SOleksij Rempel 572bb70056SOleksij Rempel struct imx_mu_priv { 582bb70056SOleksij Rempel struct device *dev; 592bb70056SOleksij Rempel void __iomem *base; 602bb70056SOleksij Rempel spinlock_t xcr_lock; /* control register lock */ 612bb70056SOleksij Rempel 622bb70056SOleksij Rempel struct mbox_controller mbox; 632bb70056SOleksij Rempel struct mbox_chan mbox_chans[IMX_MU_CHANS]; 642bb70056SOleksij Rempel 652bb70056SOleksij Rempel struct imx_mu_con_priv con_priv[IMX_MU_CHANS]; 66c6c6bc6eSRichard Zhu const struct imx_mu_dcfg *dcfg; 672bb70056SOleksij Rempel struct clk *clk; 682bb70056SOleksij Rempel int irq; 692bb70056SOleksij Rempel 70ba5f9fa0SDong Aisheng u32 xcr; 71ba5f9fa0SDong Aisheng 722bb70056SOleksij Rempel bool side_b; 732bb70056SOleksij Rempel }; 742bb70056SOleksij Rempel 7563b38357SPeng Fan struct imx_mu_dcfg { 7663b38357SPeng Fan int (*tx)(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp, void *data); 7763b38357SPeng Fan int (*rx)(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp); 7863b38357SPeng Fan void (*init)(struct imx_mu_priv *priv); 7963b38357SPeng Fan u32 xTR[4]; /* Transmit Registers */ 8063b38357SPeng Fan u32 xRR[4]; /* Receive Registers */ 8163b38357SPeng Fan u32 xSR; /* Status Register */ 8263b38357SPeng Fan u32 xCR; /* Control Register */ 83c6c6bc6eSRichard Zhu }; 84c6c6bc6eSRichard Zhu 852bb70056SOleksij Rempel static struct imx_mu_priv *to_imx_mu_priv(struct mbox_controller *mbox) 862bb70056SOleksij Rempel { 872bb70056SOleksij Rempel return container_of(mbox, struct imx_mu_priv, mbox); 882bb70056SOleksij Rempel } 892bb70056SOleksij Rempel 902bb70056SOleksij Rempel static void imx_mu_write(struct imx_mu_priv *priv, u32 val, u32 offs) 912bb70056SOleksij Rempel { 922bb70056SOleksij Rempel iowrite32(val, priv->base + offs); 932bb70056SOleksij Rempel } 942bb70056SOleksij Rempel 952bb70056SOleksij Rempel static u32 imx_mu_read(struct imx_mu_priv *priv, u32 offs) 962bb70056SOleksij Rempel { 972bb70056SOleksij Rempel return ioread32(priv->base + offs); 982bb70056SOleksij Rempel } 992bb70056SOleksij Rempel 1002bb70056SOleksij Rempel static u32 imx_mu_xcr_rmw(struct imx_mu_priv *priv, u32 set, u32 clr) 1012bb70056SOleksij Rempel { 1022bb70056SOleksij Rempel unsigned long flags; 1032bb70056SOleksij Rempel u32 val; 1042bb70056SOleksij Rempel 1052bb70056SOleksij Rempel spin_lock_irqsave(&priv->xcr_lock, flags); 106c6c6bc6eSRichard Zhu val = imx_mu_read(priv, priv->dcfg->xCR); 1072bb70056SOleksij Rempel val &= ~clr; 1082bb70056SOleksij Rempel val |= set; 109c6c6bc6eSRichard Zhu imx_mu_write(priv, val, priv->dcfg->xCR); 1102bb70056SOleksij Rempel spin_unlock_irqrestore(&priv->xcr_lock, flags); 1112bb70056SOleksij Rempel 1122bb70056SOleksij Rempel return val; 1132bb70056SOleksij Rempel } 1142bb70056SOleksij Rempel 11563b38357SPeng Fan static int imx_mu_generic_tx(struct imx_mu_priv *priv, 11663b38357SPeng Fan struct imx_mu_con_priv *cp, 11763b38357SPeng Fan void *data) 11863b38357SPeng Fan { 11963b38357SPeng Fan u32 *arg = data; 12063b38357SPeng Fan 12163b38357SPeng Fan switch (cp->type) { 12263b38357SPeng Fan case IMX_MU_TYPE_TX: 12363b38357SPeng Fan imx_mu_write(priv, *arg, priv->dcfg->xTR[cp->idx]); 12463b38357SPeng Fan imx_mu_xcr_rmw(priv, IMX_MU_xCR_TIEn(cp->idx), 0); 12563b38357SPeng Fan break; 12663b38357SPeng Fan case IMX_MU_TYPE_TXDB: 12763b38357SPeng Fan imx_mu_xcr_rmw(priv, IMX_MU_xCR_GIRn(cp->idx), 0); 12863b38357SPeng Fan tasklet_schedule(&cp->txdb_tasklet); 12963b38357SPeng Fan break; 13063b38357SPeng Fan default: 13163b38357SPeng Fan dev_warn_ratelimited(priv->dev, "Send data on wrong channel type: %d\n", cp->type); 13263b38357SPeng Fan return -EINVAL; 13363b38357SPeng Fan } 13463b38357SPeng Fan 13563b38357SPeng Fan return 0; 13663b38357SPeng Fan } 13763b38357SPeng Fan 13863b38357SPeng Fan static int imx_mu_generic_rx(struct imx_mu_priv *priv, 13963b38357SPeng Fan struct imx_mu_con_priv *cp) 14063b38357SPeng Fan { 14163b38357SPeng Fan u32 dat; 14263b38357SPeng Fan 14363b38357SPeng Fan dat = imx_mu_read(priv, priv->dcfg->xRR[cp->idx]); 14463b38357SPeng Fan mbox_chan_received_data(cp->chan, (void *)&dat); 14563b38357SPeng Fan 14663b38357SPeng Fan return 0; 14763b38357SPeng Fan } 14863b38357SPeng Fan 1490a67003bSPeng Fan static int imx_mu_scu_tx(struct imx_mu_priv *priv, 1500a67003bSPeng Fan struct imx_mu_con_priv *cp, 1510a67003bSPeng Fan void *data) 1520a67003bSPeng Fan { 1530a67003bSPeng Fan struct imx_sc_rpc_msg_max *msg = data; 1540a67003bSPeng Fan u32 *arg = data; 1550a67003bSPeng Fan int i, ret; 1560a67003bSPeng Fan u32 xsr; 1570a67003bSPeng Fan 1580a67003bSPeng Fan switch (cp->type) { 1590a67003bSPeng Fan case IMX_MU_TYPE_TX: 1609d8ca628SPeng Fan /* 1619d8ca628SPeng Fan * msg->hdr.size specifies the number of u32 words while 1629d8ca628SPeng Fan * sizeof yields bytes. 1639d8ca628SPeng Fan */ 1649d8ca628SPeng Fan 1659d8ca628SPeng Fan if (msg->hdr.size > sizeof(*msg) / 4) { 1660a67003bSPeng Fan /* 1670a67003bSPeng Fan * The real message size can be different to 1680a67003bSPeng Fan * struct imx_sc_rpc_msg_max size 1690a67003bSPeng Fan */ 1709d8ca628SPeng Fan dev_err(priv->dev, "Maximal message size (%zu bytes) exceeded on TX; got: %i bytes\n", sizeof(*msg), msg->hdr.size << 2); 1710a67003bSPeng Fan return -EINVAL; 1720a67003bSPeng Fan } 1730a67003bSPeng Fan 1740a67003bSPeng Fan for (i = 0; i < 4 && i < msg->hdr.size; i++) 1750a67003bSPeng Fan imx_mu_write(priv, *arg++, priv->dcfg->xTR[i % 4]); 1760a67003bSPeng Fan for (; i < msg->hdr.size; i++) { 1770a67003bSPeng Fan ret = readl_poll_timeout(priv->base + priv->dcfg->xSR, 1780a67003bSPeng Fan xsr, 1790a67003bSPeng Fan xsr & IMX_MU_xSR_TEn(i % 4), 1800a67003bSPeng Fan 0, 100); 1810a67003bSPeng Fan if (ret) { 1820a67003bSPeng Fan dev_err(priv->dev, "Send data index: %d timeout\n", i); 1830a67003bSPeng Fan return ret; 1840a67003bSPeng Fan } 1850a67003bSPeng Fan imx_mu_write(priv, *arg++, priv->dcfg->xTR[i % 4]); 1860a67003bSPeng Fan } 1870a67003bSPeng Fan 1880a67003bSPeng Fan imx_mu_xcr_rmw(priv, IMX_MU_xCR_TIEn(cp->idx), 0); 1890a67003bSPeng Fan break; 1900a67003bSPeng Fan default: 1910a67003bSPeng Fan dev_warn_ratelimited(priv->dev, "Send data on wrong channel type: %d\n", cp->type); 1920a67003bSPeng Fan return -EINVAL; 1930a67003bSPeng Fan } 1940a67003bSPeng Fan 1950a67003bSPeng Fan return 0; 1960a67003bSPeng Fan } 1970a67003bSPeng Fan 1980a67003bSPeng Fan static int imx_mu_scu_rx(struct imx_mu_priv *priv, 1990a67003bSPeng Fan struct imx_mu_con_priv *cp) 2000a67003bSPeng Fan { 2010a67003bSPeng Fan struct imx_sc_rpc_msg_max msg; 2020a67003bSPeng Fan u32 *data = (u32 *)&msg; 2030a67003bSPeng Fan int i, ret; 2040a67003bSPeng Fan u32 xsr; 2050a67003bSPeng Fan 2060a67003bSPeng Fan imx_mu_xcr_rmw(priv, 0, IMX_MU_xCR_RIEn(0)); 2070a67003bSPeng Fan *data++ = imx_mu_read(priv, priv->dcfg->xRR[0]); 2080a67003bSPeng Fan 2099d8ca628SPeng Fan if (msg.hdr.size > sizeof(msg) / 4) { 2109d8ca628SPeng Fan dev_err(priv->dev, "Maximal message size (%zu bytes) exceeded on RX; got: %i bytes\n", sizeof(msg), msg.hdr.size << 2); 2110a67003bSPeng Fan return -EINVAL; 2120a67003bSPeng Fan } 2130a67003bSPeng Fan 2140a67003bSPeng Fan for (i = 1; i < msg.hdr.size; i++) { 2150a67003bSPeng Fan ret = readl_poll_timeout(priv->base + priv->dcfg->xSR, xsr, 2160a67003bSPeng Fan xsr & IMX_MU_xSR_RFn(i % 4), 0, 100); 2170a67003bSPeng Fan if (ret) { 2180a67003bSPeng Fan dev_err(priv->dev, "timeout read idx %d\n", i); 2190a67003bSPeng Fan return ret; 2200a67003bSPeng Fan } 2210a67003bSPeng Fan *data++ = imx_mu_read(priv, priv->dcfg->xRR[i % 4]); 2220a67003bSPeng Fan } 2230a67003bSPeng Fan 2240a67003bSPeng Fan imx_mu_xcr_rmw(priv, IMX_MU_xCR_RIEn(0), 0); 2250a67003bSPeng Fan mbox_chan_received_data(cp->chan, (void *)&msg); 2260a67003bSPeng Fan 2270a67003bSPeng Fan return 0; 2280a67003bSPeng Fan } 2290a67003bSPeng Fan 2302bb70056SOleksij Rempel static void imx_mu_txdb_tasklet(unsigned long data) 2312bb70056SOleksij Rempel { 2322bb70056SOleksij Rempel struct imx_mu_con_priv *cp = (struct imx_mu_con_priv *)data; 2332bb70056SOleksij Rempel 2342bb70056SOleksij Rempel mbox_chan_txdone(cp->chan, 0); 2352bb70056SOleksij Rempel } 2362bb70056SOleksij Rempel 2372bb70056SOleksij Rempel static irqreturn_t imx_mu_isr(int irq, void *p) 2382bb70056SOleksij Rempel { 2392bb70056SOleksij Rempel struct mbox_chan *chan = p; 2402bb70056SOleksij Rempel struct imx_mu_priv *priv = to_imx_mu_priv(chan->mbox); 2412bb70056SOleksij Rempel struct imx_mu_con_priv *cp = chan->con_priv; 24263b38357SPeng Fan u32 val, ctrl; 2432bb70056SOleksij Rempel 244c6c6bc6eSRichard Zhu ctrl = imx_mu_read(priv, priv->dcfg->xCR); 245c6c6bc6eSRichard Zhu val = imx_mu_read(priv, priv->dcfg->xSR); 2462bb70056SOleksij Rempel 2472bb70056SOleksij Rempel switch (cp->type) { 2482bb70056SOleksij Rempel case IMX_MU_TYPE_TX: 2492bb70056SOleksij Rempel val &= IMX_MU_xSR_TEn(cp->idx) & 2502bb70056SOleksij Rempel (ctrl & IMX_MU_xCR_TIEn(cp->idx)); 2512bb70056SOleksij Rempel break; 2522bb70056SOleksij Rempel case IMX_MU_TYPE_RX: 2532bb70056SOleksij Rempel val &= IMX_MU_xSR_RFn(cp->idx) & 2542bb70056SOleksij Rempel (ctrl & IMX_MU_xCR_RIEn(cp->idx)); 2552bb70056SOleksij Rempel break; 2562bb70056SOleksij Rempel case IMX_MU_TYPE_RXDB: 2572bb70056SOleksij Rempel val &= IMX_MU_xSR_GIPn(cp->idx) & 2582bb70056SOleksij Rempel (ctrl & IMX_MU_xCR_GIEn(cp->idx)); 2592bb70056SOleksij Rempel break; 2602bb70056SOleksij Rempel default: 2612bb70056SOleksij Rempel break; 2622bb70056SOleksij Rempel } 2632bb70056SOleksij Rempel 2642bb70056SOleksij Rempel if (!val) 2652bb70056SOleksij Rempel return IRQ_NONE; 2662bb70056SOleksij Rempel 2672bb70056SOleksij Rempel if (val == IMX_MU_xSR_TEn(cp->idx)) { 2682bb70056SOleksij Rempel imx_mu_xcr_rmw(priv, 0, IMX_MU_xCR_TIEn(cp->idx)); 2692bb70056SOleksij Rempel mbox_chan_txdone(chan, 0); 2702bb70056SOleksij Rempel } else if (val == IMX_MU_xSR_RFn(cp->idx)) { 27163b38357SPeng Fan priv->dcfg->rx(priv, cp); 2722bb70056SOleksij Rempel } else if (val == IMX_MU_xSR_GIPn(cp->idx)) { 273c6c6bc6eSRichard Zhu imx_mu_write(priv, IMX_MU_xSR_GIPn(cp->idx), priv->dcfg->xSR); 2742bb70056SOleksij Rempel mbox_chan_received_data(chan, NULL); 2752bb70056SOleksij Rempel } else { 2762bb70056SOleksij Rempel dev_warn_ratelimited(priv->dev, "Not handled interrupt\n"); 2772bb70056SOleksij Rempel return IRQ_NONE; 2782bb70056SOleksij Rempel } 2792bb70056SOleksij Rempel 2802bb70056SOleksij Rempel return IRQ_HANDLED; 2812bb70056SOleksij Rempel } 2822bb70056SOleksij Rempel 2832bb70056SOleksij Rempel static int imx_mu_send_data(struct mbox_chan *chan, void *data) 2842bb70056SOleksij Rempel { 2852bb70056SOleksij Rempel struct imx_mu_priv *priv = to_imx_mu_priv(chan->mbox); 2862bb70056SOleksij Rempel struct imx_mu_con_priv *cp = chan->con_priv; 2872bb70056SOleksij Rempel 28863b38357SPeng Fan return priv->dcfg->tx(priv, cp, data); 2892bb70056SOleksij Rempel } 2902bb70056SOleksij Rempel 2912bb70056SOleksij Rempel static int imx_mu_startup(struct mbox_chan *chan) 2922bb70056SOleksij Rempel { 2932bb70056SOleksij Rempel struct imx_mu_priv *priv = to_imx_mu_priv(chan->mbox); 2942bb70056SOleksij Rempel struct imx_mu_con_priv *cp = chan->con_priv; 2952bb70056SOleksij Rempel int ret; 2962bb70056SOleksij Rempel 297676f23eaSAnson Huang pm_runtime_get_sync(priv->dev); 2982bb70056SOleksij Rempel if (cp->type == IMX_MU_TYPE_TXDB) { 2992bb70056SOleksij Rempel /* Tx doorbell don't have ACK support */ 3002bb70056SOleksij Rempel tasklet_init(&cp->txdb_tasklet, imx_mu_txdb_tasklet, 3012bb70056SOleksij Rempel (unsigned long)cp); 3022bb70056SOleksij Rempel return 0; 3032bb70056SOleksij Rempel } 3042bb70056SOleksij Rempel 30517b860bbSAnson Huang ret = request_irq(priv->irq, imx_mu_isr, IRQF_SHARED | 30617b860bbSAnson Huang IRQF_NO_SUSPEND, cp->irq_desc, chan); 3072bb70056SOleksij Rempel if (ret) { 3082bb70056SOleksij Rempel dev_err(priv->dev, 3092bb70056SOleksij Rempel "Unable to acquire IRQ %d\n", priv->irq); 3102bb70056SOleksij Rempel return ret; 3112bb70056SOleksij Rempel } 3122bb70056SOleksij Rempel 3132bb70056SOleksij Rempel switch (cp->type) { 3142bb70056SOleksij Rempel case IMX_MU_TYPE_RX: 3152bb70056SOleksij Rempel imx_mu_xcr_rmw(priv, IMX_MU_xCR_RIEn(cp->idx), 0); 3162bb70056SOleksij Rempel break; 3172bb70056SOleksij Rempel case IMX_MU_TYPE_RXDB: 3182bb70056SOleksij Rempel imx_mu_xcr_rmw(priv, IMX_MU_xCR_GIEn(cp->idx), 0); 3192bb70056SOleksij Rempel break; 3202bb70056SOleksij Rempel default: 3212bb70056SOleksij Rempel break; 3222bb70056SOleksij Rempel } 3232bb70056SOleksij Rempel 3242bb70056SOleksij Rempel return 0; 3252bb70056SOleksij Rempel } 3262bb70056SOleksij Rempel 3272bb70056SOleksij Rempel static void imx_mu_shutdown(struct mbox_chan *chan) 3282bb70056SOleksij Rempel { 3292bb70056SOleksij Rempel struct imx_mu_priv *priv = to_imx_mu_priv(chan->mbox); 3302bb70056SOleksij Rempel struct imx_mu_con_priv *cp = chan->con_priv; 3312bb70056SOleksij Rempel 332bf159d15SDaniel Baluta if (cp->type == IMX_MU_TYPE_TXDB) { 3332bb70056SOleksij Rempel tasklet_kill(&cp->txdb_tasklet); 334676f23eaSAnson Huang pm_runtime_put_sync(priv->dev); 335bf159d15SDaniel Baluta return; 336bf159d15SDaniel Baluta } 3372bb70056SOleksij Rempel 3385f0af07eSDaniel Baluta switch (cp->type) { 3395f0af07eSDaniel Baluta case IMX_MU_TYPE_TX: 3405f0af07eSDaniel Baluta imx_mu_xcr_rmw(priv, 0, IMX_MU_xCR_TIEn(cp->idx)); 3415f0af07eSDaniel Baluta break; 3425f0af07eSDaniel Baluta case IMX_MU_TYPE_RX: 3435f0af07eSDaniel Baluta imx_mu_xcr_rmw(priv, 0, IMX_MU_xCR_RIEn(cp->idx)); 3445f0af07eSDaniel Baluta break; 3455f0af07eSDaniel Baluta case IMX_MU_TYPE_RXDB: 3465f0af07eSDaniel Baluta imx_mu_xcr_rmw(priv, 0, IMX_MU_xCR_GIEn(cp->idx)); 3475f0af07eSDaniel Baluta break; 3485f0af07eSDaniel Baluta default: 3495f0af07eSDaniel Baluta break; 3505f0af07eSDaniel Baluta } 3512bb70056SOleksij Rempel 3522bb70056SOleksij Rempel free_irq(priv->irq, chan); 353676f23eaSAnson Huang pm_runtime_put_sync(priv->dev); 3542bb70056SOleksij Rempel } 3552bb70056SOleksij Rempel 3562bb70056SOleksij Rempel static const struct mbox_chan_ops imx_mu_ops = { 3572bb70056SOleksij Rempel .send_data = imx_mu_send_data, 3582bb70056SOleksij Rempel .startup = imx_mu_startup, 3592bb70056SOleksij Rempel .shutdown = imx_mu_shutdown, 3602bb70056SOleksij Rempel }; 3612bb70056SOleksij Rempel 3620a67003bSPeng Fan static struct mbox_chan *imx_mu_scu_xlate(struct mbox_controller *mbox, 3630a67003bSPeng Fan const struct of_phandle_args *sp) 3640a67003bSPeng Fan { 3650a67003bSPeng Fan u32 type, idx, chan; 3660a67003bSPeng Fan 3670a67003bSPeng Fan if (sp->args_count != 2) { 3680a67003bSPeng Fan dev_err(mbox->dev, "Invalid argument count %d\n", sp->args_count); 3690a67003bSPeng Fan return ERR_PTR(-EINVAL); 3700a67003bSPeng Fan } 3710a67003bSPeng Fan 3720a67003bSPeng Fan type = sp->args[0]; /* channel type */ 3730a67003bSPeng Fan idx = sp->args[1]; /* index */ 3740a67003bSPeng Fan 3750a67003bSPeng Fan switch (type) { 3760a67003bSPeng Fan case IMX_MU_TYPE_TX: 3770a67003bSPeng Fan case IMX_MU_TYPE_RX: 3780a67003bSPeng Fan if (idx != 0) 3790a67003bSPeng Fan dev_err(mbox->dev, "Invalid chan idx: %d\n", idx); 3800a67003bSPeng Fan chan = type; 3810a67003bSPeng Fan break; 3820a67003bSPeng Fan case IMX_MU_TYPE_RXDB: 3830a67003bSPeng Fan chan = 2 + idx; 3840a67003bSPeng Fan break; 3850a67003bSPeng Fan default: 3860a67003bSPeng Fan dev_err(mbox->dev, "Invalid chan type: %d\n", type); 3871b3a347bSDan Carpenter return ERR_PTR(-EINVAL); 3880a67003bSPeng Fan } 3890a67003bSPeng Fan 3900a67003bSPeng Fan if (chan >= mbox->num_chans) { 3910a67003bSPeng Fan dev_err(mbox->dev, "Not supported channel number: %d. (type: %d, idx: %d)\n", chan, type, idx); 3920a67003bSPeng Fan return ERR_PTR(-EINVAL); 3930a67003bSPeng Fan } 3940a67003bSPeng Fan 3950a67003bSPeng Fan return &mbox->chans[chan]; 3960a67003bSPeng Fan } 3970a67003bSPeng Fan 3982bb70056SOleksij Rempel static struct mbox_chan * imx_mu_xlate(struct mbox_controller *mbox, 3992bb70056SOleksij Rempel const struct of_phandle_args *sp) 4002bb70056SOleksij Rempel { 4012bb70056SOleksij Rempel u32 type, idx, chan; 4022bb70056SOleksij Rempel 4032bb70056SOleksij Rempel if (sp->args_count != 2) { 4042bb70056SOleksij Rempel dev_err(mbox->dev, "Invalid argument count %d\n", sp->args_count); 4052bb70056SOleksij Rempel return ERR_PTR(-EINVAL); 4062bb70056SOleksij Rempel } 4072bb70056SOleksij Rempel 4082bb70056SOleksij Rempel type = sp->args[0]; /* channel type */ 4092bb70056SOleksij Rempel idx = sp->args[1]; /* index */ 4102bb70056SOleksij Rempel chan = type * 4 + idx; 4112bb70056SOleksij Rempel 4122bb70056SOleksij Rempel if (chan >= mbox->num_chans) { 4132bb70056SOleksij Rempel dev_err(mbox->dev, "Not supported channel number: %d. (type: %d, idx: %d)\n", chan, type, idx); 4142bb70056SOleksij Rempel return ERR_PTR(-EINVAL); 4152bb70056SOleksij Rempel } 4162bb70056SOleksij Rempel 4172bb70056SOleksij Rempel return &mbox->chans[chan]; 4182bb70056SOleksij Rempel } 4192bb70056SOleksij Rempel 4202bb70056SOleksij Rempel static void imx_mu_init_generic(struct imx_mu_priv *priv) 4212bb70056SOleksij Rempel { 42263b38357SPeng Fan unsigned int i; 42363b38357SPeng Fan 42463b38357SPeng Fan for (i = 0; i < IMX_MU_CHANS; i++) { 42563b38357SPeng Fan struct imx_mu_con_priv *cp = &priv->con_priv[i]; 42663b38357SPeng Fan 42763b38357SPeng Fan cp->idx = i % 4; 42863b38357SPeng Fan cp->type = i >> 2; 42963b38357SPeng Fan cp->chan = &priv->mbox_chans[i]; 43063b38357SPeng Fan priv->mbox_chans[i].con_priv = cp; 43163b38357SPeng Fan snprintf(cp->irq_desc, sizeof(cp->irq_desc), 43263b38357SPeng Fan "imx_mu_chan[%i-%i]", cp->type, cp->idx); 43363b38357SPeng Fan } 43463b38357SPeng Fan 43563b38357SPeng Fan priv->mbox.num_chans = IMX_MU_CHANS; 43663b38357SPeng Fan priv->mbox.of_xlate = imx_mu_xlate; 43763b38357SPeng Fan 4382bb70056SOleksij Rempel if (priv->side_b) 4392bb70056SOleksij Rempel return; 4402bb70056SOleksij Rempel 4412bb70056SOleksij Rempel /* Set default MU configuration */ 442c6c6bc6eSRichard Zhu imx_mu_write(priv, 0, priv->dcfg->xCR); 4432bb70056SOleksij Rempel } 4442bb70056SOleksij Rempel 4450a67003bSPeng Fan static void imx_mu_init_scu(struct imx_mu_priv *priv) 4460a67003bSPeng Fan { 4470a67003bSPeng Fan unsigned int i; 4480a67003bSPeng Fan 4490a67003bSPeng Fan for (i = 0; i < IMX_MU_SCU_CHANS; i++) { 4500a67003bSPeng Fan struct imx_mu_con_priv *cp = &priv->con_priv[i]; 4510a67003bSPeng Fan 4520a67003bSPeng Fan cp->idx = i < 2 ? 0 : i - 2; 4530a67003bSPeng Fan cp->type = i < 2 ? i : IMX_MU_TYPE_RXDB; 4540a67003bSPeng Fan cp->chan = &priv->mbox_chans[i]; 4550a67003bSPeng Fan priv->mbox_chans[i].con_priv = cp; 4560a67003bSPeng Fan snprintf(cp->irq_desc, sizeof(cp->irq_desc), 4570a67003bSPeng Fan "imx_mu_chan[%i-%i]", cp->type, cp->idx); 4580a67003bSPeng Fan } 4590a67003bSPeng Fan 4600a67003bSPeng Fan priv->mbox.num_chans = IMX_MU_SCU_CHANS; 4610a67003bSPeng Fan priv->mbox.of_xlate = imx_mu_scu_xlate; 4620a67003bSPeng Fan 4630a67003bSPeng Fan /* Set default MU configuration */ 4640a67003bSPeng Fan imx_mu_write(priv, 0, priv->dcfg->xCR); 4650a67003bSPeng Fan } 4660a67003bSPeng Fan 4672bb70056SOleksij Rempel static int imx_mu_probe(struct platform_device *pdev) 4682bb70056SOleksij Rempel { 4692bb70056SOleksij Rempel struct device *dev = &pdev->dev; 4702bb70056SOleksij Rempel struct device_node *np = dev->of_node; 4712bb70056SOleksij Rempel struct imx_mu_priv *priv; 472c6c6bc6eSRichard Zhu const struct imx_mu_dcfg *dcfg; 4732bb70056SOleksij Rempel int ret; 4742bb70056SOleksij Rempel 4752bb70056SOleksij Rempel priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 4762bb70056SOleksij Rempel if (!priv) 4772bb70056SOleksij Rempel return -ENOMEM; 4782bb70056SOleksij Rempel 4792bb70056SOleksij Rempel priv->dev = dev; 4802bb70056SOleksij Rempel 4810c40e631SAnson Huang priv->base = devm_platform_ioremap_resource(pdev, 0); 4822bb70056SOleksij Rempel if (IS_ERR(priv->base)) 4832bb70056SOleksij Rempel return PTR_ERR(priv->base); 4842bb70056SOleksij Rempel 4852bb70056SOleksij Rempel priv->irq = platform_get_irq(pdev, 0); 4862bb70056SOleksij Rempel if (priv->irq < 0) 4872bb70056SOleksij Rempel return priv->irq; 4882bb70056SOleksij Rempel 489c6c6bc6eSRichard Zhu dcfg = of_device_get_match_data(dev); 490c6c6bc6eSRichard Zhu if (!dcfg) 491c6c6bc6eSRichard Zhu return -EINVAL; 492c6c6bc6eSRichard Zhu priv->dcfg = dcfg; 493c6c6bc6eSRichard Zhu 4942bb70056SOleksij Rempel priv->clk = devm_clk_get(dev, NULL); 4952bb70056SOleksij Rempel if (IS_ERR(priv->clk)) { 4962bb70056SOleksij Rempel if (PTR_ERR(priv->clk) != -ENOENT) 4972bb70056SOleksij Rempel return PTR_ERR(priv->clk); 4982bb70056SOleksij Rempel 4992bb70056SOleksij Rempel priv->clk = NULL; 5002bb70056SOleksij Rempel } 5012bb70056SOleksij Rempel 5022bb70056SOleksij Rempel ret = clk_prepare_enable(priv->clk); 5032bb70056SOleksij Rempel if (ret) { 5042bb70056SOleksij Rempel dev_err(dev, "Failed to enable clock\n"); 5052bb70056SOleksij Rempel return ret; 5062bb70056SOleksij Rempel } 5072bb70056SOleksij Rempel 5082bb70056SOleksij Rempel priv->side_b = of_property_read_bool(np, "fsl,mu-side-b"); 5092bb70056SOleksij Rempel 51063b38357SPeng Fan priv->dcfg->init(priv); 51163b38357SPeng Fan 5122bb70056SOleksij Rempel spin_lock_init(&priv->xcr_lock); 5132bb70056SOleksij Rempel 5142bb70056SOleksij Rempel priv->mbox.dev = dev; 5152bb70056SOleksij Rempel priv->mbox.ops = &imx_mu_ops; 5162bb70056SOleksij Rempel priv->mbox.chans = priv->mbox_chans; 5172bb70056SOleksij Rempel priv->mbox.txdone_irq = true; 5182bb70056SOleksij Rempel 5192bb70056SOleksij Rempel platform_set_drvdata(pdev, priv); 5202bb70056SOleksij Rempel 521676f23eaSAnson Huang ret = devm_mbox_controller_register(dev, &priv->mbox); 52247303f94SFabio Estevam if (ret) { 52347303f94SFabio Estevam clk_disable_unprepare(priv->clk); 524676f23eaSAnson Huang return ret; 52547303f94SFabio Estevam } 526676f23eaSAnson Huang 527676f23eaSAnson Huang pm_runtime_enable(dev); 528676f23eaSAnson Huang 529676f23eaSAnson Huang ret = pm_runtime_get_sync(dev); 530676f23eaSAnson Huang if (ret < 0) { 531676f23eaSAnson Huang pm_runtime_put_noidle(dev); 532676f23eaSAnson Huang goto disable_runtime_pm; 533676f23eaSAnson Huang } 534676f23eaSAnson Huang 535676f23eaSAnson Huang ret = pm_runtime_put_sync(dev); 536676f23eaSAnson Huang if (ret < 0) 537676f23eaSAnson Huang goto disable_runtime_pm; 538676f23eaSAnson Huang 539bb2b2624SAnson Huang clk_disable_unprepare(priv->clk); 540bb2b2624SAnson Huang 541676f23eaSAnson Huang return 0; 542676f23eaSAnson Huang 543676f23eaSAnson Huang disable_runtime_pm: 544676f23eaSAnson Huang pm_runtime_disable(dev); 545bb2b2624SAnson Huang clk_disable_unprepare(priv->clk); 546676f23eaSAnson Huang return ret; 5472bb70056SOleksij Rempel } 5482bb70056SOleksij Rempel 5492bb70056SOleksij Rempel static int imx_mu_remove(struct platform_device *pdev) 5502bb70056SOleksij Rempel { 5512bb70056SOleksij Rempel struct imx_mu_priv *priv = platform_get_drvdata(pdev); 5522bb70056SOleksij Rempel 553676f23eaSAnson Huang pm_runtime_disable(priv->dev); 5542bb70056SOleksij Rempel 5552bb70056SOleksij Rempel return 0; 5562bb70056SOleksij Rempel } 5572bb70056SOleksij Rempel 55863b38357SPeng Fan static const struct imx_mu_dcfg imx_mu_cfg_imx6sx = { 55963b38357SPeng Fan .tx = imx_mu_generic_tx, 56063b38357SPeng Fan .rx = imx_mu_generic_rx, 56163b38357SPeng Fan .init = imx_mu_init_generic, 56263b38357SPeng Fan .xTR = {0x0, 0x4, 0x8, 0xc}, 56363b38357SPeng Fan .xRR = {0x10, 0x14, 0x18, 0x1c}, 56463b38357SPeng Fan .xSR = 0x20, 56563b38357SPeng Fan .xCR = 0x24, 56663b38357SPeng Fan }; 56763b38357SPeng Fan 56863b38357SPeng Fan static const struct imx_mu_dcfg imx_mu_cfg_imx7ulp = { 56963b38357SPeng Fan .tx = imx_mu_generic_tx, 57063b38357SPeng Fan .rx = imx_mu_generic_rx, 57163b38357SPeng Fan .init = imx_mu_init_generic, 57263b38357SPeng Fan .xTR = {0x20, 0x24, 0x28, 0x2c}, 57363b38357SPeng Fan .xRR = {0x40, 0x44, 0x48, 0x4c}, 57463b38357SPeng Fan .xSR = 0x60, 57563b38357SPeng Fan .xCR = 0x64, 57663b38357SPeng Fan }; 57763b38357SPeng Fan 5780a67003bSPeng Fan static const struct imx_mu_dcfg imx_mu_cfg_imx8_scu = { 5790a67003bSPeng Fan .tx = imx_mu_scu_tx, 5800a67003bSPeng Fan .rx = imx_mu_scu_rx, 5810a67003bSPeng Fan .init = imx_mu_init_scu, 5820a67003bSPeng Fan .xTR = {0x0, 0x4, 0x8, 0xc}, 5830a67003bSPeng Fan .xRR = {0x10, 0x14, 0x18, 0x1c}, 5840a67003bSPeng Fan .xSR = 0x20, 5850a67003bSPeng Fan .xCR = 0x24, 5860a67003bSPeng Fan }; 5870a67003bSPeng Fan 5882bb70056SOleksij Rempel static const struct of_device_id imx_mu_dt_ids[] = { 589c6c6bc6eSRichard Zhu { .compatible = "fsl,imx7ulp-mu", .data = &imx_mu_cfg_imx7ulp }, 590c6c6bc6eSRichard Zhu { .compatible = "fsl,imx6sx-mu", .data = &imx_mu_cfg_imx6sx }, 5910a67003bSPeng Fan { .compatible = "fsl,imx8-mu-scu", .data = &imx_mu_cfg_imx8_scu }, 5922bb70056SOleksij Rempel { }, 5932bb70056SOleksij Rempel }; 5942bb70056SOleksij Rempel MODULE_DEVICE_TABLE(of, imx_mu_dt_ids); 5952bb70056SOleksij Rempel 596ba5f9fa0SDong Aisheng static int imx_mu_suspend_noirq(struct device *dev) 597ba5f9fa0SDong Aisheng { 598ba5f9fa0SDong Aisheng struct imx_mu_priv *priv = dev_get_drvdata(dev); 599ba5f9fa0SDong Aisheng 600bb2b2624SAnson Huang if (!priv->clk) 601ba5f9fa0SDong Aisheng priv->xcr = imx_mu_read(priv, priv->dcfg->xCR); 602ba5f9fa0SDong Aisheng 603ba5f9fa0SDong Aisheng return 0; 604ba5f9fa0SDong Aisheng } 605ba5f9fa0SDong Aisheng 606ba5f9fa0SDong Aisheng static int imx_mu_resume_noirq(struct device *dev) 607ba5f9fa0SDong Aisheng { 608ba5f9fa0SDong Aisheng struct imx_mu_priv *priv = dev_get_drvdata(dev); 609ba5f9fa0SDong Aisheng 610ba5f9fa0SDong Aisheng /* 611ba5f9fa0SDong Aisheng * ONLY restore MU when context lost, the TIE could 612ba5f9fa0SDong Aisheng * be set during noirq resume as there is MU data 613ba5f9fa0SDong Aisheng * communication going on, and restore the saved 614ba5f9fa0SDong Aisheng * value will overwrite the TIE and cause MU data 615ba5f9fa0SDong Aisheng * send failed, may lead to system freeze. This issue 616ba5f9fa0SDong Aisheng * is observed by testing freeze mode suspend. 617ba5f9fa0SDong Aisheng */ 618bb2b2624SAnson Huang if (!imx_mu_read(priv, priv->dcfg->xCR) && !priv->clk) 619ba5f9fa0SDong Aisheng imx_mu_write(priv, priv->xcr, priv->dcfg->xCR); 620ba5f9fa0SDong Aisheng 621ba5f9fa0SDong Aisheng return 0; 622ba5f9fa0SDong Aisheng } 623ba5f9fa0SDong Aisheng 624bb2b2624SAnson Huang static int imx_mu_runtime_suspend(struct device *dev) 625bb2b2624SAnson Huang { 626bb2b2624SAnson Huang struct imx_mu_priv *priv = dev_get_drvdata(dev); 627bb2b2624SAnson Huang 628bb2b2624SAnson Huang clk_disable_unprepare(priv->clk); 629bb2b2624SAnson Huang 630bb2b2624SAnson Huang return 0; 631bb2b2624SAnson Huang } 632bb2b2624SAnson Huang 633bb2b2624SAnson Huang static int imx_mu_runtime_resume(struct device *dev) 634bb2b2624SAnson Huang { 635bb2b2624SAnson Huang struct imx_mu_priv *priv = dev_get_drvdata(dev); 636bb2b2624SAnson Huang int ret; 637bb2b2624SAnson Huang 638bb2b2624SAnson Huang ret = clk_prepare_enable(priv->clk); 639bb2b2624SAnson Huang if (ret) 640bb2b2624SAnson Huang dev_err(dev, "failed to enable clock\n"); 641bb2b2624SAnson Huang 642bb2b2624SAnson Huang return ret; 643bb2b2624SAnson Huang } 644bb2b2624SAnson Huang 645ba5f9fa0SDong Aisheng static const struct dev_pm_ops imx_mu_pm_ops = { 646ba5f9fa0SDong Aisheng SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(imx_mu_suspend_noirq, 647ba5f9fa0SDong Aisheng imx_mu_resume_noirq) 648bb2b2624SAnson Huang SET_RUNTIME_PM_OPS(imx_mu_runtime_suspend, 649bb2b2624SAnson Huang imx_mu_runtime_resume, NULL) 650ba5f9fa0SDong Aisheng }; 651ba5f9fa0SDong Aisheng 6522bb70056SOleksij Rempel static struct platform_driver imx_mu_driver = { 6532bb70056SOleksij Rempel .probe = imx_mu_probe, 6542bb70056SOleksij Rempel .remove = imx_mu_remove, 6552bb70056SOleksij Rempel .driver = { 6562bb70056SOleksij Rempel .name = "imx_mu", 6572bb70056SOleksij Rempel .of_match_table = imx_mu_dt_ids, 658ba5f9fa0SDong Aisheng .pm = &imx_mu_pm_ops, 6592bb70056SOleksij Rempel }, 6602bb70056SOleksij Rempel }; 6612bb70056SOleksij Rempel module_platform_driver(imx_mu_driver); 6622bb70056SOleksij Rempel 6632bb70056SOleksij Rempel MODULE_AUTHOR("Oleksij Rempel <o.rempel@pengutronix.de>"); 6642bb70056SOleksij Rempel MODULE_DESCRIPTION("Message Unit driver for i.MX"); 6652bb70056SOleksij Rempel MODULE_LICENSE("GPL v2"); 666