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