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