HSI: omap_ssi_port: avoid calling runtime_pm_*_sync inside spinlock
[deliverable/linux.git] / drivers / hsi / controllers / omap_ssi_port.c
index 948bdc7946fb0264fa86010498aa97805a195a5f..92064221dbabe1901cbc5db690601a5361c030f7 100644 (file)
 #include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 #include <linux/pm_runtime.h>
+#include <linux/delay.h>
 
 #include <linux/gpio/consumer.h>
+#include <linux/pinctrl/consumer.h>
 #include <linux/debugfs.h>
 
 #include "omap_ssi_regs.h"
@@ -171,7 +173,7 @@ static int ssi_div_set(void *data, u64 val)
 
 DEFINE_SIMPLE_ATTRIBUTE(ssi_sst_div_fops, ssi_div_get, ssi_div_set, "%llu\n");
 
-static int __init ssi_debug_add_port(struct omap_ssi_port *omap_port,
+static int ssi_debug_add_port(struct omap_ssi_port *omap_port,
                                     struct dentry *dir)
 {
        struct hsi_port *port = to_hsi_port(omap_port->dev);
@@ -223,11 +225,21 @@ static int ssi_start_dma(struct hsi_msg *msg, int lch)
        u32 d_addr;
        u32 tmp;
 
+       /* Hold clocks during the transfer */
+       pm_runtime_get(omap_port->pdev);
+
+       if (!pm_runtime_active(omap_port->pdev)) {
+               dev_warn(&port->device, "ssi_start_dma called without runtime PM!\n");
+               pm_runtime_put(omap_port->pdev);
+               return -EREMOTEIO;
+       }
+
        if (msg->ttype == HSI_MSG_READ) {
                err = dma_map_sg(&ssi->device, msg->sgt.sgl, msg->sgt.nents,
                                                        DMA_FROM_DEVICE);
                if (err < 0) {
                        dev_dbg(&ssi->device, "DMA map SG failed !\n");
+                       pm_runtime_put(omap_port->pdev);
                        return err;
                }
                csdp = SSI_DST_BURST_4x32_BIT | SSI_DST_MEMORY_PORT |
@@ -244,6 +256,7 @@ static int ssi_start_dma(struct hsi_msg *msg, int lch)
                                                        DMA_TO_DEVICE);
                if (err < 0) {
                        dev_dbg(&ssi->device, "DMA map SG failed !\n");
+                       pm_runtime_put(omap_port->pdev);
                        return err;
                }
                csdp = SSI_SRC_BURST_4x32_BIT | SSI_SRC_MEMORY_PORT |
@@ -259,9 +272,6 @@ static int ssi_start_dma(struct hsi_msg *msg, int lch)
        dev_dbg(&ssi->device, "lch %d cdsp %08x ccr %04x s_addr %08x d_addr %08x\n",
                lch, csdp, ccr, s_addr, d_addr);
 
-       /* Hold clocks during the transfer */
-       pm_runtime_get_sync(omap_port->pdev);
-
        writew_relaxed(csdp, gdd + SSI_GDD_CSDP_REG(lch));
        writew_relaxed(SSI_BLOCK_IE | SSI_TOUT_IE, gdd + SSI_GDD_CICR_REG(lch));
        writel_relaxed(d_addr, gdd + SSI_GDD_CDSA_REG(lch));
@@ -288,11 +298,18 @@ static int ssi_start_pio(struct hsi_msg *msg)
        struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
        u32 val;
 
-       pm_runtime_get_sync(omap_port->pdev);
+       pm_runtime_get(omap_port->pdev);
+
+       if (!pm_runtime_active(omap_port->pdev)) {
+               dev_warn(&port->device, "ssi_start_pio called without runtime PM!\n");
+               pm_runtime_put(omap_port->pdev);
+               return -EREMOTEIO;
+       }
+
        if (msg->ttype == HSI_MSG_WRITE) {
                val = SSI_DATAACCEPT(msg->channel);
                /* Hold clocks for pio writes */
-               pm_runtime_get_sync(omap_port->pdev);
+               pm_runtime_get(omap_port->pdev);
        } else {
                val = SSI_DATAAVAILABLE(msg->channel) | SSI_ERROROCCURED;
        }
@@ -300,7 +317,7 @@ static int ssi_start_pio(struct hsi_msg *msg)
                                                msg->ttype ? "write" : "read");
        val |= readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
        writel(val, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
-       pm_runtime_put_sync(omap_port->pdev);
+       pm_runtime_put(omap_port->pdev);
        msg->actual_len = 0;
        msg->status = HSI_STATUS_PROCEEDING;
 
@@ -386,6 +403,8 @@ static int ssi_async(struct hsi_msg *msg)
                queue = &omap_port->rxqueue[msg->channel];
        }
        msg->status = HSI_STATUS_QUEUED;
+
+       pm_runtime_get_sync(omap_port->pdev);
        spin_lock_bh(&omap_port->lock);
        list_add_tail(&msg->link, queue);
        err = ssi_start_transfer(queue);
@@ -394,6 +413,7 @@ static int ssi_async(struct hsi_msg *msg)
                msg->status = HSI_STATUS_ERROR;
        }
        spin_unlock_bh(&omap_port->lock);
