diff options
author | Ben Dooks <ben-linux@fluff.org> | 2006-08-18 10:32:10 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2006-08-18 10:32:10 -0400 |
commit | f57e1abd1bb297994c7398478b4c37e628095243 (patch) | |
tree | f25447fd37bf0b56c5aa8b197570ac802d8ac904 | |
parent | a0c5a64552e3c57d7f9eb593c6ce21a285ac86b4 (diff) |
[ARM] 3753/1: S3C24XX: DMA fixes
Patch from Ben Dooks
A number of small issues with the S3C24XX DMA have
cropped up, which this patch fixes. These are:
- check wether we can load another buff in start
- update state handling in s3c2410_dma_lastxfer
- only reload in irq if channel is not idle
- more informative timeout errors (add source)
- do not call request_irq() with irqs locked
- added waitforstop function
The patch also adds a S3C2410_DMAOP_STARTED for
the occasions when the driver wants to ensure that
the DMA system load state is resynced after loading.
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
-rw-r--r-- | arch/arm/mach-s3c2410/dma.c | 163 | ||||
-rw-r--r-- | include/asm-arm/arch-s3c2410/dma.h | 1 |
2 files changed, 145 insertions, 19 deletions
diff --git a/arch/arm/mach-s3c2410/dma.c b/arch/arm/mach-s3c2410/dma.c index 094cc52745c5..25855452fe8c 100644 --- a/arch/arm/mach-s3c2410/dma.c +++ b/arch/arm/mach-s3c2410/dma.c | |||
@@ -112,7 +112,7 @@ dmadbg_capture(s3c2410_dma_chan_t *chan, struct s3c2410_dma_regstate *regs) | |||
112 | } | 112 | } |
113 | 113 | ||
114 | static void | 114 | static void |
115 | dmadbg_showregs(const char *fname, int line, s3c2410_dma_chan_t *chan, | 115 | dmadbg_dumpregs(const char *fname, int line, s3c2410_dma_chan_t *chan, |
116 | struct s3c2410_dma_regstate *regs) | 116 | struct s3c2410_dma_regstate *regs) |
117 | { | 117 | { |
118 | printk(KERN_DEBUG "dma%d: %s:%d: DCSRC=%08lx, DISRC=%08lx, DSTAT=%08lx DMT=%02lx, DCON=%08lx\n", | 118 | printk(KERN_DEBUG "dma%d: %s:%d: DCSRC=%08lx, DISRC=%08lx, DSTAT=%08lx DMT=%02lx, DCON=%08lx\n", |
@@ -132,7 +132,16 @@ dmadbg_showchan(const char *fname, int line, s3c2410_dma_chan_t *chan) | |||
132 | chan->number, fname, line, chan->load_state, | 132 | chan->number, fname, line, chan->load_state, |
133 | chan->curr, chan->next, chan->end); | 133 | chan->curr, chan->next, chan->end); |
134 | 134 | ||
135 | dmadbg_showregs(fname, line, chan, &state); | 135 | dmadbg_dumpregs(fname, line, chan, &state); |
136 | } | ||
137 | |||
138 | static void | ||
139 | dmadbg_showregs(const char *fname, int line, s3c2410_dma_chan_t *chan) | ||
140 | { | ||
141 | struct s3c2410_dma_regstate state; | ||
142 | |||
143 | dmadbg_capture(chan, &state); | ||
144 | dmadbg_dumpregs(fname, line, chan, &state); | ||
136 | } | 145 | } |
137 | 146 | ||
138 | #define dbg_showregs(chan) dmadbg_showregs(__FUNCTION__, __LINE__, (chan)) | 147 | #define dbg_showregs(chan) dmadbg_showregs(__FUNCTION__, __LINE__, (chan)) |
@@ -253,10 +262,14 @@ s3c2410_dma_loadbuffer(s3c2410_dma_chan_t *chan, | |||
253 | buf->next); | 262 | buf->next); |
254 | reload = (buf->next == NULL) ? S3C2410_DCON_NORELOAD : 0; | 263 | reload = (buf->next == NULL) ? S3C2410_DCON_NORELOAD : 0; |
255 | } else { | 264 | } else { |
256 | pr_debug("load_state is %d => autoreload\n", chan->load_state); | 265 | //pr_debug("load_state is %d => autoreload\n", chan->load_state); |
257 | reload = S3C2410_DCON_AUTORELOAD; | 266 | reload = S3C2410_DCON_AUTORELOAD; |
258 | } | 267 | } |
259 | 268 | ||
269 | if ((buf->data & 0xf0000000) != 0x30000000) { | ||
270 | dmawarn("dmaload: buffer is %p\n", (void *)buf->data); | ||
271 | } | ||
272 | |||
260 | writel(buf->data, chan->addr_reg); | 273 | writel(buf->data, chan->addr_reg); |
261 | 274 | ||
262 | dma_wrreg(chan, S3C2410_DMA_DCON, | 275 | dma_wrreg(chan, S3C2410_DMA_DCON, |
@@ -370,7 +383,7 @@ static int s3c2410_dma_start(s3c2410_dma_chan_t *chan) | |||
370 | tmp |= S3C2410_DMASKTRIG_ON; | 383 | tmp |= S3C2410_DMASKTRIG_ON; |
371 | dma_wrreg(chan, S3C2410_DMA_DMASKTRIG, tmp); | 384 | dma_wrreg(chan, S3C2410_DMA_DMASKTRIG, tmp); |
372 | 385 | ||
373 | pr_debug("wrote %08lx to DMASKTRIG\n", tmp); | 386 | pr_debug("dma%d: %08lx to DMASKTRIG\n", chan->number, tmp); |
374 | 387 | ||
375 | #if 0 | 388 | #if 0 |
376 | /* the dma buffer loads should take care of clearing the AUTO | 389 | /* the dma buffer loads should take care of clearing the AUTO |
@@ -384,7 +397,30 @@ static int s3c2410_dma_start(s3c2410_dma_chan_t *chan) | |||
384 | 397 | ||
385 | dbg_showchan(chan); | 398 | dbg_showchan(chan); |
386 | 399 | ||
400 | /* if we've only loaded one buffer onto the channel, then chec | ||
401 | * to see if we have another, and if so, try and load it so when | ||
402 | * the first buffer is finished, the new one will be loaded onto | ||
403 | * the channel */ | ||
404 | |||
405 | if (chan->next != NULL) { | ||
406 | if (chan->load_state == S3C2410_DMALOAD_1LOADED) { | ||
407 | |||
408 | if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { | ||
409 | pr_debug("%s: buff not yet loaded, no more todo\n", | ||
410 | __FUNCTION__); | ||
411 | } else { | ||
412 | chan->load_state = S3C2410_DMALOAD_1RUNNING; | ||
413 | s3c2410_dma_loadbuffer(chan, chan->next); | ||
414 | } | ||
415 | |||
416 | } else if (chan->load_state == S3C2410_DMALOAD_1RUNNING) { | ||
417 | s3c2410_dma_loadbuffer(chan, chan->next); | ||
418 | } | ||
419 | } | ||
420 | |||
421 | |||
387 | local_irq_restore(flags); | 422 | local_irq_restore(flags); |
423 | |||
388 | return 0; | 424 | return 0; |
389 | } | 425 | } |
390 | 426 | ||
@@ -436,12 +472,11 @@ int s3c2410_dma_enqueue(unsigned int channel, void *id, | |||
436 | buf = kmem_cache_alloc(dma_kmem, GFP_ATOMIC); | 472 | buf = kmem_cache_alloc(dma_kmem, GFP_ATOMIC); |
437 | if (buf == NULL) { | 473 | if (buf == NULL) { |
438 | pr_debug("%s: out of memory (%ld alloc)\n", | 474 | pr_debug("%s: out of memory (%ld alloc)\n", |
439 | __FUNCTION__, sizeof(*buf)); | 475 | __FUNCTION__, (long)sizeof(*buf)); |
440 | return -ENOMEM; | 476 | return -ENOMEM; |
441 | } | 477 | } |
442 | 478 | ||
443 | pr_debug("%s: new buffer %p\n", __FUNCTION__, buf); | 479 | //pr_debug("%s: new buffer %p\n", __FUNCTION__, buf); |
444 | |||
445 | //dbg_showchan(chan); | 480 | //dbg_showchan(chan); |
446 | 481 | ||
447 | buf->next = NULL; | 482 | buf->next = NULL; |
@@ -537,14 +572,20 @@ s3c2410_dma_lastxfer(s3c2410_dma_chan_t *chan) | |||
537 | case S3C2410_DMALOAD_1LOADED: | 572 | case S3C2410_DMALOAD_1LOADED: |
538 | if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { | 573 | if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { |
539 | /* flag error? */ | 574 | /* flag error? */ |
540 | printk(KERN_ERR "dma%d: timeout waiting for load\n", | 575 | printk(KERN_ERR "dma%d: timeout waiting for load (%s)\n", |
541 | chan->number); | 576 | chan->number, __FUNCTION__); |
542 | return; | 577 | return; |
543 | } | 578 | } |
544 | break; | 579 | break; |
545 | 580 | ||
581 | case S3C2410_DMALOAD_1LOADED_1RUNNING: | ||
582 | /* I belive in this case we do not have anything to do | ||
583 | * until the next buffer comes along, and we turn off the | ||
584 | * reload */ | ||
585 | return; | ||
586 | |||
546 | default: | 587 | default: |
547 | pr_debug("dma%d: lastxfer: unhandled load_state %d with no next", | 588 | pr_debug("dma%d: lastxfer: unhandled load_state %d with no next\n", |
548 | chan->number, chan->load_state); | 589 | chan->number, chan->load_state); |
549 | return; | 590 | return; |
550 | 591 | ||
@@ -629,7 +670,14 @@ s3c2410_dma_irq(int irq, void *devpw, struct pt_regs *regs) | |||
629 | } else { | 670 | } else { |
630 | } | 671 | } |
631 | 672 | ||
632 | if (chan->next != NULL) { | 673 | /* only reload if the channel is still running... our buffer done |
674 | * routine may have altered the state by requesting the dma channel | ||
675 | * to stop or shutdown... */ | ||
676 | |||
677 | /* todo: check that when the channel is shut-down from inside this | ||
678 | * function, we cope with unsetting reload, etc */ | ||
679 | |||
680 | if (chan->next != NULL && chan->state != S3C2410_DMA_IDLE) { | ||
633 | unsigned long flags; | 681 | unsigned long flags; |
634 | 682 | ||
635 | switch (chan->load_state) { | 683 | switch (chan->load_state) { |
@@ -644,8 +692,8 @@ s3c2410_dma_irq(int irq, void *devpw, struct pt_regs *regs) | |||
644 | case S3C2410_DMALOAD_1LOADED: | 692 | case S3C2410_DMALOAD_1LOADED: |
645 | if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { | 693 | if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { |
646 | /* flag error? */ | 694 | /* flag error? */ |
647 | printk(KERN_ERR "dma%d: timeout waiting for load\n", | 695 | printk(KERN_ERR "dma%d: timeout waiting for load (%s)\n", |
648 | chan->number); | 696 | chan->number, __FUNCTION__); |
649 | return IRQ_HANDLED; | 697 | return IRQ_HANDLED; |
650 | } | 698 | } |
651 | 699 | ||
@@ -678,8 +726,6 @@ s3c2410_dma_irq(int irq, void *devpw, struct pt_regs *regs) | |||
678 | return IRQ_HANDLED; | 726 | return IRQ_HANDLED; |
679 | } | 727 | } |
680 | 728 | ||
681 | |||
682 | |||
683 | /* s3c2410_request_dma | 729 | /* s3c2410_request_dma |
684 | * | 730 | * |
685 | * get control of an dma channel | 731 | * get control of an dma channel |
@@ -718,11 +764,17 @@ int s3c2410_dma_request(unsigned int channel, s3c2410_dma_client_t *client, | |||
718 | pr_debug("dma%d: %s : requesting irq %d\n", | 764 | pr_debug("dma%d: %s : requesting irq %d\n", |
719 | channel, __FUNCTION__, chan->irq); | 765 | channel, __FUNCTION__, chan->irq); |
720 | 766 | ||
767 | chan->irq_claimed = 1; | ||
768 | local_irq_restore(flags); | ||
769 | |||
721 | err = request_irq(chan->irq, s3c2410_dma_irq, IRQF_DISABLED, | 770 | err = request_irq(chan->irq, s3c2410_dma_irq, IRQF_DISABLED, |
722 | client->name, (void *)chan); | 771 | client->name, (void *)chan); |
723 | 772 | ||
773 | local_irq_save(flags); | ||
774 | |||
724 | if (err) { | 775 | if (err) { |
725 | chan->in_use = 0; | 776 | chan->in_use = 0; |
777 | chan->irq_claimed = 0; | ||
726 | local_irq_restore(flags); | 778 | local_irq_restore(flags); |
727 | 779 | ||
728 | printk(KERN_ERR "%s: cannot get IRQ %d for DMA %d\n", | 780 | printk(KERN_ERR "%s: cannot get IRQ %d for DMA %d\n", |
@@ -730,7 +782,6 @@ int s3c2410_dma_request(unsigned int channel, s3c2410_dma_client_t *client, | |||
730 | return err; | 782 | return err; |
731 | } | 783 | } |
732 | 784 | ||
733 | chan->irq_claimed = 1; | ||
734 | chan->irq_enabled = 1; | 785 | chan->irq_enabled = 1; |
735 | } | 786 | } |
736 | 787 | ||
@@ -810,6 +861,7 @@ static int s3c2410_dma_dostop(s3c2410_dma_chan_t *chan) | |||
810 | 861 | ||
811 | tmp = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG); | 862 | tmp = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG); |
812 | tmp |= S3C2410_DMASKTRIG_STOP; | 863 | tmp |= S3C2410_DMASKTRIG_STOP; |
864 | //tmp &= ~S3C2410_DMASKTRIG_ON; | ||
813 | dma_wrreg(chan, S3C2410_DMA_DMASKTRIG, tmp); | 865 | dma_wrreg(chan, S3C2410_DMA_DMASKTRIG, tmp); |
814 | 866 | ||
815 | #if 0 | 867 | #if 0 |
@@ -819,6 +871,7 @@ static int s3c2410_dma_dostop(s3c2410_dma_chan_t *chan) | |||
819 | dma_wrreg(chan, S3C2410_DMA_DCON, tmp); | 871 | dma_wrreg(chan, S3C2410_DMA_DCON, tmp); |
820 | #endif | 872 | #endif |
821 | 873 | ||
874 | /* should stop do this, or should we wait for flush? */ | ||
822 | chan->state = S3C2410_DMA_IDLE; | 875 | chan->state = S3C2410_DMA_IDLE; |
823 | chan->load_state = S3C2410_DMALOAD_NONE; | 876 | chan->load_state = S3C2410_DMALOAD_NONE; |
824 | 877 | ||
@@ -827,6 +880,22 @@ static int s3c2410_dma_dostop(s3c2410_dma_chan_t *chan) | |||
827 | return 0; | 880 | return 0; |
828 | } | 881 | } |
829 | 882 | ||
883 | void s3c2410_dma_waitforstop(s3c2410_dma_chan_t *chan) | ||
884 | { | ||
885 | unsigned long tmp; | ||
886 | unsigned int timeout = 0x10000; | ||
887 | |||
888 | while (timeout-- > 0) { | ||
889 | tmp = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG); | ||
890 | |||
891 | if (!(tmp & S3C2410_DMASKTRIG_ON)) | ||
892 | return; | ||
893 | } | ||
894 | |||
895 | pr_debug("dma%d: failed to stop?\n", chan->number); | ||
896 | } | ||
897 | |||
898 | |||
830 | /* s3c2410_dma_flush | 899 | /* s3c2410_dma_flush |
831 | * | 900 | * |
832 | * stop the channel, and remove all current and pending transfers | 901 | * stop the channel, and remove all current and pending transfers |
@@ -837,7 +906,9 @@ static int s3c2410_dma_flush(s3c2410_dma_chan_t *chan) | |||
837 | s3c2410_dma_buf_t *buf, *next; | 906 | s3c2410_dma_buf_t *buf, *next; |
838 | unsigned long flags; | 907 | unsigned long flags; |
839 | 908 | ||
840 | pr_debug("%s:\n", __FUNCTION__); | 909 | pr_debug("%s: chan %p (%d)\n", __FUNCTION__, chan, chan->number); |
910 | |||
911 | dbg_showchan(chan); | ||
841 | 912 | ||
842 | local_irq_save(flags); | 913 | local_irq_save(flags); |
843 | 914 | ||
@@ -864,11 +935,64 @@ static int s3c2410_dma_flush(s3c2410_dma_chan_t *chan) | |||
864 | } | 935 | } |
865 | } | 936 | } |
866 | 937 | ||
938 | dbg_showregs(chan); | ||
939 | |||
940 | s3c2410_dma_waitforstop(chan); | ||
941 | |||
942 | #if 0 | ||
943 | /* should also clear interrupts, according to WinCE BSP */ | ||
944 | { | ||
945 | unsigned long tmp; | ||
946 | |||
947 | tmp = dma_rdreg(chan, S3C2410_DMA_DCON); | ||
948 | tmp |= S3C2410_DCON_NORELOAD; | ||
949 | dma_wrreg(chan, S3C2410_DMA_DCON, tmp); | ||
950 | } | ||
951 | #endif | ||
952 | |||
953 | dbg_showregs(chan); | ||
954 | |||
867 | local_irq_restore(flags); | 955 | local_irq_restore(flags); |
868 | 956 | ||
869 | return 0; | 957 | return 0; |
870 | } | 958 | } |
871 | 959 | ||
960 | int | ||
961 | s3c2410_dma_started(s3c2410_dma_chan_t *chan) | ||
962 | { | ||
963 | unsigned long flags; | ||
964 | |||
965 | local_irq_save(flags); | ||
966 | |||
967 | dbg_showchan(chan); | ||
968 | |||
969 | /* if we've only loaded one buffer onto the channel, then chec | ||
970 | * to see if we have another, and if so, try and load it so when | ||
971 | * the first buffer is finished, the new one will be loaded onto | ||
972 | * the channel */ | ||
973 | |||
974 | if (chan->next != NULL) { | ||
975 | if (chan->load_state == S3C2410_DMALOAD_1LOADED) { | ||
976 | |||
977 | if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { | ||
978 | pr_debug("%s: buff not yet loaded, no more todo\n", | ||
979 | __FUNCTION__); | ||
980 | } else { | ||
981 | chan->load_state = S3C2410_DMALOAD_1RUNNING; | ||
982 | s3c2410_dma_loadbuffer(chan, chan->next); | ||
983 | } | ||
984 | |||
985 | } else if (chan->load_state == S3C2410_DMALOAD_1RUNNING) { | ||
986 | s3c2410_dma_loadbuffer(chan, chan->next); | ||
987 | } | ||
988 | } | ||
989 | |||
990 | |||
991 | local_irq_restore(flags); | ||
992 | |||
993 | return 0; | ||
994 | |||
995 | } | ||
872 | 996 | ||
873 | int | 997 | int |
874 | s3c2410_dma_ctrl(dmach_t channel, s3c2410_chan_op_t op) | 998 | s3c2410_dma_ctrl(dmach_t channel, s3c2410_chan_op_t op) |
@@ -885,14 +1009,15 @@ s3c2410_dma_ctrl(dmach_t channel, s3c2410_chan_op_t op) | |||
885 | return s3c2410_dma_dostop(chan); | 1009 | return s3c2410_dma_dostop(chan); |
886 | 1010 | ||
887 | case S3C2410_DMAOP_PAUSE: | 1011 | case S3C2410_DMAOP_PAUSE: |
888 | return -ENOENT; | ||
889 | |||
890 | case S3C2410_DMAOP_RESUME: | 1012 | case S3C2410_DMAOP_RESUME: |
891 | return -ENOENT; | 1013 | return -ENOENT; |
892 | 1014 | ||
893 | case S3C2410_DMAOP_FLUSH: | 1015 | case S3C2410_DMAOP_FLUSH: |
894 | return s3c2410_dma_flush(chan); | 1016 | return s3c2410_dma_flush(chan); |
895 | 1017 | ||
1018 | case S3C2410_DMAOP_STARTED: | ||
1019 | return s3c2410_dma_started(chan); | ||
1020 | |||
896 | case S3C2410_DMAOP_TIMEOUT: | 1021 | case S3C2410_DMAOP_TIMEOUT: |
897 | return 0; | 1022 | return 0; |
898 | 1023 | ||
diff --git a/include/asm-arm/arch-s3c2410/dma.h b/include/asm-arm/arch-s3c2410/dma.h index 72964f9b8414..7463fd5252ce 100644 --- a/include/asm-arm/arch-s3c2410/dma.h +++ b/include/asm-arm/arch-s3c2410/dma.h | |||
@@ -104,6 +104,7 @@ enum s3c2410_chan_op_e { | |||
104 | S3C2410_DMAOP_RESUME, | 104 | S3C2410_DMAOP_RESUME, |
105 | S3C2410_DMAOP_FLUSH, | 105 | S3C2410_DMAOP_FLUSH, |
106 | S3C2410_DMAOP_TIMEOUT, /* internal signal to handler */ | 106 | S3C2410_DMAOP_TIMEOUT, /* internal signal to handler */ |
107 | S3C2410_DMAOP_STARTED, /* indicate channel started */ | ||
107 | }; | 108 | }; |
108 | 109 | ||
109 | typedef enum s3c2410_chan_op_e s3c2410_chan_op_t; | 110 | typedef enum s3c2410_chan_op_e s3c2410_chan_op_t; |