397 lines
9.9 KiB
C
Executable File
397 lines
9.9 KiB
C
Executable File
/*
|
|
* (C) Copyright 2007-2013
|
|
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
|
|
* Jerry Wang <wangflord@allwinnertech.com>
|
|
*
|
|
* See file CREDITS for list of people who contributed to this
|
|
* project.
|
|
*
|
|
* 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 program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
|
* MA 02111-1307 USA
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <arch/dma.h>
|
|
#include <arch/clock.h>
|
|
#include <cache_align.h>
|
|
|
|
#ifdef CONFIG_ARCH_SUN8IW18
|
|
#define SUNXI_DMA_MAX 10
|
|
#else
|
|
#define SUNXI_DMA_MAX 4
|
|
#endif
|
|
|
|
static int dma_int_cnt;
|
|
static int dma_init = -1;
|
|
static sunxi_dma_source dma_channal_source[SUNXI_DMA_MAX];
|
|
__attribute__((section(".data"))) __aligned(16)
|
|
sunxi_dma_desc g_sunxi_dma_desc[SUNXI_DMA_MAX];
|
|
|
|
void sunxi_dma_reg_func(void *p)
|
|
{
|
|
int i;
|
|
uint pending;
|
|
sunxi_dma_reg *dma_reg = (sunxi_dma_reg *)SUNXI_DMA_BASE;
|
|
|
|
for (i = 0; i < 8 && i < SUNXI_DMA_MAX; i++) {
|
|
pending = (DMA_PKG_END_INT << (i * 4));
|
|
if (readl(&dma_reg->irq_pending0) & pending) {
|
|
writel(pending, &dma_reg->irq_pending0);
|
|
if (dma_channal_source[i].dma_func.m_func != NULL)
|
|
dma_channal_source[i].dma_func.m_func(dma_channal_source[i].dma_func.m_data);
|
|
}
|
|
}
|
|
for (i = 8; i < SUNXI_DMA_MAX; i++) {
|
|
pending = (DMA_PKG_END_INT << ((i - 8) * 4));
|
|
if (readl(&dma_reg->irq_pending1) & pending) {
|
|
writel(pending, &dma_reg->irq_pending1);
|
|
if (dma_channal_source[i].dma_func.m_func != NULL)
|
|
dma_channal_source[i].dma_func.m_func(dma_channal_source[i].dma_func.m_data);
|
|
}
|
|
}
|
|
}
|
|
|
|
void sunxi_dma_init(void)
|
|
{
|
|
int i;
|
|
sunxi_dma_reg *const dma_reg = (sunxi_dma_reg *)SUNXI_DMA_BASE;
|
|
struct sunxi_ccm_reg *const ccm =
|
|
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
|
|
|
|
if (dma_init > 0)
|
|
return ;
|
|
|
|
#if defined(CONFIG_SUNXI_VERSION1)
|
|
setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_DMA);
|
|
setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_GATE_OFFSET_DMA);
|
|
#else
|
|
/* dma : mbus clock gating */
|
|
setbits_le32(&ccm->mbus_gate, 1 << 0);
|
|
|
|
/* dma reset */
|
|
setbits_le32(&ccm->dma_gate_reset, 1 << DMA_RST_OFS);
|
|
|
|
/* dma gating */
|
|
setbits_le32(&ccm->dma_gate_reset, 1 << DMA_GATING_OFS);
|
|
#endif
|
|
|
|
#if defined(CFG_SUNXI_DMA_DEFAULT_NS)
|
|
writel(dma_reg->security & (~CFG_SUNXI_DMA_CHANNEL_MASK), &dma_reg->security);
|
|
#endif
|
|
|
|
writel(0, &dma_reg->irq_en0);
|
|
writel(0, &dma_reg->irq_en0);
|
|
|
|
writel(0xffffffff, &dma_reg->irq_pending0);
|
|
writel(0xffffffff, &dma_reg->irq_pending1);
|
|
|
|
/*auto MCLK gating disable*/
|
|
clrsetbits_le32(&dma_reg->auto_gate, 0x7 << 0, 0x7 << 0);
|
|
|
|
memset((void *)dma_channal_source, 0, SUNXI_DMA_MAX * sizeof(struct sunxi_dma_source_t));
|
|
|
|
for (i = 0; i < SUNXI_DMA_MAX; i++) {
|
|
dma_channal_source[i].used = 0;
|
|
dma_channal_source[i].channal = &(dma_reg->channal[i]);
|
|
dma_channal_source[i].desc = &g_sunxi_dma_desc[i];
|
|
}
|
|
|
|
dma_int_cnt = 0;
|
|
dma_init = 1;
|
|
|
|
return ;
|
|
}
|
|
|
|
void sunxi_dma_exit(void)
|
|
{
|
|
int i;
|
|
ulong hdma;
|
|
sunxi_dma_reg *dma_reg = (sunxi_dma_reg *)SUNXI_DMA_BASE;
|
|
struct sunxi_ccm_reg *const ccm =
|
|
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
|
|
|
|
/* free dma channal if other module not free it */
|
|
for (i = 0; i < SUNXI_DMA_MAX; i++) {
|
|
if (dma_channal_source[i].used == 1) {
|
|
hdma = (ulong)&dma_channal_source[i];
|
|
sunxi_dma_disable_int(hdma);
|
|
sunxi_dma_free_int(hdma);
|
|
writel(0, &dma_channal_source[i].channal->enable);
|
|
dma_channal_source[i].used = 0;
|
|
}
|
|
}
|
|
/* irq disable */
|
|
writel(0, &dma_reg->irq_en0);
|
|
writel(0, &dma_reg->irq_en0);
|
|
|
|
writel(0xffffffff, &dma_reg->irq_pending0);
|
|
writel(0xffffffff, &dma_reg->irq_pending1);
|
|
|
|
#if defined(CFG_SUNXI_DMA_DEFAULT_NS)
|
|
writel(dma_reg->security | CFG_SUNXI_DMA_CHANNEL_MASK, &dma_reg->security);
|
|
#endif
|
|
|
|
#if defined(CONFIG_SUNXI_VERSION1)
|
|
clrbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_DMA);
|
|
#else
|
|
/* close dma clock when dma exit */
|
|
clrbits_le32(&ccm->dma_gate_reset, 1 << DMA_GATING_OFS);
|
|
|
|
clrbits_le32(&ccm->dma_gate_reset, 1 << DMA_RST_OFS);
|
|
#endif
|
|
|
|
dma_init--;
|
|
|
|
}
|
|
|
|
ulong sunxi_dma_request_from_last(uint dmatype)
|
|
{
|
|
int i;
|
|
|
|
for (i = SUNXI_DMA_MAX - 1; i >= 0; i--) {
|
|
if (dma_channal_source[i].used == 0) {
|
|
dma_channal_source[i].used = 1;
|
|
dma_channal_source[i].channal_count = i;
|
|
return (ulong)&dma_channal_source[i];
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
ulong sunxi_dma_request(uint dmatype)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < SUNXI_DMA_MAX; i++) {
|
|
if (dma_channal_source[i].used == 0) {
|
|
dma_channal_source[i].used = 1;
|
|
dma_channal_source[i].channal_count = i;
|
|
return (ulong)&dma_channal_source[i];
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int sunxi_dma_release(ulong hdma)
|
|
{
|
|
struct sunxi_dma_source_t *dma_source = (struct sunxi_dma_source_t *)hdma;
|
|
|
|
if (!dma_source->used)
|
|
return -1;
|
|
|
|
sunxi_dma_disable_int(hdma);
|
|
sunxi_dma_free_int(hdma);
|
|
|
|
dma_source->used = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int sunxi_dma_setting(ulong hdma, sunxi_dma_set *cfg)
|
|
{
|
|
|
|
uint commit_para;
|
|
sunxi_dma_set *dma_set = cfg;
|
|
sunxi_dma_source *dma_source = (sunxi_dma_source *)hdma;
|
|
sunxi_dma_desc *desc = dma_source->desc;
|
|
void __iomem *channal_addr = (&(dma_set->channal_cfg));
|
|
|
|
if (!dma_source->used)
|
|
return -1;
|
|
|
|
if (dma_set->loop_mode)
|
|
desc->link = sunxi_get_lw32_addr((&dma_source->desc));
|
|
else
|
|
desc->link = SUNXI_DMA_LINK_NULL;
|
|
|
|
commit_para = (dma_set->wait_cyc & 0xff);
|
|
commit_para |= (dma_set->data_block_size & 0xff) << 8;
|
|
|
|
writel(commit_para, &desc->commit_para);
|
|
writel(readl(channal_addr), &desc->config);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int sunxi_dma_start(ulong hdma, phys_addr_t saddr, phys_addr_t daddr, uint bytes)
|
|
{
|
|
sunxi_dma_source *dma_source = (sunxi_dma_source *)hdma;
|
|
sunxi_dma_channal_reg *channal = dma_source->channal;
|
|
sunxi_dma_desc *desc = dma_source->desc;
|
|
uint val = 0;
|
|
|
|
if (!dma_source->used)
|
|
return -1;
|
|
|
|
/*config desc */
|
|
|
|
writel(saddr, &desc->source_addr);
|
|
writel(daddr, &desc->dest_addr);
|
|
writel(bytes, &desc->byte_count);
|
|
|
|
flush_dcache_range((unsigned long)desc, (unsigned long)desc + sizeof(sunxi_dma_desc));
|
|
|
|
/* start dma */
|
|
val = sunxi_get_lw32_addr(desc);
|
|
writel(val, &channal->desc_addr);
|
|
writel(1, &channal->enable);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int sunxi_dma_stop(ulong hdma)
|
|
{
|
|
sunxi_dma_source *dma_source = (sunxi_dma_source *)hdma;
|
|
sunxi_dma_channal_reg *channal = dma_source->channal;
|
|
|
|
if (!dma_source->used)
|
|
return -1;
|
|
writel(0, &channal->enable);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int sunxi_dma_querystatus(ulong hdma)
|
|
{
|
|
uint channal_count;
|
|
sunxi_dma_source *dma_source = (sunxi_dma_source *)hdma;
|
|
sunxi_dma_reg *dma_reg = (sunxi_dma_reg *)SUNXI_DMA_BASE;
|
|
|
|
if (!dma_source->used)
|
|
return -1;
|
|
|
|
channal_count = dma_source->channal_count;
|
|
|
|
return (readl(&dma_reg->status) >> channal_count) & 0x01;
|
|
}
|
|
|
|
int sunxi_dma_install_int(ulong hdma, void *p)
|
|
{
|
|
sunxi_dma_source *dma_channal = (sunxi_dma_source *)hdma;
|
|
sunxi_dma_reg *dma_reg = (sunxi_dma_reg *)SUNXI_DMA_BASE;
|
|
uint channal_count;
|
|
|
|
if (!dma_channal->used)
|
|
return -1;
|
|
|
|
channal_count = dma_channal->channal_count;
|
|
|
|
if (channal_count < 8)
|
|
writel((7 << channal_count * 4), &dma_reg->irq_pending0);
|
|
else
|
|
writel((7 << (channal_count - 8) * 4), &dma_reg->irq_pending1);
|
|
|
|
if (!dma_channal->dma_func.m_func) {
|
|
dma_channal->dma_func.m_func = 0;
|
|
dma_channal->dma_func.m_data = p;
|
|
} else {
|
|
printf("dma 0x%lx int is used already, you have to free it first\n", hdma);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int sunxi_dma_enable_int(ulong hdma)
|
|
{
|
|
sunxi_dma_source *dma_channal = (sunxi_dma_source *)hdma;
|
|
sunxi_dma_reg *dma_status = (sunxi_dma_reg *)SUNXI_DMA_BASE;
|
|
uint channal_count;
|
|
|
|
if (!dma_channal->used)
|
|
return -1;
|
|
|
|
channal_count = dma_channal->channal_count;
|
|
if (channal_count < 8) {
|
|
if (readl(&dma_status->irq_en0) & (DMA_PKG_END_INT << channal_count * 4)) {
|
|
printf("dma 0x%lx int is avaible already\n", hdma);
|
|
return 0;
|
|
}
|
|
setbits_le32(&dma_status->irq_en0, (DMA_PKG_END_INT << channal_count * 4));
|
|
} else {
|
|
if (readl(&dma_status->irq_en1) & (DMA_PKG_END_INT << (channal_count - 8) * 4)) {
|
|
printf("dma 0x%lx int is avaible already\n", hdma);
|
|
return 0;
|
|
}
|
|
setbits_le32(&dma_status->irq_en1, (DMA_PKG_END_INT << (channal_count - 8) * 4));
|
|
}
|
|
|
|
dma_int_cnt++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int sunxi_dma_disable_int(ulong hdma)
|
|
{
|
|
sunxi_dma_source *dma_channal = (sunxi_dma_source *)hdma;
|
|
sunxi_dma_reg *dma_reg = (sunxi_dma_reg *)SUNXI_DMA_BASE;
|
|
uint channal_count;
|
|
|
|
if (!dma_channal->used)
|
|
return -1;
|
|
|
|
channal_count = dma_channal->channal_count;
|
|
if (channal_count < 8) {
|
|
if (!(readl(&dma_reg->irq_en0) & (DMA_PKG_END_INT << channal_count * 4))) {
|
|
printf("dma 0x%lx int is not used yet\n", hdma);
|
|
return 0;
|
|
}
|
|
clrbits_le32(&dma_reg->irq_en0, (DMA_PKG_END_INT << channal_count * 4));
|
|
} else {
|
|
if (!(readl(&dma_reg->irq_en1) & (DMA_PKG_END_INT << (channal_count - 8) * 4))) {
|
|
printf("dma 0x%lx int is not used yet\n", hdma);
|
|
return 0;
|
|
}
|
|
clrbits_le32(&dma_reg->irq_en1, (DMA_PKG_END_INT << (channal_count - 8) * 4));
|
|
}
|
|
|
|
/* disable golbal int */
|
|
if (dma_int_cnt > 0)
|
|
dma_int_cnt--;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int sunxi_dma_free_int(ulong hdma)
|
|
{
|
|
sunxi_dma_source *dma_channal = (sunxi_dma_source *)hdma;
|
|
sunxi_dma_reg *dma_status = (sunxi_dma_reg *)SUNXI_DMA_BASE;
|
|
uint channal_count;
|
|
|
|
if (!dma_channal->used)
|
|
return -1;
|
|
|
|
channal_count = dma_channal->channal_count;
|
|
if (channal_count < 8)
|
|
writel((7 << channal_count), &dma_status->irq_pending0);
|
|
else
|
|
writel((7 << (channal_count - 8)), &dma_status->irq_pending1);
|
|
|
|
if (dma_channal->dma_func.m_func) {
|
|
dma_channal->dma_func.m_func = NULL;
|
|
dma_channal->dma_func.m_data = NULL;
|
|
} else {
|
|
printf("dma 0x%lx int is free, you do not need to free it again\n", hdma);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|