1/*-
2 * SPDX-License-Identifier: BSD-2-Clause OR GPL-2.0
3 *
4 * This file is provided under a dual BSD/GPLv2 license.  When using or
5 * redistributing this file, you may do so under either license.
6 *
7 * GPL LICENSE SUMMARY
8 *
9 * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of version 2 of the GNU General Public License as
13 * published by the Free Software Foundation.
14 *
15 * This program is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
23 * The full GNU General Public License is included in this distribution
24 * in the file called LICENSE.GPL.
25 *
26 * BSD LICENSE
27 *
28 * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
29 * All rights reserved.
30 *
31 * Redistribution and use in source and binary forms, with or without
32 * modification, are permitted provided that the following conditions
33 * are met:
34 *
35 *   * Redistributions of source code must retain the above copyright
36 *     notice, this list of conditions and the following disclaimer.
37 *   * Redistributions in binary form must reproduce the above copyright
38 *     notice, this list of conditions and the following disclaimer in
39 *     the documentation and/or other materials provided with the
40 *     distribution.
41 *
42 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
43 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
44 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
45 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
46 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
47 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
48 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
49 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
50 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
51 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
52 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
53 */
54
55#include <sys/cdefs.h>
56__FBSDID("$FreeBSD$");
57
58/**
59 * @file
60 *
61 * @brief This file contains the implementation for the public and protected
62 *        methods for the port configuration agent.
63 */
64
65#include <dev/isci/scil/scic_controller.h>
66#include <dev/isci/scil/scic_sds_logger.h>
67#include <dev/isci/scil/scic_sds_controller.h>
68#include <dev/isci/scil/scic_sds_port_configuration_agent.h>
69
70#define SCIC_SDS_MPC_RECONFIGURATION_TIMEOUT    (10)
71#define SCIC_SDS_APC_RECONFIGURATION_TIMEOUT    (10)
72#define SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION  (250)
73
74enum SCIC_SDS_APC_ACTIVITY
75{
76   SCIC_SDS_APC_SKIP_PHY,
77   SCIC_SDS_APC_ADD_PHY,
78   SCIC_SDS_APC_START_TIMER,
79
80   SCIC_SDS_APC_ACTIVITY_MAX
81};
82
83//******************************************************************************
84// General port configuration agent routines
85//******************************************************************************
86
87/**
88 * Compare the two SAS Address and
89 * if SAS Address One is greater than SAS Address Two then return > 0
90 * else if SAS Address One is less than SAS Address Two return < 0
91 * Otherwise they are the same return 0
92 *
93 * @param[in] address_one A SAS Address to be compared.
94 * @param[in] address_two A SAS Address to be compared.
95 *
96 * @return A signed value of x > 0 > y where
97 *         x is returned for Address One > Address Two
98 *         y is returned for Address One < Address Two
99 *         0 is returned ofr Address One = Address Two
100 */
101static
102S32 sci_sas_address_compare(
103   SCI_SAS_ADDRESS_T address_one,
104   SCI_SAS_ADDRESS_T address_two
105)
106{
107   if (address_one.high > address_two.high)
108   {
109      return 1;
110   }
111   else if (address_one.high < address_two.high)
112   {
113      return -1;
114   }
115   else if (address_one.low > address_two.low)
116   {
117      return 1;
118   }
119   else if (address_one.low < address_two.low)
120   {
121      return -1;
122   }
123
124   // The two SAS Address must be identical
125   return 0;
126}
127
128/**
129 * This routine will find a matching port for the phy.  This means that the
130 * port and phy both have the same broadcast sas address and same received
131 * sas address.
132 *
133 * @param[in] controller The controller object used for the port search.
134 * @param[in] phy The phy object to match.
135 *
136 * @return The port address or the SCI_INVALID_HANDLE if there is no matching
137 *         port.
138 *
139 * @retvalue port address if the port can be found to match the phy.
140 * @retvalue SCI_INVALID_HANDLE if there is no matching port for the phy.
141 */
142static
143SCIC_SDS_PORT_T * scic_sds_port_configuration_agent_find_port(
144   SCIC_SDS_CONTROLLER_T * controller,
145   SCIC_SDS_PHY_T        * phy
146)
147{
148   U8 port_index;
149   SCI_PORT_HANDLE_T port_handle;
150   SCI_SAS_ADDRESS_T port_sas_address;
151   SCI_SAS_ADDRESS_T port_attached_device_address;
152   SCI_SAS_ADDRESS_T phy_sas_address;
153   SCI_SAS_ADDRESS_T phy_attached_device_address;
154
155   SCIC_LOG_TRACE((
156      sci_base_object_get_logger(controller),
157      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY,
158      "scic_sds_port_confgiruation_agent_find_port(0x%08x, 0x%08x) enter\n",
159      controller, phy
160   ));
161
162   // Since this phy can be a member of a wide port check to see if one or
163   // more phys match the sent and received SAS address as this phy in which
164   // case it should participate in the same port.
165   scic_sds_phy_get_sas_address(phy, &phy_sas_address);
166   scic_sds_phy_get_attached_sas_address(phy, &phy_attached_device_address);
167
168   for (port_index = 0; port_index < SCI_MAX_PORTS; port_index++)
169   {
170      if (scic_controller_get_port_handle(controller, port_index, &port_handle) == SCI_SUCCESS)
171      {
172         SCIC_SDS_PORT_T * port = (SCIC_SDS_PORT_T *)port_handle;
173
174         scic_sds_port_get_sas_address(port, &port_sas_address);
175         scic_sds_port_get_attached_sas_address(port, &port_attached_device_address);
176
177         if (
178               (sci_sas_address_compare(port_sas_address, phy_sas_address) == 0)
179            && (sci_sas_address_compare(port_attached_device_address, phy_attached_device_address) == 0)
180            )
181         {
182            return port;
183         }
184      }
185   }
186
187   return SCI_INVALID_HANDLE;
188}
189
190/**
191 * This routine will validate the port configuration is correct for the SCU
192 * hardware.  The SCU hardware allows for port configurations as follows.
193 *    LP0 -> (PE0), (PE0, PE1), (PE0, PE1, PE2, PE3)
194 *    LP1 -> (PE1)
195 *    LP2 -> (PE2), (PE2, PE3)
196 *    LP3 -> (PE3)
197 *
198 * @param[in] controller This is the controller object that contains the
199 *            port agent
200 * @param[in] port_agent This is the port configruation agent for
201 *            the controller.
202 *
203 * @return SCI_STATUS
204 * @retval SCI_SUCCESS the port configuration is valid for this
205 *         port configuration agent.
206 * @retval SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION the port configuration
207 *         is not valid for this port configuration agent.
208 */
209static
210SCI_STATUS scic_sds_port_configuration_agent_validate_ports(
211   SCIC_SDS_CONTROLLER_T               * controller,
212   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent
213)
214{
215#if !defined(ARLINGTON_BUILD)
216   SCI_SAS_ADDRESS_T first_address;
217   SCI_SAS_ADDRESS_T second_address;
218
219   SCIC_LOG_TRACE((
220      sci_base_object_get_logger(controller),
221      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT,
222      "scic_sds_port_configuration_agent_validate_ports(0x%08x, 0x%08x) enter\n",
223      controller, port_agent
224   ));
225
226   // Sanity check the max ranges for all the phys the max index
227   // is always equal to the port range index
228   if (
229         (port_agent->phy_valid_port_range[0].max_index != 0)
230      || (port_agent->phy_valid_port_range[1].max_index != 1)
231      || (port_agent->phy_valid_port_range[2].max_index != 2)
232      || (port_agent->phy_valid_port_range[3].max_index != 3)
233      )
234   {
235      return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
236   }
237
238   // This is a request to configure a single x4 port or at least attempt
239   // to make all the phys into a single port
240   if (
241         (port_agent->phy_valid_port_range[0].min_index == 0)
242      && (port_agent->phy_valid_port_range[1].min_index == 0)
243      && (port_agent->phy_valid_port_range[2].min_index == 0)
244      && (port_agent->phy_valid_port_range[3].min_index == 0)
245      )
246   {
247      return SCI_SUCCESS;
248   }
249
250   // This is a degenerate case where phy 1 and phy 2 are assigned
251   // to the same port this is explicitly disallowed by the hardware
252   // unless they are part of the same x4 port and this condition was
253   // already checked above.
254   if (port_agent->phy_valid_port_range[2].min_index == 1)
255   {
256      return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
257   }
258
259   // PE0 and PE3 can never have the same SAS Address unless they
260   // are part of the same x4 wide port and we have already checked
261   // for this condition.
262   scic_sds_phy_get_sas_address(&controller->phy_table[0], &first_address);
263   scic_sds_phy_get_sas_address(&controller->phy_table[3], &second_address);
264
265   if (sci_sas_address_compare(first_address, second_address) == 0)
266   {
267      return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
268   }
269
270   // PE0 and PE1 are configured into a 2x1 ports make sure that the
271   // SAS Address for PE0 and PE2 are different since they can not be
272   // part of the same port.
273   if (
274         (port_agent->phy_valid_port_range[0].min_index == 0)
275      && (port_agent->phy_valid_port_range[1].min_index == 1)
276      )
277   {
278      scic_sds_phy_get_sas_address(&controller->phy_table[0], &first_address);
279      scic_sds_phy_get_sas_address(&controller->phy_table[2], &second_address);
280
281      if (sci_sas_address_compare(first_address, second_address) == 0)
282      {
283         return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
284      }
285   }
286
287   // PE2 and PE3 are configured into a 2x1 ports make sure that the
288   // SAS Address for PE1 and PE3 are different since they can not be
289   // part of the same port.
290   if (
291         (port_agent->phy_valid_port_range[2].min_index == 2)
292      && (port_agent->phy_valid_port_range[3].min_index == 3)
293      )
294   {
295      scic_sds_phy_get_sas_address(&controller->phy_table[1], &first_address);
296      scic_sds_phy_get_sas_address(&controller->phy_table[3], &second_address);
297
298      if (sci_sas_address_compare(first_address, second_address) == 0)
299      {
300         return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
301      }
302   }
303#endif // !defined(ARLINGTON_BUILD)
304
305   return SCI_SUCCESS;
306}
307
308//******************************************************************************
309// Manual port configuration agent routines
310//******************************************************************************
311
312/**
313 * This routine will verify that all of the phys in the same port are using
314 * the same SAS address.
315 *
316 * @param[in] controller This is the controller that contains the PHYs to
317 *            be verified.
318 */
319static
320SCI_STATUS scic_sds_mpc_agent_validate_phy_configuration(
321   SCIC_SDS_CONTROLLER_T               * controller,
322   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent
323)
324{
325   U32 phy_mask;
326   U32 assigned_phy_mask;
327   SCI_SAS_ADDRESS_T sas_address;
328   SCI_SAS_ADDRESS_T phy_assigned_address;
329   U8 port_index;
330   U8 phy_index;
331
332   assigned_phy_mask = 0;
333   sas_address.high = 0;
334   sas_address.low = 0;
335
336   SCIC_LOG_TRACE((
337      sci_base_object_get_logger(controller),
338      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT,
339      "scic_sds_mpc_agent_validate_phy_configuration(0x%08x, 0x%08x) enter\n",
340      controller, port_agent
341   ));
342
343   for (port_index = 0; port_index < SCI_MAX_PORTS; port_index++)
344   {
345      phy_mask = controller->oem_parameters.sds1.ports[port_index].phy_mask;
346
347      if (phy_mask != 0)
348      {
349         // Make sure that one or more of the phys were not already assinged to
350         // a different port.
351         if ((phy_mask & ~assigned_phy_mask) == 0)
352         {
353            return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
354         }
355
356         // Find the starting phy index for this round through the loop
357         for (phy_index = 0; phy_index < SCI_MAX_PHYS; phy_index++)
358         {
359            if ((1 << phy_index) & phy_mask)
360            {
361               scic_sds_phy_get_sas_address(
362                  &controller->phy_table[phy_index], &sas_address
363               );
364
365               // The phy_index can be used as the starting point for the
366               // port range since the hardware starts all logical ports
367               // the same as the PE index.
368               port_agent->phy_valid_port_range[phy_index].min_index = port_index;
369               port_agent->phy_valid_port_range[phy_index].max_index = phy_index;
370
371               if (phy_index != port_index)
372               {
373                  return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
374               }
375
376               break;
377            }
378         }
379
380         // See how many additional phys are being added to this logical port.
381         // Note: We have not moved the current phy_index so we will actually
382         //       compare the startting phy with itself.
383         //       This is expected and required to add the phy to the port.
384         while (phy_index < SCI_MAX_PHYS)
385         {
386            if ((1 << phy_index) & phy_mask)
387            {
388               scic_sds_phy_get_sas_address(
389                  &controller->phy_table[phy_index], &phy_assigned_address
390               );
391
392               if (sci_sas_address_compare(sas_address, phy_assigned_address) != 0)
393               {
394                  // The phy mask specified that this phy is part of the same port
395                  // as the starting phy and it is not so fail this configuration
396                  return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
397               }
398
399               port_agent->phy_valid_port_range[phy_index].min_index = port_index;
400               port_agent->phy_valid_port_range[phy_index].max_index = phy_index;
401
402               scic_sds_port_add_phy(
403                  &controller->port_table[port_index],
404                  &controller->phy_table[phy_index]
405               );
406
407               assigned_phy_mask |= (1 << phy_index);
408            }
409
410            phy_index++;
411         }
412      }
413   }
414
415   return scic_sds_port_configuration_agent_validate_ports(controller, port_agent);
416}
417
418/**
419 * This timer routine is used to allow the SCI User to rediscover or change
420 * device objects before a new series of link up notifications because a
421 * link down has allowed a better port configuration.
422 *
423 * @param[in] controller This is the core controller object which is used
424 *            to obtain the port configuration agent.
425 */
426static
427void scic_sds_mpc_agent_timeout_handler(
428   void * object
429)
430{
431   U8 index;
432   SCIC_SDS_CONTROLLER_T * controller = (SCIC_SDS_CONTROLLER_T *)object;
433   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent = &controller->port_agent;
434   U16 configure_phy_mask;
435
436   SCIC_LOG_TRACE((
437      sci_base_object_get_logger(controller),
438      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT,
439      "scic_sds_mpc_agent_timeout_handler(0x%08x) enter\n",
440      controller
441   ));
442
443   port_agent->timer_pending = FALSE;
444
445   // Find the mask of phys that are reported read but as yet unconfigured into a port
446   configure_phy_mask = ~port_agent->phy_configured_mask & port_agent->phy_ready_mask;
447
448   for (index = 0; index < SCI_MAX_PHYS; index++)
449   {
450      if (configure_phy_mask & (1 << index))
451      {
452         port_agent->link_up_handler(
453                        controller,
454                        port_agent,
455                        scic_sds_phy_get_port(&controller->phy_table[index]),
456                        &controller->phy_table[index]
457                     );
458      }
459   }
460}
461
462/**
463 * This method handles the manual port configuration link up notifications.
464 * Since all ports and phys are associate at initialization time we just turn
465 * around and notifiy the port object that there is a link up.  If this PHY is
466 * not associated with a port there is no action taken.
467 *
468 * @param[in] controller This is the controller object that receives the
469 *            link up notification.
470 * @param[in] port This is the port object associated with the phy.  If the
471 *            is no associated port this is an SCI_INVALID_HANDLE.
472 * @param[in] phy This is the phy object which has gone ready.
473 *
474 * @note Is it possible to get a link up notification from a phy that has
475 *       no assocoated port?
476 */
477static
478void scic_sds_mpc_agent_link_up(
479   SCIC_SDS_CONTROLLER_T               * controller,
480   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent,
481   SCIC_SDS_PORT_T                     * port,
482   SCIC_SDS_PHY_T                      * phy
483)
484{
485   SCIC_LOG_TRACE((
486      sci_base_object_get_logger(controller),
487      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY,
488      "scic_sds_mpc_agent_link_up(0x%08x, 0x%08x, 0x%08x, 0x%08x) enter\n",
489      controller, port_agent, port, phy
490   ));
491
492   // If the port has an invalid handle then the phy was not assigned to
493   // a port.  This is because the phy was not given the same SAS Address
494   // as the other PHYs in the port.
495   if (port != SCI_INVALID_HANDLE)
496   {
497      port_agent->phy_ready_mask |= (1 << scic_sds_phy_get_index(phy));
498
499      scic_sds_port_link_up(port, phy);
500
501      if ((port->active_phy_mask & (1 << scic_sds_phy_get_index(phy))) != 0)
502      {
503         port_agent->phy_configured_mask |= (1 << scic_sds_phy_get_index(phy));
504      }
505   }
506}
507
508/**
509 * This method handles the manual port configuration link down notifications.
510 * Since all ports and phys are associated at initialization time we just turn
511 * around and notifiy the port object of the link down event.  If this PHY is
512 * not associated with a port there is no action taken.
513 *
514 * @param[in] controller This is the controller object that receives the
515 *            link down notification.
516 * @param[in] port This is the port object associated with the phy.  If the
517 *            is no associated port this is an SCI_INVALID_HANDLE.  The port
518 *            is an invalid handle only if the phy was never port of this
519 *            port.  This happens when the phy is not broadcasting the same
520 *            SAS address as the other phys in the assigned port.
521 * @param[in] phy This is the phy object which has gone link down.
522 *
523 * @note Is it possible to get a link down notification from a phy that has
524 *       no assocoated port?
525 */
526static
527void scic_sds_mpc_agent_link_down(
528   SCIC_SDS_CONTROLLER_T               * controller,
529   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent,
530   SCIC_SDS_PORT_T                     * port,
531   SCIC_SDS_PHY_T                      * phy
532)
533{
534   SCIC_LOG_TRACE((
535      sci_base_object_get_logger(controller),
536      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY,
537      "scic_sds_mpc_agent_link_down(0x%08x, 0x%08x, 0x%08x, 0x%08x) enter\n",
538      controller, port_agent, port, phy
539   ));
540
541   if (port != SCI_INVALID_HANDLE)
542   {
543      // If we can form a new port from the remainder of the phys then we want
544      // to start the timer to allow the SCI User to cleanup old devices and
545      // rediscover the port before rebuilding the port with the phys that
546      // remain in the ready state.
547      port_agent->phy_ready_mask &= ~(1 << scic_sds_phy_get_index(phy));
548      port_agent->phy_configured_mask &= ~(1 << scic_sds_phy_get_index(phy));
549
550      // Check to see if there are more phys waiting to be configured into a port.
551      // If there are allow the SCI User to tear down this port, if necessary, and
552      // then reconstruc the port after the timeout.
553      if (
554            (port_agent->phy_configured_mask == 0x0000)
555         && (port_agent->phy_ready_mask != 0x0000)
556         && !port_agent->timer_pending
557         )
558      {
559         port_agent->timer_pending = TRUE;
560
561         scic_cb_timer_start(
562            controller,
563            port_agent->timer,
564            SCIC_SDS_MPC_RECONFIGURATION_TIMEOUT
565         );
566      }
567
568      scic_sds_port_link_down(port, phy);
569   }
570}
571
572//******************************************************************************
573// Automatic port configuration agent routines
574//******************************************************************************
575
576/**
577 * This routine will verify that the phys are assigned a valid SAS address for
578 * automatic port configuration mode.
579 */
580static
581SCI_STATUS scic_sds_apc_agent_validate_phy_configuration(
582   SCIC_SDS_CONTROLLER_T               * controller,
583   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent
584)
585{
586   U8 phy_index;
587   U8 port_index;
588   SCI_SAS_ADDRESS_T sas_address;
589   SCI_SAS_ADDRESS_T phy_assigned_address;
590
591   SCIC_LOG_TRACE((
592      sci_base_object_get_logger(controller),
593      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT,
594      "scic_sds_apc_agent_validate_phy_configuration(0x%08x, 0x%08x) enter\n",
595      controller, port_agent
596   ));
597
598   phy_index = 0;
599
600   while (phy_index < SCI_MAX_PHYS)
601   {
602      port_index = phy_index;
603
604      // Get the assigned SAS Address for the first PHY on the controller.
605      scic_sds_phy_get_sas_address(
606         &controller->phy_table[phy_index], &sas_address
607      );
608
609      while (++phy_index < SCI_MAX_PHYS)
610      {
611         scic_sds_phy_get_sas_address(
612            &controller->phy_table[phy_index], &phy_assigned_address
613         );
614
615         // Verify each of the SAS address are all the same for every PHY
616         if (sci_sas_address_compare(sas_address, phy_assigned_address) == 0)
617         {
618            port_agent->phy_valid_port_range[phy_index].min_index = port_index;
619            port_agent->phy_valid_port_range[phy_index].max_index = phy_index;
620         }
621         else
622         {
623            port_agent->phy_valid_port_range[phy_index].min_index = phy_index;
624            port_agent->phy_valid_port_range[phy_index].max_index = phy_index;
625            break;
626         }
627      }
628   }
629
630   return scic_sds_port_configuration_agent_validate_ports(controller, port_agent);
631}
632
633/**
634 * This routine will restart the automatic port configuration timeout
635 * timer for the next time period.  This could be caused by either a
636 * link down event or a link up event where we can not yet tell to which
637 * port a phy belongs.
638 *
639 * @param[in] controller This is the controller that to which the port
640 *            agent is assigned.
641 * @param[in] port_agent This is the port agent that is requesting the
642 *            timer start operation.
643 * @param[in] phy This is the phy that has caused the timer operation to
644 *            be scheduled.
645 * @param[in] timeout This is the timeout in ms.
646 */
647static
648void scic_sds_apc_agent_start_timer(
649   SCIC_SDS_CONTROLLER_T               * controller,
650   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent,
651   SCIC_SDS_PHY_T                      * phy,
652   U32                                   timeout
653)
654{
655   SCIC_LOG_TRACE((
656      sci_base_object_get_logger(controller),
657      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY,
658      "scic_sds_apc_agent_start_timer(0x%08x, 0x%08x, 0x%08x, 0x%08x) enter\n",
659      controller, port_agent, phy, timeout
660   ));
661
662   if (port_agent->timer_pending)
663   {
664      scic_cb_timer_stop(controller, port_agent->timer);
665   }
666
667   port_agent->timer_pending = TRUE;
668
669   scic_cb_timer_start(controller, port_agent->timer, timeout);
670}
671
672/**
673 * This method handles the automatic port configuration for link up notifications.
674 *
675 * @param[in] controller This is the controller object that receives the
676 *            link up notification.
677 * @param[in] phy This is the phy object which has gone link up.
678 * @param[in] start_timer This tells the routine if it should start the timer for
679 *            any phys that might be added to a port in the future.
680 */
681static
682void scic_sds_apc_agent_configure_ports(
683   SCIC_SDS_CONTROLLER_T               * controller,
684   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent,
685   SCIC_SDS_PHY_T                      * phy,
686   BOOL                                  start_timer
687)
688{
689   U8 port_index;
690   SCI_STATUS status;
691   SCIC_SDS_PORT_T * port;
692   SCI_PORT_HANDLE_T port_handle;
693   enum SCIC_SDS_APC_ACTIVITY apc_activity = SCIC_SDS_APC_SKIP_PHY;
694
695   SCIC_LOG_TRACE((
696      sci_base_object_get_logger(controller),
697      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY,
698      "scic_sds_apc_agent_configure_ports(0x%08x, 0x%08x, 0x%08x, %d) enter\n",
699      controller, port_agent, phy, start_timer
700   ));
701
702   port = scic_sds_port_configuration_agent_find_port(controller, phy);
703
704   if (port != SCI_INVALID_HANDLE)
705   {
706      if (scic_sds_port_is_valid_phy_assignment(port, phy->phy_index))
707         apc_activity = SCIC_SDS_APC_ADD_PHY;
708      else
709         apc_activity = SCIC_SDS_APC_SKIP_PHY;
710   }
711   else
712   {
713      // There is no matching Port for this PHY so lets search through the
714      // Ports and see if we can add the PHY to its own port or maybe start
715      // the timer and wait to see if a wider port can be made.
716      //
717      // Note the break when we reach the condition of the port id == phy id
718      for (
719             port_index = port_agent->phy_valid_port_range[phy->phy_index].min_index;
720             port_index <= port_agent->phy_valid_port_range[phy->phy_index].max_index;
721             port_index++
722          )
723      {
724         scic_controller_get_port_handle(controller, port_index, &port_handle);
725
726         port = (SCIC_SDS_PORT_T *)port_handle;
727
728         // First we must make sure that this PHY can be added to this Port.
729         if (scic_sds_port_is_valid_phy_assignment(port, phy->phy_index))
730         {
731            // Port contains a PHY with a greater PHY ID than the current
732            // PHY that has gone link up.  This phy can not be part of any
733            // port so skip it and move on.
734            if (port->active_phy_mask > (1 << phy->phy_index))
735            {
736               apc_activity = SCIC_SDS_APC_SKIP_PHY;
737               break;
738            }
739
740            // We have reached the end of our Port list and have not found
741            // any reason why we should not either add the PHY to the port
742            // or wait for more phys to become active.
743            if (port->physical_port_index == phy->phy_index)
744            {
745               // The Port either has no active PHYs.
746               // Consider that if the port had any active PHYs we would have
747               // or active PHYs with
748               // a lower PHY Id than this PHY.
749               if (apc_activity != SCIC_SDS_APC_START_TIMER)
750               {
751                  apc_activity = SCIC_SDS_APC_ADD_PHY;
752               }
753
754               break;
755            }
756
757            // The current Port has no active PHYs and this PHY could be part
758            // of this Port.  Since we dont know as yet setup to start the
759            // timer and see if there is a better configuration.
760            if (port->active_phy_mask == 0)
761            {
762               apc_activity = SCIC_SDS_APC_START_TIMER;
763            }
764         }
765         else if (port->active_phy_mask != 0)
766         {
767            // The Port has an active phy and the current Phy can not
768            // participate in this port so skip the PHY and see if
769            // there is a better configuration.
770            apc_activity = SCIC_SDS_APC_SKIP_PHY;
771         }
772      }
773   }
774
775   // Check to see if the start timer operations should instead map to an
776   // add phy operation.  This is caused because we have been waiting to
777   // add a phy to a port but could not because the automatic port
778   // configuration engine had a choice of possible ports for the phy.
779   // Since we have gone through a timeout we are going to restrict the
780   // choice to the smallest possible port.
781   if (
782         (start_timer == FALSE)
783      && (apc_activity == SCIC_SDS_APC_START_TIMER)
784      )
785   {
786      apc_activity = SCIC_SDS_APC_ADD_PHY;
787   }
788
789   switch (apc_activity)
790   {
791   case SCIC_SDS_APC_ADD_PHY:
792      status = scic_sds_port_add_phy(port, phy);
793
794      if (status == SCI_SUCCESS)
795      {
796         port_agent->phy_configured_mask |= (1 << phy->phy_index);
797      }
798      break;
799
800   case SCIC_SDS_APC_START_TIMER:
801      scic_sds_apc_agent_start_timer(
802         controller, port_agent, phy, SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION
803      );
804      break;
805
806   case SCIC_SDS_APC_SKIP_PHY:
807   default:
808      // do nothing the PHY can not be made part of a port at this time.
809      break;
810   }
811}
812
813/**
814 * This method handles the automatic port configuration for link up notifications.
815 *
816 * @param[in] controller This is the controller object that receives the
817 *            link up notification.
818 * @param[in] port This is the port object associated with the phy.  If the
819 *            is no associated port this is an SCI_INVALID_HANDLE.
820 * @param[in] phy This is the phy object which has gone link up.
821 *
822 * @note Is it possible to get a link down notification from a phy that has
823 *       no assocoated port?
824 */
825static
826void scic_sds_apc_agent_link_up(
827   SCIC_SDS_CONTROLLER_T               * controller,
828   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent,
829   SCIC_SDS_PORT_T                     * port,
830   SCIC_SDS_PHY_T                      * phy
831)
832{
833   SCIC_LOG_TRACE((
834      sci_base_object_get_logger(controller),
835      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY,
836      "scic_sds_apc_agent_link_up(0x%08x, 0x%08x, 0x%08x, 0x%08x) enter\n",
837      controller, port_agent, port, phy
838   ));
839
840   //the phy is not the part of this port, configure the port with this phy
841   if (port == SCI_INVALID_HANDLE)
842   {
843      port_agent->phy_ready_mask |= (1 << scic_sds_phy_get_index(phy));
844
845      scic_sds_apc_agent_start_timer(
846         controller, port_agent, phy, SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION
847      );
848   }
849   else
850   {
851      //the phy is already the part of the port
852
853      //if the PORT'S state is resetting then the link up is from port hard reset
854      //in this case, we need to tell the port that link up is received
855      if (  SCI_BASE_PORT_STATE_RESETTING
856            == port->parent.state_machine.current_state_id
857         )
858      {
859         //notify the port that port needs to be ready
860         port_agent->phy_ready_mask |= (1 << scic_sds_phy_get_index(phy));
861         scic_sds_port_link_up(port, phy);
862      }
863      else
864      {
865         ASSERT (0);
866      }
867   }
868}
869
870/**
871 * This method handles the automatic port configuration link down notifications.
872 * If this PHY is * not associated with a port there is no action taken.
873 *
874 * @param[in] controller This is the controller object that receives the
875 *            link down notification.
876 * @param[in] port This is the port object associated with the phy.  If the
877 *            is no associated port this is an SCI_INVALID_HANDLE.
878 * @param[in] phy This is the phy object which has gone link down.
879 *
880 * @note Is it possible to get a link down notification from a phy that has
881 *       no assocoated port?
882 */
883static
884void scic_sds_apc_agent_link_down(
885   SCIC_SDS_CONTROLLER_T               * controller,
886   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent,
887   SCIC_SDS_PORT_T                     * port,
888   SCIC_SDS_PHY_T                      * phy
889)
890{
891   SCIC_LOG_TRACE((
892      sci_base_object_get_logger(controller),
893      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY,
894      "scic_sds_apc_agent_link_down(0x%08x, 0x%08x, 0x%08x, 0x%08x) enter\n",
895      controller, port_agent, port, phy
896   ));
897
898   port_agent->phy_ready_mask &= ~(1 << scic_sds_phy_get_index(phy));
899
900   if (port != SCI_INVALID_HANDLE)
901   {
902      if (port_agent->phy_configured_mask & (1 << phy->phy_index))
903      {
904         SCI_STATUS status;
905
906         status = scic_sds_port_remove_phy(port, phy);
907
908         if (status == SCI_SUCCESS)
909         {
910            port_agent->phy_configured_mask &= ~(1 << phy->phy_index);
911         }
912      }
913   }
914}
915
916/**
917 * This routine will try to configure the phys into ports when the timer fires.
918 *
919 * @param[in] object This is actually the controller that needs to have the
920 *            pending phys configured.
921 */
922static
923void scic_sds_apc_agent_timeout_handler(
924   void * object
925)
926{
927   U32 index;
928   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent;
929   SCIC_SDS_CONTROLLER_T * controller = (SCIC_SDS_CONTROLLER_T *)object;
930   U16 configure_phy_mask;
931
932   port_agent = scic_sds_controller_get_port_configuration_agent(controller);
933
934   SCIC_LOG_TRACE((
935      sci_base_object_get_logger(controller),
936      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT,
937      "scic_sds_apc_agent_timeout_handler(0x%08x) enter\n",
938      controller
939   ));
940
941   port_agent->timer_pending = FALSE;
942
943   configure_phy_mask = ~port_agent->phy_configured_mask & port_agent->phy_ready_mask;
944
945   if (configure_phy_mask != 0x00)
946   {
947      for (index = 0; index < SCI_MAX_PHYS; index++)
948      {
949         if (configure_phy_mask & (1 << index))
950         {
951            scic_sds_apc_agent_configure_ports(
952               controller, port_agent, &controller->phy_table[index], FALSE
953            );
954         }
955      }
956
957      //Notify the controller ports are configured.
958      if (
959            (port_agent->phy_ready_mask == port_agent->phy_configured_mask) &&
960            (controller->next_phy_to_start == SCI_MAX_PHYS) &&
961            (controller->phy_startup_timer_pending == FALSE)
962         )
963      {
964         // The controller has successfully finished the start process.
965         // Inform the SCI Core user and transition to the READY state.
966         if (scic_sds_controller_is_start_complete(controller) == TRUE)
967         {
968            scic_sds_controller_port_agent_configured_ports(controller);
969         }
970      }
971   }
972}
973
974//******************************************************************************
975// Public port configuration agent routines
976//******************************************************************************
977
978/**
979 * This method will construct the port configuration agent for operation.
980 * This call is universal for both manual port configuration and automatic
981 * port configuration modes.
982 *
983 * @param[in] port_agent This is the port configuration agent for this
984 *            controller object.
985 */
986void scic_sds_port_configuration_agent_construct(
987   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent
988)
989{
990   U32 index;
991
992   port_agent->phy_configured_mask = 0x00;
993   port_agent->phy_ready_mask = 0x00;
994
995   port_agent->link_up_handler = NULL;
996   port_agent->link_down_handler = NULL;
997
998   port_agent->timer_pending = FALSE;
999   port_agent->timer = NULL;
1000
1001   for (index = 0; index < SCI_MAX_PORTS; index++)
1002   {
1003      port_agent->phy_valid_port_range[index].min_index = 0;
1004      port_agent->phy_valid_port_range[index].max_index = 0;
1005   }
1006}
1007
1008/**
1009 * This method will construct the port configuration agent for this controller.
1010 *
1011 * @param[in] controller This is the controller object for which the port
1012 *            agent is being initialized.
1013 *
1014 * @param[in] port_agent This is the port configuration agent that is being
1015 *            initialized.  The initialization path is handled differntly
1016 *            for the automatic port configuration agent and the manual port
1017 *            configuration agent.
1018 *
1019 * @return
1020 */
1021SCI_STATUS scic_sds_port_configuration_agent_initialize(
1022   SCIC_SDS_CONTROLLER_T               * controller,
1023   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent
1024)
1025{
1026   SCI_STATUS status = SCI_SUCCESS;
1027   enum SCIC_PORT_CONFIGURATION_MODE mode;
1028
1029   SCIC_LOG_TRACE((
1030      sci_base_object_get_logger(controller),
1031      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT,
1032      "scic_sds_port_configuration_agent_initialize(0x%08x, 0x%08x) enter\n",
1033      controller, port_agent
1034   ));
1035
1036   mode = controller->oem_parameters.sds1.controller.mode_type;
1037
1038   if (mode == SCIC_PORT_MANUAL_CONFIGURATION_MODE)
1039   {
1040      status = scic_sds_mpc_agent_validate_phy_configuration(controller, port_agent);
1041
1042      port_agent->link_up_handler = scic_sds_mpc_agent_link_up;
1043      port_agent->link_down_handler = scic_sds_mpc_agent_link_down;
1044
1045      port_agent->timer = scic_cb_timer_create(
1046                              controller,
1047                              scic_sds_mpc_agent_timeout_handler,
1048                              controller
1049                          );
1050   }
1051   else
1052   {
1053      status = scic_sds_apc_agent_validate_phy_configuration(controller, port_agent);
1054
1055      port_agent->link_up_handler = scic_sds_apc_agent_link_up;
1056      port_agent->link_down_handler = scic_sds_apc_agent_link_down;
1057
1058      port_agent->timer = scic_cb_timer_create(
1059                              controller,
1060                              scic_sds_apc_agent_timeout_handler,
1061                              controller
1062                          );
1063   }
1064
1065   // Make sure we have actually gotten a timer
1066   if (status == SCI_SUCCESS && port_agent->timer == NULL)
1067   {
1068      SCIC_LOG_ERROR((
1069         sci_base_object_get_logger(controller),
1070         SCIC_LOG_OBJECT_CONTROLLER,
1071         "Controller 0x%x automatic port configuration agent could not get timer.\n",
1072         controller
1073     ));
1074
1075     status = SCI_FAILURE;
1076   }
1077
1078   return status;
1079}
1080
1081/**
1082 * This method will destroy the port configuration agent for this controller.
1083 *
1084 * @param[in] controller This is the controller object for which the port
1085 *            agent is being destroyed.
1086 *
1087 * @param[in] port_agent This is the port configuration agent that is being
1088 *            destroyed.
1089 *
1090 * @return
1091 */
1092void scic_sds_port_configuration_agent_destroy(
1093   SCIC_SDS_CONTROLLER_T               * controller,
1094   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent
1095)
1096{
1097   if (port_agent->timer_pending == TRUE)
1098   {
1099      scic_cb_timer_stop(controller, port_agent->timer);
1100   }
1101
1102   scic_cb_timer_destroy(controller, port_agent->timer);
1103
1104   port_agent->timer_pending = FALSE;
1105   port_agent->timer = NULL;
1106}
1107
1108
1109/**
1110 * @brief This method release resources in for a scic port configuration agent.
1111 *
1112 * @param[in] controller This parameter specifies the core controller, one of
1113 *            its phy's resources are to be released.
1114 * @param[in] this_phy This parameter specifies the phy whose resource is to
1115 *            be released.
1116 */
1117void scic_sds_port_configuration_agent_release_resource(
1118   SCIC_SDS_CONTROLLER_T               * controller,
1119   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent
1120)
1121{
1122   SCIC_LOG_TRACE((
1123      sci_base_object_get_logger(controller),
1124      SCIC_LOG_OBJECT_PORT,
1125      "scic_sds_port_configuration_agent_release_resource(0x%x, 0x%x)\n",
1126      controller, port_agent
1127   ));
1128
1129   //Currently, the only resource to be released is a timer.
1130   if (port_agent->timer != NULL)
1131   {
1132      scic_cb_timer_destroy(controller, port_agent->timer);
1133      port_agent->timer = NULL;
1134   }
1135}
1136