diff options
author | Kyungmin Park <kyungmin.park@samsung.com> | 2010-09-28 06:27:15 -0400 |
---|---|---|
committer | David Woodhouse <David.Woodhouse@intel.com> | 2010-10-24 19:52:26 -0400 |
commit | e23abf4b774322cbeb3f15cb95756544a64dda5e (patch) | |
tree | 2fd435efec17a4879275d32eafb0bbba830c07c0 | |
parent | dcf08227e964a53a2cb39130b74842c7dcb6adde (diff) |
mtd: OneNAND: S5PC110: Implement DMA interrupt method
Implement DMA interrupt method. previous time it polls the DMA status.
It can reduce the CPU power but decrease the performance a little.
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
-rw-r--r-- | drivers/mtd/onenand/samsung.c | 85 |
1 files changed, 84 insertions, 1 deletions
diff --git a/drivers/mtd/onenand/samsung.c b/drivers/mtd/onenand/samsung.c index afdb4c544132..214ed1e763b6 100644 --- a/drivers/mtd/onenand/samsung.c +++ b/drivers/mtd/onenand/samsung.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <linux/mtd/onenand.h> | 22 | #include <linux/mtd/onenand.h> |
23 | #include <linux/mtd/partitions.h> | 23 | #include <linux/mtd/partitions.h> |
24 | #include <linux/dma-mapping.h> | 24 | #include <linux/dma-mapping.h> |
25 | #include <linux/interrupt.h> | ||
25 | 26 | ||
26 | #include <asm/mach/flash.h> | 27 | #include <asm/mach/flash.h> |
27 | #include <plat/regs-onenand.h> | 28 | #include <plat/regs-onenand.h> |
@@ -81,6 +82,17 @@ enum soc_type { | |||
81 | #define S5PC110_DMA_TRANS_CMD 0x418 | 82 | #define S5PC110_DMA_TRANS_CMD 0x418 |
82 | #define S5PC110_DMA_TRANS_STATUS 0x41C | 83 | #define S5PC110_DMA_TRANS_STATUS 0x41C |
83 | #define S5PC110_DMA_TRANS_DIR 0x420 | 84 | #define S5PC110_DMA_TRANS_DIR 0x420 |
85 | #define S5PC110_INTC_DMA_CLR 0x1004 | ||
86 | #define S5PC110_INTC_ONENAND_CLR 0x1008 | ||
87 | #define S5PC110_INTC_DMA_MASK 0x1024 | ||
88 | #define S5PC110_INTC_ONENAND_MASK 0x1028 | ||
89 | #define S5PC110_INTC_DMA_PEND 0x1044 | ||
90 | #define S5PC110_INTC_ONENAND_PEND 0x1048 | ||
91 | #define S5PC110_INTC_DMA_STATUS 0x1064 | ||
92 | #define S5PC110_INTC_ONENAND_STATUS 0x1068 | ||
93 | |||
94 | #define S5PC110_INTC_DMA_TD (1 << 24) | ||
95 | #define S5PC110_INTC_DMA_TE (1 << 16) | ||
84 | 96 | ||
85 | #define S5PC110_DMA_CFG_SINGLE (0x0 << 16) | 97 | #define S5PC110_DMA_CFG_SINGLE (0x0 << 16) |
86 | #define S5PC110_DMA_CFG_4BURST (0x2 << 16) | 98 | #define S5PC110_DMA_CFG_4BURST (0x2 << 16) |
@@ -134,6 +146,7 @@ struct s3c_onenand { | |||
134 | void __iomem *dma_addr; | 146 | void __iomem *dma_addr; |
135 | struct resource *dma_res; | 147 | struct resource *dma_res; |
136 | unsigned long phys_base; | 148 | unsigned long phys_base; |
149 | struct completion complete; | ||
137 | #ifdef CONFIG_MTD_PARTITIONS | 150 | #ifdef CONFIG_MTD_PARTITIONS |
138 | struct mtd_partition *parts; | 151 | struct mtd_partition *parts; |
139 | #endif | 152 | #endif |
@@ -531,7 +544,9 @@ static int onenand_write_bufferram(struct mtd_info *mtd, int area, | |||
531 | return 0; | 544 | return 0; |
532 | } | 545 | } |
533 | 546 | ||
534 | static int s5pc110_dma_ops(void *dst, void *src, size_t count, int direction) | 547 | static int (*s5pc110_dma_ops)(void *dst, void *src, size_t count, int direction); |
548 | |||
549 | static int s5pc110_dma_poll(void *dst, void *src, size_t count, int direction) | ||
535 | { | 550 | { |
536 | void __iomem *base = onenand->dma_addr; | 551 | void __iomem *base = onenand->dma_addr; |
537 | int status; | 552 | int status; |
@@ -575,6 +590,60 @@ static int s5pc110_dma_ops(void *dst, void *src, size_t count, int direction) | |||
575 | return 0; | 590 | return 0; |
576 | } | 591 | } |
577 | 592 | ||
593 | static irqreturn_t s5pc110_onenand_irq(int irq, void *data) | ||
594 | { | ||
595 | void __iomem *base = onenand->dma_addr; | ||
596 | int status, cmd = 0; | ||
597 | |||
598 | status = readl(base + S5PC110_INTC_DMA_STATUS); | ||
599 | |||
600 | if (likely(status & S5PC110_INTC_DMA_TD)) | ||
601 | cmd = S5PC110_DMA_TRANS_CMD_TDC; | ||
602 | |||
603 | if (unlikely(status & S5PC110_INTC_DMA_TE)) | ||
604 | cmd = S5PC110_DMA_TRANS_CMD_TEC; | ||
605 | |||
606 | writel(cmd, base + S5PC110_DMA_TRANS_CMD); | ||
607 | writel(status, base + S5PC110_INTC_DMA_CLR); | ||
608 | |||
609 | if (!onenand->complete.done) | ||
610 | complete(&onenand->complete); | ||
611 | |||
612 | return IRQ_HANDLED; | ||
613 | } | ||
614 | |||
615 | static int s5pc110_dma_irq(void *dst, void *src, size_t count, int direction) | ||
616 | { | ||
617 | void __iomem *base = onenand->dma_addr; | ||
618 | int status; | ||
619 | |||
620 | status = readl(base + S5PC110_INTC_DMA_MASK); | ||
621 | if (status) { | ||
622 | status &= ~(S5PC110_INTC_DMA_TD | S5PC110_INTC_DMA_TE); | ||
623 | writel(status, base + S5PC110_INTC_DMA_MASK); | ||
624 | } | ||
625 | |||
626 | writel(src, base + S5PC110_DMA_SRC_ADDR); | ||
627 | writel(dst, base + S5PC110_DMA_DST_ADDR); | ||
628 | |||
629 | if (direction == S5PC110_DMA_DIR_READ) { | ||
630 | writel(S5PC110_DMA_SRC_CFG_READ, base + S5PC110_DMA_SRC_CFG); | ||
631 | writel(S5PC110_DMA_DST_CFG_READ, base + S5PC110_DMA_DST_CFG); | ||
632 | } else { | ||
633 | writel(S5PC110_DMA_SRC_CFG_WRITE, base + S5PC110_DMA_SRC_CFG); | ||
634 | writel(S5PC110_DMA_DST_CFG_WRITE, base + S5PC110_DMA_DST_CFG); | ||
635 | } | ||
636 | |||
637 | writel(count, base + S5PC110_DMA_TRANS_SIZE); | ||
638 | writel(direction, base + S5PC110_DMA_TRANS_DIR); | ||
639 | |||
640 | writel(S5PC110_DMA_TRANS_CMD_TR, base + S5PC110_DMA_TRANS_CMD); | ||
641 | |||
642 | wait_for_completion_timeout(&onenand->complete, msecs_to_jiffies(20)); | ||
643 | |||
644 | return 0; | ||
645 | } | ||
646 | |||
578 | static int s5pc110_read_bufferram(struct mtd_info *mtd, int area, | 647 | static int s5pc110_read_bufferram(struct mtd_info *mtd, int area, |
579 | unsigned char *buffer, int offset, size_t count) | 648 | unsigned char *buffer, int offset, size_t count) |
580 | { | 649 | { |
@@ -919,6 +988,20 @@ static int s3c_onenand_probe(struct platform_device *pdev) | |||
919 | } | 988 | } |
920 | 989 | ||
921 | onenand->phys_base = onenand->base_res->start; | 990 | onenand->phys_base = onenand->base_res->start; |
991 | |||
992 | s5pc110_dma_ops = s5pc110_dma_poll; | ||
993 | /* Interrupt support */ | ||
994 | r = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | ||
995 | if (r) { | ||
996 | init_completion(&onenand->complete); | ||
997 | s5pc110_dma_ops = s5pc110_dma_irq; | ||
998 | err = request_irq(r->start, s5pc110_onenand_irq, | ||
999 | IRQF_SHARED, "onenand", &onenand); | ||
1000 | if (err) { | ||
1001 | dev_err(&pdev->dev, "failed to get irq\n"); | ||
1002 | goto scan_failed; | ||
1003 | } | ||
1004 | } | ||
922 | } | 1005 | } |
923 | 1006 | ||
924 | if (onenand_scan(mtd, 1)) { | 1007 | if (onenand_scan(mtd, 1)) { |