Commit 25a49b8e authored by Russell King's avatar Russell King

[ARM] Fix DMA handler race condition.

This fixes a race condition in the RiscPC DMA code, which causes
DMA for a channel to halt due to a race condition between the
hardware state machine and the software programming the next DMA
buffer.
parent af7dbd5e
......@@ -57,7 +57,7 @@ static void iomd_get_next_sg(struct scatterlist *sg, dma_t *dma)
if (end > PAGE_SIZE)
end = PAGE_SIZE;
if (offset + (int) TRANSFER_SIZE > end)
if (offset + TRANSFER_SIZE >= end)
flags |= DMA_END_L;
sg->length = end - TRANSFER_SIZE;
......@@ -95,27 +95,31 @@ static irqreturn_t iomd_dma_handle(int irq, void *dev_id, struct pt_regs *regs)
if (!(status & DMA_ST_INT))
return IRQ_HANDLED;
if (status & DMA_ST_OFL && !dma->sg)
break;
iomd_get_next_sg(&dma->cur_sg, dma);
if ((dma->state ^ status) & DMA_ST_AB)
iomd_get_next_sg(&dma->cur_sg, dma);
switch (status & (DMA_ST_OFL | DMA_ST_AB)) {
case DMA_ST_OFL: /* OIA */
case DMA_ST_AB: /* .IB */
iomd_writel(dma->cur_sg.dma_address, base + CURA);
iomd_writel(dma->cur_sg.length, base + ENDA);
dma->state = DMA_ST_AB;
break;
case DMA_ST_OFL | DMA_ST_AB: /* OIB */
case 0: /* .IA */
iomd_writel(dma->cur_sg.dma_address, base + CURB);
iomd_writel(dma->cur_sg.length, base + ENDB);
dma->state = 0;
break;
}
if (status & DMA_ST_OFL &&
dma->cur_sg.length == (DMA_END_S|DMA_END_L))
break;
} while (1);
iomd_writeb(0, dma->dma_base + CR);
dma->state = ~DMA_ST_AB;
disable_irq(irq);
return IRQ_HANDLED;
......@@ -152,6 +156,7 @@ static void iomd_enable_dma(dmach_t channel, dma_t *dma)
}
iomd_writeb(DMA_CR_C, dma_base + CR);
dma->state = DMA_ST_AB;
}
if (dma->dma_mode == DMA_MODE_READ)
......@@ -165,13 +170,11 @@ static void iomd_disable_dma(dmach_t channel, dma_t *dma)
{
unsigned long dma_base = dma->dma_base;
unsigned long flags;
unsigned int ctrl;
local_irq_save(flags);
ctrl = iomd_readb(dma_base + CR);
if (ctrl & DMA_CR_E)
if (dma->state != ~DMA_ST_AB)
disable_irq(dma->dma_irq);
iomd_writeb(ctrl & ~DMA_CR_E, dma_base + CR);
iomd_writeb(0, dma_base + CR);
local_irq_restore(flags);
}
......
......@@ -41,6 +41,7 @@ struct dma_struct {
unsigned int dma_base; /* Controller base address */
int dma_irq; /* Controller IRQ */
struct scatterlist cur_sg; /* Current controller buffer */
unsigned int state;
struct dma_ops *d_ops;
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment