2007-10-26 04:58:22 +04:00
/*
2009-05-09 01:46:40 +04:00
* Marvell 88 SE64xx / 88 SE94xx main function
*
* Copyright 2007 Red Hat , Inc .
* Copyright 2008 Marvell . < kewei @ marvell . com >
*
* This file is licensed under GPLv2 .
*
* 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 .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307
* USA
*/
2007-10-26 04:58:22 +04:00
2009-05-09 01:44:01 +04:00
# include "mv_sas.h"
2007-10-26 04:58:22 +04:00
2009-05-09 01:44:01 +04:00
static int mvs_find_tag ( struct mvs_info * mvi , struct sas_task * task , u32 * tag )
{
if ( task - > lldd_task ) {
struct mvs_slot_info * slot ;
2009-05-15 04:41:21 +04:00
slot = task - > lldd_task ;
2009-05-09 01:46:40 +04:00
* tag = slot - > slot_tag ;
2009-05-09 01:44:01 +04:00
return 1 ;
}
return 0 ;
}
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
void mvs_tag_clear ( struct mvs_info * mvi , u32 tag )
2009-05-09 01:44:01 +04:00
{
2009-05-15 04:41:21 +04:00
void * bitmap = & mvi - > tags ;
2009-05-09 01:44:01 +04:00
clear_bit ( tag , bitmap ) ;
}
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
void mvs_tag_free ( struct mvs_info * mvi , u32 tag )
2009-05-09 01:44:01 +04:00
{
mvs_tag_clear ( mvi , tag ) ;
}
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
void mvs_tag_set ( struct mvs_info * mvi , unsigned int tag )
2009-05-09 01:44:01 +04:00
{
2009-05-15 04:41:21 +04:00
void * bitmap = & mvi - > tags ;
2009-05-09 01:44:01 +04:00
set_bit ( tag , bitmap ) ;
}
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
inline int mvs_tag_alloc ( struct mvs_info * mvi , u32 * tag_out )
2009-05-09 01:44:01 +04:00
{
unsigned int index , tag ;
2009-05-15 04:41:21 +04:00
void * bitmap = & mvi - > tags ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
index = find_first_zero_bit ( bitmap , mvi - > tags_num ) ;
2009-05-09 01:44:01 +04:00
tag = index ;
2009-05-09 01:46:40 +04:00
if ( tag > = mvi - > tags_num )
2009-05-09 01:44:01 +04:00
return - SAS_QUEUE_FULL ;
mvs_tag_set ( mvi , tag ) ;
* tag_out = tag ;
return 0 ;
}
2007-10-26 04:58:22 +04:00
2009-05-09 01:44:01 +04:00
void mvs_tag_init ( struct mvs_info * mvi )
{
int i ;
2009-05-09 01:46:40 +04:00
for ( i = 0 ; i < mvi - > tags_num ; + + i )
2009-05-09 01:44:01 +04:00
mvs_tag_clear ( mvi , i ) ;
}
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
void mvs_hexdump ( u32 size , u8 * data , u32 baseaddr )
2008-02-23 16:15:27 +03:00
{
u32 i ;
u32 run ;
u32 offset ;
offset = 0 ;
while ( size ) {
2009-05-09 01:46:40 +04:00
printk ( KERN_DEBUG " %08X : " , baseaddr + offset ) ;
2008-02-23 16:15:27 +03:00
if ( size > = 16 )
run = 16 ;
else
run = size ;
size - = run ;
for ( i = 0 ; i < 16 ; i + + ) {
if ( i < run )
2009-05-09 01:46:40 +04:00
printk ( KERN_DEBUG " %02X " , ( u32 ) data [ i ] ) ;
2008-02-23 16:15:27 +03:00
else
2009-05-09 01:46:40 +04:00
printk ( KERN_DEBUG " " ) ;
2008-02-23 16:15:27 +03:00
}
2009-05-09 01:46:40 +04:00
printk ( KERN_DEBUG " : " ) ;
2008-02-23 16:15:27 +03:00
for ( i = 0 ; i < run ; i + + )
2009-05-09 01:46:40 +04:00
printk ( KERN_DEBUG " %c " ,
isalnum ( data [ i ] ) ? data [ i ] : ' . ' ) ;
printk ( KERN_DEBUG " \n " ) ;
2008-02-23 16:15:27 +03:00
data = & data [ 16 ] ;
offset + = run ;
}
2009-05-09 01:46:40 +04:00
printk ( KERN_DEBUG " \n " ) ;
2008-02-23 16:15:27 +03:00
}
2009-05-09 01:46:40 +04:00
# if (_MV_DUMP > 1)
2008-02-23 16:15:27 +03:00
static void mvs_hba_sb_dump ( struct mvs_info * mvi , u32 tag ,
enum sas_protocol proto )
{
u32 offset ;
struct mvs_slot_info * slot = & mvi - > slot_info [ tag ] ;
offset = slot - > cmd_size + MVS_OAF_SZ +
2009-05-09 01:46:40 +04:00
MVS_CHIP_DISP - > prd_size ( ) * slot - > n_elem ;
dev_printk ( KERN_DEBUG , mvi - > dev , " +---->Status buffer[%d] : \n " ,
2008-02-23 16:15:27 +03:00
tag ) ;
mvs_hexdump ( 32 , ( u8 * ) slot - > response ,
( u32 ) slot - > buf_dma + offset ) ;
}
2008-03-27 09:53:47 +03:00
# endif
2008-02-23 16:15:27 +03:00
static void mvs_hba_memory_dump ( struct mvs_info * mvi , u32 tag ,
enum sas_protocol proto )
{
2009-05-09 01:46:40 +04:00
# if (_MV_DUMP > 1)
2008-03-27 09:53:47 +03:00
u32 sz , w_ptr ;
2008-02-23 16:15:27 +03:00
u64 addr ;
struct mvs_slot_info * slot = & mvi - > slot_info [ tag ] ;
/*Delivery Queue */
2009-05-09 01:46:40 +04:00
sz = MVS_CHIP_SLOT_SZ ;
2008-03-27 09:53:47 +03:00
w_ptr = slot - > tx ;
2009-05-09 01:46:40 +04:00
addr = mvi - > tx_dma ;
dev_printk ( KERN_DEBUG , mvi - > dev ,
2008-03-27 09:53:47 +03:00
" Delivery Queue Size=%04d , WRT_PTR=%04X \n " , sz , w_ptr ) ;
2009-05-09 01:46:40 +04:00
dev_printk ( KERN_DEBUG , mvi - > dev ,
2008-02-23 16:15:27 +03:00
" Delivery Queue Base Address=0x%llX (PA) "
" (tx_dma=0x%llX), Entry=%04d \n " ,
2009-05-09 01:46:40 +04:00
addr , ( unsigned long long ) mvi - > tx_dma , w_ptr ) ;
2008-02-23 16:15:27 +03:00
mvs_hexdump ( sizeof ( u32 ) , ( u8 * ) ( & mvi - > tx [ mvi - > tx_prod ] ) ,
( u32 ) mvi - > tx_dma + sizeof ( u32 ) * w_ptr ) ;
/*Command List */
2008-03-27 09:53:47 +03:00
addr = mvi - > slot_dma ;
2009-05-09 01:46:40 +04:00
dev_printk ( KERN_DEBUG , mvi - > dev ,
2008-02-23 16:15:27 +03:00
" Command List Base Address=0x%llX (PA) "
" (slot_dma=0x%llX), Header=%03d \n " ,
2009-05-09 01:46:40 +04:00
addr , ( unsigned long long ) slot - > buf_dma , tag ) ;
dev_printk ( KERN_DEBUG , mvi - > dev , " Command Header[%03d]: \n " , tag ) ;
2008-02-23 16:15:27 +03:00
/*mvs_cmd_hdr */
mvs_hexdump ( sizeof ( struct mvs_cmd_hdr ) , ( u8 * ) ( & mvi - > slot [ tag ] ) ,
( u32 ) mvi - > slot_dma + tag * sizeof ( struct mvs_cmd_hdr ) ) ;
/*1.command table area */
2009-05-09 01:46:40 +04:00
dev_printk ( KERN_DEBUG , mvi - > dev , " +---->Command Table : \n " ) ;
2008-02-23 16:15:27 +03:00
mvs_hexdump ( slot - > cmd_size , ( u8 * ) slot - > buf , ( u32 ) slot - > buf_dma ) ;
/*2.open address frame area */
2009-05-09 01:46:40 +04:00
dev_printk ( KERN_DEBUG , mvi - > dev , " +---->Open Address Frame : \n " ) ;
2008-02-23 16:15:27 +03:00
mvs_hexdump ( MVS_OAF_SZ , ( u8 * ) slot - > buf + slot - > cmd_size ,
( u32 ) slot - > buf_dma + slot - > cmd_size ) ;
/*3.status buffer */
mvs_hba_sb_dump ( mvi , tag , proto ) ;
/*4.PRD table */
2009-05-09 01:46:40 +04:00
dev_printk ( KERN_DEBUG , mvi - > dev , " +---->PRD table : \n " ) ;
mvs_hexdump ( MVS_CHIP_DISP - > prd_size ( ) * slot - > n_elem ,
2008-02-23 16:15:27 +03:00
( u8 * ) slot - > buf + slot - > cmd_size + MVS_OAF_SZ ,
( u32 ) slot - > buf_dma + slot - > cmd_size + MVS_OAF_SZ ) ;
# endif
}
static void mvs_hba_cq_dump ( struct mvs_info * mvi )
{
2008-03-27 09:53:47 +03:00
# if (_MV_DUMP > 2)
2008-02-23 16:15:27 +03:00
u64 addr ;
void __iomem * regs = mvi - > regs ;
u32 entry = mvi - > rx_cons + 1 ;
u32 rx_desc = le32_to_cpu ( mvi - > rx [ entry ] ) ;
/*Completion Queue */
addr = mr32 ( RX_HI ) < < 16 < < 16 | mr32 ( RX_LO ) ;
2009-05-09 01:46:40 +04:00
dev_printk ( KERN_DEBUG , mvi - > dev , " Completion Task = 0x%p \n " ,
2008-03-27 09:53:47 +03:00
mvi - > slot_info [ rx_desc & RXQ_SLOT_MASK ] . task ) ;
2009-05-09 01:46:40 +04:00
dev_printk ( KERN_DEBUG , mvi - > dev ,
2008-02-23 16:15:27 +03:00
" Completion List Base Address=0x%llX (PA), "
" CQ_Entry=%04d, CQ_WP=0x%08X \n " ,
addr , entry - 1 , mvi - > rx [ 0 ] ) ;
mvs_hexdump ( sizeof ( u32 ) , ( u8 * ) ( & rx_desc ) ,
mvi - > rx_dma + sizeof ( u32 ) * entry ) ;
# endif
}
2009-05-09 01:46:40 +04:00
void mvs_get_sas_addr ( void * buf , u32 buflen )
2008-02-23 16:15:27 +03:00
{
2009-05-09 01:46:40 +04:00
/*memcpy(buf, "\x50\x05\x04\x30\x11\xab\x64\x40", 8);*/
}
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
struct mvs_info * mvs_find_dev_mvi ( struct domain_device * dev )
{
unsigned long i = 0 , j = 0 , hi = 0 ;
struct sas_ha_struct * sha = dev - > port - > ha ;
struct mvs_info * mvi = NULL ;
struct asd_sas_phy * phy ;
while ( sha - > sas_port [ i ] ) {
if ( sha - > sas_port [ i ] = = dev - > port ) {
phy = container_of ( sha - > sas_port [ i ] - > phy_list . next ,
struct asd_sas_phy , port_phy_el ) ;
j = 0 ;
while ( sha - > sas_phy [ j ] ) {
if ( sha - > sas_phy [ j ] = = phy )
break ;
j + + ;
}
break ;
}
i + + ;
}
hi = j / ( ( struct mvs_prv_info * ) sha - > lldd_ha ) - > n_phy ;
mvi = ( ( struct mvs_prv_info * ) sha - > lldd_ha ) - > mvi [ hi ] ;
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
return mvi ;
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
}
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
/* FIXME */
int mvs_find_dev_phyno ( struct domain_device * dev , int * phyno )
{
unsigned long i = 0 , j = 0 , n = 0 , num = 0 ;
2009-05-11 18:19:25 +04:00
struct mvs_device * mvi_dev = ( struct mvs_device * ) dev - > lldd_dev ;
struct mvs_info * mvi = mvi_dev - > mvi_info ;
2009-05-09 01:46:40 +04:00
struct sas_ha_struct * sha = dev - > port - > ha ;
while ( sha - > sas_port [ i ] ) {
if ( sha - > sas_port [ i ] = = dev - > port ) {
struct asd_sas_phy * phy ;
list_for_each_entry ( phy ,
& sha - > sas_port [ i ] - > phy_list , port_phy_el ) {
j = 0 ;
while ( sha - > sas_phy [ j ] ) {
if ( sha - > sas_phy [ j ] = = phy )
break ;
j + + ;
}
phyno [ n ] = ( j > = mvi - > chip - > n_phy ) ?
( j - mvi - > chip - > n_phy ) : j ;
num + + ;
n + + ;
2009-05-09 01:44:01 +04:00
}
break ;
}
2009-05-09 01:46:40 +04:00
i + + ;
}
return num ;
}
static inline void mvs_free_reg_set ( struct mvs_info * mvi ,
struct mvs_device * dev )
{
if ( ! dev ) {
mv_printk ( " device has been free. \n " ) ;
return ;
}
if ( dev - > runing_req ! = 0 )
return ;
if ( dev - > taskfileset = = MVS_ID_NOT_MAPPED )
return ;
MVS_CHIP_DISP - > free_reg_set ( mvi , & dev - > taskfileset ) ;
}
static inline u8 mvs_assign_reg_set ( struct mvs_info * mvi ,
struct mvs_device * dev )
{
if ( dev - > taskfileset ! = MVS_ID_NOT_MAPPED )
return 0 ;
return MVS_CHIP_DISP - > assign_reg_set ( mvi , & dev - > taskfileset ) ;
}
void mvs_phys_reset ( struct mvs_info * mvi , u32 phy_mask , int hard )
{
u32 no ;
for_each_phy ( phy_mask , phy_mask , no ) {
if ( ! ( phy_mask & 1 ) )
continue ;
MVS_CHIP_DISP - > phy_reset ( mvi , no , hard ) ;
}
}
/* FIXME: locking? */
int mvs_phy_control ( struct asd_sas_phy * sas_phy , enum phy_func func ,
void * funcdata )
{
int rc = 0 , phy_id = sas_phy - > id ;
u32 tmp , i = 0 , hi ;
struct sas_ha_struct * sha = sas_phy - > ha ;
struct mvs_info * mvi = NULL ;
while ( sha - > sas_phy [ i ] ) {
if ( sha - > sas_phy [ i ] = = sas_phy )
break ;
i + + ;
}
hi = i / ( ( struct mvs_prv_info * ) sha - > lldd_ha ) - > n_phy ;
mvi = ( ( struct mvs_prv_info * ) sha - > lldd_ha ) - > mvi [ hi ] ;
switch ( func ) {
case PHY_FUNC_SET_LINK_RATE :
MVS_CHIP_DISP - > phy_set_link_rate ( mvi , phy_id , funcdata ) ;
break ;
2008-02-23 16:15:27 +03:00
2009-05-09 01:44:01 +04:00
case PHY_FUNC_HARD_RESET :
2009-05-09 01:46:40 +04:00
tmp = MVS_CHIP_DISP - > read_phy_ctl ( mvi , phy_id ) ;
2009-05-09 01:44:01 +04:00
if ( tmp & PHY_RST_HARD )
break ;
2009-05-09 01:46:40 +04:00
MVS_CHIP_DISP - > phy_reset ( mvi , phy_id , 1 ) ;
2009-05-09 01:44:01 +04:00
break ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:44:01 +04:00
case PHY_FUNC_LINK_RESET :
2009-05-09 01:46:40 +04:00
MVS_CHIP_DISP - > phy_enable ( mvi , phy_id ) ;
MVS_CHIP_DISP - > phy_reset ( mvi , phy_id , 0 ) ;
2009-05-09 01:44:01 +04:00
break ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:44:01 +04:00
case PHY_FUNC_DISABLE :
2009-05-09 01:46:40 +04:00
MVS_CHIP_DISP - > phy_disable ( mvi , phy_id ) ;
break ;
2009-05-09 01:44:01 +04:00
case PHY_FUNC_RELEASE_SPINUP_HOLD :
default :
rc = - EOPNOTSUPP ;
2007-10-26 04:58:22 +04:00
}
2009-05-09 01:46:40 +04:00
msleep ( 200 ) ;
2007-10-26 04:58:22 +04:00
return rc ;
}
2009-05-09 01:46:40 +04:00
void __devinit mvs_set_sas_addr ( struct mvs_info * mvi , int port_id ,
u32 off_lo , u32 off_hi , u64 sas_addr )
{
u32 lo = ( u32 ) sas_addr ;
u32 hi = ( u32 ) ( sas_addr > > 32 ) ;
MVS_CHIP_DISP - > write_port_cfg_addr ( mvi , port_id , off_lo ) ;
MVS_CHIP_DISP - > write_port_cfg_data ( mvi , port_id , lo ) ;
MVS_CHIP_DISP - > write_port_cfg_addr ( mvi , port_id , off_hi ) ;
MVS_CHIP_DISP - > write_port_cfg_data ( mvi , port_id , hi ) ;
}
2009-05-09 01:44:01 +04:00
static void mvs_bytes_dmaed ( struct mvs_info * mvi , int i )
2008-03-27 09:53:47 +03:00
{
2009-05-09 01:44:01 +04:00
struct mvs_phy * phy = & mvi - > phy [ i ] ;
2009-05-09 01:46:40 +04:00
struct asd_sas_phy * sas_phy = & phy - > sas_phy ;
struct sas_ha_struct * sas_ha ;
2009-05-09 01:44:01 +04:00
if ( ! phy - > phy_attached )
return ;
2009-05-09 01:46:40 +04:00
if ( ! ( phy - > att_dev_info & PORT_DEV_TRGT_MASK )
& & phy - > phy_type & PORT_TYPE_SAS ) {
return ;
}
sas_ha = mvi - > sas ;
sas_ha - > notify_phy_event ( sas_phy , PHYE_OOB_DONE ) ;
2009-05-09 01:44:01 +04:00
if ( sas_phy - > phy ) {
struct sas_phy * sphy = sas_phy - > phy ;
sphy - > negotiated_linkrate = sas_phy - > linkrate ;
sphy - > minimum_linkrate = phy - > minimum_linkrate ;
sphy - > minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS ;
sphy - > maximum_linkrate = phy - > maximum_linkrate ;
2009-05-09 01:46:40 +04:00
sphy - > maximum_linkrate_hw = MVS_CHIP_DISP - > phy_max_link_rate ( ) ;
2008-03-27 09:53:47 +03:00
}
2009-05-09 01:44:01 +04:00
if ( phy - > phy_type & PORT_TYPE_SAS ) {
struct sas_identify_frame * id ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:44:01 +04:00
id = ( struct sas_identify_frame * ) phy - > frame_rcvd ;
id - > dev_type = phy - > identify . device_type ;
id - > initiator_bits = SAS_PROTOCOL_ALL ;
id - > target_bits = phy - > identify . target_port_protocols ;
} else if ( phy - > phy_type & PORT_TYPE_SATA ) {
2009-05-09 01:46:40 +04:00
/*Nothing*/
2009-05-09 01:44:01 +04:00
}
2009-05-09 01:46:40 +04:00
mv_dprintk ( " phy %d byte dmaded. \n " , i + mvi - > id * mvi - > chip - > n_phy ) ;
sas_phy - > frame_rcvd_size = phy - > frame_rcvd_size ;
mvi - > sas - > notify_port_event ( sas_phy ,
2009-05-09 01:44:01 +04:00
PORTE_BYTES_DMAED ) ;
2008-03-27 09:53:47 +03:00
}
2009-05-09 01:46:40 +04:00
int mvs_slave_alloc ( struct scsi_device * scsi_dev )
{
struct domain_device * dev = sdev_to_domain_dev ( scsi_dev ) ;
if ( dev_is_sata ( dev ) ) {
/* We don't need to rescan targets
* if REPORT_LUNS request is failed
*/
if ( scsi_dev - > lun > 0 )
return - ENXIO ;
scsi_dev - > tagged_supported = 1 ;
}
return sas_slave_alloc ( scsi_dev ) ;
}
2009-05-09 01:44:01 +04:00
int mvs_slave_configure ( struct scsi_device * sdev )
2008-03-27 09:53:47 +03:00
{
2009-05-09 01:44:01 +04:00
struct domain_device * dev = sdev_to_domain_dev ( sdev ) ;
int ret = sas_slave_configure ( sdev ) ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:44:01 +04:00
if ( ret )
return ret ;
if ( dev_is_sata ( dev ) ) {
2009-05-09 01:46:40 +04:00
/* may set PIO mode */
# if MV_DISABLE_NCQ
struct ata_port * ap = dev - > sata_dev . ap ;
struct ata_device * adev = ap - > link . device ;
adev - > flags | = ATA_DFLAG_NCQ_OFF ;
2009-05-09 01:44:01 +04:00
scsi_adjust_queue_depth ( sdev , MSG_SIMPLE_TAG , 1 ) ;
2009-05-09 01:46:40 +04:00
# endif
2009-05-09 01:44:01 +04:00
}
2008-03-27 09:53:47 +03:00
return 0 ;
2007-10-26 04:58:22 +04:00
}
2009-05-09 01:44:01 +04:00
void mvs_scan_start ( struct Scsi_Host * shost )
2007-10-26 04:58:22 +04:00
{
2009-05-09 01:46:40 +04:00
int i , j ;
unsigned short core_nr ;
struct mvs_info * mvi ;
struct sas_ha_struct * sha = SHOST_TO_SAS_HA ( shost ) ;
core_nr = ( ( struct mvs_prv_info * ) sha - > lldd_ha ) - > n_host ;
2009-05-09 01:44:01 +04:00
2009-05-09 01:46:40 +04:00
for ( j = 0 ; j < core_nr ; j + + ) {
mvi = ( ( struct mvs_prv_info * ) sha - > lldd_ha ) - > mvi [ j ] ;
for ( i = 0 ; i < mvi - > chip - > n_phy ; + + i )
mvs_bytes_dmaed ( mvi , i ) ;
2009-05-09 01:44:01 +04:00
}
2007-10-26 04:58:22 +04:00
}
2009-05-09 01:44:01 +04:00
int mvs_scan_finished ( struct Scsi_Host * shost , unsigned long time )
2007-10-26 04:58:22 +04:00
{
2009-05-09 01:44:01 +04:00
/* give the phy enabling interrupt event time to come in (1s
* is empirically about all it takes ) */
if ( time < HZ )
return 0 ;
/* Wait for discovery to finish */
scsi_flush_work ( shost ) ;
return 1 ;
2007-10-26 04:58:22 +04:00
}
2009-05-09 01:44:01 +04:00
static int mvs_task_prep_smp ( struct mvs_info * mvi ,
struct mvs_task_exec_info * tei )
2007-10-26 04:58:22 +04:00
{
2009-05-09 01:44:01 +04:00
int elem , rc , i ;
struct sas_task * task = tei - > task ;
struct mvs_cmd_hdr * hdr = tei - > hdr ;
2009-05-09 01:46:40 +04:00
struct domain_device * dev = task - > dev ;
struct asd_sas_port * sas_port = dev - > port ;
2009-05-09 01:44:01 +04:00
struct scatterlist * sg_req , * sg_resp ;
u32 req_len , resp_len , tag = tei - > tag ;
void * buf_tmp ;
u8 * buf_oaf ;
dma_addr_t buf_tmp_dma ;
2009-05-09 01:46:40 +04:00
void * buf_prd ;
2009-05-09 01:44:01 +04:00
struct mvs_slot_info * slot = & mvi - > slot_info [ tag ] ;
u32 flags = ( tei - > n_elem < < MCH_PRD_LEN_SHIFT ) ;
# if _MV_DUMP
u8 * buf_cmd ;
void * from ;
# endif
/*
* DMA - map SMP request , response buffers
*/
sg_req = & task - > smp_task . smp_req ;
2009-05-09 01:46:40 +04:00
elem = dma_map_sg ( mvi - > dev , sg_req , 1 , PCI_DMA_TODEVICE ) ;
2009-05-09 01:44:01 +04:00
if ( ! elem )
return - ENOMEM ;
req_len = sg_dma_len ( sg_req ) ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:44:01 +04:00
sg_resp = & task - > smp_task . smp_resp ;
2009-05-09 01:46:40 +04:00
elem = dma_map_sg ( mvi - > dev , sg_resp , 1 , PCI_DMA_FROMDEVICE ) ;
2009-05-09 01:44:01 +04:00
if ( ! elem ) {
rc = - ENOMEM ;
goto err_out ;
}
2009-05-09 01:46:40 +04:00
resp_len = SB_RFB_MAX ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:44:01 +04:00
/* must be in dwords */
if ( ( req_len & 0x3 ) | | ( resp_len & 0x3 ) ) {
rc = - EINVAL ;
goto err_out_2 ;
2007-10-26 04:58:22 +04:00
}
2009-05-09 01:44:01 +04:00
/*
* arrange MVS_SLOT_BUF_SZ - sized DMA buffer according to our needs
*/
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
/* region 1: command table area (MVS_SSP_CMD_SZ bytes) ***** */
2009-05-09 01:44:01 +04:00
buf_tmp = slot - > buf ;
buf_tmp_dma = slot - > buf_dma ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:44:01 +04:00
# if _MV_DUMP
buf_cmd = buf_tmp ;
hdr - > cmd_tbl = cpu_to_le64 ( buf_tmp_dma ) ;
buf_tmp + = req_len ;
buf_tmp_dma + = req_len ;
slot - > cmd_size = req_len ;
# else
hdr - > cmd_tbl = cpu_to_le64 ( sg_dma_address ( sg_req ) ) ;
# endif
2007-10-26 04:58:22 +04:00
2009-05-09 01:44:01 +04:00
/* region 2: open address frame area (MVS_OAF_SZ bytes) ********* */
buf_oaf = buf_tmp ;
hdr - > open_frame = cpu_to_le64 ( buf_tmp_dma ) ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:44:01 +04:00
buf_tmp + = MVS_OAF_SZ ;
buf_tmp_dma + = MVS_OAF_SZ ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
/* region 3: PRD table *********************************** */
2009-05-09 01:44:01 +04:00
buf_prd = buf_tmp ;
if ( tei - > n_elem )
hdr - > prd_tbl = cpu_to_le64 ( buf_tmp_dma ) ;
else
hdr - > prd_tbl = 0 ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
i = MVS_CHIP_DISP - > prd_size ( ) * tei - > n_elem ;
2009-05-09 01:44:01 +04:00
buf_tmp + = i ;
buf_tmp_dma + = i ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:44:01 +04:00
/* region 4: status buffer (larger the PRD, smaller this buf) ****** */
slot - > response = buf_tmp ;
hdr - > status_buf = cpu_to_le64 ( buf_tmp_dma ) ;
2009-05-09 01:46:40 +04:00
if ( mvi - > flags & MVF_FLAG_SOC )
hdr - > reserved [ 0 ] = 0 ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:44:01 +04:00
/*
* Fill in TX ring and command slot header
*/
slot - > tx = mvi - > tx_prod ;
mvi - > tx [ mvi - > tx_prod ] = cpu_to_le32 ( ( TXQ_CMD_SMP < < TXQ_CMD_SHIFT ) |
TXQ_MODE_I | tag |
( sas_port - > phy_mask < < TXQ_PHY_SHIFT ) ) ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:44:01 +04:00
hdr - > flags | = flags ;
hdr - > lens = cpu_to_le32 ( ( ( resp_len / 4 ) < < 16 ) | ( ( req_len - 4 ) / 4 ) ) ;
hdr - > tags = cpu_to_le32 ( tag ) ;
hdr - > data_len = 0 ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:44:01 +04:00
/* generate open address frame hdr (first 12 bytes) */
2009-05-09 01:46:40 +04:00
/* initiator, SMP, ftype 1h */
buf_oaf [ 0 ] = ( 1 < < 7 ) | ( PROTOCOL_SMP < < 4 ) | 0x01 ;
buf_oaf [ 1 ] = dev - > linkrate & 0xf ;
2009-05-09 01:44:01 +04:00
* ( u16 * ) ( buf_oaf + 2 ) = 0xFFFF ; /* SAS SPEC */
2009-05-09 01:46:40 +04:00
memcpy ( buf_oaf + 4 , dev - > sas_addr , SAS_ADDR_SIZE ) ;
2009-05-09 01:44:01 +04:00
/* fill in PRD (scatter/gather) table, if any */
2009-05-09 01:46:40 +04:00
MVS_CHIP_DISP - > make_prd ( task - > scatter , tei - > n_elem , buf_prd ) ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:44:01 +04:00
# if _MV_DUMP
/* copy cmd table */
from = kmap_atomic ( sg_page ( sg_req ) , KM_IRQ0 ) ;
memcpy ( buf_cmd , from + sg_req - > offset , req_len ) ;
kunmap_atomic ( from , KM_IRQ0 ) ;
# endif
2007-10-26 04:58:22 +04:00
return 0 ;
2009-05-09 01:44:01 +04:00
err_out_2 :
2009-05-09 01:46:40 +04:00
dma_unmap_sg ( mvi - > dev , & tei - > task - > smp_task . smp_resp , 1 ,
2009-05-09 01:44:01 +04:00
PCI_DMA_FROMDEVICE ) ;
2007-10-26 04:58:22 +04:00
err_out :
2009-05-09 01:46:40 +04:00
dma_unmap_sg ( mvi - > dev , & tei - > task - > smp_task . smp_req , 1 ,
2009-05-09 01:44:01 +04:00
PCI_DMA_TODEVICE ) ;
2008-02-23 16:15:27 +03:00
return rc ;
}
2009-05-09 01:44:01 +04:00
static u32 mvs_get_ncq_tag ( struct sas_task * task , u32 * tag )
2008-02-23 16:15:27 +03:00
{
2009-05-09 01:44:01 +04:00
struct ata_queued_cmd * qc = task - > uldd_task ;
2008-02-23 16:15:27 +03:00
2009-05-09 01:44:01 +04:00
if ( qc ) {
if ( qc - > tf . command = = ATA_CMD_FPDMA_WRITE | |
qc - > tf . command = = ATA_CMD_FPDMA_READ ) {
* tag = qc - > tag ;
return 1 ;
}
2008-02-23 16:15:27 +03:00
}
2009-05-09 01:44:01 +04:00
return 0 ;
2008-02-23 16:15:27 +03:00
}
2009-05-09 01:44:01 +04:00
static int mvs_task_prep_ata ( struct mvs_info * mvi ,
struct mvs_task_exec_info * tei )
2007-10-26 04:58:22 +04:00
{
struct sas_task * task = tei - > task ;
struct domain_device * dev = task - > dev ;
2009-05-15 04:41:21 +04:00
struct mvs_device * mvi_dev = dev - > lldd_dev ;
2007-10-26 04:58:22 +04:00
struct mvs_cmd_hdr * hdr = tei - > hdr ;
struct asd_sas_port * sas_port = dev - > port ;
2008-02-23 16:15:27 +03:00
struct mvs_slot_info * slot ;
2009-05-09 01:46:40 +04:00
void * buf_prd ;
u32 tag = tei - > tag , hdr_tag ;
u32 flags , del_q ;
2007-10-26 04:58:22 +04:00
void * buf_tmp ;
u8 * buf_cmd , * buf_oaf ;
dma_addr_t buf_tmp_dma ;
2008-02-23 16:15:27 +03:00
u32 i , req_len , resp_len ;
const u32 max_resp_len = SB_RFB_MAX ;
2009-05-09 01:46:40 +04:00
if ( mvs_assign_reg_set ( mvi , mvi_dev ) = = MVS_ID_NOT_MAPPED ) {
mv_dprintk ( " Have not enough regiset for dev %d. \n " ,
mvi_dev - > device_id ) ;
2008-02-23 16:15:27 +03:00
return - EBUSY ;
2009-05-09 01:46:40 +04:00
}
2008-02-23 16:15:27 +03:00
slot = & mvi - > slot_info [ tag ] ;
slot - > tx = mvi - > tx_prod ;
2009-05-09 01:46:40 +04:00
del_q = TXQ_MODE_I | tag |
( TXQ_CMD_STP < < TXQ_CMD_SHIFT ) |
( sas_port - > phy_mask < < TXQ_PHY_SHIFT ) |
( mvi_dev - > taskfileset < < TXQ_SRS_SHIFT ) ;
mvi - > tx [ mvi - > tx_prod ] = cpu_to_le32 ( del_q ) ;
# ifndef DISABLE_HOTPLUG_DMA_FIX
if ( task - > data_dir = = DMA_FROM_DEVICE )
flags = ( MVS_CHIP_DISP - > prd_count ( ) < < MCH_PRD_LEN_SHIFT ) ;
else
flags = ( tei - > n_elem < < MCH_PRD_LEN_SHIFT ) ;
# else
flags = ( tei - > n_elem < < MCH_PRD_LEN_SHIFT ) ;
# endif
2007-10-26 04:58:22 +04:00
if ( task - > ata_task . use_ncq )
flags | = MCH_FPDMA ;
2008-02-23 16:15:27 +03:00
if ( dev - > sata_dev . command_set = = ATAPI_COMMAND_SET ) {
if ( task - > ata_task . fis . command ! = ATA_CMD_ID_ATAPI )
flags | = MCH_ATAPI ;
}
2007-10-26 04:58:22 +04:00
/* FIXME: fill in port multiplier number */
hdr - > flags = cpu_to_le32 ( flags ) ;
2008-02-23 16:15:27 +03:00
/* FIXME: the low order order 5 bits for the TAG if enable NCQ */
2009-05-09 01:46:40 +04:00
if ( task - > ata_task . use_ncq & & mvs_get_ncq_tag ( task , & hdr_tag ) )
task - > ata_task . fis . sector_count | = ( u8 ) ( hdr_tag < < 3 ) ;
2008-03-27 09:54:50 +03:00
else
2009-05-09 01:46:40 +04:00
hdr_tag = tag ;
hdr - > tags = cpu_to_le32 ( hdr_tag ) ;
2007-10-26 04:58:22 +04:00
hdr - > data_len = cpu_to_le32 ( task - > total_xfer_len ) ;
/*
* arrange MVS_SLOT_BUF_SZ - sized DMA buffer according to our needs
*/
2008-02-23 16:15:27 +03:00
/* region 1: command table area (MVS_ATA_CMD_SZ bytes) ************** */
buf_cmd = buf_tmp = slot - > buf ;
2007-10-26 04:58:22 +04:00
buf_tmp_dma = slot - > buf_dma ;
hdr - > cmd_tbl = cpu_to_le64 ( buf_tmp_dma ) ;
buf_tmp + = MVS_ATA_CMD_SZ ;
buf_tmp_dma + = MVS_ATA_CMD_SZ ;
2008-02-23 16:15:27 +03:00
# if _MV_DUMP
slot - > cmd_size = MVS_ATA_CMD_SZ ;
# endif
2007-10-26 04:58:22 +04:00
2008-02-23 16:15:27 +03:00
/* region 2: open address frame area (MVS_OAF_SZ bytes) ********* */
2007-10-26 04:58:22 +04:00
/* used for STP. unused for SATA? */
buf_oaf = buf_tmp ;
hdr - > open_frame = cpu_to_le64 ( buf_tmp_dma ) ;
buf_tmp + = MVS_OAF_SZ ;
buf_tmp_dma + = MVS_OAF_SZ ;
2008-02-23 16:15:27 +03:00
/* region 3: PRD table ********************************************* */
2007-10-26 04:58:22 +04:00
buf_prd = buf_tmp ;
2009-05-09 01:46:40 +04:00
2008-02-23 16:15:27 +03:00
if ( tei - > n_elem )
hdr - > prd_tbl = cpu_to_le64 ( buf_tmp_dma ) ;
else
hdr - > prd_tbl = 0 ;
2009-05-09 01:46:40 +04:00
i = MVS_CHIP_DISP - > prd_size ( ) * MVS_CHIP_DISP - > prd_count ( ) ;
2007-10-26 04:58:22 +04:00
buf_tmp + = i ;
buf_tmp_dma + = i ;
2008-02-23 16:15:27 +03:00
/* region 4: status buffer (larger the PRD, smaller this buf) ****** */
2007-10-26 04:58:22 +04:00
/* FIXME: probably unused, for SATA. kept here just in case
* we get a STP / SATA error information record
*/
slot - > response = buf_tmp ;
hdr - > status_buf = cpu_to_le64 ( buf_tmp_dma ) ;
2009-05-09 01:46:40 +04:00
if ( mvi - > flags & MVF_FLAG_SOC )
hdr - > reserved [ 0 ] = 0 ;
2007-10-26 04:58:22 +04:00
2008-02-23 16:15:27 +03:00
req_len = sizeof ( struct host_to_dev_fis ) ;
2007-10-26 04:58:22 +04:00
resp_len = MVS_SLOT_BUF_SZ - MVS_ATA_CMD_SZ -
2008-02-23 16:15:27 +03:00
sizeof ( struct mvs_err_info ) - i ;
2007-10-26 04:58:22 +04:00
/* request, response lengths */
2008-02-23 16:15:27 +03:00
resp_len = min ( resp_len , max_resp_len ) ;
2007-10-26 04:58:22 +04:00
hdr - > lens = cpu_to_le32 ( ( ( resp_len / 4 ) < < 16 ) | ( req_len / 4 ) ) ;
2009-05-09 01:46:40 +04:00
if ( likely ( ! task - > ata_task . device_control_reg_update ) )
task - > ata_task . fis . flags | = 0x80 ; /* C=1: update ATA cmd reg */
2007-10-26 04:58:22 +04:00
/* fill in command FIS and ATAPI CDB */
2008-02-23 16:15:27 +03:00
memcpy ( buf_cmd , & task - > ata_task . fis , sizeof ( struct host_to_dev_fis ) ) ;
if ( dev - > sata_dev . command_set = = ATAPI_COMMAND_SET )
memcpy ( buf_cmd + STP_ATAPI_CMD ,
task - > ata_task . atapi_packet , 16 ) ;
/* generate open address frame hdr (first 12 bytes) */
2009-05-09 01:46:40 +04:00
/* initiator, STP, ftype 1h */
buf_oaf [ 0 ] = ( 1 < < 7 ) | ( PROTOCOL_STP < < 4 ) | 0x1 ;
buf_oaf [ 1 ] = dev - > linkrate & 0xf ;
* ( u16 * ) ( buf_oaf + 2 ) = cpu_to_be16 ( mvi_dev - > device_id + 1 ) ;
memcpy ( buf_oaf + 4 , dev - > sas_addr , SAS_ADDR_SIZE ) ;
2007-10-26 04:58:22 +04:00
/* fill in PRD (scatter/gather) table, if any */
2009-05-09 01:46:40 +04:00
MVS_CHIP_DISP - > make_prd ( task - > scatter , tei - > n_elem , buf_prd ) ;
# ifndef DISABLE_HOTPLUG_DMA_FIX
if ( task - > data_dir = = DMA_FROM_DEVICE )
MVS_CHIP_DISP - > dma_fix ( mvi - > bulk_buffer_dma ,
TRASH_BUCKET_SIZE , tei - > n_elem , buf_prd ) ;
# endif
2007-10-26 04:58:22 +04:00
return 0 ;
}
static int mvs_task_prep_ssp ( struct mvs_info * mvi ,
2009-05-09 01:46:40 +04:00
struct mvs_task_exec_info * tei , int is_tmf ,
struct mvs_tmf_task * tmf )
2007-10-26 04:58:22 +04:00
{
struct sas_task * task = tei - > task ;
struct mvs_cmd_hdr * hdr = tei - > hdr ;
2008-02-23 16:15:27 +03:00
struct mvs_port * port = tei - > port ;
2009-05-09 01:46:40 +04:00
struct domain_device * dev = task - > dev ;
2009-05-15 04:41:21 +04:00
struct mvs_device * mvi_dev = dev - > lldd_dev ;
2009-05-09 01:46:40 +04:00
struct asd_sas_port * sas_port = dev - > port ;
2007-10-26 04:58:22 +04:00
struct mvs_slot_info * slot ;
2009-05-09 01:46:40 +04:00
void * buf_prd ;
2007-10-26 04:58:22 +04:00
struct ssp_frame_hdr * ssp_hdr ;
void * buf_tmp ;
u8 * buf_cmd , * buf_oaf , fburst = 0 ;
dma_addr_t buf_tmp_dma ;
u32 flags ;
2008-02-23 16:15:27 +03:00
u32 resp_len , req_len , i , tag = tei - > tag ;
const u32 max_resp_len = SB_RFB_MAX ;
2009-05-09 01:46:40 +04:00
u32 phy_mask ;
2007-10-26 04:58:22 +04:00
slot = & mvi - > slot_info [ tag ] ;
2009-05-09 01:46:40 +04:00
phy_mask = ( ( port - > wide_port_phymap ) ? port - > wide_port_phymap :
sas_port - > phy_mask ) & TXQ_PHY_MASK ;
2008-02-23 16:15:27 +03:00
slot - > tx = mvi - > tx_prod ;
mvi - > tx [ mvi - > tx_prod ] = cpu_to_le32 ( TXQ_MODE_I | tag |
( TXQ_CMD_SSP < < TXQ_CMD_SHIFT ) |
2008-03-27 09:54:50 +03:00
( phy_mask < < TXQ_PHY_SHIFT ) ) ;
2007-10-26 04:58:22 +04:00
flags = MCH_RETRY ;
if ( task - > ssp_task . enable_first_burst ) {
flags | = MCH_FBURST ;
fburst = ( 1 < < 7 ) ;
}
2009-05-11 16:01:55 +04:00
if ( is_tmf )
flags | = ( MCH_SSP_FR_TASK < < MCH_SSP_FR_TYPE_SHIFT ) ;
else
flags | = ( MCH_SSP_FR_CMD < < MCH_SSP_FR_TYPE_SHIFT ) ;
hdr - > flags = cpu_to_le32 ( flags | ( tei - > n_elem < < MCH_PRD_LEN_SHIFT ) ) ;
2007-10-26 04:58:22 +04:00
hdr - > tags = cpu_to_le32 ( tag ) ;
hdr - > data_len = cpu_to_le32 ( task - > total_xfer_len ) ;
/*
* arrange MVS_SLOT_BUF_SZ - sized DMA buffer according to our needs
*/
2008-02-23 16:15:27 +03:00
/* region 1: command table area (MVS_SSP_CMD_SZ bytes) ************** */
buf_cmd = buf_tmp = slot - > buf ;
2007-10-26 04:58:22 +04:00
buf_tmp_dma = slot - > buf_dma ;
hdr - > cmd_tbl = cpu_to_le64 ( buf_tmp_dma ) ;
buf_tmp + = MVS_SSP_CMD_SZ ;
buf_tmp_dma + = MVS_SSP_CMD_SZ ;
2008-02-23 16:15:27 +03:00
# if _MV_DUMP
slot - > cmd_size = MVS_SSP_CMD_SZ ;
# endif
2007-10-26 04:58:22 +04:00
2008-02-23 16:15:27 +03:00
/* region 2: open address frame area (MVS_OAF_SZ bytes) ********* */
2007-10-26 04:58:22 +04:00
buf_oaf = buf_tmp ;
hdr - > open_frame = cpu_to_le64 ( buf_tmp_dma ) ;
buf_tmp + = MVS_OAF_SZ ;
buf_tmp_dma + = MVS_OAF_SZ ;
2008-02-23 16:15:27 +03:00
/* region 3: PRD table ********************************************* */
2007-10-26 04:58:22 +04:00
buf_prd = buf_tmp ;
2008-02-23 16:15:27 +03:00
if ( tei - > n_elem )
hdr - > prd_tbl = cpu_to_le64 ( buf_tmp_dma ) ;
else
hdr - > prd_tbl = 0 ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
i = MVS_CHIP_DISP - > prd_size ( ) * tei - > n_elem ;
2007-10-26 04:58:22 +04:00
buf_tmp + = i ;
buf_tmp_dma + = i ;
2008-02-23 16:15:27 +03:00
/* region 4: status buffer (larger the PRD, smaller this buf) ****** */
2007-10-26 04:58:22 +04:00
slot - > response = buf_tmp ;
hdr - > status_buf = cpu_to_le64 ( buf_tmp_dma ) ;
2009-05-09 01:46:40 +04:00
if ( mvi - > flags & MVF_FLAG_SOC )
hdr - > reserved [ 0 ] = 0 ;
2007-10-26 04:58:22 +04:00
resp_len = MVS_SLOT_BUF_SZ - MVS_SSP_CMD_SZ - MVS_OAF_SZ -
2008-02-23 16:15:27 +03:00
sizeof ( struct mvs_err_info ) - i ;
resp_len = min ( resp_len , max_resp_len ) ;
req_len = sizeof ( struct ssp_frame_hdr ) + 28 ;
2007-10-26 04:58:22 +04:00
/* request, response lengths */
hdr - > lens = cpu_to_le32 ( ( ( resp_len / 4 ) < < 16 ) | ( req_len / 4 ) ) ;
/* generate open address frame hdr (first 12 bytes) */
2009-05-09 01:46:40 +04:00
/* initiator, SSP, ftype 1h */
buf_oaf [ 0 ] = ( 1 < < 7 ) | ( PROTOCOL_SSP < < 4 ) | 0x1 ;
buf_oaf [ 1 ] = dev - > linkrate & 0xf ;
* ( u16 * ) ( buf_oaf + 2 ) = cpu_to_be16 ( mvi_dev - > device_id + 1 ) ;
memcpy ( buf_oaf + 4 , dev - > sas_addr , SAS_ADDR_SIZE ) ;
2007-10-26 04:58:22 +04:00
2008-02-23 16:15:27 +03:00
/* fill in SSP frame header (Command Table.SSP frame header) */
ssp_hdr = ( struct ssp_frame_hdr * ) buf_cmd ;
2009-05-09 01:46:40 +04:00
if ( is_tmf )
ssp_hdr - > frame_type = SSP_TASK ;
else
ssp_hdr - > frame_type = SSP_COMMAND ;
memcpy ( ssp_hdr - > hashed_dest_addr , dev - > hashed_sas_addr ,
2007-10-26 04:58:22 +04:00
HASHED_SAS_ADDR_SIZE ) ;
memcpy ( ssp_hdr - > hashed_src_addr ,
2009-05-09 01:46:40 +04:00
dev - > hashed_sas_addr , HASHED_SAS_ADDR_SIZE ) ;
2007-10-26 04:58:22 +04:00
ssp_hdr - > tag = cpu_to_be16 ( tag ) ;
2009-05-09 01:46:40 +04:00
/* fill in IU for TASK and Command Frame */
2007-10-26 04:58:22 +04:00
buf_cmd + = sizeof ( * ssp_hdr ) ;
memcpy ( buf_cmd , & task - > ssp_task . LUN , 8 ) ;
2009-05-09 01:46:40 +04:00
if ( ssp_hdr - > frame_type ! = SSP_TASK ) {
buf_cmd [ 9 ] = fburst | task - > ssp_task . task_attr |
( task - > ssp_task . task_prio < < 3 ) ;
memcpy ( buf_cmd + 12 , & task - > ssp_task . cdb , 16 ) ;
} else {
buf_cmd [ 10 ] = tmf - > tmf ;
switch ( tmf - > tmf ) {
case TMF_ABORT_TASK :
case TMF_QUERY_TASK :
buf_cmd [ 12 ] =
( tmf - > tag_of_task_to_be_managed > > 8 ) & 0xff ;
buf_cmd [ 13 ] =
tmf - > tag_of_task_to_be_managed & 0xff ;
break ;
default :
break ;
}
2007-10-26 04:58:22 +04:00
}
2009-05-09 01:46:40 +04:00
/* fill in PRD (scatter/gather) table, if any */
MVS_CHIP_DISP - > make_prd ( task - > scatter , tei - > n_elem , buf_prd ) ;
2007-10-26 04:58:22 +04:00
return 0 ;
}
2009-05-09 01:46:40 +04:00
# define DEV_IS_GONE(mvi_dev) ((!mvi_dev || (mvi_dev->dev_type == NO_DEVICE)))
static int mvs_task_exec ( struct sas_task * task , const int num , gfp_t gfp_flags ,
2009-05-11 16:05:26 +04:00
struct completion * completion , int is_tmf ,
struct mvs_tmf_task * tmf )
2007-10-26 04:58:22 +04:00
{
2008-02-23 16:15:27 +03:00
struct domain_device * dev = task - > dev ;
2009-05-11 18:19:25 +04:00
struct mvs_device * mvi_dev = ( struct mvs_device * ) dev - > lldd_dev ;
struct mvs_info * mvi = mvi_dev - > mvi_info ;
2007-10-26 04:58:22 +04:00
struct mvs_task_exec_info tei ;
2008-02-23 16:15:27 +03:00
struct sas_task * t = task ;
2008-03-27 09:54:50 +03:00
struct mvs_slot_info * slot ;
2008-02-23 16:15:27 +03:00
u32 tag = 0xdeadbeef , rc , n_elem = 0 ;
u32 n = num , pass = 0 ;
2009-05-09 01:46:40 +04:00
unsigned long flags = 0 ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
if ( ! dev - > port ) {
struct task_status_struct * tsm = & t - > task_status ;
tsm - > resp = SAS_TASK_UNDELIVERED ;
tsm - > stat = SAS_PHY_DOWN ;
t - > task_done ( t ) ;
return 0 ;
}
2009-05-11 16:05:26 +04:00
spin_lock_irqsave ( & mvi - > lock , flags ) ;
2008-02-23 16:15:27 +03:00
do {
2008-03-27 09:54:50 +03:00
dev = t - > dev ;
2009-05-15 04:41:21 +04:00
mvi_dev = dev - > lldd_dev ;
2009-05-09 01:46:40 +04:00
if ( DEV_IS_GONE ( mvi_dev ) ) {
if ( mvi_dev )
mv_dprintk ( " device %d not ready. \n " ,
mvi_dev - > device_id ) ;
else
mv_dprintk ( " device %016llx not ready. \n " ,
SAS_ADDR ( dev - > sas_addr ) ) ;
rc = SAS_PHY_DOWN ;
goto out_done ;
}
if ( dev - > port - > id > = mvi - > chip - > n_phy )
tei . port = & mvi - > port [ dev - > port - > id - mvi - > chip - > n_phy ] ;
else
tei . port = & mvi - > port [ dev - > port - > id ] ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:44:01 +04:00
if ( ! tei . port - > port_attached ) {
if ( sas_protocol_ata ( t - > task_proto ) ) {
2009-05-09 01:46:40 +04:00
mv_dprintk ( " port %d does not "
" attached device. \n " , dev - > port - > id ) ;
2009-05-09 01:44:01 +04:00
rc = SAS_PHY_DOWN ;
goto out_done ;
} else {
struct task_status_struct * ts = & t - > task_status ;
ts - > resp = SAS_TASK_UNDELIVERED ;
ts - > stat = SAS_PHY_DOWN ;
t - > task_done ( t ) ;
if ( n > 1 )
t = list_entry ( t - > list . next ,
struct sas_task , list ) ;
continue ;
}
}
if ( ! sas_protocol_ata ( t - > task_proto ) ) {
if ( t - > num_scatter ) {
2009-05-09 01:46:40 +04:00
n_elem = dma_map_sg ( mvi - > dev ,
t - > scatter ,
2009-05-09 01:44:01 +04:00
t - > num_scatter ,
t - > data_dir ) ;
if ( ! n_elem ) {
rc = - ENOMEM ;
goto err_out ;
}
}
} else {
n_elem = t - > num_scatter ;
}
rc = mvs_tag_alloc ( mvi , & tag ) ;
if ( rc )
goto err_out ;
slot = & mvi - > slot_info [ tag ] ;
2009-05-09 01:46:40 +04:00
2009-05-09 01:44:01 +04:00
t - > lldd_task = NULL ;
slot - > n_elem = n_elem ;
2009-05-09 01:46:40 +04:00
slot - > slot_tag = tag ;
2009-05-09 01:44:01 +04:00
memset ( slot - > buf , 0 , MVS_SLOT_BUF_SZ ) ;
2009-05-09 01:46:40 +04:00
2009-05-09 01:44:01 +04:00
tei . task = t ;
tei . hdr = & mvi - > slot [ tag ] ;
tei . tag = tag ;
tei . n_elem = n_elem ;
switch ( t - > task_proto ) {
case SAS_PROTOCOL_SMP :
rc = mvs_task_prep_smp ( mvi , & tei ) ;
break ;
case SAS_PROTOCOL_SSP :
2009-05-09 01:46:40 +04:00
rc = mvs_task_prep_ssp ( mvi , & tei , is_tmf , tmf ) ;
2009-05-09 01:44:01 +04:00
break ;
case SAS_PROTOCOL_SATA :
case SAS_PROTOCOL_STP :
case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP :
rc = mvs_task_prep_ata ( mvi , & tei ) ;
break ;
default :
2009-05-09 01:46:40 +04:00
dev_printk ( KERN_ERR , mvi - > dev ,
2009-05-09 01:44:01 +04:00
" unknown sas_task proto: 0x%x \n " ,
t - > task_proto ) ;
rc = - EINVAL ;
break ;
}
2009-05-09 01:46:40 +04:00
if ( rc ) {
mv_dprintk ( " rc is %x \n " , rc ) ;
2009-05-09 01:44:01 +04:00
goto err_out_tag ;
2009-05-09 01:46:40 +04:00
}
2009-05-09 01:44:01 +04:00
slot - > task = t ;
slot - > port = tei . port ;
2009-05-15 04:41:21 +04:00
t - > lldd_task = slot ;
2009-05-09 01:46:40 +04:00
list_add_tail ( & slot - > entry , & tei . port - > list ) ;
2009-05-09 01:44:01 +04:00
/* TODO: select normal or high priority */
spin_lock ( & t - > task_state_lock ) ;
t - > task_state_flags | = SAS_TASK_AT_INITIATOR ;
spin_unlock ( & t - > task_state_lock ) ;
mvs_hba_memory_dump ( mvi , tag , t - > task_proto ) ;
2009-05-09 01:46:40 +04:00
mvi_dev - > runing_req + + ;
2009-05-09 01:44:01 +04:00
+ + pass ;
mvi - > tx_prod = ( mvi - > tx_prod + 1 ) & ( MVS_CHIP_SLOT_SZ - 1 ) ;
if ( n > 1 )
t = list_entry ( t - > list . next , struct sas_task , list ) ;
} while ( - - n ) ;
rc = 0 ;
goto out_done ;
err_out_tag :
mvs_tag_free ( mvi , tag ) ;
err_out :
2009-05-09 01:46:40 +04:00
dev_printk ( KERN_ERR , mvi - > dev , " mvsas exec failed[%d]! \n " , rc ) ;
2009-05-09 01:44:01 +04:00
if ( ! sas_protocol_ata ( t - > task_proto ) )
if ( n_elem )
2009-05-09 01:46:40 +04:00
dma_unmap_sg ( mvi - > dev , t - > scatter , n_elem ,
2009-05-09 01:44:01 +04:00
t - > data_dir ) ;
out_done :
2009-05-09 01:46:40 +04:00
if ( likely ( pass ) ) {
MVS_CHIP_DISP - > start_delivery ( mvi ,
( mvi - > tx_prod - 1 ) & ( MVS_CHIP_SLOT_SZ - 1 ) ) ;
}
2009-05-11 16:05:26 +04:00
spin_unlock_irqrestore ( & mvi - > lock , flags ) ;
2009-05-09 01:44:01 +04:00
return rc ;
}
2009-05-09 01:46:40 +04:00
int mvs_queue_command ( struct sas_task * task , const int num ,
gfp_t gfp_flags )
{
2009-05-11 16:05:26 +04:00
return mvs_task_exec ( task , num , gfp_flags , NULL , 0 , NULL ) ;
2009-05-09 01:46:40 +04:00
}
2009-05-09 01:44:01 +04:00
static void mvs_slot_free ( struct mvs_info * mvi , u32 rx_desc )
{
u32 slot_idx = rx_desc & RXQ_SLOT_MASK ;
mvs_tag_clear ( mvi , slot_idx ) ;
}
static void mvs_slot_task_free ( struct mvs_info * mvi , struct sas_task * task ,
struct mvs_slot_info * slot , u32 slot_idx )
{
2009-05-09 01:46:40 +04:00
if ( ! slot - > task )
return ;
2009-05-09 01:44:01 +04:00
if ( ! sas_protocol_ata ( task - > task_proto ) )
if ( slot - > n_elem )
2009-05-09 01:46:40 +04:00
dma_unmap_sg ( mvi - > dev , task - > scatter ,
2009-05-09 01:44:01 +04:00
slot - > n_elem , task - > data_dir ) ;
switch ( task - > task_proto ) {
case SAS_PROTOCOL_SMP :
2009-05-09 01:46:40 +04:00
dma_unmap_sg ( mvi - > dev , & task - > smp_task . smp_resp , 1 ,
2009-05-09 01:44:01 +04:00
PCI_DMA_FROMDEVICE ) ;
2009-05-09 01:46:40 +04:00
dma_unmap_sg ( mvi - > dev , & task - > smp_task . smp_req , 1 ,
2009-05-09 01:44:01 +04:00
PCI_DMA_TODEVICE ) ;
break ;
case SAS_PROTOCOL_SATA :
case SAS_PROTOCOL_STP :
case SAS_PROTOCOL_SSP :
default :
/* do nothing */
break ;
}
2009-05-09 01:46:40 +04:00
list_del_init ( & slot - > entry ) ;
2009-05-09 01:44:01 +04:00
task - > lldd_task = NULL ;
slot - > task = NULL ;
slot - > port = NULL ;
2009-05-09 01:46:40 +04:00
slot - > slot_tag = 0xFFFFFFFF ;
mvs_slot_free ( mvi , slot_idx ) ;
2009-05-09 01:44:01 +04:00
}
static void mvs_update_wideport ( struct mvs_info * mvi , int i )
{
struct mvs_phy * phy = & mvi - > phy [ i ] ;
struct mvs_port * port = phy - > port ;
int j , no ;
2009-05-09 01:46:40 +04:00
for_each_phy ( port - > wide_port_phymap , j , no ) {
if ( j & 1 ) {
MVS_CHIP_DISP - > write_port_cfg_addr ( mvi , no ,
PHYR_WIDE_PORT ) ;
MVS_CHIP_DISP - > write_port_cfg_data ( mvi , no ,
2009-05-09 01:44:01 +04:00
port - > wide_port_phymap ) ;
} else {
2009-05-09 01:46:40 +04:00
MVS_CHIP_DISP - > write_port_cfg_addr ( mvi , no ,
PHYR_WIDE_PORT ) ;
MVS_CHIP_DISP - > write_port_cfg_data ( mvi , no ,
0 ) ;
2009-05-09 01:44:01 +04:00
}
2009-05-09 01:46:40 +04:00
}
2009-05-09 01:44:01 +04:00
}
static u32 mvs_is_phy_ready ( struct mvs_info * mvi , int i )
{
u32 tmp ;
struct mvs_phy * phy = & mvi - > phy [ i ] ;
2009-05-09 01:46:40 +04:00
struct mvs_port * port = phy - > port ;
2009-05-09 01:44:01 +04:00
2009-05-09 01:46:40 +04:00
tmp = MVS_CHIP_DISP - > read_phy_ctl ( mvi , i ) ;
2009-05-09 01:44:01 +04:00
if ( ( tmp & PHY_READY_MASK ) & & ! ( phy - > irq_status & PHYEV_POOF ) ) {
if ( ! port )
phy - > phy_attached = 1 ;
return tmp ;
}
if ( port ) {
if ( phy - > phy_type & PORT_TYPE_SAS ) {
port - > wide_port_phymap & = ~ ( 1U < < i ) ;
if ( ! port - > wide_port_phymap )
port - > port_attached = 0 ;
mvs_update_wideport ( mvi , i ) ;
} else if ( phy - > phy_type & PORT_TYPE_SATA )
port - > port_attached = 0 ;
phy - > port = NULL ;
phy - > phy_attached = 0 ;
phy - > phy_type & = ~ ( PORT_TYPE_SAS | PORT_TYPE_SATA ) ;
}
return 0 ;
}
static void * mvs_get_d2h_reg ( struct mvs_info * mvi , int i , void * buf )
{
u32 * s = ( u32 * ) buf ;
if ( ! s )
return NULL ;
2009-05-09 01:46:40 +04:00
MVS_CHIP_DISP - > write_port_cfg_addr ( mvi , i , PHYR_SATA_SIG3 ) ;
s [ 3 ] = MVS_CHIP_DISP - > read_port_cfg_data ( mvi , i ) ;
2009-05-09 01:44:01 +04:00
2009-05-09 01:46:40 +04:00
MVS_CHIP_DISP - > write_port_cfg_addr ( mvi , i , PHYR_SATA_SIG2 ) ;
s [ 2 ] = MVS_CHIP_DISP - > read_port_cfg_data ( mvi , i ) ;
2009-05-09 01:44:01 +04:00
2009-05-09 01:46:40 +04:00
MVS_CHIP_DISP - > write_port_cfg_addr ( mvi , i , PHYR_SATA_SIG1 ) ;
s [ 1 ] = MVS_CHIP_DISP - > read_port_cfg_data ( mvi , i ) ;
2009-05-09 01:44:01 +04:00
2009-05-09 01:46:40 +04:00
MVS_CHIP_DISP - > write_port_cfg_addr ( mvi , i , PHYR_SATA_SIG0 ) ;
s [ 0 ] = MVS_CHIP_DISP - > read_port_cfg_data ( mvi , i ) ;
/* Workaround: take some ATAPI devices for ATA */
if ( ( ( s [ 1 ] & 0x00FFFFFF ) = = 0x00EB1401 ) & & ( * ( u8 * ) & s [ 3 ] = = 0x01 ) )
s [ 1 ] = 0x00EB1401 | ( * ( ( u8 * ) & s [ 1 ] + 3 ) & 0x10 ) ;
2009-05-09 01:44:01 +04:00
2009-05-15 04:41:21 +04:00
return s ;
2009-05-09 01:44:01 +04:00
}
static u32 mvs_is_sig_fis_received ( u32 irq_status )
{
return irq_status & PHYEV_SIG_FIS ;
}
2009-05-09 01:46:40 +04:00
void mvs_update_phyinfo ( struct mvs_info * mvi , int i , int get_st )
2009-05-09 01:44:01 +04:00
{
struct mvs_phy * phy = & mvi - > phy [ i ] ;
2009-05-09 01:46:40 +04:00
struct sas_identify_frame * id ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
id = ( struct sas_identify_frame * ) phy - > frame_rcvd ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:44:01 +04:00
if ( get_st ) {
2009-05-09 01:46:40 +04:00
phy - > irq_status = MVS_CHIP_DISP - > read_port_irq_stat ( mvi , i ) ;
2009-05-09 01:44:01 +04:00
phy - > phy_status = mvs_is_phy_ready ( mvi , i ) ;
}
2008-02-23 16:15:27 +03:00
2009-05-09 01:44:01 +04:00
if ( phy - > phy_status ) {
2009-05-09 01:46:40 +04:00
int oob_done = 0 ;
struct asd_sas_phy * sas_phy = & mvi - > phy [ i ] . sas_phy ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
oob_done = MVS_CHIP_DISP - > oob_done ( mvi , i ) ;
MVS_CHIP_DISP - > fix_phy_info ( mvi , i , id ) ;
if ( phy - > phy_type & PORT_TYPE_SATA ) {
phy - > identify . target_port_protocols = SAS_PROTOCOL_STP ;
if ( mvs_is_sig_fis_received ( phy - > irq_status ) ) {
phy - > phy_attached = 1 ;
phy - > att_dev_sas_addr =
i + mvi - > id * mvi - > chip - > n_phy ;
if ( oob_done )
sas_phy - > oob_mode = SATA_OOB_MODE ;
phy - > frame_rcvd_size =
sizeof ( struct dev_to_host_fis ) ;
2009-05-15 04:41:21 +04:00
mvs_get_d2h_reg ( mvi , i , id ) ;
2009-05-09 01:46:40 +04:00
} else {
u32 tmp ;
dev_printk ( KERN_DEBUG , mvi - > dev ,
" Phy%d : No sig fis \n " , i ) ;
tmp = MVS_CHIP_DISP - > read_port_irq_mask ( mvi , i ) ;
MVS_CHIP_DISP - > write_port_irq_mask ( mvi , i ,
tmp | PHYEV_SIG_FIS ) ;
phy - > phy_attached = 0 ;
phy - > phy_type & = ~ PORT_TYPE_SATA ;
MVS_CHIP_DISP - > phy_reset ( mvi , i , 0 ) ;
goto out_done ;
}
} else if ( phy - > phy_type & PORT_TYPE_SAS
| | phy - > att_dev_info & PORT_SSP_INIT_MASK ) {
phy - > phy_attached = 1 ;
2009-05-09 01:44:01 +04:00
phy - > identify . device_type =
2009-05-09 01:46:40 +04:00
phy - > att_dev_info & PORT_DEV_TYPE_MASK ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:44:01 +04:00
if ( phy - > identify . device_type = = SAS_END_DEV )
phy - > identify . target_port_protocols =
SAS_PROTOCOL_SSP ;
else if ( phy - > identify . device_type ! = NO_DEVICE )
phy - > identify . target_port_protocols =
SAS_PROTOCOL_SMP ;
2009-05-09 01:46:40 +04:00
if ( oob_done )
2009-05-09 01:44:01 +04:00
sas_phy - > oob_mode = SAS_OOB_MODE ;
phy - > frame_rcvd_size =
sizeof ( struct sas_identify_frame ) ;
}
2009-05-09 01:46:40 +04:00
memcpy ( sas_phy - > attached_sas_addr ,
& phy - > att_dev_sas_addr , SAS_ADDR_SIZE ) ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
if ( MVS_CHIP_DISP - > phy_work_around )
MVS_CHIP_DISP - > phy_work_around ( mvi , i ) ;
2009-05-09 01:44:01 +04:00
}
2009-05-09 01:46:40 +04:00
mv_dprintk ( " port %d attach dev info is %x \n " ,
i + mvi - > id * mvi - > chip - > n_phy , phy - > att_dev_info ) ;
mv_dprintk ( " port %d attach sas addr is %llx \n " ,
i + mvi - > id * mvi - > chip - > n_phy , phy - > att_dev_sas_addr ) ;
2008-03-27 09:54:50 +03:00
out_done :
2009-05-09 01:44:01 +04:00
if ( get_st )
2009-05-09 01:46:40 +04:00
MVS_CHIP_DISP - > write_port_irq_stat ( mvi , i , phy - > irq_status ) ;
2007-10-26 04:58:22 +04:00
}
2009-05-09 01:46:40 +04:00
static void mvs_port_notify_formed ( struct asd_sas_phy * sas_phy , int lock )
2008-02-23 16:15:27 +03:00
{
2009-05-09 01:44:01 +04:00
struct sas_ha_struct * sas_ha = sas_phy - > ha ;
2009-05-09 01:46:40 +04:00
struct mvs_info * mvi = NULL ; int i = 0 , hi ;
2009-05-09 01:44:01 +04:00
struct mvs_phy * phy = sas_phy - > lldd_phy ;
2009-05-09 01:46:40 +04:00
struct asd_sas_port * sas_port = sas_phy - > port ;
struct mvs_port * port ;
unsigned long flags = 0 ;
if ( ! sas_port )
return ;
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
while ( sas_ha - > sas_phy [ i ] ) {
if ( sas_ha - > sas_phy [ i ] = = sas_phy )
break ;
i + + ;
}
hi = i / ( ( struct mvs_prv_info * ) sas_ha - > lldd_ha ) - > n_phy ;
mvi = ( ( struct mvs_prv_info * ) sas_ha - > lldd_ha ) - > mvi [ hi ] ;
if ( sas_port - > id > = mvi - > chip - > n_phy )
port = & mvi - > port [ sas_port - > id - mvi - > chip - > n_phy ] ;
else
port = & mvi - > port [ sas_port - > id ] ;
if ( lock )
spin_lock_irqsave ( & mvi - > lock , flags ) ;
2009-05-09 01:44:01 +04:00
port - > port_attached = 1 ;
phy - > port = port ;
if ( phy - > phy_type & PORT_TYPE_SAS ) {
port - > wide_port_phymap = sas_port - > phy_mask ;
2009-05-09 01:46:40 +04:00
mv_printk ( " set wide port phy map %x \n " , sas_port - > phy_mask ) ;
2009-05-09 01:44:01 +04:00
mvs_update_wideport ( mvi , sas_phy - > id ) ;
2008-02-23 16:15:27 +03:00
}
2009-05-09 01:46:40 +04:00
if ( lock )
spin_unlock_irqrestore ( & mvi - > lock , flags ) ;
2009-05-09 01:44:01 +04:00
}
2009-05-09 01:46:40 +04:00
static void mvs_port_notify_deformed ( struct asd_sas_phy * sas_phy , int lock )
2009-05-09 01:44:01 +04:00
{
2009-05-09 01:46:40 +04:00
/*Nothing*/
2009-05-09 01:44:01 +04:00
}
2009-05-09 01:46:40 +04:00
void mvs_port_formed ( struct asd_sas_phy * sas_phy )
{
mvs_port_notify_formed ( sas_phy , 1 ) ;
2009-05-09 01:44:01 +04:00
}
2009-05-09 01:46:40 +04:00
void mvs_port_deformed ( struct asd_sas_phy * sas_phy )
2009-05-09 01:44:01 +04:00
{
2009-05-09 01:46:40 +04:00
mvs_port_notify_deformed ( sas_phy , 1 ) ;
}
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
struct mvs_device * mvs_alloc_dev ( struct mvs_info * mvi )
{
u32 dev ;
for ( dev = 0 ; dev < MVS_MAX_DEVICES ; dev + + ) {
if ( mvi - > devices [ dev ] . dev_type = = NO_DEVICE ) {
mvi - > devices [ dev ] . device_id = dev ;
return & mvi - > devices [ dev ] ;
}
2008-02-23 16:15:27 +03:00
}
2008-03-27 09:55:04 +03:00
2009-05-09 01:46:40 +04:00
if ( dev = = MVS_MAX_DEVICES )
mv_printk ( " max support %d devices, ignore .. \n " ,
MVS_MAX_DEVICES ) ;
return NULL ;
2008-02-23 16:15:27 +03:00
}
2009-05-09 01:46:40 +04:00
void mvs_free_dev ( struct mvs_device * mvi_dev )
2007-10-26 04:58:22 +04:00
{
2009-05-09 01:46:40 +04:00
u32 id = mvi_dev - > device_id ;
memset ( mvi_dev , 0 , sizeof ( * mvi_dev ) ) ;
mvi_dev - > device_id = id ;
mvi_dev - > dev_type = NO_DEVICE ;
mvi_dev - > dev_status = MVS_DEV_NORMAL ;
mvi_dev - > taskfileset = MVS_ID_NOT_MAPPED ;
}
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
int mvs_dev_found_notify ( struct domain_device * dev , int lock )
{
unsigned long flags = 0 ;
int res = 0 ;
struct mvs_info * mvi = NULL ;
struct domain_device * parent_dev = dev - > parent ;
struct mvs_device * mvi_device ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
mvi = mvs_find_dev_mvi ( dev ) ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
if ( lock )
spin_lock_irqsave ( & mvi - > lock , flags ) ;
mvi_device = mvs_alloc_dev ( mvi ) ;
if ( ! mvi_device ) {
res = - 1 ;
goto found_out ;
2007-10-26 04:58:22 +04:00
}
2009-05-15 04:41:21 +04:00
dev - > lldd_dev = mvi_device ;
2009-05-09 01:46:40 +04:00
mvi_device - > dev_type = dev - > dev_type ;
2009-05-11 18:19:25 +04:00
mvi_device - > mvi_info = mvi ;
2009-05-09 01:46:40 +04:00
if ( parent_dev & & DEV_IS_EXPANDER ( parent_dev - > dev_type ) ) {
int phy_id ;
u8 phy_num = parent_dev - > ex_dev . num_phys ;
struct ex_phy * phy ;
for ( phy_id = 0 ; phy_id < phy_num ; phy_id + + ) {
phy = & parent_dev - > ex_dev . ex_phy [ phy_id ] ;
if ( SAS_ADDR ( phy - > attached_sas_addr ) = =
SAS_ADDR ( dev - > sas_addr ) ) {
mvi_device - > attached_phy = phy_id ;
break ;
}
}
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
if ( phy_id = = phy_num ) {
mv_printk ( " Error: no attached dev:%016llx "
" at ex:%016llx. \n " ,
SAS_ADDR ( dev - > sas_addr ) ,
SAS_ADDR ( parent_dev - > sas_addr ) ) ;
res = - 1 ;
}
2009-05-09 01:44:01 +04:00
}
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
found_out :
if ( lock )
spin_unlock_irqrestore ( & mvi - > lock , flags ) ;
return res ;
}
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
int mvs_dev_found ( struct domain_device * dev )
{
return mvs_dev_found_notify ( dev , 1 ) ;
}
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
void mvs_dev_gone_notify ( struct domain_device * dev , int lock )
{
unsigned long flags = 0 ;
2009-05-15 04:41:21 +04:00
struct mvs_device * mvi_dev = dev - > lldd_dev ;
2009-05-11 18:19:25 +04:00
struct mvs_info * mvi = mvi_dev - > mvi_info ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
if ( lock )
spin_lock_irqsave ( & mvi - > lock , flags ) ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
if ( mvi_dev ) {
mv_dprintk ( " found dev[%d:%x] is gone. \n " ,
mvi_dev - > device_id , mvi_dev - > dev_type ) ;
mvs_free_reg_set ( mvi , mvi_dev ) ;
mvs_free_dev ( mvi_dev ) ;
} else {
mv_dprintk ( " found dev has gone. \n " ) ;
2007-10-26 04:58:22 +04:00
}
2009-05-09 01:46:40 +04:00
dev - > lldd_dev = NULL ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
if ( lock )
spin_unlock_irqrestore ( & mvi - > lock , flags ) ;
2007-10-26 04:58:22 +04:00
}
2009-05-09 01:46:40 +04:00
void mvs_dev_gone ( struct domain_device * dev )
{
mvs_dev_gone_notify ( dev , 1 ) ;
}
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
static struct sas_task * mvs_alloc_task ( void )
{
struct sas_task * task = kzalloc ( sizeof ( struct sas_task ) , GFP_KERNEL ) ;
if ( task ) {
INIT_LIST_HEAD ( & task - > list ) ;
spin_lock_init ( & task - > task_state_lock ) ;
task - > task_state_flags = SAS_TASK_STATE_PENDING ;
init_timer ( & task - > timer ) ;
init_completion ( & task - > completion ) ;
2007-10-26 04:58:22 +04:00
}
2009-05-09 01:46:40 +04:00
return task ;
2009-05-09 01:44:01 +04:00
}
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
static void mvs_free_task ( struct sas_task * task )
2009-05-09 01:44:01 +04:00
{
2009-05-09 01:46:40 +04:00
if ( task ) {
BUG_ON ( ! list_empty ( & task - > list ) ) ;
kfree ( task ) ;
2007-10-26 04:58:22 +04:00
}
2009-05-09 01:46:40 +04:00
}
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
static void mvs_task_done ( struct sas_task * task )
{
if ( ! del_timer ( & task - > timer ) )
return ;
complete ( & task - > completion ) ;
2007-10-26 04:58:22 +04:00
}
2009-05-09 01:46:40 +04:00
static void mvs_tmf_timedout ( unsigned long data )
2007-10-26 04:58:22 +04:00
{
2009-05-09 01:46:40 +04:00
struct sas_task * task = ( struct sas_task * ) data ;
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
task - > task_state_flags | = SAS_TASK_STATE_ABORTED ;
complete ( & task - > completion ) ;
}
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
/* XXX */
# define MVS_TASK_TIMEOUT 20
static int mvs_exec_internal_tmf_task ( struct domain_device * dev ,
void * parameter , u32 para_len , struct mvs_tmf_task * tmf )
{
int res , retry ;
struct sas_task * task = NULL ;
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
for ( retry = 0 ; retry < 3 ; retry + + ) {
task = mvs_alloc_task ( ) ;
if ( ! task )
return - ENOMEM ;
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
task - > dev = dev ;
task - > task_proto = dev - > tproto ;
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
memcpy ( & task - > ssp_task , parameter , para_len ) ;
task - > task_done = mvs_task_done ;
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
task - > timer . data = ( unsigned long ) task ;
task - > timer . function = mvs_tmf_timedout ;
task - > timer . expires = jiffies + MVS_TASK_TIMEOUT * HZ ;
add_timer ( & task - > timer ) ;
2008-02-23 16:15:27 +03:00
2009-05-11 16:05:26 +04:00
res = mvs_task_exec ( task , 1 , GFP_KERNEL , NULL , 1 , tmf ) ;
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
if ( res ) {
del_timer ( & task - > timer ) ;
mv_printk ( " executing internel task failed:%d \n " , res ) ;
goto ex_err ;
}
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
wait_for_completion ( & task - > completion ) ;
res = - TMF_RESP_FUNC_FAILED ;
/* Even TMF timed out, return direct. */
if ( ( task - > task_state_flags & SAS_TASK_STATE_ABORTED ) ) {
if ( ! ( task - > task_state_flags & SAS_TASK_STATE_DONE ) ) {
mv_printk ( " TMF task[%x] timeout. \n " , tmf - > tmf ) ;
goto ex_err ;
}
}
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
if ( task - > task_status . resp = = SAS_TASK_COMPLETE & &
task - > task_status . stat = = SAM_GOOD ) {
res = TMF_RESP_FUNC_COMPLETE ;
break ;
}
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
if ( task - > task_status . resp = = SAS_TASK_COMPLETE & &
task - > task_status . stat = = SAS_DATA_UNDERRUN ) {
/* no error, but return the number of bytes of
* underrun */
res = task - > task_status . residual ;
break ;
}
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
if ( task - > task_status . resp = = SAS_TASK_COMPLETE & &
task - > task_status . stat = = SAS_DATA_OVERRUN ) {
mv_dprintk ( " blocked task error. \n " ) ;
res = - EMSGSIZE ;
break ;
} else {
mv_dprintk ( " task to dev %016llx response: 0x%x "
" status 0x%x \n " ,
SAS_ADDR ( dev - > sas_addr ) ,
task - > task_status . resp ,
task - > task_status . stat ) ;
mvs_free_task ( task ) ;
task = NULL ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:44:01 +04:00
}
}
2009-05-09 01:46:40 +04:00
ex_err :
BUG_ON ( retry = = 3 & & task ! = NULL ) ;
if ( task ! = NULL )
mvs_free_task ( task ) ;
return res ;
2009-05-09 01:44:01 +04:00
}
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
static int mvs_debug_issue_ssp_tmf ( struct domain_device * dev ,
u8 * lun , struct mvs_tmf_task * tmf )
2009-05-09 01:44:01 +04:00
{
2009-05-09 01:46:40 +04:00
struct sas_ssp_task ssp_task ;
DECLARE_COMPLETION_ONSTACK ( completion ) ;
if ( ! ( dev - > tproto & SAS_PROTOCOL_SSP ) )
return TMF_RESP_FUNC_ESUPP ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
strncpy ( ( u8 * ) & ssp_task . LUN , lun , 8 ) ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
return mvs_exec_internal_tmf_task ( dev , & ssp_task ,
sizeof ( ssp_task ) , tmf ) ;
}
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
/* Standard mandates link reset for ATA (type 0)
and hard reset for SSP ( type 1 ) , only for RECOVERY */
static int mvs_debug_I_T_nexus_reset ( struct domain_device * dev )
{
int rc ;
struct sas_phy * phy = sas_find_local_phy ( dev ) ;
int reset_type = ( dev - > dev_type = = SATA_DEV | |
( dev - > tproto & SAS_PROTOCOL_STP ) ) ? 0 : 1 ;
rc = sas_phy_reset ( phy , reset_type ) ;
msleep ( 2000 ) ;
return rc ;
}
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
/* mandatory SAM-3 */
int mvs_lu_reset ( struct domain_device * dev , u8 * lun )
{
unsigned long flags ;
int i , phyno [ WIDE_PORT_MAX_PHY ] , num , rc = TMF_RESP_FUNC_FAILED ;
struct mvs_tmf_task tmf_task ;
2009-05-15 04:41:21 +04:00
struct mvs_device * mvi_dev = dev - > lldd_dev ;
2009-05-11 18:19:25 +04:00
struct mvs_info * mvi = mvi_dev - > mvi_info ;
2009-05-09 01:46:40 +04:00
tmf_task . tmf = TMF_LU_RESET ;
mvi_dev - > dev_status = MVS_DEV_EH ;
rc = mvs_debug_issue_ssp_tmf ( dev , lun , & tmf_task ) ;
if ( rc = = TMF_RESP_FUNC_COMPLETE ) {
num = mvs_find_dev_phyno ( dev , phyno ) ;
spin_lock_irqsave ( & mvi - > lock , flags ) ;
for ( i = 0 ; i < num ; i + + )
mvs_release_task ( mvi , phyno [ i ] , dev ) ;
spin_unlock_irqrestore ( & mvi - > lock , flags ) ;
2009-05-09 01:44:01 +04:00
}
2009-05-09 01:46:40 +04:00
/* If failed, fall-through I_T_Nexus reset */
mv_printk ( " %s for device[%x]:rc= %d \n " , __func__ ,
mvi_dev - > device_id , rc ) ;
return rc ;
}
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
int mvs_I_T_nexus_reset ( struct domain_device * dev )
{
unsigned long flags ;
int i , phyno [ WIDE_PORT_MAX_PHY ] , num , rc = TMF_RESP_FUNC_FAILED ;
2009-05-11 18:19:25 +04:00
struct mvs_device * mvi_dev = ( struct mvs_device * ) dev - > lldd_dev ;
struct mvs_info * mvi = mvi_dev - > mvi_info ;
2009-05-09 01:46:40 +04:00
if ( mvi_dev - > dev_status ! = MVS_DEV_EH )
return TMF_RESP_FUNC_COMPLETE ;
rc = mvs_debug_I_T_nexus_reset ( dev ) ;
mv_printk ( " %s for device[%x]:rc= %d \n " ,
__func__ , mvi_dev - > device_id , rc ) ;
/* housekeeper */
num = mvs_find_dev_phyno ( dev , phyno ) ;
spin_lock_irqsave ( & mvi - > lock , flags ) ;
for ( i = 0 ; i < num ; i + + )
mvs_release_task ( mvi , phyno [ i ] , dev ) ;
spin_unlock_irqrestore ( & mvi - > lock , flags ) ;
return rc ;
}
/* optional SAM-3 */
int mvs_query_task ( struct sas_task * task )
{
u32 tag ;
struct scsi_lun lun ;
struct mvs_tmf_task tmf_task ;
int rc = TMF_RESP_FUNC_FAILED ;
if ( task - > lldd_task & & task - > task_proto & SAS_PROTOCOL_SSP ) {
struct scsi_cmnd * cmnd = ( struct scsi_cmnd * ) task - > uldd_task ;
struct domain_device * dev = task - > dev ;
2009-05-11 18:19:25 +04:00
struct mvs_device * mvi_dev = ( struct mvs_device * ) dev - > lldd_dev ;
struct mvs_info * mvi = mvi_dev - > mvi_info ;
2009-05-09 01:46:40 +04:00
int_to_scsilun ( cmnd - > device - > lun , & lun ) ;
rc = mvs_find_tag ( mvi , task , & tag ) ;
if ( rc = = 0 ) {
rc = TMF_RESP_FUNC_FAILED ;
2009-05-09 01:44:01 +04:00
return rc ;
2009-05-09 01:46:40 +04:00
}
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
tmf_task . tmf = TMF_QUERY_TASK ;
tmf_task . tag_of_task_to_be_managed = cpu_to_le16 ( tag ) ;
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
rc = mvs_debug_issue_ssp_tmf ( dev , lun . scsi_lun , & tmf_task ) ;
switch ( rc ) {
/* The task is still in Lun, release it then */
case TMF_RESP_FUNC_SUCC :
/* The task is not in Lun or failed, reset the phy */
case TMF_RESP_FUNC_FAILED :
case TMF_RESP_FUNC_COMPLETE :
break ;
}
2009-05-09 01:44:01 +04:00
}
2009-05-09 01:46:40 +04:00
mv_printk ( " %s:rc= %d \n " , __func__ , rc ) ;
return rc ;
2008-02-23 16:15:27 +03:00
}
2009-05-09 01:46:40 +04:00
/* mandatory SAM-3, still need free task/slot info */
int mvs_abort_task ( struct sas_task * task )
2008-02-23 16:15:27 +03:00
{
2009-05-09 01:46:40 +04:00
struct scsi_lun lun ;
struct mvs_tmf_task tmf_task ;
struct domain_device * dev = task - > dev ;
2009-05-11 18:19:25 +04:00
struct mvs_device * mvi_dev = ( struct mvs_device * ) dev - > lldd_dev ;
struct mvs_info * mvi = mvi_dev - > mvi_info ;
2009-05-09 01:46:40 +04:00
int rc = TMF_RESP_FUNC_FAILED ;
unsigned long flags ;
u32 tag ;
2009-05-11 18:19:25 +04:00
2009-05-09 01:46:40 +04:00
if ( mvi - > exp_req )
mvi - > exp_req - - ;
spin_lock_irqsave ( & task - > task_state_lock , flags ) ;
if ( task - > task_state_flags & SAS_TASK_STATE_DONE ) {
spin_unlock_irqrestore ( & task - > task_state_lock , flags ) ;
rc = TMF_RESP_FUNC_COMPLETE ;
goto out ;
2009-05-09 01:44:01 +04:00
}
2009-05-09 01:46:40 +04:00
spin_unlock_irqrestore ( & task - > task_state_lock , flags ) ;
if ( task - > lldd_task & & task - > task_proto & SAS_PROTOCOL_SSP ) {
struct scsi_cmnd * cmnd = ( struct scsi_cmnd * ) task - > uldd_task ;
int_to_scsilun ( cmnd - > device - > lun , & lun ) ;
rc = mvs_find_tag ( mvi , task , & tag ) ;
if ( rc = = 0 ) {
mv_printk ( " No such tag in %s \n " , __func__ ) ;
rc = TMF_RESP_FUNC_FAILED ;
return rc ;
}
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
tmf_task . tmf = TMF_ABORT_TASK ;
tmf_task . tag_of_task_to_be_managed = cpu_to_le16 ( tag ) ;
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
rc = mvs_debug_issue_ssp_tmf ( dev , lun . scsi_lun , & tmf_task ) ;
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
/* if successful, clear the task and callback forwards.*/
if ( rc = = TMF_RESP_FUNC_COMPLETE ) {
u32 slot_no ;
struct mvs_slot_info * slot ;
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
if ( task - > lldd_task ) {
2009-05-15 04:41:21 +04:00
slot = task - > lldd_task ;
2009-05-09 01:46:40 +04:00
slot_no = ( u32 ) ( slot - mvi - > slot_info ) ;
mvs_slot_complete ( mvi , slot_no , 1 ) ;
}
}
} else if ( task - > task_proto & SAS_PROTOCOL_SATA | |
task - > task_proto & SAS_PROTOCOL_STP ) {
/* to do free register_set */
} else {
/* SMP */
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
}
out :
if ( rc ! = TMF_RESP_FUNC_COMPLETE )
mv_printk ( " %s:rc= %d \n " , __func__ , rc ) ;
2009-05-09 01:44:01 +04:00
return rc ;
2008-02-23 16:15:27 +03:00
}
2009-05-09 01:46:40 +04:00
int mvs_abort_task_set ( struct domain_device * dev , u8 * lun )
2008-02-23 16:15:27 +03:00
{
2009-05-09 01:46:40 +04:00
int rc = TMF_RESP_FUNC_FAILED ;
struct mvs_tmf_task tmf_task ;
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
tmf_task . tmf = TMF_ABORT_TASK_SET ;
rc = mvs_debug_issue_ssp_tmf ( dev , lun , & tmf_task ) ;
2009-05-09 01:44:01 +04:00
2009-05-09 01:46:40 +04:00
return rc ;
2008-02-23 16:15:27 +03:00
}
2009-05-09 01:46:40 +04:00
int mvs_clear_aca ( struct domain_device * dev , u8 * lun )
2008-02-23 16:15:27 +03:00
{
2009-05-09 01:46:40 +04:00
int rc = TMF_RESP_FUNC_FAILED ;
struct mvs_tmf_task tmf_task ;
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
tmf_task . tmf = TMF_CLEAR_ACA ;
rc = mvs_debug_issue_ssp_tmf ( dev , lun , & tmf_task ) ;
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
return rc ;
}
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
int mvs_clear_task_set ( struct domain_device * dev , u8 * lun )
{
int rc = TMF_RESP_FUNC_FAILED ;
struct mvs_tmf_task tmf_task ;
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
tmf_task . tmf = TMF_CLEAR_TASK_SET ;
rc = mvs_debug_issue_ssp_tmf ( dev , lun , & tmf_task ) ;
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
return rc ;
2009-05-09 01:44:01 +04:00
}
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
static int mvs_sata_done ( struct mvs_info * mvi , struct sas_task * task ,
u32 slot_idx , int err )
2009-05-09 01:44:01 +04:00
{
2009-05-15 04:41:21 +04:00
struct mvs_device * mvi_dev = task - > dev - > lldd_dev ;
2009-05-09 01:46:40 +04:00
struct task_status_struct * tstat = & task - > task_status ;
struct ata_task_resp * resp = ( struct ata_task_resp * ) tstat - > buf ;
int stat = SAM_GOOD ;
2008-03-27 09:55:33 +03:00
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
resp - > frame_len = sizeof ( struct dev_to_host_fis ) ;
memcpy ( & resp - > ending_fis [ 0 ] ,
SATA_RECEIVED_D2H_FIS ( mvi_dev - > taskfileset ) ,
sizeof ( struct dev_to_host_fis ) ) ;
tstat - > buf_valid_size = sizeof ( * resp ) ;
if ( unlikely ( err ) )
stat = SAS_PROTO_RESPONSE ;
return stat ;
2008-02-23 16:15:27 +03:00
}
2009-05-09 01:46:40 +04:00
static int mvs_slot_err ( struct mvs_info * mvi , struct sas_task * task ,
u32 slot_idx )
2008-02-23 16:15:27 +03:00
{
2009-05-09 01:46:40 +04:00
struct mvs_slot_info * slot = & mvi - > slot_info [ slot_idx ] ;
int stat ;
u32 err_dw0 = le32_to_cpu ( * ( u32 * ) ( slot - > response ) ) ;
u32 tfs = 0 ;
enum mvs_port_type type = PORT_TYPE_SAS ;
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
if ( err_dw0 & CMD_ISS_STPD )
MVS_CHIP_DISP - > issue_stop ( mvi , type , tfs ) ;
MVS_CHIP_DISP - > command_active ( mvi , slot_idx ) ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
stat = SAM_CHECK_COND ;
2009-05-09 01:44:01 +04:00
switch ( task - > task_proto ) {
case SAS_PROTOCOL_SSP :
2009-05-09 01:46:40 +04:00
stat = SAS_ABORTED_TASK ;
break ;
case SAS_PROTOCOL_SMP :
stat = SAM_CHECK_COND ;
2009-05-09 01:44:01 +04:00
break ;
2009-05-09 01:46:40 +04:00
2009-05-09 01:44:01 +04:00
case SAS_PROTOCOL_SATA :
case SAS_PROTOCOL_STP :
2009-05-09 01:46:40 +04:00
case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP :
{
if ( err_dw0 = = 0x80400002 )
mv_printk ( " find reserved error, why? \n " ) ;
task - > ata_task . use_ncq = 0 ;
stat = SAS_PROTO_RESPONSE ;
mvs_sata_done ( mvi , task , slot_idx , 1 ) ;
2009-05-09 01:44:01 +04:00
}
2009-05-09 01:46:40 +04:00
break ;
2009-05-09 01:44:01 +04:00
default :
break ;
}
2009-05-09 01:46:40 +04:00
return stat ;
2008-03-27 09:55:33 +03:00
}
2009-05-09 01:46:40 +04:00
int mvs_slot_complete ( struct mvs_info * mvi , u32 rx_desc , u32 flags )
2007-10-26 04:58:22 +04:00
{
2009-05-09 01:46:40 +04:00
u32 slot_idx = rx_desc & RXQ_SLOT_MASK ;
struct mvs_slot_info * slot = & mvi - > slot_info [ slot_idx ] ;
struct sas_task * task = slot - > task ;
struct mvs_device * mvi_dev = NULL ;
struct task_status_struct * tstat ;
bool aborted ;
void * to ;
enum exec_status sts ;
if ( mvi - > exp_req )
mvi - > exp_req - - ;
if ( unlikely ( ! task | | ! task - > lldd_task ) )
return - 1 ;
tstat = & task - > task_status ;
2009-05-15 04:41:21 +04:00
mvi_dev = task - > dev - > lldd_dev ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
mvs_hba_cq_dump ( mvi ) ;
spin_lock ( & task - > task_state_lock ) ;
task - > task_state_flags & =
~ ( SAS_TASK_STATE_PENDING | SAS_TASK_AT_INITIATOR ) ;
task - > task_state_flags | = SAS_TASK_STATE_DONE ;
/* race condition*/
aborted = task - > task_state_flags & SAS_TASK_STATE_ABORTED ;
spin_unlock ( & task - > task_state_lock ) ;
memset ( tstat , 0 , sizeof ( * tstat ) ) ;
tstat - > resp = SAS_TASK_COMPLETE ;
if ( unlikely ( aborted ) ) {
tstat - > stat = SAS_ABORTED_TASK ;
if ( mvi_dev )
mvi_dev - > runing_req - - ;
if ( sas_protocol_ata ( task - > task_proto ) )
mvs_free_reg_set ( mvi , mvi_dev ) ;
mvs_slot_task_free ( mvi , task , slot , slot_idx ) ;
return - 1 ;
2007-10-26 04:58:22 +04:00
}
2009-05-09 01:46:40 +04:00
if ( unlikely ( ! mvi_dev | | ! slot - > port - > port_attached | | flags ) ) {
mv_dprintk ( " port has not device. \n " ) ;
tstat - > stat = SAS_PHY_DOWN ;
goto out ;
}
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
/*
if ( unlikely ( ( rx_desc & RXQ_ERR ) | | ( * ( u64 * ) slot - > response ) ) ) {
mv_dprintk ( " Find device[%016llx] RXQ_ERR %X,
err info : % 016ll x \ n " ,
SAS_ADDR ( task - > dev - > sas_addr ) ,
rx_desc , ( u64 ) ( * ( u64 * ) slot - > response ) ) ;
2007-10-26 04:58:22 +04:00
}
2009-05-09 01:46:40 +04:00
*/
/* error info record present */
if ( unlikely ( ( rx_desc & RXQ_ERR ) & & ( * ( u64 * ) slot - > response ) ) ) {
tstat - > stat = mvs_slot_err ( mvi , task , slot_idx ) ;
goto out ;
2007-10-26 04:58:22 +04:00
}
2009-05-09 01:46:40 +04:00
switch ( task - > task_proto ) {
case SAS_PROTOCOL_SSP :
/* hw says status == 0, datapres == 0 */
if ( rx_desc & RXQ_GOOD ) {
tstat - > stat = SAM_GOOD ;
tstat - > resp = SAS_TASK_COMPLETE ;
}
/* response frame present */
else if ( rx_desc & RXQ_RSP ) {
struct ssp_response_iu * iu = slot - > response +
sizeof ( struct mvs_err_info ) ;
sas_ssp_task_response ( mvi - > dev , task , iu ) ;
} else
tstat - > stat = SAM_CHECK_COND ;
break ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
case SAS_PROTOCOL_SMP : {
struct scatterlist * sg_resp = & task - > smp_task . smp_resp ;
tstat - > stat = SAM_GOOD ;
to = kmap_atomic ( sg_page ( sg_resp ) , KM_IRQ0 ) ;
memcpy ( to + sg_resp - > offset ,
slot - > response + sizeof ( struct mvs_err_info ) ,
sg_dma_len ( sg_resp ) ) ;
kunmap_atomic ( to , KM_IRQ0 ) ;
break ;
}
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
case SAS_PROTOCOL_SATA :
case SAS_PROTOCOL_STP :
case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP : {
tstat - > stat = mvs_sata_done ( mvi , task , slot_idx , 0 ) ;
break ;
}
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
default :
tstat - > stat = SAM_CHECK_COND ;
break ;
}
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
out :
2009-05-11 17:49:52 +04:00
if ( mvi_dev ) {
2009-05-09 01:46:40 +04:00
mvi_dev - > runing_req - - ;
2009-05-11 17:49:52 +04:00
if ( sas_protocol_ata ( task - > task_proto ) )
mvs_free_reg_set ( mvi , mvi_dev ) ;
}
2009-05-09 01:46:40 +04:00
mvs_slot_task_free ( mvi , task , slot , slot_idx ) ;
sts = tstat - > stat ;
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
spin_unlock ( & mvi - > lock ) ;
if ( task - > task_done )
task - > task_done ( task ) ;
else
mv_dprintk ( " why has not task_done. \n " ) ;
spin_lock ( & mvi - > lock ) ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
return sts ;
}
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
void mvs_release_task ( struct mvs_info * mvi ,
int phy_no , struct domain_device * dev )
{
int i = 0 ; u32 slot_idx ;
struct mvs_phy * phy ;
struct mvs_port * port ;
struct mvs_slot_info * slot , * slot2 ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
phy = & mvi - > phy [ phy_no ] ;
port = phy - > port ;
if ( ! port )
return ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
list_for_each_entry_safe ( slot , slot2 , & port - > list , entry ) {
struct sas_task * task ;
slot_idx = ( u32 ) ( slot - mvi - > slot_info ) ;
task = slot - > task ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
if ( dev & & task - > dev ! = dev )
continue ;
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
mv_printk ( " Release slot [%x] tag[%x], task [%p]: \n " ,
slot_idx , slot - > slot_tag , task ) ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
if ( task - > task_proto & SAS_PROTOCOL_SSP ) {
mv_printk ( " attached with SSP task CDB[ " ) ;
for ( i = 0 ; i < 16 ; i + + )
mv_printk ( " %02x " , task - > ssp_task . cdb [ i ] ) ;
mv_printk ( " ] \n " ) ;
}
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
mvs_slot_complete ( mvi , slot_idx , 1 ) ;
2007-10-26 04:58:22 +04:00
}
2009-05-09 01:46:40 +04:00
}
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
static void mvs_phy_disconnected ( struct mvs_phy * phy )
{
phy - > phy_attached = 0 ;
phy - > att_dev_info = 0 ;
phy - > att_dev_sas_addr = 0 ;
}
static void mvs_work_queue ( struct work_struct * work )
{
struct delayed_work * dw = container_of ( work , struct delayed_work , work ) ;
struct mvs_wq * mwq = container_of ( dw , struct mvs_wq , work_q ) ;
struct mvs_info * mvi = mwq - > mvi ;
unsigned long flags ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
spin_lock_irqsave ( & mvi - > lock , flags ) ;
if ( mwq - > handler & PHY_PLUG_EVENT ) {
u32 phy_no = ( unsigned long ) mwq - > data ;
struct sas_ha_struct * sas_ha = mvi - > sas ;
struct mvs_phy * phy = & mvi - > phy [ phy_no ] ;
struct asd_sas_phy * sas_phy = & phy - > sas_phy ;
if ( phy - > phy_event & PHY_PLUG_OUT ) {
u32 tmp ;
struct sas_identify_frame * id ;
id = ( struct sas_identify_frame * ) phy - > frame_rcvd ;
tmp = MVS_CHIP_DISP - > read_phy_ctl ( mvi , phy_no ) ;
phy - > phy_event & = ~ PHY_PLUG_OUT ;
if ( ! ( tmp & PHY_READY_MASK ) ) {
sas_phy_disconnected ( sas_phy ) ;
mvs_phy_disconnected ( phy ) ;
sas_ha - > notify_phy_event ( sas_phy ,
PHYE_LOSS_OF_SIGNAL ) ;
mv_dprintk ( " phy%d Removed Device \n " , phy_no ) ;
} else {
MVS_CHIP_DISP - > detect_porttype ( mvi , phy_no ) ;
mvs_update_phyinfo ( mvi , phy_no , 1 ) ;
mvs_bytes_dmaed ( mvi , phy_no ) ;
mvs_port_notify_formed ( sas_phy , 0 ) ;
mv_dprintk ( " phy%d Attached Device \n " , phy_no ) ;
}
}
}
list_del ( & mwq - > entry ) ;
spin_unlock_irqrestore ( & mvi - > lock , flags ) ;
kfree ( mwq ) ;
}
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
static int mvs_handle_event ( struct mvs_info * mvi , void * data , int handler )
{
struct mvs_wq * mwq ;
int ret = 0 ;
mwq = kmalloc ( sizeof ( struct mvs_wq ) , GFP_ATOMIC ) ;
if ( mwq ) {
mwq - > mvi = mvi ;
mwq - > data = data ;
mwq - > handler = handler ;
MV_INIT_DELAYED_WORK ( & mwq - > work_q , mvs_work_queue , mwq ) ;
list_add_tail ( & mwq - > entry , & mvi - > wq_list ) ;
schedule_delayed_work ( & mwq - > work_q , HZ * 2 ) ;
} else
ret = - ENOMEM ;
return ret ;
}
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
static void mvs_sig_time_out ( unsigned long tphy )
{
struct mvs_phy * phy = ( struct mvs_phy * ) tphy ;
struct mvs_info * mvi = phy - > mvi ;
u8 phy_no ;
for ( phy_no = 0 ; phy_no < mvi - > chip - > n_phy ; phy_no + + ) {
if ( & mvi - > phy [ phy_no ] = = phy ) {
mv_dprintk ( " Get signature time out, reset phy %d \n " ,
phy_no + mvi - > id * mvi - > chip - > n_phy ) ;
MVS_CHIP_DISP - > phy_reset ( mvi , phy_no , 1 ) ;
}
2007-10-26 04:58:22 +04:00
}
2009-05-09 01:46:40 +04:00
}
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
static void mvs_sig_remove_timer ( struct mvs_phy * phy )
{
if ( phy - > timer . function )
del_timer ( & phy - > timer ) ;
phy - > timer . function = NULL ;
}
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
void mvs_int_port ( struct mvs_info * mvi , int phy_no , u32 events )
{
u32 tmp ;
struct sas_ha_struct * sas_ha = mvi - > sas ;
struct mvs_phy * phy = & mvi - > phy [ phy_no ] ;
struct asd_sas_phy * sas_phy = & phy - > sas_phy ;
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
phy - > irq_status = MVS_CHIP_DISP - > read_port_irq_stat ( mvi , phy_no ) ;
mv_dprintk ( " port %d ctrl sts=0x%X. \n " , phy_no + mvi - > id * mvi - > chip - > n_phy ,
MVS_CHIP_DISP - > read_phy_ctl ( mvi , phy_no ) ) ;
mv_dprintk ( " Port %d irq sts = 0x%X \n " , phy_no + mvi - > id * mvi - > chip - > n_phy ,
phy - > irq_status ) ;
2008-02-23 16:15:27 +03:00
2009-05-09 01:46:40 +04:00
/*
* events is port event now ,
* we need check the interrupt status which belongs to per port .
*/
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
if ( phy - > irq_status & PHYEV_DCDR_ERR )
mv_dprintk ( " port %d STP decoding error. \n " ,
phy_no + mvi - > id * mvi - > chip - > n_phy ) ;
if ( phy - > irq_status & PHYEV_POOF ) {
if ( ! ( phy - > phy_event & PHY_PLUG_OUT ) ) {
int dev_sata = phy - > phy_type & PORT_TYPE_SATA ;
int ready ;
mvs_release_task ( mvi , phy_no , NULL ) ;
phy - > phy_event | = PHY_PLUG_OUT ;
mvs_handle_event ( mvi ,
( void * ) ( unsigned long ) phy_no ,
PHY_PLUG_EVENT ) ;
ready = mvs_is_phy_ready ( mvi , phy_no ) ;
if ( ! ready )
mv_dprintk ( " phy%d Unplug Notice \n " ,
phy_no +
mvi - > id * mvi - > chip - > n_phy ) ;
if ( ready | | dev_sata ) {
if ( MVS_CHIP_DISP - > stp_reset )
MVS_CHIP_DISP - > stp_reset ( mvi ,
phy_no ) ;
else
MVS_CHIP_DISP - > phy_reset ( mvi ,
phy_no , 0 ) ;
return ;
}
}
}
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
if ( phy - > irq_status & PHYEV_COMWAKE ) {
tmp = MVS_CHIP_DISP - > read_port_irq_mask ( mvi , phy_no ) ;
MVS_CHIP_DISP - > write_port_irq_mask ( mvi , phy_no ,
tmp | PHYEV_SIG_FIS ) ;
if ( phy - > timer . function = = NULL ) {
phy - > timer . data = ( unsigned long ) phy ;
phy - > timer . function = mvs_sig_time_out ;
phy - > timer . expires = jiffies + 10 * HZ ;
add_timer ( & phy - > timer ) ;
}
}
if ( phy - > irq_status & ( PHYEV_SIG_FIS | PHYEV_ID_DONE ) ) {
phy - > phy_status = mvs_is_phy_ready ( mvi , phy_no ) ;
mvs_sig_remove_timer ( phy ) ;
mv_dprintk ( " notify plug in on phy[%d] \n " , phy_no ) ;
if ( phy - > phy_status ) {
mdelay ( 10 ) ;
MVS_CHIP_DISP - > detect_porttype ( mvi , phy_no ) ;
if ( phy - > phy_type & PORT_TYPE_SATA ) {
tmp = MVS_CHIP_DISP - > read_port_irq_mask (
mvi , phy_no ) ;
tmp & = ~ PHYEV_SIG_FIS ;
MVS_CHIP_DISP - > write_port_irq_mask ( mvi ,
phy_no , tmp ) ;
}
mvs_update_phyinfo ( mvi , phy_no , 0 ) ;
mvs_bytes_dmaed ( mvi , phy_no ) ;
/* whether driver is going to handle hot plug */
if ( phy - > phy_event & PHY_PLUG_OUT ) {
mvs_port_notify_formed ( sas_phy , 0 ) ;
phy - > phy_event & = ~ PHY_PLUG_OUT ;
}
} else {
mv_dprintk ( " plugin interrupt but phy%d is gone \n " ,
phy_no + mvi - > id * mvi - > chip - > n_phy ) ;
}
} else if ( phy - > irq_status & PHYEV_BROAD_CH ) {
mv_dprintk ( " port %d broadcast change. \n " ,
phy_no + mvi - > id * mvi - > chip - > n_phy ) ;
/* exception for Samsung disk drive*/
mdelay ( 1000 ) ;
sas_ha - > notify_port_event ( sas_phy , PORTE_BROADCAST_RCVD ) ;
}
MVS_CHIP_DISP - > write_port_irq_stat ( mvi , phy_no , phy - > irq_status ) ;
2007-10-26 04:58:22 +04:00
}
2009-05-09 01:46:40 +04:00
int mvs_int_rx ( struct mvs_info * mvi , bool self_clear )
2007-10-26 04:58:22 +04:00
{
2009-05-09 01:46:40 +04:00
u32 rx_prod_idx , rx_desc ;
bool attn = false ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
/* the first dword in the RX ring is special: it contains
* a mirror of the hardware ' s RX producer index , so that
* we don ' t have to stall the CPU reading that register .
* The actual RX ring is offset by one dword , due to this .
*/
rx_prod_idx = mvi - > rx_cons ;
mvi - > rx_cons = le32_to_cpu ( mvi - > rx [ 0 ] ) ;
if ( mvi - > rx_cons = = 0xfff ) /* h/w hasn't touched RX ring yet */
return 0 ;
2007-10-26 04:58:22 +04:00
2009-05-09 01:46:40 +04:00
/* The CMPL_Q may come late, read from register and try again
* note : if coalescing is enabled ,
* it will need to read from register every time for sure
*/
if ( unlikely ( mvi - > rx_cons = = rx_prod_idx ) )
mvi - > rx_cons = MVS_CHIP_DISP - > rx_update ( mvi ) & RX_RING_SZ_MASK ;
if ( mvi - > rx_cons = = rx_prod_idx )
return 0 ;
while ( mvi - > rx_cons ! = rx_prod_idx ) {
/* increment our internal RX consumer pointer */
rx_prod_idx = ( rx_prod_idx + 1 ) & ( MVS_RX_RING_SZ - 1 ) ;
rx_desc = le32_to_cpu ( mvi - > rx [ rx_prod_idx + 1 ] ) ;
if ( likely ( rx_desc & RXQ_DONE ) )
mvs_slot_complete ( mvi , rx_desc , 0 ) ;
if ( rx_desc & RXQ_ATTN ) {
attn = true ;
} else if ( rx_desc & RXQ_ERR ) {
if ( ! ( rx_desc & RXQ_DONE ) )
mvs_slot_complete ( mvi , rx_desc , 0 ) ;
} else if ( rx_desc & RXQ_SLOT_RESET ) {
mvs_slot_free ( mvi , rx_desc ) ;
}
}
if ( attn & & self_clear )
MVS_CHIP_DISP - > int_full ( mvi ) ;
return 0 ;
2007-10-26 04:58:22 +04:00
}