HSI: omap_ssi_port: avoid calling runtime_pm_*_sync inside spinlock
[deliverable/linux.git] / drivers / hsi / controllers / omap_ssi_port.c
index 6b8f7739768aee770fcc0f82fa7e89078a5912f4..92064221dbabe1901cbc5db690601a5361c030f7 100644 (file)
@@ -225,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 |
@@ -246,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 |
@@ -261,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));
@@ -290,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;
        }
@@ -302,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;
 
@@ -388,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);
@@ -396,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);
 
@@ -567,12 +585,22 @@ static int ssi_flush(struct hsi_client *cl)
        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);
 
@@ -581,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;
 }
 
@@ -604,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;
 }
 
@@ -738,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;
 }
@@ -868,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;
@@ -900,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;
                }
@@ -916,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);
@@ -932,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);
+       do {
+               status_reg = readl(sys + SSI_MPU_STATUS_REG(port->num, 0));
+               status_reg &= readl(sys + SSI_MPU_ENABLE_REG(port->num, 0));
 
-       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);
+               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);
 
-       if (status_reg)
-               tasklet_hi_schedule(&omap_port->pio_tasklet);
-       else
-               enable_irq(omap_port->irq);
-}
+               status_reg = readl(sys + SSI_MPU_STATUS_REG(port->num, 0));
+               status_reg &= readl(sys + SSI_MPU_ENABLE_REG(port->num, 0));
 
-static irqreturn_t ssi_pio_isr(int irq, void *port)
-{
-       struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
-
-       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);
@@ -981,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),
@@ -1000,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 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;
@@ -1030,18 +1033,15 @@ static int 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 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;
@@ -1053,13 +1053,12 @@ static int 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);
@@ -1169,6 +1168,8 @@ static int 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;
@@ -1236,9 +1237,6 @@ static int 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;
This page took 0.034737 seconds and 5 git commands to generate.