drm/nouveau/kms/nv50-: use NVIDIA's headers for core head_olut_set()
[linux-2.6-microblaze.git] / drivers / mailbox / imx-mailbox.c
index 7906624..7205b82 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/mailbox_controller.h>
 #include <linux/module.h>
 #include <linux/of_device.h>
+#include <linux/pm_runtime.h>
 #include <linux/slab.h>
 
 #define IMX_MU_xSR_GIPn(x)     BIT(28 + (3 - (x)))
@@ -66,6 +67,8 @@ struct imx_mu_priv {
        struct clk              *clk;
        int                     irq;
 
+       u32 xcr;
+
        bool                    side_b;
 };
 
@@ -154,12 +157,17 @@ static int imx_mu_scu_tx(struct imx_mu_priv *priv,
 
        switch (cp->type) {
        case IMX_MU_TYPE_TX:
-               if (msg->hdr.size > sizeof(*msg)) {
+               /*
+                * msg->hdr.size specifies the number of u32 words while
+                * sizeof yields bytes.
+                */
+
+               if (msg->hdr.size > sizeof(*msg) / 4) {
                        /*
                         * The real message size can be different to
                         * struct imx_sc_rpc_msg_max size
                         */
-                       dev_err(priv->dev, "Exceed max msg size (%zu) on TX, got: %i\n", sizeof(*msg), msg->hdr.size);
+                       dev_err(priv->dev, "Maximal message size (%zu bytes) exceeded on TX; got: %i bytes\n", sizeof(*msg), msg->hdr.size << 2);
                        return -EINVAL;
                }
 
@@ -198,9 +206,8 @@ static int imx_mu_scu_rx(struct imx_mu_priv *priv,
        imx_mu_xcr_rmw(priv, 0, IMX_MU_xCR_RIEn(0));
        *data++ = imx_mu_read(priv, priv->dcfg->xRR[0]);
 
-       if (msg.hdr.size > sizeof(msg)) {
-               dev_err(priv->dev, "Exceed max msg size (%zu) on RX, got: %i\n",
-                       sizeof(msg), msg.hdr.size);
+       if (msg.hdr.size > sizeof(msg) / 4) {
+               dev_err(priv->dev, "Maximal message size (%zu bytes) exceeded on RX; got: %i bytes\n", sizeof(msg), msg.hdr.size << 2);
                return -EINVAL;
        }
 
@@ -285,8 +292,10 @@ static int imx_mu_startup(struct mbox_chan *chan)
 {
        struct imx_mu_priv *priv = to_imx_mu_priv(chan->mbox);
        struct imx_mu_con_priv *cp = chan->con_priv;
+       unsigned long irq_flag = IRQF_SHARED;
        int ret;
 
+       pm_runtime_get_sync(priv->dev);
        if (cp->type == IMX_MU_TYPE_TXDB) {
                /* Tx doorbell don't have ACK support */
                tasklet_init(&cp->txdb_tasklet, imx_mu_txdb_tasklet,
@@ -294,8 +303,12 @@ static int imx_mu_startup(struct mbox_chan *chan)
                return 0;
        }
 
-       ret = request_irq(priv->irq, imx_mu_isr, IRQF_SHARED |
-                         IRQF_NO_SUSPEND, cp->irq_desc, chan);
+       /* IPC MU should be with IRQF_NO_SUSPEND set */
+       if (!priv->dev->pm_domain)
+               irq_flag |= IRQF_NO_SUSPEND;
+
+       ret = request_irq(priv->irq, imx_mu_isr, irq_flag,
+                         cp->irq_desc, chan);
        if (ret) {
                dev_err(priv->dev,
                        "Unable to acquire IRQ %d\n", priv->irq);
@@ -323,6 +336,7 @@ static void imx_mu_shutdown(struct mbox_chan *chan)
 
        if (cp->type == IMX_MU_TYPE_TXDB) {
                tasklet_kill(&cp->txdb_tasklet);
+               pm_runtime_put_sync(priv->dev);
                return;
        }
 
@@ -341,6 +355,7 @@ static void imx_mu_shutdown(struct mbox_chan *chan)
        }
 
        free_irq(priv->irq, chan);
+       pm_runtime_put_sync(priv->dev);
 }
 
 static const struct mbox_chan_ops imx_mu_ops = {
@@ -374,7 +389,7 @@ static struct mbox_chan *imx_mu_scu_xlate(struct mbox_controller *mbox,
                break;
        default:
                dev_err(mbox->dev, "Invalid chan type: %d\n", type);
-               return NULL;
+               return ERR_PTR(-EINVAL);
        }
 
        if (chan >= mbox->num_chans) {
@@ -508,14 +523,39 @@ static int imx_mu_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, priv);
 
-       return devm_mbox_controller_register(dev, &priv->mbox);
+       ret = devm_mbox_controller_register(dev, &priv->mbox);
+       if (ret) {
+               clk_disable_unprepare(priv->clk);
+               return ret;
+       }
+
+       pm_runtime_enable(dev);
+
+       ret = pm_runtime_get_sync(dev);
+       if (ret < 0) {
+               pm_runtime_put_noidle(dev);
+               goto disable_runtime_pm;
+       }
+
+       ret = pm_runtime_put_sync(dev);
+       if (ret < 0)
+               goto disable_runtime_pm;
+
+       clk_disable_unprepare(priv->clk);
+
+       return 0;
+
+disable_runtime_pm:
+       pm_runtime_disable(dev);
+       clk_disable_unprepare(priv->clk);
+       return ret;
 }
 
 static int imx_mu_remove(struct platform_device *pdev)
 {
        struct imx_mu_priv *priv = platform_get_drvdata(pdev);
 
-       clk_disable_unprepare(priv->clk);
+       pm_runtime_disable(priv->dev);
 
        return 0;
 }
@@ -558,12 +598,69 @@ static const struct of_device_id imx_mu_dt_ids[] = {
 };
 MODULE_DEVICE_TABLE(of, imx_mu_dt_ids);
 
+static int imx_mu_suspend_noirq(struct device *dev)
+{
+       struct imx_mu_priv *priv = dev_get_drvdata(dev);
+
+       if (!priv->clk)
+               priv->xcr = imx_mu_read(priv, priv->dcfg->xCR);
+
+       return 0;
+}
+
+static int imx_mu_resume_noirq(struct device *dev)
+{
+       struct imx_mu_priv *priv = dev_get_drvdata(dev);
+
+       /*
+        * ONLY restore MU when context lost, the TIE could
+        * be set during noirq resume as there is MU data
+        * communication going on, and restore the saved
+        * value will overwrite the TIE and cause MU data
+        * send failed, may lead to system freeze. This issue
+        * is observed by testing freeze mode suspend.
+        */
+       if (!imx_mu_read(priv, priv->dcfg->xCR) && !priv->clk)
+               imx_mu_write(priv, priv->xcr, priv->dcfg->xCR);
+
+       return 0;
+}
+
+static int imx_mu_runtime_suspend(struct device *dev)
+{
+       struct imx_mu_priv *priv = dev_get_drvdata(dev);
+
+       clk_disable_unprepare(priv->clk);
+
+       return 0;
+}
+
+static int imx_mu_runtime_resume(struct device *dev)
+{
+       struct imx_mu_priv *priv = dev_get_drvdata(dev);
+       int ret;
+
+       ret = clk_prepare_enable(priv->clk);
+       if (ret)
+               dev_err(dev, "failed to enable clock\n");
+
+       return ret;
+}
+
+static const struct dev_pm_ops imx_mu_pm_ops = {
+       SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(imx_mu_suspend_noirq,
+                                     imx_mu_resume_noirq)
+       SET_RUNTIME_PM_OPS(imx_mu_runtime_suspend,
+                          imx_mu_runtime_resume, NULL)
+};
+
 static struct platform_driver imx_mu_driver = {
        .probe          = imx_mu_probe,
        .remove         = imx_mu_remove,
        .driver = {
                .name   = "imx_mu",
                .of_match_table = imx_mu_dt_ids,
+               .pm = &imx_mu_pm_ops,
        },
 };
 module_platform_driver(imx_mu_driver);