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> 12*11dac1d3SFranck LENORMAND #include <linux/jiffies.h> 132bb70056SOleksij Rempel #include <linux/kernel.h> 142bb70056SOleksij Rempel #include <linux/mailbox_controller.h> 152bb70056SOleksij Rempel #include <linux/module.h> 162bb70056SOleksij Rempel #include <linux/of_device.h> 17676f23eaSAnson Huang #include <linux/pm_runtime.h> 18892cb524SRobin Gong #include <linux/suspend.h> 192bb70056SOleksij Rempel #include <linux/slab.h> 202bb70056SOleksij Rempel 212bb70056SOleksij Rempel #define IMX_MU_CHANS 16 220a67003bSPeng Fan /* TX0/RX0/RXDB[0-3] */ 230a67003bSPeng Fan #define IMX_MU_SCU_CHANS 6 2497961f78SPeng Fan /* TX0/RX0 */ 2597961f78SPeng Fan #define IMX_MU_S4_CHANS 2 262bb70056SOleksij Rempel #define IMX_MU_CHAN_NAME_SIZE 20 272bb70056SOleksij Rempel 28*11dac1d3SFranck LENORMAND #define IMX_MU_SECO_TX_TOUT (msecs_to_jiffies(3000)) 29*11dac1d3SFranck LENORMAND #define IMX_MU_SECO_RX_TOUT (msecs_to_jiffies(3000)) 30*11dac1d3SFranck LENORMAND 312bb70056SOleksij Rempel enum imx_mu_chan_type { 322bb70056SOleksij Rempel IMX_MU_TYPE_TX, /* Tx */ 332bb70056SOleksij Rempel IMX_MU_TYPE_RX, /* Rx */ 342bb70056SOleksij Rempel IMX_MU_TYPE_TXDB, /* Tx doorbell */ 352bb70056SOleksij Rempel IMX_MU_TYPE_RXDB, /* Rx doorbell */ 362bb70056SOleksij Rempel }; 372bb70056SOleksij Rempel 38f689a7cfSPeng Fan enum imx_mu_xcr { 394f0b776eSPeng Fan IMX_MU_GIER, 40f689a7cfSPeng Fan IMX_MU_GCR, 41f689a7cfSPeng Fan IMX_MU_TCR, 42f689a7cfSPeng Fan IMX_MU_RCR, 43f689a7cfSPeng Fan IMX_MU_xCR_MAX, 44f689a7cfSPeng Fan }; 45f689a7cfSPeng Fan 46f689a7cfSPeng Fan enum imx_mu_xsr { 47f689a7cfSPeng Fan IMX_MU_SR, 48f689a7cfSPeng Fan IMX_MU_GSR, 49f689a7cfSPeng Fan IMX_MU_TSR, 50f689a7cfSPeng Fan IMX_MU_RSR, 51f689a7cfSPeng Fan }; 52f689a7cfSPeng Fan 530a67003bSPeng Fan struct imx_sc_rpc_msg_max { 540a67003bSPeng Fan struct imx_sc_rpc_msg hdr; 55*11dac1d3SFranck LENORMAND u32 data[30]; 560a67003bSPeng Fan }; 570a67003bSPeng Fan 5897961f78SPeng Fan struct imx_s4_rpc_msg_max { 5997961f78SPeng Fan struct imx_s4_rpc_msg hdr; 6097961f78SPeng Fan u32 data[254]; 6197961f78SPeng Fan }; 6297961f78SPeng Fan 632bb70056SOleksij Rempel struct imx_mu_con_priv { 642bb70056SOleksij Rempel unsigned int idx; 652bb70056SOleksij Rempel char irq_desc[IMX_MU_CHAN_NAME_SIZE]; 662bb70056SOleksij Rempel enum imx_mu_chan_type type; 672bb70056SOleksij Rempel struct mbox_chan *chan; 682bb70056SOleksij Rempel struct tasklet_struct txdb_tasklet; 692bb70056SOleksij Rempel }; 702bb70056SOleksij Rempel 712bb70056SOleksij Rempel struct imx_mu_priv { 722bb70056SOleksij Rempel struct device *dev; 732bb70056SOleksij Rempel void __iomem *base; 7497961f78SPeng Fan void *msg; 752bb70056SOleksij Rempel spinlock_t xcr_lock; /* control register lock */ 762bb70056SOleksij Rempel 772bb70056SOleksij Rempel struct mbox_controller mbox; 782bb70056SOleksij Rempel struct mbox_chan mbox_chans[IMX_MU_CHANS]; 792bb70056SOleksij Rempel 802bb70056SOleksij Rempel struct imx_mu_con_priv con_priv[IMX_MU_CHANS]; 81c6c6bc6eSRichard Zhu const struct imx_mu_dcfg *dcfg; 822bb70056SOleksij Rempel struct clk *clk; 832bb70056SOleksij Rempel int irq; 84892cb524SRobin Gong bool suspend; 852bb70056SOleksij Rempel 86f689a7cfSPeng Fan u32 xcr[4]; 87ba5f9fa0SDong Aisheng 882bb70056SOleksij Rempel bool side_b; 892bb70056SOleksij Rempel }; 902bb70056SOleksij Rempel 914f0b776eSPeng Fan enum imx_mu_type { 924f0b776eSPeng Fan IMX_MU_V1, 9397961f78SPeng Fan IMX_MU_V2 = BIT(1), 9497961f78SPeng Fan IMX_MU_V2_S4 = BIT(15), 954f0b776eSPeng Fan }; 964f0b776eSPeng Fan 9763b38357SPeng Fan struct imx_mu_dcfg { 9863b38357SPeng Fan int (*tx)(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp, void *data); 9963b38357SPeng Fan int (*rx)(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp); 100315d2e56SPeng Fan int (*rxdb)(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp); 10163b38357SPeng Fan void (*init)(struct imx_mu_priv *priv); 1024f0b776eSPeng Fan enum imx_mu_type type; 10332f7443dSPeng Fan u32 xTR; /* Transmit Register0 */ 10432f7443dSPeng Fan u32 xRR; /* Receive Register0 */ 105f689a7cfSPeng Fan u32 xSR[4]; /* Status Registers */ 106f689a7cfSPeng Fan u32 xCR[4]; /* Control Registers */ 107c6c6bc6eSRichard Zhu }; 108c6c6bc6eSRichard Zhu 10997961f78SPeng Fan #define IMX_MU_xSR_GIPn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(28 + (3 - (x)))) 11097961f78SPeng Fan #define IMX_MU_xSR_RFn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(24 + (3 - (x)))) 11197961f78SPeng Fan #define IMX_MU_xSR_TEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(20 + (3 - (x)))) 1124f0b776eSPeng Fan 1134f0b776eSPeng Fan /* General Purpose Interrupt Enable */ 11497961f78SPeng Fan #define IMX_MU_xCR_GIEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(28 + (3 - (x)))) 1154f0b776eSPeng Fan /* Receive Interrupt Enable */ 11697961f78SPeng Fan #define IMX_MU_xCR_RIEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(24 + (3 - (x)))) 1174f0b776eSPeng Fan /* Transmit Interrupt Enable */ 11897961f78SPeng Fan #define IMX_MU_xCR_TIEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(20 + (3 - (x)))) 1194f0b776eSPeng Fan /* General Purpose Interrupt Request */ 12097961f78SPeng Fan #define IMX_MU_xCR_GIRn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(16 + (3 - (x)))) 1214f0b776eSPeng Fan 1224f0b776eSPeng Fan 1232bb70056SOleksij Rempel static struct imx_mu_priv *to_imx_mu_priv(struct mbox_controller *mbox) 1242bb70056SOleksij Rempel { 1252bb70056SOleksij Rempel return container_of(mbox, struct imx_mu_priv, mbox); 1262bb70056SOleksij Rempel } 1272bb70056SOleksij Rempel 1282bb70056SOleksij Rempel static void imx_mu_write(struct imx_mu_priv *priv, u32 val, u32 offs) 1292bb70056SOleksij Rempel { 1302bb70056SOleksij Rempel iowrite32(val, priv->base + offs); 1312bb70056SOleksij Rempel } 1322bb70056SOleksij Rempel 1332bb70056SOleksij Rempel static u32 imx_mu_read(struct imx_mu_priv *priv, u32 offs) 1342bb70056SOleksij Rempel { 1352bb70056SOleksij Rempel return ioread32(priv->base + offs); 1362bb70056SOleksij Rempel } 1372bb70056SOleksij Rempel 138*11dac1d3SFranck LENORMAND static int imx_mu_tx_waiting_write(struct imx_mu_priv *priv, u32 val, u32 idx) 139*11dac1d3SFranck LENORMAND { 140*11dac1d3SFranck LENORMAND u64 timeout_time = get_jiffies_64() + IMX_MU_SECO_TX_TOUT; 141*11dac1d3SFranck LENORMAND u32 status; 142*11dac1d3SFranck LENORMAND u32 can_write; 143*11dac1d3SFranck LENORMAND 144*11dac1d3SFranck LENORMAND dev_dbg(priv->dev, "Trying to write %.8x to idx %d\n", val, idx); 145*11dac1d3SFranck LENORMAND 146*11dac1d3SFranck LENORMAND do { 147*11dac1d3SFranck LENORMAND status = imx_mu_read(priv, priv->dcfg->xSR[IMX_MU_TSR]); 148*11dac1d3SFranck LENORMAND can_write = status & IMX_MU_xSR_TEn(priv->dcfg->type, idx % 4); 149*11dac1d3SFranck LENORMAND } while (!can_write && time_is_after_jiffies64(timeout_time)); 150*11dac1d3SFranck LENORMAND 151*11dac1d3SFranck LENORMAND if (!can_write) { 152*11dac1d3SFranck LENORMAND dev_err(priv->dev, "timeout trying to write %.8x at %d(%.8x)\n", 153*11dac1d3SFranck LENORMAND val, idx, status); 154*11dac1d3SFranck LENORMAND return -ETIME; 155*11dac1d3SFranck LENORMAND } 156*11dac1d3SFranck LENORMAND 157*11dac1d3SFranck LENORMAND imx_mu_write(priv, val, priv->dcfg->xTR + (idx % 4) * 4); 158*11dac1d3SFranck LENORMAND 159*11dac1d3SFranck LENORMAND return 0; 160*11dac1d3SFranck LENORMAND } 161*11dac1d3SFranck LENORMAND 162*11dac1d3SFranck LENORMAND static int imx_mu_rx_waiting_read(struct imx_mu_priv *priv, u32 *val, u32 idx) 163*11dac1d3SFranck LENORMAND { 164*11dac1d3SFranck LENORMAND u64 timeout_time = get_jiffies_64() + IMX_MU_SECO_RX_TOUT; 165*11dac1d3SFranck LENORMAND u32 status; 166*11dac1d3SFranck LENORMAND u32 can_read; 167*11dac1d3SFranck LENORMAND 168*11dac1d3SFranck LENORMAND dev_dbg(priv->dev, "Trying to read from idx %d\n", idx); 169*11dac1d3SFranck LENORMAND 170*11dac1d3SFranck LENORMAND do { 171*11dac1d3SFranck LENORMAND status = imx_mu_read(priv, priv->dcfg->xSR[IMX_MU_RSR]); 172*11dac1d3SFranck LENORMAND can_read = status & IMX_MU_xSR_RFn(priv->dcfg->type, idx % 4); 173*11dac1d3SFranck LENORMAND } while (!can_read && time_is_after_jiffies64(timeout_time)); 174*11dac1d3SFranck LENORMAND 175*11dac1d3SFranck LENORMAND if (!can_read) { 176*11dac1d3SFranck LENORMAND dev_err(priv->dev, "timeout trying to read idx %d (%.8x)\n", 177*11dac1d3SFranck LENORMAND idx, status); 178*11dac1d3SFranck LENORMAND return -ETIME; 179*11dac1d3SFranck LENORMAND } 180*11dac1d3SFranck LENORMAND 181*11dac1d3SFranck LENORMAND *val = imx_mu_read(priv, priv->dcfg->xRR + (idx % 4) * 4); 182*11dac1d3SFranck LENORMAND dev_dbg(priv->dev, "Read %.8x\n", *val); 183*11dac1d3SFranck LENORMAND 184*11dac1d3SFranck LENORMAND return 0; 185*11dac1d3SFranck LENORMAND } 186*11dac1d3SFranck LENORMAND 187f689a7cfSPeng Fan static u32 imx_mu_xcr_rmw(struct imx_mu_priv *priv, enum imx_mu_xcr type, u32 set, u32 clr) 1882bb70056SOleksij Rempel { 1892bb70056SOleksij Rempel unsigned long flags; 1902bb70056SOleksij Rempel u32 val; 1912bb70056SOleksij Rempel 1922bb70056SOleksij Rempel spin_lock_irqsave(&priv->xcr_lock, flags); 193f689a7cfSPeng Fan val = imx_mu_read(priv, priv->dcfg->xCR[type]); 1942bb70056SOleksij Rempel val &= ~clr; 1952bb70056SOleksij Rempel val |= set; 196f689a7cfSPeng Fan imx_mu_write(priv, val, priv->dcfg->xCR[type]); 1972bb70056SOleksij Rempel spin_unlock_irqrestore(&priv->xcr_lock, flags); 1982bb70056SOleksij Rempel 1992bb70056SOleksij Rempel return val; 2002bb70056SOleksij Rempel } 2012bb70056SOleksij Rempel 20263b38357SPeng Fan static int imx_mu_generic_tx(struct imx_mu_priv *priv, 20363b38357SPeng Fan struct imx_mu_con_priv *cp, 20463b38357SPeng Fan void *data) 20563b38357SPeng Fan { 20663b38357SPeng Fan u32 *arg = data; 20763b38357SPeng Fan 20863b38357SPeng Fan switch (cp->type) { 20963b38357SPeng Fan case IMX_MU_TYPE_TX: 21032f7443dSPeng Fan imx_mu_write(priv, *arg, priv->dcfg->xTR + cp->idx * 4); 2114f0b776eSPeng Fan imx_mu_xcr_rmw(priv, IMX_MU_TCR, IMX_MU_xCR_TIEn(priv->dcfg->type, cp->idx), 0); 21263b38357SPeng Fan break; 21363b38357SPeng Fan case IMX_MU_TYPE_TXDB: 2144f0b776eSPeng Fan imx_mu_xcr_rmw(priv, IMX_MU_GCR, IMX_MU_xCR_GIRn(priv->dcfg->type, cp->idx), 0); 21563b38357SPeng Fan tasklet_schedule(&cp->txdb_tasklet); 21663b38357SPeng Fan break; 21763b38357SPeng Fan default: 21863b38357SPeng Fan dev_warn_ratelimited(priv->dev, "Send data on wrong channel type: %d\n", cp->type); 21963b38357SPeng Fan return -EINVAL; 22063b38357SPeng Fan } 22163b38357SPeng Fan 22263b38357SPeng Fan return 0; 22363b38357SPeng Fan } 22463b38357SPeng Fan 22563b38357SPeng Fan static int imx_mu_generic_rx(struct imx_mu_priv *priv, 22663b38357SPeng Fan struct imx_mu_con_priv *cp) 22763b38357SPeng Fan { 22863b38357SPeng Fan u32 dat; 22963b38357SPeng Fan 23032f7443dSPeng Fan dat = imx_mu_read(priv, priv->dcfg->xRR + (cp->idx) * 4); 23163b38357SPeng Fan mbox_chan_received_data(cp->chan, (void *)&dat); 23263b38357SPeng Fan 23363b38357SPeng Fan return 0; 23463b38357SPeng Fan } 23563b38357SPeng Fan 236315d2e56SPeng Fan static int imx_mu_generic_rxdb(struct imx_mu_priv *priv, 237315d2e56SPeng Fan struct imx_mu_con_priv *cp) 238315d2e56SPeng Fan { 239315d2e56SPeng Fan imx_mu_write(priv, IMX_MU_xSR_GIPn(priv->dcfg->type, cp->idx), 240315d2e56SPeng Fan priv->dcfg->xSR[IMX_MU_GSR]); 241315d2e56SPeng Fan mbox_chan_received_data(cp->chan, NULL); 242315d2e56SPeng Fan 243315d2e56SPeng Fan return 0; 244315d2e56SPeng Fan } 245315d2e56SPeng Fan 24697961f78SPeng Fan static int imx_mu_specific_tx(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp, void *data) 2470a67003bSPeng Fan { 2480a67003bSPeng Fan u32 *arg = data; 2490a67003bSPeng Fan int i, ret; 2500a67003bSPeng Fan u32 xsr; 25197961f78SPeng Fan u32 size, max_size, num_tr; 25297961f78SPeng Fan 25397961f78SPeng Fan if (priv->dcfg->type & IMX_MU_V2_S4) { 25497961f78SPeng Fan size = ((struct imx_s4_rpc_msg_max *)data)->hdr.size; 25597961f78SPeng Fan max_size = sizeof(struct imx_s4_rpc_msg_max); 25697961f78SPeng Fan num_tr = 8; 25797961f78SPeng Fan } else { 25897961f78SPeng Fan size = ((struct imx_sc_rpc_msg_max *)data)->hdr.size; 25997961f78SPeng Fan max_size = sizeof(struct imx_sc_rpc_msg_max); 26097961f78SPeng Fan num_tr = 4; 26197961f78SPeng Fan } 2620a67003bSPeng Fan 2630a67003bSPeng Fan switch (cp->type) { 2640a67003bSPeng Fan case IMX_MU_TYPE_TX: 2659d8ca628SPeng Fan /* 2669d8ca628SPeng Fan * msg->hdr.size specifies the number of u32 words while 2679d8ca628SPeng Fan * sizeof yields bytes. 2689d8ca628SPeng Fan */ 2699d8ca628SPeng Fan 27097961f78SPeng Fan if (size > max_size / 4) { 2710a67003bSPeng Fan /* 2720a67003bSPeng Fan * The real message size can be different to 27397961f78SPeng Fan * struct imx_sc_rpc_msg_max/imx_s4_rpc_msg_max size 2740a67003bSPeng Fan */ 27597961f78SPeng Fan dev_err(priv->dev, "Maximal message size (%u bytes) exceeded on TX; got: %i bytes\n", max_size, size << 2); 2760a67003bSPeng Fan return -EINVAL; 2770a67003bSPeng Fan } 2780a67003bSPeng Fan 27997961f78SPeng Fan for (i = 0; i < num_tr && i < size; i++) 28097961f78SPeng Fan imx_mu_write(priv, *arg++, priv->dcfg->xTR + (i % num_tr) * 4); 28197961f78SPeng Fan for (; i < size; i++) { 282f689a7cfSPeng Fan ret = readl_poll_timeout(priv->base + priv->dcfg->xSR[IMX_MU_TSR], 2830a67003bSPeng Fan xsr, 28497961f78SPeng Fan xsr & IMX_MU_xSR_TEn(priv->dcfg->type, i % num_tr), 28581a9d3b9SRanjani Vaidyanathan 0, 5 * USEC_PER_SEC); 2860a67003bSPeng Fan if (ret) { 2870a67003bSPeng Fan dev_err(priv->dev, "Send data index: %d timeout\n", i); 2880a67003bSPeng Fan return ret; 2890a67003bSPeng Fan } 29097961f78SPeng Fan imx_mu_write(priv, *arg++, priv->dcfg->xTR + (i % num_tr) * 4); 2910a67003bSPeng Fan } 2920a67003bSPeng Fan 2934f0b776eSPeng Fan imx_mu_xcr_rmw(priv, IMX_MU_TCR, IMX_MU_xCR_TIEn(priv->dcfg->type, cp->idx), 0); 2940a67003bSPeng Fan break; 2950a67003bSPeng Fan default: 2960a67003bSPeng Fan dev_warn_ratelimited(priv->dev, "Send data on wrong channel type: %d\n", cp->type); 2970a67003bSPeng Fan return -EINVAL; 2980a67003bSPeng Fan } 2990a67003bSPeng Fan 3000a67003bSPeng Fan return 0; 3010a67003bSPeng Fan } 3020a67003bSPeng Fan 30397961f78SPeng Fan static int imx_mu_specific_rx(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp) 3040a67003bSPeng Fan { 30597961f78SPeng Fan u32 *data; 3060a67003bSPeng Fan int i, ret; 3070a67003bSPeng Fan u32 xsr; 30897961f78SPeng Fan u32 size, max_size; 30997961f78SPeng Fan 31097961f78SPeng Fan data = (u32 *)priv->msg; 3110a67003bSPeng Fan 3124f0b776eSPeng Fan imx_mu_xcr_rmw(priv, IMX_MU_RCR, 0, IMX_MU_xCR_RIEn(priv->dcfg->type, 0)); 31332f7443dSPeng Fan *data++ = imx_mu_read(priv, priv->dcfg->xRR); 3140a67003bSPeng Fan 31597961f78SPeng Fan if (priv->dcfg->type & IMX_MU_V2_S4) { 31697961f78SPeng Fan size = ((struct imx_s4_rpc_msg_max *)priv->msg)->hdr.size; 31797961f78SPeng Fan max_size = sizeof(struct imx_s4_rpc_msg_max); 31897961f78SPeng Fan } else { 31997961f78SPeng Fan size = ((struct imx_sc_rpc_msg_max *)priv->msg)->hdr.size; 32097961f78SPeng Fan max_size = sizeof(struct imx_sc_rpc_msg_max); 32197961f78SPeng Fan } 32297961f78SPeng Fan 32397961f78SPeng Fan if (size > max_size / 4) { 32497961f78SPeng Fan dev_err(priv->dev, "Maximal message size (%u bytes) exceeded on RX; got: %i bytes\n", max_size, size << 2); 3250a67003bSPeng Fan return -EINVAL; 3260a67003bSPeng Fan } 3270a67003bSPeng Fan 32897961f78SPeng Fan for (i = 1; i < size; i++) { 329f689a7cfSPeng Fan ret = readl_poll_timeout(priv->base + priv->dcfg->xSR[IMX_MU_RSR], xsr, 33081a9d3b9SRanjani Vaidyanathan xsr & IMX_MU_xSR_RFn(priv->dcfg->type, i % 4), 0, 33181a9d3b9SRanjani Vaidyanathan 5 * USEC_PER_SEC); 3320a67003bSPeng Fan if (ret) { 3330a67003bSPeng Fan dev_err(priv->dev, "timeout read idx %d\n", i); 3340a67003bSPeng Fan return ret; 3350a67003bSPeng Fan } 33632f7443dSPeng Fan *data++ = imx_mu_read(priv, priv->dcfg->xRR + (i % 4) * 4); 3370a67003bSPeng Fan } 3380a67003bSPeng Fan 3394f0b776eSPeng Fan imx_mu_xcr_rmw(priv, IMX_MU_RCR, IMX_MU_xCR_RIEn(priv->dcfg->type, 0), 0); 34097961f78SPeng Fan mbox_chan_received_data(cp->chan, (void *)priv->msg); 3410a67003bSPeng Fan 3420a67003bSPeng Fan return 0; 3430a67003bSPeng Fan } 3440a67003bSPeng Fan 345*11dac1d3SFranck LENORMAND static int imx_mu_seco_tx(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp, 346*11dac1d3SFranck LENORMAND void *data) 347*11dac1d3SFranck LENORMAND { 348*11dac1d3SFranck LENORMAND struct imx_sc_rpc_msg_max *msg = data; 349*11dac1d3SFranck LENORMAND u32 *arg = data; 350*11dac1d3SFranck LENORMAND u32 byte_size; 351*11dac1d3SFranck LENORMAND int err; 352*11dac1d3SFranck LENORMAND int i; 353*11dac1d3SFranck LENORMAND 354*11dac1d3SFranck LENORMAND dev_dbg(priv->dev, "Sending message\n"); 355*11dac1d3SFranck LENORMAND 356*11dac1d3SFranck LENORMAND switch (cp->type) { 357*11dac1d3SFranck LENORMAND case IMX_MU_TYPE_TXDB: 358*11dac1d3SFranck LENORMAND byte_size = msg->hdr.size * sizeof(u32); 359*11dac1d3SFranck LENORMAND if (byte_size > sizeof(*msg)) { 360*11dac1d3SFranck LENORMAND /* 361*11dac1d3SFranck LENORMAND * The real message size can be different to 362*11dac1d3SFranck LENORMAND * struct imx_sc_rpc_msg_max size 363*11dac1d3SFranck LENORMAND */ 364*11dac1d3SFranck LENORMAND dev_err(priv->dev, 365*11dac1d3SFranck LENORMAND "Exceed max msg size (%zu) on TX, got: %i\n", 366*11dac1d3SFranck LENORMAND sizeof(*msg), byte_size); 367*11dac1d3SFranck LENORMAND return -EINVAL; 368*11dac1d3SFranck LENORMAND } 369*11dac1d3SFranck LENORMAND 370*11dac1d3SFranck LENORMAND print_hex_dump_debug("from client ", DUMP_PREFIX_OFFSET, 4, 4, 371*11dac1d3SFranck LENORMAND data, byte_size, false); 372*11dac1d3SFranck LENORMAND 373*11dac1d3SFranck LENORMAND /* Send first word */ 374*11dac1d3SFranck LENORMAND dev_dbg(priv->dev, "Sending header\n"); 375*11dac1d3SFranck LENORMAND imx_mu_write(priv, *arg++, priv->dcfg->xTR); 376*11dac1d3SFranck LENORMAND 377*11dac1d3SFranck LENORMAND /* Send signaling */ 378*11dac1d3SFranck LENORMAND dev_dbg(priv->dev, "Sending signaling\n"); 379*11dac1d3SFranck LENORMAND imx_mu_xcr_rmw(priv, IMX_MU_GCR, 380*11dac1d3SFranck LENORMAND IMX_MU_xCR_GIRn(priv->dcfg->type, cp->idx), 0); 381*11dac1d3SFranck LENORMAND 382*11dac1d3SFranck LENORMAND /* Send words to fill the mailbox */ 383*11dac1d3SFranck LENORMAND for (i = 1; i < 4 && i < msg->hdr.size; i++) { 384*11dac1d3SFranck LENORMAND dev_dbg(priv->dev, "Sending word %d\n", i); 385*11dac1d3SFranck LENORMAND imx_mu_write(priv, *arg++, 386*11dac1d3SFranck LENORMAND priv->dcfg->xTR + (i % 4) * 4); 387*11dac1d3SFranck LENORMAND } 388*11dac1d3SFranck LENORMAND 389*11dac1d3SFranck LENORMAND /* Send rest of message waiting for remote read */ 390*11dac1d3SFranck LENORMAND for (; i < msg->hdr.size; i++) { 391*11dac1d3SFranck LENORMAND dev_dbg(priv->dev, "Sending word %d\n", i); 392*11dac1d3SFranck LENORMAND err = imx_mu_tx_waiting_write(priv, *arg++, i); 393*11dac1d3SFranck LENORMAND if (err) { 394*11dac1d3SFranck LENORMAND dev_err(priv->dev, "Timeout tx %d\n", i); 395*11dac1d3SFranck LENORMAND return err; 396*11dac1d3SFranck LENORMAND } 397*11dac1d3SFranck LENORMAND } 398*11dac1d3SFranck LENORMAND 399*11dac1d3SFranck LENORMAND /* Simulate hack for mbox framework */ 400*11dac1d3SFranck LENORMAND tasklet_schedule(&cp->txdb_tasklet); 401*11dac1d3SFranck LENORMAND 402*11dac1d3SFranck LENORMAND break; 403*11dac1d3SFranck LENORMAND default: 404*11dac1d3SFranck LENORMAND dev_warn_ratelimited(priv->dev, 405*11dac1d3SFranck LENORMAND "Send data on wrong channel type: %d\n", 406*11dac1d3SFranck LENORMAND cp->type); 407*11dac1d3SFranck LENORMAND return -EINVAL; 408*11dac1d3SFranck LENORMAND } 409*11dac1d3SFranck LENORMAND 410*11dac1d3SFranck LENORMAND return 0; 411*11dac1d3SFranck LENORMAND } 412*11dac1d3SFranck LENORMAND 413*11dac1d3SFranck LENORMAND static int imx_mu_seco_rxdb(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp) 414*11dac1d3SFranck LENORMAND { 415*11dac1d3SFranck LENORMAND struct imx_sc_rpc_msg_max msg; 416*11dac1d3SFranck LENORMAND u32 *data = (u32 *)&msg; 417*11dac1d3SFranck LENORMAND u32 byte_size; 418*11dac1d3SFranck LENORMAND int err = 0; 419*11dac1d3SFranck LENORMAND int i; 420*11dac1d3SFranck LENORMAND 421*11dac1d3SFranck LENORMAND dev_dbg(priv->dev, "Receiving message\n"); 422*11dac1d3SFranck LENORMAND 423*11dac1d3SFranck LENORMAND /* Read header */ 424*11dac1d3SFranck LENORMAND dev_dbg(priv->dev, "Receiving header\n"); 425*11dac1d3SFranck LENORMAND *data++ = imx_mu_read(priv, priv->dcfg->xRR); 426*11dac1d3SFranck LENORMAND byte_size = msg.hdr.size * sizeof(u32); 427*11dac1d3SFranck LENORMAND if (byte_size > sizeof(msg)) { 428*11dac1d3SFranck LENORMAND dev_err(priv->dev, "Exceed max msg size (%zu) on RX, got: %i\n", 429*11dac1d3SFranck LENORMAND sizeof(msg), byte_size); 430*11dac1d3SFranck LENORMAND err = -EINVAL; 431*11dac1d3SFranck LENORMAND goto error; 432*11dac1d3SFranck LENORMAND } 433*11dac1d3SFranck LENORMAND 434*11dac1d3SFranck LENORMAND /* Read message waiting they are written */ 435*11dac1d3SFranck LENORMAND for (i = 1; i < msg.hdr.size; i++) { 436*11dac1d3SFranck LENORMAND dev_dbg(priv->dev, "Receiving word %d\n", i); 437*11dac1d3SFranck LENORMAND err = imx_mu_rx_waiting_read(priv, data++, i); 438*11dac1d3SFranck LENORMAND if (err) { 439*11dac1d3SFranck LENORMAND dev_err(priv->dev, "Timeout rx %d\n", i); 440*11dac1d3SFranck LENORMAND goto error; 441*11dac1d3SFranck LENORMAND } 442*11dac1d3SFranck LENORMAND } 443*11dac1d3SFranck LENORMAND 444*11dac1d3SFranck LENORMAND /* Clear GIP */ 445*11dac1d3SFranck LENORMAND imx_mu_write(priv, IMX_MU_xSR_GIPn(priv->dcfg->type, cp->idx), 446*11dac1d3SFranck LENORMAND priv->dcfg->xSR[IMX_MU_GSR]); 447*11dac1d3SFranck LENORMAND 448*11dac1d3SFranck LENORMAND print_hex_dump_debug("to client ", DUMP_PREFIX_OFFSET, 4, 4, 449*11dac1d3SFranck LENORMAND &msg, byte_size, false); 450*11dac1d3SFranck LENORMAND 451*11dac1d3SFranck LENORMAND /* send data to client */ 452*11dac1d3SFranck LENORMAND dev_dbg(priv->dev, "Sending message to client\n"); 453*11dac1d3SFranck LENORMAND mbox_chan_received_data(cp->chan, (void *)&msg); 454*11dac1d3SFranck LENORMAND 455*11dac1d3SFranck LENORMAND goto exit; 456*11dac1d3SFranck LENORMAND 457*11dac1d3SFranck LENORMAND error: 458*11dac1d3SFranck LENORMAND mbox_chan_received_data(cp->chan, ERR_PTR(err)); 459*11dac1d3SFranck LENORMAND 460*11dac1d3SFranck LENORMAND exit: 461*11dac1d3SFranck LENORMAND return err; 462*11dac1d3SFranck LENORMAND } 463*11dac1d3SFranck LENORMAND 4642bb70056SOleksij Rempel static void imx_mu_txdb_tasklet(unsigned long data) 4652bb70056SOleksij Rempel { 4662bb70056SOleksij Rempel struct imx_mu_con_priv *cp = (struct imx_mu_con_priv *)data; 4672bb70056SOleksij Rempel 4682bb70056SOleksij Rempel mbox_chan_txdone(cp->chan, 0); 4692bb70056SOleksij Rempel } 4702bb70056SOleksij Rempel 4712bb70056SOleksij Rempel static irqreturn_t imx_mu_isr(int irq, void *p) 4722bb70056SOleksij Rempel { 4732bb70056SOleksij Rempel struct mbox_chan *chan = p; 4742bb70056SOleksij Rempel struct imx_mu_priv *priv = to_imx_mu_priv(chan->mbox); 4752bb70056SOleksij Rempel struct imx_mu_con_priv *cp = chan->con_priv; 47663b38357SPeng Fan u32 val, ctrl; 4772bb70056SOleksij Rempel 4782bb70056SOleksij Rempel switch (cp->type) { 4792bb70056SOleksij Rempel case IMX_MU_TYPE_TX: 480f689a7cfSPeng Fan ctrl = imx_mu_read(priv, priv->dcfg->xCR[IMX_MU_TCR]); 481f689a7cfSPeng Fan val = imx_mu_read(priv, priv->dcfg->xSR[IMX_MU_TSR]); 4824f0b776eSPeng Fan val &= IMX_MU_xSR_TEn(priv->dcfg->type, cp->idx) & 4834f0b776eSPeng Fan (ctrl & IMX_MU_xCR_TIEn(priv->dcfg->type, cp->idx)); 4842bb70056SOleksij Rempel break; 4852bb70056SOleksij Rempel case IMX_MU_TYPE_RX: 486f689a7cfSPeng Fan ctrl = imx_mu_read(priv, priv->dcfg->xCR[IMX_MU_RCR]); 487f689a7cfSPeng Fan val = imx_mu_read(priv, priv->dcfg->xSR[IMX_MU_RSR]); 4884f0b776eSPeng Fan val &= IMX_MU_xSR_RFn(priv->dcfg->type, cp->idx) & 4894f0b776eSPeng Fan (ctrl & IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx)); 4902bb70056SOleksij Rempel break; 4912bb70056SOleksij Rempel case IMX_MU_TYPE_RXDB: 4924f0b776eSPeng Fan ctrl = imx_mu_read(priv, priv->dcfg->xCR[IMX_MU_GIER]); 493f689a7cfSPeng Fan val = imx_mu_read(priv, priv->dcfg->xSR[IMX_MU_GSR]); 4944f0b776eSPeng Fan val &= IMX_MU_xSR_GIPn(priv->dcfg->type, cp->idx) & 4954f0b776eSPeng Fan (ctrl & IMX_MU_xCR_GIEn(priv->dcfg->type, cp->idx)); 4962bb70056SOleksij Rempel break; 4972bb70056SOleksij Rempel default: 498e80a7e7eSNathan Chancellor dev_warn_ratelimited(priv->dev, "Unhandled channel type %d\n", 499e80a7e7eSNathan Chancellor cp->type); 500e80a7e7eSNathan Chancellor return IRQ_NONE; 5012bb70056SOleksij Rempel } 5022bb70056SOleksij Rempel 5032bb70056SOleksij Rempel if (!val) 5042bb70056SOleksij Rempel return IRQ_NONE; 5052bb70056SOleksij Rempel 5064f0b776eSPeng Fan if ((val == IMX_MU_xSR_TEn(priv->dcfg->type, cp->idx)) && 5074f0b776eSPeng Fan (cp->type == IMX_MU_TYPE_TX)) { 5084f0b776eSPeng Fan imx_mu_xcr_rmw(priv, IMX_MU_TCR, 0, IMX_MU_xCR_TIEn(priv->dcfg->type, cp->idx)); 5092bb70056SOleksij Rempel mbox_chan_txdone(chan, 0); 5104f0b776eSPeng Fan } else if ((val == IMX_MU_xSR_RFn(priv->dcfg->type, cp->idx)) && 5114f0b776eSPeng Fan (cp->type == IMX_MU_TYPE_RX)) { 51263b38357SPeng Fan priv->dcfg->rx(priv, cp); 5134f0b776eSPeng Fan } else if ((val == IMX_MU_xSR_GIPn(priv->dcfg->type, cp->idx)) && 5144f0b776eSPeng Fan (cp->type == IMX_MU_TYPE_RXDB)) { 515315d2e56SPeng Fan priv->dcfg->rxdb(priv, cp); 5162bb70056SOleksij Rempel } else { 5172bb70056SOleksij Rempel dev_warn_ratelimited(priv->dev, "Not handled interrupt\n"); 5182bb70056SOleksij Rempel return IRQ_NONE; 5192bb70056SOleksij Rempel } 5202bb70056SOleksij Rempel 521892cb524SRobin Gong if (priv->suspend) 522892cb524SRobin Gong pm_system_wakeup(); 523892cb524SRobin Gong 5242bb70056SOleksij Rempel return IRQ_HANDLED; 5252bb70056SOleksij Rempel } 5262bb70056SOleksij Rempel 5272bb70056SOleksij Rempel static int imx_mu_send_data(struct mbox_chan *chan, void *data) 5282bb70056SOleksij Rempel { 5292bb70056SOleksij Rempel struct imx_mu_priv *priv = to_imx_mu_priv(chan->mbox); 5302bb70056SOleksij Rempel struct imx_mu_con_priv *cp = chan->con_priv; 5312bb70056SOleksij Rempel 53263b38357SPeng Fan return priv->dcfg->tx(priv, cp, data); 5332bb70056SOleksij Rempel } 5342bb70056SOleksij Rempel 5352bb70056SOleksij Rempel static int imx_mu_startup(struct mbox_chan *chan) 5362bb70056SOleksij Rempel { 5372bb70056SOleksij Rempel struct imx_mu_priv *priv = to_imx_mu_priv(chan->mbox); 5382bb70056SOleksij Rempel struct imx_mu_con_priv *cp = chan->con_priv; 539b7b2796bSAnson Huang unsigned long irq_flag = IRQF_SHARED; 5402bb70056SOleksij Rempel int ret; 5412bb70056SOleksij Rempel 542676f23eaSAnson Huang pm_runtime_get_sync(priv->dev); 5432bb70056SOleksij Rempel if (cp->type == IMX_MU_TYPE_TXDB) { 5442bb70056SOleksij Rempel /* Tx doorbell don't have ACK support */ 5452bb70056SOleksij Rempel tasklet_init(&cp->txdb_tasklet, imx_mu_txdb_tasklet, 5462bb70056SOleksij Rempel (unsigned long)cp); 5472bb70056SOleksij Rempel return 0; 5482bb70056SOleksij Rempel } 5492bb70056SOleksij Rempel 550b7b2796bSAnson Huang /* IPC MU should be with IRQF_NO_SUSPEND set */ 551b7b2796bSAnson Huang if (!priv->dev->pm_domain) 552b7b2796bSAnson Huang irq_flag |= IRQF_NO_SUSPEND; 553b7b2796bSAnson Huang 554b7b2796bSAnson Huang ret = request_irq(priv->irq, imx_mu_isr, irq_flag, 555b7b2796bSAnson Huang cp->irq_desc, chan); 5562bb70056SOleksij Rempel if (ret) { 5572bb70056SOleksij Rempel dev_err(priv->dev, 5582bb70056SOleksij Rempel "Unable to acquire IRQ %d\n", priv->irq); 5592bb70056SOleksij Rempel return ret; 5602bb70056SOleksij Rempel } 5612bb70056SOleksij Rempel 5622bb70056SOleksij Rempel switch (cp->type) { 5632bb70056SOleksij Rempel case IMX_MU_TYPE_RX: 5644f0b776eSPeng Fan imx_mu_xcr_rmw(priv, IMX_MU_RCR, IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx), 0); 5652bb70056SOleksij Rempel break; 5662bb70056SOleksij Rempel case IMX_MU_TYPE_RXDB: 5674f0b776eSPeng Fan imx_mu_xcr_rmw(priv, IMX_MU_GIER, IMX_MU_xCR_GIEn(priv->dcfg->type, cp->idx), 0); 5682bb70056SOleksij Rempel break; 5692bb70056SOleksij Rempel default: 5702bb70056SOleksij Rempel break; 5712bb70056SOleksij Rempel } 5722bb70056SOleksij Rempel 5732bb70056SOleksij Rempel return 0; 5742bb70056SOleksij Rempel } 5752bb70056SOleksij Rempel 5762bb70056SOleksij Rempel static void imx_mu_shutdown(struct mbox_chan *chan) 5772bb70056SOleksij Rempel { 5782bb70056SOleksij Rempel struct imx_mu_priv *priv = to_imx_mu_priv(chan->mbox); 5792bb70056SOleksij Rempel struct imx_mu_con_priv *cp = chan->con_priv; 5802bb70056SOleksij Rempel 581bf159d15SDaniel Baluta if (cp->type == IMX_MU_TYPE_TXDB) { 5822bb70056SOleksij Rempel tasklet_kill(&cp->txdb_tasklet); 583676f23eaSAnson Huang pm_runtime_put_sync(priv->dev); 584bf159d15SDaniel Baluta return; 585bf159d15SDaniel Baluta } 5862bb70056SOleksij Rempel 5875f0af07eSDaniel Baluta switch (cp->type) { 5885f0af07eSDaniel Baluta case IMX_MU_TYPE_TX: 5894f0b776eSPeng Fan imx_mu_xcr_rmw(priv, IMX_MU_TCR, 0, IMX_MU_xCR_TIEn(priv->dcfg->type, cp->idx)); 5905f0af07eSDaniel Baluta break; 5915f0af07eSDaniel Baluta case IMX_MU_TYPE_RX: 5924f0b776eSPeng Fan imx_mu_xcr_rmw(priv, IMX_MU_RCR, 0, IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx)); 5935f0af07eSDaniel Baluta break; 5945f0af07eSDaniel Baluta case IMX_MU_TYPE_RXDB: 5954f0b776eSPeng Fan imx_mu_xcr_rmw(priv, IMX_MU_GIER, 0, IMX_MU_xCR_GIEn(priv->dcfg->type, cp->idx)); 5965f0af07eSDaniel Baluta break; 5975f0af07eSDaniel Baluta default: 5985f0af07eSDaniel Baluta break; 5995f0af07eSDaniel Baluta } 6002bb70056SOleksij Rempel 6012bb70056SOleksij Rempel free_irq(priv->irq, chan); 602676f23eaSAnson Huang pm_runtime_put_sync(priv->dev); 6032bb70056SOleksij Rempel } 6042bb70056SOleksij Rempel 6052bb70056SOleksij Rempel static const struct mbox_chan_ops imx_mu_ops = { 6062bb70056SOleksij Rempel .send_data = imx_mu_send_data, 6072bb70056SOleksij Rempel .startup = imx_mu_startup, 6082bb70056SOleksij Rempel .shutdown = imx_mu_shutdown, 6092bb70056SOleksij Rempel }; 6102bb70056SOleksij Rempel 61197961f78SPeng Fan static struct mbox_chan *imx_mu_specific_xlate(struct mbox_controller *mbox, 6120a67003bSPeng Fan const struct of_phandle_args *sp) 6130a67003bSPeng Fan { 6140a67003bSPeng Fan u32 type, idx, chan; 6150a67003bSPeng Fan 6160a67003bSPeng Fan if (sp->args_count != 2) { 6170a67003bSPeng Fan dev_err(mbox->dev, "Invalid argument count %d\n", sp->args_count); 6180a67003bSPeng Fan return ERR_PTR(-EINVAL); 6190a67003bSPeng Fan } 6200a67003bSPeng Fan 6210a67003bSPeng Fan type = sp->args[0]; /* channel type */ 6220a67003bSPeng Fan idx = sp->args[1]; /* index */ 6230a67003bSPeng Fan 6240a67003bSPeng Fan switch (type) { 6250a67003bSPeng Fan case IMX_MU_TYPE_TX: 6260a67003bSPeng Fan case IMX_MU_TYPE_RX: 6270a67003bSPeng Fan if (idx != 0) 6280a67003bSPeng Fan dev_err(mbox->dev, "Invalid chan idx: %d\n", idx); 6290a67003bSPeng Fan chan = type; 6300a67003bSPeng Fan break; 6310a67003bSPeng Fan case IMX_MU_TYPE_RXDB: 6320a67003bSPeng Fan chan = 2 + idx; 6330a67003bSPeng Fan break; 6340a67003bSPeng Fan default: 6350a67003bSPeng Fan dev_err(mbox->dev, "Invalid chan type: %d\n", type); 6361b3a347bSDan Carpenter return ERR_PTR(-EINVAL); 6370a67003bSPeng Fan } 6380a67003bSPeng Fan 6390a67003bSPeng Fan if (chan >= mbox->num_chans) { 6400a67003bSPeng Fan dev_err(mbox->dev, "Not supported channel number: %d. (type: %d, idx: %d)\n", chan, type, idx); 6410a67003bSPeng Fan return ERR_PTR(-EINVAL); 6420a67003bSPeng Fan } 6430a67003bSPeng Fan 6440a67003bSPeng Fan return &mbox->chans[chan]; 6450a67003bSPeng Fan } 6460a67003bSPeng Fan 6472bb70056SOleksij Rempel static struct mbox_chan * imx_mu_xlate(struct mbox_controller *mbox, 6482bb70056SOleksij Rempel const struct of_phandle_args *sp) 6492bb70056SOleksij Rempel { 6502bb70056SOleksij Rempel u32 type, idx, chan; 6512bb70056SOleksij Rempel 6522bb70056SOleksij Rempel if (sp->args_count != 2) { 6532bb70056SOleksij Rempel dev_err(mbox->dev, "Invalid argument count %d\n", sp->args_count); 6542bb70056SOleksij Rempel return ERR_PTR(-EINVAL); 6552bb70056SOleksij Rempel } 6562bb70056SOleksij Rempel 6572bb70056SOleksij Rempel type = sp->args[0]; /* channel type */ 6582bb70056SOleksij Rempel idx = sp->args[1]; /* index */ 6592bb70056SOleksij Rempel chan = type * 4 + idx; 6602bb70056SOleksij Rempel 6612bb70056SOleksij Rempel if (chan >= mbox->num_chans) { 6622bb70056SOleksij Rempel dev_err(mbox->dev, "Not supported channel number: %d. (type: %d, idx: %d)\n", chan, type, idx); 6632bb70056SOleksij Rempel return ERR_PTR(-EINVAL); 6642bb70056SOleksij Rempel } 6652bb70056SOleksij Rempel 6662bb70056SOleksij Rempel return &mbox->chans[chan]; 6672bb70056SOleksij Rempel } 6682bb70056SOleksij Rempel 669*11dac1d3SFranck LENORMAND static struct mbox_chan *imx_mu_seco_xlate(struct mbox_controller *mbox, 670*11dac1d3SFranck LENORMAND const struct of_phandle_args *sp) 671*11dac1d3SFranck LENORMAND { 672*11dac1d3SFranck LENORMAND u32 type; 673*11dac1d3SFranck LENORMAND 674*11dac1d3SFranck LENORMAND if (sp->args_count < 1) { 675*11dac1d3SFranck LENORMAND dev_err(mbox->dev, "Invalid argument count %d\n", sp->args_count); 676*11dac1d3SFranck LENORMAND return ERR_PTR(-EINVAL); 677*11dac1d3SFranck LENORMAND } 678*11dac1d3SFranck LENORMAND 679*11dac1d3SFranck LENORMAND type = sp->args[0]; /* channel type */ 680*11dac1d3SFranck LENORMAND 681*11dac1d3SFranck LENORMAND /* Only supports TXDB and RXDB */ 682*11dac1d3SFranck LENORMAND if (type == IMX_MU_TYPE_TX || type == IMX_MU_TYPE_RX) { 683*11dac1d3SFranck LENORMAND dev_err(mbox->dev, "Invalid type: %d\n", type); 684*11dac1d3SFranck LENORMAND return ERR_PTR(-EINVAL); 685*11dac1d3SFranck LENORMAND } 686*11dac1d3SFranck LENORMAND 687*11dac1d3SFranck LENORMAND return imx_mu_xlate(mbox, sp); 688*11dac1d3SFranck LENORMAND } 689*11dac1d3SFranck LENORMAND 6902bb70056SOleksij Rempel static void imx_mu_init_generic(struct imx_mu_priv *priv) 6912bb70056SOleksij Rempel { 69263b38357SPeng Fan unsigned int i; 69363b38357SPeng Fan 69463b38357SPeng Fan for (i = 0; i < IMX_MU_CHANS; i++) { 69563b38357SPeng Fan struct imx_mu_con_priv *cp = &priv->con_priv[i]; 69663b38357SPeng Fan 69763b38357SPeng Fan cp->idx = i % 4; 69863b38357SPeng Fan cp->type = i >> 2; 69963b38357SPeng Fan cp->chan = &priv->mbox_chans[i]; 70063b38357SPeng Fan priv->mbox_chans[i].con_priv = cp; 70163b38357SPeng Fan snprintf(cp->irq_desc, sizeof(cp->irq_desc), 70263b38357SPeng Fan "imx_mu_chan[%i-%i]", cp->type, cp->idx); 70363b38357SPeng Fan } 70463b38357SPeng Fan 70563b38357SPeng Fan priv->mbox.num_chans = IMX_MU_CHANS; 70663b38357SPeng Fan priv->mbox.of_xlate = imx_mu_xlate; 70763b38357SPeng Fan 7082bb70056SOleksij Rempel if (priv->side_b) 7092bb70056SOleksij Rempel return; 7102bb70056SOleksij Rempel 7112bb70056SOleksij Rempel /* Set default MU configuration */ 712f689a7cfSPeng Fan for (i = 0; i < IMX_MU_xCR_MAX; i++) 713f689a7cfSPeng Fan imx_mu_write(priv, 0, priv->dcfg->xCR[i]); 7142bb70056SOleksij Rempel } 7152bb70056SOleksij Rempel 71697961f78SPeng Fan static void imx_mu_init_specific(struct imx_mu_priv *priv) 7170a67003bSPeng Fan { 7180a67003bSPeng Fan unsigned int i; 71997961f78SPeng Fan int num_chans = priv->dcfg->type & IMX_MU_V2_S4 ? IMX_MU_S4_CHANS : IMX_MU_SCU_CHANS; 7200a67003bSPeng Fan 72197961f78SPeng Fan for (i = 0; i < num_chans; i++) { 7220a67003bSPeng Fan struct imx_mu_con_priv *cp = &priv->con_priv[i]; 7230a67003bSPeng Fan 7240a67003bSPeng Fan cp->idx = i < 2 ? 0 : i - 2; 7250a67003bSPeng Fan cp->type = i < 2 ? i : IMX_MU_TYPE_RXDB; 7260a67003bSPeng Fan cp->chan = &priv->mbox_chans[i]; 7270a67003bSPeng Fan priv->mbox_chans[i].con_priv = cp; 7280a67003bSPeng Fan snprintf(cp->irq_desc, sizeof(cp->irq_desc), 7290a67003bSPeng Fan "imx_mu_chan[%i-%i]", cp->type, cp->idx); 7300a67003bSPeng Fan } 7310a67003bSPeng Fan 73297961f78SPeng Fan priv->mbox.num_chans = num_chans; 73397961f78SPeng Fan priv->mbox.of_xlate = imx_mu_specific_xlate; 7340a67003bSPeng Fan 7350a67003bSPeng Fan /* Set default MU configuration */ 736f689a7cfSPeng Fan for (i = 0; i < IMX_MU_xCR_MAX; i++) 737f689a7cfSPeng Fan imx_mu_write(priv, 0, priv->dcfg->xCR[i]); 7380a67003bSPeng Fan } 7390a67003bSPeng Fan 740*11dac1d3SFranck LENORMAND static void imx_mu_init_seco(struct imx_mu_priv *priv) 741*11dac1d3SFranck LENORMAND { 742*11dac1d3SFranck LENORMAND imx_mu_init_generic(priv); 743*11dac1d3SFranck LENORMAND priv->mbox.of_xlate = imx_mu_seco_xlate; 744*11dac1d3SFranck LENORMAND } 745*11dac1d3SFranck LENORMAND 7462bb70056SOleksij Rempel static int imx_mu_probe(struct platform_device *pdev) 7472bb70056SOleksij Rempel { 7482bb70056SOleksij Rempel struct device *dev = &pdev->dev; 7492bb70056SOleksij Rempel struct device_node *np = dev->of_node; 7502bb70056SOleksij Rempel struct imx_mu_priv *priv; 751c6c6bc6eSRichard Zhu const struct imx_mu_dcfg *dcfg; 7522bb70056SOleksij Rempel int ret; 75397961f78SPeng Fan u32 size; 7542bb70056SOleksij Rempel 7552bb70056SOleksij Rempel priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 7562bb70056SOleksij Rempel if (!priv) 7572bb70056SOleksij Rempel return -ENOMEM; 7582bb70056SOleksij Rempel 7592bb70056SOleksij Rempel priv->dev = dev; 7602bb70056SOleksij Rempel 7610c40e631SAnson Huang priv->base = devm_platform_ioremap_resource(pdev, 0); 7622bb70056SOleksij Rempel if (IS_ERR(priv->base)) 7632bb70056SOleksij Rempel return PTR_ERR(priv->base); 7642bb70056SOleksij Rempel 7652bb70056SOleksij Rempel priv->irq = platform_get_irq(pdev, 0); 7662bb70056SOleksij Rempel if (priv->irq < 0) 7672bb70056SOleksij Rempel return priv->irq; 7682bb70056SOleksij Rempel 769c6c6bc6eSRichard Zhu dcfg = of_device_get_match_data(dev); 770c6c6bc6eSRichard Zhu if (!dcfg) 771c6c6bc6eSRichard Zhu return -EINVAL; 772c6c6bc6eSRichard Zhu priv->dcfg = dcfg; 773c6c6bc6eSRichard Zhu 77497961f78SPeng Fan if (priv->dcfg->type & IMX_MU_V2_S4) 77597961f78SPeng Fan size = sizeof(struct imx_s4_rpc_msg_max); 77697961f78SPeng Fan else 77797961f78SPeng Fan size = sizeof(struct imx_sc_rpc_msg_max); 77897961f78SPeng Fan 77997961f78SPeng Fan priv->msg = devm_kzalloc(dev, size, GFP_KERNEL); 78005d06f37SDan Carpenter if (!priv->msg) 78105d06f37SDan Carpenter return -ENOMEM; 78297961f78SPeng Fan 7832bb70056SOleksij Rempel priv->clk = devm_clk_get(dev, NULL); 7842bb70056SOleksij Rempel if (IS_ERR(priv->clk)) { 7852bb70056SOleksij Rempel if (PTR_ERR(priv->clk) != -ENOENT) 7862bb70056SOleksij Rempel return PTR_ERR(priv->clk); 7872bb70056SOleksij Rempel 7882bb70056SOleksij Rempel priv->clk = NULL; 7892bb70056SOleksij Rempel } 7902bb70056SOleksij Rempel 7912bb70056SOleksij Rempel ret = clk_prepare_enable(priv->clk); 7922bb70056SOleksij Rempel if (ret) { 7932bb70056SOleksij Rempel dev_err(dev, "Failed to enable clock\n"); 7942bb70056SOleksij Rempel return ret; 7952bb70056SOleksij Rempel } 7962bb70056SOleksij Rempel 7972bb70056SOleksij Rempel priv->side_b = of_property_read_bool(np, "fsl,mu-side-b"); 7982bb70056SOleksij Rempel 79963b38357SPeng Fan priv->dcfg->init(priv); 80063b38357SPeng Fan 8012bb70056SOleksij Rempel spin_lock_init(&priv->xcr_lock); 8022bb70056SOleksij Rempel 8032bb70056SOleksij Rempel priv->mbox.dev = dev; 8042bb70056SOleksij Rempel priv->mbox.ops = &imx_mu_ops; 8052bb70056SOleksij Rempel priv->mbox.chans = priv->mbox_chans; 8062bb70056SOleksij Rempel priv->mbox.txdone_irq = true; 8072bb70056SOleksij Rempel 8082bb70056SOleksij Rempel platform_set_drvdata(pdev, priv); 8092bb70056SOleksij Rempel 810676f23eaSAnson Huang ret = devm_mbox_controller_register(dev, &priv->mbox); 81147303f94SFabio Estevam if (ret) { 81247303f94SFabio Estevam clk_disable_unprepare(priv->clk); 813676f23eaSAnson Huang return ret; 81447303f94SFabio Estevam } 815676f23eaSAnson Huang 816676f23eaSAnson Huang pm_runtime_enable(dev); 817676f23eaSAnson Huang 818676f23eaSAnson Huang ret = pm_runtime_get_sync(dev); 819676f23eaSAnson Huang if (ret < 0) { 820676f23eaSAnson Huang pm_runtime_put_noidle(dev); 821676f23eaSAnson Huang goto disable_runtime_pm; 822676f23eaSAnson Huang } 823676f23eaSAnson Huang 824676f23eaSAnson Huang ret = pm_runtime_put_sync(dev); 825676f23eaSAnson Huang if (ret < 0) 826676f23eaSAnson Huang goto disable_runtime_pm; 827676f23eaSAnson Huang 828bb2b2624SAnson Huang clk_disable_unprepare(priv->clk); 829bb2b2624SAnson Huang 830676f23eaSAnson Huang return 0; 831676f23eaSAnson Huang 832676f23eaSAnson Huang disable_runtime_pm: 833676f23eaSAnson Huang pm_runtime_disable(dev); 834bb2b2624SAnson Huang clk_disable_unprepare(priv->clk); 835676f23eaSAnson Huang return ret; 8362bb70056SOleksij Rempel } 8372bb70056SOleksij Rempel 8382bb70056SOleksij Rempel static int imx_mu_remove(struct platform_device *pdev) 8392bb70056SOleksij Rempel { 8402bb70056SOleksij Rempel struct imx_mu_priv *priv = platform_get_drvdata(pdev); 8412bb70056SOleksij Rempel 842676f23eaSAnson Huang pm_runtime_disable(priv->dev); 8432bb70056SOleksij Rempel 8442bb70056SOleksij Rempel return 0; 8452bb70056SOleksij Rempel } 8462bb70056SOleksij Rempel 84763b38357SPeng Fan static const struct imx_mu_dcfg imx_mu_cfg_imx6sx = { 84863b38357SPeng Fan .tx = imx_mu_generic_tx, 84963b38357SPeng Fan .rx = imx_mu_generic_rx, 850315d2e56SPeng Fan .rxdb = imx_mu_generic_rxdb, 85163b38357SPeng Fan .init = imx_mu_init_generic, 85232f7443dSPeng Fan .xTR = 0x0, 85332f7443dSPeng Fan .xRR = 0x10, 854f689a7cfSPeng Fan .xSR = {0x20, 0x20, 0x20, 0x20}, 855f689a7cfSPeng Fan .xCR = {0x24, 0x24, 0x24, 0x24}, 85663b38357SPeng Fan }; 85763b38357SPeng Fan 85863b38357SPeng Fan static const struct imx_mu_dcfg imx_mu_cfg_imx7ulp = { 85963b38357SPeng Fan .tx = imx_mu_generic_tx, 86063b38357SPeng Fan .rx = imx_mu_generic_rx, 861315d2e56SPeng Fan .rxdb = imx_mu_generic_rxdb, 86263b38357SPeng Fan .init = imx_mu_init_generic, 86332f7443dSPeng Fan .xTR = 0x20, 86432f7443dSPeng Fan .xRR = 0x40, 865f689a7cfSPeng Fan .xSR = {0x60, 0x60, 0x60, 0x60}, 866f689a7cfSPeng Fan .xCR = {0x64, 0x64, 0x64, 0x64}, 86763b38357SPeng Fan }; 86863b38357SPeng Fan 8694f0b776eSPeng Fan static const struct imx_mu_dcfg imx_mu_cfg_imx8ulp = { 8704f0b776eSPeng Fan .tx = imx_mu_generic_tx, 8714f0b776eSPeng Fan .rx = imx_mu_generic_rx, 872315d2e56SPeng Fan .rxdb = imx_mu_generic_rxdb, 8734f0b776eSPeng Fan .init = imx_mu_init_generic, 874315d2e56SPeng Fan .rxdb = imx_mu_generic_rxdb, 8754f0b776eSPeng Fan .type = IMX_MU_V2, 8764f0b776eSPeng Fan .xTR = 0x200, 8774f0b776eSPeng Fan .xRR = 0x280, 8784f0b776eSPeng Fan .xSR = {0xC, 0x118, 0x124, 0x12C}, 8794f0b776eSPeng Fan .xCR = {0x110, 0x114, 0x120, 0x128}, 8804f0b776eSPeng Fan }; 8814f0b776eSPeng Fan 88297961f78SPeng Fan static const struct imx_mu_dcfg imx_mu_cfg_imx8ulp_s4 = { 88397961f78SPeng Fan .tx = imx_mu_specific_tx, 88497961f78SPeng Fan .rx = imx_mu_specific_rx, 88597961f78SPeng Fan .init = imx_mu_init_specific, 88697961f78SPeng Fan .type = IMX_MU_V2 | IMX_MU_V2_S4, 88797961f78SPeng Fan .xTR = 0x200, 88897961f78SPeng Fan .xRR = 0x280, 88997961f78SPeng Fan .xSR = {0xC, 0x118, 0x124, 0x12C}, 89097961f78SPeng Fan .xCR = {0x110, 0x114, 0x120, 0x128}, 89197961f78SPeng Fan }; 89297961f78SPeng Fan 8930a67003bSPeng Fan static const struct imx_mu_dcfg imx_mu_cfg_imx8_scu = { 89497961f78SPeng Fan .tx = imx_mu_specific_tx, 89597961f78SPeng Fan .rx = imx_mu_specific_rx, 89697961f78SPeng Fan .init = imx_mu_init_specific, 897315d2e56SPeng Fan .rxdb = imx_mu_generic_rxdb, 8984f0b776eSPeng Fan .xTR = 0x0, 8994f0b776eSPeng Fan .xRR = 0x10, 900f689a7cfSPeng Fan .xSR = {0x20, 0x20, 0x20, 0x20}, 901f689a7cfSPeng Fan .xCR = {0x24, 0x24, 0x24, 0x24}, 9020a67003bSPeng Fan }; 9030a67003bSPeng Fan 904*11dac1d3SFranck LENORMAND static const struct imx_mu_dcfg imx_mu_cfg_imx8_seco = { 905*11dac1d3SFranck LENORMAND .tx = imx_mu_seco_tx, 906*11dac1d3SFranck LENORMAND .rx = imx_mu_generic_rx, 907*11dac1d3SFranck LENORMAND .rxdb = imx_mu_seco_rxdb, 908*11dac1d3SFranck LENORMAND .init = imx_mu_init_seco, 909*11dac1d3SFranck LENORMAND .xTR = 0x0, 910*11dac1d3SFranck LENORMAND .xRR = 0x10, 911*11dac1d3SFranck LENORMAND .xSR = {0x20, 0x20, 0x20, 0x20}, 912*11dac1d3SFranck LENORMAND .xCR = {0x24, 0x24, 0x24, 0x24}, 913*11dac1d3SFranck LENORMAND }; 914*11dac1d3SFranck LENORMAND 9152bb70056SOleksij Rempel static const struct of_device_id imx_mu_dt_ids[] = { 916c6c6bc6eSRichard Zhu { .compatible = "fsl,imx7ulp-mu", .data = &imx_mu_cfg_imx7ulp }, 917c6c6bc6eSRichard Zhu { .compatible = "fsl,imx6sx-mu", .data = &imx_mu_cfg_imx6sx }, 9184f0b776eSPeng Fan { .compatible = "fsl,imx8ulp-mu", .data = &imx_mu_cfg_imx8ulp }, 91997961f78SPeng Fan { .compatible = "fsl,imx8ulp-mu-s4", .data = &imx_mu_cfg_imx8ulp_s4 }, 9200a67003bSPeng Fan { .compatible = "fsl,imx8-mu-scu", .data = &imx_mu_cfg_imx8_scu }, 921*11dac1d3SFranck LENORMAND { .compatible = "fsl,imx8-mu-seco", .data = &imx_mu_cfg_imx8_seco }, 9222bb70056SOleksij Rempel { }, 9232bb70056SOleksij Rempel }; 9242bb70056SOleksij Rempel MODULE_DEVICE_TABLE(of, imx_mu_dt_ids); 9252bb70056SOleksij Rempel 92603b70130SNathan Chancellor static int __maybe_unused imx_mu_suspend_noirq(struct device *dev) 927ba5f9fa0SDong Aisheng { 928ba5f9fa0SDong Aisheng struct imx_mu_priv *priv = dev_get_drvdata(dev); 929f689a7cfSPeng Fan int i; 930ba5f9fa0SDong Aisheng 931f689a7cfSPeng Fan if (!priv->clk) { 932f689a7cfSPeng Fan for (i = 0; i < IMX_MU_xCR_MAX; i++) 933f689a7cfSPeng Fan priv->xcr[i] = imx_mu_read(priv, priv->dcfg->xCR[i]); 934f689a7cfSPeng Fan } 935ba5f9fa0SDong Aisheng 936892cb524SRobin Gong priv->suspend = true; 937892cb524SRobin Gong 938ba5f9fa0SDong Aisheng return 0; 939ba5f9fa0SDong Aisheng } 940ba5f9fa0SDong Aisheng 94103b70130SNathan Chancellor static int __maybe_unused imx_mu_resume_noirq(struct device *dev) 942ba5f9fa0SDong Aisheng { 943ba5f9fa0SDong Aisheng struct imx_mu_priv *priv = dev_get_drvdata(dev); 944f689a7cfSPeng Fan int i; 945ba5f9fa0SDong Aisheng 946ba5f9fa0SDong Aisheng /* 947ba5f9fa0SDong Aisheng * ONLY restore MU when context lost, the TIE could 948ba5f9fa0SDong Aisheng * be set during noirq resume as there is MU data 949ba5f9fa0SDong Aisheng * communication going on, and restore the saved 950ba5f9fa0SDong Aisheng * value will overwrite the TIE and cause MU data 951ba5f9fa0SDong Aisheng * send failed, may lead to system freeze. This issue 952ba5f9fa0SDong Aisheng * is observed by testing freeze mode suspend. 953ba5f9fa0SDong Aisheng */ 9548219efd0SRobin Gong if (!priv->clk && !imx_mu_read(priv, priv->dcfg->xCR[0])) { 955f689a7cfSPeng Fan for (i = 0; i < IMX_MU_xCR_MAX; i++) 956f689a7cfSPeng Fan imx_mu_write(priv, priv->xcr[i], priv->dcfg->xCR[i]); 957f689a7cfSPeng Fan } 958ba5f9fa0SDong Aisheng 959892cb524SRobin Gong priv->suspend = false; 960892cb524SRobin Gong 961ba5f9fa0SDong Aisheng return 0; 962ba5f9fa0SDong Aisheng } 963ba5f9fa0SDong Aisheng 96403b70130SNathan Chancellor static int __maybe_unused imx_mu_runtime_suspend(struct device *dev) 965bb2b2624SAnson Huang { 966bb2b2624SAnson Huang struct imx_mu_priv *priv = dev_get_drvdata(dev); 967bb2b2624SAnson Huang 968bb2b2624SAnson Huang clk_disable_unprepare(priv->clk); 969bb2b2624SAnson Huang 970bb2b2624SAnson Huang return 0; 971bb2b2624SAnson Huang } 972bb2b2624SAnson Huang 97303b70130SNathan Chancellor static int __maybe_unused imx_mu_runtime_resume(struct device *dev) 974bb2b2624SAnson Huang { 975bb2b2624SAnson Huang struct imx_mu_priv *priv = dev_get_drvdata(dev); 976bb2b2624SAnson Huang int ret; 977bb2b2624SAnson Huang 978bb2b2624SAnson Huang ret = clk_prepare_enable(priv->clk); 979bb2b2624SAnson Huang if (ret) 980bb2b2624SAnson Huang dev_err(dev, "failed to enable clock\n"); 981bb2b2624SAnson Huang 982bb2b2624SAnson Huang return ret; 983bb2b2624SAnson Huang } 984bb2b2624SAnson Huang 985ba5f9fa0SDong Aisheng static const struct dev_pm_ops imx_mu_pm_ops = { 986ba5f9fa0SDong Aisheng SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(imx_mu_suspend_noirq, 987ba5f9fa0SDong Aisheng imx_mu_resume_noirq) 988bb2b2624SAnson Huang SET_RUNTIME_PM_OPS(imx_mu_runtime_suspend, 989bb2b2624SAnson Huang imx_mu_runtime_resume, NULL) 990ba5f9fa0SDong Aisheng }; 991ba5f9fa0SDong Aisheng 9922bb70056SOleksij Rempel static struct platform_driver imx_mu_driver = { 9932bb70056SOleksij Rempel .probe = imx_mu_probe, 9942bb70056SOleksij Rempel .remove = imx_mu_remove, 9952bb70056SOleksij Rempel .driver = { 9962bb70056SOleksij Rempel .name = "imx_mu", 9972bb70056SOleksij Rempel .of_match_table = imx_mu_dt_ids, 998ba5f9fa0SDong Aisheng .pm = &imx_mu_pm_ops, 9992bb70056SOleksij Rempel }, 10002bb70056SOleksij Rempel }; 10012bb70056SOleksij Rempel module_platform_driver(imx_mu_driver); 10022bb70056SOleksij Rempel 10032bb70056SOleksij Rempel MODULE_AUTHOR("Oleksij Rempel <o.rempel@pengutronix.de>"); 10042bb70056SOleksij Rempel MODULE_DESCRIPTION("Message Unit driver for i.MX"); 10052bb70056SOleksij Rempel MODULE_LICENSE("GPL v2"); 1006