2005-04-17 02:20:36 +04:00
/*
* linux / arch / arm / kernel / dma - isa . c
*
* Copyright ( C ) 1999 - 2000 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 .
*
* ISA DMA primitives
* Taken from various sources , including :
* linux / include / asm / dma . h : Defines for using and allocating dma channels .
* Written by Hennus Bergman , 1992.
* High DMA channel support & info by Hannu Savolainen and John Boyd ,
* Nov . 1992.
* arch / arm / kernel / dma - ebsa285 . c
* Copyright ( C ) 1998 Phil Blundell
*/
# include <linux/ioport.h>
# include <linux/init.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/dma.h>
# include <asm/mach/dma.h>
# define ISA_DMA_MASK 0
# define ISA_DMA_MODE 1
# define ISA_DMA_CLRFF 2
# define ISA_DMA_PGHI 3
# define ISA_DMA_PGLO 4
# define ISA_DMA_ADDR 5
# define ISA_DMA_COUNT 6
static unsigned int isa_dma_port [ 8 ] [ 7 ] = {
/* MASK MODE CLRFF PAGE_HI PAGE_LO ADDR COUNT */
{ 0x0a , 0x0b , 0x0c , 0x487 , 0x087 , 0x00 , 0x01 } ,
{ 0x0a , 0x0b , 0x0c , 0x483 , 0x083 , 0x02 , 0x03 } ,
{ 0x0a , 0x0b , 0x0c , 0x481 , 0x081 , 0x04 , 0x05 } ,
{ 0x0a , 0x0b , 0x0c , 0x482 , 0x082 , 0x06 , 0x07 } ,
{ 0xd4 , 0xd6 , 0xd8 , 0x000 , 0x000 , 0xc0 , 0xc2 } ,
{ 0xd4 , 0xd6 , 0xd8 , 0x48b , 0x08b , 0xc4 , 0xc6 } ,
{ 0xd4 , 0xd6 , 0xd8 , 0x489 , 0x089 , 0xc8 , 0xca } ,
{ 0xd4 , 0xd6 , 0xd8 , 0x48a , 0x08a , 0xcc , 0xce }
} ;
2008-12-08 18:58:50 +03:00
static int isa_get_dma_residue ( unsigned int chan , dma_t * dma )
2005-04-17 02:20:36 +04:00
{
2008-12-08 18:58:50 +03:00
unsigned int io_port = isa_dma_port [ chan ] [ ISA_DMA_COUNT ] ;
2005-04-17 02:20:36 +04:00
int count ;
count = 1 + inb ( io_port ) ;
count | = inb ( io_port ) < < 8 ;
2008-12-08 18:58:50 +03:00
return chan < 4 ? count : ( count < < 1 ) ;
2005-04-17 02:20:36 +04:00
}
2008-12-08 18:58:50 +03:00
static void isa_enable_dma ( unsigned int chan , dma_t * dma )
2005-04-17 02:20:36 +04:00
{
if ( dma - > invalid ) {
unsigned long address , length ;
2006-01-04 18:08:30 +03:00
unsigned int mode ;
enum dma_data_direction direction ;
2005-04-17 02:20:36 +04:00
2009-01-02 15:18:53 +03:00
mode = ( chan & 3 ) | dma - > dma_mode ;
2005-04-17 02:20:36 +04:00
switch ( dma - > dma_mode & DMA_MODE_MASK ) {
case DMA_MODE_READ :
2006-01-04 18:08:30 +03:00
direction = DMA_FROM_DEVICE ;
2005-04-17 02:20:36 +04:00
break ;
case DMA_MODE_WRITE :
2006-01-04 18:08:30 +03:00
direction = DMA_TO_DEVICE ;
2005-04-17 02:20:36 +04:00
break ;
case DMA_MODE_CASCADE :
2006-01-04 18:08:30 +03:00
direction = DMA_BIDIRECTIONAL ;
2005-04-17 02:20:36 +04:00
break ;
default :
2006-01-04 18:08:30 +03:00
direction = DMA_NONE ;
2005-04-17 02:20:36 +04:00
break ;
}
2006-01-04 18:08:30 +03:00
if ( ! dma - > sg ) {
2005-04-17 02:20:36 +04:00
/*
* Cope with ISA - style drivers which expect cache
* coherence .
*/
2006-01-04 18:08:30 +03:00
dma - > sg = & dma - > buf ;
dma - > sgcount = 1 ;
dma - > buf . length = dma - > count ;
dma - > buf . dma_address = dma_map_single ( NULL ,
dma - > addr , dma - > count ,
2005-04-17 02:20:36 +04:00
direction ) ;
}
address = dma - > buf . dma_address ;
length = dma - > buf . length - 1 ;
2008-12-08 18:58:50 +03:00
outb ( address > > 16 , isa_dma_port [ chan ] [ ISA_DMA_PGLO ] ) ;
outb ( address > > 24 , isa_dma_port [ chan ] [ ISA_DMA_PGHI ] ) ;
2005-04-17 02:20:36 +04:00
2008-12-08 18:58:50 +03:00
if ( chan > = 4 ) {
2005-04-17 02:20:36 +04:00
address > > = 1 ;
length > > = 1 ;
}
2008-12-08 18:58:50 +03:00
outb ( 0 , isa_dma_port [ chan ] [ ISA_DMA_CLRFF ] ) ;
2005-04-17 02:20:36 +04:00
2008-12-08 18:58:50 +03:00
outb ( address , isa_dma_port [ chan ] [ ISA_DMA_ADDR ] ) ;
outb ( address > > 8 , isa_dma_port [ chan ] [ ISA_DMA_ADDR ] ) ;
2005-04-17 02:20:36 +04:00
2008-12-08 18:58:50 +03:00
outb ( length , isa_dma_port [ chan ] [ ISA_DMA_COUNT ] ) ;
outb ( length > > 8 , isa_dma_port [ chan ] [ ISA_DMA_COUNT ] ) ;
2005-04-17 02:20:36 +04:00
2008-12-08 18:58:50 +03:00
outb ( mode , isa_dma_port [ chan ] [ ISA_DMA_MODE ] ) ;
2005-04-17 02:20:36 +04:00
dma - > invalid = 0 ;
}
2008-12-08 18:58:50 +03:00
outb ( chan & 3 , isa_dma_port [ chan ] [ ISA_DMA_MASK ] ) ;
2005-04-17 02:20:36 +04:00
}
2008-12-08 18:58:50 +03:00
static void isa_disable_dma ( unsigned int chan , dma_t * dma )
2005-04-17 02:20:36 +04:00
{
2008-12-08 18:58:50 +03:00
outb ( chan | 4 , isa_dma_port [ chan ] [ ISA_DMA_MASK ] ) ;
2005-04-17 02:20:36 +04:00
}
static struct dma_ops isa_dma_ops = {
. type = " ISA " ,
. enable = isa_enable_dma ,
. disable = isa_disable_dma ,
. residue = isa_get_dma_residue ,
} ;
2006-05-17 01:09:46 +04:00
static struct resource dma_resources [ ] = { {
. name = " dma1 " ,
. start = 0x0000 ,
. end = 0x000f
} , {
. name = " dma low page " ,
. start = 0x0080 ,
. end = 0x008f
} , {
. name = " dma2 " ,
. start = 0x00c0 ,
. end = 0x00df
} , {
. name = " dma high page " ,
. start = 0x0480 ,
. end = 0x048f
} } ;
2005-04-17 02:20:36 +04:00
2008-12-08 19:33:30 +03:00
static dma_t isa_dma [ 8 ] ;
/*
* ISA DMA always starts at channel 0
*/
void __init isa_init_dma ( void )
2005-04-17 02:20:36 +04:00
{
/*
* Try to autodetect presence of an ISA DMA controller .
* We do some minimal initialisation , and check that
* channel 0 ' s DMA address registers are writeable .
*/
outb ( 0xff , 0x0d ) ;
outb ( 0xff , 0xda ) ;
/*
* Write high and low address , and then read them back
* in the same order .
*/
outb ( 0x55 , 0x00 ) ;
outb ( 0xaa , 0x00 ) ;
if ( inb ( 0 ) = = 0x55 & & inb ( 0 ) = = 0xaa ) {
2008-12-08 19:33:30 +03:00
unsigned int chan , i ;
2005-04-17 02:20:36 +04:00
2008-12-08 18:58:50 +03:00
for ( chan = 0 ; chan < 8 ; chan + + ) {
2008-12-08 19:33:30 +03:00
isa_dma [ chan ] . d_ops = & isa_dma_ops ;
2008-12-08 18:58:50 +03:00
isa_disable_dma ( chan , NULL ) ;
2005-04-17 02:20:36 +04:00
}
outb ( 0x40 , 0x0b ) ;
outb ( 0x41 , 0x0b ) ;
outb ( 0x42 , 0x0b ) ;
outb ( 0x43 , 0x0b ) ;
outb ( 0xc0 , 0xd6 ) ;
outb ( 0x41 , 0xd6 ) ;
outb ( 0x42 , 0xd6 ) ;
outb ( 0x43 , 0xd6 ) ;
outb ( 0 , 0xd4 ) ;
outb ( 0x10 , 0x08 ) ;
outb ( 0x10 , 0xd0 ) ;
/*
* Is this correct ? According to my documentation , it
* doesn ' t appear to be . It should be :
* outb ( 0x3f , 0x40b ) ; outb ( 0x3f , 0x4d6 ) ;
*/
outb ( 0x30 , 0x40b ) ;
outb ( 0x31 , 0x40b ) ;
outb ( 0x32 , 0x40b ) ;
outb ( 0x33 , 0x40b ) ;
outb ( 0x31 , 0x4d6 ) ;
outb ( 0x32 , 0x4d6 ) ;
outb ( 0x33 , 0x4d6 ) ;
request_dma ( DMA_ISA_CASCADE , " cascade " ) ;
2007-10-17 14:14:25 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( dma_resources ) ; i + + )
2005-04-17 02:20:36 +04:00
request_resource ( & ioport_resource , dma_resources + i ) ;
2008-12-08 19:33:30 +03:00
for ( chan = 0 ; chan < 8 ; chan + + ) {
int ret = isa_dma_add ( chan , & isa_dma [ chan ] ) ;
if ( ret )
printk ( KERN_ERR " ISADMA%u: unable to register: %d \n " ,
chan , ret ) ;
}
2005-04-17 02:20:36 +04:00
}
}