]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/dma/mpc512x_dma.c
Merge tag 'v2.6.38' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[mv-sheeva.git] / drivers / dma / mpc512x_dma.c
index 4e9cbf3005947a519e8d69ee6126753d2feaf287..59c270192ccc308e60726b02a96ea64d2f28eecc 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) Freescale Semicondutor, Inc. 2007, 2008.
  * Copyright (C) Semihalf 2009
+ * Copyright (C) Ilya Yanok, Emcraft Systems 2010
  *
  * Written by Piotr Ziecik <kosmo@semihalf.com>. Hardware description
  * (defines, structures and comments) was taken from MPC5121 DMA driver
@@ -70,6 +71,8 @@
 #define MPC_DMA_DMAES_SBE      (1 << 1)
 #define MPC_DMA_DMAES_DBE      (1 << 0)
 
+#define MPC_DMA_DMAGPOR_SNOOP_ENABLE   (1 << 6)
+
 #define MPC_DMA_TSIZE_1                0x00
 #define MPC_DMA_TSIZE_2                0x01
 #define MPC_DMA_TSIZE_4                0x02
@@ -104,7 +107,10 @@ struct __attribute__ ((__packed__)) mpc_dma_regs {
        /* 0x30 */
        u32 dmahrsh;            /* DMA hw request status high(ch63~32) */
        u32 dmahrsl;            /* DMA hardware request status low(ch31~0) */
-       u32 dmaihsa;            /* DMA interrupt high select AXE(ch63~32) */
+       union {
+               u32 dmaihsa;    /* DMA interrupt high select AXE(ch63~32) */
+               u32 dmagpor;    /* (General purpose register on MPC8308) */
+       };
        u32 dmailsa;            /* DMA interrupt low select AXE(ch31~0) */
        /* 0x40 ~ 0xff */
        u32 reserve0[48];       /* Reserved */
@@ -195,7 +201,9 @@ struct mpc_dma {
        struct mpc_dma_regs __iomem     *regs;
        struct mpc_dma_tcd __iomem      *tcd;
        int                             irq;
+       int                             irq2;
        uint                            error_status;
+       int                             is_mpc8308;
 
        /* Lock for error_status field in this structure */
        spinlock_t                      error_status_lock;
@@ -252,11 +260,13 @@ static void mpc_dma_execute(struct mpc_dma_chan *mchan)
                prev = mdesc;
        }
 
-       prev->tcd->start = 0;
        prev->tcd->int_maj = 1;
 
        /* Send first descriptor in chain into hardware */
        memcpy_toio(&mdma->tcd[cid], first->tcd, sizeof(struct mpc_dma_tcd));
+
+       if (first != prev)
+               mdma->tcd[cid].e_sg = 1;
        out_8(&mdma->regs->dmassrt, cid);
 }
 
@@ -274,6 +284,9 @@ static void mpc_dma_irq_process(struct mpc_dma *mdma, u32 is, u32 es, int off)
 
                spin_lock(&mchan->lock);
 
+               out_8(&mdma->regs->dmacint, ch + off);
+               out_8(&mdma->regs->dmacerr, ch + off);
+
                /* Check error status */
                if (es & (1 << ch))
                        list_for_each_entry(mdesc, &mchan->active, node)
@@ -302,36 +315,68 @@ static irqreturn_t mpc_dma_irq(int irq, void *data)
        spin_unlock(&mdma->error_status_lock);
 
        /* Handle interrupt on each channel */
-       mpc_dma_irq_process(mdma, in_be32(&mdma->regs->dmainth),
+       if (mdma->dma.chancnt > 32) {
+               mpc_dma_irq_process(mdma, in_be32(&mdma->regs->dmainth),
                                        in_be32(&mdma->regs->dmaerrh), 32);
+       }
        mpc_dma_irq_process(mdma, in_be32(&mdma->regs->dmaintl),
                                        in_be32(&mdma->regs->dmaerrl), 0);
 
-       /* Ack interrupt on all channels */
-       out_be32(&mdma->regs->dmainth, 0xFFFFFFFF);
-       out_be32(&mdma->regs->dmaintl, 0xFFFFFFFF);
-       out_be32(&mdma->regs->dmaerrh, 0xFFFFFFFF);
-       out_be32(&mdma->regs->dmaerrl, 0xFFFFFFFF);
-
        /* Schedule tasklet */
        tasklet_schedule(&mdma->tasklet);
 
        return IRQ_HANDLED;
 }
 
