2010-10-06 12:25:55 +04:00
/*
* drivers / dma / imx - dma . c
*
* This file contains a driver for the Freescale i . MX DMA engine
* found on i . MX1 / 21 / 27
*
* Copyright 2010 Sascha Hauer , Pengutronix < s . hauer @ pengutronix . de >
2012-03-02 12:28:47 +04:00
* Copyright 2012 Javier Martin , Vista Silicon < javier . martin @ vista - silicon . com >
2010-10-06 12:25:55 +04:00
*
* The code contained herein is licensed under the GNU General Public
* License . You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations :
*
* http : //www.opensource.org/licenses/gpl-license.html
* http : //www.gnu.org/copyleft/gpl.html
*/
# include <linux/init.h>
2011-08-30 11:08:24 +04:00
# include <linux/module.h>
2010-10-06 12:25:55 +04:00
# include <linux/types.h>
# include <linux/mm.h>
# include <linux/interrupt.h>
# include <linux/spinlock.h>
# include <linux/device.h>
# include <linux/dma-mapping.h>
# include <linux/slab.h>
# include <linux/platform_device.h>
2012-03-22 17:54:01 +04:00
# include <linux/clk.h>
2010-10-06 12:25:55 +04:00
# include <linux/dmaengine.h>
2012-03-09 13:25:25 +04:00
# include <linux/module.h>
2010-10-06 12:25:55 +04:00
# include <asm/irq.h>
2012-03-22 17:54:01 +04:00
# include <mach/dma.h>
2010-10-06 12:25:55 +04:00
# include <mach/hardware.h>
2012-03-07 02:34:26 +04:00
# include "dmaengine.h"
2012-03-02 12:28:47 +04:00
# define IMXDMA_MAX_CHAN_DESCRIPTORS 16
2012-03-22 17:54:01 +04:00
# define IMX_DMA_CHANNELS 16
# define IMX_DMA_LENGTH_LOOP ((unsigned int)-1)
# define IMX_DMA_MEMSIZE_32 (0 << 4)
# define IMX_DMA_MEMSIZE_8 (1 << 4)
# define IMX_DMA_MEMSIZE_16 (2 << 4)
# define IMX_DMA_TYPE_LINEAR (0 << 10)
# define IMX_DMA_TYPE_2D (1 << 10)
# define IMX_DMA_TYPE_FIFO (2 << 10)
# define IMX_DMA_ERR_BURST (1 << 0)
# define IMX_DMA_ERR_REQUEST (1 << 1)
# define IMX_DMA_ERR_TRANSFER (1 << 2)
# define IMX_DMA_ERR_BUFFER (1 << 3)
# define IMX_DMA_ERR_TIMEOUT (1 << 4)
# define DMA_DCR 0x00 /* Control Register */
# define DMA_DISR 0x04 /* Interrupt status Register */
# define DMA_DIMR 0x08 /* Interrupt mask Register */
# define DMA_DBTOSR 0x0c /* Burst timeout status Register */
# define DMA_DRTOSR 0x10 /* Request timeout Register */
# define DMA_DSESR 0x14 /* Transfer Error Status Register */
# define DMA_DBOSR 0x18 /* Buffer overflow status Register */
# define DMA_DBTOCR 0x1c /* Burst timeout control Register */
# define DMA_WSRA 0x40 /* W-Size Register A */
# define DMA_XSRA 0x44 /* X-Size Register A */
# define DMA_YSRA 0x48 /* Y-Size Register A */
# define DMA_WSRB 0x4c /* W-Size Register B */
# define DMA_XSRB 0x50 /* X-Size Register B */
# define DMA_YSRB 0x54 /* Y-Size Register B */
# define DMA_SAR(x) (0x80 + ((x) << 6)) /* Source Address Registers */
# define DMA_DAR(x) (0x84 + ((x) << 6)) /* Destination Address Registers */
# define DMA_CNTR(x) (0x88 + ((x) << 6)) /* Count Registers */
# define DMA_CCR(x) (0x8c + ((x) << 6)) /* Control Registers */
# define DMA_RSSR(x) (0x90 + ((x) << 6)) /* Request source select Registers */
# define DMA_BLR(x) (0x94 + ((x) << 6)) /* Burst length Registers */
# define DMA_RTOR(x) (0x98 + ((x) << 6)) /* Request timeout Registers */
# define DMA_BUCR(x) (0x98 + ((x) << 6)) /* Bus Utilization Registers */
# define DMA_CCNR(x) (0x9C + ((x) << 6)) /* Channel counter Registers */
# define DCR_DRST (1<<1)
# define DCR_DEN (1<<0)
# define DBTOCR_EN (1<<15)
# define DBTOCR_CNT(x) ((x) & 0x7fff)
# define CNTR_CNT(x) ((x) & 0xffffff)
# define CCR_ACRPT (1<<14)
# define CCR_DMOD_LINEAR (0x0 << 12)
# define CCR_DMOD_2D (0x1 << 12)
# define CCR_DMOD_FIFO (0x2 << 12)
# define CCR_DMOD_EOBFIFO (0x3 << 12)
# define CCR_SMOD_LINEAR (0x0 << 10)
# define CCR_SMOD_2D (0x1 << 10)
# define CCR_SMOD_FIFO (0x2 << 10)
# define CCR_SMOD_EOBFIFO (0x3 << 10)
# define CCR_MDIR_DEC (1<<9)
# define CCR_MSEL_B (1<<8)
# define CCR_DSIZ_32 (0x0 << 6)
# define CCR_DSIZ_8 (0x1 << 6)
# define CCR_DSIZ_16 (0x2 << 6)
# define CCR_SSIZ_32 (0x0 << 4)
# define CCR_SSIZ_8 (0x1 << 4)
# define CCR_SSIZ_16 (0x2 << 4)
# define CCR_REN (1<<3)
# define CCR_RPT (1<<2)
# define CCR_FRC (1<<1)
# define CCR_CEN (1<<0)
# define RTOR_EN (1<<15)
# define RTOR_CLK (1<<14)
# define RTOR_PSC (1<<13)
2012-03-02 12:28:47 +04:00
enum imxdma_prep_type {
IMXDMA_DESC_MEMCPY ,
IMXDMA_DESC_INTERLEAVED ,
IMXDMA_DESC_SLAVE_SG ,
IMXDMA_DESC_CYCLIC ,
} ;
2012-03-22 17:54:01 +04:00
/*
* struct imxdma_channel_internal - i . MX specific DMA extension
* @ name : name specified by DMA client
* @ irq_handler : client callback for end of transfer
* @ err_handler : client callback for error condition
* @ data : clients context data for callbacks
* @ dma_mode : direction of the transfer % DMA_MODE_READ or % DMA_MODE_WRITE
* @ sg : pointer to the actual read / written chunk for scatter - gather emulation
* @ resbytes : total residual number of bytes to transfer
* ( it can be lower or same as sum of SG mapped chunk sizes )
* @ sgcount : number of chunks to be read / written
*
* Structure is used for IMX DMA processing . It would be probably good
* @ struct dma_struct in the future for external interfacing and use
* @ struct imxdma_channel_internal only as extension to it .
*/
struct imxdma_channel_internal {
struct scatterlist * sg ;
unsigned int resbytes ;
int in_use ;
struct timer_list watchdog ;
int hw_chaining ;
} ;
2012-03-02 12:28:47 +04:00
struct imxdma_desc {
struct list_head node ;
struct dma_async_tx_descriptor desc ;
enum dma_status status ;
dma_addr_t src ;
dma_addr_t dest ;
size_t len ;
2012-03-22 17:54:03 +04:00
enum dma_transfer_direction direction ;
2012-03-02 12:28:47 +04:00
enum imxdma_prep_type type ;
/* For memcpy and interleaved */
unsigned int config_port ;
unsigned int config_mem ;
/* For interleaved transfers */
unsigned int x ;
unsigned int y ;
unsigned int w ;
/* For slave sg and cyclic */
struct scatterlist * sg ;
unsigned int sgcount ;
} ;
2010-10-06 12:25:55 +04:00
struct imxdma_channel {
2012-03-22 17:54:01 +04:00
struct imxdma_channel_internal internal ;
2010-10-06 12:25:55 +04:00
struct imxdma_engine * imxdma ;
unsigned int channel ;
2012-03-02 12:28:47 +04:00
struct tasklet_struct dma_tasklet ;
struct list_head ld_free ;
struct list_head ld_queue ;
struct list_head ld_active ;
int descs_allocated ;
2010-10-06 12:25:55 +04:00
enum dma_slave_buswidth word_size ;
dma_addr_t per_address ;
u32 watermark_level ;
struct dma_chan chan ;
spinlock_t lock ;
struct dma_async_tx_descriptor desc ;
enum dma_status status ;
int dma_request ;
struct scatterlist * sg_list ;
2012-03-22 17:54:06 +04:00
u32 ccr_from_device ;
u32 ccr_to_device ;
2010-10-06 12:25:55 +04:00
} ;
struct imxdma_engine {
struct device * dev ;
2011-01-12 15:14:37 +03:00
struct device_dma_parameters dma_parms ;
2010-10-06 12:25:55 +04:00
struct dma_device dma_device ;
2012-03-22 17:54:01 +04:00
struct imxdma_channel channel [ IMX_DMA_CHANNELS ] ;
2010-10-06 12:25:55 +04:00
} ;
static struct imxdma_channel * to_imxdma_chan ( struct dma_chan * chan )
{
return container_of ( chan , struct imxdma_channel , chan ) ;
}
2012-03-02 12:28:47 +04:00
static inline bool imxdma_chan_is_doing_cyclic ( struct imxdma_channel * imxdmac )
2010-10-06 12:25:55 +04:00
{
2012-03-02 12:28:47 +04:00
struct imxdma_desc * desc ;
if ( ! list_empty ( & imxdmac - > ld_active ) ) {
desc = list_first_entry ( & imxdmac - > ld_active , struct imxdma_desc ,
node ) ;
if ( desc - > type = = IMXDMA_DESC_CYCLIC )
return true ;
}
return false ;
2010-10-06 12:25:55 +04:00
}
2012-03-22 17:54:01 +04:00
/* TODO: put this inside any struct */
static void __iomem * imx_dmav1_baseaddr ;
static struct clk * dma_clk ;
static void imx_dmav1_writel ( unsigned val , unsigned offset )
{
__raw_writel ( val , imx_dmav1_baseaddr + offset ) ;
}
static unsigned imx_dmav1_readl ( unsigned offset )
2010-10-06 12:25:55 +04:00
{
2012-03-22 17:54:01 +04:00
return __raw_readl ( imx_dmav1_baseaddr + offset ) ;
}
2010-10-06 12:25:55 +04:00
2012-03-22 17:54:01 +04:00
static int imxdma_hw_chain ( struct imxdma_channel_internal * imxdma )
{
if ( cpu_is_mx27 ( ) )
return imxdma - > hw_chaining ;
else
return 0 ;
}
/*
* imxdma_sg_next - prepare next chunk for scatter - gather DMA emulation
*/
2012-03-22 17:54:03 +04:00
static inline int imxdma_sg_next ( struct imxdma_desc * d , struct scatterlist * sg )
2012-03-22 17:54:01 +04:00
{
2012-03-22 17:54:03 +04:00
struct imxdma_channel * imxdmac = to_imxdma_chan ( d - > desc . chan ) ;
2012-03-22 17:54:01 +04:00
struct imxdma_channel_internal * imxdma = & imxdmac - > internal ;
unsigned long now ;
now = min ( imxdma - > resbytes , sg - > length ) ;
if ( imxdma - > resbytes ! = IMX_DMA_LENGTH_LOOP )
imxdma - > resbytes - = now ;
2012-03-22 17:54:03 +04:00
if ( d - > direction = = DMA_DEV_TO_MEM )
2012-03-22 17:54:01 +04:00
imx_dmav1_writel ( sg - > dma_address , DMA_DAR ( imxdmac - > channel ) ) ;
else
imx_dmav1_writel ( sg - > dma_address , DMA_SAR ( imxdmac - > channel ) ) ;
imx_dmav1_writel ( now , DMA_CNTR ( imxdmac - > channel ) ) ;
pr_debug ( " imxdma%d: next sg chunk dst 0x%08x, src 0x%08x, "
" size 0x%08x \n " , imxdmac - > channel ,
imx_dmav1_readl ( DMA_DAR ( imxdmac - > channel ) ) ,
imx_dmav1_readl ( DMA_SAR ( imxdmac - > channel ) ) ,
imx_dmav1_readl ( DMA_CNTR ( imxdmac - > channel ) ) ) ;
return now ;
}
2012-03-22 17:54:03 +04:00
static void imxdma_enable_hw ( struct imxdma_desc * d )
2012-03-22 17:54:01 +04:00
{
2012-03-22 17:54:03 +04:00
struct imxdma_channel * imxdmac = to_imxdma_chan ( d - > desc . chan ) ;
2012-03-22 17:54:01 +04:00
int channel = imxdmac - > channel ;
unsigned long flags ;
pr_debug ( " imxdma%d: imx_dma_enable \n " , channel ) ;
if ( imxdmac - > internal . in_use )
return ;
local_irq_save ( flags ) ;
imx_dmav1_writel ( 1 < < channel , DMA_DISR ) ;
imx_dmav1_writel ( imx_dmav1_readl ( DMA_DIMR ) & ~ ( 1 < < channel ) , DMA_DIMR ) ;
imx_dmav1_writel ( imx_dmav1_readl ( DMA_CCR ( channel ) ) | CCR_CEN |
CCR_ACRPT , DMA_CCR ( channel ) ) ;
if ( ( cpu_is_mx21 ( ) | | cpu_is_mx27 ( ) ) & &
imxdmac - > internal . sg & & imxdma_hw_chain ( & imxdmac - > internal ) ) {
imxdmac - > internal . sg = sg_next ( imxdmac - > internal . sg ) ;
if ( imxdmac - > internal . sg ) {
u32 tmp ;
2012-03-22 17:54:03 +04:00
imxdma_sg_next ( d , imxdmac - > internal . sg ) ;
2012-03-22 17:54:01 +04:00
tmp = imx_dmav1_readl ( DMA_CCR ( channel ) ) ;
imx_dmav1_writel ( tmp | CCR_RPT | CCR_ACRPT ,
DMA_CCR ( channel ) ) ;
}
}
imxdmac - > internal . in_use = 1 ;
local_irq_restore ( flags ) ;
}
static void imxdma_disable_hw ( struct imxdma_channel * imxdmac )
{
int channel = imxdmac - > channel ;
unsigned long flags ;
pr_debug ( " imxdma%d: imx_dma_disable \n " , channel ) ;
if ( imxdma_hw_chain ( & imxdmac - > internal ) )
del_timer ( & imxdmac - > internal . watchdog ) ;
local_irq_save ( flags ) ;
imx_dmav1_writel ( imx_dmav1_readl ( DMA_DIMR ) | ( 1 < < channel ) , DMA_DIMR ) ;
imx_dmav1_writel ( imx_dmav1_readl ( DMA_CCR ( channel ) ) & ~ CCR_CEN ,
DMA_CCR ( channel ) ) ;
imx_dmav1_writel ( 1 < < channel , DMA_DISR ) ;
imxdmac - > internal . in_use = 0 ;
local_irq_restore ( flags ) ;
}
static void imxdma_watchdog ( unsigned long data )
2010-10-06 12:25:55 +04:00
{
2012-03-22 17:54:01 +04:00
struct imxdma_channel * imxdmac = ( struct imxdma_channel * ) data ;
int channel = imxdmac - > channel ;
2010-10-06 12:25:55 +04:00
2012-03-22 17:54:01 +04:00
imx_dmav1_writel ( 0 , DMA_CCR ( channel ) ) ;
imxdmac - > internal . in_use = 0 ;
imxdmac - > internal . sg = NULL ;
/* Tasklet watchdog error handler */
2012-03-02 12:28:47 +04:00
tasklet_schedule ( & imxdmac - > dma_tasklet ) ;
2012-03-22 17:54:01 +04:00
pr_debug ( " imxdma%d: watchdog timeout! \n " , imxdmac - > channel ) ;
}
static irqreturn_t imxdma_err_handler ( int irq , void * dev_id )
{
struct imxdma_engine * imxdma = dev_id ;
struct imxdma_channel_internal * internal ;
unsigned int err_mask ;
int i , disr ;
int errcode ;
disr = imx_dmav1_readl ( DMA_DISR ) ;
err_mask = imx_dmav1_readl ( DMA_DBTOSR ) |
imx_dmav1_readl ( DMA_DRTOSR ) |
imx_dmav1_readl ( DMA_DSESR ) |
imx_dmav1_readl ( DMA_DBOSR ) ;
if ( ! err_mask )
return IRQ_HANDLED ;
imx_dmav1_writel ( disr & err_mask , DMA_DISR ) ;
for ( i = 0 ; i < IMX_DMA_CHANNELS ; i + + ) {
if ( ! ( err_mask & ( 1 < < i ) ) )
continue ;
internal = & imxdma - > channel [ i ] . internal ;
errcode = 0 ;
if ( imx_dmav1_readl ( DMA_DBTOSR ) & ( 1 < < i ) ) {
imx_dmav1_writel ( 1 < < i , DMA_DBTOSR ) ;
errcode | = IMX_DMA_ERR_BURST ;
}
if ( imx_dmav1_readl ( DMA_DRTOSR ) & ( 1 < < i ) ) {
imx_dmav1_writel ( 1 < < i , DMA_DRTOSR ) ;
errcode | = IMX_DMA_ERR_REQUEST ;
}
if ( imx_dmav1_readl ( DMA_DSESR ) & ( 1 < < i ) ) {
imx_dmav1_writel ( 1 < < i , DMA_DSESR ) ;
errcode | = IMX_DMA_ERR_TRANSFER ;
}
if ( imx_dmav1_readl ( DMA_DBOSR ) & ( 1 < < i ) ) {
imx_dmav1_writel ( 1 < < i , DMA_DBOSR ) ;
errcode | = IMX_DMA_ERR_BUFFER ;
}
/* Tasklet error handler */
tasklet_schedule ( & imxdma - > channel [ i ] . dma_tasklet ) ;
printk ( KERN_WARNING
" DMA timeout on channel %d -%s%s%s%s \n " , i ,
errcode & IMX_DMA_ERR_BURST ? " burst " : " " ,
errcode & IMX_DMA_ERR_REQUEST ? " request " : " " ,
errcode & IMX_DMA_ERR_TRANSFER ? " transfer " : " " ,
errcode & IMX_DMA_ERR_BUFFER ? " buffer " : " " ) ;
}
return IRQ_HANDLED ;
2010-10-06 12:25:55 +04:00
}
2012-03-22 17:54:01 +04:00
static void dma_irq_handle_channel ( struct imxdma_channel * imxdmac )
2010-10-06 12:25:55 +04:00
{
2012-03-22 17:54:01 +04:00
struct imxdma_channel_internal * imxdma = & imxdmac - > internal ;
int chno = imxdmac - > channel ;
2012-03-22 17:54:03 +04:00
struct imxdma_desc * desc ;
2012-03-22 17:54:01 +04:00
if ( imxdma - > sg ) {
u32 tmp ;
imxdma - > sg = sg_next ( imxdma - > sg ) ;
if ( imxdma - > sg ) {
2012-03-22 17:54:03 +04:00
spin_lock ( & imxdmac - > lock ) ;
if ( list_empty ( & imxdmac - > ld_active ) ) {
spin_unlock ( & imxdmac - > lock ) ;
goto out ;
}
desc = list_first_entry ( & imxdmac - > ld_active ,
struct imxdma_desc ,
node ) ;
spin_unlock ( & imxdmac - > lock ) ;
imxdma_sg_next ( desc , imxdma - > sg ) ;
2012-03-22 17:54:01 +04:00
tmp = imx_dmav1_readl ( DMA_CCR ( chno ) ) ;
if ( imxdma_hw_chain ( imxdma ) ) {
/* FIXME: The timeout should probably be
* configurable
*/
mod_timer ( & imxdma - > watchdog ,
jiffies + msecs_to_jiffies ( 500 ) ) ;
tmp | = CCR_CEN | CCR_RPT | CCR_ACRPT ;
imx_dmav1_writel ( tmp , DMA_CCR ( chno ) ) ;
} else {
imx_dmav1_writel ( tmp & ~ CCR_CEN , DMA_CCR ( chno ) ) ;
tmp | = CCR_CEN ;
}
imx_dmav1_writel ( tmp , DMA_CCR ( chno ) ) ;
if ( imxdma_chan_is_doing_cyclic ( imxdmac ) )
/* Tasklet progression */
tasklet_schedule ( & imxdmac - > dma_tasklet ) ;
2010-10-06 12:25:55 +04:00
2012-03-22 17:54:01 +04:00
return ;
}
if ( imxdma_hw_chain ( imxdma ) ) {
del_timer ( & imxdma - > watchdog ) ;
return ;
}
}
2012-03-22 17:54:03 +04:00
out :
2012-03-22 17:54:01 +04:00
imx_dmav1_writel ( 0 , DMA_CCR ( chno ) ) ;
imxdma - > in_use = 0 ;
/* Tasklet irq */
2012-03-02 12:28:47 +04:00
tasklet_schedule ( & imxdmac - > dma_tasklet ) ;
}
2012-03-22 17:54:01 +04:00
static irqreturn_t dma_irq_handler ( int irq , void * dev_id )
{
struct imxdma_engine * imxdma = dev_id ;
struct imxdma_channel_internal * internal ;
int i , disr ;
if ( cpu_is_mx21 ( ) | | cpu_is_mx27 ( ) )
imxdma_err_handler ( irq , dev_id ) ;
disr = imx_dmav1_readl ( DMA_DISR ) ;
pr_debug ( " imxdma: dma_irq_handler called, disr=0x%08x \n " ,
disr ) ;
imx_dmav1_writel ( disr , DMA_DISR ) ;
for ( i = 0 ; i < IMX_DMA_CHANNELS ; i + + ) {
if ( disr & ( 1 < < i ) ) {
internal = & imxdma - > channel [ i ] . internal ;
dma_irq_handle_channel ( & imxdma - > channel [ i ] ) ;
}
}
return IRQ_HANDLED ;
}
2012-03-02 12:28:47 +04:00
static int imxdma_xfer_desc ( struct imxdma_desc * d )
{
struct imxdma_channel * imxdmac = to_imxdma_chan ( d - > desc . chan ) ;
2012-03-22 17:54:04 +04:00
struct imxdma_engine * imxdma = imxdmac - > imxdma ;
2012-03-02 12:28:47 +04:00
/* Configure and enable */
switch ( d - > type ) {
case IMXDMA_DESC_MEMCPY :
2012-03-22 17:54:04 +04:00
imxdmac - > internal . sg = NULL ;
imx_dmav1_writel ( d - > src , DMA_SAR ( imxdmac - > channel ) ) ;
imx_dmav1_writel ( d - > dest , DMA_DAR ( imxdmac - > channel ) ) ;
imx_dmav1_writel ( d - > config_mem | ( d - > config_port < < 2 ) ,
DMA_CCR ( imxdmac - > channel ) ) ;
2012-03-22 17:54:01 +04:00
2012-03-22 17:54:04 +04:00
imx_dmav1_writel ( d - > len , DMA_CNTR ( imxdmac - > channel ) ) ;
dev_dbg ( imxdma - > dev , " %s channel: %d dest=0x%08x src=0x%08x "
" dma_length=%d \n " , __func__ , imxdmac - > channel ,
d - > dest , d - > src , d - > len ) ;
break ;
2012-03-22 17:54:01 +04:00
/* Cyclic transfer is the same as slave_sg with special sg configuration. */
2012-03-02 12:28:47 +04:00
case IMXDMA_DESC_CYCLIC :
case IMXDMA_DESC_SLAVE_SG :
2012-03-22 17:54:06 +04:00
imxdmac - > internal . sg = d - > sg ;
imxdmac - > internal . resbytes = d - > len ;
if ( d - > direction = = DMA_DEV_TO_MEM ) {
imx_dmav1_writel ( imxdmac - > per_address ,
DMA_SAR ( imxdmac - > channel ) ) ;
imx_dmav1_writel ( imxdmac - > ccr_from_device ,
DMA_CCR ( imxdmac - > channel ) ) ;
dev_dbg ( imxdma - > dev , " %s channel: %d sg=%p sgcount=%d "
" total length=%d dev_addr=0x%08x (dev2mem) \n " ,
__func__ , imxdmac - > channel , d - > sg , d - > sgcount ,
d - > len , imxdmac - > per_address ) ;
} else if ( d - > direction = = DMA_MEM_TO_DEV ) {
imx_dmav1_writel ( imxdmac - > per_address ,
DMA_DAR ( imxdmac - > channel ) ) ;
imx_dmav1_writel ( imxdmac - > ccr_to_device ,
DMA_CCR ( imxdmac - > channel ) ) ;
dev_dbg ( imxdma - > dev , " %s channel: %d sg=%p sgcount=%d "
" total length=%d dev_addr=0x%08x (mem2dev) \n " ,
__func__ , imxdmac - > channel , d - > sg , d - > sgcount ,
d - > len , imxdmac - > per_address ) ;
} else {
dev_err ( imxdma - > dev , " %s channel: %d bad dma mode \n " ,
__func__ , imxdmac - > channel ) ;
return - EINVAL ;
}
imxdma_sg_next ( d , d - > sg ) ;
2012-03-02 12:28:47 +04:00
break ;
default :
return - EINVAL ;
}
2012-03-22 17:54:03 +04:00
imxdma_enable_hw ( d ) ;
2012-03-02 12:28:47 +04:00
return 0 ;
}
static void imxdma_tasklet ( unsigned long data )
{
struct imxdma_channel * imxdmac = ( void * ) data ;
struct imxdma_engine * imxdma = imxdmac - > imxdma ;
struct imxdma_desc * desc ;
spin_lock ( & imxdmac - > lock ) ;
if ( list_empty ( & imxdmac - > ld_active ) ) {
/* Someone might have called terminate all */
goto out ;
}
desc = list_first_entry ( & imxdmac - > ld_active , struct imxdma_desc , node ) ;
if ( desc - > desc . callback )
desc - > desc . callback ( desc - > desc . callback_param ) ;
2012-03-13 11:09:49 +04:00
dma_cookie_complete ( & desc - > desc ) ;
2012-03-02 12:28:47 +04:00
/* If we are dealing with a cyclic descriptor keep it on ld_active */
if ( imxdma_chan_is_doing_cyclic ( imxdmac ) )
goto out ;
list_move_tail ( imxdmac - > ld_active . next , & imxdmac - > ld_free ) ;
if ( ! list_empty ( & imxdmac - > ld_queue ) ) {
desc = list_first_entry ( & imxdmac - > ld_queue , struct imxdma_desc ,
node ) ;
list_move_tail ( imxdmac - > ld_queue . next , & imxdmac - > ld_active ) ;
if ( imxdma_xfer_desc ( desc ) < 0 )
dev_warn ( imxdma - > dev , " %s: channel: %d couldn't xfer desc \n " ,
__func__ , imxdmac - > channel ) ;
}
out :
spin_unlock ( & imxdmac - > lock ) ;
2010-10-06 12:25:55 +04:00
}
static int imxdma_control ( struct dma_chan * chan , enum dma_ctrl_cmd cmd ,
unsigned long arg )
{
struct imxdma_channel * imxdmac = to_imxdma_chan ( chan ) ;
struct dma_slave_config * dmaengine_cfg = ( void * ) arg ;
2012-03-02 12:28:47 +04:00
unsigned long flags ;
2010-10-06 12:25:55 +04:00
unsigned int mode = 0 ;
switch ( cmd ) {
case DMA_TERMINATE_ALL :
2012-03-22 17:54:01 +04:00
imxdma_disable_hw ( imxdmac ) ;
2012-03-02 12:28:47 +04:00
spin_lock_irqsave ( & imxdmac - > lock , flags ) ;
list_splice_tail_init ( & imxdmac - > ld_active , & imxdmac - > ld_free ) ;
list_splice_tail_init ( & imxdmac - > ld_queue , & imxdmac - > ld_free ) ;
spin_unlock_irqrestore ( & imxdmac - > lock , flags ) ;
2010-10-06 12:25:55 +04:00
return 0 ;
case DMA_SLAVE_CONFIG :
2011-10-13 21:04:23 +04:00
if ( dmaengine_cfg - > direction = = DMA_DEV_TO_MEM ) {
2010-10-06 12:25:55 +04:00
imxdmac - > per_address = dmaengine_cfg - > src_addr ;
imxdmac - > watermark_level = dmaengine_cfg - > src_maxburst ;
imxdmac - > word_size = dmaengine_cfg - > src_addr_width ;
} else {
imxdmac - > per_address = dmaengine_cfg - > dst_addr ;
imxdmac - > watermark_level = dmaengine_cfg - > dst_maxburst ;
imxdmac - > word_size = dmaengine_cfg - > dst_addr_width ;
}
switch ( imxdmac - > word_size ) {
case DMA_SLAVE_BUSWIDTH_1_BYTE :
mode = IMX_DMA_MEMSIZE_8 ;
break ;
case DMA_SLAVE_BUSWIDTH_2_BYTES :
mode = IMX_DMA_MEMSIZE_16 ;
break ;
default :
case DMA_SLAVE_BUSWIDTH_4_BYTES :
mode = IMX_DMA_MEMSIZE_32 ;
break ;
}
2012-03-22 17:54:05 +04:00
imxdmac - > internal . hw_chaining = 1 ;
if ( ! imxdma_hw_chain ( & imxdmac - > internal ) )
return - EINVAL ;
2012-03-22 17:54:06 +04:00
imxdmac - > ccr_from_device = ( mode | IMX_DMA_TYPE_FIFO ) |
2012-03-22 17:54:05 +04:00
( ( IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR ) < < 2 ) |
CCR_REN ;
2012-03-22 17:54:06 +04:00
imxdmac - > ccr_to_device =
2012-03-22 17:54:05 +04:00
( IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR ) |
( ( mode | IMX_DMA_TYPE_FIFO ) < < 2 ) | CCR_REN ;
imx_dmav1_writel ( imxdmac - > dma_request ,
DMA_RSSR ( imxdmac - > channel ) ) ;
2012-03-22 17:54:01 +04:00
/* Set burst length */
imx_dmav1_writel ( imxdmac - > watermark_level * imxdmac - > word_size ,
DMA_BLR ( imxdmac - > channel ) ) ;
2010-10-06 12:25:55 +04:00
return 0 ;
default :
return - ENOSYS ;
}
return - EINVAL ;
}
static enum dma_status imxdma_tx_status ( struct dma_chan * chan ,
dma_cookie_t cookie ,
struct dma_tx_state * txstate )
{
2012-03-07 02:35:27 +04:00
return dma_cookie_status ( chan , cookie , txstate ) ;
2010-10-06 12:25:55 +04:00
}
static dma_cookie_t imxdma_tx_submit ( struct dma_async_tx_descriptor * tx )
{
struct imxdma_channel * imxdmac = to_imxdma_chan ( tx - > chan ) ;
dma_cookie_t cookie ;
2012-03-02 12:28:47 +04:00
unsigned long flags ;
2010-10-06 12:25:55 +04:00
2012-03-02 12:28:47 +04:00
spin_lock_irqsave ( & imxdmac - > lock , flags ) ;
2012-03-07 02:34:46 +04:00
cookie = dma_cookie_assign ( tx ) ;
2012-03-02 12:28:47 +04:00
spin_unlock_irqrestore ( & imxdmac - > lock , flags ) ;
2010-10-06 12:25:55 +04:00
return cookie ;
}
static int imxdma_alloc_chan_resources ( struct dma_chan * chan )
{
struct imxdma_channel * imxdmac = to_imxdma_chan ( chan ) ;
struct imx_dma_data * data = chan - > private ;
2012-02-28 20:08:17 +04:00
if ( data ! = NULL )
imxdmac - > dma_request = data - > dma_request ;
2010-10-06 12:25:55 +04:00
2012-03-02 12:28:47 +04:00
while ( imxdmac - > descs_allocated < IMXDMA_MAX_CHAN_DESCRIPTORS ) {
struct imxdma_desc * desc ;
2010-10-06 12:25:55 +04:00
2012-03-02 12:28:47 +04:00
desc = kzalloc ( sizeof ( * desc ) , GFP_KERNEL ) ;
if ( ! desc )
break ;
__memzero ( & desc - > desc , sizeof ( struct dma_async_tx_descriptor ) ) ;
dma_async_tx_descriptor_init ( & desc - > desc , chan ) ;
desc - > desc . tx_submit = imxdma_tx_submit ;
/* txd.flags will be overwritten in prep funcs */
desc - > desc . flags = DMA_CTRL_ACK ;
desc - > status = DMA_SUCCESS ;
list_add_tail ( & desc - > node , & imxdmac - > ld_free ) ;
imxdmac - > descs_allocated + + ;
}
2010-10-06 12:25:55 +04:00
2012-03-02 12:28:47 +04:00
if ( ! imxdmac - > descs_allocated )
return - ENOMEM ;
return imxdmac - > descs_allocated ;
2010-10-06 12:25:55 +04:00
}
static void imxdma_free_chan_resources ( struct dma_chan * chan )
{
struct imxdma_channel * imxdmac = to_imxdma_chan ( chan ) ;
2012-03-02 12:28:47 +04:00
struct imxdma_desc * desc , * _desc ;
unsigned long flags ;
spin_lock_irqsave ( & imxdmac - > lock , flags ) ;
2010-10-06 12:25:55 +04:00
2012-03-22 17:54:01 +04:00
imxdma_disable_hw ( imxdmac ) ;
2012-03-02 12:28:47 +04:00
list_splice_tail_init ( & imxdmac - > ld_active , & imxdmac - > ld_free ) ;
list_splice_tail_init ( & imxdmac - > ld_queue , & imxdmac - > ld_free ) ;
spin_unlock_irqrestore ( & imxdmac - > lock , flags ) ;
list_for_each_entry_safe ( desc , _desc , & imxdmac - > ld_free , node ) {
kfree ( desc ) ;
imxdmac - > descs_allocated - - ;
}
INIT_LIST_HEAD ( & imxdmac - > ld_free ) ;
2010-10-06 12:25:55 +04:00
if ( imxdmac - > sg_list ) {
kfree ( imxdmac - > sg_list ) ;
imxdmac - > sg_list = NULL ;
}
}
static struct dma_async_tx_descriptor * imxdma_prep_slave_sg (
struct dma_chan * chan , struct scatterlist * sgl ,
2011-10-13 21:04:23 +04:00
unsigned int sg_len , enum dma_transfer_direction direction ,
2012-03-09 00:35:13 +04:00
unsigned long flags , void * context )
2010-10-06 12:25:55 +04:00
{
struct imxdma_channel * imxdmac = to_imxdma_chan ( chan ) ;
struct scatterlist * sg ;
2012-03-02 12:28:47 +04:00
int i , dma_length = 0 ;
struct imxdma_desc * desc ;
2010-10-06 12:25:55 +04:00
2012-03-02 12:28:47 +04:00
if ( list_empty ( & imxdmac - > ld_free ) | |
imxdma_chan_is_doing_cyclic ( imxdmac ) )
2010-10-06 12:25:55 +04:00
return NULL ;
2012-03-02 12:28:47 +04:00
desc = list_first_entry ( & imxdmac - > ld_free , struct imxdma_desc , node ) ;
2010-10-06 12:25:55 +04:00
for_each_sg ( sgl , sg , sg_len , i ) {
dma_length + = sg - > length ;
}
2011-01-12 16:13:23 +03:00
switch ( imxdmac - > word_size ) {
case DMA_SLAVE_BUSWIDTH_4_BYTES :
if ( sgl - > length & 3 | | sgl - > dma_address & 3 )
return NULL ;
break ;
case DMA_SLAVE_BUSWIDTH_2_BYTES :
if ( sgl - > length & 1 | | sgl - > dma_address & 1 )
return NULL ;
break ;
case DMA_SLAVE_BUSWIDTH_1_BYTE :
break ;
default :
return NULL ;
}
2012-03-02 12:28:47 +04:00
desc - > type = IMXDMA_DESC_SLAVE_SG ;
desc - > sg = sgl ;
desc - > sgcount = sg_len ;
desc - > len = dma_length ;
2012-03-22 17:54:03 +04:00
desc - > direction = direction ;
2012-03-02 12:28:47 +04:00
if ( direction = = DMA_DEV_TO_MEM ) {
desc - > src = imxdmac - > per_address ;
} else {
desc - > dest = imxdmac - > per_address ;
}
desc - > desc . callback = NULL ;
desc - > desc . callback_param = NULL ;
2010-10-06 12:25:55 +04:00
2012-03-02 12:28:47 +04:00
return & desc - > desc ;
2010-10-06 12:25:55 +04:00
}
static struct dma_async_tx_descriptor * imxdma_prep_dma_cyclic (
struct dma_chan * chan , dma_addr_t dma_addr , size_t buf_len ,
2012-03-09 00:35:13 +04:00
size_t period_len , enum dma_transfer_direction direction ,
void * context )
2010-10-06 12:25:55 +04:00
{
struct imxdma_channel * imxdmac = to_imxdma_chan ( chan ) ;
struct imxdma_engine * imxdma = imxdmac - > imxdma ;
2012-03-02 12:28:47 +04:00
struct imxdma_desc * desc ;
int i ;
2010-10-06 12:25:55 +04:00
unsigned int periods = buf_len / period_len ;
dev_dbg ( imxdma - > dev , " %s channel: %d buf_len=%d period_len=%d \n " ,
__func__ , imxdmac - > channel , buf_len , period_len ) ;
2012-03-02 12:28:47 +04:00
if ( list_empty ( & imxdmac - > ld_free ) | |
imxdma_chan_is_doing_cyclic ( imxdmac ) )
2010-10-06 12:25:55 +04:00
return NULL ;
2012-03-02 12:28:47 +04:00
desc = list_first_entry ( & imxdmac - > ld_free , struct imxdma_desc , node ) ;
2010-10-06 12:25:55 +04:00
if ( imxdmac - > sg_list )
kfree ( imxdmac - > sg_list ) ;
imxdmac - > sg_list = kcalloc ( periods + 1 ,
sizeof ( struct scatterlist ) , GFP_KERNEL ) ;
if ( ! imxdmac - > sg_list )
return NULL ;
sg_init_table ( imxdmac - > sg_list , periods ) ;
for ( i = 0 ; i < periods ; i + + ) {
imxdmac - > sg_list [ i ] . page_link = 0 ;
imxdmac - > sg_list [ i ] . offset = 0 ;
imxdmac - > sg_list [ i ] . dma_address = dma_addr ;
imxdmac - > sg_list [ i ] . length = period_len ;
dma_addr + = period_len ;
}
/* close the loop */
imxdmac - > sg_list [ periods ] . offset = 0 ;
imxdmac - > sg_list [ periods ] . length = 0 ;
imxdmac - > sg_list [ periods ] . page_link =
( ( unsigned long ) imxdmac - > sg_list | 0x01 ) & ~ 0x02 ;
2012-03-02 12:28:47 +04:00
desc - > type = IMXDMA_DESC_CYCLIC ;
desc - > sg = imxdmac - > sg_list ;
desc - > sgcount = periods ;
desc - > len = IMX_DMA_LENGTH_LOOP ;
2012-03-22 17:54:03 +04:00
desc - > direction = direction ;
2012-03-02 12:28:47 +04:00
if ( direction = = DMA_DEV_TO_MEM ) {
desc - > src = imxdmac - > per_address ;
} else {
desc - > dest = imxdmac - > per_address ;
}
desc - > desc . callback = NULL ;
desc - > desc . callback_param = NULL ;
2010-10-06 12:25:55 +04:00
2012-03-02 12:28:47 +04:00
return & desc - > desc ;
2010-10-06 12:25:55 +04:00
}
2012-02-28 20:08:17 +04:00
static struct dma_async_tx_descriptor * imxdma_prep_dma_memcpy (
struct dma_chan * chan , dma_addr_t dest ,
dma_addr_t src , size_t len , unsigned long flags )
{
struct imxdma_channel * imxdmac = to_imxdma_chan ( chan ) ;
struct imxdma_engine * imxdma = imxdmac - > imxdma ;
2012-03-02 12:28:47 +04:00
struct imxdma_desc * desc ;
2012-02-28 20:08:17 +04:00
dev_dbg ( imxdma - > dev , " %s channel: %d src=0x%x dst=0x%x len=%d \n " ,
__func__ , imxdmac - > channel , src , dest , len ) ;
2012-03-02 12:28:47 +04:00
if ( list_empty ( & imxdmac - > ld_free ) | |
imxdma_chan_is_doing_cyclic ( imxdmac ) )
2012-02-28 20:08:17 +04:00
return NULL ;
2012-03-02 12:28:47 +04:00
desc = list_first_entry ( & imxdmac - > ld_free , struct imxdma_desc , node ) ;
2012-02-28 20:08:17 +04:00
2012-03-02 12:28:47 +04:00
desc - > type = IMXDMA_DESC_MEMCPY ;
desc - > src = src ;
desc - > dest = dest ;
desc - > len = len ;
2012-03-22 17:54:03 +04:00
desc - > direction = DMA_MEM_TO_MEM ;
2012-03-02 12:28:47 +04:00
desc - > config_port = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR ;
desc - > config_mem = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR ;
desc - > desc . callback = NULL ;
desc - > desc . callback_param = NULL ;
2012-02-28 20:08:17 +04:00
2012-03-02 12:28:47 +04:00
return & desc - > desc ;
2012-02-28 20:08:17 +04:00
}
2010-10-06 12:25:55 +04:00
static void imxdma_issue_pending ( struct dma_chan * chan )
{
2012-01-09 13:32:49 +04:00
struct imxdma_channel * imxdmac = to_imxdma_chan ( chan ) ;
2012-03-02 12:28:47 +04:00
struct imxdma_engine * imxdma = imxdmac - > imxdma ;
struct imxdma_desc * desc ;
unsigned long flags ;
spin_lock_irqsave ( & imxdmac - > lock , flags ) ;
if ( list_empty ( & imxdmac - > ld_active ) & &
! list_empty ( & imxdmac - > ld_queue ) ) {
desc = list_first_entry ( & imxdmac - > ld_queue ,
struct imxdma_desc , node ) ;
if ( imxdma_xfer_desc ( desc ) < 0 ) {
dev_warn ( imxdma - > dev ,
" %s: channel: %d couldn't issue DMA xfer \n " ,
__func__ , imxdmac - > channel ) ;
} else {
list_move_tail ( imxdmac - > ld_queue . next ,
& imxdmac - > ld_active ) ;
}
}
spin_unlock_irqrestore ( & imxdmac - > lock , flags ) ;
2010-10-06 12:25:55 +04:00
}
static int __init imxdma_probe ( struct platform_device * pdev )
2012-03-22 17:54:01 +04:00
{
2010-10-06 12:25:55 +04:00
struct imxdma_engine * imxdma ;
int ret , i ;
2012-03-22 17:54:01 +04:00
if ( cpu_is_mx1 ( ) )
imx_dmav1_baseaddr = MX1_IO_ADDRESS ( MX1_DMA_BASE_ADDR ) ;
else if ( cpu_is_mx21 ( ) )
imx_dmav1_baseaddr = MX21_IO_ADDRESS ( MX21_DMA_BASE_ADDR ) ;
else if ( cpu_is_mx27 ( ) )
imx_dmav1_baseaddr = MX27_IO_ADDRESS ( MX27_DMA_BASE_ADDR ) ;
else
return 0 ;
dma_clk = clk_get ( NULL , " dma " ) ;
if ( IS_ERR ( dma_clk ) )
return PTR_ERR ( dma_clk ) ;
clk_enable ( dma_clk ) ;
/* reset DMA module */
imx_dmav1_writel ( DCR_DRST , DMA_DCR ) ;
if ( cpu_is_mx1 ( ) ) {
ret = request_irq ( MX1_DMA_INT , dma_irq_handler , 0 , " DMA " , imxdma ) ;
if ( ret ) {
pr_crit ( " Can't register IRQ for DMA \n " ) ;
return ret ;
}
ret = request_irq ( MX1_DMA_ERR , imxdma_err_handler , 0 , " DMA " , imxdma ) ;
if ( ret ) {
pr_crit ( " Can't register ERRIRQ for DMA \n " ) ;
free_irq ( MX1_DMA_INT , NULL ) ;
return ret ;
}
}
/* enable DMA module */
imx_dmav1_writel ( DCR_DEN , DMA_DCR ) ;
/* clear all interrupts */
imx_dmav1_writel ( ( 1 < < IMX_DMA_CHANNELS ) - 1 , DMA_DISR ) ;
/* disable interrupts */
imx_dmav1_writel ( ( 1 < < IMX_DMA_CHANNELS ) - 1 , DMA_DIMR ) ;
2010-10-06 12:25:55 +04:00
imxdma = kzalloc ( sizeof ( * imxdma ) , GFP_KERNEL ) ;
if ( ! imxdma )
return - ENOMEM ;
INIT_LIST_HEAD ( & imxdma - > dma_device . channels ) ;
2011-01-31 13:35:59 +03:00
dma_cap_set ( DMA_SLAVE , imxdma - > dma_device . cap_mask ) ;
dma_cap_set ( DMA_CYCLIC , imxdma - > dma_device . cap_mask ) ;
2012-02-28 20:08:17 +04:00
dma_cap_set ( DMA_MEMCPY , imxdma - > dma_device . cap_mask ) ;
2011-01-31 13:35:59 +03:00
2010-10-06 12:25:55 +04:00
/* Initialize channel parameters */
2012-03-22 17:54:01 +04:00
for ( i = 0 ; i < IMX_DMA_CHANNELS ; i + + ) {
2010-10-06 12:25:55 +04:00
struct imxdma_channel * imxdmac = & imxdma - > channel [ i ] ;
2012-03-22 17:54:01 +04:00
memset ( & imxdmac - > internal , 0 , sizeof ( imxdmac - > internal ) ) ;
if ( cpu_is_mx21 ( ) | | cpu_is_mx27 ( ) ) {
ret = request_irq ( MX2x_INT_DMACH0 + i ,
dma_irq_handler , 0 , " DMA " , imxdma ) ;
if ( ret ) {
pr_crit ( " Can't register IRQ %d for DMA channel %d \n " ,
MX2x_INT_DMACH0 + i , i ) ;
goto err_init ;
}
init_timer ( & imxdmac - > internal . watchdog ) ;
imxdmac - > internal . watchdog . function = & imxdma_watchdog ;
imxdmac - > internal . watchdog . data = ( unsigned long ) imxdmac ;
2010-10-20 10:37:19 +04:00
}
2010-10-06 12:25:55 +04:00
imxdmac - > imxdma = imxdma ;
spin_lock_init ( & imxdmac - > lock ) ;
2012-03-02 12:28:47 +04:00
INIT_LIST_HEAD ( & imxdmac - > ld_queue ) ;
INIT_LIST_HEAD ( & imxdmac - > ld_free ) ;
INIT_LIST_HEAD ( & imxdmac - > ld_active ) ;
tasklet_init ( & imxdmac - > dma_tasklet , imxdma_tasklet ,
( unsigned long ) imxdmac ) ;
2010-10-06 12:25:55 +04:00
imxdmac - > chan . device = & imxdma - > dma_device ;
2012-03-07 02:36:27 +04:00
dma_cookie_init ( & imxdmac - > chan ) ;
2010-10-06 12:25:55 +04:00
imxdmac - > channel = i ;
/* Add the channel to the DMAC list */
2012-03-02 12:28:47 +04:00
list_add_tail ( & imxdmac - > chan . device_node ,
& imxdma - > dma_device . channels ) ;
2010-10-06 12:25:55 +04:00
}
imxdma - > dev = & pdev - > dev ;
imxdma - > dma_device . dev = & pdev - > dev ;
imxdma - > dma_device . device_alloc_chan_resources = imxdma_alloc_chan_resources ;
imxdma - > dma_device . device_free_chan_resources = imxdma_free_chan_resources ;
imxdma - > dma_device . device_tx_status = imxdma_tx_status ;
imxdma - > dma_device . device_prep_slave_sg = imxdma_prep_slave_sg ;
imxdma - > dma_device . device_prep_dma_cyclic = imxdma_prep_dma_cyclic ;
2012-02-28 20:08:17 +04:00
imxdma - > dma_device . device_prep_dma_memcpy = imxdma_prep_dma_memcpy ;
2010-10-06 12:25:55 +04:00
imxdma - > dma_device . device_control = imxdma_control ;
imxdma - > dma_device . device_issue_pending = imxdma_issue_pending ;
platform_set_drvdata ( pdev , imxdma ) ;
2012-02-28 20:08:17 +04:00
imxdma - > dma_device . copy_align = 2 ; /* 2^2 = 4 bytes alignment */
2011-01-12 15:14:37 +03:00
imxdma - > dma_device . dev - > dma_parms = & imxdma - > dma_parms ;
dma_set_max_seg_size ( imxdma - > dma_device . dev , 0xffffff ) ;
2010-10-06 12:25:55 +04:00
ret = dma_async_device_register ( & imxdma - > dma_device ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " unable to register \n " ) ;
goto err_init ;
}
return 0 ;
err_init :
2012-03-22 17:54:01 +04:00
if ( cpu_is_mx21 ( ) | | cpu_is_mx27 ( ) ) {
while ( - - i > = 0 )
free_irq ( MX2x_INT_DMACH0 + i , NULL ) ;
} else if cpu_is_mx1 ( ) {
free_irq ( MX1_DMA_INT , NULL ) ;
free_irq ( MX1_DMA_ERR , NULL ) ;
2010-10-06 12:25:55 +04:00
}
kfree ( imxdma ) ;
return ret ;
}
static int __exit imxdma_remove ( struct platform_device * pdev )
{
struct imxdma_engine * imxdma = platform_get_drvdata ( pdev ) ;
int i ;
dma_async_device_unregister ( & imxdma - > dma_device ) ;
2012-03-22 17:54:01 +04:00
if ( cpu_is_mx21 ( ) | | cpu_is_mx27 ( ) ) {
for ( i = 0 ; i < IMX_DMA_CHANNELS ; i + + )
free_irq ( MX2x_INT_DMACH0 + i , NULL ) ;
} else if cpu_is_mx1 ( ) {
free_irq ( MX1_DMA_INT , NULL ) ;
free_irq ( MX1_DMA_ERR , NULL ) ;
2010-10-06 12:25:55 +04:00
}
kfree ( imxdma ) ;
return 0 ;
}
static struct platform_driver imxdma_driver = {
. driver = {
. name = " imx-dma " ,
} ,
. remove = __exit_p ( imxdma_remove ) ,
} ;
static int __init imxdma_module_init ( void )
{
return platform_driver_probe ( & imxdma_driver , imxdma_probe ) ;
}
subsys_initcall ( imxdma_module_init ) ;
MODULE_AUTHOR ( " Sascha Hauer, Pengutronix <s.hauer@pengutronix.de> " ) ;
MODULE_DESCRIPTION ( " i.MX dma driver " ) ;
MODULE_LICENSE ( " GPL " ) ;