scic_sds_port_configuration_agent.c revision 298955
1130561Sobrien/*-
2130561Sobrien * This file is provided under a dual BSD/GPLv2 license.  When using or
3130561Sobrien * redistributing this file, you may do so under either license.
4130561Sobrien *
5130561Sobrien * GPL LICENSE SUMMARY
6130561Sobrien *
7130561Sobrien * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
8130561Sobrien *
9130561Sobrien * This program is free software; you can redistribute it and/or modify
10130561Sobrien * it under the terms of version 2 of the GNU General Public License as
11130561Sobrien * published by the Free Software Foundation.
12130561Sobrien *
13130561Sobrien * This program is distributed in the hope that it will be useful, but
14130561Sobrien * WITHOUT ANY WARRANTY; without even the implied warranty of
15130561Sobrien * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16130561Sobrien * General Public License for more details.
17130561Sobrien *
18130561Sobrien * You should have received a copy of the GNU General Public License
19130561Sobrien * along with this program; if not, write to the Free Software
20130561Sobrien * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
21130561Sobrien * The full GNU General Public License is included in this distribution
22130561Sobrien * in the file called LICENSE.GPL.
23130561Sobrien *
24130561Sobrien * BSD LICENSE
25130561Sobrien *
26130561Sobrien * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
27130561Sobrien * All rights reserved.
28130561Sobrien *
29130561Sobrien * Redistribution and use in source and binary forms, with or without
30130561Sobrien * modification, are permitted provided that the following conditions
31130561Sobrien * are met:
32130561Sobrien *
33130561Sobrien *   * Redistributions of source code must retain the above copyright
34130561Sobrien *     notice, this list of conditions and the following disclaimer.
35130561Sobrien *   * Redistributions in binary form must reproduce the above copyright
36130561Sobrien *     notice, this list of conditions and the following disclaimer in
37130561Sobrien *     the documentation and/or other materials provided with the
38130561Sobrien *     distribution.
39130561Sobrien *
40130561Sobrien * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
41130561Sobrien * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
42130561Sobrien * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
43130561Sobrien * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
44130561Sobrien * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
45130561Sobrien * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
46130561Sobrien * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
47130561Sobrien * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
48130561Sobrien * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
49130561Sobrien * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
50130561Sobrien * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
51130561Sobrien */
52130561Sobrien
53130561Sobrien#include <sys/cdefs.h>
54130561Sobrien__FBSDID("$FreeBSD: head/sys/dev/isci/scil/scic_sds_port_configuration_agent.c 298955 2016-05-03 03:41:25Z pfg $");
55130561Sobrien
56130561Sobrien/**
57130561Sobrien * @file
58130561Sobrien *
59130561Sobrien * @brief This file contains the implementation for the public and protected
60130561Sobrien *        methods for the port configuration agent.
61130561Sobrien */
62130561Sobrien
63130561Sobrien#include <dev/isci/scil/scic_controller.h>
64130561Sobrien#include <dev/isci/scil/scic_sds_logger.h>
65130561Sobrien#include <dev/isci/scil/scic_sds_controller.h>
66130561Sobrien#include <dev/isci/scil/scic_sds_port_configuration_agent.h>
67130561Sobrien
68130561Sobrien#define SCIC_SDS_MPC_RECONFIGURATION_TIMEOUT    (10)
69130561Sobrien#define SCIC_SDS_APC_RECONFIGURATION_TIMEOUT    (10)
70130561Sobrien#define SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION  (250)
71130561Sobrien
72130561Sobrienenum SCIC_SDS_APC_ACTIVITY
73130561Sobrien{
74130561Sobrien   SCIC_SDS_APC_SKIP_PHY,
75130561Sobrien   SCIC_SDS_APC_ADD_PHY,
76130561Sobrien   SCIC_SDS_APC_START_TIMER,
77130561Sobrien
78130561Sobrien   SCIC_SDS_APC_ACTIVITY_MAX
79130561Sobrien};
80130561Sobrien
81130561Sobrien//******************************************************************************
82130561Sobrien// General port configuration agent routines
83130561Sobrien//******************************************************************************
84130561Sobrien
85130561Sobrien/**
86130561Sobrien * Compare the two SAS Address and
87130561Sobrien * if SAS Address One is greater than SAS Address Two then return > 0
88130561Sobrien * else if SAS Address One is less than SAS Address Two return < 0
89130561Sobrien * Otherwise they are the same return 0
90130561Sobrien *
91130561Sobrien * @param[in] address_one A SAS Address to be compared.
92130561Sobrien * @param[in] address_two A SAS Address to be compared.
93130561Sobrien *
94130561Sobrien * @return A signed value of x > 0 > y where
95130561Sobrien *         x is returned for Address One > Address Two
96130561Sobrien *         y is returned for Address One < Address Two
97130561Sobrien *         0 is returned ofr Address One = Address Two
98130561Sobrien */
99130561Sobrienstatic
100130561SobrienS32 sci_sas_address_compare(
101130561Sobrien   SCI_SAS_ADDRESS_T address_one,
102130561Sobrien   SCI_SAS_ADDRESS_T address_two
103130561Sobrien)
104130561Sobrien{
105130561Sobrien   if (address_one.high > address_two.high)
106130561Sobrien   {
107130561Sobrien      return 1;
108130561Sobrien   }
109130561Sobrien   else if (address_one.high < address_two.high)
110130561Sobrien   {
111130561Sobrien      return -1;
112130561Sobrien   }
113130561Sobrien   else if (address_one.low > address_two.low)
114130561Sobrien   {
115130561Sobrien      return 1;
116130561Sobrien   }
117130561Sobrien   else if (address_one.low < address_two.low)
118130561Sobrien   {
119130561Sobrien      return -1;
120130561Sobrien   }
121130561Sobrien
122130561Sobrien   // The two SAS Address must be identical
123130561Sobrien   return 0;
124130561Sobrien}
125130561Sobrien
126130561Sobrien/**
127130561Sobrien * This routine will find a matching port for the phy.  This means that the
128130561Sobrien * port and phy both have the same broadcast sas address and same received
129130561Sobrien * sas address.
130130561Sobrien *
131130561Sobrien * @param[in] controller The controller object used for the port search.
132130561Sobrien * @param[in] phy The phy object to match.
133130561Sobrien *
134130561Sobrien * @return The port address or the SCI_INVALID_HANDLE if there is no matching
135130561Sobrien *         port.
136130561Sobrien *
137130561Sobrien * @retvalue port address if the port can be found to match the phy.
138130561Sobrien * @retvalue SCI_INVALID_HANDLE if there is no matching port for the phy.
139130561Sobrien */
140130561Sobrienstatic
141130561SobrienSCIC_SDS_PORT_T * scic_sds_port_configuration_agent_find_port(
142130561Sobrien   SCIC_SDS_CONTROLLER_T * controller,
143130561Sobrien   SCIC_SDS_PHY_T        * phy
144130561Sobrien)
145130561Sobrien{
146130561Sobrien   U8 port_index;
147130561Sobrien   SCI_PORT_HANDLE_T port_handle;
148130561Sobrien   SCI_SAS_ADDRESS_T port_sas_address;
149130561Sobrien   SCI_SAS_ADDRESS_T port_attached_device_address;
150130561Sobrien   SCI_SAS_ADDRESS_T phy_sas_address;
151130561Sobrien   SCI_SAS_ADDRESS_T phy_attached_device_address;
152130561Sobrien
153130561Sobrien   SCIC_LOG_TRACE((
154130561Sobrien      sci_base_object_get_logger(controller),
155130561Sobrien      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY,
156130561Sobrien      "scic_sds_port_confgiruation_agent_find_port(0x%08x, 0x%08x) enter\n",
157130561Sobrien      controller, phy
158130561Sobrien   ));
159130561Sobrien
160130561Sobrien   // Since this phy can be a member of a wide port check to see if one or
161130561Sobrien   // more phys match the sent and received SAS address as this phy in which
162130561Sobrien   // case it should participate in the same port.
163130561Sobrien   scic_sds_phy_get_sas_address(phy, &phy_sas_address);
164130561Sobrien   scic_sds_phy_get_attached_sas_address(phy, &phy_attached_device_address);
165130561Sobrien
166130561Sobrien   for (port_index = 0; port_index < SCI_MAX_PORTS; port_index++)
167130561Sobrien   {
168130561Sobrien      if (scic_controller_get_port_handle(controller, port_index, &port_handle) == SCI_SUCCESS)
169130561Sobrien      {
170130561Sobrien         SCIC_SDS_PORT_T * port = (SCIC_SDS_PORT_T *)port_handle;
171130561Sobrien
172130561Sobrien         scic_sds_port_get_sas_address(port, &port_sas_address);
173130561Sobrien         scic_sds_port_get_attached_sas_address(port, &port_attached_device_address);
174130561Sobrien
175130561Sobrien         if (
176130561Sobrien               (sci_sas_address_compare(port_sas_address, phy_sas_address) == 0)
177130561Sobrien            && (sci_sas_address_compare(port_attached_device_address, phy_attached_device_address) == 0)
178130561Sobrien            )
179130561Sobrien         {
180130561Sobrien            return port;
181130561Sobrien         }
182130561Sobrien      }
183130561Sobrien   }
184130561Sobrien
185130561Sobrien   return SCI_INVALID_HANDLE;
186130561Sobrien}
187130561Sobrien
188130561Sobrien/**
189130561Sobrien * This routine will validate the port configuration is correct for the SCU
190130561Sobrien * hardware.  The SCU hardware allows for port configurations as follows.
191130561Sobrien *    LP0 -> (PE0), (PE0, PE1), (PE0, PE1, PE2, PE3)
192130561Sobrien *    LP1 -> (PE1)
193130561Sobrien *    LP2 -> (PE2), (PE2, PE3)
194130561Sobrien *    LP3 -> (PE3)
195130561Sobrien *
196130561Sobrien * @param[in] controller This is the controller object that contains the
197130561Sobrien *            port agent
198130561Sobrien * @param[in] port_agent This is the port configruation agent for
199130561Sobrien *            the controller.
200130561Sobrien *
201130561Sobrien * @return SCI_STATUS
202130561Sobrien * @retval SCI_SUCCESS the port configuration is valid for this
203130561Sobrien *         port configuration agent.
204130561Sobrien * @retval SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION the port configuration
205130561Sobrien *         is not valid for this port configuration agent.
206130561Sobrien */
207130561Sobrienstatic
208130561SobrienSCI_STATUS scic_sds_port_configuration_agent_validate_ports(
209130561Sobrien   SCIC_SDS_CONTROLLER_T               * controller,
210130561Sobrien   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent
211130561Sobrien)
212130561Sobrien{
213130561Sobrien#if !defined(ARLINGTON_BUILD)
214130561Sobrien   SCI_SAS_ADDRESS_T first_address;
215130561Sobrien   SCI_SAS_ADDRESS_T second_address;
216130561Sobrien
217130561Sobrien   SCIC_LOG_TRACE((
218130561Sobrien      sci_base_object_get_logger(controller),
219130561Sobrien      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT,
220130561Sobrien      "scic_sds_port_configuration_agent_validate_ports(0x%08x, 0x%08x) enter\n",
221130561Sobrien      controller, port_agent
222130561Sobrien   ));
223130561Sobrien
224130561Sobrien   // Sanity check the max ranges for all the phys the max index
225130561Sobrien   // is always equal to the port range index
226130561Sobrien   if (
227130561Sobrien         (port_agent->phy_valid_port_range[0].max_index != 0)
228130561Sobrien      || (port_agent->phy_valid_port_range[1].max_index != 1)
229130561Sobrien      || (port_agent->phy_valid_port_range[2].max_index != 2)
230130561Sobrien      || (port_agent->phy_valid_port_range[3].max_index != 3)
231130561Sobrien      )
232130561Sobrien   {
233130561Sobrien      return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
234130561Sobrien   }
235130561Sobrien
236130561Sobrien   // This is a request to configure a single x4 port or at least attempt
237130561Sobrien   // to make all the phys into a single port
238130561Sobrien   if (
239130561Sobrien         (port_agent->phy_valid_port_range[0].min_index == 0)
240130561Sobrien      && (port_agent->phy_valid_port_range[1].min_index == 0)
241130561Sobrien      && (port_agent->phy_valid_port_range[2].min_index == 0)
242130561Sobrien      && (port_agent->phy_valid_port_range[3].min_index == 0)
243130561Sobrien      )
244130561Sobrien   {
245130561Sobrien      return SCI_SUCCESS;
246130561Sobrien   }
247130561Sobrien
248130561Sobrien   // This is a degenerate case where phy 1 and phy 2 are assigned
249130561Sobrien   // to the same port this is explicitly disallowed by the hardware
250130561Sobrien   // unless they are part of the same x4 port and this condition was
251130561Sobrien   // already checked above.
252130561Sobrien   if (port_agent->phy_valid_port_range[2].min_index == 1)
253130561Sobrien   {
254130561Sobrien      return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
255130561Sobrien   }
256130561Sobrien
257130561Sobrien   // PE0 and PE3 can never have the same SAS Address unless they
258130561Sobrien   // are part of the same x4 wide port and we have already checked
259130561Sobrien   // for this condition.
260130561Sobrien   scic_sds_phy_get_sas_address(&controller->phy_table[0], &first_address);
261130561Sobrien   scic_sds_phy_get_sas_address(&controller->phy_table[3], &second_address);
262130561Sobrien
263130561Sobrien   if (sci_sas_address_compare(first_address, second_address) == 0)
264130561Sobrien   {
265130561Sobrien      return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
266130561Sobrien   }
267130561Sobrien
268130561Sobrien   // PE0 and PE1 are configured into a 2x1 ports make sure that the
269130561Sobrien   // SAS Address for PE0 and PE2 are different since they can not be
270130561Sobrien   // part of the same port.
271130561Sobrien   if (
272130561Sobrien         (port_agent->phy_valid_port_range[0].min_index == 0)
273130561Sobrien      && (port_agent->phy_valid_port_range[1].min_index == 1)
274130561Sobrien      )
275130561Sobrien   {
276130561Sobrien      scic_sds_phy_get_sas_address(&controller->phy_table[0], &first_address);
277130561Sobrien      scic_sds_phy_get_sas_address(&controller->phy_table[2], &second_address);
278130561Sobrien
279130561Sobrien      if (sci_sas_address_compare(first_address, second_address) == 0)
280130561Sobrien      {
281130561Sobrien         return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
282130561Sobrien      }
283130561Sobrien   }
284130561Sobrien
285130561Sobrien   // PE2 and PE3 are configured into a 2x1 ports make sure that the
286130561Sobrien   // SAS Address for PE1 and PE3 are different since they can not be
287130561Sobrien   // part of the same port.
288130561Sobrien   if (
289130561Sobrien         (port_agent->phy_valid_port_range[2].min_index == 2)
290130561Sobrien      && (port_agent->phy_valid_port_range[3].min_index == 3)
291130561Sobrien      )
292130561Sobrien   {
293130561Sobrien      scic_sds_phy_get_sas_address(&controller->phy_table[1], &first_address);
294130561Sobrien      scic_sds_phy_get_sas_address(&controller->phy_table[3], &second_address);
295130561Sobrien
296130561Sobrien      if (sci_sas_address_compare(first_address, second_address) == 0)
297130561Sobrien      {
298130561Sobrien         return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
299130561Sobrien      }
300130561Sobrien   }
301130561Sobrien#endif // !defined(ARLINGTON_BUILD)
302130561Sobrien
303130561Sobrien   return SCI_SUCCESS;
304130561Sobrien}
305130561Sobrien
306130561Sobrien//******************************************************************************
307130561Sobrien// Manual port configuration agent routines
308130561Sobrien//******************************************************************************
309130561Sobrien
310130561Sobrien/**
311130561Sobrien * This routine will verify that all of the phys in the same port are using
312130561Sobrien * the same SAS address.
313130561Sobrien *
314130561Sobrien * @param[in] controller This is the controller that contains the PHYs to
315130561Sobrien *            be verified.
316130561Sobrien */
317130561Sobrienstatic
318130561SobrienSCI_STATUS scic_sds_mpc_agent_validate_phy_configuration(
319130561Sobrien   SCIC_SDS_CONTROLLER_T               * controller,
320130561Sobrien   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent
321130561Sobrien)
322130561Sobrien{
323130561Sobrien   U32 phy_mask;
324130561Sobrien   U32 assigned_phy_mask;
325130561Sobrien   SCI_SAS_ADDRESS_T sas_address;
326130561Sobrien   SCI_SAS_ADDRESS_T phy_assigned_address;
327130561Sobrien   U8 port_index;
328130561Sobrien   U8 phy_index;
329130561Sobrien
330130561Sobrien   assigned_phy_mask = 0;
331130561Sobrien   sas_address.high = 0;
332130561Sobrien   sas_address.low = 0;
333130561Sobrien
334130561Sobrien   SCIC_LOG_TRACE((
335130561Sobrien      sci_base_object_get_logger(controller),
336130561Sobrien      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT,
337130561Sobrien      "scic_sds_mpc_agent_validate_phy_configuration(0x%08x, 0x%08x) enter\n",
338130561Sobrien      controller, port_agent
339130561Sobrien   ));
340130561Sobrien
341130561Sobrien   for (port_index = 0; port_index < SCI_MAX_PORTS; port_index++)
342130561Sobrien   {
343130561Sobrien      phy_mask = controller->oem_parameters.sds1.ports[port_index].phy_mask;
344130561Sobrien
345130561Sobrien      if (phy_mask != 0)
346130561Sobrien      {
347130561Sobrien         // Make sure that one or more of the phys were not already assinged to
348130561Sobrien         // a different port.
349130561Sobrien         if ((phy_mask & ~assigned_phy_mask) == 0)
350130561Sobrien         {
351130561Sobrien            return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
352130561Sobrien         }
353130561Sobrien
354130561Sobrien         // Find the starting phy index for this round through the loop
355130561Sobrien         for (phy_index = 0; phy_index < SCI_MAX_PHYS; phy_index++)
356130561Sobrien         {
357130561Sobrien            if ((1 << phy_index) & phy_mask)
358130561Sobrien            {
359130561Sobrien               scic_sds_phy_get_sas_address(
360130561Sobrien                  &controller->phy_table[phy_index], &sas_address
361130561Sobrien               );
362130561Sobrien
363130561Sobrien               // The phy_index can be used as the starting point for the
364130561Sobrien               // port range since the hardware starts all logical ports
365130561Sobrien               // the same as the PE index.
366130561Sobrien               port_agent->phy_valid_port_range[phy_index].min_index = port_index;
367130561Sobrien               port_agent->phy_valid_port_range[phy_index].max_index = phy_index;
368130561Sobrien
369130561Sobrien               if (phy_index != port_index)
370130561Sobrien               {
371130561Sobrien                  return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
372130561Sobrien               }
373130561Sobrien
374130561Sobrien               break;
375130561Sobrien            }
376130561Sobrien         }
377130561Sobrien
378130561Sobrien         // See how many additional phys are being added to this logical port.
379130561Sobrien         // Note: We have not moved the current phy_index so we will actually
380130561Sobrien         //       compare the startting phy with itself.
381130561Sobrien         //       This is expected and required to add the phy to the port.
382130561Sobrien         while (phy_index < SCI_MAX_PHYS)
383130561Sobrien         {
384130561Sobrien            if ((1 << phy_index) & phy_mask)
385130561Sobrien            {
386130561Sobrien               scic_sds_phy_get_sas_address(
387130561Sobrien                  &controller->phy_table[phy_index], &phy_assigned_address
388130561Sobrien               );
389130561Sobrien
390130561Sobrien               if (sci_sas_address_compare(sas_address, phy_assigned_address) != 0)
391130561Sobrien               {
392130561Sobrien                  // The phy mask specified that this phy is part of the same port
393130561Sobrien                  // as the starting phy and it is not so fail this configuration
394130561Sobrien                  return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
395130561Sobrien               }
396130561Sobrien
397130561Sobrien               port_agent->phy_valid_port_range[phy_index].min_index = port_index;
398130561Sobrien               port_agent->phy_valid_port_range[phy_index].max_index = phy_index;
399130561Sobrien
400130561Sobrien               scic_sds_port_add_phy(
401130561Sobrien                  &controller->port_table[port_index],
402130561Sobrien                  &controller->phy_table[phy_index]
403130561Sobrien               );
404130561Sobrien
405130561Sobrien               assigned_phy_mask |= (1 << phy_index);
406130561Sobrien            }
407130561Sobrien
408130561Sobrien            phy_index++;
409130561Sobrien         }
410130561Sobrien      }
411130561Sobrien   }
412130561Sobrien
413130561Sobrien   return scic_sds_port_configuration_agent_validate_ports(controller, port_agent);
414130561Sobrien}
415130561Sobrien
416130561Sobrien/**
417130561Sobrien * This timer routine is used to allow the SCI User to rediscover or change
418130561Sobrien * device objects before a new series of link up notifications because a
419130561Sobrien * link down has allowed a better port configuration.
420130561Sobrien *
421130561Sobrien * @param[in] controller This is the core controller object which is used
422130561Sobrien *            to obtain the port configuration agent.
423130561Sobrien */
424130561Sobrienstatic
425130561Sobrienvoid scic_sds_mpc_agent_timeout_handler(
426130561Sobrien   void * object
427130561Sobrien)
428130561Sobrien{
429130561Sobrien   U8 index;
430130561Sobrien   SCIC_SDS_CONTROLLER_T * controller = (SCIC_SDS_CONTROLLER_T *)object;
431130561Sobrien   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent = &controller->port_agent;
432130561Sobrien   U16 configure_phy_mask;
433130561Sobrien
434130561Sobrien   SCIC_LOG_TRACE((
435130561Sobrien      sci_base_object_get_logger(controller),
436130561Sobrien      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT,
437130561Sobrien      "scic_sds_mpc_agent_timeout_handler(0x%08x) enter\n",
438130561Sobrien      controller
439130561Sobrien   ));
440130561Sobrien
441130561Sobrien   port_agent->timer_pending = FALSE;
442130561Sobrien
443130561Sobrien   // Find the mask of phys that are reported read but as yet unconfigured into a port
444130561Sobrien   configure_phy_mask = ~port_agent->phy_configured_mask & port_agent->phy_ready_mask;
445130561Sobrien
446130561Sobrien   for (index = 0; index < SCI_MAX_PHYS; index++)
447130561Sobrien   {
448130561Sobrien      if (configure_phy_mask & (1 << index))
449130561Sobrien      {
450130561Sobrien         port_agent->link_up_handler(
451130561Sobrien                        controller,
452130561Sobrien                        port_agent,
453130561Sobrien                        scic_sds_phy_get_port(&controller->phy_table[index]),
454130561Sobrien                        &controller->phy_table[index]
455130561Sobrien                     );
456130561Sobrien      }
457130561Sobrien   }
458130561Sobrien}
459130561Sobrien
460130561Sobrien/**
461130561Sobrien * This method handles the manual port configuration link up notifications.
462130561Sobrien * Since all ports and phys are associate at initialization time we just turn
463130561Sobrien * around and notifiy the port object that there is a link up.  If this PHY is
464130561Sobrien * not associated with a port there is no action taken.
465130561Sobrien *
466130561Sobrien * @param[in] controller This is the controller object that receives the
467130561Sobrien *            link up notification.
468130561Sobrien * @param[in] port This is the port object associated with the phy.  If the
469130561Sobrien *            is no associated port this is an SCI_INVALID_HANDLE.
470130561Sobrien * @param[in] phy This is the phy object which has gone ready.
471130561Sobrien *
472130561Sobrien * @note Is it possible to get a link up notification from a phy that has
473130561Sobrien *       no assocoated port?
474130561Sobrien */
475130561Sobrienstatic
476130561Sobrienvoid scic_sds_mpc_agent_link_up(
477130561Sobrien   SCIC_SDS_CONTROLLER_T               * controller,
478130561Sobrien   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent,
479130561Sobrien   SCIC_SDS_PORT_T                     * port,
480130561Sobrien   SCIC_SDS_PHY_T                      * phy
481130561Sobrien)
482130561Sobrien{
483130561Sobrien   SCIC_LOG_TRACE((
484130561Sobrien      sci_base_object_get_logger(controller),
485130561Sobrien      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY,
486130561Sobrien      "scic_sds_mpc_agent_link_up(0x%08x, 0x%08x, 0x%08x, 0x%08x) enter\n",
487130561Sobrien      controller, port_agent, port, phy
488130561Sobrien   ));
489130561Sobrien
490130561Sobrien   // If the port has an invalid handle then the phy was not assigned to
491130561Sobrien   // a port.  This is because the phy was not given the same SAS Address
492130561Sobrien   // as the other PHYs in the port.
493130561Sobrien   if (port != SCI_INVALID_HANDLE)
494130561Sobrien   {
495130561Sobrien      port_agent->phy_ready_mask |= (1 << scic_sds_phy_get_index(phy));
496130561Sobrien
497130561Sobrien      scic_sds_port_link_up(port, phy);
498130561Sobrien
499130561Sobrien      if ((port->active_phy_mask & (1 << scic_sds_phy_get_index(phy))) != 0)
500130561Sobrien      {
501130561Sobrien         port_agent->phy_configured_mask |= (1 << scic_sds_phy_get_index(phy));
502130561Sobrien      }
503130561Sobrien   }
504130561Sobrien}
505130561Sobrien
506130561Sobrien/**
507130561Sobrien * This method handles the manual port configuration link down notifications.
508130561Sobrien * Since all ports and phys are associated at initialization time we just turn
509130561Sobrien * around and notifiy the port object of the link down event.  If this PHY is
510130561Sobrien * not associated with a port there is no action taken.
511130561Sobrien *
512130561Sobrien * @param[in] controller This is the controller object that receives the
513130561Sobrien *            link down notification.
514130561Sobrien * @param[in] port This is the port object associated with the phy.  If the
515130561Sobrien *            is no associated port this is an SCI_INVALID_HANDLE.  The port
516130561Sobrien *            is an invalid handle only if the phy was never port of this
517130561Sobrien *            port.  This happens when the phy is not broadcasting the same
518130561Sobrien *            SAS address as the other phys in the assigned port.
519130561Sobrien * @param[in] phy This is the phy object which has gone link down.
520130561Sobrien *
521130561Sobrien * @note Is it possible to get a link down notification from a phy that has
522130561Sobrien *       no assocoated port?
523130561Sobrien */
524130561Sobrienstatic
525130561Sobrienvoid scic_sds_mpc_agent_link_down(
526130561Sobrien   SCIC_SDS_CONTROLLER_T               * controller,
527130561Sobrien   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent,
528130561Sobrien   SCIC_SDS_PORT_T                     * port,
529130561Sobrien   SCIC_SDS_PHY_T                      * phy
530130561Sobrien)
531130561Sobrien{
532130561Sobrien   SCIC_LOG_TRACE((
533130561Sobrien      sci_base_object_get_logger(controller),
534130561Sobrien      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY,
535130561Sobrien      "scic_sds_mpc_agent_link_down(0x%08x, 0x%08x, 0x%08x, 0x%08x) enter\n",
536130561Sobrien      controller, port_agent, port, phy
537130561Sobrien   ));
538130561Sobrien
539130561Sobrien   if (port != SCI_INVALID_HANDLE)
540130561Sobrien   {
541130561Sobrien      // If we can form a new port from the remainder of the phys then we want
542130561Sobrien      // to start the timer to allow the SCI User to cleanup old devices and
543130561Sobrien      // rediscover the port before rebuilding the port with the phys that
544130561Sobrien      // remain in the ready state.
545130561Sobrien      port_agent->phy_ready_mask &= ~(1 << scic_sds_phy_get_index(phy));
546130561Sobrien      port_agent->phy_configured_mask &= ~(1 << scic_sds_phy_get_index(phy));
547130561Sobrien
548130561Sobrien      // Check to see if there are more phys waiting to be configured into a port.
549130561Sobrien      // If there are allow the SCI User to tear down this port, if necessary, and
550130561Sobrien      // then reconstruc the port after the timeout.
551130561Sobrien      if (
552130561Sobrien            (port_agent->phy_configured_mask == 0x0000)
553130561Sobrien         && (port_agent->phy_ready_mask != 0x0000)
554130561Sobrien         && !port_agent->timer_pending
555130561Sobrien         )
556130561Sobrien      {
557130561Sobrien         port_agent->timer_pending = TRUE;
558130561Sobrien
559130561Sobrien         scic_cb_timer_start(
560130561Sobrien            controller,
561130561Sobrien            port_agent->timer,
562130561Sobrien            SCIC_SDS_MPC_RECONFIGURATION_TIMEOUT
563130561Sobrien         );
564130561Sobrien      }
565130561Sobrien
566130561Sobrien      scic_sds_port_link_down(port, phy);
567130561Sobrien   }
568130561Sobrien}
569130561Sobrien
570130561Sobrien//******************************************************************************
571130561Sobrien// Automatic port configuration agent routines
572130561Sobrien//******************************************************************************
573130561Sobrien
574130561Sobrien/**
575130561Sobrien * This routine will verify that the phys are assigned a valid SAS address for
576130561Sobrien * automatic port configuration mode.
577130561Sobrien */
578130561Sobrienstatic
579130561SobrienSCI_STATUS scic_sds_apc_agent_validate_phy_configuration(
580130561Sobrien   SCIC_SDS_CONTROLLER_T               * controller,
581130561Sobrien   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent
582130561Sobrien)
583130561Sobrien{
584130561Sobrien   U8 phy_index;
585130561Sobrien   U8 port_index;
586130561Sobrien   SCI_SAS_ADDRESS_T sas_address;
587130561Sobrien   SCI_SAS_ADDRESS_T phy_assigned_address;
588130561Sobrien
589130561Sobrien   SCIC_LOG_TRACE((
590130561Sobrien      sci_base_object_get_logger(controller),
591130561Sobrien      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT,
592130561Sobrien      "scic_sds_apc_agent_validate_phy_configuration(0x%08x, 0x%08x) enter\n",
593130561Sobrien      controller, port_agent
594130561Sobrien   ));
595130561Sobrien
596130561Sobrien   phy_index = 0;
597130561Sobrien
598130561Sobrien   while (phy_index < SCI_MAX_PHYS)
599130561Sobrien   {
600130561Sobrien      port_index = phy_index;
601130561Sobrien
602130561Sobrien      // Get the assigned SAS Address for the first PHY on the controller.
603130561Sobrien      scic_sds_phy_get_sas_address(
604130561Sobrien         &controller->phy_table[phy_index], &sas_address
605130561Sobrien      );
606130561Sobrien
607130561Sobrien      while (++phy_index < SCI_MAX_PHYS)
608130561Sobrien      {
609130561Sobrien         scic_sds_phy_get_sas_address(
610130561Sobrien            &controller->phy_table[phy_index], &phy_assigned_address
611130561Sobrien         );
612130561Sobrien
613130561Sobrien         // Verify each of the SAS address are all the same for every PHY
614130561Sobrien         if (sci_sas_address_compare(sas_address, phy_assigned_address) == 0)
615130561Sobrien         {
616130561Sobrien            port_agent->phy_valid_port_range[phy_index].min_index = port_index;
617130561Sobrien            port_agent->phy_valid_port_range[phy_index].max_index = phy_index;
618130561Sobrien         }
619130561Sobrien         else
620130561Sobrien         {
621130561Sobrien            port_agent->phy_valid_port_range[phy_index].min_index = phy_index;
622130561Sobrien            port_agent->phy_valid_port_range[phy_index].max_index = phy_index;
623130561Sobrien            break;
624130561Sobrien         }
625130561Sobrien      }
626130561Sobrien   }
627130561Sobrien
628130561Sobrien   return scic_sds_port_configuration_agent_validate_ports(controller, port_agent);
629130561Sobrien}
630130561Sobrien
631130561Sobrien/**
632130561Sobrien * This routine will restart the automatic port configuration timeout
633130561Sobrien * timer for the next time period.  This could be caused by either a
634130561Sobrien * link down event or a link up event where we can not yet tell to which
635130561Sobrien * port a phy belongs.
636130561Sobrien *
637130561Sobrien * @param[in] controller This is the controller that to which the port
638130561Sobrien *            agent is assigned.
639130561Sobrien * @param[in] port_agent This is the port agent that is requesting the
640130561Sobrien *            timer start operation.
641130561Sobrien * @param[in] phy This is the phy that has caused the timer operation to
642130561Sobrien *            be scheduled.
643130561Sobrien * @param[in] timeout This is the timeout in ms.
644130561Sobrien */
645130561Sobrienstatic
646130561Sobrienvoid scic_sds_apc_agent_start_timer(
647130561Sobrien   SCIC_SDS_CONTROLLER_T               * controller,
648130561Sobrien   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent,
649130561Sobrien   SCIC_SDS_PHY_T                      * phy,
650130561Sobrien   U32                                   timeout
651130561Sobrien)
652130561Sobrien{
653130561Sobrien   SCIC_LOG_TRACE((
654130561Sobrien      sci_base_object_get_logger(controller),
655130561Sobrien      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY,
656130561Sobrien      "scic_sds_apc_agent_start_timer(0x%08x, 0x%08x, 0x%08x, 0x%08x) enter\n",
657130561Sobrien      controller, port_agent, phy, timeout
658130561Sobrien   ));
659130561Sobrien
660130561Sobrien   if (port_agent->timer_pending)
661130561Sobrien   {
662130561Sobrien      scic_cb_timer_stop(controller, port_agent->timer);
663130561Sobrien   }
664130561Sobrien
665130561Sobrien   port_agent->timer_pending = TRUE;
666130561Sobrien
667130561Sobrien   scic_cb_timer_start(controller, port_agent->timer, timeout);
668130561Sobrien}
669130561Sobrien
670130561Sobrien/**
671130561Sobrien * This method handles the automatic port configuration for link up notifications.
672130561Sobrien *
673130561Sobrien * @param[in] controller This is the controller object that receives the
674130561Sobrien *            link up notification.
675130561Sobrien * @param[in] phy This is the phy object which has gone link up.
676130561Sobrien * @param[in] start_timer This tells the routine if it should start the timer for
677130561Sobrien *            any phys that might be added to a port in the future.
678130561Sobrien */
679130561Sobrienstatic
680130561Sobrienvoid scic_sds_apc_agent_configure_ports(
681130561Sobrien   SCIC_SDS_CONTROLLER_T               * controller,
682130561Sobrien   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent,
683130561Sobrien   SCIC_SDS_PHY_T                      * phy,
684130561Sobrien   BOOL                                  start_timer
685130561Sobrien)
686130561Sobrien{
687130561Sobrien   U8 port_index;
688130561Sobrien   SCI_STATUS status;
689130561Sobrien   SCIC_SDS_PORT_T * port;
690130561Sobrien   SCI_PORT_HANDLE_T port_handle;
691130561Sobrien   enum SCIC_SDS_APC_ACTIVITY apc_activity = SCIC_SDS_APC_SKIP_PHY;
692130561Sobrien
693130561Sobrien   SCIC_LOG_TRACE((
694130561Sobrien      sci_base_object_get_logger(controller),
695130561Sobrien      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY,
696130561Sobrien      "scic_sds_apc_agent_configure_ports(0x%08x, 0x%08x, 0x%08x, %d) enter\n",
697130561Sobrien      controller, port_agent, phy, start_timer
698130561Sobrien   ));
699130561Sobrien
700130561Sobrien   port = scic_sds_port_configuration_agent_find_port(controller, phy);
701130561Sobrien
702130561Sobrien   if (port != SCI_INVALID_HANDLE)
703130561Sobrien   {
704130561Sobrien      if (scic_sds_port_is_valid_phy_assignment(port, phy->phy_index))
705130561Sobrien         apc_activity = SCIC_SDS_APC_ADD_PHY;
706130561Sobrien      else
707130561Sobrien         apc_activity = SCIC_SDS_APC_SKIP_PHY;
708130561Sobrien   }
709130561Sobrien   else
710130561Sobrien   {
711130561Sobrien      // There is no matching Port for this PHY so lets search through the
712130561Sobrien      // Ports and see if we can add the PHY to its own port or maybe start
713130561Sobrien      // the timer and wait to see if a wider port can be made.
714130561Sobrien      //
715130561Sobrien      // Note the break when we reach the condition of the port id == phy id
716130561Sobrien      for (
717130561Sobrien             port_index = port_agent->phy_valid_port_range[phy->phy_index].min_index;
718130561Sobrien             port_index <= port_agent->phy_valid_port_range[phy->phy_index].max_index;
719130561Sobrien             port_index++
720130561Sobrien          )
721130561Sobrien      {
722130561Sobrien         scic_controller_get_port_handle(controller, port_index, &port_handle);
723130561Sobrien
724130561Sobrien         port = (SCIC_SDS_PORT_T *)port_handle;
725130561Sobrien
726130561Sobrien         // First we must make sure that this PHY can be added to this Port.
727130561Sobrien         if (scic_sds_port_is_valid_phy_assignment(port, phy->phy_index))
728130561Sobrien         {
729130561Sobrien            // Port contains a PHY with a greater PHY ID than the current
730130561Sobrien            // PHY that has gone link up.  This phy can not be part of any
731130561Sobrien            // port so skip it and move on.
732130561Sobrien            if (port->active_phy_mask > (1 << phy->phy_index))
733130561Sobrien            {
734130561Sobrien               apc_activity = SCIC_SDS_APC_SKIP_PHY;
735130561Sobrien               break;
736130561Sobrien            }
737130561Sobrien
738130561Sobrien            // We have reached the end of our Port list and have not found
739130561Sobrien            // any reason why we should not either add the PHY to the port
740130561Sobrien            // or wait for more phys to become active.
741130561Sobrien            if (port->physical_port_index == phy->phy_index)
742130561Sobrien            {
743130561Sobrien               // The Port either has no active PHYs.
744130561Sobrien               // Consider that if the port had any active PHYs we would have
745130561Sobrien               // or active PHYs with
746130561Sobrien               // a lower PHY Id than this PHY.
747130561Sobrien               if (apc_activity != SCIC_SDS_APC_START_TIMER)
748130561Sobrien               {
749130561Sobrien                  apc_activity = SCIC_SDS_APC_ADD_PHY;
750130561Sobrien               }
751130561Sobrien
752130561Sobrien               break;
753130561Sobrien            }
754130561Sobrien
755130561Sobrien            // The current Port has no active PHYs and this PHY could be part
756130561Sobrien            // of this Port.  Since we dont know as yet setup to start the
757130561Sobrien            // timer and see if there is a better configuration.
758130561Sobrien            if (port->active_phy_mask == 0)
759130561Sobrien            {
760130561Sobrien               apc_activity = SCIC_SDS_APC_START_TIMER;
761130561Sobrien            }
762130561Sobrien         }
763130561Sobrien         else if (port->active_phy_mask != 0)
764130561Sobrien         {
765130561Sobrien            // The Port has an active phy and the current Phy can not
766130561Sobrien            // participate in this port so skip the PHY and see if
767130561Sobrien            // there is a better configuration.
768130561Sobrien            apc_activity = SCIC_SDS_APC_SKIP_PHY;
769130561Sobrien         }
770130561Sobrien      }
771130561Sobrien   }
772130561Sobrien
773130561Sobrien   // Check to see if the start timer operations should instead map to an
774130561Sobrien   // add phy operation.  This is caused because we have been waiting to
775130561Sobrien   // add a phy to a port but could not because the automatic port
776130561Sobrien   // configuration engine had a choice of possible ports for the phy.
777130561Sobrien   // Since we have gone through a timeout we are going to restrict the
778130561Sobrien   // choice to the smallest possible port.
779130561Sobrien   if (
780130561Sobrien         (start_timer == FALSE)
781130561Sobrien      && (apc_activity == SCIC_SDS_APC_START_TIMER)
782130561Sobrien      )
783130561Sobrien   {
784130561Sobrien      apc_activity = SCIC_SDS_APC_ADD_PHY;
785130561Sobrien   }
786130561Sobrien
787130561Sobrien   switch (apc_activity)
788130561Sobrien   {
789130561Sobrien   case SCIC_SDS_APC_ADD_PHY:
790130561Sobrien      status = scic_sds_port_add_phy(port, phy);
791130561Sobrien
792130561Sobrien      if (status == SCI_SUCCESS)
793130561Sobrien      {
794130561Sobrien         port_agent->phy_configured_mask |= (1 << phy->phy_index);
795130561Sobrien      }
796130561Sobrien      break;
797130561Sobrien
798130561Sobrien   case SCIC_SDS_APC_START_TIMER:
799130561Sobrien      scic_sds_apc_agent_start_timer(
800130561Sobrien         controller, port_agent, phy, SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION
801130561Sobrien      );
802130561Sobrien      break;
803130561Sobrien
804130561Sobrien   case SCIC_SDS_APC_SKIP_PHY:
805130561Sobrien   default:
806130561Sobrien      // do nothing the PHY can not be made part of a port at this time.
807130561Sobrien      break;
808130561Sobrien   }
809130561Sobrien}
810130561Sobrien
811130561Sobrien/**
812130561Sobrien * This method handles the automatic port configuration for link up notifications.
813130561Sobrien *
814130561Sobrien * @param[in] controller This is the controller object that receives the
815130561Sobrien *            link up notification.
816130561Sobrien * @param[in] port This is the port object associated with the phy.  If the
817130561Sobrien *            is no associated port this is an SCI_INVALID_HANDLE.
818130561Sobrien * @param[in] phy This is the phy object which has gone link up.
819130561Sobrien *
820130561Sobrien * @note Is it possible to get a link down notification from a phy that has
821130561Sobrien *       no assocoated port?
822130561Sobrien */
823130561Sobrienstatic
824130561Sobrienvoid scic_sds_apc_agent_link_up(
825130561Sobrien   SCIC_SDS_CONTROLLER_T               * controller,
826130561Sobrien   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent,
827130561Sobrien   SCIC_SDS_PORT_T                     * port,
828130561Sobrien   SCIC_SDS_PHY_T                      * phy
829130561Sobrien)
830130561Sobrien{
831130561Sobrien   SCIC_LOG_TRACE((
832130561Sobrien      sci_base_object_get_logger(controller),
833130561Sobrien      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY,
834130561Sobrien      "scic_sds_apc_agent_link_up(0x%08x, 0x%08x, 0x%08x, 0x%08x) enter\n",
835130561Sobrien      controller, port_agent, port, phy
836130561Sobrien   ));
837130561Sobrien
838130561Sobrien   //the phy is not the part of this port, configure the port with this phy
839130561Sobrien   if (port == SCI_INVALID_HANDLE)
840130561Sobrien   {
841130561Sobrien      port_agent->phy_ready_mask |= (1 << scic_sds_phy_get_index(phy));
842130561Sobrien
843130561Sobrien      scic_sds_apc_agent_start_timer(
844130561Sobrien         controller, port_agent, phy, SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION
845130561Sobrien      );
846130561Sobrien   }
847130561Sobrien   else
848130561Sobrien   {
849130561Sobrien      //the phy is already the part of the port
850130561Sobrien
851130561Sobrien      //if the PORT'S state is resetting then the link up is from port hard reset
852130561Sobrien      //in this case, we need to tell the port that link up is received
853130561Sobrien      if (  SCI_BASE_PORT_STATE_RESETTING
854130561Sobrien            == port->parent.state_machine.current_state_id
855130561Sobrien         )
856130561Sobrien      {
857130561Sobrien         //notify the port that port needs to be ready
858130561Sobrien         port_agent->phy_ready_mask |= (1 << scic_sds_phy_get_index(phy));
859130561Sobrien         scic_sds_port_link_up(port, phy);
860130561Sobrien      }
861130561Sobrien      else
862130561Sobrien      {
863130561Sobrien         ASSERT (0);
864130561Sobrien      }
865130561Sobrien   }
866130561Sobrien}
867130561Sobrien
868130561Sobrien/**
869130561Sobrien * This method handles the automatic port configuration link down notifications.
870130561Sobrien * If this PHY is * not associated with a port there is no action taken.
871130561Sobrien *
872130561Sobrien * @param[in] controller This is the controller object that receives the
873130561Sobrien *            link down notification.
874130561Sobrien * @param[in] port This is the port object associated with the phy.  If the
875130561Sobrien *            is no associated port this is an SCI_INVALID_HANDLE.
876130561Sobrien * @param[in] phy This is the phy object which has gone link down.
877130561Sobrien *
878130561Sobrien * @note Is it possible to get a link down notification from a phy that has
879130561Sobrien *       no assocoated port?
880130561Sobrien */
881130561Sobrienstatic
882130561Sobrienvoid scic_sds_apc_agent_link_down(
883130561Sobrien   SCIC_SDS_CONTROLLER_T               * controller,
884130561Sobrien   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent,
885130561Sobrien   SCIC_SDS_PORT_T                     * port,
886130561Sobrien   SCIC_SDS_PHY_T                      * phy
887130561Sobrien)
888130561Sobrien{
889130561Sobrien   SCIC_LOG_TRACE((
890130561Sobrien      sci_base_object_get_logger(controller),
891130561Sobrien      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY,
892130561Sobrien      "scic_sds_apc_agent_link_down(0x%08x, 0x%08x, 0x%08x, 0x%08x) enter\n",
893130561Sobrien      controller, port_agent, port, phy
894130561Sobrien   ));
895130561Sobrien
896130561Sobrien   port_agent->phy_ready_mask &= ~(1 << scic_sds_phy_get_index(phy));
897130561Sobrien
898130561Sobrien   if (port != SCI_INVALID_HANDLE)
899130561Sobrien   {
900130561Sobrien      if (port_agent->phy_configured_mask & (1 << phy->phy_index))
901130561Sobrien      {
902130561Sobrien         SCI_STATUS status;
903130561Sobrien
904130561Sobrien         status = scic_sds_port_remove_phy(port, phy);
905130561Sobrien
906130561Sobrien         if (status == SCI_SUCCESS)
907130561Sobrien         {
908130561Sobrien            port_agent->phy_configured_mask &= ~(1 << phy->phy_index);
909130561Sobrien         }
910130561Sobrien      }
911130561Sobrien   }
912130561Sobrien}
913130561Sobrien
914130561Sobrien/**
915130561Sobrien * This routine will try to configure the phys into ports when the timer fires.
916130561Sobrien *
917130561Sobrien * @param[in] object This is actually the controller that needs to have the
918130561Sobrien *            pending phys configured.
919130561Sobrien */
920130561Sobrienstatic
921130561Sobrienvoid scic_sds_apc_agent_timeout_handler(
922130561Sobrien   void * object
923130561Sobrien)
924130561Sobrien{
925130561Sobrien   U32 index;
926130561Sobrien   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent;
927130561Sobrien   SCIC_SDS_CONTROLLER_T * controller = (SCIC_SDS_CONTROLLER_T *)object;
928130561Sobrien   U16 configure_phy_mask;
929130561Sobrien
930130561Sobrien   port_agent = scic_sds_controller_get_port_configuration_agent(controller);
931130561Sobrien
932130561Sobrien   SCIC_LOG_TRACE((
933130561Sobrien      sci_base_object_get_logger(controller),
934130561Sobrien      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT,
935130561Sobrien      "scic_sds_apc_agent_timeout_handler(0x%08x) enter\n",
936130561Sobrien      controller
937130561Sobrien   ));
938130561Sobrien
939130561Sobrien   port_agent->timer_pending = FALSE;
940130561Sobrien
941130561Sobrien   configure_phy_mask = ~port_agent->phy_configured_mask & port_agent->phy_ready_mask;
942130561Sobrien
943130561Sobrien   if (configure_phy_mask != 0x00)
944130561Sobrien   {
945130561Sobrien      for (index = 0; index < SCI_MAX_PHYS; index++)
946130561Sobrien      {
947130561Sobrien         if (configure_phy_mask & (1 << index))
948130561Sobrien         {
949130561Sobrien            scic_sds_apc_agent_configure_ports(
950130561Sobrien               controller, port_agent, &controller->phy_table[index], FALSE
951130561Sobrien            );
952130561Sobrien         }
953130561Sobrien      }
954130561Sobrien
955130561Sobrien      //Notify the controller ports are configured.
956130561Sobrien      if (
957130561Sobrien            (port_agent->phy_ready_mask == port_agent->phy_configured_mask) &&
958130561Sobrien            (controller->next_phy_to_start == SCI_MAX_PHYS) &&
959130561Sobrien            (controller->phy_startup_timer_pending == FALSE)
960130561Sobrien         )
961130561Sobrien      {
962130561Sobrien         // The controller has successfully finished the start process.
963130561Sobrien         // Inform the SCI Core user and transition to the READY state.
964130561Sobrien         if (scic_sds_controller_is_start_complete(controller) == TRUE)
965130561Sobrien         {
966130561Sobrien            scic_sds_controller_port_agent_configured_ports(controller);
967130561Sobrien         }
968130561Sobrien      }
969130561Sobrien   }
970130561Sobrien}
971130561Sobrien
972130561Sobrien//******************************************************************************
973130561Sobrien// Public port configuration agent routines
974130561Sobrien//******************************************************************************
975130561Sobrien
976130561Sobrien/**
977130561Sobrien * This method will construct the port configuration agent for operation.
978130561Sobrien * This call is universal for both manual port configuration and automatic
979130561Sobrien * port configuration modes.
980130561Sobrien *
981130561Sobrien * @param[in] port_agent This is the port configuration agent for this
982130561Sobrien *            controller object.
983130561Sobrien */
984130561Sobrienvoid scic_sds_port_configuration_agent_construct(
985130561Sobrien   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent
986130561Sobrien)
987130561Sobrien{
988130561Sobrien   U32 index;
989130561Sobrien
990130561Sobrien   port_agent->phy_configured_mask = 0x00;
991130561Sobrien   port_agent->phy_ready_mask = 0x00;
992130561Sobrien
993130561Sobrien   port_agent->link_up_handler = NULL;
994130561Sobrien   port_agent->link_down_handler = NULL;
995130561Sobrien
996130561Sobrien   port_agent->timer_pending = FALSE;
997130561Sobrien   port_agent->timer = NULL;
998130561Sobrien
999130561Sobrien   for (index = 0; index < SCI_MAX_PORTS; index++)
1000130561Sobrien   {
1001130561Sobrien      port_agent->phy_valid_port_range[index].min_index = 0;
1002130561Sobrien      port_agent->phy_valid_port_range[index].max_index = 0;
1003130561Sobrien   }
1004130561Sobrien}
1005130561Sobrien
1006130561Sobrien/**
1007130561Sobrien * This method will construct the port configuration agent for this controller.
1008130561Sobrien *
1009130561Sobrien * @param[in] controller This is the controller object for which the port
1010130561Sobrien *            agent is being initialized.
1011130561Sobrien *
1012130561Sobrien * @param[in] port_agent This is the port configuration agent that is being
1013130561Sobrien *            initialized.  The initialization path is handled differntly
1014130561Sobrien *            for the automatic port configuration agent and the manual port
1015130561Sobrien *            configuration agent.
1016130561Sobrien *
1017130561Sobrien * @return
1018130561Sobrien */
1019130561SobrienSCI_STATUS scic_sds_port_configuration_agent_initialize(
1020130561Sobrien   SCIC_SDS_CONTROLLER_T               * controller,
1021130561Sobrien   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent
1022130561Sobrien)
1023130561Sobrien{
1024130561Sobrien   SCI_STATUS status = SCI_SUCCESS;
1025130561Sobrien   enum SCIC_PORT_CONFIGURATION_MODE mode;
1026130561Sobrien
1027130561Sobrien   SCIC_LOG_TRACE((
1028130561Sobrien      sci_base_object_get_logger(controller),
1029130561Sobrien      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT,
1030130561Sobrien      "scic_sds_port_configuration_agent_initialize(0x%08x, 0x%08x) enter\n",
1031130561Sobrien      controller, port_agent
1032130561Sobrien   ));
1033130561Sobrien
1034130561Sobrien   mode = controller->oem_parameters.sds1.controller.mode_type;
1035130561Sobrien
1036130561Sobrien   if (mode == SCIC_PORT_MANUAL_CONFIGURATION_MODE)
1037130561Sobrien   {
1038130561Sobrien      status = scic_sds_mpc_agent_validate_phy_configuration(controller, port_agent);
1039130561Sobrien
1040130561Sobrien      port_agent->link_up_handler = scic_sds_mpc_agent_link_up;
1041130561Sobrien      port_agent->link_down_handler = scic_sds_mpc_agent_link_down;
1042130561Sobrien
1043      port_agent->timer = scic_cb_timer_create(
1044                              controller,
1045                              scic_sds_mpc_agent_timeout_handler,
1046                              controller
1047                          );
1048   }
1049   else
1050   {
1051      status = scic_sds_apc_agent_validate_phy_configuration(controller, port_agent);
1052
1053      port_agent->link_up_handler = scic_sds_apc_agent_link_up;
1054      port_agent->link_down_handler = scic_sds_apc_agent_link_down;
1055
1056      port_agent->timer = scic_cb_timer_create(
1057                              controller,
1058                              scic_sds_apc_agent_timeout_handler,
1059                              controller
1060                          );
1061   }
1062
1063   // Make sure we have actually gotten a timer
1064   if (status == SCI_SUCCESS && port_agent->timer == NULL)
1065   {
1066      SCIC_LOG_ERROR((
1067         sci_base_object_get_logger(controller),
1068         SCIC_LOG_OBJECT_CONTROLLER,
1069         "Controller 0x%x automatic port configuration agent could not get timer.\n",
1070         controller
1071     ));
1072
1073     status = SCI_FAILURE;
1074   }
1075
1076   return status;
1077}
1078
1079/**
1080 * This method will destroy the port configuration agent for this controller.
1081 *
1082 * @param[in] controller This is the controller object for which the port
1083 *            agent is being destroyed.
1084 *
1085 * @param[in] port_agent This is the port configuration agent that is being
1086 *            destroyed.
1087 *
1088 * @return
1089 */
1090void scic_sds_port_configuration_agent_destroy(
1091   SCIC_SDS_CONTROLLER_T               * controller,
1092   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent
1093)
1094{
1095   if (port_agent->timer_pending == TRUE)
1096   {
1097      scic_cb_timer_stop(controller, port_agent->timer);
1098   }
1099
1100   scic_cb_timer_destroy(controller, port_agent->timer);
1101
1102   port_agent->timer_pending = FALSE;
1103   port_agent->timer = NULL;
1104}
1105
1106
1107/**
1108 * @brief This method release resources in for a scic port configuration agent.
1109 *
1110 * @param[in] controller This parameter specifies the core controller, one of
1111 *            its phy's resources are to be released.
1112 * @param[in] this_phy This parameter specifies the phy whose resource is to
1113 *            be released.
1114 */
1115void scic_sds_port_configuration_agent_release_resource(
1116   SCIC_SDS_CONTROLLER_T               * controller,
1117   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent
1118)
1119{
1120   SCIC_LOG_TRACE((
1121      sci_base_object_get_logger(controller),
1122      SCIC_LOG_OBJECT_PORT,
1123      "scic_sds_port_configuration_agent_release_resource(0x%x, 0x%x)\n",
1124      controller, port_agent
1125   ));
1126
1127   //Currently, the only resource to be released is a timer.
1128   if (port_agent->timer != NULL)
1129   {
1130      scic_cb_timer_destroy(controller, port_agent->timer);
1131      port_agent->timer = NULL;
1132   }
1133}
1134