blob: 334cb9821b170807978b77d563113276c16dd02b [file] [log] [blame]
/*
* IMG DMA Controller (DMAC) specific DMA code.
*
* Copyright (C) 2010,2012 Imagination Technologies Ltd.
*/
#include <linux/errno.h>
#include <linux/export.h>
#include <linux/spinlock.h>
#include <linux/seq_file.h>
#include <linux/proc_fs.h>
#include <linux/dma-mapping.h>
#include <linux/io.h>
#include <asm/img_dma.h>
#include <asm/img_dmac.h>
#if defined(CONFIG_SOC_CHORUS2)
#include <asm/soc-chorus2/dma.h>
#include <asm/soc-chorus2/c2_irqnums.h>
#else
#error /*Add your SoC here*/
#endif
int img_dma_reset(int dmanr)
{
unsigned int *dmac_base;
if (dmanr >= MAX_DMA_CHANNELS)
return -EINVAL;
dmac_base = (unsigned int *)(DMAC_HWBASE +
(dmanr * DMAC_CHANNEL_STRIDE));
/* Set SRST bit in DMAC_COUNT_REG */
iowrite32(DMAC_CNTN_REG_SRST_BIT, dmac_base+DMAC_COUNT_REG);
/* Clear all other registers */
iowrite32(0, dmac_base+DMAC_SETUP_REG);
iowrite32(0, dmac_base+DMAC_PERIPH_REG);
iowrite32(0, dmac_base+DMAC_IRQSTAT_REG);
iowrite32(0, dmac_base+DMAC_2DMODE_REG);
/* Finally release the SRST bit */
iowrite32(0, dmac_base+DMAC_COUNT_REG);
return 0;
}
EXPORT_SYMBOL(img_dma_reset);
int img_dma_get_irq(int dmanr)
{
return external_irq_map(DMA0_IRQ_NUM + dmanr);
}
EXPORT_SYMBOL(img_dma_get_irq);
int img_dma_set_data_address(int dmanr, u32 address)
{
u32 *base;
base = get_dma_regs(dmanr);
iowrite32(address, base+DMAC_SETUP_REG);
return 0;
}
EXPORT_SYMBOL(img_dma_set_data_address);
int img_dma_set_length(int dmanr, unsigned long bytes, unsigned int width)
{
u32 c;
u32 *base;
u16 count = (u16)bytes;
base = get_dma_regs(dmanr);
c = ioread32(base+DMAC_COUNT_REG);
c &= ~DMAC_CNTN_REG_PW_MASK;
switch (width) {
case IMG_DMA_WIDTH_32:
c |= DMAC_CNTN_REG_PW_32;
count /= 4;
break;
case IMG_DMA_WIDTH_16:
c |= DMAC_CNTN_REG_PW_16;
count /= 2;
break;
case IMG_DMA_WIDTH_8:
c |= DMAC_CNTN_REG_PW_8;
break;
default:
BUG();
break;
}
c |= count;
iowrite32(c, base+DMAC_COUNT_REG);
return 0;
}
EXPORT_SYMBOL(img_dma_set_length);
int img_dma_set_io_address(int dmanr, u32 address,
unsigned int burst_size)
{
u32 *base;
u32 addr, periph;
base = get_dma_regs(dmanr);
periph = ioread32(base + DMAC_PERIPH_REG);
/* We set all the non-list mode bits here. */
addr = (address & DMAC_PHADDRN_REG_ADDR_BITS) >> 2;
addr |= ((burst_size << DMAC_PHADDRN_REG_BURST_S)
& DMAC_PHADDRN_REG_BURST_BITS);
periph &= ~(DMAC_PHADDRN_REG_ADDR_BITS|DMAC_PHADDRN_REG_BURST_BITS);
periph |= addr;
iowrite32(periph, base + DMAC_PERIPH_REG);
return 0;
}
EXPORT_SYMBOL(img_dma_set_io_address);
int img_dma_set_direction(int dmanr, enum img_dma_direction direction)
{
u32 *base;
u32 c;
base = get_dma_regs(dmanr);
c = ioread32(base+DMAC_COUNT_REG);
switch (direction) {
case DMA_FROM_DEVICE:
c |= DMAC_CNTN_REG_DIR_BIT;
break;
case DMA_TO_DEVICE:
c &= ~DMAC_CNTN_REG_DIR_BIT;
break;
default:
BUG();
break;
}
iowrite32(c, base+DMAC_COUNT_REG);
return 0;
}
EXPORT_SYMBOL(img_dma_set_direction);
int img_dma_start(int dmanr)
{
u32 *base;
u32 c;
u32 s;
base = get_dma_regs(dmanr);
/* Clear down the done bit */
s = ioread32(base+DMAC_IRQSTAT_REG);
s &= ~DMAC_IRQSTATN_REG_FIN_BIT;
iowrite32(s, base+DMAC_IRQSTAT_REG);
c = ioread32(base+DMAC_COUNT_REG);
c &= ~DMAC_CNTN_REG_SRST_BIT;
iowrite32(c, base+DMAC_COUNT_REG);
c |= DMAC_CNTN_REG_EN_BIT;
iowrite32(c, base+DMAC_COUNT_REG);
return 0;
}
EXPORT_SYMBOL(img_dma_start);
int img_dma_is_finished(int dmanr)
{
u32 *base;
u32 s;
base = get_dma_regs(dmanr);
s = ioread32(base+DMAC_IRQSTAT_REG);
return s & DMAC_IRQSTATN_REG_FIN_BIT;
}
EXPORT_SYMBOL(img_dma_is_finished);
int img_dma_set_list_addr(int dma_channel, u32 address)
{
u32 temp;
u32 *base;
if (dma_channel >= MAX_DMA_CHANNELS)
return -EINVAL;
base = get_dma_regs(dma_channel);
iowrite32(address, base+DMAC_SETUP_REG);
temp = ioread32(base+DMAC_COUNT_REG);
temp |= DMAC_CNTN_REG_LIEN_BIT;
iowrite32(temp, base+DMAC_COUNT_REG);
return 0;
}
EXPORT_SYMBOL(img_dma_set_list_addr);
int img_dma_start_list(int dma_channel)
{
u32 temp;
u32 *base;
if (dma_channel >= MAX_DMA_CHANNELS)
return -EINVAL;
base = get_dma_regs(dma_channel);
temp = ioread32(base+DMAC_COUNT_REG);
temp |= DMAC_CNTN_REG_LEN_BIT;
iowrite32(temp, base+DMAC_COUNT_REG);
return 0;
}
EXPORT_SYMBOL(img_dma_start_list);
int img_dma_stop_list(int dma_channel)
{
u32 temp;
u32 *base;
if (dma_channel >= MAX_DMA_CHANNELS)
return -EINVAL;
base = get_dma_regs(dma_channel);
temp = ioread32(base+DMAC_COUNT_REG);
temp &= ~DMAC_CNTN_REG_LEN_BIT;
iowrite32(temp, base+DMAC_COUNT_REG);
return 0;
}
EXPORT_SYMBOL(img_dma_stop_list);
int img_dma_get_int_status(int dma_channel, u32 *status)
{
u32 *base;
if (dma_channel >= MAX_DMA_CHANNELS)
return -EINVAL;
base = get_dma_regs(dma_channel);
*status = ioread32(base + DMAC_IRQSTAT_REG);
return 0;
}
EXPORT_SYMBOL(img_dma_get_int_status);
int img_dma_set_int_status(int dma_channel, u32 status)
{
u32 *base;
if (dma_channel >= MAX_DMA_CHANNELS)
return -EINVAL;
base = get_dma_regs(dma_channel);
iowrite32(status, base + DMAC_IRQSTAT_REG);
return 0;
}
EXPORT_SYMBOL(img_dma_set_int_status);
int img_dma_has_request(int dma_channel)
{
u32 *base;
if (dma_channel >= MAX_DMA_CHANNELS)
return -EINVAL;
base = get_dma_regs(dma_channel);
return !!(ioread32(base + DMAC_COUNT_REG) & DMAC_CNTN_REG_DREQ_BIT);
}
EXPORT_SYMBOL(img_dma_has_request);
/**
* img_dma_set_access_delay() - sets the access delay
* @dma_channel: DMA channel number
* @delay: Delay in range 0-7, where actual delay = @delay * 256
* cycles
*
*/
int img_dma_set_access_delay(int dma_channel, u8 delay)
{
u32 *base;
u32 paddr;
if (dma_channel >= MAX_DMA_CHANNELS)
return -EINVAL;
if (delay > 7)
return -EINVAL;
base = get_dma_regs(dma_channel);
paddr = ioread32(base + DMAC_PERIPH_REG);
paddr |= delay << DMAC_PHADDRN_REG_ACCDEL_S;
iowrite32(paddr, base + DMAC_PERIPH_REG);
return 0;
}
EXPORT_SYMBOL(img_dma_set_access_delay);