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