Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * This file is provided under a dual BSD/GPLv2 license.  When using or
0003  * redistributing this file, you may do so under either license.
0004  *
0005  * GPL LICENSE SUMMARY
0006  *
0007  * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
0008  *
0009  * This program is free software; you can redistribute it and/or modify
0010  * it under the terms of version 2 of the GNU General Public License as
0011  * published by the Free Software Foundation.
0012  *
0013  * This program is distributed in the hope that it will be useful, but
0014  * WITHOUT ANY WARRANTY; without even the implied warranty of
0015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0016  * General Public License for more details.
0017  *
0018  * You should have received a copy of the GNU General Public License
0019  * along with this program; if not, write to the Free Software
0020  * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
0021  * The full GNU General Public License is included in this distribution
0022  * in the file called LICENSE.GPL.
0023  *
0024  * BSD LICENSE
0025  *
0026  * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
0027  * All rights reserved.
0028  *
0029  * Redistribution and use in source and binary forms, with or without
0030  * modification, are permitted provided that the following conditions
0031  * are met:
0032  *
0033  *   * Redistributions of source code must retain the above copyright
0034  *     notice, this list of conditions and the following disclaimer.
0035  *   * Redistributions in binary form must reproduce the above copyright
0036  *     notice, this list of conditions and the following disclaimer in
0037  *     the documentation and/or other materials provided with the
0038  *     distribution.
0039  *   * Neither the name of Intel Corporation nor the names of its
0040  *     contributors may be used to endorse or promote products derived
0041  *     from this software without specific prior written permission.
0042  *
0043  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
0044  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
0045  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
0046  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
0047  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
0048  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
0049  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
0050  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
0051  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0052  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
0053  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0054  */
0055 
0056 #include "host.h"
0057 
0058 #define SCIC_SDS_MPC_RECONFIGURATION_TIMEOUT    (10)
0059 #define SCIC_SDS_APC_RECONFIGURATION_TIMEOUT    (10)
0060 #define SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION  (1000)
0061 
0062 enum SCIC_SDS_APC_ACTIVITY {
0063     SCIC_SDS_APC_SKIP_PHY,
0064     SCIC_SDS_APC_ADD_PHY,
0065     SCIC_SDS_APC_START_TIMER,
0066 
0067     SCIC_SDS_APC_ACTIVITY_MAX
0068 };
0069 
0070 /*
0071  * ******************************************************************************
0072  * General port configuration agent routines
0073  * ****************************************************************************** */
0074 
0075 /**
0076  * sci_sas_address_compare()
0077  * @address_one: A SAS Address to be compared.
0078  * @address_two: A SAS Address to be compared.
0079  *
0080  * Compare the two SAS Address and if SAS Address One is greater than SAS
0081  * Address Two then return > 0 else if SAS Address One is less than SAS Address
0082  * Two return < 0 Otherwise they are the same return 0 A signed value of x > 0
0083  * > y where x is returned for Address One > Address Two y is returned for
0084  * Address One < Address Two 0 is returned ofr Address One = Address Two
0085  */
0086 static s32 sci_sas_address_compare(
0087     struct sci_sas_address address_one,
0088     struct sci_sas_address address_two)
0089 {
0090     if (address_one.high > address_two.high) {
0091         return 1;
0092     } else if (address_one.high < address_two.high) {
0093         return -1;
0094     } else if (address_one.low > address_two.low) {
0095         return 1;
0096     } else if (address_one.low < address_two.low) {
0097         return -1;
0098     }
0099 
0100     /* The two SAS Address must be identical */
0101     return 0;
0102 }
0103 
0104 /**
0105  * sci_port_configuration_agent_find_port()
0106  * @ihost: The controller object used for the port search.
0107  * @iphy: The phy object to match.
0108  *
0109  * This routine will find a matching port for the phy.  This means that the
0110  * port and phy both have the same broadcast sas address and same received sas
0111  * address. The port address or the NULL if there is no matching
0112  * port. port address if the port can be found to match the phy.
0113  * NULL if there is no matching port for the phy.
0114  */
0115 static struct isci_port *sci_port_configuration_agent_find_port(
0116     struct isci_host *ihost,
0117     struct isci_phy *iphy)
0118 {
0119     u8 i;
0120     struct sci_sas_address port_sas_address;
0121     struct sci_sas_address port_attached_device_address;
0122     struct sci_sas_address phy_sas_address;
0123     struct sci_sas_address phy_attached_device_address;
0124 
0125     /*
0126      * Since this phy can be a member of a wide port check to see if one or
0127      * more phys match the sent and received SAS address as this phy in which
0128      * case it should participate in the same port.
0129      */
0130     sci_phy_get_sas_address(iphy, &phy_sas_address);
0131     sci_phy_get_attached_sas_address(iphy, &phy_attached_device_address);
0132 
0133     for (i = 0; i < ihost->logical_port_entries; i++) {
0134         struct isci_port *iport = &ihost->ports[i];
0135 
0136         sci_port_get_sas_address(iport, &port_sas_address);
0137         sci_port_get_attached_sas_address(iport, &port_attached_device_address);
0138 
0139         if (sci_sas_address_compare(port_sas_address, phy_sas_address) == 0 &&
0140             sci_sas_address_compare(port_attached_device_address, phy_attached_device_address) == 0)
0141             return iport;
0142     }
0143 
0144     return NULL;
0145 }
0146 
0147 /**
0148  * sci_port_configuration_agent_validate_ports()
0149  * @ihost: This is the controller object that contains the port agent
0150  * @port_agent: This is the port configuration agent for the controller.
0151  *
0152  * This routine will validate the port configuration is correct for the SCU
0153  * hardware.  The SCU hardware allows for port configurations as follows. LP0
0154  * -> (PE0), (PE0, PE1), (PE0, PE1, PE2, PE3) LP1 -> (PE1) LP2 -> (PE2), (PE2,
0155  * PE3) LP3 -> (PE3) enum sci_status SCI_SUCCESS the port configuration is valid for
0156  * this port configuration agent. SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION
0157  * the port configuration is not valid for this port configuration agent.
0158  */
0159 static enum sci_status sci_port_configuration_agent_validate_ports(
0160     struct isci_host *ihost,
0161     struct sci_port_configuration_agent *port_agent)
0162 {
0163     struct sci_sas_address first_address;
0164     struct sci_sas_address second_address;
0165 
0166     /*
0167      * Sanity check the max ranges for all the phys the max index
0168      * is always equal to the port range index */
0169     if (port_agent->phy_valid_port_range[0].max_index != 0 ||
0170         port_agent->phy_valid_port_range[1].max_index != 1 ||
0171         port_agent->phy_valid_port_range[2].max_index != 2 ||
0172         port_agent->phy_valid_port_range[3].max_index != 3)
0173         return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
0174 
0175     /*
0176      * This is a request to configure a single x4 port or at least attempt
0177      * to make all the phys into a single port */
0178     if (port_agent->phy_valid_port_range[0].min_index == 0 &&
0179         port_agent->phy_valid_port_range[1].min_index == 0 &&
0180         port_agent->phy_valid_port_range[2].min_index == 0 &&
0181         port_agent->phy_valid_port_range[3].min_index == 0)
0182         return SCI_SUCCESS;
0183 
0184     /*
0185      * This is a degenerate case where phy 1 and phy 2 are assigned
0186      * to the same port this is explicitly disallowed by the hardware
0187      * unless they are part of the same x4 port and this condition was
0188      * already checked above. */
0189     if (port_agent->phy_valid_port_range[2].min_index == 1) {
0190         return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
0191     }
0192 
0193     /*
0194      * PE0 and PE3 can never have the same SAS Address unless they
0195      * are part of the same x4 wide port and we have already checked
0196      * for this condition. */
0197     sci_phy_get_sas_address(&ihost->phys[0], &first_address);
0198     sci_phy_get_sas_address(&ihost->phys[3], &second_address);
0199 
0200     if (sci_sas_address_compare(first_address, second_address) == 0) {
0201         return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
0202     }
0203 
0204     /*
0205      * PE0 and PE1 are configured into a 2x1 ports make sure that the
0206      * SAS Address for PE0 and PE2 are different since they can not be
0207      * part of the same port. */
0208     if (port_agent->phy_valid_port_range[0].min_index == 0 &&
0209         port_agent->phy_valid_port_range[1].min_index == 1) {
0210         sci_phy_get_sas_address(&ihost->phys[0], &first_address);
0211         sci_phy_get_sas_address(&ihost->phys[2], &second_address);
0212 
0213         if (sci_sas_address_compare(first_address, second_address) == 0) {
0214             return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
0215         }
0216     }
0217 
0218     /*
0219      * PE2 and PE3 are configured into a 2x1 ports make sure that the
0220      * SAS Address for PE1 and PE3 are different since they can not be
0221      * part of the same port. */
0222     if (port_agent->phy_valid_port_range[2].min_index == 2 &&
0223         port_agent->phy_valid_port_range[3].min_index == 3) {
0224         sci_phy_get_sas_address(&ihost->phys[1], &first_address);
0225         sci_phy_get_sas_address(&ihost->phys[3], &second_address);
0226 
0227         if (sci_sas_address_compare(first_address, second_address) == 0) {
0228             return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
0229         }
0230     }
0231 
0232     return SCI_SUCCESS;
0233 }
0234 
0235 /*
0236  * ******************************************************************************
0237  * Manual port configuration agent routines
0238  * ****************************************************************************** */
0239 
0240 /* verify all of the phys in the same port are using the same SAS address */
0241 static enum sci_status
0242 sci_mpc_agent_validate_phy_configuration(struct isci_host *ihost,
0243                           struct sci_port_configuration_agent *port_agent)
0244 {
0245     u32 phy_mask;
0246     u32 assigned_phy_mask;
0247     struct sci_sas_address sas_address;
0248     struct sci_sas_address phy_assigned_address;
0249     u8 port_index;
0250     u8 phy_index;
0251 
0252     assigned_phy_mask = 0;
0253     sas_address.high = 0;
0254     sas_address.low = 0;
0255 
0256     for (port_index = 0; port_index < SCI_MAX_PORTS; port_index++) {
0257         phy_mask = ihost->oem_parameters.ports[port_index].phy_mask;
0258 
0259         if (!phy_mask)
0260             continue;
0261         /*
0262          * Make sure that one or more of the phys were not already assinged to
0263          * a different port. */
0264         if ((phy_mask & ~assigned_phy_mask) == 0) {
0265             return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
0266         }
0267 
0268         /* Find the starting phy index for this round through the loop */
0269         for (phy_index = 0; phy_index < SCI_MAX_PHYS; phy_index++) {
0270             if ((phy_mask & (1 << phy_index)) == 0)
0271                 continue;
0272             sci_phy_get_sas_address(&ihost->phys[phy_index],
0273                              &sas_address);
0274 
0275             /*
0276              * The phy_index can be used as the starting point for the
0277              * port range since the hardware starts all logical ports
0278              * the same as the PE index. */
0279             port_agent->phy_valid_port_range[phy_index].min_index = port_index;
0280             port_agent->phy_valid_port_range[phy_index].max_index = phy_index;
0281 
0282             if (phy_index != port_index) {
0283                 return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
0284             }
0285 
0286             break;
0287         }
0288 
0289         /*
0290          * See how many additional phys are being added to this logical port.
0291          * Note: We have not moved the current phy_index so we will actually
0292          *       compare the startting phy with itself.
0293          *       This is expected and required to add the phy to the port. */
0294         for (; phy_index < SCI_MAX_PHYS; phy_index++) {
0295             if ((phy_mask & (1 << phy_index)) == 0)
0296                 continue;
0297             sci_phy_get_sas_address(&ihost->phys[phy_index],
0298                              &phy_assigned_address);
0299 
0300             if (sci_sas_address_compare(sas_address, phy_assigned_address) != 0) {
0301                 /*
0302                  * The phy mask specified that this phy is part of the same port
0303                  * as the starting phy and it is not so fail this configuration */
0304                 return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
0305             }
0306 
0307             port_agent->phy_valid_port_range[phy_index].min_index = port_index;
0308             port_agent->phy_valid_port_range[phy_index].max_index = phy_index;
0309 
0310             sci_port_add_phy(&ihost->ports[port_index],
0311                           &ihost->phys[phy_index]);
0312 
0313             assigned_phy_mask |= (1 << phy_index);
0314         }
0315 
0316     }
0317 
0318     return sci_port_configuration_agent_validate_ports(ihost, port_agent);
0319 }
0320 
0321 static void mpc_agent_timeout(struct timer_list *t)
0322 {
0323     u8 index;
0324     struct sci_timer *tmr = from_timer(tmr, t, timer);
0325     struct sci_port_configuration_agent *port_agent;
0326     struct isci_host *ihost;
0327     unsigned long flags;
0328     u16 configure_phy_mask;
0329 
0330     port_agent = container_of(tmr, typeof(*port_agent), timer);
0331     ihost = container_of(port_agent, typeof(*ihost), port_agent);
0332 
0333     spin_lock_irqsave(&ihost->scic_lock, flags);
0334 
0335     if (tmr->cancel)
0336         goto done;
0337 
0338     port_agent->timer_pending = false;
0339 
0340     /* Find the mask of phys that are reported read but as yet unconfigured into a port */
0341     configure_phy_mask = ~port_agent->phy_configured_mask & port_agent->phy_ready_mask;
0342 
0343     for (index = 0; index < SCI_MAX_PHYS; index++) {
0344         struct isci_phy *iphy = &ihost->phys[index];
0345 
0346         if (configure_phy_mask & (1 << index)) {
0347             port_agent->link_up_handler(ihost, port_agent,
0348                             phy_get_non_dummy_port(iphy),
0349                             iphy);
0350         }
0351     }
0352 
0353 done:
0354     spin_unlock_irqrestore(&ihost->scic_lock, flags);
0355 }
0356 
0357 static void sci_mpc_agent_link_up(struct isci_host *ihost,
0358                        struct sci_port_configuration_agent *port_agent,
0359                        struct isci_port *iport,
0360                        struct isci_phy *iphy)
0361 {
0362     /* If the port is NULL then the phy was not assigned to a port.
0363      * This is because the phy was not given the same SAS Address as
0364      * the other PHYs in the port.
0365      */
0366     if (!iport)
0367         return;
0368 
0369     port_agent->phy_ready_mask |= (1 << iphy->phy_index);
0370     sci_port_link_up(iport, iphy);
0371     if ((iport->active_phy_mask & (1 << iphy->phy_index)))
0372         port_agent->phy_configured_mask |= (1 << iphy->phy_index);
0373 }
0374 
0375 /**
0376  * sci_mpc_agent_link_down()
0377  * @ihost: This is the controller object that receives the link down
0378  *    notification.
0379  * @port_agent: This is the port configuration agent for the controller.
0380  * @iport: This is the port object associated with the phy.  If the is no
0381  *    associated port this is an NULL.  The port is an invalid
0382  *    handle only if the phy was never port of this port.  This happens when
0383  *    the phy is not broadcasting the same SAS address as the other phys in the
0384  *    assigned port.
0385  * @iphy: This is the phy object which has gone link down.
0386  *
0387  * This function handles the manual port configuration link down notifications.
0388  * Since all ports and phys are associated at initialization time we just turn
0389  * around and notifiy the port object of the link down event.  If this PHY is
0390  * not associated with a port there is no action taken. Is it possible to get a
0391  * link down notification from a phy that has no assocoated port?
0392  */
0393 static void sci_mpc_agent_link_down(
0394     struct isci_host *ihost,
0395     struct sci_port_configuration_agent *port_agent,
0396     struct isci_port *iport,
0397     struct isci_phy *iphy)
0398 {
0399     if (iport != NULL) {
0400         /*
0401          * If we can form a new port from the remainder of the phys
0402          * then we want to start the timer to allow the SCI User to
0403          * cleanup old devices and rediscover the port before
0404          * rebuilding the port with the phys that remain in the ready
0405          * state.
0406          */
0407         port_agent->phy_ready_mask &= ~(1 << iphy->phy_index);
0408         port_agent->phy_configured_mask &= ~(1 << iphy->phy_index);
0409 
0410         /*
0411          * Check to see if there are more phys waiting to be
0412          * configured into a port. If there are allow the SCI User
0413          * to tear down this port, if necessary, and then reconstruct
0414          * the port after the timeout.
0415          */
0416         if ((port_agent->phy_configured_mask == 0x0000) &&
0417             (port_agent->phy_ready_mask != 0x0000) &&
0418             !port_agent->timer_pending) {
0419             port_agent->timer_pending = true;
0420 
0421             sci_mod_timer(&port_agent->timer,
0422                       SCIC_SDS_MPC_RECONFIGURATION_TIMEOUT);
0423         }
0424 
0425         sci_port_link_down(iport, iphy);
0426     }
0427 }
0428 
0429 /* verify phys are assigned a valid SAS address for automatic port
0430  * configuration mode.
0431  */
0432 static enum sci_status
0433 sci_apc_agent_validate_phy_configuration(struct isci_host *ihost,
0434                           struct sci_port_configuration_agent *port_agent)
0435 {
0436     u8 phy_index;
0437     u8 port_index;
0438     struct sci_sas_address sas_address;
0439     struct sci_sas_address phy_assigned_address;
0440 
0441     phy_index = 0;
0442 
0443     while (phy_index < SCI_MAX_PHYS) {
0444         port_index = phy_index;
0445 
0446         /* Get the assigned SAS Address for the first PHY on the controller. */
0447         sci_phy_get_sas_address(&ihost->phys[phy_index],
0448                         &sas_address);
0449 
0450         while (++phy_index < SCI_MAX_PHYS) {
0451             sci_phy_get_sas_address(&ihost->phys[phy_index],
0452                              &phy_assigned_address);
0453 
0454             /* Verify each of the SAS address are all the same for every PHY */
0455             if (sci_sas_address_compare(sas_address, phy_assigned_address) == 0) {
0456                 port_agent->phy_valid_port_range[phy_index].min_index = port_index;
0457                 port_agent->phy_valid_port_range[phy_index].max_index = phy_index;
0458             } else {
0459                 port_agent->phy_valid_port_range[phy_index].min_index = phy_index;
0460                 port_agent->phy_valid_port_range[phy_index].max_index = phy_index;
0461                 break;
0462             }
0463         }
0464     }
0465 
0466     return sci_port_configuration_agent_validate_ports(ihost, port_agent);
0467 }
0468 
0469 /*
0470  * This routine will restart the automatic port configuration timeout
0471  * timer for the next time period. This could be caused by either a link
0472  * down event or a link up event where we can not yet tell to which a phy
0473  * belongs.
0474  */
0475 static void sci_apc_agent_start_timer(struct sci_port_configuration_agent *port_agent,
0476                       u32 timeout)
0477 {
0478     port_agent->timer_pending = true;
0479     sci_mod_timer(&port_agent->timer, timeout);
0480 }
0481 
0482 static void sci_apc_agent_configure_ports(struct isci_host *ihost,
0483                            struct sci_port_configuration_agent *port_agent,
0484                            struct isci_phy *iphy,
0485                            bool start_timer)
0486 {
0487     u8 port_index;
0488     enum sci_status status;
0489     struct isci_port *iport;
0490     enum SCIC_SDS_APC_ACTIVITY apc_activity = SCIC_SDS_APC_SKIP_PHY;
0491 
0492     iport = sci_port_configuration_agent_find_port(ihost, iphy);
0493 
0494     if (iport) {
0495         if (sci_port_is_valid_phy_assignment(iport, iphy->phy_index))
0496             apc_activity = SCIC_SDS_APC_ADD_PHY;
0497         else
0498             apc_activity = SCIC_SDS_APC_SKIP_PHY;
0499     } else {
0500         /*
0501          * There is no matching Port for this PHY so lets search through the
0502          * Ports and see if we can add the PHY to its own port or maybe start
0503          * the timer and wait to see if a wider port can be made.
0504          *
0505          * Note the break when we reach the condition of the port id == phy id */
0506         for (port_index = port_agent->phy_valid_port_range[iphy->phy_index].min_index;
0507              port_index <= port_agent->phy_valid_port_range[iphy->phy_index].max_index;
0508              port_index++) {
0509 
0510             iport = &ihost->ports[port_index];
0511 
0512             /* First we must make sure that this PHY can be added to this Port. */
0513             if (sci_port_is_valid_phy_assignment(iport, iphy->phy_index)) {
0514                 /*
0515                  * Port contains a PHY with a greater PHY ID than the current
0516                  * PHY that has gone link up.  This phy can not be part of any
0517                  * port so skip it and move on. */
0518                 if (iport->active_phy_mask > (1 << iphy->phy_index)) {
0519                     apc_activity = SCIC_SDS_APC_SKIP_PHY;
0520                     break;
0521                 }
0522 
0523                 /*
0524                  * We have reached the end of our Port list and have not found
0525                  * any reason why we should not either add the PHY to the port
0526                  * or wait for more phys to become active. */
0527                 if (iport->physical_port_index == iphy->phy_index) {
0528                     /*
0529                      * The Port either has no active PHYs.
0530                      * Consider that if the port had any active PHYs we would have
0531                      * or active PHYs with
0532                      * a lower PHY Id than this PHY. */
0533                     if (apc_activity != SCIC_SDS_APC_START_TIMER) {
0534                         apc_activity = SCIC_SDS_APC_ADD_PHY;
0535                     }
0536 
0537                     break;
0538                 }
0539 
0540                 /*
0541                  * The current Port has no active PHYs and this PHY could be part
0542                  * of this Port.  Since we dont know as yet setup to start the
0543                  * timer and see if there is a better configuration. */
0544                 if (iport->active_phy_mask == 0) {
0545                     apc_activity = SCIC_SDS_APC_START_TIMER;
0546                 }
0547             } else if (iport->active_phy_mask != 0) {
0548                 /*
0549                  * The Port has an active phy and the current Phy can not
0550                  * participate in this port so skip the PHY and see if
0551                  * there is a better configuration. */
0552                 apc_activity = SCIC_SDS_APC_SKIP_PHY;
0553             }
0554         }
0555     }
0556 
0557     /*
0558      * Check to see if the start timer operations should instead map to an
0559      * add phy operation.  This is caused because we have been waiting to
0560      * add a phy to a port but could not becuase the automatic port
0561      * configuration engine had a choice of possible ports for the phy.
0562      * Since we have gone through a timeout we are going to restrict the
0563      * choice to the smallest possible port. */
0564     if (
0565         (start_timer == false)
0566         && (apc_activity == SCIC_SDS_APC_START_TIMER)
0567         ) {
0568         apc_activity = SCIC_SDS_APC_ADD_PHY;
0569     }
0570 
0571     switch (apc_activity) {
0572     case SCIC_SDS_APC_ADD_PHY:
0573         status = sci_port_add_phy(iport, iphy);
0574 
0575         if (status == SCI_SUCCESS) {
0576             port_agent->phy_configured_mask |= (1 << iphy->phy_index);
0577         }
0578         break;
0579 
0580     case SCIC_SDS_APC_START_TIMER:
0581         sci_apc_agent_start_timer(port_agent,
0582                       SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION);
0583         break;
0584 
0585     case SCIC_SDS_APC_SKIP_PHY:
0586     default:
0587         /* do nothing the PHY can not be made part of a port at this time. */
0588         break;
0589     }
0590 }
0591 
0592 /**
0593  * sci_apc_agent_link_up - handle apc link up events
0594  * @ihost: This is the controller object that receives the link up
0595  *    notification.
0596  * @port_agent: This is the port configuration agent for the controller.
0597  * @iport: This is the port object associated with the phy.  If the is no
0598  *    associated port this is an NULL.
0599  * @iphy: This is the phy object which has gone link up.
0600  *
0601  * This method handles the automatic port configuration for link up
0602  * notifications. Is it possible to get a link down notification from a phy
0603  * that has no assocoated port?
0604  */
0605 static void sci_apc_agent_link_up(struct isci_host *ihost,
0606                        struct sci_port_configuration_agent *port_agent,
0607                        struct isci_port *iport,
0608                        struct isci_phy *iphy)
0609 {
0610     u8 phy_index  = iphy->phy_index;
0611 
0612     if (!iport) {
0613         /* the phy is not the part of this port */
0614         port_agent->phy_ready_mask |= 1 << phy_index;
0615         sci_apc_agent_start_timer(port_agent,
0616                       SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION);
0617     } else {
0618         /* the phy is already the part of the port */
0619         port_agent->phy_ready_mask |= 1 << phy_index;
0620         sci_port_link_up(iport, iphy);
0621     }
0622 }
0623 
0624 /**
0625  * sci_apc_agent_link_down()
0626  * @ihost: This is the controller object that receives the link down
0627  *    notification.
0628  * @port_agent: This is the port configuration agent for the controller.
0629  * @iport: This is the port object associated with the phy.  If the is no
0630  *    associated port this is an NULL.
0631  * @iphy: This is the phy object which has gone link down.
0632  *
0633  * This method handles the automatic port configuration link down
0634  * notifications. not associated with a port there is no action taken. Is it
0635  * possible to get a link down notification from a phy that has no assocoated
0636  * port?
0637  */
0638 static void sci_apc_agent_link_down(
0639     struct isci_host *ihost,
0640     struct sci_port_configuration_agent *port_agent,
0641     struct isci_port *iport,
0642     struct isci_phy *iphy)
0643 {
0644     port_agent->phy_ready_mask &= ~(1 << iphy->phy_index);
0645 
0646     if (!iport)
0647         return;
0648     if (port_agent->phy_configured_mask & (1 << iphy->phy_index)) {
0649         enum sci_status status;
0650 
0651         status = sci_port_remove_phy(iport, iphy);
0652 
0653         if (status == SCI_SUCCESS)
0654             port_agent->phy_configured_mask &= ~(1 << iphy->phy_index);
0655     }
0656 }
0657 
0658 /* configure the phys into ports when the timer fires */
0659 static void apc_agent_timeout(struct timer_list *t)
0660 {
0661     u32 index;
0662     struct sci_timer *tmr = from_timer(tmr, t, timer);
0663     struct sci_port_configuration_agent *port_agent;
0664     struct isci_host *ihost;
0665     unsigned long flags;
0666     u16 configure_phy_mask;
0667 
0668     port_agent = container_of(tmr, typeof(*port_agent), timer);
0669     ihost = container_of(port_agent, typeof(*ihost), port_agent);
0670 
0671     spin_lock_irqsave(&ihost->scic_lock, flags);
0672 
0673     if (tmr->cancel)
0674         goto done;
0675 
0676     port_agent->timer_pending = false;
0677 
0678     configure_phy_mask = ~port_agent->phy_configured_mask & port_agent->phy_ready_mask;
0679 
0680     if (!configure_phy_mask)
0681         goto done;
0682 
0683     for (index = 0; index < SCI_MAX_PHYS; index++) {
0684         if ((configure_phy_mask & (1 << index)) == 0)
0685             continue;
0686 
0687         sci_apc_agent_configure_ports(ihost, port_agent,
0688                            &ihost->phys[index], false);
0689     }
0690 
0691     if (is_controller_start_complete(ihost))
0692         sci_controller_transition_to_ready(ihost, SCI_SUCCESS);
0693 
0694 done:
0695     spin_unlock_irqrestore(&ihost->scic_lock, flags);
0696 }
0697 
0698 /*
0699  * ******************************************************************************
0700  * Public port configuration agent routines
0701  * ****************************************************************************** */
0702 
0703 /*
0704  * This method will construct the port configuration agent for operation. This
0705  * call is universal for both manual port configuration and automatic port
0706  * configuration modes.
0707  */
0708 void sci_port_configuration_agent_construct(
0709     struct sci_port_configuration_agent *port_agent)
0710 {
0711     u32 index;
0712 
0713     port_agent->phy_configured_mask = 0x00;
0714     port_agent->phy_ready_mask = 0x00;
0715 
0716     port_agent->link_up_handler = NULL;
0717     port_agent->link_down_handler = NULL;
0718 
0719     port_agent->timer_pending = false;
0720 
0721     for (index = 0; index < SCI_MAX_PORTS; index++) {
0722         port_agent->phy_valid_port_range[index].min_index = 0;
0723         port_agent->phy_valid_port_range[index].max_index = 0;
0724     }
0725 }
0726 
0727 bool is_port_config_apc(struct isci_host *ihost)
0728 {
0729     return ihost->port_agent.link_up_handler == sci_apc_agent_link_up;
0730 }
0731 
0732 enum sci_status sci_port_configuration_agent_initialize(
0733     struct isci_host *ihost,
0734     struct sci_port_configuration_agent *port_agent)
0735 {
0736     enum sci_status status;
0737     enum sci_port_configuration_mode mode;
0738 
0739     mode = ihost->oem_parameters.controller.mode_type;
0740 
0741     if (mode == SCIC_PORT_MANUAL_CONFIGURATION_MODE) {
0742         status = sci_mpc_agent_validate_phy_configuration(
0743                 ihost, port_agent);
0744 
0745         port_agent->link_up_handler = sci_mpc_agent_link_up;
0746         port_agent->link_down_handler = sci_mpc_agent_link_down;
0747 
0748         sci_init_timer(&port_agent->timer, mpc_agent_timeout);
0749     } else {
0750         status = sci_apc_agent_validate_phy_configuration(
0751                 ihost, port_agent);
0752 
0753         port_agent->link_up_handler = sci_apc_agent_link_up;
0754         port_agent->link_down_handler = sci_apc_agent_link_down;
0755 
0756         sci_init_timer(&port_agent->timer, apc_agent_timeout);
0757     }
0758 
0759     return status;
0760 }