+       pm_runtime_put(omap_port->pdev);
        dev_dbg(&port->device, "msg status %d ttype %d ch %d\n",
                                msg->status, msg->ttype, msg->channel);
 
@@ -514,6 +534,11 @@ static int ssi_flush(struct hsi_client *cl)
 
        pm_runtime_get_sync(omap_port->pdev);
        spin_lock_bh(&omap_port->lock);
+
+       /* stop all ssi communication */
+       pinctrl_pm_select_idle_state(omap_port->pdev);
+       udelay(1); /* wait for racing frames */
+
        /* Stop all DMA transfers */
        for (i = 0; i < SSI_MAX_GDD_LCH; i++) {
                msg = omap_ssi->gdd_trn[i].msg;
@@ -550,18 +575,32 @@ static int ssi_flush(struct hsi_client *cl)
                ssi_flush_queue(&omap_port->rxqueue[i], NULL);
        }
        ssi_flush_queue(&omap_port->brkqueue, NULL);
+
+       /* Resume SSI communication */
+       pinctrl_pm_select_default_state(omap_port->pdev);
+
        spin_unlock_bh(&omap_port->lock);
        pm_runtime_put_sync(omap_port->pdev);
 
        return 0;
 }
 
+static void start_tx_work(struct work_struct *work)
+{
+       struct omap_ssi_port *omap_port =
+                               container_of(work, struct omap_ssi_port, work);
+       struct hsi_port *port = to_hsi_port(omap_port->dev);
+       struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+       struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+
+       pm_runtime_get_sync(omap_port->pdev); /* Grab clocks */
+       writel(SSI_WAKE(0), omap_ssi->sys + SSI_SET_WAKE_REG(port->num));
+}
+
 static int ssi_start_tx(struct hsi_client *cl)
 {
        struct hsi_port *port = hsi_get_port(cl);
        struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
-       struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
-       struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
 
        dev_dbg(&port->device, "Wake out high %d\n", omap_port->wk_refcount);
 
@@ -570,10 +609,10 @@ static int ssi_start_tx(struct hsi_client *cl)
                spin_unlock_bh(&omap_port->wk_lock);
                return 0;
        }
-       pm_runtime_get_sync(omap_port->pdev); /* Grab clocks */
-       writel(SSI_WAKE(0), omap_ssi->sys + SSI_SET_WAKE_REG(port->num));
        spin_unlock_bh(&omap_port->wk_lock);
 
+       schedule_work(&omap_port->work);
+
        return 0;
 }
 
@@ -593,9 +632,10 @@ static int ssi_stop_tx(struct hsi_client *cl)
                return 0;
        }
        writel(SSI_WAKE(0), omap_ssi->sys + SSI_CLEAR_WAKE_REG(port->num));
