2008-02-01 02:13:34 +03:00
/* sun3x_esp.c: ESP front-end for Sun3x systems.
2005-04-17 02:20:36 +04:00
*
2008-02-01 02:13:34 +03:00
* Copyright ( C ) 2007 , 2008 Thomas Bogendoerfer ( tsbogend @ alpha . franken . de )
2005-04-17 02:20:36 +04:00
*/
# include <linux/kernel.h>
# include <linux/types.h>
# include <linux/delay.h>
2008-02-01 02:13:34 +03:00
# include <linux/module.h>
# include <linux/init.h>
# include <linux/platform_device.h>
# include <linux/dma-mapping.h>
2005-04-17 02:20:36 +04:00
# include <linux/interrupt.h>
# include <asm/sun3x.h>
2008-02-01 02:13:34 +03:00
# include <asm/io.h>
# include <asm/dma.h>
2005-04-17 02:20:36 +04:00
# include <asm/dvma.h>
2008-02-01 02:13:34 +03:00
/* DMA controller reg offsets */
# define DMA_CSR 0x00UL /* rw DMA control/status register 0x00 */
# define DMA_ADDR 0x04UL /* rw DMA transfer address register 0x04 */
# define DMA_COUNT 0x08UL /* rw DMA transfer count register 0x08 */
# define DMA_TEST 0x0cUL /* rw DMA test/debug register 0x0c */
2005-04-17 02:20:36 +04:00
2008-02-01 02:13:34 +03:00
# include <scsi/scsi_host.h>
2005-04-17 02:20:36 +04:00
2008-02-01 02:13:34 +03:00
# include "esp_scsi.h"
2005-04-17 02:20:36 +04:00
2008-02-01 02:13:34 +03:00
# define DRV_MODULE_NAME "sun3x_esp"
# define PFX DRV_MODULE_NAME ": "
# define DRV_VERSION "1.000"
# define DRV_MODULE_RELDATE "Nov 1, 2007"
2005-04-17 02:20:36 +04:00
2008-02-01 02:13:34 +03:00
/*
* m68k always assumes readl / writel operate on little endian
* mmio space ; this is wrong at least for Sun3x , so we
* need to workaround this until a proper way is found
*/
#if 0
# define dma_read32(REG) \
readl ( esp - > dma_regs + ( REG ) )
# define dma_write32(VAL, REG) \
writel ( ( VAL ) , esp - > dma_regs + ( REG ) )
# else
# define dma_read32(REG) \
* ( volatile u32 * ) ( esp - > dma_regs + ( REG ) )
# define dma_write32(VAL, REG) \
do { * ( volatile u32 * ) ( esp - > dma_regs + ( REG ) ) = ( VAL ) ; } while ( 0 )
# endif
static void sun3x_esp_write8 ( struct esp * esp , u8 val , unsigned long reg )
{
writeb ( val , esp - > regs + ( reg * 4UL ) ) ;
2005-04-17 02:20:36 +04:00
}
2008-02-01 02:13:34 +03:00
static u8 sun3x_esp_read8 ( struct esp * esp , unsigned long reg )
2005-04-17 02:20:36 +04:00
{
2008-02-01 02:13:34 +03:00
return readb ( esp - > regs + ( reg * 4UL ) ) ;
2005-04-17 02:20:36 +04:00
}
2008-02-01 02:13:34 +03:00
static dma_addr_t sun3x_esp_map_single ( struct esp * esp , void * buf ,
size_t sz , int dir )
2005-04-17 02:20:36 +04:00
{
2008-02-01 02:13:34 +03:00
return dma_map_single ( esp - > dev , buf , sz , dir ) ;
2005-04-17 02:20:36 +04:00
}
2008-02-01 02:13:34 +03:00
static int sun3x_esp_map_sg ( struct esp * esp , struct scatterlist * sg ,
int num_sg , int dir )
2005-04-17 02:20:36 +04:00
{
2008-02-01 02:13:34 +03:00
return dma_map_sg ( esp - > dev , sg , num_sg , dir ) ;
2005-04-17 02:20:36 +04:00
}
2008-02-01 02:13:34 +03:00
static void sun3x_esp_unmap_single ( struct esp * esp , dma_addr_t addr ,
size_t sz , int dir )
2005-04-17 02:20:36 +04:00
{
2008-02-01 02:13:34 +03:00
dma_unmap_single ( esp - > dev , addr , sz , dir ) ;
2005-04-17 02:20:36 +04:00
}
2008-02-01 02:13:34 +03:00
static void sun3x_esp_unmap_sg ( struct esp * esp , struct scatterlist * sg ,
int num_sg , int dir )
2005-04-17 02:20:36 +04:00
{
2008-02-01 02:13:34 +03:00
dma_unmap_sg ( esp - > dev , sg , num_sg , dir ) ;
2005-04-17 02:20:36 +04:00
}
2008-02-01 02:13:34 +03:00
static int sun3x_esp_irq_pending ( struct esp * esp )
2005-04-17 02:20:36 +04:00
{
2008-02-01 02:13:34 +03:00
if ( dma_read32 ( DMA_CSR ) & ( DMA_HNDL_INTR | DMA_HNDL_ERROR ) )
return 1 ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
2008-02-01 02:13:34 +03:00
static void sun3x_esp_reset_dma ( struct esp * esp )
{
u32 val ;
2005-04-17 02:20:36 +04:00
2008-02-01 02:13:34 +03:00
val = dma_read32 ( DMA_CSR ) ;
dma_write32 ( val | DMA_RST_SCSI , DMA_CSR ) ;
dma_write32 ( val & ~ DMA_RST_SCSI , DMA_CSR ) ;
2005-04-17 02:20:36 +04:00
2008-02-01 02:13:34 +03:00
/* Enable interrupts. */
val = dma_read32 ( DMA_CSR ) ;
dma_write32 ( val | DMA_INT_ENAB , DMA_CSR ) ;
2005-04-17 02:20:36 +04:00
}
2008-02-01 02:13:34 +03:00
static void sun3x_esp_dma_drain ( struct esp * esp )
2005-04-17 02:20:36 +04:00
{
2008-02-01 02:13:34 +03:00
u32 csr ;
int lim ;
2005-04-17 02:20:36 +04:00
2008-02-01 02:13:34 +03:00
csr = dma_read32 ( DMA_CSR ) ;
if ( ! ( csr & DMA_FIFO_ISDRAIN ) )
return ;
2005-04-17 02:20:36 +04:00
2008-02-01 02:13:34 +03:00
dma_write32 ( csr | DMA_FIFO_STDRAIN , DMA_CSR ) ;
2005-04-17 02:20:36 +04:00
2008-02-01 02:13:34 +03:00
lim = 1000 ;
while ( dma_read32 ( DMA_CSR ) & DMA_FIFO_ISDRAIN ) {
if ( - - lim = = 0 ) {
printk ( KERN_ALERT PFX " esp%d: DMA will not drain! \n " ,
esp - > host - > unique_id ) ;
break ;
}
udelay ( 1 ) ;
}
2005-04-17 02:20:36 +04:00
}
2008-02-01 02:13:34 +03:00
static void sun3x_esp_dma_invalidate ( struct esp * esp )
2005-04-17 02:20:36 +04:00
{
2008-02-01 02:13:34 +03:00
u32 val ;
int lim ;
lim = 1000 ;
while ( ( val = dma_read32 ( DMA_CSR ) ) & DMA_PEND_READ ) {
if ( - - lim = = 0 ) {
printk ( KERN_ALERT PFX " esp%d: DMA will not "
" invalidate! \n " , esp - > host - > unique_id ) ;
break ;
}
udelay ( 1 ) ;
}
2005-04-17 02:20:36 +04:00
2008-02-01 02:13:34 +03:00
val & = ~ ( DMA_ENABLE | DMA_ST_WRITE | DMA_BCNT_ENAB ) ;
val | = DMA_FIFO_INV ;
dma_write32 ( val , DMA_CSR ) ;
val & = ~ DMA_FIFO_INV ;
dma_write32 ( val , DMA_CSR ) ;
2005-04-17 02:20:36 +04:00
}
2008-02-01 02:13:34 +03:00
static void sun3x_esp_send_dma_cmd ( struct esp * esp , u32 addr , u32 esp_count ,
u32 dma_count , int write , u8 cmd )
2005-04-17 02:20:36 +04:00
{
2008-02-01 02:13:34 +03:00
u32 csr ;
BUG_ON ( ! ( cmd & ESP_CMD_DMA ) ) ;
sun3x_esp_write8 ( esp , ( esp_count > > 0 ) & 0xff , ESP_TCLOW ) ;
sun3x_esp_write8 ( esp , ( esp_count > > 8 ) & 0xff , ESP_TCMED ) ;
csr = dma_read32 ( DMA_CSR ) ;
csr | = DMA_ENABLE ;
if ( write )
csr | = DMA_ST_WRITE ;
else
csr & = ~ DMA_ST_WRITE ;
dma_write32 ( csr , DMA_CSR ) ;
dma_write32 ( addr , DMA_ADDR ) ;
scsi_esp_cmd ( esp , cmd ) ;
2005-04-17 02:20:36 +04:00
}
2008-02-01 02:13:34 +03:00
static int sun3x_esp_dma_error ( struct esp * esp )
2005-04-17 02:20:36 +04:00
{
2008-02-01 02:13:34 +03:00
u32 csr = dma_read32 ( DMA_CSR ) ;
2005-04-17 02:20:36 +04:00
2008-02-01 02:13:34 +03:00
if ( csr & DMA_HNDL_ERROR )
return 1 ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2008-02-01 02:13:34 +03:00
static const struct esp_driver_ops sun3x_esp_ops = {
. esp_write8 = sun3x_esp_write8 ,
. esp_read8 = sun3x_esp_read8 ,
. map_single = sun3x_esp_map_single ,
. map_sg = sun3x_esp_map_sg ,
. unmap_single = sun3x_esp_unmap_single ,
. unmap_sg = sun3x_esp_unmap_sg ,
. irq_pending = sun3x_esp_irq_pending ,
. reset_dma = sun3x_esp_reset_dma ,
. dma_drain = sun3x_esp_dma_drain ,
. dma_invalidate = sun3x_esp_dma_invalidate ,
. send_dma_cmd = sun3x_esp_send_dma_cmd ,
. dma_error = sun3x_esp_dma_error ,
} ;
static int __devinit esp_sun3x_probe ( struct platform_device * dev )
2005-04-17 02:20:36 +04:00
{
2008-02-01 02:13:34 +03:00
struct scsi_host_template * tpnt = & scsi_esp_template ;
struct Scsi_Host * host ;
struct esp * esp ;
struct resource * res ;
int err = - ENOMEM ;
2005-04-17 02:20:36 +04:00
2008-02-01 02:13:34 +03:00
host = scsi_host_alloc ( tpnt , sizeof ( struct esp ) ) ;
if ( ! host )
goto fail ;
2005-04-17 02:20:36 +04:00
2008-02-01 02:13:34 +03:00
host - > max_id = 8 ;
esp = shost_priv ( host ) ;
2005-04-17 02:20:36 +04:00
2008-02-01 02:13:34 +03:00
esp - > host = host ;
esp - > dev = dev ;
esp - > ops = & sun3x_esp_ops ;
2005-04-17 02:20:36 +04:00
2008-02-01 02:13:34 +03:00
res = platform_get_resource ( dev , IORESOURCE_MEM , 0 ) ;
2008-10-14 20:00:19 +04:00
if ( ! res | | ! res - > start )
2008-02-01 02:13:34 +03:00
goto fail_unlink ;
2005-04-17 02:20:36 +04:00
2008-02-01 02:13:34 +03:00
esp - > regs = ioremap_nocache ( res - > start , 0x20 ) ;
if ( ! esp - > regs )
goto fail_unmap_regs ;
2005-04-17 02:20:36 +04:00
2008-02-01 02:13:34 +03:00
res = platform_get_resource ( dev , IORESOURCE_MEM , 1 ) ;
2008-10-14 20:00:19 +04:00
if ( ! res | | ! res - > start )
2008-02-01 02:13:34 +03:00
goto fail_unmap_regs ;
2005-04-17 02:20:36 +04:00
2008-02-01 02:13:34 +03:00
esp - > dma_regs = ioremap_nocache ( res - > start , 0x10 ) ;
2005-04-17 02:20:36 +04:00
2008-02-01 02:13:34 +03:00
esp - > command_block = dma_alloc_coherent ( esp - > dev , 16 ,
& esp - > command_block_dma ,
GFP_KERNEL ) ;
if ( ! esp - > command_block )
goto fail_unmap_regs_dma ;
2005-04-17 02:20:36 +04:00
2008-02-01 02:13:34 +03:00
host - > irq = platform_get_irq ( dev , 0 ) ;
err = request_irq ( host - > irq , scsi_esp_intr , IRQF_SHARED ,
" SUN3X ESP " , esp ) ;
if ( err < 0 )
goto fail_unmap_command_block ;
2005-04-17 02:20:36 +04:00
2008-02-01 02:13:34 +03:00
esp - > scsi_id = 7 ;
esp - > host - > this_id = esp - > scsi_id ;
esp - > scsi_id_mask = ( 1 < < esp - > scsi_id ) ;
esp - > cfreq = 20000000 ;
2005-04-17 02:20:36 +04:00
2008-02-01 02:13:34 +03:00
dev_set_drvdata ( & dev - > dev , esp ) ;
err = scsi_esp_register ( esp , & dev - > dev ) ;
if ( err )
goto fail_free_irq ;
return 0 ;
fail_free_irq :
free_irq ( host - > irq , esp ) ;
fail_unmap_command_block :
dma_free_coherent ( esp - > dev , 16 ,
esp - > command_block ,
esp - > command_block_dma ) ;
fail_unmap_regs_dma :
iounmap ( esp - > dma_regs ) ;
fail_unmap_regs :
iounmap ( esp - > regs ) ;
fail_unlink :
scsi_host_put ( host ) ;
fail :
return err ;
2005-04-17 02:20:36 +04:00
}
2008-02-01 02:13:34 +03:00
static int __devexit esp_sun3x_remove ( struct platform_device * dev )
2005-04-17 02:20:36 +04:00
{
2008-02-01 02:13:34 +03:00
struct esp * esp = dev_get_drvdata ( & dev - > dev ) ;
unsigned int irq = esp - > host - > irq ;
u32 val ;
2005-04-17 02:20:36 +04:00
2008-02-01 02:13:34 +03:00
scsi_esp_unregister ( esp ) ;
2005-04-17 02:20:36 +04:00
2008-02-01 02:13:34 +03:00
/* Disable interrupts. */
val = dma_read32 ( DMA_CSR ) ;
dma_write32 ( val & ~ DMA_INT_ENAB , DMA_CSR ) ;
2005-04-17 02:20:36 +04:00
2008-02-01 02:13:34 +03:00
free_irq ( irq , esp ) ;
dma_free_coherent ( esp - > dev , 16 ,
esp - > command_block ,
esp - > command_block_dma ) ;
2005-04-17 02:20:36 +04:00
2008-02-01 02:13:34 +03:00
scsi_host_put ( esp - > host ) ;
2005-04-17 02:20:36 +04:00
2008-02-01 02:13:34 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2008-02-01 02:13:34 +03:00
static struct platform_driver esp_sun3x_driver = {
. probe = esp_sun3x_probe ,
. remove = __devexit_p ( esp_sun3x_remove ) ,
. driver = {
. name = " sun3x_esp " ,
2008-04-19 00:57:19 +04:00
. owner = THIS_MODULE ,
2008-02-01 02:13:34 +03:00
} ,
2005-04-17 02:20:36 +04:00
} ;
2008-02-01 02:13:34 +03:00
static int __init sun3x_esp_init ( void )
{
return platform_driver_register ( & esp_sun3x_driver ) ;
}
2005-04-17 02:20:36 +04:00
2008-02-01 02:13:34 +03:00
static void __exit sun3x_esp_exit ( void )
{
platform_driver_unregister ( & esp_sun3x_driver ) ;
}
2005-04-17 02:20:36 +04:00
2008-02-01 02:13:34 +03:00
MODULE_DESCRIPTION ( " Sun3x ESP SCSI driver " ) ;
MODULE_AUTHOR ( " Thomas Bogendoerfer (tsbogend@alpha.franken.de) " ) ;
2005-04-17 02:20:36 +04:00
MODULE_LICENSE ( " GPL " ) ;
2008-02-01 02:13:34 +03:00
MODULE_VERSION ( DRV_VERSION ) ;
module_init ( sun3x_esp_init ) ;
module_exit ( sun3x_esp_exit ) ;
2008-04-19 00:57:19 +04:00
MODULE_ALIAS ( " platform:sun3x_esp " ) ;