2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2005-04-17 02:20:36 +04:00
/*
* linux / arch / arm / mach - rpc / dma . c
*
* Copyright ( C ) 1998 Russell King
*
* 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>
2016-12-24 22:46:01 +03:00
# include <linux/uaccess.h>
2005-04-17 02:20:36 +04:00
# 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 ;
2019-05-02 19:56:49 +03:00
void __iomem * base ; /* Controller base address */
2009-02-22 00:36:22 +03:00
int irq ; /* Controller IRQ */
2019-05-02 19:56:49 +03:00
unsigned int state ;
2019-05-02 19:37:51 +03:00
dma_addr_t cur_addr ;
unsigned int cur_len ;
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)
2019-05-02 19:37:51 +03:00
static void iomd_get_next_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 ) {
2019-05-02 19:37:51 +03:00
idma - > cur_addr = idma - > dma_addr ;
offset = idma - > cur_addr & ~ PAGE_MASK ;
2005-04-17 02:20:36 +04:00
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 ;
2019-05-02 19:37:51 +03:00
idma - > cur_len = end - TRANSFER_SIZE ;
2005-04-17 02:20:36 +04:00
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 ;
2019-05-02 19:37:51 +03:00
idma - > cur_addr = 0 ;
idma - > cur_len = 0 ;
2005-04-17 02:20:36 +04:00
}
2019-05-02 19:37:51 +03:00
idma - > cur_len | = flags ;
2005-04-17 02:20:36 +04:00
}
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 ;
2019-05-02 19:56:49 +03:00
void __iomem * base = idma - > base ;
2019-05-02 19:43:36 +03:00
unsigned int state = idma - > state ;
2019-05-02 19:50:55 +03:00
unsigned int status , cur , end ;
2005-04-17 02:20:36 +04:00
do {
2019-05-02 19:56:49 +03:00
status = readb ( base + ST ) ;
2005-04-17 02:20:36 +04:00
if ( ! ( status & DMA_ST_INT ) )
2019-05-02 19:43:36 +03:00
goto out ;
2005-04-17 02:20:36 +04:00
2019-05-02 19:43:36 +03:00
if ( ( state ^ status ) & DMA_ST_AB )
2019-05-02 19:37:51 +03:00
iomd_get_next_sg ( idma ) ;
2005-04-17 02:20:36 +04:00
2019-05-02 19:50:55 +03:00
// This efficiently implements state = OFL != AB ? AB : 0
state = ( ( status > > 2 ) ^ status ) & DMA_ST_AB ;
if ( state ) {
cur = CURA ;
end = ENDA ;
} else {
cur = CURB ;
end = ENDB ;
2005-04-17 02:20:36 +04:00
}
2019-05-02 19:56:49 +03:00
writel ( idma - > cur_addr , base + cur ) ;
writel ( idma - > cur_len , base + end ) ;
2005-04-17 02:20:36 +04:00
if ( status & DMA_ST_OFL & &
2019-05-02 19:37:51 +03:00
idma - > cur_len = = ( DMA_END_S | DMA_END_L ) )
2005-04-17 02:20:36 +04:00
break ;
} while ( 1 ) ;
2019-05-02 19:43:36 +03:00
state = ~ DMA_ST_AB ;
2019-05-02 19:19:18 +03:00
disable_irq_nosync ( irq ) ;
2019-05-02 19:43:36 +03:00
out :
idma - > state = state ;
2005-04-17 02:20:36 +04:00
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 ,
2014-03-05 01:04:50 +04:00
0 , 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
}
2018-12-21 16:56:38 +03:00
static struct device isa_dma_dev = {
. init_name = " fallback device " ,
. coherent_dma_mask = ~ ( dma_addr_t ) 0 ,
. dma_mask = & isa_dma_dev . coherent_dma_mask ,
} ;
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 ) ;
2019-05-02 19:56:49 +03:00
void __iomem * 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 ;
2018-12-21 16:56:38 +03:00
idma - > dma . buf . dma_address = dma_map_single ( & isa_dma_dev ,
2008-12-08 20:35:48 +03:00
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
}
2019-05-02 19:19:18 +03:00
idma - > dma_addr = idma - > dma . sg - > dma_address ;
idma - > dma_len = idma - > dma . sg - > length ;
2019-05-02 19:56:49 +03:00
writeb ( DMA_CR_C , 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 ;
2019-05-02 19:56:49 +03:00
writeb ( ctrl , 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 ) ;
2019-05-02 19:56:49 +03:00
void __iomem * 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 ) ;
2019-05-02 19:56:49 +03:00
writeb ( 0 , base + CR ) ;
2005-04-17 02:20:36 +04:00
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
2019-05-02 19:56:49 +03:00
iomd_dma [ DMA_0 ] . base = IOMD_BASE + IOMD_IO0CURA ;
2008-12-08 20:35:48 +03:00
iomd_dma [ DMA_0 ] . irq = IRQ_DMA0 ;
2019-05-02 19:56:49 +03:00
iomd_dma [ DMA_1 ] . base = IOMD_BASE + IOMD_IO1CURA ;
2008-12-08 20:35:48 +03:00
iomd_dma [ DMA_1 ] . irq = IRQ_DMA1 ;
2019-05-02 19:56:49 +03:00
iomd_dma [ DMA_2 ] . base = IOMD_BASE + IOMD_IO2CURA ;
2008-12-08 20:35:48 +03:00
iomd_dma [ DMA_2 ] . irq = IRQ_DMA2 ;
2019-05-02 19:56:49 +03:00
iomd_dma [ DMA_3 ] . base = IOMD_BASE + IOMD_IO3CURA ;
2008-12-08 20:35:48 +03:00
iomd_dma [ DMA_3 ] . irq = IRQ_DMA3 ;
2019-05-02 19:56:49 +03:00
iomd_dma [ DMA_S0 ] . base = IOMD_BASE + IOMD_SD0CURA ;
2008-12-08 20:35:48 +03:00
iomd_dma [ DMA_S0 ] . irq = IRQ_DMAS0 ;
2019-05-02 19:56:49 +03:00
iomd_dma [ DMA_S1 ] . base = IOMD_BASE + IOMD_SD1CURA ;
2008-12-08 20:35:48 +03:00
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 ) ;