-       pm_runtime_put_sync(omap_port->pdev); /* Release clocks */
        spin_unlock_bh(&omap_port->wk_lock);
 
+       pm_runtime_put(omap_port->pdev); /* Release clocks */
+
        return 0;
 }
 
@@ -727,32 +767,30 @@ static int ssi_release(struct hsi_client *cl)
        struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
        struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
 
-       spin_lock_bh(&omap_port->lock);
        pm_runtime_get_sync(omap_port->pdev);
+       spin_lock_bh(&omap_port->lock);
        /* Stop all the pending DMA requests for that client */
        ssi_cleanup_gdd(ssi, cl);
        /* Now cleanup all the queues */
        ssi_cleanup_queues(cl);
-       pm_runtime_put_sync(omap_port->pdev);
        /* If it is the last client of the port, do extra checks and cleanup */
        if (port->claimed <= 1) {
                /*
                 * Drop the clock reference for the incoming wake line
                 * if it is still kept high by the other side.
                 */
-               if (omap_port->wkin_cken) {
+               if (test_and_clear_bit(SSI_WAKE_EN, &omap_port->flags))
                        pm_runtime_put_sync(omap_port->pdev);
-                       omap_port->wkin_cken = 0;
-               }
-               pm_runtime_get_sync(omap_port->pdev);
+               pm_runtime_get(omap_port->pdev);
                /* Stop any SSI TX/RX without a client */
                ssi_set_port_mode(omap_port, SSI_MODE_SLEEP);
                omap_port->sst.mode = SSI_MODE_SLEEP;
                omap_port->ssr.mode = SSI_MODE_SLEEP;
-               pm_runtime_put_sync(omap_port->pdev);
+               pm_runtime_put(omap_port->pdev);
                WARN_ON(omap_port->wk_refcount != 0);
        }
        spin_unlock_bh(&omap_port->lock);
+       pm_runtime_put_sync(omap_port->pdev);
 
        return 0;
 }
@@ -857,7 +895,7 @@ static void ssi_pio_complete(struct hsi_port *port, struct list_head *queue)
        u32 reg;
        u32 val;
 
-       spin_lock(&omap_port->lock);
+       spin_lock_bh(&omap_port->lock);
        msg = list_first_entry(queue, struct hsi_msg, link);
        if ((!msg->sgt.nents) || (!msg->sgt.sgl->length)) {
                msg->actual_len = 0;
@@ -889,7 +927,7 @@ static void ssi_pio_complete(struct hsi_port *port, struct list_head *queue)
                                        (msg->ttype == HSI_MSG_WRITE))) {
                        writel(val, omap_ssi->sys +
                                        SSI_MPU_STATUS_REG(port->num, 0));
-                       spin_unlock(&omap_port->lock);
+                       spin_unlock_bh(&omap_port->lock);
 
                        return;
                }
@@ -905,12 +943,12 @@ static void ssi_pio_complete(struct hsi_port *port, struct list_head *queue)
        writel_relaxed(reg, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
        writel_relaxed(val, omap_ssi->sys + SSI_MPU_STATUS_REG(port->num, 0));
        list_del(&msg->link);
-       spin_unlock(&omap_port->lock);
+       spin_unlock_bh(&omap_port->lock);
        msg->complete(msg);
        ssi_transfer(omap_port, queue);
 }
 
