2005-04-17 02:20:36 +04:00
/*
* Qlogic FAS408 ISA card driver
*
* Copyright 1994 , Tom Zerucha .
* tz @ execpc . com
*
* Redistributable under terms of the GNU General Public License
*
* For the avoidance of doubt the " preferred form " of this code is one which
* is in an open non patent encumbered format . Where cryptographic key signing
* forms part of the process of creating an executable the information
* including keys needed to generate an equivalently functional executable
* are deemed to be part of the source code .
*
* Check qlogicfas408 . c for more credits and info .
*/
# include <linux/module.h>
# include <linux/blkdev.h> /* to get disk capacity */
# include <linux/kernel.h>
# include <linux/string.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/ioport.h>
# include <linux/proc_fs.h>
# include <linux/unistd.h>
# include <linux/spinlock.h>
# include <linux/stat.h>
# include <asm/io.h>
# include <asm/irq.h>
# include <asm/dma.h>
# include "scsi.h"
# include <scsi/scsi_host.h>
# include "qlogicfas408.h"
/* Set the following to 2 to use normal interrupt (active high/totempole-
* tristate ) , otherwise use 0 ( REQUIRED FOR PCMCIA ) for active low , open
* drain
*/
# define INT_TYPE 2
static char qlogicfas_name [ ] = " qlogicfas " ;
/*
* Look for qlogic card and init if found
*/
2005-10-31 20:31:40 +03:00
static struct Scsi_Host * __qlogicfas_detect ( struct scsi_host_template * host ,
2005-04-17 02:20:36 +04:00
int qbase ,
int qlirq )
{
int qltyp ; /* type of chip */
int qinitid ;
struct Scsi_Host * hreg ; /* registered host structure */
struct qlogicfas408_priv * priv ;
/* Qlogic Cards only exist at 0x230 or 0x330 (the chip itself
* decodes the address - I check 230 first since MIDI cards are
* typically at 0x330
*
* Theoretically , two Qlogic cards can coexist in the same system .
* This should work by simply using this as a loadable module for
* the second card , but I haven ' t tested this .
*/
if ( ! qbase | | qlirq = = - 1 )
goto err ;
if ( ! request_region ( qbase , 0x10 , qlogicfas_name ) ) {
printk ( KERN_INFO " %s: address %#x is busy \n " , qlogicfas_name ,
qbase ) ;
goto err ;
}
if ( ! qlogicfas408_detect ( qbase , INT_TYPE ) ) {
printk ( KERN_WARNING " %s: probe failed for %#x \n " ,
qlogicfas_name ,
qbase ) ;
goto err_release_mem ;
}
printk ( KERN_INFO " %s: Using preset base address of %03x, "
" IRQ %d \n " , qlogicfas_name , qbase , qlirq ) ;
qltyp = qlogicfas408_get_chip_type ( qbase , INT_TYPE ) ;
qinitid = host - > this_id ;
if ( qinitid < 0 )
qinitid = 7 ; /* if no ID, use 7 */
qlogicfas408_setup ( qbase , qinitid , INT_TYPE ) ;
hreg = scsi_host_alloc ( host , sizeof ( struct qlogicfas408_priv ) ) ;
if ( ! hreg )
goto err_release_mem ;
priv = get_priv_by_host ( hreg ) ;
hreg - > io_port = qbase ;
hreg - > n_io_port = 16 ;
hreg - > dma_channel = - 1 ;
if ( qlirq ! = - 1 )
hreg - > irq = qlirq ;
priv - > qbase = qbase ;
priv - > qlirq = qlirq ;
priv - > qinitid = qinitid ;
priv - > shost = hreg ;
priv - > int_type = INT_TYPE ;
sprintf ( priv - > qinfo ,
" Qlogicfas Driver version 0.46, chip %02X at %03X, IRQ %d, TPdma:%d " ,
qltyp , qbase , qlirq , QL_TURBO_PDMA ) ;
host - > name = qlogicfas_name ;
if ( request_irq ( qlirq , qlogicfas408_ihandl , 0 , qlogicfas_name , hreg ) )
goto free_scsi_host ;
if ( scsi_add_host ( hreg , NULL ) )
goto free_interrupt ;
scsi_scan_host ( hreg ) ;
return hreg ;
free_interrupt :
free_irq ( qlirq , hreg ) ;
free_scsi_host :
scsi_host_put ( hreg ) ;
err_release_mem :
release_region ( qbase , 0x10 ) ;
err :
return NULL ;
}
# define MAX_QLOGICFAS 8
static struct qlogicfas408_priv * cards ;
static int iobase [ MAX_QLOGICFAS ] ;
static int irq [ MAX_QLOGICFAS ] = { [ 0 . . . MAX_QLOGICFAS - 1 ] = - 1 } ;
module_param_array ( iobase , int , NULL , 0 ) ;
module_param_array ( irq , int , NULL , 0 ) ;
MODULE_PARM_DESC ( iobase , " I/O address " ) ;
MODULE_PARM_DESC ( irq , " IRQ " ) ;
2005-10-31 20:31:40 +03:00
static int __devinit qlogicfas_detect ( struct scsi_host_template * sht )
2005-04-17 02:20:36 +04:00
{
struct Scsi_Host * shost ;
struct qlogicfas408_priv * priv ;
int num ;
for ( num = 0 ; num < MAX_QLOGICFAS ; num + + ) {
shost = __qlogicfas_detect ( sht , iobase [ num ] , irq [ num ] ) ;
if ( shost = = NULL ) {
/* no more devices */
break ;
}
priv = get_priv_by_host ( shost ) ;
priv - > next = cards ;
cards = priv ;
}
return num ;
}
static int qlogicfas_release ( struct Scsi_Host * shost )
{
struct qlogicfas408_priv * priv = get_priv_by_host ( shost ) ;
2007-08-15 22:56:59 +04:00
scsi_remove_host ( shost ) ;
2005-04-17 02:20:36 +04:00
if ( shost - > irq ) {
qlogicfas408_disable_ints ( priv ) ;
free_irq ( shost - > irq , shost ) ;
}
if ( shost - > dma_channel ! = 0xff )
free_dma ( shost - > dma_channel ) ;
if ( shost - > io_port & & shost - > n_io_port )
release_region ( shost - > io_port , shost - > n_io_port ) ;
scsi_host_put ( shost ) ;
return 0 ;
}
/*
* The driver template is also needed for PCMCIA
*/
2005-10-31 20:31:40 +03:00
static struct scsi_host_template qlogicfas_driver_template = {
2005-04-17 02:20:36 +04:00
. module = THIS_MODULE ,
. name = qlogicfas_name ,
. proc_name = qlogicfas_name ,
. info = qlogicfas408_info ,
. queuecommand = qlogicfas408_queuecommand ,
. eh_abort_handler = qlogicfas408_abort ,
. eh_bus_reset_handler = qlogicfas408_bus_reset ,
. bios_param = qlogicfas408_biosparam ,
. can_queue = 1 ,
. this_id = - 1 ,
. sg_tablesize = SG_ALL ,
. cmd_per_lun = 1 ,
. use_clustering = DISABLE_CLUSTERING ,
} ;
static __init int qlogicfas_init ( void )
{
if ( ! qlogicfas_detect ( & qlogicfas_driver_template ) ) {
/* no cards found */
printk ( KERN_INFO " %s: no cards were found, please specify "
" I/O address and IRQ using iobase= and irq= "
" options " , qlogicfas_name ) ;
return - ENODEV ;
}
return 0 ;
}
static __exit void qlogicfas_exit ( void )
{
struct qlogicfas408_priv * priv ;
for ( priv = cards ; priv ! = NULL ; priv = priv - > next )
qlogicfas_release ( priv - > shost ) ;
}
MODULE_AUTHOR ( " Tom Zerucha, Michael Griffith " ) ;
MODULE_DESCRIPTION ( " Driver for the Qlogic FAS408 based ISA card " ) ;
MODULE_LICENSE ( " GPL " ) ;
module_init ( qlogicfas_init ) ;
module_exit ( qlogicfas_exit ) ;