xref: /freebsd/sys/dev/isci/scil/scic_sds_port.c (revision 22cf89c938886d14f5796fc49f9f020c23ea8eaf)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause OR GPL-2.0
3  *
4  * This file is provided under a dual BSD/GPLv2 license.  When using or
5  * redistributing this file, you may do so under either license.
6  *
7  * GPL LICENSE SUMMARY
8  *
9  * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of version 2 of the GNU General Public License as
13  * published by the Free Software Foundation.
14  *
15  * This program is distributed in the hope that it will be useful, but
16  * WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
23  * The full GNU General Public License is included in this distribution
24  * in the file called LICENSE.GPL.
25  *
26  * BSD LICENSE
27  *
28  * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
29  * All rights reserved.
30  *
31  * Redistribution and use in source and binary forms, with or without
32  * modification, are permitted provided that the following conditions
33  * are met:
34  *
35  *   * Redistributions of source code must retain the above copyright
36  *     notice, this list of conditions and the following disclaimer.
37  *   * Redistributions in binary form must reproduce the above copyright
38  *     notice, this list of conditions and the following disclaimer in
39  *     the documentation and/or other materials provided with the
40  *     distribution.
41  *
42  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
43  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
44  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
45  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
46  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
47  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
48  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
49  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
50  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
51  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
52  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
53  */
54 
55 #include <sys/cdefs.h>
56 /**
57  * @file
58  *
59  * @brief This file contains the implementation for the public and protected
60  *        methods for the SCIC_SDS_PORT object.
61  */
62 
63 #include <dev/isci/scil/scic_phy.h>
64 #include <dev/isci/scil/scic_port.h>
65 #include <dev/isci/scil/scic_controller.h>
66 #include <dev/isci/scil/scic_user_callback.h>
67 
68 #include <dev/isci/scil/scic_sds_controller.h>
69 #include <dev/isci/scil/scic_sds_port.h>
70 #include <dev/isci/scil/scic_sds_phy.h>
71 #include <dev/isci/scil/scic_sds_remote_device.h>
72 #include <dev/isci/scil/scic_sds_request.h>
73 #include <dev/isci/scil/scic_sds_port_registers.h>
74 #include <dev/isci/scil/scic_sds_logger.h>
75 #include <dev/isci/scil/scic_sds_phy_registers.h>
76 
77 #include <dev/isci/scil/intel_sas.h>
78 #include <dev/isci/scil/scic_sds_remote_node_context.h>
79 #include <dev/isci/scil/sci_util.h>
80 
81 #define SCIC_SDS_PORT_MIN_TIMER_COUNT  (SCI_MAX_PORTS)
82 #define SCIC_SDS_PORT_MAX_TIMER_COUNT  (SCI_MAX_PORTS)
83 
84 #define SCIC_SDS_PORT_HARD_RESET_TIMEOUT  (1000)
85 #define SCU_DUMMY_INDEX    (0xFFFF)
86 
87 /**
88  * This method will return a TRUE value if the specified phy can be assigned
89  * to this port
90  *
91  * The following is a list of phys for each port that are allowed:
92  * - Port 0 - 3 2 1 0
93  * - Port 1 -     1
94  * - Port 2 - 3 2
95  * - Port 3 - 3
96  *
97  * This method doesn't preclude all configurations.  It merely ensures
98  * that a phy is part of the allowable set of phy identifiers for
99  * that port.  For example, one could assign phy 3 to port 0 and no other
100  * phys.  Please refer to scic_sds_port_is_phy_mask_valid() for
101  * information regarding whether the phy_mask for a port can be supported.
102  *
103  * @param[in] this_port This is the port object to which the phy is being
104  *       assigned.
105  * @param[in] phy_index This is the phy index that is being assigned to the
106  *       port.
107  *
108  * @return BOOL
109  * @retval TRUE if this is a valid phy assignment for the port
110  * @retval FALSE if this is not a valid phy assignment for the port
111  */
112 BOOL scic_sds_port_is_valid_phy_assignment(
113    SCIC_SDS_PORT_T *this_port,
114    U32              phy_index
115 )
116 {
117    // Initialize to invalid value.
118    U32  existing_phy_index = SCI_MAX_PHYS;
119    U32  index;
120 
121    if ((this_port->physical_port_index == 1) && (phy_index != 1))
122    {
123       return FALSE;
124    }
125 
126    if (this_port->physical_port_index == 3 && phy_index != 3)
127    {
128       return FALSE;
129    }
130 
131    if (
132           (this_port->physical_port_index == 2)
133        && ((phy_index == 0) || (phy_index == 1))
134       )
135    {
136       return FALSE;
137    }
138 
139    for (index = 0; index < SCI_MAX_PHYS; index++)
140    {
141       if (  (this_port->phy_table[index] != NULL)
142          && (index != phy_index) )
143       {
144          existing_phy_index = index;
145       }
146    }
147 
148    // Ensure that all of the phys in the port are capable of
149    // operating at the same maximum link rate.
150    if (
151          (existing_phy_index < SCI_MAX_PHYS)
152       && (this_port->owning_controller->user_parameters.sds1.phys[
153              phy_index].max_speed_generation !=
154           this_port->owning_controller->user_parameters.sds1.phys[
155              existing_phy_index].max_speed_generation)
156       )
157       return FALSE;
158 
159    return TRUE;
160 }
161 
162 /**
163  * @brief This method requests a list (mask) of the phys contained in the
164  *        supplied SAS port.
165  *
166  * @param[in]  this_port a handle corresponding to the SAS port for which
167  *             to return the phy mask.
168  *
169  * @return Return a bit mask indicating which phys are a part of this port.
170  *         Each bit corresponds to a phy identifier (e.g. bit 0 = phy id 0).
171  */
172 U32 scic_sds_port_get_phys(
173    SCIC_SDS_PORT_T * this_port
174 )
175 {
176    U32 index;
177    U32 mask;
178 
179    SCIC_LOG_TRACE((
180       sci_base_object_get_logger(this_port),
181       SCIC_LOG_OBJECT_PORT,
182       "scic_sds_port_get_phys(0x%x) enter\n",
183       this_port
184    ));
185 
186    mask = 0;
187 
188    for (index = 0; index < SCI_MAX_PHYS; index++)
189    {
190       if (this_port->phy_table[index] != NULL)
191       {
192          mask |= (1 << index);
193       }
194    }
195 
196    return mask;
197 }
198 
199 /**
200  * This method will return a TRUE value if the port's phy mask can be
201  * supported by the SCU.
202  *
203  * The following is a list of valid PHY mask configurations for each
204  * port:
205  * - Port 0 - [[3  2] 1] 0
206  * - Port 1 -        [1]
207  * - Port 2 - [[3] 2]
208  * - Port 3 -  [3]
209  *
210  * @param[in] this_port This is the port object for which to determine
211  *       if the phy mask can be supported.
212  *
213  * @return This method returns a boolean indication specifying if the
214  *         phy mask can be supported.
215  * @retval TRUE if this is a valid phy assignment for the port
216  * @retval FALSE if this is not a valid phy assignment for the port
217  */
218 BOOL scic_sds_port_is_phy_mask_valid(
219    SCIC_SDS_PORT_T *this_port,
220    U32              phy_mask
221 )
222 {
223    if (this_port->physical_port_index == 0)
224    {
225       if (  ((phy_mask & 0x0F) == 0x0F)
226          || ((phy_mask & 0x03) == 0x03)
227          || ((phy_mask & 0x01) == 0x01)
228          || (phy_mask == 0) )
229          return TRUE;
230    }
231    else if (this_port->physical_port_index == 1)
232    {
233       if (  ((phy_mask & 0x02) == 0x02)
234          || (phy_mask == 0) )
235          return TRUE;
236    }
237    else if (this_port->physical_port_index == 2)
238    {
239       if (  ((phy_mask & 0x0C) == 0x0C)
240          || ((phy_mask & 0x04) == 0x04)
241          || (phy_mask == 0) )
242          return TRUE;
243    }
244    else if (this_port->physical_port_index == 3)
245    {
246       if (  ((phy_mask & 0x08) == 0x08)
247          || (phy_mask == 0) )
248          return TRUE;
249    }
250 
251    return FALSE;
252 }
253 
254 /**
255  * This method retrieves a currently active (i.e. connected) phy
256  * contained in the port.  Currently, the lowest order phy that is
257  * connected is returned.
258  *
259  * @param[in] this_port This parameter specifies the port from which
260  *            to return a connected phy.
261  *
262  * @return This method returns a pointer to a SCIS_SDS_PHY object.
263  * @retval NULL This value is returned if there are no currently
264  *         active (i.e. connected to a remote end point) phys
265  *         contained in the port.
266  * @retval All other values specify a SCIC_SDS_PHY object that is
267  *         active in the port.
268  */
269 SCIC_SDS_PHY_T * scic_sds_port_get_a_connected_phy(
270    SCIC_SDS_PORT_T *this_port
271 )
272 {
273    U32             index;
274    SCIC_SDS_PHY_T *phy;
275 
276    for (index = 0; index < SCI_MAX_PHYS; index++)
277    {
278       // Ensure that the phy is both part of the port and currently
279       // connected to the remote end-point.
280       phy = this_port->phy_table[index];
281       if (
282             (phy != NULL)
283          && scic_sds_port_active_phy(this_port, phy)
284          )
285       {
286          return phy;
287       }
288    }
289 
290    return NULL;
291 }
292 
293 /**
294  * This method attempts to make the assignment of the phy to the port.
295  * If successful the phy is assigned to the ports phy table.
296  *
297  * @param[in, out] port The port object to which the phy assignement
298  *                 is being made.
299  * @param[in, out] phy The phy which is being assigned to the port.
300  *
301  * @return BOOL
302  * @retval TRUE if the phy assignment can be made.
303  * @retval FALSE if the phy assignement can not be made.
304  *
305  * @note This is a functional test that only fails if the phy is currently
306  *       assigned to a different port.
307  */
308 SCI_STATUS scic_sds_port_set_phy(
309    SCIC_SDS_PORT_T *port,
310    SCIC_SDS_PHY_T  *phy
311 )
312 {
313    // Check to see if we can add this phy to a port
314    // that means that the phy is not part of a port and that the port does
315    // not already have a phy assigned to the phy index.
316    if (
317          (port->phy_table[phy->phy_index] == SCI_INVALID_HANDLE)
318       && (scic_sds_phy_get_port(phy) == SCI_INVALID_HANDLE)
319       && scic_sds_port_is_valid_phy_assignment(port, phy->phy_index)
320       )
321    {
322       // Phy is being added in the stopped state so we are in MPC mode
323       // make logical port index = physical port index
324       port->logical_port_index = port->physical_port_index;
325       port->phy_table[phy->phy_index] = phy;
326       scic_sds_phy_set_port(phy, port);
327 
328       return SCI_SUCCESS;
329    }
330 
331    return SCI_FAILURE;
332 }
333 
334 /**
335  * This method will clear the phy assigned to this port.  This method fails
336  * if this phy is not currently assigned to this port.
337  *
338  * @param[in, out] port The port from which the phy is being cleared.
339  * @param[in, out] phy The phy being cleared from the port.
340  *
341  * @return BOOL
342  * @retval TRUE if the phy is removed from the port.
343  * @retval FALSE if this phy is not assined to this port.
344  */
345 SCI_STATUS scic_sds_port_clear_phy(
346    SCIC_SDS_PORT_T *port,
347    SCIC_SDS_PHY_T  *phy
348 )
349 {
350    // Make sure that this phy is part of this port
351    if (
352            (port->phy_table[phy->phy_index] == phy)
353         && (scic_sds_phy_get_port(phy) == port)
354       )
355    {
356       // Yep it is assigned to this port so remove it
357       scic_sds_phy_set_port(
358          phy,
359          &scic_sds_port_get_controller(port)->port_table[SCI_MAX_PORTS]
360       );
361 
362       port->phy_table[phy->phy_index] = SCI_INVALID_HANDLE;
363 
364       return SCI_SUCCESS;
365    }
366 
367    return SCI_FAILURE;
368 }
369 
370 /**
371  * This method will add a PHY to the selected port.
372  *
373  * @param[in] this_port This parameter specifies the port in which the phy will
374  *            be added.
375  *
376  * @param[in] the_phy This parameter is the phy which is to be added to the
377  *            port.
378  *
379  * @return This method returns an SCI_STATUS.
380  * @retval SCI_SUCCESS the phy has been added to the port.
381  * @retval Any other status is failre to add the phy to the port.
382  */
383 SCI_STATUS scic_sds_port_add_phy(
384    SCIC_SDS_PORT_T * this_port,
385    SCIC_SDS_PHY_T  * the_phy
386 )
387 {
388    return this_port->state_handlers->parent.add_phy_handler(
389                                           &this_port->parent, &the_phy->parent);
390 }
391 
392 
393 /**
394  * This method will remove the PHY from the selected PORT.
395  *
396  * @param[in] this_port This parameter specifies the port in which the phy will
397  *            be added.
398  *
399  * @param[in] the_phy This parameter is the phy which is to be added to the
400  *            port.
401  *
402  * @return This method returns an SCI_STATUS.
403  * @retval SCI_SUCCESS the phy has been removed from the port.
404  * @retval Any other status is failre to add the phy to the port.
405  */
406 SCI_STATUS scic_sds_port_remove_phy(
407    SCIC_SDS_PORT_T * this_port,
408    SCIC_SDS_PHY_T  * the_phy
409 )
410 {
411    return this_port->state_handlers->parent.remove_phy_handler(
412                                           &this_port->parent, &the_phy->parent);
413 }
414 
415 /**
416  * @brief This method requests the SAS address for the supplied SAS port
417  *        from the SCI implementation.
418  *
419  * @param[in]  this_port a handle corresponding to the SAS port for which
420  *             to return the SAS address.
421  * @param[out] sas_address This parameter specifies a pointer to a SAS
422  *             address structure into which the core will copy the SAS
423  *             address for the port.
424  *
425  * @return none
426  */
427 void scic_sds_port_get_sas_address(
428    SCIC_SDS_PORT_T   * this_port,
429    SCI_SAS_ADDRESS_T * sas_address
430 )
431 {
432    U32 index;
433 
434    SCIC_LOG_TRACE((
435       sci_base_object_get_logger(this_port),
436       SCIC_LOG_OBJECT_PORT,
437       "scic_sds_port_get_sas_address(0x%x, 0x%x) enter\n",
438       this_port, sas_address
439    ));
440 
441    sas_address->high = 0;
442    sas_address->low  = 0;
443 
444    for (index = 0; index < SCI_MAX_PHYS; index++)
445    {
446       if (this_port->phy_table[index] != NULL)
447       {
448          scic_sds_phy_get_sas_address(this_port->phy_table[index], sas_address);
449       }
450    }
451 }
452 
453 /**
454  * @brief This method will indicate which protocols are supported by this
455  *        port.
456  *
457  * @param[in]  this_port a handle corresponding to the SAS port for which
458  *             to return the supported protocols.
459  * @param[out] protocols This parameter specifies a pointer to an IAF
460  *             protocol field structure into which the core will copy
461  *             the protocol values for the port.  The values are
462  *             returned as part of a bit mask in order to allow for
463  *             multi-protocol support.
464  *
465  * @return none
466  */
467 static
468 void scic_sds_port_get_protocols(
469    SCIC_SDS_PORT_T                            * this_port,
470    SCI_SAS_IDENTIFY_ADDRESS_FRAME_PROTOCOLS_T * protocols
471 )
472 {
473    U8 index;
474 
475    SCIC_LOG_TRACE((
476       sci_base_object_get_logger(this_port),
477       SCIC_LOG_OBJECT_PORT,
478       "scic_sds_port_get_protocols(0x%x, 0x%x) enter\n",
479       this_port, protocols
480    ));
481 
482    protocols->u.all = 0;
483 
484    for (index = 0; index < SCI_MAX_PHYS; index++)
485    {
486       if (this_port->phy_table[index] != NULL)
487       {
488          scic_sds_phy_get_protocols(this_port->phy_table[index], protocols);
489       }
490    }
491 }
492 
493 /**
494  * @brief This method requests the SAS address for the device directly
495  *        attached to this SAS port.
496  *
497  * @param[in]  this_port a handle corresponding to the SAS port for which
498  *             to return the SAS address.
499  * @param[out] sas_address This parameter specifies a pointer to a SAS
500  *             address structure into which the core will copy the SAS
501  *             address for the device directly attached to the port.
502  *
503  * @return none
504  */
505 void scic_sds_port_get_attached_sas_address(
506    SCIC_SDS_PORT_T   * this_port,
507    SCI_SAS_ADDRESS_T * sas_address
508 )
509 {
510    SCI_SAS_IDENTIFY_ADDRESS_FRAME_PROTOCOLS_T protocols;
511    SCIC_SDS_PHY_T  *phy;
512 
513    SCIC_LOG_TRACE((
514       sci_base_object_get_logger(this_port),
515       SCIC_LOG_OBJECT_PORT,
516       "scic_sds_port_get_attached_sas_address(0x%x, 0x%x) enter\n",
517       this_port, sas_address
518    ));
519 
520    // Ensure that the phy is both part of the port and currently
521    // connected to the remote end-point.
522    phy = scic_sds_port_get_a_connected_phy(this_port);
523    if (phy != NULL)
524    {
525       scic_sds_phy_get_attached_phy_protocols(phy, &protocols);
526 
527       if (!protocols.u.bits.stp_target)
528       {
529          scic_sds_phy_get_attached_sas_address(phy, sas_address);
530       }
531       else
532       {
533          scic_sds_phy_get_sas_address(phy, sas_address);
534          sas_address->low += phy->phy_index;
535 
536 		 //Need to make up attached STP device's SAS address in
537 		 //the same order as recorded IAF from SSP device.
538 		 sas_address->high = SCIC_SWAP_DWORD(sas_address->high);
539 		 sas_address->low = SCIC_SWAP_DWORD(sas_address->low);
540       }
541    }
542    else
543    {
544       sas_address->high = 0;
545       sas_address->low  = 0;
546    }
547 }
548 
549 /**
550  * @brief This method will indicate which protocols are supported by this
551  *        remote device.
552  *
553  * @param[in]  this_port a handle corresponding to the SAS port for which
554  *             to return the supported protocols.
555  * @param[out] protocols This parameter specifies a pointer to an IAF
556  *             protocol field structure into which the core will copy
557  *             the protocol values for the port.  The values are
558  *             returned as part of a bit mask in order to allow for
559  *             multi-protocol support.
560  *
561  * @return none
562  */
563 void scic_sds_port_get_attached_protocols(
564    SCIC_SDS_PORT_T                            * this_port,
565    SCI_SAS_IDENTIFY_ADDRESS_FRAME_PROTOCOLS_T * protocols
566 )
567 {
568    SCIC_SDS_PHY_T  *phy;
569 
570    SCIC_LOG_TRACE((
571       sci_base_object_get_logger(this_port),
572       SCIC_LOG_OBJECT_PORT,
573       "scic_sds_port_get_attached_protocols(0x%x, 0x%x) enter\n",
574       this_port, protocols
575    ));
576 
577    // Ensure that the phy is both part of the port and currently
578    // connected to the remote end-point.
579    phy = scic_sds_port_get_a_connected_phy(this_port);
580    if (phy != NULL)
581       scic_sds_phy_get_attached_phy_protocols(phy, protocols);
582    else
583       protocols->u.all = 0;
584 }
585 
586 /**
587  * @brief This method returns the amount of memory required for a port
588  *        object.
589  *
590  * @return U32
591  */
592 U32 scic_sds_port_get_object_size(void)
593 {
594    return sizeof(SCIC_SDS_PORT_T);
595 }
596 
597 /**
598  * @brief This method returns the minimum number of timers required for all
599  *        port objects.
600  *
601  * @return U32
602  */
603 U32 scic_sds_port_get_min_timer_count(void)
604 {
605    return SCIC_SDS_PORT_MIN_TIMER_COUNT;
606 }
607 
608 /**
609  * @brief This method returns the maximum number of timers required for all
610  *        port objects.
611  *
612  * @return U32
613  */
614 U32 scic_sds_port_get_max_timer_count(void)
615 {
616    return SCIC_SDS_PORT_MAX_TIMER_COUNT;
617 }
618 
619 #ifdef SCI_LOGGING
620 void scic_sds_port_initialize_state_logging(
621    SCIC_SDS_PORT_T *this_port
622 )
623 {
624    sci_base_state_machine_logger_initialize(
625       &this_port->parent.state_machine_logger,
626       &this_port->parent.state_machine,
627       &this_port->parent.parent,
628       scic_cb_logger_log_states,
629       "SCIC_SDS_PORT_T", "base state machine",
630       SCIC_LOG_OBJECT_PORT
631    );
632 
633    sci_base_state_machine_logger_initialize(
634       &this_port->ready_substate_machine_logger,
635       &this_port->ready_substate_machine,
636       &this_port->parent.parent,
637       scic_cb_logger_log_states,
638       "SCIC_SDS_PORT_T", "ready substate machine",
639       SCIC_LOG_OBJECT_PORT
640    );
641 }
642 #endif
643 
644 /**
645  * This routine will construct a dummy remote node context data structure
646  * This structure will be posted to the hardware to work around a scheduler
647  * error in the hardware.
648  *
649  * @param[in] this_port The logical port on which we need to create the
650  *            remote node context.
651  * @param[in] rni The remote node index for this remote node context.
652  *
653  * @return none
654  */
655 static
656 void scic_sds_port_construct_dummy_rnc(
657    SCIC_SDS_PORT_T *this_port,
658    U16              rni
659 )
660 {
661    SCU_REMOTE_NODE_CONTEXT_T * rnc;
662 
663    rnc = &(this_port->owning_controller->remote_node_context_table[rni]);
664 
665    memset(rnc, 0, sizeof(SCU_REMOTE_NODE_CONTEXT_T));
666 
667    rnc->ssp.remote_sas_address_hi = 0;
668    rnc->ssp.remote_sas_address_lo = 0;
669 
670    rnc->ssp.remote_node_index = rni;
671    rnc->ssp.remote_node_port_width = 1;
672    rnc->ssp.logical_port_index = this_port->physical_port_index;
673 
674    rnc->ssp.nexus_loss_timer_enable = FALSE;
675    rnc->ssp.check_bit = FALSE;
676    rnc->ssp.is_valid = TRUE;
677    rnc->ssp.is_remote_node_context = TRUE;
678    rnc->ssp.function_number = 0;
679    rnc->ssp.arbitration_wait_time = 0;
680 }
681 
682 /**
683  * This routine will construct a dummy task context data structure.  This
684  * structure will be posted to the hardwre to work around a scheduler error
685  * in the hardware.
686  *
687  * @param[in] this_port The logical port on which we need to create the
688  *            remote node context.
689  *            context.
690  * @param[in] tci The remote node index for this remote node context.
691  *
692  */
693 static
694 void scic_sds_port_construct_dummy_task(
695    SCIC_SDS_PORT_T *this_port,
696    U16              tci
697 )
698 {
699    SCU_TASK_CONTEXT_T * task_context;
700 
701    task_context = scic_sds_controller_get_task_context_buffer(this_port->owning_controller, tci);
702 
703    memset(task_context, 0, sizeof(SCU_TASK_CONTEXT_T));
704 
705    task_context->abort = 0;
706    task_context->priority = 0;
707    task_context->initiator_request = 1;
708    task_context->connection_rate = 1;
709    task_context->protocol_engine_index = 0;
710    task_context->logical_port_index = this_port->physical_port_index;
711    task_context->protocol_type = SCU_TASK_CONTEXT_PROTOCOL_SSP;
712    task_context->task_index = scic_sds_io_tag_get_index(tci);
713    task_context->valid = SCU_TASK_CONTEXT_VALID;
714    task_context->context_type = SCU_TASK_CONTEXT_TYPE;
715 
716    task_context->remote_node_index = this_port->reserved_rni;
717    task_context->command_code = 0;
718 
719    task_context->link_layer_control = 0;
720    task_context->do_not_dma_ssp_good_response = 1;
721    task_context->strict_ordering = 0;
722    task_context->control_frame = 0;
723    task_context->timeout_enable = 0;
724    task_context->block_guard_enable = 0;
725 
726    task_context->address_modifier = 0;
727 
728    task_context->task_phase = 0x01;
729 }
730 
731 /**
732  * This routine will free any allocated dummy resources for this port.
733  *
734  * @param[in, out] this_port The port on which the resources are being destroyed.
735  */
736 static
737 void scic_sds_port_destroy_dummy_resources(
738    SCIC_SDS_PORT_T * this_port
739 )
740 {
741    if (this_port->reserved_tci != SCU_DUMMY_INDEX)
742    {
743       scic_controller_free_io_tag(
744          this_port->owning_controller, this_port->reserved_tci
745       );
746    }
747 
748    if (this_port->reserved_rni != SCU_DUMMY_INDEX)
749    {
750       scic_sds_remote_node_table_release_remote_node_index(
751          &this_port->owning_controller->available_remote_nodes, 1, this_port->reserved_rni
752       );
753    }
754 
755    this_port->reserved_rni = SCU_DUMMY_INDEX;
756    this_port->reserved_tci = SCU_DUMMY_INDEX;
757 }
758 
759 /**
760  * @brief
761  *
762  * @param[in] this_port
763  * @param[in] port_index
764  * @param[in] owning_controller
765  */
766 void scic_sds_port_construct(
767    SCIC_SDS_PORT_T         *this_port,
768    U8                      port_index,
769    SCIC_SDS_CONTROLLER_T   *owning_controller
770 )
771 {
772    U32 index;
773 
774    sci_base_port_construct(
775       &this_port->parent,
776       sci_base_object_get_logger(owning_controller),
777       scic_sds_port_state_table
778    );
779 
780    sci_base_state_machine_construct(
781       scic_sds_port_get_ready_substate_machine(this_port),
782       &this_port->parent.parent,
783       scic_sds_port_ready_substate_table,
784       SCIC_SDS_PORT_READY_SUBSTATE_WAITING
785    );
786 
787    scic_sds_port_initialize_state_logging(this_port);
788 
789    this_port->logical_port_index  = SCIC_SDS_DUMMY_PORT;
790    this_port->physical_port_index = port_index;
791    this_port->active_phy_mask     = 0;
792    this_port->enabled_phy_mask    = 0;
793    this_port->owning_controller = owning_controller;
794 
795    this_port->started_request_count = 0;
796    this_port->assigned_device_count = 0;
797 
798    this_port->reserved_rni = SCU_DUMMY_INDEX;
799    this_port->reserved_tci = SCU_DUMMY_INDEX;
800 
801    this_port->timer_handle = SCI_INVALID_HANDLE;
802 
803    this_port->port_task_scheduler_registers = NULL;
804 
805    for (index = 0; index < SCI_MAX_PHYS; index++)
806    {
807       this_port->phy_table[index] = NULL;
808    }
809 }
810 
811 /**
812  * @brief This method performs initialization of the supplied port.
813  *        Initialization includes:
814  *        - state machine initialization
815  *        - member variable initialization
816  *        - configuring the phy_mask
817  *
818  * @param[in] this_port
819  * @param[in] transport_layer_registers
820  * @param[in] port_task_scheduler_registers
821  * @param[in] port_configuration_regsiter
822  *
823  * @return SCI_STATUS
824  * @retval SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION This value is
825  *         returned if the phy being added to the port
826  */
827 SCI_STATUS scic_sds_port_initialize(
828    SCIC_SDS_PORT_T *this_port,
829    void            *port_task_scheduler_registers,
830    void            *port_configuration_regsiter,
831    void            *viit_registers
832 )
833 {
834    this_port->port_task_scheduler_registers  = port_task_scheduler_registers;
835    this_port->port_pe_configuration_register = port_configuration_regsiter;
836    this_port->viit_registers                 = viit_registers;
837 
838    return SCI_SUCCESS;
839 }
840 
841 /**
842  * This method is the a general link up handler for the SCIC_SDS_PORT object.
843  * This function will determine if this SCIC_SDS_PHY can
844  * be assigned to this SCIC_SDS_PORT object. If the SCIC_SDS_PHY object can
845  * is not a valid PHY for this port then the function will notify the SCIC_USER.
846  * A PHY can only be part of a port if it's attached SAS ADDRESS is the same as
847  * all other PHYs in the same port.
848  *
849  * @param[in] this_port This is the SCIC_SDS_PORT object for which has a phy
850  *       that has gone link up.
851  * @param[in] the_phy This is the SCIC_SDS_PHY object that has gone link up.
852  * @param[in] do_notify_user This parameter specifies whether to inform
853  *            the user (via scic_cb_port_link_up()) as to the fact that
854  *            a new phy as become ready.
855  * @param[in] do_resume_phy This parameter specifies whether to resume the phy.
856  *            If this function is called from MPC mode, it will be always true.
857  *            for APC, this will be false, so that phys could be resumed later
858  *
859  * @return none
860  */
861 void scic_sds_port_general_link_up_handler(
862    SCIC_SDS_PORT_T * this_port,
863    SCIC_SDS_PHY_T  * the_phy,
864    BOOL              do_notify_user,
865    BOOL              do_resume_phy
866 )
867 {
868    SCI_SAS_ADDRESS_T  port_sas_address;
869    SCI_SAS_ADDRESS_T  phy_sas_address;
870 
871    scic_sds_port_get_attached_sas_address(this_port, &port_sas_address);
872    scic_sds_phy_get_attached_sas_address(the_phy, &phy_sas_address);
873 
874    // If the SAS address of the new phy matches the SAS address of
875    // other phys in the port OR this is the first phy in the port,
876    // then activate the phy and allow it to be used for operations
877    // in this port.
878    if (
879          (
880             (phy_sas_address.high == port_sas_address.high)
881          && (phy_sas_address.low  == port_sas_address.low )
882          )
883          || (this_port->active_phy_mask == 0)
884       )
885    {
886       scic_sds_port_activate_phy(this_port, the_phy, do_notify_user, do_resume_phy);
887 
888       if (this_port->parent.state_machine.current_state_id
889           == SCI_BASE_PORT_STATE_RESETTING)
890       {
891          sci_base_state_machine_change_state(
892             &this_port->parent.state_machine, SCI_BASE_PORT_STATE_READY
893          );
894       }
895    }
896    else
897    {
898       scic_sds_port_invalid_link_up(this_port, the_phy);
899    }
900 }
901 
902 // ---------------------------------------------------------------------------
903 
904 SCI_STATUS scic_port_add_phy(
905    SCI_PORT_HANDLE_T handle,
906    SCI_PHY_HANDLE_T phy
907 )
908 {
909    #if defined (SCI_LOGGING)
910    SCIC_SDS_PORT_T *this_port = (SCIC_SDS_PORT_T *)handle;
911    #endif // defined (SCI_LOGGING)
912 
913    SCIC_LOG_TRACE((
914       sci_base_object_get_logger(this_port),
915       SCIC_LOG_OBJECT_PORT,
916       "scic_port_add_phy(0x%x, 0x%x) enter\n",
917       handle, phy
918    ));
919 
920    SCIC_LOG_ERROR((
921       sci_base_object_get_logger(this_port),
922       SCIC_LOG_OBJECT_PORT,
923       "Interface function scic_port_add_phy() has been deprecated. "
924       "PORT configuration is handled through the OEM parameters.\n"
925    ));
926 
927    return SCI_FAILURE_ADDING_PHY_UNSUPPORTED;
928 
929 }
930 
931 // ---------------------------------------------------------------------------
932 
933 SCI_STATUS scic_port_remove_phy(
934    SCI_PORT_HANDLE_T handle,
935    SCI_PHY_HANDLE_T phy
936 )
937 {
938    #if defined (SCI_LOGGING)
939    SCIC_SDS_PORT_T *this_port = (SCIC_SDS_PORT_T *)handle;
940    #endif // defined (SCI_LOGGING)
941 
942    SCIC_LOG_TRACE((
943       sci_base_object_get_logger(this_port),
944       SCIC_LOG_OBJECT_PORT,
945       "scic_port_remove_phy(0x%x, 0x%x) enter\n",
946       handle, phy
947    ));
948 
949    SCIC_LOG_ERROR((
950       sci_base_object_get_logger(this_port),
951       SCIC_LOG_OBJECT_PORT,
952       "Interface function scic_port_remove_phy() has been deprecated. "
953       "PORT configuration is handled through the OEM parameters.\n"
954    ));
955 
956    return SCI_FAILURE_ADDING_PHY_UNSUPPORTED;
957 }
958 
959 // ---------------------------------------------------------------------------
960 
961 SCI_STATUS scic_port_get_properties(
962    SCI_PORT_HANDLE_T        port,
963    SCIC_PORT_PROPERTIES_T * properties
964 )
965 {
966    SCIC_SDS_PORT_T *this_port = (SCIC_SDS_PORT_T *)port;
967 
968    SCIC_LOG_TRACE((
969       sci_base_object_get_logger(this_port),
970       SCIC_LOG_OBJECT_PORT,
971       "scic_port_get_properties(0x%x, 0x%x) enter\n",
972       port, properties
973    ));
974 
975    if (
976          (port == SCI_INVALID_HANDLE)
977       || (this_port->logical_port_index == SCIC_SDS_DUMMY_PORT)
978       )
979    {
980       return SCI_FAILURE_INVALID_PORT;
981    }
982 
983    properties->index    = this_port->logical_port_index;
984    properties->phy_mask = scic_sds_port_get_phys(this_port);
985    scic_sds_port_get_sas_address(this_port, &properties->local.sas_address);
986    scic_sds_port_get_protocols(this_port, &properties->local.protocols);
987    scic_sds_port_get_attached_sas_address(this_port, &properties->remote.sas_address);
988    scic_sds_port_get_attached_protocols(this_port, &properties->remote.protocols);
989 
990    return SCI_SUCCESS;
991 }
992 
993 // ---------------------------------------------------------------------------
994 
995 SCI_STATUS scic_port_hard_reset(
996    SCI_PORT_HANDLE_T handle,
997    U32               reset_timeout
998 )
999 {
1000    SCIC_SDS_PORT_T * this_port = (SCIC_SDS_PORT_T *)handle;
1001 
1002    SCIC_LOG_TRACE((
1003       sci_base_object_get_logger(this_port),
1004       SCIC_LOG_OBJECT_PORT,
1005       "scic_port_hard_reset(0x%x, 0x%x) enter\n",
1006       handle, reset_timeout
1007    ));
1008 
1009    return this_port->state_handlers->parent.reset_handler(
1010                                        &this_port->parent,
1011                                        reset_timeout
1012                                      );
1013 }
1014 
1015 /**
1016  * This method assigns the direct attached device ID for this port.
1017  *
1018  * @param[in] this_port The port for which the direct attached device id is to
1019  *       be assigned.
1020  * @param[in] device_id The direct attached device ID to assign to the port.
1021  *       This will be the RNi for the device
1022  */
1023 void scic_sds_port_setup_transports(
1024    SCIC_SDS_PORT_T * this_port,
1025    U32               device_id
1026 )
1027 {
1028    U8 index;
1029 
1030    for (index = 0; index < SCI_MAX_PHYS; index++)
1031    {
1032       if (this_port->active_phy_mask & (1 << index))
1033       {
1034          scic_sds_phy_setup_transport(this_port->phy_table[index], device_id);
1035       }
1036    }
1037 }
1038 
1039 /**
1040  * This method will resume the phy which is already added in the port.
1041  * Activation includes:
1042  * - enabling the Protocol Engine in the silicon.
1043  * - update the reay mask.
1044  *
1045  * @param[in] this_port This is the port on which the phy should be enabled.
1046  * @return none
1047  */
1048 static
1049 void scic_sds_port_resume_phy(
1050    SCIC_SDS_PORT_T * this_port,
1051    SCIC_SDS_PHY_T  * the_phy
1052 )
1053 {
1054    scic_sds_phy_resume (the_phy);
1055    this_port->enabled_phy_mask |= 1 << the_phy->phy_index;
1056 }
1057 /**
1058  * This method will activate the phy in the port.
1059  * Activation includes:
1060  * - adding the phy to the port
1061  * - enabling the Protocol Engine in the silicon.
1062  * - notifying the user that the link is up.
1063  *
1064  * @param[in] this_port This is the port on which the phy should be enabled.
1065  * @param[in] the_phy This is the specific phy which to enable.
1066  * @param[in] do_notify_user This parameter specifies whether to inform
1067  *            the user (via scic_cb_port_link_up()) as to the fact that
1068  *            a new phy as become ready.
1069  * @param[in] do_resume_phy This parameter specifies whether to resume the phy.
1070  *            If this function is called from MPC mode, it will be always true.
1071  *            for APC, this will be false, so that phys could be resumed later
1072  *
1073 
1074  * @return none
1075  */
1076 void scic_sds_port_activate_phy(
1077    SCIC_SDS_PORT_T * this_port,
1078    SCIC_SDS_PHY_T  * the_phy,
1079    BOOL              do_notify_user,
1080    BOOL              do_resume_phy
1081 )
1082 {
1083    SCIC_SDS_CONTROLLER_T                      * controller;
1084    SCI_SAS_IDENTIFY_ADDRESS_FRAME_PROTOCOLS_T   protocols;
1085 
1086    SCIC_LOG_TRACE((
1087       sci_base_object_get_logger(this_port),
1088       SCIC_LOG_OBJECT_PORT,
1089       "scic_sds_port_activate_phy(0x%x,0x%x,0x%x) enter\n",
1090       this_port, the_phy, do_notify_user
1091    ));
1092 
1093    controller = scic_sds_port_get_controller(this_port);
1094    scic_sds_phy_get_attached_phy_protocols(the_phy, &protocols);
1095 
1096    // If this is sata port then the phy has already been resumed
1097    if (!protocols.u.bits.stp_target)
1098    {
1099       if (do_resume_phy == TRUE)
1100       {
1101          scic_sds_port_resume_phy(this_port, the_phy);
1102       }
1103    }
1104 
1105    this_port->active_phy_mask |= 1 << the_phy->phy_index;
1106 
1107    scic_sds_controller_clear_invalid_phy(controller, the_phy);
1108 
1109    if (do_notify_user == TRUE)
1110       scic_cb_port_link_up(this_port->owning_controller, this_port, the_phy);
1111 }
1112 
1113 /**
1114  * This method will deactivate the supplied phy in the port.
1115  *
1116  * @param[in] this_port This is the port on which the phy should be
1117  *            deactivated.
1118  * @param[in] the_phy This is the specific phy that is no longer
1119  *            active in the port.
1120  * @param[in] do_notify_user This parameter specifies whether to inform
1121  *            the user (via scic_cb_port_link_down()) as to the fact that
1122  *            a new phy as become ready.
1123  *
1124  * @return none
1125  */
1126 void scic_sds_port_deactivate_phy(
1127    SCIC_SDS_PORT_T * this_port,
1128    SCIC_SDS_PHY_T  * the_phy,
1129    BOOL              do_notify_user
1130 )
1131 {
1132    SCIC_LOG_TRACE((
1133       sci_base_object_get_logger(this_port),
1134       SCIC_LOG_OBJECT_PORT,
1135       "scic_sds_port_deactivate_phy(0x%x,0x%x,0x%x) enter\n",
1136       this_port, the_phy, do_notify_user
1137    ));
1138 
1139    this_port->active_phy_mask &= ~(1 << the_phy->phy_index);
1140    this_port->enabled_phy_mask  &= ~(1 << the_phy->phy_index);
1141 
1142    the_phy->max_negotiated_speed = SCI_SAS_NO_LINK_RATE;
1143 
1144    // Re-assign the phy back to the LP as if it were a narrow port for APC mode.
1145    // For MPC mode, the phy will remain in the port
1146    if (this_port->owning_controller->oem_parameters.sds1.controller.mode_type
1147        == SCIC_PORT_AUTOMATIC_CONFIGURATION_MODE)
1148    {
1149    SCU_PCSPExCR_WRITE(this_port, the_phy->phy_index, the_phy->phy_index);
1150    }
1151 
1152    if (do_notify_user == TRUE)
1153       scic_cb_port_link_down(this_port->owning_controller, this_port, the_phy);
1154 }
1155 
1156 /**
1157  * This method will disable the phy and report that the phy is not valid for this
1158  * port object.
1159  *
1160  * @param[in] this_port This is the port on which the phy should be disabled.
1161  * @param[in] the_phy This is the specific phy which to disabled.
1162  *
1163  * @return None
1164  */
1165 void scic_sds_port_invalid_link_up(
1166    SCIC_SDS_PORT_T * this_port,
1167    SCIC_SDS_PHY_T  * the_phy
1168 )
1169 {
1170    SCIC_SDS_CONTROLLER_T * controller = scic_sds_port_get_controller(this_port);
1171 
1172    // Check to see if we have alreay reported this link as bad and if not go
1173    // ahead and tell the SCI_USER that we have discovered an invalid link.
1174    if ((controller->invalid_phy_mask & (1 << the_phy->phy_index)) == 0)
1175    {
1176       scic_sds_controller_set_invalid_phy(controller, the_phy);
1177 
1178       scic_cb_port_invalid_link_up(controller, this_port, the_phy);
1179    }
1180 }
1181 
1182 /**
1183  * @brief This method returns FALSE if the port only has a single phy object
1184  *        assigned.  If there are no phys or more than one phy then the
1185  *        method will return TRUE.
1186  *
1187  * @param[in] this_port The port for which the wide port condition is to be
1188  *            checked.
1189  *
1190  * @return BOOL
1191  * @retval TRUE Is returned if this is a wide ported port.
1192  * @retval FALSE Is returned if this is a narrow port.
1193  */
1194 static
1195 BOOL scic_sds_port_is_wide(
1196    SCIC_SDS_PORT_T *this_port
1197 )
1198 {
1199    U32 index;
1200    U32 phy_count = 0;
1201 
1202    for (index = 0; index < SCI_MAX_PHYS; index++)
1203    {
1204       if (this_port->phy_table[index] != NULL)
1205       {
1206          phy_count++;
1207       }
1208    }
1209 
1210    return (phy_count != 1);
1211 }
1212 
1213 /**
1214  * @brief This method is called by the PHY object when the link is detected.
1215  *        if the port wants the PHY to continue on to the link up state then
1216  *        the port layer must return TRUE.  If the port object returns FALSE
1217  *        the phy object must halt its attempt to go link up.
1218  *
1219  * @param[in] this_port The port associated with the phy object.
1220  * @param[in] the_phy The phy object that is trying to go link up.
1221  *
1222  * @return TRUE if the phy object can continue to the link up condition.
1223  * @retval TRUE Is returned if this phy can continue to the ready state.
1224  * @retval FALSE Is returned if can not continue on to the ready state.
1225  *
1226  * @note This notification is in place for wide ports and direct attached
1227  *       phys.  Since there are no wide ported SATA devices this could
1228  *       become an invalid port configuration.
1229  */
1230 BOOL scic_sds_port_link_detected(
1231    SCIC_SDS_PORT_T *this_port,
1232    SCIC_SDS_PHY_T  *the_phy
1233 )
1234 {
1235    SCI_SAS_IDENTIFY_ADDRESS_FRAME_PROTOCOLS_T protocols;
1236 
1237    scic_sds_phy_get_attached_phy_protocols(the_phy, &protocols);
1238 
1239    if (
1240          (this_port->logical_port_index != SCIC_SDS_DUMMY_PORT)
1241       && (protocols.u.bits.stp_target)
1242       )
1243    {
1244       if (scic_sds_port_is_wide(this_port))
1245       {
1246          //direct attached Sata phy cannot be in wide port.
1247          scic_sds_port_invalid_link_up( this_port, the_phy);
1248       return FALSE;
1249    }
1250       else
1251       {
1252          SCIC_SDS_PORT_T *destination_port = &(this_port->owning_controller->port_table[the_phy->phy_index]);
1253 
1254          //add the phy to the its logical port for direct attached SATA. The phy will be added
1255          //to port whose port_index will be the phy_index.
1256          SCU_PCSPExCR_WRITE( destination_port, the_phy->phy_index, the_phy->phy_index);
1257       }
1258    }
1259 
1260    return TRUE;
1261 }
1262 
1263 /**
1264  * @brief This method is the entry point for the phy to inform
1265  *        the port that it is now in a ready state
1266  *
1267  * @param[in] this_port
1268  * @param[in] phy
1269  */
1270 void scic_sds_port_link_up(
1271    SCIC_SDS_PORT_T *this_port,
1272    SCIC_SDS_PHY_T  *the_phy
1273 )
1274 {
1275    the_phy->is_in_link_training = FALSE;
1276 
1277    this_port->state_handlers->link_up_handler(this_port, the_phy);
1278 }
1279 
1280 /**
1281  * @brief This method is the entry point for the phy to inform
1282  *        the port that it is no longer in a ready state
1283  *
1284  * @param[in] this_port
1285  * @param[in] phy
1286  */
1287 void scic_sds_port_link_down(
1288    SCIC_SDS_PORT_T *this_port,
1289    SCIC_SDS_PHY_T  *the_phy
1290 )
1291 {
1292    this_port->state_handlers->link_down_handler(this_port, the_phy);
1293 }
1294 
1295 /**
1296  * @brief This method is called to start an IO request on this port.
1297  *
1298  * @param[in] this_port
1299  * @param[in] the_device
1300  * @param[in] the_io_request
1301  *
1302  * @return SCI_STATUS
1303  */
1304 SCI_STATUS scic_sds_port_start_io(
1305    SCIC_SDS_PORT_T          *this_port,
1306    SCIC_SDS_REMOTE_DEVICE_T *the_device,
1307    SCIC_SDS_REQUEST_T       *the_io_request
1308 )
1309 {
1310    return this_port->state_handlers->start_io_handler(
1311                                        this_port, the_device, the_io_request);
1312 }
1313 
1314 /**
1315  * @brief This method is called to complete an IO request to the port.
1316  *
1317  * @param[in] this_port
1318  * @param[in] the_device
1319  * @param[in] the_io_request
1320  *
1321  * @return SCI_STATUS
1322  */
1323 SCI_STATUS scic_sds_port_complete_io(
1324    SCIC_SDS_PORT_T          *this_port,
1325    SCIC_SDS_REMOTE_DEVICE_T *the_device,
1326    SCIC_SDS_REQUEST_T       *the_io_request
1327 )
1328 {
1329    return this_port->state_handlers->complete_io_handler(
1330                                        this_port, the_device, the_io_request);
1331 }
1332 
1333 /**
1334  * @brief This method is provided to timeout requests for port operations.
1335  *        Mostly its for the port reset operation.
1336  *
1337  * @param[in] port This is the parameter or cookie value that is provided
1338  *       to the timer construct operation.
1339  */
1340 void scic_sds_port_timeout_handler(
1341    void *port
1342 )
1343 {
1344    U32 current_state;
1345    SCIC_SDS_PORT_T * this_port;
1346 
1347    this_port = (SCIC_SDS_PORT_T *)port;
1348    current_state = sci_base_state_machine_get_state(
1349                            &this_port->parent.state_machine);
1350 
1351    if (current_state == SCI_BASE_PORT_STATE_RESETTING)
1352    {
1353       // if the port is still in the resetting state then the timeout fired
1354       // before the reset completed.
1355       sci_base_state_machine_change_state(
1356          &this_port->parent.state_machine,
1357          SCI_BASE_PORT_STATE_FAILED
1358       );
1359    }
1360    else if (current_state == SCI_BASE_PORT_STATE_STOPPED)
1361    {
1362       // if the port is stopped then the start request failed
1363       // In this case stay in the stopped state.
1364       SCIC_LOG_ERROR((
1365          sci_base_object_get_logger(this_port),
1366          SCIC_LOG_OBJECT_PORT,
1367          "SCIC Port 0x%x failed to stop before tiemout.\n",
1368          this_port
1369       ));
1370    }
1371    else if (current_state == SCI_BASE_PORT_STATE_STOPPING)
1372    {
1373       // if the port is still stopping then the stop has not completed
1374       scic_cb_port_stop_complete(
1375          scic_sds_port_get_controller(this_port),
1376          port,
1377          SCI_FAILURE_TIMEOUT
1378       );
1379    }
1380    else
1381    {
1382       // The port is in the ready state and we have a timer reporting a timeout
1383       // this should not happen.
1384       SCIC_LOG_ERROR((
1385          sci_base_object_get_logger(this_port),
1386          SCIC_LOG_OBJECT_PORT,
1387          "SCIC Port 0x%x is processing a timeout operation in state %d.\n",
1388          this_port, current_state
1389       ));
1390    }
1391 }
1392 
1393 // ---------------------------------------------------------------------------
1394 
1395 #ifdef SCIC_DEBUG_ENABLED
1396 void scic_sds_port_decrement_request_count(
1397    SCIC_SDS_PORT_T *this_port
1398 )
1399 {
1400    if (this_port->started_request_count == 0)
1401    {
1402       SCIC_LOG_WARNING((
1403          sci_base_object_get_logger(this_port),
1404          SCIC_LOG_OBJECT_PORT,
1405          "SCIC Port object requested to decrement started io count past zero.\n"
1406       ));
1407    }
1408    else
1409    {
1410       this_port->started_request_count--;
1411    }
1412 }
1413 #endif
1414 
1415 /**
1416  * @brief This function updates the hardwares VIIT entry for this port.
1417  *
1418  * @param[in] this_port
1419  */
1420 void scic_sds_port_update_viit_entry(
1421    SCIC_SDS_PORT_T *this_port
1422 )
1423 {
1424    SCI_SAS_ADDRESS_T sas_address;
1425 
1426    scic_sds_port_get_sas_address(this_port, &sas_address);
1427 
1428    scu_port_viit_register_write(
1429       this_port, initiator_sas_address_hi, sas_address.high);
1430 
1431    scu_port_viit_register_write(
1432       this_port, initiator_sas_address_lo, sas_address.low);
1433 
1434    // This value get cleared just in case its not already cleared
1435    scu_port_viit_register_write(
1436       this_port, reserved, 0);
1437 
1438 
1439    // We are required to update the status register last
1440    scu_port_viit_register_write(
1441       this_port, status, (
1442            SCU_VIIT_ENTRY_ID_VIIT
1443          | SCU_VIIT_IPPT_INITIATOR
1444          | ((1UL << this_port->physical_port_index) << SCU_VIIT_ENTRY_LPVIE_SHIFT)
1445          | SCU_VIIT_STATUS_ALL_VALID
1446          )
1447    );
1448 }
1449 
1450 /**
1451  * @brief This method returns the maximum allowed speed for data transfers
1452  *        on this port.  This maximum allowed speed evaluates to the maximum
1453  *        speed of the slowest phy in the port.
1454  *
1455  * @param[in] this_port This parameter specifies the port for which to
1456  *            retrieve the maximum allowed speed.
1457  *
1458  * @return This method returns the maximum negotiated speed of the slowest
1459  *         phy in the port.
1460  */
1461 SCI_SAS_LINK_RATE scic_sds_port_get_max_allowed_speed(
1462    SCIC_SDS_PORT_T * this_port
1463 )
1464 {
1465    U16                index             = 0;
1466    SCI_SAS_LINK_RATE  max_allowed_speed = SCI_SAS_600_GB;
1467    SCIC_SDS_PHY_T   * phy               = NULL;
1468 
1469    // Loop through all of the phys in this port and find the phy with the
1470    // lowest maximum link rate.
1471    for (index = 0; index < SCI_MAX_PHYS; index++)
1472    {
1473       phy = this_port->phy_table[index];
1474       if (
1475             (phy != NULL)
1476          && (scic_sds_port_active_phy(this_port, phy) == TRUE)
1477          && (phy->max_negotiated_speed < max_allowed_speed)
1478          )
1479          max_allowed_speed = phy->max_negotiated_speed;
1480    }
1481 
1482    return max_allowed_speed;
1483 }
1484 
1485 
1486 /**
1487  * @brief This method passes the event to core user.
1488  * @param[in] this_port The port that a BCN happens.
1489  * @param[in] this_phy  The phy that receives BCN.
1490  *
1491  * @return none
1492  */
1493 void scic_sds_port_broadcast_change_received(
1494    SCIC_SDS_PORT_T * this_port,
1495    SCIC_SDS_PHY_T * this_phy
1496 )
1497 {
1498    //notify the user.
1499    scic_cb_port_bc_change_primitive_recieved(
1500       this_port->owning_controller, this_port, this_phy
1501    );
1502 }
1503 
1504 
1505 /**
1506  * @brief This API methhod enables the broadcast change notification from
1507  *        underneath hardware.
1508  * @param[in] this_port The port that a BCN had been disabled from.
1509  *
1510  * @return none
1511  */
1512 void scic_port_enable_broadcast_change_notification(
1513    SCI_PORT_HANDLE_T  port
1514 )
1515 {
1516    SCIC_SDS_PORT_T * this_port = (SCIC_SDS_PORT_T *)port;
1517    SCIC_SDS_PHY_T * phy;
1518    U32 register_value;
1519    U8 index;
1520 
1521    // Loop through all of the phys to enable BCN.
1522    for (index = 0; index < SCI_MAX_PHYS; index++)
1523    {
1524       phy = this_port->phy_table[index];
1525       if ( phy != NULL)
1526       {
1527          register_value = SCU_SAS_LLCTL_READ(phy);
1528 
1529          // clear the bit by writing 1.
1530          SCU_SAS_LLCTL_WRITE(phy, register_value);
1531       }
1532    }
1533 }
1534 
1535 /**
1536  * @brief This method release resources in for a scic port.
1537  *
1538  * @param[in] controller This parameter specifies the core controller, one of
1539  *            its phy's resources are to be released.
1540  * @param[in] this_port This parameter specifies the port whose resource is to
1541  *            be released.
1542  */
1543 void scic_sds_port_release_resource(
1544    SCIC_SDS_CONTROLLER_T * controller,
1545    SCIC_SDS_PORT_T *this_port
1546 )
1547 {
1548    SCIC_LOG_TRACE((
1549       sci_base_object_get_logger(this_port),
1550       SCIC_LOG_OBJECT_PORT,
1551       "scic_sds_port_release_resource(0x%x, 0x%x)\n",
1552       controller, this_port
1553    ));
1554 
1555    //Currently, the only resource to be released is a timer.
1556    if (this_port->timer_handle != NULL)
1557    {
1558       scic_cb_timer_destroy(controller, this_port->timer_handle);
1559       this_port->timer_handle = NULL;
1560    }
1561 }
1562 
1563 
1564 //******************************************************************************
1565 //* PORT STATE MACHINE
1566 //******************************************************************************
1567 
1568 //***************************************************************************
1569 //*  DEFAULT HANDLERS
1570 //***************************************************************************
1571 
1572 /**
1573  * This is the default method for port a start request.  It will report a
1574  * warning and exit.
1575  *
1576  * @param[in] port This is the SCI_BASE_PORT object which is cast into a
1577  *       SCIC_SDS_PORT object.
1578  *
1579  * @return SCI_STATUS
1580  * @retval SCI_FAILURE_INVALID_STATE
1581  */
1582 SCI_STATUS scic_sds_port_default_start_handler(
1583    SCI_BASE_PORT_T *port
1584 )
1585 {
1586    SCIC_LOG_WARNING((
1587       sci_base_object_get_logger((SCIC_SDS_PORT_T *)port),
1588       SCIC_LOG_OBJECT_PORT,
1589       "SCIC Port 0x%08x requested to start while in invalid state %d\n",
1590       port,
1591       sci_base_state_machine_get_state(
1592          scic_sds_port_get_base_state_machine((SCIC_SDS_PORT_T *)port))
1593    ));
1594 
1595    return SCI_FAILURE_INVALID_STATE;
1596 }
1597 
1598 /**
1599  * This is the default method for a port stop request.  It will report a
1600  * warning and exit.
1601  *
1602  * @param[in] port This is the SCI_BASE_PORT object which is cast into a
1603  *       SCIC_SDS_PORT object.
1604  *
1605  * @return SCI_STATUS
1606  * @retval SCI_FAILURE_INVALID_STATE
1607  */
1608 SCI_STATUS scic_sds_port_default_stop_handler(
1609    SCI_BASE_PORT_T *port
1610 )
1611 {
1612    SCIC_LOG_WARNING((
1613       sci_base_object_get_logger((SCIC_SDS_PORT_T *)port),
1614       SCIC_LOG_OBJECT_PORT,
1615       "SCIC Port 0x%08x requested to stop while in invalid state %d\n",
1616       port,
1617       sci_base_state_machine_get_state(
1618          scic_sds_port_get_base_state_machine((SCIC_SDS_PORT_T *)port))
1619    ));
1620 
1621    return SCI_FAILURE_INVALID_STATE;
1622 }
1623 
1624 /**
1625  * This is the default method for a port destruct request.  It will report a
1626  * warning and exit.
1627  *
1628  * @param[in] port This is the SCI_BASE_PORT object which is cast into a
1629  *       SCIC_SDS_PORT object.
1630  *
1631  * @return SCI_STATUS
1632  * @retval SCI_FAILURE_INVALID_STATE
1633  */
1634 SCI_STATUS scic_sds_port_default_destruct_handler(
1635    SCI_BASE_PORT_T *port
1636 )
1637 {
1638    SCIC_LOG_WARNING((
1639       sci_base_object_get_logger((SCIC_SDS_PORT_T *)port),
1640       SCIC_LOG_OBJECT_PORT,
1641       "SCIC Port 0x%08x requested to destruct while in invalid state %d\n",
1642       port,
1643       sci_base_state_machine_get_state(
1644          scic_sds_port_get_base_state_machine((SCIC_SDS_PORT_T *)port))
1645    ));
1646 
1647    return SCI_FAILURE_INVALID_STATE;
1648 }
1649 
1650 /**
1651  * This is the default method for a port reset request.  It will report a
1652  * warning and exit.
1653  *
1654  * @param[in] port This is the SCI_BASE_PORT object which is cast into a
1655  *       SCIC_SDS_PORT object.
1656  * @param[in] timeout This is the timeout for the reset request to complete.
1657  *
1658  * @return SCI_STATUS
1659  * @retval SCI_FAILURE_INVALID_STATE
1660  */
1661 SCI_STATUS scic_sds_port_default_reset_handler(
1662    SCI_BASE_PORT_T * port,
1663    U32               timeout
1664 )
1665 {
1666    SCIC_LOG_WARNING((
1667       sci_base_object_get_logger((SCIC_SDS_PORT_T *)port),
1668       SCIC_LOG_OBJECT_PORT,
1669       "SCIC Port 0x%08x requested to reset while in invalid state %d\n",
1670       port,
1671       sci_base_state_machine_get_state(
1672          scic_sds_port_get_base_state_machine((SCIC_SDS_PORT_T *)port))
1673    ));
1674 
1675    return SCI_FAILURE_INVALID_STATE;
1676 }
1677 
1678 /**
1679  * This is the default method for a port add phy request.  It will report a
1680  * warning and exit.
1681  *
1682  * @param[in] port This is the SCI_BASE_PORT object which is cast into a
1683  *       SCIC_SDS_PORT object.
1684  *
1685  * @return SCI_STATUS
1686  * @retval SCI_FAILURE_INVALID_STATE
1687  */
1688 SCI_STATUS scic_sds_port_default_add_phy_handler(
1689    SCI_BASE_PORT_T *port,
1690    SCI_BASE_PHY_T  *phy
1691 )
1692 {
1693    SCIC_LOG_WARNING((
1694       sci_base_object_get_logger((SCIC_SDS_PORT_T *)port),
1695       SCIC_LOG_OBJECT_PORT,
1696       "SCIC Port 0x%08x requested to add phy 0x%08x while in invalid state %d\n",
1697       port, phy,
1698       sci_base_state_machine_get_state(
1699          scic_sds_port_get_base_state_machine((SCIC_SDS_PORT_T *)port))
1700    ));
1701 
1702    return SCI_FAILURE_INVALID_STATE;
1703 }
1704 
1705 /**
1706  * This is the default method for a port remove phy request.  It will report a
1707  * warning and exit.
1708  *
1709  * @param[in] port This is the SCI_BASE_PORT object which is cast into a
1710  *       SCIC_SDS_PORT object.
1711  *
1712  * @return SCI_STATUS
1713  * @retval SCI_FAILURE_INVALID_STATE
1714  */
1715 SCI_STATUS scic_sds_port_default_remove_phy_handler(
1716    SCI_BASE_PORT_T *port,
1717    SCI_BASE_PHY_T  *phy
1718 )
1719 {
1720    SCIC_LOG_WARNING((
1721       sci_base_object_get_logger((SCIC_SDS_PORT_T *)port),
1722       SCIC_LOG_OBJECT_PORT,
1723       "SCIC Port 0x%08x requested to remove phy 0x%08x while in invalid state %d\n",
1724       port, phy,
1725       sci_base_state_machine_get_state(
1726          scic_sds_port_get_base_state_machine((SCIC_SDS_PORT_T *)port))
1727    ));
1728 
1729    return SCI_FAILURE_INVALID_STATE;
1730 }
1731 
1732 /**
1733  * This is the default method for a port unsolicited frame request.  It will
1734  * report a warning and exit.
1735  *
1736  * @param[in] port This is the SCI_BASE_PORT object which is cast into a
1737  *       SCIC_SDS_PORT object.
1738  *
1739  * @return SCI_STATUS
1740  * @retval SCI_FAILURE_INVALID_STATE
1741  *
1742  * @todo Is it even possible to receive an unsolicited frame directed to a
1743  *       port object?  It seems possible if we implementing virtual functions
1744  *       but until then?
1745  */
1746 SCI_STATUS scic_sds_port_default_frame_handler(
1747    SCIC_SDS_PORT_T * port,
1748    U32               frame_index
1749 )
1750 {
1751    SCIC_SDS_PORT_T *this_port = (SCIC_SDS_PORT_T *)port;
1752 
1753    SCIC_LOG_WARNING((
1754       sci_base_object_get_logger(this_port),
1755       SCIC_LOG_OBJECT_PORT,
1756       "SCIC Port 0x%08x requested to process frame %d while in invalid state %d\n",
1757       port, frame_index,
1758       sci_base_state_machine_get_state(
1759          scic_sds_port_get_base_state_machine(this_port))
1760    ));
1761 
1762    scic_sds_controller_release_frame(
1763       scic_sds_port_get_controller(this_port), frame_index
1764    );
1765 
1766    return SCI_FAILURE_INVALID_STATE;
1767 }
1768 
1769 /**
1770  * This is the default method for a port event request.  It will report a
1771  * warning and exit.
1772  *
1773  * @param[in] port This is the SCI_BASE_PORT object which is cast into a
1774  *       SCIC_SDS_PORT object.
1775  *
1776  * @return SCI_STATUS
1777  * @retval SCI_FAILURE_INVALID_STATE
1778  */
1779 SCI_STATUS scic_sds_port_default_event_handler(
1780    SCIC_SDS_PORT_T * port,
1781    U32               event_code
1782 )
1783 {
1784    SCIC_LOG_WARNING((
1785       sci_base_object_get_logger((SCIC_SDS_PORT_T *)port),
1786       SCIC_LOG_OBJECT_PORT,
1787       "SCIC Port 0x%08x requested to process event 0x%08x while in invalid state %d\n",
1788       port, event_code,
1789       sci_base_state_machine_get_state(
1790          scic_sds_port_get_base_state_machine((SCIC_SDS_PORT_T *)port))
1791    ));
1792 
1793    return SCI_FAILURE_INVALID_STATE;
1794 }
1795 
1796 /**
1797  * This is the default method for a port link up notification.  It will report
1798  * a warning and exit.
1799  *
1800  * @param[in] port This is the SCI_BASE_PORT object which is cast into a
1801  *       SCIC_SDS_PORT object.
1802  *
1803  * @return SCI_STATUS
1804  * @retval SCI_FAILURE_INVALID_STATE
1805  */
1806 void scic_sds_port_default_link_up_handler(
1807    SCIC_SDS_PORT_T *this_port,
1808    SCIC_SDS_PHY_T  *phy
1809 )
1810 {
1811    SCIC_LOG_WARNING((
1812       sci_base_object_get_logger(this_port),
1813       SCIC_LOG_OBJECT_PORT,
1814       "SCIC Port 0x%08x received link_up notification from phy 0x%08x while in invalid state %d\n",
1815       this_port, phy,
1816       sci_base_state_machine_get_state(
1817          scic_sds_port_get_base_state_machine(this_port))
1818    ));
1819 }
1820 
1821 /**
1822  * This is the default method for a port link down notification.  It will
1823  * report a warning and exit.
1824  *
1825  * @param[in] port This is the SCI_BASE_PORT object which is cast into a
1826  *       SCIC_SDS_PORT object.
1827  *
1828  * @return SCI_STATUS
1829  * @retval SCI_FAILURE_INVALID_STATE
1830  */
1831 void scic_sds_port_default_link_down_handler(
1832    SCIC_SDS_PORT_T *this_port,
1833    SCIC_SDS_PHY_T  *phy
1834 )
1835 {
1836    SCIC_LOG_WARNING((
1837       sci_base_object_get_logger(this_port),
1838       SCIC_LOG_OBJECT_PORT,
1839       "SCIC Port 0x%08x received link down notification from phy 0x%08x while in invalid state %d\n",
1840       this_port, phy,
1841       sci_base_state_machine_get_state(
1842          scic_sds_port_get_base_state_machine(this_port))
1843    ));
1844 }
1845 
1846 /**
1847  * This is the default method for a port start io request.  It will report a
1848  * warning and exit.
1849  *
1850  * @param[in] port This is the SCI_BASE_PORT object which is cast into a
1851  *       SCIC_SDS_PORT object.
1852  *
1853  * @return SCI_STATUS
1854  * @retval SCI_FAILURE_INVALID_STATE
1855  */
1856 SCI_STATUS scic_sds_port_default_start_io_handler(
1857    SCIC_SDS_PORT_T          *this_port,
1858    SCIC_SDS_REMOTE_DEVICE_T *device,
1859    SCIC_SDS_REQUEST_T       *io_request
1860 )
1861 {
1862    SCIC_LOG_WARNING((
1863       sci_base_object_get_logger(this_port),
1864       SCIC_LOG_OBJECT_PORT,
1865       "SCIC Port 0x%08x requested to start io request 0x%08x while in invalid state %d\n",
1866       this_port, io_request,
1867       sci_base_state_machine_get_state(
1868          scic_sds_port_get_base_state_machine(this_port))
1869    ));
1870 
1871    return SCI_FAILURE_INVALID_STATE;
1872 }
1873 
1874 /**
1875  * This is the default method for a port complete io request.  It will report
1876  * a warning and exit.
1877  *
1878  * @param[in] port This is the SCI_BASE_PORT object which is cast into a
1879  *       SCIC_SDS_PORT object.
1880  *
1881  * @return SCI_STATUS
1882  * @retval SCI_FAILURE_INVALID_STATE
1883  */
1884 SCI_STATUS scic_sds_port_default_complete_io_handler(
1885    SCIC_SDS_PORT_T          *this_port,
1886    SCIC_SDS_REMOTE_DEVICE_T *device,
1887    SCIC_SDS_REQUEST_T       *io_request
1888 )
1889 {
1890    SCIC_LOG_WARNING((
1891       sci_base_object_get_logger(this_port),
1892       SCIC_LOG_OBJECT_PORT,
1893       "SCIC Port 0x%08x requested to complete io request 0x%08x while in invalid state %d\n",
1894       this_port, io_request,
1895       sci_base_state_machine_get_state(
1896          scic_sds_port_get_base_state_machine(this_port))
1897    ));
1898 
1899    return SCI_FAILURE_INVALID_STATE;
1900 }
1901 
1902 //****************************************************************************
1903 //* GENERAL STATE HANDLERS
1904 //****************************************************************************
1905 
1906 /**
1907  * This is a general complete io request handler for the SCIC_SDS_PORT object.
1908  *
1909  * @param[in] port This is the SCIC_SDS_PORT object on which the io request
1910  *       count will be decremented.
1911  * @param[in] device This is the SCIC_SDS_REMOTE_DEVICE object to which the io
1912  *       request is being directed.  This parameter is not required to
1913  *       complete this operation.
1914  * @param[in] io_request This is the request that is being completed on this
1915  *       port object.  This parameter is not required to complete this
1916  *       operation.
1917  *
1918  * @return SCI_STATUS
1919  * @retval SCI_SUCCESS
1920  */
1921 static
1922 SCI_STATUS scic_sds_port_general_complete_io_handler(
1923    SCIC_SDS_PORT_T          *port,
1924    SCIC_SDS_REMOTE_DEVICE_T *device,
1925    SCIC_SDS_REQUEST_T       *io_request
1926 )
1927 {
1928    SCIC_SDS_PORT_T *this_port = (SCIC_SDS_PORT_T *)port;
1929 
1930    scic_sds_port_decrement_request_count(this_port);
1931 
1932    return SCI_SUCCESS;
1933 }
1934 
1935 //****************************************************************************
1936 //* STOPPED STATE HANDLERS
1937 //****************************************************************************
1938 static
1939 BOOL scic_sds_port_requires_scheduler_workaround(
1940    SCIC_SDS_PORT_T * this_port
1941 )
1942 {
1943    if (
1944          (
1945            this_port->owning_controller->logical_port_entries
1946          < this_port->owning_controller->task_context_entries
1947          )
1948       && (
1949            this_port->owning_controller->logical_port_entries
1950          < this_port->owning_controller->remote_node_entries
1951          )
1952       )
1953    {
1954       return TRUE;
1955    }
1956 
1957    return FALSE;
1958 }
1959 
1960 
1961 /**
1962  * This method takes the SCIC_SDS_PORT from a stopped state and attempts to
1963  * start it.  To start a port it must have no assiged devices and it must have
1964  * at least one phy assigned to it.  If those conditions are met then the port
1965  * can transition to the ready state.
1966  *
1967  * @param[in] port This is the SCI_BASE_PORT object which is cast into a
1968  *       SCIC_SDS_PORT object.
1969  *
1970  * @return SCI_STATUS
1971  * @retval SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION This SCIC_SDS_PORT
1972  *         object could not be started because the port configuration is not
1973  *         valid.
1974  * @retval SCI_SUCCESS the start request is successful and the SCIC_SDS_PORT
1975  *         object has transitioned to the SCI_BASE_PORT_STATE_READY.
1976  */
1977 static
1978 SCI_STATUS scic_sds_port_stopped_state_start_handler(
1979    SCI_BASE_PORT_T *port
1980 )
1981 {
1982    U32 phy_mask;
1983    SCI_STATUS status = SCI_SUCCESS;
1984    SCIC_SDS_PORT_T *this_port = (SCIC_SDS_PORT_T *)port;
1985 
1986    if (this_port->assigned_device_count > 0)
1987    {
1988       /// @todo This is a start failure operation because there are still
1989       ///       devices assigned to this port.  There must be no devices
1990       ///       assigned to a port on a start operation.
1991       return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
1992    }
1993 
1994    this_port->timer_handle = scic_cb_timer_create(
1995       scic_sds_port_get_controller(this_port),
1996       scic_sds_port_timeout_handler,
1997       this_port
1998    );
1999 
2000    if (this_port->timer_handle == SCI_INVALID_HANDLE)
2001    {
2002       return SCI_FAILURE_INSUFFICIENT_RESOURCES;
2003    }
2004 
2005    if (scic_sds_port_requires_scheduler_workaround(this_port))
2006    {
2007    if (this_port->reserved_rni == SCU_DUMMY_INDEX)
2008    {
2009       this_port->reserved_rni =
2010          scic_sds_remote_node_table_allocate_remote_node(
2011             &this_port->owning_controller->available_remote_nodes, 1
2012          );
2013 
2014       if (this_port->reserved_rni != SCU_DUMMY_INDEX)
2015       {
2016          scic_sds_port_construct_dummy_rnc(
2017             this_port,
2018             this_port->reserved_rni
2019          );
2020       }
2021       else
2022       {
2023          status = SCI_FAILURE_INSUFFICIENT_RESOURCES;
2024       }
2025    }
2026 
2027    if (this_port->reserved_tci == SCU_DUMMY_INDEX)
2028    {
2029       // Allocate a TCI and remove the sequence nibble
2030       this_port->reserved_tci =
2031          scic_controller_allocate_io_tag(this_port->owning_controller);
2032 
2033       if (this_port->reserved_tci != SCU_DUMMY_INDEX)
2034       {
2035          scic_sds_port_construct_dummy_task(this_port, this_port->reserved_tci);
2036       }
2037       else
2038       {
2039          status = SCI_FAILURE_INSUFFICIENT_RESOURCES;
2040       }
2041    }
2042    }
2043 
2044    if (status == SCI_SUCCESS)
2045    {
2046       phy_mask = scic_sds_port_get_phys(this_port);
2047 
2048       // There are one or more phys assigned to this port.  Make sure
2049       // the port's phy mask is in fact legal and supported by the
2050       // silicon.
2051       if (scic_sds_port_is_phy_mask_valid(this_port, phy_mask) == TRUE)
2052       {
2053          sci_base_state_machine_change_state(
2054             scic_sds_port_get_base_state_machine(this_port),
2055             SCI_BASE_PORT_STATE_READY
2056          );
2057       }
2058       else
2059       {
2060          status = SCI_FAILURE;
2061       }
2062    }
2063 
2064    if (status != SCI_SUCCESS)
2065    {
2066       scic_sds_port_destroy_dummy_resources(this_port);
2067    }
2068 
2069    return status;
2070 }
2071 
2072 /**
2073  * This method takes the SCIC_SDS_PORT that is in a stopped state and handles
2074  * a stop request.  This function takes no action.
2075  *
2076  * @param[in] port This is the SCI_BASE_PORT object which is cast into a
2077  *       SCIC_SDS_PORT object.
2078  *
2079  * @return SCI_STATUS
2080  * @retval SCI_SUCCESS the stop request is successful as the SCIC_SDS_PORT
2081  *         object is already stopped.
2082  */
2083 static
2084 SCI_STATUS scic_sds_port_stopped_state_stop_handler(
2085    SCI_BASE_PORT_T *port
2086 )
2087 {
2088    // We are already stopped so there is nothing to do here
2089    return SCI_SUCCESS;
2090 }
2091 
2092 /**
2093  * This method takes the SCIC_SDS_PORT that is in a stopped state and handles
2094  * the destruct request.  The stopped state is the only state in which the
2095  * SCIC_SDS_PORT can be destroyed.  This function causes the port object to
2096  * transition to the SCI_BASE_PORT_STATE_FINAL.
2097  *
2098  * @param[in] port This is the SCI_BASE_PORT object which is cast into a
2099  *       SCIC_SDS_PORT object.
2100  *
2101  * @return SCI_STATUS
2102  * @retval SCI_SUCCESS
2103  */
2104 static
2105 SCI_STATUS scic_sds_port_stopped_state_destruct_handler(
2106    SCI_BASE_PORT_T *port
2107 )
2108 {
2109    SCIC_SDS_PORT_T *this_port = (SCIC_SDS_PORT_T *)port;
2110 
2111    sci_base_state_machine_stop(&this_port->parent.state_machine);
2112 
2113    return SCI_SUCCESS;
2114 }
2115 
2116 /**
2117  * This method takes the SCIC_SDS_PORT that is in a stopped state and handles
2118  * the add phy request.  In MPC mode the only time a phy can be added to a
2119  * port is in the SCI_BASE_PORT_STATE_STOPPED.
2120  *
2121  * @param[in] port This is the SCI_BASE_PORT object which is cast into a
2122  *       SCIC_SDS_PORT object.
2123  * @param[in] phy This is the SCI_BASE_PHY object which is cast into a
2124  *       SCIC_SDS_PHY object.
2125  *
2126  * @return SCI_STATUS
2127  * @retval SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION is returned when the phy
2128  *         can not be added to the port.
2129  * @retval SCI_SUCCESS if the phy is added to the port.
2130  */
2131 static
2132 SCI_STATUS scic_sds_port_stopped_state_add_phy_handler(
2133    SCI_BASE_PORT_T *port,
2134    SCI_BASE_PHY_T  *phy
2135 )
2136 {
2137    SCIC_SDS_PORT_T * this_port = (SCIC_SDS_PORT_T *)port;
2138    SCIC_SDS_PHY_T  * this_phy  = (SCIC_SDS_PHY_T  *)phy;
2139    SCI_SAS_ADDRESS_T port_sas_address;
2140 
2141    // Read the port assigned SAS Address if there is one
2142    scic_sds_port_get_sas_address(this_port, &port_sas_address);
2143 
2144    if (port_sas_address.high != 0 && port_sas_address.low != 0)
2145    {
2146       SCI_SAS_ADDRESS_T phy_sas_address;
2147 
2148       // Make sure that the PHY SAS Address matches the SAS Address
2149       // for this port.
2150       scic_sds_phy_get_sas_address(this_phy, &phy_sas_address);
2151 
2152       if (
2153             (port_sas_address.high != phy_sas_address.high)
2154          || (port_sas_address.low  != phy_sas_address.low)
2155          )
2156       {
2157          return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
2158       }
2159    }
2160 
2161    return scic_sds_port_set_phy(this_port, this_phy);
2162 }
2163 
2164 
2165 /**
2166  * This method takes the SCIC_SDS_PORT that is in a stopped state and handles
2167  * the remove phy request.  In MPC mode the only time a phy can be removed
2168  * from a port is in the SCI_BASE_PORT_STATE_STOPPED.
2169  *
2170  * @param[in] port This is the SCI_BASE_PORT object which is cast into a
2171  *       SCIC_SDS_PORT object.
2172  * @param[in] phy This is the SCI_BASE_PHY object which is cast into a
2173  *       SCIC_SDS_PHY object.
2174  *
2175  * @return SCI_STATUS
2176  * @retval SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION is returned when the phy
2177  *         can not be added to the port.
2178  * @retval SCI_SUCCESS if the phy is added to the port.
2179  */
2180 static
2181 SCI_STATUS scic_sds_port_stopped_state_remove_phy_handler(
2182    SCI_BASE_PORT_T *port,
2183    SCI_BASE_PHY_T  *phy
2184 )
2185 {
2186    SCIC_SDS_PORT_T *this_port = (SCIC_SDS_PORT_T *)port;
2187    SCIC_SDS_PHY_T  *this_phy  = (SCIC_SDS_PHY_T  *)phy;
2188 
2189    return scic_sds_port_clear_phy(this_port, this_phy);
2190 }
2191 
2192 //****************************************************************************
2193 //*  READY STATE HANDLERS
2194 //****************************************************************************
2195 
2196 //****************************************************************************
2197 //*  RESETTING STATE HANDLERS
2198 //****************************************************************************
2199 
2200 //****************************************************************************
2201 //*  STOPPING STATE HANDLERS
2202 //****************************************************************************
2203 
2204 /**
2205  * This method takes the SCIC_SDS_PORT that is in a stopping state and handles
2206  * the complete io request. Should the request count reach 0 then the port
2207  * object will transition to the stopped state.
2208  *
2209  * @param[in] port This is the SCIC_SDS_PORT object on which the io request
2210  *       count will be decremented.
2211  * @param[in] device This is the SCIC_SDS_REMOTE_DEVICE object to which the io
2212  *       request is being directed.  This parameter is not required to
2213  *       complete this operation.
2214  * @param[in] io_request This is the request that is being completed on this
2215  *       port object.  This parameter is not required to complete this
2216  *       operation.
2217  *
2218  * @return SCI_STATUS
2219  * @retval SCI_SUCCESS
2220  */
2221 static
2222 SCI_STATUS scic_sds_port_stopping_state_complete_io_handler(
2223    SCIC_SDS_PORT_T          *port,
2224    SCIC_SDS_REMOTE_DEVICE_T *device,
2225    SCIC_SDS_REQUEST_T       *io_request
2226 )
2227 {
2228    SCIC_SDS_PORT_T *this_port = (SCIC_SDS_PORT_T *)port;
2229 
2230    scic_sds_port_decrement_request_count(this_port);
2231 
2232    if (this_port->started_request_count == 0)
2233    {
2234       sci_base_state_machine_change_state(
2235          scic_sds_port_get_base_state_machine(this_port),
2236          SCI_BASE_PORT_STATE_STOPPED
2237       );
2238    }
2239 
2240    return SCI_SUCCESS;
2241 }
2242 
2243 //****************************************************************************
2244 //*  RESETTING STATE HANDLERS
2245 //****************************************************************************
2246 
2247 /**
2248  * This method will stop a failed port.  This causes a transition to the
2249  * stopping state.
2250  *
2251  * @param[in] port This is the port object which is being requested to stop.
2252  *
2253  * @return SCI_STATUS
2254  * @retval SCI_SUCCESS
2255  */
2256 static
2257 SCI_STATUS scic_sds_port_reset_state_stop_handler(
2258    SCI_BASE_PORT_T *port
2259 )
2260 {
2261    SCIC_SDS_PORT_T *this_port = (SCIC_SDS_PORT_T *)port;
2262 
2263    sci_base_state_machine_change_state(
2264       &this_port->parent.state_machine,
2265       SCI_BASE_PORT_STATE_STOPPING
2266    );
2267 
2268    return SCI_SUCCESS;
2269 }
2270 
2271 /**
2272  * This method will transition a failed port to its ready state.  The port
2273  * failed because a hard reset request timed out but at some time later one or
2274  * more phys in the port became ready.
2275  *
2276  * @param[in] port This is the SCI_BASE_PORT object which is cast into a
2277  *       SCIC_SDS_PORT object.
2278  *
2279  * @return SCI_STATUS
2280  * @retval SCI_SUCCESS
2281  */
2282 static
2283 void scic_sds_port_reset_state_link_up_handler(
2284    SCIC_SDS_PORT_T *this_port,
2285    SCIC_SDS_PHY_T  *phy
2286 )
2287 {
2288    /// @todo We should make sure that the phy that has gone link up is the same
2289    ///       one on which we sent the reset.  It is possible that the phy on
2290    ///       which we sent the reset is not the one that has gone link up and we
2291    ///       want to make sure that phy being reset comes back.  Consider the
2292    ///       case where a reset is sent but before the hardware processes the
2293    ///       reset it get a link up on the port because of a hot plug event.
2294    ///       because of the reset request this phy will go link down almost
2295    ///       immediately.
2296 
2297    // In the resetting state we don't notify the user regarding
2298    // link up and link down notifications.
2299    scic_sds_port_general_link_up_handler(this_port, phy, FALSE, TRUE);
2300 }
2301 
2302 /**
2303  * This method process link down notifications that occur during a
2304  * port reset operation. Link downs can occur during the reset operation.
2305  *
2306  * @param[in] port This is the SCI_BASE_PORT object which is cast into a
2307  *       SCIC_SDS_PORT object.
2308  *
2309  * @return SCI_STATUS
2310  * @retval SCI_SUCCESS
2311  */
2312 static
2313 void scic_sds_port_reset_state_link_down_handler(
2314    SCIC_SDS_PORT_T *this_port,
2315    SCIC_SDS_PHY_T  *phy
2316 )
2317 {
2318    // In the resetting state we don't notify the user regarding
2319    // link up and link down notifications.
2320    scic_sds_port_deactivate_phy(this_port, phy, FALSE);
2321 }
2322 
2323 // ---------------------------------------------------------------------------
2324 
2325 SCIC_SDS_PORT_STATE_HANDLER_T
2326    scic_sds_port_state_handler_table[SCI_BASE_PORT_MAX_STATES] =
2327 {
2328    // SCI_BASE_PORT_STATE_STOPPED
2329    {
2330       {
2331          scic_sds_port_stopped_state_start_handler,
2332          scic_sds_port_stopped_state_stop_handler,
2333          scic_sds_port_stopped_state_destruct_handler,
2334          scic_sds_port_default_reset_handler,
2335          scic_sds_port_stopped_state_add_phy_handler,
2336          scic_sds_port_stopped_state_remove_phy_handler
2337       },
2338       scic_sds_port_default_frame_handler,
2339       scic_sds_port_default_event_handler,
2340       scic_sds_port_default_link_up_handler,
2341       scic_sds_port_default_link_down_handler,
2342       scic_sds_port_default_start_io_handler,
2343       scic_sds_port_default_complete_io_handler
2344    },
2345    // SCI_BASE_PORT_STATE_STOPPING
2346    {
2347       {
2348          scic_sds_port_default_start_handler,
2349          scic_sds_port_default_stop_handler,
2350          scic_sds_port_default_destruct_handler,
2351          scic_sds_port_default_reset_handler,
2352          scic_sds_port_default_add_phy_handler,
2353          scic_sds_port_default_remove_phy_handler
2354       },
2355       scic_sds_port_default_frame_handler,
2356       scic_sds_port_default_event_handler,
2357       scic_sds_port_default_link_up_handler,
2358       scic_sds_port_default_link_down_handler,
2359       scic_sds_port_default_start_io_handler,
2360       scic_sds_port_stopping_state_complete_io_handler
2361    },
2362    // SCI_BASE_PORT_STATE_READY
2363    {
2364       {
2365          scic_sds_port_default_start_handler,
2366          scic_sds_port_default_stop_handler,
2367          scic_sds_port_default_destruct_handler,
2368          scic_sds_port_default_reset_handler,
2369          scic_sds_port_default_add_phy_handler,
2370          scic_sds_port_default_remove_phy_handler
2371       },
2372       scic_sds_port_default_frame_handler,
2373       scic_sds_port_default_event_handler,
2374       scic_sds_port_default_link_up_handler,
2375       scic_sds_port_default_link_down_handler,
2376       scic_sds_port_default_start_io_handler,
2377       scic_sds_port_general_complete_io_handler
2378    },
2379    // SCI_BASE_PORT_STATE_RESETTING
2380    {
2381       {
2382          scic_sds_port_default_start_handler,
2383          scic_sds_port_reset_state_stop_handler,
2384          scic_sds_port_default_destruct_handler,
2385          scic_sds_port_default_reset_handler,
2386          scic_sds_port_default_add_phy_handler,
2387          scic_sds_port_default_remove_phy_handler
2388       },
2389       scic_sds_port_default_frame_handler,
2390       scic_sds_port_default_event_handler,
2391       scic_sds_port_reset_state_link_up_handler,
2392       scic_sds_port_reset_state_link_down_handler,
2393       scic_sds_port_default_start_io_handler,
2394       scic_sds_port_general_complete_io_handler
2395    },
2396    // SCI_BASE_PORT_STATE_FAILED
2397    {
2398       {
2399          scic_sds_port_default_start_handler,
2400          scic_sds_port_default_stop_handler,
2401          scic_sds_port_default_destruct_handler,
2402          scic_sds_port_default_reset_handler,
2403          scic_sds_port_default_add_phy_handler,
2404          scic_sds_port_default_remove_phy_handler
2405       },
2406       scic_sds_port_default_frame_handler,
2407       scic_sds_port_default_event_handler,
2408       scic_sds_port_default_link_up_handler,
2409       scic_sds_port_default_link_down_handler,
2410       scic_sds_port_default_start_io_handler,
2411       scic_sds_port_general_complete_io_handler
2412    }
2413 };
2414 
2415 //******************************************************************************
2416 //*  PORT STATE PRIVATE METHODS
2417 //******************************************************************************
2418 
2419 /**
2420  * This method will enable the SCU Port Task Scheduler for this port object
2421  * but will leave the port task scheduler in a suspended state.
2422  *
2423  * @param[in] this_port This is the port object which to suspend.
2424  *
2425  * @return none
2426  */
2427 static
2428 void scic_sds_port_enable_port_task_scheduler(
2429    SCIC_SDS_PORT_T *this_port
2430 )
2431 {
2432    U32 pts_control_value;
2433 
2434    pts_control_value = scu_port_task_scheduler_read(this_port, control);
2435 
2436    pts_control_value |= SCU_PTSxCR_GEN_BIT(ENABLE) | SCU_PTSxCR_GEN_BIT(SUSPEND);
2437 
2438    scu_port_task_scheduler_write(this_port, control, pts_control_value);
2439 }
2440 
2441 /**
2442  * This method will disable the SCU port task scheduler for this port
2443  * object.
2444  *
2445  * @param[in] this_port This is the port object which to resume.
2446  *
2447  * @return none
2448  */
2449 static
2450 void scic_sds_port_disable_port_task_scheduler(
2451    SCIC_SDS_PORT_T *this_port
2452 )
2453 {
2454    U32 pts_control_value;
2455 
2456    pts_control_value = scu_port_task_scheduler_read(this_port, control);
2457 
2458    pts_control_value &= ~(   SCU_PTSxCR_GEN_BIT(ENABLE)
2459                            | SCU_PTSxCR_GEN_BIT(SUSPEND) );
2460 
2461    scu_port_task_scheduler_write(this_port, control, pts_control_value);
2462 }
2463 
2464 /**
2465  *
2466  */
2467 static
2468 void scic_sds_port_post_dummy_remote_node(
2469       SCIC_SDS_PORT_T *this_port
2470 )
2471 {
2472    U32 command;
2473    SCU_REMOTE_NODE_CONTEXT_T * rnc;
2474 
2475    if (this_port->reserved_rni != SCU_DUMMY_INDEX)
2476    {
2477    rnc = &(this_port->owning_controller->remote_node_context_table[this_port->reserved_rni]);
2478 
2479    rnc->ssp.is_valid = TRUE;
2480 
2481    command = (
2482        (SCU_CONTEXT_COMMAND_POST_RNC_32)
2483      | (this_port->physical_port_index << SCU_CONTEXT_COMMAND_LOGICAL_PORT_SHIFT)
2484      | (this_port->reserved_rni)
2485    );
2486 
2487    scic_sds_controller_post_request(this_port->owning_controller, command);
2488 
2489    scic_cb_stall_execution(10);
2490 
2491    command = (
2492        (SCU_CONTEXT_COMMAND_POST_RNC_SUSPEND_TX_RX)
2493      | (this_port->physical_port_index << SCU_CONTEXT_COMMAND_LOGICAL_PORT_SHIFT)
2494      | (this_port->reserved_rni)
2495    );
2496 
2497    scic_sds_controller_post_request(this_port->owning_controller, command);
2498 }
2499 }
2500 
2501 /**
2502  *
2503  */
2504 static
2505 void scic_sds_port_invalidate_dummy_remote_node(
2506    SCIC_SDS_PORT_T *this_port
2507 )
2508 {
2509    U32 command;
2510    SCU_REMOTE_NODE_CONTEXT_T * rnc;
2511 
2512    if (this_port->reserved_rni != SCU_DUMMY_INDEX)
2513    {
2514    rnc = &(this_port->owning_controller->remote_node_context_table[this_port->reserved_rni]);
2515 
2516    rnc->ssp.is_valid = FALSE;
2517 
2518    scic_cb_stall_execution(10);
2519 
2520    command = (
2521        (SCU_CONTEXT_COMMAND_POST_RNC_INVALIDATE)
2522      | (this_port->physical_port_index << SCU_CONTEXT_COMMAND_LOGICAL_PORT_SHIFT)
2523      | (this_port->reserved_rni)
2524    );
2525 
2526    scic_sds_controller_post_request(this_port->owning_controller, command);
2527 }
2528 }
2529 
2530 //******************************************************************************
2531 //*  PORT STATE METHODS
2532 //******************************************************************************
2533 
2534 /**
2535  * This method will perform the actions required by the SCIC_SDS_PORT on
2536  * entering the SCI_BASE_PORT_STATE_STOPPED. This function sets the stopped
2537  * state handlers for the SCIC_SDS_PORT object and disables the port task
2538  * scheduler in the hardware.
2539  *
2540  * @param[in] object This is the SCI_BASE_OBJECT which is cast to a
2541  *       SCIC_SDS_PORT object.
2542  *
2543  * @return none
2544  */
2545 static
2546 void scic_sds_port_stopped_state_enter(
2547    SCI_BASE_OBJECT_T *object
2548 )
2549 {
2550    SCIC_SDS_PORT_T *this_port;
2551    this_port = (SCIC_SDS_PORT_T *)object;
2552 
2553    scic_sds_port_set_base_state_handlers(
2554       this_port, SCI_BASE_PORT_STATE_STOPPED
2555    );
2556 
2557    if (
2558          SCI_BASE_PORT_STATE_STOPPING
2559       == this_port->parent.state_machine.previous_state_id
2560       )
2561    {
2562       // If we enter this state becasuse of a request to stop
2563       // the port then we want to disable the hardwares port
2564       // task scheduler.
2565       scic_sds_port_disable_port_task_scheduler(this_port);
2566    }
2567 }
2568 
2569 /**
2570  * This method will perform the actions required by the SCIC_SDS_PORT on
2571  * exiting the SCI_BASE_STATE_STOPPED. This function enables the SCU hardware
2572  * port task scheduler.
2573  *
2574  * @param[in] object This is the SCI_BASE_OBJECT which is cast to a
2575  *       SCIC_SDS_PORT object.
2576  *
2577  * @return none
2578  */
2579 static
2580 void scic_sds_port_stopped_state_exit(
2581    SCI_BASE_OBJECT_T *object
2582 )
2583 {
2584    SCIC_SDS_PORT_T *this_port;
2585    this_port = (SCIC_SDS_PORT_T *)object;
2586 
2587    // Enable and suspend the port task scheduler
2588    scic_sds_port_enable_port_task_scheduler(this_port);
2589 }
2590 
2591 /**
2592  * This method will perform the actions required by the SCIC_SDS_PORT on
2593  * entering the SCI_BASE_PORT_STATE_READY. This function sets the ready state
2594  * handlers for the SCIC_SDS_PORT object, reports the port object as not ready
2595  * and starts the ready substate machine.
2596  *
2597  * @param[in] object This is the SCI_BASE_OBJECT which is cast to a
2598  *       SCIC_SDS_PORT object.
2599  *
2600  * @return none
2601  */
2602 static
2603 void scic_sds_port_ready_state_enter(
2604    SCI_BASE_OBJECT_T *object
2605 )
2606 {
2607    SCIC_SDS_PORT_T *this_port;
2608    this_port = (SCIC_SDS_PORT_T *)object;
2609 
2610    // Put the ready state handlers in place though they will not be there long
2611    scic_sds_port_set_base_state_handlers(
2612       this_port, SCI_BASE_PORT_STATE_READY
2613    );
2614 
2615    if (
2616           SCI_BASE_PORT_STATE_RESETTING
2617       == this_port->parent.state_machine.previous_state_id
2618       )
2619    {
2620       scic_cb_port_hard_reset_complete(
2621          scic_sds_port_get_controller(this_port),
2622          this_port,
2623          SCI_SUCCESS
2624       );
2625    }
2626    else
2627    {
2628       // Notify the caller that the port is not yet ready
2629       scic_cb_port_not_ready(
2630          scic_sds_port_get_controller(this_port),
2631          this_port,
2632          SCIC_PORT_NOT_READY_NO_ACTIVE_PHYS
2633       );
2634    }
2635 
2636    // Post and suspend the dummy remote node context for this
2637    // port.
2638    scic_sds_port_post_dummy_remote_node(this_port);
2639 
2640    // Start the ready substate machine
2641    sci_base_state_machine_start(
2642       scic_sds_port_get_ready_substate_machine(this_port)
2643    );
2644 }
2645 
2646 /**
2647  * This method will perform the actions required by the SCIC_SDS_PORT on
2648  * exiting the SCI_BASE_STATE_READY. This function does nothing.
2649  *
2650  * @param[in] object This is the SCI_BASE_OBJECT which is cast to a
2651  *       SCIC_SDS_PORT object.
2652  *
2653  * @return none
2654  */
2655 static
2656 void scic_sds_port_ready_state_exit(
2657    SCI_BASE_OBJECT_T *object
2658 )
2659 {
2660    SCIC_SDS_PORT_T *this_port;
2661    this_port = (SCIC_SDS_PORT_T *)object;
2662 
2663    sci_base_state_machine_stop(&this_port->ready_substate_machine);
2664 
2665    scic_cb_stall_execution(10);
2666    scic_sds_port_invalidate_dummy_remote_node(this_port);
2667 }
2668 
2669 /**
2670  * This method will perform the actions required by the SCIC_SDS_PORT on
2671  * entering the SCI_BASE_PORT_STATE_RESETTING. This function sets the
2672  * resetting state handlers for the SCIC_SDS_PORT object.
2673  *
2674  * @param[in] object This is the SCI_BASE_OBJECT which is cast to a
2675  *       SCIC_SDS_PORT object.
2676  *
2677  * @return none
2678  */
2679 static
2680 void scic_sds_port_resetting_state_enter(
2681    SCI_BASE_OBJECT_T *object
2682 )
2683 {
2684    SCIC_SDS_PORT_T *this_port;
2685    this_port = (SCIC_SDS_PORT_T *)object;
2686 
2687    scic_sds_port_set_base_state_handlers(
2688       this_port, SCI_BASE_PORT_STATE_RESETTING
2689    );
2690 }
2691 
2692 /**
2693  * This method will perform the actions required by the SCIC_SDS_PORT on
2694  * exiting the SCI_BASE_STATE_RESETTING. This function does nothing.
2695  *
2696  * @param[in] object This is the SCI_BASE_OBJECT which is cast to a
2697  *       SCIC_SDS_PORT object.
2698  *
2699  * @return none
2700  */
2701 static
2702 void scic_sds_port_resetting_state_exit(
2703    SCI_BASE_OBJECT_T *object
2704 )
2705 {
2706    SCIC_SDS_PORT_T *this_port;
2707    this_port = (SCIC_SDS_PORT_T *)object;
2708 
2709    scic_cb_timer_stop(
2710       scic_sds_port_get_controller(this_port),
2711       this_port->timer_handle
2712    );
2713 }
2714 
2715 /**
2716  * This method will perform the actions required by the SCIC_SDS_PORT on
2717  * entering the SCI_BASE_PORT_STATE_STOPPING. This function sets the stopping
2718  * state handlers for the SCIC_SDS_PORT object.
2719  *
2720  * @param[in] object This is the SCI_BASE_OBJECT which is cast to a
2721  *       SCIC_SDS_PORT object.
2722  *
2723  * @return none
2724  */
2725 static
2726 void scic_sds_port_stopping_state_enter(
2727    SCI_BASE_OBJECT_T *object
2728 )
2729 {
2730    SCIC_SDS_PORT_T *this_port;
2731    this_port = (SCIC_SDS_PORT_T *)object;
2732 
2733    scic_sds_port_set_base_state_handlers(
2734       this_port, SCI_BASE_PORT_STATE_STOPPING
2735    );
2736 
2737    if (this_port->started_request_count == 0)
2738    {
2739       sci_base_state_machine_change_state(
2740          &this_port->parent.state_machine,
2741          SCI_BASE_PORT_STATE_STOPPED
2742       );
2743    }
2744 }
2745 
2746 /**
2747  * This method will perform the actions required by the SCIC_SDS_PORT on
2748  * exiting the SCI_BASE_STATE_STOPPING. This function does nothing.
2749  *
2750  * @param[in] object This is the SCI_BASE_OBJECT which is cast to a
2751  *       SCIC_SDS_PORT object.
2752  *
2753  * @return none
2754  */
2755 static
2756 void scic_sds_port_stopping_state_exit(
2757    SCI_BASE_OBJECT_T *object
2758 )
2759 {
2760    SCIC_SDS_PORT_T *this_port;
2761    this_port = (SCIC_SDS_PORT_T *)object;
2762 
2763    scic_cb_timer_stop(
2764       scic_sds_port_get_controller(this_port),
2765       this_port->timer_handle
2766    );
2767 
2768    scic_cb_timer_destroy(
2769       scic_sds_port_get_controller(this_port),
2770       this_port->timer_handle
2771    );
2772    this_port->timer_handle = NULL;
2773 
2774    scic_sds_port_destroy_dummy_resources(this_port);
2775 }
2776 
2777 /**
2778  * This method will perform the actions required by the SCIC_SDS_PORT on
2779  * entering the SCI_BASE_PORT_STATE_STOPPING. This function sets the stopping
2780  * state handlers for the SCIC_SDS_PORT object.
2781  *
2782  * @param[in] object This is the SCI_BASE_OBJECT which is cast to a
2783  *       SCIC_SDS_PORT object.
2784  *
2785  * @return none
2786  */
2787 static
2788 void scic_sds_port_failed_state_enter(
2789    SCI_BASE_OBJECT_T *object
2790 )
2791 {
2792    SCIC_SDS_PORT_T *this_port;
2793    this_port = (SCIC_SDS_PORT_T *)object;
2794 
2795    scic_sds_port_set_base_state_handlers(
2796       this_port,
2797       SCI_BASE_PORT_STATE_FAILED
2798    );
2799 
2800    scic_cb_port_hard_reset_complete(
2801       scic_sds_port_get_controller(this_port),
2802       this_port,
2803       SCI_FAILURE_TIMEOUT
2804    );
2805 }
2806 
2807 // ---------------------------------------------------------------------------
2808 
2809 SCI_BASE_STATE_T scic_sds_port_state_table[SCI_BASE_PORT_MAX_STATES] =
2810 {
2811    {
2812       SCI_BASE_PORT_STATE_STOPPED,
2813       scic_sds_port_stopped_state_enter,
2814       scic_sds_port_stopped_state_exit
2815    },
2816    {
2817       SCI_BASE_PORT_STATE_STOPPING,
2818       scic_sds_port_stopping_state_enter,
2819       scic_sds_port_stopping_state_exit
2820    },
2821    {
2822       SCI_BASE_PORT_STATE_READY,
2823       scic_sds_port_ready_state_enter,
2824       scic_sds_port_ready_state_exit
2825    },
2826    {
2827       SCI_BASE_PORT_STATE_RESETTING,
2828       scic_sds_port_resetting_state_enter,
2829       scic_sds_port_resetting_state_exit
2830    },
2831    {
2832       SCI_BASE_PORT_STATE_FAILED,
2833       scic_sds_port_failed_state_enter,
2834       NULL
2835    }
2836 };
2837 
2838 //******************************************************************************
2839 //* PORT READY SUB-STATE MACHINE
2840 //******************************************************************************
2841 
2842 //****************************************************************************
2843 //*  READY SUBSTATE HANDLERS
2844 //****************************************************************************
2845 
2846 /**
2847  * This method is the general ready state stop handler for the SCIC_SDS_PORT
2848  * object.  This function will transition the ready substate machine to its
2849  * final state.
2850  *
2851  * @param[in] port This is the SCI_BASE_PORT object which is cast into a
2852  *       SCIC_SDS_PORT object.
2853  *
2854  * @return SCI_STATUS
2855  * @retval SCI_SUCCESS
2856  */
2857 static
2858 SCI_STATUS scic_sds_port_ready_substate_stop_handler(
2859    SCI_BASE_PORT_T *port
2860 )
2861 {
2862    SCIC_SDS_PORT_T *this_port = (SCIC_SDS_PORT_T *)port;
2863 
2864    sci_base_state_machine_change_state(
2865       &this_port->parent.state_machine,
2866       SCI_BASE_PORT_STATE_STOPPING
2867    );
2868 
2869    return SCI_SUCCESS;
2870 }
2871 
2872 /**
2873  * This method is the general ready substate complete io handler for the
2874  * SCIC_SDS_PORT object.  This function decrments the outstanding request
2875  * count for this port object.
2876  *
2877  * @param[in] port This is the SCI_BASE_PORT object which is cast into a
2878  *       SCIC_SDS_PORT object.
2879  * @param[in] device This is the SCI_BASE_REMOTE_DEVICE object which is not
2880  *       used in this function.
2881  * @param[in] io_request This is the SCI_BASE_REQUEST object which is not used
2882  *       in this function.
2883  *
2884  * @return SCI_STATUS
2885  * @retval SCI_SUCCESS
2886  */
2887 static
2888 SCI_STATUS scic_sds_port_ready_substate_complete_io_handler(
2889    SCIC_SDS_PORT_T               *port,
2890    struct SCIC_SDS_REMOTE_DEVICE *device,
2891    struct SCIC_SDS_REQUEST       *io_request
2892 )
2893 {
2894    SCIC_SDS_PORT_T *this_port = (SCIC_SDS_PORT_T *)port;
2895 
2896    scic_sds_port_decrement_request_count(this_port);
2897 
2898    return SCI_SUCCESS;
2899 }
2900 
2901 static
2902 SCI_STATUS scic_sds_port_ready_substate_add_phy_handler(
2903    SCI_BASE_PORT_T *port,
2904    SCI_BASE_PHY_T  *phy
2905 )
2906 {
2907    SCIC_SDS_PORT_T * this_port = (SCIC_SDS_PORT_T *)port;
2908    SCIC_SDS_PHY_T  * this_phy  = (SCIC_SDS_PHY_T  *)phy;
2909    SCI_STATUS        status;
2910 
2911    status = scic_sds_port_set_phy(this_port, this_phy);
2912 
2913    if (status == SCI_SUCCESS)
2914    {
2915       scic_sds_port_general_link_up_handler(this_port, this_phy, TRUE, FALSE);
2916 
2917       this_port->not_ready_reason = SCIC_PORT_NOT_READY_RECONFIGURING;
2918 
2919       sci_base_state_machine_change_state(
2920          &this_port->ready_substate_machine,
2921          SCIC_SDS_PORT_READY_SUBSTATE_CONFIGURING
2922       );
2923    }
2924 
2925    return status;
2926 }
2927 
2928 static
2929 SCI_STATUS scic_sds_port_ready_substate_remove_phy_handler(
2930    SCI_BASE_PORT_T *port,
2931    SCI_BASE_PHY_T  *phy
2932 )
2933 {
2934    SCIC_SDS_PORT_T * this_port = (SCIC_SDS_PORT_T *)port;
2935    SCIC_SDS_PHY_T  * this_phy  = (SCIC_SDS_PHY_T  *)phy;
2936    SCI_STATUS        status;
2937 
2938    status = scic_sds_port_clear_phy(this_port, this_phy);
2939 
2940    if (status == SCI_SUCCESS)
2941    {
2942       scic_sds_port_deactivate_phy(this_port, this_phy, TRUE);
2943 
2944       this_port->not_ready_reason = SCIC_PORT_NOT_READY_RECONFIGURING;
2945 
2946       sci_base_state_machine_change_state(
2947          &this_port->ready_substate_machine,
2948          SCIC_SDS_PORT_READY_SUBSTATE_CONFIGURING
2949       );
2950    }
2951 
2952    return status;
2953 }
2954 
2955 //****************************************************************************
2956 //*  READY SUBSTATE WAITING HANDLERS
2957 //****************************************************************************
2958 
2959 /**
2960  * This method is the ready waiting substate link up handler for the
2961  * SCIC_SDS_PORT object.  This methos will report the link up condition for
2962  * this port and will transition to the ready operational substate.
2963  *
2964  * @param[in] this_port This is the SCIC_SDS_PORT object that which has a phy
2965  *       that has gone link up.
2966  * @param[in] the_phy This is the SCIC_SDS_PHY object that has gone link up.
2967  *
2968  * @return none
2969  */
2970 static
2971 void scic_sds_port_ready_waiting_substate_link_up_handler(
2972    SCIC_SDS_PORT_T *this_port,
2973    SCIC_SDS_PHY_T  *the_phy
2974 )
2975 {
2976    // Since this is the first phy going link up for the port we can just enable
2977    // it and continue.
2978    scic_sds_port_activate_phy(this_port, the_phy, TRUE, TRUE);
2979 
2980    sci_base_state_machine_change_state(
2981       &this_port->ready_substate_machine,
2982       SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL
2983    );
2984 }
2985 
2986 /**
2987  * This method is the ready waiting substate start io handler for the
2988  * SCIC_SDS_PORT object. The port object can not accept new requests so the
2989  * request is failed.
2990  *
2991  * @param[in] port This is the SCI_BASE_PORT object which is cast into a
2992  *       SCIC_SDS_PORT object.
2993  * @param[in] device This is the SCI_BASE_REMOTE_DEVICE object which is not
2994  *       used in this request.
2995  * @param[in] io_request This is the SCI_BASE_REQUEST object which is not used
2996  *       in this function.
2997  *
2998  * @return SCI_STATUS
2999  * @retval SCI_FAILURE_INVALID_STATE
3000  */
3001 static
3002 SCI_STATUS scic_sds_port_ready_waiting_substate_start_io_handler(
3003    SCIC_SDS_PORT_T          *port,
3004    SCIC_SDS_REMOTE_DEVICE_T *device,
3005    SCIC_SDS_REQUEST_T       *io_request
3006 )
3007 {
3008    return SCI_FAILURE_INVALID_STATE;
3009 }
3010 
3011 //****************************************************************************
3012 //*  READY SUBSTATE OPERATIONAL HANDLERS
3013 //****************************************************************************
3014 
3015 /**
3016  * This method will cause the port to reset.
3017  *
3018  * @param[in] port This is the SCI_BASE_PORT object which is cast into a
3019  *       SCIC_SDS_PORT object.
3020  * @param[in] timeout This is the timeout for the reset request to complete.
3021  *
3022  * @return SCI_STATUS
3023  * @retval SCI_SUCCESS
3024  */
3025 static
3026 SCI_STATUS scic_sds_port_ready_operational_substate_reset_handler(
3027    SCI_BASE_PORT_T * port,
3028    U32               timeout
3029 )
3030 {
3031    SCI_STATUS        status = SCI_FAILURE_INVALID_PHY;
3032    U32               phy_index;
3033    SCIC_SDS_PORT_T * this_port = (SCIC_SDS_PORT_T *)port;
3034    SCIC_SDS_PHY_T  * selected_phy = SCI_INVALID_HANDLE;
3035 
3036 
3037    // Select a phy on which we can send the hard reset request.
3038    for (
3039          phy_index = 0;
3040             (phy_index < SCI_MAX_PHYS)
3041          && (selected_phy == SCI_INVALID_HANDLE);
3042          phy_index++
3043        )
3044    {
3045       selected_phy = this_port->phy_table[phy_index];
3046 
3047       if (
3048             (selected_phy != SCI_INVALID_HANDLE)
3049          && !scic_sds_port_active_phy(this_port, selected_phy)
3050          )
3051       {
3052          // We found a phy but it is not ready select different phy
3053          selected_phy = SCI_INVALID_HANDLE;
3054       }
3055    }
3056 
3057    // If we have a phy then go ahead and start the reset procedure
3058    if (selected_phy != SCI_INVALID_HANDLE)
3059    {
3060       status = scic_sds_phy_reset(selected_phy);
3061 
3062       if (status == SCI_SUCCESS)
3063       {
3064          scic_cb_timer_start(
3065             scic_sds_port_get_controller(this_port),
3066             this_port->timer_handle,
3067             timeout
3068          );
3069 
3070          this_port->not_ready_reason = SCIC_PORT_NOT_READY_HARD_RESET_REQUESTED;
3071 
3072          sci_base_state_machine_change_state(
3073             &this_port->parent.state_machine,
3074             SCI_BASE_PORT_STATE_RESETTING
3075          );
3076       }
3077    }
3078 
3079    return status;
3080 }
3081 
3082 /**
3083  * This method is the ready operational substate link up handler for the
3084  * SCIC_SDS_PORT object. This function notifies the SCI User that the phy has
3085  * gone link up.
3086  *
3087  * @param[in] this_port This is the SCIC_SDS_PORT object that which has a phy
3088  *       that has gone link up.
3089  * @param[in] the_phy This is the SCIC_SDS_PHY object that has gone link up.
3090  *
3091  * @return none
3092  */
3093 static
3094 void scic_sds_port_ready_operational_substate_link_up_handler(
3095    SCIC_SDS_PORT_T *this_port,
3096    SCIC_SDS_PHY_T  *the_phy
3097 )
3098 {
3099    scic_sds_port_general_link_up_handler(this_port, the_phy, TRUE, TRUE);
3100 }
3101 
3102 /**
3103  * This method is the ready operational substate link down handler for the
3104  * SCIC_SDS_PORT object. This function notifies the SCI User that the phy has
3105  * gone link down and if this is the last phy in the port the port will change
3106  * state to the ready waiting substate.
3107  *
3108  * @param[in] this_port This is the SCIC_SDS_PORT object that which has a phy
3109  *       that has gone link down.
3110  * @param[in] the_phy This is the SCIC_SDS_PHY object that has gone link down.
3111  *
3112  * @return none
3113  */
3114 static
3115 void scic_sds_port_ready_operational_substate_link_down_handler(
3116    SCIC_SDS_PORT_T *this_port,
3117    SCIC_SDS_PHY_T  *the_phy
3118 )
3119 {
3120    scic_sds_port_deactivate_phy(this_port, the_phy, TRUE);
3121 
3122    // If there are no active phys left in the port, then transition
3123    // the port to the WAITING state until such time as a phy goes
3124    // link up.
3125    if (this_port->active_phy_mask == 0)
3126    {
3127       sci_base_state_machine_change_state(
3128          scic_sds_port_get_ready_substate_machine(this_port),
3129          SCIC_SDS_PORT_READY_SUBSTATE_WAITING
3130       );
3131    }
3132 }
3133 
3134 /**
3135  * This method is the ready operational substate start io handler for the
3136  * SCIC_SDS_PORT object.  This function incremetns the outstanding request
3137  * count for this port object.
3138  *
3139  * @param[in] port This is the SCI_BASE_PORT object which is cast into a
3140  *       SCIC_SDS_PORT object.
3141  * @param[in] device This is the SCI_BASE_REMOTE_DEVICE object which is not
3142  *       used in this function.
3143  * @param[in] io_request This is the SCI_BASE_REQUEST object which is not used
3144  *       in this function.
3145  *
3146  * @return SCI_STATUS
3147  * @retval SCI_SUCCESS
3148  */
3149 static
3150 SCI_STATUS scic_sds_port_ready_operational_substate_start_io_handler(
3151    SCIC_SDS_PORT_T          *port,
3152    SCIC_SDS_REMOTE_DEVICE_T *device,
3153    SCIC_SDS_REQUEST_T       *io_request
3154 )
3155 {
3156    SCIC_SDS_PORT_T *this_port = (SCIC_SDS_PORT_T *)port;
3157 
3158    scic_sds_port_increment_request_count(this_port);
3159 
3160    return SCI_SUCCESS;
3161 }
3162 
3163 //****************************************************************************
3164 //*  READY SUBSTATE OPERATIONAL HANDLERS
3165 //****************************************************************************
3166 
3167 /**
3168  * This is the default method for a port add phy request.  It will report a
3169  * warning and exit.
3170  *
3171  * @param[in] port This is the SCI_BASE_PORT object which is cast into a
3172  *       SCIC_SDS_PORT object.
3173  *
3174  * @return SCI_STATUS
3175  * @retval SCI_FAILURE_INVALID_STATE
3176  */
3177 static
3178 SCI_STATUS scic_sds_port_ready_configuring_substate_add_phy_handler(
3179    SCI_BASE_PORT_T *port,
3180    SCI_BASE_PHY_T  *phy
3181 )
3182 {
3183    SCIC_SDS_PORT_T * this_port = (SCIC_SDS_PORT_T *)port;
3184    SCIC_SDS_PHY_T  * this_phy  = (SCIC_SDS_PHY_T  *)phy;
3185    SCI_STATUS        status;
3186 
3187    status = scic_sds_port_set_phy(this_port, this_phy);
3188 
3189    if (status == SCI_SUCCESS)
3190    {
3191       scic_sds_port_general_link_up_handler(this_port, this_phy, TRUE, FALSE);
3192 
3193       // Re-enter the configuring state since this may be the last phy in
3194       // the port.
3195       sci_base_state_machine_change_state(
3196          &this_port->ready_substate_machine,
3197          SCIC_SDS_PORT_READY_SUBSTATE_CONFIGURING
3198       );
3199    }
3200 
3201    return status;
3202 }
3203 
3204 /**
3205  * This is the default method for a port remove phy request.  It will report a
3206  * warning and exit.
3207  *
3208  * @param[in] port This is the SCI_BASE_PORT object which is cast into a
3209  *       SCIC_SDS_PORT object.
3210  *
3211  * @return SCI_STATUS
3212  * @retval SCI_FAILURE_INVALID_STATE
3213  */
3214 static
3215 SCI_STATUS scic_sds_port_ready_configuring_substate_remove_phy_handler(
3216    SCI_BASE_PORT_T *port,
3217    SCI_BASE_PHY_T  *phy
3218 )
3219 {
3220    SCIC_SDS_PORT_T * this_port = (SCIC_SDS_PORT_T *)port;
3221    SCIC_SDS_PHY_T  * this_phy  = (SCIC_SDS_PHY_T  *)phy;
3222    SCI_STATUS        status;
3223 
3224    status = scic_sds_port_clear_phy(this_port, this_phy);
3225 
3226    if (status == SCI_SUCCESS)
3227    {
3228       scic_sds_port_deactivate_phy(this_port, this_phy, TRUE);
3229 
3230       // Re-enter the configuring state since this may be the last phy in
3231       // the port.
3232       sci_base_state_machine_change_state(
3233          &this_port->ready_substate_machine,
3234          SCIC_SDS_PORT_READY_SUBSTATE_CONFIGURING
3235       );
3236    }
3237 
3238    return status;
3239 }
3240 
3241 /**
3242  * This method will decrement the outstanding request count for this port.
3243  * If the request count goes to 0 then the port can be reprogrammed with
3244  * its new phy data.
3245  *
3246  * @param[in] port This is the port that is being requested to complete
3247  *            the io request.
3248  * @param[in] device This is the device on which the io is completing.
3249  * @param[in] io_request This is the io request that is completing.
3250  */
3251 static
3252 SCI_STATUS scic_sds_port_ready_configuring_substate_complete_io_handler(
3253    SCIC_SDS_PORT_T          *port,
3254    SCIC_SDS_REMOTE_DEVICE_T *device,
3255    SCIC_SDS_REQUEST_T       *io_request
3256 )
3257 {
3258    scic_sds_port_decrement_request_count(port);
3259 
3260    if (port->started_request_count == 0)
3261    {
3262       sci_base_state_machine_change_state(
3263          &port->ready_substate_machine,
3264          SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL
3265       );
3266    }
3267 
3268    return SCI_SUCCESS;
3269 }
3270 
3271 // ---------------------------------------------------------------------------
3272 
3273 SCIC_SDS_PORT_STATE_HANDLER_T
3274    scic_sds_port_ready_substate_handler_table[SCIC_SDS_PORT_READY_MAX_SUBSTATES] =
3275 {
3276    // SCIC_SDS_PORT_READY_SUBSTATE_WAITING
3277    {
3278       {
3279          scic_sds_port_default_start_handler,
3280          scic_sds_port_ready_substate_stop_handler,
3281          scic_sds_port_default_destruct_handler,
3282          scic_sds_port_default_reset_handler,
3283          scic_sds_port_ready_substate_add_phy_handler,
3284          scic_sds_port_default_remove_phy_handler
3285       },
3286       scic_sds_port_default_frame_handler,
3287       scic_sds_port_default_event_handler,
3288       scic_sds_port_ready_waiting_substate_link_up_handler,
3289       scic_sds_port_default_link_down_handler,
3290       scic_sds_port_ready_waiting_substate_start_io_handler,
3291       scic_sds_port_ready_substate_complete_io_handler,
3292    },
3293    // SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL
3294    {
3295       {
3296          scic_sds_port_default_start_handler,
3297          scic_sds_port_ready_substate_stop_handler,
3298          scic_sds_port_default_destruct_handler,
3299          scic_sds_port_ready_operational_substate_reset_handler,
3300          scic_sds_port_ready_substate_add_phy_handler,
3301          scic_sds_port_ready_substate_remove_phy_handler
3302       },
3303       scic_sds_port_default_frame_handler,
3304       scic_sds_port_default_event_handler,
3305       scic_sds_port_ready_operational_substate_link_up_handler,
3306       scic_sds_port_ready_operational_substate_link_down_handler,
3307       scic_sds_port_ready_operational_substate_start_io_handler,
3308       scic_sds_port_ready_substate_complete_io_handler
3309    },
3310    // SCIC_SDS_PORT_READY_SUBSTATE_CONFIGURING
3311    {
3312       {
3313          scic_sds_port_default_start_handler,
3314          scic_sds_port_ready_substate_stop_handler,
3315          scic_sds_port_default_destruct_handler,
3316          scic_sds_port_default_reset_handler,
3317          scic_sds_port_ready_configuring_substate_add_phy_handler,
3318          scic_sds_port_ready_configuring_substate_remove_phy_handler
3319       },
3320       scic_sds_port_default_frame_handler,
3321       scic_sds_port_default_event_handler,
3322       scic_sds_port_default_link_up_handler,
3323       scic_sds_port_default_link_down_handler,
3324       scic_sds_port_default_start_io_handler,
3325       scic_sds_port_ready_configuring_substate_complete_io_handler
3326    }
3327 };
3328 
3329 /**
3330  * This macro sets the port ready substate handlers.
3331  */
3332 #define scic_sds_port_set_ready_state_handlers(port, state_id) \
3333    scic_sds_port_set_state_handlers( \
3334       port, &scic_sds_port_ready_substate_handler_table[(state_id)] \
3335    )
3336 
3337 //******************************************************************************
3338 //*  PORT STATE PRIVATE METHODS
3339 //******************************************************************************
3340 
3341 /**
3342  * This method will susped the port task scheduler for this port object.
3343  *
3344  * @param[in] this_port This is the SCIC_SDS_PORT object to suspend.
3345  *
3346  * @return none
3347  */
3348 void scic_sds_port_suspend_port_task_scheduler(
3349    SCIC_SDS_PORT_T *this_port
3350 )
3351 {
3352    U32 pts_control_value;
3353 
3354    pts_control_value = scu_port_task_scheduler_read(this_port, control);
3355    pts_control_value |= SCU_PTSxCR_GEN_BIT(SUSPEND);
3356    scu_port_task_scheduler_write(this_port, control, pts_control_value);
3357 }
3358 
3359 /**
3360  * This method will resume the port task scheduler for this port object.
3361  *
3362  * @param[in] this_port This is the SCIC_SDS_PORT object to resume.
3363  *
3364  * @return none
3365  */
3366 void scic_sds_port_resume_port_task_scheduler(
3367    SCIC_SDS_PORT_T *this_port
3368 )
3369 {
3370    U32 pts_control_value;
3371 
3372    pts_control_value = scu_port_task_scheduler_read(this_port, control);
3373 
3374    pts_control_value &= ~SCU_PTSxCR_GEN_BIT(SUSPEND);
3375 
3376    scu_port_task_scheduler_write(this_port, control, pts_control_value);
3377 }
3378 
3379 /**
3380  * This routine will post the dummy request.  This will prevent the hardware
3381  * scheduler from posting new requests to the front of the scheduler queue
3382  * causing a starvation problem for currently ongoing requests.
3383  *
3384  * @parm[in] this_port The port on which the task must be posted.
3385  *
3386  * @return none
3387  */
3388 static
3389 void scic_sds_port_post_dummy_request(
3390    SCIC_SDS_PORT_T *this_port
3391 )
3392 {
3393    U32 command;
3394    SCU_TASK_CONTEXT_T * task_context;
3395 
3396    if (this_port->reserved_tci != SCU_DUMMY_INDEX)
3397    {
3398    task_context = scic_sds_controller_get_task_context_buffer(
3399                      this_port->owning_controller,
3400                      this_port->reserved_tci
3401                   );
3402 
3403    task_context->abort = 0;
3404 
3405    command = (
3406          (SCU_CONTEXT_COMMAND_REQUEST_TYPE_POST_TC)
3407       | (this_port->physical_port_index << SCU_CONTEXT_COMMAND_LOGICAL_PORT_SHIFT)
3408       | (this_port->reserved_tci)
3409    );
3410 
3411    scic_sds_controller_post_request(this_port->owning_controller, command);
3412 }
3413 }
3414 
3415 /**
3416  * This routine will abort the dummy request.  This will alow the hardware to
3417  * power down parts of the silicon to save power.
3418  *
3419  * @parm[in] this_port The port on which the task must be aborted.
3420  *
3421  * @return none
3422  */
3423 static
3424 void scic_sds_port_abort_dummy_request(
3425    SCIC_SDS_PORT_T *this_port
3426 )
3427 {
3428    U32 command;
3429    SCU_TASK_CONTEXT_T * task_context;
3430 
3431    if (this_port->reserved_tci != SCU_DUMMY_INDEX)
3432    {
3433    task_context = scic_sds_controller_get_task_context_buffer(
3434                      this_port->owning_controller,
3435                      this_port->reserved_tci
3436                   );
3437 
3438    task_context->abort = 1;
3439 
3440    command = (
3441         (SCU_CONTEXT_COMMAND_REQUEST_POST_TC_ABORT)
3442       | (this_port->physical_port_index << SCU_CONTEXT_COMMAND_LOGICAL_PORT_SHIFT)
3443       | (this_port->reserved_tci)
3444    );
3445 
3446    scic_sds_controller_post_request(this_port->owning_controller, command);
3447 }
3448 }
3449 
3450 //******************************************************************************
3451 //*  PORT READY SUBSTATE METHODS
3452 //******************************************************************************
3453 
3454 /**
3455  * This method will perform the actions required by the SCIC_SDS_PORT on
3456  * entering the SCIC_SDS_PORT_READY_SUBSTATE_WAITING. This function checks the
3457  * port for any ready phys.  If there is at least one phy in a ready state
3458  * then the port transitions to the ready operational substate.
3459  *
3460  * @param[in] object This is the SCI_BASE_OBJECT which is cast to a
3461  *       SCIC_SDS_PORT object.
3462  *
3463  * @return none
3464  */
3465 static
3466 void scic_sds_port_ready_substate_waiting_enter(
3467    SCI_BASE_OBJECT_T *object
3468 )
3469 {
3470    SCIC_SDS_PORT_T *this_port = (SCIC_SDS_PORT_T *)object;
3471 
3472    scic_sds_port_set_ready_state_handlers(
3473       this_port, SCIC_SDS_PORT_READY_SUBSTATE_WAITING
3474    );
3475 
3476    scic_sds_port_suspend_port_task_scheduler(this_port);
3477 
3478 
3479    this_port->not_ready_reason = SCIC_PORT_NOT_READY_NO_ACTIVE_PHYS;
3480 
3481    if (this_port->active_phy_mask != 0)
3482    {
3483       // At least one of the phys on the port is ready
3484       sci_base_state_machine_change_state(
3485          &this_port->ready_substate_machine,
3486          SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL
3487       );
3488    }
3489 }
3490 
3491 /**
3492  * This method will perform the actions required by the SCIC_SDS_PORT on
3493  * exiting the SCIC_SDS_PORT_READY_SUBSTATE_WAITING. This function resume the
3494  * PTSG that was suspended at the entry of this state.
3495  *
3496  * @param[in] object This is the SCI_BASE_OBJECT which is cast to a
3497  *       SCIC_SDS_PORT object.
3498  *
3499  * @return none
3500  */
3501 static
3502 void scic_sds_port_ready_substate_waiting_exit(
3503    SCI_BASE_OBJECT_T *object
3504 )
3505 {
3506    SCIC_SDS_PORT_T *this_port = (SCIC_SDS_PORT_T *)object;
3507    scic_sds_port_resume_port_task_scheduler(this_port);
3508 }
3509 
3510 /**
3511  * This method will perform the actions required by the SCIC_SDS_PORT on
3512  * entering the SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL. This function sets
3513  * the state handlers for the port object, notifies the SCI User that the port
3514  * is ready, and resumes port operations.
3515  *
3516  * @param[in] object This is the SCI_BASE_OBJECT which is cast to a
3517  *       SCIC_SDS_PORT object.
3518  *
3519  * @return none
3520  */
3521 static
3522 void scic_sds_port_ready_substate_operational_enter(
3523    SCI_BASE_OBJECT_T *object
3524 )
3525 {
3526    U32 index;
3527    SCIC_SDS_PORT_T *this_port = (SCIC_SDS_PORT_T *)object;
3528 
3529    scic_sds_port_set_ready_state_handlers(
3530       this_port, SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL
3531    );
3532 
3533    scic_cb_port_ready(
3534       scic_sds_port_get_controller(this_port), this_port
3535    );
3536 
3537    for (index = 0; index < SCI_MAX_PHYS; index++)
3538    {
3539       if (this_port->phy_table[index] != NULL)
3540       {
3541          scic_sds_port_write_phy_assignment(
3542             this_port, this_port->phy_table[index]
3543          );
3544 
3545          //if the bit at the index location for active phy mask is set and
3546          //enabled_phy_mask is not set then resume the phy
3547          if ( ( (this_port->active_phy_mask ^ this_port->enabled_phy_mask) & (1 << index) ) != 0)
3548          {
3549             scic_sds_port_resume_phy (
3550                this_port,
3551                this_port->phy_table[index]
3552             );
3553          }
3554       }
3555    }
3556 
3557    scic_sds_port_update_viit_entry(this_port);
3558 
3559    // Post the dummy task for the port so the hardware can schedule
3560    // io correctly
3561    scic_sds_port_post_dummy_request(this_port);
3562 }
3563 
3564 /**
3565  * This method will perform the actions required by the SCIC_SDS_PORT on
3566  * exiting the SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL. This function reports
3567  * the port not ready and suspends the port task scheduler.
3568  *
3569  * @param[in] object This is the SCI_BASE_OBJECT which is cast to a
3570  *       SCIC_SDS_PORT object.
3571  *
3572  * @return none
3573  */
3574 static
3575 void scic_sds_port_ready_substate_operational_exit(
3576    SCI_BASE_OBJECT_T *object
3577 )
3578 {
3579    SCIC_SDS_PORT_T *this_port = (SCIC_SDS_PORT_T *)object;
3580 
3581    // Kill the dummy task for this port if it has not yet posted
3582    // the hardware will treat this as a NOP and just return abort
3583    // complete.
3584    scic_sds_port_abort_dummy_request(this_port);
3585 
3586    scic_cb_port_not_ready(
3587       scic_sds_port_get_controller(this_port),
3588       this_port,
3589       this_port->not_ready_reason
3590    );
3591 }
3592 
3593 //******************************************************************************
3594 //*  PORT READY CONFIGURING METHODS
3595 //******************************************************************************
3596 
3597 /**
3598  * This method will perform the actions required by the SCIC_SDS_PORT on
3599  * exiting the SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL. This function reports
3600  * the port not ready and suspends the port task scheduler.
3601  *
3602  * @param[in] object This is the SCI_BASE_OBJECT which is cast to a
3603  *       SCIC_SDS_PORT object.
3604  *
3605  * @return none
3606  */
3607 static
3608 void scic_sds_port_ready_substate_configuring_enter(
3609    SCI_BASE_OBJECT_T *object
3610 )
3611 {
3612    SCIC_SDS_PORT_T *this_port = (SCIC_SDS_PORT_T *)object;
3613 
3614    scic_sds_port_set_ready_state_handlers(
3615       this_port, SCIC_SDS_PORT_READY_SUBSTATE_CONFIGURING
3616    );
3617 
3618    if (this_port->active_phy_mask == 0)
3619    {
3620       scic_cb_port_not_ready(
3621          scic_sds_port_get_controller(this_port),
3622          this_port,
3623          SCIC_PORT_NOT_READY_NO_ACTIVE_PHYS
3624       );
3625 
3626       sci_base_state_machine_change_state(
3627          &this_port->ready_substate_machine,
3628          SCIC_SDS_PORT_READY_SUBSTATE_WAITING
3629       );
3630    }
3631    //do not wait for IO to go to 0 in this state.
3632    else
3633    {
3634       sci_base_state_machine_change_state(
3635          &this_port->ready_substate_machine,
3636          SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL
3637       );
3638    }
3639 }
3640 
3641 // ---------------------------------------------------------------------------
3642 
3643 SCI_BASE_STATE_T
3644    scic_sds_port_ready_substate_table[SCIC_SDS_PORT_READY_MAX_SUBSTATES] =
3645 {
3646    {
3647       SCIC_SDS_PORT_READY_SUBSTATE_WAITING,
3648       scic_sds_port_ready_substate_waiting_enter,
3649       scic_sds_port_ready_substate_waiting_exit
3650    },
3651    {
3652       SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL,
3653       scic_sds_port_ready_substate_operational_enter,
3654       scic_sds_port_ready_substate_operational_exit
3655    },
3656    {
3657       SCIC_SDS_PORT_READY_SUBSTATE_CONFIGURING,
3658       scic_sds_port_ready_substate_configuring_enter,
3659       NULL
3660    }
3661 };
3662 
3663