-/* DMA Tasklet */
-static void mpc_dma_tasklet(unsigned long data)
+/* proccess completed descriptors */
+static void mpc_dma_process_completed(struct mpc_dma *mdma)
 {
-       struct mpc_dma *mdma = (void *)data;
        dma_cookie_t last_cookie = 0;
        struct mpc_dma_chan *mchan;
        struct mpc_dma_desc *mdesc;
        struct dma_async_tx_descriptor *desc;
        unsigned long flags;
        LIST_HEAD(list);
-       uint es;
        int i;
 
+       for (i = 0; i < mdma->dma.chancnt; i++) {
+               mchan = &mdma->channels[i];
+
+               /* Get all completed descriptors */
+               spin_lock_irqsave(&mchan->lock, flags);
+               if (!list_empty(&mchan->completed))
+                       list_splice_tail_init(&mchan->completed, &list);
+               spin_unlock_irqrestore(&mchan->lock, flags);
+
+               if (list_empty(&list))
+                       continue;
+
+               /* Execute callbacks and run dependencies */
+               list_for_each_entry(mdesc, &list, node) {
+                       desc = &mdesc->desc;
+
+                       if (desc->callback)
+                               desc->callback(desc->callback_param);
+
+                       last_cookie = desc->cookie;
+                       dma_run_dependencies(desc);
+               }
+
+               /* Free descriptors */
+               spin_lock_irqsave(&mchan->lock, flags);
+               list_splice_tail_init(&list, &mchan->free);
+               mchan->completed_cookie = last_cookie;
+               spin_unlock_irqrestore(&mchan->lock, flags);
+       }
+}
+
+/* DMA Tasklet */
+static void mpc_dma_tasklet(unsigned long data)
+{
+       struct mpc_dma *mdma = (void *)data;
+       unsigned long flags;
+       uint es;
+
        spin_lock_irqsave(&mdma->error_status_lock, flags);
        es = mdma->error_status;
        mdma->error_status = 0;
@@ -370,35 +415,7 @@ static void mpc_dma_tasklet(unsigned long data)
                        dev_err(mdma->dma.dev, "- Destination Bus Error\n");
        }
 
-       for (i = 0; i < mdma->dma.chancnt; i++) {
-               mchan = &mdma->channels[i];
-
-               /* Get all completed descriptors */
-               spin_lock_irqsave(&mchan->lock, flags);
-               if (!list_empty(&mchan->completed))
-                       list_splice_tail_init(&mchan->completed, &list);
-               spin_unlock_irqrestore(&mchan->lock, flags);
-
-               if (list_empty(&list))
-                       continue;
-
-               /* Execute callbacks and run dependencies */
-               list_for_each_entry(mdesc, &list, node) {
-                       desc = &mdesc->desc;
-
-                       if (desc->callback)
-                               desc->callback(desc->callback_param);
-
-                       last_cookie = desc->cookie;
-                       dma_run_dependencies(desc);
-               }
-
-               /* Free descriptors */
-               spin_lock_irqsave(&mchan->lock, flags);
-               list_splice_tail_init(&list, &mchan->free);
-               mchan->completed_cookie = last_cookie;
-               spin_unlock_irqrestore(&mchan->lock, flags);
-       }
+       mpc_dma_process_completed(mdma);
 }
 
 /* Submit descriptor to hardware */
