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 } 1136