-static void ssi_pio_tasklet(unsigned long ssi_port)
+static irqreturn_t ssi_pio_thread(int irq, void *ssi_port)
 {
        struct hsi_port *port = (struct hsi_port *)ssi_port;
        struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
@@ -921,41 +959,33 @@ static void ssi_pio_tasklet(unsigned long ssi_port)
        u32 status_reg;
 
        pm_runtime_get_sync(omap_port->pdev);
-       status_reg = readl(sys + SSI_MPU_STATUS_REG(port->num, 0));
-       status_reg &= readl(sys + SSI_MPU_ENABLE_REG(port->num, 0));
-
-       for (ch = 0; ch < omap_port->channels; ch++) {
-               if (status_reg & SSI_DATAACCEPT(ch))
-                       ssi_pio_complete(port, &omap_port->txqueue[ch]);
-               if (status_reg & SSI_DATAAVAILABLE(ch))
-                       ssi_pio_complete(port, &omap_port->rxqueue[ch]);
-       }
-       if (status_reg & SSI_BREAKDETECTED)
-               ssi_break_complete(port);
-       if (status_reg & SSI_ERROROCCURED)
-               ssi_error(port);
 
-       status_reg = readl(sys + SSI_MPU_STATUS_REG(port->num, 0));
-       status_reg &= readl(sys + SSI_MPU_ENABLE_REG(port->num, 0));
-       pm_runtime_put_sync(omap_port->pdev);
+       do {
+               status_reg = readl(sys + SSI_MPU_STATUS_REG(port->num, 0));
+               status_reg &= readl(sys + SSI_MPU_ENABLE_REG(port->num, 0));
 
-       if (status_reg)
-               tasklet_hi_schedule(&omap_port->pio_tasklet);
-       else
-               enable_irq(omap_port->irq);
-}
+               for (ch = 0; ch < omap_port->channels; ch++) {
+                       if (status_reg & SSI_DATAACCEPT(ch))
+                               ssi_pio_complete(port, &omap_port->txqueue[ch]);
+                       if (status_reg & SSI_DATAAVAILABLE(ch))
+                               ssi_pio_complete(port, &omap_port->rxqueue[ch]);
+               }
+               if (status_reg & SSI_BREAKDETECTED)
+                       ssi_break_complete(port);
+               if (status_reg & SSI_ERROROCCURED)
+                       ssi_error(port);
 
-static irqreturn_t ssi_pio_isr(int irq, void *port)
-{
-       struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+               status_reg = readl(sys + SSI_MPU_STATUS_REG(port->num, 0));
+               status_reg &= readl(sys + SSI_MPU_ENABLE_REG(port->num, 0));
 
-       tasklet_hi_schedule(&omap_port->pio_tasklet);
-       disable_irq_nosync(irq);
+               /* TODO: sleep if we retry? */
+       } while (status_reg);
 
+       pm_runtime_put(omap_port->pdev);
        return IRQ_HANDLED;
 }
 
-static void ssi_wake_tasklet(unsigned long ssi_port)
+static irqreturn_t ssi_wake_thread(int irq __maybe_unused, void *ssi_port)
 {
        struct hsi_port *port = (struct hsi_port *)ssi_port;
        struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
@@ -970,12 +1000,8 @@ static void ssi_wake_tasklet(unsigned long ssi_port)
                 * This workaround will avoid breaking the clock reference
                 * count when such a situation ocurrs.
                 */
-               spin_lock(&omap_port->lock);
-               if (!omap_port->wkin_cken) {
-                       omap_port->wkin_cken = 1;
+               if (!test_and_set_bit(SSI_WAKE_EN, &omap_port->flags))
                        pm_runtime_get_sync(omap_port->pdev);
-               }
-               spin_unlock(&omap_port->lock);
                dev_dbg(&ssi->device, "Wake in high\n");
                if (omap_port->wktest) { /* FIXME: HACK ! To be removed */
                        writel(SSI_WAKE(0),
@@ -989,26 +1015,14 @@ static void ssi_wake_tasklet(unsigned long ssi_port)
                                omap_ssi->sys + SSI_CLEAR_WAKE_REG(port->num));
                }
                hsi_event(port, HSI_EVENT_STOP_RX);
-               spin_lock(&omap_port->lock);
-               if (omap_port->wkin_cken) {
+               if (test_and_clear_bit(SSI_WAKE_EN, &omap_port->flags))
                        pm_runtime_put_sync(omap_port->pdev);
-                       omap_port->wkin_cken = 0;
-               }
-               spin_unlock(&omap_port->lock);
        }
