diff --git a/sys/dev/isci/scil/scic_sds_controller.c b/sys/dev/isci/scil/scic_sds_controller.c index 2532d87a37db..1cb0d0301dec 100644 --- a/sys/dev/isci/scil/scic_sds_controller.c +++ b/sys/dev/isci/scil/scic_sds_controller.c @@ -1,7044 +1,7044 @@ /*- * SPDX-License-Identifier: BSD-2-Clause OR GPL-2.0 * * 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. * * 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 __FBSDID("$FreeBSD$"); /** * @file * * @brief This file contains the implementation of the SCIC_SDS_CONTROLLER * public, protected, and private methods. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SCU_CONTEXT_RAM_INIT_STALL_TIME 200 #define SCIC_SDS_CONTROLLER_MIN_TIMER_COUNT 3 #define SCIC_SDS_CONTROLLER_MAX_TIMER_COUNT 3 #define SCU_MAX_ZPT_DWORD_INDEX 131 /** * The number of milliseconds to wait for a phy to start. */ #define SCIC_SDS_CONTROLLER_PHY_START_TIMEOUT 100 /** * The number of milliseconds to wait while a given phy is consuming * power before allowing another set of phys to consume power. * Ultimately, this will be specified by OEM parameter. */ #define SCIC_SDS_CONTROLLER_POWER_CONTROL_INTERVAL 500 /** * This macro will return the cycle bit of the completion queue entry */ #define COMPLETION_QUEUE_CYCLE_BIT(x) ((x) & 0x80000000) /** * This macro will normalize the completion queue get pointer so its value * can be used as an index into an array */ #define NORMALIZE_GET_POINTER(x) \ ((x) & SMU_COMPLETION_QUEUE_GET_POINTER_MASK) /** * This macro will normalize the completion queue put pointer so its value * can be used as an array inde */ #define NORMALIZE_PUT_POINTER(x) \ ((x) & SMU_COMPLETION_QUEUE_PUT_POINTER_MASK) /** * This macro will normalize the completion queue cycle pointer so it * matches the completion queue cycle bit */ #define NORMALIZE_GET_POINTER_CYCLE_BIT(x) \ (((U32)(SMU_CQGR_CYCLE_BIT & (x))) << (31 - SMU_COMPLETION_QUEUE_GET_CYCLE_BIT_SHIFT)) /** * This macro will normalize the completion queue event entry so its value * can be used as an index. */ #define NORMALIZE_EVENT_POINTER(x) \ ( \ ((U32)((x) & SMU_COMPLETION_QUEUE_GET_EVENT_POINTER_MASK)) \ >> SMU_COMPLETION_QUEUE_GET_EVENT_POINTER_SHIFT \ ) /** * This macro will increment the controllers completion queue index value * and possibly toggle the cycle bit if the completion queue index wraps * back to 0. */ #define INCREMENT_COMPLETION_QUEUE_GET(controller, index, cycle) \ INCREMENT_QUEUE_GET( \ (index), \ (cycle), \ (controller)->completion_queue_entries, \ SMU_CQGR_CYCLE_BIT \ ) /** * This macro will increment the controllers event queue index value and * possibly toggle the event cycle bit if the event queue index wraps back * to 0. */ #define INCREMENT_EVENT_QUEUE_GET(controller, index, cycle) \ INCREMENT_QUEUE_GET( \ (index), \ (cycle), \ (controller)->completion_event_entries, \ SMU_CQGR_EVENT_CYCLE_BIT \ ) //****************************************************************************- //* SCIC SDS Controller Initialization Methods //****************************************************************************- /** * @brief This timer is used to start another phy after we have given up on * the previous phy to transition to the ready state. * * @param[in] controller */ static void scic_sds_controller_phy_startup_timeout_handler( void *controller ) { SCI_STATUS status; SCIC_SDS_CONTROLLER_T *this_controller; this_controller = (SCIC_SDS_CONTROLLER_T *)controller; this_controller->phy_startup_timer_pending = FALSE; status = SCI_FAILURE; while (status != SCI_SUCCESS) { status = scic_sds_controller_start_next_phy(this_controller); } } /** * This method initializes the phy startup operations for controller start. * * @param this_controller */ static SCI_STATUS scic_sds_controller_initialize_phy_startup( SCIC_SDS_CONTROLLER_T *this_controller ) { this_controller->phy_startup_timer = scic_cb_timer_create( this_controller, scic_sds_controller_phy_startup_timeout_handler, this_controller ); if (this_controller->phy_startup_timer == NULL) { return SCI_FAILURE_INSUFFICIENT_RESOURCES; } else { this_controller->next_phy_to_start = 0; this_controller->phy_startup_timer_pending = FALSE; } return SCI_SUCCESS; } /** * This method initializes the power control operations for the controller * object. * * @param this_controller */ void scic_sds_controller_initialize_power_control( SCIC_SDS_CONTROLLER_T *this_controller ) { this_controller->power_control.timer = scic_cb_timer_create( this_controller, scic_sds_controller_power_control_timer_handler, this_controller ); memset( this_controller->power_control.requesters, 0, sizeof(this_controller->power_control.requesters) ); this_controller->power_control.phys_waiting = 0; this_controller->power_control.remote_devices_granted_power = 0; } // --------------------------------------------------------------------------- #define SCU_REMOTE_NODE_CONTEXT_ALIGNMENT (32) #define SCU_TASK_CONTEXT_ALIGNMENT (256) #define SCU_UNSOLICITED_FRAME_ADDRESS_ALIGNMENT (64) #define SCU_UNSOLICITED_FRAME_BUFFER_ALIGNMENT (1024) #define SCU_UNSOLICITED_FRAME_HEADER_ALIGNMENT (64) // --------------------------------------------------------------------------- /** * @brief This method builds the memory descriptor table for this * controller. * * @param[in] this_controller This parameter specifies the controller * object for which to build the memory table. * * @return none */ void scic_sds_controller_build_memory_descriptor_table( SCIC_SDS_CONTROLLER_T *this_controller ) { sci_base_mde_construct( &this_controller->memory_descriptors[SCU_MDE_COMPLETION_QUEUE], SCU_COMPLETION_RAM_ALIGNMENT, (sizeof(U32) * this_controller->completion_queue_entries), (SCI_MDE_ATTRIBUTE_CACHEABLE | SCI_MDE_ATTRIBUTE_PHYSICALLY_CONTIGUOUS) ); sci_base_mde_construct( &this_controller->memory_descriptors[SCU_MDE_REMOTE_NODE_CONTEXT], SCU_REMOTE_NODE_CONTEXT_ALIGNMENT, this_controller->remote_node_entries * sizeof(SCU_REMOTE_NODE_CONTEXT_T), SCI_MDE_ATTRIBUTE_PHYSICALLY_CONTIGUOUS ); sci_base_mde_construct( &this_controller->memory_descriptors[SCU_MDE_TASK_CONTEXT], SCU_TASK_CONTEXT_ALIGNMENT, this_controller->task_context_entries * sizeof(SCU_TASK_CONTEXT_T), SCI_MDE_ATTRIBUTE_PHYSICALLY_CONTIGUOUS ); // The UF buffer address table size must be programmed to a power // of 2. Find the first power of 2 that is equal to or greater then // the number of unsolicited frame buffers to be utilized. scic_sds_unsolicited_frame_control_set_address_table_count( &this_controller->uf_control ); sci_base_mde_construct( &this_controller->memory_descriptors[SCU_MDE_UF_BUFFER], SCU_UNSOLICITED_FRAME_BUFFER_ALIGNMENT, scic_sds_unsolicited_frame_control_get_mde_size(this_controller->uf_control), SCI_MDE_ATTRIBUTE_PHYSICALLY_CONTIGUOUS ); } /** * @brief This method validates the driver supplied memory descriptor * table. * * @param[in] this_controller * * @return SCI_STATUS */ SCI_STATUS scic_sds_controller_validate_memory_descriptor_table( SCIC_SDS_CONTROLLER_T *this_controller ) { BOOL mde_list_valid; mde_list_valid = sci_base_mde_is_valid( &this_controller->memory_descriptors[SCU_MDE_COMPLETION_QUEUE], SCU_COMPLETION_RAM_ALIGNMENT, (sizeof(U32) * this_controller->completion_queue_entries), (SCI_MDE_ATTRIBUTE_CACHEABLE | SCI_MDE_ATTRIBUTE_PHYSICALLY_CONTIGUOUS) ); if (mde_list_valid == FALSE) return SCI_FAILURE_UNSUPPORTED_INFORMATION_FIELD; mde_list_valid = sci_base_mde_is_valid( &this_controller->memory_descriptors[SCU_MDE_REMOTE_NODE_CONTEXT], SCU_REMOTE_NODE_CONTEXT_ALIGNMENT, this_controller->remote_node_entries * sizeof(SCU_REMOTE_NODE_CONTEXT_T), SCI_MDE_ATTRIBUTE_PHYSICALLY_CONTIGUOUS ); if (mde_list_valid == FALSE) return SCI_FAILURE_UNSUPPORTED_INFORMATION_FIELD; mde_list_valid = sci_base_mde_is_valid( &this_controller->memory_descriptors[SCU_MDE_TASK_CONTEXT], SCU_TASK_CONTEXT_ALIGNMENT, this_controller->task_context_entries * sizeof(SCU_TASK_CONTEXT_T), SCI_MDE_ATTRIBUTE_PHYSICALLY_CONTIGUOUS ); if (mde_list_valid == FALSE) return SCI_FAILURE_UNSUPPORTED_INFORMATION_FIELD; mde_list_valid = sci_base_mde_is_valid( &this_controller->memory_descriptors[SCU_MDE_UF_BUFFER], SCU_UNSOLICITED_FRAME_BUFFER_ALIGNMENT, scic_sds_unsolicited_frame_control_get_mde_size(this_controller->uf_control), SCI_MDE_ATTRIBUTE_PHYSICALLY_CONTIGUOUS ); if (mde_list_valid == FALSE) return SCI_FAILURE_UNSUPPORTED_INFORMATION_FIELD; return SCI_SUCCESS; } /** * @brief This method initializes the controller with the physical memory * addresses that are used to communicate with the driver. * * @param[in] this_controller * * @return none */ void scic_sds_controller_ram_initialization( SCIC_SDS_CONTROLLER_T *this_controller ) { SCI_PHYSICAL_MEMORY_DESCRIPTOR_T *mde; // The completion queue is actually placed in cacheable memory // Therefore it no longer comes out of memory in the MDL. mde = &this_controller->memory_descriptors[SCU_MDE_COMPLETION_QUEUE]; this_controller->completion_queue = (U32*) mde->virtual_address; SMU_CQBAR_WRITE(this_controller, mde->physical_address); // Program the location of the Remote Node Context table // into the SCU. mde = &this_controller->memory_descriptors[SCU_MDE_REMOTE_NODE_CONTEXT]; this_controller->remote_node_context_table = (SCU_REMOTE_NODE_CONTEXT_T *) mde->virtual_address; SMU_RNCBAR_WRITE(this_controller, mde->physical_address); // Program the location of the Task Context table into the SCU. mde = &this_controller->memory_descriptors[SCU_MDE_TASK_CONTEXT]; this_controller->task_context_table = (SCU_TASK_CONTEXT_T *) mde->virtual_address; SMU_HTTBAR_WRITE(this_controller, mde->physical_address); mde = &this_controller->memory_descriptors[SCU_MDE_UF_BUFFER]; scic_sds_unsolicited_frame_control_construct( &this_controller->uf_control, mde, this_controller ); // Inform the silicon as to the location of the UF headers and // address table. SCU_UFHBAR_WRITE( this_controller, this_controller->uf_control.headers.physical_address); SCU_PUFATHAR_WRITE( this_controller, this_controller->uf_control.address_table.physical_address); //enable the ECC correction and detection. SCU_SECR0_WRITE( this_controller, (SIGNLE_BIT_ERROR_CORRECTION_ENABLE | MULTI_BIT_ERROR_REPORTING_ENABLE | SINGLE_BIT_ERROR_REPORTING_ENABLE) ); SCU_SECR1_WRITE( this_controller, (SIGNLE_BIT_ERROR_CORRECTION_ENABLE | MULTI_BIT_ERROR_REPORTING_ENABLE | SINGLE_BIT_ERROR_REPORTING_ENABLE) ); } /** * @brief This method initializes the task context data for the controller. * * @param[in] this_controller * * @return none */ void scic_sds_controller_assign_task_entries( SCIC_SDS_CONTROLLER_T *this_controller ) { U32 task_assignment; // Assign all the TCs to function 0 // TODO: Do we actually need to read this register to write it back? task_assignment = SMU_TCA_READ(this_controller, 0); task_assignment = ( task_assignment | (SMU_TCA_GEN_VAL(STARTING, 0)) | (SMU_TCA_GEN_VAL(ENDING, this_controller->task_context_entries - 1)) | (SMU_TCA_GEN_BIT(RANGE_CHECK_ENABLE)) ); SMU_TCA_WRITE(this_controller, 0, task_assignment); } /** * @brief This method initializes the hardware completion queue. * * @param[in] this_controller */ void scic_sds_controller_initialize_completion_queue( SCIC_SDS_CONTROLLER_T *this_controller ) { U32 index; U32 completion_queue_control_value; U32 completion_queue_get_value; U32 completion_queue_put_value; this_controller->completion_queue_get = 0; completion_queue_control_value = ( SMU_CQC_QUEUE_LIMIT_SET(this_controller->completion_queue_entries - 1) | SMU_CQC_EVENT_LIMIT_SET(this_controller->completion_event_entries - 1) ); SMU_CQC_WRITE(this_controller, completion_queue_control_value); // Set the completion queue get pointer and enable the queue completion_queue_get_value = ( (SMU_CQGR_GEN_VAL(POINTER, 0)) | (SMU_CQGR_GEN_VAL(EVENT_POINTER, 0)) | (SMU_CQGR_GEN_BIT(ENABLE)) | (SMU_CQGR_GEN_BIT(EVENT_ENABLE)) ); SMU_CQGR_WRITE(this_controller, completion_queue_get_value); this_controller->completion_queue_get = completion_queue_get_value; // Set the completion queue put pointer completion_queue_put_value = ( (SMU_CQPR_GEN_VAL(POINTER, 0)) | (SMU_CQPR_GEN_VAL(EVENT_POINTER, 0)) ); SMU_CQPR_WRITE(this_controller, completion_queue_put_value); // Initialize the cycle bit of the completion queue entries for (index = 0; index < this_controller->completion_queue_entries; index++) { // If get.cycle_bit != completion_queue.cycle_bit // its not a valid completion queue entry // so at system start all entries are invalid this_controller->completion_queue[index] = 0x80000000; } } /** * @brief This method initializes the hardware unsolicited frame queue. * * @param[in] this_controller */ void scic_sds_controller_initialize_unsolicited_frame_queue( SCIC_SDS_CONTROLLER_T *this_controller ) { U32 frame_queue_control_value; U32 frame_queue_get_value; U32 frame_queue_put_value; // Write the queue size frame_queue_control_value = SCU_UFQC_GEN_VAL(QUEUE_SIZE, this_controller->uf_control.address_table.count); SCU_UFQC_WRITE(this_controller, frame_queue_control_value); // Setup the get pointer for the unsolicited frame queue frame_queue_get_value = ( SCU_UFQGP_GEN_VAL(POINTER, 0) | SCU_UFQGP_GEN_BIT(ENABLE_BIT) ); SCU_UFQGP_WRITE(this_controller, frame_queue_get_value); // Setup the put pointer for the unsolicited frame queue frame_queue_put_value = SCU_UFQPP_GEN_VAL(POINTER, 0); SCU_UFQPP_WRITE(this_controller, frame_queue_put_value); } /** * @brief This method enables the hardware port task scheduler. * * @param[in] this_controller */ void scic_sds_controller_enable_port_task_scheduler( SCIC_SDS_CONTROLLER_T *this_controller ) { U32 port_task_scheduler_value; port_task_scheduler_value = SCU_PTSGCR_READ(this_controller); port_task_scheduler_value |= (SCU_PTSGCR_GEN_BIT(ETM_ENABLE) | SCU_PTSGCR_GEN_BIT(PTSG_ENABLE)); SCU_PTSGCR_WRITE(this_controller, port_task_scheduler_value); } // --------------------------------------------------------------------------- #ifdef ARLINGTON_BUILD /** * This method will read from the lexington status register. This is required * as a read fence to the lexington register writes. * * @param this_controller */ void scic_sds_controller_lex_status_read_fence( SCIC_SDS_CONTROLLER_T *this_controller ) { U32 lex_status; // Read Fence lex_status = lex_register_read( this_controller, this_controller->lex_registers + 0xC4); SCIC_LOG_TRACE(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_CONTROLLER, "Controller 0x%x lex_status = 0x%08x\n", this_controller, lex_status )); } /** * This method will initialize the arlington through the LEX_BAR. * * @param this_controller */ void scic_sds_controller_lex_atux_initialization( SCIC_SDS_CONTROLLER_T *this_controller ) { // 1. Reset all SCU PHY lex_register_write( this_controller, this_controller->lex_registers + 0x28, 0x0020FFFF) ; // 2. Write to LEX_CTRL lex_register_write( this_controller, this_controller->lex_registers + 0xC0, 0x00000700); scic_sds_controller_lex_status_read_fence(this_controller); // 3. Enable PCI Master lex_register_write( this_controller, this_controller->lex_registers + 0x70, 0x00000002); // 4. Enable SCU Register Clock Domain lex_register_write( this_controller, this_controller->lex_registers + 0xC0, 0x00000300); scic_sds_controller_lex_status_read_fence(this_controller); // 5.1 Release PHY-A Reg Reset lex_register_write( this_controller, this_controller->lex_registers + 0x28, 0x0000FFFF); // 5.2 Initialize the AFE for PHY-A scic_sds_controller_afe_initialization(this_controller); scic_sds_controller_lex_status_read_fence(this_controller); #if 0 // 5.3 Release PHY Reg Reset lex_register_write( this_controller, this_controller->lex_registers + 0x28, 0x0000FFFF); #endif // 6.1 Release PHY-B Reg Reset lex_register_write( this_controller, this_controller->lex_registers + 0x28, 0x0040FFFF) ; // 6.2 Initialize the AFE for PHY-B scic_sds_controller_afe_initialization(this_controller); scic_sds_controller_lex_status_read_fence(this_controller); #if 0 // 6.3 Release PHY-B Reg Reset lex_register_write( this_controller, this_controller->lex_registers + 0x28, 0x0040FFFF) ; #endif // 7. Enable SCU clock domaion lex_register_write( this_controller, this_controller->lex_registers + 0xC0, 0x00000100); scic_sds_controller_lex_status_read_fence(this_controller); // 8. Release LEX SCU Reset lex_register_write( this_controller, this_controller->lex_registers + 0xC0, 0x00000000); scic_sds_controller_lex_status_read_fence(this_controller); #if !defined(DISABLE_INTERRUPTS) // 8a. Set legacy interrupts (SCU INTx to PCI-x INTA) lex_register_write( this_controller, this_controller->lex_registers + 0xC0, 0x00000800); scic_sds_controller_lex_status_read_fence(this_controller); #endif #if 0 // 9. Override TXOLVL //write to lex_ctrl lex_register_write( this_controller, this_controller->lex_registers + 0xC0, 0x27800000); #endif // 10. Release PHY-A & PHY-B Resets lex_register_write( this_controller, this_controller->lex_registers + 0x28, 0x0000FF77); lex_register_write( this_controller, this_controller->lex_registers + 0x28, 0x0000FF55); lex_register_write( this_controller, this_controller->lex_registers + 0x28, 0x0000FF11); lex_register_write( this_controller, this_controller->lex_registers + 0x28, 0x0000FF00); lex_register_write( this_controller, this_controller->lex_registers + 0x28, 0x0003FF00); } #endif // ARLINGTON_BUILD // --------------------------------------------------------------------------- #ifdef ARLINGTON_BUILD /** * This method enables chipwatch on the arlington board * * @param[in] this_controller */ void scic_sds_controller_enable_chipwatch( SCIC_SDS_CONTROLLER_T *this_controller ) { lex_register_write( this_controller, this_controller->lex_registers + 0x88, 0x09090909); lex_register_write( this_controller, this_controller->lex_registers + 0x8C, 0xcac9c862); } #endif /** * This macro is used to delay between writes to the AFE registers * during AFE initialization. */ #define AFE_REGISTER_WRITE_DELAY 10 /** * Initialize the AFE for this phy index. * * @todo We need to read the AFE setup from the OEM parameters * * @param[in] this_controller * * @return none */ #if defined(ARLINGTON_BUILD) void scic_sds_controller_afe_initialization( SCIC_SDS_CONTROLLER_T *this_controller ) { // 1. Establish Power // Hold Bias, PLL, and RX TX in reset and powerdown // pe_afe0_rst_n = 0 // pe_afe0_txpdn0,1,2,3 = 1 // pe_afe0_rxpdn0,1,2,3 = 1 // pe_afe0_txrst0,1,2,3_n = 0 // pe_afe0_rxrst0,1,2,3_n = 0 // wait 1us // pe_afe0_rst_n = 1 // wait 1us scu_afe_register_write( this_controller, afe_pll_control, 0x00247506); // 2. Write 0x00000000 to AFE XCVR Ctrl2 scu_afe_register_write( this_controller, afe_dfx_transceiver_status_clear, 0x00000000); // 3. afe0_override_en = 0 // afe0_pll_dis_override = 0 // afe0_tx_rst_override = 0 // afe0_pll_dis = 1 // pe_afe0_txrate = 01 // pe_afe0_rxrate = 01 // pe_afe0_txdis = 11 // pe_afe0_txoob = 1 // pe_afe0_txovlv = 9'b001110000 scu_afe_register_write( this_controller, afe_transceiver_control0[0], 0x0700141e); // 4. Configure PLL Unit // Write 0x00200506 to AFE PLL Ctrl Register 0 scu_afe_register_write(this_controller, afe_pll_control, 0x00200506); scu_afe_register_write(this_controller, afe_pll_dfx_control, 0x10000080); // 5. Configure Bias Unit scu_afe_register_write(this_controller, afe_bias_control[0], 0x00124814); scu_afe_register_write(this_controller, afe_bias_control[1], 0x24900000); // 6. Configure Transceiver Units scu_afe_register_write( this_controller, afe_transceiver_control0[0], 0x0702941e); scu_afe_register_write( this_controller, afe_transceiver_control1[0], 0x0000000a); // 7. Configure RX Units scu_afe_register_write( this_controller, afe_transceiver_equalization_control[0], 0x00ba2223); scu_afe_register_write( this_controller, reserved_0028_003c[2], 0x00000000); // 8. Configure TX Units scu_afe_register_write( this_controller, afe_dfx_transmit_control_register[0], 0x03815428); // 9. Transfer control to PE signals scu_afe_register_write( this_controller, afe_dfx_transceiver_status_clear, 0x00000010); // 10. Release PLL Powerdown scu_afe_register_write(this_controller, afe_pll_control, 0x00200504); // 11. Release PLL Reset scu_afe_register_write(this_controller, afe_pll_control, 0x00200505); // 12. Wait for PLL to Lock // (afe0_comm_sta [1:0] should go to 1'b11, and // [5:2] is 0x5, 0x6, 0x7, 0x8, or 0x9 scu_afe_register_write(this_controller, afe_pll_control, 0x00200501); while ((scu_afe_register_read(this_controller, afe_common_status) & 0x03) != 0x03) { // Give time for the PLLs to lock scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); } // 13. pe_afe0_rxpdn0 = 0 // pe_afe0_rxrst0 = 1 // pe_afe0_txrst0_n = 1 // pe_afe_txoob0_n = 0 scu_afe_register_write( this_controller, afe_transceiver_control0[0], 0x07028c11); } #elif defined(PLEASANT_RIDGE_BUILD) void scic_sds_controller_afe_initialization( SCIC_SDS_CONTROLLER_T *this_controller ) { U32 afe_status; U32 phy_id; #if defined(SPREADSHEET_AFE_SETTINGS) // Clear DFX Status registers scu_afe_register_write( this_controller, afe_dfx_master_control0, 0x0000000f); // Configure bias currents to normal scu_afe_register_write( this_controller, afe_bias_control, 0x0000aa00); // Enable PLL scu_afe_register_write( this_controller, afe_pll_control0, 0x80000908); // Wait for the PLL to lock do { scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); afe_status = scu_afe_register_read( this_controller, afe_common_block_status); } while((afe_status & 0x00001000) == 0); for (phy_id = 0; phy_id < SCI_MAX_PHYS; phy_id++) { // Initialize transceiver channels scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_channel_control, 0x00000157); // Configure transceiver modes scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_xcvr_control0, 0x38016d1a); // Configure receiver parameters scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_xcvr_control1, 0x01501014); // Configure transmitter parameters scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_tx_control, 0x00000000); // Configure transmitter equalization scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_tx_amp_control0, 0x000bdd08); scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_tx_amp_control1, 0x000ffc00); scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_tx_amp_control2, 0x000b7c09); scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_tx_amp_control3, 0x000afc6e); // Configure transmitter SSC parameters scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_tx_ssc_control, 0x00000000); // Configure receiver parameters scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_rx_ssc_control0, 0x3208903f); // Start power on sequence // Enable bias currents to transceivers and wait 200ns scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_channel_control, 0x00000154); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); // Take receiver out of power down and wait 200ns scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_xcvr_control0, 0x3801611a); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); // Take receiver out of reset and wait 200ns scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_xcvr_control0, 0x3801631a); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); // Take transmitter out of power down and wait 200ns scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_xcvr_control0, 0x38016318); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); // Take transmitter out of reset and wait 200ns scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_xcvr_control0, 0x38016319); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); // Take transmitter out of DC idle scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_xcvr_control0, 0x38016319); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); } // Transfer control to the PEs scu_afe_register_write( this_controller, afe_dfx_master_control0, 0x00010f00); #else // !defined(SPREADSHEET_AFE_SETTINGS) // These are the AFEE settings used by the SV group // Clear DFX Status registers scu_afe_register_write( this_controller, afe_dfx_master_control0, 0x0081000f); // Configure bias currents to normal scu_afe_register_write( this_controller, afe_bias_control, 0x0000aa00); // Enable PLL scu_afe_register_write( this_controller, afe_pll_control0, 0x80000908); // Wait for the PLL to lock // Note: this is done later in the SV shell script however this looks // like the location to do this since we have enabled the PLL. do { scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); afe_status = scu_afe_register_read( this_controller, afe_common_block_status); } while((afe_status & 0x00001000) == 0); // Make sure BIST is disabled scu_afe_register_write( this_controller, afe_dfx_master_control1, 0x00000000); // Shorten SAS SNW lock time scu_afe_register_write( this_controller, afe_pmsn_master_control0, 0x7bd316ad); for (phy_id = 0; phy_id < SCI_MAX_PHYS; phy_id++) { // Initialize transceiver channels scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_channel_control, 0x00000174); // Configure SSC control scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_tx_ssc_control, 0x00030000); // Configure transceiver modes scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_xcvr_control0, 0x0000651a); // Power up TX RX and RX OOB scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_xcvr_control0, 0x00006518); // Enable RX OOB Detect scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_xcvr_control0, 0x00006518); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); #if 0 // Configure transmitter parameters scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_tx_control, 0x00000000); // Configure transmitter equalization scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_tx_amp_control0, 0x000bdd08); scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_tx_amp_control1, 0x000ffc00); scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_tx_amp_control2, 0x000b7c09); scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_tx_amp_control3, 0x000afc6e); // Configure transmitter SSC parameters // Power up TX RX scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_channel_control, 0x00000154); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); // FFE = Max scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_dfx_rx_control1, 0x00000080); // DFE1-5 = small scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_dfx_rx_control1, 0x01041042); // Enable DFE/FFE and freeze scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_rx_ssc_control0, 0x320891bf); #endif // Take receiver out of power down and wait 200ns scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_xcvr_control0, 0x00006118); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); // TX Electrical Idle scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_xcvr_control0, 0x00006108); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); // Leave DFE/FFE on scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_rx_ssc_control0, 0x0317108f); // Configure receiver parameters scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_xcvr_control1, 0x01e00021); // Bring RX out of reset scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_xcvr_control0, 0x00006109); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_xcvr_control0, 0x00006009); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_xcvr_control0, 0x00006209); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); } // Transfer control to the PEs scu_afe_register_write( this_controller, afe_dfx_master_control0, 0x00010f00); #endif } #elif defined(PBG_HBA_A0_BUILD) || defined(PBG_HBA_A2_BUILD) || defined(PBG_HBA_BETA_BUILD) || defined(PBG_BUILD) void scic_sds_controller_afe_initialization( SCIC_SDS_CONTROLLER_T *this_controller ) { U32 afe_status; U32 phy_id; U8 cable_selection_mask; if ( (this_controller->pci_revision != SCIC_SDS_PCI_REVISION_A0) && (this_controller->pci_revision != SCIC_SDS_PCI_REVISION_A2) && (this_controller->pci_revision != SCIC_SDS_PCI_REVISION_B0) && (this_controller->pci_revision != SCIC_SDS_PCI_REVISION_C0) && (this_controller->pci_revision != SCIC_SDS_PCI_REVISION_C1) ) { // A programming bug has occurred if we are attempting to // support a PCI revision other than those listed. Default // to B0, and attempt to limp along if it isn't B0. ASSERT(FALSE); this_controller->pci_revision = SCIC_SDS_PCI_REVISION_C1; } cable_selection_mask = this_controller->oem_parameters.sds1.controller.cable_selection_mask; // These are the AFEE settings used by the SV group // Clear DFX Status registers scu_afe_register_write( this_controller, afe_dfx_master_control0, 0x0081000f); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); if ( (this_controller->pci_revision == SCIC_SDS_PCI_REVISION_B0) || (this_controller->pci_revision == SCIC_SDS_PCI_REVISION_C0) || (this_controller->pci_revision == SCIC_SDS_PCI_REVISION_C1) ) { // PM Rx Equalization Save, PM SPhy Rx Acknowledgement Timer, PM Stagger Timer scu_afe_register_write( this_controller, afe_pmsn_master_control2, 0x0007FFFF); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); } // Configure bias currents to normal if (this_controller->pci_revision == SCIC_SDS_PCI_REVISION_A0) scu_afe_register_write(this_controller, afe_bias_control, 0x00005500); else if (this_controller->pci_revision == SCIC_SDS_PCI_REVISION_A2) scu_afe_register_write(this_controller, afe_bias_control, 0x00005A00); else if ( (this_controller->pci_revision == SCIC_SDS_PCI_REVISION_B0) || (this_controller->pci_revision == SCIC_SDS_PCI_REVISION_C0) ) scu_afe_register_write(this_controller, afe_bias_control, 0x00005F00); else if (this_controller->pci_revision == SCIC_SDS_PCI_REVISION_C1) scu_afe_register_write(this_controller, afe_bias_control, 0x00005500); // For C0 the AFE BIAS Control is unchanged scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); // Enable PLL if ( (this_controller->pci_revision == SCIC_SDS_PCI_REVISION_A0) || (this_controller->pci_revision == SCIC_SDS_PCI_REVISION_A2) ) { scu_afe_register_write(this_controller, afe_pll_control0, 0x80040908); } else if ( (this_controller->pci_revision == SCIC_SDS_PCI_REVISION_B0) || (this_controller->pci_revision == SCIC_SDS_PCI_REVISION_C0) ) { scu_afe_register_write(this_controller, afe_pll_control0, 0x80040A08); } else if (this_controller->pci_revision == SCIC_SDS_PCI_REVISION_C1) { scu_afe_register_write(this_controller, afe_pll_control0, 0x80000b08); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); scu_afe_register_write(this_controller, afe_pll_control0, 0x00000b08); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); scu_afe_register_write(this_controller, afe_pll_control0, 0x80000b08); } scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); // Wait for the PLL to lock // Note: this is done later in the SV shell script however this looks // like the location to do this since we have enabled the PLL. do { afe_status = scu_afe_register_read( this_controller, afe_common_block_status); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); } while((afe_status & 0x00001000) == 0); if ( (this_controller->pci_revision == SCIC_SDS_PCI_REVISION_A0) || (this_controller->pci_revision == SCIC_SDS_PCI_REVISION_A2) ) { // Shorten SAS SNW lock time (RxLock timer value from 76 us to 50 us) scu_afe_register_write( this_controller, afe_pmsn_master_control0, 0x7bcc96ad); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); } for (phy_id = 0; phy_id < SCI_MAX_PHYS; phy_id++) { U8 cable_length_long = (cable_selection_mask >> phy_id) & 1; U8 cable_length_medium = (cable_selection_mask >> (phy_id + 4)) & 1; if ( (this_controller->pci_revision == SCIC_SDS_PCI_REVISION_A0) || (this_controller->pci_revision == SCIC_SDS_PCI_REVISION_A2) ) { // All defaults, except the Receive Word Alignament/Comma Detect // Enable....(0xe800) scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_xcvr_control0, 0x00004512 ); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_xcvr_control1, 0x0050100F ); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); } else if (this_controller->pci_revision == SCIC_SDS_PCI_REVISION_B0) { // Configure transmitter SSC parameters scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_tx_ssc_control, 0x00030000 ); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); } else if (this_controller->pci_revision == SCIC_SDS_PCI_REVISION_C0) { // Configure transmitter SSC parameters scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_tx_ssc_control, 0x00010202 ); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); // All defaults, except the Receive Word Alignament/Comma Detect // Enable....(0xe800) scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_xcvr_control0, 0x00014500 ); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); } else if (this_controller->pci_revision == SCIC_SDS_PCI_REVISION_C1) { // Configure transmitter SSC parameters scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_tx_ssc_control, 0x00010202 ); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); // All defaults, except the Receive Word Alignament/Comma Detect // Enable....(0xe800) scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_xcvr_control0, 0x0001C500 ); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); } // Power up TX and RX out from power down (PWRDNTX and PWRDNRX) // & increase TX int & ext bias 20%....(0xe85c) if (this_controller->pci_revision == SCIC_SDS_PCI_REVISION_A0) { scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_channel_control, 0x000003D4 ); } else if (this_controller->pci_revision == SCIC_SDS_PCI_REVISION_A2) { scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_channel_control, 0x000003F0 ); } else if (this_controller->pci_revision == SCIC_SDS_PCI_REVISION_B0) { // Power down TX and RX (PWRDNTX and PWRDNRX) scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_channel_control, 0x000003d7 ); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); // Power up TX and RX out from power down (PWRDNTX and PWRDNRX) // & increase TX int & ext bias 20%....(0xe85c) scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_channel_control, 0x000003d4 ); } else if (this_controller->pci_revision == SCIC_SDS_PCI_REVISION_C0) { scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_channel_control, 0x000001e7 ); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); // Power up TX and RX out from power down (PWRDNTX and PWRDNRX) // & increase TX int & ext bias 20%....(0xe85c) scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_channel_control, 0x000001e4 ); } else if (this_controller->pci_revision == SCIC_SDS_PCI_REVISION_C1) { scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_channel_control, cable_length_long ? 0x000002F7 : cable_length_medium ? 0x000001F7 : 0x000001F7 ); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); // Power up TX and RX out from power down (PWRDNTX and PWRDNRX) // & increase TX int & ext bias 20%....(0xe85c) scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_channel_control, cable_length_long ? 0x000002F4 : cable_length_medium ? 0x000001F4 : 0x000001F4 ); } scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); if ( (this_controller->pci_revision == SCIC_SDS_PCI_REVISION_A0) || (this_controller->pci_revision == SCIC_SDS_PCI_REVISION_A2) ) { // Enable TX equalization (0xe824) scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_tx_control, 0x00040000 ); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); } if ( (this_controller->pci_revision == SCIC_SDS_PCI_REVISION_A0) || (this_controller->pci_revision == SCIC_SDS_PCI_REVISION_A2) || (this_controller->pci_revision == SCIC_SDS_PCI_REVISION_B0) ) { // RDPI=0x0(RX Power On), RXOOBDETPDNC=0x0, TPD=0x0(TX Power On), // RDD=0x0(RX Detect Enabled) ....(0xe800) scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_xcvr_control0, 0x00004100); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); } else if (this_controller->pci_revision == SCIC_SDS_PCI_REVISION_C0) { scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_xcvr_control0, 0x00014100); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); } else if (this_controller->pci_revision == SCIC_SDS_PCI_REVISION_C1) { scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_xcvr_control0, 0x0001c100); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); } // Leave DFE/FFE on if (this_controller->pci_revision == SCIC_SDS_PCI_REVISION_A0) { scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_rx_ssc_control0, 0x3F09983F ); } else if (this_controller->pci_revision == SCIC_SDS_PCI_REVISION_A2) { scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_rx_ssc_control0, 0x3F11103F ); } else if (this_controller->pci_revision == SCIC_SDS_PCI_REVISION_B0) { scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_rx_ssc_control0, 0x3F11103F ); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); // Enable TX equalization (0xe824) scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_tx_control, 0x00040000); } else if (this_controller->pci_revision == SCIC_SDS_PCI_REVISION_C0) { scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_xcvr_control1, 0x01400c0f); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_rx_ssc_control0, 0x3f6f103f); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); // Enable TX equalization (0xe824) scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_tx_control, 0x00040000); } else if (this_controller->pci_revision == SCIC_SDS_PCI_REVISION_C1) { scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_xcvr_control1, cable_length_long ? 0x01500C0C : cable_length_medium ? 0x01400C0D : 0x02400C0D ); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_dfx_rx_control1, 0x000003e0); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_rx_ssc_control0, cable_length_long ? 0x33091C1F : cable_length_medium ? 0x3315181F : 0x2B17161F ); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); // Enable TX equalization (0xe824) scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_tx_control, 0x00040000); } scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_tx_amp_control0, this_controller->oem_parameters.sds1.phys[phy_id].afe_tx_amp_control0 ); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_tx_amp_control1, this_controller->oem_parameters.sds1.phys[phy_id].afe_tx_amp_control1 ); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_tx_amp_control2, this_controller->oem_parameters.sds1.phys[phy_id].afe_tx_amp_control2 ); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); scu_afe_register_write( this_controller, scu_afe_xcvr[phy_id].afe_tx_amp_control3, this_controller->oem_parameters.sds1.phys[phy_id].afe_tx_amp_control3 ); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); } // Transfer control to the PEs scu_afe_register_write( this_controller, afe_dfx_master_control0, 0x00010f00); scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); } #else #error "Unsupported board type" #endif //****************************************************************************- //* SCIC SDS Controller Internal Start/Stop Routines //****************************************************************************- /** * @brief This method will attempt to transition into the ready state * for the controller and indicate that the controller start * operation has completed if all criteria are met. * * @param[in,out] this_controller This parameter indicates the controller * object for which to transition to ready. * @param[in] status This parameter indicates the status value to be * pass into the call to scic_cb_controller_start_complete(). * * @return none. */ static void scic_sds_controller_transition_to_ready( SCIC_SDS_CONTROLLER_T *this_controller, SCI_STATUS status ) { SCIC_LOG_TRACE(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_CONTROLLER, "scic_sds_controller_transition_to_ready(0x%x, 0x%x) enter\n", this_controller, status )); if (this_controller->parent.state_machine.current_state_id == SCI_BASE_CONTROLLER_STATE_STARTING) { // We move into the ready state, because some of the phys/ports // may be up and operational. sci_base_state_machine_change_state( scic_sds_controller_get_base_state_machine(this_controller), SCI_BASE_CONTROLLER_STATE_READY ); scic_cb_controller_start_complete(this_controller, status); } } /** * @brief This method is the general timeout handler for the controller. * It will take the correct timetout action based on the current * controller state * * @param[in] controller This parameter indicates the controller on which * a timeout occurred. * * @return none */ void scic_sds_controller_timeout_handler( SCI_CONTROLLER_HANDLE_T controller ) { SCI_BASE_CONTROLLER_STATES current_state; SCIC_SDS_CONTROLLER_T *this_controller; this_controller = (SCIC_SDS_CONTROLLER_T *)controller; current_state = sci_base_state_machine_get_state( scic_sds_controller_get_base_state_machine(this_controller) ); if (current_state == SCI_BASE_CONTROLLER_STATE_STARTING) { scic_sds_controller_transition_to_ready( this_controller, SCI_FAILURE_TIMEOUT ); } else if (current_state == SCI_BASE_CONTROLLER_STATE_STOPPING) { sci_base_state_machine_change_state( scic_sds_controller_get_base_state_machine(this_controller), SCI_BASE_CONTROLLER_STATE_FAILED ); scic_cb_controller_stop_complete(controller, SCI_FAILURE_TIMEOUT); } else { /// @todo Now what do we want to do in this case? SCIC_LOG_ERROR(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_CONTROLLER, "Controller timer fired when controller was not in a state being timed.\n" )); } } /** * @brief * * @param[in] this_controller * * @return SCI_STATUS */ SCI_STATUS scic_sds_controller_stop_ports( SCIC_SDS_CONTROLLER_T *this_controller ) { U32 index; SCI_STATUS status; SCI_STATUS port_status; status = SCI_SUCCESS; for (index = 0; index < this_controller->logical_port_entries; index++) { port_status = this_controller->port_table[index]. state_handlers->parent.stop_handler(&this_controller->port_table[index].parent); if ( (port_status != SCI_SUCCESS) && (port_status != SCI_FAILURE_INVALID_STATE) ) { status = SCI_FAILURE; SCIC_LOG_WARNING(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT, "Controller stop operation failed to stop port %d because of status %d.\n", this_controller->port_table[index].logical_port_index, port_status )); } } return status; } /** * @brief * * @param[in] this_controller */ static void scic_sds_controller_phy_timer_start( SCIC_SDS_CONTROLLER_T *this_controller ) { scic_cb_timer_start( this_controller, this_controller->phy_startup_timer, SCIC_SDS_CONTROLLER_PHY_START_TIMEOUT ); this_controller->phy_startup_timer_pending = TRUE; } /** * @brief * * @param[in] this_controller */ void scic_sds_controller_phy_timer_stop( SCIC_SDS_CONTROLLER_T *this_controller ) { scic_cb_timer_stop( this_controller, this_controller->phy_startup_timer ); this_controller->phy_startup_timer_pending = FALSE; } /** * @brief This method is called internally to determine whether the * controller start process is complete. This is only true when: * - all links have been given an opportunity to start * - have no indication of a connected device * - have an indication of a connected device and it has * finished the link training process. * * @param[in] this_controller This parameter specifies the controller * object for which to start the next phy. * * @return BOOL */ BOOL scic_sds_controller_is_start_complete( SCIC_SDS_CONTROLLER_T *this_controller ) { U8 index; for (index = 0; index < SCI_MAX_PHYS; index++) { SCIC_SDS_PHY_T *the_phy = & this_controller->phy_table[index]; if ( ( this_controller->oem_parameters.sds1.controller.mode_type == SCIC_PORT_AUTOMATIC_CONFIGURATION_MODE ) || ( ( this_controller->oem_parameters.sds1.controller.mode_type == SCIC_PORT_MANUAL_CONFIGURATION_MODE ) && (scic_sds_phy_get_port(the_phy) != SCI_INVALID_HANDLE) ) ) { /** * The controller start operation is complete if and only * if: * - all links have been given an opportunity to start * - have no indication of a connected device * - have an indication of a connected device and it has * finished the link training process. */ if ( ( (the_phy->is_in_link_training == FALSE) && (the_phy->parent.state_machine.current_state_id == SCI_BASE_PHY_STATE_INITIAL) ) || ( (the_phy->is_in_link_training == FALSE) && (the_phy->parent.state_machine.current_state_id == SCI_BASE_PHY_STATE_STOPPED) ) || ( (the_phy->is_in_link_training == TRUE) && (the_phy->parent.state_machine.current_state_id == SCI_BASE_PHY_STATE_STARTING) ) || ( this_controller->port_agent.phy_ready_mask != this_controller->port_agent.phy_configured_mask ) ) { return FALSE; } } } return TRUE; } /** * @brief This method is called internally by the controller object to * start the next phy on the controller. If all the phys have * been starte, then this method will attempt to transition the * controller to the READY state and inform the user * (scic_cb_controller_start_complete()). * * @param[in] this_controller This parameter specifies the controller * object for which to start the next phy. * * @return SCI_STATUS */ SCI_STATUS scic_sds_controller_start_next_phy( SCIC_SDS_CONTROLLER_T *this_controller ) { SCI_STATUS status; status = SCI_SUCCESS; if (this_controller->phy_startup_timer_pending == FALSE) { if (this_controller->next_phy_to_start == SCI_MAX_PHYS) { // The controller has successfully finished the start process. // Inform the SCI Core user and transition to the READY state. if (scic_sds_controller_is_start_complete(this_controller) == TRUE) { scic_sds_controller_transition_to_ready( this_controller, SCI_SUCCESS ); } } else { SCIC_SDS_PHY_T * the_phy; the_phy = &this_controller->phy_table[this_controller->next_phy_to_start]; if ( this_controller->oem_parameters.sds1.controller.mode_type == SCIC_PORT_MANUAL_CONFIGURATION_MODE ) { if (scic_sds_phy_get_port(the_phy) == SCI_INVALID_HANDLE) { this_controller->next_phy_to_start++; // Caution recursion ahead be forwarned // // The PHY was never added to a PORT in MPC mode so start the next phy in sequence // This phy will never go link up and will not draw power the OEM parameters either // configured the phy incorrectly for the PORT or it was never assigned to a PORT return scic_sds_controller_start_next_phy(this_controller); } } status = scic_phy_start(the_phy); if (status == SCI_SUCCESS) { scic_sds_controller_phy_timer_start(this_controller); } else { SCIC_LOG_WARNING(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PHY, "Controller stop operation failed to stop phy %d because of status %d.\n", this_controller->phy_table[this_controller->next_phy_to_start].phy_index, status )); } this_controller->next_phy_to_start++; } } return status; } /** * @brief * * @param[in] this_controller * * @return SCI_STATUS */ SCI_STATUS scic_sds_controller_stop_phys( SCIC_SDS_CONTROLLER_T *this_controller ) { U32 index; SCI_STATUS status; SCI_STATUS phy_status; status = SCI_SUCCESS; for (index = 0; index < SCI_MAX_PHYS; index++) { phy_status = scic_phy_stop(&this_controller->phy_table[index]); if ( (phy_status != SCI_SUCCESS) && (phy_status != SCI_FAILURE_INVALID_STATE) ) { status = SCI_FAILURE; SCIC_LOG_WARNING(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PHY, "Controller stop operation failed to stop phy %d because of status %d.\n", this_controller->phy_table[index].phy_index, phy_status )); } } return status; } /** * @brief * * @param[in] this_controller * * @return SCI_STATUS */ SCI_STATUS scic_sds_controller_stop_devices( SCIC_SDS_CONTROLLER_T *this_controller ) { U32 index; SCI_STATUS status; SCI_STATUS device_status; status = SCI_SUCCESS; for (index = 0; index < this_controller->remote_node_entries; index++) { if (this_controller->device_table[index] != SCI_INVALID_HANDLE) { /// @todo What timeout value do we want to provide to this request? device_status = scic_remote_device_stop(this_controller->device_table[index], 0); if ( (device_status != SCI_SUCCESS) && (device_status != SCI_FAILURE_INVALID_STATE) ) { SCIC_LOG_WARNING(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_SSP_REMOTE_TARGET, "Controller stop operation failed to stop device 0x%x because of status %d.\n", this_controller->device_table[index], device_status )); } } } return status; } //****************************************************************************- //* SCIC SDS Controller Power Control (Staggered Spinup) //****************************************************************************- /** * This method starts the power control timer for this controller object. * * @param this_controller */ static void scic_sds_controller_power_control_timer_start( SCIC_SDS_CONTROLLER_T *this_controller ) { scic_cb_timer_start( this_controller, this_controller->power_control.timer, SCIC_SDS_CONTROLLER_POWER_CONTROL_INTERVAL ); this_controller->power_control.timer_started = TRUE; } /** * This method stops the power control timer for this controller object. * * @param this_controller */ static void scic_sds_controller_power_control_timer_stop( SCIC_SDS_CONTROLLER_T *this_controller ) { if (this_controller->power_control.timer_started) { scic_cb_timer_stop( this_controller, this_controller->power_control.timer ); this_controller->power_control.timer_started = FALSE; } } /** * This method stops and starts the power control timer for this controller object. * * @param this_controller */ static void scic_sds_controller_power_control_timer_restart( SCIC_SDS_CONTROLLER_T *this_controller ) { scic_sds_controller_power_control_timer_stop(this_controller); scic_sds_controller_power_control_timer_start(this_controller); } /** * @brief * * @param[in] controller */ void scic_sds_controller_power_control_timer_handler( void *controller ) { SCIC_SDS_CONTROLLER_T *this_controller; this_controller = (SCIC_SDS_CONTROLLER_T *)controller; this_controller->power_control.remote_devices_granted_power = 0; if (this_controller->power_control.phys_waiting == 0) { this_controller->power_control.timer_started = FALSE; } else { SCIC_SDS_PHY_T *the_phy = NULL; U8 i; for (i=0; (i < SCI_MAX_PHYS) && (this_controller->power_control.phys_waiting != 0); i++) { if (this_controller->power_control.requesters[i] != NULL) { if ( this_controller->power_control.remote_devices_granted_power < this_controller->oem_parameters.sds1.controller.max_number_concurrent_device_spin_up ) { the_phy = this_controller->power_control.requesters[i]; this_controller->power_control.requesters[i] = NULL; this_controller->power_control.phys_waiting--; this_controller->power_control.remote_devices_granted_power ++; scic_sds_phy_consume_power_handler(the_phy); if (the_phy->protocol == SCIC_SDS_PHY_PROTOCOL_SAS) { U8 j; SCIC_SDS_PHY_T * current_requester_phy; for (j = 0; j < SCI_MAX_PHYS; j++) { current_requester_phy = this_controller->power_control.requesters[j]; //Search the power_control queue to see if there are other phys attached to //the same remote device. If found, take all of them out of await_sas_power state. if (current_requester_phy != NULL && current_requester_phy != the_phy && current_requester_phy->phy_type.sas.identify_address_frame_buffer.sas_address.high == the_phy->phy_type.sas.identify_address_frame_buffer.sas_address.high && current_requester_phy->phy_type.sas.identify_address_frame_buffer.sas_address.low == the_phy->phy_type.sas.identify_address_frame_buffer.sas_address.low) { this_controller->power_control.requesters[j] = NULL; this_controller->power_control.phys_waiting--; scic_sds_phy_consume_power_handler(current_requester_phy); } } } } else { break; } } } // It doesn't matter if the power list is empty, we need to start the // timer in case another phy becomes ready. scic_sds_controller_power_control_timer_start(this_controller); } } /** * @brief This method inserts the phy in the stagger spinup control queue. * * @param[in] this_controller * @param[in] the_phy */ void scic_sds_controller_power_control_queue_insert( SCIC_SDS_CONTROLLER_T *this_controller, SCIC_SDS_PHY_T *the_phy ) { ASSERT (the_phy != NULL); if( this_controller->power_control.remote_devices_granted_power < this_controller->oem_parameters.sds1.controller.max_number_concurrent_device_spin_up ) { this_controller->power_control.remote_devices_granted_power ++; scic_sds_phy_consume_power_handler(the_phy); //stop and start the power_control timer. When the timer fires, the //no_of_devices_granted_power will be set to 0 scic_sds_controller_power_control_timer_restart (this_controller); } else { //there are phys, attached to the same sas address as this phy, are already //in READY state, this phy don't need wait. U8 i; SCIC_SDS_PHY_T * current_phy; for(i = 0; i < SCI_MAX_PHYS; i++) { current_phy = &this_controller->phy_table[i]; if (current_phy->parent.state_machine.current_state_id == SCI_BASE_PHY_STATE_READY && current_phy->protocol == SCIC_SDS_PHY_PROTOCOL_SAS && current_phy->phy_type.sas.identify_address_frame_buffer.sas_address.high == the_phy->phy_type.sas.identify_address_frame_buffer.sas_address.high && current_phy->phy_type.sas.identify_address_frame_buffer.sas_address.low == the_phy->phy_type.sas.identify_address_frame_buffer.sas_address.low) { scic_sds_phy_consume_power_handler(the_phy); break; } } if (i == SCI_MAX_PHYS) { //Add the phy in the waiting list this_controller->power_control.requesters[the_phy->phy_index] = the_phy; this_controller->power_control.phys_waiting++; } } } /** * @brief This method removes the phy from the stagger spinup control * queue. * * @param[in] this_controller * @param[in] the_phy */ void scic_sds_controller_power_control_queue_remove( SCIC_SDS_CONTROLLER_T *this_controller, SCIC_SDS_PHY_T *the_phy ) { ASSERT (the_phy != NULL); if (this_controller->power_control.requesters[the_phy->phy_index] != NULL) { this_controller->power_control.phys_waiting--; } this_controller->power_control.requesters[the_phy->phy_index] = NULL; } //****************************************************************************- //* SCIC SDS Controller Completion Routines //****************************************************************************- /** * @brief This method returns a TRUE value if the completion queue has * entries that can be processed * * @param[in] this_controller * * @return BOOL * @retval TRUE if the completion queue has entries to process * FALSE if the completion queue has no entries to process */ static BOOL scic_sds_controller_completion_queue_has_entries( SCIC_SDS_CONTROLLER_T *this_controller ) { U32 get_value = this_controller->completion_queue_get; U32 get_index = get_value & SMU_COMPLETION_QUEUE_GET_POINTER_MASK; if ( NORMALIZE_GET_POINTER_CYCLE_BIT(get_value) == COMPLETION_QUEUE_CYCLE_BIT(this_controller->completion_queue[get_index]) ) { return TRUE; } return FALSE; } // --------------------------------------------------------------------------- /** * @brief This method processes a task completion notification. This is * called from within the controller completion handler. * * @param[in] this_controller * @param[in] completion_entry * * @return none */ static void scic_sds_controller_task_completion( SCIC_SDS_CONTROLLER_T *this_controller, U32 completion_entry ) { U32 index; SCIC_SDS_REQUEST_T *io_request; index = SCU_GET_COMPLETION_INDEX(completion_entry); io_request = this_controller->io_request_table[index]; // Make sure that we really want to process this IO request if ( (io_request != SCI_INVALID_HANDLE) && (io_request->io_tag != SCI_CONTROLLER_INVALID_IO_TAG) && ( scic_sds_io_tag_get_sequence(io_request->io_tag) == this_controller->io_request_sequence[index] ) ) { // Yep this is a valid io request pass it along to the io request handler scic_sds_io_request_tc_completion(io_request, completion_entry); } } /** * @brief This method processes an SDMA completion event. This is called * from within the controller completion handler. * * @param[in] this_controller * @param[in] completion_entry * * @return none */ static void scic_sds_controller_sdma_completion( SCIC_SDS_CONTROLLER_T *this_controller, U32 completion_entry ) { U32 index; SCIC_SDS_REQUEST_T *io_request; SCIC_SDS_REMOTE_DEVICE_T *device; index = SCU_GET_COMPLETION_INDEX(completion_entry); switch (scu_get_command_request_type(completion_entry)) { case SCU_CONTEXT_COMMAND_REQUEST_TYPE_POST_TC: case SCU_CONTEXT_COMMAND_REQUEST_TYPE_DUMP_TC: io_request = this_controller->io_request_table[index]; SCIC_LOG_ERROR(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_SMP_IO_REQUEST | SCIC_LOG_OBJECT_SSP_IO_REQUEST | SCIC_LOG_OBJECT_STP_IO_REQUEST, "SCIC SDS Completion type SDMA %x for io request %x\n", completion_entry, io_request )); /// @todo For a post TC operation we need to fail the IO request break; case SCU_CONTEXT_COMMAND_REQUEST_TYPE_DUMP_RNC: case SCU_CONTEXT_COMMAND_REQUEST_TYPE_OTHER_RNC: case SCU_CONTEXT_COMMAND_REQUEST_TYPE_POST_RNC: device = this_controller->device_table[index]; SCIC_LOG_ERROR(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_SSP_REMOTE_TARGET | SCIC_LOG_OBJECT_SMP_REMOTE_TARGET | SCIC_LOG_OBJECT_STP_REMOTE_TARGET, "SCIC SDS Completion type SDMA %x for remote device %x\n", completion_entry, device )); /// @todo For a port RNC operation we need to fail the device break; default: SCIC_LOG_ERROR(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_CONTROLLER, "SCIC SDS Completion unknown SDMA completion type %x\n", completion_entry )); break; } /// This is an unexpected completion type and is un-recoverable /// Transition to the failed state and wait for a controller reset sci_base_state_machine_change_state( scic_sds_controller_get_base_state_machine(this_controller), SCI_BASE_CONTROLLER_STATE_FAILED ); } /** * This method processes an unsolicited frame message. This is called from * within the controller completion handler. * * @param[in] this_controller * @param[in] completion_entry * * @return none */ static void scic_sds_controller_unsolicited_frame( SCIC_SDS_CONTROLLER_T *this_controller, U32 completion_entry ) { U32 index; U32 frame_index; SCU_UNSOLICITED_FRAME_HEADER_T * frame_header; SCIC_SDS_PHY_T * phy; SCIC_SDS_REMOTE_DEVICE_T * device; SCI_STATUS result = SCI_FAILURE; frame_index = SCU_GET_FRAME_INDEX(completion_entry); frame_header = this_controller->uf_control.buffers.array[frame_index].header; this_controller->uf_control.buffers.array[frame_index].state = UNSOLICITED_FRAME_IN_USE; if (SCU_GET_FRAME_ERROR(completion_entry)) { /// @todo If the IAF frame or SIGNATURE FIS frame has an error will /// this cause a problem? We expect the phy initialization will /// fail if there is an error in the frame. scic_sds_controller_release_frame(this_controller, frame_index); return; } if (frame_header->is_address_frame) { index = SCU_GET_PROTOCOL_ENGINE_INDEX(completion_entry); phy = &this_controller->phy_table[index]; if (phy != NULL) { result = scic_sds_phy_frame_handler(phy, frame_index); } } else { index = SCU_GET_COMPLETION_INDEX(completion_entry); if (index == SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX) { // This is a signature fis or a frame from a direct attached SATA // device that has not yet been created. In either case forwared // the frame to the PE and let it take care of the frame data. index = SCU_GET_PROTOCOL_ENGINE_INDEX(completion_entry); phy = &this_controller->phy_table[index]; result = scic_sds_phy_frame_handler(phy, frame_index); } else { if (index < this_controller->remote_node_entries) device = this_controller->device_table[index]; else device = NULL; if (device != NULL) result = scic_sds_remote_device_frame_handler(device, frame_index); else scic_sds_controller_release_frame(this_controller, frame_index); } } if (result != SCI_SUCCESS) { /// @todo Is there any reason to report some additional error message /// when we get this failure notifiction? } } /** * @brief This method processes an event completion entry. This is called * from within the controller completion handler. * * @param[in] this_controller * @param[in] completion_entry * * @return none */ static void scic_sds_controller_event_completion( SCIC_SDS_CONTROLLER_T *this_controller, U32 completion_entry ) { U32 index; SCIC_SDS_REQUEST_T *io_request; SCIC_SDS_REMOTE_DEVICE_T *device; SCIC_SDS_PHY_T *phy; index = SCU_GET_COMPLETION_INDEX(completion_entry); switch (scu_get_event_type(completion_entry)) { case SCU_EVENT_TYPE_SMU_COMMAND_ERROR: /// @todo The driver did something wrong and we need to fix the condtion. SCIC_LOG_ERROR(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_CONTROLLER, "SCIC Controller 0x%x received SMU command error 0x%x\n", this_controller, completion_entry )); break; case SCU_EVENT_TYPE_FATAL_MEMORY_ERROR: // report fatal memory error this_controller->parent.error = SCI_CONTROLLER_FATAL_MEMORY_ERROR; sci_base_state_machine_change_state( scic_sds_controller_get_base_state_machine(this_controller), SCI_BASE_CONTROLLER_STATE_FAILED ); //continue as in following events case SCU_EVENT_TYPE_SMU_PCQ_ERROR: case SCU_EVENT_TYPE_SMU_ERROR: SCIC_LOG_ERROR(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_CONTROLLER, "SCIC Controller 0x%x received fatal controller event 0x%x\n", this_controller, completion_entry )); break; case SCU_EVENT_TYPE_TRANSPORT_ERROR: io_request = this_controller->io_request_table[index]; scic_sds_io_request_event_handler(io_request, completion_entry); break; case SCU_EVENT_TYPE_PTX_SCHEDULE_EVENT: switch (scu_get_event_specifier(completion_entry)) { case SCU_EVENT_SPECIFIC_SMP_RESPONSE_NO_PE: case SCU_EVENT_SPECIFIC_TASK_TIMEOUT: io_request = this_controller->io_request_table[index]; if (io_request != SCI_INVALID_HANDLE) { scic_sds_io_request_event_handler(io_request, completion_entry); } else { SCIC_LOG_WARNING(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_SMP_IO_REQUEST | SCIC_LOG_OBJECT_SSP_IO_REQUEST | SCIC_LOG_OBJECT_STP_IO_REQUEST, "SCIC Controller 0x%x received event 0x%x for io request object that doesnt exist.\n", this_controller, completion_entry )); } break; case SCU_EVENT_SPECIFIC_IT_NEXUS_TIMEOUT: device = this_controller->device_table[index]; if (device != SCI_INVALID_HANDLE) { scic_sds_remote_device_event_handler(device, completion_entry); } else { SCIC_LOG_WARNING(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_SMP_REMOTE_TARGET | SCIC_LOG_OBJECT_SSP_REMOTE_TARGET | SCIC_LOG_OBJECT_STP_REMOTE_TARGET, "SCIC Controller 0x%x received event 0x%x for remote device object that doesnt exist.\n", this_controller, completion_entry )); } break; } break; case SCU_EVENT_TYPE_BROADCAST_CHANGE: // direct the broadcast change event to the phy first and then let // the phy redirect the broadcast change to the port object case SCU_EVENT_TYPE_ERR_CNT_EVENT: // direct error counter event to the phy object since that is where // we get the event notification. This is a type 4 event. case SCU_EVENT_TYPE_OSSP_EVENT: index = SCU_GET_PROTOCOL_ENGINE_INDEX(completion_entry); phy = &this_controller->phy_table[index]; scic_sds_phy_event_handler(phy, completion_entry); break; case SCU_EVENT_TYPE_RNC_SUSPEND_TX: case SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX: case SCU_EVENT_TYPE_RNC_OPS_MISC: if (index < this_controller->remote_node_entries) { device = this_controller->device_table[index]; if (device != NULL) { scic_sds_remote_device_event_handler(device, completion_entry); } } else { SCIC_LOG_ERROR(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_SMP_REMOTE_TARGET | SCIC_LOG_OBJECT_SSP_REMOTE_TARGET | SCIC_LOG_OBJECT_STP_REMOTE_TARGET, "SCIC Controller 0x%x received event 0x%x for remote device object 0x%0x that doesnt exist.\n", this_controller, completion_entry, index )); } break; default: SCIC_LOG_WARNING(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_CONTROLLER, "SCIC Controller received unknown event code %x\n", completion_entry )); break; } } /** * @brief This method is a private routine for processing the completion * queue entries. * * @param[in] this_controller * * @return none */ static void scic_sds_controller_process_completions( SCIC_SDS_CONTROLLER_T *this_controller ) { U32 completion_count = 0; U32 completion_entry; U32 get_index; U32 get_cycle; U32 event_index; U32 event_cycle; SCIC_LOG_TRACE(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_CONTROLLER, "scic_sds_controller_process_completions(0x%x) enter\n", this_controller )); SCIC_LOG_TRACE(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_COMPLETION_QUEUE, "completion queue beginning get : 0x%08x\n", this_controller->completion_queue_get )); // Get the component parts of the completion queue get_index = NORMALIZE_GET_POINTER(this_controller->completion_queue_get); get_cycle = SMU_CQGR_CYCLE_BIT & this_controller->completion_queue_get; event_index = NORMALIZE_EVENT_POINTER(this_controller->completion_queue_get); event_cycle = SMU_CQGR_EVENT_CYCLE_BIT & this_controller->completion_queue_get; while ( NORMALIZE_GET_POINTER_CYCLE_BIT(get_cycle) == COMPLETION_QUEUE_CYCLE_BIT(this_controller->completion_queue[get_index]) ) { completion_count++; completion_entry = this_controller->completion_queue[get_index]; INCREMENT_COMPLETION_QUEUE_GET(this_controller, get_index, get_cycle); SCIC_LOG_TRACE(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_COMPLETION_QUEUE, "completion queue entry : 0x%08x\n", completion_entry )); switch (SCU_GET_COMPLETION_TYPE(completion_entry)) { case SCU_COMPLETION_TYPE_TASK: scic_sds_controller_task_completion(this_controller, completion_entry); break; case SCU_COMPLETION_TYPE_SDMA: scic_sds_controller_sdma_completion(this_controller, completion_entry); break; case SCU_COMPLETION_TYPE_UFI: scic_sds_controller_unsolicited_frame(this_controller, completion_entry); break; case SCU_COMPLETION_TYPE_EVENT: scic_sds_controller_event_completion(this_controller, completion_entry); break; case SCU_COMPLETION_TYPE_NOTIFY: // Presently we do the same thing with a notify event that we do with the // other event codes. INCREMENT_EVENT_QUEUE_GET(this_controller, event_index, event_cycle); scic_sds_controller_event_completion(this_controller, completion_entry); break; default: SCIC_LOG_WARNING(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_CONTROLLER, "SCIC Controller received unknown completion type %x\n", completion_entry )); break; } } // Update the get register if we completed one or more entries if (completion_count > 0) { this_controller->completion_queue_get = SMU_CQGR_GEN_BIT(ENABLE) | SMU_CQGR_GEN_BIT(EVENT_ENABLE) | event_cycle | SMU_CQGR_GEN_VAL(EVENT_POINTER, event_index) | get_cycle | SMU_CQGR_GEN_VAL(POINTER, get_index) ; SMU_CQGR_WRITE(this_controller, this_controller->completion_queue_get); } SCIC_LOG_TRACE(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_COMPLETION_QUEUE, "completion queue ending get : 0x%08x\n", this_controller->completion_queue_get )); } /** * @brief This method is a private routine for processing the completion * queue entries. * * @param[in] this_controller * * @return none */ static void scic_sds_controller_transitioned_process_completions( SCIC_SDS_CONTROLLER_T * this_controller ) { U32 completion_count = 0; U32 completion_entry; U32 get_index; U32 get_cycle; U32 event_index; U32 event_cycle; SCIC_LOG_TRACE(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_CONTROLLER, "scic_sds_controller_transitioned_process_completions(0x%x) enter\n", this_controller )); SCIC_LOG_TRACE(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_COMPLETION_QUEUE, "completion queue beginning get : 0x%08x\n", this_controller->completion_queue_get )); // Get the component parts of the completion queue get_index = NORMALIZE_GET_POINTER(this_controller->completion_queue_get); get_cycle = SMU_CQGR_CYCLE_BIT & this_controller->completion_queue_get; event_index = NORMALIZE_EVENT_POINTER(this_controller->completion_queue_get); event_cycle = SMU_CQGR_EVENT_CYCLE_BIT & this_controller->completion_queue_get; while ( NORMALIZE_GET_POINTER_CYCLE_BIT(get_cycle) == COMPLETION_QUEUE_CYCLE_BIT( this_controller->completion_queue[get_index]) ) { completion_count++; completion_entry = this_controller->completion_queue[get_index]; INCREMENT_COMPLETION_QUEUE_GET(this_controller, get_index, get_cycle); SCIC_LOG_TRACE(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_COMPLETION_QUEUE, "completion queue entry : 0x%08x\n", completion_entry )); switch (SCU_GET_COMPLETION_TYPE(completion_entry)) { case SCU_COMPLETION_TYPE_TASK: scic_sds_controller_task_completion(this_controller, completion_entry); break; case SCU_COMPLETION_TYPE_NOTIFY: INCREMENT_EVENT_QUEUE_GET(this_controller, event_index, event_cycle); // Fall-through case SCU_COMPLETION_TYPE_EVENT: case SCU_COMPLETION_TYPE_SDMA: case SCU_COMPLETION_TYPE_UFI: default: SCIC_LOG_WARNING(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_CONTROLLER, "SCIC Controller ignoring completion type %x\n", completion_entry )); break; } } // Update the get register if we completed one or more entries if (completion_count > 0) { this_controller->completion_queue_get = SMU_CQGR_GEN_BIT(ENABLE) | SMU_CQGR_GEN_BIT(EVENT_ENABLE) | event_cycle | SMU_CQGR_GEN_VAL(EVENT_POINTER, event_index) | get_cycle | SMU_CQGR_GEN_VAL(POINTER, get_index) ; SMU_CQGR_WRITE(this_controller, this_controller->completion_queue_get); } SCIC_LOG_TRACE(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_COMPLETION_QUEUE, "completion queue ending get : 0x%08x\n", this_controller->completion_queue_get )); } //****************************************************************************- //* SCIC SDS Controller Interrupt and Completion functions //****************************************************************************- /** * @brief This method provides standard (common) processing of interrupts * for polling and legacy based interrupts. * * @param[in] controller * @param[in] interrupt_status * * @return This method returns a boolean (BOOL) indication as to * whether an completions are pending to be processed. * @retval TRUE if an interrupt is to be processed * @retval FALSE if no interrupt was pending */ static BOOL scic_sds_controller_standard_interrupt_handler( SCIC_SDS_CONTROLLER_T *this_controller, U32 interrupt_status ) { BOOL is_completion_needed = FALSE; SCIC_LOG_TRACE(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_CONTROLLER, "scic_sds_controller_standard_interrupt_handler(0x%d,0x%d) enter\n", this_controller, interrupt_status )); if ( (interrupt_status & SMU_ISR_QUEUE_ERROR) || ( (interrupt_status & SMU_ISR_QUEUE_SUSPEND) && (!scic_sds_controller_completion_queue_has_entries(this_controller)) ) ) { // We have a fatal error on the read of the completion queue bar // OR // We have a fatal error there is nothing in the completion queue // but we have a report from the hardware that the queue is full /// @todo how do we request the a controller reset is_completion_needed = TRUE; this_controller->encountered_fatal_error = TRUE; } if (scic_sds_controller_completion_queue_has_entries(this_controller)) { is_completion_needed = TRUE; } return is_completion_needed; } /** * @brief This is the method provided to handle polling for interrupts * for the controller object. * * @param[in] controller * * @return BOOL * @retval TRUE if an interrupt is to be processed * @retval FALSE if no interrupt was pending */ static BOOL scic_sds_controller_polling_interrupt_handler( SCI_CONTROLLER_HANDLE_T controller ) { U32 interrupt_status; SCIC_SDS_CONTROLLER_T *this_controller = (SCIC_SDS_CONTROLLER_T*)controller; SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER, "scic_sds_controller_polling_interrupt_handler(0x%d) enter\n", controller )); /* * In INTERRUPT_POLLING_MODE we exit the interrupt handler if the hardware * indicates nothing is pending. Since we are not being called from a real * interrupt, we don't want to confuse the hardware by servicing the * completion queue before the hardware indicates it is ready. We'll * simply wait for another polling interval and check again. */ interrupt_status = SMU_ISR_READ(this_controller); if ((interrupt_status & (SMU_ISR_COMPLETION | SMU_ISR_QUEUE_ERROR | SMU_ISR_QUEUE_SUSPEND)) == 0) { return FALSE; } return scic_sds_controller_standard_interrupt_handler( controller, interrupt_status ); } /** * @brief This is the method provided to handle completions when interrupt * polling is in use. * * @param[in] controller * * @return none */ static void scic_sds_controller_polling_completion_handler( SCI_CONTROLLER_HANDLE_T controller ) { SCIC_SDS_CONTROLLER_T *this_controller = (SCIC_SDS_CONTROLLER_T *)controller; SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER, "scic_sds_controller_polling_completion_handler(0x%d) enter\n", controller )); if (this_controller->encountered_fatal_error == TRUE) { SCIC_LOG_ERROR(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_CONTROLLER, "SCIC Controller has encountered a fatal error.\n" )); sci_base_state_machine_change_state( scic_sds_controller_get_base_state_machine(this_controller), SCI_BASE_CONTROLLER_STATE_FAILED ); } else if (scic_sds_controller_completion_queue_has_entries(this_controller)) { if (this_controller->restrict_completions == FALSE) scic_sds_controller_process_completions(this_controller); else scic_sds_controller_transitioned_process_completions(this_controller); } /* * The interrupt handler does not adjust the CQ's * get pointer. So, SCU's INTx pin stays asserted during the * interrupt handler even though it tries to clear the interrupt * source. Therefore, the completion handler must ensure that the * interrupt source is cleared. Otherwise, we get a spurious * interrupt for which the interrupt handler will not issue a * corresponding completion event. Also, we unmask interrupts. */ SMU_ISR_WRITE( this_controller, (U32)(SMU_ISR_COMPLETION | SMU_ISR_QUEUE_ERROR | SMU_ISR_QUEUE_SUSPEND) ); } #if !defined(DISABLE_INTERRUPTS) /** * @brief This is the method provided to handle legacy interrupts for the * controller object. * * @param[in] controller * * @return BOOL * @retval TRUE if an interrupt is processed * FALSE if no interrupt was processed */ static BOOL scic_sds_controller_legacy_interrupt_handler( SCI_CONTROLLER_HANDLE_T controller ) { U32 interrupt_status; BOOL is_completion_needed; SCIC_SDS_CONTROLLER_T *this_controller = (SCIC_SDS_CONTROLLER_T*)controller; interrupt_status = SMU_ISR_READ(this_controller); is_completion_needed = scic_sds_controller_standard_interrupt_handler( this_controller, interrupt_status ); return is_completion_needed; } /** * @brief This is the method provided to handle legacy completions it is * expected that the SCI User will call this completion handler * anytime the interrupt handler reports that it has handled an * interrupt. * * @param[in] controller * * @return none */ static void scic_sds_controller_legacy_completion_handler( SCI_CONTROLLER_HANDLE_T controller ) { SCIC_SDS_CONTROLLER_T *this_controller = (SCIC_SDS_CONTROLLER_T *)controller; SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER, "scic_sds_controller_legacy_completion_handler(0x%d) enter\n", controller )); scic_sds_controller_polling_completion_handler(controller); SMU_IMR_WRITE(this_controller, 0x00000000); #ifdef IMR_READ_FENCE { volatile U32 int_mask_value = 0; ULONG count = 0; /* * Temporary code since we have seen with legacy interrupts * that interrupts are still masked after clearing the mask * above. This may be an Arlington problem or it may be an * old driver problem. Presently this code is turned off * since we have not seen this problem recently. */ do { int_mask_value = SMU_IMR_READ(this_controler); if (count++ > 10) { #ifdef ALLOW_ENTER_DEBUGGER __debugbreak(); #endif break; } } while (int_mask_value != 0); } #endif } /** * @brief This is the method provided to handle an MSIX interrupt message * when there is just a single MSIX message being provided by the * hardware. This mode of operation is single vector mode. * * @param[in] controller * * @return BOOL * @retval TRUE if an interrupt is processed * FALSE if no interrupt was processed */ static BOOL scic_sds_controller_single_vector_interrupt_handler( SCI_CONTROLLER_HANDLE_T controller ) { U32 interrupt_status; SCIC_SDS_CONTROLLER_T *this_controller; this_controller = (SCIC_SDS_CONTROLLER_T *)controller; // Mask the interrupts // There is a race in the hardware that could cause us not to be notified // of an interrupt completion if we do not take this step. We will unmask // the interrupts in the completion routine. SMU_IMR_WRITE(this_controller, 0xFFFFFFFF); interrupt_status = SMU_ISR_READ(this_controller); interrupt_status &= (SMU_ISR_QUEUE_ERROR | SMU_ISR_QUEUE_SUSPEND); if ( (interrupt_status == 0) && scic_sds_controller_completion_queue_has_entries(this_controller) ) { // There is at least one completion queue entry to process so we can // return a success and ignore for now the case of an error interrupt SMU_ISR_WRITE(this_controller, SMU_ISR_COMPLETION); return TRUE; } if (interrupt_status != 0) { // There is an error interrupt pending so let it through and handle // in the callback return TRUE; } // Clear any offending interrupts since we could not find any to handle // and unmask them all SMU_ISR_WRITE(this_controller, 0x00000000); SMU_IMR_WRITE(this_controller, 0x00000000); return FALSE; } /** * @brief This is the method provided to handle completions for a single * MSIX message. * * @param[in] controller */ static void scic_sds_controller_single_vector_completion_handler( SCI_CONTROLLER_HANDLE_T controller ) { U32 interrupt_status; SCIC_SDS_CONTROLLER_T *this_controller; this_controller = (SCIC_SDS_CONTROLLER_T *)controller; SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER, "scic_sds_controller_single_vector_completion_handler(0x%d) enter\n", controller )); interrupt_status = SMU_ISR_READ(this_controller); interrupt_status &= (SMU_ISR_QUEUE_ERROR | SMU_ISR_QUEUE_SUSPEND); if (interrupt_status & SMU_ISR_QUEUE_ERROR) { SCIC_LOG_ERROR(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_CONTROLLER, "SCIC Controller has encountered a fatal error.\n" )); // We have a fatal condition and must reset the controller // Leave the interrupt mask in place and get the controller reset sci_base_state_machine_change_state( scic_sds_controller_get_base_state_machine(this_controller), SCI_BASE_CONTROLLER_STATE_FAILED ); return; } if ( (interrupt_status & SMU_ISR_QUEUE_SUSPEND) && !scic_sds_controller_completion_queue_has_entries(this_controller) ) { SCIC_LOG_ERROR(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_CONTROLLER, "SCIC Controller has encountered a fatal error.\n" )); // We have a fatal condtion and must reset the controller // Leave the interrupt mask in place and get the controller reset sci_base_state_machine_change_state( scic_sds_controller_get_base_state_machine(this_controller), SCI_BASE_CONTROLLER_STATE_FAILED ); return; } if (scic_sds_controller_completion_queue_has_entries(this_controller)) { scic_sds_controller_process_completions(this_controller); // We dont care which interrupt got us to processing the completion queu // so clear them both. SMU_ISR_WRITE( this_controller, (SMU_ISR_COMPLETION | SMU_ISR_QUEUE_SUSPEND) ); } SMU_IMR_WRITE(this_controller, 0x00000000); } /** * @brief This is the method provided to handle a MSIX message for a normal * completion. * * @param[in] controller * * @return BOOL * @retval TRUE if an interrupt is processed * FALSE if no interrupt was processed */ static BOOL scic_sds_controller_normal_vector_interrupt_handler( SCI_CONTROLLER_HANDLE_T controller ) { SCIC_SDS_CONTROLLER_T *this_controller; this_controller = (SCIC_SDS_CONTROLLER_T *)controller; if (scic_sds_controller_completion_queue_has_entries(this_controller)) { return TRUE; } else { // we have a spurious interrupt it could be that we have already // emptied the completion queue from a previous interrupt SMU_ISR_WRITE(this_controller, SMU_ISR_COMPLETION); // There is a race in the hardware that could cause us not to be notified // of an interrupt completion if we do not take this step. We will mask // then unmask the interrupts so if there is another interrupt pending // the clearing of the interrupt source we get the next interrupt message. SMU_IMR_WRITE(this_controller, 0xFF000000); SMU_IMR_WRITE(this_controller, 0x00000000); } return FALSE; } /** * @brief This is the method provided to handle the completions for a * normal MSIX message. * * @param[in] controller */ static void scic_sds_controller_normal_vector_completion_handler( SCI_CONTROLLER_HANDLE_T controller ) { SCIC_SDS_CONTROLLER_T *this_controller; this_controller = (SCIC_SDS_CONTROLLER_T *)controller; SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER, "scic_sds_controller_normal_vector_completion_handler(0x%d) enter\n", controller )); // Empty out the completion queue if (scic_sds_controller_completion_queue_has_entries(this_controller)) { scic_sds_controller_process_completions(this_controller); } // Clear the interrupt and enable all interrupts again SMU_ISR_WRITE(this_controller, SMU_ISR_COMPLETION); // Could we write the value of SMU_ISR_COMPLETION? SMU_IMR_WRITE(this_controller, 0xFF000000); SMU_IMR_WRITE(this_controller, 0x00000000); } /** * @brief This is the method provided to handle the error MSIX message * interrupt. This is the normal operating mode for the hardware if * MSIX is enabled. * * @param[in] controller * * @return BOOL * @retval TRUE if an interrupt is processed * FALSE if no interrupt was processed */ static BOOL scic_sds_controller_error_vector_interrupt_handler( SCI_CONTROLLER_HANDLE_T controller ) { U32 interrupt_status; SCIC_SDS_CONTROLLER_T *this_controller; this_controller = (SCIC_SDS_CONTROLLER_T *)controller; interrupt_status = SMU_ISR_READ(this_controller); interrupt_status &= (SMU_ISR_QUEUE_ERROR | SMU_ISR_QUEUE_SUSPEND); if (interrupt_status != 0) { // There is an error interrupt pending so let it through and handle // in the callback return TRUE; } // There is a race in the hardware that could cause us not to be notified // of an interrupt completion if we do not take this step. We will mask // then unmask the error interrupts so if there was another interrupt // pending we will be notified. // Could we write the value of (SMU_ISR_QUEUE_ERROR | SMU_ISR_QUEUE_SUSPEND)? SMU_IMR_WRITE(this_controller, 0x000000FF); SMU_IMR_WRITE(this_controller, 0x00000000); return FALSE; } /** * @brief This is the method provided to handle the error completions when * the hardware is using two MSIX messages. * * @param[in] controller */ static void scic_sds_controller_error_vector_completion_handler( SCI_CONTROLLER_HANDLE_T controller ) { U32 interrupt_status; SCIC_SDS_CONTROLLER_T *this_controller; this_controller = (SCIC_SDS_CONTROLLER_T *)controller; SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER, "scic_sds_controller_error_vector_completion_handler(0x%d) enter\n", controller )); interrupt_status = SMU_ISR_READ(this_controller); if ( (interrupt_status & SMU_ISR_QUEUE_SUSPEND) && scic_sds_controller_completion_queue_has_entries(this_controller) ) { scic_sds_controller_process_completions(this_controller); SMU_ISR_WRITE(this_controller, SMU_ISR_QUEUE_SUSPEND); } else { SCIC_LOG_ERROR(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_CONTROLLER, "SCIC Controller reports CRC error on completion ISR %x\n", interrupt_status )); sci_base_state_machine_change_state( scic_sds_controller_get_base_state_machine(this_controller), SCI_BASE_CONTROLLER_STATE_FAILED ); return; } // If we dont process any completions I am not sure that we want to do this. // We are in the middle of a hardware fault and should probably be reset. SMU_IMR_WRITE(this_controller, 0x00000000); } #endif // !defined(DISABLE_INTERRUPTS) //****************************************************************************- //* SCIC SDS Controller External Methods //****************************************************************************- /** * @brief This method returns the sizeof the SCIC SDS Controller Object * * @return U32 */ U32 scic_sds_controller_get_object_size(void) { return sizeof(SCIC_SDS_CONTROLLER_T); } /** * This method returns the minimum number of timers that are required by the * controller object. This will include required timers for phys and ports. * * @return U32 * @retval The minimum number of timers that are required to make this * controller operational. */ U32 scic_sds_controller_get_min_timer_count(void) { return SCIC_SDS_CONTROLLER_MIN_TIMER_COUNT + scic_sds_port_get_min_timer_count() + scic_sds_phy_get_min_timer_count(); } /** * This method returns the maximum number of timers that are required by the * controller object. This will include required timers for phys and ports. * * @return U32 * @retval The maximum number of timers that will be used by the controller * object */ U32 scic_sds_controller_get_max_timer_count(void) { return SCIC_SDS_CONTROLLER_MAX_TIMER_COUNT + scic_sds_port_get_max_timer_count() + scic_sds_phy_get_max_timer_count(); } /** * @brief * * @param[in] this_controller * @param[in] the_port * @param[in] the_phy * * @return none */ void scic_sds_controller_link_up( SCIC_SDS_CONTROLLER_T *this_controller, SCIC_SDS_PORT_T *the_port, SCIC_SDS_PHY_T *the_phy ) { if (this_controller->state_handlers->link_up_handler != NULL) { this_controller->state_handlers->link_up_handler( this_controller, the_port, the_phy); } else { SCIC_LOG_INFO(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_CONTROLLER, "SCIC Controller linkup event from phy %d in unexpected state %d\n", the_phy->phy_index, sci_base_state_machine_get_state( scic_sds_controller_get_base_state_machine(this_controller)) )); } } /** * @brief * * @param[in] this_controller * @param[in] the_port * @param[in] the_phy */ void scic_sds_controller_link_down( SCIC_SDS_CONTROLLER_T *this_controller, SCIC_SDS_PORT_T *the_port, SCIC_SDS_PHY_T *the_phy ) { if (this_controller->state_handlers->link_down_handler != NULL) { this_controller->state_handlers->link_down_handler( this_controller, the_port, the_phy); } else { SCIC_LOG_INFO(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_CONTROLLER, "SCIC Controller linkdown event from phy %d in unexpected state %d\n", the_phy->phy_index, sci_base_state_machine_get_state( scic_sds_controller_get_base_state_machine(this_controller)) )); } } /** * @brief This method is called by the remote device to inform the controller * that this remote device has started. * * @param[in] this_controller * @param[in] the_device */ void scic_sds_controller_remote_device_started( SCIC_SDS_CONTROLLER_T * this_controller, SCIC_SDS_REMOTE_DEVICE_T * the_device ) { if (this_controller->state_handlers->remote_device_started_handler != NULL) { this_controller->state_handlers->remote_device_started_handler( this_controller, the_device ); } else { SCIC_LOG_INFO(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_CONTROLLER, "SCIC Controller 0x%x remote device started event from device 0x%x in unexpected state %d\n", this_controller, the_device, sci_base_state_machine_get_state( scic_sds_controller_get_base_state_machine(this_controller)) )); } } /** * @brief This is a helper method to determine if any remote devices on this * controller are still in the stopping state. * * @param[in] this_controller */ BOOL scic_sds_controller_has_remote_devices_stopping( SCIC_SDS_CONTROLLER_T * this_controller ) { U32 index; for (index = 0; index < this_controller->remote_node_entries; index++) { if ( (this_controller->device_table[index] != NULL) && ( this_controller->device_table[index]->parent.state_machine.current_state_id == SCI_BASE_REMOTE_DEVICE_STATE_STOPPING ) ) { return TRUE; } } return FALSE; } /** * @brief This method is called by the remote device to inform the controller * object that the remote device has stopped. * * @param[in] this_controller * @param[in] the_device */ void scic_sds_controller_remote_device_stopped( SCIC_SDS_CONTROLLER_T * this_controller, SCIC_SDS_REMOTE_DEVICE_T * the_device ) { if (this_controller->state_handlers->remote_device_stopped_handler != NULL) { this_controller->state_handlers->remote_device_stopped_handler( this_controller, the_device ); } else { SCIC_LOG_INFO(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_CONTROLLER, "SCIC Controller 0x%x remote device stopped event from device 0x%x in unexpected state %d\n", this_controller, the_device, sci_base_state_machine_get_state( scic_sds_controller_get_base_state_machine(this_controller)) )); } } /** * @brief This method will write to the SCU PCP register the request value. * The method is used to suspend/resume ports, devices, and phys. * * @param[in] this_controller * @param[in] request */ void scic_sds_controller_post_request( SCIC_SDS_CONTROLLER_T *this_controller, U32 request ) { SCIC_LOG_INFO(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_COMPLETION_QUEUE, "SCIC Controller 0x%08x post request 0x%08x\n", this_controller, request )); SMU_PCP_WRITE(this_controller, request); } /** * @brief This method will copy the soft copy of the task context into * the physical memory accessible by the controller. * * @note After this call is made the SCIC_SDS_IO_REQUEST object will * always point to the physical memory version of the task context. * Thus, all subsequent updates to the task context are performed in * the TC table (i.e. DMAable memory). * * @param[in] this_controller This parameter specifies the controller for * which to copy the task context. * @param[in] this_request This parameter specifies the request for which * the task context is being copied. * * @return none */ void scic_sds_controller_copy_task_context( SCIC_SDS_CONTROLLER_T *this_controller, SCIC_SDS_REQUEST_T *this_request ) { SCU_TASK_CONTEXT_T *task_context_buffer; task_context_buffer = scic_sds_controller_get_task_context_buffer( this_controller, this_request->io_tag ); memcpy( task_context_buffer, this_request->task_context_buffer, SCI_FIELD_OFFSET(SCU_TASK_CONTEXT_T, sgl_snapshot_ac) ); // Now that the soft copy of the TC has been copied into the TC // table accessible by the silicon. Thus, any further changes to // the TC (e.g. TC termination) occur in the appropriate location. this_request->task_context_buffer = task_context_buffer; } /** * @brief This method returns the task context buffer for the given io tag. * * @param[in] this_controller * @param[in] io_tag * * @return struct SCU_TASK_CONTEXT* */ SCU_TASK_CONTEXT_T * scic_sds_controller_get_task_context_buffer( SCIC_SDS_CONTROLLER_T * this_controller, U16 io_tag ) { U16 task_index = scic_sds_io_tag_get_index(io_tag); if (task_index < this_controller->task_context_entries) { return &this_controller->task_context_table[task_index]; } return NULL; } /** * @brief This method returnst the sequence value from the io tag value * * @param[in] this_controller * @param[in] io_tag * * @return U16 */ U16 scic_sds_controller_get_io_sequence_from_tag( SCIC_SDS_CONTROLLER_T *this_controller, U16 io_tag ) { return scic_sds_io_tag_get_sequence(io_tag); } /** * @brief This method returns the IO request associated with the tag value * * @param[in] this_controller * @param[in] io_tag * * @return SCIC_SDS_IO_REQUEST_T* * @retval NULL if there is no valid IO request at the tag value */ SCIC_SDS_REQUEST_T *scic_sds_controller_get_io_request_from_tag( SCIC_SDS_CONTROLLER_T *this_controller, U16 io_tag ) { U16 task_index; U16 task_sequence; task_index = scic_sds_io_tag_get_index(io_tag); if (task_index < this_controller->task_context_entries) { if (this_controller->io_request_table[task_index] != SCI_INVALID_HANDLE) { task_sequence = scic_sds_io_tag_get_sequence(io_tag); if (task_sequence == this_controller->io_request_sequence[task_index]) { return this_controller->io_request_table[task_index]; } } } return SCI_INVALID_HANDLE; } /** * @brief This method allocates remote node index and the reserves the * remote node context space for use. This method can fail if there * are no more remote node index available. * * @param[in] this_controller This is the controller object which contains * the set of free remote node ids * @param[in] the_devce This is the device object which is requesting the a * remote node id - * @param[out] node_id This is the remote node id that is assinged to the + * @param[out] node_id This is the remote node id that is assigned to the * device if one is available * * @return SCI_STATUS * @retval SCI_FAILURE_OUT_OF_RESOURCES if there are no available remote * node index available. */ SCI_STATUS scic_sds_controller_allocate_remote_node_context( SCIC_SDS_CONTROLLER_T * this_controller, SCIC_SDS_REMOTE_DEVICE_T * the_device, U16 * node_id ) { U16 node_index; U32 remote_node_count = scic_sds_remote_device_node_count(the_device); node_index = scic_sds_remote_node_table_allocate_remote_node( &this_controller->available_remote_nodes, remote_node_count ); if (node_index != SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX) { this_controller->device_table[node_index] = the_device; *node_id = node_index; return SCI_SUCCESS; } return SCI_FAILURE_INSUFFICIENT_RESOURCES; } /** * @brief This method frees the remote node index back to the available * pool. Once this is done the remote node context buffer is no * longer valid and can not be used. * * @param[in] this_controller * @param[in] the_device * @param[in] node_id * * @return none */ void scic_sds_controller_free_remote_node_context( SCIC_SDS_CONTROLLER_T * this_controller, SCIC_SDS_REMOTE_DEVICE_T * the_device, U16 node_id ) { U32 remote_node_count = scic_sds_remote_device_node_count(the_device); if (this_controller->device_table[node_id] == the_device) { this_controller->device_table[node_id] = SCI_INVALID_HANDLE; scic_sds_remote_node_table_release_remote_node_index( &this_controller->available_remote_nodes, remote_node_count, node_id ); } } /** * @brief This method returns the SCU_REMOTE_NODE_CONTEXT for the specified * remote node id. * * @param[in] this_controller * @param[in] node_id * * @return SCU_REMOTE_NODE_CONTEXT_T* */ SCU_REMOTE_NODE_CONTEXT_T *scic_sds_controller_get_remote_node_context_buffer( SCIC_SDS_CONTROLLER_T *this_controller, U16 node_id ) { if ( (node_id < this_controller->remote_node_entries) && (this_controller->device_table[node_id] != SCI_INVALID_HANDLE) ) { return &this_controller->remote_node_context_table[node_id]; } return NULL; } /** * This method will combind the frame header and frame buffer to create * a SATA D2H register FIS * * @param[out] resposne_buffer This is the buffer into which the D2H register * FIS will be constructed. * @param[in] frame_header This is the frame header returned by the hardware. * @param[in] frame_buffer This is the frame buffer returned by the hardware. * * @erturn none */ void scic_sds_controller_copy_sata_response( void * response_buffer, void * frame_header, void * frame_buffer ) { memcpy( response_buffer, frame_header, sizeof(U32) ); memcpy( (char *)((char *)response_buffer + sizeof(U32)), frame_buffer, sizeof(SATA_FIS_REG_D2H_T) - sizeof(U32) ); } /** * @brief This method releases the frame once this is done the frame is * available for re-use by the hardware. The data contained in the * frame header and frame buffer is no longer valid. * The UF queue get pointer is only updated if UF control indicates * this is appropriate. * * @param[in] this_controller * @param[in] frame_index * * @return none */ void scic_sds_controller_release_frame( SCIC_SDS_CONTROLLER_T *this_controller, U32 frame_index ) { if (scic_sds_unsolicited_frame_control_release_frame( &this_controller->uf_control, frame_index) == TRUE) SCU_UFQGP_WRITE(this_controller, this_controller->uf_control.get); } #ifdef SCI_LOGGING void scic_sds_controller_initialize_state_logging( SCIC_SDS_CONTROLLER_T *this_controller ) { sci_base_state_machine_logger_initialize( &this_controller->parent.state_machine_logger, &this_controller->parent.state_machine, &this_controller->parent.parent, scic_cb_logger_log_states, "SCIC_SDS_CONTROLLER_T", "base state machine", SCIC_LOG_OBJECT_CONTROLLER ); } void scic_sds_controller_deinitialize_state_logging( SCIC_SDS_CONTROLLER_T *this_controller ) { sci_base_state_machine_logger_deinitialize( &this_controller->parent.state_machine_logger, &this_controller->parent.state_machine ); } #endif /** * @brief This method sets user parameters and OEM parameters to * default values. Users can override these values utilizing * the scic_user_parameters_set() and scic_oem_parameters_set() * methods. * * @param[in] controller This parameter specifies the controller for * which to set the configuration parameters to their * default values. * * @return none */ static void scic_sds_controller_set_default_config_parameters( SCIC_SDS_CONTROLLER_T *this_controller ) { U16 index; // Default to APC mode. this_controller->oem_parameters.sds1.controller.mode_type = SCIC_PORT_AUTOMATIC_CONFIGURATION_MODE; // Default to 1 this_controller->oem_parameters.sds1.controller.max_number_concurrent_device_spin_up = 1; // Default to no SSC operation. this_controller->oem_parameters.sds1.controller.ssc_sata_tx_spread_level = 0; this_controller->oem_parameters.sds1.controller.ssc_sas_tx_spread_level = 0; this_controller->oem_parameters.sds1.controller.ssc_sas_tx_type = 0; // Default to all phys to using short cables this_controller->oem_parameters.sds1.controller.cable_selection_mask = 0; // Initialize all of the port parameter information to narrow ports. for (index = 0; index < SCI_MAX_PORTS; index++) { this_controller->oem_parameters.sds1.ports[index].phy_mask = 0; } // Initialize all of the phy parameter information. for (index = 0; index < SCI_MAX_PHYS; index++) { // Default to 6G (i.e. Gen 3) for now. User can override if // they choose. this_controller->user_parameters.sds1.phys[index].max_speed_generation = 2; //the frequencies cannot be 0 this_controller->user_parameters.sds1.phys[index].align_insertion_frequency = 0x7f; this_controller->user_parameters.sds1.phys[index].in_connection_align_insertion_frequency = 0xff; this_controller->user_parameters.sds1.phys[index].notify_enable_spin_up_insertion_frequency = 0x33; // Previous Vitesse based expanders had a arbitration issue that // is worked around by having the upper 32-bits of SAS address // with a value greater then the Vitesse company identifier. // Hence, usage of 0x5FCFFFFF. this_controller->oem_parameters.sds1.phys[index].sas_address.sci_format.high = 0x5FCFFFFF; // Add in controller index to ensure each controller will have unique SAS addresses by default. this_controller->oem_parameters.sds1.phys[index].sas_address.sci_format.low = 0x00000001 + this_controller->controller_index; if ( (this_controller->pci_revision == SCIC_SDS_PCI_REVISION_A0) || (this_controller->pci_revision == SCIC_SDS_PCI_REVISION_A2) || (this_controller->pci_revision == SCIC_SDS_PCI_REVISION_B0) ) { this_controller->oem_parameters.sds1.phys[index].afe_tx_amp_control0 = 0x000E7C03; this_controller->oem_parameters.sds1.phys[index].afe_tx_amp_control1 = 0x000E7C03; this_controller->oem_parameters.sds1.phys[index].afe_tx_amp_control2 = 0x000E7C03; this_controller->oem_parameters.sds1.phys[index].afe_tx_amp_control3 = 0x000E7C03; } else // This must be SCIC_SDS_PCI_REVISION_C0 { this_controller->oem_parameters.sds1.phys[index].afe_tx_amp_control0 = 0x000BDD08; this_controller->oem_parameters.sds1.phys[index].afe_tx_amp_control1 = 0x000B7069; this_controller->oem_parameters.sds1.phys[index].afe_tx_amp_control2 = 0x000B7C09; this_controller->oem_parameters.sds1.phys[index].afe_tx_amp_control3 = 0x000AFC6E; } } this_controller->user_parameters.sds1.stp_inactivity_timeout = 5; this_controller->user_parameters.sds1.ssp_inactivity_timeout = 5; this_controller->user_parameters.sds1.stp_max_occupancy_timeout = 5; this_controller->user_parameters.sds1.ssp_max_occupancy_timeout = 20; this_controller->user_parameters.sds1.no_outbound_task_timeout = 20; } /** * @brief This method release resources in SCI controller. * * @param[in] this_controller This parameter specifies the core * controller and associated objects whose resources are to be * released. * * @return This method returns a value indicating if the operation succeeded. * @retval SCI_SUCCESS This value indicates that all the timers are destroyed. * @retval SCI_FAILURE This value indicates certain failure during the process * of cleaning timer resource. */ static SCI_STATUS scic_sds_controller_release_resource( SCIC_SDS_CONTROLLER_T * this_controller ) { SCIC_SDS_PORT_T * port; SCIC_SDS_PHY_T * phy; U8 index; SCIC_LOG_TRACE(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_INITIALIZATION, "scic_sds_controller_release_resource(0x%x) enter\n", this_controller )); if(this_controller->phy_startup_timer != NULL) { scic_cb_timer_destroy(this_controller, this_controller->phy_startup_timer); this_controller->phy_startup_timer = NULL; } if(this_controller->power_control.timer != NULL) { scic_cb_timer_destroy(this_controller, this_controller->power_control.timer); this_controller->power_control.timer = NULL; } if(this_controller->timeout_timer != NULL) { scic_cb_timer_destroy(this_controller, this_controller->timeout_timer); this_controller->timeout_timer = NULL; } scic_sds_port_configuration_agent_release_resource( this_controller, &this_controller->port_agent); for(index = 0; index < SCI_MAX_PORTS+1; index++) { port = &this_controller->port_table[index]; scic_sds_port_release_resource(this_controller, port); } for(index = 0; index < SCI_MAX_PHYS; index++) { phy = &this_controller->phy_table[index]; scic_sds_phy_release_resource(this_controller, phy); } return SCI_SUCCESS; } /** * @brief This method process the ports configured message from port configuration * agent. * * @param[in] this_controller This parameter specifies the core * controller that its ports are configured. * * @return None. */ void scic_sds_controller_port_agent_configured_ports( SCIC_SDS_CONTROLLER_T * this_controller ) { //simply transit to ready. The function below checks the controller state scic_sds_controller_transition_to_ready( this_controller, SCI_SUCCESS ); } //****************************************************************************- //* SCIC Controller Public Methods //****************************************************************************- SCI_STATUS scic_controller_construct( SCI_LIBRARY_HANDLE_T library, SCI_CONTROLLER_HANDLE_T controller, void * user_object ) { SCIC_SDS_LIBRARY_T *my_library; SCIC_SDS_CONTROLLER_T *this_controller; my_library = (SCIC_SDS_LIBRARY_T *)library; this_controller = (SCIC_SDS_CONTROLLER_T *)controller; SCIC_LOG_TRACE(( sci_base_object_get_logger(library), SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_INITIALIZATION, "scic_controller_construct(0x%x, 0x%x) enter\n", library, controller )); // Just clear out the memory of the structure to be safe. memset(this_controller, 0, sizeof(SCIC_SDS_CONTROLLER_T)); // Make sure that the static data is assigned before moving onto the // base constroller construct as this will cause the controller to // enter its initial state and the controller_index and pci_revision // will be required to complete those operations correctly this_controller->controller_index = scic_sds_library_get_controller_index(my_library, this_controller); this_controller->pci_revision = my_library->pci_revision; sci_base_controller_construct( &this_controller->parent, sci_base_object_get_logger(my_library), scic_sds_controller_state_table, this_controller->memory_descriptors, ARRAY_SIZE(this_controller->memory_descriptors), NULL ); sci_object_set_association(controller, user_object); scic_sds_controller_initialize_state_logging(this_controller); scic_sds_pci_bar_initialization(this_controller); return SCI_SUCCESS; } // --------------------------------------------------------------------------- SCI_STATUS scic_controller_initialize( SCI_CONTROLLER_HANDLE_T controller ) { SCI_STATUS status = SCI_FAILURE_INVALID_STATE; SCIC_SDS_CONTROLLER_T *this_controller; this_controller = (SCIC_SDS_CONTROLLER_T *)controller; SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER, "scic_controller_initialize(0x%x, 0x%d) enter\n", controller )); if (this_controller->state_handlers->parent.initialize_handler != NULL) { status = this_controller->state_handlers->parent.initialize_handler( (SCI_BASE_CONTROLLER_T *)controller ); } else { SCIC_LOG_WARNING(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_CONTROLLER, "SCIC Controller initialize operation requested in invalid state %d\n", sci_base_state_machine_get_state( scic_sds_controller_get_base_state_machine(this_controller)) )); } return status; } // --------------------------------------------------------------------------- U32 scic_controller_get_suggested_start_timeout( SCI_CONTROLLER_HANDLE_T controller ) { // Validate the user supplied parameters. if (controller == SCI_INVALID_HANDLE) return 0; // The suggested minimum timeout value for a controller start operation: // // Signature FIS Timeout // + Phy Start Timeout // + Number of Phy Spin Up Intervals // --------------------------------- // Number of milliseconds for the controller start operation. // // NOTE: The number of phy spin up intervals will be equivalent // to the number of phys divided by the number phys allowed // per interval - 1 (once OEM parameters are supported). // Currently we assume only 1 phy per interval. return (SCIC_SDS_SIGNATURE_FIS_TIMEOUT + SCIC_SDS_CONTROLLER_PHY_START_TIMEOUT + ((SCI_MAX_PHYS-1) * SCIC_SDS_CONTROLLER_POWER_CONTROL_INTERVAL)); } // --------------------------------------------------------------------------- SCI_STATUS scic_controller_start( SCI_CONTROLLER_HANDLE_T controller, U32 timeout ) { SCI_STATUS status = SCI_FAILURE_INVALID_STATE; SCIC_SDS_CONTROLLER_T *this_controller; this_controller = (SCIC_SDS_CONTROLLER_T *)controller; SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER, "scic_controller_start(0x%x, 0x%d) enter\n", controller, timeout )); if (this_controller->state_handlers->parent.start_handler != NULL) { status = this_controller->state_handlers->parent.start_handler( (SCI_BASE_CONTROLLER_T *)controller, timeout ); } else { SCIC_LOG_WARNING(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_CONTROLLER, "SCIC Controller start operation requested in invalid state %d\n", sci_base_state_machine_get_state( scic_sds_controller_get_base_state_machine(this_controller)) )); } return status; } // --------------------------------------------------------------------------- SCI_STATUS scic_controller_stop( SCI_CONTROLLER_HANDLE_T controller, U32 timeout ) { SCI_STATUS status = SCI_FAILURE_INVALID_STATE; SCIC_SDS_CONTROLLER_T *this_controller; this_controller = (SCIC_SDS_CONTROLLER_T *)controller; SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER, "scic_controller_stop(0x%x, 0x%d) enter\n", controller, timeout )); if (this_controller->state_handlers->parent.stop_handler != NULL) { status = this_controller->state_handlers->parent.stop_handler( (SCI_BASE_CONTROLLER_T *)controller, timeout ); } else { SCIC_LOG_WARNING(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_CONTROLLER, "SCIC Controller stop operation requested in invalid state %d\n", sci_base_state_machine_get_state( scic_sds_controller_get_base_state_machine(this_controller)) )); } return status; } // --------------------------------------------------------------------------- SCI_STATUS scic_controller_reset( SCI_CONTROLLER_HANDLE_T controller ) { SCI_STATUS status = SCI_FAILURE_INVALID_STATE; SCIC_SDS_CONTROLLER_T *this_controller; this_controller = (SCIC_SDS_CONTROLLER_T *)controller; SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER, "scic_controller_reset(0x%x) enter\n", controller )); if (this_controller->state_handlers->parent.reset_handler != NULL) { status = this_controller->state_handlers->parent.reset_handler( (SCI_BASE_CONTROLLER_T *)controller ); } else { SCIC_LOG_WARNING(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_CONTROLLER, "SCIC Controller reset operation requested in invalid state %d\n", sci_base_state_machine_get_state( scic_sds_controller_get_base_state_machine(this_controller)) )); } return status; } // --------------------------------------------------------------------------- SCI_STATUS scic_controller_get_handler_methods( SCIC_INTERRUPT_TYPE interrupt_type, U16 message_count, SCIC_CONTROLLER_HANDLER_METHODS_T *handler_methods ) { SCI_STATUS status = SCI_FAILURE_UNSUPPORTED_MESSAGE_COUNT; switch (interrupt_type) { #if !defined(DISABLE_INTERRUPTS) case SCIC_LEGACY_LINE_INTERRUPT_TYPE: if (message_count == 0) { handler_methods[0].interrupt_handler = scic_sds_controller_legacy_interrupt_handler; handler_methods[0].completion_handler = scic_sds_controller_legacy_completion_handler; status = SCI_SUCCESS; } break; case SCIC_MSIX_INTERRUPT_TYPE: if (message_count == 1) { handler_methods[0].interrupt_handler = scic_sds_controller_single_vector_interrupt_handler; handler_methods[0].completion_handler = scic_sds_controller_single_vector_completion_handler; status = SCI_SUCCESS; } else if (message_count == 2) { handler_methods[0].interrupt_handler = scic_sds_controller_normal_vector_interrupt_handler; handler_methods[0].completion_handler = scic_sds_controller_normal_vector_completion_handler; handler_methods[1].interrupt_handler = scic_sds_controller_error_vector_interrupt_handler; handler_methods[1].completion_handler = scic_sds_controller_error_vector_completion_handler; status = SCI_SUCCESS; } break; #endif // !defined(DISABLE_INTERRUPTS) case SCIC_NO_INTERRUPTS: if (message_count == 0) { handler_methods[0].interrupt_handler = scic_sds_controller_polling_interrupt_handler; handler_methods[0].completion_handler = scic_sds_controller_polling_completion_handler; status = SCI_SUCCESS; } break; default: status = SCI_FAILURE_INVALID_PARAMETER_VALUE; break; } return status; } // --------------------------------------------------------------------------- SCI_IO_STATUS scic_controller_start_io( SCI_CONTROLLER_HANDLE_T controller, SCI_REMOTE_DEVICE_HANDLE_T remote_device, SCI_IO_REQUEST_HANDLE_T io_request, U16 io_tag ) { SCI_STATUS status; SCIC_SDS_CONTROLLER_T *this_controller; this_controller = (SCIC_SDS_CONTROLLER_T *)controller; SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER, "scic_controller_start_io(0x%x, 0x%x, 0x%x, 0x%x) enter\n", controller, remote_device, io_request, io_tag )); status = this_controller->state_handlers->parent.start_io_handler( &this_controller->parent, (SCI_BASE_REMOTE_DEVICE_T *)remote_device, (SCI_BASE_REQUEST_T *)io_request, io_tag ); return (SCI_IO_STATUS)status; } // --------------------------------------------------------------------------- SCI_STATUS scic_controller_terminate_request( SCI_CONTROLLER_HANDLE_T controller, SCI_REMOTE_DEVICE_HANDLE_T remote_device, SCI_IO_REQUEST_HANDLE_T request ) { SCI_STATUS status; SCIC_SDS_CONTROLLER_T *this_controller; this_controller = (SCIC_SDS_CONTROLLER_T *)controller; SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER, "scic_controller_terminate_request(0x%x, 0x%x, 0x%x) enter\n", controller, remote_device, request )); status = this_controller->state_handlers->terminate_request_handler( &this_controller->parent, (SCI_BASE_REMOTE_DEVICE_T *)remote_device, (SCI_BASE_REQUEST_T *)request ); return status; } // --------------------------------------------------------------------------- SCI_STATUS scic_controller_complete_io( SCI_CONTROLLER_HANDLE_T controller, SCI_REMOTE_DEVICE_HANDLE_T remote_device, SCI_IO_REQUEST_HANDLE_T io_request ) { SCI_STATUS status; SCIC_SDS_CONTROLLER_T *this_controller; this_controller = (SCIC_SDS_CONTROLLER_T *)controller; SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER, "scic_controller_complete_io(0x%x, 0x%x, 0x%x) enter\n", controller, remote_device, io_request )); status = this_controller->state_handlers->parent.complete_io_handler( &this_controller->parent, (SCI_BASE_REMOTE_DEVICE_T *)remote_device, (SCI_BASE_REQUEST_T *)io_request ); return status; } // --------------------------------------------------------------------------- #if !defined(DISABLE_TASK_MANAGEMENT) SCI_TASK_STATUS scic_controller_start_task( SCI_CONTROLLER_HANDLE_T controller, SCI_REMOTE_DEVICE_HANDLE_T remote_device, SCI_TASK_REQUEST_HANDLE_T task_request, U16 task_tag ) { SCI_STATUS status = SCI_FAILURE_INVALID_STATE; SCIC_SDS_CONTROLLER_T *this_controller; this_controller = (SCIC_SDS_CONTROLLER_T *)controller; SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER, "scic_controller_start_task(0x%x, 0x%x, 0x%x, 0x%x) enter\n", controller, remote_device, task_request, task_tag )); if (this_controller->state_handlers->parent.start_task_handler != NULL) { status = this_controller->state_handlers->parent.start_task_handler( &this_controller->parent, (SCI_BASE_REMOTE_DEVICE_T *)remote_device, (SCI_BASE_REQUEST_T *)task_request, task_tag ); } else { SCIC_LOG_INFO(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER, "SCIC Controller starting task from invalid state\n" )); } return (SCI_TASK_STATUS)status; } // --------------------------------------------------------------------------- SCI_STATUS scic_controller_complete_task( SCI_CONTROLLER_HANDLE_T controller, SCI_REMOTE_DEVICE_HANDLE_T remote_device, SCI_TASK_REQUEST_HANDLE_T task_request ) { SCI_STATUS status = SCI_FAILURE_INVALID_STATE; SCIC_SDS_CONTROLLER_T *this_controller; this_controller = (SCIC_SDS_CONTROLLER_T *)controller; SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER, "scic_controller_complete_task(0x%x, 0x%x, 0x%x) enter\n", controller, remote_device, task_request )); if (this_controller->state_handlers->parent.complete_task_handler != NULL) { status = this_controller->state_handlers->parent.complete_task_handler( &this_controller->parent, (SCI_BASE_REMOTE_DEVICE_T *)remote_device, (SCI_BASE_REQUEST_T *)task_request ); } else { SCIC_LOG_INFO(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER, "SCIC Controller completing task from invalid state\n" )); } return status; } #endif // !defined(DISABLE_TASK_MANAGEMENT) // --------------------------------------------------------------------------- SCI_STATUS scic_controller_get_port_handle( SCI_CONTROLLER_HANDLE_T controller, U8 port_index, SCI_PORT_HANDLE_T * port_handle ) { SCIC_SDS_CONTROLLER_T *this_controller; this_controller = (SCIC_SDS_CONTROLLER_T *)controller; SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER, "scic_controller_get_port_handle(0x%x, 0x%x, 0x%x) enter\n", controller, port_index, port_handle )); if (port_index < this_controller->logical_port_entries) { *port_handle = &this_controller->port_table[port_index]; return SCI_SUCCESS; } return SCI_FAILURE_INVALID_PORT; } // --------------------------------------------------------------------------- SCI_STATUS scic_controller_get_phy_handle( SCI_CONTROLLER_HANDLE_T controller, U8 phy_index, SCI_PHY_HANDLE_T * phy_handle ) { SCIC_SDS_CONTROLLER_T *this_controller; this_controller = (SCIC_SDS_CONTROLLER_T *)controller; SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER, "scic_controller_get_phy_handle(0x%x, 0x%x, 0x%x) enter\n", controller, phy_index, phy_handle )); if (phy_index < ARRAY_SIZE(this_controller->phy_table)) { *phy_handle = &this_controller->phy_table[phy_index]; return SCI_SUCCESS; } SCIC_LOG_ERROR(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_CONTROLLER, "Controller:0x%x PhyId:0x%x invalid phy index\n", this_controller, phy_index )); return SCI_FAILURE_INVALID_PHY; } // --------------------------------------------------------------------------- U16 scic_controller_allocate_io_tag( SCI_CONTROLLER_HANDLE_T controller ) { U16 task_context; U16 sequence_count; SCIC_SDS_CONTROLLER_T *this_controller; this_controller = (SCIC_SDS_CONTROLLER_T *)controller; SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER, "scic_controller_allocate_io_tag(0x%x) enter\n", controller )); if (!sci_pool_empty(this_controller->tci_pool)) { sci_pool_get(this_controller->tci_pool, task_context); sequence_count = this_controller->io_request_sequence[task_context]; return scic_sds_io_tag_construct(sequence_count, task_context); } return SCI_CONTROLLER_INVALID_IO_TAG; } // --------------------------------------------------------------------------- SCI_STATUS scic_controller_free_io_tag( SCI_CONTROLLER_HANDLE_T controller, U16 io_tag ) { U16 sequence; U16 index; SCIC_SDS_CONTROLLER_T *this_controller; this_controller = (SCIC_SDS_CONTROLLER_T *)controller; ASSERT(io_tag != SCI_CONTROLLER_INVALID_IO_TAG); SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER, "scic_controller_free_io_tag(0x%x, 0x%x) enter\n", controller, io_tag )); sequence = scic_sds_io_tag_get_sequence(io_tag); index = scic_sds_io_tag_get_index(io_tag); if (!sci_pool_full(this_controller->tci_pool)) { if (sequence == this_controller->io_request_sequence[index]) { scic_sds_io_sequence_increment( this_controller->io_request_sequence[index]); sci_pool_put(this_controller->tci_pool, index); return SCI_SUCCESS; } } return SCI_FAILURE_INVALID_IO_TAG; } // --------------------------------------------------------------------------- void scic_controller_enable_interrupts( SCI_CONTROLLER_HANDLE_T controller ) { SCIC_SDS_CONTROLLER_T *this_controller; this_controller = (SCIC_SDS_CONTROLLER_T *)controller; ASSERT(this_controller->smu_registers != NULL); SMU_IMR_WRITE(this_controller, 0x00000000); } // --------------------------------------------------------------------------- void scic_controller_disable_interrupts( SCI_CONTROLLER_HANDLE_T controller ) { SCIC_SDS_CONTROLLER_T *this_controller; this_controller = (SCIC_SDS_CONTROLLER_T *)controller; ASSERT(this_controller->smu_registers != NULL); SMU_IMR_WRITE(this_controller, 0xffffffff); } // --------------------------------------------------------------------------- SCI_STATUS scic_controller_set_mode( SCI_CONTROLLER_HANDLE_T controller, SCI_CONTROLLER_MODE operating_mode ) { SCIC_SDS_CONTROLLER_T *this_controller = (SCIC_SDS_CONTROLLER_T*)controller; SCI_STATUS status = SCI_SUCCESS; SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER, "scic_controller_set_mode(0x%x, 0x%x) enter\n", controller, operating_mode )); if ( (this_controller->parent.state_machine.current_state_id == SCI_BASE_CONTROLLER_STATE_INITIALIZING) || (this_controller->parent.state_machine.current_state_id == SCI_BASE_CONTROLLER_STATE_INITIALIZED) ) { switch (operating_mode) { case SCI_MODE_SPEED: this_controller->remote_node_entries = MIN(this_controller->remote_node_entries, SCI_MAX_REMOTE_DEVICES); this_controller->task_context_entries = MIN(this_controller->task_context_entries, SCU_IO_REQUEST_COUNT); this_controller->uf_control.buffers.count = MIN(this_controller->uf_control.buffers.count, SCU_UNSOLICITED_FRAME_COUNT); this_controller->completion_event_entries = MIN(this_controller->completion_event_entries, SCU_EVENT_COUNT); this_controller->completion_queue_entries = MIN(this_controller->completion_queue_entries, SCU_COMPLETION_QUEUE_COUNT); scic_sds_controller_build_memory_descriptor_table(this_controller); break; case SCI_MODE_SIZE: this_controller->remote_node_entries = MIN(this_controller->remote_node_entries, SCI_MIN_REMOTE_DEVICES); this_controller->task_context_entries = MIN(this_controller->task_context_entries, SCI_MIN_IO_REQUESTS); this_controller->uf_control.buffers.count = MIN(this_controller->uf_control.buffers.count, SCU_MIN_UNSOLICITED_FRAMES); this_controller->completion_event_entries = MIN(this_controller->completion_event_entries, SCU_MIN_EVENTS); this_controller->completion_queue_entries = MIN(this_controller->completion_queue_entries, SCU_MIN_COMPLETION_QUEUE_ENTRIES); scic_sds_controller_build_memory_descriptor_table(this_controller); break; default: status = SCI_FAILURE_INVALID_PARAMETER_VALUE; break; } } else status = SCI_FAILURE_INVALID_STATE; return status; } /** * This method will reset the controller hardware. * * @param[in] this_controller The controller that is to be reset. */ void scic_sds_controller_reset_hardware( SCIC_SDS_CONTROLLER_T * this_controller ) { // Disable interrupts so we dont take any spurious interrupts scic_controller_disable_interrupts(this_controller); // Reset the SCU SMU_SMUSRCR_WRITE(this_controller, 0xFFFFFFFF); // Delay for 1ms to before clearing the CQP and UFQPR. scic_cb_stall_execution(1000); // The write to the CQGR clears the CQP SMU_CQGR_WRITE(this_controller, 0x00000000); // The write to the UFQGP clears the UFQPR SCU_UFQGP_WRITE(this_controller, 0x00000000); } // --------------------------------------------------------------------------- SCI_STATUS scic_user_parameters_set( SCI_CONTROLLER_HANDLE_T controller, SCIC_USER_PARAMETERS_T * scic_parms ) { SCIC_SDS_CONTROLLER_T * this_controller = (SCIC_SDS_CONTROLLER_T*)controller; if ( (this_controller->parent.state_machine.current_state_id == SCI_BASE_CONTROLLER_STATE_RESET) || (this_controller->parent.state_machine.current_state_id == SCI_BASE_CONTROLLER_STATE_INITIALIZING) || (this_controller->parent.state_machine.current_state_id == SCI_BASE_CONTROLLER_STATE_INITIALIZED) ) { U16 index; // Validate the user parameters. If they are not legal, then // return a failure. for (index = 0; index < SCI_MAX_PHYS; index++) { if (! ( scic_parms->sds1.phys[index].max_speed_generation <= SCIC_SDS_PARM_MAX_SPEED && scic_parms->sds1.phys[index].max_speed_generation > SCIC_SDS_PARM_NO_SPEED ) ) return SCI_FAILURE_INVALID_PARAMETER_VALUE; if ( (scic_parms->sds1.phys[index].in_connection_align_insertion_frequency < 3) || (scic_parms->sds1.phys[index].align_insertion_frequency == 0) || (scic_parms->sds1.phys[index].notify_enable_spin_up_insertion_frequency == 0) ) { return SCI_FAILURE_INVALID_PARAMETER_VALUE; } } if ( (scic_parms->sds1.stp_inactivity_timeout == 0) || (scic_parms->sds1.ssp_inactivity_timeout == 0) || (scic_parms->sds1.stp_max_occupancy_timeout == 0) || (scic_parms->sds1.ssp_max_occupancy_timeout == 0) || (scic_parms->sds1.no_outbound_task_timeout == 0) ) { return SCI_FAILURE_INVALID_PARAMETER_VALUE; } memcpy( (&this_controller->user_parameters), scic_parms, sizeof(*scic_parms)); return SCI_SUCCESS; } return SCI_FAILURE_INVALID_STATE; } // --------------------------------------------------------------------------- void scic_user_parameters_get( SCI_CONTROLLER_HANDLE_T controller, SCIC_USER_PARAMETERS_T * scic_parms ) { SCIC_SDS_CONTROLLER_T * this_controller = (SCIC_SDS_CONTROLLER_T*)controller; memcpy(scic_parms, (&this_controller->user_parameters), sizeof(*scic_parms)); } // --------------------------------------------------------------------------- SCI_STATUS scic_oem_parameters_set( SCI_CONTROLLER_HANDLE_T controller, SCIC_OEM_PARAMETERS_T * scic_parms, U8 scic_parms_version ) { SCIC_SDS_CONTROLLER_T * this_controller = (SCIC_SDS_CONTROLLER_T*)controller; SCI_BIOS_OEM_PARAM_ELEMENT_T *old_oem_params = (SCI_BIOS_OEM_PARAM_ELEMENT_T *)(&(scic_parms->sds1)); if ( (this_controller->parent.state_machine.current_state_id == SCI_BASE_CONTROLLER_STATE_RESET) || (this_controller->parent.state_machine.current_state_id == SCI_BASE_CONTROLLER_STATE_INITIALIZING) || (this_controller->parent.state_machine.current_state_id == SCI_BASE_CONTROLLER_STATE_INITIALIZED) ) { U16 index; U8 combined_phy_mask = 0; /* * Set the OEM parameter version for the controller. This comes * from the OEM parameter block header or the registry depending * on what WCDL is set to retrieve. */ this_controller->oem_parameters_version = scic_parms_version; // Validate the oem parameters. If they are not legal, then // return a failure. for(index=0; indexsds1.ports[index].phy_mask > SCIC_SDS_PARM_PHY_MASK_MAX) { return SCI_FAILURE_INVALID_PARAMETER_VALUE; } } for(index=0; indexsds1.phys[index].sas_address.sci_format.high == 0 && scic_parms->sds1.phys[index].sas_address.sci_format.low == 0 ) { return SCI_FAILURE_INVALID_PARAMETER_VALUE; } #if defined(PBG_HBA_A0_BUILD) || defined(PBG_HBA_A2_BUILD) || defined(PBG_HBA_BETA_BUILD) || defined(PBG_BUILD) if ( (scic_parms->sds1.phys[index].afe_tx_amp_control0 == 0) || (scic_parms->sds1.phys[index].afe_tx_amp_control1 == 0) || (scic_parms->sds1.phys[index].afe_tx_amp_control2 == 0) || (scic_parms->sds1.phys[index].afe_tx_amp_control3 == 0) ) { return SCI_FAILURE_INVALID_PARAMETER_VALUE; } #endif } if (scic_parms->sds1.controller.mode_type == SCIC_PORT_AUTOMATIC_CONFIGURATION_MODE) { for(index=0; indexsds1.ports[index].phy_mask != 0) { return SCI_FAILURE_INVALID_PARAMETER_VALUE; } } } else if (scic_parms->sds1.controller.mode_type == SCIC_PORT_MANUAL_CONFIGURATION_MODE) { for(index=0; indexsds1.ports[index].phy_mask; } if (combined_phy_mask == 0) { return SCI_FAILURE_INVALID_PARAMETER_VALUE; } } else { return SCI_FAILURE_INVALID_PARAMETER_VALUE; } if (scic_parms->sds1.controller.max_number_concurrent_device_spin_up > MAX_CONCURRENT_DEVICE_SPIN_UP_COUNT) { return SCI_FAILURE_INVALID_PARAMETER_VALUE; } if (old_oem_params->controller.do_enable_ssc != 0) { if ( (scic_parms_version == SCI_OEM_PARAM_VER_1_0) && (old_oem_params->controller.do_enable_ssc != 0x01)) return SCI_FAILURE_INVALID_PARAMETER_VALUE; if (scic_parms_version >= SCI_OEM_PARAM_VER_1_1) { SCI_BIOS_OEM_PARAM_ELEMENT_v_1_1_T *oem_params = (SCI_BIOS_OEM_PARAM_ELEMENT_v_1_1_T*)(&(scic_parms->sds1)); U8 test = oem_params->controller.ssc_sata_tx_spread_level; if ( !((test == 0x0) || (test == 0x2) || (test == 0x3) || (test == 0x6) || (test == 0x7)) ) return SCI_FAILURE_INVALID_PARAMETER_VALUE; test = oem_params->controller.ssc_sas_tx_spread_level; if (oem_params->controller.ssc_sas_tx_type == 0) { if ( !((test == 0x0) || (test == 0x2) || (test == 0x3)) ) return SCI_FAILURE_INVALID_PARAMETER_VALUE; } else if (oem_params->controller.ssc_sas_tx_type == 1) { if ( !((test == 0x0) || (test == 0x3) || (test == 0x6)) ) return SCI_FAILURE_INVALID_PARAMETER_VALUE; } } } memcpy( (&this_controller->oem_parameters), scic_parms, sizeof(*scic_parms)); return SCI_SUCCESS; } return SCI_FAILURE_INVALID_STATE; } // --------------------------------------------------------------------------- void scic_oem_parameters_get( SCI_CONTROLLER_HANDLE_T controller, SCIC_OEM_PARAMETERS_T * scic_parms ) { SCIC_SDS_CONTROLLER_T * this_controller = (SCIC_SDS_CONTROLLER_T*)controller; memcpy(scic_parms, (&this_controller->oem_parameters), sizeof(*scic_parms)); } // --------------------------------------------------------------------------- #if !defined(DISABLE_INTERRUPTS) #define INTERRUPT_COALESCE_TIMEOUT_BASE_RANGE_LOWER_BOUND_NS 853 #define INTERRUPT_COALESCE_TIMEOUT_BASE_RANGE_UPPER_BOUND_NS 1280 #define INTERRUPT_COALESCE_TIMEOUT_MAX_US 2700000 #define INTERRUPT_COALESCE_NUMBER_MAX 256 #define INTERRUPT_COALESCE_TIMEOUT_ENCODE_MIN 7 #define INTERRUPT_COALESCE_TIMEOUT_ENCODE_MAX 28 SCI_STATUS scic_controller_set_interrupt_coalescence( SCI_CONTROLLER_HANDLE_T controller, U32 coalesce_number, U32 coalesce_timeout ) { SCIC_SDS_CONTROLLER_T * scic_controller = (SCIC_SDS_CONTROLLER_T *)controller; U8 timeout_encode = 0; U32 min = 0; U32 max = 0; //Check if the input parameters fall in the range. if (coalesce_number > INTERRUPT_COALESCE_NUMBER_MAX) return SCI_FAILURE_INVALID_PARAMETER_VALUE; // Defined encoding for interrupt coalescing timeout: // Value Min Max Units // ----- --- --- ----- // 0 - - Disabled // 1 13.3 20.0 ns // 2 26.7 40.0 // 3 53.3 80.0 // 4 106.7 160.0 // 5 213.3 320.0 // 6 426.7 640.0 // 7 853.3 1280.0 // 8 1.7 2.6 us // 9 3.4 5.1 // 10 6.8 10.2 // 11 13.7 20.5 // 12 27.3 41.0 // 13 54.6 81.9 // 14 109.2 163.8 // 15 218.5 327.7 // 16 436.9 655.4 // 17 873.8 1310.7 // 18 1.7 2.6 ms // 19 3.5 5.2 // 20 7.0 10.5 // 21 14.0 21.0 // 22 28.0 41.9 // 23 55.9 83.9 // 24 111.8 167.8 // 25 223.7 335.5 // 26 447.4 671.1 // 27 894.8 1342.2 // 28 1.8 2.7 s // Others Undefined //Use the table above to decide the encode of interrupt coalescing timeout //value for register writing. if (coalesce_timeout == 0) timeout_encode = 0; else { //make the timeout value in unit of (10 ns). coalesce_timeout = coalesce_timeout * 100; min = INTERRUPT_COALESCE_TIMEOUT_BASE_RANGE_LOWER_BOUND_NS / 10; max = INTERRUPT_COALESCE_TIMEOUT_BASE_RANGE_UPPER_BOUND_NS / 10; //get the encode of timeout for register writing. for ( timeout_encode = INTERRUPT_COALESCE_TIMEOUT_ENCODE_MIN; timeout_encode <= INTERRUPT_COALESCE_TIMEOUT_ENCODE_MAX; timeout_encode++ ) { if (min <= coalesce_timeout && max > coalesce_timeout) break; else if (coalesce_timeout >= max && coalesce_timeout < min*2 && coalesce_timeout <= INTERRUPT_COALESCE_TIMEOUT_MAX_US*100) { if ( (coalesce_timeout-max) < (2*min - coalesce_timeout) ) break; else { timeout_encode++; break; } } else { max = max*2; min = min*2; } } if ( timeout_encode == INTERRUPT_COALESCE_TIMEOUT_ENCODE_MAX+1 ) //the value is out of range. return SCI_FAILURE_INVALID_PARAMETER_VALUE; } SMU_ICC_WRITE( scic_controller, (SMU_ICC_GEN_VAL(NUMBER, coalesce_number)| SMU_ICC_GEN_VAL(TIMER, timeout_encode)) ); scic_controller->interrupt_coalesce_number = (U16)coalesce_number; scic_controller->interrupt_coalesce_timeout = coalesce_timeout/100; return SCI_SUCCESS; } // --------------------------------------------------------------------------- void scic_controller_get_interrupt_coalescence( SCI_CONTROLLER_HANDLE_T controller, U32 * coalesce_number, U32 * coalesce_timeout ) { SCIC_SDS_CONTROLLER_T * scic_controller = (SCIC_SDS_CONTROLLER_T *)controller; *coalesce_number = scic_controller->interrupt_coalesce_number; *coalesce_timeout = scic_controller->interrupt_coalesce_timeout; } #endif // !defined(DISABLE_INTERRUPTS) // --------------------------------------------------------------------------- U32 scic_controller_get_scratch_ram_size( SCI_CONTROLLER_HANDLE_T controller ) { return SCU_SCRATCH_RAM_SIZE_IN_DWORDS; } // --------------------------------------------------------------------------- SCI_STATUS scic_controller_read_scratch_ram_dword( SCI_CONTROLLER_HANDLE_T controller, U32 offset, U32 * value ) { U32 zpt_index; SCIC_SDS_CONTROLLER_T * scic_controller = (SCIC_SDS_CONTROLLER_T *)controller; U32 status = SMU_SMUCSR_READ(scic_controller); //Check if the SCU Scratch RAM been initialized, if not return zeros if ((status & SCU_RAM_INIT_COMPLETED) != SCU_RAM_INIT_COMPLETED) { *value = 0x00000000; return SCI_SUCCESS; } if (offset < scic_controller_get_scratch_ram_size(controller)) { if(offset <= SCU_MAX_ZPT_DWORD_INDEX) { zpt_index = offset + (offset - (offset % 4)) + 4; *value = scu_controller_scratch_ram_register_read(scic_controller,zpt_index); } else //offset > SCU_MAX_ZPT_DWORD_INDEX { offset = offset - 132; zpt_index = offset + (offset - (offset % 4)) + 4; *value = scu_controller_scratch_ram_register_read_ext(scic_controller,zpt_index); } return SCI_SUCCESS; } else { return SCI_FAILURE_INVALID_PARAMETER_VALUE; } } // --------------------------------------------------------------------------- SCI_STATUS scic_controller_write_scratch_ram_dword( SCI_CONTROLLER_HANDLE_T controller, U32 offset, U32 value ) { U32 zpt_index; if (offset < scic_controller_get_scratch_ram_size(controller)) { SCIC_SDS_CONTROLLER_T * scic_controller = (SCIC_SDS_CONTROLLER_T *)controller; if(offset <= SCU_MAX_ZPT_DWORD_INDEX) { zpt_index = offset + (offset - (offset % 4)) + 4; scu_controller_scratch_ram_register_write(scic_controller,zpt_index,value); } else //offset > SCU_MAX_ZPT_DWORD_INDEX { offset = offset - 132; zpt_index = offset + (offset - (offset % 4)) + 4; scu_controller_scratch_ram_register_write_ext(scic_controller,zpt_index,value); } return SCI_SUCCESS; } else { return SCI_FAILURE_INVALID_PARAMETER_VALUE; } } // --------------------------------------------------------------------------- SCI_STATUS scic_controller_suspend( SCI_CONTROLLER_HANDLE_T controller ) { SCIC_SDS_CONTROLLER_T * this_controller = (SCIC_SDS_CONTROLLER_T*)controller; U8 index; // As a precaution, disable interrupts. The user is required // to re-enable interrupts if so desired after the call. scic_controller_disable_interrupts(controller); // Stop all the timers // Maybe change the states of the objects to avoid processing stuff. // Suspend the Ports in order to ensure no unexpected // frame reception occurs on the links from the target for (index = 0; index < SCI_MAX_PORTS; index++) scic_sds_port_suspend_port_task_scheduler( &(this_controller->port_table[index])); // Disable/Reset the completion queue and unsolicited frame // queue. SMU_CQGR_WRITE(this_controller, 0x00000000); SCU_UFQGP_WRITE(this_controller, 0x00000000); // Clear any interrupts that may be pending or may have been generated // by setting CQGR and CQPR back to 0 SMU_ISR_WRITE(this_controller, 0xFFFFFFFF); //reset the software get pointer to completion queue. this_controller->completion_queue_get = 0; return SCI_SUCCESS; } // --------------------------------------------------------------------------- SCI_STATUS scic_controller_resume( SCI_CONTROLLER_HANDLE_T controller ) { SCIC_SDS_CONTROLLER_T * this_controller = (SCIC_SDS_CONTROLLER_T*)controller; U8 index; // Initialize the completion queue and unsolicited frame queue. scic_sds_controller_initialize_completion_queue(this_controller); scic_sds_controller_initialize_unsolicited_frame_queue(this_controller); this_controller->restrict_completions = FALSE; // Release the port suspensions to allow for further successful // operation. for (index = 0; index < SCI_MAX_PORTS; index++) scic_sds_port_resume_port_task_scheduler( &(this_controller->port_table[index])); //check the link layer status register DWORD sync acquired bit to detect //link down event. If there is any link down event happened during controller //suspension, restart phy state machine. for (index = 0; index < SCI_MAX_PHYS; index ++) { SCIC_SDS_PHY_T * curr_phy = &this_controller->phy_table[index]; U32 link_layer_status = SCU_SAS_LLSTA_READ(curr_phy); if ((link_layer_status & SCU_SAS_LLSTA_DWORD_SYNCA_BIT) == 0) { //Need to put the phy back to start OOB. Then an appropriate link event //message will be send to scic user. scic_sds_phy_restart_starting_state(curr_phy); } } return SCI_SUCCESS; } // --------------------------------------------------------------------------- SCI_STATUS scic_controller_transition( SCI_CONTROLLER_HANDLE_T controller, BOOL restrict_completions ) { SCI_STATUS result = SCI_FAILURE_INVALID_STATE; SCIC_SDS_CONTROLLER_T * this_controller = (SCIC_SDS_CONTROLLER_T*)controller; U8 index; SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER, "scic_controller_transition(0x%x) enter\n", controller )); if (this_controller->parent.state_machine.current_state_id == SCI_BASE_CONTROLLER_STATE_READY) { // Ensure that there are no outstanding IO operations at this // time. for (index = 0; index < SCI_MAX_PORTS; index++) { if (this_controller->port_table[index].started_request_count != 0) return result; } scic_controller_suspend(controller); // Loop through the memory descriptor list and reprogram // the silicon memory registers accordingly. result = scic_sds_controller_validate_memory_descriptor_table( this_controller); if (result == SCI_SUCCESS) { scic_sds_controller_ram_initialization(this_controller); this_controller->restrict_completions = restrict_completions; } scic_controller_resume(controller); } return result; } // --------------------------------------------------------------------------- SCI_STATUS scic_controller_get_max_ports( SCI_CONTROLLER_HANDLE_T controller, U8 * count ) { *count = SCI_MAX_PORTS; return SCI_SUCCESS; } // --------------------------------------------------------------------------- SCI_STATUS scic_controller_get_max_phys( SCI_CONTROLLER_HANDLE_T controller, U8 * count ) { *count = SCI_MAX_PHYS; return SCI_SUCCESS; } //****************************************************************************** //* CONTROLLER STATE MACHINE //****************************************************************************** /** * This macro returns the maximum number of logical ports supported by the * hardware. The caller passes in the value read from the device context * capacity register and this macro will mash and shift the value * appropriately. */ #define smu_dcc_get_max_ports(dcc_value) \ ( \ ( ((U32)((dcc_value) & SMU_DEVICE_CONTEXT_CAPACITY_MAX_LP_MASK)) \ >> SMU_DEVICE_CONTEXT_CAPACITY_MAX_LP_SHIFT ) + 1\ ) /** * This macro returns the maximum number of task contexts supported by the * hardware. The caller passes in the value read from the device context * capacity register and this macro will mash and shift the value * appropriately. */ #define smu_dcc_get_max_task_context(dcc_value) \ ( \ ( ((U32)((dcc_value) & SMU_DEVICE_CONTEXT_CAPACITY_MAX_TC_MASK)) \ >> SMU_DEVICE_CONTEXT_CAPACITY_MAX_TC_SHIFT ) + 1\ ) /** * This macro returns the maximum number of remote node contexts supported * by the hardware. The caller passes in the value read from the device * context capacity register and this macro will mash and shift the value * appropriately. */ #define smu_dcc_get_max_remote_node_context(dcc_value) \ ( \ ( ( (U32)((dcc_value) & SMU_DEVICE_CONTEXT_CAPACITY_MAX_RNC_MASK) )\ >> SMU_DEVICE_CONTEXT_CAPACITY_MAX_RNC_SHIFT ) + 1\ ) //***************************************************************************** //* DEFAULT STATE HANDLERS //***************************************************************************** /** * This method is called when the SCIC_SDS_CONTROLLER default start * io/task handler is in place. * - Issue a warning message * * @param[in] controller This is SCI_BASE_CONTROLLER object which is cast * into a SCIC_SDS_CONTROLLER object. * @param[in] remote_device This is SCI_BASE_REMOTE_DEVICE which, if it was * used, would be cast to a SCIC_SDS_REMOTE_DEVICE. * @param[in] io_request This is the SCI_BASE_REQUEST which, if it was used, * would be cast to a SCIC_SDS_IO_REQUEST. * @param[in] io_tag This is the IO tag to be assigned to the IO request or * SCI_CONTROLLER_INVALID_IO_TAG. * * @return SCI_STATUS * @retval SCI_FAILURE_INVALID_STATE */ static SCI_STATUS scic_sds_controller_default_start_operation_handler( SCI_BASE_CONTROLLER_T *controller, SCI_BASE_REMOTE_DEVICE_T *remote_device, SCI_BASE_REQUEST_T *io_request, U16 io_tag ) { SCIC_SDS_CONTROLLER_T *this_controller; this_controller = (SCIC_SDS_CONTROLLER_T *)controller; SCIC_LOG_WARNING(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_CONTROLLER, "SCIC Controller requested to start an io/task from invalid state %d\n", sci_base_state_machine_get_state( scic_sds_controller_get_base_state_machine(this_controller)) )); return SCI_FAILURE_INVALID_STATE; } /** * This method is called when the SCIC_SDS_CONTROLLER default * request handler is in place. * - Issue a warning message * * @param[in] controller This is SCI_BASE_CONTROLLER object which is cast * into a SCIC_SDS_CONTROLLER object. * @param[in] remote_device This is SCI_BASE_REMOTE_DEVICE which, if it was * used, would be cast to a SCIC_SDS_REMOTE_DEVICE. * @param[in] io_request This is the SCI_BASE_REQUEST which, if it was used, * would be cast to a SCIC_SDS_IO_REQUEST. * * @return SCI_STATUS * @retval SCI_FAILURE_INVALID_STATE */ static SCI_STATUS scic_sds_controller_default_request_handler( SCI_BASE_CONTROLLER_T *controller, SCI_BASE_REMOTE_DEVICE_T *remote_device, SCI_BASE_REQUEST_T *io_request ) { SCIC_SDS_CONTROLLER_T *this_controller; this_controller = (SCIC_SDS_CONTROLLER_T *)controller; SCIC_LOG_WARNING(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_CONTROLLER, "SCIC Controller request operation from invalid state %d\n", sci_base_state_machine_get_state( scic_sds_controller_get_base_state_machine(this_controller)) )); return SCI_FAILURE_INVALID_STATE; } //***************************************************************************** //* GENERAL (COMMON) STATE HANDLERS //***************************************************************************** /** * This method is called when the SCIC_SDS_CONTROLLER is in the ready state * reset handler is in place. * - Transition to SCI_BASE_CONTROLLER_STATE_RESETTING * * @param[in] controller The SCI_BASE_CONTROLLER object which is cast into a * SCIC_SDS_CONTROLLER object. * * @return SCI_STATUS * @retval SCI_SUCCESS */ static SCI_STATUS scic_sds_controller_general_reset_handler( SCI_BASE_CONTROLLER_T *controller ) { SCIC_SDS_CONTROLLER_T *this_controller; this_controller = (SCIC_SDS_CONTROLLER_T *)controller; SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER, "scic_sds_controller_resetting_state_enter(0x%x) enter\n", controller )); //Release resource. So far only resource to be released are timers. scic_sds_controller_release_resource(this_controller); // The reset operation is not a graceful cleanup just perform the state // transition. sci_base_state_machine_change_state( scic_sds_controller_get_base_state_machine(this_controller), SCI_BASE_CONTROLLER_STATE_RESETTING ); return SCI_SUCCESS; } //***************************************************************************** //* RESET STATE HANDLERS //***************************************************************************** /** * This method is the SCIC_SDS_CONTROLLER initialize handler for the reset * state. * - Currently this function does nothing * * @param[in] controller This is the SCI_BASE_CONTROLLER object which is cast * into a SCIC_SDS_CONTROLLER object. * * @return SCI_STATUS * @retval SCI_FAILURE * * @todo This function is not yet implemented and is a valid request from the * reset state. */ static SCI_STATUS scic_sds_controller_reset_state_initialize_handler( SCI_BASE_CONTROLLER_T *controller ) { U32 index; SCI_STATUS result = SCI_SUCCESS; SCIC_SDS_CONTROLLER_T *this_controller; this_controller = (SCIC_SDS_CONTROLLER_T *)controller; SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_INITIALIZATION, "scic_sds_controller_reset_state_initialize_handler(0x%x) enter\n", controller )); sci_base_state_machine_change_state( scic_sds_controller_get_base_state_machine(this_controller), SCI_BASE_CONTROLLER_STATE_INITIALIZING ); this_controller->timeout_timer = scic_cb_timer_create( controller, scic_sds_controller_timeout_handler, controller ); scic_sds_controller_initialize_power_control(this_controller); /// todo: This should really be done in the reset state enter but /// the controller has not yet been initialized before getting /// to the reset enter state so the PCI BAR is not yet assigned scic_sds_controller_reset_hardware(this_controller); #if defined(ARLINGTON_BUILD) scic_sds_controller_lex_atux_initialization(this_controller); #elif defined(PLEASANT_RIDGE_BUILD) \ || defined(PBG_HBA_A0_BUILD) \ || defined(PBG_HBA_A2_BUILD) scic_sds_controller_afe_initialization(this_controller); #elif defined(PBG_HBA_BETA_BUILD) || defined(PBG_BUILD) // There is nothing to do here for B0 since we do not have to // program the AFE registers. /// @todo The AFE settings are supposed to be correct for the B0 but /// presently they seem to be wrong. scic_sds_controller_afe_initialization(this_controller); #else // !defined(ARLINGTON_BUILD) && !defined(PLEASANT_RIDGE_BUILD) // What other systems do we want to add here? #endif // !defined(ARLINGTON_BUILD) && !defined(PLEASANT_RIDGE_BUILD) if (SCI_SUCCESS == result) { U32 status; U32 terminate_loop; // Take the hardware out of reset SMU_SMUSRCR_WRITE(this_controller, 0x00000000); /// @todo Provide meaningfull error code for hardware failure //result = SCI_FAILURE_CONTROLLER_HARDWARE; result = SCI_FAILURE; terminate_loop = 100; while (terminate_loop-- && (result != SCI_SUCCESS)) { // Loop until the hardware reports success scic_cb_stall_execution(SCU_CONTEXT_RAM_INIT_STALL_TIME); status = SMU_SMUCSR_READ(this_controller); if ((status & SCU_RAM_INIT_COMPLETED) == SCU_RAM_INIT_COMPLETED) { result = SCI_SUCCESS; } } } #ifdef ARLINGTON_BUILD scic_sds_controller_enable_chipwatch(this_controller); #endif if (result == SCI_SUCCESS) { U32 max_supported_ports; U32 max_supported_devices; U32 max_supported_io_requests; U32 device_context_capacity; // Determine what are the actaul device capacities that the // hardware will support device_context_capacity = SMU_DCC_READ(this_controller); max_supported_ports = smu_dcc_get_max_ports(device_context_capacity); max_supported_devices = smu_dcc_get_max_remote_node_context(device_context_capacity); max_supported_io_requests = smu_dcc_get_max_task_context(device_context_capacity); // Make all PEs that are unassigned match up with the logical ports for (index = 0; index < max_supported_ports; index++) { scu_register_write( this_controller, this_controller->scu_registers->peg0.ptsg.protocol_engine[index], index ); } // Now that we have the correct hardware reported minimum values // build the MDL for the controller. Default to a performance // configuration. scic_controller_set_mode(this_controller, SCI_MODE_SPEED); // Record the smaller of the two capacity values this_controller->logical_port_entries = MIN(max_supported_ports, this_controller->logical_port_entries); this_controller->task_context_entries = MIN(max_supported_io_requests, this_controller->task_context_entries); this_controller->remote_node_entries = MIN(max_supported_devices, this_controller->remote_node_entries); } // Initialize hardware PCI Relaxed ordering in DMA engines if (result == SCI_SUCCESS) { U32 dma_configuration; // Configure the payload DMA dma_configuration = SCU_PDMACR_READ(this_controller); dma_configuration |= SCU_PDMACR_GEN_BIT(PCI_RELAXED_ORDERING_ENABLE); SCU_PDMACR_WRITE(this_controller, dma_configuration); // Configure the control DMA dma_configuration = SCU_CDMACR_READ(this_controller); dma_configuration |= SCU_CDMACR_GEN_BIT(PCI_RELAXED_ORDERING_ENABLE); SCU_CDMACR_WRITE(this_controller, dma_configuration); } // Initialize the PHYs before the PORTs because the PHY registers // are accessed during the port initialization. if (result == SCI_SUCCESS) { // Initialize the phys for (index = 0; (result == SCI_SUCCESS) && (index < SCI_MAX_PHYS); index++) { result = scic_sds_phy_initialize( &this_controller->phy_table[index], &this_controller->scu_registers->peg0.pe[index].tl, &this_controller->scu_registers->peg0.pe[index].ll ); } } //Initialize the SGPIO Unit for HARDWARE controlled SGPIO if(result == SCI_SUCCESS) { scic_sgpio_hardware_initialize(this_controller); } if (result == SCI_SUCCESS) { // Initialize the logical ports for (index = 0; (index < this_controller->logical_port_entries) && (result == SCI_SUCCESS); index++) { result = scic_sds_port_initialize( &this_controller->port_table[index], &this_controller->scu_registers->peg0.ptsg.port[index], &this_controller->scu_registers->peg0.ptsg.protocol_engine, &this_controller->scu_registers->peg0.viit[index] ); } } if (SCI_SUCCESS == result) { result = scic_sds_port_configuration_agent_initialize( this_controller, &this_controller->port_agent ); } // Advance the controller state machine if (result == SCI_SUCCESS) { sci_base_state_machine_change_state( scic_sds_controller_get_base_state_machine(this_controller), SCI_BASE_CONTROLLER_STATE_INITIALIZED ); } else { //stay in the same state and release the resource scic_sds_controller_release_resource(this_controller); SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_INITIALIZATION, "Invalid Port Configuration from scic_sds_controller_reset_state_initialize_handler(0x%x) \n", controller )); } return result; } //***************************************************************************** //* INITIALIZED STATE HANDLERS //***************************************************************************** /** * This method is the SCIC_SDS_CONTROLLER start handler for the initialized * state. * - Validate we have a good memory descriptor table * - Initialze the physical memory before programming the hardware * - Program the SCU hardware with the physical memory addresses passed in * the memory descriptor table. * - Initialzie the TCi pool * - Initialize the RNi pool * - Initialize the completion queue * - Initialize the unsolicited frame data * - Take the SCU port task scheduler out of reset * - Start the first phy object. * - Transition to SCI_BASE_CONTROLLER_STATE_STARTING. * * @param[in] controller This is the SCI_BASE_CONTROLLER object which is cast * into a SCIC_SDS_CONTROLLER object. * @param[in] timeout This is the allowed time for the controller object to * reach the started state. * * @return SCI_STATUS * @retval SCI_SUCCESS if all of the controller start operations complete * @retval SCI_FAILURE_UNSUPPORTED_INFORMATION_FIELD if one or more of the * memory descriptor fields is invalid. */ static SCI_STATUS scic_sds_controller_initialized_state_start_handler( SCI_BASE_CONTROLLER_T * controller, U32 timeout ) { U16 index; SCI_STATUS result; SCIC_SDS_CONTROLLER_T * this_controller; this_controller = (SCIC_SDS_CONTROLLER_T *)controller; // Make sure that the SCI User filled in the memory descriptor table correctly result = scic_sds_controller_validate_memory_descriptor_table(this_controller); if (result == SCI_SUCCESS) { // The memory descriptor list looks good so program the hardware scic_sds_controller_ram_initialization(this_controller); } if (SCI_SUCCESS == result) { // Build the TCi free pool sci_pool_initialize(this_controller->tci_pool); for (index = 0; index < this_controller->task_context_entries; index++) { sci_pool_put(this_controller->tci_pool, index); } // Build the RNi free pool scic_sds_remote_node_table_initialize( &this_controller->available_remote_nodes, this_controller->remote_node_entries ); } if (SCI_SUCCESS == result) { // Before anything else lets make sure we will not be interrupted // by the hardware. scic_controller_disable_interrupts(controller); // Enable the port task scheduler scic_sds_controller_enable_port_task_scheduler(this_controller); // Assign all the task entries to this controller physical function scic_sds_controller_assign_task_entries(this_controller); // Now initialze the completion queue scic_sds_controller_initialize_completion_queue(this_controller); // Initialize the unsolicited frame queue for use scic_sds_controller_initialize_unsolicited_frame_queue(this_controller); // Setup the phy start timer result = scic_sds_controller_initialize_phy_startup(this_controller); } // Start all of the ports on this controller for ( index = 0; (index < this_controller->logical_port_entries) && (result == SCI_SUCCESS); index++ ) { result = this_controller->port_table[index]. state_handlers->parent.start_handler(&this_controller->port_table[index].parent); } if (SCI_SUCCESS == result) { scic_sds_controller_start_next_phy(this_controller); // See if the user requested to timeout this operation. if (timeout != 0) scic_cb_timer_start(controller, this_controller->timeout_timer, timeout); sci_base_state_machine_change_state( scic_sds_controller_get_base_state_machine(this_controller), SCI_BASE_CONTROLLER_STATE_STARTING ); } return result; } //***************************************************************************** //* STARTING STATE HANDLERS //***************************************************************************** /** * This method is called when the SCIC_SDS_CONTROLLER is in the starting state * link up handler is called. This method will perform the following: * - Stop the phy timer * - Start the next phy * - Report the link up condition to the port object * * @param[in] controller This is SCIC_SDS_CONTROLLER which receives the link up * notification. * @param[in] port This is SCIC_SDS_PORT with which the phy is associated. * @param[in] phy This is the SCIC_SDS_PHY which has gone link up. * * @return none */ static void scic_sds_controller_starting_state_link_up_handler( SCIC_SDS_CONTROLLER_T *this_controller, SCIC_SDS_PORT_T *port, SCIC_SDS_PHY_T *phy ) { scic_sds_controller_phy_timer_stop(this_controller); this_controller->port_agent.link_up_handler( this_controller, &this_controller->port_agent, port, phy ); //scic_sds_port_link_up(port, phy); scic_sds_controller_start_next_phy(this_controller); } /** * This method is called when the SCIC_SDS_CONTROLLER is in the starting state * link down handler is called. * - Report the link down condition to the port object * * @param[in] controller This is SCIC_SDS_CONTROLLER which receives the * link down notification. * @param[in] port This is SCIC_SDS_PORT with which the phy is associated. * @param[in] phy This is the SCIC_SDS_PHY which has gone link down. * * @return none */ static void scic_sds_controller_starting_state_link_down_handler( SCIC_SDS_CONTROLLER_T *this_controller, SCIC_SDS_PORT_T *port, SCIC_SDS_PHY_T *phy ) { this_controller->port_agent.link_down_handler( this_controller, &this_controller->port_agent, port, phy ); //scic_sds_port_link_down(port, phy); } //***************************************************************************** //* READY STATE HANDLERS //***************************************************************************** /** * This method is called when the SCIC_SDS_CONTROLLER is in the ready state * stop handler is called. * - Start the timeout timer * - Transition to SCI_BASE_CONTROLLER_STATE_STOPPING. * * @param[in] controller The SCI_BASE_CONTROLLER object which is cast into a * SCIC_SDS_CONTROLLER object. * @param[in] timeout The timeout for when the stop operation should report a * failure. * * @return SCI_STATUS * @retval SCI_SUCCESS */ static SCI_STATUS scic_sds_controller_ready_state_stop_handler( SCI_BASE_CONTROLLER_T *controller, U32 timeout ) { SCIC_SDS_CONTROLLER_T *this_controller; this_controller = (SCIC_SDS_CONTROLLER_T *)controller; // See if the user requested to timeout this operation if (timeout != 0) scic_cb_timer_start(controller, this_controller->timeout_timer, timeout); sci_base_state_machine_change_state( scic_sds_controller_get_base_state_machine(this_controller), SCI_BASE_CONTROLLER_STATE_STOPPING ); return SCI_SUCCESS; } /** * This method is called when the SCIC_SDS_CONTROLLER is in the ready state * and the start io handler is called. * - Start the io request on the remote device * - if successful * - assign the io_request to the io_request_table * - post the request to the hardware * * @param[in] controller This is SCI_BASE_CONTROLLER object which is cast * into a SCIC_SDS_CONTROLLER object. * @param[in] remote_device This is SCI_BASE_REMOTE_DEVICE which is cast to a * SCIC_SDS_REMOTE_DEVICE object. * @param[in] io_request This is the SCI_BASE_REQUEST which is cast to a * SCIC_SDS_IO_REQUEST object. * @param[in] io_tag This is the IO tag to be assigned to the IO request or * SCI_CONTROLLER_INVALID_IO_TAG. * * @return SCI_STATUS * @retval SCI_SUCCESS if the start io operation succeeds * @retval SCI_FAILURE_INSUFFICIENT_RESOURCES if the IO tag could not be * allocated for the io request. * @retval SCI_FAILURE_INVALID_STATE if one or more objects are not in a valid * state to accept io requests. * * @todo How does the io_tag parameter get assigned to the io request? */ static SCI_STATUS scic_sds_controller_ready_state_start_io_handler( SCI_BASE_CONTROLLER_T *controller, SCI_BASE_REMOTE_DEVICE_T *remote_device, SCI_BASE_REQUEST_T *io_request, U16 io_tag ) { SCI_STATUS status; SCIC_SDS_CONTROLLER_T *this_controller; SCIC_SDS_REQUEST_T *the_request; SCIC_SDS_REMOTE_DEVICE_T *the_device; this_controller = (SCIC_SDS_CONTROLLER_T *)controller; the_request = (SCIC_SDS_REQUEST_T *)io_request; the_device = (SCIC_SDS_REMOTE_DEVICE_T *)remote_device; status = scic_sds_remote_device_start_io(this_controller, the_device, the_request); if (status == SCI_SUCCESS) { this_controller->io_request_table[ scic_sds_io_tag_get_index(the_request->io_tag)] = the_request; scic_sds_controller_post_request( this_controller, scic_sds_request_get_post_context(the_request) ); } return status; } /** * This method is called when the SCIC_SDS_CONTROLLER is in the ready state * and the complete io handler is called. * - Complete the io request on the remote device * - if successful * - remove the io_request to the io_request_table * * @param[in] controller This is SCI_BASE_CONTROLLER object which is cast * into a SCIC_SDS_CONTROLLER object. * @param[in] remote_device This is SCI_BASE_REMOTE_DEVICE which is cast to a * SCIC_SDS_REMOTE_DEVICE object. * @param[in] io_request This is the SCI_BASE_REQUEST which is cast to a * SCIC_SDS_IO_REQUEST object. * * @return SCI_STATUS * @retval SCI_SUCCESS if the start io operation succeeds * @retval SCI_FAILURE_INVALID_STATE if one or more objects are not in a valid * state to accept io requests. */ static SCI_STATUS scic_sds_controller_ready_state_complete_io_handler( SCI_BASE_CONTROLLER_T *controller, SCI_BASE_REMOTE_DEVICE_T *remote_device, SCI_BASE_REQUEST_T *io_request ) { U16 index; SCI_STATUS status; SCIC_SDS_CONTROLLER_T *this_controller; SCIC_SDS_REQUEST_T *the_request; SCIC_SDS_REMOTE_DEVICE_T *the_device; this_controller = (SCIC_SDS_CONTROLLER_T *)controller; the_request = (SCIC_SDS_REQUEST_T *)io_request; the_device = (SCIC_SDS_REMOTE_DEVICE_T *)remote_device; status = scic_sds_remote_device_complete_io( this_controller, the_device, the_request); if (status == SCI_SUCCESS) { index = scic_sds_io_tag_get_index(the_request->io_tag); this_controller->io_request_table[index] = SCI_INVALID_HANDLE; } return status; } /** * This method is called when the SCIC_SDS_CONTROLLER is in the ready state * and the continue io handler is called. * * @param[in] controller This is SCI_BASE_CONTROLLER object which is cast * into a SCIC_SDS_CONTROLLER object. * @param[in] remote_device This is SCI_BASE_REMOTE_DEVICE which is cast to a * SCIC_SDS_REMOTE_DEVICE object. * @param[in] io_request This is the SCI_BASE_REQUEST which is cast to a * SCIC_SDS_IO_REQUEST object. * * @return SCI_STATUS */ static SCI_STATUS scic_sds_controller_ready_state_continue_io_handler( SCI_BASE_CONTROLLER_T *controller, SCI_BASE_REMOTE_DEVICE_T *remote_device, SCI_BASE_REQUEST_T *io_request ) { SCIC_SDS_CONTROLLER_T *this_controller; SCIC_SDS_REQUEST_T *the_request; the_request = (SCIC_SDS_REQUEST_T *)io_request; this_controller = (SCIC_SDS_CONTROLLER_T *)controller; this_controller->io_request_table[ scic_sds_io_tag_get_index(the_request->io_tag)] = the_request; scic_sds_controller_post_request( this_controller, scic_sds_request_get_post_context(the_request) ); return SCI_SUCCESS; } /** * This method is called when the SCIC_SDS_CONTROLLER is in the ready state * and the start task handler is called. * - The remote device is requested to start the task request * - if successful * - assign the task to the io_request_table * - post the request to the SCU hardware * * @param[in] controller This is SCI_BASE_CONTROLLER object which is cast * into a SCIC_SDS_CONTROLLER object. * @param[in] remote_device This is SCI_BASE_REMOTE_DEVICE which is cast to a * SCIC_SDS_REMOTE_DEVICE object. * @param[in] io_request This is the SCI_BASE_REQUEST which is cast to a * SCIC_SDS_IO_REQUEST object. * @param[in] task_tag This is the task tag to be assigned to the task request * or SCI_CONTROLLER_INVALID_IO_TAG. * * @return SCI_STATUS * @retval SCI_SUCCESS if the start io operation succeeds * @retval SCI_FAILURE_INSUFFICIENT_RESOURCES if the IO tag could not be * allocated for the io request. * @retval SCI_FAILURE_INVALID_STATE if one or more objects are not in a valid * state to accept io requests. * * @todo How does the io tag get assigned in this code path? */ static SCI_STATUS scic_sds_controller_ready_state_start_task_handler( SCI_BASE_CONTROLLER_T *controller, SCI_BASE_REMOTE_DEVICE_T *remote_device, SCI_BASE_REQUEST_T *io_request, U16 task_tag ) { SCIC_SDS_CONTROLLER_T *this_controller = (SCIC_SDS_CONTROLLER_T *) controller; SCIC_SDS_REQUEST_T *the_request = (SCIC_SDS_REQUEST_T *) io_request; SCIC_SDS_REMOTE_DEVICE_T *the_device = (SCIC_SDS_REMOTE_DEVICE_T *) remote_device; SCI_STATUS status; status = scic_sds_remote_device_start_task( this_controller, the_device, the_request ); if (status == SCI_SUCCESS) { this_controller->io_request_table[ scic_sds_io_tag_get_index(the_request->io_tag)] = the_request; scic_sds_controller_post_request( this_controller, scic_sds_request_get_post_context(the_request) ); } else if (status == SCI_FAILURE_RESET_DEVICE_PARTIAL_SUCCESS) { this_controller->io_request_table[ scic_sds_io_tag_get_index(the_request->io_tag)] = the_request; //We will let framework know this task request started successfully, //although core is still woring on starting the request (to post tc when //RNC is resumed.) status = SCI_SUCCESS; } return status; } /** * This method is called when the SCIC_SDS_CONTROLLER is in the ready state * and the terminate request handler is called. * - call the io request terminate function * - if successful * - post the terminate request to the SCU hardware * * @param[in] controller This is SCI_BASE_CONTROLLER object which is cast * into a SCIC_SDS_CONTROLLER object. * @param[in] remote_device This is SCI_BASE_REMOTE_DEVICE which is cast to a * SCIC_SDS_REMOTE_DEVICE object. * @param[in] io_request This is the SCI_BASE_REQUEST which is cast to a * SCIC_SDS_IO_REQUEST object. * * @return SCI_STATUS * @retval SCI_SUCCESS if the start io operation succeeds * @retval SCI_FAILURE_INVALID_STATE if one or more objects are not in a valid * state to accept io requests. */ static SCI_STATUS scic_sds_controller_ready_state_terminate_request_handler( SCI_BASE_CONTROLLER_T *controller, SCI_BASE_REMOTE_DEVICE_T *remote_device, SCI_BASE_REQUEST_T *io_request ) { SCIC_SDS_CONTROLLER_T *this_controller = (SCIC_SDS_CONTROLLER_T *) controller; SCIC_SDS_REQUEST_T *the_request = (SCIC_SDS_REQUEST_T *) io_request; SCI_STATUS status; status = scic_sds_io_request_terminate(the_request); if (status == SCI_SUCCESS) { // Utilize the original post context command and or in the POST_TC_ABORT // request sub-type. scic_sds_controller_post_request( this_controller, scic_sds_request_get_post_context(the_request) | SCU_CONTEXT_COMMAND_REQUEST_POST_TC_ABORT ); } return status; } /** * This method is called when the SCIC_SDS_CONTROLLER is in the starting state * link up handler is called. This method will perform the following: * - Stop the phy timer * - Start the next phy * - Report the link up condition to the port object * * @param[in] controller This is SCIC_SDS_CONTROLLER which receives the link up * notification. * @param[in] port This is SCIC_SDS_PORT with which the phy is associated. * @param[in] phy This is the SCIC_SDS_PHY which has gone link up. * * @return none */ static void scic_sds_controller_ready_state_link_up_handler( SCIC_SDS_CONTROLLER_T *this_controller, SCIC_SDS_PORT_T *port, SCIC_SDS_PHY_T *phy ) { this_controller->port_agent.link_up_handler( this_controller, &this_controller->port_agent, port, phy ); } /** * This method is called when the SCIC_SDS_CONTROLLER is in the starting state * link down handler is called. * - Report the link down condition to the port object * * @param[in] controller This is SCIC_SDS_CONTROLLER which receives the * link down notification. * @param[in] port This is SCIC_SDS_PORT with which the phy is associated. * @param[in] phy This is the SCIC_SDS_PHY which has gone link down. * * @return none */ static void scic_sds_controller_ready_state_link_down_handler( SCIC_SDS_CONTROLLER_T *this_controller, SCIC_SDS_PORT_T *port, SCIC_SDS_PHY_T *phy ) { this_controller->port_agent.link_down_handler( this_controller, &this_controller->port_agent, port, phy ); } //***************************************************************************** //* STOPPING STATE HANDLERS //***************************************************************************** /** * This method is called when the SCIC_SDS_CONTROLLER is in a stopping state * and the complete io handler is called. * - This function is not yet implemented * * @param[in] controller This is SCI_BASE_CONTROLLER object which is cast * into a SCIC_SDS_CONTROLLER object. * @param[in] remote_device This is SCI_BASE_REMOTE_DEVICE which is cast to a * SCIC_SDS_REMOTE_DEVICE object. * @param[in] io_request This is the SCI_BASE_REQUEST which is cast to a * SCIC_SDS_IO_REQUEST object. * * @return SCI_STATUS * @retval SCI_FAILURE */ static SCI_STATUS scic_sds_controller_stopping_state_complete_io_handler( SCI_BASE_CONTROLLER_T *controller, SCI_BASE_REMOTE_DEVICE_T *remote_device, SCI_BASE_REQUEST_T *io_request ) { SCIC_SDS_CONTROLLER_T *this_controller; this_controller = (SCIC_SDS_CONTROLLER_T *)controller; /// @todo Implement this function return SCI_FAILURE; } /** * This method is called when the SCIC_SDS_CONTROLLER is in a stopping state * and the a remote device has stopped. * * @param[in] controller This is SCI_BASE_CONTROLLER object which is cast * into a SCIC_SDS_CONTROLLER object. * @param[in] remote_device This is SCI_BASE_REMOTE_DEVICE which is cast to a * SCIC_SDS_REMOTE_DEVICE object. * * @return none */ static void scic_sds_controller_stopping_state_device_stopped_handler( SCIC_SDS_CONTROLLER_T * controller, SCIC_SDS_REMOTE_DEVICE_T * remote_device ) { if (!scic_sds_controller_has_remote_devices_stopping(controller)) { sci_base_state_machine_change_state( &controller->parent.state_machine, SCI_BASE_CONTROLLER_STATE_STOPPED ); } } //***************************************************************************** //* STOPPED STATE HANDLERS //***************************************************************************** //***************************************************************************** //* FAILED STATE HANDLERS //***************************************************************************** /** * This method is called when the SCIC_SDS_CONTROLLER failed state start * io/task handler is in place. * - Issue a warning message * * @param[in] controller This is SCI_BASE_CONTROLLER object which is cast * into a SCIC_SDS_CONTROLLER object. * @param[in] remote_device This is SCI_BASE_REMOTE_DEVICE which, if it was * used, would be cast to a SCIC_SDS_REMOTE_DEVICE. * @param[in] io_request This is the SCI_BASE_REQUEST which, if it was used, * would be cast to a SCIC_SDS_IO_REQUEST. * @param[in] io_tag This is the IO tag to be assigned to the IO request or * SCI_CONTROLLER_INVALID_IO_TAG. * * @return SCI_FAILURE * @retval SCI_FAILURE */ static SCI_STATUS scic_sds_controller_failed_state_start_operation_handler( SCI_BASE_CONTROLLER_T *controller, SCI_BASE_REMOTE_DEVICE_T *remote_device, SCI_BASE_REQUEST_T *io_request, U16 io_tag ) { SCIC_SDS_CONTROLLER_T *this_controller; this_controller = (SCIC_SDS_CONTROLLER_T *)controller; SCIC_LOG_WARNING(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_CONTROLLER, "SCIC Controller requested to start an io/task from failed state %d\n", sci_base_state_machine_get_state( scic_sds_controller_get_base_state_machine(this_controller)) )); return SCI_FAILURE; } /** * This method is called when the SCIC_SDS_CONTROLLER is in the failed state * reset handler is in place. * - Transition to SCI_BASE_CONTROLLER_STATE_RESETTING * * @param[in] controller The SCI_BASE_CONTROLLER object which is cast into a * SCIC_SDS_CONTROLLER object. * * @return SCI_STATUS * @retval SCI_FAILURE if fatal memory error occurred */ static SCI_STATUS scic_sds_controller_failed_state_reset_handler( SCI_BASE_CONTROLLER_T *controller ) { SCIC_SDS_CONTROLLER_T *this_controller; this_controller = (SCIC_SDS_CONTROLLER_T *)controller; if (this_controller->parent.error == SCI_CONTROLLER_FATAL_MEMORY_ERROR) { SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER, "scic_sds_controller_resetting_state_enter(0x%x) enter\n not allowed with fatal memory error", controller )); return SCI_FAILURE; } else { return scic_sds_controller_general_reset_handler(controller); } } /** * This method is called when the SCIC_SDS_CONTROLLER is in the failed state * and the terminate request handler is called. * - call the io request terminate function * - if successful * - post the terminate request to the SCU hardware * * @param[in] controller This is SCI_BASE_CONTROLLER object which is cast * into a SCIC_SDS_CONTROLLER object. * @param[in] remote_device This is SCI_BASE_REMOTE_DEVICE which is cast to a * SCIC_SDS_REMOTE_DEVICE object. * @param[in] io_request This is the SCI_BASE_REQUEST which is cast to a * SCIC_SDS_IO_REQUEST object. * * @return SCI_STATUS * @retval SCI_SUCCESS if the start io operation succeeds * @retval SCI_FAILURE_INVALID_STATE if one or more objects are not in a valid * state to accept io requests. */ static SCI_STATUS scic_sds_controller_failed_state_terminate_request_handler( SCI_BASE_CONTROLLER_T *controller, SCI_BASE_REMOTE_DEVICE_T *remote_device, SCI_BASE_REQUEST_T *io_request ) { SCIC_SDS_REQUEST_T *the_request = (SCIC_SDS_REQUEST_T *) io_request; return scic_sds_io_request_terminate(the_request); } SCIC_SDS_CONTROLLER_STATE_HANDLER_T scic_sds_controller_state_handler_table[SCI_BASE_CONTROLLER_MAX_STATES] = { // SCI_BASE_CONTROLLER_STATE_INITIAL { { NULL, NULL, NULL, NULL, scic_sds_controller_default_start_operation_handler, scic_sds_controller_default_start_operation_handler, scic_sds_controller_default_request_handler, scic_sds_controller_default_request_handler, scic_sds_controller_default_request_handler, NULL, NULL }, scic_sds_controller_default_request_handler, NULL, NULL, NULL, NULL }, // SCI_BASE_CONTROLLER_STATE_RESET { { NULL, NULL, NULL, scic_sds_controller_reset_state_initialize_handler, scic_sds_controller_default_start_operation_handler, scic_sds_controller_default_start_operation_handler, scic_sds_controller_default_request_handler, scic_sds_controller_default_request_handler, scic_sds_controller_default_request_handler, NULL, NULL }, scic_sds_controller_default_request_handler, NULL, NULL, NULL, NULL }, // SCI_BASE_CONTROLLER_STATE_INITIALIZING { { NULL, NULL, NULL, NULL, scic_sds_controller_default_start_operation_handler, scic_sds_controller_default_start_operation_handler, scic_sds_controller_default_request_handler, scic_sds_controller_default_request_handler, scic_sds_controller_default_request_handler, NULL, NULL }, scic_sds_controller_default_request_handler, NULL, NULL, NULL, NULL }, // SCI_BASE_CONTROLLER_STATE_INITIALIZED { { scic_sds_controller_initialized_state_start_handler, NULL, NULL, NULL, scic_sds_controller_default_start_operation_handler, scic_sds_controller_default_start_operation_handler, scic_sds_controller_default_request_handler, scic_sds_controller_default_request_handler, scic_sds_controller_default_request_handler, NULL, NULL }, scic_sds_controller_default_request_handler, NULL, NULL, NULL, NULL }, // SCI_BASE_CONTROLLER_STATE_STARTING { { NULL, NULL, NULL, NULL, scic_sds_controller_default_start_operation_handler, scic_sds_controller_default_start_operation_handler, scic_sds_controller_default_request_handler, scic_sds_controller_default_request_handler, scic_sds_controller_default_request_handler, NULL, NULL }, scic_sds_controller_default_request_handler, scic_sds_controller_starting_state_link_up_handler, scic_sds_controller_starting_state_link_down_handler, NULL, NULL }, // SCI_BASE_CONTROLLER_STATE_READY { { NULL, scic_sds_controller_ready_state_stop_handler, scic_sds_controller_general_reset_handler, NULL, scic_sds_controller_ready_state_start_io_handler, scic_sds_controller_default_start_operation_handler, scic_sds_controller_ready_state_complete_io_handler, scic_sds_controller_default_request_handler, scic_sds_controller_ready_state_continue_io_handler, scic_sds_controller_ready_state_start_task_handler, scic_sds_controller_ready_state_complete_io_handler }, scic_sds_controller_ready_state_terminate_request_handler, scic_sds_controller_ready_state_link_up_handler, scic_sds_controller_ready_state_link_down_handler, NULL, NULL }, // SCI_BASE_CONTROLLER_STATE_RESETTING { { NULL, NULL, NULL, NULL, scic_sds_controller_default_start_operation_handler, scic_sds_controller_default_start_operation_handler, scic_sds_controller_default_request_handler, scic_sds_controller_default_request_handler, scic_sds_controller_default_request_handler, NULL, NULL }, scic_sds_controller_default_request_handler, NULL, NULL, NULL, NULL }, // SCI_BASE_CONTROLLER_STATE_STOPPING { { NULL, NULL, NULL, NULL, scic_sds_controller_default_start_operation_handler, scic_sds_controller_default_start_operation_handler, scic_sds_controller_stopping_state_complete_io_handler, scic_sds_controller_default_request_handler, scic_sds_controller_default_request_handler, NULL, NULL }, scic_sds_controller_default_request_handler, NULL, NULL, NULL, scic_sds_controller_stopping_state_device_stopped_handler }, // SCI_BASE_CONTROLLER_STATE_STOPPED { { NULL, NULL, scic_sds_controller_failed_state_reset_handler, NULL, scic_sds_controller_default_start_operation_handler, scic_sds_controller_default_start_operation_handler, scic_sds_controller_default_request_handler, scic_sds_controller_default_request_handler, scic_sds_controller_default_request_handler, NULL, NULL }, scic_sds_controller_default_request_handler, NULL, NULL, NULL, NULL }, // SCI_BASE_CONTROLLER_STATE_FAILED { { NULL, NULL, scic_sds_controller_general_reset_handler, NULL, scic_sds_controller_failed_state_start_operation_handler, scic_sds_controller_failed_state_start_operation_handler, scic_sds_controller_default_request_handler, scic_sds_controller_default_request_handler, scic_sds_controller_default_request_handler, NULL, NULL }, scic_sds_controller_failed_state_terminate_request_handler, NULL, NULL, NULL } }; /** * This method implements the actions taken by the SCIC_SDS_CONTROLLER on * entry to the SCI_BASE_CONTROLLER_STATE_INITIAL. * - Set the state handlers to the controllers initial state. * * @param[in] object This is the SCI_BASE_OBJECT which is cast to a * SCIC_SDS_CONTROLLER object. * * @return none * * @todo This function should initialze the controller object. */ static void scic_sds_controller_initial_state_enter( SCI_BASE_OBJECT_T *object ) { SCIC_SDS_CONTROLLER_T *this_controller; this_controller= (SCIC_SDS_CONTROLLER_T *)object; scic_sds_controller_set_base_state_handlers( this_controller, SCI_BASE_CONTROLLER_STATE_INITIAL); sci_base_state_machine_change_state( &this_controller->parent.state_machine, SCI_BASE_CONTROLLER_STATE_RESET); } /** * This method implements the actions taken by the SCIC_SDS_CONTROLLER on * entry to the SCI_BASE_CONTROLLER_STATE_RESET. * - Set the state handlers to the controllers reset state. * * @param[in] object This is the SCI_BASE_OBJECT which is cast to a * SCIC_SDS_CONTROLLER object. * * @return none */ static void scic_sds_controller_reset_state_enter( SCI_BASE_OBJECT_T *object ) { U8 index; SCIC_SDS_CONTROLLER_T *this_controller; this_controller= (SCIC_SDS_CONTROLLER_T *)object; scic_sds_controller_set_base_state_handlers( this_controller, SCI_BASE_CONTROLLER_STATE_RESET); scic_sds_port_configuration_agent_construct(&this_controller->port_agent); // Construct the ports for this controller for (index = 0; index < (SCI_MAX_PORTS + 1); index++) { scic_sds_port_construct( &this_controller->port_table[index], (index == SCI_MAX_PORTS) ? SCIC_SDS_DUMMY_PORT : index, this_controller ); } // Construct the phys for this controller for (index = 0; index < SCI_MAX_PHYS; index++) { // Add all the PHYs to the dummy port scic_sds_phy_construct( &this_controller->phy_table[index], &this_controller->port_table[SCI_MAX_PORTS], index ); } this_controller->invalid_phy_mask = 0; // Set the default maximum values this_controller->completion_event_entries = SCU_EVENT_COUNT; this_controller->completion_queue_entries = SCU_COMPLETION_QUEUE_COUNT; this_controller->remote_node_entries = SCI_MAX_REMOTE_DEVICES; this_controller->logical_port_entries = SCI_MAX_PORTS; this_controller->task_context_entries = SCU_IO_REQUEST_COUNT; this_controller->uf_control.buffers.count = SCU_UNSOLICITED_FRAME_COUNT; this_controller->uf_control.address_table.count= SCU_UNSOLICITED_FRAME_COUNT; // Initialize the User and OEM parameters to default values. scic_sds_controller_set_default_config_parameters(this_controller); } /** * This method implements the actions taken by the SCIC_SDS_CONTROLLER on * entry to the SCI_BASE_CONTROLLER_STATE_INITIALIZING. * - Set the state handlers to the controllers initializing state. * * @param[in] object This is the SCI_BASE_OBJECT which is cast to a * SCIC_SDS_CONTROLLER object. * * @return none */ static void scic_sds_controller_initializing_state_enter( SCI_BASE_OBJECT_T *object ) { SCIC_SDS_CONTROLLER_T *this_controller; this_controller= (SCIC_SDS_CONTROLLER_T *)object; scic_sds_controller_set_base_state_handlers( this_controller, SCI_BASE_CONTROLLER_STATE_INITIALIZING); } /** * This method implements the actions taken by the SCIC_SDS_CONTROLLER on * entry to the SCI_BASE_CONTROLLER_STATE_INITIALIZED. * - Set the state handlers to the controllers initialized state. * * @param[in] object This is the SCI_BASE_OBJECT which is cast to a * SCIC_SDS_CONTROLLER object. * * @return none */ static void scic_sds_controller_initialized_state_enter( SCI_BASE_OBJECT_T *object ) { SCIC_SDS_CONTROLLER_T *this_controller; this_controller= (SCIC_SDS_CONTROLLER_T *)object; scic_sds_controller_set_base_state_handlers( this_controller, SCI_BASE_CONTROLLER_STATE_INITIALIZED); } /** * This method implements the actions taken by the SCIC_SDS_CONTROLLER on * entry to the SCI_BASE_CONTROLLER_STATE_STARTING. * - Set the state handlers to the controllers starting state. * * @param[in] object This is the SCI_BASE_OBJECT which is cast to a * SCIC_SDS_CONTROLLER object. * * @return none */ static void scic_sds_controller_starting_state_enter( SCI_BASE_OBJECT_T *object ) { SCIC_SDS_CONTROLLER_T *this_controller; this_controller= (SCIC_SDS_CONTROLLER_T *)object; scic_sds_controller_set_base_state_handlers( this_controller, SCI_BASE_CONTROLLER_STATE_STARTING); } /** * This method implements the actions taken by the SCIC_SDS_CONTROLLER on exit * from the SCI_BASE_CONTROLLER_STATE_STARTING. * - This function stops the controller starting timeout timer. * * @param[in] object This is the SCI_BASE_OBJECT which is cast to a * SCIC_SDS_CONTROLLER object. * * @return none */ static void scic_sds_controller_starting_state_exit( SCI_BASE_OBJECT_T *object ) { SCIC_SDS_CONTROLLER_T *this_controller; this_controller= (SCIC_SDS_CONTROLLER_T *)object; scic_cb_timer_stop(object, this_controller->timeout_timer); // We are done with this timer since we are exiting the starting // state so remove it scic_cb_timer_destroy( this_controller, this_controller->phy_startup_timer ); this_controller->phy_startup_timer = NULL; } /** * This method implements the actions taken by the SCIC_SDS_CONTROLLER on * entry to the SCI_BASE_CONTROLLER_STATE_READY. * - Set the state handlers to the controllers ready state. * * @param[in] object This is the SCI_BASE_OBJECT which is cast to a * SCIC_SDS_CONTROLLER object. * * @return none */ static void scic_sds_controller_ready_state_enter( SCI_BASE_OBJECT_T *object ) { U32 clock_gating_unit_value; SCIC_SDS_CONTROLLER_T *this_controller; this_controller= (SCIC_SDS_CONTROLLER_T *)object; scic_sds_controller_set_base_state_handlers( this_controller, SCI_BASE_CONTROLLER_STATE_READY); /** * enable clock gating for power control of the scu unit */ clock_gating_unit_value = SMU_CGUCR_READ(this_controller); clock_gating_unit_value &= ~( SMU_CGUCR_GEN_BIT(REGCLK_ENABLE) | SMU_CGUCR_GEN_BIT(TXCLK_ENABLE) | SMU_CGUCR_GEN_BIT(XCLK_ENABLE) ); clock_gating_unit_value |= SMU_CGUCR_GEN_BIT(IDLE_ENABLE); SMU_CGUCR_WRITE(this_controller, clock_gating_unit_value); //set the default interrupt coalescence number and timeout value. scic_controller_set_interrupt_coalescence( this_controller, 0x10, 250); } /** * This method implements the actions taken by the SCIC_SDS_CONTROLLER on exit * from the SCI_BASE_CONTROLLER_STATE_READY. * - This function does nothing. * * @param[in] object This is the SCI_BASE_OBJECT which is cast to a * SCIC_SDS_CONTROLLER object. * * @return none */ static void scic_sds_controller_ready_state_exit( SCI_BASE_OBJECT_T *object ) { U32 clock_gating_unit_value; SCIC_SDS_CONTROLLER_T *this_controller; this_controller= (SCIC_SDS_CONTROLLER_T *)object; /** * restore clock gating for power control of the scu unit */ clock_gating_unit_value = SMU_CGUCR_READ(this_controller); clock_gating_unit_value &= ~SMU_CGUCR_GEN_BIT(IDLE_ENABLE); clock_gating_unit_value |= ( SMU_CGUCR_GEN_BIT(REGCLK_ENABLE) | SMU_CGUCR_GEN_BIT(TXCLK_ENABLE) | SMU_CGUCR_GEN_BIT(XCLK_ENABLE) ); SMU_CGUCR_WRITE(this_controller, clock_gating_unit_value); //disable interrupt coalescence. scic_controller_set_interrupt_coalescence(this_controller, 0, 0); } /** * This method implements the actions taken by the SCIC_SDS_CONTROLLER on * entry to the SCI_BASE_CONTROLLER_STATE_READY. * - Set the state handlers to the controllers ready state. * - Stop all of the remote devices on this controller * - Stop the ports on this controller * - Stop the phys on this controller * * @param[in] object This is the SCI_BASE_OBJECT which is cast to a * SCIC_SDS_CONTROLLER object. * * @return none */ static void scic_sds_controller_stopping_state_enter( SCI_BASE_OBJECT_T *object ) { SCIC_SDS_CONTROLLER_T *this_controller; this_controller= (SCIC_SDS_CONTROLLER_T *)object; scic_sds_controller_set_base_state_handlers( this_controller, SCI_BASE_CONTROLLER_STATE_STOPPING); // Stop all of the components for this controller in the reverse order // from which they are initialized. scic_sds_controller_stop_devices(this_controller); scic_sds_controller_stop_ports(this_controller); if (!scic_sds_controller_has_remote_devices_stopping(this_controller)) { sci_base_state_machine_change_state( &this_controller->parent.state_machine, SCI_BASE_CONTROLLER_STATE_STOPPED ); } } /** * This method implements the actions taken by the SCIC_SDS_CONTROLLER on exit * from the SCI_BASE_CONTROLLER_STATE_STOPPING. * - This function stops the controller stopping timeout timer. * * @param[in] object This is the SCI_BASE_OBJECT which is cast to a * SCIC_SDS_CONTROLLER object. * * @return none */ static void scic_sds_controller_stopping_state_exit( SCI_BASE_OBJECT_T *object ) { SCIC_SDS_CONTROLLER_T *this_controller; this_controller= (SCIC_SDS_CONTROLLER_T *)object; scic_cb_timer_stop(this_controller, this_controller->timeout_timer); } /** * This method implements the actions taken by the SCIC_SDS_CONTROLLER on * entry to the SCI_BASE_CONTROLLER_STATE_STOPPED. * - Set the state handlers to the controllers stopped state. * * @param[in] object This is the SCI_BASE_OBJECT which is cast to a * SCIC_SDS_CONTROLLER object. * * @return none */ static void scic_sds_controller_stopped_state_enter( SCI_BASE_OBJECT_T *object ) { SCIC_SDS_CONTROLLER_T *this_controller; this_controller= (SCIC_SDS_CONTROLLER_T *)object; scic_sds_controller_set_base_state_handlers( this_controller, SCI_BASE_CONTROLLER_STATE_STOPPED); // We are done with this timer until the next timer we initialize scic_cb_timer_destroy( this_controller, this_controller->timeout_timer ); this_controller->timeout_timer = NULL; // Controller has stopped so disable all the phys on this controller scic_sds_controller_stop_phys(this_controller); scic_sds_port_configuration_agent_destroy( this_controller, &this_controller->port_agent ); scic_cb_controller_stop_complete(this_controller, SCI_SUCCESS); } /** * This method implements the actions taken by the SCIC_SDS_CONTROLLER on * entry to the SCI_BASE_CONTROLLER_STATE_RESETTING. * - Set the state handlers to the controllers resetting state. * - Write to the SCU hardware reset register to force a reset * - Transition to the SCI_BASE_CONTROLLER_STATE_RESET * * @param[in] object This is the SCI_BASE_OBJECT which is cast to a * SCIC_SDS_CONTROLLER object. * * @return none */ static void scic_sds_controller_resetting_state_enter( SCI_BASE_OBJECT_T *object ) { SCIC_SDS_CONTROLLER_T *this_controller; this_controller= (SCIC_SDS_CONTROLLER_T *)object; SCIC_LOG_TRACE(( sci_base_object_get_logger(this_controller), SCIC_LOG_OBJECT_CONTROLLER, "scic_sds_controller_resetting_state_enter(0x%x) enter\n", this_controller )); scic_sds_controller_set_base_state_handlers( this_controller, SCI_BASE_CONTROLLER_STATE_RESETTING); scic_sds_controller_reset_hardware(this_controller); sci_base_state_machine_change_state( scic_sds_controller_get_base_state_machine(this_controller), SCI_BASE_CONTROLLER_STATE_RESET ); } static SCI_STATUS scic_sds_abort_reqests( SCIC_SDS_CONTROLLER_T * controller, SCIC_SDS_REMOTE_DEVICE_T * remote_device, SCIC_SDS_PORT_T * port ) { SCI_STATUS status = SCI_SUCCESS; SCI_STATUS terminate_status = SCI_SUCCESS; SCIC_SDS_REQUEST_T *the_request; U32 index; U32 request_count; if (remote_device != NULL) request_count = remote_device->started_request_count; else if (port != NULL) request_count = port->started_request_count; else request_count = SCI_MAX_IO_REQUESTS; for (index = 0; (index < SCI_MAX_IO_REQUESTS) && (request_count > 0); index++) { the_request = controller->io_request_table[index]; if (the_request != NULL) { if (the_request->target_device == remote_device || the_request->target_device->owning_port == port || (remote_device == NULL && port == NULL)) { terminate_status = scic_controller_terminate_request( controller, the_request->target_device, the_request ); if (terminate_status != SCI_SUCCESS) status = terminate_status; request_count--; } } } return status; } SCI_STATUS scic_sds_terminate_reqests( SCIC_SDS_CONTROLLER_T *this_controller, SCIC_SDS_REMOTE_DEVICE_T *this_remote_device, SCIC_SDS_PORT_T *this_port ) { SCI_STATUS status = SCI_SUCCESS; SCI_STATUS abort_status = SCI_SUCCESS; // move all request to abort state abort_status = scic_sds_abort_reqests(this_controller, this_remote_device, this_port); if (abort_status != SCI_SUCCESS) status = abort_status; //move all request to complete state if (this_controller->parent.error == SCI_CONTROLLER_FATAL_MEMORY_ERROR) abort_status = scic_sds_abort_reqests(this_controller, this_remote_device, this_port); if (abort_status != SCI_SUCCESS) status = abort_status; return status; } static SCI_STATUS scic_sds_terminate_all_requests( SCIC_SDS_CONTROLLER_T * controller ) { return scic_sds_terminate_reqests(controller, NULL, NULL); } /** * This method implements the actions taken by the SCIC_SDS_CONTROLLER on * entry to the SCI_BASE_CONTROLLER_STATE_FAILED. * - Set the state handlers to the controllers failed state. * * @param[in] object This is the SCI_BASE_OBJECT which is cast to a * SCIC_SDS_CONTROLLER object. * * @return none */ static void scic_sds_controller_failed_state_enter( SCI_BASE_OBJECT_T *object ) { SCIC_SDS_CONTROLLER_T *this_controller; this_controller= (SCIC_SDS_CONTROLLER_T *)object; scic_sds_controller_set_base_state_handlers( this_controller, SCI_BASE_CONTROLLER_STATE_FAILED); if (this_controller->parent.error == SCI_CONTROLLER_FATAL_MEMORY_ERROR) scic_sds_terminate_all_requests(this_controller); else scic_sds_controller_release_resource(this_controller); //notify framework the controller failed. scic_cb_controller_error(this_controller, this_controller->parent.error); } // --------------------------------------------------------------------------- SCI_BASE_STATE_T scic_sds_controller_state_table[SCI_BASE_CONTROLLER_MAX_STATES] = { { SCI_BASE_CONTROLLER_STATE_INITIAL, scic_sds_controller_initial_state_enter, NULL, }, { SCI_BASE_CONTROLLER_STATE_RESET, scic_sds_controller_reset_state_enter, NULL, }, { SCI_BASE_CONTROLLER_STATE_INITIALIZING, scic_sds_controller_initializing_state_enter, NULL, }, { SCI_BASE_CONTROLLER_STATE_INITIALIZED, scic_sds_controller_initialized_state_enter, NULL, }, { SCI_BASE_CONTROLLER_STATE_STARTING, scic_sds_controller_starting_state_enter, scic_sds_controller_starting_state_exit, }, { SCI_BASE_CONTROLLER_STATE_READY, scic_sds_controller_ready_state_enter, scic_sds_controller_ready_state_exit, }, { SCI_BASE_CONTROLLER_STATE_RESETTING, scic_sds_controller_resetting_state_enter, NULL, }, { SCI_BASE_CONTROLLER_STATE_STOPPING, scic_sds_controller_stopping_state_enter, scic_sds_controller_stopping_state_exit, }, { SCI_BASE_CONTROLLER_STATE_STOPPED, scic_sds_controller_stopped_state_enter, NULL, }, { SCI_BASE_CONTROLLER_STATE_FAILED, scic_sds_controller_failed_state_enter, NULL, } }; diff --git a/sys/dev/isci/scil/scic_sds_port.c b/sys/dev/isci/scil/scic_sds_port.c index 938d82d48e46..04810e04088b 100644 --- a/sys/dev/isci/scil/scic_sds_port.c +++ b/sys/dev/isci/scil/scic_sds_port.c @@ -1,3664 +1,3664 @@ /*- * SPDX-License-Identifier: BSD-2-Clause OR GPL-2.0 * * 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. * * 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 __FBSDID("$FreeBSD$"); /** * @file * * @brief This file contains the implementation for the public and protected * methods for the SCIC_SDS_PORT object. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SCIC_SDS_PORT_MIN_TIMER_COUNT (SCI_MAX_PORTS) #define SCIC_SDS_PORT_MAX_TIMER_COUNT (SCI_MAX_PORTS) #define SCIC_SDS_PORT_HARD_RESET_TIMEOUT (1000) #define SCU_DUMMY_INDEX (0xFFFF) /** * 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. * * @param[in] this_port This is the port object to which the phy is being * assigned. * @param[in] phy_index This is the phy index that is being assigned to the * port. * * @return BOOL * @retval TRUE if this is a valid phy assignment for the port * @retval FALSE if this is not a valid phy assignment for the port */ BOOL scic_sds_port_is_valid_phy_assignment( SCIC_SDS_PORT_T *this_port, U32 phy_index ) { // Initialize to invalid value. U32 existing_phy_index = SCI_MAX_PHYS; U32 index; if ((this_port->physical_port_index == 1) && (phy_index != 1)) { return FALSE; } if (this_port->physical_port_index == 3 && phy_index != 3) { return FALSE; } if ( (this_port->physical_port_index == 2) && ((phy_index == 0) || (phy_index == 1)) ) { return FALSE; } for (index = 0; index < SCI_MAX_PHYS; index++) { if ( (this_port->phy_table[index] != NULL) && (index != phy_index) ) { existing_phy_index = index; } } // 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) && (this_port->owning_controller->user_parameters.sds1.phys[ phy_index].max_speed_generation != this_port->owning_controller->user_parameters.sds1.phys[ existing_phy_index].max_speed_generation) ) return FALSE; return TRUE; } /** * @brief This method requests a list (mask) of the phys contained in the * supplied SAS port. * * @param[in] this_port a handle corresponding to the SAS port for which * to return the phy mask. * * @return 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). */ U32 scic_sds_port_get_phys( SCIC_SDS_PORT_T * this_port ) { U32 index; U32 mask; SCIC_LOG_TRACE(( sci_base_object_get_logger(this_port), SCIC_LOG_OBJECT_PORT, "scic_sds_port_get_phys(0x%x) enter\n", this_port )); mask = 0; for (index = 0; index < SCI_MAX_PHYS; index++) { if (this_port->phy_table[index] != NULL) { mask |= (1 << index); } } return mask; } /** * 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] * * @param[in] this_port This is the port object for which to determine * if the phy mask can be supported. * * @return This method returns a boolean indication specifying if the * phy mask can be supported. * @retval TRUE if this is a valid phy assignment for the port * @retval FALSE if this is not a valid phy assignment for the port */ BOOL scic_sds_port_is_phy_mask_valid( SCIC_SDS_PORT_T *this_port, U32 phy_mask ) { if (this_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 (this_port->physical_port_index == 1) { if ( ((phy_mask & 0x02) == 0x02) || (phy_mask == 0) ) return TRUE; } else if (this_port->physical_port_index == 2) { if ( ((phy_mask & 0x0C) == 0x0C) || ((phy_mask & 0x04) == 0x04) || (phy_mask == 0) ) return TRUE; } else if (this_port->physical_port_index == 3) { if ( ((phy_mask & 0x08) == 0x08) || (phy_mask == 0) ) return TRUE; } return FALSE; } /** * This method retrieves a currently active (i.e. connected) phy * contained in the port. Currently, the lowest order phy that is * connected is returned. * * @param[in] this_port This parameter specifies the port from which * to return a connected phy. * * @return This method returns a pointer to a SCIS_SDS_PHY object. * @retval NULL This value is returned if there are no currently * active (i.e. connected to a remote end point) phys * contained in the port. * @retval All other values specify a SCIC_SDS_PHY object that is * active in the port. */ SCIC_SDS_PHY_T * scic_sds_port_get_a_connected_phy( SCIC_SDS_PORT_T *this_port ) { U32 index; SCIC_SDS_PHY_T *phy; 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 = this_port->phy_table[index]; if ( (phy != NULL) && scic_sds_port_active_phy(this_port, phy) ) { return phy; } } return NULL; } /** * This method attempts to make the assignment of the phy to the port. * If successful the phy is assigned to the ports phy table. * * @param[in, out] port The port object to which the phy assignement * is being made. * @param[in, out] phy The phy which is being assigned to the port. * * @return BOOL * @retval TRUE if the phy assignment can be made. * @retval FALSE if the phy assignement can not be made. * * @note This is a functional test that only fails if the phy is currently * assigned to a different port. */ SCI_STATUS scic_sds_port_set_phy( SCIC_SDS_PORT_T *port, SCIC_SDS_PHY_T *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. + // not already have a phy assigned to the phy index. if ( (port->phy_table[phy->phy_index] == SCI_INVALID_HANDLE) && (scic_sds_phy_get_port(phy) == SCI_INVALID_HANDLE) && 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; } /** * This method will clear the phy assigned to this port. This method fails - * if this phy is not currently assinged to this port. + * if this phy is not currently assigned to this port. * * @param[in, out] port The port from which the phy is being cleared. * @param[in, out] phy The phy being cleared from the port. * * @return BOOL * @retval TRUE if the phy is removed from the port. * @retval FALSE if this phy is not assined to this port. */ SCI_STATUS scic_sds_port_clear_phy( SCIC_SDS_PORT_T *port, SCIC_SDS_PHY_T *phy ) { // Make sure that this phy is part of this port if ( (port->phy_table[phy->phy_index] == phy) && (scic_sds_phy_get_port(phy) == port) ) { // Yep it is assigned to this port so remove it scic_sds_phy_set_port( phy, &scic_sds_port_get_controller(port)->port_table[SCI_MAX_PORTS] ); port->phy_table[phy->phy_index] = SCI_INVALID_HANDLE; return SCI_SUCCESS; } return SCI_FAILURE; } /** * This method will add a PHY to the selected port. * * @param[in] this_port This parameter specifies the port in which the phy will * be added. * * @param[in] the_phy This parameter is the phy which is to be added to the * port. * * @return This method returns an SCI_STATUS. * @retval SCI_SUCCESS the phy has been added to the port. * @retval Any other status is failre to add the phy to the port. */ SCI_STATUS scic_sds_port_add_phy( SCIC_SDS_PORT_T * this_port, SCIC_SDS_PHY_T * the_phy ) { return this_port->state_handlers->parent.add_phy_handler( &this_port->parent, &the_phy->parent); } /** * This method will remove the PHY from the selected PORT. * * @param[in] this_port This parameter specifies the port in which the phy will * be added. * * @param[in] the_phy This parameter is the phy which is to be added to the * port. * * @return This method returns an SCI_STATUS. * @retval SCI_SUCCESS the phy has been removed from the port. * @retval Any other status is failre to add the phy to the port. */ SCI_STATUS scic_sds_port_remove_phy( SCIC_SDS_PORT_T * this_port, SCIC_SDS_PHY_T * the_phy ) { return this_port->state_handlers->parent.remove_phy_handler( &this_port->parent, &the_phy->parent); } /** * @brief This method requests the SAS address for the supplied SAS port * from the SCI implementation. * * @param[in] this_port a handle corresponding to the SAS port for which * to return the SAS address. * @param[out] sas_address This parameter specifies a pointer to a SAS * address structure into which the core will copy the SAS * address for the port. * * @return none */ void scic_sds_port_get_sas_address( SCIC_SDS_PORT_T * this_port, SCI_SAS_ADDRESS_T * sas_address ) { U32 index; SCIC_LOG_TRACE(( sci_base_object_get_logger(this_port), SCIC_LOG_OBJECT_PORT, "scic_sds_port_get_sas_address(0x%x, 0x%x) enter\n", this_port, sas_address )); sas_address->high = 0; sas_address->low = 0; for (index = 0; index < SCI_MAX_PHYS; index++) { if (this_port->phy_table[index] != NULL) { scic_sds_phy_get_sas_address(this_port->phy_table[index], sas_address); } } } /** * @brief This method will indicate which protocols are supported by this * port. * * @param[in] this_port a handle corresponding to the SAS port for which * to return the supported protocols. * @param[out] protocols This parameter specifies a pointer to an IAF * protocol field structure into which the core will copy * the protocol values for the port. The values are * returned as part of a bit mask in order to allow for * multi-protocol support. * * @return none */ static void scic_sds_port_get_protocols( SCIC_SDS_PORT_T * this_port, SCI_SAS_IDENTIFY_ADDRESS_FRAME_PROTOCOLS_T * protocols ) { U8 index; SCIC_LOG_TRACE(( sci_base_object_get_logger(this_port), SCIC_LOG_OBJECT_PORT, "scic_sds_port_get_protocols(0x%x, 0x%x) enter\n", this_port, protocols )); protocols->u.all = 0; for (index = 0; index < SCI_MAX_PHYS; index++) { if (this_port->phy_table[index] != NULL) { scic_sds_phy_get_protocols(this_port->phy_table[index], protocols); } } } /** * @brief This method requests the SAS address for the device directly * attached to this SAS port. * * @param[in] this_port a handle corresponding to the SAS port for which * to return the SAS address. * @param[out] 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. * * @return none */ void scic_sds_port_get_attached_sas_address( SCIC_SDS_PORT_T * this_port, SCI_SAS_ADDRESS_T * sas_address ) { SCI_SAS_IDENTIFY_ADDRESS_FRAME_PROTOCOLS_T protocols; SCIC_SDS_PHY_T *phy; SCIC_LOG_TRACE(( sci_base_object_get_logger(this_port), SCIC_LOG_OBJECT_PORT, "scic_sds_port_get_attached_sas_address(0x%x, 0x%x) enter\n", this_port, sas_address )); // Ensure that the phy is both part of the port and currently // connected to the remote end-point. phy = scic_sds_port_get_a_connected_phy(this_port); if (phy != NULL) { scic_sds_phy_get_attached_phy_protocols(phy, &protocols); if (!protocols.u.bits.stp_target) { scic_sds_phy_get_attached_sas_address(phy, sas_address); } else { scic_sds_phy_get_sas_address(phy, sas_address); sas_address->low += phy->phy_index; //Need to make up attached STP device's SAS address in //the same order as recorded IAF from SSP device. sas_address->high = SCIC_SWAP_DWORD(sas_address->high); sas_address->low = SCIC_SWAP_DWORD(sas_address->low); } } else { sas_address->high = 0; sas_address->low = 0; } } /** * @brief This method will indicate which protocols are supported by this * remote device. * * @param[in] this_port a handle corresponding to the SAS port for which * to return the supported protocols. * @param[out] protocols This parameter specifies a pointer to an IAF * protocol field structure into which the core will copy * the protocol values for the port. The values are * returned as part of a bit mask in order to allow for * multi-protocol support. * * @return none */ void scic_sds_port_get_attached_protocols( SCIC_SDS_PORT_T * this_port, SCI_SAS_IDENTIFY_ADDRESS_FRAME_PROTOCOLS_T * protocols ) { SCIC_SDS_PHY_T *phy; SCIC_LOG_TRACE(( sci_base_object_get_logger(this_port), SCIC_LOG_OBJECT_PORT, "scic_sds_port_get_attached_protocols(0x%x, 0x%x) enter\n", this_port, protocols )); // Ensure that the phy is both part of the port and currently // connected to the remote end-point. phy = scic_sds_port_get_a_connected_phy(this_port); if (phy != NULL) scic_sds_phy_get_attached_phy_protocols(phy, protocols); else protocols->u.all = 0; } /** * @brief This method returns the amount of memory required for a port * object. * * @return U32 */ U32 scic_sds_port_get_object_size(void) { return sizeof(SCIC_SDS_PORT_T); } /** * @brief This method returns the minimum number of timers required for all * port objects. * * @return U32 */ U32 scic_sds_port_get_min_timer_count(void) { return SCIC_SDS_PORT_MIN_TIMER_COUNT; } /** * @brief This method returns the maximum number of timers required for all * port objects. * * @return U32 */ U32 scic_sds_port_get_max_timer_count(void) { return SCIC_SDS_PORT_MAX_TIMER_COUNT; } #ifdef SCI_LOGGING void scic_sds_port_initialize_state_logging( SCIC_SDS_PORT_T *this_port ) { sci_base_state_machine_logger_initialize( &this_port->parent.state_machine_logger, &this_port->parent.state_machine, &this_port->parent.parent, scic_cb_logger_log_states, "SCIC_SDS_PORT_T", "base state machine", SCIC_LOG_OBJECT_PORT ); sci_base_state_machine_logger_initialize( &this_port->ready_substate_machine_logger, &this_port->ready_substate_machine, &this_port->parent.parent, scic_cb_logger_log_states, "SCIC_SDS_PORT_T", "ready substate machine", SCIC_LOG_OBJECT_PORT ); } #endif /** * 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. * * @param[in] this_port The logical port on which we need to create the * remote node context. * @param[in] rni The remote node index for this remote node context. * * @return none */ static void scic_sds_port_construct_dummy_rnc( SCIC_SDS_PORT_T *this_port, U16 rni ) { SCU_REMOTE_NODE_CONTEXT_T * rnc; rnc = &(this_port->owning_controller->remote_node_context_table[rni]); memset(rnc, 0, sizeof(SCU_REMOTE_NODE_CONTEXT_T)); 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 = this_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; } /** * 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. * * @param[in] this_port The logical port on which we need to create the * remote node context. * context. * @param[in] tci The remote node index for this remote node context. * */ static void scic_sds_port_construct_dummy_task( SCIC_SDS_PORT_T *this_port, U16 tci ) { SCU_TASK_CONTEXT_T * task_context; task_context = scic_sds_controller_get_task_context_buffer(this_port->owning_controller, tci); memset(task_context, 0, sizeof(SCU_TASK_CONTEXT_T)); 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 = this_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 = this_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; } /** * This routine will free any allocated dummy resources for this port. * * @param[in, out] this_port The port on which the resources are being destroyed. */ static void scic_sds_port_destroy_dummy_resources( SCIC_SDS_PORT_T * this_port ) { if (this_port->reserved_tci != SCU_DUMMY_INDEX) { scic_controller_free_io_tag( this_port->owning_controller, this_port->reserved_tci ); } if (this_port->reserved_rni != SCU_DUMMY_INDEX) { scic_sds_remote_node_table_release_remote_node_index( &this_port->owning_controller->available_remote_nodes, 1, this_port->reserved_rni ); } this_port->reserved_rni = SCU_DUMMY_INDEX; this_port->reserved_tci = SCU_DUMMY_INDEX; } /** * @brief * * @param[in] this_port * @param[in] port_index * @param[in] owning_controller */ void scic_sds_port_construct( SCIC_SDS_PORT_T *this_port, U8 port_index, SCIC_SDS_CONTROLLER_T *owning_controller ) { U32 index; sci_base_port_construct( &this_port->parent, sci_base_object_get_logger(owning_controller), scic_sds_port_state_table ); sci_base_state_machine_construct( scic_sds_port_get_ready_substate_machine(this_port), &this_port->parent.parent, scic_sds_port_ready_substate_table, SCIC_SDS_PORT_READY_SUBSTATE_WAITING ); scic_sds_port_initialize_state_logging(this_port); this_port->logical_port_index = SCIC_SDS_DUMMY_PORT; this_port->physical_port_index = port_index; this_port->active_phy_mask = 0; this_port->enabled_phy_mask = 0; this_port->owning_controller = owning_controller; this_port->started_request_count = 0; this_port->assigned_device_count = 0; this_port->reserved_rni = SCU_DUMMY_INDEX; this_port->reserved_tci = SCU_DUMMY_INDEX; this_port->timer_handle = SCI_INVALID_HANDLE; this_port->port_task_scheduler_registers = NULL; for (index = 0; index < SCI_MAX_PHYS; index++) { this_port->phy_table[index] = NULL; } } /** * @brief This method performs initialization of the supplied port. * Initialization includes: * - state machine initialization * - member variable initialization * - configuring the phy_mask * * @param[in] this_port * @param[in] transport_layer_registers * @param[in] port_task_scheduler_registers * @param[in] port_configuration_regsiter * * @return SCI_STATUS * @retval SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION This value is * returned if the phy being added to the port */ SCI_STATUS scic_sds_port_initialize( SCIC_SDS_PORT_T *this_port, void *port_task_scheduler_registers, void *port_configuration_regsiter, void *viit_registers ) { this_port->port_task_scheduler_registers = port_task_scheduler_registers; this_port->port_pe_configuration_register = port_configuration_regsiter; this_port->viit_registers = viit_registers; return SCI_SUCCESS; } /** * This method is the a general link up handler for the SCIC_SDS_PORT object. * This function will determine if this SCIC_SDS_PHY can * be assigned to this SCIC_SDS_PORT object. If the SCIC_SDS_PHY object can * is not a valid PHY for this port then the function will notify the SCIC_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. * * @param[in] this_port This is the SCIC_SDS_PORT object for which has a phy * that has gone link up. * @param[in] the_phy This is the SCIC_SDS_PHY object that has gone link up. * @param[in] 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. * @param[in] do_resume_phy This parameter specifies whether to resume the phy. * If this function is called from MPC mode, it will be always true. * for APC, this will be false, so that phys could be resumed later * * @return none */ void scic_sds_port_general_link_up_handler( SCIC_SDS_PORT_T * this_port, SCIC_SDS_PHY_T * the_phy, BOOL do_notify_user, BOOL do_resume_phy ) { SCI_SAS_ADDRESS_T port_sas_address; SCI_SAS_ADDRESS_T phy_sas_address; scic_sds_port_get_attached_sas_address(this_port, &port_sas_address); scic_sds_phy_get_attached_sas_address(the_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 ) ) || (this_port->active_phy_mask == 0) ) { scic_sds_port_activate_phy(this_port, the_phy, do_notify_user, do_resume_phy); if (this_port->parent.state_machine.current_state_id == SCI_BASE_PORT_STATE_RESETTING) { sci_base_state_machine_change_state( &this_port->parent.state_machine, SCI_BASE_PORT_STATE_READY ); } } else { scic_sds_port_invalid_link_up(this_port, the_phy); } } // --------------------------------------------------------------------------- SCI_STATUS scic_port_add_phy( SCI_PORT_HANDLE_T handle, SCI_PHY_HANDLE_T phy ) { #if defined (SCI_LOGGING) SCIC_SDS_PORT_T *this_port = (SCIC_SDS_PORT_T *)handle; #endif // defined (SCI_LOGGING) SCIC_LOG_TRACE(( sci_base_object_get_logger(this_port), SCIC_LOG_OBJECT_PORT, "scic_port_add_phy(0x%x, 0x%x) enter\n", handle, phy )); SCIC_LOG_ERROR(( sci_base_object_get_logger(this_port), SCIC_LOG_OBJECT_PORT, "Interface function scic_port_add_phy() has been deprecated. " "PORT configuration is handled through the OEM parameters.\n" )); return SCI_FAILURE_ADDING_PHY_UNSUPPORTED; } // --------------------------------------------------------------------------- SCI_STATUS scic_port_remove_phy( SCI_PORT_HANDLE_T handle, SCI_PHY_HANDLE_T phy ) { #if defined (SCI_LOGGING) SCIC_SDS_PORT_T *this_port = (SCIC_SDS_PORT_T *)handle; #endif // defined (SCI_LOGGING) SCIC_LOG_TRACE(( sci_base_object_get_logger(this_port), SCIC_LOG_OBJECT_PORT, "scic_port_remove_phy(0x%x, 0x%x) enter\n", handle, phy )); SCIC_LOG_ERROR(( sci_base_object_get_logger(this_port), SCIC_LOG_OBJECT_PORT, "Interface function scic_port_remove_phy() has been deprecated. " "PORT configuration is handled through the OEM parameters.\n" )); return SCI_FAILURE_ADDING_PHY_UNSUPPORTED; } // --------------------------------------------------------------------------- SCI_STATUS scic_port_get_properties( SCI_PORT_HANDLE_T port, SCIC_PORT_PROPERTIES_T * properties ) { SCIC_SDS_PORT_T *this_port = (SCIC_SDS_PORT_T *)port; SCIC_LOG_TRACE(( sci_base_object_get_logger(this_port), SCIC_LOG_OBJECT_PORT, "scic_port_get_properties(0x%x, 0x%x) enter\n", port, properties )); if ( (port == SCI_INVALID_HANDLE) || (this_port->logical_port_index == SCIC_SDS_DUMMY_PORT) ) { return SCI_FAILURE_INVALID_PORT; } properties->index = this_port->logical_port_index; properties->phy_mask = scic_sds_port_get_phys(this_port); scic_sds_port_get_sas_address(this_port, &properties->local.sas_address); scic_sds_port_get_protocols(this_port, &properties->local.protocols); scic_sds_port_get_attached_sas_address(this_port, &properties->remote.sas_address); scic_sds_port_get_attached_protocols(this_port, &properties->remote.protocols); return SCI_SUCCESS; } // --------------------------------------------------------------------------- SCI_STATUS scic_port_hard_reset( SCI_PORT_HANDLE_T handle, U32 reset_timeout ) { SCIC_SDS_PORT_T * this_port = (SCIC_SDS_PORT_T *)handle; SCIC_LOG_TRACE(( sci_base_object_get_logger(this_port), SCIC_LOG_OBJECT_PORT, "scic_port_hard_reset(0x%x, 0x%x) enter\n", handle, reset_timeout )); return this_port->state_handlers->parent.reset_handler( &this_port->parent, reset_timeout ); } /** * This method assigns the direct attached device ID for this port. * * @param[in] this_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( SCIC_SDS_PORT_T * this_port, U32 device_id ) { U8 index; for (index = 0; index < SCI_MAX_PHYS; index++) { if (this_port->active_phy_mask & (1 << index)) { scic_sds_phy_setup_transport(this_port->phy_table[index], device_id); } } } /** * This method will resume the phy which is already added in the port. * Activation includes: * - enabling the Protocol Engine in the silicon. * - update the reay mask. * * @param[in] this_port This is the port on which the phy should be enabled. * @return none */ static void scic_sds_port_resume_phy( SCIC_SDS_PORT_T * this_port, SCIC_SDS_PHY_T * the_phy ) { scic_sds_phy_resume (the_phy); this_port->enabled_phy_mask |= 1 << the_phy->phy_index; } /** * This method 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. * * @param[in] this_port This is the port on which the phy should be enabled. * @param[in] the_phy This is the specific phy which to enable. * @param[in] 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. * @param[in] do_resume_phy This parameter specifies whether to resume the phy. * If this function is called from MPC mode, it will be always true. * for APC, this will be false, so that phys could be resumed later * * @return none */ void scic_sds_port_activate_phy( SCIC_SDS_PORT_T * this_port, SCIC_SDS_PHY_T * the_phy, BOOL do_notify_user, BOOL do_resume_phy ) { SCIC_SDS_CONTROLLER_T * controller; SCI_SAS_IDENTIFY_ADDRESS_FRAME_PROTOCOLS_T protocols; SCIC_LOG_TRACE(( sci_base_object_get_logger(this_port), SCIC_LOG_OBJECT_PORT, "scic_sds_port_activate_phy(0x%x,0x%x,0x%x) enter\n", this_port, the_phy, do_notify_user )); controller = scic_sds_port_get_controller(this_port); scic_sds_phy_get_attached_phy_protocols(the_phy, &protocols); // If this is sata port then the phy has already been resumed if (!protocols.u.bits.stp_target) { if (do_resume_phy == TRUE) { scic_sds_port_resume_phy(this_port, the_phy); } } this_port->active_phy_mask |= 1 << the_phy->phy_index; scic_sds_controller_clear_invalid_phy(controller, the_phy); if (do_notify_user == TRUE) scic_cb_port_link_up(this_port->owning_controller, this_port, the_phy); } /** * This method will deactivate the supplied phy in the port. * * @param[in] this_port This is the port on which the phy should be * deactivated. * @param[in] the_phy This is the specific phy that is no longer * active in the port. * @param[in] do_notify_user This parameter specifies whether to inform * the user (via scic_cb_port_link_down()) as to the fact that * a new phy as become ready. * * @return none */ void scic_sds_port_deactivate_phy( SCIC_SDS_PORT_T * this_port, SCIC_SDS_PHY_T * the_phy, BOOL do_notify_user ) { SCIC_LOG_TRACE(( sci_base_object_get_logger(this_port), SCIC_LOG_OBJECT_PORT, "scic_sds_port_deactivate_phy(0x%x,0x%x,0x%x) enter\n", this_port, the_phy, do_notify_user )); this_port->active_phy_mask &= ~(1 << the_phy->phy_index); this_port->enabled_phy_mask &= ~(1 << the_phy->phy_index); the_phy->max_negotiated_speed = SCI_SAS_NO_LINK_RATE; // Re-assign the phy back to the LP as if it were a narrow port for APC mode. // For MPC mode, the phy will remain in the port if (this_port->owning_controller->oem_parameters.sds1.controller.mode_type == SCIC_PORT_AUTOMATIC_CONFIGURATION_MODE) { SCU_PCSPExCR_WRITE(this_port, the_phy->phy_index, the_phy->phy_index); } if (do_notify_user == TRUE) scic_cb_port_link_down(this_port->owning_controller, this_port, the_phy); } /** * This method will disable the phy and report that the phy is not valid for this * port object. * * @param[in] this_port This is the port on which the phy should be disabled. * @param[in] the_phy This is the specific phy which to disabled. * * @return None */ void scic_sds_port_invalid_link_up( SCIC_SDS_PORT_T * this_port, SCIC_SDS_PHY_T * the_phy ) { SCIC_SDS_CONTROLLER_T * controller = scic_sds_port_get_controller(this_port); // 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 ((controller->invalid_phy_mask & (1 << the_phy->phy_index)) == 0) { scic_sds_controller_set_invalid_phy(controller, the_phy); scic_cb_port_invalid_link_up(controller, this_port, the_phy); } } /** * @brief 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. * * @param[in] this_port The port for which the wide port condition is to be * checked. * * @return BOOL * @retval TRUE Is returned if this is a wide ported port. * @retval FALSE Is returned if this is a narrow port. */ static BOOL scic_sds_port_is_wide( SCIC_SDS_PORT_T *this_port ) { U32 index; U32 phy_count = 0; for (index = 0; index < SCI_MAX_PHYS; index++) { if (this_port->phy_table[index] != NULL) { phy_count++; } } return (phy_count != 1); } /** * @brief 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. * * @param[in] this_port The port associated with the phy object. * @param[in] the_phy The phy object that is trying to go link up. * * @return TRUE if the phy object can continue to the link up condition. * @retval TRUE Is returned if this phy can continue to the ready state. * @retval FALSE Is returned if can not continue on to the ready state. * * @note 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( SCIC_SDS_PORT_T *this_port, SCIC_SDS_PHY_T *the_phy ) { SCI_SAS_IDENTIFY_ADDRESS_FRAME_PROTOCOLS_T protocols; scic_sds_phy_get_attached_phy_protocols(the_phy, &protocols); if ( (this_port->logical_port_index != SCIC_SDS_DUMMY_PORT) && (protocols.u.bits.stp_target) ) { if (scic_sds_port_is_wide(this_port)) { //direct attached Sata phy cannot be in wide port. scic_sds_port_invalid_link_up( this_port, the_phy); return FALSE; } else { SCIC_SDS_PORT_T *destination_port = &(this_port->owning_controller->port_table[the_phy->phy_index]); //add the phy to the its logical port for direct attached SATA. The phy will be added //to port whose port_index will be the phy_index. SCU_PCSPExCR_WRITE( destination_port, the_phy->phy_index, the_phy->phy_index); } } return TRUE; } /** * @brief This method is the entry point for the phy to inform * the port that it is now in a ready state * * @param[in] this_port * @param[in] phy */ void scic_sds_port_link_up( SCIC_SDS_PORT_T *this_port, SCIC_SDS_PHY_T *the_phy ) { the_phy->is_in_link_training = FALSE; this_port->state_handlers->link_up_handler(this_port, the_phy); } /** * @brief This method is the entry point for the phy to inform * the port that it is no longer in a ready state * * @param[in] this_port * @param[in] phy */ void scic_sds_port_link_down( SCIC_SDS_PORT_T *this_port, SCIC_SDS_PHY_T *the_phy ) { this_port->state_handlers->link_down_handler(this_port, the_phy); } /** * @brief This method is called to start an IO request on this port. * * @param[in] this_port * @param[in] the_device * @param[in] the_io_request * * @return SCI_STATUS */ SCI_STATUS scic_sds_port_start_io( SCIC_SDS_PORT_T *this_port, SCIC_SDS_REMOTE_DEVICE_T *the_device, SCIC_SDS_REQUEST_T *the_io_request ) { return this_port->state_handlers->start_io_handler( this_port, the_device, the_io_request); } /** * @brief This method is called to complete an IO request to the port. * * @param[in] this_port * @param[in] the_device * @param[in] the_io_request * * @return SCI_STATUS */ SCI_STATUS scic_sds_port_complete_io( SCIC_SDS_PORT_T *this_port, SCIC_SDS_REMOTE_DEVICE_T *the_device, SCIC_SDS_REQUEST_T *the_io_request ) { return this_port->state_handlers->complete_io_handler( this_port, the_device, the_io_request); } /** * @brief This method is provided to timeout requests for port operations. * Mostly its for the port reset operation. * * @param[in] port This is the parameter or cookie value that is provided * to the timer construct operation. */ void scic_sds_port_timeout_handler( void *port ) { U32 current_state; SCIC_SDS_PORT_T * this_port; this_port = (SCIC_SDS_PORT_T *)port; current_state = sci_base_state_machine_get_state( &this_port->parent.state_machine); if (current_state == SCI_BASE_PORT_STATE_RESETTING) { // if the port is still in the resetting state then the timeout fired // before the reset completed. sci_base_state_machine_change_state( &this_port->parent.state_machine, SCI_BASE_PORT_STATE_FAILED ); } else if (current_state == SCI_BASE_PORT_STATE_STOPPED) { // if the port is stopped then the start request failed // In this case stay in the stopped state. SCIC_LOG_ERROR(( sci_base_object_get_logger(this_port), SCIC_LOG_OBJECT_PORT, "SCIC Port 0x%x failed to stop before tiemout.\n", this_port )); } else if (current_state == SCI_BASE_PORT_STATE_STOPPING) { // if the port is still stopping then the stop has not completed scic_cb_port_stop_complete( scic_sds_port_get_controller(this_port), port, SCI_FAILURE_TIMEOUT ); } else { // The port is in the ready state and we have a timer reporting a timeout // this should not happen. SCIC_LOG_ERROR(( sci_base_object_get_logger(this_port), SCIC_LOG_OBJECT_PORT, "SCIC Port 0x%x is processing a timeout operation in state %d.\n", this_port, current_state )); } } // --------------------------------------------------------------------------- #ifdef SCIC_DEBUG_ENABLED void scic_sds_port_decrement_request_count( SCIC_SDS_PORT_T *this_port ) { if (this_port->started_request_count == 0) { SCIC_LOG_WARNING(( sci_base_object_get_logger(this_port), SCIC_LOG_OBJECT_PORT, "SCIC Port object requested to decrement started io count past zero.\n" )); } else { this_port->started_request_count--; } } #endif /** * @brief This function updates the hardwares VIIT entry for this port. * * @param[in] this_port */ void scic_sds_port_update_viit_entry( SCIC_SDS_PORT_T *this_port ) { SCI_SAS_ADDRESS_T sas_address; scic_sds_port_get_sas_address(this_port, &sas_address); scu_port_viit_register_write( this_port, initiator_sas_address_hi, sas_address.high); scu_port_viit_register_write( this_port, initiator_sas_address_lo, sas_address.low); // This value get cleared just in case its not already cleared scu_port_viit_register_write( this_port, reserved, 0); // We are required to update the status register last scu_port_viit_register_write( this_port, status, ( SCU_VIIT_ENTRY_ID_VIIT | SCU_VIIT_IPPT_INITIATOR | ((1UL << this_port->physical_port_index) << SCU_VIIT_ENTRY_LPVIE_SHIFT) | SCU_VIIT_STATUS_ALL_VALID ) ); } /** * @brief 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. * * @param[in] this_port This parameter specifies the port for which to * retrieve the maximum allowed speed. * * @return This method returns the maximum negotiated speed of the slowest * phy in the port. */ SCI_SAS_LINK_RATE scic_sds_port_get_max_allowed_speed( SCIC_SDS_PORT_T * this_port ) { U16 index = 0; SCI_SAS_LINK_RATE max_allowed_speed = SCI_SAS_600_GB; SCIC_SDS_PHY_T * 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 = this_port->phy_table[index]; if ( (phy != NULL) && (scic_sds_port_active_phy(this_port, phy) == TRUE) && (phy->max_negotiated_speed < max_allowed_speed) ) max_allowed_speed = phy->max_negotiated_speed; } return max_allowed_speed; } /** * @brief This method passes the event to core user. * @param[in] this_port The port that a BCN happens. * @param[in] this_phy The phy that receives BCN. * * @return none */ void scic_sds_port_broadcast_change_received( SCIC_SDS_PORT_T * this_port, SCIC_SDS_PHY_T * this_phy ) { //notify the user. scic_cb_port_bc_change_primitive_recieved( this_port->owning_controller, this_port, this_phy ); } /** * @brief This API methhod enables the broadcast change notification from * underneath hardware. * @param[in] this_port The port that a BCN had been disabled from. * * @return none */ void scic_port_enable_broadcast_change_notification( SCI_PORT_HANDLE_T port ) { SCIC_SDS_PORT_T * this_port = (SCIC_SDS_PORT_T *)port; SCIC_SDS_PHY_T * phy; U32 register_value; U8 index; // Loop through all of the phys to enable BCN. for (index = 0; index < SCI_MAX_PHYS; index++) { phy = this_port->phy_table[index]; if ( phy != NULL) { register_value = SCU_SAS_LLCTL_READ(phy); // clear the bit by writing 1. SCU_SAS_LLCTL_WRITE(phy, register_value); } } } /** * @brief This method release resources in for a scic port. * * @param[in] controller This parameter specifies the core controller, one of * its phy's resources are to be released. * @param[in] this_port This parameter specifies the port whose resource is to * be released. */ void scic_sds_port_release_resource( SCIC_SDS_CONTROLLER_T * controller, SCIC_SDS_PORT_T *this_port ) { SCIC_LOG_TRACE(( sci_base_object_get_logger(this_port), SCIC_LOG_OBJECT_PORT, "scic_sds_port_release_resource(0x%x, 0x%x)\n", controller, this_port )); //Currently, the only resource to be released is a timer. if (this_port->timer_handle != NULL) { scic_cb_timer_destroy(controller, this_port->timer_handle); this_port->timer_handle = NULL; } } //****************************************************************************** //* PORT STATE MACHINE //****************************************************************************** //*************************************************************************** //* DEFAULT HANDLERS //*************************************************************************** /** * This is the default method for port a start request. It will report a * warning and exit. * * @param[in] port This is the SCI_BASE_PORT object which is cast into a * SCIC_SDS_PORT object. * * @return SCI_STATUS * @retval SCI_FAILURE_INVALID_STATE */ SCI_STATUS scic_sds_port_default_start_handler( SCI_BASE_PORT_T *port ) { SCIC_LOG_WARNING(( sci_base_object_get_logger((SCIC_SDS_PORT_T *)port), SCIC_LOG_OBJECT_PORT, "SCIC Port 0x%08x requested to start while in invalid state %d\n", port, sci_base_state_machine_get_state( scic_sds_port_get_base_state_machine((SCIC_SDS_PORT_T *)port)) )); return SCI_FAILURE_INVALID_STATE; } /** * This is the default method for a port stop request. It will report a * warning and exit. * * @param[in] port This is the SCI_BASE_PORT object which is cast into a * SCIC_SDS_PORT object. * * @return SCI_STATUS * @retval SCI_FAILURE_INVALID_STATE */ SCI_STATUS scic_sds_port_default_stop_handler( SCI_BASE_PORT_T *port ) { SCIC_LOG_WARNING(( sci_base_object_get_logger((SCIC_SDS_PORT_T *)port), SCIC_LOG_OBJECT_PORT, "SCIC Port 0x%08x requested to stop while in invalid state %d\n", port, sci_base_state_machine_get_state( scic_sds_port_get_base_state_machine((SCIC_SDS_PORT_T *)port)) )); return SCI_FAILURE_INVALID_STATE; } /** * This is the default method for a port destruct request. It will report a * warning and exit. * * @param[in] port This is the SCI_BASE_PORT object which is cast into a * SCIC_SDS_PORT object. * * @return SCI_STATUS * @retval SCI_FAILURE_INVALID_STATE */ SCI_STATUS scic_sds_port_default_destruct_handler( SCI_BASE_PORT_T *port ) { SCIC_LOG_WARNING(( sci_base_object_get_logger((SCIC_SDS_PORT_T *)port), SCIC_LOG_OBJECT_PORT, "SCIC Port 0x%08x requested to destruct while in invalid state %d\n", port, sci_base_state_machine_get_state( scic_sds_port_get_base_state_machine((SCIC_SDS_PORT_T *)port)) )); return SCI_FAILURE_INVALID_STATE; } /** * This is the default method for a port reset request. It will report a * warning and exit. * * @param[in] port This is the SCI_BASE_PORT object which is cast into a * SCIC_SDS_PORT object. * @param[in] timeout This is the timeout for the reset request to complete. * * @return SCI_STATUS * @retval SCI_FAILURE_INVALID_STATE */ SCI_STATUS scic_sds_port_default_reset_handler( SCI_BASE_PORT_T * port, U32 timeout ) { SCIC_LOG_WARNING(( sci_base_object_get_logger((SCIC_SDS_PORT_T *)port), SCIC_LOG_OBJECT_PORT, "SCIC Port 0x%08x requested to reset while in invalid state %d\n", port, sci_base_state_machine_get_state( scic_sds_port_get_base_state_machine((SCIC_SDS_PORT_T *)port)) )); return SCI_FAILURE_INVALID_STATE; } /** * This is the default method for a port add phy request. It will report a * warning and exit. * * @param[in] port This is the SCI_BASE_PORT object which is cast into a * SCIC_SDS_PORT object. * * @return SCI_STATUS * @retval SCI_FAILURE_INVALID_STATE */ SCI_STATUS scic_sds_port_default_add_phy_handler( SCI_BASE_PORT_T *port, SCI_BASE_PHY_T *phy ) { SCIC_LOG_WARNING(( sci_base_object_get_logger((SCIC_SDS_PORT_T *)port), SCIC_LOG_OBJECT_PORT, "SCIC Port 0x%08x requested to add phy 0x%08x while in invalid state %d\n", port, phy, sci_base_state_machine_get_state( scic_sds_port_get_base_state_machine((SCIC_SDS_PORT_T *)port)) )); return SCI_FAILURE_INVALID_STATE; } /** * This is the default method for a port remove phy request. It will report a * warning and exit. * * @param[in] port This is the SCI_BASE_PORT object which is cast into a * SCIC_SDS_PORT object. * * @return SCI_STATUS * @retval SCI_FAILURE_INVALID_STATE */ SCI_STATUS scic_sds_port_default_remove_phy_handler( SCI_BASE_PORT_T *port, SCI_BASE_PHY_T *phy ) { SCIC_LOG_WARNING(( sci_base_object_get_logger((SCIC_SDS_PORT_T *)port), SCIC_LOG_OBJECT_PORT, "SCIC Port 0x%08x requested to remove phy 0x%08x while in invalid state %d\n", port, phy, sci_base_state_machine_get_state( scic_sds_port_get_base_state_machine((SCIC_SDS_PORT_T *)port)) )); return SCI_FAILURE_INVALID_STATE; } /** * This is the default method for a port unsolicited frame request. It will * report a warning and exit. * * @param[in] port This is the SCI_BASE_PORT object which is cast into a * SCIC_SDS_PORT object. * * @return SCI_STATUS * @retval SCI_FAILURE_INVALID_STATE * * @todo Is it even possible to receive an unsolicited frame directed to a * port object? It seems possible if we implementing virtual functions * but until then? */ SCI_STATUS scic_sds_port_default_frame_handler( SCIC_SDS_PORT_T * port, U32 frame_index ) { SCIC_SDS_PORT_T *this_port = (SCIC_SDS_PORT_T *)port; SCIC_LOG_WARNING(( sci_base_object_get_logger(this_port), SCIC_LOG_OBJECT_PORT, "SCIC Port 0x%08x requested to process frame %d while in invalid state %d\n", port, frame_index, sci_base_state_machine_get_state( scic_sds_port_get_base_state_machine(this_port)) )); scic_sds_controller_release_frame( scic_sds_port_get_controller(this_port), frame_index ); return SCI_FAILURE_INVALID_STATE; } /** * This is the default method for a port event request. It will report a * warning and exit. * * @param[in] port This is the SCI_BASE_PORT object which is cast into a * SCIC_SDS_PORT object. * * @return SCI_STATUS * @retval SCI_FAILURE_INVALID_STATE */ SCI_STATUS scic_sds_port_default_event_handler( SCIC_SDS_PORT_T * port, U32 event_code ) { SCIC_LOG_WARNING(( sci_base_object_get_logger((SCIC_SDS_PORT_T *)port), SCIC_LOG_OBJECT_PORT, "SCIC Port 0x%08x requested to process event 0x%08x while in invalid state %d\n", port, event_code, sci_base_state_machine_get_state( scic_sds_port_get_base_state_machine((SCIC_SDS_PORT_T *)port)) )); return SCI_FAILURE_INVALID_STATE; } /** * This is the default method for a port link up notification. It will report * a warning and exit. * * @param[in] port This is the SCI_BASE_PORT object which is cast into a * SCIC_SDS_PORT object. * * @return SCI_STATUS * @retval SCI_FAILURE_INVALID_STATE */ void scic_sds_port_default_link_up_handler( SCIC_SDS_PORT_T *this_port, SCIC_SDS_PHY_T *phy ) { SCIC_LOG_WARNING(( sci_base_object_get_logger(this_port), SCIC_LOG_OBJECT_PORT, "SCIC Port 0x%08x received link_up notification from phy 0x%08x while in invalid state %d\n", this_port, phy, sci_base_state_machine_get_state( scic_sds_port_get_base_state_machine(this_port)) )); } /** * This is the default method for a port link down notification. It will * report a warning and exit. * * @param[in] port This is the SCI_BASE_PORT object which is cast into a * SCIC_SDS_PORT object. * * @return SCI_STATUS * @retval SCI_FAILURE_INVALID_STATE */ void scic_sds_port_default_link_down_handler( SCIC_SDS_PORT_T *this_port, SCIC_SDS_PHY_T *phy ) { SCIC_LOG_WARNING(( sci_base_object_get_logger(this_port), SCIC_LOG_OBJECT_PORT, "SCIC Port 0x%08x received link down notification from phy 0x%08x while in invalid state %d\n", this_port, phy, sci_base_state_machine_get_state( scic_sds_port_get_base_state_machine(this_port)) )); } /** * This is the default method for a port start io request. It will report a * warning and exit. * * @param[in] port This is the SCI_BASE_PORT object which is cast into a * SCIC_SDS_PORT object. * * @return SCI_STATUS * @retval SCI_FAILURE_INVALID_STATE */ SCI_STATUS scic_sds_port_default_start_io_handler( SCIC_SDS_PORT_T *this_port, SCIC_SDS_REMOTE_DEVICE_T *device, SCIC_SDS_REQUEST_T *io_request ) { SCIC_LOG_WARNING(( sci_base_object_get_logger(this_port), SCIC_LOG_OBJECT_PORT, "SCIC Port 0x%08x requested to start io request 0x%08x while in invalid state %d\n", this_port, io_request, sci_base_state_machine_get_state( scic_sds_port_get_base_state_machine(this_port)) )); return SCI_FAILURE_INVALID_STATE; } /** * This is the default method for a port complete io request. It will report * a warning and exit. * * @param[in] port This is the SCI_BASE_PORT object which is cast into a * SCIC_SDS_PORT object. * * @return SCI_STATUS * @retval SCI_FAILURE_INVALID_STATE */ SCI_STATUS scic_sds_port_default_complete_io_handler( SCIC_SDS_PORT_T *this_port, SCIC_SDS_REMOTE_DEVICE_T *device, SCIC_SDS_REQUEST_T *io_request ) { SCIC_LOG_WARNING(( sci_base_object_get_logger(this_port), SCIC_LOG_OBJECT_PORT, "SCIC Port 0x%08x requested to complete io request 0x%08x while in invalid state %d\n", this_port, io_request, sci_base_state_machine_get_state( scic_sds_port_get_base_state_machine(this_port)) )); return SCI_FAILURE_INVALID_STATE; } //**************************************************************************** //* GENERAL STATE HANDLERS //**************************************************************************** /** * This is a general complete io request handler for the SCIC_SDS_PORT object. * * @param[in] port This is the SCIC_SDS_PORT object on which the io request * count will be decremented. * @param[in] device This is the SCIC_SDS_REMOTE_DEVICE object to which the io * request is being directed. This parameter is not required to * complete this operation. * @param[in] io_request This is the request that is being completed on this * port object. This parameter is not required to complete this * operation. * * @return SCI_STATUS * @retval SCI_SUCCESS */ static SCI_STATUS scic_sds_port_general_complete_io_handler( SCIC_SDS_PORT_T *port, SCIC_SDS_REMOTE_DEVICE_T *device, SCIC_SDS_REQUEST_T *io_request ) { SCIC_SDS_PORT_T *this_port = (SCIC_SDS_PORT_T *)port; scic_sds_port_decrement_request_count(this_port); return SCI_SUCCESS; } //**************************************************************************** //* STOPPED STATE HANDLERS //**************************************************************************** static BOOL scic_sds_port_requires_scheduler_workaround( SCIC_SDS_PORT_T * this_port ) { if ( ( this_port->owning_controller->logical_port_entries < this_port->owning_controller->task_context_entries ) && ( this_port->owning_controller->logical_port_entries < this_port->owning_controller->remote_node_entries ) ) { return TRUE; } return FALSE; } /** * This method takes the SCIC_SDS_PORT from a stopped state and attempts to * start it. To start a port it must have no assiged devices and it must have * at least one phy assigned to it. If those conditions are met then the port * can transition to the ready state. * * @param[in] port This is the SCI_BASE_PORT object which is cast into a * SCIC_SDS_PORT object. * * @return SCI_STATUS * @retval SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION This SCIC_SDS_PORT * object could not be started because the port configuration is not * valid. * @retval SCI_SUCCESS the start request is successful and the SCIC_SDS_PORT * object has transitioned to the SCI_BASE_PORT_STATE_READY. */ static SCI_STATUS scic_sds_port_stopped_state_start_handler( SCI_BASE_PORT_T *port ) { U32 phy_mask; SCI_STATUS status = SCI_SUCCESS; SCIC_SDS_PORT_T *this_port = (SCIC_SDS_PORT_T *)port; if (this_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; } this_port->timer_handle = scic_cb_timer_create( scic_sds_port_get_controller(this_port), scic_sds_port_timeout_handler, this_port ); if (this_port->timer_handle == SCI_INVALID_HANDLE) { return SCI_FAILURE_INSUFFICIENT_RESOURCES; } if (scic_sds_port_requires_scheduler_workaround(this_port)) { if (this_port->reserved_rni == SCU_DUMMY_INDEX) { this_port->reserved_rni = scic_sds_remote_node_table_allocate_remote_node( &this_port->owning_controller->available_remote_nodes, 1 ); if (this_port->reserved_rni != SCU_DUMMY_INDEX) { scic_sds_port_construct_dummy_rnc( this_port, this_port->reserved_rni ); } else { status = SCI_FAILURE_INSUFFICIENT_RESOURCES; } } if (this_port->reserved_tci == SCU_DUMMY_INDEX) { // Allocate a TCI and remove the sequence nibble this_port->reserved_tci = scic_controller_allocate_io_tag(this_port->owning_controller); if (this_port->reserved_tci != SCU_DUMMY_INDEX) { scic_sds_port_construct_dummy_task(this_port, this_port->reserved_tci); } else { status = SCI_FAILURE_INSUFFICIENT_RESOURCES; } } } if (status == SCI_SUCCESS) { phy_mask = scic_sds_port_get_phys(this_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(this_port, phy_mask) == TRUE) { sci_base_state_machine_change_state( scic_sds_port_get_base_state_machine(this_port), SCI_BASE_PORT_STATE_READY ); } else { status = SCI_FAILURE; } } if (status != SCI_SUCCESS) { scic_sds_port_destroy_dummy_resources(this_port); } return status; } /** * This method takes the SCIC_SDS_PORT that is in a stopped state and handles * a stop request. This function takes no action. * * @param[in] port This is the SCI_BASE_PORT object which is cast into a * SCIC_SDS_PORT object. * * @return SCI_STATUS * @retval SCI_SUCCESS the stop request is successful as the SCIC_SDS_PORT * object is already stopped. */ static SCI_STATUS scic_sds_port_stopped_state_stop_handler( SCI_BASE_PORT_T *port ) { // We are already stopped so there is nothing to do here return SCI_SUCCESS; } /** * This method takes the SCIC_SDS_PORT that is in a stopped state and handles * the destruct request. The stopped state is the only state in which the * SCIC_SDS_PORT can be destroyed. This function causes the port object to * transition to the SCI_BASE_PORT_STATE_FINAL. * * @param[in] port This is the SCI_BASE_PORT object which is cast into a * SCIC_SDS_PORT object. * * @return SCI_STATUS * @retval SCI_SUCCESS */ static SCI_STATUS scic_sds_port_stopped_state_destruct_handler( SCI_BASE_PORT_T *port ) { SCIC_SDS_PORT_T *this_port = (SCIC_SDS_PORT_T *)port; sci_base_state_machine_stop(&this_port->parent.state_machine); return SCI_SUCCESS; } /** * This method takes the SCIC_SDS_PORT that is in a stopped state and handles * the add phy request. In MPC mode the only time a phy can be added to a * port is in the SCI_BASE_PORT_STATE_STOPPED. * * @param[in] port This is the SCI_BASE_PORT object which is cast into a * SCIC_SDS_PORT object. * @param[in] phy This is the SCI_BASE_PHY object which is cast into a * SCIC_SDS_PHY object. * * @return SCI_STATUS * @retval SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION is returned when the phy * can not be added to the port. * @retval SCI_SUCCESS if the phy is added to the port. */ static SCI_STATUS scic_sds_port_stopped_state_add_phy_handler( SCI_BASE_PORT_T *port, SCI_BASE_PHY_T *phy ) { SCIC_SDS_PORT_T * this_port = (SCIC_SDS_PORT_T *)port; SCIC_SDS_PHY_T * this_phy = (SCIC_SDS_PHY_T *)phy; SCI_SAS_ADDRESS_T port_sas_address; // Read the port assigned SAS Address if there is one scic_sds_port_get_sas_address(this_port, &port_sas_address); if (port_sas_address.high != 0 && port_sas_address.low != 0) { SCI_SAS_ADDRESS_T phy_sas_address; // Make sure that the PHY SAS Address matches the SAS Address // for this port. scic_sds_phy_get_sas_address(this_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(this_port, this_phy); } /** * This method takes the SCIC_SDS_PORT that is in a stopped state and handles * the remove phy request. In MPC mode the only time a phy can be removed * from a port is in the SCI_BASE_PORT_STATE_STOPPED. * * @param[in] port This is the SCI_BASE_PORT object which is cast into a * SCIC_SDS_PORT object. * @param[in] phy This is the SCI_BASE_PHY object which is cast into a * SCIC_SDS_PHY object. * * @return SCI_STATUS * @retval SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION is returned when the phy * can not be added to the port. * @retval SCI_SUCCESS if the phy is added to the port. */ static SCI_STATUS scic_sds_port_stopped_state_remove_phy_handler( SCI_BASE_PORT_T *port, SCI_BASE_PHY_T *phy ) { SCIC_SDS_PORT_T *this_port = (SCIC_SDS_PORT_T *)port; SCIC_SDS_PHY_T *this_phy = (SCIC_SDS_PHY_T *)phy; return scic_sds_port_clear_phy(this_port, this_phy); } //**************************************************************************** //* READY STATE HANDLERS //**************************************************************************** //**************************************************************************** //* RESETTING STATE HANDLERS //**************************************************************************** //**************************************************************************** //* STOPPING STATE HANDLERS //**************************************************************************** /** * This method takes the SCIC_SDS_PORT that is in a stopping state and handles * the complete io request. Should the request count reach 0 then the port * object will transition to the stopped state. * * @param[in] port This is the SCIC_SDS_PORT object on which the io request * count will be decremented. * @param[in] device This is the SCIC_SDS_REMOTE_DEVICE object to which the io * request is being directed. This parameter is not required to * complete this operation. * @param[in] io_request This is the request that is being completed on this * port object. This parameter is not required to complete this * operation. * * @return SCI_STATUS * @retval SCI_SUCCESS */ static SCI_STATUS scic_sds_port_stopping_state_complete_io_handler( SCIC_SDS_PORT_T *port, SCIC_SDS_REMOTE_DEVICE_T *device, SCIC_SDS_REQUEST_T *io_request ) { SCIC_SDS_PORT_T *this_port = (SCIC_SDS_PORT_T *)port; scic_sds_port_decrement_request_count(this_port); if (this_port->started_request_count == 0) { sci_base_state_machine_change_state( scic_sds_port_get_base_state_machine(this_port), SCI_BASE_PORT_STATE_STOPPED ); } return SCI_SUCCESS; } //**************************************************************************** //* RESETTING STATE HANDLERS //**************************************************************************** /** * This method will stop a failed port. This causes a transition to the * stopping state. * * @param[in] port This is the port object which is being requested to stop. * * @return SCI_STATUS * @retval SCI_SUCCESS */ static SCI_STATUS scic_sds_port_reset_state_stop_handler( SCI_BASE_PORT_T *port ) { SCIC_SDS_PORT_T *this_port = (SCIC_SDS_PORT_T *)port; sci_base_state_machine_change_state( &this_port->parent.state_machine, SCI_BASE_PORT_STATE_STOPPING ); return SCI_SUCCESS; } /** * This method will transition a failed port to its ready state. The port * failed because a hard reset request timed out but at some time later one or * more phys in the port became ready. * * @param[in] port This is the SCI_BASE_PORT object which is cast into a * SCIC_SDS_PORT object. * * @return SCI_STATUS * @retval SCI_SUCCESS */ static void scic_sds_port_reset_state_link_up_handler( SCIC_SDS_PORT_T *this_port, SCIC_SDS_PHY_T *phy ) { /// @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(this_port, phy, FALSE, TRUE); } /** * This method process link down notifications that occur during a * port reset operation. Link downs can occur during the reset operation. * * @param[in] port This is the SCI_BASE_PORT object which is cast into a * SCIC_SDS_PORT object. * * @return SCI_STATUS * @retval SCI_SUCCESS */ static void scic_sds_port_reset_state_link_down_handler( SCIC_SDS_PORT_T *this_port, SCIC_SDS_PHY_T *phy ) { // In the resetting state we don't notify the user regarding // link up and link down notifications. scic_sds_port_deactivate_phy(this_port, phy, FALSE); } // --------------------------------------------------------------------------- SCIC_SDS_PORT_STATE_HANDLER_T scic_sds_port_state_handler_table[SCI_BASE_PORT_MAX_STATES] = { // SCI_BASE_PORT_STATE_STOPPED { { scic_sds_port_stopped_state_start_handler, scic_sds_port_stopped_state_stop_handler, scic_sds_port_stopped_state_destruct_handler, scic_sds_port_default_reset_handler, scic_sds_port_stopped_state_add_phy_handler, scic_sds_port_stopped_state_remove_phy_handler }, scic_sds_port_default_frame_handler, scic_sds_port_default_event_handler, scic_sds_port_default_link_up_handler, scic_sds_port_default_link_down_handler, scic_sds_port_default_start_io_handler, scic_sds_port_default_complete_io_handler }, // SCI_BASE_PORT_STATE_STOPPING { { scic_sds_port_default_start_handler, scic_sds_port_default_stop_handler, scic_sds_port_default_destruct_handler, scic_sds_port_default_reset_handler, scic_sds_port_default_add_phy_handler, scic_sds_port_default_remove_phy_handler }, scic_sds_port_default_frame_handler, scic_sds_port_default_event_handler, scic_sds_port_default_link_up_handler, scic_sds_port_default_link_down_handler, scic_sds_port_default_start_io_handler, scic_sds_port_stopping_state_complete_io_handler }, // SCI_BASE_PORT_STATE_READY { { scic_sds_port_default_start_handler, scic_sds_port_default_stop_handler, scic_sds_port_default_destruct_handler, scic_sds_port_default_reset_handler, scic_sds_port_default_add_phy_handler, scic_sds_port_default_remove_phy_handler }, scic_sds_port_default_frame_handler, scic_sds_port_default_event_handler, scic_sds_port_default_link_up_handler, scic_sds_port_default_link_down_handler, scic_sds_port_default_start_io_handler, scic_sds_port_general_complete_io_handler }, // SCI_BASE_PORT_STATE_RESETTING { { scic_sds_port_default_start_handler, scic_sds_port_reset_state_stop_handler, scic_sds_port_default_destruct_handler, scic_sds_port_default_reset_handler, scic_sds_port_default_add_phy_handler, scic_sds_port_default_remove_phy_handler }, scic_sds_port_default_frame_handler, scic_sds_port_default_event_handler, scic_sds_port_reset_state_link_up_handler, scic_sds_port_reset_state_link_down_handler, scic_sds_port_default_start_io_handler, scic_sds_port_general_complete_io_handler }, // SCI_BASE_PORT_STATE_FAILED { { scic_sds_port_default_start_handler, scic_sds_port_default_stop_handler, scic_sds_port_default_destruct_handler, scic_sds_port_default_reset_handler, scic_sds_port_default_add_phy_handler, scic_sds_port_default_remove_phy_handler }, scic_sds_port_default_frame_handler, scic_sds_port_default_event_handler, scic_sds_port_default_link_up_handler, scic_sds_port_default_link_down_handler, scic_sds_port_default_start_io_handler, scic_sds_port_general_complete_io_handler } }; //****************************************************************************** //* PORT STATE PRIVATE METHODS //****************************************************************************** /** * This method will enable the SCU Port Task Scheduler for this port object * but will leave the port task scheduler in a suspended state. * * @param[in] this_port This is the port object which to suspend. * * @return none */ static void scic_sds_port_enable_port_task_scheduler( SCIC_SDS_PORT_T *this_port ) { U32 pts_control_value; pts_control_value = scu_port_task_scheduler_read(this_port, control); pts_control_value |= SCU_PTSxCR_GEN_BIT(ENABLE) | SCU_PTSxCR_GEN_BIT(SUSPEND); scu_port_task_scheduler_write(this_port, control, pts_control_value); } /** * This method will disable the SCU port task scheduler for this port * object. * * @param[in] this_port This is the port object which to resume. * * @return none */ static void scic_sds_port_disable_port_task_scheduler( SCIC_SDS_PORT_T *this_port ) { U32 pts_control_value; pts_control_value = scu_port_task_scheduler_read(this_port, control); pts_control_value &= ~( SCU_PTSxCR_GEN_BIT(ENABLE) | SCU_PTSxCR_GEN_BIT(SUSPEND) ); scu_port_task_scheduler_write(this_port, control, pts_control_value); } /** * */ static void scic_sds_port_post_dummy_remote_node( SCIC_SDS_PORT_T *this_port ) { U32 command; SCU_REMOTE_NODE_CONTEXT_T * rnc; if (this_port->reserved_rni != SCU_DUMMY_INDEX) { rnc = &(this_port->owning_controller->remote_node_context_table[this_port->reserved_rni]); rnc->ssp.is_valid = TRUE; command = ( (SCU_CONTEXT_COMMAND_POST_RNC_32) | (this_port->physical_port_index << SCU_CONTEXT_COMMAND_LOGICAL_PORT_SHIFT) | (this_port->reserved_rni) ); scic_sds_controller_post_request(this_port->owning_controller, command); scic_cb_stall_execution(10); command = ( (SCU_CONTEXT_COMMAND_POST_RNC_SUSPEND_TX_RX) | (this_port->physical_port_index << SCU_CONTEXT_COMMAND_LOGICAL_PORT_SHIFT) | (this_port->reserved_rni) ); scic_sds_controller_post_request(this_port->owning_controller, command); } } /** * */ static void scic_sds_port_invalidate_dummy_remote_node( SCIC_SDS_PORT_T *this_port ) { U32 command; SCU_REMOTE_NODE_CONTEXT_T * rnc; if (this_port->reserved_rni != SCU_DUMMY_INDEX) { rnc = &(this_port->owning_controller->remote_node_context_table[this_port->reserved_rni]); rnc->ssp.is_valid = FALSE; scic_cb_stall_execution(10); command = ( (SCU_CONTEXT_COMMAND_POST_RNC_INVALIDATE) | (this_port->physical_port_index << SCU_CONTEXT_COMMAND_LOGICAL_PORT_SHIFT) | (this_port->reserved_rni) ); scic_sds_controller_post_request(this_port->owning_controller, command); } } //****************************************************************************** //* PORT STATE METHODS //****************************************************************************** /** * This method will perform the actions required by the SCIC_SDS_PORT on * entering the SCI_BASE_PORT_STATE_STOPPED. This function sets the stopped * state handlers for the SCIC_SDS_PORT object and disables the port task * scheduler in the hardware. * * @param[in] object This is the SCI_BASE_OBJECT which is cast to a * SCIC_SDS_PORT object. * * @return none */ static void scic_sds_port_stopped_state_enter( SCI_BASE_OBJECT_T *object ) { SCIC_SDS_PORT_T *this_port; this_port = (SCIC_SDS_PORT_T *)object; scic_sds_port_set_base_state_handlers( this_port, SCI_BASE_PORT_STATE_STOPPED ); if ( SCI_BASE_PORT_STATE_STOPPING == this_port->parent.state_machine.previous_state_id ) { // 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(this_port); } } /** * This method will perform the actions required by the SCIC_SDS_PORT on * exiting the SCI_BASE_STATE_STOPPED. This function enables the SCU hardware * port task scheduler. * * @param[in] object This is the SCI_BASE_OBJECT which is cast to a * SCIC_SDS_PORT object. * * @return none */ static void scic_sds_port_stopped_state_exit( SCI_BASE_OBJECT_T *object ) { SCIC_SDS_PORT_T *this_port; this_port = (SCIC_SDS_PORT_T *)object; // Enable and suspend the port task scheduler scic_sds_port_enable_port_task_scheduler(this_port); } /** * This method will perform the actions required by the SCIC_SDS_PORT on * entering the SCI_BASE_PORT_STATE_READY. This function sets the ready state * handlers for the SCIC_SDS_PORT object, reports the port object as not ready * and starts the ready substate machine. * * @param[in] object This is the SCI_BASE_OBJECT which is cast to a * SCIC_SDS_PORT object. * * @return none */ static void scic_sds_port_ready_state_enter( SCI_BASE_OBJECT_T *object ) { SCIC_SDS_PORT_T *this_port; this_port = (SCIC_SDS_PORT_T *)object; // Put the ready state handlers in place though they will not be there long scic_sds_port_set_base_state_handlers( this_port, SCI_BASE_PORT_STATE_READY ); if ( SCI_BASE_PORT_STATE_RESETTING == this_port->parent.state_machine.previous_state_id ) { scic_cb_port_hard_reset_complete( scic_sds_port_get_controller(this_port), this_port, SCI_SUCCESS ); } else { // Notify the caller that the port is not yet ready scic_cb_port_not_ready( scic_sds_port_get_controller(this_port), this_port, SCIC_PORT_NOT_READY_NO_ACTIVE_PHYS ); } // Post and suspend the dummy remote node context for this // port. scic_sds_port_post_dummy_remote_node(this_port); // Start the ready substate machine sci_base_state_machine_start( scic_sds_port_get_ready_substate_machine(this_port) ); } /** * This method will perform the actions required by the SCIC_SDS_PORT on * exiting the SCI_BASE_STATE_READY. This function does nothing. * * @param[in] object This is the SCI_BASE_OBJECT which is cast to a * SCIC_SDS_PORT object. * * @return none */ static void scic_sds_port_ready_state_exit( SCI_BASE_OBJECT_T *object ) { SCIC_SDS_PORT_T *this_port; this_port = (SCIC_SDS_PORT_T *)object; sci_base_state_machine_stop(&this_port->ready_substate_machine); scic_cb_stall_execution(10); scic_sds_port_invalidate_dummy_remote_node(this_port); } /** * This method will perform the actions required by the SCIC_SDS_PORT on * entering the SCI_BASE_PORT_STATE_RESETTING. This function sets the * resetting state handlers for the SCIC_SDS_PORT object. * * @param[in] object This is the SCI_BASE_OBJECT which is cast to a * SCIC_SDS_PORT object. * * @return none */ static void scic_sds_port_resetting_state_enter( SCI_BASE_OBJECT_T *object ) { SCIC_SDS_PORT_T *this_port; this_port = (SCIC_SDS_PORT_T *)object; scic_sds_port_set_base_state_handlers( this_port, SCI_BASE_PORT_STATE_RESETTING ); } /** * This method will perform the actions required by the SCIC_SDS_PORT on * exiting the SCI_BASE_STATE_RESETTING. This function does nothing. * * @param[in] object This is the SCI_BASE_OBJECT which is cast to a * SCIC_SDS_PORT object. * * @return none */ static void scic_sds_port_resetting_state_exit( SCI_BASE_OBJECT_T *object ) { SCIC_SDS_PORT_T *this_port; this_port = (SCIC_SDS_PORT_T *)object; scic_cb_timer_stop( scic_sds_port_get_controller(this_port), this_port->timer_handle ); } /** * This method will perform the actions required by the SCIC_SDS_PORT on * entering the SCI_BASE_PORT_STATE_STOPPING. This function sets the stopping * state handlers for the SCIC_SDS_PORT object. * * @param[in] object This is the SCI_BASE_OBJECT which is cast to a * SCIC_SDS_PORT object. * * @return none */ static void scic_sds_port_stopping_state_enter( SCI_BASE_OBJECT_T *object ) { SCIC_SDS_PORT_T *this_port; this_port = (SCIC_SDS_PORT_T *)object; scic_sds_port_set_base_state_handlers( this_port, SCI_BASE_PORT_STATE_STOPPING ); if (this_port->started_request_count == 0) { sci_base_state_machine_change_state( &this_port->parent.state_machine, SCI_BASE_PORT_STATE_STOPPED ); } } /** * This method will perform the actions required by the SCIC_SDS_PORT on * exiting the SCI_BASE_STATE_STOPPING. This function does nothing. * * @param[in] object This is the SCI_BASE_OBJECT which is cast to a * SCIC_SDS_PORT object. * * @return none */ static void scic_sds_port_stopping_state_exit( SCI_BASE_OBJECT_T *object ) { SCIC_SDS_PORT_T *this_port; this_port = (SCIC_SDS_PORT_T *)object; scic_cb_timer_stop( scic_sds_port_get_controller(this_port), this_port->timer_handle ); scic_cb_timer_destroy( scic_sds_port_get_controller(this_port), this_port->timer_handle ); this_port->timer_handle = NULL; scic_sds_port_destroy_dummy_resources(this_port); } /** * This method will perform the actions required by the SCIC_SDS_PORT on * entering the SCI_BASE_PORT_STATE_STOPPING. This function sets the stopping * state handlers for the SCIC_SDS_PORT object. * * @param[in] object This is the SCI_BASE_OBJECT which is cast to a * SCIC_SDS_PORT object. * * @return none */ static void scic_sds_port_failed_state_enter( SCI_BASE_OBJECT_T *object ) { SCIC_SDS_PORT_T *this_port; this_port = (SCIC_SDS_PORT_T *)object; scic_sds_port_set_base_state_handlers( this_port, SCI_BASE_PORT_STATE_FAILED ); scic_cb_port_hard_reset_complete( scic_sds_port_get_controller(this_port), this_port, SCI_FAILURE_TIMEOUT ); } // --------------------------------------------------------------------------- SCI_BASE_STATE_T scic_sds_port_state_table[SCI_BASE_PORT_MAX_STATES] = { { SCI_BASE_PORT_STATE_STOPPED, scic_sds_port_stopped_state_enter, scic_sds_port_stopped_state_exit }, { SCI_BASE_PORT_STATE_STOPPING, scic_sds_port_stopping_state_enter, scic_sds_port_stopping_state_exit }, { SCI_BASE_PORT_STATE_READY, scic_sds_port_ready_state_enter, scic_sds_port_ready_state_exit }, { SCI_BASE_PORT_STATE_RESETTING, scic_sds_port_resetting_state_enter, scic_sds_port_resetting_state_exit }, { SCI_BASE_PORT_STATE_FAILED, scic_sds_port_failed_state_enter, NULL } }; //****************************************************************************** //* PORT READY SUB-STATE MACHINE //****************************************************************************** //**************************************************************************** //* READY SUBSTATE HANDLERS //**************************************************************************** /** * This method is the general ready state stop handler for the SCIC_SDS_PORT * object. This function will transition the ready substate machine to its * final state. * * @param[in] port This is the SCI_BASE_PORT object which is cast into a * SCIC_SDS_PORT object. * * @return SCI_STATUS * @retval SCI_SUCCESS */ static SCI_STATUS scic_sds_port_ready_substate_stop_handler( SCI_BASE_PORT_T *port ) { SCIC_SDS_PORT_T *this_port = (SCIC_SDS_PORT_T *)port; sci_base_state_machine_change_state( &this_port->parent.state_machine, SCI_BASE_PORT_STATE_STOPPING ); return SCI_SUCCESS; } /** * This method is the general ready substate complete io handler for the * SCIC_SDS_PORT object. This function decrments the outstanding request * count for this port object. * * @param[in] port This is the SCI_BASE_PORT object which is cast into a * SCIC_SDS_PORT object. * @param[in] device This is the SCI_BASE_REMOTE_DEVICE object which is not * used in this function. * @param[in] io_request This is the SCI_BASE_REQUEST object which is not used * in this function. * * @return SCI_STATUS * @retval SCI_SUCCESS */ static SCI_STATUS scic_sds_port_ready_substate_complete_io_handler( SCIC_SDS_PORT_T *port, struct SCIC_SDS_REMOTE_DEVICE *device, struct SCIC_SDS_REQUEST *io_request ) { SCIC_SDS_PORT_T *this_port = (SCIC_SDS_PORT_T *)port; scic_sds_port_decrement_request_count(this_port); return SCI_SUCCESS; } static SCI_STATUS scic_sds_port_ready_substate_add_phy_handler( SCI_BASE_PORT_T *port, SCI_BASE_PHY_T *phy ) { SCIC_SDS_PORT_T * this_port = (SCIC_SDS_PORT_T *)port; SCIC_SDS_PHY_T * this_phy = (SCIC_SDS_PHY_T *)phy; SCI_STATUS status; status = scic_sds_port_set_phy(this_port, this_phy); if (status == SCI_SUCCESS) { scic_sds_port_general_link_up_handler(this_port, this_phy, TRUE, FALSE); this_port->not_ready_reason = SCIC_PORT_NOT_READY_RECONFIGURING; sci_base_state_machine_change_state( &this_port->ready_substate_machine, SCIC_SDS_PORT_READY_SUBSTATE_CONFIGURING ); } return status; } static SCI_STATUS scic_sds_port_ready_substate_remove_phy_handler( SCI_BASE_PORT_T *port, SCI_BASE_PHY_T *phy ) { SCIC_SDS_PORT_T * this_port = (SCIC_SDS_PORT_T *)port; SCIC_SDS_PHY_T * this_phy = (SCIC_SDS_PHY_T *)phy; SCI_STATUS status; status = scic_sds_port_clear_phy(this_port, this_phy); if (status == SCI_SUCCESS) { scic_sds_port_deactivate_phy(this_port, this_phy, TRUE); this_port->not_ready_reason = SCIC_PORT_NOT_READY_RECONFIGURING; sci_base_state_machine_change_state( &this_port->ready_substate_machine, SCIC_SDS_PORT_READY_SUBSTATE_CONFIGURING ); } return status; } //**************************************************************************** //* READY SUBSTATE WAITING HANDLERS //**************************************************************************** /** * This method is the ready waiting substate link up handler for the * SCIC_SDS_PORT object. This methos will report the link up condition for * this port and will transition to the ready operational substate. * * @param[in] this_port This is the SCIC_SDS_PORT object that which has a phy * that has gone link up. * @param[in] the_phy This is the SCIC_SDS_PHY object that has gone link up. * * @return none */ static void scic_sds_port_ready_waiting_substate_link_up_handler( SCIC_SDS_PORT_T *this_port, SCIC_SDS_PHY_T *the_phy ) { // Since this is the first phy going link up for the port we can just enable // it and continue. scic_sds_port_activate_phy(this_port, the_phy, TRUE, TRUE); sci_base_state_machine_change_state( &this_port->ready_substate_machine, SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL ); } /** * This method is the ready waiting substate start io handler for the * SCIC_SDS_PORT object. The port object can not accept new requests so the * request is failed. * * @param[in] port This is the SCI_BASE_PORT object which is cast into a * SCIC_SDS_PORT object. * @param[in] device This is the SCI_BASE_REMOTE_DEVICE object which is not * used in this request. * @param[in] io_request This is the SCI_BASE_REQUEST object which is not used * in this function. * * @return SCI_STATUS * @retval SCI_FAILURE_INVALID_STATE */ static SCI_STATUS scic_sds_port_ready_waiting_substate_start_io_handler( SCIC_SDS_PORT_T *port, SCIC_SDS_REMOTE_DEVICE_T *device, SCIC_SDS_REQUEST_T *io_request ) { return SCI_FAILURE_INVALID_STATE; } //**************************************************************************** //* READY SUBSTATE OPERATIONAL HANDLERS //**************************************************************************** /** * This method will cause the port to reset. * * @param[in] port This is the SCI_BASE_PORT object which is cast into a * SCIC_SDS_PORT object. * @param[in] timeout This is the timeout for the reset request to complete. * * @return SCI_STATUS * @retval SCI_SUCCESS */ static SCI_STATUS scic_sds_port_ready_operational_substate_reset_handler( SCI_BASE_PORT_T * port, U32 timeout ) { SCI_STATUS status = SCI_FAILURE_INVALID_PHY; U32 phy_index; SCIC_SDS_PORT_T * this_port = (SCIC_SDS_PORT_T *)port; SCIC_SDS_PHY_T * selected_phy = SCI_INVALID_HANDLE; // Select a phy on which we can send the hard reset request. for ( phy_index = 0; (phy_index < SCI_MAX_PHYS) && (selected_phy == SCI_INVALID_HANDLE); phy_index++ ) { selected_phy = this_port->phy_table[phy_index]; if ( (selected_phy != SCI_INVALID_HANDLE) && !scic_sds_port_active_phy(this_port, selected_phy) ) { // We found a phy but it is not ready select different phy selected_phy = SCI_INVALID_HANDLE; } } // If we have a phy then go ahead and start the reset procedure if (selected_phy != SCI_INVALID_HANDLE) { status = scic_sds_phy_reset(selected_phy); if (status == SCI_SUCCESS) { scic_cb_timer_start( scic_sds_port_get_controller(this_port), this_port->timer_handle, timeout ); this_port->not_ready_reason = SCIC_PORT_NOT_READY_HARD_RESET_REQUESTED; sci_base_state_machine_change_state( &this_port->parent.state_machine, SCI_BASE_PORT_STATE_RESETTING ); } } return status; } /** * This method is the ready operational substate link up handler for the * SCIC_SDS_PORT object. This function notifies the SCI User that the phy has * gone link up. * * @param[in] this_port This is the SCIC_SDS_PORT object that which has a phy * that has gone link up. * @param[in] the_phy This is the SCIC_SDS_PHY object that has gone link up. * * @return none */ static void scic_sds_port_ready_operational_substate_link_up_handler( SCIC_SDS_PORT_T *this_port, SCIC_SDS_PHY_T *the_phy ) { scic_sds_port_general_link_up_handler(this_port, the_phy, TRUE, TRUE); } /** * This method is the ready operational substate link down handler for the * SCIC_SDS_PORT object. This function notifies the SCI User that the phy has * gone link down and if this is the last phy in the port the port will change * state to the ready waiting substate. * * @param[in] this_port This is the SCIC_SDS_PORT object that which has a phy * that has gone link down. * @param[in] the_phy This is the SCIC_SDS_PHY object that has gone link down. * * @return none */ static void scic_sds_port_ready_operational_substate_link_down_handler( SCIC_SDS_PORT_T *this_port, SCIC_SDS_PHY_T *the_phy ) { scic_sds_port_deactivate_phy(this_port, the_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 (this_port->active_phy_mask == 0) { sci_base_state_machine_change_state( scic_sds_port_get_ready_substate_machine(this_port), SCIC_SDS_PORT_READY_SUBSTATE_WAITING ); } } /** * This method is the ready operational substate start io handler for the * SCIC_SDS_PORT object. This function incremetns the outstanding request * count for this port object. * * @param[in] port This is the SCI_BASE_PORT object which is cast into a * SCIC_SDS_PORT object. * @param[in] device This is the SCI_BASE_REMOTE_DEVICE object which is not * used in this function. * @param[in] io_request This is the SCI_BASE_REQUEST object which is not used * in this function. * * @return SCI_STATUS * @retval SCI_SUCCESS */ static SCI_STATUS scic_sds_port_ready_operational_substate_start_io_handler( SCIC_SDS_PORT_T *port, SCIC_SDS_REMOTE_DEVICE_T *device, SCIC_SDS_REQUEST_T *io_request ) { SCIC_SDS_PORT_T *this_port = (SCIC_SDS_PORT_T *)port; scic_sds_port_increment_request_count(this_port); return SCI_SUCCESS; } //**************************************************************************** //* READY SUBSTATE OPERATIONAL HANDLERS //**************************************************************************** /** * This is the default method for a port add phy request. It will report a * warning and exit. * * @param[in] port This is the SCI_BASE_PORT object which is cast into a * SCIC_SDS_PORT object. * * @return SCI_STATUS * @retval SCI_FAILURE_INVALID_STATE */ static SCI_STATUS scic_sds_port_ready_configuring_substate_add_phy_handler( SCI_BASE_PORT_T *port, SCI_BASE_PHY_T *phy ) { SCIC_SDS_PORT_T * this_port = (SCIC_SDS_PORT_T *)port; SCIC_SDS_PHY_T * this_phy = (SCIC_SDS_PHY_T *)phy; SCI_STATUS status; status = scic_sds_port_set_phy(this_port, this_phy); if (status == SCI_SUCCESS) { scic_sds_port_general_link_up_handler(this_port, this_phy, TRUE, FALSE); // Re-enter the configuring state since this may be the last phy in // the port. sci_base_state_machine_change_state( &this_port->ready_substate_machine, SCIC_SDS_PORT_READY_SUBSTATE_CONFIGURING ); } return status; } /** * This is the default method for a port remove phy request. It will report a * warning and exit. * * @param[in] port This is the SCI_BASE_PORT object which is cast into a * SCIC_SDS_PORT object. * * @return SCI_STATUS * @retval SCI_FAILURE_INVALID_STATE */ static SCI_STATUS scic_sds_port_ready_configuring_substate_remove_phy_handler( SCI_BASE_PORT_T *port, SCI_BASE_PHY_T *phy ) { SCIC_SDS_PORT_T * this_port = (SCIC_SDS_PORT_T *)port; SCIC_SDS_PHY_T * this_phy = (SCIC_SDS_PHY_T *)phy; SCI_STATUS status; status = scic_sds_port_clear_phy(this_port, this_phy); if (status == SCI_SUCCESS) { scic_sds_port_deactivate_phy(this_port, this_phy, TRUE); // Re-enter the configuring state since this may be the last phy in // the port. sci_base_state_machine_change_state( &this_port->ready_substate_machine, SCIC_SDS_PORT_READY_SUBSTATE_CONFIGURING ); } return status; } /** * This method will decrement the outstanding request count for this port. * If the request count goes to 0 then the port can be reprogrammed with * its new phy data. * * @param[in] port This is the port that is being requested to complete * the io request. * @param[in] device This is the device on which the io is completing. * @param[in] io_request This is the io request that is completing. */ static SCI_STATUS scic_sds_port_ready_configuring_substate_complete_io_handler( SCIC_SDS_PORT_T *port, SCIC_SDS_REMOTE_DEVICE_T *device, SCIC_SDS_REQUEST_T *io_request ) { scic_sds_port_decrement_request_count(port); if (port->started_request_count == 0) { sci_base_state_machine_change_state( &port->ready_substate_machine, SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL ); } return SCI_SUCCESS; } // --------------------------------------------------------------------------- SCIC_SDS_PORT_STATE_HANDLER_T scic_sds_port_ready_substate_handler_table[SCIC_SDS_PORT_READY_MAX_SUBSTATES] = { // SCIC_SDS_PORT_READY_SUBSTATE_WAITING { { scic_sds_port_default_start_handler, scic_sds_port_ready_substate_stop_handler, scic_sds_port_default_destruct_handler, scic_sds_port_default_reset_handler, scic_sds_port_ready_substate_add_phy_handler, scic_sds_port_default_remove_phy_handler }, scic_sds_port_default_frame_handler, scic_sds_port_default_event_handler, scic_sds_port_ready_waiting_substate_link_up_handler, scic_sds_port_default_link_down_handler, scic_sds_port_ready_waiting_substate_start_io_handler, scic_sds_port_ready_substate_complete_io_handler, }, // SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL { { scic_sds_port_default_start_handler, scic_sds_port_ready_substate_stop_handler, scic_sds_port_default_destruct_handler, scic_sds_port_ready_operational_substate_reset_handler, scic_sds_port_ready_substate_add_phy_handler, scic_sds_port_ready_substate_remove_phy_handler }, scic_sds_port_default_frame_handler, scic_sds_port_default_event_handler, scic_sds_port_ready_operational_substate_link_up_handler, scic_sds_port_ready_operational_substate_link_down_handler, scic_sds_port_ready_operational_substate_start_io_handler, scic_sds_port_ready_substate_complete_io_handler }, // SCIC_SDS_PORT_READY_SUBSTATE_CONFIGURING { { scic_sds_port_default_start_handler, scic_sds_port_ready_substate_stop_handler, scic_sds_port_default_destruct_handler, scic_sds_port_default_reset_handler, scic_sds_port_ready_configuring_substate_add_phy_handler, scic_sds_port_ready_configuring_substate_remove_phy_handler }, scic_sds_port_default_frame_handler, scic_sds_port_default_event_handler, scic_sds_port_default_link_up_handler, scic_sds_port_default_link_down_handler, scic_sds_port_default_start_io_handler, scic_sds_port_ready_configuring_substate_complete_io_handler } }; /** * This macro sets the port ready substate handlers. */ #define scic_sds_port_set_ready_state_handlers(port, state_id) \ scic_sds_port_set_state_handlers( \ port, &scic_sds_port_ready_substate_handler_table[(state_id)] \ ) //****************************************************************************** //* PORT STATE PRIVATE METHODS //****************************************************************************** /** * This method will susped the port task scheduler for this port object. * * @param[in] this_port This is the SCIC_SDS_PORT object to suspend. * * @return none */ void scic_sds_port_suspend_port_task_scheduler( SCIC_SDS_PORT_T *this_port ) { U32 pts_control_value; pts_control_value = scu_port_task_scheduler_read(this_port, control); pts_control_value |= SCU_PTSxCR_GEN_BIT(SUSPEND); scu_port_task_scheduler_write(this_port, control, pts_control_value); } /** * This method will resume the port task scheduler for this port object. * * @param[in] this_port This is the SCIC_SDS_PORT object to resume. * * @return none */ void scic_sds_port_resume_port_task_scheduler( SCIC_SDS_PORT_T *this_port ) { U32 pts_control_value; pts_control_value = scu_port_task_scheduler_read(this_port, control); pts_control_value &= ~SCU_PTSxCR_GEN_BIT(SUSPEND); scu_port_task_scheduler_write(this_port, control, pts_control_value); } /** * This routine will post the dummy request. This will prevent the hardware * scheduler from posting new requests to the front of the scheduler queue * causing a starvation problem for currently ongoing requests. * * @parm[in] this_port The port on which the task must be posted. * * @return none */ static void scic_sds_port_post_dummy_request( SCIC_SDS_PORT_T *this_port ) { U32 command; SCU_TASK_CONTEXT_T * task_context; if (this_port->reserved_tci != SCU_DUMMY_INDEX) { task_context = scic_sds_controller_get_task_context_buffer( this_port->owning_controller, this_port->reserved_tci ); task_context->abort = 0; command = ( (SCU_CONTEXT_COMMAND_REQUEST_TYPE_POST_TC) | (this_port->physical_port_index << SCU_CONTEXT_COMMAND_LOGICAL_PORT_SHIFT) | (this_port->reserved_tci) ); scic_sds_controller_post_request(this_port->owning_controller, command); } } /** * This routine will abort the dummy request. This will alow the hardware to * power down parts of the silicon to save power. * * @parm[in] this_port The port on which the task must be aborted. * * @return none */ static void scic_sds_port_abort_dummy_request( SCIC_SDS_PORT_T *this_port ) { U32 command; SCU_TASK_CONTEXT_T * task_context; if (this_port->reserved_tci != SCU_DUMMY_INDEX) { task_context = scic_sds_controller_get_task_context_buffer( this_port->owning_controller, this_port->reserved_tci ); task_context->abort = 1; command = ( (SCU_CONTEXT_COMMAND_REQUEST_POST_TC_ABORT) | (this_port->physical_port_index << SCU_CONTEXT_COMMAND_LOGICAL_PORT_SHIFT) | (this_port->reserved_tci) ); scic_sds_controller_post_request(this_port->owning_controller, command); } } //****************************************************************************** //* PORT READY SUBSTATE METHODS //****************************************************************************** /** * This method will perform the actions required by the SCIC_SDS_PORT on * entering the SCIC_SDS_PORT_READY_SUBSTATE_WAITING. This function checks the * port for any ready phys. If there is at least one phy in a ready state * then the port transitions to the ready operational substate. * * @param[in] object This is the SCI_BASE_OBJECT which is cast to a * SCIC_SDS_PORT object. * * @return none */ static void scic_sds_port_ready_substate_waiting_enter( SCI_BASE_OBJECT_T *object ) { SCIC_SDS_PORT_T *this_port = (SCIC_SDS_PORT_T *)object; scic_sds_port_set_ready_state_handlers( this_port, SCIC_SDS_PORT_READY_SUBSTATE_WAITING ); scic_sds_port_suspend_port_task_scheduler(this_port); this_port->not_ready_reason = SCIC_PORT_NOT_READY_NO_ACTIVE_PHYS; if (this_port->active_phy_mask != 0) { // At least one of the phys on the port is ready sci_base_state_machine_change_state( &this_port->ready_substate_machine, SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL ); } } /** * This method will perform the actions required by the SCIC_SDS_PORT on * exiting the SCIC_SDS_PORT_READY_SUBSTATE_WAITING. This function resume the * PTSG that was suspended at the entry of this state. * * @param[in] object This is the SCI_BASE_OBJECT which is cast to a * SCIC_SDS_PORT object. * * @return none */ static void scic_sds_port_ready_substate_waiting_exit( SCI_BASE_OBJECT_T *object ) { SCIC_SDS_PORT_T *this_port = (SCIC_SDS_PORT_T *)object; scic_sds_port_resume_port_task_scheduler(this_port); } /** * This method will perform the actions required by the SCIC_SDS_PORT on * entering the SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL. This function sets * the state handlers for the port object, notifies the SCI User that the port * is ready, and resumes port operations. * * @param[in] object This is the SCI_BASE_OBJECT which is cast to a * SCIC_SDS_PORT object. * * @return none */ static void scic_sds_port_ready_substate_operational_enter( SCI_BASE_OBJECT_T *object ) { U32 index; SCIC_SDS_PORT_T *this_port = (SCIC_SDS_PORT_T *)object; scic_sds_port_set_ready_state_handlers( this_port, SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL ); scic_cb_port_ready( scic_sds_port_get_controller(this_port), this_port ); for (index = 0; index < SCI_MAX_PHYS; index++) { if (this_port->phy_table[index] != NULL) { scic_sds_port_write_phy_assignment( this_port, this_port->phy_table[index] ); //if the bit at the index location for active phy mask is set and //enabled_phy_mask is not set then resume the phy if ( ( (this_port->active_phy_mask ^ this_port->enabled_phy_mask) & (1 << index) ) != 0) { scic_sds_port_resume_phy ( this_port, this_port->phy_table[index] ); } } } scic_sds_port_update_viit_entry(this_port); // Post the dummy task for the port so the hardware can schedule // io correctly scic_sds_port_post_dummy_request(this_port); } /** * This method will perform the actions required by the 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. * * @param[in] object This is the SCI_BASE_OBJECT which is cast to a * SCIC_SDS_PORT object. * * @return none */ static void scic_sds_port_ready_substate_operational_exit( SCI_BASE_OBJECT_T *object ) { SCIC_SDS_PORT_T *this_port = (SCIC_SDS_PORT_T *)object; // 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(this_port); scic_cb_port_not_ready( scic_sds_port_get_controller(this_port), this_port, this_port->not_ready_reason ); } //****************************************************************************** //* PORT READY CONFIGURING METHODS //****************************************************************************** /** * This method will perform the actions required by the 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. * * @param[in] object This is the SCI_BASE_OBJECT which is cast to a * SCIC_SDS_PORT object. * * @return none */ static void scic_sds_port_ready_substate_configuring_enter( SCI_BASE_OBJECT_T *object ) { SCIC_SDS_PORT_T *this_port = (SCIC_SDS_PORT_T *)object; scic_sds_port_set_ready_state_handlers( this_port, SCIC_SDS_PORT_READY_SUBSTATE_CONFIGURING ); if (this_port->active_phy_mask == 0) { scic_cb_port_not_ready( scic_sds_port_get_controller(this_port), this_port, SCIC_PORT_NOT_READY_NO_ACTIVE_PHYS ); sci_base_state_machine_change_state( &this_port->ready_substate_machine, SCIC_SDS_PORT_READY_SUBSTATE_WAITING ); } //do not wait for IO to go to 0 in this state. else { sci_base_state_machine_change_state( &this_port->ready_substate_machine, SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL ); } } // --------------------------------------------------------------------------- SCI_BASE_STATE_T scic_sds_port_ready_substate_table[SCIC_SDS_PORT_READY_MAX_SUBSTATES] = { { SCIC_SDS_PORT_READY_SUBSTATE_WAITING, scic_sds_port_ready_substate_waiting_enter, scic_sds_port_ready_substate_waiting_exit }, { SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL, scic_sds_port_ready_substate_operational_enter, scic_sds_port_ready_substate_operational_exit }, { SCIC_SDS_PORT_READY_SUBSTATE_CONFIGURING, scic_sds_port_ready_substate_configuring_enter, NULL } }; diff --git a/sys/dev/isci/scil/scic_sds_port_configuration_agent.c b/sys/dev/isci/scil/scic_sds_port_configuration_agent.c index 3832898314f8..022c81492ac5 100644 --- a/sys/dev/isci/scil/scic_sds_port_configuration_agent.c +++ b/sys/dev/isci/scil/scic_sds_port_configuration_agent.c @@ -1,1135 +1,1135 @@ /*- * SPDX-License-Identifier: BSD-2-Clause OR GPL-2.0 * * 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. * * 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 __FBSDID("$FreeBSD$"); /** * @file * * @brief This file contains the implementation for the public and protected * methods for the port configuration agent. */ #include #include #include #include #define SCIC_SDS_MPC_RECONFIGURATION_TIMEOUT (10) #define SCIC_SDS_APC_RECONFIGURATION_TIMEOUT (10) #define SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION (250) 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 //****************************************************************************** /** * 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 * * @param[in] address_one A SAS Address to be compared. * @param[in] address_two A SAS Address to be compared. * * @return 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( SCI_SAS_ADDRESS_T address_one, SCI_SAS_ADDRESS_T 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; } /** * 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 address. * * @param[in] controller The controller object used for the port search. * @param[in] phy The phy object to match. * * @return The port address or the SCI_INVALID_HANDLE if there is no matching * port. * * @retvalue port address if the port can be found to match the phy. * @retvalue SCI_INVALID_HANDLE if there is no matching port for the phy. */ static SCIC_SDS_PORT_T * scic_sds_port_configuration_agent_find_port( SCIC_SDS_CONTROLLER_T * controller, SCIC_SDS_PHY_T * phy ) { U8 port_index; SCI_PORT_HANDLE_T port_handle; SCI_SAS_ADDRESS_T port_sas_address; SCI_SAS_ADDRESS_T port_attached_device_address; SCI_SAS_ADDRESS_T phy_sas_address; SCI_SAS_ADDRESS_T phy_attached_device_address; SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY, "scic_sds_port_confgiruation_agent_find_port(0x%08x, 0x%08x) enter\n", controller, phy )); // 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 // case it should participate in the same port. scic_sds_phy_get_sas_address(phy, &phy_sas_address); scic_sds_phy_get_attached_sas_address(phy, &phy_attached_device_address); for (port_index = 0; port_index < SCI_MAX_PORTS; port_index++) { if (scic_controller_get_port_handle(controller, port_index, &port_handle) == SCI_SUCCESS) { SCIC_SDS_PORT_T * port = (SCIC_SDS_PORT_T *)port_handle; scic_sds_port_get_sas_address(port, &port_sas_address); scic_sds_port_get_attached_sas_address(port, &port_attached_device_address); 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) ) { return port; } } } return SCI_INVALID_HANDLE; } /** * 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) * * @param[in] controller This is the controller object that contains the * port agent * @param[in] port_agent This is the port configruation agent for * the controller. * * @return SCI_STATUS * @retval SCI_SUCCESS the port configuration is valid for this * port configuration agent. * @retval SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION the port configuration * is not valid for this port configuration agent. */ static SCI_STATUS scic_sds_port_configuration_agent_validate_ports( SCIC_SDS_CONTROLLER_T * controller, SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent ) { #if !defined(ARLINGTON_BUILD) SCI_SAS_ADDRESS_T first_address; SCI_SAS_ADDRESS_T second_address; SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT, "scic_sds_port_configuration_agent_validate_ports(0x%08x, 0x%08x) enter\n", controller, port_agent )); // Sanity check the max ranges for all the phys the max index // is always equal to the port range index 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) ) { 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 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) ) { 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. scic_sds_phy_get_sas_address(&controller->phy_table[0], &first_address); scic_sds_phy_get_sas_address(&controller->phy_table[3], &second_address); if (sci_sas_address_compare(first_address, second_address) == 0) { return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION; } // PE0 and PE1 are configured into a 2x1 ports make sure that the // SAS Address for PE0 and PE2 are different since they can not be // part of the same port. if ( (port_agent->phy_valid_port_range[0].min_index == 0) && (port_agent->phy_valid_port_range[1].min_index == 1) ) { scic_sds_phy_get_sas_address(&controller->phy_table[0], &first_address); scic_sds_phy_get_sas_address(&controller->phy_table[2], &second_address); if (sci_sas_address_compare(first_address, second_address) == 0) { return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION; } } // PE2 and PE3 are configured into a 2x1 ports make sure that the // SAS Address for PE1 and PE3 are different since they can not be // part of the same port. if ( (port_agent->phy_valid_port_range[2].min_index == 2) && (port_agent->phy_valid_port_range[3].min_index == 3) ) { scic_sds_phy_get_sas_address(&controller->phy_table[1], &first_address); scic_sds_phy_get_sas_address(&controller->phy_table[3], &second_address); if (sci_sas_address_compare(first_address, second_address) == 0) { return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION; } } #endif // !defined(ARLINGTON_BUILD) return SCI_SUCCESS; } //****************************************************************************** // Manual port configuration agent routines //****************************************************************************** /** * This routine will verify that all of the phys in the same port are using * the same SAS address. * * @param[in] controller This is the controller that contains the PHYs to * be verified. */ static SCI_STATUS scic_sds_mpc_agent_validate_phy_configuration( SCIC_SDS_CONTROLLER_T * controller, SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent ) { U32 phy_mask; U32 assigned_phy_mask; SCI_SAS_ADDRESS_T sas_address; SCI_SAS_ADDRESS_T phy_assigned_address; U8 port_index; U8 phy_index; assigned_phy_mask = 0; sas_address.high = 0; sas_address.low = 0; SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT, "scic_sds_mpc_agent_validate_phy_configuration(0x%08x, 0x%08x) enter\n", controller, port_agent )); for (port_index = 0; port_index < SCI_MAX_PORTS; port_index++) { phy_mask = controller->oem_parameters.sds1.ports[port_index].phy_mask; if (phy_mask != 0) { - // Make sure that one or more of the phys were not already assinged to + // Make sure that one or more of the phys were not already assigned to // a different port. if ((phy_mask & ~assigned_phy_mask) == 0) { return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION; } // Find the starting phy index for this round through the loop for (phy_index = 0; phy_index < SCI_MAX_PHYS; phy_index++) { if ((1 << phy_index) & phy_mask) { scic_sds_phy_get_sas_address( &controller->phy_table[phy_index], &sas_address ); // 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; if (phy_index != port_index) { return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION; } break; } } // 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. while (phy_index < SCI_MAX_PHYS) { if ((1 << phy_index) & phy_mask) { scic_sds_phy_get_sas_address( &controller->phy_table[phy_index], &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; } port_agent->phy_valid_port_range[phy_index].min_index = port_index; port_agent->phy_valid_port_range[phy_index].max_index = phy_index; scic_sds_port_add_phy( &controller->port_table[port_index], &controller->phy_table[phy_index] ); assigned_phy_mask |= (1 << phy_index); } phy_index++; } } } return scic_sds_port_configuration_agent_validate_ports(controller, port_agent); } /** * This timer routine is used to allow the SCI User to rediscover or change * device objects before a new series of link up notifications because a * link down has allowed a better port configuration. * * @param[in] controller This is the core controller object which is used * to obtain the port configuration agent. */ static void scic_sds_mpc_agent_timeout_handler( void * object ) { U8 index; SCIC_SDS_CONTROLLER_T * controller = (SCIC_SDS_CONTROLLER_T *)object; SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent = &controller->port_agent; U16 configure_phy_mask; SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT, "scic_sds_mpc_agent_timeout_handler(0x%08x) enter\n", controller )); 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++) { if (configure_phy_mask & (1 << index)) { port_agent->link_up_handler( controller, port_agent, scic_sds_phy_get_port(&controller->phy_table[index]), &controller->phy_table[index] ); } } } /** * This method handles the manual port configuration link up notifications. * Since all ports and phys are associate at initialization time we just turn * around and notifiy the port object that there is a link up. If this PHY is * not associated with a port there is no action taken. * * @param[in] controller This is the controller object that receives the * link up notification. * @param[in] port This is the port object associated with the phy. If the * is no associated port this is an SCI_INVALID_HANDLE. * @param[in] phy This is the phy object which has gone ready. * * @note Is it possible to get a link up notification from a phy that has * no assocoated port? */ static void scic_sds_mpc_agent_link_up( SCIC_SDS_CONTROLLER_T * controller, SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent, SCIC_SDS_PORT_T * port, SCIC_SDS_PHY_T * phy ) { SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY, "scic_sds_mpc_agent_link_up(0x%08x, 0x%08x, 0x%08x, 0x%08x) enter\n", controller, port_agent, port, phy )); // If the port has an invalid handle 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. if (port != SCI_INVALID_HANDLE) { port_agent->phy_ready_mask |= (1 << scic_sds_phy_get_index(phy)); scic_sds_port_link_up(port, phy); if ((port->active_phy_mask & (1 << scic_sds_phy_get_index(phy))) != 0) { port_agent->phy_configured_mask |= (1 << scic_sds_phy_get_index(phy)); } } } /** * This method handles the manual port configuration link down notifications. * 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. * * @param[in] controller This is the controller object that receives the * link down notification. * @param[in] port This is the port object associated with the phy. If the * is no associated port this is an SCI_INVALID_HANDLE. The port * is an invalid 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. * @param[in] phy This is the phy object which has gone link down. * * @note Is it possible to get a link down notification from a phy that has * no assocoated port? */ static void scic_sds_mpc_agent_link_down( SCIC_SDS_CONTROLLER_T * controller, SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent, SCIC_SDS_PORT_T * port, SCIC_SDS_PHY_T * phy ) { SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY, "scic_sds_mpc_agent_link_down(0x%08x, 0x%08x, 0x%08x, 0x%08x) enter\n", controller, port_agent, port, phy )); if (port != SCI_INVALID_HANDLE) { // 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. port_agent->phy_ready_mask &= ~(1 << scic_sds_phy_get_index(phy)); port_agent->phy_configured_mask &= ~(1 << scic_sds_phy_get_index(phy)); // 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 reconstruc the port after the timeout. if ( (port_agent->phy_configured_mask == 0x0000) && (port_agent->phy_ready_mask != 0x0000) && !port_agent->timer_pending ) { port_agent->timer_pending = TRUE; scic_cb_timer_start( controller, port_agent->timer, SCIC_SDS_MPC_RECONFIGURATION_TIMEOUT ); } scic_sds_port_link_down(port, phy); } } //****************************************************************************** // Automatic port configuration agent routines //****************************************************************************** /** * This routine will verify that the phys are assigned a valid SAS address for * automatic port configuration mode. */ static SCI_STATUS scic_sds_apc_agent_validate_phy_configuration( SCIC_SDS_CONTROLLER_T * controller, SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent ) { U8 phy_index; U8 port_index; SCI_SAS_ADDRESS_T sas_address; SCI_SAS_ADDRESS_T phy_assigned_address; SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT, "scic_sds_apc_agent_validate_phy_configuration(0x%08x, 0x%08x) enter\n", controller, port_agent )); 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. scic_sds_phy_get_sas_address( &controller->phy_table[phy_index], &sas_address ); while (++phy_index < SCI_MAX_PHYS) { scic_sds_phy_get_sas_address( &controller->phy_table[phy_index], &phy_assigned_address ); // 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; } } } return scic_sds_port_configuration_agent_validate_ports(controller, port_agent); } /** * 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 * port a phy belongs. * * @param[in] controller This is the controller that to which the port * agent is assigned. * @param[in] port_agent This is the port agent that is requesting the * timer start operation. * @param[in] phy This is the phy that has caused the timer operation to * be scheduled. * @param[in] timeout This is the timeout in ms. */ static void scic_sds_apc_agent_start_timer( SCIC_SDS_CONTROLLER_T * controller, SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent, SCIC_SDS_PHY_T * phy, U32 timeout ) { SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY, "scic_sds_apc_agent_start_timer(0x%08x, 0x%08x, 0x%08x, 0x%08x) enter\n", controller, port_agent, phy, timeout )); if (port_agent->timer_pending) { scic_cb_timer_stop(controller, port_agent->timer); } port_agent->timer_pending = TRUE; scic_cb_timer_start(controller, port_agent->timer, timeout); } /** * This method handles the automatic port configuration for link up notifications. * * @param[in] controller This is the controller object that receives the * link up notification. * @param[in] phy This is the phy object which has gone link up. * @param[in] start_timer This tells the routine if it should start the timer for * any phys that might be added to a port in the future. */ static void scic_sds_apc_agent_configure_ports( SCIC_SDS_CONTROLLER_T * controller, SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent, SCIC_SDS_PHY_T * phy, BOOL start_timer ) { U8 port_index; SCI_STATUS status; SCIC_SDS_PORT_T * port; SCI_PORT_HANDLE_T port_handle; enum SCIC_SDS_APC_ACTIVITY apc_activity = SCIC_SDS_APC_SKIP_PHY; SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY, "scic_sds_apc_agent_configure_ports(0x%08x, 0x%08x, 0x%08x, %d) enter\n", controller, port_agent, phy, start_timer )); port = scic_sds_port_configuration_agent_find_port(controller, phy); if (port != SCI_INVALID_HANDLE) { if (scic_sds_port_is_valid_phy_assignment(port, phy->phy_index)) 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 for ( port_index = port_agent->phy_valid_port_range[phy->phy_index].min_index; port_index <= port_agent->phy_valid_port_range[phy->phy_index].max_index; port_index++ ) { scic_controller_get_port_handle(controller, port_index, &port_handle); port = (SCIC_SDS_PORT_T *)port_handle; // First we must make sure that this PHY can be added to this Port. if (scic_sds_port_is_valid_phy_assignment(port, phy->phy_index)) { // 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. if (port->active_phy_mask > (1 << phy->phy_index)) { 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. if (port->physical_port_index == phy->phy_index) { // 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. if (port->active_phy_mask == 0) { apc_activity = SCIC_SDS_APC_START_TIMER; } } else if (port->active_phy_mask != 0) { // 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 because 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: status = scic_sds_port_add_phy(port, phy); if (status == SCI_SUCCESS) { port_agent->phy_configured_mask |= (1 << phy->phy_index); } break; case SCIC_SDS_APC_START_TIMER: scic_sds_apc_agent_start_timer( controller, port_agent, phy, SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION ); break; case SCIC_SDS_APC_SKIP_PHY: default: // do nothing the PHY can not be made part of a port at this time. break; } } /** * This method handles the automatic port configuration for link up notifications. * * @param[in] controller This is the controller object that receives the * link up notification. * @param[in] port This is the port object associated with the phy. If the * is no associated port this is an SCI_INVALID_HANDLE. * @param[in] phy This is the phy object which has gone link up. * * @note Is it possible to get a link down notification from a phy that has * no assocoated port? */ static void scic_sds_apc_agent_link_up( SCIC_SDS_CONTROLLER_T * controller, SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent, SCIC_SDS_PORT_T * port, SCIC_SDS_PHY_T * phy ) { SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY, "scic_sds_apc_agent_link_up(0x%08x, 0x%08x, 0x%08x, 0x%08x) enter\n", controller, port_agent, port, phy )); //the phy is not the part of this port, configure the port with this phy if (port == SCI_INVALID_HANDLE) { port_agent->phy_ready_mask |= (1 << scic_sds_phy_get_index(phy)); scic_sds_apc_agent_start_timer( controller, port_agent, phy, SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION ); } else { //the phy is already the part of the port //if the PORT'S state is resetting then the link up is from port hard reset //in this case, we need to tell the port that link up is received if ( SCI_BASE_PORT_STATE_RESETTING == port->parent.state_machine.current_state_id ) { //notify the port that port needs to be ready port_agent->phy_ready_mask |= (1 << scic_sds_phy_get_index(phy)); scic_sds_port_link_up(port, phy); } else { ASSERT (0); } } } /** * This method handles the automatic port configuration link down notifications. * If this PHY is * not associated with a port there is no action taken. * * @param[in] controller This is the controller object that receives the * link down notification. * @param[in] port This is the port object associated with the phy. If the * is no associated port this is an SCI_INVALID_HANDLE. * @param[in] phy This is the phy object which has gone link down. * * @note Is it possible to get a link down notification from a phy that has * no assocoated port? */ static void scic_sds_apc_agent_link_down( SCIC_SDS_CONTROLLER_T * controller, SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent, SCIC_SDS_PORT_T * port, SCIC_SDS_PHY_T * phy ) { SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY, "scic_sds_apc_agent_link_down(0x%08x, 0x%08x, 0x%08x, 0x%08x) enter\n", controller, port_agent, port, phy )); port_agent->phy_ready_mask &= ~(1 << scic_sds_phy_get_index(phy)); if (port != SCI_INVALID_HANDLE) { if (port_agent->phy_configured_mask & (1 << phy->phy_index)) { SCI_STATUS status; status = scic_sds_port_remove_phy(port, phy); if (status == SCI_SUCCESS) { port_agent->phy_configured_mask &= ~(1 << phy->phy_index); } } } } /** * This routine will try to configure the phys into ports when the timer fires. * * @param[in] object This is actually the controller that needs to have the * pending phys configured. */ static void scic_sds_apc_agent_timeout_handler( void * object ) { U32 index; SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent; SCIC_SDS_CONTROLLER_T * controller = (SCIC_SDS_CONTROLLER_T *)object; U16 configure_phy_mask; port_agent = scic_sds_controller_get_port_configuration_agent(controller); SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT, "scic_sds_apc_agent_timeout_handler(0x%08x) enter\n", controller )); port_agent->timer_pending = FALSE; configure_phy_mask = ~port_agent->phy_configured_mask & port_agent->phy_ready_mask; if (configure_phy_mask != 0x00) { for (index = 0; index < SCI_MAX_PHYS; index++) { if (configure_phy_mask & (1 << index)) { scic_sds_apc_agent_configure_ports( controller, port_agent, &controller->phy_table[index], FALSE ); } } //Notify the controller ports are configured. if ( (port_agent->phy_ready_mask == port_agent->phy_configured_mask) && (controller->next_phy_to_start == SCI_MAX_PHYS) && (controller->phy_startup_timer_pending == FALSE) ) { // The controller has successfully finished the start process. // Inform the SCI Core user and transition to the READY state. if (scic_sds_controller_is_start_complete(controller) == TRUE) { scic_sds_controller_port_agent_configured_ports(controller); } } } } //****************************************************************************** // 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. * * @param[in] port_agent This is the port configuration agent for this * controller object. */ void scic_sds_port_configuration_agent_construct( SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent ) { 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; port_agent->timer = NULL; 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; } } /** * This method will construct the port configuration agent for this controller. * * @param[in] controller This is the controller object for which the port * agent is being initialized. * * @param[in] port_agent This is the port configuration agent that is being * initialized. The initialization path is handled differently * for the automatic port configuration agent and the manual port * configuration agent. * * @return */ SCI_STATUS scic_sds_port_configuration_agent_initialize( SCIC_SDS_CONTROLLER_T * controller, SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent ) { SCI_STATUS status = SCI_SUCCESS; enum SCIC_PORT_CONFIGURATION_MODE mode; SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT, "scic_sds_port_configuration_agent_initialize(0x%08x, 0x%08x) enter\n", controller, port_agent )); mode = controller->oem_parameters.sds1.controller.mode_type; if (mode == SCIC_PORT_MANUAL_CONFIGURATION_MODE) { status = scic_sds_mpc_agent_validate_phy_configuration(controller, port_agent); port_agent->link_up_handler = scic_sds_mpc_agent_link_up; port_agent->link_down_handler = scic_sds_mpc_agent_link_down; port_agent->timer = scic_cb_timer_create( controller, scic_sds_mpc_agent_timeout_handler, controller ); } else { status = scic_sds_apc_agent_validate_phy_configuration(controller, port_agent); port_agent->link_up_handler = scic_sds_apc_agent_link_up; port_agent->link_down_handler = scic_sds_apc_agent_link_down; port_agent->timer = scic_cb_timer_create( controller, scic_sds_apc_agent_timeout_handler, controller ); } // Make sure we have actually gotten a timer if (status == SCI_SUCCESS && port_agent->timer == NULL) { SCIC_LOG_ERROR(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER, "Controller 0x%x automatic port configuration agent could not get timer.\n", controller )); status = SCI_FAILURE; } return status; } /** * This method will destroy the port configuration agent for this controller. * * @param[in] controller This is the controller object for which the port * agent is being destroyed. * * @param[in] port_agent This is the port configuration agent that is being * destroyed. * * @return */ void scic_sds_port_configuration_agent_destroy( SCIC_SDS_CONTROLLER_T * controller, SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent ) { if (port_agent->timer_pending == TRUE) { scic_cb_timer_stop(controller, port_agent->timer); } scic_cb_timer_destroy(controller, port_agent->timer); port_agent->timer_pending = FALSE; port_agent->timer = NULL; } /** * @brief This method release resources in for a scic port configuration agent. * * @param[in] controller This parameter specifies the core controller, one of * its phy's resources are to be released. * @param[in] this_phy This parameter specifies the phy whose resource is to * be released. */ void scic_sds_port_configuration_agent_release_resource( SCIC_SDS_CONTROLLER_T * controller, SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent ) { SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_PORT, "scic_sds_port_configuration_agent_release_resource(0x%x, 0x%x)\n", controller, port_agent )); //Currently, the only resource to be released is a timer. if (port_agent->timer != NULL) { scic_cb_timer_destroy(controller, port_agent->timer); port_agent->timer = NULL; } } diff --git a/sys/dev/isci/scil/scic_sds_remote_device.h b/sys/dev/isci/scil/scic_sds_remote_device.h index 70107e99ba72..6eca23c857a5 100644 --- a/sys/dev/isci/scil/scic_sds_remote_device.h +++ b/sys/dev/isci/scil/scic_sds_remote_device.h @@ -1,648 +1,648 @@ /*- * SPDX-License-Identifier: BSD-2-Clause OR GPL-2.0 * * 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. * * 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. * * $FreeBSD$ */ #ifndef _SCIC_SDS_REMOTE_DEVICE_H_ #define _SCIC_SDS_REMOTE_DEVICE_H_ /** * @file * * @brief This file contains the structures, constants, and prototypes for the * SCIC_SDS_REMOTE_DEVICE object. */ #ifdef __cplusplus extern "C" { #endif // __cplusplus #include #include #include #include #include #include struct SCIC_SDS_CONTROLLER; struct SCIC_SDS_PORT; struct SCIC_SDS_REQUEST; struct SCIC_SDS_REMOTE_DEVICE_STATE_HANDLER; /** * @enum SCIC_SDS_SSP_REMOTE_DEVICE_READY_SUBSTATES * * This is the enumeration of the ready substates for the * SCIC_SDS_REMOTE_DEVICE. */ enum SCIC_SDS_SSP_REMOTE_DEVICE_READY_SUBSTATES { /** * This is the initial state for the remote device ready substate. */ SCIC_SDS_SSP_REMOTE_DEVICE_READY_SUBSTATE_INITIAL, /** * This is the ready operational substate for the remote device. This is the * normal operational state for a remote device. */ SCIC_SDS_SSP_REMOTE_DEVICE_READY_SUBSTATE_OPERATIONAL, /** * This is the suspended state for the remote device. This is the state that * the device is placed in when a RNC suspend is received by the SCU hardware. */ SCIC_SDS_SSP_REMOTE_DEVICE_READY_SUBSTATE_SUSPENDED, /** * This is the final state that the device is placed in before a change to the * base state machine. */ SCIC_SDS_SSP_REMOTE_DEVICE_READY_SUBSTATE_FINAL, SCIC_SDS_SSP_REMOTE_DEVICE_READY_MAX_SUBSTATES }; /** * @enum SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATES * * This is the enumeration for the SCIC_SDS_REMOTE_DEVICE ready substates for * the STP remote device. */ enum SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATES { /** * This is the idle substate for the stp remote device. When there are no * active IO for the device it is in this state. */ SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATE_IDLE, /** * This is the command state for the STP remote device. This state is * entered when the device is processing a non-NCQ command. The device object * will fail any new start IO requests until this command is complete. */ SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATE_CMD, /** * This is the NCQ state for the STP remote device. This state is entered * when the device is processing an NCQ reuqest. It will remain in this state * so long as there is one or more NCQ requests being processed. */ SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATE_NCQ, /** * This is the NCQ error state for the STP remote device. This state is * entered when an SDB error FIS is received by the device object while in the * NCQ state. The device object will only accept a READ LOG command while in * this state. */ SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATE_NCQ_ERROR, #if !defined(DISABLE_ATAPI) /** * This is the ATAPI error state for the STP ATAPI remote device. This state is * entered when ATAPI device sends error status FIS without data while the device * object is in CMD state. A suspension event is expected in this state. The device * object will resume right away. */ SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATE_ATAPI_ERROR, #endif /** * This is the READY substate indicates the device is waiting for the RESET task * coming to be recovered from certain hardware specific error. */ SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATE_AWAIT_RESET, SCIC_SDS_STP_REMOTE_DEVICE_READY_MAX_SUBSTATES }; /** * @enum SCIC_SDS_SMP_REMOTE_DEVICE_READY_SUBSTATES * * This is the enumeration of the ready substates for the SMP REMOTE DEVICE. */ enum SCIC_SDS_SMP_REMOTE_DEVICE_READY_SUBSTATES { /** * This is the ready operational substate for the remote device. This is the * normal operational state for a remote device. */ SCIC_SDS_SMP_REMOTE_DEVICE_READY_SUBSTATE_IDLE, /** * This is the suspended state for the remote device. This is the state that * the device is placed in when a RNC suspend is received by the SCU hardware. */ SCIC_SDS_SMP_REMOTE_DEVICE_READY_SUBSTATE_CMD, SCIC_SDS_SMP_REMOTE_DEVICE_READY_MAX_SUBSTATES }; /** * @struct SCIC_SDS_REMOTE_DEVICE * * @brief This structure contains the data for an SCU implementation of * the SCU Core device data. */ typedef struct SCIC_SDS_REMOTE_DEVICE { /** * This field is the common base for all remote device objects. */ SCI_BASE_REMOTE_DEVICE_T parent; /** * This field is the programmed device port width. This value is written to * the RCN data structure to tell the SCU how many open connections this * device can have. */ U32 device_port_width; /** * This field is the programmed connection rate for this remote device. It is * used to program the TC with the maximum allowed connection rate. */ SCI_SAS_LINK_RATE connection_rate; /** * This field contains the allowed target protocols for this remote device. */ SMP_DISCOVER_RESPONSE_PROTOCOLS_T target_protocols; /** * This field contains the device SAS address. */ SCI_SAS_ADDRESS_T device_address; /** - * This filed is assinged the value of TRUE if the device is directly attached + * This filed is assigned the value of TRUE if the device is directly attached * to the port. */ BOOL is_direct_attached; #if !defined(DISABLE_ATAPI) /** - * This filed is assinged the value of TRUE if the device is an ATAPI device. + * This filed is assigned the value of TRUE if the device is an ATAPI device. */ BOOL is_atapi; #endif /** * This filed contains a pointer back to the port to which this device is * assigned. */ struct SCIC_SDS_PORT *owning_port; /** * This field contains the SCU silicon remote node context specific * information. */ struct SCIC_SDS_REMOTE_NODE_CONTEXT * rnc; /** * This field contains the stated request count for the remote device. The * device can not reach the SCI_BASE_REMOTE_DEVICE_STATE_STOPPED until all * requests are complete and the rnc_posted value is FALSE. */ U32 started_request_count; /** * This field contains a pointer to the working request object. It is only * used only for SATA requests since the unsolicited frames we get from the * hardware have no Tag value to look up the io request object. */ struct SCIC_SDS_REQUEST * working_request; /** * This field contains the reason for the remote device going not_ready. It is * assigned in the state handlers and used in the state transition. */ U32 not_ready_reason; /** * This field is TRUE if this remote device has an initialized ready substate * machine. SSP devices do not have a ready substate machine and STP devices * have a ready substate machine. */ BOOL has_ready_substate_machine; /** * This field contains the state machine for the ready substate machine for * this SCIC_SDS_REMOTE_DEVICE object. */ SCI_BASE_STATE_MACHINE_T ready_substate_machine; /** * This field maintains the set of state handlers for the remote device * object. These are changed each time the remote device enters a new state. */ struct SCIC_SDS_REMOTE_DEVICE_STATE_HANDLER *state_handlers; #ifdef SCI_LOGGING /** * This field conatins the ready substate machine logger. The logger will * emit a message each time the ready substate machine changes state. */ SCI_BASE_STATE_MACHINE_LOGGER_T ready_substate_machine_logger; #endif } SCIC_SDS_REMOTE_DEVICE_T; typedef SCI_STATUS (*SCIC_SDS_REMOTE_DEVICE_HANDLER_T)( SCIC_SDS_REMOTE_DEVICE_T *this_device); typedef SCI_STATUS (*SCIC_SDS_REMOTE_DEVICE_SUSPEND_HANDLER_T)( SCIC_SDS_REMOTE_DEVICE_T *this_device, U32 suspend_type); typedef SCI_STATUS (*SCIC_SDS_REMOTE_DEVICE_RESUME_HANDLER_T)( SCIC_SDS_REMOTE_DEVICE_T *this_device); typedef SCI_STATUS (*SCIC_SDS_REMOTE_DEVICE_FRAME_HANDLER_T)( SCIC_SDS_REMOTE_DEVICE_T *this_device, U32 frame_index); typedef SCI_STATUS (*SCIC_SDS_REMOTE_DEVICE_EVENT_HANDLER_T)( SCIC_SDS_REMOTE_DEVICE_T *this_device, U32 event_code); typedef void (*SCIC_SDS_REMOTE_DEVICE_READY_NOT_READY_HANDLER_T)( SCIC_SDS_REMOTE_DEVICE_T *this_device); /** * @struct SCIC_SDS_REMOTE_DEVICE_STATE_HANDLER * @brief This structure conains the state handlers that are needed to * process requests for the SCU remote device objects. */ typedef struct SCIC_SDS_REMOTE_DEVICE_STATE_HANDLER { SCI_BASE_REMOTE_DEVICE_STATE_HANDLER_T parent; SCIC_SDS_REMOTE_DEVICE_SUSPEND_HANDLER_T suspend_handler; SCIC_SDS_REMOTE_DEVICE_RESUME_HANDLER_T resume_handler; SCIC_SDS_REMOTE_DEVICE_EVENT_HANDLER_T event_handler; SCIC_SDS_REMOTE_DEVICE_FRAME_HANDLER_T frame_handler; } SCIC_SDS_REMOTE_DEVICE_STATE_HANDLER_T; extern SCI_BASE_STATE_T scic_sds_remote_device_state_table[]; extern SCI_BASE_STATE_T scic_sds_ssp_remote_device_ready_substate_table[]; extern SCI_BASE_STATE_T scic_sds_stp_remote_device_ready_substate_table[]; extern SCI_BASE_STATE_T scic_sds_smp_remote_device_ready_substate_table[]; extern SCIC_SDS_REMOTE_DEVICE_STATE_HANDLER_T scic_sds_remote_device_state_handler_table[]; extern SCIC_SDS_REMOTE_DEVICE_STATE_HANDLER_T scic_sds_ssp_remote_device_ready_substate_handler_table[]; extern SCIC_SDS_REMOTE_DEVICE_STATE_HANDLER_T scic_sds_stp_remote_device_ready_substate_handler_table[]; extern SCIC_SDS_REMOTE_DEVICE_STATE_HANDLER_T scic_sds_smp_remote_device_ready_substate_handler_table[]; /** * This macro incrments the request count for this device */ #define scic_sds_remote_device_increment_request_count(this_device) \ ((this_device)->started_request_count++) /** * This macro decrements the request count for this device. This count * will never decrment past 0. */ #define scic_sds_remote_device_decrement_request_count(this_device) \ ((this_device)->started_request_count > 0 ? \ (this_device)->started_request_count-- : 0) /** * This is a helper macro to return the current device request count. */ #define scic_sds_remote_device_get_request_count(this_device) \ ((this_device)->started_request_count) /** * This macro returns the owning port of this remote device obejct. */ #define scic_sds_remote_device_get_port(this_device) \ ((this_device)->owning_port) /** * This macro returns the controller object that contains this device * object */ #define scic_sds_remote_device_get_controller(this_device) \ scic_sds_port_get_controller(scic_sds_remote_device_get_port(this_device)) /** * This macro sets the remote device state handlers pointer and is set on * entry to each device state. */ #define scic_sds_remote_device_set_state_handlers(this_device, handlers) \ ((this_device)->state_handlers = (handlers)) /** * This macro returns the base sate machine object for the remote device. */ #define scic_sds_remote_device_get_base_state_machine(this_device) \ (&(this_device)->parent.state_machine) /** * This macro returns the remote device ready substate machine */ #define scic_sds_remote_device_get_ready_substate_machine(this_device) \ (&(this_device)->ready_substate_machine) /** * This macro returns the owning port of this device */ #define scic_sds_remote_device_get_port(this_device) \ ((this_device)->owning_port) /** * This macro returns the remote device sequence value */ #define scic_sds_remote_device_get_sequence(this_device) \ ( \ scic_sds_remote_device_get_controller(this_device)->\ remote_device_sequence[(this_device)->rnc->remote_node_index] \ ) /** * This macro returns the controllers protocol engine group */ #define scic_sds_remote_device_get_controller_peg(this_device) \ ( \ scic_sds_controller_get_protocol_engine_group( \ scic_sds_port_get_controller( \ scic_sds_remote_device_get_port(this_device) \ ) \ ) \ ) /** * This macro returns the port index for the devices owning port */ #define scic_sds_remote_device_get_port_index(this_device) \ (scic_sds_port_get_index(scic_sds_remote_device_get_port(this_device))) /** * This macro returns the remote node index for this device object */ #define scic_sds_remote_device_get_index(this_device) \ ((this_device)->rnc->remote_node_index) /** * This macro builds a remote device context for the SCU post request * operation */ #define scic_sds_remote_device_build_command_context(device, command) \ ( (command) \ | ((U32)(scic_sds_remote_device_get_controller_peg((device))) << SCU_CONTEXT_COMMAND_PROTOCOL_ENGINE_GROUP_SHIFT)\ | ((U32)(scic_sds_remote_device_get_port_index((device))) << SCU_CONTEXT_COMMAND_LOGICAL_PORT_SHIFT) \ | (scic_sds_remote_device_get_index((device))) \ ) /** * This macro makes the working request assingment for the remote device * object. To clear the working request use this macro with a NULL request * object. */ #define scic_sds_remote_device_set_working_request(device, request) \ ((device)->working_request = (request)) // --------------------------------------------------------------------------- U32 scic_sds_remote_device_get_min_timer_count(void); U32 scic_sds_remote_device_get_max_timer_count(void); SCI_STATUS scic_sds_remote_device_frame_handler( SCIC_SDS_REMOTE_DEVICE_T *this_device, U32 frame_index ); SCI_STATUS scic_sds_remote_device_event_handler( SCIC_SDS_REMOTE_DEVICE_T *this_device, U32 event_code ); SCI_STATUS scic_sds_remote_device_start_io( struct SCIC_SDS_CONTROLLER *controller, SCIC_SDS_REMOTE_DEVICE_T *this_device, struct SCIC_SDS_REQUEST *io_request ); SCI_STATUS scic_sds_remote_device_complete_io( struct SCIC_SDS_CONTROLLER *controller, SCIC_SDS_REMOTE_DEVICE_T *this_device, struct SCIC_SDS_REQUEST *io_request ); SCI_STATUS scic_sds_remote_device_resume( SCIC_SDS_REMOTE_DEVICE_T *this_device ); SCI_STATUS scic_sds_remote_device_suspend( SCIC_SDS_REMOTE_DEVICE_T *this_device, U32 suspend_type ); SCI_STATUS scic_sds_remote_device_start_task( struct SCIC_SDS_CONTROLLER *controller, SCIC_SDS_REMOTE_DEVICE_T *this_device, struct SCIC_SDS_REQUEST *io_request ); void scic_sds_remote_device_post_request( SCIC_SDS_REMOTE_DEVICE_T * this_device, U32 request ); #if !defined(DISABLE_ATAPI) BOOL scic_sds_remote_device_is_atapi( SCIC_SDS_REMOTE_DEVICE_T *this_device ); #else // !defined(DISABLE_ATAPI) #define scic_sds_remote_device_is_atapi(this_device) FALSE #endif // !defined(DISABLE_ATAPI) // --------------------------------------------------------------------------- #ifdef SCI_LOGGING void scic_sds_remote_device_initialize_state_logging( SCIC_SDS_REMOTE_DEVICE_T *this_device ); void scic_sds_remote_device_deinitialize_state_logging( SCIC_SDS_REMOTE_DEVICE_T *this_device ); #else // SCI_LOGGING #define scic_sds_remote_device_initialize_state_logging(x) #define scic_sds_remote_device_deinitialize_state_logging(x) #endif // SCI_LOGGING // --------------------------------------------------------------------------- void scic_sds_remote_device_start_request( SCIC_SDS_REMOTE_DEVICE_T * this_device, struct SCIC_SDS_REQUEST * the_request, SCI_STATUS status ); void scic_sds_remote_device_continue_request( SCIC_SDS_REMOTE_DEVICE_T * this_device ); SCI_STATUS scic_sds_remote_device_default_start_handler( SCI_BASE_REMOTE_DEVICE_T *this_device ); SCI_STATUS scic_sds_remote_device_default_stop_handler( SCI_BASE_REMOTE_DEVICE_T *this_device ); SCI_STATUS scic_sds_remote_device_default_fail_handler( SCI_BASE_REMOTE_DEVICE_T *this_device ); SCI_STATUS scic_sds_remote_device_default_destruct_handler( SCI_BASE_REMOTE_DEVICE_T *this_device ); SCI_STATUS scic_sds_remote_device_default_reset_handler( SCI_BASE_REMOTE_DEVICE_T *device ); SCI_STATUS scic_sds_remote_device_default_reset_complete_handler( SCI_BASE_REMOTE_DEVICE_T *device ); SCI_STATUS scic_sds_remote_device_default_start_request_handler( SCI_BASE_REMOTE_DEVICE_T *device, SCI_BASE_REQUEST_T *request ); SCI_STATUS scic_sds_remote_device_default_complete_request_handler( SCI_BASE_REMOTE_DEVICE_T *device, SCI_BASE_REQUEST_T *request ); SCI_STATUS scic_sds_remote_device_default_continue_request_handler( SCI_BASE_REMOTE_DEVICE_T *device, SCI_BASE_REQUEST_T *request ); SCI_STATUS scic_sds_remote_device_default_suspend_handler( SCIC_SDS_REMOTE_DEVICE_T *this_device, U32 suspend_type ); SCI_STATUS scic_sds_remote_device_default_resume_handler( SCIC_SDS_REMOTE_DEVICE_T *this_device ); SCI_STATUS scic_sds_remote_device_default_event_handler( SCIC_SDS_REMOTE_DEVICE_T *this_device, U32 event_code ); SCI_STATUS scic_sds_remote_device_default_frame_handler( SCIC_SDS_REMOTE_DEVICE_T *this_device, U32 frame_index ); // --------------------------------------------------------------------------- SCI_STATUS scic_sds_remote_device_ready_state_stop_handler( SCI_BASE_REMOTE_DEVICE_T *device ); SCI_STATUS scic_sds_remote_device_ready_state_reset_handler( SCI_BASE_REMOTE_DEVICE_T *device ); SCI_STATUS scic_sds_remote_device_general_frame_handler( SCIC_SDS_REMOTE_DEVICE_T *this_device, U32 frame_index ); SCI_STATUS scic_sds_remote_device_general_event_handler( SCIC_SDS_REMOTE_DEVICE_T *this_device, U32 event_code ); SCI_STATUS scic_sds_ssp_remote_device_ready_suspended_substate_resume_handler( SCIC_SDS_REMOTE_DEVICE_T *this_device ); // --------------------------------------------------------------------------- void scic_sds_remote_device_get_info_from_smp_discover_response( SCIC_SDS_REMOTE_DEVICE_T * this_device, SMP_RESPONSE_DISCOVER_T * discover_response ); #ifdef __cplusplus } #endif // __cplusplus #endif // _SCIC_SDS_REMOTE_DEVICE_H_