2008-08-30 11:36:11 +04:00
/* ebus.c: EBUS DMA library code.
2005-04-17 02:20:36 +04:00
*
* Copyright ( C ) 1997 Eddie C . Dost ( ecd @ skynet . be )
* Copyright ( C ) 1999 David S . Miller ( davem @ redhat . com )
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/types.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/delay.h>
2008-08-30 10:10:21 +04:00
# include <asm/ebus_dma.h>
2007-05-08 11:43:56 +04:00
# include <asm/io.h>
2005-04-17 02:20:36 +04:00
# define EBDMA_CSR 0x00UL /* Control/Status */
# define EBDMA_ADDR 0x04UL /* DMA Address */
# define EBDMA_COUNT 0x08UL /* DMA Count */
# define EBDMA_CSR_INT_PEND 0x00000001
# define EBDMA_CSR_ERR_PEND 0x00000002
# define EBDMA_CSR_DRAIN 0x00000004
# define EBDMA_CSR_INT_EN 0x00000010
# define EBDMA_CSR_RESET 0x00000080
# define EBDMA_CSR_WRITE 0x00000100
# define EBDMA_CSR_EN_DMA 0x00000200
# define EBDMA_CSR_CYC_PEND 0x00000400
# define EBDMA_CSR_DIAG_RD_DONE 0x00000800
# define EBDMA_CSR_DIAG_WR_DONE 0x00001000
# define EBDMA_CSR_EN_CNT 0x00002000
# define EBDMA_CSR_TC 0x00004000
# define EBDMA_CSR_DIS_CSR_DRN 0x00010000
# define EBDMA_CSR_BURST_SZ_MASK 0x000c0000
# define EBDMA_CSR_BURST_SZ_1 0x00080000
# define EBDMA_CSR_BURST_SZ_4 0x00000000
# define EBDMA_CSR_BURST_SZ_8 0x00040000
# define EBDMA_CSR_BURST_SZ_16 0x000c0000
# define EBDMA_CSR_DIAG_EN 0x00100000
# define EBDMA_CSR_DIS_ERR_PEND 0x00400000
# define EBDMA_CSR_TCI_DIS 0x00800000
# define EBDMA_CSR_EN_NEXT 0x01000000
# define EBDMA_CSR_DMA_ON 0x02000000
# define EBDMA_CSR_A_LOADED 0x04000000
# define EBDMA_CSR_NA_LOADED 0x08000000
# define EBDMA_CSR_DEV_ID_MASK 0xf0000000
# define EBUS_DMA_RESET_TIMEOUT 10000
static void __ebus_dma_reset ( struct ebus_dma_info * p , int no_drain )
{
int i ;
u32 val = 0 ;
writel ( EBDMA_CSR_RESET , p - > regs + EBDMA_CSR ) ;
udelay ( 1 ) ;
if ( no_drain )
return ;
for ( i = EBUS_DMA_RESET_TIMEOUT ; i > 0 ; i - - ) {
val = readl ( p - > regs + EBDMA_CSR ) ;
if ( ! ( val & ( EBDMA_CSR_DRAIN | EBDMA_CSR_CYC_PEND ) ) )
break ;
udelay ( 10 ) ;
}
}
2006-10-08 16:23:28 +04:00
static irqreturn_t ebus_dma_irq ( int irq , void * dev_id )
2005-04-17 02:20:36 +04:00
{
struct ebus_dma_info * p = dev_id ;
unsigned long flags ;
u32 csr = 0 ;
spin_lock_irqsave ( & p - > lock , flags ) ;
csr = readl ( p - > regs + EBDMA_CSR ) ;
writel ( csr , p - > regs + EBDMA_CSR ) ;
spin_unlock_irqrestore ( & p - > lock , flags ) ;
if ( csr & EBDMA_CSR_ERR_PEND ) {
printk ( KERN_CRIT " ebus_dma(%s): DMA error! \n " , p - > name ) ;
p - > callback ( p , EBUS_DMA_EVENT_ERROR , p - > client_cookie ) ;
return IRQ_HANDLED ;
} else if ( csr & EBDMA_CSR_INT_PEND ) {
p - > callback ( p ,
( csr & EBDMA_CSR_TC ) ?
EBUS_DMA_EVENT_DMA : EBUS_DMA_EVENT_DEVICE ,
p - > client_cookie ) ;
return IRQ_HANDLED ;
}
return IRQ_NONE ;
}
int ebus_dma_register ( struct ebus_dma_info * p )
{
u32 csr ;
if ( ! p - > regs )
return - EINVAL ;
if ( p - > flags & ~ ( EBUS_DMA_FLAG_USE_EBDMA_HANDLER |
EBUS_DMA_FLAG_TCI_DISABLE ) )
return - EINVAL ;
if ( ( p - > flags & EBUS_DMA_FLAG_USE_EBDMA_HANDLER ) & & ! p - > callback )
return - EINVAL ;
if ( ! strlen ( p - > name ) )
return - EINVAL ;
__ebus_dma_reset ( p , 1 ) ;
csr = EBDMA_CSR_BURST_SZ_16 | EBDMA_CSR_EN_CNT ;
if ( p - > flags & EBUS_DMA_FLAG_TCI_DISABLE )
csr | = EBDMA_CSR_TCI_DIS ;
writel ( csr , p - > regs + EBDMA_CSR ) ;
return 0 ;
}
EXPORT_SYMBOL ( ebus_dma_register ) ;
int ebus_dma_irq_enable ( struct ebus_dma_info * p , int on )
{
unsigned long flags ;
u32 csr ;
if ( on ) {
if ( p - > flags & EBUS_DMA_FLAG_USE_EBDMA_HANDLER ) {
2006-07-02 06:29:26 +04:00
if ( request_irq ( p - > irq , ebus_dma_irq , IRQF_SHARED , p - > name , p ) )
2005-04-17 02:20:36 +04:00
return - EBUSY ;
}
spin_lock_irqsave ( & p - > lock , flags ) ;
csr = readl ( p - > regs + EBDMA_CSR ) ;
csr | = EBDMA_CSR_INT_EN ;
writel ( csr , p - > regs + EBDMA_CSR ) ;
spin_unlock_irqrestore ( & p - > lock , flags ) ;
} else {
spin_lock_irqsave ( & p - > lock , flags ) ;
csr = readl ( p - > regs + EBDMA_CSR ) ;
csr & = ~ EBDMA_CSR_INT_EN ;
writel ( csr , p - > regs + EBDMA_CSR ) ;
spin_unlock_irqrestore ( & p - > lock , flags ) ;
if ( p - > flags & EBUS_DMA_FLAG_USE_EBDMA_HANDLER ) {
free_irq ( p - > irq , p ) ;
}
}
return 0 ;
}
EXPORT_SYMBOL ( ebus_dma_irq_enable ) ;
void ebus_dma_unregister ( struct ebus_dma_info * p )
{
unsigned long flags ;
u32 csr ;
int irq_on = 0 ;
spin_lock_irqsave ( & p - > lock , flags ) ;
csr = readl ( p - > regs + EBDMA_CSR ) ;
if ( csr & EBDMA_CSR_INT_EN ) {
csr & = ~ EBDMA_CSR_INT_EN ;
writel ( csr , p - > regs + EBDMA_CSR ) ;
irq_on = 1 ;
}
spin_unlock_irqrestore ( & p - > lock , flags ) ;
if ( irq_on )
free_irq ( p - > irq , p ) ;
}
EXPORT_SYMBOL ( ebus_dma_unregister ) ;
int ebus_dma_request ( struct ebus_dma_info * p , dma_addr_t bus_addr , size_t len )
{
unsigned long flags ;
u32 csr ;
int err ;
if ( len > = ( 1 < < 24 ) )
return - EINVAL ;
spin_lock_irqsave ( & p - > lock , flags ) ;
csr = readl ( p - > regs + EBDMA_CSR ) ;
err = - EINVAL ;
if ( ! ( csr & EBDMA_CSR_EN_DMA ) )
goto out ;
err = - EBUSY ;
if ( csr & EBDMA_CSR_NA_LOADED )
goto out ;
writel ( len , p - > regs + EBDMA_COUNT ) ;
writel ( bus_addr , p - > regs + EBDMA_ADDR ) ;
err = 0 ;
out :
spin_unlock_irqrestore ( & p - > lock , flags ) ;
return err ;
}
EXPORT_SYMBOL ( ebus_dma_request ) ;
void ebus_dma_prepare ( struct ebus_dma_info * p , int write )
{
unsigned long flags ;
u32 csr ;
spin_lock_irqsave ( & p - > lock , flags ) ;
__ebus_dma_reset ( p , 0 ) ;
csr = ( EBDMA_CSR_INT_EN |
EBDMA_CSR_EN_CNT |
EBDMA_CSR_BURST_SZ_16 |
EBDMA_CSR_EN_NEXT ) ;
if ( write )
csr | = EBDMA_CSR_WRITE ;
if ( p - > flags & EBUS_DMA_FLAG_TCI_DISABLE )
csr | = EBDMA_CSR_TCI_DIS ;
writel ( csr , p - > regs + EBDMA_CSR ) ;
spin_unlock_irqrestore ( & p - > lock , flags ) ;
}
EXPORT_SYMBOL ( ebus_dma_prepare ) ;
unsigned int ebus_dma_residue ( struct ebus_dma_info * p )
{
return readl ( p - > regs + EBDMA_COUNT ) ;
}
EXPORT_SYMBOL ( ebus_dma_residue ) ;
unsigned int ebus_dma_addr ( struct ebus_dma_info * p )
{
return readl ( p - > regs + EBDMA_ADDR ) ;
}
EXPORT_SYMBOL ( ebus_dma_addr ) ;
void ebus_dma_enable ( struct ebus_dma_info * p , int on )
{
unsigned long flags ;
u32 orig_csr , csr ;
spin_lock_irqsave ( & p - > lock , flags ) ;
orig_csr = csr = readl ( p - > regs + EBDMA_CSR ) ;
if ( on )
csr | = EBDMA_CSR_EN_DMA ;
else
csr & = ~ EBDMA_CSR_EN_DMA ;
if ( ( orig_csr & EBDMA_CSR_EN_DMA ) ! =
( csr & EBDMA_CSR_EN_DMA ) )
writel ( csr , p - > regs + EBDMA_CSR ) ;
spin_unlock_irqrestore ( & p - > lock , flags ) ;
}
EXPORT_SYMBOL ( ebus_dma_enable ) ;