Merge tag 'remoteproc-3.15-cleanups' of git://git.kernel.org/pub/scm/linux/kernel...
[cascardo/linux.git] / drivers / dma / omap-dma.c
index 00f8e56..b19f04f 100644 (file)
@@ -30,6 +30,10 @@ struct omap_dmadev {
        void __iomem *base;
        const struct omap_dma_reg *reg_map;
        struct omap_system_dma_plat_info *plat;
+       bool legacy;
+       spinlock_t irq_lock;
+       uint32_t irq_enable_mask;
+       struct omap_chan *lch_map[32];
 };
 
 struct omap_chan {
@@ -37,6 +41,7 @@ struct omap_chan {
        struct list_head node;
        void __iomem *channel_base;
        const struct omap_dma_reg *reg_map;
+       uint32_t ccr;
 
        struct dma_slave_config cfg;
        unsigned dma_sig;
@@ -254,10 +259,22 @@ static void omap_dma_clear_csr(struct omap_chan *c)
                omap_dma_chan_write(c, CSR, ~0);
 }
 
+static unsigned omap_dma_get_csr(struct omap_chan *c)
+{
+       unsigned val = omap_dma_chan_read(c, CSR);
+
+       if (!dma_omap1())
+               omap_dma_chan_write(c, CSR, val);
+
+       return val;
+}
+
 static void omap_dma_assign(struct omap_dmadev *od, struct omap_chan *c,
        unsigned lch)
 {
        c->channel_base = od->base + od->plat->channel_stride * lch;
+
+       od->lch_map[lch] = c;
 }
 
 static void omap_dma_start(struct omap_chan *c, struct omap_desc *d)
@@ -460,32 +477,118 @@ static void omap_dma_sched(unsigned long data)
        }
 }
 
+static irqreturn_t omap_dma_irq(int irq, void *devid)
+{
+       struct omap_dmadev *od = devid;
+       unsigned status, channel;
+
+       spin_lock(&od->irq_lock);
+
+       status = omap_dma_glbl_read(od, IRQSTATUS_L1);
+       status &= od->irq_enable_mask;
+       if (status == 0) {
+               spin_unlock(&od->irq_lock);
+               return IRQ_NONE;
+       }
+
+       while ((channel = ffs(status)) != 0) {
+               unsigned mask, csr;
+               struct omap_chan *c;
+
+               channel -= 1;
+               mask = BIT(channel);
+               status &= ~mask;
+
+               c = od->lch_map[channel];
+               if (c == NULL) {
+                       /* This should never happen */
+                       dev_err(od->ddev.dev, "invalid channel %u\n", channel);
+                       continue;
+               }
+
+               csr = omap_dma_get_csr(c);
+               omap_dma_glbl_write(od, IRQSTATUS_L1, mask);
+
+               omap_dma_callback(channel, csr, c);
+       }
+
+       spin_unlock(&od->irq_lock);
+
+       return IRQ_HANDLED;
+}
+
 static int omap_dma_alloc_chan_resources(struct dma_chan *chan)
 {
        struct omap_dmadev *od = to_omap_dma_dev(chan->device);
        struct omap_chan *c = to_omap_dma_chan(chan);
        int ret;
 
-       dev_dbg(od->ddev.dev, "allocating channel for %u\n", c->dma_sig);
+       if (od->legacy) {
+               ret = omap_request_dma(c->dma_sig, "DMA engine",
+                                      omap_dma_callback, c, &c->dma_ch);
+       } else {
+               ret = omap_request_dma(c->dma_sig, "DMA engine", NULL, NULL,
+                                      &c->dma_ch);
+       }
 
-       ret = omap_request_dma(c->dma_sig, "DMA engine", omap_dma_callback,
-                              c, &c->dma_ch);
+       dev_dbg(od->ddev.dev, "allocating channel %u for %u\n",
+               c->dma_ch, c->dma_sig);
 
-       if (ret >= 0)
+       if (ret >= 0) {
                omap_dma_assign(od, c, c->dma_ch);
 
+               if (!od->legacy) {
+                       unsigned val;
+
+                       spin_lock_irq(&od->irq_lock);
+                       val = BIT(c->dma_ch);
+                       omap_dma_glbl_write(od, IRQSTATUS_L1, val);
+                       od->irq_enable_mask |= val;
+                       omap_dma_glbl_write(od, IRQENABLE_L1, od->irq_enable_mask);
+
+                       val = omap_dma_glbl_read(od, IRQENABLE_L0);
+                       val &= ~BIT(c->dma_ch);
+                       omap_dma_glbl_write(od, IRQENABLE_L0, val);
+                       spin_unlock_irq(&od->irq_lock);
+               }
+       }
+
+       if (dma_omap1()) {
+               if (__dma_omap16xx(od->plat->dma_attr)) {
+                       c->ccr = CCR_OMAP31_DISABLE;
+                       /* Duplicate what plat-omap/dma.c does */
+                       c->ccr |= c->dma_ch + 1;
+               } else {
+                       c->ccr = c->dma_sig & 0x1f;
+               }
+       } else {
+               c->ccr = c->dma_sig & 0x1f;
+               c->ccr |= (c->dma_sig & ~0x1f) << 14;
+       }
+       if (od->plat->errata & DMA_ERRATA_IFRAME_BUFFERING)
+               c->ccr |= CCR_BUFFERING_DISABLE;
+
        return ret;
 }
 
 static void omap_dma_free_chan_resources(struct dma_chan *chan)
 {
+       struct omap_dmadev *od = to_omap_dma_dev(chan->device);
        struct omap_chan *c = to_omap_dma_chan(chan);
 
+       if (!od->legacy) {
+               spin_lock_irq(&od->irq_lock);
+               od->irq_enable_mask &= ~BIT(c->dma_ch);
+               omap_dma_glbl_write(od, IRQENABLE_L1, od->irq_enable_mask);
+               spin_unlock_irq(&od->irq_lock);
+       }
+
        c->channel_base = NULL;
+       od->lch_map[c->dma_ch] = NULL;
        vchan_free_chan_resources(&c->vc);
        omap_free_dma(c->dma_ch);
 
-       dev_dbg(c->vc.chan.device->dev, "freeing channel for %u\n", c->dma_sig);
+       dev_dbg(od->ddev.dev, "freeing channel for %u\n", c->dma_sig);
 }
 
 static size_t omap_dma_sg_size(struct omap_sg *sg)