-}
-
-static irqreturn_t ssi_wake_isr(int irq __maybe_unused, void *ssi_port)
-{
-       struct omap_ssi_port *omap_port = hsi_port_drvdata(ssi_port);
-
-       tasklet_hi_schedule(&omap_port->wake_tasklet);
 
        return IRQ_HANDLED;
 }
 
-static int __init ssi_port_irq(struct hsi_port *port,
-                                               struct platform_device *pd)
+static int ssi_port_irq(struct hsi_port *port, struct platform_device *pd)
 {
        struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
        int err;
@@ -1019,18 +1033,15 @@ static int __init ssi_port_irq(struct hsi_port *port,
                return err;
        }
        omap_port->irq = err;
-       tasklet_init(&omap_port->pio_tasklet, ssi_pio_tasklet,
-                                                       (unsigned long)port);
-       err = devm_request_irq(&port->device, omap_port->irq, ssi_pio_isr,
-                                               0, "mpu_irq0", port);
+       err = devm_request_threaded_irq(&port->device, omap_port->irq, NULL,
+                               ssi_pio_thread, IRQF_ONESHOT, "SSI PORT", port);
        if (err < 0)
                dev_err(&port->device, "Request IRQ %d failed (%d)\n",
                                                        omap_port->irq, err);
        return err;
 }
 
