2005-04-17 02:20:36 +04:00
/*
* linux / arch / arm / mach - rpc / dma . c
*
* Copyright ( C ) 1998 Russell King
*
* 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 .
*
* DMA functions specific to RiscPC architecture
*/
# include <linux/mman.h>
# include <linux/init.h>
# include <linux/interrupt.h>
2006-01-04 18:08:30 +03:00
# include <linux/dma-mapping.h>
2008-09-06 15:10:45 +04:00
# include <linux/io.h>
2005-04-17 02:20:36 +04:00
# include <asm/page.h>
# include <asm/dma.h>
# include <asm/fiq.h>
# include <asm/irq.h>
2008-08-05 19:14:15 +04:00
# include <mach/hardware.h>
2005-04-17 02:20:36 +04:00
# include <asm/uaccess.h>
# include <asm/mach/dma.h>
# include <asm/hardware/iomd.h>
2009-02-22 00:36:22 +03:00
struct iomd_dma {
struct dma_struct dma ;
unsigned int state ;
unsigned long base ; /* Controller base address */
int irq ; /* Controller IRQ */
struct scatterlist cur_sg ; /* Current controller buffer */
2009-02-22 00:38:56 +03:00
dma_addr_t dma_addr ;
unsigned int dma_len ;
2009-02-22 00:36:22 +03:00
} ;
2005-04-17 02:20:36 +04:00
#if 0
typedef enum {
dma_size_8 = 1 ,
dma_size_16 = 2 ,
dma_size_32 = 4 ,
dma_size_128 = 16
} dma_size_t ;
# endif
# define TRANSFER_SIZE 2
# define CURA (0)
# define ENDA (IOMD_IO0ENDA - IOMD_IO0CURA)
# define CURB (IOMD_IO0CURB - IOMD_IO0CURA)
# define ENDB (IOMD_IO0ENDB - IOMD_IO0CURA)
# define CR (IOMD_IO0CR - IOMD_IO0CURA)
# define ST (IOMD_IO0ST - IOMD_IO0CURA)
2008-12-08 20:35:48 +03:00
static void iomd_get_next_sg ( struct scatterlist * sg , struct iomd_dma * idma )
2005-04-17 02:20:36 +04:00
{
unsigned long end , offset , flags = 0 ;
2008-12-08 20:35:48 +03:00
if ( idma - > dma . sg ) {
2009-02-22 00:38:56 +03:00
sg - > dma_address = idma - > dma_addr ;
2005-04-17 02:20:36 +04:00
offset = sg - > dma_address & ~ PAGE_MASK ;
2009-02-22 00:38:56 +03:00
end = offset + idma - > dma_len ;
2005-04-17 02:20:36 +04:00
if ( end > PAGE_SIZE )
end = PAGE_SIZE ;
if ( offset + TRANSFER_SIZE > = end )
flags | = DMA_END_L ;
sg - > length = end - TRANSFER_SIZE ;
2009-02-22 00:38:56 +03:00
idma - > dma_len - = end - offset ;
idma - > dma_addr + = end - offset ;
2005-04-17 02:20:36 +04:00
2009-02-22 00:38:56 +03:00
if ( idma - > dma_len = = 0 ) {
2008-12-08 20:35:48 +03:00
if ( idma - > dma . sgcount > 1 ) {
2008-12-08 22:03:58 +03:00
idma - > dma . sg = sg_next ( idma - > dma . sg ) ;
2009-02-22 00:38:56 +03:00
idma - > dma_addr = idma - > dma . sg - > dma_address ;
idma - > dma_len = idma - > dma . sg - > length ;
2008-12-08 20:35:48 +03:00
idma - > dma . sgcount - - ;
2005-04-17 02:20:36 +04:00
} else {
2008-12-08 20:35:48 +03:00
idma - > dma . sg = NULL ;
2005-04-17 02:20:36 +04:00
flags | = DMA_END_S ;
}
}
} else {
flags = DMA_END_S | DMA_END_L ;
sg - > dma_address = 0 ;
sg - > length = 0 ;
}
sg - > length | = flags ;
}
2006-10-06 21:53:39 +04:00
static irqreturn_t iomd_dma_handle ( int irq , void * dev_id )
2005-04-17 02:20:36 +04:00
{
2008-12-08 20:35:48 +03:00
struct iomd_dma * idma = dev_id ;
unsigned long base = idma - > base ;
2005-04-17 02:20:36 +04:00
do {
unsigned int status ;
status = iomd_readb ( base + ST ) ;
if ( ! ( status & DMA_ST_INT ) )
return IRQ_HANDLED ;
2008-12-08 20:35:48 +03:00
if ( ( idma - > state ^ status ) & DMA_ST_AB )
iomd_get_next_sg ( & idma - > cur_sg , idma ) ;
2005-04-17 02:20:36 +04:00
switch ( status & ( DMA_ST_OFL | DMA_ST_AB ) ) {
case DMA_ST_OFL : /* OIA */
case DMA_ST_AB : /* .IB */
2008-12-08 20:35:48 +03:00
iomd_writel ( idma - > cur_sg . dma_address , base + CURA ) ;
iomd_writel ( idma - > cur_sg . length , base + ENDA ) ;
idma - > state = DMA_ST_AB ;
2005-04-17 02:20:36 +04:00
break ;
case DMA_ST_OFL | DMA_ST_AB : /* OIB */
case 0 : /* .IA */
2008-12-08 20:35:48 +03:00
iomd_writel ( idma - > cur_sg . dma_address , base + CURB ) ;
iomd_writel ( idma - > cur_sg . length , base + ENDB ) ;
idma - > state = 0 ;
2005-04-17 02:20:36 +04:00
break ;
}
if ( status & DMA_ST_OFL & &
2008-12-08 20:35:48 +03:00
idma - > cur_sg . length = = ( DMA_END_S | DMA_END_L ) )
2005-04-17 02:20:36 +04:00
break ;
} while ( 1 ) ;
2008-12-08 20:35:48 +03:00
idma - > state = ~ DMA_ST_AB ;
2005-04-17 02:20:36 +04:00
disable_irq ( irq ) ;
return IRQ_HANDLED ;
}
2008-12-08 18:58:50 +03:00
static int iomd_request_dma ( unsigned int chan , dma_t * dma )
2005-04-17 02:20:36 +04:00
{
2008-12-08 20:35:48 +03:00
struct iomd_dma * idma = container_of ( dma , struct iomd_dma , dma ) ;
return request_irq ( idma - > irq , iomd_dma_handle ,
IRQF_DISABLED , idma - > dma . device_id , idma ) ;
2005-04-17 02:20:36 +04:00
}
2008-12-08 18:58:50 +03:00
static void iomd_free_dma ( unsigned int chan , dma_t * dma )
2005-04-17 02:20:36 +04:00
{
2008-12-08 20:35:48 +03:00
struct iomd_dma * idma = container_of ( dma , struct iomd_dma , dma ) ;
free_irq ( idma - > irq , idma ) ;
2005-04-17 02:20:36 +04:00
}
2008-12-08 18:58:50 +03:00
static void iomd_enable_dma ( unsigned int chan , dma_t * dma )
2005-04-17 02:20:36 +04:00
{
2008-12-08 20:35:48 +03:00
struct iomd_dma * idma = container_of ( dma , struct iomd_dma , dma ) ;
unsigned long dma_base = idma - > base ;
2005-04-17 02:20:36 +04:00
unsigned int ctrl = TRANSFER_SIZE | DMA_CR_E ;
2008-12-08 20:35:48 +03:00
if ( idma - > dma . invalid ) {
idma - > dma . invalid = 0 ;
2005-04-17 02:20:36 +04:00
/*
* Cope with ISA - style drivers which expect cache
* coherence .
*/
2008-12-08 20:35:48 +03:00
if ( ! idma - > dma . sg ) {
idma - > dma . sg = & idma - > dma . buf ;
idma - > dma . sgcount = 1 ;
idma - > dma . buf . length = idma - > dma . count ;
idma - > dma . buf . dma_address = dma_map_single ( NULL ,
idma - > dma . addr , idma - > dma . count ,
idma - > dma . dma_mode = = DMA_MODE_READ ?
2006-01-04 18:08:30 +03:00
DMA_FROM_DEVICE : DMA_TO_DEVICE ) ;
2005-04-17 02:20:36 +04:00
}
iomd_writeb ( DMA_CR_C , dma_base + CR ) ;
2008-12-08 20:35:48 +03:00
idma - > state = DMA_ST_AB ;
2005-04-17 02:20:36 +04:00
}
2008-12-08 20:35:48 +03:00
if ( idma - > dma . dma_mode = = DMA_MODE_READ )
2005-04-17 02:20:36 +04:00
ctrl | = DMA_CR_D ;
iomd_writeb ( ctrl , dma_base + CR ) ;
2008-12-08 20:35:48 +03:00
enable_irq ( idma - > irq ) ;
2005-04-17 02:20:36 +04:00
}
2008-12-08 18:58:50 +03:00
static void iomd_disable_dma ( unsigned int chan , dma_t * dma )
2005-04-17 02:20:36 +04:00
{
2008-12-08 20:35:48 +03:00
struct iomd_dma * idma = container_of ( dma , struct iomd_dma , dma ) ;
unsigned long dma_base = idma - > base ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
local_irq_save ( flags ) ;
2008-12-08 20:35:48 +03:00
if ( idma - > state ! = ~ DMA_ST_AB )
disable_irq ( idma - > irq ) ;
2005-04-17 02:20:36 +04:00
iomd_writeb ( 0 , dma_base + CR ) ;
local_irq_restore ( flags ) ;
}
2008-12-08 18:58:50 +03:00
static int iomd_set_dma_speed ( unsigned int chan , dma_t * dma , int cycle )
2005-04-17 02:20:36 +04:00
{
int tcr , speed ;
if ( cycle < 188 )
speed = 3 ;
else if ( cycle < = 250 )
speed = 2 ;
else if ( cycle < 438 )
speed = 1 ;
else
speed = 0 ;
tcr = iomd_readb ( IOMD_DMATCR ) ;
speed & = 3 ;
2008-12-08 18:58:50 +03:00
switch ( chan ) {
2005-04-17 02:20:36 +04:00
case DMA_0 :
tcr = ( tcr & ~ 0x03 ) | speed ;
break ;
case DMA_1 :
tcr = ( tcr & ~ 0x0c ) | ( speed < < 2 ) ;
break ;
case DMA_2 :
tcr = ( tcr & ~ 0x30 ) | ( speed < < 4 ) ;
break ;
case DMA_3 :
tcr = ( tcr & ~ 0xc0 ) | ( speed < < 6 ) ;
break ;
default :
break ;
}
iomd_writeb ( tcr , IOMD_DMATCR ) ;
return speed ;
}
static struct dma_ops iomd_dma_ops = {
. type = " IOMD " ,
. request = iomd_request_dma ,
. free = iomd_free_dma ,
. enable = iomd_enable_dma ,
. disable = iomd_disable_dma ,
. setspeed = iomd_set_dma_speed ,
} ;
static struct fiq_handler fh = {
. name = " floppydma "
} ;
2009-02-22 00:36:22 +03:00
struct floppy_dma {
struct dma_struct dma ;
unsigned int fiq ;
} ;
2008-12-08 18:58:50 +03:00
static void floppy_enable_dma ( unsigned int chan , dma_t * dma )
2005-04-17 02:20:36 +04:00
{
2008-12-08 20:35:48 +03:00
struct floppy_dma * fdma = container_of ( dma , struct floppy_dma , dma ) ;
2005-04-17 02:20:36 +04:00
void * fiqhandler_start ;
unsigned int fiqhandler_length ;
struct pt_regs regs ;
2008-12-08 20:35:48 +03:00
if ( fdma - > dma . sg )
2005-04-17 02:20:36 +04:00
BUG ( ) ;
2008-12-08 20:35:48 +03:00
if ( fdma - > dma . dma_mode = = DMA_MODE_READ ) {
2005-04-17 02:20:36 +04:00
extern unsigned char floppy_fiqin_start , floppy_fiqin_end ;
fiqhandler_start = & floppy_fiqin_start ;
fiqhandler_length = & floppy_fiqin_end - & floppy_fiqin_start ;
} else {
extern unsigned char floppy_fiqout_start , floppy_fiqout_end ;
fiqhandler_start = & floppy_fiqout_start ;
fiqhandler_length = & floppy_fiqout_end - & floppy_fiqout_start ;
}
2008-12-08 20:35:48 +03:00
regs . ARM_r9 = fdma - > dma . count ;
regs . ARM_r10 = ( unsigned long ) fdma - > dma . addr ;
2005-04-17 02:20:36 +04:00
regs . ARM_fp = ( unsigned long ) FLOPPYDMA_BASE ;
if ( claim_fiq ( & fh ) ) {
printk ( " floppydma: couldn't claim FIQ. \n " ) ;
return ;
}
set_fiq_handler ( fiqhandler_start , fiqhandler_length ) ;
set_fiq_regs ( & regs ) ;
2008-12-08 20:35:48 +03:00
enable_fiq ( fdma - > fiq ) ;
2005-04-17 02:20:36 +04:00
}
2008-12-08 18:58:50 +03:00
static void floppy_disable_dma ( unsigned int chan , dma_t * dma )
2005-04-17 02:20:36 +04:00
{
2008-12-08 20:35:48 +03:00
struct floppy_dma * fdma = container_of ( dma , struct floppy_dma , dma ) ;
disable_fiq ( fdma - > fiq ) ;
2005-04-17 02:20:36 +04:00
release_fiq ( & fh ) ;
}
2008-12-08 18:58:50 +03:00
static int floppy_get_residue ( unsigned int chan , dma_t * dma )
2005-04-17 02:20:36 +04:00
{
struct pt_regs regs ;
get_fiq_regs ( & regs ) ;
return regs . ARM_r9 ;
}
static struct dma_ops floppy_dma_ops = {
. type = " FIQDMA " ,
. enable = floppy_enable_dma ,
. disable = floppy_disable_dma ,
. residue = floppy_get_residue ,
} ;
/*
* This is virtual DMA - we don ' t need anything here .
*/
2008-12-08 18:58:50 +03:00
static void sound_enable_disable_dma ( unsigned int chan , dma_t * dma )
2005-04-17 02:20:36 +04:00
{
}
static struct dma_ops sound_dma_ops = {
. type = " VIRTUAL " ,
. enable = sound_enable_disable_dma ,
. disable = sound_enable_disable_dma ,
} ;
2008-12-08 20:35:48 +03:00
static struct iomd_dma iomd_dma [ 6 ] ;
2008-12-08 19:33:30 +03:00
2008-12-08 20:35:48 +03:00
static struct floppy_dma floppy_dma = {
. dma = {
. d_ops = & floppy_dma_ops ,
} ,
. fiq = FIQ_FLOPPYDATA ,
2008-12-08 19:33:30 +03:00
} ;
static dma_t sound_dma = {
. d_ops = & sound_dma_ops ,
} ;
static int __init rpc_dma_init ( void )
2005-04-17 02:20:36 +04:00
{
2008-12-08 19:33:30 +03:00
unsigned int i ;
int ret ;
2005-04-17 02:20:36 +04:00
iomd_writeb ( 0 , IOMD_IO0CR ) ;
iomd_writeb ( 0 , IOMD_IO1CR ) ;
iomd_writeb ( 0 , IOMD_IO2CR ) ;
iomd_writeb ( 0 , IOMD_IO3CR ) ;
iomd_writeb ( 0xa0 , IOMD_DMATCR ) ;
/*
* Setup DMA channels 2 , 3 to be for podules
* and channels 0 , 1 for internal devices
*/
iomd_writeb ( DMA_EXT_IO3 | DMA_EXT_IO2 , IOMD_DMAEXT ) ;
2008-12-08 19:33:30 +03:00
2008-12-08 20:35:48 +03:00
iomd_dma [ DMA_0 ] . base = IOMD_IO0CURA ;
iomd_dma [ DMA_0 ] . irq = IRQ_DMA0 ;
iomd_dma [ DMA_1 ] . base = IOMD_IO1CURA ;
iomd_dma [ DMA_1 ] . irq = IRQ_DMA1 ;
iomd_dma [ DMA_2 ] . base = IOMD_IO2CURA ;
iomd_dma [ DMA_2 ] . irq = IRQ_DMA2 ;
iomd_dma [ DMA_3 ] . base = IOMD_IO3CURA ;
iomd_dma [ DMA_3 ] . irq = IRQ_DMA3 ;
iomd_dma [ DMA_S0 ] . base = IOMD_SD0CURA ;
iomd_dma [ DMA_S0 ] . irq = IRQ_DMAS0 ;
iomd_dma [ DMA_S1 ] . base = IOMD_SD1CURA ;
iomd_dma [ DMA_S1 ] . irq = IRQ_DMAS1 ;
2008-12-08 19:33:30 +03:00
for ( i = DMA_0 ; i < = DMA_S1 ; i + + ) {
2008-12-08 20:35:48 +03:00
iomd_dma [ i ] . dma . d_ops = & iomd_dma_ops ;
2008-12-08 19:33:30 +03:00
2008-12-08 20:35:48 +03:00
ret = isa_dma_add ( i , & iomd_dma [ i ] . dma ) ;
2008-12-08 19:33:30 +03:00
if ( ret )
printk ( " IOMDDMA%u: unable to register: %d \n " , i , ret ) ;
}
2008-12-08 20:35:48 +03:00
ret = isa_dma_add ( DMA_VIRTUAL_FLOPPY , & floppy_dma . dma ) ;
2008-12-08 19:33:30 +03:00
if ( ret )
printk ( " IOMDFLOPPY: unable to register: %d \n " , ret ) ;
ret = isa_dma_add ( DMA_VIRTUAL_SOUND , & sound_dma ) ;
if ( ret )
printk ( " IOMDSOUND: unable to register: %d \n " , ret ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2008-12-08 19:33:30 +03:00
core_initcall ( rpc_dma_init ) ;