2011-07-03 09:56:22 +04: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"
2011-05-09 02:49:15 +04:00
# include "host.h"
2011-07-03 09:56:22 +04:00
# include "phy.h"
2011-05-10 13:28:45 +04:00
# include "scu_event_codes.h"
# include "timers.h"
2011-05-10 13:28:46 +04:00
# include "probe_roms.h"
2011-07-03 09:56:22 +04:00
2011-05-10 13:28:45 +04:00
/* Maximum arbitration wait time in micro-seconds */
# define SCIC_SDS_PHY_MAX_ARBITRATION_WAIT_TIME (700)
enum sas_linkrate sci_phy_linkrate ( struct scic_sds_phy * sci_phy )
{
return sci_phy - > max_negotiated_speed ;
}
/*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * SCIC SDS PHY Internal Methods
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/**
* This method will initialize the phy transport layer registers
* @ sci_phy :
* @ transport_layer_registers
*
* enum sci_status
*/
static enum sci_status scic_sds_phy_transport_layer_initialization (
struct scic_sds_phy * sci_phy ,
struct scu_transport_layer_registers __iomem * transport_layer_registers )
{
u32 tl_control ;
sci_phy - > transport_layer_registers = transport_layer_registers ;
writel ( SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX ,
& sci_phy - > transport_layer_registers - > stp_rni ) ;
/*
* Hardware team recommends that we enable the STP prefetch for all
* transports
*/
tl_control = readl ( & sci_phy - > transport_layer_registers - > control ) ;
tl_control | = SCU_TLCR_GEN_BIT ( STP_WRITE_DATA_PREFETCH ) ;
writel ( tl_control , & sci_phy - > transport_layer_registers - > control ) ;
return SCI_SUCCESS ;
}
/**
* This method will initialize the phy link layer registers
* @ sci_phy :
* @ link_layer_registers :
*
* enum sci_status
*/
static enum sci_status
scic_sds_phy_link_layer_initialization ( struct scic_sds_phy * sci_phy ,
struct scu_link_layer_registers __iomem * link_layer_registers )
{
struct scic_sds_controller * scic =
sci_phy - > owning_port - > owning_controller ;
int phy_idx = sci_phy - > phy_index ;
struct sci_phy_user_params * phy_user =
& scic - > user_parameters . sds1 . phys [ phy_idx ] ;
struct sci_phy_oem_params * phy_oem =
& scic - > oem_parameters . sds1 . phys [ phy_idx ] ;
u32 phy_configuration ;
struct scic_phy_cap phy_cap ;
u32 parity_check = 0 ;
u32 parity_count = 0 ;
u32 llctl , link_rate ;
u32 clksm_value = 0 ;
sci_phy - > link_layer_registers = link_layer_registers ;
/* Set our IDENTIFY frame data */
# define SCI_END_DEVICE 0x01
writel ( SCU_SAS_TIID_GEN_BIT ( SMP_INITIATOR ) |
SCU_SAS_TIID_GEN_BIT ( SSP_INITIATOR ) |
SCU_SAS_TIID_GEN_BIT ( STP_INITIATOR ) |
SCU_SAS_TIID_GEN_BIT ( DA_SATA_HOST ) |
SCU_SAS_TIID_GEN_VAL ( DEVICE_TYPE , SCI_END_DEVICE ) ,
& sci_phy - > link_layer_registers - > transmit_identification ) ;
/* Write the device SAS Address */
writel ( 0xFEDCBA98 ,
& sci_phy - > link_layer_registers - > sas_device_name_high ) ;
writel ( phy_idx , & sci_phy - > link_layer_registers - > sas_device_name_low ) ;
/* Write the source SAS Address */
writel ( phy_oem - > sas_address . high ,
& sci_phy - > link_layer_registers - > source_sas_address_high ) ;
writel ( phy_oem - > sas_address . low ,
& sci_phy - > link_layer_registers - > source_sas_address_low ) ;
/* Clear and Set the PHY Identifier */
writel ( 0 , & sci_phy - > link_layer_registers - > identify_frame_phy_id ) ;
writel ( SCU_SAS_TIPID_GEN_VALUE ( ID , phy_idx ) ,
& sci_phy - > link_layer_registers - > identify_frame_phy_id ) ;
/* Change the initial state of the phy configuration register */
phy_configuration =
readl ( & sci_phy - > link_layer_registers - > phy_configuration ) ;
/* Hold OOB state machine in reset */
phy_configuration | = SCU_SAS_PCFG_GEN_BIT ( OOB_RESET ) ;
writel ( phy_configuration ,
& sci_phy - > link_layer_registers - > phy_configuration ) ;
/* Configure the SNW capabilities */
phy_cap . all = 0 ;
phy_cap . start = 1 ;
phy_cap . gen3_no_ssc = 1 ;
phy_cap . gen2_no_ssc = 1 ;
phy_cap . gen1_no_ssc = 1 ;
if ( scic - > oem_parameters . sds1 . controller . do_enable_ssc = = true ) {
phy_cap . gen3_ssc = 1 ;
phy_cap . gen2_ssc = 1 ;
phy_cap . gen1_ssc = 1 ;
}
/*
* The SAS specification indicates that the phy_capabilities that
* are transmitted shall have an even parity . Calculate the parity . */
parity_check = phy_cap . all ;
while ( parity_check ! = 0 ) {
if ( parity_check & 0x1 )
parity_count + + ;
parity_check > > = 1 ;
}
/*
* If parity indicates there are an odd number of bits set , then
* set the parity bit to 1 in the phy capabilities . */
if ( ( parity_count % 2 ) ! = 0 )
phy_cap . parity = 1 ;
writel ( phy_cap . all , & sci_phy - > link_layer_registers - > phy_capabilities ) ;
/* Set the enable spinup period but disable the ability to send
* notify enable spinup
*/
writel ( SCU_ENSPINUP_GEN_VAL ( COUNT ,
phy_user - > notify_enable_spin_up_insertion_frequency ) ,
& sci_phy - > link_layer_registers - > notify_enable_spinup_control ) ;
/* Write the ALIGN Insertion Ferequency for connected phy and
* inpendent of connected state
*/
clksm_value = SCU_ALIGN_INSERTION_FREQUENCY_GEN_VAL ( CONNECTED ,
phy_user - > in_connection_align_insertion_frequency ) ;
clksm_value | = SCU_ALIGN_INSERTION_FREQUENCY_GEN_VAL ( GENERAL ,
phy_user - > align_insertion_frequency ) ;
writel ( clksm_value , & sci_phy - > link_layer_registers - > clock_skew_management ) ;
/* @todo Provide a way to write this register correctly */
writel ( 0x02108421 ,
& sci_phy - > link_layer_registers - > afe_lookup_table_control ) ;
llctl = SCU_SAS_LLCTL_GEN_VAL ( NO_OUTBOUND_TASK_TIMEOUT ,
( u8 ) scic - > user_parameters . sds1 . no_outbound_task_timeout ) ;
switch ( phy_user - > max_speed_generation ) {
case SCIC_SDS_PARM_GEN3_SPEED :
link_rate = SCU_SAS_LINK_LAYER_CONTROL_MAX_LINK_RATE_GEN3 ;
break ;
case SCIC_SDS_PARM_GEN2_SPEED :
link_rate = SCU_SAS_LINK_LAYER_CONTROL_MAX_LINK_RATE_GEN2 ;
break ;
default :
link_rate = SCU_SAS_LINK_LAYER_CONTROL_MAX_LINK_RATE_GEN1 ;
break ;
}
llctl | = SCU_SAS_LLCTL_GEN_VAL ( MAX_LINK_RATE , link_rate ) ;
writel ( llctl , & sci_phy - > link_layer_registers - > link_layer_control ) ;
if ( is_a0 ( ) | | is_a2 ( ) ) {
/* Program the max ARB time for the PHY to 700us so we inter-operate with
* the PMC expander which shuts down PHYs if the expander PHY generates too
* many breaks . This time value will guarantee that the initiator PHY will
* generate the break .
*/
writel ( SCIC_SDS_PHY_MAX_ARBITRATION_WAIT_TIME ,
& sci_phy - > link_layer_registers - > maximum_arbitration_wait_timer_timeout ) ;
}
/*
* Set the link layer hang detection to 500 ms ( 0x1F4 ) from its default
* value of 128 ms . Max value is 511 ms .
*/
writel ( 0x1F4 , & sci_phy - > link_layer_registers - > link_layer_hang_detection_timeout ) ;
/* We can exit the initial state to the stopped state */
sci_base_state_machine_change_state ( & sci_phy - > state_machine ,
SCI_BASE_PHY_STATE_STOPPED ) ;
return SCI_SUCCESS ;
}
/**
* This function will handle the sata SIGNATURE FIS timeout condition . It will
* restart the starting substate machine since we dont know what has actually
* happening .
*/
static void scic_sds_phy_sata_timeout ( void * phy )
{
struct scic_sds_phy * sci_phy = phy ;
dev_dbg ( sciphy_to_dev ( sci_phy ) ,
" %s: SCIC SDS Phy 0x%p did not receive signature fis before "
" timeout. \n " ,
__func__ ,
sci_phy ) ;
sci_base_state_machine_change_state ( & sci_phy - > state_machine ,
SCI_BASE_PHY_STATE_STARTING ) ;
}
/**
* This method returns the port currently containing this phy . If the phy is
* currently contained by the dummy port , then the phy is considered to not
* be part of a port .
* @ sci_phy : This parameter specifies the phy for which to retrieve the
* containing port .
*
* This method returns a handle to a port that contains the supplied phy .
* NULL This value is returned if the phy is not part of a real
* port ( i . e . it ' s contained in the dummy port ) . ! NULL All other
* values indicate a handle / pointer to the port containing the phy .
*/
struct scic_sds_port * scic_sds_phy_get_port (
struct scic_sds_phy * sci_phy )
{
if ( scic_sds_port_get_index ( sci_phy - > owning_port ) = = SCIC_SDS_DUMMY_PORT )
return NULL ;
return sci_phy - > owning_port ;
}
/**
* This method will assign a port to the phy object .
* @ out ] : sci_phy This parameter specifies the phy for which to assign a port
* object .
*
*
*/
void scic_sds_phy_set_port (
struct scic_sds_phy * sci_phy ,
struct scic_sds_port * sci_port )
{
sci_phy - > owning_port = sci_port ;
if ( sci_phy - > bcn_received_while_port_unassigned ) {
sci_phy - > bcn_received_while_port_unassigned = false ;
scic_sds_port_broadcast_change_received ( sci_phy - > owning_port , sci_phy ) ;
}
}
/**
* This method will initialize the constructed phy
* @ sci_phy :
* @ link_layer_registers :
*
* enum sci_status
*/
enum sci_status scic_sds_phy_initialize (
struct scic_sds_phy * sci_phy ,
struct scu_transport_layer_registers __iomem * transport_layer_registers ,
struct scu_link_layer_registers __iomem * link_layer_registers )
{
struct scic_sds_controller * scic = scic_sds_phy_get_controller ( sci_phy ) ;
struct isci_host * ihost = scic_to_ihost ( scic ) ;
/* Create the SIGNATURE FIS Timeout timer for this phy */
sci_phy - > sata_timeout_timer =
isci_timer_create (
ihost ,
sci_phy ,
scic_sds_phy_sata_timeout ) ;
/* Perfrom the initialization of the TL hardware */
scic_sds_phy_transport_layer_initialization (
sci_phy ,
transport_layer_registers ) ;
/* Perofrm the initialization of the PE hardware */
scic_sds_phy_link_layer_initialization ( sci_phy , link_layer_registers ) ;
/*
* There is nothing that needs to be done in this state just
* transition to the stopped state . */
sci_base_state_machine_change_state ( & sci_phy - > state_machine ,
SCI_BASE_PHY_STATE_STOPPED ) ;
return SCI_SUCCESS ;
}
/**
* This method assigns the direct attached device ID for this phy .
*
* @ sci_phy The phy for which the direct attached device id is to
* be assigned .
* @ device_id The direct attached device ID to assign to the phy .
* This will either be the RNi for the device or an invalid RNi if there
* is no current device assigned to the phy .
*/
void scic_sds_phy_setup_transport (
struct scic_sds_phy * sci_phy ,
u32 device_id )
{
u32 tl_control ;
writel ( device_id , & sci_phy - > transport_layer_registers - > stp_rni ) ;
/*
* The read should guarantee that the first write gets posted
* before the next write
*/
tl_control = readl ( & sci_phy - > transport_layer_registers - > control ) ;
tl_control | = SCU_TLCR_GEN_BIT ( CLEAR_TCI_NCQ_MAPPING_TABLE ) ;
writel ( tl_control , & sci_phy - > transport_layer_registers - > control ) ;
}
/**
*
* @ sci_phy : The phy object to be suspended .
*
* This function will perform the register reads / writes to suspend the SCU
* hardware protocol engine . none
*/
static void scic_sds_phy_suspend (
struct scic_sds_phy * sci_phy )
{
u32 scu_sas_pcfg_value ;
scu_sas_pcfg_value =
readl ( & sci_phy - > link_layer_registers - > phy_configuration ) ;
scu_sas_pcfg_value | = SCU_SAS_PCFG_GEN_BIT ( SUSPEND_PROTOCOL_ENGINE ) ;
writel ( scu_sas_pcfg_value ,
& sci_phy - > link_layer_registers - > phy_configuration ) ;
scic_sds_phy_setup_transport (
sci_phy ,
SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX ) ;
}
void scic_sds_phy_resume ( struct scic_sds_phy * sci_phy )
{
u32 scu_sas_pcfg_value ;
scu_sas_pcfg_value =
readl ( & sci_phy - > link_layer_registers - > phy_configuration ) ;
scu_sas_pcfg_value & = ~ SCU_SAS_PCFG_GEN_BIT ( SUSPEND_PROTOCOL_ENGINE ) ;
writel ( scu_sas_pcfg_value ,
& sci_phy - > link_layer_registers - > phy_configuration ) ;
}
void scic_sds_phy_get_sas_address ( struct scic_sds_phy * sci_phy ,
struct sci_sas_address * sas_address )
{
sas_address - > high = readl ( & sci_phy - > link_layer_registers - > source_sas_address_high ) ;
sas_address - > low = readl ( & sci_phy - > link_layer_registers - > source_sas_address_low ) ;
}
void scic_sds_phy_get_attached_sas_address ( struct scic_sds_phy * sci_phy ,
struct sci_sas_address * sas_address )
{
struct sas_identify_frame * iaf ;
struct isci_phy * iphy = sci_phy_to_iphy ( sci_phy ) ;
iaf = & iphy - > frame_rcvd . iaf ;
memcpy ( sas_address , iaf - > sas_addr , SAS_ADDR_SIZE ) ;
}
void scic_sds_phy_get_protocols ( struct scic_sds_phy * sci_phy ,
struct scic_phy_proto * protocols )
{
protocols - > all =
( u16 ) ( readl ( & sci_phy - >
link_layer_registers - > transmit_identification ) &
0x0000FFFF ) ;
}
enum sci_status scic_sds_phy_start ( struct scic_sds_phy * sci_phy )
{
2011-05-12 14:44:24 +04:00
struct scic_sds_controller * scic = sci_phy - > owning_port - > owning_controller ;
enum scic_sds_phy_states state = sci_phy - > state_machine . current_state_id ;
struct isci_host * ihost = scic_to_ihost ( scic ) ;
if ( state ! = SCI_BASE_PHY_STATE_STOPPED ) {
dev_dbg ( sciphy_to_dev ( sci_phy ) ,
" %s: in wrong state: %d \n " , __func__ , state ) ;
return SCI_FAILURE_INVALID_STATE ;
}
/* Create the SIGNATURE FIS Timeout timer for this phy */
sci_phy - > sata_timeout_timer = isci_timer_create ( ihost , sci_phy ,
scic_sds_phy_sata_timeout ) ;
if ( sci_phy - > sata_timeout_timer )
sci_base_state_machine_change_state ( & sci_phy - > state_machine ,
SCI_BASE_PHY_STATE_STARTING ) ;
return SCI_SUCCESS ;
2011-05-10 13:28:45 +04:00
}
enum sci_status scic_sds_phy_stop ( struct scic_sds_phy * sci_phy )
{
2011-05-12 15:01:03 +04:00
enum scic_sds_phy_states state = sci_phy - > state_machine . current_state_id ;
switch ( state ) {
case SCIC_SDS_PHY_STARTING_SUBSTATE_INITIAL :
case SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_OSSP_EN :
case SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SAS_SPEED_EN :
case SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SAS_POWER :
case SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SATA_POWER :
case SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SATA_PHY_EN :
case SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SATA_SPEED_EN :
case SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SIG_FIS_UF :
case SCIC_SDS_PHY_STARTING_SUBSTATE_FINAL :
case SCI_BASE_PHY_STATE_READY :
break ;
default :
dev_dbg ( sciphy_to_dev ( sci_phy ) ,
" %s: in wrong state: %d \n " , __func__ , state ) ;
return SCI_FAILURE_INVALID_STATE ;
}
sci_base_state_machine_change_state ( & sci_phy - > state_machine ,
SCI_BASE_PHY_STATE_STOPPED ) ;
return SCI_SUCCESS ;
2011-05-10 13:28:45 +04:00
}
2011-05-12 15:02:07 +04:00
enum sci_status scic_sds_phy_reset ( struct scic_sds_phy * sci_phy )
2011-05-10 13:28:45 +04:00
{
2011-05-12 15:02:07 +04:00
enum scic_sds_phy_states state = sci_phy - > state_machine . current_state_id ;
if ( state ! = SCI_BASE_PHY_STATE_READY ) {
dev_dbg ( sciphy_to_dev ( sci_phy ) ,
" %s: in wrong state: %d \n " , __func__ , state ) ;
return SCI_FAILURE_INVALID_STATE ;
}
sci_base_state_machine_change_state ( & sci_phy - > state_machine ,
SCI_BASE_PHY_STATE_RESETTING ) ;
return SCI_SUCCESS ;
2011-05-10 13:28:45 +04:00
}
2011-05-12 15:51:41 +04:00
enum sci_status scic_sds_phy_consume_power_handler ( struct scic_sds_phy * sci_phy )
2011-05-10 13:28:45 +04:00
{
2011-05-12 15:51:41 +04:00
enum scic_sds_phy_states state = sci_phy - > state_machine . current_state_id ;
switch ( state ) {
case SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SAS_POWER : {
u32 enable_spinup ;
enable_spinup = readl ( & sci_phy - > link_layer_registers - > notify_enable_spinup_control ) ;
enable_spinup | = SCU_ENSPINUP_GEN_BIT ( ENABLE ) ;
writel ( enable_spinup , & sci_phy - > link_layer_registers - > notify_enable_spinup_control ) ;
/* Change state to the final state this substate machine has run to completion */
sci_base_state_machine_change_state ( & sci_phy - > state_machine ,
SCIC_SDS_PHY_STARTING_SUBSTATE_FINAL ) ;
return SCI_SUCCESS ;
}
case SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SATA_POWER : {
u32 scu_sas_pcfg_value ;
/* Release the spinup hold state and reset the OOB state machine */
scu_sas_pcfg_value =
readl ( & sci_phy - > link_layer_registers - > phy_configuration ) ;
scu_sas_pcfg_value & =
~ ( SCU_SAS_PCFG_GEN_BIT ( SATA_SPINUP_HOLD ) | SCU_SAS_PCFG_GEN_BIT ( OOB_ENABLE ) ) ;
scu_sas_pcfg_value | = SCU_SAS_PCFG_GEN_BIT ( OOB_RESET ) ;
writel ( scu_sas_pcfg_value ,
& sci_phy - > link_layer_registers - > phy_configuration ) ;
/* Now restart the OOB operation */
scu_sas_pcfg_value & = ~ SCU_SAS_PCFG_GEN_BIT ( OOB_RESET ) ;
scu_sas_pcfg_value | = SCU_SAS_PCFG_GEN_BIT ( OOB_ENABLE ) ;
writel ( scu_sas_pcfg_value ,
& sci_phy - > link_layer_registers - > phy_configuration ) ;
/* Change state to the final state this substate machine has run to completion */
sci_base_state_machine_change_state ( & sci_phy - > state_machine ,
SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SATA_PHY_EN ) ;
return SCI_SUCCESS ;
}
default :
dev_dbg ( sciphy_to_dev ( sci_phy ) ,
" %s: in wrong state: %d \n " , __func__ , state ) ;
return SCI_FAILURE_INVALID_STATE ;
}
2011-05-10 13:28:45 +04:00
}
/*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * SCIC SDS PHY HELPER FUNCTIONS
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/**
*
* @ sci_phy : The phy object that received SAS PHY DETECTED .
*
* This method continues the link training for the phy as if it were a SAS PHY
* instead of a SATA PHY . This is done because the completion queue had a SAS
* PHY DETECTED event when the state machine was expecting a SATA PHY event .
* none
*/
static void scic_sds_phy_start_sas_link_training (
struct scic_sds_phy * sci_phy )
{
u32 phy_control ;
phy_control =
readl ( & sci_phy - > link_layer_registers - > phy_configuration ) ;
phy_control | = SCU_SAS_PCFG_GEN_BIT ( SATA_SPINUP_HOLD ) ;
writel ( phy_control ,
& sci_phy - > link_layer_registers - > phy_configuration ) ;
sci_base_state_machine_change_state (
2011-05-11 03:54:23 +04:00
& sci_phy - > state_machine ,
2011-05-10 13:28:45 +04:00
SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SAS_SPEED_EN
) ;
sci_phy - > protocol = SCIC_SDS_PHY_PROTOCOL_SAS ;
}
/**
*
* @ sci_phy : The phy object that received a SATA SPINUP HOLD event
*
* This method continues the link training for the phy as if it were a SATA PHY
* instead of a SAS PHY . This is done because the completion queue had a SATA
* SPINUP HOLD event when the state machine was expecting a SAS PHY event . none
*/
static void scic_sds_phy_start_sata_link_training (
struct scic_sds_phy * sci_phy )
{
sci_base_state_machine_change_state (
2011-05-11 03:54:23 +04:00
& sci_phy - > state_machine ,
2011-05-10 13:28:45 +04:00
SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SATA_POWER
) ;
sci_phy - > protocol = SCIC_SDS_PHY_PROTOCOL_SATA ;
}
/**
* scic_sds_phy_complete_link_training - perform processing common to
* all protocols upon completion of link training .
* @ sci_phy : This parameter specifies the phy object for which link training
* has completed .
* @ max_link_rate : This parameter specifies the maximum link rate to be
* associated with this phy .
* @ next_state : This parameter specifies the next state for the phy ' s starting
* sub - state machine .
*
*/
static void scic_sds_phy_complete_link_training (
struct scic_sds_phy * sci_phy ,
enum sas_linkrate max_link_rate ,
u32 next_state )
{
sci_phy - > max_negotiated_speed = max_link_rate ;
2011-05-11 03:54:23 +04:00
sci_base_state_machine_change_state ( & sci_phy - > state_machine ,
2011-05-10 13:28:45 +04:00
next_state ) ;
}
2011-05-12 15:27:29 +04:00
enum sci_status scic_sds_phy_event_handler ( struct scic_sds_phy * sci_phy ,
u32 event_code )
2011-05-11 03:54:23 +04:00
{
2011-05-12 15:27:29 +04:00
enum scic_sds_phy_states state = sci_phy - > state_machine . current_state_id ;
2011-05-11 03:54:23 +04:00
2011-05-12 15:27:29 +04:00
switch ( state ) {
case SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_OSSP_EN :
switch ( scu_get_event_code ( event_code ) ) {
case SCU_EVENT_SAS_PHY_DETECTED :
scic_sds_phy_start_sas_link_training ( sci_phy ) ;
sci_phy - > is_in_link_training = true ;
break ;
case SCU_EVENT_SATA_SPINUP_HOLD :
scic_sds_phy_start_sata_link_training ( sci_phy ) ;
sci_phy - > is_in_link_training = true ;
break ;
default :
dev_dbg ( sciphy_to_dev ( sci_phy ) ,
" %s: PHY starting substate machine received "
" unexpected event_code %x \n " ,
__func__ ,
event_code ) ;
return SCI_FAILURE ;
}
return SCI_SUCCESS ;
case SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SAS_SPEED_EN :
switch ( scu_get_event_code ( event_code ) ) {
case SCU_EVENT_SAS_PHY_DETECTED :
/*
* Why is this being reported again by the controller ?
* We would re - enter this state so just stay here */
break ;
case SCU_EVENT_SAS_15 :
case SCU_EVENT_SAS_15_SSC :
scic_sds_phy_complete_link_training (
sci_phy ,
SAS_LINK_RATE_1_5_GBPS ,
SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_IAF_UF ) ;
break ;
case SCU_EVENT_SAS_30 :
case SCU_EVENT_SAS_30_SSC :
scic_sds_phy_complete_link_training (
sci_phy ,
SAS_LINK_RATE_3_0_GBPS ,
SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_IAF_UF ) ;
break ;
case SCU_EVENT_SAS_60 :
case SCU_EVENT_SAS_60_SSC :
scic_sds_phy_complete_link_training (
sci_phy ,
SAS_LINK_RATE_6_0_GBPS ,
SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_IAF_UF ) ;
break ;
case SCU_EVENT_SATA_SPINUP_HOLD :
/*
* We were doing SAS PHY link training and received a SATA PHY event
* continue OOB / SN as if this were a SATA PHY */
scic_sds_phy_start_sata_link_training ( sci_phy ) ;
break ;
case SCU_EVENT_LINK_FAILURE :
/* Link failure change state back to the starting state */
sci_base_state_machine_change_state ( & sci_phy - > state_machine ,
SCI_BASE_PHY_STATE_STARTING ) ;
break ;
default :
dev_warn ( sciphy_to_dev ( sci_phy ) ,
" %s: PHY starting substate machine received "
" unexpected event_code %x \n " ,
__func__ , event_code ) ;
return SCI_FAILURE ;
break ;
}
return SCI_SUCCESS ;
case SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_IAF_UF :
switch ( scu_get_event_code ( event_code ) ) {
case SCU_EVENT_SAS_PHY_DETECTED :
/* Backup the state machine */
scic_sds_phy_start_sas_link_training ( sci_phy ) ;
break ;
case SCU_EVENT_SATA_SPINUP_HOLD :
/* We were doing SAS PHY link training and received a
* SATA PHY event continue OOB / SN as if this were a
* SATA PHY
*/
scic_sds_phy_start_sata_link_training ( sci_phy ) ;
break ;
case SCU_EVENT_RECEIVED_IDENTIFY_TIMEOUT :
case SCU_EVENT_LINK_FAILURE :
case SCU_EVENT_HARD_RESET_RECEIVED :
/* Start the oob/sn state machine over again */
sci_base_state_machine_change_state ( & sci_phy - > state_machine ,
SCI_BASE_PHY_STATE_STARTING ) ;
break ;
default :
dev_warn ( sciphy_to_dev ( sci_phy ) ,
" %s: PHY starting substate machine received "
" unexpected event_code %x \n " ,
__func__ , event_code ) ;
return SCI_FAILURE ;
}
return SCI_SUCCESS ;
case SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SAS_POWER :
switch ( scu_get_event_code ( event_code ) ) {
case SCU_EVENT_LINK_FAILURE :
/* Link failure change state back to the starting state */
sci_base_state_machine_change_state ( & sci_phy - > state_machine ,
SCI_BASE_PHY_STATE_STARTING ) ;
break ;
default :
dev_warn ( sciphy_to_dev ( sci_phy ) ,
" %s: PHY starting substate machine received unexpected "
" event_code %x \n " ,
__func__ ,
event_code ) ;
return SCI_FAILURE ;
}
return SCI_SUCCESS ;
case SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SATA_POWER :
switch ( scu_get_event_code ( event_code ) ) {
case SCU_EVENT_LINK_FAILURE :
/* Link failure change state back to the starting state */
sci_base_state_machine_change_state ( & sci_phy - > state_machine ,
SCI_BASE_PHY_STATE_STARTING ) ;
break ;
case SCU_EVENT_SATA_SPINUP_HOLD :
/* These events are received every 10ms and are
* expected while in this state
*/
break ;
case SCU_EVENT_SAS_PHY_DETECTED :
/* There has been a change in the phy type before OOB/SN for the
* SATA finished start down the SAS link traning path .
*/
scic_sds_phy_start_sas_link_training ( sci_phy ) ;
break ;
default :
dev_warn ( sciphy_to_dev ( sci_phy ) ,
" %s: PHY starting substate machine received "
" unexpected event_code %x \n " ,
__func__ , event_code ) ;
2011-05-11 03:54:23 +04:00
2011-05-12 15:27:29 +04:00
return SCI_FAILURE ;
}
return SCI_SUCCESS ;
case SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SATA_PHY_EN :
switch ( scu_get_event_code ( event_code ) ) {
case SCU_EVENT_LINK_FAILURE :
/* Link failure change state back to the starting state */
sci_base_state_machine_change_state ( & sci_phy - > state_machine ,
SCI_BASE_PHY_STATE_STARTING ) ;
break ;
case SCU_EVENT_SATA_SPINUP_HOLD :
/* These events might be received since we dont know how many may be in
* the completion queue while waiting for power
*/
break ;
case SCU_EVENT_SATA_PHY_DETECTED :
sci_phy - > protocol = SCIC_SDS_PHY_PROTOCOL_SATA ;
/* We have received the SATA PHY notification change state */
sci_base_state_machine_change_state ( & sci_phy - > state_machine ,
SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SATA_SPEED_EN ) ;
break ;
case SCU_EVENT_SAS_PHY_DETECTED :
/* There has been a change in the phy type before OOB/SN for the
* SATA finished start down the SAS link traning path .
*/
scic_sds_phy_start_sas_link_training ( sci_phy ) ;
break ;
default :
dev_warn ( sciphy_to_dev ( sci_phy ) ,
" %s: PHY starting substate machine received "
" unexpected event_code %x \n " ,
__func__ ,
event_code ) ;
2011-05-11 03:54:23 +04:00
2011-05-12 15:27:29 +04:00
return SCI_FAILURE ; ;
}
return SCI_SUCCESS ;
case SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SATA_SPEED_EN :
switch ( scu_get_event_code ( event_code ) ) {
case SCU_EVENT_SATA_PHY_DETECTED :
/*
* The hardware reports multiple SATA PHY detected events
* ignore the extras */
break ;
case SCU_EVENT_SATA_15 :
case SCU_EVENT_SATA_15_SSC :
scic_sds_phy_complete_link_training (
sci_phy ,
SAS_LINK_RATE_1_5_GBPS ,
SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SIG_FIS_UF ) ;
break ;
case SCU_EVENT_SATA_30 :
case SCU_EVENT_SATA_30_SSC :
scic_sds_phy_complete_link_training (
sci_phy ,
SAS_LINK_RATE_3_0_GBPS ,
SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SIG_FIS_UF ) ;
break ;
case SCU_EVENT_SATA_60 :
case SCU_EVENT_SATA_60_SSC :
scic_sds_phy_complete_link_training (
sci_phy ,
SAS_LINK_RATE_6_0_GBPS ,
SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SIG_FIS_UF ) ;
break ;
case SCU_EVENT_LINK_FAILURE :
/* Link failure change state back to the starting state */
sci_base_state_machine_change_state ( & sci_phy - > state_machine ,
SCI_BASE_PHY_STATE_STARTING ) ;
break ;
case SCU_EVENT_SAS_PHY_DETECTED :
/*
* There has been a change in the phy type before OOB / SN for the
* SATA finished start down the SAS link traning path . */
scic_sds_phy_start_sas_link_training ( sci_phy ) ;
break ;
default :
dev_warn ( sciphy_to_dev ( sci_phy ) ,
" %s: PHY starting substate machine received "
" unexpected event_code %x \n " ,
__func__ , event_code ) ;
2011-05-11 03:54:23 +04:00
2011-05-12 15:27:29 +04:00
return SCI_FAILURE ;
}
return SCI_SUCCESS ;
case SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SIG_FIS_UF :
switch ( scu_get_event_code ( event_code ) ) {
case SCU_EVENT_SATA_PHY_DETECTED :
/* Backup the state machine */
sci_base_state_machine_change_state ( & sci_phy - > state_machine ,
SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SATA_SPEED_EN ) ;
break ;
2011-05-11 03:54:23 +04:00
2011-05-12 15:27:29 +04:00
case SCU_EVENT_LINK_FAILURE :
/* Link failure change state back to the starting state */
sci_base_state_machine_change_state ( & sci_phy - > state_machine ,
SCI_BASE_PHY_STATE_STARTING ) ;
break ;
default :
dev_warn ( sciphy_to_dev ( sci_phy ) ,
" %s: PHY starting substate machine received "
" unexpected event_code %x \n " ,
__func__ ,
event_code ) ;
return SCI_FAILURE ;
}
return SCI_SUCCESS ;
case SCI_BASE_PHY_STATE_READY :
switch ( scu_get_event_code ( event_code ) ) {
case SCU_EVENT_LINK_FAILURE :
/* Link failure change state back to the starting state */
sci_base_state_machine_change_state ( & sci_phy - > state_machine ,
SCI_BASE_PHY_STATE_STARTING ) ;
break ;
case SCU_EVENT_BROADCAST_CHANGE :
/* Broadcast change received. Notify the port. */
if ( scic_sds_phy_get_port ( sci_phy ) ! = NULL )
scic_sds_port_broadcast_change_received ( sci_phy - > owning_port , sci_phy ) ;
else
sci_phy - > bcn_received_while_port_unassigned = true ;
break ;
default :
dev_warn ( sciphy_to_dev ( sci_phy ) ,
" %sP SCIC PHY 0x%p ready state machine received "
" unexpected event_code %x \n " ,
__func__ , sci_phy , event_code ) ;
return SCI_FAILURE_INVALID_STATE ;
}
return SCI_SUCCESS ;
case SCI_BASE_PHY_STATE_RESETTING :
switch ( scu_get_event_code ( event_code ) ) {
case SCU_EVENT_HARD_RESET_TRANSMITTED :
/* Link failure change state back to the starting state */
sci_base_state_machine_change_state ( & sci_phy - > state_machine ,
SCI_BASE_PHY_STATE_STARTING ) ;
break ;
default :
dev_warn ( sciphy_to_dev ( sci_phy ) ,
" %s: SCIC PHY 0x%p resetting state machine received "
" unexpected event_code %x \n " ,
__func__ , sci_phy , event_code ) ;
return SCI_FAILURE_INVALID_STATE ;
break ;
}
return SCI_SUCCESS ;
default :
dev_dbg ( sciphy_to_dev ( sci_phy ) ,
" %s: in wrong state: %d \n " , __func__ , state ) ;
return SCI_FAILURE_INVALID_STATE ;
}
2011-05-11 03:54:23 +04:00
}
2011-05-12 15:27:29 +04:00
enum sci_status scic_sds_phy_frame_handler ( struct scic_sds_phy * sci_phy ,
u32 frame_index )
2011-05-11 03:54:23 +04:00
{
2011-05-12 15:27:29 +04:00
enum scic_sds_phy_states state = sci_phy - > state_machine . current_state_id ;
struct scic_sds_controller * scic = sci_phy - > owning_port - > owning_controller ;
enum sci_status result ;
2011-05-11 03:54:23 +04:00
2011-05-12 15:27:29 +04:00
switch ( state ) {
case SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_IAF_UF : {
u32 * frame_words ;
struct sas_identify_frame iaf ;
struct isci_phy * iphy = sci_phy_to_iphy ( sci_phy ) ;
2011-05-11 03:54:23 +04:00
2011-05-12 15:27:29 +04:00
result = scic_sds_unsolicited_frame_control_get_header ( & scic - > uf_control ,
frame_index ,
( void * * ) & frame_words ) ;
2011-05-11 03:54:23 +04:00
2011-05-12 15:27:29 +04:00
if ( result ! = SCI_SUCCESS )
return result ;
sci_swab32_cpy ( & iaf , frame_words , sizeof ( iaf ) / sizeof ( u32 ) ) ;
if ( iaf . frame_type = = 0 ) {
u32 state ;
memcpy ( & iphy - > frame_rcvd . iaf , & iaf , sizeof ( iaf ) ) ;
if ( iaf . smp_tport ) {
/* We got the IAF for an expander PHY go to the final
* state since there are no power requirements for
* expander phys .
*/
state = SCIC_SDS_PHY_STARTING_SUBSTATE_FINAL ;
} else {
/* We got the IAF we can now go to the await spinup
* semaphore state
*/
state = SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SAS_POWER ;
}
sci_base_state_machine_change_state ( & sci_phy - > state_machine ,
state ) ;
result = SCI_SUCCESS ;
} else
dev_warn ( sciphy_to_dev ( sci_phy ) ,
" %s: PHY starting substate machine received "
" unexpected frame id %x \n " ,
__func__ , frame_index ) ;
scic_sds_controller_release_frame ( scic , frame_index ) ;
return result ;
2011-05-11 03:54:23 +04:00
}
2011-05-12 15:27:29 +04:00
case SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SIG_FIS_UF : {
struct dev_to_host_fis * frame_header ;
u32 * fis_frame_data ;
struct isci_phy * iphy = sci_phy_to_iphy ( sci_phy ) ;
result = scic_sds_unsolicited_frame_control_get_header (
& ( scic_sds_phy_get_controller ( sci_phy ) - > uf_control ) ,
frame_index ,
( void * * ) & frame_header ) ;
if ( result ! = SCI_SUCCESS )
return result ;
2011-05-11 03:54:23 +04:00
2011-05-12 15:27:29 +04:00
if ( ( frame_header - > fis_type = = FIS_REGD2H ) & &
! ( frame_header - > status & ATA_BUSY ) ) {
scic_sds_unsolicited_frame_control_get_buffer ( & scic - > uf_control ,
frame_index ,
( void * * ) & fis_frame_data ) ;
scic_sds_controller_copy_sata_response ( & iphy - > frame_rcvd . fis ,
frame_header ,
fis_frame_data ) ;
/* got IAF we can now go to the await spinup semaphore state */
sci_base_state_machine_change_state ( & sci_phy - > state_machine ,
SCIC_SDS_PHY_STARTING_SUBSTATE_FINAL ) ;
result = SCI_SUCCESS ;
} else
dev_warn ( sciphy_to_dev ( sci_phy ) ,
" %s: PHY starting substate machine received "
" unexpected frame id %x \n " ,
__func__ , frame_index ) ;
/* Regardless of the result we are done with this frame with it */
scic_sds_controller_release_frame ( scic , frame_index ) ;
return result ;
}
default :
dev_dbg ( sciphy_to_dev ( sci_phy ) ,
" %s: in wrong state: %d \n " , __func__ , state ) ;
return SCI_FAILURE_INVALID_STATE ;
}
2011-05-11 03:54:23 +04:00
}
2011-05-10 13:28:45 +04:00
static void scic_sds_phy_starting_initial_substate_enter ( void * object )
{
struct scic_sds_phy * sci_phy = object ;
/* This is just an temporary state go off to the starting state */
2011-05-11 03:54:23 +04:00
sci_base_state_machine_change_state ( & sci_phy - > state_machine ,
2011-05-10 13:28:45 +04:00
SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_OSSP_EN ) ;
}
static void scic_sds_phy_starting_await_sas_power_substate_enter ( void * object )
{
struct scic_sds_phy * sci_phy = object ;
2011-05-12 15:51:41 +04:00
struct scic_sds_controller * scic = sci_phy - > owning_port - > owning_controller ;
2011-05-10 13:28:45 +04:00
2011-05-12 15:51:41 +04:00
scic_sds_controller_power_control_queue_insert ( scic , sci_phy ) ;
2011-05-10 13:28:45 +04:00
}
static void scic_sds_phy_starting_await_sas_power_substate_exit ( void * object )
{
struct scic_sds_phy * sci_phy = object ;
2011-05-12 15:51:41 +04:00
struct scic_sds_controller * scic = sci_phy - > owning_port - > owning_controller ;
2011-05-10 13:28:45 +04:00
2011-05-12 15:51:41 +04:00
scic_sds_controller_power_control_queue_remove ( scic , sci_phy ) ;
2011-05-10 13:28:45 +04:00
}
static void scic_sds_phy_starting_await_sata_power_substate_enter ( void * object )
{
struct scic_sds_phy * sci_phy = object ;
2011-05-12 15:51:41 +04:00
struct scic_sds_controller * scic = sci_phy - > owning_port - > owning_controller ;
2011-05-10 13:28:45 +04:00
2011-05-12 15:51:41 +04:00
scic_sds_controller_power_control_queue_insert ( scic , sci_phy ) ;
2011-05-10 13:28:45 +04:00
}
static void scic_sds_phy_starting_await_sata_power_substate_exit ( void * object )
{
struct scic_sds_phy * sci_phy = object ;
2011-05-12 15:51:41 +04:00
struct scic_sds_controller * scic = sci_phy - > owning_port - > owning_controller ;
2011-05-10 13:28:45 +04:00
2011-05-12 15:51:41 +04:00
scic_sds_controller_power_control_queue_remove ( scic , sci_phy ) ;
2011-05-10 13:28:45 +04:00
}
static void scic_sds_phy_starting_await_sata_phy_substate_enter ( void * object )
{
struct scic_sds_phy * sci_phy = object ;
isci_timer_start ( sci_phy - > sata_timeout_timer ,
SCIC_SDS_SATA_LINK_TRAINING_TIMEOUT ) ;
}
2011-05-12 15:51:41 +04:00
static void scic_sds_phy_starting_await_sata_phy_substate_exit ( void * object )
2011-05-10 13:28:45 +04:00
{
struct scic_sds_phy * sci_phy = object ;
isci_timer_stop ( sci_phy - > sata_timeout_timer ) ;
}
static void scic_sds_phy_starting_await_sata_speed_substate_enter ( void * object )
{
struct scic_sds_phy * sci_phy = object ;
isci_timer_start ( sci_phy - > sata_timeout_timer ,
SCIC_SDS_SATA_LINK_TRAINING_TIMEOUT ) ;
}
2011-05-12 15:51:41 +04:00
static void scic_sds_phy_starting_await_sata_speed_substate_exit (
2011-05-10 13:28:45 +04:00
void * object )
{
struct scic_sds_phy * sci_phy = object ;
isci_timer_stop ( sci_phy - > sata_timeout_timer ) ;
}
static void scic_sds_phy_starting_await_sig_fis_uf_substate_enter ( void * object )
{
struct scic_sds_phy * sci_phy = object ;
2011-05-12 15:51:41 +04:00
if ( scic_sds_port_link_detected ( sci_phy - > owning_port , sci_phy ) ) {
2011-05-10 13:28:45 +04:00
/*
* Clear the PE suspend condition so we can actually
* receive SIG FIS
* The hardware will not respond to the XRDY until the PE
* suspend condition is cleared .
*/
scic_sds_phy_resume ( sci_phy ) ;
isci_timer_start ( sci_phy - > sata_timeout_timer ,
SCIC_SDS_SIGNATURE_FIS_TIMEOUT ) ;
} else
sci_phy - > is_in_link_training = false ;
}
2011-05-12 15:51:41 +04:00
static void scic_sds_phy_starting_await_sig_fis_uf_substate_exit ( void * object )
2011-05-10 13:28:45 +04:00
{
struct scic_sds_phy * sci_phy = object ;
isci_timer_stop ( sci_phy - > sata_timeout_timer ) ;
}
static void scic_sds_phy_starting_final_substate_enter ( void * object )
{
struct scic_sds_phy * sci_phy = object ;
/* State machine has run to completion so exit out and change
* the base state machine to the ready state
*/
sci_base_state_machine_change_state ( & sci_phy - > state_machine ,
SCI_BASE_PHY_STATE_READY ) ;
}
/**
*
* @ sci_phy : This is the struct scic_sds_phy object to stop .
*
* This method will stop the struct scic_sds_phy object . This does not reset the
* protocol engine it just suspends it and places it in a state where it will
* not cause the end device to power up . none
*/
static void scu_link_layer_stop_protocol_engine (
struct scic_sds_phy * sci_phy )
{
u32 scu_sas_pcfg_value ;
u32 enable_spinup_value ;
/* Suspend the protocol engine and place it in a sata spinup hold state */
scu_sas_pcfg_value =
readl ( & sci_phy - > link_layer_registers - > phy_configuration ) ;
scu_sas_pcfg_value | =
( SCU_SAS_PCFG_GEN_BIT ( OOB_RESET ) |
SCU_SAS_PCFG_GEN_BIT ( SUSPEND_PROTOCOL_ENGINE ) |
SCU_SAS_PCFG_GEN_BIT ( SATA_SPINUP_HOLD ) ) ;
writel ( scu_sas_pcfg_value ,
& sci_phy - > link_layer_registers - > phy_configuration ) ;
/* Disable the notify enable spinup primitives */
enable_spinup_value = readl ( & sci_phy - > link_layer_registers - > notify_enable_spinup_control ) ;
enable_spinup_value & = ~ SCU_ENSPINUP_GEN_BIT ( ENABLE ) ;
writel ( enable_spinup_value , & sci_phy - > link_layer_registers - > notify_enable_spinup_control ) ;
}
/**
*
*
* This method will start the OOB / SN state machine for this struct scic_sds_phy object .
*/
static void scu_link_layer_start_oob (
struct scic_sds_phy * sci_phy )
{
u32 scu_sas_pcfg_value ;
scu_sas_pcfg_value =
readl ( & sci_phy - > link_layer_registers - > phy_configuration ) ;
scu_sas_pcfg_value | = SCU_SAS_PCFG_GEN_BIT ( OOB_ENABLE ) ;
scu_sas_pcfg_value & =
~ ( SCU_SAS_PCFG_GEN_BIT ( OOB_RESET ) |
SCU_SAS_PCFG_GEN_BIT ( HARD_RESET ) ) ;
writel ( scu_sas_pcfg_value ,
& sci_phy - > link_layer_registers - > phy_configuration ) ;
}
/**
*
*
* This method will transmit a hard reset request on the specified phy . The SCU
* hardware requires that we reset the OOB state machine and set the hard reset
* bit in the phy configuration register . We then must start OOB over with the
* hard reset bit set .
*/
static void scu_link_layer_tx_hard_reset (
struct scic_sds_phy * sci_phy )
{
u32 phy_configuration_value ;
/*
* SAS Phys must wait for the HARD_RESET_TX event notification to transition
* to the starting state . */
phy_configuration_value =
readl ( & sci_phy - > link_layer_registers - > phy_configuration ) ;
phy_configuration_value | =
( SCU_SAS_PCFG_GEN_BIT ( HARD_RESET ) |
SCU_SAS_PCFG_GEN_BIT ( OOB_RESET ) ) ;
writel ( phy_configuration_value ,
& sci_phy - > link_layer_registers - > phy_configuration ) ;
/* Now take the OOB state machine out of reset */
phy_configuration_value | = SCU_SAS_PCFG_GEN_BIT ( OOB_ENABLE ) ;
phy_configuration_value & = ~ SCU_SAS_PCFG_GEN_BIT ( OOB_RESET ) ;
writel ( phy_configuration_value ,
& sci_phy - > link_layer_registers - > phy_configuration ) ;
}
static void scic_sds_phy_stopped_state_enter ( void * object )
{
struct scic_sds_phy * sci_phy = object ;
2011-05-12 15:51:41 +04:00
struct scic_sds_port * sci_port = sci_phy - > owning_port ;
struct scic_sds_controller * scic = sci_port - > owning_controller ;
2011-05-10 13:28:45 +04:00
struct isci_host * ihost = scic_to_ihost ( scic ) ;
/*
* @ todo We need to get to the controller to place this PE in a
* reset state
*/
if ( sci_phy - > sata_timeout_timer ! = NULL ) {
isci_del_timer ( ihost , sci_phy - > sata_timeout_timer ) ;
sci_phy - > sata_timeout_timer = NULL ;
}
scu_link_layer_stop_protocol_engine ( sci_phy ) ;
2011-05-12 15:51:41 +04:00
if ( sci_phy - > state_machine . previous_state_id ! = SCI_BASE_PHY_STATE_INITIAL )
scic_sds_controller_link_down ( scic_sds_phy_get_controller ( sci_phy ) ,
scic_sds_phy_get_port ( sci_phy ) ,
sci_phy ) ;
2011-05-10 13:28:45 +04:00
}
static void scic_sds_phy_starting_state_enter ( void * object )
{
struct scic_sds_phy * sci_phy = object ;
scu_link_layer_stop_protocol_engine ( sci_phy ) ;
scu_link_layer_start_oob ( sci_phy ) ;
/* We don't know what kind of phy we are going to be just yet */
sci_phy - > protocol = SCIC_SDS_PHY_PROTOCOL_UNKNOWN ;
sci_phy - > bcn_received_while_port_unassigned = false ;
2011-05-12 15:51:41 +04:00
if ( sci_phy - > state_machine . previous_state_id = = SCI_BASE_PHY_STATE_READY )
scic_sds_controller_link_down ( scic_sds_phy_get_controller ( sci_phy ) ,
scic_sds_phy_get_port ( sci_phy ) ,
sci_phy ) ;
2011-05-11 03:54:23 +04:00
sci_base_state_machine_change_state ( & sci_phy - > state_machine ,
SCIC_SDS_PHY_STARTING_SUBSTATE_INITIAL ) ;
2011-05-10 13:28:45 +04:00
}
static void scic_sds_phy_ready_state_enter ( void * object )
{
struct scic_sds_phy * sci_phy = object ;
2011-05-12 15:51:41 +04:00
scic_sds_controller_link_up ( scic_sds_phy_get_controller ( sci_phy ) ,
scic_sds_phy_get_port ( sci_phy ) ,
sci_phy ) ;
2011-05-10 13:28:45 +04:00
}
static void scic_sds_phy_ready_state_exit ( void * object )
{
struct scic_sds_phy * sci_phy = object ;
scic_sds_phy_suspend ( sci_phy ) ;
}
static void scic_sds_phy_resetting_state_enter ( void * object )
{
struct scic_sds_phy * sci_phy = object ;
2011-05-12 15:51:41 +04:00
/* The phy is being reset, therefore deactivate it from the port. In
* the resetting state we don ' t notify the user regarding link up and
* link down notifications
*/
2011-05-10 13:28:45 +04:00
scic_sds_port_deactivate_phy ( sci_phy - > owning_port , sci_phy , false ) ;
if ( sci_phy - > protocol = = SCIC_SDS_PHY_PROTOCOL_SAS ) {
scu_link_layer_tx_hard_reset ( sci_phy ) ;
} else {
2011-05-12 15:51:41 +04:00
/* The SCU does not need to have a discrete reset state so
2011-05-10 13:28:45 +04:00
* just go back to the starting state .
*/
2011-05-12 15:51:41 +04:00
sci_base_state_machine_change_state ( & sci_phy - > state_machine ,
SCI_BASE_PHY_STATE_STARTING ) ;
2011-05-10 13:28:45 +04:00
}
}
static const struct sci_base_state scic_sds_phy_state_table [ ] = {
2011-05-12 15:51:41 +04:00
[ SCI_BASE_PHY_STATE_INITIAL ] = { } ,
2011-05-10 13:28:45 +04:00
[ SCI_BASE_PHY_STATE_STOPPED ] = {
. enter_state = scic_sds_phy_stopped_state_enter ,
} ,
[ SCI_BASE_PHY_STATE_STARTING ] = {
. enter_state = scic_sds_phy_starting_state_enter ,
} ,
2011-05-11 03:54:23 +04:00
[ SCIC_SDS_PHY_STARTING_SUBSTATE_INITIAL ] = {
. enter_state = scic_sds_phy_starting_initial_substate_enter ,
} ,
2011-05-12 15:51:41 +04:00
[ SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_OSSP_EN ] = { } ,
[ SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SAS_SPEED_EN ] = { } ,
[ SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_IAF_UF ] = { } ,
2011-05-11 03:54:23 +04:00
[ SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SAS_POWER ] = {
. enter_state = scic_sds_phy_starting_await_sas_power_substate_enter ,
. exit_state = scic_sds_phy_starting_await_sas_power_substate_exit ,
} ,
[ SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SATA_POWER ] = {
. enter_state = scic_sds_phy_starting_await_sata_power_substate_enter ,
. exit_state = scic_sds_phy_starting_await_sata_power_substate_exit
} ,
[ SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SATA_PHY_EN ] = {
. enter_state = scic_sds_phy_starting_await_sata_phy_substate_enter ,
. exit_state = scic_sds_phy_starting_await_sata_phy_substate_exit
} ,
[ SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SATA_SPEED_EN ] = {
. enter_state = scic_sds_phy_starting_await_sata_speed_substate_enter ,
. exit_state = scic_sds_phy_starting_await_sata_speed_substate_exit
} ,
[ SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SIG_FIS_UF ] = {
. enter_state = scic_sds_phy_starting_await_sig_fis_uf_substate_enter ,
. exit_state = scic_sds_phy_starting_await_sig_fis_uf_substate_exit
} ,
[ SCIC_SDS_PHY_STARTING_SUBSTATE_FINAL ] = {
. enter_state = scic_sds_phy_starting_final_substate_enter ,
} ,
2011-05-10 13:28:45 +04:00
[ SCI_BASE_PHY_STATE_READY ] = {
. enter_state = scic_sds_phy_ready_state_enter ,
. exit_state = scic_sds_phy_ready_state_exit ,
} ,
[ SCI_BASE_PHY_STATE_RESETTING ] = {
. enter_state = scic_sds_phy_resetting_state_enter ,
} ,
2011-05-12 15:51:41 +04:00
[ SCI_BASE_PHY_STATE_FINAL ] = { } ,
2011-05-10 13:28:45 +04:00
} ;
void scic_sds_phy_construct ( struct scic_sds_phy * sci_phy ,
struct scic_sds_port * owning_port , u8 phy_index )
{
sci_base_state_machine_construct ( & sci_phy - > state_machine ,
sci_phy ,
scic_sds_phy_state_table ,
SCI_BASE_PHY_STATE_INITIAL ) ;
sci_base_state_machine_start ( & sci_phy - > state_machine ) ;
/* Copy the rest of the input data to our locals */
sci_phy - > owning_port = owning_port ;
sci_phy - > phy_index = phy_index ;
sci_phy - > bcn_received_while_port_unassigned = false ;
sci_phy - > protocol = SCIC_SDS_PHY_PROTOCOL_UNKNOWN ;
sci_phy - > link_layer_registers = NULL ;
sci_phy - > max_negotiated_speed = SAS_LINK_RATE_UNKNOWN ;
sci_phy - > sata_timeout_timer = NULL ;
}
2011-07-03 09:56:22 +04:00
2011-05-07 04:36:38 +04:00
void isci_phy_init ( struct isci_phy * iphy , struct isci_host * ihost , int index )
2011-07-03 09:56:22 +04:00
{
2011-02-25 21:25:21 +03:00
union scic_oem_parameters oem ;
2011-05-07 04:36:38 +04:00
u64 sci_sas_addr ;
__be64 sas_addr ;
scic_oem_parameters_get ( & ihost - > sci , & oem ) ;
sci_sas_addr = oem . sds1 . phys [ index ] . sas_address . high ;
sci_sas_addr < < = 32 ;
sci_sas_addr | = oem . sds1 . phys [ index ] . sas_address . low ;
sas_addr = cpu_to_be64 ( sci_sas_addr ) ;
memcpy ( iphy - > sas_addr , & sas_addr , sizeof ( sas_addr ) ) ;
iphy - > isci_port = NULL ;
iphy - > sas_phy . enabled = 0 ;
iphy - > sas_phy . id = index ;
iphy - > sas_phy . sas_addr = & iphy - > sas_addr [ 0 ] ;
iphy - > sas_phy . frame_rcvd = ( u8 * ) & iphy - > frame_rcvd ;
iphy - > sas_phy . ha = & ihost - > sas_ha ;
iphy - > sas_phy . lldd_phy = iphy ;
iphy - > sas_phy . enabled = 1 ;
iphy - > sas_phy . class = SAS ;
iphy - > sas_phy . iproto = SAS_PROTOCOL_ALL ;
iphy - > sas_phy . tproto = 0 ;
iphy - > sas_phy . type = PHY_TYPE_PHYSICAL ;
iphy - > sas_phy . role = PHY_ROLE_INITIATOR ;
iphy - > sas_phy . oob_mode = OOB_NOT_CONNECTED ;
iphy - > sas_phy . linkrate = SAS_LINK_RATE_UNKNOWN ;
memset ( & iphy - > frame_rcvd , 0 , sizeof ( iphy - > frame_rcvd ) ) ;
2011-07-03 09:56:22 +04:00
}
/**
* isci_phy_control ( ) - This function is one of the SAS Domain Template
* functions . This is a phy management function .
* @ phy : This parameter specifies the sphy being controlled .
* @ func : This parameter specifies the phy control function being invoked .
* @ buf : This parameter is specific to the phy function being invoked .
*
* status , zero indicates success .
*/
2011-03-02 23:31:24 +03:00
int isci_phy_control ( struct asd_sas_phy * sas_phy ,
enum phy_func func ,
void * buf )
2011-07-03 09:56:22 +04:00
{
2011-03-02 23:31:24 +03:00
int ret = 0 ;
struct isci_phy * iphy = sas_phy - > lldd_phy ;
struct isci_port * iport = iphy - > isci_port ;
struct isci_host * ihost = sas_phy - > ha - > lldd_ha ;
unsigned long flags ;
2011-07-03 09:56:22 +04:00
2011-03-02 23:31:24 +03:00
dev_dbg ( & ihost - > pdev - > dev ,
" %s: phy %p; func %d; buf %p; isci phy %p, port %p \n " ,
__func__ , sas_phy , func , buf , iphy , iport ) ;
2011-07-03 09:56:22 +04:00
switch ( func ) {
2011-03-02 23:31:24 +03:00
case PHY_FUNC_DISABLE :
spin_lock_irqsave ( & ihost - > scic_lock , flags ) ;
2011-05-07 04:36:38 +04:00
scic_sds_phy_stop ( & iphy - > sci ) ;
2011-03-02 23:31:24 +03:00
spin_unlock_irqrestore ( & ihost - > scic_lock , flags ) ;
break ;
2011-07-03 09:56:22 +04:00
case PHY_FUNC_LINK_RESET :
2011-03-02 23:31:24 +03:00
spin_lock_irqsave ( & ihost - > scic_lock , flags ) ;
2011-05-07 04:36:38 +04:00
scic_sds_phy_stop ( & iphy - > sci ) ;
scic_sds_phy_start ( & iphy - > sci ) ;
2011-03-02 23:31:24 +03:00
spin_unlock_irqrestore ( & ihost - > scic_lock , flags ) ;
break ;
case PHY_FUNC_HARD_RESET :
if ( ! iport )
return - ENODEV ;
2011-07-03 09:56:22 +04:00
/* Perform the port reset. */
2011-04-01 00:10:44 +04:00
ret = isci_port_perform_hard_reset ( ihost , iport , iphy ) ;
2011-07-03 09:56:22 +04:00
break ;
default :
2011-03-02 23:31:24 +03:00
dev_dbg ( & ihost - > pdev - > dev ,
" %s: phy %p; func %d NOT IMPLEMENTED! \n " ,
__func__ , sas_phy , func ) ;
ret = - ENOSYS ;
2011-07-03 09:56:22 +04:00
break ;
}
return ret ;
}