|
@@ -45,7 +45,8 @@
|
|
|
#define DMA_STATUS_MASK_BITS 0x3
|
|
|
#define DMA_STATUS_SHIFT_BITS 16
|
|
|
#define DMA_STATUS_IRQ(x) (0x1 << (x))
|
|
|
-#define DMA_STATUS_ERR(x) (0x1 << ((x) + 8))
|
|
|
+#define DMA_STATUS0_ERR(x) (0x1 << ((x) + 8))
|
|
|
+#define DMA_STATUS2_ERR(x) (0x1 << (x))
|
|
|
|
|
|
#define DMA_DESC_WIDTH_SHIFT_BITS 12
|
|
|
#define DMA_DESC_WIDTH_1_BYTE (0x3 << DMA_DESC_WIDTH_SHIFT_BITS)
|
|
@@ -133,6 +134,7 @@ struct pch_dma {
|
|
|
#define PCH_DMA_CTL3 0x0C
|
|
|
#define PCH_DMA_STS0 0x10
|
|
|
#define PCH_DMA_STS1 0x14
|
|
|
+#define PCH_DMA_STS2 0x18
|
|
|
|
|
|
#define dma_readl(pd, name) \
|
|
|
readl((pd)->membase + PCH_DMA_##name)
|
|
@@ -183,13 +185,19 @@ static void pdc_enable_irq(struct dma_chan *chan, int enable)
|
|
|
{
|
|
|
struct pch_dma *pd = to_pd(chan->device);
|
|
|
u32 val;
|
|
|
+ int pos;
|
|
|
+
|
|
|
+ if (chan->chan_id < 8)
|
|
|
+ pos = chan->chan_id;
|
|
|
+ else
|
|
|
+ pos = chan->chan_id + 8;
|
|
|
|
|
|
val = dma_readl(pd, CTL2);
|
|
|
|
|
|
if (enable)
|
|
|
- val |= 0x1 << chan->chan_id;
|
|
|
+ val |= 0x1 << pos;
|
|
|
else
|
|
|
- val &= ~(0x1 << chan->chan_id);
|
|
|
+ val &= ~(0x1 << pos);
|
|
|
|
|
|
dma_writel(pd, CTL2, val);
|
|
|
|
|
@@ -262,7 +270,7 @@ static void pdc_set_mode(struct dma_chan *chan, u32 mode)
|
|
|
chan->chan_id, val);
|
|
|
}
|
|
|
|
|
|
-static u32 pdc_get_status(struct pch_dma_chan *pd_chan)
|
|
|
+static u32 pdc_get_status0(struct pch_dma_chan *pd_chan)
|
|
|
{
|
|
|
struct pch_dma *pd = to_pd(pd_chan->chan.device);
|
|
|
u32 val;
|
|
@@ -272,9 +280,27 @@ static u32 pdc_get_status(struct pch_dma_chan *pd_chan)
|
|
|
DMA_STATUS_BITS_PER_CH * pd_chan->chan.chan_id));
|
|
|
}
|
|
|
|
|
|
+static u32 pdc_get_status2(struct pch_dma_chan *pd_chan)
|
|
|
+{
|
|
|
+ struct pch_dma *pd = to_pd(pd_chan->chan.device);
|
|
|
+ u32 val;
|
|
|
+
|
|
|
+ val = dma_readl(pd, STS2);
|
|
|
+ return DMA_STATUS_MASK_BITS & (val >> (DMA_STATUS_SHIFT_BITS +
|
|
|
+ DMA_STATUS_BITS_PER_CH * (pd_chan->chan.chan_id - 8)));
|
|
|
+}
|
|
|
+
|
|
|
static bool pdc_is_idle(struct pch_dma_chan *pd_chan)
|
|
|
{
|
|
|
- if (pdc_get_status(pd_chan) == DMA_STATUS_IDLE)
|
|
|
+ u32 sts;
|
|
|
+
|
|
|
+ if (pd_chan->chan.chan_id < 8)
|
|
|
+ sts = pdc_get_status0(pd_chan);
|
|
|
+ else
|
|
|
+ sts = pdc_get_status2(pd_chan);
|
|
|
+
|
|
|
+
|
|
|
+ if (sts == DMA_STATUS_IDLE)
|
|
|
return true;
|
|
|
else
|
|
|
return false;
|
|
@@ -693,30 +719,45 @@ static irqreturn_t pd_irq(int irq, void *devid)
|
|
|
struct pch_dma *pd = (struct pch_dma *)devid;
|
|
|
struct pch_dma_chan *pd_chan;
|
|
|
u32 sts0;
|
|
|
+ u32 sts2;
|
|
|
int i;
|
|
|
- int ret = IRQ_NONE;
|
|
|
+ int ret0 = IRQ_NONE;
|
|
|
+ int ret2 = IRQ_NONE;
|
|
|
|
|
|
sts0 = dma_readl(pd, STS0);
|
|
|
+ sts2 = dma_readl(pd, STS2);
|
|
|
|
|
|
dev_dbg(pd->dma.dev, "pd_irq sts0: %x\n", sts0);
|
|
|
|
|
|
for (i = 0; i < pd->dma.chancnt; i++) {
|
|
|
pd_chan = &pd->channels[i];
|
|
|
|
|
|
- if (sts0 & DMA_STATUS_IRQ(i)) {
|
|
|
- if (sts0 & DMA_STATUS_ERR(i))
|
|
|
- set_bit(0, &pd_chan->err_status);
|
|
|
+ if (i < 8) {
|
|
|
+ if (sts0 & DMA_STATUS_IRQ(i)) {
|
|
|
+ if (sts0 & DMA_STATUS0_ERR(i))
|
|
|
+ set_bit(0, &pd_chan->err_status);
|
|
|
|
|
|
- tasklet_schedule(&pd_chan->tasklet);
|
|
|
- ret = IRQ_HANDLED;
|
|
|
- }
|
|
|
+ tasklet_schedule(&pd_chan->tasklet);
|
|
|
+ ret0 = IRQ_HANDLED;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (sts2 & DMA_STATUS_IRQ(i - 8)) {
|
|
|
+ if (sts2 & DMA_STATUS2_ERR(i))
|
|
|
+ set_bit(0, &pd_chan->err_status);
|
|
|
|
|
|
+ tasklet_schedule(&pd_chan->tasklet);
|
|
|
+ ret2 = IRQ_HANDLED;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/* clear interrupt bits in status register */
|
|
|
- dma_writel(pd, STS0, sts0);
|
|
|
+ if (ret0)
|
|
|
+ dma_writel(pd, STS0, sts0);
|
|
|
+ if (ret2)
|
|
|
+ dma_writel(pd, STS2, sts2);
|
|
|
|
|
|
- return ret;
|
|
|
+ return ret0 | ret2;
|
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_PM
|