]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/dma/sh/rcar-dmac.c
dmaengine: rcar-dmac: clear channel register when error
[linux.git] / drivers / dma / sh / rcar-dmac.c
index 2a2ccd9c78e4cc1f8e7b7a72b22613530a0c1afe..9dc73ac92359d65b99ba22837c63548d8b8791d0 100644 (file)
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Renesas R-Car Gen2 DMA Controller Driver
  *
  * Copyright (C) 2014 Renesas Electronics Inc.
  *
  * Author: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
- *
- * This is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
  */
 
 #include <linux/delay.h>
@@ -431,7 +428,8 @@ static void rcar_dmac_chan_start_xfer(struct rcar_dmac_chan *chan)
                chcr |= RCAR_DMACHCR_DPM_DISABLED | RCAR_DMACHCR_IE;
        }
 
-       rcar_dmac_chan_write(chan, RCAR_DMACHCR, chcr | RCAR_DMACHCR_DE);
+       rcar_dmac_chan_write(chan, RCAR_DMACHCR,
+                            chcr | RCAR_DMACHCR_DE | RCAR_DMACHCR_CAIE);
 }
 
 static int rcar_dmac_init(struct rcar_dmac *dmac)
@@ -783,7 +781,8 @@ static void rcar_dmac_chan_halt(struct rcar_dmac_chan *chan)
        u32 chcr = rcar_dmac_chan_read(chan, RCAR_DMACHCR);
 
        chcr &= ~(RCAR_DMACHCR_DSE | RCAR_DMACHCR_DSIE | RCAR_DMACHCR_IE |
-                 RCAR_DMACHCR_TE | RCAR_DMACHCR_DE);
+                 RCAR_DMACHCR_TE | RCAR_DMACHCR_DE |
+                 RCAR_DMACHCR_CAE | RCAR_DMACHCR_CAIE);
        rcar_dmac_chan_write(chan, RCAR_DMACHCR, chcr);
        rcar_dmac_chcr_de_barrier(chan);
 }
@@ -812,12 +811,7 @@ static void rcar_dmac_chan_reinit(struct rcar_dmac_chan *chan)
        }
 }
 
-static void rcar_dmac_stop(struct rcar_dmac *dmac)
-{
-       rcar_dmac_write(dmac, RCAR_DMAOR, 0);
-}
-
-static void rcar_dmac_abort(struct rcar_dmac *dmac)
+static void rcar_dmac_stop_all_chan(struct rcar_dmac *dmac)
 {
        unsigned int i;
 
@@ -826,14 +820,13 @@ static void rcar_dmac_abort(struct rcar_dmac *dmac)
                struct rcar_dmac_chan *chan = &dmac->channels[i];
 
                /* Stop and reinitialize the channel. */
-               spin_lock(&chan->lock);
+               spin_lock_irq(&chan->lock);
                rcar_dmac_chan_halt(chan);
-               spin_unlock(&chan->lock);
-
-               rcar_dmac_chan_reinit(chan);
+               spin_unlock_irq(&chan->lock);
        }
 }
 
+
 /* -----------------------------------------------------------------------------
  * Descriptors preparation
  */
@@ -1522,11 +1515,26 @@ static irqreturn_t rcar_dmac_isr_channel(int irq, void *dev)
        u32 mask = RCAR_DMACHCR_DSE | RCAR_DMACHCR_TE;
        struct rcar_dmac_chan *chan = dev;
        irqreturn_t ret = IRQ_NONE;
+       bool reinit = false;
        u32 chcr;
 
        spin_lock(&chan->lock);
 
        chcr = rcar_dmac_chan_read(chan, RCAR_DMACHCR);
