]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/dev/isci/scil/scic_sds_port_configuration_agent.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / dev / isci / scil / scic_sds_port_configuration_agent.c
1 /*-
2  * This file is provided under a dual BSD/GPLv2 license.  When using or
3  * redistributing this file, you may do so under either license.
4  *
5  * GPL LICENSE SUMMARY
6  *
7  * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of version 2 of the GNU General Public License as
11  * published by the Free Software Foundation.
12  *
13  * This program is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
21  * The full GNU General Public License is included in this distribution
22  * in the file called LICENSE.GPL.
23  *
24  * BSD LICENSE
25  *
26  * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
27  * All rights reserved.
28  *
29  * Redistribution and use in source and binary forms, with or without
30  * modification, are permitted provided that the following conditions
31  * are met:
32  *
33  *   * Redistributions of source code must retain the above copyright
34  *     notice, this list of conditions and the following disclaimer.
35  *   * Redistributions in binary form must reproduce the above copyright
36  *     notice, this list of conditions and the following disclaimer in
37  *     the documentation and/or other materials provided with the
38  *     distribution.
39  *
40  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
41  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
42  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
43  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
44  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
45  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
46  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
47  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
48  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
49  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
50  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
51  */
52
53 #include <sys/cdefs.h>
54 __FBSDID("$FreeBSD$");
55
56 /**
57  * @file
58  *
59  * @brief This file contains the implementation for the public and protected
60  *        methods for the port configuration agent.
61  */
62
63 #include <dev/isci/scil/scic_controller.h>
64 #include <dev/isci/scil/scic_sds_logger.h>
65 #include <dev/isci/scil/scic_sds_controller.h>
66 #include <dev/isci/scil/scic_sds_port_configuration_agent.h>
67
68 #define SCIC_SDS_MPC_RECONFIGURATION_TIMEOUT    (10)
69 #define SCIC_SDS_APC_RECONFIGURATION_TIMEOUT    (10)
70 #define SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION  (250)
71
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
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
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
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
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 assinged to
348          // a different port.
349          if ((phy_mask & ~assigned_phy_mask) == 0)
350          {
351             return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
352          }
353
354          // Find the starting phy index for this round through the loop
355          for (phy_index = 0; phy_index < SCI_MAX_PHYS; phy_index++)
356          {
357             if ((1 << phy_index) & phy_mask)
358             {
359                scic_sds_phy_get_sas_address(
360                   &controller->phy_table[phy_index], &sas_address
361                );
362
363                // The phy_index can be used as the starting point for the
364                // port range since the hardware starts all logical ports
365                // the same as the PE index.
366                port_agent->phy_valid_port_range[phy_index].min_index = port_index;
367                port_agent->phy_valid_port_range[phy_index].max_index = phy_index;
368
369                if (phy_index != port_index)
370                {
371                   return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
372                }
373
374                break;
375             }
376          }
377
378          // See how many additional phys are being added to this logical port.
379          // Note: We have not moved the current phy_index so we will actually
380          //       compare the startting phy with itself.
381          //       This is expected and required to add the phy to the port.
382          while (phy_index < SCI_MAX_PHYS)
383          {
384             if ((1 << phy_index) & phy_mask)
385             {
386                scic_sds_phy_get_sas_address(
387                   &controller->phy_table[phy_index], &phy_assigned_address
388                );
389
390                if (sci_sas_address_compare(sas_address, phy_assigned_address) != 0)
391                {
392                   // The phy mask specified that this phy is part of the same port
393                   // as the starting phy and it is not so fail this configuration
394                   return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
395                }
396
397                port_agent->phy_valid_port_range[phy_index].min_index = port_index;
398                port_agent->phy_valid_port_range[phy_index].max_index = phy_index;
399
400                scic_sds_port_add_phy(
401                   &controller->port_table[port_index],
402                   &controller->phy_table[phy_index]
403                );
404
405                assigned_phy_mask |= (1 << phy_index);
406             }
407
408             phy_index++;
409          }
410       }
411    }
412
413    return scic_sds_port_configuration_agent_validate_ports(controller, port_agent);
414 }
415
416 /**
417  * This timer routine is used to allow the SCI User to rediscover or change
418  * device objects before a new series of link up notifications because a
419  * link down has allowed a better port configuration.
420  *
421  * @param[in] controller This is the core controller object which is used
422  *            to obtain the port configuration agent.
423  */
424 static
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
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
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
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
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
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 becuase the automatic port
776    // configuration engine had a choice of possible ports for the phy.
777    // Since we have gone through a timeout we are going to restrict the
778    // choice to the smallest possible port.
779    if (
780          (start_timer == FALSE)
781       && (apc_activity == SCIC_SDS_APC_START_TIMER)
782       )
783    {
784       apc_activity = SCIC_SDS_APC_ADD_PHY;
785    }
786
787    switch (apc_activity)
788    {
789    case SCIC_SDS_APC_ADD_PHY:
790       status = scic_sds_port_add_phy(port, phy);
791
792       if (status == SCI_SUCCESS)
793       {
794          port_agent->phy_configured_mask |= (1 << phy->phy_index);
795       }
796       break;
797
798    case SCIC_SDS_APC_START_TIMER:
799       scic_sds_apc_agent_start_timer(
800          controller, port_agent, phy, SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION
801       );
802       break;
803
804    case SCIC_SDS_APC_SKIP_PHY:
805    default:
806       // do nothing the PHY can not be made part of a port at this time.
807       break;
808    }
809 }
810
811 /**
812  * This method handles the automatic port configuration for link up notifications.
813  *
814  * @param[in] controller This is the controller object that receives the
815  *            link up notification.
816  * @param[in] port This is the port object associated with the phy.  If the
817  *            is no associated port this is an SCI_INVALID_HANDLE.
818  * @param[in] phy This is the phy object which has gone link up.
819  *
820  * @note Is it possible to get a link down notification from a phy that has
821  *       no assocoated port?
822  */
823 static
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 recieved
853       if (  SCI_BASE_PORT_STATE_RESETTING
854             == port->parent.state_machine.current_state_id
855          )
856       {
857          //notify the port that port needs to be ready
858          port_agent->phy_ready_mask |= (1 << scic_sds_phy_get_index(phy));
859          scic_sds_port_link_up(port, phy);
860       }
861       else
862       {
863          ASSERT (0);
864       }
865    }
866 }
867
868 /**
869  * This method handles the automatic port configuration link down notifications.
870  * If this PHY is * not associated with a port there is no action taken.
871  *
872  * @param[in] controller This is the controller object that receives the
873  *            link down notification.
874  * @param[in] port This is the port object associated with the phy.  If the
875  *            is no associated port this is an SCI_INVALID_HANDLE.
876  * @param[in] phy This is the phy object which has gone link down.
877  *
878  * @note Is it possible to get a link down notification from a phy that has
879  *       no assocoated port?
880  */
881 static
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
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  */
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 differntly
1014  *            for the automatic port configuration agent and the manual port
1015  *            configuration agent.
1016  *
1017  * @return
1018  */
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  */
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  */
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 }