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 /**
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
72 enum 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 */
99 static
sci_sas_address_compare(SCI_SAS_ADDRESS_T address_one,SCI_SAS_ADDRESS_T address_two)100 S32 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 */
140 static
scic_sds_port_configuration_agent_find_port(SCIC_SDS_CONTROLLER_T * controller,SCIC_SDS_PHY_T * phy)141 SCIC_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 */
207 static
scic_sds_port_configuration_agent_validate_ports(SCIC_SDS_CONTROLLER_T * controller,SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent)208 SCI_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 */
317 static
scic_sds_mpc_agent_validate_phy_configuration(SCIC_SDS_CONTROLLER_T * controller,SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent)318 SCI_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 assigned 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 */
424 static
scic_sds_mpc_agent_timeout_handler(void * object)425 void 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 */
475 static
scic_sds_mpc_agent_link_up(SCIC_SDS_CONTROLLER_T * controller,SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent,SCIC_SDS_PORT_T * port,SCIC_SDS_PHY_T * phy)476 void 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 */
524 static
scic_sds_mpc_agent_link_down(SCIC_SDS_CONTROLLER_T * controller,SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent,SCIC_SDS_PORT_T * port,SCIC_SDS_PHY_T * phy)525 void 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 */
578 static
scic_sds_apc_agent_validate_phy_configuration(SCIC_SDS_CONTROLLER_T * controller,SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent)579 SCI_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 */
645 static
scic_sds_apc_agent_start_timer(SCIC_SDS_CONTROLLER_T * controller,SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent,SCIC_SDS_PHY_T * phy,U32 timeout)646 void 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 */
679 static
scic_sds_apc_agent_configure_ports(SCIC_SDS_CONTROLLER_T * controller,SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent,SCIC_SDS_PHY_T * phy,BOOL start_timer)680 void 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 because 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 */
823 static
scic_sds_apc_agent_link_up(SCIC_SDS_CONTROLLER_T * controller,SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent,SCIC_SDS_PORT_T * port,SCIC_SDS_PHY_T * phy)824 void 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 received
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 */
881 static
scic_sds_apc_agent_link_down(SCIC_SDS_CONTROLLER_T * controller,SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent,SCIC_SDS_PORT_T * port,SCIC_SDS_PHY_T * phy)882 void 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 */
920 static
scic_sds_apc_agent_timeout_handler(void * object)921 void 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 */
scic_sds_port_configuration_agent_construct(SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent)984 void 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 differently
1014 * for the automatic port configuration agent and the manual port
1015 * configuration agent.
1016 *
1017 * @return
1018 */
scic_sds_port_configuration_agent_initialize(SCIC_SDS_CONTROLLER_T * controller,SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent)1019 SCI_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 */
scic_sds_port_configuration_agent_destroy(SCIC_SDS_CONTROLLER_T * controller,SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent)1090 void 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 */
scic_sds_port_configuration_agent_release_resource(SCIC_SDS_CONTROLLER_T * controller,SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent)1115 void 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