+       if (chcr & RCAR_DMACHCR_CAE) {
+               struct rcar_dmac *dmac = to_rcar_dmac(chan->chan.device);
+
+               /*
+                * We don't need to call rcar_dmac_chan_halt()
+                * because channel is already stopped in error case.
+                * We need to clear register and check DE bit as recovery.
+                */
+               rcar_dmac_write(dmac, RCAR_DMACHCLR, 1 << chan->index);
+               rcar_dmac_chcr_de_barrier(chan);
+               reinit = true;
+               goto spin_lock_end;
+       }
+
        if (chcr & RCAR_DMACHCR_TE)
                mask |= RCAR_DMACHCR_DE;
        rcar_dmac_chan_write(chan, RCAR_DMACHCR, chcr & ~mask);
@@ -1539,8 +1547,16 @@ static irqreturn_t rcar_dmac_isr_channel(int irq, void *dev)
        if (chcr & RCAR_DMACHCR_TE)
                ret |= rcar_dmac_isr_transfer_end(chan);
 
+spin_lock_end:
        spin_unlock(&chan->lock);
 
+       if (reinit) {
+               dev_err(chan->chan.device->dev, "Channel Address Error\n");
+
+               rcar_dmac_chan_reinit(chan);
+               ret = IRQ_HANDLED;
+       }
+
        return ret;
 }
 
@@ -1597,24 +1613,6 @@ static irqreturn_t rcar_dmac_isr_channel_thread(int irq, void *dev)
        return IRQ_HANDLED;
 }
 
-static irqreturn_t rcar_dmac_isr_error(int irq, void *data)
-{
-       struct rcar_dmac *dmac = data;
-
-       if (!(rcar_dmac_read(dmac, RCAR_DMAOR) & RCAR_DMAOR_AE))
-               return IRQ_NONE;
-
-       /*
-        * An unrecoverable error occurred on an unknown channel. Halt the DMAC,
-        * abort transfers on all channels, and reinitialize the DMAC.
-        */
-       rcar_dmac_stop(dmac);
-       rcar_dmac_abort(dmac);
-       rcar_dmac_init(dmac);
-
-       return IRQ_HANDLED;
-}
-
 /* -----------------------------------------------------------------------------
  * OF xlate and channel filter
  */
@@ -1784,8 +1782,6 @@ static int rcar_dmac_probe(struct platform_device *pdev)
        struct rcar_dmac *dmac;
        struct resource *mem;
        unsigned int i;
-       char *irqname;
-       int irq;
        int ret;
 
        dmac = devm_kzalloc(&pdev->dev, sizeof(*dmac), GFP_KERNEL);
@@ -1824,17 +1820,6 @@ static int rcar_dmac_probe(struct platform_device *pdev)
        if (IS_ERR(dmac->iomem))
                return PTR_ERR(dmac->iomem);
 
-       irq = platform_get_irq_byname(pdev, "error");
-       if (irq < 0) {
-               dev_err(&pdev->dev, "no error IRQ specified\n");
-               return -ENODEV;
-       }
-
-       irqname = devm_kasprintf(dmac->dev, GFP_KERNEL, "%s:error",
-                                dev_name(dmac->dev));
-       if (!irqname)
-               return -ENOMEM;
-
        /* Enable runtime PM and initialize the device. */
        pm_runtime_enable(&pdev->dev);
        ret = pm_runtime_get_sync(&pdev->dev);
@@ -1885,14 +1870,6 @@ static int rcar_dmac_probe(struct platform_device *pdev)
                        goto error;
        }
 
-       ret = devm_request_irq(&pdev->dev, irq, rcar_dmac_isr_error, 0,
-                              irqname, dmac);
-       if (ret) {
-               dev_err(&pdev->dev, "failed to request IRQ %u (%d)\n",
-                       irq, ret);
-               return ret;
-       }
-
        /* Register the DMAC as a DMA provider for DT. */
        ret = of_dma_controller_register(pdev->dev.of_node, rcar_dmac_of_xlate,
                                         NULL);
@@ -1932,7 +1909,7 @@ static void rcar_dmac_shutdown(struct platform_device *pdev)
 {
        struct rcar_dmac *dmac = platform_get_drvdata(pdev);
 
-       rcar_dmac_stop(dmac);
+       rcar_dmac_stop_all_chan(dmac);
 }
 
 static const struct of_device_id rcar_dmac_of_ids[] = {