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