761 lines · c
1/*2 * This file is provided under a dual BSD/GPLv2 license. When using or3 * redistributing this file, you may do so under either license.4 *5 * GPL LICENSE SUMMARY6 *7 * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.8 *9 * This program is free software; you can redistribute it and/or modify10 * it under the terms of version 2 of the GNU General Public License as11 * published by the Free Software Foundation.12 *13 * This program is distributed in the hope that it will be useful, but14 * WITHOUT ANY WARRANTY; without even the implied warranty of15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU16 * General Public License for more details.17 *18 * You should have received a copy of the GNU General Public License19 * along with this program; if not, write to the Free Software20 * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.21 * The full GNU General Public License is included in this distribution22 * in the file called LICENSE.GPL.23 *24 * BSD LICENSE25 *26 * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.27 * All rights reserved.28 *29 * Redistribution and use in source and binary forms, with or without30 * modification, are permitted provided that the following conditions31 * are met:32 *33 * * Redistributions of source code must retain the above copyright34 * notice, this list of conditions and the following disclaimer.35 * * Redistributions in binary form must reproduce the above copyright36 * notice, this list of conditions and the following disclaimer in37 * the documentation and/or other materials provided with the38 * distribution.39 * * Neither the name of Intel Corporation nor the names of its40 * contributors may be used to endorse or promote products derived41 * from this software without specific prior written permission.42 *43 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS44 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT45 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR46 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT47 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,48 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT49 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,50 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY51 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT52 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE53 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.54 */55 56#include "host.h"57 58#define SCIC_SDS_MPC_RECONFIGURATION_TIMEOUT (10)59#define SCIC_SDS_APC_RECONFIGURATION_TIMEOUT (10)60#define SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION (1000)61 62enum SCIC_SDS_APC_ACTIVITY {63 SCIC_SDS_APC_SKIP_PHY,64 SCIC_SDS_APC_ADD_PHY,65 SCIC_SDS_APC_START_TIMER,66 67 SCIC_SDS_APC_ACTIVITY_MAX68};69 70/*71 * ******************************************************************************72 * General port configuration agent routines73 * ****************************************************************************** */74 75/**76 * sci_sas_address_compare()77 * @address_one: A SAS Address to be compared.78 * @address_two: A SAS Address to be compared.79 *80 * Compare the two SAS Address and if SAS Address One is greater than SAS81 * Address Two then return > 0 else if SAS Address One is less than SAS Address82 * Two return < 0 Otherwise they are the same return 0 A signed value of x > 083 * > y where x is returned for Address One > Address Two y is returned for84 * Address One < Address Two 0 is returned ofr Address One = Address Two85 */86static s32 sci_sas_address_compare(87 struct sci_sas_address address_one,88 struct sci_sas_address address_two)89{90 if (address_one.high > address_two.high) {91 return 1;92 } else if (address_one.high < address_two.high) {93 return -1;94 } else if (address_one.low > address_two.low) {95 return 1;96 } else if (address_one.low < address_two.low) {97 return -1;98 }99 100 /* The two SAS Address must be identical */101 return 0;102}103 104/**105 * sci_port_configuration_agent_find_port()106 * @ihost: The controller object used for the port search.107 * @iphy: The phy object to match.108 *109 * This routine will find a matching port for the phy. This means that the110 * port and phy both have the same broadcast sas address and same received sas111 * address. The port address or the NULL if there is no matching112 * port. port address if the port can be found to match the phy.113 * NULL if there is no matching port for the phy.114 */115static struct isci_port *sci_port_configuration_agent_find_port(116 struct isci_host *ihost,117 struct isci_phy *iphy)118{119 u8 i;120 struct sci_sas_address port_sas_address;121 struct sci_sas_address port_attached_device_address;122 struct sci_sas_address phy_sas_address;123 struct sci_sas_address phy_attached_device_address;124 125 /*126 * Since this phy can be a member of a wide port check to see if one or127 * more phys match the sent and received SAS address as this phy in which128 * case it should participate in the same port.129 */130 sci_phy_get_sas_address(iphy, &phy_sas_address);131 sci_phy_get_attached_sas_address(iphy, &phy_attached_device_address);132 133 for (i = 0; i < ihost->logical_port_entries; i++) {134 struct isci_port *iport = &ihost->ports[i];135 136 sci_port_get_sas_address(iport, &port_sas_address);137 sci_port_get_attached_sas_address(iport, &port_attached_device_address);138 139 if (sci_sas_address_compare(port_sas_address, phy_sas_address) == 0 &&140 sci_sas_address_compare(port_attached_device_address, phy_attached_device_address) == 0)141 return iport;142 }143 144 return NULL;145}146 147/**148 * sci_port_configuration_agent_validate_ports()149 * @ihost: This is the controller object that contains the port agent150 * @port_agent: This is the port configuration agent for the controller.151 *152 * This routine will validate the port configuration is correct for the SCU153 * hardware. The SCU hardware allows for port configurations as follows. LP0154 * -> (PE0), (PE0, PE1), (PE0, PE1, PE2, PE3) LP1 -> (PE1) LP2 -> (PE2), (PE2,155 * PE3) LP3 -> (PE3) enum sci_status SCI_SUCCESS the port configuration is valid for156 * this port configuration agent. SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION157 * the port configuration is not valid for this port configuration agent.158 */159static enum sci_status sci_port_configuration_agent_validate_ports(160 struct isci_host *ihost,161 struct sci_port_configuration_agent *port_agent)162{163 struct sci_sas_address first_address;164 struct sci_sas_address second_address;165 166 /*167 * Sanity check the max ranges for all the phys the max index168 * is always equal to the port range index */169 if (port_agent->phy_valid_port_range[0].max_index != 0 ||170 port_agent->phy_valid_port_range[1].max_index != 1 ||171 port_agent->phy_valid_port_range[2].max_index != 2 ||172 port_agent->phy_valid_port_range[3].max_index != 3)173 return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;174 175 /*176 * This is a request to configure a single x4 port or at least attempt177 * to make all the phys into a single port */178 if (port_agent->phy_valid_port_range[0].min_index == 0 &&179 port_agent->phy_valid_port_range[1].min_index == 0 &&180 port_agent->phy_valid_port_range[2].min_index == 0 &&181 port_agent->phy_valid_port_range[3].min_index == 0)182 return SCI_SUCCESS;183 184 /*185 * This is a degenerate case where phy 1 and phy 2 are assigned186 * to the same port this is explicitly disallowed by the hardware187 * unless they are part of the same x4 port and this condition was188 * already checked above. */189 if (port_agent->phy_valid_port_range[2].min_index == 1) {190 return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;191 }192 193 /*194 * PE0 and PE3 can never have the same SAS Address unless they195 * are part of the same x4 wide port and we have already checked196 * for this condition. */197 sci_phy_get_sas_address(&ihost->phys[0], &first_address);198 sci_phy_get_sas_address(&ihost->phys[3], &second_address);199 200 if (sci_sas_address_compare(first_address, second_address) == 0) {201 return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;202 }203 204 /*205 * PE0 and PE1 are configured into a 2x1 ports make sure that the206 * SAS Address for PE0 and PE2 are different since they can not be207 * part of the same port. */208 if (port_agent->phy_valid_port_range[0].min_index == 0 &&209 port_agent->phy_valid_port_range[1].min_index == 1) {210 sci_phy_get_sas_address(&ihost->phys[0], &first_address);211 sci_phy_get_sas_address(&ihost->phys[2], &second_address);212 213 if (sci_sas_address_compare(first_address, second_address) == 0) {214 return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;215 }216 }217 218 /*219 * PE2 and PE3 are configured into a 2x1 ports make sure that the220 * SAS Address for PE1 and PE3 are different since they can not be221 * part of the same port. */222 if (port_agent->phy_valid_port_range[2].min_index == 2 &&223 port_agent->phy_valid_port_range[3].min_index == 3) {224 sci_phy_get_sas_address(&ihost->phys[1], &first_address);225 sci_phy_get_sas_address(&ihost->phys[3], &second_address);226 227 if (sci_sas_address_compare(first_address, second_address) == 0) {228 return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;229 }230 }231 232 return SCI_SUCCESS;233}234 235/*236 * ******************************************************************************237 * Manual port configuration agent routines238 * ****************************************************************************** */239 240/* verify all of the phys in the same port are using the same SAS address */241static enum sci_status242sci_mpc_agent_validate_phy_configuration(struct isci_host *ihost,243 struct sci_port_configuration_agent *port_agent)244{245 u32 phy_mask;246 u32 assigned_phy_mask;247 struct sci_sas_address sas_address;248 struct sci_sas_address phy_assigned_address;249 u8 port_index;250 u8 phy_index;251 252 assigned_phy_mask = 0;253 sas_address.high = 0;254 sas_address.low = 0;255 256 for (port_index = 0; port_index < SCI_MAX_PORTS; port_index++) {257 phy_mask = ihost->oem_parameters.ports[port_index].phy_mask;258 259 if (!phy_mask)260 continue;261 /*262 * Make sure that one or more of the phys were not already assinged to263 * a different port. */264 if ((phy_mask & ~assigned_phy_mask) == 0) {265 return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;266 }267 268 /* Find the starting phy index for this round through the loop */269 for (phy_index = 0; phy_index < SCI_MAX_PHYS; phy_index++) {270 if ((phy_mask & (1 << phy_index)) == 0)271 continue;272 sci_phy_get_sas_address(&ihost->phys[phy_index],273 &sas_address);274 275 /*276 * The phy_index can be used as the starting point for the277 * port range since the hardware starts all logical ports278 * the same as the PE index. */279 port_agent->phy_valid_port_range[phy_index].min_index = port_index;280 port_agent->phy_valid_port_range[phy_index].max_index = phy_index;281 282 if (phy_index != port_index) {283 return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;284 }285 286 break;287 }288 289 /*290 * See how many additional phys are being added to this logical port.291 * Note: We have not moved the current phy_index so we will actually292 * compare the startting phy with itself.293 * This is expected and required to add the phy to the port. */294 for (; phy_index < SCI_MAX_PHYS; phy_index++) {295 if ((phy_mask & (1 << phy_index)) == 0)296 continue;297 sci_phy_get_sas_address(&ihost->phys[phy_index],298 &phy_assigned_address);299 300 if (sci_sas_address_compare(sas_address, phy_assigned_address) != 0) {301 /*302 * The phy mask specified that this phy is part of the same port303 * as the starting phy and it is not so fail this configuration */304 return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;305 }306 307 port_agent->phy_valid_port_range[phy_index].min_index = port_index;308 port_agent->phy_valid_port_range[phy_index].max_index = phy_index;309 310 sci_port_add_phy(&ihost->ports[port_index],311 &ihost->phys[phy_index]);312 313 assigned_phy_mask |= (1 << phy_index);314 }315 316 }317 318 return sci_port_configuration_agent_validate_ports(ihost, port_agent);319}320 321static void mpc_agent_timeout(struct timer_list *t)322{323 u8 index;324 struct sci_timer *tmr = from_timer(tmr, t, timer);325 struct sci_port_configuration_agent *port_agent;326 struct isci_host *ihost;327 unsigned long flags;328 u16 configure_phy_mask;329 330 port_agent = container_of(tmr, typeof(*port_agent), timer);331 ihost = container_of(port_agent, typeof(*ihost), port_agent);332 333 spin_lock_irqsave(&ihost->scic_lock, flags);334 335 if (tmr->cancel)336 goto done;337 338 port_agent->timer_pending = false;339 340 /* Find the mask of phys that are reported read but as yet unconfigured into a port */341 configure_phy_mask = ~port_agent->phy_configured_mask & port_agent->phy_ready_mask;342 343 for (index = 0; index < SCI_MAX_PHYS; index++) {344 struct isci_phy *iphy = &ihost->phys[index];345 346 if (configure_phy_mask & (1 << index)) {347 port_agent->link_up_handler(ihost, port_agent,348 phy_get_non_dummy_port(iphy),349 iphy);350 }351 }352 353done:354 spin_unlock_irqrestore(&ihost->scic_lock, flags);355}356 357static void sci_mpc_agent_link_up(struct isci_host *ihost,358 struct sci_port_configuration_agent *port_agent,359 struct isci_port *iport,360 struct isci_phy *iphy)361{362 /* If the port is NULL then the phy was not assigned to a port.363 * This is because the phy was not given the same SAS Address as364 * the other PHYs in the port.365 */366 if (!iport)367 return;368 369 port_agent->phy_ready_mask |= (1 << iphy->phy_index);370 sci_port_link_up(iport, iphy);371 if ((iport->active_phy_mask & (1 << iphy->phy_index)))372 port_agent->phy_configured_mask |= (1 << iphy->phy_index);373}374 375/**376 * sci_mpc_agent_link_down()377 * @ihost: This is the controller object that receives the link down378 * notification.379 * @port_agent: This is the port configuration agent for the controller.380 * @iport: This is the port object associated with the phy. If the is no381 * associated port this is an NULL. The port is an invalid382 * handle only if the phy was never port of this port. This happens when383 * the phy is not broadcasting the same SAS address as the other phys in the384 * assigned port.385 * @iphy: This is the phy object which has gone link down.386 *387 * This function handles the manual port configuration link down notifications.388 * Since all ports and phys are associated at initialization time we just turn389 * around and notifiy the port object of the link down event. If this PHY is390 * not associated with a port there is no action taken. Is it possible to get a391 * link down notification from a phy that has no assocoated port?392 */393static void sci_mpc_agent_link_down(394 struct isci_host *ihost,395 struct sci_port_configuration_agent *port_agent,396 struct isci_port *iport,397 struct isci_phy *iphy)398{399 if (iport != NULL) {400 /*401 * If we can form a new port from the remainder of the phys402 * then we want to start the timer to allow the SCI User to403 * cleanup old devices and rediscover the port before404 * rebuilding the port with the phys that remain in the ready405 * state.406 */407 port_agent->phy_ready_mask &= ~(1 << iphy->phy_index);408 port_agent->phy_configured_mask &= ~(1 << iphy->phy_index);409 410 /*411 * Check to see if there are more phys waiting to be412 * configured into a port. If there are allow the SCI User413 * to tear down this port, if necessary, and then reconstruct414 * the port after the timeout.415 */416 if ((port_agent->phy_configured_mask == 0x0000) &&417 (port_agent->phy_ready_mask != 0x0000) &&418 !port_agent->timer_pending) {419 port_agent->timer_pending = true;420 421 sci_mod_timer(&port_agent->timer,422 SCIC_SDS_MPC_RECONFIGURATION_TIMEOUT);423 }424 425 sci_port_link_down(iport, iphy);426 }427}428 429/* verify phys are assigned a valid SAS address for automatic port430 * configuration mode.431 */432static enum sci_status433sci_apc_agent_validate_phy_configuration(struct isci_host *ihost,434 struct sci_port_configuration_agent *port_agent)435{436 u8 phy_index;437 u8 port_index;438 struct sci_sas_address sas_address;439 struct sci_sas_address phy_assigned_address;440 441 phy_index = 0;442 443 while (phy_index < SCI_MAX_PHYS) {444 port_index = phy_index;445 446 /* Get the assigned SAS Address for the first PHY on the controller. */447 sci_phy_get_sas_address(&ihost->phys[phy_index],448 &sas_address);449 450 while (++phy_index < SCI_MAX_PHYS) {451 sci_phy_get_sas_address(&ihost->phys[phy_index],452 &phy_assigned_address);453 454 /* Verify each of the SAS address are all the same for every PHY */455 if (sci_sas_address_compare(sas_address, phy_assigned_address) == 0) {456 port_agent->phy_valid_port_range[phy_index].min_index = port_index;457 port_agent->phy_valid_port_range[phy_index].max_index = phy_index;458 } else {459 port_agent->phy_valid_port_range[phy_index].min_index = phy_index;460 port_agent->phy_valid_port_range[phy_index].max_index = phy_index;461 break;462 }463 }464 }465 466 return sci_port_configuration_agent_validate_ports(ihost, port_agent);467}468 469/*470 * This routine will restart the automatic port configuration timeout471 * timer for the next time period. This could be caused by either a link472 * down event or a link up event where we can not yet tell to which a phy473 * belongs.474 */475static void sci_apc_agent_start_timer(struct sci_port_configuration_agent *port_agent,476 u32 timeout)477{478 port_agent->timer_pending = true;479 sci_mod_timer(&port_agent->timer, timeout);480}481 482static void sci_apc_agent_configure_ports(struct isci_host *ihost,483 struct sci_port_configuration_agent *port_agent,484 struct isci_phy *iphy,485 bool start_timer)486{487 u8 port_index;488 enum sci_status status;489 struct isci_port *iport;490 enum SCIC_SDS_APC_ACTIVITY apc_activity = SCIC_SDS_APC_SKIP_PHY;491 492 iport = sci_port_configuration_agent_find_port(ihost, iphy);493 494 if (iport) {495 if (sci_port_is_valid_phy_assignment(iport, iphy->phy_index))496 apc_activity = SCIC_SDS_APC_ADD_PHY;497 else498 apc_activity = SCIC_SDS_APC_SKIP_PHY;499 } else {500 /*501 * There is no matching Port for this PHY so lets search through the502 * Ports and see if we can add the PHY to its own port or maybe start503 * the timer and wait to see if a wider port can be made.504 *505 * Note the break when we reach the condition of the port id == phy id */506 for (port_index = port_agent->phy_valid_port_range[iphy->phy_index].min_index;507 port_index <= port_agent->phy_valid_port_range[iphy->phy_index].max_index;508 port_index++) {509 510 iport = &ihost->ports[port_index];511 512 /* First we must make sure that this PHY can be added to this Port. */513 if (sci_port_is_valid_phy_assignment(iport, iphy->phy_index)) {514 /*515 * Port contains a PHY with a greater PHY ID than the current516 * PHY that has gone link up. This phy can not be part of any517 * port so skip it and move on. */518 if (iport->active_phy_mask > (1 << iphy->phy_index)) {519 apc_activity = SCIC_SDS_APC_SKIP_PHY;520 break;521 }522 523 /*524 * We have reached the end of our Port list and have not found525 * any reason why we should not either add the PHY to the port526 * or wait for more phys to become active. */527 if (iport->physical_port_index == iphy->phy_index) {528 /*529 * The Port either has no active PHYs.530 * Consider that if the port had any active PHYs we would have531 * or active PHYs with532 * a lower PHY Id than this PHY. */533 if (apc_activity != SCIC_SDS_APC_START_TIMER) {534 apc_activity = SCIC_SDS_APC_ADD_PHY;535 }536 537 break;538 }539 540 /*541 * The current Port has no active PHYs and this PHY could be part542 * of this Port. Since we dont know as yet setup to start the543 * timer and see if there is a better configuration. */544 if (iport->active_phy_mask == 0) {545 apc_activity = SCIC_SDS_APC_START_TIMER;546 }547 } else if (iport->active_phy_mask != 0) {548 /*549 * The Port has an active phy and the current Phy can not550 * participate in this port so skip the PHY and see if551 * there is a better configuration. */552 apc_activity = SCIC_SDS_APC_SKIP_PHY;553 }554 }555 }556 557 /*558 * Check to see if the start timer operations should instead map to an559 * add phy operation. This is caused because we have been waiting to560 * add a phy to a port but could not becuase the automatic port561 * configuration engine had a choice of possible ports for the phy.562 * Since we have gone through a timeout we are going to restrict the563 * choice to the smallest possible port. */564 if (565 (start_timer == false)566 && (apc_activity == SCIC_SDS_APC_START_TIMER)567 ) {568 apc_activity = SCIC_SDS_APC_ADD_PHY;569 }570 571 switch (apc_activity) {572 case SCIC_SDS_APC_ADD_PHY:573 status = sci_port_add_phy(iport, iphy);574 575 if (status == SCI_SUCCESS) {576 port_agent->phy_configured_mask |= (1 << iphy->phy_index);577 }578 break;579 580 case SCIC_SDS_APC_START_TIMER:581 sci_apc_agent_start_timer(port_agent,582 SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION);583 break;584 585 case SCIC_SDS_APC_SKIP_PHY:586 default:587 /* do nothing the PHY can not be made part of a port at this time. */588 break;589 }590}591 592/**593 * sci_apc_agent_link_up - handle apc link up events594 * @ihost: This is the controller object that receives the link up595 * notification.596 * @port_agent: This is the port configuration agent for the controller.597 * @iport: This is the port object associated with the phy. If the is no598 * associated port this is an NULL.599 * @iphy: This is the phy object which has gone link up.600 *601 * This method handles the automatic port configuration for link up602 * notifications. Is it possible to get a link down notification from a phy603 * that has no assocoated port?604 */605static void sci_apc_agent_link_up(struct isci_host *ihost,606 struct sci_port_configuration_agent *port_agent,607 struct isci_port *iport,608 struct isci_phy *iphy)609{610 u8 phy_index = iphy->phy_index;611 612 if (!iport) {613 /* the phy is not the part of this port */614 port_agent->phy_ready_mask |= 1 << phy_index;615 sci_apc_agent_start_timer(port_agent,616 SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION);617 } else {618 /* the phy is already the part of the port */619 port_agent->phy_ready_mask |= 1 << phy_index;620 sci_port_link_up(iport, iphy);621 }622}623 624/**625 * sci_apc_agent_link_down()626 * @ihost: This is the controller object that receives the link down627 * notification.628 * @port_agent: This is the port configuration agent for the controller.629 * @iport: This is the port object associated with the phy. If the is no630 * associated port this is an NULL.631 * @iphy: This is the phy object which has gone link down.632 *633 * This method handles the automatic port configuration link down634 * notifications. not associated with a port there is no action taken. Is it635 * possible to get a link down notification from a phy that has no assocoated636 * port?637 */638static void sci_apc_agent_link_down(639 struct isci_host *ihost,640 struct sci_port_configuration_agent *port_agent,641 struct isci_port *iport,642 struct isci_phy *iphy)643{644 port_agent->phy_ready_mask &= ~(1 << iphy->phy_index);645 646 if (!iport)647 return;648 if (port_agent->phy_configured_mask & (1 << iphy->phy_index)) {649 enum sci_status status;650 651 status = sci_port_remove_phy(iport, iphy);652 653 if (status == SCI_SUCCESS)654 port_agent->phy_configured_mask &= ~(1 << iphy->phy_index);655 }656}657 658/* configure the phys into ports when the timer fires */659static void apc_agent_timeout(struct timer_list *t)660{661 u32 index;662 struct sci_timer *tmr = from_timer(tmr, t, timer);663 struct sci_port_configuration_agent *port_agent;664 struct isci_host *ihost;665 unsigned long flags;666 u16 configure_phy_mask;667 668 port_agent = container_of(tmr, typeof(*port_agent), timer);669 ihost = container_of(port_agent, typeof(*ihost), port_agent);670 671 spin_lock_irqsave(&ihost->scic_lock, flags);672 673 if (tmr->cancel)674 goto done;675 676 port_agent->timer_pending = false;677 678 configure_phy_mask = ~port_agent->phy_configured_mask & port_agent->phy_ready_mask;679 680 if (!configure_phy_mask)681 goto done;682 683 for (index = 0; index < SCI_MAX_PHYS; index++) {684 if ((configure_phy_mask & (1 << index)) == 0)685 continue;686 687 sci_apc_agent_configure_ports(ihost, port_agent,688 &ihost->phys[index], false);689 }690 691 if (is_controller_start_complete(ihost))692 sci_controller_transition_to_ready(ihost, SCI_SUCCESS);693 694done:695 spin_unlock_irqrestore(&ihost->scic_lock, flags);696}697 698/*699 * ******************************************************************************700 * Public port configuration agent routines701 * ****************************************************************************** */702 703/*704 * This method will construct the port configuration agent for operation. This705 * call is universal for both manual port configuration and automatic port706 * configuration modes.707 */708void sci_port_configuration_agent_construct(709 struct sci_port_configuration_agent *port_agent)710{711 u32 index;712 713 port_agent->phy_configured_mask = 0x00;714 port_agent->phy_ready_mask = 0x00;715 716 port_agent->link_up_handler = NULL;717 port_agent->link_down_handler = NULL;718 719 port_agent->timer_pending = false;720 721 for (index = 0; index < SCI_MAX_PORTS; index++) {722 port_agent->phy_valid_port_range[index].min_index = 0;723 port_agent->phy_valid_port_range[index].max_index = 0;724 }725}726 727bool is_port_config_apc(struct isci_host *ihost)728{729 return ihost->port_agent.link_up_handler == sci_apc_agent_link_up;730}731 732enum sci_status sci_port_configuration_agent_initialize(733 struct isci_host *ihost,734 struct sci_port_configuration_agent *port_agent)735{736 enum sci_status status;737 enum sci_port_configuration_mode mode;738 739 mode = ihost->oem_parameters.controller.mode_type;740 741 if (mode == SCIC_PORT_MANUAL_CONFIGURATION_MODE) {742 status = sci_mpc_agent_validate_phy_configuration(743 ihost, port_agent);744 745 port_agent->link_up_handler = sci_mpc_agent_link_up;746 port_agent->link_down_handler = sci_mpc_agent_link_down;747 748 sci_init_timer(&port_agent->timer, mpc_agent_timeout);749 } else {750 status = sci_apc_agent_validate_phy_configuration(751 ihost, port_agent);752 753 port_agent->link_up_handler = sci_apc_agent_link_up;754 port_agent->link_down_handler = sci_apc_agent_link_down;755 756 sci_init_timer(&port_agent->timer, apc_agent_timeout);757 }758 759 return status;760}761