1
0
Fork 0

dmaengine: omap-dma: control start/stop directly

Program the non-cyclic mode DMA start/stop directly, rather than via
arch/arm/plat-omap/dma.c.

Acked-by: Tony Lindgren <tony@atomide.com>
Acked-by: Vinod Koul <vinod.koul@intel.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
hifive-unleashed-5.1
Russell King 2013-11-02 17:07:09 +00:00
parent 913a2d0c69
commit fa3ad86ae0
1 changed files with 141 additions and 10 deletions

View File

@ -5,6 +5,7 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/delay.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
@ -60,6 +61,7 @@ struct omap_desc {
uint8_t sync_mode; /* OMAP_DMA_SYNC_xxx */
uint8_t sync_type; /* OMAP_DMA_xxx_SYNC* */
uint8_t periph_port; /* Peripheral port */
uint16_t cicr; /* CICR value */
unsigned sglen;
struct omap_sg sg[0];
@ -95,6 +97,111 @@ static void omap_dma_desc_free(struct virt_dma_desc *vd)
kfree(container_of(vd, struct omap_desc, vd));
}
static void omap_dma_start(struct omap_chan *c, struct omap_desc *d)
{
struct omap_dmadev *od = to_omap_dma_dev(c->vc.chan.device);
uint32_t val;
if (__dma_omap15xx(od->plat->dma_attr))
c->plat->dma_write(0, CPC, c->dma_ch);
else
c->plat->dma_write(0, CDAC, c->dma_ch);
if (!__dma_omap15xx(od->plat->dma_attr) && c->cyclic) {
val = c->plat->dma_read(CLNK_CTRL, c->dma_ch);
if (dma_omap1())
val &= ~(1 << 14);
val |= c->dma_ch | 1 << 15;
c->plat->dma_write(val, CLNK_CTRL, c->dma_ch);
} else if (od->plat->errata & DMA_ERRATA_PARALLEL_CHANNELS)
c->plat->dma_write(c->dma_ch, CLNK_CTRL, c->dma_ch);
/* Clear CSR */
if (dma_omap1())
c->plat->dma_read(CSR, c->dma_ch);
else
c->plat->dma_write(~0, CSR, c->dma_ch);
/* Enable interrupts */
c->plat->dma_write(d->cicr, CICR, c->dma_ch);
val = c->plat->dma_read(CCR, c->dma_ch);
if (od->plat->errata & DMA_ERRATA_IFRAME_BUFFERING)
val |= OMAP_DMA_CCR_BUFFERING_DISABLE;
val |= OMAP_DMA_CCR_EN;
mb();
c->plat->dma_write(val, CCR, c->dma_ch);
}
static void omap_dma_stop(struct omap_chan *c)
{
struct omap_dmadev *od = to_omap_dma_dev(c->vc.chan.device);
uint32_t val;
/* disable irq */
c->plat->dma_write(0, CICR, c->dma_ch);
/* Clear CSR */
if (dma_omap1())
c->plat->dma_read(CSR, c->dma_ch);
else
c->plat->dma_write(~0, CSR, c->dma_ch);
val = c->plat->dma_read(CCR, c->dma_ch);
if (od->plat->errata & DMA_ERRATA_i541 &&
val & OMAP_DMA_CCR_SEL_SRC_DST_SYNC) {
uint32_t sysconfig;
unsigned i;
sysconfig = c->plat->dma_read(OCP_SYSCONFIG, c->dma_ch);
val = sysconfig & ~DMA_SYSCONFIG_MIDLEMODE_MASK;
val |= DMA_SYSCONFIG_MIDLEMODE(DMA_IDLEMODE_NO_IDLE);
c->plat->dma_write(val, OCP_SYSCONFIG, c->dma_ch);
val = c->plat->dma_read(CCR, c->dma_ch);
val &= ~OMAP_DMA_CCR_EN;
c->plat->dma_write(val, CCR, c->dma_ch);
/* Wait for sDMA FIFO to drain */
for (i = 0; ; i++) {
val = c->plat->dma_read(CCR, c->dma_ch);
if (!(val & (OMAP_DMA_CCR_RD_ACTIVE | OMAP_DMA_CCR_WR_ACTIVE)))
break;
if (i > 100)
break;
udelay(5);
}
if (val & (OMAP_DMA_CCR_RD_ACTIVE | OMAP_DMA_CCR_WR_ACTIVE))
dev_err(c->vc.chan.device->dev,
"DMA drain did not complete on lch %d\n",
c->dma_ch);
c->plat->dma_write(sysconfig, OCP_SYSCONFIG, c->dma_ch);
} else {
val &= ~OMAP_DMA_CCR_EN;
c->plat->dma_write(val, CCR, c->dma_ch);
}
mb();
if (!__dma_omap15xx(od->plat->dma_attr) && c->cyclic) {
val = c->plat->dma_read(CLNK_CTRL, c->dma_ch);
if (dma_omap1())
val |= 1 << 14; /* set the STOP_LNK bit */
else
val &= ~(1 << 15); /* Clear the ENABLE_LNK bit */
c->plat->dma_write(val, CLNK_CTRL, c->dma_ch);
}
}
static void omap_dma_start_sg(struct omap_chan *c, struct omap_desc *d,
unsigned idx)
{
@ -113,7 +220,7 @@ static void omap_dma_start_sg(struct omap_chan *c, struct omap_desc *d,
c->plat->dma_write(sg->en, CEN, c->dma_ch);
c->plat->dma_write(sg->fn, CFN, c->dma_ch);
omap_start_dma(c->dma_ch);
omap_dma_start(c, d);
}
static void omap_dma_start_desc(struct omap_chan *c)
@ -434,6 +541,12 @@ static struct dma_async_tx_descriptor *omap_dma_prep_slave_sg(
d->sync_mode = OMAP_DMA_SYNC_FRAME;
d->sync_type = sync_type;
d->periph_port = OMAP_DMA_PORT_TIPB;
d->cicr = OMAP_DMA_DROP_IRQ | OMAP_DMA_BLOCK_IRQ;
if (dma_omap1())
d->cicr |= OMAP1_DMA_TOUT_IRQ;
else
d->cicr |= OMAP2_DMA_MISALIGNED_ERR_IRQ | OMAP2_DMA_TRANS_ERR_IRQ;
/*
* Build our scatterlist entries: each contains the address,
@ -463,6 +576,7 @@ static struct dma_async_tx_descriptor *omap_dma_prep_dma_cyclic(
size_t period_len, enum dma_transfer_direction dir, unsigned long flags,
void *context)
{
struct omap_dmadev *od = to_omap_dma_dev(chan->device);
struct omap_chan *c = to_omap_dma_chan(chan);
enum dma_slave_buswidth dev_width;
struct omap_desc *d;
@ -519,15 +633,25 @@ static struct dma_async_tx_descriptor *omap_dma_prep_dma_cyclic(
d->sg[0].en = period_len / es_bytes[es];
d->sg[0].fn = buf_len / period_len;
d->sglen = 1;
d->cicr = OMAP_DMA_DROP_IRQ;
if (flags & DMA_PREP_INTERRUPT)
d->cicr |= OMAP_DMA_FRAME_IRQ;
if (dma_omap1())
d->cicr |= OMAP1_DMA_TOUT_IRQ;
else
d->cicr |= OMAP2_DMA_MISALIGNED_ERR_IRQ | OMAP2_DMA_TRANS_ERR_IRQ;
if (!c->cyclic) {
c->cyclic = true;
omap_dma_link_lch(c->dma_ch, c->dma_ch);
if (flags & DMA_PREP_INTERRUPT)
omap_enable_dma_irq(c->dma_ch, OMAP_DMA_FRAME_IRQ);
if (__dma_omap15xx(od->plat->dma_attr)) {
uint32_t val;
omap_disable_dma_irq(c->dma_ch, OMAP_DMA_BLOCK_IRQ);
val = c->plat->dma_read(CCR, c->dma_ch);
val |= 3 << 8;
c->plat->dma_write(val, CCR, c->dma_ch);
}
}
if (dma_omap2plus()) {
@ -568,20 +692,27 @@ static int omap_dma_terminate_all(struct omap_chan *c)
/*
* Stop DMA activity: we assume the callback will not be called
* after omap_stop_dma() returns (even if it does, it will see
* after omap_dma_stop() returns (even if it does, it will see
* c->desc is NULL and exit.)
*/
if (c->desc) {
c->desc = NULL;
/* Avoid stopping the dma twice */
if (!c->paused)
omap_stop_dma(c->dma_ch);
omap_dma_stop(c);
}
if (c->cyclic) {
c->cyclic = false;
c->paused = false;
omap_dma_unlink_lch(c->dma_ch, c->dma_ch);
if (__dma_omap15xx(od->plat->dma_attr)) {
uint32_t val;
val = c->plat->dma_read(CCR, c->dma_ch);
val &= ~(3 << 8);
c->plat->dma_write(val, CCR, c->dma_ch);
}
}
vchan_get_all_descriptors(&c->vc, &head);
@ -598,7 +729,7 @@ static int omap_dma_pause(struct omap_chan *c)
return -EINVAL;
if (!c->paused) {
omap_stop_dma(c->dma_ch);
omap_dma_stop(c);
c->paused = true;
}
@ -612,7 +743,7 @@ static int omap_dma_resume(struct omap_chan *c)
return -EINVAL;
if (c->paused) {
omap_start_dma(c->dma_ch);
omap_dma_start(c, c->desc);
c->paused = false;
}