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