@@ -563,6 +580,7 @@ static struct dma_async_tx_descriptor *
 mpc_dma_prep_memcpy(struct dma_chan *chan, dma_addr_t dst, dma_addr_t src,
                                        size_t len, unsigned long flags)
 {
+       struct mpc_dma *mdma = dma_chan_to_mpc_dma(chan);
        struct mpc_dma_chan *mchan = dma_chan_to_mpc_dma_chan(chan);
        struct mpc_dma_desc *mdesc = NULL;
        struct mpc_dma_tcd *tcd;
@@ -577,8 +595,11 @@ mpc_dma_prep_memcpy(struct dma_chan *chan, dma_addr_t dst, dma_addr_t src,
        }
        spin_unlock_irqrestore(&mchan->lock, iflags);
 
-       if (!mdesc)
+       if (!mdesc) {
+               /* try to free completed descriptors */
+               mpc_dma_process_completed(mdma);
                return NULL;
+       }
 
        mdesc->error = 0;
        tcd = mdesc->tcd;
@@ -591,7 +612,8 @@ mpc_dma_prep_memcpy(struct dma_chan *chan, dma_addr_t dst, dma_addr_t src,
                tcd->dsize = MPC_DMA_TSIZE_32;
                tcd->soff = 32;
                tcd->doff = 32;
-       } else if (IS_ALIGNED(src | dst | len, 16)) {
+       } else if (!mdma->is_mpc8308 && IS_ALIGNED(src | dst | len, 16)) {
+               /* MPC8308 doesn't support 16 byte transfers */
                tcd->ssize = MPC_DMA_TSIZE_16;
                tcd->dsize = MPC_DMA_TSIZE_16;
                tcd->soff = 16;
@@ -651,6 +673,15 @@ static int __devinit mpc_dma_probe(struct platform_device *op,
                return -EINVAL;
        }
 
+       if (of_device_is_compatible(dn, "fsl,mpc8308-dma")) {
+               mdma->is_mpc8308 = 1;
+               mdma->irq2 = irq_of_parse_and_map(dn, 1);
+               if (mdma->irq2 == NO_IRQ) {
+                       dev_err(dev, "Error mapping IRQ!\n");
+                       return -EINVAL;
+               }
+       }
+
        retval = of_address_to_resource(dn, 0, &res);
        if (retval) {
                dev_err(dev, "Error parsing memory region!\n");
@@ -681,11 +712,23 @@ static int __devinit mpc_dma_probe(struct platform_device *op,
                return -EINVAL;
        }
 
+       if (mdma->is_mpc8308) {
+               retval = devm_request_irq(dev, mdma->irq2, &mpc_dma_irq, 0,
+                               DRV_NAME, mdma);
+               if (retval) {
+                       dev_err(dev, "Error requesting IRQ2!\n");
+                       return -EINVAL;
+               }
+       }
+
        spin_lock_init(&mdma->error_status_lock);
 
        dma = &mdma->dma;
        dma->dev = dev;
-       dma->chancnt = MPC_DMA_CHANNELS;
+       if (!mdma->is_mpc8308)
+               dma->chancnt = MPC_DMA_CHANNELS;
+       else
+               dma->chancnt = 16; /* MPC8308 DMA has only 16 channels */
        dma->device_alloc_chan_resources = mpc_dma_alloc_chan_resources;
        dma->device_free_chan_resources = mpc_dma_free_chan_resources;
        dma->device_issue_pending = mpc_dma_issue_pending;
@@ -721,26 +764,40 @@ static int __devinit mpc_dma_probe(struct platform_device *op,
         * - Round-robin group arbitration,
         * - Round-robin channel arbitration.
         */
-       out_be32(&mdma->regs->dmacr, MPC_DMA_DMACR_EDCG |
-                               MPC_DMA_DMACR_ERGA | MPC_DMA_DMACR_ERCA);
-
-       /* Disable hardware DMA requests */
-       out_be32(&mdma->regs->dmaerqh, 0);
-       out_be32(&mdma->regs->dmaerql, 0);
-
-       /* Disable error interrupts */
-       out_be32(&mdma->regs->dmaeeih, 0);
-       out_be32(&mdma->regs->dmaeeil, 0);
-
-       /* Clear interrupts status */
-       out_be32(&mdma->regs->dmainth, 0xFFFFFFFF);
-       out_be32(&mdma->regs->dmaintl, 0xFFFFFFFF);
-       out_be32(&mdma->regs->dmaerrh, 0xFFFFFFFF);
-       out_be32(&mdma->regs->dmaerrl, 0xFFFFFFFF);
-
-       /* Route interrupts to IPIC */
-       out_be32(&mdma->regs->dmaihsa, 0);
-       out_be32(&mdma->regs->dmailsa, 0);
+       if (!mdma->is_mpc8308) {
+               out_be32(&mdma->regs->dmacr, MPC_DMA_DMACR_EDCG |
+                                       MPC_DMA_DMACR_ERGA | MPC_DMA_DMACR_ERCA);
+
+               /* Disable hardware DMA requests */
+               out_be32(&mdma->regs->dmaerqh, 0);
+               out_be32(&mdma->regs->dmaerql, 0);
+
+               /* Disable error interrupts */
+               out_be32(&mdma->regs->dmaeeih, 0);
+               out_be32(&mdma->regs->dmaeeil, 0);
+
+               /* Clear interrupts status */
+               out_be32(&mdma->regs->dmainth, 0xFFFFFFFF);
+               out_be32(&mdma->regs->dmaintl, 0xFFFFFFFF);
+               out_be32(&mdma->regs->dmaerrh, 0xFFFFFFFF);
+               out_be32(&mdma->regs->dmaerrl, 0xFFFFFFFF);
+
+               /* Route interrupts to IPIC */
+               out_be32(&mdma->regs->dmaihsa, 0);
+               out_be32(&mdma->regs->dmailsa, 0);
+       } else {
+               /* MPC8308 has 16 channels and lacks some registers */
+               out_be32(&mdma->regs->dmacr, MPC_DMA_DMACR_ERCA);
+
+               /* enable snooping */
+               out_be32(&mdma->regs->dmagpor, MPC_DMA_DMAGPOR_SNOOP_ENABLE);
+               /* Disable error interrupts */
+               out_be32(&mdma->regs->dmaeeil, 0);
+
+               /* Clear interrupts status */
+               out_be32(&mdma->regs->dmaintl, 0xFFFF);
+               out_be32(&mdma->regs->dmaerrl, 0xFFFF);
+       }
 
        /* Register DMA engine */
        dev_set_drvdata(dev, mdma);