Skip to content

Commit

Permalink
mtd: OneNAND: S5PC110: Implement DMA interrupt method
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Kyungmin Park authored and David Woodhouse committed Oct 24, 2010
1 parent dcf0822 commit e23abf4
Showing 1 changed file with 84 additions and 1 deletion.
85 changes: 84 additions & 1 deletion drivers/mtd/onenand/samsung.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <linux/mtd/onenand.h>
#include <linux/mtd/partitions.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>

#include <asm/mach/flash.h>
#include <plat/regs-onenand.h>
Expand Down Expand Up @@ -81,6 +82,17 @@ enum soc_type {
#define S5PC110_DMA_TRANS_CMD 0x418
#define S5PC110_DMA_TRANS_STATUS 0x41C
#define S5PC110_DMA_TRANS_DIR 0x420
#define S5PC110_INTC_DMA_CLR 0x1004
#define S5PC110_INTC_ONENAND_CLR 0x1008
#define S5PC110_INTC_DMA_MASK 0x1024
#define S5PC110_INTC_ONENAND_MASK 0x1028
#define S5PC110_INTC_DMA_PEND 0x1044
#define S5PC110_INTC_ONENAND_PEND 0x1048
#define S5PC110_INTC_DMA_STATUS 0x1064
#define S5PC110_INTC_ONENAND_STATUS 0x1068

#define S5PC110_INTC_DMA_TD (1 << 24)
#define S5PC110_INTC_DMA_TE (1 << 16)

#define S5PC110_DMA_CFG_SINGLE (0x0 << 16)
#define S5PC110_DMA_CFG_4BURST (0x2 << 16)
Expand Down Expand Up @@ -134,6 +146,7 @@ struct s3c_onenand {
void __iomem *dma_addr;
struct resource *dma_res;
unsigned long phys_base;
struct completion complete;
#ifdef CONFIG_MTD_PARTITIONS
struct mtd_partition *parts;
#endif
Expand Down Expand Up @@ -531,7 +544,9 @@ static int onenand_write_bufferram(struct mtd_info *mtd, int area,
return 0;
}

static int s5pc110_dma_ops(void *dst, void *src, size_t count, int direction)
static int (*s5pc110_dma_ops)(void *dst, void *src, size_t count, int direction);

static int s5pc110_dma_poll(void *dst, void *src, size_t count, int direction)
{
void __iomem *base = onenand->dma_addr;
int status;
Expand Down Expand Up @@ -575,6 +590,60 @@ static int s5pc110_dma_ops(void *dst, void *src, size_t count, int direction)
return 0;
}

static irqreturn_t s5pc110_onenand_irq(int irq, void *data)
{
void __iomem *base = onenand->dma_addr;
int status, cmd = 0;

status = readl(base + S5PC110_INTC_DMA_STATUS);

if (likely(status & S5PC110_INTC_DMA_TD))
cmd = S5PC110_DMA_TRANS_CMD_TDC;

if (unlikely(status & S5PC110_INTC_DMA_TE))
cmd = S5PC110_DMA_TRANS_CMD_TEC;

writel(cmd, base + S5PC110_DMA_TRANS_CMD);
writel(status, base + S5PC110_INTC_DMA_CLR);

if (!onenand->complete.done)
complete(&onenand->complete);

return IRQ_HANDLED;
}

static int s5pc110_dma_irq(void *dst, void *src, size_t count, int direction)
{
void __iomem *base = onenand->dma_addr;
int status;

status = readl(base + S5PC110_INTC_DMA_MASK);
if (status) {
status &= ~(S5PC110_INTC_DMA_TD | S5PC110_INTC_DMA_TE);
writel(status, base + S5PC110_INTC_DMA_MASK);
}

writel(src, base + S5PC110_DMA_SRC_ADDR);
writel(dst, base + S5PC110_DMA_DST_ADDR);

if (direction == S5PC110_DMA_DIR_READ) {
writel(S5PC110_DMA_SRC_CFG_READ, base + S5PC110_DMA_SRC_CFG);
writel(S5PC110_DMA_DST_CFG_READ, base + S5PC110_DMA_DST_CFG);
} else {
writel(S5PC110_DMA_SRC_CFG_WRITE, base + S5PC110_DMA_SRC_CFG);
writel(S5PC110_DMA_DST_CFG_WRITE, base + S5PC110_DMA_DST_CFG);
}

writel(count, base + S5PC110_DMA_TRANS_SIZE);
writel(direction, base + S5PC110_DMA_TRANS_DIR);

writel(S5PC110_DMA_TRANS_CMD_TR, base + S5PC110_DMA_TRANS_CMD);

wait_for_completion_timeout(&onenand->complete, msecs_to_jiffies(20));

return 0;
}

static int s5pc110_read_bufferram(struct mtd_info *mtd, int area,
unsigned char *buffer, int offset, size_t count)
{
Expand Down Expand Up @@ -919,6 +988,20 @@ static int s3c_onenand_probe(struct platform_device *pdev)
}

onenand->phys_base = onenand->base_res->start;

s5pc110_dma_ops = s5pc110_dma_poll;
/* Interrupt support */
r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (r) {
init_completion(&onenand->complete);
s5pc110_dma_ops = s5pc110_dma_irq;
err = request_irq(r->start, s5pc110_onenand_irq,
IRQF_SHARED, "onenand", &onenand);
if (err) {
dev_err(&pdev->dev, "failed to get irq\n");
goto scan_failed;
}
}
}

if (onenand_scan(mtd, 1)) {
Expand Down

0 comments on commit e23abf4

Please sign in to comment.