-static int __init ssi_wake_irq(struct hsi_port *port,
-                                               struct platform_device *pd)
+static int ssi_wake_irq(struct hsi_port *port, struct platform_device *pd)
 {
        struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
        int cawake_irq;
@@ -1042,13 +1053,12 @@ static int __init ssi_wake_irq(struct hsi_port *port,
        }
 
        cawake_irq = gpiod_to_irq(omap_port->wake_gpio);
-
        omap_port->wake_irq = cawake_irq;
-       tasklet_init(&omap_port->wake_tasklet, ssi_wake_tasklet,
-                                                       (unsigned long)port);
-       err = devm_request_irq(&port->device, cawake_irq, ssi_wake_isr,
-               IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
-                                                       "cawake", port);
+
+       err = devm_request_threaded_irq(&port->device, cawake_irq, NULL,
+               ssi_wake_thread,
+               IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+               "SSI cawake", port);
        if (err < 0)
                dev_err(&port->device, "Request Wake in IRQ %d failed %d\n",
                                                cawake_irq, err);
@@ -1060,7 +1070,7 @@ static int __init ssi_wake_irq(struct hsi_port *port,
        return err;
 }
 
-static void __init ssi_queues_init(struct omap_ssi_port *omap_port)
+static void ssi_queues_init(struct omap_ssi_port *omap_port)
 {
        unsigned int ch;
 
@@ -1071,7 +1081,7 @@ static void __init ssi_queues_init(struct omap_ssi_port *omap_port)
        INIT_LIST_HEAD(&omap_port->brkqueue);
 }
 
-static int __init ssi_port_get_iomem(struct platform_device *pd,
+static int ssi_port_get_iomem(struct platform_device *pd,
                const char *name, void __iomem **pbase, dma_addr_t *phy)
 {
        struct hsi_port *port = platform_get_drvdata(pd);
@@ -1104,7 +1114,7 @@ static int __init ssi_port_get_iomem(struct platform_device *pd,
        return 0;
 }
 
-static int __init ssi_port_probe(struct platform_device *pd)
+static int ssi_port_probe(struct platform_device *pd)
 {
        struct device_node *np = pd->dev.of_node;
        struct hsi_port *port;
@@ -1117,11 +1127,6 @@ static int __init ssi_port_probe(struct platform_device *pd)
 
        dev_dbg(&pd->dev, "init ssi port...\n");
 
-       if (!try_module_get(ssi->owner)) {
-               dev_err(&pd->dev, "could not increment parent module refcount\n");
-               return -ENODEV;
-       }
-
        if (!ssi->port || !omap_ssi->port) {
                dev_err(&pd->dev, "ssi controller not initialized!\n");
                err = -ENODEV;
@@ -1163,6 +1168,8 @@ static int __init ssi_port_probe(struct platform_device *pd)
        omap_port->pdev = &pd->dev;
        omap_port->port_id = port_id;
 
+       INIT_WORK(&omap_port->work, start_tx_work);
+
        /* initialize HSI port */
        port->async     = ssi_async;
        port->setup     = ssi_setup;
@@ -1217,7 +1224,7 @@ error:
        return err;
 }
 
-static int __exit ssi_port_remove(struct platform_device *pd)
+static int ssi_port_remove(struct platform_device *pd)
 {
        struct hsi_port *port = platform_get_drvdata(pd);
        struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
@@ -1230,9 +1237,6 @@ static int __exit ssi_port_remove(struct platform_device *pd)
 
        hsi_port_unregister_clients(port);
 
-       tasklet_kill(&omap_port->wake_tasklet);
-       tasklet_kill(&omap_port->pio_tasklet);
-
        port->async     = hsi_dummy_msg;
        port->setup     = hsi_dummy_cl;
        port->flush     = hsi_dummy_cl;
@@ -1242,12 +1246,28 @@ static int __exit ssi_port_remove(struct platform_device *pd)
 
        omap_ssi->port[omap_port->port_id] = NULL;
        platform_set_drvdata(pd, NULL);
-       module_put(ssi->owner);
        pm_runtime_disable(&pd->dev);
 
        return 0;
 }
 
+static int ssi_restore_divisor(struct omap_ssi_port *omap_port)
+{
+       writel_relaxed(omap_port->sst.divisor,
+                               omap_port->sst_base + SSI_SST_DIVISOR_REG);
+
+       return 0;
+}
+
+void omap_ssi_port_update_fclk(struct hsi_controller *ssi,
+                              struct omap_ssi_port *omap_port)
+{
+       /* update divisor */
+       u32 div = ssi_calculate_div(ssi);
+       omap_port->sst.divisor = div;
+       ssi_restore_divisor(omap_port);
+}
+
 #ifdef CONFIG_PM
 static int ssi_save_port_ctx(struct omap_ssi_port *omap_port)
 {
@@ -1300,14 +1320,6 @@ static int ssi_restore_port_mode(struct omap_ssi_port *omap_port)
        return 0;
 }
 
-static int ssi_restore_divisor(struct omap_ssi_port *omap_port)
-{
-       writel_relaxed(omap_port->sst.divisor,
-                               omap_port->sst_base + SSI_SST_DIVISOR_REG);
-
-       return 0;
-}
-
 static int omap_ssi_port_runtime_suspend(struct device *dev)
 {
        struct hsi_port *port = dev_get_drvdata(dev);
@@ -1369,19 +1381,12 @@ MODULE_DEVICE_TABLE(of, omap_ssi_port_of_match);
 #define omap_ssi_port_of_match NULL
 #endif
 
-static struct platform_driver ssi_port_pdriver = {
-       .remove = __exit_p(ssi_port_remove),
+struct platform_driver ssi_port_pdriver = {
+       .probe = ssi_port_probe,
+       .remove = ssi_port_remove,
        .driver = {
                .name   = "omap_ssi_port",
                .of_match_table = omap_ssi_port_of_match,
                .pm     = DEV_PM_OPS,
        },
 };
-
-module_platform_driver_probe(ssi_port_pdriver, ssi_port_probe);
-
-MODULE_ALIAS("platform:omap_ssi_port");
-MODULE_AUTHOR("Carlos Chinea <carlos.chinea@nokia.com>");
-MODULE_AUTHOR("Sebastian Reichel <sre@kernel.org>");
-MODULE_DESCRIPTION("Synchronous Serial Interface Port Driver");
-MODULE_LICENSE("GPL v2");
This page took 0.049613 seconds and 5 git commands to generate.