2006-05-16 10:38:09 +04:00
/*
* HighPoint RR3xxx controller driver for Linux
* Copyright ( C ) 2006 HighPoint Technologies , Inc . 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 ; version 2 of the License .
*
* 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 .
*
* Please report bugs / comments / suggestions to linux @ highpoint - tech . com
*
* For more information , visit http : //www.highpoint-tech.com
*/
# include <linux/config.h>
# include <linux/module.h>
# include <linux/types.h>
# include <linux/string.h>
# include <linux/kernel.h>
# include <linux/pci.h>
# include <linux/interrupt.h>
# include <linux/errno.h>
# include <linux/delay.h>
# include <linux/timer.h>
# include <linux/spinlock.h>
# include <linux/hdreg.h>
# include <asm/uaccess.h>
# include <asm/io.h>
# include <asm/div64.h>
# include <scsi/scsi_cmnd.h>
# include <scsi/scsi_device.h>
# include <scsi/scsi.h>
# include <scsi/scsi_tcq.h>
# include <scsi/scsi_host.h>
# include "hptiop.h"
MODULE_AUTHOR ( " HighPoint Technologies, Inc. " ) ;
MODULE_DESCRIPTION ( " HighPoint RocketRAID 3xxx SATA Controller Driver " ) ;
static char driver_name [ ] = " hptiop " ;
static const char driver_name_long [ ] = " RocketRAID 3xxx SATA Controller driver " ;
static const char driver_ver [ ] = " v1.0 (060426) " ;
static void hptiop_host_request_callback ( struct hptiop_hba * hba , u32 tag ) ;
static void hptiop_iop_request_callback ( struct hptiop_hba * hba , u32 tag ) ;
static void hptiop_message_callback ( struct hptiop_hba * hba , u32 msg ) ;
static inline void hptiop_pci_posting_flush ( struct hpt_iopmu __iomem * iop )
{
readl ( & iop - > outbound_intstatus ) ;
}
static int iop_wait_ready ( struct hpt_iopmu __iomem * iop , u32 millisec )
{
u32 req = 0 ;
int i ;
for ( i = 0 ; i < millisec ; i + + ) {
req = readl ( & iop - > inbound_queue ) ;
if ( req ! = IOPMU_QUEUE_EMPTY )
break ;
msleep ( 1 ) ;
}
if ( req ! = IOPMU_QUEUE_EMPTY ) {
writel ( req , & iop - > outbound_queue ) ;
hptiop_pci_posting_flush ( iop ) ;
return 0 ;
}
return - 1 ;
}
static void hptiop_request_callback ( struct hptiop_hba * hba , u32 tag )
{
if ( ( tag & IOPMU_QUEUE_MASK_HOST_BITS ) = = IOPMU_QUEUE_ADDR_HOST_BIT )
return hptiop_host_request_callback ( hba ,
tag & ~ IOPMU_QUEUE_ADDR_HOST_BIT ) ;
else
return hptiop_iop_request_callback ( hba , tag ) ;
}
static inline void hptiop_drain_outbound_queue ( struct hptiop_hba * hba )
{
u32 req ;
while ( ( req = readl ( & hba - > iop - > outbound_queue ) ) ! = IOPMU_QUEUE_EMPTY ) {
if ( req & IOPMU_QUEUE_MASK_HOST_BITS )
hptiop_request_callback ( hba , req ) ;
else {
struct hpt_iop_request_header __iomem * p ;
p = ( struct hpt_iop_request_header __iomem * )
( ( char __iomem * ) hba - > iop + req ) ;
if ( readl ( & p - > flags ) & IOP_REQUEST_FLAG_SYNC_REQUEST ) {
if ( readl ( & p - > context ) )
hptiop_request_callback ( hba , req ) ;
else
writel ( 1 , & p - > context ) ;
}
else
hptiop_request_callback ( hba , req ) ;
}
}
}
static int __iop_intr ( struct hptiop_hba * hba )
{
struct hpt_iopmu __iomem * iop = hba - > iop ;
u32 status ;
int ret = 0 ;
status = readl ( & iop - > outbound_intstatus ) ;
if ( status & IOPMU_OUTBOUND_INT_MSG0 ) {
u32 msg = readl ( & iop - > outbound_msgaddr0 ) ;
dprintk ( " received outbound msg %x \n " , msg ) ;
writel ( IOPMU_OUTBOUND_INT_MSG0 , & iop - > outbound_intstatus ) ;
hptiop_message_callback ( hba , msg ) ;
ret = 1 ;
}
if ( status & IOPMU_OUTBOUND_INT_POSTQUEUE ) {
hptiop_drain_outbound_queue ( hba ) ;
ret = 1 ;
}
return ret ;
}
static int iop_send_sync_request ( struct hptiop_hba * hba ,
void __iomem * _req , u32 millisec )
{
struct hpt_iop_request_header __iomem * req = _req ;
u32 i ;
writel ( readl ( & req - > flags ) | IOP_REQUEST_FLAG_SYNC_REQUEST ,
& req - > flags ) ;
writel ( 0 , & req - > context ) ;
writel ( ( unsigned long ) req - ( unsigned long ) hba - > iop ,
& hba - > iop - > inbound_queue ) ;
hptiop_pci_posting_flush ( hba - > iop ) ;
for ( i = 0 ; i < millisec ; i + + ) {
__iop_intr ( hba ) ;
if ( readl ( & req - > context ) )
return 0 ;
msleep ( 1 ) ;
}
return - 1 ;
}
static int iop_send_sync_msg ( struct hptiop_hba * hba , u32 msg , u32 millisec )
{
u32 i ;
hba - > msg_done = 0 ;
writel ( msg , & hba - > iop - > inbound_msgaddr0 ) ;
hptiop_pci_posting_flush ( hba - > iop ) ;
for ( i = 0 ; i < millisec ; i + + ) {
spin_lock_irq ( hba - > host - > host_lock ) ;
__iop_intr ( hba ) ;
spin_unlock_irq ( hba - > host - > host_lock ) ;
if ( hba - > msg_done )
break ;
msleep ( 1 ) ;
}
return hba - > msg_done ? 0 : - 1 ;
}
static int iop_get_config ( struct hptiop_hba * hba ,
struct hpt_iop_request_get_config * config )
{
u32 req32 ;
struct hpt_iop_request_get_config __iomem * req ;
req32 = readl ( & hba - > iop - > inbound_queue ) ;
if ( req32 = = IOPMU_QUEUE_EMPTY )
return - 1 ;
req = ( struct hpt_iop_request_get_config __iomem * )
( ( unsigned long ) hba - > iop + req32 ) ;
writel ( 0 , & req - > header . flags ) ;
writel ( IOP_REQUEST_TYPE_GET_CONFIG , & req - > header . type ) ;
writel ( sizeof ( struct hpt_iop_request_get_config ) , & req - > header . size ) ;
writel ( IOP_RESULT_PENDING , & req - > header . result ) ;
if ( iop_send_sync_request ( hba , req , 20000 ) ) {
dprintk ( " Get config send cmd failed \n " ) ;
return - 1 ;
}
memcpy_fromio ( config , req , sizeof ( * config ) ) ;
writel ( req32 , & hba - > iop - > outbound_queue ) ;
return 0 ;
}
static int iop_set_config ( struct hptiop_hba * hba ,
struct hpt_iop_request_set_config * config )
{
u32 req32 ;
struct hpt_iop_request_set_config __iomem * req ;
req32 = readl ( & hba - > iop - > inbound_queue ) ;
if ( req32 = = IOPMU_QUEUE_EMPTY )
return - 1 ;
req = ( struct hpt_iop_request_set_config __iomem * )
( ( unsigned long ) hba - > iop + req32 ) ;
memcpy_toio ( ( u8 __iomem * ) req + sizeof ( struct hpt_iop_request_header ) ,
( u8 * ) config + sizeof ( struct hpt_iop_request_header ) ,
sizeof ( struct hpt_iop_request_set_config ) -
sizeof ( struct hpt_iop_request_header ) ) ;
writel ( 0 , & req - > header . flags ) ;
writel ( IOP_REQUEST_TYPE_SET_CONFIG , & req - > header . type ) ;
writel ( sizeof ( struct hpt_iop_request_set_config ) , & req - > header . size ) ;
writel ( IOP_RESULT_PENDING , & req - > header . result ) ;
if ( iop_send_sync_request ( hba , req , 20000 ) ) {
dprintk ( " Set config send cmd failed \n " ) ;
return - 1 ;
}
writel ( req32 , & hba - > iop - > outbound_queue ) ;
return 0 ;
}
static int hptiop_initialize_iop ( struct hptiop_hba * hba )
{
struct hpt_iopmu __iomem * iop = hba - > iop ;
/* enable interrupts */
writel ( ~ ( IOPMU_OUTBOUND_INT_POSTQUEUE | IOPMU_OUTBOUND_INT_MSG0 ) ,
& iop - > outbound_intmask ) ;
hba - > initialized = 1 ;
/* start background tasks */
if ( iop_send_sync_msg ( hba ,
IOPMU_INBOUND_MSG0_START_BACKGROUND_TASK , 5000 ) ) {
printk ( KERN_ERR " scsi%d: fail to start background task \n " ,
hba - > host - > host_no ) ;
return - 1 ;
}
return 0 ;
}
static int hptiop_map_pci_bar ( struct hptiop_hba * hba )
{
u32 mem_base_phy , length ;
void __iomem * mem_base_virt ;
struct pci_dev * pcidev = hba - > pcidev ;
if ( ! ( pci_resource_flags ( pcidev , 0 ) & IORESOURCE_MEM ) ) {
printk ( KERN_ERR " scsi%d: pci resource invalid \n " ,
hba - > host - > host_no ) ;
return - 1 ;
}
mem_base_phy = pci_resource_start ( pcidev , 0 ) ;
length = pci_resource_len ( pcidev , 0 ) ;
mem_base_virt = ioremap ( mem_base_phy , length ) ;
if ( ! mem_base_virt ) {
printk ( KERN_ERR " scsi%d: Fail to ioremap memory space \n " ,
hba - > host - > host_no ) ;
return - 1 ;
}
hba - > iop = mem_base_virt ;
dprintk ( " hptiop_map_pci_bar: iop=%p \n " , hba - > iop ) ;
return 0 ;
}
static void hptiop_message_callback ( struct hptiop_hba * hba , u32 msg )
{
dprintk ( " iop message 0x%x \n " , msg ) ;
if ( ! hba - > initialized )
return ;
if ( msg = = IOPMU_INBOUND_MSG0_RESET ) {
atomic_set ( & hba - > resetting , 0 ) ;
wake_up ( & hba - > reset_wq ) ;
}
else if ( msg < = IOPMU_INBOUND_MSG0_MAX )
hba - > msg_done = 1 ;
}
static inline struct hptiop_request * get_req ( struct hptiop_hba * hba )
{
struct hptiop_request * ret ;
dprintk ( " get_req : req=%p \n " , hba - > req_list ) ;
ret = hba - > req_list ;
if ( ret )
hba - > req_list = ret - > next ;
return ret ;
}
static inline void free_req ( struct hptiop_hba * hba , struct hptiop_request * req )
{
dprintk ( " free_req(%d, %p) \n " , req - > index , req ) ;
req - > next = hba - > req_list ;
hba - > req_list = req ;
}
static void hptiop_host_request_callback ( struct hptiop_hba * hba , u32 tag )
{
struct hpt_iop_request_scsi_command * req ;
struct scsi_cmnd * scp ;
req = ( struct hpt_iop_request_scsi_command * ) hba - > reqs [ tag ] . req_virt ;
dprintk ( " hptiop_host_request_callback: req=%p, type=%d, "
" result=%d, context=0x%x tag=%d \n " ,
req , req - > header . type , req - > header . result ,
req - > header . context , tag ) ;
BUG_ON ( ! req - > header . result ) ;
BUG_ON ( req - > header . type ! = cpu_to_le32 ( IOP_REQUEST_TYPE_SCSI_COMMAND ) ) ;
scp = hba - > reqs [ tag ] . scp ;
if ( HPT_SCP ( scp ) - > mapped ) {
if ( scp - > use_sg )
pci_unmap_sg ( hba - > pcidev ,
( struct scatterlist * ) scp - > request_buffer ,
scp - > use_sg ,
scp - > sc_data_direction
) ;
else
pci_unmap_single ( hba - > pcidev ,
HPT_SCP ( scp ) - > dma_handle ,
scp - > request_bufflen ,
scp - > sc_data_direction
) ;
}
switch ( le32_to_cpu ( req - > header . result ) ) {
case IOP_RESULT_SUCCESS :
scp - > result = ( DID_OK < < 16 ) ;
break ;
case IOP_RESULT_BAD_TARGET :
scp - > result = ( DID_BAD_TARGET < < 16 ) ;
break ;
case IOP_RESULT_BUSY :
scp - > result = ( DID_BUS_BUSY < < 16 ) ;
break ;
case IOP_RESULT_RESET :
scp - > result = ( DID_RESET < < 16 ) ;
break ;
case IOP_RESULT_FAIL :
scp - > result = ( DID_ERROR < < 16 ) ;
break ;
case IOP_RESULT_INVALID_REQUEST :
scp - > result = ( DID_ABORT < < 16 ) ;
break ;
case IOP_RESULT_MODE_SENSE_CHECK_CONDITION :
scp - > result = SAM_STAT_CHECK_CONDITION ;
memset ( & scp - > sense_buffer ,
0 , sizeof ( scp - > sense_buffer ) ) ;
memcpy ( & scp - > sense_buffer ,
& req - > sg_list , le32_to_cpu ( req - > dataxfer_length ) ) ;
break ;
default :
scp - > result = ( ( DRIVER_INVALID | SUGGEST_ABORT ) < < 24 ) |
( DID_ABORT < < 16 ) ;
break ;
}
dprintk ( " scsi_done(%p) \n " , scp ) ;
scp - > scsi_done ( scp ) ;
free_req ( hba , & hba - > reqs [ tag ] ) ;
}
void hptiop_iop_request_callback ( struct hptiop_hba * hba , u32 tag )
{
struct hpt_iop_request_header __iomem * req ;
struct hpt_iop_request_ioctl_command __iomem * p ;
struct hpt_ioctl_k * arg ;
req = ( struct hpt_iop_request_header __iomem * )
( ( unsigned long ) hba - > iop + tag ) ;
dprintk ( " hptiop_iop_request_callback: req=%p, type=%d, "
" result=%d, context=0x%x tag=%d \n " ,
req , readl ( & req - > type ) , readl ( & req - > result ) ,
readl ( & req - > context ) , tag ) ;
BUG_ON ( ! readl ( & req - > result ) ) ;
BUG_ON ( readl ( & req - > type ) ! = IOP_REQUEST_TYPE_IOCTL_COMMAND ) ;
p = ( struct hpt_iop_request_ioctl_command __iomem * ) req ;
arg = ( struct hpt_ioctl_k * ) ( unsigned long )
( readl ( & req - > context ) |
( ( u64 ) readl ( & req - > context_hi32 ) < < 32 ) ) ;
if ( readl ( & req - > result ) = = IOP_RESULT_SUCCESS ) {
arg - > result = HPT_IOCTL_RESULT_OK ;
if ( arg - > outbuf_size )
memcpy_fromio ( arg - > outbuf ,
& p - > buf [ ( readl ( & p - > inbuf_size ) + 3 ) & ~ 3 ] ,
arg - > outbuf_size ) ;
if ( arg - > bytes_returned )
* arg - > bytes_returned = arg - > outbuf_size ;
}
else
arg - > result = HPT_IOCTL_RESULT_FAILED ;
arg - > done ( arg ) ;
writel ( tag , & hba - > iop - > outbound_queue ) ;
}
static irqreturn_t hptiop_intr ( int irq , void * dev_id , struct pt_regs * regs )
{
struct hptiop_hba * hba = dev_id ;
int handled ;
unsigned long flags ;
spin_lock_irqsave ( hba - > host - > host_lock , flags ) ;
handled = __iop_intr ( hba ) ;
spin_unlock_irqrestore ( hba - > host - > host_lock , flags ) ;
return handled ;
}
static int hptiop_buildsgl ( struct scsi_cmnd * scp , struct hpt_iopsg * psg )
{
struct Scsi_Host * host = scp - > device - > host ;
struct hptiop_hba * hba = ( struct hptiop_hba * ) host - > hostdata ;
struct scatterlist * sglist = ( struct scatterlist * ) scp - > request_buffer ;
/*
* though we ' ll not get non - use_sg fields anymore ,
* keep use_sg checking anyway
*/
if ( scp - > use_sg ) {
int idx ;
HPT_SCP ( scp ) - > sgcnt = pci_map_sg ( hba - > pcidev ,
sglist , scp - > use_sg ,
scp - > sc_data_direction ) ;
HPT_SCP ( scp ) - > mapped = 1 ;
BUG_ON ( HPT_SCP ( scp ) - > sgcnt > hba - > max_sg_descriptors ) ;
for ( idx = 0 ; idx < HPT_SCP ( scp ) - > sgcnt ; idx + + ) {
psg [ idx ] . pci_address =
cpu_to_le64 ( sg_dma_address ( & sglist [ idx ] ) ) ;
psg [ idx ] . size = cpu_to_le32 ( sg_dma_len ( & sglist [ idx ] ) ) ;
psg [ idx ] . eot = ( idx = = HPT_SCP ( scp ) - > sgcnt - 1 ) ?
cpu_to_le32 ( 1 ) : 0 ;
}
return HPT_SCP ( scp ) - > sgcnt ;
} else {
HPT_SCP ( scp ) - > dma_handle = pci_map_single (
hba - > pcidev ,
scp - > request_buffer ,
scp - > request_bufflen ,
scp - > sc_data_direction
) ;
HPT_SCP ( scp ) - > mapped = 1 ;
psg - > pci_address = cpu_to_le64 ( HPT_SCP ( scp ) - > dma_handle ) ;
psg - > size = cpu_to_le32 ( scp - > request_bufflen ) ;
psg - > eot = cpu_to_le32 ( 1 ) ;
return 1 ;
}
}
static int hptiop_queuecommand ( struct scsi_cmnd * scp ,
void ( * done ) ( struct scsi_cmnd * ) )
{
struct Scsi_Host * host = scp - > device - > host ;
struct hptiop_hba * hba = ( struct hptiop_hba * ) host - > hostdata ;
struct hpt_iop_request_scsi_command * req ;
int sg_count = 0 ;
struct hptiop_request * _req ;
BUG_ON ( ! done ) ;
scp - > scsi_done = done ;
_req = get_req ( hba ) ;
if ( _req = = NULL ) {
dprintk ( " hptiop_queuecmd : no free req \n " ) ;
2006-06-14 12:50:57 +04:00
return SCSI_MLQUEUE_HOST_BUSY ;
2006-05-16 10:38:09 +04:00
}
_req - > scp = scp ;
dprintk ( " hptiop_queuecmd(scp=%p) %d/%d/%d/%d cdb=(%x-%x-%x) "
" req_index=%d, req=%p \n " ,
scp ,
host - > host_no , scp - > device - > channel ,
scp - > device - > id , scp - > device - > lun ,
* ( ( u32 * ) & scp - > cmnd ) ,
* ( ( u32 * ) & scp - > cmnd + 1 ) ,
* ( ( u32 * ) & scp - > cmnd + 2 ) ,
_req - > index , _req - > req_virt ) ;
scp - > result = 0 ;
if ( scp - > device - > channel | | scp - > device - > lun | |
scp - > device - > id > hba - > max_devices ) {
scp - > result = DID_BAD_TARGET < < 16 ;
free_req ( hba , _req ) ;
goto cmd_done ;
}
req = ( struct hpt_iop_request_scsi_command * ) _req - > req_virt ;
/* build S/G table */
if ( scp - > request_bufflen )
sg_count = hptiop_buildsgl ( scp , req - > sg_list ) ;
else
HPT_SCP ( scp ) - > mapped = 0 ;
req - > header . flags = cpu_to_le32 ( IOP_REQUEST_FLAG_OUTPUT_CONTEXT ) ;
req - > header . type = cpu_to_le32 ( IOP_REQUEST_TYPE_SCSI_COMMAND ) ;
req - > header . result = cpu_to_le32 ( IOP_RESULT_PENDING ) ;
req - > header . context = cpu_to_le32 ( IOPMU_QUEUE_ADDR_HOST_BIT |
( u32 ) _req - > index ) ;
req - > header . context_hi32 = 0 ;
2006-06-14 06:40:34 +04:00
req - > dataxfer_length = cpu_to_le32 ( scp - > request_bufflen ) ;
2006-05-16 10:38:09 +04:00
req - > channel = scp - > device - > channel ;
req - > target = scp - > device - > id ;
req - > lun = scp - > device - > lun ;
req - > header . size = cpu_to_le32 (
sizeof ( struct hpt_iop_request_scsi_command )
- sizeof ( struct hpt_iopsg )
+ sg_count * sizeof ( struct hpt_iopsg ) ) ;
memcpy ( req - > cdb , scp - > cmnd , sizeof ( req - > cdb ) ) ;
writel ( IOPMU_QUEUE_ADDR_HOST_BIT | _req - > req_shifted_phy ,
& hba - > iop - > inbound_queue ) ;
return 0 ;
cmd_done :
dprintk ( " scsi_done(scp=%p) \n " , scp ) ;
scp - > scsi_done ( scp ) ;
return 0 ;
}
static const char * hptiop_info ( struct Scsi_Host * host )
{
return driver_name_long ;
}
static int hptiop_reset_hba ( struct hptiop_hba * hba )
{
if ( atomic_xchg ( & hba - > resetting , 1 ) = = 0 ) {
atomic_inc ( & hba - > reset_count ) ;
writel ( IOPMU_INBOUND_MSG0_RESET ,
2006-07-24 11:48:54 +04:00
& hba - > iop - > inbound_msgaddr0 ) ;
2006-05-16 10:38:09 +04:00
hptiop_pci_posting_flush ( hba - > iop ) ;
}
wait_event_timeout ( hba - > reset_wq ,
atomic_read ( & hba - > resetting ) = = 0 , 60 * HZ ) ;
if ( atomic_read ( & hba - > resetting ) ) {
/* IOP is in unkown state, abort reset */
printk ( KERN_ERR " scsi%d: reset failed \n " , hba - > host - > host_no ) ;
return - 1 ;
}
if ( iop_send_sync_msg ( hba ,
IOPMU_INBOUND_MSG0_START_BACKGROUND_TASK , 5000 ) ) {
dprintk ( " scsi%d: fail to start background task \n " ,
hba - > host - > host_no ) ;
}
return 0 ;
}
static int hptiop_reset ( struct scsi_cmnd * scp )
{
struct Scsi_Host * host = scp - > device - > host ;
struct hptiop_hba * hba = ( struct hptiop_hba * ) host - > hostdata ;
printk ( KERN_WARNING " hptiop_reset(%d/%d/%d) scp=%p \n " ,
scp - > device - > host - > host_no , scp - > device - > channel ,
scp - > device - > id , scp ) ;
return hptiop_reset_hba ( hba ) ? FAILED : SUCCESS ;
}
static int hptiop_adjust_disk_queue_depth ( struct scsi_device * sdev ,
int queue_depth )
{
if ( queue_depth > 256 )
queue_depth = 256 ;
scsi_adjust_queue_depth ( sdev , MSG_ORDERED_TAG , queue_depth ) ;
return queue_depth ;
}
static ssize_t hptiop_show_version ( struct class_device * class_dev , char * buf )
{
return snprintf ( buf , PAGE_SIZE , " %s \n " , driver_ver ) ;
}
static ssize_t hptiop_show_fw_version ( struct class_device * class_dev , char * buf )
{
struct Scsi_Host * host = class_to_shost ( class_dev ) ;
struct hptiop_hba * hba = ( struct hptiop_hba * ) host - > hostdata ;
return snprintf ( buf , PAGE_SIZE , " %d.%d.%d.%d \n " ,
hba - > firmware_version > > 24 ,
( hba - > firmware_version > > 16 ) & 0xff ,
( hba - > firmware_version > > 8 ) & 0xff ,
hba - > firmware_version & 0xff ) ;
}
static struct class_device_attribute hptiop_attr_version = {
. attr = {
. name = " driver-version " ,
. mode = S_IRUGO ,
} ,
. show = hptiop_show_version ,
} ;
static struct class_device_attribute hptiop_attr_fw_version = {
. attr = {
. name = " firmware-version " ,
. mode = S_IRUGO ,
} ,
. show = hptiop_show_fw_version ,
} ;
static struct class_device_attribute * hptiop_attrs [ ] = {
& hptiop_attr_version ,
& hptiop_attr_fw_version ,
NULL
} ;
static struct scsi_host_template driver_template = {
. module = THIS_MODULE ,
. name = driver_name ,
. queuecommand = hptiop_queuecommand ,
. eh_device_reset_handler = hptiop_reset ,
. eh_bus_reset_handler = hptiop_reset ,
. info = hptiop_info ,
. unchecked_isa_dma = 0 ,
. emulated = 0 ,
. use_clustering = ENABLE_CLUSTERING ,
. proc_name = driver_name ,
. shost_attrs = hptiop_attrs ,
. this_id = - 1 ,
. change_queue_depth = hptiop_adjust_disk_queue_depth ,
} ;
static int __devinit hptiop_probe ( struct pci_dev * pcidev ,
const struct pci_device_id * id )
{
struct Scsi_Host * host = NULL ;
struct hptiop_hba * hba ;
struct hpt_iop_request_get_config iop_config ;
struct hpt_iop_request_set_config set_config ;
dma_addr_t start_phy ;
void * start_virt ;
u32 offset , i , req_size ;
dprintk ( " hptiop_probe(%p) \n " , pcidev ) ;
if ( pci_enable_device ( pcidev ) ) {
printk ( KERN_ERR " hptiop: fail to enable pci device \n " ) ;
return - ENODEV ;
}
printk ( KERN_INFO " adapter at PCI %d:%d:%d, IRQ %d \n " ,
pcidev - > bus - > number , pcidev - > devfn > > 3 , pcidev - > devfn & 7 ,
pcidev - > irq ) ;
pci_set_master ( pcidev ) ;
/* Enable 64bit DMA if possible */
if ( pci_set_dma_mask ( pcidev , DMA_64BIT_MASK ) ) {
if ( pci_set_dma_mask ( pcidev , DMA_32BIT_MASK ) ) {
printk ( KERN_ERR " hptiop: fail to set dma_mask \n " ) ;
goto disable_pci_device ;
}
}
if ( pci_request_regions ( pcidev , driver_name ) ) {
printk ( KERN_ERR " hptiop: pci_request_regions failed \n " ) ;
goto disable_pci_device ;
}
host = scsi_host_alloc ( & driver_template , sizeof ( struct hptiop_hba ) ) ;
if ( ! host ) {
printk ( KERN_ERR " hptiop: fail to alloc scsi host \n " ) ;
goto free_pci_regions ;
}
hba = ( struct hptiop_hba * ) host - > hostdata ;
hba - > pcidev = pcidev ;
hba - > host = host ;
hba - > initialized = 0 ;
atomic_set ( & hba - > resetting , 0 ) ;
atomic_set ( & hba - > reset_count , 0 ) ;
init_waitqueue_head ( & hba - > reset_wq ) ;
init_waitqueue_head ( & hba - > ioctl_wq ) ;
host - > max_lun = 1 ;
host - > max_channel = 0 ;
host - > io_port = 0 ;
host - > n_io_port = 0 ;
host - > irq = pcidev - > irq ;
if ( hptiop_map_pci_bar ( hba ) )
goto free_scsi_host ;
if ( iop_wait_ready ( hba - > iop , 20000 ) ) {
printk ( KERN_ERR " scsi%d: firmware not ready \n " ,
hba - > host - > host_no ) ;
goto unmap_pci_bar ;
}
if ( iop_get_config ( hba , & iop_config ) ) {
printk ( KERN_ERR " scsi%d: get config failed \n " ,
hba - > host - > host_no ) ;
goto unmap_pci_bar ;
}
hba - > max_requests = min ( le32_to_cpu ( iop_config . max_requests ) ,
HPTIOP_MAX_REQUESTS ) ;
hba - > max_devices = le32_to_cpu ( iop_config . max_devices ) ;
hba - > max_request_size = le32_to_cpu ( iop_config . request_size ) ;
hba - > max_sg_descriptors = le32_to_cpu ( iop_config . max_sg_count ) ;
hba - > firmware_version = le32_to_cpu ( iop_config . firmware_version ) ;
hba - > sdram_size = le32_to_cpu ( iop_config . sdram_size ) ;
host - > max_sectors = le32_to_cpu ( iop_config . data_transfer_length ) > > 9 ;
host - > max_id = le32_to_cpu ( iop_config . max_devices ) ;
host - > sg_tablesize = le32_to_cpu ( iop_config . max_sg_count ) ;
host - > can_queue = le32_to_cpu ( iop_config . max_requests ) ;
host - > cmd_per_lun = le32_to_cpu ( iop_config . max_requests ) ;
host - > max_cmd_len = 16 ;
set_config . vbus_id = cpu_to_le32 ( host - > host_no ) ;
set_config . iop_id = cpu_to_le32 ( host - > host_no ) ;
if ( iop_set_config ( hba , & set_config ) ) {
printk ( KERN_ERR " scsi%d: set config failed \n " ,
hba - > host - > host_no ) ;
goto unmap_pci_bar ;
}
pci_set_drvdata ( pcidev , host ) ;
2006-07-02 06:29:42 +04:00
if ( request_irq ( pcidev - > irq , hptiop_intr , IRQF_SHARED ,
2006-05-16 10:38:09 +04:00
driver_name , hba ) ) {
printk ( KERN_ERR " scsi%d: request irq %d failed \n " ,
hba - > host - > host_no , pcidev - > irq ) ;
2006-07-30 21:13:36 +04:00
goto unmap_pci_bar ;
2006-05-16 10:38:09 +04:00
}
/* Allocate request mem */
req_size = sizeof ( struct hpt_iop_request_scsi_command )
+ sizeof ( struct hpt_iopsg ) * ( hba - > max_sg_descriptors - 1 ) ;
if ( ( req_size & 0x1f ) ! = 0 )
req_size = ( req_size + 0x1f ) & ~ 0x1f ;
dprintk ( " req_size=%d, max_requests=%d \n " , req_size , hba - > max_requests ) ;
hba - > req_size = req_size ;
start_virt = dma_alloc_coherent ( & pcidev - > dev ,
hba - > req_size * hba - > max_requests + 0x20 ,
& start_phy , GFP_KERNEL ) ;
if ( ! start_virt ) {
printk ( KERN_ERR " scsi%d: fail to alloc request mem \n " ,
hba - > host - > host_no ) ;
goto free_request_irq ;
}
hba - > dma_coherent = start_virt ;
hba - > dma_coherent_handle = start_phy ;
if ( ( start_phy & 0x1f ) ! = 0 )
{
offset = ( ( start_phy + 0x1f ) & ~ 0x1f ) - start_phy ;
start_phy + = offset ;
start_virt + = offset ;
}
hba - > req_list = start_virt ;
for ( i = 0 ; i < hba - > max_requests ; i + + ) {
hba - > reqs [ i ] . next = NULL ;
hba - > reqs [ i ] . req_virt = start_virt ;
hba - > reqs [ i ] . req_shifted_phy = start_phy > > 5 ;
hba - > reqs [ i ] . index = i ;
free_req ( hba , & hba - > reqs [ i ] ) ;
start_virt = ( char * ) start_virt + hba - > req_size ;
start_phy = start_phy + hba - > req_size ;
}
/* Enable Interrupt and start background task */
if ( hptiop_initialize_iop ( hba ) )
goto free_request_mem ;
2006-07-30 21:13:36 +04:00
if ( scsi_add_host ( host , & pcidev - > dev ) ) {
printk ( KERN_ERR " scsi%d: scsi_add_host failed \n " ,
hba - > host - > host_no ) ;
goto free_request_mem ;
}
2006-05-16 10:38:09 +04:00
scsi_scan_host ( host ) ;
dprintk ( " scsi%d: hptiop_probe successfully \n " , hba - > host - > host_no ) ;
return 0 ;
free_request_mem :
dma_free_coherent ( & hba - > pcidev - > dev ,
hba - > req_size * hba - > max_requests + 0x20 ,
hba - > dma_coherent , hba - > dma_coherent_handle ) ;
free_request_irq :
free_irq ( hba - > pcidev - > irq , hba ) ;
unmap_pci_bar :
iounmap ( hba - > iop ) ;
free_pci_regions :
pci_release_regions ( pcidev ) ;
free_scsi_host :
scsi_host_put ( host ) ;
disable_pci_device :
pci_disable_device ( pcidev ) ;
dprintk ( " scsi%d: hptiop_probe fail \n " , host - > host_no ) ;
return - ENODEV ;
}
static void hptiop_shutdown ( struct pci_dev * pcidev )
{
struct Scsi_Host * host = pci_get_drvdata ( pcidev ) ;
struct hptiop_hba * hba = ( struct hptiop_hba * ) host - > hostdata ;
struct hpt_iopmu __iomem * iop = hba - > iop ;
u32 int_mask ;
dprintk ( " hptiop_shutdown(%p) \n " , hba ) ;
/* stop the iop */
if ( iop_send_sync_msg ( hba , IOPMU_INBOUND_MSG0_SHUTDOWN , 60000 ) )
printk ( KERN_ERR " scsi%d: shutdown the iop timeout \n " ,
hba - > host - > host_no ) ;
/* disable all outbound interrupts */
int_mask = readl ( & iop - > outbound_intmask ) ;
writel ( int_mask |
IOPMU_OUTBOUND_INT_MSG0 | IOPMU_OUTBOUND_INT_POSTQUEUE ,
& iop - > outbound_intmask ) ;
hptiop_pci_posting_flush ( iop ) ;
}
static void hptiop_remove ( struct pci_dev * pcidev )
{
struct Scsi_Host * host = pci_get_drvdata ( pcidev ) ;
struct hptiop_hba * hba = ( struct hptiop_hba * ) host - > hostdata ;
dprintk ( " scsi%d: hptiop_remove \n " , hba - > host - > host_no ) ;
2006-06-14 12:50:57 +04:00
scsi_remove_host ( host ) ;
2006-05-16 10:38:09 +04:00
hptiop_shutdown ( pcidev ) ;
free_irq ( hba - > pcidev - > irq , hba ) ;
dma_free_coherent ( & hba - > pcidev - > dev ,
hba - > req_size * hba - > max_requests + 0x20 ,
hba - > dma_coherent ,
hba - > dma_coherent_handle ) ;
iounmap ( hba - > iop ) ;
pci_release_regions ( hba - > pcidev ) ;
pci_set_drvdata ( hba - > pcidev , NULL ) ;
pci_disable_device ( hba - > pcidev ) ;
scsi_host_put ( host ) ;
}
static struct pci_device_id hptiop_id_table [ ] = {
{ PCI_DEVICE ( 0x1103 , 0x3220 ) } ,
{ PCI_DEVICE ( 0x1103 , 0x3320 ) } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( pci , hptiop_id_table ) ;
static struct pci_driver hptiop_pci_driver = {
. name = driver_name ,
. id_table = hptiop_id_table ,
. probe = hptiop_probe ,
. remove = hptiop_remove ,
. shutdown = hptiop_shutdown ,
} ;
static int __init hptiop_module_init ( void )
{
printk ( KERN_INFO " %s %s \n " , driver_name_long , driver_ver ) ;
2006-07-30 21:13:36 +04:00
return pci_register_driver ( & hptiop_pci_driver ) ;
2006-05-16 10:38:09 +04:00
}
static void __exit hptiop_module_exit ( void )
{
pci_unregister_driver ( & hptiop_pci_driver ) ;
}
module_init ( hptiop_module_init ) ;
module_exit ( hptiop_module_exit ) ;
MODULE_LICENSE ( " GPL " ) ;