@@ -700,7 +803,7 @@ static struct dma_async_tx_descriptor *omap_dma_prep_slave_sg(
        d->dev_addr = dev_addr;
        d->es = es;
 
-       d->ccr = CCR_SYNC_FRAME;
+       d->ccr = c->ccr | CCR_SYNC_FRAME;
        if (dir == DMA_DEV_TO_MEM)
                d->ccr |= CCR_DST_AMODE_POSTINC | CCR_SRC_AMODE_CONSTANT;
        else
@@ -710,14 +813,6 @@ static struct dma_async_tx_descriptor *omap_dma_prep_slave_sg(
        d->csdp = es;
 
        if (dma_omap1()) {
-               if (__dma_omap16xx(od->plat->dma_attr)) {
-                       d->ccr |= CCR_OMAP31_DISABLE;
-                       /* Duplicate what plat-omap/dma.c does */
-                       d->ccr |= c->dma_ch + 1;
-               } else {
-                       d->ccr |= c->dma_sig & 0x1f;
-               }
-
                d->cicr |= CICR_TOUT_IE;
 
                if (dir == DMA_DEV_TO_MEM)
@@ -725,16 +820,11 @@ static struct dma_async_tx_descriptor *omap_dma_prep_slave_sg(
                else
                        d->csdp |= CSDP_DST_PORT_TIPB | CSDP_SRC_PORT_EMIFF;
        } else {
-               d->ccr |= (c->dma_sig & ~0x1f) << 14;
-               d->ccr |= c->dma_sig & 0x1f;
-
                if (dir == DMA_DEV_TO_MEM)
                        d->ccr |= CCR_TRIGGER_SRC;
 
                d->cicr |= CICR_MISALIGNED_ERR_IE | CICR_TRANS_ERR_IE;
        }
-       if (od->plat->errata & DMA_ERRATA_IFRAME_BUFFERING)
-               d->ccr |= CCR_BUFFERING_DISABLE;
        if (od->plat->errata & DMA_ERRATA_PARALLEL_CHANNELS)
                d->clnk_ctrl = c->dma_ch;
 
@@ -816,7 +906,7 @@ static struct dma_async_tx_descriptor *omap_dma_prep_dma_cyclic(
        d->sg[0].fn = buf_len / period_len;
        d->sglen = 1;
 
-       d->ccr = 0;
+       d->ccr = c->ccr;
        if (dir == DMA_DEV_TO_MEM)
                d->ccr |= CCR_DST_AMODE_POSTINC | CCR_SRC_AMODE_CONSTANT;
        else
@@ -829,14 +919,6 @@ static struct dma_async_tx_descriptor *omap_dma_prep_dma_cyclic(
        d->csdp = es;
 
        if (dma_omap1()) {
-               if (__dma_omap16xx(od->plat->dma_attr)) {
-                       d->ccr |= CCR_OMAP31_DISABLE;
-                       /* Duplicate what plat-omap/dma.c does */
-                       d->ccr |= c->dma_ch + 1;
-               } else {
-                       d->ccr |= c->dma_sig & 0x1f;
-               }
-
                d->cicr |= CICR_TOUT_IE;
 
                if (dir == DMA_DEV_TO_MEM)
@@ -844,9 +926,6 @@ static struct dma_async_tx_descriptor *omap_dma_prep_dma_cyclic(
                else
                        d->csdp |= CSDP_DST_PORT_MPUI | CSDP_SRC_PORT_EMIFF;
        } else {
-               d->ccr |= (c->dma_sig & ~0x1f) << 14;
-               d->ccr |= c->dma_sig & 0x1f;
-
                if (burst)
                        d->ccr |= CCR_SYNC_PACKET;
                else
@@ -859,8 +938,6 @@ static struct dma_async_tx_descriptor *omap_dma_prep_dma_cyclic(
 
                d->csdp |= CSDP_DST_BURST_64 | CSDP_SRC_BURST_64;
        }
-       if (od->plat->errata & DMA_ERRATA_IFRAME_BUFFERING)
-               d->ccr |= CCR_BUFFERING_DISABLE;
 
        if (__dma_omap15xx(od->plat->dma_attr))
                d->ccr |= CCR_AUTO_INIT | CCR_REPEAT;
@@ -1011,11 +1088,28 @@ static void omap_dma_free(struct omap_dmadev *od)
        }
 }
 
+#define OMAP_DMA_BUSWIDTHS     (BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \
+                                BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \
+                                BIT(DMA_SLAVE_BUSWIDTH_4_BYTES))
+
+static int omap_dma_device_slave_caps(struct dma_chan *dchan,
+                                     struct dma_slave_caps *caps)
+{
+       caps->src_addr_widths = OMAP_DMA_BUSWIDTHS;
+       caps->dstn_addr_widths = OMAP_DMA_BUSWIDTHS;
+       caps->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
+       caps->cmd_pause = true;
+       caps->cmd_terminate = true;
+       caps->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
+
+       return 0;
+}
+
 static int omap_dma_probe(struct platform_device *pdev)
 {
        struct omap_dmadev *od;
        struct resource *res;
-       int rc, i;
+       int rc, i, irq;
 
        od = devm_kzalloc(&pdev->dev, sizeof(*od), GFP_KERNEL);
        if (!od)
@@ -1041,10 +1135,12 @@ static int omap_dma_probe(struct platform_device *pdev)
        od->ddev.device_prep_slave_sg = omap_dma_prep_slave_sg;
        od->ddev.device_prep_dma_cyclic = omap_dma_prep_dma_cyclic;
        od->ddev.device_control = omap_dma_control;
+       od->ddev.device_slave_caps = omap_dma_device_slave_caps;
        od->ddev.dev = &pdev->dev;
        INIT_LIST_HEAD(&od->ddev.channels);
        INIT_LIST_HEAD(&od->pending);
        spin_lock_init(&od->lock);
+       spin_lock_init(&od->irq_lock);
 
        tasklet_init(&od->task, omap_dma_sched, (unsigned long)od);
 
@@ -1056,6 +1152,21 @@ static int omap_dma_probe(struct platform_device *pdev)
                }
        }
 
+       irq = platform_get_irq(pdev, 1);
+       if (irq <= 0) {
+               dev_info(&pdev->dev, "failed to get L1 IRQ: %d\n", irq);
+               od->legacy = true;
+       } else {
+               /* Disable all interrupts */
+               od->irq_enable_mask = 0;
+               omap_dma_glbl_write(od, IRQENABLE_L1, 0);
+
+               rc = devm_request_irq(&pdev->dev, irq, omap_dma_irq,
+                                     IRQF_SHARED, "omap-dma-engine", od);
+               if (rc)
+                       return rc;
+       }
+
        rc = dma_async_device_register(&od->ddev);
        if (rc) {
                pr_warn("OMAP-DMA: failed to register slave DMA engine device: %d\n",
@@ -1092,6 +1203,12 @@ static int omap_dma_remove(struct platform_device *pdev)
                of_dma_controller_free(pdev->dev.of_node);
 
        dma_async_device_unregister(&od->ddev);
+
+       if (!od->legacy) {
+               /* Disable all interrupts */
+               omap_dma_glbl_write(od, IRQENABLE_L0, 0);
+       }
+
        omap_dma_free(od);
 
        return 0;