2010-12-20 18:27:19 -08:00
/*
* OMAP1 / OMAP7xx - specific DMA driver
*
* Copyright ( C ) 2003 - 2008 Nokia Corporation
* Author : Juha Yrjölä < juha . yrjola @ nokia . com >
* DMA channel linking for 1610 by Samuel Ortiz < samuel . ortiz @ nokia . com >
* Graphics DMA and LCD DMA graphics tranformations
* by Imre Deak < imre . deak @ nokia . com >
* OMAP2 / 3 support Copyright ( C ) 2004 - 2007 Texas Instruments , Inc .
* Some functions based on earlier dma - omap . c Copyright ( C ) 2001 RidgeRun , Inc .
*
* Copyright ( C ) 2010 Texas Instruments Incorporated - http : //www.ti.com/
* Converted DMA library into platform driver
* - G , Manjunath Kondaiah < manjugk @ ti . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/err.h>
# include <linux/slab.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/device.h>
2012-02-24 10:34:34 -08:00
# include <linux/io.h>
2013-01-11 11:24:19 -08:00
# include <linux/dma-mapping.h>
2012-11-30 08:41:50 -08:00
# include <linux/omap-dma.h>
2012-10-02 13:39:28 -07:00
# include <mach/tc.h>
2012-08-30 16:57:21 -07:00
# include <mach/irqs.h>
2010-12-20 18:27:19 -08:00
# define OMAP1_DMA_BASE (0xfffed800)
2010-12-20 18:27:19 -08:00
# define OMAP1_LOGICAL_DMA_CH_COUNT 17
static u32 enable_1510_mode ;
2013-11-08 18:04:06 +00:00
static const struct omap_dma_reg reg_map [ ] = {
[ GCR ] = { 0x0400 , 0x00 , OMAP_DMA_REG_16BIT } ,
[ GSCR ] = { 0x0404 , 0x00 , OMAP_DMA_REG_16BIT } ,
[ GRST1 ] = { 0x0408 , 0x00 , OMAP_DMA_REG_16BIT } ,
[ HW_ID ] = { 0x0442 , 0x00 , OMAP_DMA_REG_16BIT } ,
[ PCH2_ID ] = { 0x0444 , 0x00 , OMAP_DMA_REG_16BIT } ,
[ PCH0_ID ] = { 0x0446 , 0x00 , OMAP_DMA_REG_16BIT } ,
[ PCH1_ID ] = { 0x0448 , 0x00 , OMAP_DMA_REG_16BIT } ,
[ PCHG_ID ] = { 0x044a , 0x00 , OMAP_DMA_REG_16BIT } ,
[ PCHD_ID ] = { 0x044c , 0x00 , OMAP_DMA_REG_16BIT } ,
[ CAPS_0 ] = { 0x044e , 0x00 , OMAP_DMA_REG_2X16BIT } ,
[ CAPS_1 ] = { 0x0452 , 0x00 , OMAP_DMA_REG_2X16BIT } ,
[ CAPS_2 ] = { 0x0456 , 0x00 , OMAP_DMA_REG_16BIT } ,
[ CAPS_3 ] = { 0x0458 , 0x00 , OMAP_DMA_REG_16BIT } ,
[ CAPS_4 ] = { 0x045a , 0x00 , OMAP_DMA_REG_16BIT } ,
[ PCH2_SR ] = { 0x0460 , 0x00 , OMAP_DMA_REG_16BIT } ,
[ PCH0_SR ] = { 0x0480 , 0x00 , OMAP_DMA_REG_16BIT } ,
[ PCH1_SR ] = { 0x0482 , 0x00 , OMAP_DMA_REG_16BIT } ,
[ PCHD_SR ] = { 0x04c0 , 0x00 , OMAP_DMA_REG_16BIT } ,
2010-12-20 18:27:19 -08:00
/* Common Registers */
2013-11-08 18:04:06 +00:00
[ CSDP ] = { 0x0000 , 0x40 , OMAP_DMA_REG_16BIT } ,
[ CCR ] = { 0x0002 , 0x40 , OMAP_DMA_REG_16BIT } ,
[ CICR ] = { 0x0004 , 0x40 , OMAP_DMA_REG_16BIT } ,
[ CSR ] = { 0x0006 , 0x40 , OMAP_DMA_REG_16BIT } ,
[ CEN ] = { 0x0010 , 0x40 , OMAP_DMA_REG_16BIT } ,
[ CFN ] = { 0x0012 , 0x40 , OMAP_DMA_REG_16BIT } ,
[ CSFI ] = { 0x0014 , 0x40 , OMAP_DMA_REG_16BIT } ,
[ CSEI ] = { 0x0016 , 0x40 , OMAP_DMA_REG_16BIT } ,
[ CPC ] = { 0x0018 , 0x40 , OMAP_DMA_REG_16BIT } , /* 15xx only */
[ CSAC ] = { 0x0018 , 0x40 , OMAP_DMA_REG_16BIT } ,
[ CDAC ] = { 0x001a , 0x40 , OMAP_DMA_REG_16BIT } ,
[ CDEI ] = { 0x001c , 0x40 , OMAP_DMA_REG_16BIT } ,
[ CDFI ] = { 0x001e , 0x40 , OMAP_DMA_REG_16BIT } ,
[ CLNK_CTRL ] = { 0x0028 , 0x40 , OMAP_DMA_REG_16BIT } ,
2010-12-20 18:27:19 -08:00
/* Channel specific register offsets */
2013-11-08 18:04:06 +00:00
[ CSSA ] = { 0x0008 , 0x40 , OMAP_DMA_REG_2X16BIT } ,
[ CDSA ] = { 0x000c , 0x40 , OMAP_DMA_REG_2X16BIT } ,
[ COLOR ] = { 0x0020 , 0x40 , OMAP_DMA_REG_2X16BIT } ,
[ CCR2 ] = { 0x0024 , 0x40 , OMAP_DMA_REG_16BIT } ,
[ LCH_CTRL ] = { 0x002a , 0x40 , OMAP_DMA_REG_16BIT } ,
2010-12-20 18:27:19 -08:00
} ;
2010-12-20 18:27:19 -08:00
static struct resource res [ ] __initdata = {
[ 0 ] = {
. start = OMAP1_DMA_BASE ,
. end = OMAP1_DMA_BASE + SZ_2K - 1 ,
. flags = IORESOURCE_MEM ,
} ,
[ 1 ] = {
. name = " 0 " ,
. start = INT_DMA_CH0_6 ,
. flags = IORESOURCE_IRQ ,
} ,
[ 2 ] = {
. name = " 1 " ,
. start = INT_DMA_CH1_7 ,
. flags = IORESOURCE_IRQ ,
} ,
[ 3 ] = {
. name = " 2 " ,
. start = INT_DMA_CH2_8 ,
. flags = IORESOURCE_IRQ ,
} ,
[ 4 ] = {
. name = " 3 " ,
. start = INT_DMA_CH3 ,
. flags = IORESOURCE_IRQ ,
} ,
[ 5 ] = {
. name = " 4 " ,
. start = INT_DMA_CH4 ,
. flags = IORESOURCE_IRQ ,
} ,
[ 6 ] = {
. name = " 5 " ,
. start = INT_DMA_CH5 ,
. flags = IORESOURCE_IRQ ,
} ,
2010-12-20 18:27:19 -08:00
/* Handled in lcd_dma.c */
2010-12-20 18:27:19 -08:00
[ 7 ] = {
. name = " 6 " ,
. start = INT_1610_DMA_CH6 ,
. flags = IORESOURCE_IRQ ,
} ,
/* irq's for omap16xx and omap7xx */
[ 8 ] = {
. name = " 7 " ,
. start = INT_1610_DMA_CH7 ,
. flags = IORESOURCE_IRQ ,
} ,
[ 9 ] = {
. name = " 8 " ,
. start = INT_1610_DMA_CH8 ,
. flags = IORESOURCE_IRQ ,
} ,
[ 10 ] = {
. name = " 9 " ,
. start = INT_1610_DMA_CH9 ,
. flags = IORESOURCE_IRQ ,
} ,
[ 11 ] = {
. name = " 10 " ,
. start = INT_1610_DMA_CH10 ,
. flags = IORESOURCE_IRQ ,
} ,
[ 12 ] = {
. name = " 11 " ,
. start = INT_1610_DMA_CH11 ,
. flags = IORESOURCE_IRQ ,
} ,
[ 13 ] = {
. name = " 12 " ,
. start = INT_1610_DMA_CH12 ,
. flags = IORESOURCE_IRQ ,
} ,
[ 14 ] = {
. name = " 13 " ,
. start = INT_1610_DMA_CH13 ,
. flags = IORESOURCE_IRQ ,
} ,
[ 15 ] = {
. name = " 14 " ,
. start = INT_1610_DMA_CH14 ,
. flags = IORESOURCE_IRQ ,
} ,
[ 16 ] = {
. name = " 15 " ,
. start = INT_1610_DMA_CH15 ,
. flags = IORESOURCE_IRQ ,
} ,
[ 17 ] = {
. name = " 16 " ,
. start = INT_DMA_LCD ,
. flags = IORESOURCE_IRQ ,
} ,
} ;
2010-12-20 18:27:19 -08:00
static void __iomem * dma_base ;
static inline void dma_write ( u32 val , int reg , int lch )
{
2013-11-08 18:04:06 +00:00
void __iomem * addr = dma_base ;
addr + = reg_map [ reg ] . offset ;
addr + = reg_map [ reg ] . stride * lch ;
__raw_writew ( val , addr ) ;
if ( reg_map [ reg ] . type = = OMAP_DMA_REG_2X16BIT )
__raw_writew ( val > > 16 , addr + 2 ) ;
2010-12-20 18:27:19 -08:00
}
static inline u32 dma_read ( int reg , int lch )
{
2013-11-08 18:04:06 +00:00
void __iomem * addr = dma_base ;
uint32_t val ;
addr + = reg_map [ reg ] . offset ;
addr + = reg_map [ reg ] . stride * lch ;
val = __raw_readw ( addr ) ;
if ( reg_map [ reg ] . type = = OMAP_DMA_REG_2X16BIT )
val | = __raw_readw ( addr + 2 ) < < 16 ;
2010-12-20 18:27:19 -08:00
return val ;
}
static void omap1_clear_lch_regs ( int lch )
{
2013-11-08 14:53:35 +00:00
int i ;
2010-12-20 18:27:19 -08:00
2013-11-08 14:53:35 +00:00
for ( i = CPC ; i < = COLOR ; i + = 1 )
2010-12-20 18:27:19 -08:00
dma_write ( 0 , i , lch ) ;
}
static void omap1_clear_dma ( int lch )
{
u32 l ;
l = dma_read ( CCR , lch ) ;
l & = ~ OMAP_DMA_CCR_EN ;
dma_write ( l , CCR , lch ) ;
/* Clear pending interrupts */
l = dma_read ( CSR , lch ) ;
}
static void omap1_show_dma_caps ( void )
{
if ( enable_1510_mode ) {
printk ( KERN_INFO " DMA support for OMAP15xx initialized \n " ) ;
} else {
u16 w ;
printk ( KERN_INFO " OMAP DMA hardware version %d \n " ,
dma_read ( HW_ID , 0 ) ) ;
printk ( KERN_INFO " DMA capabilities: %08x:%08x:%04x:%04x:%04x \n " ,
dma_read ( CAPS_0 , 0 ) , dma_read ( CAPS_1 , 0 ) ,
dma_read ( CAPS_2 , 0 ) , dma_read ( CAPS_3 , 0 ) ,
dma_read ( CAPS_4 , 0 ) ) ;
/* Disable OMAP 3.0/3.1 compatibility mode. */
w = dma_read ( GSCR , 0 ) ;
w | = 1 < < 3 ;
dma_write ( w , GSCR , 0 ) ;
}
return ;
}
2013-11-08 18:06:37 +00:00
static unsigned configure_dma_errata ( void )
2010-12-20 18:27:19 -08:00
{
2013-11-08 18:06:37 +00:00
unsigned errata = 0 ;
2010-12-20 18:27:19 -08:00
/*
* Erratum 3.2 / 3.3 : sometimes 0 is returned if CSAC / CDAC is
* read before the DMA controller finished disabling the channel .
*/
if ( ! cpu_is_omap15xx ( ) )
SET_DMA_ERRATA ( DMA_ERRATA_3_3 ) ;
return errata ;
}
2013-01-11 11:24:19 -08:00
static const struct platform_device_info omap_dma_dev_info = {
. name = " omap-dma-engine " ,
. id = - 1 ,
. dma_mask = DMA_BIT_MASK ( 32 ) ,
} ;
2010-12-20 18:27:19 -08:00
static int __init omap1_system_dma_init ( void )
{
struct omap_system_dma_plat_info * p ;
2010-12-20 18:27:19 -08:00
struct omap_dma_dev_attr * d ;
2013-01-11 11:24:19 -08:00
struct platform_device * pdev , * dma_pdev ;
2010-12-20 18:27:19 -08:00
int ret ;
pdev = platform_device_alloc ( " omap_dma_system " , 0 ) ;
if ( ! pdev ) {
pr_err ( " %s: Unable to device alloc for dma \n " ,
__func__ ) ;
return - ENOMEM ;
}
2010-12-20 18:27:19 -08:00
dma_base = ioremap ( res [ 0 ] . start , resource_size ( & res [ 0 ] ) ) ;
if ( ! dma_base ) {
pr_err ( " %s: Unable to ioremap \n " , __func__ ) ;
2011-05-13 18:46:10 +02:00
ret = - ENODEV ;
goto exit_device_put ;
2010-12-20 18:27:19 -08:00
}
2010-12-20 18:27:19 -08:00
ret = platform_device_add_resources ( pdev , res , ARRAY_SIZE ( res ) ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " %s: Unable to add resources for %s%d \n " ,
__func__ , pdev - > name , pdev - > id ) ;
2013-05-08 16:48:02 -07:00
goto exit_iounmap ;
2010-12-20 18:27:19 -08:00
}
p = kzalloc ( sizeof ( struct omap_system_dma_plat_info ) , GFP_KERNEL ) ;
if ( ! p ) {
dev_err ( & pdev - > dev , " %s: Unable to allocate 'p' for %s \n " ,
__func__ , pdev - > name ) ;
ret = - ENOMEM ;
2013-05-08 16:48:02 -07:00
goto exit_iounmap ;
2010-12-20 18:27:19 -08:00
}
2010-12-20 18:27:19 -08:00
d = kzalloc ( sizeof ( struct omap_dma_dev_attr ) , GFP_KERNEL ) ;
if ( ! d ) {
dev_err ( & pdev - > dev , " %s: Unable to allocate 'd' for %s \n " ,
__func__ , pdev - > name ) ;
ret = - ENOMEM ;
goto exit_release_p ;
}
d - > lch_count = OMAP1_LOGICAL_DMA_CH_COUNT ;
/* Valid attributes for omap1 plus processors */
if ( cpu_is_omap15xx ( ) )
d - > dev_caps = ENABLE_1510_MODE ;
enable_1510_mode = d - > dev_caps & ENABLE_1510_MODE ;
2012-10-30 11:03:22 -07:00
if ( cpu_is_omap16xx ( ) )
d - > dev_caps = ENABLE_16XX_MODE ;
2010-12-20 18:27:19 -08:00
d - > dev_caps | = SRC_PORT ;
d - > dev_caps | = DST_PORT ;
d - > dev_caps | = SRC_INDEX ;
d - > dev_caps | = DST_INDEX ;
d - > dev_caps | = IS_BURST_ONLY4 ;
d - > dev_caps | = CLEAR_CSR_ON_READ ;
d - > dev_caps | = IS_WORD_16 ;
d - > chan = kzalloc ( sizeof ( struct omap_dma_lch ) *
( d - > lch_count ) , GFP_KERNEL ) ;
if ( ! d - > chan ) {
2012-07-26 00:54:26 -06:00
dev_err ( & pdev - > dev ,
" %s: Memory allocation failed for d->chan! \n " ,
__func__ ) ;
2013-05-16 11:25:07 -07:00
ret = - ENOMEM ;
2010-12-20 18:27:19 -08:00
goto exit_release_d ;
}
if ( cpu_is_omap15xx ( ) )
d - > chan_count = 9 ;
else if ( cpu_is_omap16xx ( ) | | cpu_is_omap7xx ( ) ) {
if ( ! ( d - > dev_caps & ENABLE_1510_MODE ) )
d - > chan_count = 16 ;
else
d - > chan_count = 9 ;
}
p - > dma_attr = d ;
p - > show_dma_caps = omap1_show_dma_caps ;
p - > clear_lch_regs = omap1_clear_lch_regs ;
p - > clear_dma = omap1_clear_dma ;
p - > dma_write = dma_write ;
p - > dma_read = dma_read ;
p - > errata = configure_dma_errata ( ) ;
2010-12-20 18:27:19 -08:00
ret = platform_device_add_data ( pdev , p , sizeof ( * p ) ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " %s: Unable to add resources for %s%d \n " ,
__func__ , pdev - > name , pdev - > id ) ;
2010-12-20 18:27:19 -08:00
goto exit_release_chan ;
2010-12-20 18:27:19 -08:00
}
ret = platform_device_add ( pdev ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " %s: Unable to add resources for %s%d \n " ,
__func__ , pdev - > name , pdev - > id ) ;
2010-12-20 18:27:19 -08:00
goto exit_release_chan ;
2010-12-20 18:27:19 -08:00
}
2013-01-11 11:24:19 -08:00
dma_pdev = platform_device_register_full ( & omap_dma_dev_info ) ;
if ( IS_ERR ( dma_pdev ) ) {
ret = PTR_ERR ( dma_pdev ) ;
goto exit_release_pdev ;
}
2010-12-20 18:27:19 -08:00
return ret ;
2013-01-11 11:24:19 -08:00
exit_release_pdev :
platform_device_del ( pdev ) ;
2010-12-20 18:27:19 -08:00
exit_release_chan :
kfree ( d - > chan ) ;
exit_release_d :
kfree ( d ) ;
exit_release_p :
kfree ( p ) ;
2013-05-08 16:48:02 -07:00
exit_iounmap :
iounmap ( dma_base ) ;
2011-05-13 18:46:10 +02:00
exit_device_put :
platform_device_put ( pdev ) ;
2010-12-20 18:27:19 -08:00
return ret ;
}
arch_initcall ( omap1_system_dma_init ) ;