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 }
Cache object: 3954af57acb5a76a6d9cf5d9fe26135b
|