The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/isci/scil/scic_sds_port_configuration_agent.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * SPDX-License-Identifier: BSD-2-Clause OR GPL-2.0
    3  *
    4  * This file is provided under a dual BSD/GPLv2 license.  When using or
    5  * redistributing this file, you may do so under either license.
    6  *
    7  * GPL LICENSE SUMMARY
    8  *
    9  * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
   10  *
   11  * This program is free software; you can redistribute it and/or modify
   12  * it under the terms of version 2 of the GNU General Public License as
   13  * published by the Free Software Foundation.
   14  *
   15  * This program is distributed in the hope that it will be useful, but
   16  * WITHOUT ANY WARRANTY; without even the implied warranty of
   17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   18  * General Public License for more details.
   19  *
   20  * You should have received a copy of the GNU General Public License
   21  * along with this program; if not, write to the Free Software
   22  * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
   23  * The full GNU General Public License is included in this distribution
   24  * in the file called LICENSE.GPL.
   25  *
   26  * BSD LICENSE
   27  *
   28  * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
   29  * All rights reserved.
   30  *
   31  * Redistribution and use in source and binary forms, with or without
   32  * modification, are permitted provided that the following conditions
   33  * are met:
   34  *
   35  *   * Redistributions of source code must retain the above copyright
   36  *     notice, this list of conditions and the following disclaimer.
   37  *   * Redistributions in binary form must reproduce the above copyright
   38  *     notice, this list of conditions and the following disclaimer in
   39  *     the documentation and/or other materials provided with the
   40  *     distribution.
   41  *
   42  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   43  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   44  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
   45  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
   46  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   47  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   48  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   49  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   50  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   51  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   52  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   53  */
   54 
   55 #include <sys/cdefs.h>
   56 __FBSDID("$FreeBSD$");
   57 
   58 /**
   59  * @file
   60  *
   61  * @brief This file contains the implementation for the public and protected
   62  *        methods for the port configuration agent.
   63  */
   64 
   65 #include <dev/isci/scil/scic_controller.h>
   66 #include <dev/isci/scil/scic_sds_logger.h>
   67 #include <dev/isci/scil/scic_sds_controller.h>
   68 #include <dev/isci/scil/scic_sds_port_configuration_agent.h>
   69 
   70 #define SCIC_SDS_MPC_RECONFIGURATION_TIMEOUT    (10)
   71 #define SCIC_SDS_APC_RECONFIGURATION_TIMEOUT    (10)
   72 #define SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION  (250)
   73 
   74 enum SCIC_SDS_APC_ACTIVITY
   75 {
   76    SCIC_SDS_APC_SKIP_PHY,
   77    SCIC_SDS_APC_ADD_PHY,
   78    SCIC_SDS_APC_START_TIMER,
   79 
   80    SCIC_SDS_APC_ACTIVITY_MAX
   81 };
   82 
   83 //******************************************************************************
   84 // General port configuration agent routines
   85 //******************************************************************************
   86 
   87 /**
   88  * Compare the two SAS Address and
   89  * if SAS Address One is greater than SAS Address Two then return > 0
   90  * else if SAS Address One is less than SAS Address Two return < 0
   91  * Otherwise they are the same return 0
   92  *
   93  * @param[in] address_one A SAS Address to be compared.
   94  * @param[in] address_two A SAS Address to be compared.
   95  *
   96  * @return A signed value of x > 0 > y where
   97  *         x is returned for Address One > Address Two
   98  *         y is returned for Address One < Address Two
   99  *         0 is returned ofr Address One = Address Two
  100  */
  101 static
  102 S32 sci_sas_address_compare(
  103    SCI_SAS_ADDRESS_T address_one,
  104    SCI_SAS_ADDRESS_T address_two
  105 )
  106 {
  107    if (address_one.high > address_two.high)
  108    {
  109       return 1;
  110    }
  111    else if (address_one.high < address_two.high)
  112    {
  113       return -1;
  114    }
  115    else if (address_one.low > address_two.low)
  116    {
  117       return 1;
  118    }
  119    else if (address_one.low < address_two.low)
  120    {
  121       return -1;
  122    }
  123 
  124    // The two SAS Address must be identical
  125    return 0;
  126 }
  127 
  128 /**
  129  * This routine will find a matching port for the phy.  This means that the
  130  * port and phy both have the same broadcast sas address and same received
  131  * sas address.
  132  *
  133  * @param[in] controller The controller object used for the port search.
  134  * @param[in] phy The phy object to match.
  135  *
  136  * @return The port address or the SCI_INVALID_HANDLE if there is no matching
  137  *         port.
  138  *
  139  * @retvalue port address if the port can be found to match the phy.
  140  * @retvalue SCI_INVALID_HANDLE if there is no matching port for the phy.
  141  */
  142 static
  143 SCIC_SDS_PORT_T * scic_sds_port_configuration_agent_find_port(
  144    SCIC_SDS_CONTROLLER_T * controller,
  145    SCIC_SDS_PHY_T        * phy
  146 )
  147 {
  148    U8 port_index;
  149    SCI_PORT_HANDLE_T port_handle;
  150    SCI_SAS_ADDRESS_T port_sas_address;
  151    SCI_SAS_ADDRESS_T port_attached_device_address;
  152    SCI_SAS_ADDRESS_T phy_sas_address;
  153    SCI_SAS_ADDRESS_T phy_attached_device_address;
  154 
  155    SCIC_LOG_TRACE((
  156       sci_base_object_get_logger(controller),
  157       SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY,
  158       "scic_sds_port_confgiruation_agent_find_port(0x%08x, 0x%08x) enter\n",
  159       controller, phy
  160    ));
  161 
  162    // Since this phy can be a member of a wide port check to see if one or
  163    // more phys match the sent and received SAS address as this phy in which
  164    // case it should participate in the same port.
  165    scic_sds_phy_get_sas_address(phy, &phy_sas_address);
  166    scic_sds_phy_get_attached_sas_address(phy, &phy_attached_device_address);
  167 
  168    for (port_index = 0; port_index < SCI_MAX_PORTS; port_index++)
  169    {
  170       if (scic_controller_get_port_handle(controller, port_index, &port_handle) == SCI_SUCCESS)
  171       {
  172          SCIC_SDS_PORT_T * port = (SCIC_SDS_PORT_T *)port_handle;
  173 
  174          scic_sds_port_get_sas_address(port, &port_sas_address);
  175          scic_sds_port_get_attached_sas_address(port, &port_attached_device_address);
  176 
  177          if (
  178                (sci_sas_address_compare(port_sas_address, phy_sas_address) == 0)
  179             && (sci_sas_address_compare(port_attached_device_address, phy_attached_device_address) == 0)
  180             )
  181          {
  182             return port;
  183          }
  184       }
  185    }
  186 
  187    return SCI_INVALID_HANDLE;
  188 }
  189 
  190 /**
  191  * This routine will validate the port configuration is correct for the SCU
  192  * hardware.  The SCU hardware allows for port configurations as follows.
  193  *    LP0 -> (PE0), (PE0, PE1), (PE0, PE1, PE2, PE3)
  194  *    LP1 -> (PE1)
  195  *    LP2 -> (PE2), (PE2, PE3)
  196  *    LP3 -> (PE3)
  197  *
  198  * @param[in] controller This is the controller object that contains the
  199  *            port agent
  200  * @param[in] port_agent This is the port configruation agent for
  201  *            the controller.
  202  *
  203  * @return SCI_STATUS
  204  * @retval SCI_SUCCESS the port configuration is valid for this
  205  *         port configuration agent.
  206  * @retval SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION the port configuration
  207  *         is not valid for this port configuration agent.
  208  */
  209 static
  210 SCI_STATUS scic_sds_port_configuration_agent_validate_ports(
  211    SCIC_SDS_CONTROLLER_T               * controller,
  212    SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent
  213 )
  214 {
  215 #if !defined(ARLINGTON_BUILD)
  216    SCI_SAS_ADDRESS_T first_address;
  217    SCI_SAS_ADDRESS_T second_address;
  218 
  219    SCIC_LOG_TRACE((
  220       sci_base_object_get_logger(controller),
  221       SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT,
  222       "scic_sds_port_configuration_agent_validate_ports(0x%08x, 0x%08x) enter\n",
  223       controller, port_agent
  224    ));
  225 
  226    // Sanity check the max ranges for all the phys the max index
  227    // is always equal to the port range index
  228    if (
  229          (port_agent->phy_valid_port_range[0].max_index != 0)
  230       || (port_agent->phy_valid_port_range[1].max_index != 1)
  231       || (port_agent->phy_valid_port_range[2].max_index != 2)
  232       || (port_agent->phy_valid_port_range[3].max_index != 3)
  233       )
  234    {
  235       return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
  236    }
  237 
  238    // This is a request to configure a single x4 port or at least attempt
  239    // to make all the phys into a single port
  240    if (
  241          (port_agent->phy_valid_port_range[0].min_index == 0)
  242       && (port_agent->phy_valid_port_range[1].min_index == 0)
  243       && (port_agent->phy_valid_port_range[2].min_index == 0)
  244       && (port_agent->phy_valid_port_range[3].min_index == 0)
  245       )
  246    {
  247       return SCI_SUCCESS;
  248    }
  249 
  250    // This is a degenerate case where phy 1 and phy 2 are assigned
  251    // to the same port this is explicitly disallowed by the hardware
  252    // unless they are part of the same x4 port and this condition was
  253    // already checked above.
  254    if (port_agent->phy_valid_port_range[2].min_index == 1)
  255    {
  256       return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
  257    }
  258 
  259    // PE0 and PE3 can never have the same SAS Address unless they
  260    // are part of the same x4 wide port and we have already checked
  261    // for this condition.
  262    scic_sds_phy_get_sas_address(&controller->phy_table[0], &first_address);
  263    scic_sds_phy_get_sas_address(&controller->phy_table[3], &second_address);
  264 
  265    if (sci_sas_address_compare(first_address, second_address) == 0)
  266    {
  267       return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
  268    }
  269 
  270    // PE0 and PE1 are configured into a 2x1 ports make sure that the
  271    // SAS Address for PE0 and PE2 are different since they can not be
  272    // part of the same port.
  273    if (
  274          (port_agent->phy_valid_port_range[0].min_index == 0)
  275       && (port_agent->phy_valid_port_range[1].min_index == 1)
  276       )
  277    {
  278       scic_sds_phy_get_sas_address(&controller->phy_table[0], &first_address);
  279       scic_sds_phy_get_sas_address(&controller->phy_table[2], &second_address);
  280 
  281       if (sci_sas_address_compare(first_address, second_address) == 0)
  282       {
  283          return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
  284       }
  285    }
  286 
  287    // PE2 and PE3 are configured into a 2x1 ports make sure that the
  288    // SAS Address for PE1 and PE3 are different since they can not be
  289    // part of the same port.
  290    if (
  291          (port_agent->phy_valid_port_range[2].min_index == 2)
  292       && (port_agent->phy_valid_port_range[3].min_index == 3)
  293       )
  294    {
  295       scic_sds_phy_get_sas_address(&controller->phy_table[1], &first_address);
  296       scic_sds_phy_get_sas_address(&controller->phy_table[3], &second_address);
  297 
  298       if (sci_sas_address_compare(first_address, second_address) == 0)
  299       {
  300          return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
  301       }
  302    }
  303 #endif // !defined(ARLINGTON_BUILD)
  304 
  305    return SCI_SUCCESS;
  306 }
  307 
  308 //******************************************************************************
  309 // Manual port configuration agent routines
  310 //******************************************************************************
  311 
  312 /**
  313  * This routine will verify that all of the phys in the same port are using
  314  * the same SAS address.
  315  *
  316  * @param[in] controller This is the controller that contains the PHYs to
  317  *            be verified.
  318  */
  319 static
  320 SCI_STATUS scic_sds_mpc_agent_validate_phy_configuration(
  321    SCIC_SDS_CONTROLLER_T               * controller,
  322    SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent
  323 )
  324 {
  325    U32 phy_mask;
  326    U32 assigned_phy_mask;
  327    SCI_SAS_ADDRESS_T sas_address;
  328    SCI_SAS_ADDRESS_T phy_assigned_address;
  329    U8 port_index;
  330    U8 phy_index;
  331 
  332    assigned_phy_mask = 0;
  333    sas_address.high = 0;
  334    sas_address.low = 0;
  335 
  336    SCIC_LOG_TRACE((
  337       sci_base_object_get_logger(controller),
  338       SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT,
  339       "scic_sds_mpc_agent_validate_phy_configuration(0x%08x, 0x%08x) enter\n",
  340       controller, port_agent
  341    ));
  342 
  343    for (port_index = 0; port_index < SCI_MAX_PORTS; port_index++)
  344    {
  345       phy_mask = controller->oem_parameters.sds1.ports[port_index].phy_mask;
  346 
  347       if (phy_mask != 0)
  348       {
  349          // Make sure that one or more of the phys were not already assigned to
  350          // a different port.
  351          if ((phy_mask & ~assigned_phy_mask) == 0)
  352          {
  353             return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
  354          }
  355 
  356          // Find the starting phy index for this round through the loop
  357          for (phy_index = 0; phy_index < SCI_MAX_PHYS; phy_index++)
  358          {
  359             if ((1 << phy_index) & phy_mask)
  360             {
  361                scic_sds_phy_get_sas_address(
  362                   &controller->phy_table[phy_index], &sas_address
  363                );
  364 
  365                // The phy_index can be used as the starting point for the
  366                // port range since the hardware starts all logical ports
  367                // the same as the PE index.
  368                port_agent->phy_valid_port_range[phy_index].min_index = port_index;
  369                port_agent->phy_valid_port_range[phy_index].max_index = phy_index;
  370 
  371                if (phy_index != port_index)
  372                {
  373                   return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
  374                }
  375 
  376                break;
  377             }
  378          }
  379 
  380          // See how many additional phys are being added to this logical port.
  381          // Note: We have not moved the current phy_index so we will actually
  382          //       compare the startting phy with itself.
  383          //       This is expected and required to add the phy to the port.
  384          while (phy_index < SCI_MAX_PHYS)
  385          {
  386             if ((1 << phy_index) & phy_mask)
  387             {
  388                scic_sds_phy_get_sas_address(
  389                   &controller->phy_table[phy_index], &phy_assigned_address
  390                );
  391 
  392                if (sci_sas_address_compare(sas_address, phy_assigned_address) != 0)
  393                {
  394                   // The phy mask specified that this phy is part of the same port
  395                   // as the starting phy and it is not so fail this configuration
  396                   return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
  397                }
  398 
  399                port_agent->phy_valid_port_range[phy_index].min_index = port_index;
  400                port_agent->phy_valid_port_range[phy_index].max_index = phy_index;
  401 
  402                scic_sds_port_add_phy(
  403                   &controller->port_table[port_index],
  404                   &controller->phy_table[phy_index]
  405                );
  406 
  407                assigned_phy_mask |= (1 << phy_index);
  408             }
  409 
  410             phy_index++;
  411          }
  412       }
  413    }
  414 
  415    return scic_sds_port_configuration_agent_validate_ports(controller, port_agent);
  416 }
  417 
  418 /**
  419  * This timer routine is used to allow the SCI User to rediscover or change
  420  * device objects before a new series of link up notifications because a
  421  * link down has allowed a better port configuration.
  422  *
  423  * @param[in] controller This is the core controller object which is used
  424  *            to obtain the port configuration agent.
  425  */
  426 static
  427 void scic_sds_mpc_agent_timeout_handler(
  428    void * object
  429 )
  430 {
  431    U8 index;
  432    SCIC_SDS_CONTROLLER_T * controller = (SCIC_SDS_CONTROLLER_T *)object;
  433    SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent = &controller->port_agent;
  434    U16 configure_phy_mask;
  435 
  436    SCIC_LOG_TRACE((
  437       sci_base_object_get_logger(controller),
  438       SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT,
  439       "scic_sds_mpc_agent_timeout_handler(0x%08x) enter\n",
  440       controller
  441    ));
  442 
  443    port_agent->timer_pending = FALSE;
  444 
  445    // Find the mask of phys that are reported read but as yet unconfigured into a port
  446    configure_phy_mask = ~port_agent->phy_configured_mask & port_agent->phy_ready_mask;
  447 
  448    for (index = 0; index < SCI_MAX_PHYS; index++)
  449    {
  450       if (configure_phy_mask & (1 << index))
  451       {
  452          port_agent->link_up_handler(
  453                         controller,
  454                         port_agent,
  455                         scic_sds_phy_get_port(&controller->phy_table[index]),
  456                         &controller->phy_table[index]
  457                      );
  458       }
  459    }
  460 }
  461 
  462 /**
  463  * This method handles the manual port configuration link up notifications.
  464  * Since all ports and phys are associate at initialization time we just turn
  465  * around and notifiy the port object that there is a link up.  If this PHY is
  466  * not associated with a port there is no action taken.
  467  *
  468  * @param[in] controller This is the controller object that receives the
  469  *            link up notification.
  470  * @param[in] port This is the port object associated with the phy.  If the
  471  *            is no associated port this is an SCI_INVALID_HANDLE.
  472  * @param[in] phy This is the phy object which has gone ready.
  473  *
  474  * @note Is it possible to get a link up notification from a phy that has
  475  *       no assocoated port?
  476  */
  477 static
  478 void scic_sds_mpc_agent_link_up(
  479    SCIC_SDS_CONTROLLER_T               * controller,
  480    SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent,
  481    SCIC_SDS_PORT_T                     * port,
  482    SCIC_SDS_PHY_T                      * phy
  483 )
  484 {
  485    SCIC_LOG_TRACE((
  486       sci_base_object_get_logger(controller),
  487       SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY,
  488       "scic_sds_mpc_agent_link_up(0x%08x, 0x%08x, 0x%08x, 0x%08x) enter\n",
  489       controller, port_agent, port, phy
  490    ));
  491 
  492    // If the port has an invalid handle then the phy was not assigned to
  493    // a port.  This is because the phy was not given the same SAS Address
  494    // as the other PHYs in the port.
  495    if (port != SCI_INVALID_HANDLE)
  496    {
  497       port_agent->phy_ready_mask |= (1 << scic_sds_phy_get_index(phy));
  498 
  499       scic_sds_port_link_up(port, phy);
  500 
  501       if ((port->active_phy_mask & (1 << scic_sds_phy_get_index(phy))) != 0)
  502       {
  503          port_agent->phy_configured_mask |= (1 << scic_sds_phy_get_index(phy));
  504       }
  505    }
  506 }
  507 
  508 /**
  509  * This method handles the manual port configuration link down notifications.
  510  * Since all ports and phys are associated at initialization time we just turn
  511  * around and notifiy the port object of the link down event.  If this PHY is
  512  * not associated with a port there is no action taken.
  513  *
  514  * @param[in] controller This is the controller object that receives the
  515  *            link down notification.
  516  * @param[in] port This is the port object associated with the phy.  If the
  517  *            is no associated port this is an SCI_INVALID_HANDLE.  The port
  518  *            is an invalid handle only if the phy was never port of this
  519  *            port.  This happens when the phy is not broadcasting the same
  520  *            SAS address as the other phys in the assigned port.
  521  * @param[in] phy This is the phy object which has gone link down.
  522  *
  523  * @note Is it possible to get a link down notification from a phy that has
  524  *       no assocoated port?
  525  */
  526 static
  527 void scic_sds_mpc_agent_link_down(
  528    SCIC_SDS_CONTROLLER_T               * controller,
  529    SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent,
  530    SCIC_SDS_PORT_T                     * port,
  531    SCIC_SDS_PHY_T                      * phy
  532 )
  533 {
  534    SCIC_LOG_TRACE((
  535       sci_base_object_get_logger(controller),
  536       SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY,
  537       "scic_sds_mpc_agent_link_down(0x%08x, 0x%08x, 0x%08x, 0x%08x) enter\n",
  538       controller, port_agent, port, phy
  539    ));
  540 
  541    if (port != SCI_INVALID_HANDLE)
  542    {
  543       // If we can form a new port from the remainder of the phys then we want
  544       // to start the timer to allow the SCI User to cleanup old devices and
  545       // rediscover the port before rebuilding the port with the phys that
  546       // remain in the ready state.
  547       port_agent->phy_ready_mask &= ~(1 << scic_sds_phy_get_index(phy));
  548       port_agent->phy_configured_mask &= ~(1 << scic_sds_phy_get_index(phy));
  549 
  550       // Check to see if there are more phys waiting to be configured into a port.
  551       // If there are allow the SCI User to tear down this port, if necessary, and
  552       // then reconstruc the port after the timeout.
  553       if (
  554             (port_agent->phy_configured_mask == 0x0000)
  555          && (port_agent->phy_ready_mask != 0x0000)
  556          && !port_agent->timer_pending
  557          )
  558       {
  559          port_agent->timer_pending = TRUE;
  560 
  561          scic_cb_timer_start(
  562             controller,
  563             port_agent->timer,
  564             SCIC_SDS_MPC_RECONFIGURATION_TIMEOUT
  565          );
  566       }
  567 
  568       scic_sds_port_link_down(port, phy);
  569    }
  570 }
  571 
  572 //******************************************************************************
  573 // Automatic port configuration agent routines
  574 //******************************************************************************
  575 
  576 /**
  577  * This routine will verify that the phys are assigned a valid SAS address for
  578  * automatic port configuration mode.
  579  */
  580 static
  581 SCI_STATUS scic_sds_apc_agent_validate_phy_configuration(
  582    SCIC_SDS_CONTROLLER_T               * controller,
  583    SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent
  584 )
  585 {
  586    U8 phy_index;
  587    U8 port_index;
  588    SCI_SAS_ADDRESS_T sas_address;
  589    SCI_SAS_ADDRESS_T phy_assigned_address;
  590 
  591    SCIC_LOG_TRACE((
  592       sci_base_object_get_logger(controller),
  593       SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT,
  594       "scic_sds_apc_agent_validate_phy_configuration(0x%08x, 0x%08x) enter\n",
  595       controller, port_agent
  596    ));
  597 
  598    phy_index = 0;
  599 
  600    while (phy_index < SCI_MAX_PHYS)
  601    {
  602       port_index = phy_index;
  603 
  604       // Get the assigned SAS Address for the first PHY on the controller.
  605       scic_sds_phy_get_sas_address(
  606          &controller->phy_table[phy_index], &sas_address
  607       );
  608 
  609       while (++phy_index < SCI_MAX_PHYS)
  610       {
  611          scic_sds_phy_get_sas_address(
  612             &controller->phy_table[phy_index], &phy_assigned_address
  613          );
  614 
  615          // Verify each of the SAS address are all the same for every PHY
  616          if (sci_sas_address_compare(sas_address, phy_assigned_address) == 0)
  617          {
  618             port_agent->phy_valid_port_range[phy_index].min_index = port_index;
  619             port_agent->phy_valid_port_range[phy_index].max_index = phy_index;
  620          }
  621          else
  622          {
  623             port_agent->phy_valid_port_range[phy_index].min_index = phy_index;
  624             port_agent->phy_valid_port_range[phy_index].max_index = phy_index;
  625             break;
  626          }
  627       }
  628    }
  629 
  630    return scic_sds_port_configuration_agent_validate_ports(controller, port_agent);
  631 }
  632 
  633 /**
  634  * This routine will restart the automatic port configuration timeout
  635  * timer for the next time period.  This could be caused by either a
  636  * link down event or a link up event where we can not yet tell to which
  637  * port a phy belongs.
  638  *
  639  * @param[in] controller This is the controller that to which the port
  640  *            agent is assigned.
  641  * @param[in] port_agent This is the port agent that is requesting the
  642  *            timer start operation.
  643  * @param[in] phy This is the phy that has caused the timer operation to
  644  *            be scheduled.
  645  * @param[in] timeout This is the timeout in ms.
  646  */
  647 static
  648 void scic_sds_apc_agent_start_timer(
  649    SCIC_SDS_CONTROLLER_T               * controller,
  650    SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent,
  651    SCIC_SDS_PHY_T                      * phy,
  652    U32                                   timeout
  653 )
  654 {
  655    SCIC_LOG_TRACE((
  656       sci_base_object_get_logger(controller),
  657       SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY,
  658       "scic_sds_apc_agent_start_timer(0x%08x, 0x%08x, 0x%08x, 0x%08x) enter\n",
  659       controller, port_agent, phy, timeout
  660    ));
  661 
  662    if (port_agent->timer_pending)
  663    {
  664       scic_cb_timer_stop(controller, port_agent->timer);
  665    }
  666 
  667    port_agent->timer_pending = TRUE;
  668 
  669    scic_cb_timer_start(controller, port_agent->timer, timeout);
  670 }
  671 
  672 /**
  673  * This method handles the automatic port configuration for link up notifications.
  674  *
  675  * @param[in] controller This is the controller object that receives the
  676  *            link up notification.
  677  * @param[in] phy This is the phy object which has gone link up.
  678  * @param[in] start_timer This tells the routine if it should start the timer for
  679  *            any phys that might be added to a port in the future.
  680  */
  681 static
  682 void scic_sds_apc_agent_configure_ports(
  683    SCIC_SDS_CONTROLLER_T               * controller,
  684    SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent,
  685    SCIC_SDS_PHY_T                      * phy,
  686    BOOL                                  start_timer
  687 )
  688 {
  689    U8 port_index;
  690    SCI_STATUS status;
  691    SCIC_SDS_PORT_T * port;
  692    SCI_PORT_HANDLE_T port_handle;
  693    enum SCIC_SDS_APC_ACTIVITY apc_activity = SCIC_SDS_APC_SKIP_PHY;
  694 
  695    SCIC_LOG_TRACE((
  696       sci_base_object_get_logger(controller),
  697       SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY,
  698       "scic_sds_apc_agent_configure_ports(0x%08x, 0x%08x, 0x%08x, %d) enter\n",
  699       controller, port_agent, phy, start_timer
  700    ));
  701 
  702    port = scic_sds_port_configuration_agent_find_port(controller, phy);
  703 
  704    if (port != SCI_INVALID_HANDLE)
  705    {
  706       if (scic_sds_port_is_valid_phy_assignment(port, phy->phy_index))
  707          apc_activity = SCIC_SDS_APC_ADD_PHY;
  708       else
  709          apc_activity = SCIC_SDS_APC_SKIP_PHY;
  710    }
  711    else
  712    {
  713       // There is no matching Port for this PHY so lets search through the
  714       // Ports and see if we can add the PHY to its own port or maybe start
  715       // the timer and wait to see if a wider port can be made.
  716       //
  717       // Note the break when we reach the condition of the port id == phy id
  718       for (
  719              port_index = port_agent->phy_valid_port_range[phy->phy_index].min_index;
  720              port_index <= port_agent->phy_valid_port_range[phy->phy_index].max_index;
  721              port_index++
  722           )
  723       {
  724          scic_controller_get_port_handle(controller, port_index, &port_handle);
  725 
  726          port = (SCIC_SDS_PORT_T *)port_handle;
  727 
  728          // First we must make sure that this PHY can be added to this Port.
  729          if (scic_sds_port_is_valid_phy_assignment(port, phy->phy_index))
  730          {
  731             // Port contains a PHY with a greater PHY ID than the current
  732             // PHY that has gone link up.  This phy can not be part of any
  733             // port so skip it and move on.
  734             if (port->active_phy_mask > (1 << phy->phy_index))
  735             {
  736                apc_activity = SCIC_SDS_APC_SKIP_PHY;
  737                break;
  738             }
  739 
  740             // We have reached the end of our Port list and have not found
  741             // any reason why we should not either add the PHY to the port
  742             // or wait for more phys to become active.
  743             if (port->physical_port_index == phy->phy_index)
  744             {
  745                // The Port either has no active PHYs.
  746                // Consider that if the port had any active PHYs we would have
  747                // or active PHYs with
  748                // a lower PHY Id than this PHY.
  749                if (apc_activity != SCIC_SDS_APC_START_TIMER)
  750                {
  751                   apc_activity = SCIC_SDS_APC_ADD_PHY;
  752                }
  753 
  754                break;
  755             }
  756 
  757             // The current Port has no active PHYs and this PHY could be part
  758             // of this Port.  Since we dont know as yet setup to start the
  759             // timer and see if there is a better configuration.
  760             if (port->active_phy_mask == 0)
  761             {
  762                apc_activity = SCIC_SDS_APC_START_TIMER;
  763             }
  764          }
  765          else if (port->active_phy_mask != 0)
  766          {
  767             // The Port has an active phy and the current Phy can not
  768             // participate in this port so skip the PHY and see if
  769             // there is a better configuration.
  770             apc_activity = SCIC_SDS_APC_SKIP_PHY;
  771          }
  772       }
  773    }
  774 
  775    // Check to see if the start timer operations should instead map to an
  776    // add phy operation.  This is caused because we have been waiting to
  777    // add a phy to a port but could not because the automatic port
  778    // configuration engine had a choice of possible ports for the phy.
  779    // Since we have gone through a timeout we are going to restrict the
  780    // choice to the smallest possible port.
  781    if (
  782          (start_timer == FALSE)
  783       && (apc_activity == SCIC_SDS_APC_START_TIMER)
  784       )
  785    {
  786       apc_activity = SCIC_SDS_APC_ADD_PHY;
  787    }
  788 
  789    switch (apc_activity)
  790    {
  791    case SCIC_SDS_APC_ADD_PHY:
  792       status = scic_sds_port_add_phy(port, phy);
  793 
  794       if (status == SCI_SUCCESS)
  795       {
  796          port_agent->phy_configured_mask |= (1 << phy->phy_index);
  797       }
  798       break;
  799 
  800    case SCIC_SDS_APC_START_TIMER:
  801       scic_sds_apc_agent_start_timer(
  802          controller, port_agent, phy, SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION
  803       );
  804       break;
  805 
  806    case SCIC_SDS_APC_SKIP_PHY:
  807    default:
  808       // do nothing the PHY can not be made part of a port at this time.
  809       break;
  810    }
  811 }
  812 
  813 /**
  814  * This method handles the automatic port configuration for link up notifications.
  815  *
  816  * @param[in] controller This is the controller object that receives the
  817  *            link up notification.
  818  * @param[in] port This is the port object associated with the phy.  If the
  819  *            is no associated port this is an SCI_INVALID_HANDLE.
  820  * @param[in] phy This is the phy object which has gone link up.
  821  *
  822  * @note Is it possible to get a link down notification from a phy that has
  823  *       no assocoated port?
  824  */
  825 static
  826 void scic_sds_apc_agent_link_up(
  827    SCIC_SDS_CONTROLLER_T               * controller,
  828    SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent,
  829    SCIC_SDS_PORT_T                     * port,
  830    SCIC_SDS_PHY_T                      * phy
  831 )
  832 {
  833    SCIC_LOG_TRACE((
  834       sci_base_object_get_logger(controller),
  835       SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY,
  836       "scic_sds_apc_agent_link_up(0x%08x, 0x%08x, 0x%08x, 0x%08x) enter\n",
  837       controller, port_agent, port, phy
  838    ));
  839 
  840    //the phy is not the part of this port, configure the port with this phy
  841    if (port == SCI_INVALID_HANDLE)
  842    {
  843       port_agent->phy_ready_mask |= (1 << scic_sds_phy_get_index(phy));
  844 
  845       scic_sds_apc_agent_start_timer(
  846          controller, port_agent, phy, SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION
  847       );
  848    }
  849    else
  850    {
  851       //the phy is already the part of the port
  852 
  853       //if the PORT'S state is resetting then the link up is from port hard reset
  854       //in this case, we need to tell the port that link up is received
  855       if (  SCI_BASE_PORT_STATE_RESETTING
  856             == port->parent.state_machine.current_state_id
  857          )
  858       {
  859          //notify the port that port needs to be ready
  860          port_agent->phy_ready_mask |= (1 << scic_sds_phy_get_index(phy));
  861          scic_sds_port_link_up(port, phy);
  862       }
  863       else
  864       {
  865          ASSERT (0);
  866       }
  867    }
  868 }
  869 
  870 /**
  871  * This method handles the automatic port configuration link down notifications.
  872  * If this PHY is * not associated with a port there is no action taken.
  873  *
  874  * @param[in] controller This is the controller object that receives the
  875  *            link down notification.
  876  * @param[in] port This is the port object associated with the phy.  If the
  877  *            is no associated port this is an SCI_INVALID_HANDLE.
  878  * @param[in] phy This is the phy object which has gone link down.
  879  *
  880  * @note Is it possible to get a link down notification from a phy that has
  881  *       no assocoated port?
  882  */
  883 static
  884 void scic_sds_apc_agent_link_down(
  885    SCIC_SDS_CONTROLLER_T               * controller,
  886    SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent,
  887    SCIC_SDS_PORT_T                     * port,
  888    SCIC_SDS_PHY_T                      * phy
  889 )
  890 {
  891    SCIC_LOG_TRACE((
  892       sci_base_object_get_logger(controller),
  893       SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY,
  894       "scic_sds_apc_agent_link_down(0x%08x, 0x%08x, 0x%08x, 0x%08x) enter\n",
  895       controller, port_agent, port, phy
  896    ));
  897 
  898    port_agent->phy_ready_mask &= ~(1 << scic_sds_phy_get_index(phy));
  899 
  900    if (port != SCI_INVALID_HANDLE)
  901    {
  902       if (port_agent->phy_configured_mask & (1 << phy->phy_index))
  903       {
  904          SCI_STATUS status;
  905 
  906          status = scic_sds_port_remove_phy(port, phy);
  907 
  908          if (status == SCI_SUCCESS)
  909          {
  910             port_agent->phy_configured_mask &= ~(1 << phy->phy_index);
  911          }
  912       }
  913    }
  914 }
  915 
  916 /**
  917  * This routine will try to configure the phys into ports when the timer fires.
  918  *
  919  * @param[in] object This is actually the controller that needs to have the
  920  *            pending phys configured.
  921  */
  922 static
  923 void scic_sds_apc_agent_timeout_handler(
  924    void * object
  925 )
  926 {
  927    U32 index;
  928    SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent;
  929    SCIC_SDS_CONTROLLER_T * controller = (SCIC_SDS_CONTROLLER_T *)object;
  930    U16 configure_phy_mask;
  931 
  932    port_agent = scic_sds_controller_get_port_configuration_agent(controller);
  933 
  934    SCIC_LOG_TRACE((
  935       sci_base_object_get_logger(controller),
  936       SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT,
  937       "scic_sds_apc_agent_timeout_handler(0x%08x) enter\n",
  938       controller
  939    ));
  940 
  941    port_agent->timer_pending = FALSE;
  942 
  943    configure_phy_mask = ~port_agent->phy_configured_mask & port_agent->phy_ready_mask;
  944 
  945    if (configure_phy_mask != 0x00)
  946    {
  947       for (index = 0; index < SCI_MAX_PHYS; index++)
  948       {
  949          if (configure_phy_mask & (1 << index))
  950          {
  951             scic_sds_apc_agent_configure_ports(
  952                controller, port_agent, &controller->phy_table[index], FALSE
  953             );
  954          }
  955       }
  956 
  957       //Notify the controller ports are configured.
  958       if (
  959             (port_agent->phy_ready_mask == port_agent->phy_configured_mask) &&
  960             (controller->next_phy_to_start == SCI_MAX_PHYS) &&
  961             (controller->phy_startup_timer_pending == FALSE)
  962          )
  963       {
  964          // The controller has successfully finished the start process.
  965          // Inform the SCI Core user and transition to the READY state.
  966          if (scic_sds_controller_is_start_complete(controller) == TRUE)
  967          {
  968             scic_sds_controller_port_agent_configured_ports(controller);
  969          }
  970       }
  971    }
  972 }
  973 
  974 //******************************************************************************
  975 // Public port configuration agent routines
  976 //******************************************************************************
  977 
  978 /**
  979  * This method will construct the port configuration agent for operation.
  980  * This call is universal for both manual port configuration and automatic
  981  * port configuration modes.
  982  *
  983  * @param[in] port_agent This is the port configuration agent for this
  984  *            controller object.
  985  */
  986 void scic_sds_port_configuration_agent_construct(
  987    SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent
  988 )
  989 {
  990    U32 index;
  991 
  992    port_agent->phy_configured_mask = 0x00;
  993    port_agent->phy_ready_mask = 0x00;
  994 
  995    port_agent->link_up_handler = NULL;
  996    port_agent->link_down_handler = NULL;
  997 
  998    port_agent->timer_pending = FALSE;
  999    port_agent->timer = NULL;
 1000 
 1001    for (index = 0; index < SCI_MAX_PORTS; index++)
 1002    {
 1003       port_agent->phy_valid_port_range[index].min_index = 0;
 1004       port_agent->phy_valid_port_range[index].max_index = 0;
 1005    }
 1006 }
 1007 
 1008 /**
 1009  * This method will construct the port configuration agent for this controller.
 1010  *
 1011  * @param[in] controller This is the controller object for which the port
 1012  *            agent is being initialized.
 1013  *
 1014  * @param[in] port_agent This is the port configuration agent that is being
 1015  *            initialized.  The initialization path is handled differently
 1016  *            for the automatic port configuration agent and the manual port
 1017  *            configuration agent.
 1018  *
 1019  * @return
 1020  */
 1021 SCI_STATUS scic_sds_port_configuration_agent_initialize(
 1022    SCIC_SDS_CONTROLLER_T               * controller,
 1023    SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent
 1024 )
 1025 {
 1026    SCI_STATUS status = SCI_SUCCESS;
 1027    enum SCIC_PORT_CONFIGURATION_MODE mode;
 1028 
 1029    SCIC_LOG_TRACE((
 1030       sci_base_object_get_logger(controller),
 1031       SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT,
 1032       "scic_sds_port_configuration_agent_initialize(0x%08x, 0x%08x) enter\n",
 1033       controller, port_agent
 1034    ));
 1035 
 1036    mode = controller->oem_parameters.sds1.controller.mode_type;
 1037 
 1038    if (mode == SCIC_PORT_MANUAL_CONFIGURATION_MODE)
 1039    {
 1040       status = scic_sds_mpc_agent_validate_phy_configuration(controller, port_agent);
 1041 
 1042       port_agent->link_up_handler = scic_sds_mpc_agent_link_up;
 1043       port_agent->link_down_handler = scic_sds_mpc_agent_link_down;
 1044 
 1045       port_agent->timer = scic_cb_timer_create(
 1046                               controller,
 1047                               scic_sds_mpc_agent_timeout_handler,
 1048                               controller
 1049                           );
 1050    }
 1051    else
 1052    {
 1053       status = scic_sds_apc_agent_validate_phy_configuration(controller, port_agent);
 1054 
 1055       port_agent->link_up_handler = scic_sds_apc_agent_link_up;
 1056       port_agent->link_down_handler = scic_sds_apc_agent_link_down;
 1057 
 1058       port_agent->timer = scic_cb_timer_create(
 1059                               controller,
 1060                               scic_sds_apc_agent_timeout_handler,
 1061                               controller
 1062                           );
 1063    }
 1064 
 1065    // Make sure we have actually gotten a timer
 1066    if (status == SCI_SUCCESS && port_agent->timer == NULL)
 1067    {
 1068       SCIC_LOG_ERROR((
 1069          sci_base_object_get_logger(controller),
 1070          SCIC_LOG_OBJECT_CONTROLLER,
 1071          "Controller 0x%x automatic port configuration agent could not get timer.\n",
 1072          controller
 1073      ));
 1074 
 1075      status = SCI_FAILURE;
 1076    }
 1077 
 1078    return status;
 1079 }
 1080 
 1081 /**
 1082  * This method will destroy the port configuration agent for this controller.
 1083  *
 1084  * @param[in] controller This is the controller object for which the port
 1085  *            agent is being destroyed.
 1086  *
 1087  * @param[in] port_agent This is the port configuration agent that is being
 1088  *            destroyed.
 1089  *
 1090  * @return
 1091  */
 1092 void scic_sds_port_configuration_agent_destroy(
 1093    SCIC_SDS_CONTROLLER_T               * controller,
 1094    SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent
 1095 )
 1096 {
 1097    if (port_agent->timer_pending == TRUE)
 1098    {
 1099       scic_cb_timer_stop(controller, port_agent->timer);
 1100    }
 1101 
 1102    scic_cb_timer_destroy(controller, port_agent->timer);
 1103 
 1104    port_agent->timer_pending = FALSE;
 1105    port_agent->timer = NULL;
 1106 }
 1107 
 1108 
 1109 /**
 1110  * @brief This method release resources in for a scic port configuration agent.
 1111  *
 1112  * @param[in] controller This parameter specifies the core controller, one of
 1113  *            its phy's resources are to be released.
 1114  * @param[in] this_phy This parameter specifies the phy whose resource is to
 1115  *            be released.
 1116  */
 1117 void scic_sds_port_configuration_agent_release_resource(
 1118    SCIC_SDS_CONTROLLER_T               * controller,
 1119    SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent
 1120 )
 1121 {
 1122    SCIC_LOG_TRACE((
 1123       sci_base_object_get_logger(controller),
 1124       SCIC_LOG_OBJECT_PORT,
 1125       "scic_sds_port_configuration_agent_release_resource(0x%x, 0x%x)\n",
 1126       controller, port_agent
 1127    ));
 1128 
 1129    //Currently, the only resource to be released is a timer.
 1130    if (port_agent->timer != NULL)
 1131    {
 1132       scic_cb_timer_destroy(controller, port_agent->timer);
 1133       port_agent->timer = NULL;
 1134    }
 1135 }

Cache object: 3954af57acb5a76a6d9cf5d9fe26135b


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.