2011-07-02 22:56:22 -07:00
/*
* This file is provided under a dual BSD / GPLv2 license . When using or
* redistributing this file , you may do so under either license .
*
* GPL LICENSE SUMMARY
*
* Copyright ( c ) 2008 - 2011 Intel Corporation . All rights reserved .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation .
*
* 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 . , 51 Franklin St - Fifth Floor , Boston , MA 02110 - 1301 USA .
* The full GNU General Public License is included in this distribution
* in the file called LICENSE . GPL .
*
* BSD LICENSE
*
* Copyright ( c ) 2008 - 2011 Intel Corporation . All rights reserved .
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions
* are met :
*
* * Redistributions of source code must retain the above copyright
* notice , this list of conditions and the following disclaimer .
* * Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in
* the documentation and / or other materials provided with the
* distribution .
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* " AS IS " AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT
* LIMITED TO , THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL ,
* SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT
* LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE ,
* DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*/
# include "isci.h"
# include "port.h"
# include "request.h"
2011-05-10 02:28:46 -07:00
# define SCIC_SDS_PORT_HARD_RESET_TIMEOUT (1000)
# define SCU_DUMMY_INDEX (0xFFFF)
2011-07-02 22:56:22 -07:00
2011-05-07 10:11:43 -07:00
static void isci_port_change_state ( struct isci_port * iport , enum isci_status status )
{
unsigned long flags ;
2011-07-02 22:56:22 -07:00
2011-05-07 10:11:43 -07:00
dev_dbg ( & iport - > isci_host - > pdev - > dev ,
" %s: iport = %p, state = 0x%x \n " ,
__func__ , iport , status ) ;
2011-07-02 22:56:22 -07:00
2011-05-07 10:11:43 -07:00
/* XXX pointless lock */
spin_lock_irqsave ( & iport - > state_lock , flags ) ;
iport - > status = status ;
spin_unlock_irqrestore ( & iport - > state_lock , flags ) ;
}
2011-07-02 22:56:22 -07:00
2011-05-10 02:28:46 -07:00
/*
* This function will indicate which protocols are supported by this port .
* @ sci_port : a handle corresponding to the SAS port for which to return the
* supported protocols .
* @ protocols : This parameter specifies a pointer to a data structure
* which the core will copy the protocol values for the port from the
* transmit_identification register .
*/
static void
scic_sds_port_get_protocols ( struct scic_sds_port * sci_port ,
struct scic_phy_proto * protocols )
2011-07-02 22:56:22 -07:00
{
2011-05-10 02:28:46 -07:00
u8 index ;
protocols - > all = 0 ;
for ( index = 0 ; index < SCI_MAX_PHYS ; index + + ) {
if ( sci_port - > phy_table [ index ] ! = NULL ) {
scic_sds_phy_get_protocols ( sci_port - > phy_table [ index ] ,
protocols ) ;
}
}
2011-07-02 22:56:22 -07:00
}
/**
2011-05-10 02:28:46 -07:00
* This method requests a list ( mask ) of the phys contained in the supplied SAS
* port .
* @ sci_port : a handle corresponding to the SAS port for which to return the
* phy mask .
2011-07-02 22:56:22 -07:00
*
2011-05-10 02:28:46 -07:00
* Return a bit mask indicating which phys are a part of this port . Each bit
* corresponds to a phy identifier ( e . g . bit 0 = phy id 0 ) .
2011-07-02 22:56:22 -07:00
*/
2011-05-10 02:28:46 -07:00
static u32 scic_sds_port_get_phys ( struct scic_sds_port * sci_port )
2011-07-02 22:56:22 -07:00
{
2011-05-10 02:28:46 -07:00
u32 index ;
u32 mask ;
mask = 0 ;
for ( index = 0 ; index < SCI_MAX_PHYS ; index + + ) {
if ( sci_port - > phy_table [ index ] ! = NULL ) {
mask | = ( 1 < < index ) ;
}
}
return mask ;
2011-07-02 22:56:22 -07:00
}
2011-05-10 02:28:46 -07:00
/**
* scic_port_get_properties ( ) - This method simply returns the properties
* regarding the port , such as : physical index , protocols , sas address , etc .
* @ port : this parameter specifies the port for which to retrieve the physical
* index .
* @ properties : This parameter specifies the properties structure into which to
* copy the requested information .
*
* Indicate if the user specified a valid port . SCI_SUCCESS This value is
* returned if the specified port was valid . SCI_FAILURE_INVALID_PORT This
* value is returned if the specified port is not valid . When this value is
* returned , no data is copied to the properties output parameter .
*/
static enum sci_status scic_port_get_properties ( struct scic_sds_port * port ,
struct scic_port_properties * prop )
2011-07-02 22:56:22 -07:00
{
2011-05-10 02:28:46 -07:00
if ( ( port = = NULL ) | |
( port - > logical_port_index = = SCIC_SDS_DUMMY_PORT ) )
return SCI_FAILURE_INVALID_PORT ;
2011-07-02 22:56:22 -07:00
2011-05-10 02:28:46 -07:00
prop - > index = port - > logical_port_index ;
prop - > phy_mask = scic_sds_port_get_phys ( port ) ;
scic_sds_port_get_sas_address ( port , & prop - > local . sas_address ) ;
scic_sds_port_get_protocols ( port , & prop - > local . protocols ) ;
scic_sds_port_get_attached_sas_address ( port , & prop - > remote . sas_address ) ;
2011-07-02 22:56:22 -07:00
2011-05-10 02:28:46 -07:00
return SCI_SUCCESS ;
2011-07-02 22:56:22 -07:00
}
2011-05-10 02:28:46 -07:00
static void isci_port_link_up ( struct isci_host * isci_host ,
struct scic_sds_port * port ,
struct scic_sds_phy * phy )
2011-07-02 22:56:22 -07:00
{
unsigned long flags ;
struct scic_port_properties properties ;
2011-05-06 17:36:38 -07:00
struct isci_phy * isci_phy = sci_phy_to_iphy ( phy ) ;
2011-05-07 10:11:43 -07:00
struct isci_port * isci_port = sci_port_to_iport ( port ) ;
2011-07-02 22:56:22 -07:00
unsigned long success = true ;
BUG_ON ( isci_phy - > isci_port ! = NULL ) ;
2011-04-12 17:28:43 -07:00
2011-07-02 22:56:22 -07:00
isci_phy - > isci_port = isci_port ;
dev_dbg ( & isci_host - > pdev - > dev ,
" %s: isci_port = %p \n " ,
__func__ , isci_port ) ;
spin_lock_irqsave ( & isci_phy - > sas_phy . frame_rcvd_lock , flags ) ;
isci_port_change_state ( isci_phy - > isci_port , isci_starting ) ;
scic_port_get_properties ( port , & properties ) ;
2011-05-04 18:22:33 -07:00
if ( phy - > protocol = = SCIC_SDS_PHY_PROTOCOL_SATA ) {
2011-02-25 10:25:21 -08:00
u64 attached_sas_address ;
2011-07-02 22:56:22 -07:00
isci_phy - > sas_phy . oob_mode = SATA_OOB_MODE ;
2011-05-04 15:02:02 -07:00
isci_phy - > sas_phy . frame_rcvd_size = sizeof ( struct dev_to_host_fis ) ;
2011-07-02 22:56:22 -07:00
/*
* For direct - attached SATA devices , the SCI core will
* automagically assign a SAS address to the end device
* for the purpose of creating a port . This SAS address
* will not be the same as assigned to the PHY and needs
* to be obtained from struct scic_port_properties properties .
*/
2011-02-25 10:25:21 -08:00
attached_sas_address = properties . remote . sas_address . high ;
attached_sas_address < < = 32 ;
attached_sas_address | = properties . remote . sas_address . low ;
swab64s ( & attached_sas_address ) ;
2011-07-02 22:56:22 -07:00
2011-02-25 10:25:21 -08:00
memcpy ( & isci_phy - > sas_phy . attached_sas_addr ,
& attached_sas_address , sizeof ( attached_sas_address ) ) ;
2011-05-04 18:22:33 -07:00
} else if ( phy - > protocol = = SCIC_SDS_PHY_PROTOCOL_SAS ) {
2011-07-02 22:56:22 -07:00
isci_phy - > sas_phy . oob_mode = SAS_OOB_MODE ;
2011-05-04 15:37:52 -07:00
isci_phy - > sas_phy . frame_rcvd_size = sizeof ( struct sas_identify_frame ) ;
2011-07-02 22:56:22 -07:00
/* Copy the attached SAS address from the IAF */
memcpy ( isci_phy - > sas_phy . attached_sas_addr ,
2011-05-04 15:37:52 -07:00
isci_phy - > frame_rcvd . iaf . sas_addr , SAS_ADDR_SIZE ) ;
2011-07-02 22:56:22 -07:00
} else {
dev_err ( & isci_host - > pdev - > dev , " %s: unkown target \n " , __func__ ) ;
success = false ;
}
2011-02-18 09:25:13 -08:00
isci_phy - > sas_phy . phy - > negotiated_linkrate = sci_phy_linkrate ( phy ) ;
2011-07-02 22:56:22 -07:00
spin_unlock_irqrestore ( & isci_phy - > sas_phy . frame_rcvd_lock , flags ) ;
/* Notify libsas that we have an address frame, if indeed
* we ' ve found an SSP , SMP , or STP target */
if ( success )
isci_host - > sas_ha . notify_port_event ( & isci_phy - > sas_phy ,
PORTE_BYTES_DMAED ) ;
}
/**
* isci_port_link_down ( ) - This function is called by the sci core when a link
* becomes inactive .
* @ isci_host : This parameter specifies the isci host object .
* @ phy : This parameter specifies the isci phy with the active link .
* @ port : This parameter specifies the isci port with the active link .
*
*/
2011-05-10 02:28:46 -07:00
static void isci_port_link_down ( struct isci_host * isci_host ,
struct isci_phy * isci_phy ,
struct isci_port * isci_port )
2011-07-02 22:56:22 -07:00
{
struct isci_remote_device * isci_device ;
dev_dbg ( & isci_host - > pdev - > dev ,
" %s: isci_port = %p \n " , __func__ , isci_port ) ;
if ( isci_port ) {
/* check to see if this is the last phy on this port. */
if ( isci_phy - > sas_phy . port
& & isci_phy - > sas_phy . port - > num_phys = = 1 ) {
/* change the state for all devices on this port.
* The next task sent to this device will be returned
* as SAS_TASK_UNDELIVERED , and the scsi mid layer
* will remove the target
*/
list_for_each_entry ( isci_device ,
& isci_port - > remote_dev_list ,
node ) {
dev_dbg ( & isci_host - > pdev - > dev ,
" %s: isci_device = %p \n " ,
__func__ , isci_device ) ;
isci_remote_device_change_state ( isci_device ,
isci_stopping ) ;
}
}
isci_port_change_state ( isci_port , isci_stopping ) ;
}
/* Notify libsas of the borken link, this will trigger calls to our
* isci_port_deformed and isci_dev_gone functions .
*/
sas_phy_disconnected ( & isci_phy - > sas_phy ) ;
isci_host - > sas_ha . notify_phy_event ( & isci_phy - > sas_phy ,
PHYE_LOSS_OF_SIGNAL ) ;
isci_phy - > isci_port = NULL ;
dev_dbg ( & isci_host - > pdev - > dev ,
" %s: isci_port = %p - Done \n " , __func__ , isci_port ) ;
}
/**
* isci_port_ready ( ) - This function is called by the sci core when a link
* becomes ready .
* @ isci_host : This parameter specifies the isci host object .
* @ port : This parameter specifies the sci port with the active link .
*
*/
2011-05-10 02:28:46 -07:00
static void isci_port_ready ( struct isci_host * isci_host , struct isci_port * isci_port )
2011-07-02 22:56:22 -07:00
{
dev_dbg ( & isci_host - > pdev - > dev ,
" %s: isci_port = %p \n " , __func__ , isci_port ) ;
complete_all ( & isci_port - > start_complete ) ;
isci_port_change_state ( isci_port , isci_ready ) ;
return ;
}
/**
* isci_port_not_ready ( ) - This function is called by the sci core when a link
* is not ready . All remote devices on this link will be removed if they are
* in the stopping state .
* @ isci_host : This parameter specifies the isci host object .
* @ port : This parameter specifies the sci port with the active link .
*
*/
2011-05-10 02:28:46 -07:00
static void isci_port_not_ready ( struct isci_host * isci_host , struct isci_port * isci_port )
2011-07-02 22:56:22 -07:00
{
dev_dbg ( & isci_host - > pdev - > dev ,
" %s: isci_port = %p \n " , __func__ , isci_port ) ;
}
2011-05-10 02:28:46 -07:00
static void isci_port_stop_complete ( struct scic_sds_controller * scic ,
struct scic_sds_port * sci_port ,
enum sci_status completion_status )
{
dev_dbg ( & scic_to_ihost ( scic ) - > pdev - > dev , " Port stop complete \n " ) ;
}
2011-07-02 22:56:22 -07:00
/**
* isci_port_hard_reset_complete ( ) - This function is called by the sci core
* when the hard reset complete notification has been received .
* @ port : This parameter specifies the sci port with the active link .
* @ completion_status : This parameter specifies the core status for the reset
* process .
*
*/
2011-05-10 02:28:46 -07:00
static void isci_port_hard_reset_complete ( struct isci_port * isci_port ,
enum sci_status completion_status )
2011-07-02 22:56:22 -07:00
{
dev_dbg ( & isci_port - > isci_host - > pdev - > dev ,
" %s: isci_port = %p, completion_status=%x \n " ,
__func__ , isci_port , completion_status ) ;
/* Save the status of the hard reset from the port. */
isci_port - > hard_reset_status = completion_status ;
complete_all ( & isci_port - > hard_reset_complete ) ;
}
2011-03-31 13:10:44 -07:00
2011-05-10 02:28:46 -07:00
/* This method will return a true value if the specified phy can be assigned to
* this port The following is a list of phys for each port that are allowed : -
* Port 0 - 3 2 1 0 - Port 1 - 1 - Port 2 - 3 2 - Port 3 - 3 This method
* doesn ' t preclude all configurations . It merely ensures that a phy is part
* of the allowable set of phy identifiers for that port . For example , one
* could assign phy 3 to port 0 and no other phys . Please refer to
* scic_sds_port_is_phy_mask_valid ( ) for information regarding whether the
* phy_mask for a port can be supported . bool true if this is a valid phy
* assignment for the port false if this is not a valid phy assignment for the
* port
*/
bool scic_sds_port_is_valid_phy_assignment ( struct scic_sds_port * sci_port ,
u32 phy_index )
2011-07-02 22:56:22 -07:00
{
2011-05-10 02:28:46 -07:00
/* Initialize to invalid value. */
u32 existing_phy_index = SCI_MAX_PHYS ;
u32 index ;
2011-07-02 22:56:22 -07:00
2011-05-10 02:28:46 -07:00
if ( ( sci_port - > physical_port_index = = 1 ) & & ( phy_index ! = 1 ) ) {
return false ;
}
2011-07-02 22:56:22 -07:00
2011-05-10 02:28:46 -07:00
if ( sci_port - > physical_port_index = = 3 & & phy_index ! = 3 ) {
return false ;
}
2011-07-02 22:56:22 -07:00
2011-05-10 02:28:46 -07:00
if (
( sci_port - > physical_port_index = = 2 )
& & ( ( phy_index = = 0 ) | | ( phy_index = = 1 ) )
) {
return false ;
}
2011-07-02 22:56:22 -07:00
2011-05-10 02:28:46 -07:00
for ( index = 0 ; index < SCI_MAX_PHYS ; index + + ) {
if ( ( sci_port - > phy_table [ index ] ! = NULL )
& & ( index ! = phy_index ) ) {
existing_phy_index = index ;
}
}
2011-07-02 22:56:22 -07:00
2011-05-10 02:28:46 -07:00
/*
* Ensure that all of the phys in the port are capable of
* operating at the same maximum link rate . */
if (
( existing_phy_index < SCI_MAX_PHYS )
& & ( sci_port - > owning_controller - > user_parameters . sds1 . phys [
phy_index ] . max_speed_generation ! =
sci_port - > owning_controller - > user_parameters . sds1 . phys [
existing_phy_index ] . max_speed_generation )
)
return false ;
2011-07-02 22:56:22 -07:00
2011-05-10 02:28:46 -07:00
return true ;
}
2011-07-02 22:56:22 -07:00
2011-05-10 02:28:46 -07:00
/**
*
* @ sci_port : This is the port object for which to determine if the phy mask
* can be supported .
*
* This method will return a true value if the port ' s phy mask can be supported
* by the SCU . The following is a list of valid PHY mask configurations for
* each port : - Port 0 - [ [ 3 2 ] 1 ] 0 - Port 1 - [ 1 ] - Port 2 - [ [ 3 ] 2 ]
* - Port 3 - [ 3 ] This method returns a boolean indication specifying if the
* phy mask can be supported . true if this is a valid phy assignment for the
* port false if this is not a valid phy assignment for the port
*/
static bool scic_sds_port_is_phy_mask_valid (
struct scic_sds_port * sci_port ,
u32 phy_mask )
{
if ( sci_port - > physical_port_index = = 0 ) {
if ( ( ( phy_mask & 0x0F ) = = 0x0F )
| | ( ( phy_mask & 0x03 ) = = 0x03 )
| | ( ( phy_mask & 0x01 ) = = 0x01 )
| | ( phy_mask = = 0 ) )
return true ;
} else if ( sci_port - > physical_port_index = = 1 ) {
if ( ( ( phy_mask & 0x02 ) = = 0x02 )
| | ( phy_mask = = 0 ) )
return true ;
} else if ( sci_port - > physical_port_index = = 2 ) {
if ( ( ( phy_mask & 0x0C ) = = 0x0C )
| | ( ( phy_mask & 0x04 ) = = 0x04 )
| | ( phy_mask = = 0 ) )
return true ;
} else if ( sci_port - > physical_port_index = = 3 ) {
if ( ( ( phy_mask & 0x08 ) = = 0x08 )
| | ( phy_mask = = 0 ) )
return true ;
}
2011-07-02 22:56:22 -07:00
2011-05-10 02:28:46 -07:00
return false ;
}
2011-07-02 22:56:22 -07:00
2011-05-10 02:28:46 -07:00
/**
*
* @ sci_port : This parameter specifies the port from which to return a
* connected phy .
*
* This method retrieves a currently active ( i . e . connected ) phy contained in
* the port . Currently , the lowest order phy that is connected is returned .
* This method returns a pointer to a SCIS_SDS_PHY object . NULL This value is
* returned if there are no currently active ( i . e . connected to a remote end
* point ) phys contained in the port . All other values specify a struct scic_sds_phy
* object that is active in the port .
*/
static struct scic_sds_phy * scic_sds_port_get_a_connected_phy (
struct scic_sds_port * sci_port
) {
u32 index ;
struct scic_sds_phy * phy ;
2011-07-02 22:56:22 -07:00
2011-05-10 02:28:46 -07:00
for ( index = 0 ; index < SCI_MAX_PHYS ; index + + ) {
/*
* Ensure that the phy is both part of the port and currently
* connected to the remote end - point . */
phy = sci_port - > phy_table [ index ] ;
if (
( phy ! = NULL )
& & scic_sds_port_active_phy ( sci_port , phy )
) {
return phy ;
}
2011-07-02 22:56:22 -07:00
}
2011-05-10 02:28:46 -07:00
return NULL ;
}
/**
* scic_sds_port_set_phy ( ) -
* @ out ] : port The port object to which the phy assignement is being made .
* @ out ] : phy The phy which is being assigned to the port .
*
* This method attempts to make the assignment of the phy to the port . If
* successful the phy is assigned to the ports phy table . bool true if the phy
* assignment can be made . false if the phy assignement can not be made . This
* is a functional test that only fails if the phy is currently assigned to a
* different port .
*/
static enum sci_status scic_sds_port_set_phy (
struct scic_sds_port * port ,
struct scic_sds_phy * phy )
{
/*
* Check to see if we can add this phy to a port
* that means that the phy is not part of a port and that the port does
* not already have a phy assinged to the phy index . */
if (
( port - > phy_table [ phy - > phy_index ] = = NULL )
2011-05-12 06:00:31 -07:00
& & ( phy_get_non_dummy_port ( phy ) = = NULL )
2011-05-10 02:28:46 -07:00
& & scic_sds_port_is_valid_phy_assignment ( port , phy - > phy_index )
) {
/*
* Phy is being added in the stopped state so we are in MPC mode
* make logical port index = physical port index */
port - > logical_port_index = port - > physical_port_index ;
port - > phy_table [ phy - > phy_index ] = phy ;
scic_sds_phy_set_port ( phy , port ) ;
return SCI_SUCCESS ;
}
return SCI_FAILURE ;
}
/**
* scic_sds_port_clear_phy ( ) -
* @ out ] : port The port from which the phy is being cleared .
* @ out ] : phy The phy being cleared from the port .
*
* This method will clear the phy assigned to this port . This method fails if
* this phy is not currently assinged to this port . bool true if the phy is
* removed from the port . false if this phy is not assined to this port .
*/
static enum sci_status scic_sds_port_clear_phy (
struct scic_sds_port * port ,
struct scic_sds_phy * phy )
{
/* Make sure that this phy is part of this port */
if ( port - > phy_table [ phy - > phy_index ] = = phy & &
2011-05-12 06:00:31 -07:00
phy_get_non_dummy_port ( phy ) = = port ) {
2011-05-10 02:28:46 -07:00
struct scic_sds_controller * scic = port - > owning_controller ;
struct isci_host * ihost = scic_to_ihost ( scic ) ;
/* Yep it is assigned to this port so remove it */
scic_sds_phy_set_port ( phy , & ihost - > ports [ SCI_MAX_PORTS ] . sci ) ;
port - > phy_table [ phy - > phy_index ] = NULL ;
return SCI_SUCCESS ;
}
return SCI_FAILURE ;
}
/**
* This method requests the SAS address for the supplied SAS port from the SCI
* implementation .
* @ sci_port : a handle corresponding to the SAS port for which to return the
* SAS address .
* @ sas_address : This parameter specifies a pointer to a SAS address structure
* into which the core will copy the SAS address for the port .
*
*/
void scic_sds_port_get_sas_address (
struct scic_sds_port * sci_port ,
struct sci_sas_address * sas_address )
{
u32 index ;
sas_address - > high = 0 ;
sas_address - > low = 0 ;
for ( index = 0 ; index < SCI_MAX_PHYS ; index + + ) {
if ( sci_port - > phy_table [ index ] ! = NULL ) {
scic_sds_phy_get_sas_address ( sci_port - > phy_table [ index ] , sas_address ) ;
}
}
}
/*
* This function requests the SAS address for the device directly attached to
* this SAS port .
* @ sci_port : a handle corresponding to the SAS port for which to return the
* SAS address .
* @ sas_address : This parameter specifies a pointer to a SAS address structure
* into which the core will copy the SAS address for the device directly
* attached to the port .
*
*/
void scic_sds_port_get_attached_sas_address (
struct scic_sds_port * sci_port ,
struct sci_sas_address * sas_address )
{
struct scic_sds_phy * sci_phy ;
/*
* Ensure that the phy is both part of the port and currently
* connected to the remote end - point .
2011-07-02 22:56:22 -07:00
*/
2011-05-10 02:28:46 -07:00
sci_phy = scic_sds_port_get_a_connected_phy ( sci_port ) ;
if ( sci_phy ) {
if ( sci_phy - > protocol ! = SCIC_SDS_PHY_PROTOCOL_SATA ) {
scic_sds_phy_get_attached_sas_address ( sci_phy ,
sas_address ) ;
} else {
scic_sds_phy_get_sas_address ( sci_phy , sas_address ) ;
sas_address - > low + = sci_phy - > phy_index ;
}
} else {
sas_address - > high = 0 ;
sas_address - > low = 0 ;
}
}
2011-03-31 13:10:44 -07:00
2011-05-10 02:28:46 -07:00
/**
* scic_sds_port_construct_dummy_rnc ( ) - create dummy rnc for si workaround
*
* @ sci_port : logical port on which we need to create the remote node context
* @ rni : remote node index for this remote node context .
*
* This routine will construct a dummy remote node context data structure
* This structure will be posted to the hardware to work around a scheduler
* error in the hardware .
*/
static void scic_sds_port_construct_dummy_rnc ( struct scic_sds_port * sci_port , u16 rni )
{
union scu_remote_node_context * rnc ;
rnc = & sci_port - > owning_controller - > remote_node_context_table [ rni ] ;
memset ( rnc , 0 , sizeof ( union scu_remote_node_context ) ) ;
rnc - > ssp . remote_sas_address_hi = 0 ;
rnc - > ssp . remote_sas_address_lo = 0 ;
rnc - > ssp . remote_node_index = rni ;
rnc - > ssp . remote_node_port_width = 1 ;
rnc - > ssp . logical_port_index = sci_port - > physical_port_index ;
rnc - > ssp . nexus_loss_timer_enable = false ;
rnc - > ssp . check_bit = false ;
rnc - > ssp . is_valid = true ;
rnc - > ssp . is_remote_node_context = true ;
rnc - > ssp . function_number = 0 ;
rnc - > ssp . arbitration_wait_time = 0 ;
}
/**
* scic_sds_port_construct_dummy_task ( ) - create dummy task for si workaround
* @ sci_port The logical port on which we need to create the
* remote node context .
* context .
* @ tci The remote node index for this remote node context .
*
* This routine will construct a dummy task context data structure . This
* structure will be posted to the hardwre to work around a scheduler error
* in the hardware .
*
*/
static void scic_sds_port_construct_dummy_task ( struct scic_sds_port * sci_port , u16 tci )
{
struct scu_task_context * task_context ;
task_context = scic_sds_controller_get_task_context_buffer ( sci_port - > owning_controller , tci ) ;
memset ( task_context , 0 , sizeof ( struct scu_task_context ) ) ;
task_context - > abort = 0 ;
task_context - > priority = 0 ;
task_context - > initiator_request = 1 ;
task_context - > connection_rate = 1 ;
task_context - > protocol_engine_index = 0 ;
task_context - > logical_port_index = sci_port - > physical_port_index ;
task_context - > protocol_type = SCU_TASK_CONTEXT_PROTOCOL_SSP ;
task_context - > task_index = scic_sds_io_tag_get_index ( tci ) ;
task_context - > valid = SCU_TASK_CONTEXT_VALID ;
task_context - > context_type = SCU_TASK_CONTEXT_TYPE ;
task_context - > remote_node_index = sci_port - > reserved_rni ;
task_context - > command_code = 0 ;
task_context - > link_layer_control = 0 ;
task_context - > do_not_dma_ssp_good_response = 1 ;
task_context - > strict_ordering = 0 ;
task_context - > control_frame = 0 ;
task_context - > timeout_enable = 0 ;
task_context - > block_guard_enable = 0 ;
task_context - > address_modifier = 0 ;
task_context - > task_phase = 0x01 ;
}
static void scic_sds_port_destroy_dummy_resources ( struct scic_sds_port * sci_port )
{
struct scic_sds_controller * scic = sci_port - > owning_controller ;
if ( sci_port - > reserved_tci ! = SCU_DUMMY_INDEX )
scic_controller_free_io_tag ( scic , sci_port - > reserved_tci ) ;
if ( sci_port - > reserved_rni ! = SCU_DUMMY_INDEX )
scic_sds_remote_node_table_release_remote_node_index ( & scic - > available_remote_nodes ,
1 , sci_port - > reserved_rni ) ;
sci_port - > reserved_rni = SCU_DUMMY_INDEX ;
sci_port - > reserved_tci = SCU_DUMMY_INDEX ;
}
/**
* This method performs initialization of the supplied port . Initialization
* includes : - state machine initialization - member variable initialization
* - configuring the phy_mask
* @ sci_port :
* @ transport_layer_registers :
* @ port_task_scheduler_registers :
* @ port_configuration_regsiter :
*
* enum sci_status SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION This value is returned
* if the phy being added to the port
*/
enum sci_status scic_sds_port_initialize (
struct scic_sds_port * sci_port ,
void __iomem * port_task_scheduler_registers ,
void __iomem * port_configuration_regsiter ,
void __iomem * viit_registers )
{
sci_port - > port_task_scheduler_registers = port_task_scheduler_registers ;
sci_port - > port_pe_configuration_register = port_configuration_regsiter ;
sci_port - > viit_registers = viit_registers ;
return SCI_SUCCESS ;
}
/**
* This method assigns the direct attached device ID for this port .
*
* @ param [ in ] sci_port The port for which the direct attached device id is to
* be assigned .
* @ param [ in ] device_id The direct attached device ID to assign to the port .
* This will be the RNi for the device
*/
void scic_sds_port_setup_transports (
struct scic_sds_port * sci_port ,
u32 device_id )
{
u8 index ;
for ( index = 0 ; index < SCI_MAX_PHYS ; index + + ) {
if ( sci_port - > active_phy_mask & ( 1 < < index ) )
scic_sds_phy_setup_transport ( sci_port - > phy_table [ index ] , device_id ) ;
2011-07-02 22:56:22 -07:00
}
2011-05-10 02:28:46 -07:00
}
2011-07-02 22:56:22 -07:00
2011-05-10 02:28:46 -07:00
/**
*
* @ sci_port : This is the port on which the phy should be enabled .
* @ sci_phy : This is the specific phy which to enable .
* @ do_notify_user : This parameter specifies whether to inform the user ( via
* scic_cb_port_link_up ( ) ) as to the fact that a new phy as become ready .
*
* This function will activate the phy in the port .
* Activation includes : - adding
* the phy to the port - enabling the Protocol Engine in the silicon . -
* notifying the user that the link is up . none
*/
static void scic_sds_port_activate_phy ( struct scic_sds_port * sci_port ,
struct scic_sds_phy * sci_phy ,
bool do_notify_user )
{
struct scic_sds_controller * scic = sci_port - > owning_controller ;
struct isci_host * ihost = scic_to_ihost ( scic ) ;
if ( sci_phy - > protocol ! = SCIC_SDS_PHY_PROTOCOL_SATA )
scic_sds_phy_resume ( sci_phy ) ;
sci_port - > active_phy_mask | = 1 < < sci_phy - > phy_index ;
scic_sds_controller_clear_invalid_phy ( scic , sci_phy ) ;
if ( do_notify_user = = true )
isci_port_link_up ( ihost , sci_port , sci_phy ) ;
2011-07-02 22:56:22 -07:00
}
2011-03-26 16:11:51 -07:00
2011-05-10 02:28:46 -07:00
void scic_sds_port_deactivate_phy ( struct scic_sds_port * sci_port ,
struct scic_sds_phy * sci_phy ,
bool do_notify_user )
2011-03-26 16:11:51 -07:00
{
2011-05-10 02:28:46 -07:00
struct scic_sds_controller * scic = scic_sds_port_get_controller ( sci_port ) ;
struct isci_port * iport = sci_port_to_iport ( sci_port ) ;
struct isci_host * ihost = scic_to_ihost ( scic ) ;
struct isci_phy * iphy = sci_phy_to_iphy ( sci_phy ) ;
sci_port - > active_phy_mask & = ~ ( 1 < < sci_phy - > phy_index ) ;
sci_phy - > max_negotiated_speed = SAS_LINK_RATE_UNKNOWN ;
/* Re-assign the phy back to the LP as if it were a narrow port */
writel ( sci_phy - > phy_index ,
& sci_port - > port_pe_configuration_register [ sci_phy - > phy_index ] ) ;
if ( do_notify_user = = true )
isci_port_link_down ( ihost , iphy , iport ) ;
}
/**
*
* @ sci_port : This is the port on which the phy should be disabled .
* @ sci_phy : This is the specific phy which to disabled .
*
* This function will disable the phy and report that the phy is not valid for
* this port object . None
*/
static void scic_sds_port_invalid_link_up ( struct scic_sds_port * sci_port ,
struct scic_sds_phy * sci_phy )
{
struct scic_sds_controller * scic = sci_port - > owning_controller ;
/*
* Check to see if we have alreay reported this link as bad and if
* not go ahead and tell the SCI_USER that we have discovered an
* invalid link .
*/
if ( ( scic - > invalid_phy_mask & ( 1 < < sci_phy - > phy_index ) ) = = 0 ) {
scic_sds_controller_set_invalid_phy ( scic , sci_phy ) ;
dev_warn ( & scic_to_ihost ( scic ) - > pdev - > dev , " Invalid link up! \n " ) ;
}
}
2011-05-11 23:52:21 +00:00
static bool is_port_ready_state ( enum scic_sds_port_states state )
{
switch ( state ) {
case SCI_BASE_PORT_STATE_READY :
case SCIC_SDS_PORT_READY_SUBSTATE_WAITING :
case SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL :
case SCIC_SDS_PORT_READY_SUBSTATE_CONFIGURING :
return true ;
default :
return false ;
}
}
/* flag dummy rnc hanling when exiting a ready state */
static void port_state_machine_change ( struct scic_sds_port * sci_port ,
enum scic_sds_port_states state )
{
struct sci_base_state_machine * sm = & sci_port - > state_machine ;
enum scic_sds_port_states old_state = sm - > current_state_id ;
if ( is_port_ready_state ( old_state ) & & ! is_port_ready_state ( state ) )
sci_port - > ready_exit = true ;
sci_base_state_machine_change_state ( sm , state ) ;
sci_port - > ready_exit = false ;
}
2011-05-10 02:28:46 -07:00
/**
* scic_sds_port_general_link_up_handler - phy can be assigned to port ?
* @ sci_port : scic_sds_port object for which has a phy that has gone link up .
* @ sci_phy : This is the struct scic_sds_phy object that has gone link up .
* @ do_notify_user : This parameter specifies whether to inform the user ( via
* scic_cb_port_link_up ( ) ) as to the fact that a new phy as become ready .
*
* Determine if this phy can be assigned to this
* port . If the phy is not a valid PHY for
* this port then the function will notify the user . A PHY can only be
* part of a port if it ' s attached SAS ADDRESS is the same as all other PHYs in
* the same port . none
*/
static void scic_sds_port_general_link_up_handler ( struct scic_sds_port * sci_port ,
struct scic_sds_phy * sci_phy ,
bool do_notify_user )
{
struct sci_sas_address port_sas_address ;
struct sci_sas_address phy_sas_address ;
scic_sds_port_get_attached_sas_address ( sci_port , & port_sas_address ) ;
scic_sds_phy_get_attached_sas_address ( sci_phy , & phy_sas_address ) ;
/* If the SAS address of the new phy matches the SAS address of
* other phys in the port OR this is the first phy in the port ,
* then activate the phy and allow it to be used for operations
* in this port .
*/
if ( ( phy_sas_address . high = = port_sas_address . high & &
phy_sas_address . low = = port_sas_address . low ) | |
sci_port - > active_phy_mask = = 0 ) {
struct sci_base_state_machine * sm = & sci_port - > state_machine ;
scic_sds_port_activate_phy ( sci_port , sci_phy , do_notify_user ) ;
if ( sm - > current_state_id = = SCI_BASE_PORT_STATE_RESETTING )
2011-05-11 23:52:21 +00:00
port_state_machine_change ( sci_port , SCI_BASE_PORT_STATE_READY ) ;
2011-05-10 02:28:46 -07:00
} else
scic_sds_port_invalid_link_up ( sci_port , sci_phy ) ;
}
/**
* This method returns false if the port only has a single phy object assigned .
* If there are no phys or more than one phy then the method will return
* true .
* @ sci_port : The port for which the wide port condition is to be checked .
*
* bool true Is returned if this is a wide ported port . false Is returned if
* this is a narrow port .
*/
static bool scic_sds_port_is_wide ( struct scic_sds_port * sci_port )
{
u32 index ;
u32 phy_count = 0 ;
for ( index = 0 ; index < SCI_MAX_PHYS ; index + + ) {
if ( sci_port - > phy_table [ index ] ! = NULL ) {
phy_count + + ;
}
}
return phy_count ! = 1 ;
}
/**
* This method is called by the PHY object when the link is detected . if the
* port wants the PHY to continue on to the link up state then the port
* layer must return true . If the port object returns false the phy object
* must halt its attempt to go link up .
* @ sci_port : The port associated with the phy object .
* @ sci_phy : The phy object that is trying to go link up .
*
* true if the phy object can continue to the link up condition . true Is
* returned if this phy can continue to the ready state . false Is returned if
* can not continue on to the ready state . This notification is in place for
* wide ports and direct attached phys . Since there are no wide ported SATA
* devices this could become an invalid port configuration .
*/
bool scic_sds_port_link_detected (
struct scic_sds_port * sci_port ,
struct scic_sds_phy * sci_phy )
{
if ( ( sci_port - > logical_port_index ! = SCIC_SDS_DUMMY_PORT ) & &
( sci_phy - > protocol = = SCIC_SDS_PHY_PROTOCOL_SATA ) & &
scic_sds_port_is_wide ( sci_port ) ) {
scic_sds_port_invalid_link_up ( sci_port , sci_phy ) ;
return false ;
}
return true ;
}
2011-05-19 11:59:10 +00:00
static void port_timeout ( unsigned long data )
2011-05-10 02:28:46 -07:00
{
2011-05-19 11:59:10 +00:00
struct sci_timer * tmr = ( struct sci_timer * ) data ;
struct scic_sds_port * sci_port = container_of ( tmr , typeof ( * sci_port ) , timer ) ;
struct isci_host * ihost = scic_to_ihost ( sci_port - > owning_controller ) ;
unsigned long flags ;
2011-05-10 02:28:46 -07:00
u32 current_state ;
2011-05-19 11:59:10 +00:00
spin_lock_irqsave ( & ihost - > scic_lock , flags ) ;
if ( tmr - > cancel )
goto done ;
2011-05-11 23:52:21 +00:00
current_state = sci_base_state_machine_get_state ( & sci_port - > state_machine ) ;
2011-05-10 02:28:46 -07:00
if ( current_state = = SCI_BASE_PORT_STATE_RESETTING ) {
2011-05-11 23:52:21 +00:00
/* if the port is still in the resetting state then the timeout
* fired before the reset completed .
2011-05-10 02:28:46 -07:00
*/
2011-05-11 23:52:21 +00:00
port_state_machine_change ( sci_port , SCI_BASE_PORT_STATE_FAILED ) ;
2011-05-10 02:28:46 -07:00
} else if ( current_state = = SCI_BASE_PORT_STATE_STOPPED ) {
2011-05-11 23:52:21 +00:00
/* if the port is stopped then the start request failed In this
* case stay in the stopped state .
2011-05-10 02:28:46 -07:00
*/
dev_err ( sciport_to_dev ( sci_port ) ,
" %s: SCIC Port 0x%p failed to stop before tiemout. \n " ,
__func__ ,
sci_port ) ;
} else if ( current_state = = SCI_BASE_PORT_STATE_STOPPING ) {
2011-05-11 23:52:21 +00:00
/* if the port is still stopping then the stop has not completed */
isci_port_stop_complete ( sci_port - > owning_controller ,
sci_port ,
SCI_FAILURE_TIMEOUT ) ;
2011-05-10 02:28:46 -07:00
} else {
2011-05-11 23:52:21 +00:00
/* The port is in the ready state and we have a timer
2011-05-10 02:28:46 -07:00
* reporting a timeout this should not happen .
*/
dev_err ( sciport_to_dev ( sci_port ) ,
" %s: SCIC Port 0x%p is processing a timeout operation "
2011-05-11 23:52:21 +00:00
" in state %d. \n " , __func__ , sci_port , current_state ) ;
2011-05-10 02:28:46 -07:00
}
2011-05-19 11:59:10 +00:00
done :
spin_unlock_irqrestore ( & ihost - > scic_lock , flags ) ;
2011-05-10 02:28:46 -07:00
}
/* --------------------------------------------------------------------------- */
/**
* This function updates the hardwares VIIT entry for this port .
*
*
*/
static void scic_sds_port_update_viit_entry ( struct scic_sds_port * sci_port )
{
struct sci_sas_address sas_address ;
scic_sds_port_get_sas_address ( sci_port , & sas_address ) ;
writel ( sas_address . high ,
& sci_port - > viit_registers - > initiator_sas_address_hi ) ;
writel ( sas_address . low ,
& sci_port - > viit_registers - > initiator_sas_address_lo ) ;
/* This value get cleared just in case its not already cleared */
writel ( 0 , & sci_port - > viit_registers - > reserved ) ;
/* We are required to update the status register last */
writel ( SCU_VIIT_ENTRY_ID_VIIT |
SCU_VIIT_IPPT_INITIATOR |
( ( 1 < < sci_port - > physical_port_index ) < < SCU_VIIT_ENTRY_LPVIE_SHIFT ) |
SCU_VIIT_STATUS_ALL_VALID ,
& sci_port - > viit_registers - > status ) ;
}
/**
* This method returns the maximum allowed speed for data transfers on this
* port . This maximum allowed speed evaluates to the maximum speed of the
* slowest phy in the port .
* @ sci_port : This parameter specifies the port for which to retrieve the
* maximum allowed speed .
*
* This method returns the maximum negotiated speed of the slowest phy in the
* port .
*/
enum sas_linkrate scic_sds_port_get_max_allowed_speed (
struct scic_sds_port * sci_port )
{
u16 index ;
enum sas_linkrate max_allowed_speed = SAS_LINK_RATE_6_0_GBPS ;
struct scic_sds_phy * phy = NULL ;
/*
* Loop through all of the phys in this port and find the phy with the
* lowest maximum link rate . */
for ( index = 0 ; index < SCI_MAX_PHYS ; index + + ) {
phy = sci_port - > phy_table [ index ] ;
if (
( phy ! = NULL )
& & ( scic_sds_port_active_phy ( sci_port , phy ) = = true )
& & ( phy - > max_negotiated_speed < max_allowed_speed )
)
max_allowed_speed = phy - > max_negotiated_speed ;
}
return max_allowed_speed ;
}
static void scic_port_enable_broadcast_change_notification ( struct scic_sds_port * port )
{
struct scic_sds_phy * phy ;
u32 register_value ;
u8 index ;
/* Loop through all of the phys to enable BCN. */
for ( index = 0 ; index < SCI_MAX_PHYS ; index + + ) {
phy = port - > phy_table [ index ] ;
if ( phy ! = NULL ) {
register_value =
readl ( & phy - > link_layer_registers - > link_layer_control ) ;
/* clear the bit by writing 1. */
writel ( register_value ,
& phy - > link_layer_registers - > link_layer_control ) ;
}
}
}
/**
*
* @ sci_port : This is the struct scic_sds_port object to suspend .
*
* This method will susped the port task scheduler for this port object . none
*/
static void
scic_sds_port_suspend_port_task_scheduler ( struct scic_sds_port * port )
{
u32 pts_control_value ;
pts_control_value = readl ( & port - > port_task_scheduler_registers - > control ) ;
pts_control_value | = SCU_PTSxCR_GEN_BIT ( SUSPEND ) ;
writel ( pts_control_value , & port - > port_task_scheduler_registers - > control ) ;
}
/**
* scic_sds_port_post_dummy_request ( ) - post dummy / workaround request
* @ sci_port : port to post task
*
* Prevent the hardware scheduler from posting new requests to the front
* of the scheduler queue causing a starvation problem for currently
* ongoing requests .
*
*/
static void scic_sds_port_post_dummy_request ( struct scic_sds_port * sci_port )
{
u32 command ;
struct scu_task_context * task_context ;
struct scic_sds_controller * scic = sci_port - > owning_controller ;
u16 tci = sci_port - > reserved_tci ;
task_context = scic_sds_controller_get_task_context_buffer ( scic , tci ) ;
task_context - > abort = 0 ;
command = SCU_CONTEXT_COMMAND_REQUEST_TYPE_POST_TC |
sci_port - > physical_port_index < < SCU_CONTEXT_COMMAND_LOGICAL_PORT_SHIFT |
tci ;
scic_sds_controller_post_request ( scic , command ) ;
}
/**
* This routine will abort the dummy request . This will alow the hardware to
* power down parts of the silicon to save power .
*
* @ sci_port : The port on which the task must be aborted .
*
*/
static void scic_sds_port_abort_dummy_request ( struct scic_sds_port * sci_port )
{
struct scic_sds_controller * scic = sci_port - > owning_controller ;
u16 tci = sci_port - > reserved_tci ;
struct scu_task_context * tc ;
u32 command ;
tc = scic_sds_controller_get_task_context_buffer ( scic , tci ) ;
tc - > abort = 1 ;
command = SCU_CONTEXT_COMMAND_REQUEST_POST_TC_ABORT |
sci_port - > physical_port_index < < SCU_CONTEXT_COMMAND_LOGICAL_PORT_SHIFT |
tci ;
scic_sds_controller_post_request ( scic , command ) ;
}
/**
*
* @ sci_port : This is the struct scic_sds_port object to resume .
*
* This method will resume the port task scheduler for this port object . none
*/
static void
scic_sds_port_resume_port_task_scheduler ( struct scic_sds_port * port )
{
u32 pts_control_value ;
pts_control_value = readl ( & port - > port_task_scheduler_registers - > control ) ;
pts_control_value & = ~ SCU_PTSxCR_GEN_BIT ( SUSPEND ) ;
writel ( pts_control_value , & port - > port_task_scheduler_registers - > control ) ;
}
2011-05-12 07:42:17 -07:00
static void scic_sds_port_ready_substate_waiting_enter ( struct sci_base_state_machine * sm )
2011-05-10 02:28:46 -07:00
{
2011-05-12 07:42:17 -07:00
struct scic_sds_port * sci_port = container_of ( sm , typeof ( * sci_port ) , state_machine ) ;
2011-05-10 02:28:46 -07:00
scic_sds_port_suspend_port_task_scheduler ( sci_port ) ;
sci_port - > not_ready_reason = SCIC_PORT_NOT_READY_NO_ACTIVE_PHYS ;
if ( sci_port - > active_phy_mask ! = 0 ) {
/* At least one of the phys on the port is ready */
2011-05-11 23:52:21 +00:00
port_state_machine_change ( sci_port ,
SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL ) ;
2011-05-10 02:28:46 -07:00
}
}
2011-05-12 07:42:17 -07:00
static void scic_sds_port_ready_substate_operational_enter ( struct sci_base_state_machine * sm )
2011-05-10 02:28:46 -07:00
{
u32 index ;
2011-05-12 07:42:17 -07:00
struct scic_sds_port * sci_port = container_of ( sm , typeof ( * sci_port ) , state_machine ) ;
2011-05-10 02:28:46 -07:00
struct scic_sds_controller * scic = sci_port - > owning_controller ;
struct isci_host * ihost = scic_to_ihost ( scic ) ;
struct isci_port * iport = sci_port_to_iport ( sci_port ) ;
isci_port_ready ( ihost , iport ) ;
for ( index = 0 ; index < SCI_MAX_PHYS ; index + + ) {
if ( sci_port - > phy_table [ index ] ) {
writel ( sci_port - > physical_port_index ,
& sci_port - > port_pe_configuration_register [
sci_port - > phy_table [ index ] - > phy_index ] ) ;
}
}
scic_sds_port_update_viit_entry ( sci_port ) ;
scic_sds_port_resume_port_task_scheduler ( sci_port ) ;
/*
* Post the dummy task for the port so the hardware can schedule
* io correctly
*/
scic_sds_port_post_dummy_request ( sci_port ) ;
}
2011-05-11 23:52:21 +00:00
static void scic_sds_port_invalidate_dummy_remote_node ( struct scic_sds_port * sci_port )
{
struct scic_sds_controller * scic = sci_port - > owning_controller ;
u8 phys_index = sci_port - > physical_port_index ;
union scu_remote_node_context * rnc ;
u16 rni = sci_port - > reserved_rni ;
u32 command ;
rnc = & scic - > remote_node_context_table [ rni ] ;
rnc - > ssp . is_valid = false ;
/* ensure the preceding tc abort request has reached the
* controller and give it ample time to act before posting the rnc
* invalidate
*/
readl ( & scic - > smu_registers - > interrupt_status ) ; /* flush */
udelay ( 10 ) ;
command = SCU_CONTEXT_COMMAND_POST_RNC_INVALIDATE |
phys_index < < SCU_CONTEXT_COMMAND_LOGICAL_PORT_SHIFT | rni ;
scic_sds_controller_post_request ( scic , command ) ;
}
2011-05-10 02:28:46 -07:00
/**
*
* @ object : This is the object which is cast to a struct scic_sds_port object .
*
* This method will perform the actions required by the struct scic_sds_port on
* exiting the SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL . This function reports
* the port not ready and suspends the port task scheduler . none
*/
2011-05-12 07:42:17 -07:00
static void scic_sds_port_ready_substate_operational_exit ( struct sci_base_state_machine * sm )
2011-05-10 02:28:46 -07:00
{
2011-05-12 07:42:17 -07:00
struct scic_sds_port * sci_port = container_of ( sm , typeof ( * sci_port ) , state_machine ) ;
2011-05-10 02:28:46 -07:00
struct scic_sds_controller * scic = sci_port - > owning_controller ;
struct isci_host * ihost = scic_to_ihost ( scic ) ;
struct isci_port * iport = sci_port_to_iport ( sci_port ) ;
/*
* Kill the dummy task for this port if it has not yet posted
* the hardware will treat this as a NOP and just return abort
* complete .
*/
scic_sds_port_abort_dummy_request ( sci_port ) ;
isci_port_not_ready ( ihost , iport ) ;
2011-05-11 23:52:21 +00:00
if ( sci_port - > ready_exit )
scic_sds_port_invalidate_dummy_remote_node ( sci_port ) ;
2011-05-10 02:28:46 -07:00
}
2011-05-12 07:42:17 -07:00
static void scic_sds_port_ready_substate_configuring_enter ( struct sci_base_state_machine * sm )
2011-05-10 02:28:46 -07:00
{
2011-05-12 07:42:17 -07:00
struct scic_sds_port * sci_port = container_of ( sm , typeof ( * sci_port ) , state_machine ) ;
2011-05-10 02:28:46 -07:00
struct scic_sds_controller * scic = sci_port - > owning_controller ;
struct isci_host * ihost = scic_to_ihost ( scic ) ;
struct isci_port * iport = sci_port_to_iport ( sci_port ) ;
if ( sci_port - > active_phy_mask = = 0 ) {
isci_port_not_ready ( ihost , iport ) ;
2011-05-11 23:52:21 +00:00
port_state_machine_change ( sci_port ,
SCIC_SDS_PORT_READY_SUBSTATE_WAITING ) ;
2011-05-10 02:28:46 -07:00
} else if ( sci_port - > started_request_count = = 0 )
2011-05-11 23:52:21 +00:00
port_state_machine_change ( sci_port ,
SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL ) ;
2011-05-10 02:28:46 -07:00
}
2011-05-12 07:42:17 -07:00
static void scic_sds_port_ready_substate_configuring_exit ( struct sci_base_state_machine * sm )
2011-05-10 02:28:46 -07:00
{
2011-05-12 07:42:17 -07:00
struct scic_sds_port * sci_port = container_of ( sm , typeof ( * sci_port ) , state_machine ) ;
2011-05-10 02:28:46 -07:00
scic_sds_port_suspend_port_task_scheduler ( sci_port ) ;
2011-05-11 23:52:21 +00:00
if ( sci_port - > ready_exit )
scic_sds_port_invalidate_dummy_remote_node ( sci_port ) ;
2011-05-10 02:28:46 -07:00
}
2011-05-11 23:52:26 +00:00
enum sci_status scic_sds_port_start ( struct scic_sds_port * sci_port )
{
struct scic_sds_controller * scic = sci_port - > owning_controller ;
enum sci_status status = SCI_SUCCESS ;
enum scic_sds_port_states state ;
u32 phy_mask ;
state = sci_port - > state_machine . current_state_id ;
if ( state ! = SCI_BASE_PORT_STATE_STOPPED ) {
dev_warn ( sciport_to_dev ( sci_port ) ,
" %s: in wrong state: %d \n " , __func__ , state ) ;
return SCI_FAILURE_INVALID_STATE ;
}
if ( sci_port - > assigned_device_count > 0 ) {
/* TODO This is a start failure operation because
* there are still devices assigned to this port .
* There must be no devices assigned to a port on a
* start operation .
*/
return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION ;
}
if ( sci_port - > reserved_rni = = SCU_DUMMY_INDEX ) {
u16 rni = scic_sds_remote_node_table_allocate_remote_node (
& scic - > available_remote_nodes , 1 ) ;
if ( rni ! = SCU_DUMMY_INDEX )
scic_sds_port_construct_dummy_rnc ( sci_port , rni ) ;
else
status = SCI_FAILURE_INSUFFICIENT_RESOURCES ;
sci_port - > reserved_rni = rni ;
}
if ( sci_port - > reserved_tci = = SCU_DUMMY_INDEX ) {
/* Allocate a TCI and remove the sequence nibble */
u16 tci = scic_controller_allocate_io_tag ( scic ) ;
if ( tci ! = SCU_DUMMY_INDEX )
scic_sds_port_construct_dummy_task ( sci_port , tci ) ;
else
status = SCI_FAILURE_INSUFFICIENT_RESOURCES ;
sci_port - > reserved_tci = tci ;
}
if ( status = = SCI_SUCCESS ) {
phy_mask = scic_sds_port_get_phys ( sci_port ) ;
/*
* There are one or more phys assigned to this port . Make sure
* the port ' s phy mask is in fact legal and supported by the
* silicon .
*/
if ( scic_sds_port_is_phy_mask_valid ( sci_port , phy_mask ) = = true ) {
port_state_machine_change ( sci_port ,
SCI_BASE_PORT_STATE_READY ) ;
return SCI_SUCCESS ;
}
status = SCI_FAILURE ;
}
if ( status ! = SCI_SUCCESS )
scic_sds_port_destroy_dummy_resources ( sci_port ) ;
return status ;
}
2011-05-11 23:52:31 +00:00
enum sci_status scic_sds_port_stop ( struct scic_sds_port * sci_port )
{
enum scic_sds_port_states state ;
state = sci_port - > state_machine . current_state_id ;
switch ( state ) {
case SCI_BASE_PORT_STATE_STOPPED :
return SCI_SUCCESS ;
case SCIC_SDS_PORT_READY_SUBSTATE_WAITING :
case SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL :
case SCIC_SDS_PORT_READY_SUBSTATE_CONFIGURING :
case SCI_BASE_PORT_STATE_RESETTING :
port_state_machine_change ( sci_port ,
SCI_BASE_PORT_STATE_STOPPING ) ;
return SCI_SUCCESS ;
default :
dev_warn ( sciport_to_dev ( sci_port ) ,
" %s: in wrong state: %d \n " , __func__ , state ) ;
return SCI_FAILURE_INVALID_STATE ;
}
}
2011-05-12 19:10:03 +00:00
static enum sci_status scic_port_hard_reset ( struct scic_sds_port * sci_port , u32 timeout )
{
enum sci_status status = SCI_FAILURE_INVALID_PHY ;
struct scic_sds_phy * selected_phy = NULL ;
enum scic_sds_port_states state ;
u32 phy_index ;
state = sci_port - > state_machine . current_state_id ;
if ( state ! = SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL ) {
dev_warn ( sciport_to_dev ( sci_port ) ,
" %s: in wrong state: %d \n " , __func__ , state ) ;
return SCI_FAILURE_INVALID_STATE ;
}
/* Select a phy on which we can send the hard reset request. */
for ( phy_index = 0 ; phy_index < SCI_MAX_PHYS & & ! selected_phy ; phy_index + + ) {
selected_phy = sci_port - > phy_table [ phy_index ] ;
if ( selected_phy & &
! scic_sds_port_active_phy ( sci_port , selected_phy ) ) {
/*
* We found a phy but it is not ready select
* different phy
*/
selected_phy = NULL ;
}
}
/* If we have a phy then go ahead and start the reset procedure */
if ( ! selected_phy )
return status ;
status = scic_sds_phy_reset ( selected_phy ) ;
if ( status ! = SCI_SUCCESS )
return status ;
2011-05-19 11:59:10 +00:00
sci_mod_timer ( & sci_port - > timer , timeout ) ;
2011-05-12 19:10:03 +00:00
sci_port - > not_ready_reason = SCIC_PORT_NOT_READY_HARD_RESET_REQUESTED ;
port_state_machine_change ( sci_port ,
SCI_BASE_PORT_STATE_RESETTING ) ;
return SCI_SUCCESS ;
}
/**
* scic_sds_port_add_phy ( ) -
* @ sci_port : This parameter specifies the port in which the phy will be added .
* @ sci_phy : This parameter is the phy which is to be added to the port .
*
* This method will add a PHY to the selected port . This method returns an
* enum sci_status . SCI_SUCCESS the phy has been added to the port . Any other
* status is a failure to add the phy to the port .
*/
enum sci_status scic_sds_port_add_phy ( struct scic_sds_port * sci_port ,
struct scic_sds_phy * sci_phy )
{
enum sci_status status ;
enum scic_sds_port_states state ;
state = sci_port - > state_machine . current_state_id ;
switch ( state ) {
case SCI_BASE_PORT_STATE_STOPPED : {
struct sci_sas_address port_sas_address ;
/* Read the port assigned SAS Address if there is one */
scic_sds_port_get_sas_address ( sci_port , & port_sas_address ) ;
if ( port_sas_address . high ! = 0 & & port_sas_address . low ! = 0 ) {
struct sci_sas_address phy_sas_address ;
/* Make sure that the PHY SAS Address matches the SAS Address
* for this port
*/
scic_sds_phy_get_sas_address ( sci_phy , & phy_sas_address ) ;
if ( port_sas_address . high ! = phy_sas_address . high | |
port_sas_address . low ! = phy_sas_address . low )
return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION ;
}
return scic_sds_port_set_phy ( sci_port , sci_phy ) ;
}
case SCIC_SDS_PORT_READY_SUBSTATE_WAITING :
case SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL :
status = scic_sds_port_set_phy ( sci_port , sci_phy ) ;
if ( status ! = SCI_SUCCESS )
return status ;
scic_sds_port_general_link_up_handler ( sci_port , sci_phy , true ) ;
sci_port - > not_ready_reason = SCIC_PORT_NOT_READY_RECONFIGURING ;
port_state_machine_change ( sci_port , SCIC_SDS_PORT_READY_SUBSTATE_CONFIGURING ) ;
return status ;
case SCIC_SDS_PORT_READY_SUBSTATE_CONFIGURING :
status = scic_sds_port_set_phy ( sci_port , sci_phy ) ;
if ( status ! = SCI_SUCCESS )
return status ;
scic_sds_port_general_link_up_handler ( sci_port , sci_phy , true ) ;
/* Re-enter the configuring state since this may be the last phy in
* the port .
*/
port_state_machine_change ( sci_port ,
SCIC_SDS_PORT_READY_SUBSTATE_CONFIGURING ) ;
return SCI_SUCCESS ;
default :
dev_warn ( sciport_to_dev ( sci_port ) ,
" %s: in wrong state: %d \n " , __func__ , state ) ;
return SCI_FAILURE_INVALID_STATE ;
}
}
/**
* scic_sds_port_remove_phy ( ) -
* @ sci_port : This parameter specifies the port in which the phy will be added .
* @ sci_phy : This parameter is the phy which is to be added to the port .
*
* This method will remove the PHY from the selected PORT . This method returns
* an enum sci_status . SCI_SUCCESS the phy has been removed from the port . Any
* other status is a failure to add the phy to the port .
*/
enum sci_status scic_sds_port_remove_phy ( struct scic_sds_port * sci_port ,
struct scic_sds_phy * sci_phy )
{
enum sci_status status ;
enum scic_sds_port_states state ;
state = sci_port - > state_machine . current_state_id ;
switch ( state ) {
case SCI_BASE_PORT_STATE_STOPPED :
return scic_sds_port_clear_phy ( sci_port , sci_phy ) ;
case SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL :
status = scic_sds_port_clear_phy ( sci_port , sci_phy ) ;
if ( status ! = SCI_SUCCESS )
return status ;
scic_sds_port_deactivate_phy ( sci_port , sci_phy , true ) ;
sci_port - > not_ready_reason = SCIC_PORT_NOT_READY_RECONFIGURING ;
port_state_machine_change ( sci_port ,
SCIC_SDS_PORT_READY_SUBSTATE_CONFIGURING ) ;
return SCI_SUCCESS ;
case SCIC_SDS_PORT_READY_SUBSTATE_CONFIGURING :
status = scic_sds_port_clear_phy ( sci_port , sci_phy ) ;
if ( status ! = SCI_SUCCESS )
return status ;
scic_sds_port_deactivate_phy ( sci_port , sci_phy , true ) ;
/* Re-enter the configuring state since this may be the last phy in
* the port
*/
port_state_machine_change ( sci_port ,
SCIC_SDS_PORT_READY_SUBSTATE_CONFIGURING ) ;
2011-05-12 19:10:14 +00:00
return SCI_SUCCESS ;
default :
dev_warn ( sciport_to_dev ( sci_port ) ,
" %s: in wrong state: %d \n " , __func__ , state ) ;
return SCI_FAILURE_INVALID_STATE ;
}
}
2011-05-12 19:10:03 +00:00
2011-05-12 19:10:14 +00:00
enum sci_status scic_sds_port_link_up ( struct scic_sds_port * sci_port ,
struct scic_sds_phy * sci_phy )
{
enum scic_sds_port_states state ;
state = sci_port - > state_machine . current_state_id ;
switch ( state ) {
case SCIC_SDS_PORT_READY_SUBSTATE_WAITING :
/* Since this is the first phy going link up for the port we
* can just enable it and continue
*/
scic_sds_port_activate_phy ( sci_port , sci_phy , true ) ;
port_state_machine_change ( sci_port ,
SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL ) ;
return SCI_SUCCESS ;
case SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL :
scic_sds_port_general_link_up_handler ( sci_port , sci_phy , true ) ;
return SCI_SUCCESS ;
case SCI_BASE_PORT_STATE_RESETTING :
/* TODO We should make sure that the phy that has gone
* link up is the same one on which we sent the reset . It is
* possible that the phy on which we sent the reset is not the
* one that has gone link up and we want to make sure that
* phy being reset comes back . Consider the case where a
* reset is sent but before the hardware processes the reset it
* get a link up on the port because of a hot plug event .
* because of the reset request this phy will go link down
* almost immediately .
*/
/* In the resetting state we don't notify the user regarding
* link up and link down notifications .
*/
scic_sds_port_general_link_up_handler ( sci_port , sci_phy , false ) ;
return SCI_SUCCESS ;
default :
dev_warn ( sciport_to_dev ( sci_port ) ,
" %s: in wrong state: %d \n " , __func__ , state ) ;
return SCI_FAILURE_INVALID_STATE ;
}
}
enum sci_status scic_sds_port_link_down ( struct scic_sds_port * sci_port ,
struct scic_sds_phy * sci_phy )
{
enum scic_sds_port_states state ;
state = sci_port - > state_machine . current_state_id ;
switch ( state ) {
case SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL :
scic_sds_port_deactivate_phy ( sci_port , sci_phy , true ) ;
/* If there are no active phys left in the port, then
* transition the port to the WAITING state until such time
* as a phy goes link up
*/
if ( sci_port - > active_phy_mask = = 0 )
port_state_machine_change ( sci_port ,
SCIC_SDS_PORT_READY_SUBSTATE_WAITING ) ;
return SCI_SUCCESS ;
case SCI_BASE_PORT_STATE_RESETTING :
/* In the resetting state we don't notify the user regarding
* link up and link down notifications . */
scic_sds_port_deactivate_phy ( sci_port , sci_phy , false ) ;
2011-05-12 19:10:03 +00:00
return SCI_SUCCESS ;
default :
dev_warn ( sciport_to_dev ( sci_port ) ,
" %s: in wrong state: %d \n " , __func__ , state ) ;
return SCI_FAILURE_INVALID_STATE ;
}
}
2011-05-12 07:16:06 -07:00
enum sci_status scic_sds_port_start_io ( struct scic_sds_port * sci_port ,
struct scic_sds_remote_device * sci_dev ,
struct scic_sds_request * sci_req )
{
enum scic_sds_port_states state ;
state = sci_port - > state_machine . current_state_id ;
switch ( state ) {
case SCIC_SDS_PORT_READY_SUBSTATE_WAITING :
return SCI_FAILURE_INVALID_STATE ;
case SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL :
sci_port - > started_request_count + + ;
return SCI_SUCCESS ;
default :
dev_warn ( sciport_to_dev ( sci_port ) ,
" %s: in wrong state: %d \n " , __func__ , state ) ;
return SCI_FAILURE_INVALID_STATE ;
2011-05-10 02:28:46 -07:00
}
2011-05-12 07:16:06 -07:00
}
2011-05-10 02:28:46 -07:00
2011-05-12 07:16:06 -07:00
enum sci_status scic_sds_port_complete_io ( struct scic_sds_port * sci_port ,
struct scic_sds_remote_device * sci_dev ,
struct scic_sds_request * sci_req )
{
enum scic_sds_port_states state ;
state = sci_port - > state_machine . current_state_id ;
switch ( state ) {
case SCI_BASE_PORT_STATE_STOPPED :
dev_warn ( sciport_to_dev ( sci_port ) ,
" %s: in wrong state: %d \n " , __func__ , state ) ;
return SCI_FAILURE_INVALID_STATE ;
case SCI_BASE_PORT_STATE_STOPPING :
scic_sds_port_decrement_request_count ( sci_port ) ;
if ( sci_port - > started_request_count = = 0 )
port_state_machine_change ( sci_port ,
SCI_BASE_PORT_STATE_STOPPED ) ;
break ;
case SCI_BASE_PORT_STATE_READY :
case SCI_BASE_PORT_STATE_RESETTING :
case SCI_BASE_PORT_STATE_FAILED :
case SCIC_SDS_PORT_READY_SUBSTATE_WAITING :
case SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL :
scic_sds_port_decrement_request_count ( sci_port ) ;
break ;
case SCIC_SDS_PORT_READY_SUBSTATE_CONFIGURING :
scic_sds_port_decrement_request_count ( sci_port ) ;
if ( sci_port - > started_request_count = = 0 ) {
port_state_machine_change ( sci_port ,
SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL ) ;
}
break ;
}
return SCI_SUCCESS ;
}
2011-05-10 02:28:46 -07:00
/**
*
* @ sci_port : This is the port object which to suspend .
*
* This method will enable the SCU Port Task Scheduler for this port object but
* will leave the port task scheduler in a suspended state . none
*/
static void
scic_sds_port_enable_port_task_scheduler ( struct scic_sds_port * port )
{
u32 pts_control_value ;
pts_control_value = readl ( & port - > port_task_scheduler_registers - > control ) ;
pts_control_value | = SCU_PTSxCR_GEN_BIT ( ENABLE ) | SCU_PTSxCR_GEN_BIT ( SUSPEND ) ;
writel ( pts_control_value , & port - > port_task_scheduler_registers - > control ) ;
}
/**
*
* @ sci_port : This is the port object which to resume .
*
* This method will disable the SCU port task scheduler for this port object .
* none
*/
static void
scic_sds_port_disable_port_task_scheduler ( struct scic_sds_port * port )
{
u32 pts_control_value ;
pts_control_value = readl ( & port - > port_task_scheduler_registers - > control ) ;
pts_control_value & =
~ ( SCU_PTSxCR_GEN_BIT ( ENABLE ) | SCU_PTSxCR_GEN_BIT ( SUSPEND ) ) ;
writel ( pts_control_value , & port - > port_task_scheduler_registers - > control ) ;
}
static void scic_sds_port_post_dummy_remote_node ( struct scic_sds_port * sci_port )
{
struct scic_sds_controller * scic = sci_port - > owning_controller ;
u8 phys_index = sci_port - > physical_port_index ;
union scu_remote_node_context * rnc ;
u16 rni = sci_port - > reserved_rni ;
u32 command ;
rnc = & scic - > remote_node_context_table [ rni ] ;
rnc - > ssp . is_valid = true ;
command = SCU_CONTEXT_COMMAND_POST_RNC_32 |
phys_index < < SCU_CONTEXT_COMMAND_LOGICAL_PORT_SHIFT | rni ;
scic_sds_controller_post_request ( scic , command ) ;
/* ensure hardware has seen the post rnc command and give it
* ample time to act before sending the suspend
*/
readl ( & scic - > smu_registers - > interrupt_status ) ; /* flush */
udelay ( 10 ) ;
command = SCU_CONTEXT_COMMAND_POST_RNC_SUSPEND_TX_RX |
phys_index < < SCU_CONTEXT_COMMAND_LOGICAL_PORT_SHIFT | rni ;
scic_sds_controller_post_request ( scic , command ) ;
}
2011-05-12 07:42:17 -07:00
static void scic_sds_port_stopped_state_enter ( struct sci_base_state_machine * sm )
2011-05-10 02:28:46 -07:00
{
2011-05-12 07:42:17 -07:00
struct scic_sds_port * sci_port = container_of ( sm , typeof ( * sci_port ) , state_machine ) ;
2011-05-10 02:28:46 -07:00
2011-05-12 07:16:06 -07:00
if ( sci_port - > state_machine . previous_state_id = = SCI_BASE_PORT_STATE_STOPPING ) {
2011-05-10 02:28:46 -07:00
/*
* If we enter this state becasuse of a request to stop
* the port then we want to disable the hardwares port
* task scheduler . */
scic_sds_port_disable_port_task_scheduler ( sci_port ) ;
}
}
2011-05-12 07:42:17 -07:00
static void scic_sds_port_stopped_state_exit ( struct sci_base_state_machine * sm )
2011-05-10 02:28:46 -07:00
{
2011-05-12 07:42:17 -07:00
struct scic_sds_port * sci_port = container_of ( sm , typeof ( * sci_port ) , state_machine ) ;
2011-05-10 02:28:46 -07:00
/* Enable and suspend the port task scheduler */
scic_sds_port_enable_port_task_scheduler ( sci_port ) ;
}
2011-05-12 07:42:17 -07:00
static void scic_sds_port_ready_state_enter ( struct sci_base_state_machine * sm )
2011-05-10 02:28:46 -07:00
{
2011-05-12 07:42:17 -07:00
struct scic_sds_port * sci_port = container_of ( sm , typeof ( * sci_port ) , state_machine ) ;
2011-05-10 02:28:46 -07:00
struct scic_sds_controller * scic = sci_port - > owning_controller ;
struct isci_host * ihost = scic_to_ihost ( scic ) ;
struct isci_port * iport = sci_port_to_iport ( sci_port ) ;
u32 prev_state ;
prev_state = sci_port - > state_machine . previous_state_id ;
if ( prev_state = = SCI_BASE_PORT_STATE_RESETTING )
isci_port_hard_reset_complete ( iport , SCI_SUCCESS ) ;
else
isci_port_not_ready ( ihost , iport ) ;
/* Post and suspend the dummy remote node context for this port. */
scic_sds_port_post_dummy_remote_node ( sci_port ) ;
/* Start the ready substate machine */
2011-05-11 23:52:21 +00:00
port_state_machine_change ( sci_port ,
SCIC_SDS_PORT_READY_SUBSTATE_WAITING ) ;
2011-05-10 02:28:46 -07:00
}
2011-05-12 07:42:17 -07:00
static void scic_sds_port_resetting_state_exit ( struct sci_base_state_machine * sm )
2011-05-10 02:28:46 -07:00
{
2011-05-12 07:42:17 -07:00
struct scic_sds_port * sci_port = container_of ( sm , typeof ( * sci_port ) , state_machine ) ;
2011-05-10 02:28:46 -07:00
2011-05-19 11:59:10 +00:00
sci_del_timer ( & sci_port - > timer ) ;
2011-05-10 02:28:46 -07:00
}
2011-05-12 07:42:17 -07:00
static void scic_sds_port_stopping_state_exit ( struct sci_base_state_machine * sm )
2011-05-10 02:28:46 -07:00
{
2011-05-12 07:42:17 -07:00
struct scic_sds_port * sci_port = container_of ( sm , typeof ( * sci_port ) , state_machine ) ;
2011-05-10 02:28:46 -07:00
2011-05-19 11:59:10 +00:00
sci_del_timer ( & sci_port - > timer ) ;
2011-05-10 02:28:46 -07:00
scic_sds_port_destroy_dummy_resources ( sci_port ) ;
}
2011-05-12 07:42:17 -07:00
static void scic_sds_port_failed_state_enter ( struct sci_base_state_machine * sm )
2011-05-10 02:28:46 -07:00
{
2011-05-12 07:42:17 -07:00
struct scic_sds_port * sci_port = container_of ( sm , typeof ( * sci_port ) , state_machine ) ;
2011-05-10 02:28:46 -07:00
struct isci_port * iport = sci_port_to_iport ( sci_port ) ;
isci_port_hard_reset_complete ( iport , SCI_FAILURE_TIMEOUT ) ;
}
/* --------------------------------------------------------------------------- */
static const struct sci_base_state scic_sds_port_state_table [ ] = {
[ SCI_BASE_PORT_STATE_STOPPED ] = {
. enter_state = scic_sds_port_stopped_state_enter ,
. exit_state = scic_sds_port_stopped_state_exit
} ,
[ SCI_BASE_PORT_STATE_STOPPING ] = {
. exit_state = scic_sds_port_stopping_state_exit
} ,
[ SCI_BASE_PORT_STATE_READY ] = {
. enter_state = scic_sds_port_ready_state_enter ,
2011-05-11 23:52:21 +00:00
} ,
[ SCIC_SDS_PORT_READY_SUBSTATE_WAITING ] = {
. enter_state = scic_sds_port_ready_substate_waiting_enter ,
} ,
[ SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL ] = {
. enter_state = scic_sds_port_ready_substate_operational_enter ,
. exit_state = scic_sds_port_ready_substate_operational_exit
} ,
[ SCIC_SDS_PORT_READY_SUBSTATE_CONFIGURING ] = {
. enter_state = scic_sds_port_ready_substate_configuring_enter ,
. exit_state = scic_sds_port_ready_substate_configuring_exit
2011-05-10 02:28:46 -07:00
} ,
[ SCI_BASE_PORT_STATE_RESETTING ] = {
. exit_state = scic_sds_port_resetting_state_exit
} ,
[ SCI_BASE_PORT_STATE_FAILED ] = {
. enter_state = scic_sds_port_failed_state_enter ,
}
} ;
void scic_sds_port_construct ( struct scic_sds_port * sci_port , u8 index ,
struct scic_sds_controller * scic )
{
sci_base_state_machine_construct ( & sci_port - > state_machine ,
scic_sds_port_state_table ,
SCI_BASE_PORT_STATE_STOPPED ) ;
sci_base_state_machine_start ( & sci_port - > state_machine ) ;
sci_port - > logical_port_index = SCIC_SDS_DUMMY_PORT ;
sci_port - > physical_port_index = index ;
sci_port - > active_phy_mask = 0 ;
2011-05-11 23:52:21 +00:00
sci_port - > ready_exit = false ;
2011-05-10 02:28:46 -07:00
sci_port - > owning_controller = scic ;
sci_port - > started_request_count = 0 ;
sci_port - > assigned_device_count = 0 ;
sci_port - > reserved_rni = SCU_DUMMY_INDEX ;
sci_port - > reserved_tci = SCU_DUMMY_INDEX ;
2011-05-19 11:59:10 +00:00
sci_init_timer ( & sci_port - > timer , port_timeout ) ;
2011-05-10 02:28:46 -07:00
sci_port - > port_task_scheduler_registers = NULL ;
for ( index = 0 ; index < SCI_MAX_PHYS ; index + + )
sci_port - > phy_table [ index ] = NULL ;
}
void isci_port_init ( struct isci_port * iport , struct isci_host * ihost , int index )
{
INIT_LIST_HEAD ( & iport - > remote_dev_list ) ;
INIT_LIST_HEAD ( & iport - > domain_dev_list ) ;
spin_lock_init ( & iport - > state_lock ) ;
init_completion ( & iport - > start_complete ) ;
iport - > isci_host = ihost ;
isci_port_change_state ( iport , isci_freed ) ;
}
/**
* isci_port_get_state ( ) - This function gets the status of the port object .
* @ isci_port : This parameter points to the isci_port object
*
* status of the object as a isci_status enum .
*/
enum isci_status isci_port_get_state (
struct isci_port * isci_port )
{
return isci_port - > status ;
}
static void isci_port_bc_change_received ( struct isci_host * ihost ,
struct scic_sds_port * sci_port ,
struct scic_sds_phy * sci_phy )
{
struct isci_phy * iphy = sci_phy_to_iphy ( sci_phy ) ;
dev_dbg ( & ihost - > pdev - > dev , " %s: iphy = %p, sas_phy = %p \n " ,
__func__ , iphy , & iphy - > sas_phy ) ;
ihost - > sas_ha . notify_port_event ( & iphy - > sas_phy , PORTE_BROADCAST_RCVD ) ;
scic_port_enable_broadcast_change_notification ( sci_port ) ;
}
void scic_sds_port_broadcast_change_received (
struct scic_sds_port * sci_port ,
struct scic_sds_phy * sci_phy )
{
struct scic_sds_controller * scic = sci_port - > owning_controller ;
struct isci_host * ihost = scic_to_ihost ( scic ) ;
/* notify the user. */
isci_port_bc_change_received ( ihost , sci_port , sci_phy ) ;
}
int isci_port_perform_hard_reset ( struct isci_host * ihost , struct isci_port * iport ,
struct isci_phy * iphy )
{
unsigned long flags ;
enum sci_status status ;
int ret = TMF_RESP_FUNC_COMPLETE ;
dev_dbg ( & ihost - > pdev - > dev , " %s: iport = %p \n " ,
__func__ , iport ) ;
init_completion ( & iport - > hard_reset_complete ) ;
spin_lock_irqsave ( & ihost - > scic_lock , flags ) ;
# define ISCI_PORT_RESET_TIMEOUT SCIC_SDS_SIGNATURE_FIS_TIMEOUT
status = scic_port_hard_reset ( & iport - > sci , ISCI_PORT_RESET_TIMEOUT ) ;
spin_unlock_irqrestore ( & ihost - > scic_lock , flags ) ;
if ( status = = SCI_SUCCESS ) {
wait_for_completion ( & iport - > hard_reset_complete ) ;
dev_dbg ( & ihost - > pdev - > dev ,
" %s: iport = %p; hard reset completion \n " ,
__func__ , iport ) ;
if ( iport - > hard_reset_status ! = SCI_SUCCESS )
ret = TMF_RESP_FUNC_FAILED ;
} else {
ret = TMF_RESP_FUNC_FAILED ;
dev_err ( & ihost - > pdev - > dev ,
" %s: iport = %p; scic_port_hard_reset call "
" failed 0x%x \n " ,
__func__ , iport , status ) ;
}
/* If the hard reset for the port has failed, consider this
* the same as link failures on all phys in the port .
*/
if ( ret ! = TMF_RESP_FUNC_COMPLETE ) {
dev_err ( & ihost - > pdev - > dev ,
" %s: iport = %p; hard reset failed "
" (0x%x) - sending link down to libsas for phy %p \n " ,
__func__ , iport , iport - > hard_reset_status , iphy ) ;
isci_port_link_down ( ihost , iphy , iport ) ;
}
return ret ;
}
/**
* isci_port_deformed ( ) - This function is called by libsas when a port becomes
* inactive .
* @ phy : This parameter specifies the libsas phy with the inactive port .
*
*/
void isci_port_deformed ( struct asd_sas_phy * phy )
{
pr_debug ( " %s: sas_phy = %p \n " , __func__ , phy ) ;
}
/**
* isci_port_formed ( ) - This function is called by libsas when a port becomes
* active .
* @ phy : This parameter specifies the libsas phy with the active port .
*
*/
void isci_port_formed ( struct asd_sas_phy * phy )
{
pr_debug ( " %s: sas_phy = %p, sas_port = %p \n " , __func__ , phy , phy - > port ) ;
2011-03-26 16:11:51 -07:00
}