2008-11-25 13:38:32 +00:00
|
|
|
/***************************************************************************
|
|
|
|
* __________ __ ___.
|
|
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
|
|
* \/ \/ \/ \/ \/
|
|
|
|
* $Id$
|
|
|
|
*
|
|
|
|
* Copyright © 2008 Rafaël Carré
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
|
|
* KIND, either express or implied.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
2010-07-02 06:07:42 +00:00
|
|
|
#include "config.h"
|
|
|
|
#include "system.h"
|
2008-11-25 13:38:32 +00:00
|
|
|
#include "pl081.h"
|
|
|
|
#include "dma-target.h"
|
|
|
|
#include "panic.h"
|
|
|
|
|
2008-12-04 22:54:06 +00:00
|
|
|
static int dma_used = 0;
|
2008-12-04 20:48:19 +00:00
|
|
|
static void (*dma_callback[2])(void); /* 2 channels */
|
2008-11-26 16:02:00 +00:00
|
|
|
|
2008-12-04 22:54:06 +00:00
|
|
|
void dma_retain(void)
|
|
|
|
{
|
|
|
|
if(++dma_used == 1)
|
|
|
|
{
|
2010-07-02 06:00:00 +00:00
|
|
|
bitset32(&CGU_PERI, CGU_DMA_CLOCK_ENABLE);
|
2011-01-19 09:15:23 +00:00
|
|
|
bitset32(&DMAC_CONFIGURATION, 1<<0);
|
2008-12-04 22:54:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void dma_release(void)
|
|
|
|
{
|
|
|
|
if(--dma_used == 0)
|
|
|
|
{
|
2011-01-19 09:15:23 +00:00
|
|
|
bitclr32(&DMAC_CONFIGURATION, 1<<0);
|
2010-07-02 06:00:00 +00:00
|
|
|
bitclr32(&CGU_PERI, CGU_DMA_CLOCK_ENABLE);
|
2008-12-04 22:54:06 +00:00
|
|
|
}
|
2010-06-17 19:14:42 +00:00
|
|
|
if (dma_used < 0)
|
|
|
|
panicf("dma_used < 0!");
|
2008-12-04 22:54:06 +00:00
|
|
|
}
|
|
|
|
|
2008-11-25 13:38:32 +00:00
|
|
|
void dma_init(void)
|
|
|
|
{
|
2010-04-08 06:12:51 +00:00
|
|
|
#if CONFIG_CPU == AS3525
|
2009-04-07 17:08:26 +00:00
|
|
|
DMAC_SYNC = 0xffff; /* disable synchronisation logic */
|
2010-04-08 06:12:51 +00:00
|
|
|
#endif
|
2009-11-24 12:05:53 +00:00
|
|
|
VIC_INT_ENABLE = INTERRUPT_DMAC;
|
2008-11-25 13:38:32 +00:00
|
|
|
}
|
|
|
|
|
2011-01-19 09:15:23 +00:00
|
|
|
void dma_pause_channel(int channel)
|
2008-12-04 20:48:19 +00:00
|
|
|
{
|
2011-01-19 09:15:23 +00:00
|
|
|
/* Disable the channel - clears the FIFO after sending last word */
|
|
|
|
bitclr32(&DMAC_CH_CONFIGURATION(channel), 1<<0);
|
|
|
|
/* Wait for it to go inactive */
|
|
|
|
while (DMAC_CH_CONFIGURATION(channel) & (1<<17));
|
2008-12-04 20:48:19 +00:00
|
|
|
}
|
|
|
|
|
2011-01-19 09:15:23 +00:00
|
|
|
void dma_resume_channel(int channel)
|
|
|
|
{
|
|
|
|
/* Resume - must reinit to where it left off (so the docs say) */
|
|
|
|
unsigned long control = DMAC_CH_CONTROL(channel);
|
|
|
|
if ((control & 0x7ff) == 0)
|
|
|
|
return; /* empty */
|
|
|
|
|
|
|
|
DMAC_INT_TC_CLEAR = (1<<channel);
|
|
|
|
DMAC_INT_ERR_CLEAR = (1<<channel);
|
|
|
|
DMAC_CH_SRC_ADDR(channel) = DMAC_CH_SRC_ADDR(channel);
|
|
|
|
DMAC_CH_DST_ADDR(channel) = DMAC_CH_DST_ADDR(channel);
|
|
|
|
DMAC_CH_LLI(channel) = DMAC_CH_LLI(channel);
|
|
|
|
DMAC_CH_CONTROL(channel) = control;
|
|
|
|
bitset32(&DMAC_CH_CONFIGURATION(channel), (1<<0));
|
|
|
|
}
|
|
|
|
|
|
|
|
void dma_disable_channel(int channel) \
|
|
|
|
__attribute__((alias("dma_pause_channel")));
|
|
|
|
|
2008-11-25 13:38:32 +00:00
|
|
|
void dma_enable_channel(int channel, void *src, void *dst, int peri,
|
|
|
|
int flow_controller, bool src_inc, bool dst_inc,
|
2008-12-04 20:48:19 +00:00
|
|
|
size_t size, int nwords, void (*callback)(void))
|
2008-11-25 13:38:32 +00:00
|
|
|
{
|
2008-12-04 20:48:19 +00:00
|
|
|
dma_callback[channel] = callback;
|
|
|
|
|
2009-11-23 19:44:13 +00:00
|
|
|
/* Clear any pending interrupts leftover from previous operation */
|
|
|
|
DMAC_INT_TC_CLEAR = (1<<channel);
|
|
|
|
DMAC_INT_ERR_CLEAR = (1<<channel);
|
2008-11-25 13:38:32 +00:00
|
|
|
|
|
|
|
DMAC_CH_SRC_ADDR(channel) = (int)src;
|
|
|
|
DMAC_CH_DST_ADDR(channel) = (int)dst;
|
|
|
|
|
2009-11-23 19:44:13 +00:00
|
|
|
/* When LLI is 0 channel is disabled upon transfer completion */
|
|
|
|
DMAC_CH_LLI(channel) = 0;
|
|
|
|
|
|
|
|
/* Channel Control Register */
|
|
|
|
DMAC_CH_CONTROL(channel) =
|
|
|
|
((1<<31) /* LLI triggers terminal count interrupt */
|
|
|
|
/* | (1<<30) */ /* cacheable = 1, non = 0 */
|
|
|
|
/* | (1<<29) */ /* bufferable = 1, non = 0 */
|
|
|
|
/* | (1<<28) */ /* privileged = 1, user = 0 */
|
|
|
|
| (dst_inc? (1<<27): 0) /* specify address increment */
|
|
|
|
| (src_inc? (1<<26): 0) /* specify address increment */
|
|
|
|
/* [25:24] */ /* undefined */
|
|
|
|
| (2<<21) /* dst width = word, 32bit */
|
|
|
|
| (2<<18) /* src width = word, 32bit */
|
|
|
|
/* OF uses transfers of 4 * 32 bits words on memory, i2sin, i2sout */
|
|
|
|
/* OF uses transfers of 8 * 32 bits words on SD */
|
|
|
|
| (nwords<<15) /* dst size */
|
|
|
|
| (nwords<<12) /* src size */
|
|
|
|
| ((size & 0x7ff)<<0)); /* transfer size */
|
|
|
|
|
|
|
|
/* Channel Config Register */
|
2008-11-25 13:38:32 +00:00
|
|
|
DMAC_CH_CONFIGURATION(channel) =
|
2009-11-23 19:44:13 +00:00
|
|
|
/* [31:19] */ /* Read undefined. Write as zero */
|
|
|
|
/* (0<<18) */ /* Halt Bit */
|
|
|
|
/* (0<<17) */ /* Active Bit */
|
|
|
|
/* (0<<16) */ /* Lock Bit */
|
|
|
|
(1<<15) /* terminal count interrupt mask */
|
|
|
|
| (1<<14) /* interrupt error mask */
|
|
|
|
| (flow_controller<<11) /* flow controller is peripheral or SDMAC */
|
|
|
|
/* we set the same peripheral as source and destination because we
|
|
|
|
* always use memory-to-peripheral or peripheral-to-memory transfers */
|
|
|
|
| (peri<<6) /* dst peripheral */
|
|
|
|
| (peri<<1) /* src peripheral */
|
|
|
|
| (1<<0); /* enable channel */
|
2008-11-25 13:38:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* isr */
|
|
|
|
void INT_DMAC(void)
|
|
|
|
{
|
2008-12-04 20:48:19 +00:00
|
|
|
unsigned int channel;
|
|
|
|
|
2011-12-12 20:12:22 +00:00
|
|
|
/* Lowest channel index is serviced first */
|
2008-12-04 20:48:19 +00:00
|
|
|
for(channel = 0; channel < 2; channel++)
|
|
|
|
if(DMAC_INT_STATUS & (1<<channel))
|
|
|
|
{
|
|
|
|
if(DMAC_INT_ERROR_STATUS & (1<<channel))
|
|
|
|
panicf("DMA error, channel %d", channel);
|
2008-11-25 13:38:32 +00:00
|
|
|
|
2008-12-04 20:48:19 +00:00
|
|
|
/* clear terminal count interrupt */
|
2009-04-07 17:08:26 +00:00
|
|
|
DMAC_INT_TC_CLEAR = (1<<channel);
|
2008-11-25 13:38:32 +00:00
|
|
|
|
2008-12-04 20:48:19 +00:00
|
|
|
if(dma_callback[channel])
|
|
|
|
dma_callback[channel]();
|
|
|
|
}
|
2008-11-25 13:38:32 +00:00
|
|
|
}
|