diff options
Diffstat (limited to 'arch/arm/plat-s3c64xx/dma.c')
-rw-r--r-- | arch/arm/plat-s3c64xx/dma.c | 76 |
1 files changed, 52 insertions, 24 deletions
diff --git a/arch/arm/plat-s3c64xx/dma.c b/arch/arm/plat-s3c64xx/dma.c index 266a10745a85..d554b936fcfb 100644 --- a/arch/arm/plat-s3c64xx/dma.c +++ b/arch/arm/plat-s3c64xx/dma.c | |||
@@ -151,8 +151,6 @@ static void s3c64xx_dma_fill_lli(struct s3c2410_dma_chan *chan, | |||
151 | src = chan->dev_addr; | 151 | src = chan->dev_addr; |
152 | dst = data; | 152 | dst = data; |
153 | control0 = PL080_CONTROL_SRC_AHB2; | 153 | control0 = PL080_CONTROL_SRC_AHB2; |
154 | control0 |= (u32)chan->hw_width << PL080_CONTROL_SWIDTH_SHIFT; | ||
155 | control0 |= 2 << PL080_CONTROL_DWIDTH_SHIFT; | ||
156 | control0 |= PL080_CONTROL_DST_INCR; | 154 | control0 |= PL080_CONTROL_DST_INCR; |
157 | break; | 155 | break; |
158 | 156 | ||
@@ -160,8 +158,6 @@ static void s3c64xx_dma_fill_lli(struct s3c2410_dma_chan *chan, | |||
160 | src = data; | 158 | src = data; |
161 | dst = chan->dev_addr; | 159 | dst = chan->dev_addr; |
162 | control0 = PL080_CONTROL_DST_AHB2; | 160 | control0 = PL080_CONTROL_DST_AHB2; |
163 | control0 |= (u32)chan->hw_width << PL080_CONTROL_DWIDTH_SHIFT; | ||
164 | control0 |= 2 << PL080_CONTROL_SWIDTH_SHIFT; | ||
165 | control0 |= PL080_CONTROL_SRC_INCR; | 161 | control0 |= PL080_CONTROL_SRC_INCR; |
166 | break; | 162 | break; |
167 | default: | 163 | default: |
@@ -173,6 +169,8 @@ static void s3c64xx_dma_fill_lli(struct s3c2410_dma_chan *chan, | |||
173 | control1 = size >> chan->hw_width; /* size in no of xfers */ | 169 | control1 = size >> chan->hw_width; /* size in no of xfers */ |
174 | control0 |= PL080_CONTROL_PROT_SYS; /* always in priv. mode */ | 170 | control0 |= PL080_CONTROL_PROT_SYS; /* always in priv. mode */ |
175 | control0 |= PL080_CONTROL_TC_IRQ_EN; /* always fire IRQ */ | 171 | control0 |= PL080_CONTROL_TC_IRQ_EN; /* always fire IRQ */ |
172 | control0 |= (u32)chan->hw_width << PL080_CONTROL_DWIDTH_SHIFT; | ||
173 | control0 |= (u32)chan->hw_width << PL080_CONTROL_SWIDTH_SHIFT; | ||
176 | 174 | ||
177 | lli->src_addr = src; | 175 | lli->src_addr = src; |
178 | lli->dst_addr = dst; | 176 | lli->dst_addr = dst; |
@@ -339,6 +337,7 @@ int s3c2410_dma_enqueue(unsigned int channel, void *id, | |||
339 | struct s3c64xx_dma_buff *next; | 337 | struct s3c64xx_dma_buff *next; |
340 | struct s3c64xx_dma_buff *buff; | 338 | struct s3c64xx_dma_buff *buff; |
341 | struct pl080s_lli *lli; | 339 | struct pl080s_lli *lli; |
340 | unsigned long flags; | ||
342 | int ret; | 341 | int ret; |
343 | 342 | ||
344 | WARN_ON(!chan); | 343 | WARN_ON(!chan); |
@@ -366,6 +365,8 @@ int s3c2410_dma_enqueue(unsigned int channel, void *id, | |||
366 | 365 | ||
367 | s3c64xx_dma_fill_lli(chan, lli, data, size); | 366 | s3c64xx_dma_fill_lli(chan, lli, data, size); |
368 | 367 | ||
368 | local_irq_save(flags); | ||
369 | |||
369 | if ((next = chan->next) != NULL) { | 370 | if ((next = chan->next) != NULL) { |
370 | struct s3c64xx_dma_buff *end = chan->end; | 371 | struct s3c64xx_dma_buff *end = chan->end; |
371 | struct pl080s_lli *endlli = end->lli; | 372 | struct pl080s_lli *endlli = end->lli; |
@@ -397,6 +398,8 @@ int s3c2410_dma_enqueue(unsigned int channel, void *id, | |||
397 | s3c64xx_lli_to_regs(chan, lli); | 398 | s3c64xx_lli_to_regs(chan, lli); |
398 | } | 399 | } |
399 | 400 | ||
401 | local_irq_restore(flags); | ||
402 | |||
400 | show_lli(lli); | 403 | show_lli(lli); |
401 | 404 | ||
402 | dbg_showchan(chan); | 405 | dbg_showchan(chan); |
@@ -560,26 +563,11 @@ int s3c2410_dma_free(unsigned int channel, struct s3c2410_dma_client *client) | |||
560 | 563 | ||
561 | EXPORT_SYMBOL(s3c2410_dma_free); | 564 | EXPORT_SYMBOL(s3c2410_dma_free); |
562 | 565 | ||
563 | |||
564 | static void s3c64xx_dma_tcirq(struct s3c64xx_dmac *dmac, int offs) | ||
565 | { | ||
566 | struct s3c2410_dma_chan *chan = dmac->channels + offs; | ||
567 | |||
568 | /* note, we currently do not bother to work out which buffer | ||
569 | * or buffers have been completed since the last tc-irq. */ | ||
570 | |||
571 | if (chan->callback_fn) | ||
572 | (chan->callback_fn)(chan, chan->curr->pw, 0, S3C2410_RES_OK); | ||
573 | } | ||
574 | |||
575 | static void s3c64xx_dma_errirq(struct s3c64xx_dmac *dmac, int offs) | ||
576 | { | ||
577 | printk(KERN_DEBUG "%s: offs %d\n", __func__, offs); | ||
578 | } | ||
579 | |||
580 | static irqreturn_t s3c64xx_dma_irq(int irq, void *pw) | 566 | static irqreturn_t s3c64xx_dma_irq(int irq, void *pw) |
581 | { | 567 | { |
582 | struct s3c64xx_dmac *dmac = pw; | 568 | struct s3c64xx_dmac *dmac = pw; |
569 | struct s3c2410_dma_chan *chan; | ||
570 | enum s3c2410_dma_buffresult res; | ||
583 | u32 tcstat, errstat; | 571 | u32 tcstat, errstat; |
584 | u32 bit; | 572 | u32 bit; |
585 | int offs; | 573 | int offs; |
@@ -588,14 +576,54 @@ static irqreturn_t s3c64xx_dma_irq(int irq, void *pw) | |||
588 | errstat = readl(dmac->regs + PL080_ERR_STATUS); | 576 | errstat = readl(dmac->regs + PL080_ERR_STATUS); |
589 | 577 | ||
590 | for (offs = 0, bit = 1; offs < 8; offs++, bit <<= 1) { | 578 | for (offs = 0, bit = 1; offs < 8; offs++, bit <<= 1) { |
579 | struct s3c64xx_dma_buff *buff; | ||
580 | |||
581 | if (!(errstat & bit) && !(tcstat & bit)) | ||
582 | continue; | ||
583 | |||
584 | chan = dmac->channels + offs; | ||
585 | res = S3C2410_RES_ERR; | ||
586 | |||
591 | if (tcstat & bit) { | 587 | if (tcstat & bit) { |
592 | writel(bit, dmac->regs + PL080_TC_CLEAR); | 588 | writel(bit, dmac->regs + PL080_TC_CLEAR); |
593 | s3c64xx_dma_tcirq(dmac, offs); | 589 | res = S3C2410_RES_OK; |
594 | } | 590 | } |
595 | 591 | ||
596 | if (errstat & bit) { | 592 | if (errstat & bit) |
597 | s3c64xx_dma_errirq(dmac, offs); | ||
598 | writel(bit, dmac->regs + PL080_ERR_CLEAR); | 593 | writel(bit, dmac->regs + PL080_ERR_CLEAR); |
594 | |||
595 | /* 'next' points to the buffer that is next to the | ||
596 | * currently active buffer. | ||
597 | * For CIRCULAR queues, 'next' will be same as 'curr' | ||
598 | * when 'end' is the active buffer. | ||
599 | */ | ||
600 | buff = chan->curr; | ||
601 | while (buff && buff != chan->next | ||
602 | && buff->next != chan->next) | ||
603 | buff = buff->next; | ||
604 | |||
605 | if (!buff) | ||
606 | BUG(); | ||
607 | |||
608 | if (buff == chan->next) | ||
609 | buff = chan->end; | ||
610 | |||
611 | s3c64xx_dma_bufffdone(chan, buff, res); | ||
612 | |||
613 | /* Free the node and update curr, if non-circular queue */ | ||
614 | if (!(chan->flags & S3C2410_DMAF_CIRCULAR)) { | ||
615 | chan->curr = buff->next; | ||
616 | s3c64xx_dma_freebuff(buff); | ||
617 | } | ||
618 | |||
619 | /* Update 'next' */ | ||
620 | buff = chan->next; | ||
621 | if (chan->next == chan->end) { | ||
622 | chan->next = chan->curr; | ||
623 | if (!(chan->flags & S3C2410_DMAF_CIRCULAR)) | ||
624 | chan->end = NULL; | ||
625 | } else { | ||
626 | chan->next = buff->next; | ||
599 | } | 627 | } |
600 | } | 628 | } |
601 | 629 | ||