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 .
*/
2011-05-09 04:34:44 +04:00
# include "host.h"
2011-07-03 09:56:22 +04:00
# define SCIC_SDS_MPC_RECONFIGURATION_TIMEOUT (10)
# define SCIC_SDS_APC_RECONFIGURATION_TIMEOUT (10)
2012-02-29 13:07:56 +04:00
# define SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION (1000)
2011-07-03 09:56:22 +04:00
enum SCIC_SDS_APC_ACTIVITY {
SCIC_SDS_APC_SKIP_PHY ,
SCIC_SDS_APC_ADD_PHY ,
SCIC_SDS_APC_START_TIMER ,
SCIC_SDS_APC_ACTIVITY_MAX
} ;
/*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* General port configuration agent routines
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/**
*
* @ address_one : A SAS Address to be compared .
* @ address_two : A SAS Address to be compared .
*
* Compare the two SAS Address and if SAS Address One is greater than SAS
* Address Two then return > 0 else if SAS Address One is less than SAS Address
* Two return < 0 Otherwise they are the same return 0 A signed value of x > 0
* > y where x is returned for Address One > Address Two y is returned for
* Address One < Address Two 0 is returned ofr Address One = Address Two
*/
static s32 sci_sas_address_compare (
struct sci_sas_address address_one ,
struct sci_sas_address address_two )
{
if ( address_one . high > address_two . high ) {
return 1 ;
} else if ( address_one . high < address_two . high ) {
return - 1 ;
} else if ( address_one . low > address_two . low ) {
return 1 ;
} else if ( address_one . low < address_two . low ) {
return - 1 ;
}
/* The two SAS Address must be identical */
return 0 ;
}
/**
*
* @ controller : The controller object used for the port search .
* @ phy : The phy object to match .
*
* This routine will find a matching port for the phy . This means that the
* port and phy both have the same broadcast sas address and same received sas
2011-02-08 19:28:42 +03:00
* address . The port address or the NULL if there is no matching
2011-07-03 09:56:22 +04:00
* port . port address if the port can be found to match the phy .
2011-02-08 19:28:42 +03:00
* NULL if there is no matching port for the phy .
2011-07-03 09:56:22 +04:00
*/
2011-07-01 06:14:33 +04:00
static struct isci_port * sci_port_configuration_agent_find_port (
2011-07-01 04:38:32 +04:00
struct isci_host * ihost ,
2011-06-29 02:05:53 +04:00
struct isci_phy * iphy )
2011-07-03 09:56:22 +04:00
{
2011-05-05 05:11:43 +04:00
u8 i ;
2011-07-03 09:56:22 +04:00
struct sci_sas_address port_sas_address ;
struct sci_sas_address port_attached_device_address ;
struct sci_sas_address phy_sas_address ;
struct sci_sas_address phy_attached_device_address ;
/*
* Since this phy can be a member of a wide port check to see if one or
* more phys match the sent and received SAS address as this phy in which
2011-05-05 05:11:43 +04:00
* case it should participate in the same port .
*/
2011-07-01 06:14:33 +04:00
sci_phy_get_sas_address ( iphy , & phy_sas_address ) ;
sci_phy_get_attached_sas_address ( iphy , & phy_attached_device_address ) ;
2011-07-03 09:56:22 +04:00
2011-07-01 04:38:32 +04:00
for ( i = 0 ; i < ihost - > logical_port_entries ; i + + ) {
2011-06-30 00:09:25 +04:00
struct isci_port * iport = & ihost - > ports [ i ] ;
2011-07-03 09:56:22 +04:00
2011-07-01 06:14:33 +04:00
sci_port_get_sas_address ( iport , & port_sas_address ) ;
sci_port_get_attached_sas_address ( iport , & port_attached_device_address ) ;
2011-07-03 09:56:22 +04:00
2011-05-07 21:11:43 +04:00
if ( sci_sas_address_compare ( port_sas_address , phy_sas_address ) = = 0 & &
sci_sas_address_compare ( port_attached_device_address , phy_attached_device_address ) = = 0 )
2011-06-30 00:09:25 +04:00
return iport ;
2011-07-03 09:56:22 +04:00
}
2011-02-08 19:28:42 +03:00
return NULL ;
2011-07-03 09:56:22 +04:00
}
/**
*
* @ controller : This is the controller object that contains the port agent
2019-10-24 18:25:43 +03:00
* @ port_agent : This is the port configuration agent for the controller .
2011-07-03 09:56:22 +04:00
*
* This routine will validate the port configuration is correct for the SCU
* hardware . The SCU hardware allows for port configurations as follows . LP0
* - > ( PE0 ) , ( PE0 , PE1 ) , ( PE0 , PE1 , PE2 , PE3 ) LP1 - > ( PE1 ) LP2 - > ( PE2 ) , ( PE2 ,
* PE3 ) LP3 - > ( PE3 ) enum sci_status SCI_SUCCESS the port configuration is valid for
* this port configuration agent . SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION
* the port configuration is not valid for this port configuration agent .
*/
2011-07-01 06:14:33 +04:00
static enum sci_status sci_port_configuration_agent_validate_ports (
2011-07-01 04:38:32 +04:00
struct isci_host * ihost ,
2011-07-01 06:14:33 +04:00
struct sci_port_configuration_agent * port_agent )
2011-07-03 09:56:22 +04:00
{
struct sci_sas_address first_address ;
struct sci_sas_address second_address ;
/*
* Sanity check the max ranges for all the phys the max index
* is always equal to the port range index */
2011-05-07 04:36:38 +04:00
if ( port_agent - > phy_valid_port_range [ 0 ] . max_index ! = 0 | |
port_agent - > phy_valid_port_range [ 1 ] . max_index ! = 1 | |
port_agent - > phy_valid_port_range [ 2 ] . max_index ! = 2 | |
port_agent - > phy_valid_port_range [ 3 ] . max_index ! = 3 )
2011-07-03 09:56:22 +04:00
return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION ;
/*
* This is a request to configure a single x4 port or at least attempt
* to make all the phys into a single port */
2011-05-07 04:36:38 +04:00
if ( port_agent - > phy_valid_port_range [ 0 ] . min_index = = 0 & &
port_agent - > phy_valid_port_range [ 1 ] . min_index = = 0 & &
port_agent - > phy_valid_port_range [ 2 ] . min_index = = 0 & &
port_agent - > phy_valid_port_range [ 3 ] . min_index = = 0 )
2011-07-03 09:56:22 +04:00
return SCI_SUCCESS ;
/*
* This is a degenerate case where phy 1 and phy 2 are assigned
* to the same port this is explicitly disallowed by the hardware
* unless they are part of the same x4 port and this condition was
* already checked above . */
if ( port_agent - > phy_valid_port_range [ 2 ] . min_index = = 1 ) {
return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION ;
}
/*
* PE0 and PE3 can never have the same SAS Address unless they
* are part of the same x4 wide port and we have already checked
* for this condition . */
2011-07-01 06:14:33 +04:00
sci_phy_get_sas_address ( & ihost - > phys [ 0 ] , & first_address ) ;
sci_phy_get_sas_address ( & ihost - > phys [ 3 ] , & second_address ) ;
2011-07-03 09:56:22 +04:00
if ( sci_sas_address_compare ( first_address , second_address ) = = 0 ) {
return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION ;
}
/*
* PE0 and PE1 are configured into a 2 x1 ports make sure that the
* SAS Address for PE0 and PE2 are different since they can not be
* part of the same port . */
2011-05-07 04:36:38 +04:00
if ( port_agent - > phy_valid_port_range [ 0 ] . min_index = = 0 & &
port_agent - > phy_valid_port_range [ 1 ] . min_index = = 1 ) {
2011-07-01 06:14:33 +04:00
sci_phy_get_sas_address ( & ihost - > phys [ 0 ] , & first_address ) ;
sci_phy_get_sas_address ( & ihost - > phys [ 2 ] , & second_address ) ;
2011-07-03 09:56:22 +04:00
if ( sci_sas_address_compare ( first_address , second_address ) = = 0 ) {
return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION ;
}
}
/*
* PE2 and PE3 are configured into a 2 x1 ports make sure that the
* SAS Address for PE1 and PE3 are different since they can not be
* part of the same port . */
2011-05-07 04:36:38 +04:00
if ( port_agent - > phy_valid_port_range [ 2 ] . min_index = = 2 & &
port_agent - > phy_valid_port_range [ 3 ] . min_index = = 3 ) {
2011-07-01 06:14:33 +04:00
sci_phy_get_sas_address ( & ihost - > phys [ 1 ] , & first_address ) ;
sci_phy_get_sas_address ( & ihost - > phys [ 3 ] , & second_address ) ;
2011-07-03 09:56:22 +04:00
if ( sci_sas_address_compare ( first_address , second_address ) = = 0 ) {
return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION ;
}
}
return SCI_SUCCESS ;
}
/*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Manual port configuration agent routines
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2011-07-01 04:38:32 +04:00
/* verify all of the phys in the same port are using the same SAS address */
static enum sci_status
2011-07-01 06:14:33 +04:00
sci_mpc_agent_validate_phy_configuration ( struct isci_host * ihost ,
struct sci_port_configuration_agent * port_agent )
2011-07-03 09:56:22 +04:00
{
u32 phy_mask ;
u32 assigned_phy_mask ;
struct sci_sas_address sas_address ;
struct sci_sas_address phy_assigned_address ;
u8 port_index ;
u8 phy_index ;
assigned_phy_mask = 0 ;
sas_address . high = 0 ;
sas_address . low = 0 ;
for ( port_index = 0 ; port_index < SCI_MAX_PORTS ; port_index + + ) {
2011-07-01 06:14:33 +04:00
phy_mask = ihost - > oem_parameters . ports [ port_index ] . phy_mask ;
2011-07-03 09:56:22 +04:00
2011-05-07 04:36:38 +04:00
if ( ! phy_mask )
continue ;
/*
* Make sure that one or more of the phys were not already assinged to
* a different port . */
if ( ( phy_mask & ~ assigned_phy_mask ) = = 0 ) {
return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION ;
}
2011-07-03 09:56:22 +04:00
2011-05-07 04:36:38 +04:00
/* Find the starting phy index for this round through the loop */
for ( phy_index = 0 ; phy_index < SCI_MAX_PHYS ; phy_index + + ) {
if ( ( phy_mask & ( 1 < < phy_index ) ) = = 0 )
continue ;
2011-07-01 06:14:33 +04:00
sci_phy_get_sas_address ( & ihost - > phys [ phy_index ] ,
2011-05-07 04:36:38 +04:00
& sas_address ) ;
2011-07-03 09:56:22 +04:00
2011-05-07 04:36:38 +04:00
/*
* The phy_index can be used as the starting point for the
* port range since the hardware starts all logical ports
* the same as the PE index . */
port_agent - > phy_valid_port_range [ phy_index ] . min_index = port_index ;
port_agent - > phy_valid_port_range [ phy_index ] . max_index = phy_index ;
2011-07-03 09:56:22 +04:00
2011-05-07 04:36:38 +04:00
if ( phy_index ! = port_index ) {
return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION ;
2011-07-03 09:56:22 +04:00
}
2011-05-07 04:36:38 +04:00
break ;
}
2011-07-03 09:56:22 +04:00
2011-05-07 04:36:38 +04:00
/*
* See how many additional phys are being added to this logical port .
* Note : We have not moved the current phy_index so we will actually
* compare the startting phy with itself .
* This is expected and required to add the phy to the port . */
2018-04-20 12:57:16 +03:00
for ( ; phy_index < SCI_MAX_PHYS ; phy_index + + ) {
2011-05-07 04:36:38 +04:00
if ( ( phy_mask & ( 1 < < phy_index ) ) = = 0 )
continue ;
2011-07-01 06:14:33 +04:00
sci_phy_get_sas_address ( & ihost - > phys [ phy_index ] ,
2011-05-07 04:36:38 +04:00
& phy_assigned_address ) ;
if ( sci_sas_address_compare ( sas_address , phy_assigned_address ) ! = 0 ) {
/*
* The phy mask specified that this phy is part of the same port
* as the starting phy and it is not so fail this configuration */
return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION ;
}
2011-07-03 09:56:22 +04:00
2011-05-07 04:36:38 +04:00
port_agent - > phy_valid_port_range [ phy_index ] . min_index = port_index ;
port_agent - > phy_valid_port_range [ phy_index ] . max_index = phy_index ;
2011-07-03 09:56:22 +04:00
2011-07-01 06:14:33 +04:00
sci_port_add_phy ( & ihost - > ports [ port_index ] ,
2011-06-29 02:05:53 +04:00
& ihost - > phys [ phy_index ] ) ;
2011-07-03 09:56:22 +04:00
2011-05-07 04:36:38 +04:00
assigned_phy_mask | = ( 1 < < phy_index ) ;
2011-07-03 09:56:22 +04:00
}
2011-05-07 04:36:38 +04:00
2011-07-03 09:56:22 +04:00
}
2011-07-01 06:14:33 +04:00
return sci_port_configuration_agent_validate_ports ( ihost , port_agent ) ;
2011-07-03 09:56:22 +04:00
}
2017-09-02 09:21:24 +03:00
static void mpc_agent_timeout ( struct timer_list * t )
2011-07-03 09:56:22 +04:00
{
u8 index ;
2017-09-02 09:21:24 +03:00
struct sci_timer * tmr = from_timer ( tmr , t , timer ) ;
2011-07-01 06:14:33 +04:00
struct sci_port_configuration_agent * port_agent ;
2011-05-20 07:00:51 +04:00
struct isci_host * ihost ;
unsigned long flags ;
2011-07-03 09:56:22 +04:00
u16 configure_phy_mask ;
2011-05-20 07:00:51 +04:00
port_agent = container_of ( tmr , typeof ( * port_agent ) , timer ) ;
2011-07-01 04:38:32 +04:00
ihost = container_of ( port_agent , typeof ( * ihost ) , port_agent ) ;
2011-05-20 07:00:51 +04:00
spin_lock_irqsave ( & ihost - > scic_lock , flags ) ;
if ( tmr - > cancel )
goto done ;
2011-07-03 09:56:22 +04:00
port_agent - > timer_pending = false ;
/* Find the mask of phys that are reported read but as yet unconfigured into a port */
configure_phy_mask = ~ port_agent - > phy_configured_mask & port_agent - > phy_ready_mask ;
for ( index = 0 ; index < SCI_MAX_PHYS ; index + + ) {
2011-06-29 02:05:53 +04:00
struct isci_phy * iphy = & ihost - > phys [ index ] ;
2011-05-07 04:36:38 +04:00
2011-07-03 09:56:22 +04:00
if ( configure_phy_mask & ( 1 < < index ) ) {
2011-07-01 04:38:32 +04:00
port_agent - > link_up_handler ( ihost , port_agent ,
2011-06-29 02:05:53 +04:00
phy_get_non_dummy_port ( iphy ) ,
iphy ) ;
2011-07-03 09:56:22 +04:00
}
}
2011-05-20 07:00:51 +04:00
done :
spin_unlock_irqrestore ( & ihost - > scic_lock , flags ) ;
2011-07-03 09:56:22 +04:00
}
2011-07-01 06:14:33 +04:00
static void sci_mpc_agent_link_up ( struct isci_host * ihost ,
struct sci_port_configuration_agent * port_agent ,
2011-06-30 00:09:25 +04:00
struct isci_port * iport ,
2011-06-29 02:05:53 +04:00
struct isci_phy * iphy )
2011-07-03 09:56:22 +04:00
{
2011-06-30 00:09:25 +04:00
/* If the port is NULL then the phy was not assigned to a port.
* This is because the phy was not given the same SAS Address as
* the other PHYs in the port .
2011-06-29 02:05:53 +04:00
*/
2011-06-30 00:09:25 +04:00
if ( ! iport )
return ;
2011-07-03 09:56:22 +04:00
2011-07-01 13:25:15 +04:00
port_agent - > phy_ready_mask | = ( 1 < < iphy - > phy_index ) ;
2011-07-01 06:14:33 +04:00
sci_port_link_up ( iport , iphy ) ;
2011-07-01 13:25:15 +04:00
if ( ( iport - > active_phy_mask & ( 1 < < iphy - > phy_index ) ) )
port_agent - > phy_configured_mask | = ( 1 < < iphy - > phy_index ) ;
2011-07-03 09:56:22 +04:00
}
/**
*
* @ controller : This is the controller object that receives the link down
* notification .
* @ port : This is the port object associated with the phy . If the is no
2011-02-08 19:28:42 +03:00
* associated port this is an NULL . The port is an invalid
2011-07-03 09:56:22 +04:00
* handle only if the phy was never port of this port . This happens when
* the phy is not broadcasting the same SAS address as the other phys in the
* assigned port .
* @ phy : This is the phy object which has gone link down .
*
2011-03-27 03:11:51 +04:00
* This function handles the manual port configuration link down notifications .
2011-07-03 09:56:22 +04:00
* Since all ports and phys are associated at initialization time we just turn
* around and notifiy the port object of the link down event . If this PHY is
* not associated with a port there is no action taken . Is it possible to get a
* link down notification from a phy that has no assocoated port ?
*/
2011-07-01 06:14:33 +04:00
static void sci_mpc_agent_link_down (
2011-07-01 04:38:32 +04:00
struct isci_host * ihost ,
2011-07-01 06:14:33 +04:00
struct sci_port_configuration_agent * port_agent ,
2011-06-30 00:09:25 +04:00
struct isci_port * iport ,
2011-06-29 02:05:53 +04:00
struct isci_phy * iphy )
2011-07-03 09:56:22 +04:00
{
2011-06-30 00:09:25 +04:00
if ( iport ! = NULL ) {
2011-07-03 09:56:22 +04:00
/*
2011-03-27 03:11:51 +04:00
* If we can form a new port from the remainder of the phys
* then we want to start the timer to allow the SCI User to
* cleanup old devices and rediscover the port before
* rebuilding the port with the phys that remain in the ready
* state .
*/
2011-07-01 13:25:15 +04:00
port_agent - > phy_ready_mask & = ~ ( 1 < < iphy - > phy_index ) ;
port_agent - > phy_configured_mask & = ~ ( 1 < < iphy - > phy_index ) ;
2011-07-03 09:56:22 +04:00
/*
2011-03-27 03:11:51 +04:00
* Check to see if there are more phys waiting to be
* configured into a port . If there are allow the SCI User
* to tear down this port , if necessary , and then reconstruct
* the port after the timeout .
*/
if ( ( port_agent - > phy_configured_mask = = 0x0000 ) & &
( port_agent - > phy_ready_mask ! = 0x0000 ) & &
! port_agent - > timer_pending ) {
2011-07-03 09:56:22 +04:00
port_agent - > timer_pending = true ;
2011-05-20 07:00:51 +04:00
sci_mod_timer ( & port_agent - > timer ,
SCIC_SDS_MPC_RECONFIGURATION_TIMEOUT ) ;
2011-07-03 09:56:22 +04:00
}
2011-07-01 06:14:33 +04:00
sci_port_link_down ( iport , iphy ) ;
2011-07-03 09:56:22 +04:00
}
}
2011-07-01 04:38:32 +04:00
/* verify phys are assigned a valid SAS address for automatic port
* configuration mode .
2011-07-03 09:56:22 +04:00
*/
2011-07-01 04:38:32 +04:00
static enum sci_status
2011-07-01 06:14:33 +04:00
sci_apc_agent_validate_phy_configuration ( struct isci_host * ihost ,
struct sci_port_configuration_agent * port_agent )
2011-07-03 09:56:22 +04:00
{
u8 phy_index ;
u8 port_index ;
struct sci_sas_address sas_address ;
struct sci_sas_address phy_assigned_address ;
phy_index = 0 ;
while ( phy_index < SCI_MAX_PHYS ) {
port_index = phy_index ;
/* Get the assigned SAS Address for the first PHY on the controller. */
2011-07-01 06:14:33 +04:00
sci_phy_get_sas_address ( & ihost - > phys [ phy_index ] ,
2011-05-07 04:36:38 +04:00
& sas_address ) ;
2011-07-03 09:56:22 +04:00
while ( + + phy_index < SCI_MAX_PHYS ) {
2011-07-01 06:14:33 +04:00
sci_phy_get_sas_address ( & ihost - > phys [ phy_index ] ,
2011-05-07 04:36:38 +04:00
& phy_assigned_address ) ;
2011-07-03 09:56:22 +04:00
/* Verify each of the SAS address are all the same for every PHY */
if ( sci_sas_address_compare ( sas_address , phy_assigned_address ) = = 0 ) {
port_agent - > phy_valid_port_range [ phy_index ] . min_index = port_index ;
port_agent - > phy_valid_port_range [ phy_index ] . max_index = phy_index ;
} else {
port_agent - > phy_valid_port_range [ phy_index ] . min_index = phy_index ;
port_agent - > phy_valid_port_range [ phy_index ] . max_index = phy_index ;
break ;
}
}
}
2011-07-01 06:14:33 +04:00
return sci_port_configuration_agent_validate_ports ( ihost , port_agent ) ;
2011-07-03 09:56:22 +04:00
}
2012-01-04 13:33:31 +04:00
/*
* This routine will restart the automatic port configuration timeout
* timer for the next time period . This could be caused by either a link
* down event or a link up event where we can not yet tell to which a phy
* belongs .
*/
2012-02-29 13:07:56 +04:00
static void sci_apc_agent_start_timer ( struct sci_port_configuration_agent * port_agent ,
u32 timeout )
2012-01-04 13:33:31 +04:00
{
port_agent - > timer_pending = true ;
sci_mod_timer ( & port_agent - > timer , timeout ) ;
}
2011-07-01 06:14:33 +04:00
static void sci_apc_agent_configure_ports ( struct isci_host * ihost ,
struct sci_port_configuration_agent * port_agent ,
2011-06-29 02:05:53 +04:00
struct isci_phy * iphy ,
bool start_timer )
2011-07-03 09:56:22 +04:00
{
u8 port_index ;
enum sci_status status ;
2011-06-30 00:09:25 +04:00
struct isci_port * iport ;
2011-07-03 09:56:22 +04:00
enum SCIC_SDS_APC_ACTIVITY apc_activity = SCIC_SDS_APC_SKIP_PHY ;
2011-07-01 06:14:33 +04:00
iport = sci_port_configuration_agent_find_port ( ihost , iphy ) ;
2011-07-03 09:56:22 +04:00
2011-06-30 00:09:25 +04:00
if ( iport ) {
2011-07-01 06:14:33 +04:00
if ( sci_port_is_valid_phy_assignment ( iport , iphy - > phy_index ) )
2011-07-03 09:56:22 +04:00
apc_activity = SCIC_SDS_APC_ADD_PHY ;
else
apc_activity = SCIC_SDS_APC_SKIP_PHY ;
} else {
/*
* There is no matching Port for this PHY so lets search through the
* Ports and see if we can add the PHY to its own port or maybe start
* the timer and wait to see if a wider port can be made .
*
* Note the break when we reach the condition of the port id = = phy id */
2011-06-30 00:09:25 +04:00
for ( port_index = port_agent - > phy_valid_port_range [ iphy - > phy_index ] . min_index ;
port_index < = port_agent - > phy_valid_port_range [ iphy - > phy_index ] . max_index ;
port_index + + ) {
2011-07-03 09:56:22 +04:00
2011-06-30 00:09:25 +04:00
iport = & ihost - > ports [ port_index ] ;
2011-07-03 09:56:22 +04:00
/* First we must make sure that this PHY can be added to this Port. */
2011-07-01 06:14:33 +04:00
if ( sci_port_is_valid_phy_assignment ( iport , iphy - > phy_index ) ) {
2011-07-03 09:56:22 +04:00
/*
* Port contains a PHY with a greater PHY ID than the current
* PHY that has gone link up . This phy can not be part of any
* port so skip it and move on . */
2011-06-30 00:09:25 +04:00
if ( iport - > active_phy_mask > ( 1 < < iphy - > phy_index ) ) {
2011-07-03 09:56:22 +04:00
apc_activity = SCIC_SDS_APC_SKIP_PHY ;
break ;
}
/*
* We have reached the end of our Port list and have not found
* any reason why we should not either add the PHY to the port
* or wait for more phys to become active . */
2011-06-30 00:09:25 +04:00
if ( iport - > physical_port_index = = iphy - > phy_index ) {
2011-07-03 09:56:22 +04:00
/*
* The Port either has no active PHYs .
* Consider that if the port had any active PHYs we would have
* or active PHYs with
* a lower PHY Id than this PHY . */
if ( apc_activity ! = SCIC_SDS_APC_START_TIMER ) {
apc_activity = SCIC_SDS_APC_ADD_PHY ;
}
break ;
}
/*
* The current Port has no active PHYs and this PHY could be part
* of this Port . Since we dont know as yet setup to start the
* timer and see if there is a better configuration . */
2011-06-30 00:09:25 +04:00
if ( iport - > active_phy_mask = = 0 ) {
2011-07-03 09:56:22 +04:00
apc_activity = SCIC_SDS_APC_START_TIMER ;
}
2011-06-30 00:09:25 +04:00
} else if ( iport - > active_phy_mask ! = 0 ) {
2011-07-03 09:56:22 +04:00
/*
* The Port has an active phy and the current Phy can not
* participate in this port so skip the PHY and see if
* there is a better configuration . */
apc_activity = SCIC_SDS_APC_SKIP_PHY ;
}
}
}
/*
* Check to see if the start timer operations should instead map to an
* add phy operation . This is caused because we have been waiting to
* add a phy to a port but could not becuase the automatic port
* configuration engine had a choice of possible ports for the phy .
* Since we have gone through a timeout we are going to restrict the
* choice to the smallest possible port . */
if (
( start_timer = = false )
& & ( apc_activity = = SCIC_SDS_APC_START_TIMER )
) {
apc_activity = SCIC_SDS_APC_ADD_PHY ;
}
switch ( apc_activity ) {
case SCIC_SDS_APC_ADD_PHY :
2011-07-01 06:14:33 +04:00
status = sci_port_add_phy ( iport , iphy ) ;
2011-07-03 09:56:22 +04:00
if ( status = = SCI_SUCCESS ) {
2011-06-29 02:05:53 +04:00
port_agent - > phy_configured_mask | = ( 1 < < iphy - > phy_index ) ;
2011-07-03 09:56:22 +04:00
}
break ;
case SCIC_SDS_APC_START_TIMER :
2012-01-04 13:33:31 +04:00
sci_apc_agent_start_timer ( port_agent ,
SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION ) ;
2011-07-03 09:56:22 +04:00
break ;
case SCIC_SDS_APC_SKIP_PHY :
default :
/* do nothing the PHY can not be made part of a port at this time. */
break ;
}
}
/**
2011-07-01 06:14:33 +04:00
* sci_apc_agent_link_up - handle apc link up events
2011-02-23 11:09:14 +03:00
* @ scic : This is the controller object that receives the link up
2011-07-03 09:56:22 +04:00
* notification .
2011-02-23 11:09:14 +03:00
* @ sci_port : This is the port object associated with the phy . If the is no
2011-02-08 19:28:42 +03:00
* associated port this is an NULL .
2011-02-23 11:09:14 +03:00
* @ sci_phy : This is the phy object which has gone link up .
2011-07-03 09:56:22 +04:00
*
* This method handles the automatic port configuration for link up
* notifications . Is it possible to get a link down notification from a phy
* that has no assocoated port ?
*/
2011-07-01 06:14:33 +04:00
static void sci_apc_agent_link_up ( struct isci_host * ihost ,
struct sci_port_configuration_agent * port_agent ,
2011-06-30 00:09:25 +04:00
struct isci_port * iport ,
2011-06-29 02:05:53 +04:00
struct isci_phy * iphy )
2011-07-03 09:56:22 +04:00
{
2011-06-29 02:05:53 +04:00
u8 phy_index = iphy - > phy_index ;
2011-07-03 09:56:22 +04:00
2011-06-30 00:09:25 +04:00
if ( ! iport ) {
2011-02-23 11:09:14 +03:00
/* the phy is not the part of this port */
port_agent - > phy_ready_mask | = 1 < < phy_index ;
2012-01-04 13:33:31 +04:00
sci_apc_agent_start_timer ( port_agent ,
SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION ) ;
2011-02-23 11:09:14 +03:00
} else {
/* the phy is already the part of the port */
port_agent - > phy_ready_mask | = 1 < < phy_index ;
2011-07-01 06:14:33 +04:00
sci_port_link_up ( iport , iphy ) ;
2011-02-23 11:09:14 +03:00
}
2011-07-03 09:56:22 +04:00
}
/**
*
* @ controller : This is the controller object that receives the link down
* notification .
2011-06-30 00:09:25 +04:00
* @ iport : This is the port object associated with the phy . If the is no
2011-02-08 19:28:42 +03:00
* associated port this is an NULL .
2011-06-30 00:09:25 +04:00
* @ iphy : This is the phy object which has gone link down .
2011-07-03 09:56:22 +04:00
*
* This method handles the automatic port configuration link down
* notifications . not associated with a port there is no action taken . Is it
* possible to get a link down notification from a phy that has no assocoated
* port ?
*/
2011-07-01 06:14:33 +04:00
static void sci_apc_agent_link_down (
2011-07-01 04:38:32 +04:00
struct isci_host * ihost ,
2011-07-01 06:14:33 +04:00
struct sci_port_configuration_agent * port_agent ,
2011-06-30 00:09:25 +04:00
struct isci_port * iport ,
2011-06-29 02:05:53 +04:00
struct isci_phy * iphy )
2011-07-03 09:56:22 +04:00
{
2011-07-01 13:25:15 +04:00
port_agent - > phy_ready_mask & = ~ ( 1 < < iphy - > phy_index ) ;
2011-07-03 09:56:22 +04:00
2011-06-30 00:09:25 +04:00
if ( ! iport )
return ;
if ( port_agent - > phy_configured_mask & ( 1 < < iphy - > phy_index ) ) {
enum sci_status status ;
2011-07-03 09:56:22 +04:00
2011-07-01 06:14:33 +04:00
status = sci_port_remove_phy ( iport , iphy ) ;
2011-07-03 09:56:22 +04:00
2011-06-30 00:09:25 +04:00
if ( status = = SCI_SUCCESS )
port_agent - > phy_configured_mask & = ~ ( 1 < < iphy - > phy_index ) ;
2011-07-03 09:56:22 +04:00
}
}
2011-05-07 04:36:38 +04:00
/* configure the phys into ports when the timer fires */
2017-09-02 09:21:24 +03:00
static void apc_agent_timeout ( struct timer_list * t )
2011-07-03 09:56:22 +04:00
{
u32 index ;
2017-09-02 09:21:24 +03:00
struct sci_timer * tmr = from_timer ( tmr , t , timer ) ;
2011-07-01 06:14:33 +04:00
struct sci_port_configuration_agent * port_agent ;
2011-05-20 07:00:51 +04:00
struct isci_host * ihost ;
unsigned long flags ;
2011-07-03 09:56:22 +04:00
u16 configure_phy_mask ;
2011-05-20 07:00:51 +04:00
port_agent = container_of ( tmr , typeof ( * port_agent ) , timer ) ;
2011-07-01 04:38:32 +04:00
ihost = container_of ( port_agent , typeof ( * ihost ) , port_agent ) ;
2011-05-20 07:00:51 +04:00
spin_lock_irqsave ( & ihost - > scic_lock , flags ) ;
if ( tmr - > cancel )
goto done ;
2011-07-03 09:56:22 +04:00
port_agent - > timer_pending = false ;
configure_phy_mask = ~ port_agent - > phy_configured_mask & port_agent - > phy_ready_mask ;
2011-05-07 04:36:38 +04:00
if ( ! configure_phy_mask )
2011-09-29 05:35:32 +04:00
goto done ;
2011-05-07 04:36:38 +04:00
for ( index = 0 ; index < SCI_MAX_PHYS ; index + + ) {
if ( ( configure_phy_mask & ( 1 < < index ) ) = = 0 )
continue ;
2011-07-01 06:14:33 +04:00
sci_apc_agent_configure_ports ( ihost , port_agent ,
2011-06-29 02:05:53 +04:00
& ihost - > phys [ index ] , false ) ;
2011-07-03 09:56:22 +04:00
}
2011-05-20 07:00:51 +04:00
2012-02-29 13:07:56 +04:00
if ( is_controller_start_complete ( ihost ) )
sci_controller_transition_to_ready ( ihost , SCI_SUCCESS ) ;
2011-05-20 07:00:51 +04:00
done :
spin_unlock_irqrestore ( & ihost - > scic_lock , flags ) ;
2011-07-03 09:56:22 +04:00
}
/*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Public port configuration agent routines
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/**
*
*
* This method will construct the port configuration agent for operation . This
* call is universal for both manual port configuration and automatic port
* configuration modes .
*/
2011-07-01 06:14:33 +04:00
void sci_port_configuration_agent_construct (
struct sci_port_configuration_agent * port_agent )
2011-07-03 09:56:22 +04:00
{
u32 index ;
port_agent - > phy_configured_mask = 0x00 ;
port_agent - > phy_ready_mask = 0x00 ;
port_agent - > link_up_handler = NULL ;
port_agent - > link_down_handler = NULL ;
port_agent - > timer_pending = false ;
for ( index = 0 ; index < SCI_MAX_PORTS ; index + + ) {
port_agent - > phy_valid_port_range [ index ] . min_index = 0 ;
port_agent - > phy_valid_port_range [ index ] . max_index = 0 ;
}
}
2012-02-29 13:07:56 +04:00
bool is_port_config_apc ( struct isci_host * ihost )
{
return ihost - > port_agent . link_up_handler = = sci_apc_agent_link_up ;
}
2011-07-01 06:14:33 +04:00
enum sci_status sci_port_configuration_agent_initialize (
2011-07-01 04:38:32 +04:00
struct isci_host * ihost ,
2011-07-01 06:14:33 +04:00
struct sci_port_configuration_agent * port_agent )
2011-07-03 09:56:22 +04:00
{
2011-05-20 07:00:51 +04:00
enum sci_status status ;
2011-07-01 06:14:33 +04:00
enum sci_port_configuration_mode mode ;
2011-07-03 09:56:22 +04:00
2011-07-01 06:14:33 +04:00
mode = ihost - > oem_parameters . controller . mode_type ;
2011-07-03 09:56:22 +04:00
if ( mode = = SCIC_PORT_MANUAL_CONFIGURATION_MODE ) {
2011-07-01 06:14:33 +04:00
status = sci_mpc_agent_validate_phy_configuration (
2011-07-01 04:38:32 +04:00
ihost , port_agent ) ;
2011-07-03 09:56:22 +04:00
2011-07-01 06:14:33 +04:00
port_agent - > link_up_handler = sci_mpc_agent_link_up ;
port_agent - > link_down_handler = sci_mpc_agent_link_down ;
2011-07-03 09:56:22 +04:00
2011-05-20 07:00:51 +04:00
sci_init_timer ( & port_agent - > timer , mpc_agent_timeout ) ;
2011-07-03 09:56:22 +04:00
} else {
2011-07-01 06:14:33 +04:00
status = sci_apc_agent_validate_phy_configuration (
2011-07-01 04:38:32 +04:00
ihost , port_agent ) ;
2011-07-03 09:56:22 +04:00
2011-07-01 06:14:33 +04:00
port_agent - > link_up_handler = sci_apc_agent_link_up ;
port_agent - > link_down_handler = sci_apc_agent_link_down ;
2011-07-03 09:56:22 +04:00
2011-05-20 07:00:51 +04:00
sci_init_timer ( & port_agent - > timer , apc_agent_timeout ) ;
2011-07-03 09:56:22 +04:00
}
return status ;
}