2005-04-17 02:20:36 +04:00
/*
* Initio A100 device driver for Linux .
*
* Copyright ( c ) 1994 - 1998 Initio Corporation
* Copyright ( c ) 2003 - 2004 Christoph Hellwig
* All rights reserved .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 , or ( at your option )
* any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; see the file COPYING . If not , write to
* the Free Software Foundation , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions
* are met :
* 1. Redistributions of source code must retain the above copyright
* notice , this list of conditions , and the following disclaimer ,
* without modification , immediately at the beginning of the file .
* 2. Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the distribution .
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission .
*
* Where this Software is combined with software released under the terms of
* the GNU General Public License ( " GPL " ) and the terms of the GPL would require the
* combined work to also be released under the terms of the GPL , the terms
* and conditions of this License will apply in addition to those of the
* GPL with the exception of any terms or conditions of this License that
* conflict with , or are expressly prohibited by , the GPL .
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ` ` AS IS ' ' AND
* ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED . IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL
* DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION )
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT
* LIABILITY , OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE .
*/
/*
* Revision History :
* 07 / 02 / 98 hl - v .91 n Initial drivers .
* 09 / 14 / 98 hl - v1 .01 Support new Kernel .
* 09 / 22 / 98 hl - v1 .01 a Support reset .
* 09 / 24 / 98 hl - v1 .01 b Fixed reset .
* 10 / 05 / 98 hl - v1 .02 split the source code and release .
* 12 / 19 / 98 bv - v1 .02 a Use spinlocks for 2.1 .95 and up
* 01 / 31 / 99 bv - v1 .02 b Use mdelay instead of waitForPause
* 08 / 08 / 99 bv - v1 .02 c Use waitForPause again .
* 06 / 25 / 02 Doug Ledford < dledford @ redhat . com > - v1 .02 d
* - Remove limit on number of controllers
* - Port to DMA mapping API
* - Clean up interrupt handler registration
* - Fix memory leaks
* - Fix allocation of scsi host structs and private data
* 11 / 18 / 03 Christoph Hellwig < hch @ lst . de >
* - Port to new probing API
* - Fix some more leaks in init failure cases
* 9 / 28 / 04 Christoph Hellwig < hch @ lst . de >
* - merge the two source files
* - remove internal queueing code
*/
# include <linux/module.h>
# include <linux/errno.h>
# include <linux/delay.h>
# include <linux/interrupt.h>
# include <linux/pci.h>
# include <linux/init.h>
# include <linux/blkdev.h>
# include <linux/spinlock.h>
# include <linux/kernel.h>
# include <linux/string.h>
# include <linux/ioport.h>
# include <linux/slab.h>
2006-03-28 13:56:48 +04:00
# include <linux/dma-mapping.h>
2005-04-17 02:20:36 +04:00
# include <asm/io.h>
# include <asm/irq.h>
# include <scsi/scsi.h>
# include <scsi/scsi_cmnd.h>
# include <scsi/scsi_device.h>
# include <scsi/scsi_host.h>
# include "a100u2w.h"
# define JIFFIES_TO_MS(t) ((t) * 1000 / HZ)
# define MS_TO_JIFFIES(j) ((j * HZ) / 1000)
static ORC_SCB * orc_alloc_scb ( ORC_HCS * hcsp ) ;
static void inia100SCBPost ( BYTE * pHcb , BYTE * pScb ) ;
static NVRAM nvram , * nvramp = & nvram ;
static UCHAR dftNvRam [ 64 ] =
{
/*----------header -------------*/
0x01 , /* 0x00: Sub System Vendor ID 0 */
0x11 , /* 0x01: Sub System Vendor ID 1 */
0x60 , /* 0x02: Sub System ID 0 */
0x10 , /* 0x03: Sub System ID 1 */
0x00 , /* 0x04: SubClass */
0x01 , /* 0x05: Vendor ID 0 */
0x11 , /* 0x06: Vendor ID 1 */
0x60 , /* 0x07: Device ID 0 */
0x10 , /* 0x08: Device ID 1 */
0x00 , /* 0x09: Reserved */
0x00 , /* 0x0A: Reserved */
0x01 , /* 0x0B: Revision of Data Structure */
/* -- Host Adapter Structure --- */
0x01 , /* 0x0C: Number Of SCSI Channel */
0x01 , /* 0x0D: BIOS Configuration 1 */
0x00 , /* 0x0E: BIOS Configuration 2 */
0x00 , /* 0x0F: BIOS Configuration 3 */
/* --- SCSI Channel 0 Configuration --- */
0x07 , /* 0x10: H/A ID */
0x83 , /* 0x11: Channel Configuration */
0x20 , /* 0x12: MAX TAG per target */
0x0A , /* 0x13: SCSI Reset Recovering time */
0x00 , /* 0x14: Channel Configuration4 */
0x00 , /* 0x15: Channel Configuration5 */
/* SCSI Channel 0 Target Configuration */
/* 0x16-0x25 */
0xC8 , 0xC8 , 0xC8 , 0xC8 , 0xC8 , 0xC8 , 0xC8 , 0xC8 ,
0xC8 , 0xC8 , 0xC8 , 0xC8 , 0xC8 , 0xC8 , 0xC8 , 0xC8 ,
/* --- SCSI Channel 1 Configuration --- */
0x07 , /* 0x26: H/A ID */
0x83 , /* 0x27: Channel Configuration */
0x20 , /* 0x28: MAX TAG per target */
0x0A , /* 0x29: SCSI Reset Recovering time */
0x00 , /* 0x2A: Channel Configuration4 */
0x00 , /* 0x2B: Channel Configuration5 */
/* SCSI Channel 1 Target Configuration */
/* 0x2C-0x3B */
0xC8 , 0xC8 , 0xC8 , 0xC8 , 0xC8 , 0xC8 , 0xC8 , 0xC8 ,
0xC8 , 0xC8 , 0xC8 , 0xC8 , 0xC8 , 0xC8 , 0xC8 , 0xC8 ,
0x00 , /* 0x3C: Reserved */
0x00 , /* 0x3D: Reserved */
0x00 , /* 0x3E: Reserved */
0x00 /* 0x3F: Checksum */
} ;
/***************************************************************************/
static void waitForPause ( unsigned amount )
{
ULONG the_time = jiffies + MS_TO_JIFFIES ( amount ) ;
while ( time_before_eq ( jiffies , the_time ) )
cpu_relax ( ) ;
}
/***************************************************************************/
static UCHAR waitChipReady ( ORC_HCS * hcsp )
{
int i ;
for ( i = 0 ; i < 10 ; i + + ) { /* Wait 1 second for report timeout */
if ( ORC_RD ( hcsp - > HCS_Base , ORC_HCTRL ) & HOSTSTOP ) /* Wait HOSTSTOP set */
return 1 ;
waitForPause ( 100 ) ; /* wait 100ms before try again */
}
return 0 ;
}
/***************************************************************************/
static UCHAR waitFWReady ( ORC_HCS * hcsp )
{
int i ;
for ( i = 0 ; i < 10 ; i + + ) { /* Wait 1 second for report timeout */
if ( ORC_RD ( hcsp - > HCS_Base , ORC_HSTUS ) & RREADY ) /* Wait READY set */
return 1 ;
waitForPause ( 100 ) ; /* wait 100ms before try again */
}
return 0 ;
}
/***************************************************************************/
static UCHAR waitSCSIRSTdone ( ORC_HCS * hcsp )
{
int i ;
for ( i = 0 ; i < 10 ; i + + ) { /* Wait 1 second for report timeout */
if ( ! ( ORC_RD ( hcsp - > HCS_Base , ORC_HCTRL ) & SCSIRST ) ) /* Wait SCSIRST done */
return 1 ;
waitForPause ( 100 ) ; /* wait 100ms before try again */
}
return 0 ;
}
/***************************************************************************/
static UCHAR waitHDOoff ( ORC_HCS * hcsp )
{
int i ;
for ( i = 0 ; i < 10 ; i + + ) { /* Wait 1 second for report timeout */
if ( ! ( ORC_RD ( hcsp - > HCS_Base , ORC_HCTRL ) & HDO ) ) /* Wait HDO off */
return 1 ;
waitForPause ( 100 ) ; /* wait 100ms before try again */
}
return 0 ;
}
/***************************************************************************/
static UCHAR waitHDIset ( ORC_HCS * hcsp , UCHAR * pData )
{
int i ;
for ( i = 0 ; i < 10 ; i + + ) { /* Wait 1 second for report timeout */
if ( ( * pData = ORC_RD ( hcsp - > HCS_Base , ORC_HSTUS ) ) & HDI )
return 1 ; /* Wait HDI set */
waitForPause ( 100 ) ; /* wait 100ms before try again */
}
return 0 ;
}
/***************************************************************************/
static unsigned short get_FW_version ( ORC_HCS * hcsp )
{
UCHAR bData ;
union {
unsigned short sVersion ;
unsigned char cVersion [ 2 ] ;
} Version ;
ORC_WR ( hcsp - > HCS_Base + ORC_HDATA , ORC_CMD_VERSION ) ;
ORC_WR ( hcsp - > HCS_Base + ORC_HCTRL , HDO ) ;
if ( waitHDOoff ( hcsp ) = = 0 ) /* Wait HDO off */
return 0 ;
if ( waitHDIset ( hcsp , & bData ) = = 0 ) /* Wait HDI set */
return 0 ;
Version . cVersion [ 0 ] = ORC_RD ( hcsp - > HCS_Base , ORC_HDATA ) ;
ORC_WR ( hcsp - > HCS_Base + ORC_HSTUS , bData ) ; /* Clear HDI */
if ( waitHDIset ( hcsp , & bData ) = = 0 ) /* Wait HDI set */
return 0 ;
Version . cVersion [ 1 ] = ORC_RD ( hcsp - > HCS_Base , ORC_HDATA ) ;
ORC_WR ( hcsp - > HCS_Base + ORC_HSTUS , bData ) ; /* Clear HDI */
return ( Version . sVersion ) ;
}
/***************************************************************************/
static UCHAR set_NVRAM ( ORC_HCS * hcsp , unsigned char address , unsigned char value )
{
ORC_WR ( hcsp - > HCS_Base + ORC_HDATA , ORC_CMD_SET_NVM ) ; /* Write command */
ORC_WR ( hcsp - > HCS_Base + ORC_HCTRL , HDO ) ;
if ( waitHDOoff ( hcsp ) = = 0 ) /* Wait HDO off */
return 0 ;
ORC_WR ( hcsp - > HCS_Base + ORC_HDATA , address ) ; /* Write address */
ORC_WR ( hcsp - > HCS_Base + ORC_HCTRL , HDO ) ;
if ( waitHDOoff ( hcsp ) = = 0 ) /* Wait HDO off */
return 0 ;
ORC_WR ( hcsp - > HCS_Base + ORC_HDATA , value ) ; /* Write value */
ORC_WR ( hcsp - > HCS_Base + ORC_HCTRL , HDO ) ;
if ( waitHDOoff ( hcsp ) = = 0 ) /* Wait HDO off */
return 0 ;
return 1 ;
}
/***************************************************************************/
static UCHAR get_NVRAM ( ORC_HCS * hcsp , unsigned char address , unsigned char * pDataIn )
{
unsigned char bData ;
ORC_WR ( hcsp - > HCS_Base + ORC_HDATA , ORC_CMD_GET_NVM ) ; /* Write command */
ORC_WR ( hcsp - > HCS_Base + ORC_HCTRL , HDO ) ;
if ( waitHDOoff ( hcsp ) = = 0 ) /* Wait HDO off */
return 0 ;
ORC_WR ( hcsp - > HCS_Base + ORC_HDATA , address ) ; /* Write address */
ORC_WR ( hcsp - > HCS_Base + ORC_HCTRL , HDO ) ;
if ( waitHDOoff ( hcsp ) = = 0 ) /* Wait HDO off */
return 0 ;
if ( waitHDIset ( hcsp , & bData ) = = 0 ) /* Wait HDI set */
return 0 ;
* pDataIn = ORC_RD ( hcsp - > HCS_Base , ORC_HDATA ) ;
ORC_WR ( hcsp - > HCS_Base + ORC_HSTUS , bData ) ; /* Clear HDI */
return 1 ;
}
/***************************************************************************/
static void orc_exec_scb ( ORC_HCS * hcsp , ORC_SCB * scbp )
{
scbp - > SCB_Status = ORCSCB_POST ;
ORC_WR ( hcsp - > HCS_Base + ORC_PQUEUE , scbp - > SCB_ScbIdx ) ;
return ;
}
/***********************************************************************
Read SCSI H / A configuration parameters from serial EEPROM
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int se2_rd_all ( ORC_HCS * hcsp )
{
int i ;
UCHAR * np , chksum = 0 ;
np = ( UCHAR * ) nvramp ;
for ( i = 0 ; i < 64 ; i + + , np + + ) { /* <01> */
if ( get_NVRAM ( hcsp , ( unsigned char ) i , np ) = = 0 )
return - 1 ;
// *np++ = get_NVRAM(hcsp, (unsigned char ) i);
}
/*------ Is ckecksum ok ? ------*/
np = ( UCHAR * ) nvramp ;
for ( i = 0 ; i < 63 ; i + + )
chksum + = * np + + ;
if ( nvramp - > CheckSum ! = ( UCHAR ) chksum )
return - 1 ;
return 1 ;
}
/************************************************************************
Update SCSI H / A configuration parameters from serial EEPROM
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void se2_update_all ( ORC_HCS * hcsp )
{ /* setup default pattern */
int i ;
UCHAR * np , * np1 , chksum = 0 ;
/* Calculate checksum first */
np = ( UCHAR * ) dftNvRam ;
for ( i = 0 ; i < 63 ; i + + )
chksum + = * np + + ;
* np = chksum ;
np = ( UCHAR * ) dftNvRam ;
np1 = ( UCHAR * ) nvramp ;
for ( i = 0 ; i < 64 ; i + + , np + + , np1 + + ) {
if ( * np ! = * np1 ) {
set_NVRAM ( hcsp , ( unsigned char ) i , * np ) ;
}
}
return ;
}
/*************************************************************************
Function name : read_eeprom
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void read_eeprom ( ORC_HCS * hcsp )
{
if ( se2_rd_all ( hcsp ) ! = 1 ) {
se2_update_all ( hcsp ) ; /* setup default pattern */
se2_rd_all ( hcsp ) ; /* load again */
}
}
/***************************************************************************/
static UCHAR load_FW ( ORC_HCS * hcsp )
{
U32 dData ;
USHORT wBIOSAddress ;
USHORT i ;
UCHAR * pData , bData ;
bData = ORC_RD ( hcsp - > HCS_Base , ORC_GCFG ) ;
ORC_WR ( hcsp - > HCS_Base + ORC_GCFG , bData | EEPRG ) ; /* Enable EEPROM programming */
ORC_WR ( hcsp - > HCS_Base + ORC_EBIOSADR2 , 0x00 ) ;
ORC_WRSHORT ( hcsp - > HCS_Base + ORC_EBIOSADR0 , 0x00 ) ;
if ( ORC_RD ( hcsp - > HCS_Base , ORC_EBIOSDATA ) ! = 0x55 ) {
ORC_WR ( hcsp - > HCS_Base + ORC_GCFG , bData ) ; /* Disable EEPROM programming */
return 0 ;
}
ORC_WRSHORT ( hcsp - > HCS_Base + ORC_EBIOSADR0 , 0x01 ) ;
if ( ORC_RD ( hcsp - > HCS_Base , ORC_EBIOSDATA ) ! = 0xAA ) {
ORC_WR ( hcsp - > HCS_Base + ORC_GCFG , bData ) ; /* Disable EEPROM programming */
return 0 ;
}
ORC_WR ( hcsp - > HCS_Base + ORC_RISCCTL , PRGMRST | DOWNLOAD ) ; /* Enable SRAM programming */
pData = ( UCHAR * ) & dData ;
dData = 0 ; /* Initial FW address to 0 */
ORC_WRSHORT ( hcsp - > HCS_Base + ORC_EBIOSADR0 , 0x10 ) ;
* pData = ORC_RD ( hcsp - > HCS_Base , ORC_EBIOSDATA ) ; /* Read from BIOS */
ORC_WRSHORT ( hcsp - > HCS_Base + ORC_EBIOSADR0 , 0x11 ) ;
* ( pData + 1 ) = ORC_RD ( hcsp - > HCS_Base , ORC_EBIOSDATA ) ; /* Read from BIOS */
ORC_WRSHORT ( hcsp - > HCS_Base + ORC_EBIOSADR0 , 0x12 ) ;
* ( pData + 2 ) = ORC_RD ( hcsp - > HCS_Base , ORC_EBIOSDATA ) ; /* Read from BIOS */
ORC_WR ( hcsp - > HCS_Base + ORC_EBIOSADR2 , * ( pData + 2 ) ) ;
ORC_WRLONG ( hcsp - > HCS_Base + ORC_FWBASEADR , dData ) ; /* Write FW address */
wBIOSAddress = ( USHORT ) dData ; /* FW code locate at BIOS address + ? */
for ( i = 0 , pData = ( UCHAR * ) & dData ; /* Download the code */
i < 0x1000 ; /* Firmware code size = 4K */
i + + , wBIOSAddress + + ) {
ORC_WRSHORT ( hcsp - > HCS_Base + ORC_EBIOSADR0 , wBIOSAddress ) ;
* pData + + = ORC_RD ( hcsp - > HCS_Base , ORC_EBIOSDATA ) ; /* Read from BIOS */
if ( ( i % 4 ) = = 3 ) {
ORC_WRLONG ( hcsp - > HCS_Base + ORC_RISCRAM , dData ) ; /* Write every 4 bytes */
pData = ( UCHAR * ) & dData ;
}
}
ORC_WR ( hcsp - > HCS_Base + ORC_RISCCTL , PRGMRST | DOWNLOAD ) ; /* Reset program count 0 */
wBIOSAddress - = 0x1000 ; /* Reset the BIOS adddress */
for ( i = 0 , pData = ( UCHAR * ) & dData ; /* Check the code */
i < 0x1000 ; /* Firmware code size = 4K */
i + + , wBIOSAddress + + ) {
ORC_WRSHORT ( hcsp - > HCS_Base + ORC_EBIOSADR0 , wBIOSAddress ) ;
* pData + + = ORC_RD ( hcsp - > HCS_Base , ORC_EBIOSDATA ) ; /* Read from BIOS */
if ( ( i % 4 ) = = 3 ) {
if ( ORC_RDLONG ( hcsp - > HCS_Base , ORC_RISCRAM ) ! = dData ) {
ORC_WR ( hcsp - > HCS_Base + ORC_RISCCTL , PRGMRST ) ; /* Reset program to 0 */
ORC_WR ( hcsp - > HCS_Base + ORC_GCFG , bData ) ; /*Disable EEPROM programming */
return 0 ;
}
pData = ( UCHAR * ) & dData ;
}
}
ORC_WR ( hcsp - > HCS_Base + ORC_RISCCTL , PRGMRST ) ; /* Reset program to 0 */
ORC_WR ( hcsp - > HCS_Base + ORC_GCFG , bData ) ; /* Disable EEPROM programming */
return 1 ;
}
/***************************************************************************/
static void setup_SCBs ( ORC_HCS * hcsp )
{
ORC_SCB * pVirScb ;
int i ;
ESCB * pVirEscb ;
dma_addr_t pPhysEscb ;
/* Setup SCB HCS_Base and SCB Size registers */
ORC_WR ( hcsp - > HCS_Base + ORC_SCBSIZE , ORC_MAXQUEUE ) ; /* Total number of SCBs */
/* SCB HCS_Base address 0 */
ORC_WRLONG ( hcsp - > HCS_Base + ORC_SCBBASE0 , hcsp - > HCS_physScbArray ) ;
/* SCB HCS_Base address 1 */
ORC_WRLONG ( hcsp - > HCS_Base + ORC_SCBBASE1 , hcsp - > HCS_physScbArray ) ;
/* setup scatter list address with one buffer */
pVirScb = hcsp - > HCS_virScbArray ;
pVirEscb = hcsp - > HCS_virEscbArray ;
for ( i = 0 ; i < ORC_MAXQUEUE ; i + + ) {
pPhysEscb = ( hcsp - > HCS_physEscbArray + ( sizeof ( ESCB ) * i ) ) ;
pVirScb - > SCB_SGPAddr = ( U32 ) pPhysEscb ;
pVirScb - > SCB_SensePAddr = ( U32 ) pPhysEscb ;
pVirScb - > SCB_EScb = pVirEscb ;
pVirScb - > SCB_ScbIdx = i ;
pVirScb + + ;
pVirEscb + + ;
}
return ;
}
/***************************************************************************/
static void initAFlag ( ORC_HCS * hcsp )
{
UCHAR i , j ;
for ( i = 0 ; i < MAX_CHANNELS ; i + + ) {
for ( j = 0 ; j < 8 ; j + + ) {
hcsp - > BitAllocFlag [ i ] [ j ] = 0xffffffff ;
}
}
}
/***************************************************************************/
static int init_orchid ( ORC_HCS * hcsp )
{
UBYTE * readBytep ;
USHORT revision ;
UCHAR i ;
initAFlag ( hcsp ) ;
ORC_WR ( hcsp - > HCS_Base + ORC_GIMSK , 0xFF ) ; /* Disable all interrupt */
if ( ORC_RD ( hcsp - > HCS_Base , ORC_HSTUS ) & RREADY ) { /* Orchid is ready */
revision = get_FW_version ( hcsp ) ;
if ( revision = = 0xFFFF ) {
ORC_WR ( hcsp - > HCS_Base + ORC_HCTRL , DEVRST ) ; /* Reset Host Adapter */
if ( waitChipReady ( hcsp ) = = 0 )
return ( - 1 ) ;
load_FW ( hcsp ) ; /* Download FW */
setup_SCBs ( hcsp ) ; /* Setup SCB HCS_Base and SCB Size registers */
ORC_WR ( hcsp - > HCS_Base + ORC_HCTRL , 0 ) ; /* clear HOSTSTOP */
if ( waitFWReady ( hcsp ) = = 0 )
return ( - 1 ) ;
/* Wait for firmware ready */
} else {
setup_SCBs ( hcsp ) ; /* Setup SCB HCS_Base and SCB Size registers */
}
} else { /* Orchid is not Ready */
ORC_WR ( hcsp - > HCS_Base + ORC_HCTRL , DEVRST ) ; /* Reset Host Adapter */
if ( waitChipReady ( hcsp ) = = 0 )
return ( - 1 ) ;
load_FW ( hcsp ) ; /* Download FW */
setup_SCBs ( hcsp ) ; /* Setup SCB HCS_Base and SCB Size registers */
ORC_WR ( hcsp - > HCS_Base + ORC_HCTRL , HDO ) ; /* Do Hardware Reset & */
/* clear HOSTSTOP */
if ( waitFWReady ( hcsp ) = = 0 ) /* Wait for firmware ready */
return ( - 1 ) ;
}
/*------------- get serial EEProm settting -------*/
read_eeprom ( hcsp ) ;
if ( nvramp - > Revision ! = 1 )
return ( - 1 ) ;
hcsp - > HCS_SCSI_ID = nvramp - > SCSI0Id ;
hcsp - > HCS_BIOS = nvramp - > BIOSConfig1 ;
hcsp - > HCS_MaxTar = MAX_TARGETS ;
readBytep = ( UCHAR * ) & ( nvramp - > Target00Config ) ;
for ( i = 0 ; i < 16 ; readBytep + + , i + + ) {
hcsp - > TargetFlag [ i ] = * readBytep ;
hcsp - > MaximumTags [ i ] = ORC_MAXTAGS ;
} /* for */
if ( nvramp - > SCSI0Config & NCC_BUSRESET ) { /* Reset SCSI bus */
hcsp - > HCS_Flags | = HCF_SCSI_RESET ;
}
ORC_WR ( hcsp - > HCS_Base + ORC_GIMSK , 0xFB ) ; /* enable RP FIFO interrupt */
return ( 0 ) ;
}
/*****************************************************************************
Function name : orc_reset_scsi_bus
Description : Reset registers , reset a hanging bus and
kill active and disconnected commands for target w / o soft reset
Input : pHCB - Pointer to host adapter structure
Output : None .
Return : pSRB - Pointer to SCSI request block .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int orc_reset_scsi_bus ( ORC_HCS * pHCB )
{ /* I need Host Control Block Information */
ULONG flags ;
spin_lock_irqsave ( & ( pHCB - > BitAllocFlagLock ) , flags ) ;
initAFlag ( pHCB ) ;
/* reset scsi bus */
ORC_WR ( pHCB - > HCS_Base + ORC_HCTRL , SCSIRST ) ;
if ( waitSCSIRSTdone ( pHCB ) = = 0 ) {
spin_unlock_irqrestore ( & ( pHCB - > BitAllocFlagLock ) , flags ) ;
return FAILED ;
} else {
spin_unlock_irqrestore ( & ( pHCB - > BitAllocFlagLock ) , flags ) ;
return SUCCESS ;
}
}
/*****************************************************************************
Function name : orc_device_reset
Description : Reset registers , reset a hanging bus and
kill active and disconnected commands for target w / o soft reset
Input : pHCB - Pointer to host adapter structure
Output : None .
Return : pSRB - Pointer to SCSI request block .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int orc_device_reset ( ORC_HCS * pHCB , struct scsi_cmnd * SCpnt , unsigned int target )
{ /* I need Host Control Block Information */
ORC_SCB * pScb ;
ESCB * pVirEscb ;
ORC_SCB * pVirScb ;
UCHAR i ;
ULONG flags ;
spin_lock_irqsave ( & ( pHCB - > BitAllocFlagLock ) , flags ) ;
pScb = ( ORC_SCB * ) NULL ;
pVirEscb = ( ESCB * ) NULL ;
/* setup scatter list address with one buffer */
pVirScb = pHCB - > HCS_virScbArray ;
initAFlag ( pHCB ) ;
/* device reset */
for ( i = 0 ; i < ORC_MAXQUEUE ; i + + ) {
pVirEscb = pVirScb - > SCB_EScb ;
if ( ( pVirScb - > SCB_Status ) & & ( pVirEscb - > SCB_Srb = = SCpnt ) )
break ;
pVirScb + + ;
}
if ( i = = ORC_MAXQUEUE ) {
printk ( " Unable to Reset - No SCB Found \n " ) ;
spin_unlock_irqrestore ( & ( pHCB - > BitAllocFlagLock ) , flags ) ;
return FAILED ;
}
if ( ( pScb = orc_alloc_scb ( pHCB ) ) = = NULL ) {
spin_unlock_irqrestore ( & ( pHCB - > BitAllocFlagLock ) , flags ) ;
return FAILED ;
}
pScb - > SCB_Opcode = ORC_BUSDEVRST ;
pScb - > SCB_Target = target ;
pScb - > SCB_HaStat = 0 ;
pScb - > SCB_TaStat = 0 ;
pScb - > SCB_Status = 0x0 ;
pScb - > SCB_Link = 0xFF ;
pScb - > SCB_Reserved0 = 0 ;
pScb - > SCB_Reserved1 = 0 ;
pScb - > SCB_XferLen = 0 ;
pScb - > SCB_SGLen = 0 ;
pVirEscb - > SCB_Srb = NULL ;
pVirEscb - > SCB_Srb = SCpnt ;
orc_exec_scb ( pHCB , pScb ) ; /* Start execute SCB */
spin_unlock_irqrestore ( & ( pHCB - > BitAllocFlagLock ) , flags ) ;
return SUCCESS ;
}
/***************************************************************************/
static ORC_SCB * __orc_alloc_scb ( ORC_HCS * hcsp )
{
ORC_SCB * pTmpScb ;
UCHAR Ch ;
ULONG idx ;
UCHAR index ;
UCHAR i ;
Ch = hcsp - > HCS_Index ;
for ( i = 0 ; i < 8 ; i + + ) {
for ( index = 0 ; index < 32 ; index + + ) {
if ( ( hcsp - > BitAllocFlag [ Ch ] [ i ] > > index ) & 0x01 ) {
hcsp - > BitAllocFlag [ Ch ] [ i ] & = ~ ( 1 < < index ) ;
break ;
}
}
idx = index + 32 * i ;
pTmpScb = ( ORC_SCB * ) ( ( ULONG ) hcsp - > HCS_virScbArray + ( idx * sizeof ( ORC_SCB ) ) ) ;
return ( pTmpScb ) ;
}
return ( NULL ) ;
}
static ORC_SCB * orc_alloc_scb ( ORC_HCS * hcsp )
{
ORC_SCB * pTmpScb ;
ULONG flags ;
spin_lock_irqsave ( & ( hcsp - > BitAllocFlagLock ) , flags ) ;
pTmpScb = __orc_alloc_scb ( hcsp ) ;
spin_unlock_irqrestore ( & ( hcsp - > BitAllocFlagLock ) , flags ) ;
return ( pTmpScb ) ;
}
/***************************************************************************/
static void orc_release_scb ( ORC_HCS * hcsp , ORC_SCB * scbp )
{
ULONG flags ;
UCHAR Index ;
UCHAR i ;
UCHAR Ch ;
spin_lock_irqsave ( & ( hcsp - > BitAllocFlagLock ) , flags ) ;
Ch = hcsp - > HCS_Index ;
Index = scbp - > SCB_ScbIdx ;
i = Index / 32 ;
Index % = 32 ;
hcsp - > BitAllocFlag [ Ch ] [ i ] | = ( 1 < < Index ) ;
spin_unlock_irqrestore ( & ( hcsp - > BitAllocFlagLock ) , flags ) ;
}
/*****************************************************************************
Function name : abort_SCB
Description : Abort a queued command .
( commands that are on the bus can ' t be aborted easily )
Input : pHCB - Pointer to host adapter structure
Output : None .
Return : pSRB - Pointer to SCSI request block .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int abort_SCB ( ORC_HCS * hcsp , ORC_SCB * pScb )
{
unsigned char bData , bStatus ;
ORC_WR ( hcsp - > HCS_Base + ORC_HDATA , ORC_CMD_ABORT_SCB ) ; /* Write command */
ORC_WR ( hcsp - > HCS_Base + ORC_HCTRL , HDO ) ;
if ( waitHDOoff ( hcsp ) = = 0 ) /* Wait HDO off */
return 0 ;
ORC_WR ( hcsp - > HCS_Base + ORC_HDATA , pScb - > SCB_ScbIdx ) ; /* Write address */
ORC_WR ( hcsp - > HCS_Base + ORC_HCTRL , HDO ) ;
if ( waitHDOoff ( hcsp ) = = 0 ) /* Wait HDO off */
return 0 ;
if ( waitHDIset ( hcsp , & bData ) = = 0 ) /* Wait HDI set */
return 0 ;
bStatus = ORC_RD ( hcsp - > HCS_Base , ORC_HDATA ) ;
ORC_WR ( hcsp - > HCS_Base + ORC_HSTUS , bData ) ; /* Clear HDI */
if ( bStatus = = 1 ) /* 0 - Successfully */
return 0 ; /* 1 - Fail */
return 1 ;
}
/*****************************************************************************
Function name : inia100_abort
Description : Abort a queued command .
( commands that are on the bus can ' t be aborted easily )
Input : pHCB - Pointer to host adapter structure
Output : None .
Return : pSRB - Pointer to SCSI request block .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int orc_abort_srb ( ORC_HCS * hcsp , struct scsi_cmnd * SCpnt )
{
ESCB * pVirEscb ;
ORC_SCB * pVirScb ;
UCHAR i ;
ULONG flags ;
spin_lock_irqsave ( & ( hcsp - > BitAllocFlagLock ) , flags ) ;
pVirScb = hcsp - > HCS_virScbArray ;
for ( i = 0 ; i < ORC_MAXQUEUE ; i + + , pVirScb + + ) {
pVirEscb = pVirScb - > SCB_EScb ;
if ( ( pVirScb - > SCB_Status ) & & ( pVirEscb - > SCB_Srb = = SCpnt ) ) {
if ( pVirScb - > SCB_TagMsg = = 0 ) {
spin_unlock_irqrestore ( & ( hcsp - > BitAllocFlagLock ) , flags ) ;
return FAILED ;
} else {
if ( abort_SCB ( hcsp , pVirScb ) ) {
pVirEscb - > SCB_Srb = NULL ;
spin_unlock_irqrestore ( & ( hcsp - > BitAllocFlagLock ) , flags ) ;
return SUCCESS ;
} else {
spin_unlock_irqrestore ( & ( hcsp - > BitAllocFlagLock ) , flags ) ;
return FAILED ;
}
}
}
}
spin_unlock_irqrestore ( & ( hcsp - > BitAllocFlagLock ) , flags ) ;
return FAILED ;
}
/***********************************************************************
Routine Description :
This is the interrupt service routine for the Orchid SCSI adapter .
It reads the interrupt register to determine if the adapter is indeed
the source of the interrupt and clears the interrupt at the device .
Arguments :
HwDeviceExtension - HBA miniport driver ' s adapter data storage
Return Value :
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void orc_interrupt (
ORC_HCS * hcsp
)
{
BYTE bScbIdx ;
ORC_SCB * pScb ;
if ( ORC_RD ( hcsp - > HCS_Base , ORC_RQUEUECNT ) = = 0 ) {
return ; // 0;
}
do {
bScbIdx = ORC_RD ( hcsp - > HCS_Base , ORC_RQUEUE ) ;
pScb = ( ORC_SCB * ) ( ( ULONG ) hcsp - > HCS_virScbArray + ( ULONG ) ( sizeof ( ORC_SCB ) * bScbIdx ) ) ;
pScb - > SCB_Status = 0x0 ;
inia100SCBPost ( ( BYTE * ) hcsp , ( BYTE * ) pScb ) ;
} while ( ORC_RD ( hcsp - > HCS_Base , ORC_RQUEUECNT ) ) ;
return ; //1;
} /* End of I1060Interrupt() */
/*****************************************************************************
Function name : inia100BuildSCB
Description :
Input : pHCB - Pointer to host adapter structure
Output : None .
Return : pSRB - Pointer to SCSI request block .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void inia100BuildSCB ( ORC_HCS * pHCB , ORC_SCB * pSCB , struct scsi_cmnd * SCpnt )
{ /* Create corresponding SCB */
struct scatterlist * pSrbSG ;
ORC_SG * pSG ; /* Pointer to SG list */
int i , count_sg ;
ESCB * pEScb ;
pEScb = pSCB - > SCB_EScb ;
pEScb - > SCB_Srb = SCpnt ;
pSG = NULL ;
pSCB - > SCB_Opcode = ORC_EXECSCSI ;
pSCB - > SCB_Flags = SCF_NO_DCHK ; /* Clear done bit */
pSCB - > SCB_Target = SCpnt - > device - > id ;
pSCB - > SCB_Lun = SCpnt - > device - > lun ;
pSCB - > SCB_Reserved0 = 0 ;
pSCB - > SCB_Reserved1 = 0 ;
pSCB - > SCB_SGLen = 0 ;
if ( ( pSCB - > SCB_XferLen = ( U32 ) SCpnt - > request_bufflen ) ) {
pSG = ( ORC_SG * ) & pEScb - > ESCB_SGList [ 0 ] ;
if ( SCpnt - > use_sg ) {
pSrbSG = ( struct scatterlist * ) SCpnt - > request_buffer ;
count_sg = pci_map_sg ( pHCB - > pdev , pSrbSG , SCpnt - > use_sg ,
SCpnt - > sc_data_direction ) ;
pSCB - > SCB_SGLen = ( U32 ) ( count_sg * 8 ) ;
for ( i = 0 ; i < count_sg ; i + + , pSG + + , pSrbSG + + ) {
pSG - > SG_Ptr = ( U32 ) sg_dma_address ( pSrbSG ) ;
pSG - > SG_Len = ( U32 ) sg_dma_len ( pSrbSG ) ;
}
} else if ( SCpnt - > request_bufflen ! = 0 ) { /* Non SG */
pSCB - > SCB_SGLen = 0x8 ;
SCpnt - > SCp . dma_handle = pci_map_single ( pHCB - > pdev ,
SCpnt - > request_buffer ,
SCpnt - > request_bufflen ,
SCpnt - > sc_data_direction ) ;
pSG - > SG_Ptr = ( U32 ) SCpnt - > SCp . dma_handle ;
pSG - > SG_Len = ( U32 ) SCpnt - > request_bufflen ;
} else {
pSCB - > SCB_SGLen = 0 ;
pSG - > SG_Ptr = 0 ;
pSG - > SG_Len = 0 ;
}
}
pSCB - > SCB_SGPAddr = ( U32 ) pSCB - > SCB_SensePAddr ;
pSCB - > SCB_HaStat = 0 ;
pSCB - > SCB_TaStat = 0 ;
pSCB - > SCB_Link = 0xFF ;
pSCB - > SCB_SenseLen = SENSE_SIZE ;
pSCB - > SCB_CDBLen = SCpnt - > cmd_len ;
if ( pSCB - > SCB_CDBLen > = IMAX_CDB ) {
printk ( " max cdb length= %x \b " , SCpnt - > cmd_len ) ;
pSCB - > SCB_CDBLen = IMAX_CDB ;
}
pSCB - > SCB_Ident = SCpnt - > device - > lun | DISC_ALLOW ;
if ( SCpnt - > device - > tagged_supported ) { /* Tag Support */
pSCB - > SCB_TagMsg = SIMPLE_QUEUE_TAG ; /* Do simple tag only */
} else {
pSCB - > SCB_TagMsg = 0 ; /* No tag support */
}
memcpy ( & pSCB - > SCB_CDB [ 0 ] , & SCpnt - > cmnd , pSCB - > SCB_CDBLen ) ;
return ;
}
/*****************************************************************************
Function name : inia100_queue
Description : Queue a command and setup interrupts for a free bus .
Input : pHCB - Pointer to host adapter structure
Output : None .
Return : pSRB - Pointer to SCSI request block .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int inia100_queue ( struct scsi_cmnd * SCpnt , void ( * done ) ( struct scsi_cmnd * ) )
{
register ORC_SCB * pSCB ;
ORC_HCS * pHCB ; /* Point to Host adapter control block */
pHCB = ( ORC_HCS * ) SCpnt - > device - > host - > hostdata ;
SCpnt - > scsi_done = done ;
/* Get free SCSI control block */
if ( ( pSCB = orc_alloc_scb ( pHCB ) ) = = NULL )
return SCSI_MLQUEUE_HOST_BUSY ;
inia100BuildSCB ( pHCB , pSCB , SCpnt ) ;
orc_exec_scb ( pHCB , pSCB ) ; /* Start execute SCB */
return ( 0 ) ;
}
/*****************************************************************************
Function name : inia100_abort
Description : Abort a queued command .
( commands that are on the bus can ' t be aborted easily )
Input : pHCB - Pointer to host adapter structure
Output : None .
Return : pSRB - Pointer to SCSI request block .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int inia100_abort ( struct scsi_cmnd * SCpnt )
{
ORC_HCS * hcsp ;
hcsp = ( ORC_HCS * ) SCpnt - > device - > host - > hostdata ;
return orc_abort_srb ( hcsp , SCpnt ) ;
}
/*****************************************************************************
Function name : inia100_reset
Description : Reset registers , reset a hanging bus and
kill active and disconnected commands for target w / o soft reset
Input : pHCB - Pointer to host adapter structure
Output : None .
Return : pSRB - Pointer to SCSI request block .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int inia100_bus_reset ( struct scsi_cmnd * SCpnt )
{ /* I need Host Control Block Information */
ORC_HCS * pHCB ;
pHCB = ( ORC_HCS * ) SCpnt - > device - > host - > hostdata ;
return orc_reset_scsi_bus ( pHCB ) ;
}
/*****************************************************************************
Function name : inia100_device_reset
Description : Reset the device
Input : pHCB - Pointer to host adapter structure
Output : None .
Return : pSRB - Pointer to SCSI request block .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int inia100_device_reset ( struct scsi_cmnd * SCpnt )
{ /* I need Host Control Block Information */
ORC_HCS * pHCB ;
pHCB = ( ORC_HCS * ) SCpnt - > device - > host - > hostdata ;
2005-10-25 02:05:09 +04:00
return orc_device_reset ( pHCB , SCpnt , scmd_id ( SCpnt ) ) ;
2005-04-17 02:20:36 +04:00
}
/*****************************************************************************
Function name : inia100SCBPost
Description : This is callback routine be called when orc finish one
SCSI command .
Input : pHCB - Pointer to host adapter control block .
pSCB - Pointer to SCSI control block .
Output : None .
Return : None .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void inia100SCBPost ( BYTE * pHcb , BYTE * pScb )
{
struct scsi_cmnd * pSRB ; /* Pointer to SCSI request block */
ORC_HCS * pHCB ;
ORC_SCB * pSCB ;
ESCB * pEScb ;
pHCB = ( ORC_HCS * ) pHcb ;
pSCB = ( ORC_SCB * ) pScb ;
pEScb = pSCB - > SCB_EScb ;
if ( ( pSRB = ( struct scsi_cmnd * ) pEScb - > SCB_Srb ) = = 0 ) {
printk ( " inia100SCBPost: SRB pointer is empty \n " ) ;
orc_release_scb ( pHCB , pSCB ) ; /* Release SCB for current channel */
return ;
}
pEScb - > SCB_Srb = NULL ;
switch ( pSCB - > SCB_HaStat ) {
case 0x0 :
case 0xa : /* Linked command complete without error and linked normally */
case 0xb : /* Linked command complete without error interrupt generated */
pSCB - > SCB_HaStat = 0 ;
break ;
case 0x11 : /* Selection time out-The initiator selection or target
reselection was not complete within the SCSI Time out period */
pSCB - > SCB_HaStat = DID_TIME_OUT ;
break ;
case 0x14 : /* Target bus phase sequence failure-An invalid bus phase or bus
phase sequence was requested by the target . The host adapter
will generate a SCSI Reset Condition , notifying the host with
a SCRD interrupt */
pSCB - > SCB_HaStat = DID_RESET ;
break ;
case 0x1a : /* SCB Aborted. 07/21/98 */
pSCB - > SCB_HaStat = DID_ABORT ;
break ;
case 0x12 : /* Data overrun/underrun-The target attempted to transfer more data
than was allocated by the Data Length field or the sum of the
Scatter / Gather Data Length fields . */
case 0x13 : /* Unexpected bus free-The target dropped the SCSI BSY at an unexpected time. */
case 0x16 : /* Invalid CCB Operation Code-The first byte of the CCB was invalid. */
default :
printk ( " inia100: %x %x \n " , pSCB - > SCB_HaStat , pSCB - > SCB_TaStat ) ;
pSCB - > SCB_HaStat = DID_ERROR ; /* Couldn't find any better */
break ;
}
if ( pSCB - > SCB_TaStat = = 2 ) { /* Check condition */
memcpy ( ( unsigned char * ) & pSRB - > sense_buffer [ 0 ] ,
( unsigned char * ) & pEScb - > ESCB_SGList [ 0 ] , SENSE_SIZE ) ;
}
pSRB - > result = pSCB - > SCB_TaStat | ( pSCB - > SCB_HaStat < < 16 ) ;
if ( pSRB - > use_sg ) {
pci_unmap_sg ( pHCB - > pdev ,
( struct scatterlist * ) pSRB - > request_buffer ,
pSRB - > use_sg , pSRB - > sc_data_direction ) ;
} else if ( pSRB - > request_bufflen ! = 0 ) {
pci_unmap_single ( pHCB - > pdev , pSRB - > SCp . dma_handle ,
pSRB - > request_bufflen ,
pSRB - > sc_data_direction ) ;
}
pSRB - > scsi_done ( pSRB ) ; /* Notify system DONE */
orc_release_scb ( pHCB , pSCB ) ; /* Release SCB for current channel */
}
/*
* Interrupt handler ( main routine of the driver )
*/
static irqreturn_t inia100_intr ( int irqno , void * devid , struct pt_regs * regs )
{
struct Scsi_Host * host = ( struct Scsi_Host * ) devid ;
ORC_HCS * pHcb = ( ORC_HCS * ) host - > hostdata ;
unsigned long flags ;
spin_lock_irqsave ( host - > host_lock , flags ) ;
orc_interrupt ( pHcb ) ;
spin_unlock_irqrestore ( host - > host_lock , flags ) ;
return IRQ_HANDLED ;
}
static struct scsi_host_template inia100_template = {
. proc_name = " inia100 " ,
. name = inia100_REVID ,
. queuecommand = inia100_queue ,
. eh_abort_handler = inia100_abort ,
. eh_bus_reset_handler = inia100_bus_reset ,
. eh_device_reset_handler = inia100_device_reset ,
. can_queue = 1 ,
. this_id = 1 ,
. sg_tablesize = SG_ALL ,
. cmd_per_lun = 1 ,
. use_clustering = ENABLE_CLUSTERING ,
} ;
static int __devinit inia100_probe_one ( struct pci_dev * pdev ,
const struct pci_device_id * id )
{
struct Scsi_Host * shost ;
ORC_HCS * pHCB ;
unsigned long port , bios ;
int error = - ENODEV ;
u32 sz ;
unsigned long dBiosAdr ;
char * pbBiosAdr ;
if ( pci_enable_device ( pdev ) )
goto out ;
2006-03-28 13:56:48 +04:00
if ( pci_set_dma_mask ( pdev , DMA_32BIT_MASK ) ) {
2005-04-17 02:20:36 +04:00
printk ( KERN_WARNING " Unable to set 32bit DMA "
" on inia100 adapter, ignoring. \n " ) ;
goto out_disable_device ;
}
pci_set_master ( pdev ) ;
port = pci_resource_start ( pdev , 0 ) ;
if ( ! request_region ( port , 256 , " inia100 " ) ) {
printk ( KERN_WARNING " inia100: io port 0x%lx, is busy. \n " , port ) ;
goto out_disable_device ;
}
/* <02> read from base address + 0x50 offset to get the bios balue. */
bios = ORC_RDWORD ( port , 0x50 ) ;
shost = scsi_host_alloc ( & inia100_template , sizeof ( ORC_HCS ) ) ;
if ( ! shost )
goto out_release_region ;
pHCB = ( ORC_HCS * ) shost - > hostdata ;
pHCB - > pdev = pdev ;
pHCB - > HCS_Base = port ;
pHCB - > HCS_BIOS = bios ;
spin_lock_init ( & pHCB - > BitAllocFlagLock ) ;
/* Get total memory needed for SCB */
sz = ORC_MAXQUEUE * sizeof ( ORC_SCB ) ;
pHCB - > HCS_virScbArray = pci_alloc_consistent ( pdev , sz ,
& pHCB - > HCS_physScbArray ) ;
if ( ! pHCB - > HCS_virScbArray ) {
printk ( " inia100: SCB memory allocation error \n " ) ;
goto out_host_put ;
}
memset ( pHCB - > HCS_virScbArray , 0 , sz ) ;
/* Get total memory needed for ESCB */
sz = ORC_MAXQUEUE * sizeof ( ESCB ) ;
pHCB - > HCS_virEscbArray = pci_alloc_consistent ( pdev , sz ,
& pHCB - > HCS_physEscbArray ) ;
if ( ! pHCB - > HCS_virEscbArray ) {
printk ( " inia100: ESCB memory allocation error \n " ) ;
goto out_free_scb_array ;
}
memset ( pHCB - > HCS_virEscbArray , 0 , sz ) ;
dBiosAdr = pHCB - > HCS_BIOS ;
dBiosAdr = ( dBiosAdr < < 4 ) ;
pbBiosAdr = phys_to_virt ( dBiosAdr ) ;
if ( init_orchid ( pHCB ) ) { /* Initialize orchid chip */
printk ( " inia100: initial orchid fail!! \n " ) ;
goto out_free_escb_array ;
}
shost - > io_port = pHCB - > HCS_Base ;
shost - > n_io_port = 0xff ;
shost - > can_queue = ORC_MAXQUEUE ;
shost - > unique_id = shost - > io_port ;
shost - > max_id = pHCB - > HCS_MaxTar ;
shost - > max_lun = 16 ;
shost - > irq = pHCB - > HCS_Intr = pdev - > irq ;
shost - > this_id = pHCB - > HCS_SCSI_ID ; /* Assign HCS index */
shost - > sg_tablesize = TOTAL_SG_ENTRY ;
/* Initial orc chip */
error = request_irq ( pdev - > irq , inia100_intr , SA_SHIRQ ,
" inia100 " , shost ) ;
if ( error < 0 ) {
printk ( KERN_WARNING " inia100: unable to get irq %d \n " ,
pdev - > irq ) ;
goto out_free_escb_array ;
}
pci_set_drvdata ( pdev , shost ) ;
error = scsi_add_host ( shost , & pdev - > dev ) ;
if ( error )
goto out_free_irq ;
scsi_scan_host ( shost ) ;
return 0 ;
out_free_irq :
free_irq ( shost - > irq , shost ) ;
out_free_escb_array :
pci_free_consistent ( pdev , ORC_MAXQUEUE * sizeof ( ESCB ) ,
pHCB - > HCS_virEscbArray , pHCB - > HCS_physEscbArray ) ;
out_free_scb_array :
pci_free_consistent ( pdev , ORC_MAXQUEUE * sizeof ( ORC_SCB ) ,
pHCB - > HCS_virScbArray , pHCB - > HCS_physScbArray ) ;
out_host_put :
scsi_host_put ( shost ) ;
out_release_region :
release_region ( port , 256 ) ;
out_disable_device :
pci_disable_device ( pdev ) ;
out :
return error ;
}
static void __devexit inia100_remove_one ( struct pci_dev * pdev )
{
struct Scsi_Host * shost = pci_get_drvdata ( pdev ) ;
ORC_HCS * pHCB = ( ORC_HCS * ) shost - > hostdata ;
scsi_remove_host ( shost ) ;
free_irq ( shost - > irq , shost ) ;
pci_free_consistent ( pdev , ORC_MAXQUEUE * sizeof ( ESCB ) ,
pHCB - > HCS_virEscbArray , pHCB - > HCS_physEscbArray ) ;
pci_free_consistent ( pdev , ORC_MAXQUEUE * sizeof ( ORC_SCB ) ,
pHCB - > HCS_virScbArray , pHCB - > HCS_physScbArray ) ;
release_region ( shost - > io_port , 256 ) ;
scsi_host_put ( shost ) ;
}
static struct pci_device_id inia100_pci_tbl [ ] = {
{ PCI_VENDOR_ID_INIT , 0x1060 , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , 0 } ,
{ 0 , }
} ;
MODULE_DEVICE_TABLE ( pci , inia100_pci_tbl ) ;
static struct pci_driver inia100_pci_driver = {
. name = " inia100 " ,
. id_table = inia100_pci_tbl ,
. probe = inia100_probe_one ,
. remove = __devexit_p ( inia100_remove_one ) ,
} ;
static int __init inia100_init ( void )
{
return pci_module_init ( & inia100_pci_driver ) ;
}
static void __exit inia100_exit ( void )
{
pci_unregister_driver ( & inia100_pci_driver ) ;
}
MODULE_DESCRIPTION ( " Initio A100U2W SCSI driver " ) ;
MODULE_AUTHOR ( " Initio Corporation " ) ;
MODULE_LICENSE ( " Dual BSD/GPL " ) ;
module_init ( inia100_init ) ;
module_exit ( inia100_exit ) ;