246c2127a7
Reduce DMA maximum transfer size since transfering 64Kb requires to set a size of 0 and it's not worth adding checks everywhere to handle this special case. Also add statistics about unaligned transfer (wrt to cache). Update debug screen accordingly and simplify it so it can fit smaller screens too. Change-Id: I18391702f5e100a21f6f8d1ebab28d9f2bd8c66f
267 lines
9.1 KiB
C
267 lines
9.1 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2011 by amaury Pouly
|
|
*
|
|
* Based on Rockbox iriver bootloader by Linus Nielsen Feltzing
|
|
* and the ipodlinux bootloader by Daniel Palffy and Bernard Leach
|
|
*
|
|
* 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.
|
|
*
|
|
****************************************************************************/
|
|
#include "config.h"
|
|
#include "system.h"
|
|
#include "dma-imx233.h"
|
|
#include "lcd.h"
|
|
#include "string.h"
|
|
|
|
// statistics about unaligned transfers
|
|
static int apb_nr_unaligned[32];
|
|
|
|
void imx233_dma_init(void)
|
|
{
|
|
/* Enable APHB and APBX */
|
|
imx233_reset_block(&HW_APBH_CTRL0);
|
|
imx233_reset_block(&HW_APBX_CTRL0);
|
|
}
|
|
|
|
void imx233_dma_reset_channel(unsigned chan)
|
|
{
|
|
volatile uint32_t *ptr;
|
|
uint32_t bm;
|
|
if(APB_IS_APBX_CHANNEL(chan))
|
|
{
|
|
ptr = &HW_APBX_CHANNEL_CTRL;
|
|
bm = HW_APBX_CHANNEL_CTRL__RESET_CHANNEL(APB_GET_DMA_CHANNEL(chan));
|
|
}
|
|
else
|
|
{
|
|
ptr = &HW_APBH_CTRL0;
|
|
bm = HW_APBH_CTRL0__RESET_CHANNEL(APB_GET_DMA_CHANNEL(chan));
|
|
}
|
|
__REG_SET(*ptr) = bm;
|
|
/* wait for end of reset */
|
|
while(*ptr & bm)
|
|
;
|
|
}
|
|
|
|
void imx233_dma_clkgate_channel(unsigned chan, bool enable_clock)
|
|
{
|
|
if(APB_IS_APBX_CHANNEL(chan))
|
|
return;
|
|
if(enable_clock)
|
|
__REG_CLR(HW_APBH_CTRL0) =
|
|
HW_APBH_CTRL0__CLKGATE_CHANNEL(APB_GET_DMA_CHANNEL(chan));
|
|
else
|
|
__REG_SET(HW_APBH_CTRL0) =
|
|
HW_APBH_CTRL0__CLKGATE_CHANNEL(APB_GET_DMA_CHANNEL(chan));
|
|
}
|
|
|
|
void imx233_dma_freeze_channel(unsigned chan, bool freeze)
|
|
{
|
|
volatile uint32_t *ptr;
|
|
uint32_t bm;
|
|
if(APB_IS_APBX_CHANNEL(chan))
|
|
{
|
|
ptr = &HW_APBX_CHANNEL_CTRL;
|
|
bm = HW_APBX_CHANNEL_CTRL__FREEZE_CHANNEL(APB_GET_DMA_CHANNEL(chan));
|
|
}
|
|
else
|
|
{
|
|
ptr = &HW_APBH_CTRL0;
|
|
bm = HW_APBH_CTRL0__FREEZE_CHANNEL(APB_GET_DMA_CHANNEL(chan));
|
|
}
|
|
|
|
if(freeze)
|
|
__REG_SET(*ptr) = bm;
|
|
else
|
|
__REG_CLR(*ptr) = bm;
|
|
}
|
|
|
|
void imx233_dma_enable_channel_interrupt(unsigned chan, bool enable)
|
|
{
|
|
volatile uint32_t *ptr;
|
|
uint32_t bm;
|
|
if(APB_IS_APBX_CHANNEL(chan))
|
|
{
|
|
ptr = &HW_APBX_CTRL1;
|
|
bm = HW_APBX_CTRL1__CHx_CMDCMPLT_IRQ_EN(APB_GET_DMA_CHANNEL(chan));
|
|
}
|
|
else
|
|
{
|
|
ptr = &HW_APBH_CTRL1;
|
|
bm = HW_APBH_CTRL1__CHx_CMDCMPLT_IRQ_EN(APB_GET_DMA_CHANNEL(chan));
|
|
}
|
|
|
|
if(enable)
|
|
{
|
|
__REG_SET(*ptr) = bm;
|
|
imx233_dma_clear_channel_interrupt(chan);
|
|
}
|
|
else
|
|
__REG_CLR(*ptr) = bm;
|
|
}
|
|
|
|
void imx233_dma_clear_channel_interrupt(unsigned chan)
|
|
{
|
|
if(APB_IS_APBX_CHANNEL(chan))
|
|
{
|
|
__REG_CLR(HW_APBX_CTRL1) =
|
|
HW_APBX_CTRL1__CHx_CMDCMPLT_IRQ(APB_GET_DMA_CHANNEL(chan));
|
|
__REG_CLR(HW_APBX_CTRL2) =
|
|
HW_APBX_CTRL2__CHx_ERROR_IRQ(APB_GET_DMA_CHANNEL(chan));
|
|
}
|
|
else
|
|
{
|
|
__REG_CLR(HW_APBH_CTRL1) =
|
|
HW_APBH_CTRL1__CHx_CMDCMPLT_IRQ(APB_GET_DMA_CHANNEL(chan));
|
|
__REG_CLR(HW_APBH_CTRL2) =
|
|
HW_APBH_CTRL2__CHx_ERROR_IRQ(APB_GET_DMA_CHANNEL(chan));
|
|
}
|
|
}
|
|
|
|
bool imx233_dma_is_channel_error_irq(unsigned chan)
|
|
{
|
|
if(APB_IS_APBX_CHANNEL(chan))
|
|
return !!(HW_APBX_CTRL2 &
|
|
HW_APBX_CTRL2__CHx_ERROR_IRQ(APB_GET_DMA_CHANNEL(chan)));
|
|
else
|
|
return !!(HW_APBH_CTRL2 &
|
|
HW_APBH_CTRL2__CHx_ERROR_IRQ(APB_GET_DMA_CHANNEL(chan)));
|
|
}
|
|
|
|
/* Commit and/or discard all DMA descriptors and buffers pointed by them,
|
|
* handle circular lists. At the same time, convert virtual pointers to
|
|
* real ones */
|
|
static void imx233_dma_commit_and_discard(unsigned chan, struct apb_dma_command_t *cmd)
|
|
{
|
|
/* We handle circular descriptors by using unused bits:
|
|
* bits 8-11 are not used by the hardware so we first go through the whole
|
|
* list and mark them all a special value at the same time we commit buffers
|
|
* and then we go through the list another time to clear the mark and
|
|
* commit the descriptors */
|
|
struct apb_dma_command_t *cur = cmd;
|
|
|
|
while((cur->cmd & HW_APB_CHx_CMD__UNUSED_BM) != HW_APB_CHx_CMD__UNUSED_MAGIC)
|
|
{
|
|
cur->cmd = (cur->cmd & ~HW_APB_CHx_CMD__UNUSED_BM) | HW_APB_CHx_CMD__UNUSED_MAGIC;
|
|
int op = cur->cmd & HW_APB_CHx_CMD__COMMAND_BM;
|
|
int sz = __XTRACT_EX(cur->cmd, HW_APB_CHx_CMD__XFER_COUNT);
|
|
/* device > host: discard */
|
|
if(op == HW_APB_CHx_CMD__COMMAND__WRITE)
|
|
discard_dcache_range(cur->buffer, sz);
|
|
/* host > device: commit and discard */
|
|
else if(op == HW_APB_CHx_CMD__COMMAND__READ)
|
|
commit_discard_dcache_range(cur->buffer, sz);
|
|
if((uint32_t)cur->buffer % CACHEALIGN_SIZE)
|
|
apb_nr_unaligned[chan]++;
|
|
/* Virtual to physical buffer pointer conversion */
|
|
cur->buffer = PHYSICAL_ADDR(cur->buffer);
|
|
/* chain ? */
|
|
if(cur->cmd & HW_APB_CHx_CMD__CHAIN)
|
|
cur = cur->next;
|
|
else
|
|
break;
|
|
}
|
|
|
|
cur = cmd;
|
|
while((cur->cmd & HW_APB_CHx_CMD__UNUSED_BM) != 0)
|
|
{
|
|
cur->cmd = cur->cmd & ~HW_APB_CHx_CMD__UNUSED_BM;
|
|
int sz = __XTRACT_EX(cur->cmd, HW_APB_CHx_CMD__CMDWORDS) * sizeof(uint32_t);
|
|
/* commit descriptor and discard descriptor */
|
|
/* chain ? */
|
|
if(cur->cmd & HW_APB_CHx_CMD__CHAIN)
|
|
{
|
|
struct apb_dma_command_t *next = cur->next;
|
|
cur->next = PHYSICAL_ADDR(cur->next);
|
|
commit_dcache_range(cur, sizeof(struct apb_dma_command_t) + sz);
|
|
cur = next;
|
|
}
|
|
else
|
|
{
|
|
commit_dcache_range(cur, sizeof(struct apb_dma_command_t) + sz);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void imx233_dma_start_command(unsigned chan, struct apb_dma_command_t *cmd)
|
|
{
|
|
imx233_dma_commit_and_discard(chan, cmd);
|
|
if(APB_IS_APBX_CHANNEL(chan))
|
|
{
|
|
HW_APBX_CHx_NXTCMDAR(APB_GET_DMA_CHANNEL(chan)) = (uint32_t)PHYSICAL_ADDR(cmd);
|
|
HW_APBX_CHx_SEMA(APB_GET_DMA_CHANNEL(chan)) = 1;
|
|
}
|
|
else
|
|
{
|
|
HW_APBH_CHx_NXTCMDAR(APB_GET_DMA_CHANNEL(chan)) = (uint32_t)PHYSICAL_ADDR(cmd);
|
|
HW_APBH_CHx_SEMA(APB_GET_DMA_CHANNEL(chan)) = 1;
|
|
}
|
|
}
|
|
|
|
int imx233_dma_wait_completion(unsigned chan, unsigned tmo)
|
|
{
|
|
tmo += current_tick;
|
|
volatile uint32_t *sema;
|
|
if(APB_IS_APBX_CHANNEL(chan))
|
|
sema = &HW_APBX_CHx_SEMA(APB_GET_DMA_CHANNEL(chan));
|
|
else
|
|
sema = &HW_APBH_CHx_SEMA(APB_GET_DMA_CHANNEL(chan));
|
|
|
|
while(*sema & HW_APB_CHx_SEMA__PHORE_BM && !TIME_AFTER(current_tick, tmo))
|
|
yield();
|
|
return __XTRACT_EX(*sema, HW_APB_CHx_SEMA__PHORE);
|
|
}
|
|
|
|
struct imx233_dma_info_t imx233_dma_get_info(unsigned chan, unsigned flags)
|
|
{
|
|
struct imx233_dma_info_t s;
|
|
memset(&s, 0, sizeof(s));
|
|
bool apbx = APB_IS_APBX_CHANNEL(chan);
|
|
int dmac = APB_GET_DMA_CHANNEL(chan);
|
|
if(flags & DMA_INFO_CURCMDADDR)
|
|
s.cur_cmd_addr = apbx ? HW_APBX_CHx_CURCMDAR(dmac) : HW_APBH_CHx_CURCMDAR(dmac);
|
|
if(flags & DMA_INFO_NXTCMDADDR)
|
|
s.nxt_cmd_addr = apbx ? HW_APBX_CHx_NXTCMDAR(dmac) : HW_APBH_CHx_NXTCMDAR(dmac);
|
|
if(flags & DMA_INFO_CMD)
|
|
s.cmd = apbx ? HW_APBX_CHx_CMD(dmac) : HW_APBH_CHx_CMD(dmac);
|
|
if(flags & DMA_INFO_BAR)
|
|
s.bar = apbx ? HW_APBX_CHx_BAR(dmac) : HW_APBH_CHx_BAR(dmac);
|
|
if(flags & DMA_INFO_AHB_BYTES)
|
|
s.ahb_bytes = apbx ? __XTRACT_EX(HW_APBX_CHx_DEBUG2(dmac), HW_APBX_CHx_DEBUG2__AHB_BYTES) :
|
|
__XTRACT_EX(HW_APBH_CHx_DEBUG2(dmac), HW_APBH_CHx_DEBUG2__AHB_BYTES);
|
|
if(flags & DMA_INFO_APB_BYTES)
|
|
s.apb_bytes = apbx ? __XTRACT_EX(HW_APBX_CHx_DEBUG2(dmac), HW_APBX_CHx_DEBUG2__APB_BYTES) :
|
|
__XTRACT_EX(HW_APBH_CHx_DEBUG2(dmac), HW_APBH_CHx_DEBUG2__APB_BYTES);
|
|
if(flags & DMA_INFO_FREEZED)
|
|
s.freezed = apbx ? HW_APBX_CHANNEL_CTRL & HW_APBX_CHANNEL_CTRL__FREEZE_CHANNEL(dmac) :
|
|
HW_APBH_CTRL0 & HW_APBH_CTRL0__FREEZE_CHANNEL(dmac);
|
|
if(flags & DMA_INFO_GATED)
|
|
s.gated = apbx ? false : HW_APBH_CTRL0 & HW_APBH_CTRL0__CLKGATE_CHANNEL(dmac);
|
|
if(flags & DMA_INFO_INTERRUPT)
|
|
{
|
|
s.int_enabled = apbx ? HW_APBX_CTRL1 & HW_APBX_CTRL1__CHx_CMDCMPLT_IRQ_EN(dmac) :
|
|
HW_APBH_CTRL1 & HW_APBH_CTRL1__CHx_CMDCMPLT_IRQ_EN(dmac);
|
|
s.int_cmdcomplt = apbx ? HW_APBX_CTRL1 & HW_APBX_CTRL1__CHx_CMDCMPLT_IRQ(dmac) :
|
|
HW_APBH_CTRL1 & HW_APBH_CTRL1__CHx_CMDCMPLT_IRQ(dmac);
|
|
s.int_error = apbx ? HW_APBX_CTRL2 & HW_APBX_CTRL2__CHx_ERROR_IRQ(dmac) :
|
|
HW_APBH_CTRL2 & HW_APBH_CTRL2__CHx_ERROR_IRQ(dmac);
|
|
}
|
|
s.nr_unaligned = apb_nr_unaligned[chan];
|
|
return s;
|
|
}
|
|
|