1230557Sjimharris/*-
2230557Sjimharris * This file is provided under a dual BSD/GPLv2 license.  When using or
3230557Sjimharris * redistributing this file, you may do so under either license.
4230557Sjimharris *
5230557Sjimharris * GPL LICENSE SUMMARY
6230557Sjimharris *
7230557Sjimharris * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
8230557Sjimharris *
9230557Sjimharris * This program is free software; you can redistribute it and/or modify
10230557Sjimharris * it under the terms of version 2 of the GNU General Public License as
11230557Sjimharris * published by the Free Software Foundation.
12230557Sjimharris *
13230557Sjimharris * This program is distributed in the hope that it will be useful, but
14230557Sjimharris * WITHOUT ANY WARRANTY; without even the implied warranty of
15230557Sjimharris * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16230557Sjimharris * General Public License for more details.
17230557Sjimharris *
18230557Sjimharris * You should have received a copy of the GNU General Public License
19230557Sjimharris * along with this program; if not, write to the Free Software
20230557Sjimharris * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
21230557Sjimharris * The full GNU General Public License is included in this distribution
22230557Sjimharris * in the file called LICENSE.GPL.
23230557Sjimharris *
24230557Sjimharris * BSD LICENSE
25230557Sjimharris *
26230557Sjimharris * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
27230557Sjimharris * All rights reserved.
28230557Sjimharris *
29230557Sjimharris * Redistribution and use in source and binary forms, with or without
30230557Sjimharris * modification, are permitted provided that the following conditions
31230557Sjimharris * are met:
32230557Sjimharris *
33230557Sjimharris *   * Redistributions of source code must retain the above copyright
34230557Sjimharris *     notice, this list of conditions and the following disclaimer.
35230557Sjimharris *   * Redistributions in binary form must reproduce the above copyright
36230557Sjimharris *     notice, this list of conditions and the following disclaimer in
37230557Sjimharris *     the documentation and/or other materials provided with the
38230557Sjimharris *     distribution.
39230557Sjimharris *
40230557Sjimharris * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
41230557Sjimharris * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
42230557Sjimharris * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
43230557Sjimharris * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
44230557Sjimharris * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
45230557Sjimharris * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
46230557Sjimharris * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
47230557Sjimharris * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
48230557Sjimharris * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
49230557Sjimharris * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
50230557Sjimharris * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
51230557Sjimharris */
52230557Sjimharris
53230557Sjimharris#include <sys/cdefs.h>
54230557Sjimharris__FBSDID("$FreeBSD$");
55230557Sjimharris
56230557Sjimharris/**
57230557Sjimharris * @file
58230557Sjimharris *
59230557Sjimharris * @brief This file contains the implementation for the public and protected
60230557Sjimharris *        methods for the port configuration agent.
61230557Sjimharris */
62230557Sjimharris
63230557Sjimharris#include <dev/isci/scil/scic_controller.h>
64230557Sjimharris#include <dev/isci/scil/scic_sds_logger.h>
65230557Sjimharris#include <dev/isci/scil/scic_sds_controller.h>
66230557Sjimharris#include <dev/isci/scil/scic_sds_port_configuration_agent.h>
67230557Sjimharris
68230557Sjimharris#define SCIC_SDS_MPC_RECONFIGURATION_TIMEOUT    (10)
69230557Sjimharris#define SCIC_SDS_APC_RECONFIGURATION_TIMEOUT    (10)
70230557Sjimharris#define SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION  (250)
71230557Sjimharris
72230557Sjimharrisenum SCIC_SDS_APC_ACTIVITY
73230557Sjimharris{
74230557Sjimharris   SCIC_SDS_APC_SKIP_PHY,
75230557Sjimharris   SCIC_SDS_APC_ADD_PHY,
76230557Sjimharris   SCIC_SDS_APC_START_TIMER,
77230557Sjimharris
78230557Sjimharris   SCIC_SDS_APC_ACTIVITY_MAX
79230557Sjimharris};
80230557Sjimharris
81230557Sjimharris//******************************************************************************
82230557Sjimharris// General port configuration agent routines
83230557Sjimharris//******************************************************************************
84230557Sjimharris
85230557Sjimharris/**
86230557Sjimharris * Compare the two SAS Address and
87230557Sjimharris * if SAS Address One is greater than SAS Address Two then return > 0
88230557Sjimharris * else if SAS Address One is less than SAS Address Two return < 0
89230557Sjimharris * Otherwise they are the same return 0
90230557Sjimharris *
91230557Sjimharris * @param[in] address_one A SAS Address to be compared.
92230557Sjimharris * @param[in] address_two A SAS Address to be compared.
93230557Sjimharris *
94230557Sjimharris * @return A signed value of x > 0 > y where
95230557Sjimharris *         x is returned for Address One > Address Two
96230557Sjimharris *         y is returned for Address One < Address Two
97230557Sjimharris *         0 is returned ofr Address One = Address Two
98230557Sjimharris */
99230557Sjimharrisstatic
100230557SjimharrisS32 sci_sas_address_compare(
101230557Sjimharris   SCI_SAS_ADDRESS_T address_one,
102230557Sjimharris   SCI_SAS_ADDRESS_T address_two
103230557Sjimharris)
104230557Sjimharris{
105230557Sjimharris   if (address_one.high > address_two.high)
106230557Sjimharris   {
107230557Sjimharris      return 1;
108230557Sjimharris   }
109230557Sjimharris   else if (address_one.high < address_two.high)
110230557Sjimharris   {
111230557Sjimharris      return -1;
112230557Sjimharris   }
113230557Sjimharris   else if (address_one.low > address_two.low)
114230557Sjimharris   {
115230557Sjimharris      return 1;
116230557Sjimharris   }
117230557Sjimharris   else if (address_one.low < address_two.low)
118230557Sjimharris   {
119230557Sjimharris      return -1;
120230557Sjimharris   }
121230557Sjimharris
122230557Sjimharris   // The two SAS Address must be identical
123230557Sjimharris   return 0;
124230557Sjimharris}
125230557Sjimharris
126230557Sjimharris/**
127230557Sjimharris * This routine will find a matching port for the phy.  This means that the
128230557Sjimharris * port and phy both have the same broadcast sas address and same received
129230557Sjimharris * sas address.
130230557Sjimharris *
131230557Sjimharris * @param[in] controller The controller object used for the port search.
132230557Sjimharris * @param[in] phy The phy object to match.
133230557Sjimharris *
134230557Sjimharris * @return The port address or the SCI_INVALID_HANDLE if there is no matching
135230557Sjimharris *         port.
136230557Sjimharris *
137230557Sjimharris * @retvalue port address if the port can be found to match the phy.
138230557Sjimharris * @retvalue SCI_INVALID_HANDLE if there is no matching port for the phy.
139230557Sjimharris */
140230557Sjimharrisstatic
141230557SjimharrisSCIC_SDS_PORT_T * scic_sds_port_configuration_agent_find_port(
142230557Sjimharris   SCIC_SDS_CONTROLLER_T * controller,
143230557Sjimharris   SCIC_SDS_PHY_T        * phy
144230557Sjimharris)
145230557Sjimharris{
146230557Sjimharris   U8 port_index;
147230557Sjimharris   SCI_PORT_HANDLE_T port_handle;
148230557Sjimharris   SCI_SAS_ADDRESS_T port_sas_address;
149230557Sjimharris   SCI_SAS_ADDRESS_T port_attached_device_address;
150230557Sjimharris   SCI_SAS_ADDRESS_T phy_sas_address;
151230557Sjimharris   SCI_SAS_ADDRESS_T phy_attached_device_address;
152230557Sjimharris
153230557Sjimharris   SCIC_LOG_TRACE((
154230557Sjimharris      sci_base_object_get_logger(controller),
155230557Sjimharris      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY,
156230557Sjimharris      "scic_sds_port_confgiruation_agent_find_port(0x%08x, 0x%08x) enter\n",
157230557Sjimharris      controller, phy
158230557Sjimharris   ));
159230557Sjimharris
160230557Sjimharris   // Since this phy can be a member of a wide port check to see if one or
161230557Sjimharris   // more phys match the sent and received SAS address as this phy in which
162230557Sjimharris   // case it should participate in the same port.
163230557Sjimharris   scic_sds_phy_get_sas_address(phy, &phy_sas_address);
164230557Sjimharris   scic_sds_phy_get_attached_sas_address(phy, &phy_attached_device_address);
165230557Sjimharris
166230557Sjimharris   for (port_index = 0; port_index < SCI_MAX_PORTS; port_index++)
167230557Sjimharris   {
168230557Sjimharris      if (scic_controller_get_port_handle(controller, port_index, &port_handle) == SCI_SUCCESS)
169230557Sjimharris      {
170230557Sjimharris         SCIC_SDS_PORT_T * port = (SCIC_SDS_PORT_T *)port_handle;
171230557Sjimharris
172230557Sjimharris         scic_sds_port_get_sas_address(port, &port_sas_address);
173230557Sjimharris         scic_sds_port_get_attached_sas_address(port, &port_attached_device_address);
174230557Sjimharris
175230557Sjimharris         if (
176230557Sjimharris               (sci_sas_address_compare(port_sas_address, phy_sas_address) == 0)
177230557Sjimharris            && (sci_sas_address_compare(port_attached_device_address, phy_attached_device_address) == 0)
178230557Sjimharris            )
179230557Sjimharris         {
180230557Sjimharris            return port;
181230557Sjimharris         }
182230557Sjimharris      }
183230557Sjimharris   }
184230557Sjimharris
185230557Sjimharris   return SCI_INVALID_HANDLE;
186230557Sjimharris}
187230557Sjimharris
188230557Sjimharris/**
189230557Sjimharris * This routine will validate the port configuration is correct for the SCU
190230557Sjimharris * hardware.  The SCU hardware allows for port configurations as follows.
191230557Sjimharris *    LP0 -> (PE0), (PE0, PE1), (PE0, PE1, PE2, PE3)
192230557Sjimharris *    LP1 -> (PE1)
193230557Sjimharris *    LP2 -> (PE2), (PE2, PE3)
194230557Sjimharris *    LP3 -> (PE3)
195230557Sjimharris *
196230557Sjimharris * @param[in] controller This is the controller object that contains the
197230557Sjimharris *            port agent
198230557Sjimharris * @param[in] port_agent This is the port configruation agent for
199230557Sjimharris *            the controller.
200230557Sjimharris *
201230557Sjimharris * @return SCI_STATUS
202230557Sjimharris * @retval SCI_SUCCESS the port configuration is valid for this
203230557Sjimharris *         port configuration agent.
204230557Sjimharris * @retval SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION the port configuration
205230557Sjimharris *         is not valid for this port configuration agent.
206230557Sjimharris */
207230557Sjimharrisstatic
208230557SjimharrisSCI_STATUS scic_sds_port_configuration_agent_validate_ports(
209230557Sjimharris   SCIC_SDS_CONTROLLER_T               * controller,
210230557Sjimharris   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent
211230557Sjimharris)
212230557Sjimharris{
213230557Sjimharris#if !defined(ARLINGTON_BUILD)
214230557Sjimharris   SCI_SAS_ADDRESS_T first_address;
215230557Sjimharris   SCI_SAS_ADDRESS_T second_address;
216230557Sjimharris
217230557Sjimharris   SCIC_LOG_TRACE((
218230557Sjimharris      sci_base_object_get_logger(controller),
219230557Sjimharris      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT,
220230557Sjimharris      "scic_sds_port_configuration_agent_validate_ports(0x%08x, 0x%08x) enter\n",
221230557Sjimharris      controller, port_agent
222230557Sjimharris   ));
223230557Sjimharris
224230557Sjimharris   // Sanity check the max ranges for all the phys the max index
225230557Sjimharris   // is always equal to the port range index
226230557Sjimharris   if (
227230557Sjimharris         (port_agent->phy_valid_port_range[0].max_index != 0)
228230557Sjimharris      || (port_agent->phy_valid_port_range[1].max_index != 1)
229230557Sjimharris      || (port_agent->phy_valid_port_range[2].max_index != 2)
230230557Sjimharris      || (port_agent->phy_valid_port_range[3].max_index != 3)
231230557Sjimharris      )
232230557Sjimharris   {
233230557Sjimharris      return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
234230557Sjimharris   }
235230557Sjimharris
236230557Sjimharris   // This is a request to configure a single x4 port or at least attempt
237230557Sjimharris   // to make all the phys into a single port
238230557Sjimharris   if (
239230557Sjimharris         (port_agent->phy_valid_port_range[0].min_index == 0)
240230557Sjimharris      && (port_agent->phy_valid_port_range[1].min_index == 0)
241230557Sjimharris      && (port_agent->phy_valid_port_range[2].min_index == 0)
242230557Sjimharris      && (port_agent->phy_valid_port_range[3].min_index == 0)
243230557Sjimharris      )
244230557Sjimharris   {
245230557Sjimharris      return SCI_SUCCESS;
246230557Sjimharris   }
247230557Sjimharris
248230557Sjimharris   // This is a degenerate case where phy 1 and phy 2 are assigned
249230557Sjimharris   // to the same port this is explicitly disallowed by the hardware
250230557Sjimharris   // unless they are part of the same x4 port and this condition was
251230557Sjimharris   // already checked above.
252230557Sjimharris   if (port_agent->phy_valid_port_range[2].min_index == 1)
253230557Sjimharris   {
254230557Sjimharris      return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
255230557Sjimharris   }
256230557Sjimharris
257230557Sjimharris   // PE0 and PE3 can never have the same SAS Address unless they
258230557Sjimharris   // are part of the same x4 wide port and we have already checked
259230557Sjimharris   // for this condition.
260230557Sjimharris   scic_sds_phy_get_sas_address(&controller->phy_table[0], &first_address);
261230557Sjimharris   scic_sds_phy_get_sas_address(&controller->phy_table[3], &second_address);
262230557Sjimharris
263230557Sjimharris   if (sci_sas_address_compare(first_address, second_address) == 0)
264230557Sjimharris   {
265230557Sjimharris      return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
266230557Sjimharris   }
267230557Sjimharris
268230557Sjimharris   // PE0 and PE1 are configured into a 2x1 ports make sure that the
269230557Sjimharris   // SAS Address for PE0 and PE2 are different since they can not be
270230557Sjimharris   // part of the same port.
271230557Sjimharris   if (
272230557Sjimharris         (port_agent->phy_valid_port_range[0].min_index == 0)
273230557Sjimharris      && (port_agent->phy_valid_port_range[1].min_index == 1)
274230557Sjimharris      )
275230557Sjimharris   {
276230557Sjimharris      scic_sds_phy_get_sas_address(&controller->phy_table[0], &first_address);
277230557Sjimharris      scic_sds_phy_get_sas_address(&controller->phy_table[2], &second_address);
278230557Sjimharris
279230557Sjimharris      if (sci_sas_address_compare(first_address, second_address) == 0)
280230557Sjimharris      {
281230557Sjimharris         return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
282230557Sjimharris      }
283230557Sjimharris   }
284230557Sjimharris
285230557Sjimharris   // PE2 and PE3 are configured into a 2x1 ports make sure that the
286230557Sjimharris   // SAS Address for PE1 and PE3 are different since they can not be
287230557Sjimharris   // part of the same port.
288230557Sjimharris   if (
289230557Sjimharris         (port_agent->phy_valid_port_range[2].min_index == 2)
290230557Sjimharris      && (port_agent->phy_valid_port_range[3].min_index == 3)
291230557Sjimharris      )
292230557Sjimharris   {
293230557Sjimharris      scic_sds_phy_get_sas_address(&controller->phy_table[1], &first_address);
294230557Sjimharris      scic_sds_phy_get_sas_address(&controller->phy_table[3], &second_address);
295230557Sjimharris
296230557Sjimharris      if (sci_sas_address_compare(first_address, second_address) == 0)
297230557Sjimharris      {
298230557Sjimharris         return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
299230557Sjimharris      }
300230557Sjimharris   }
301230557Sjimharris#endif // !defined(ARLINGTON_BUILD)
302230557Sjimharris
303230557Sjimharris   return SCI_SUCCESS;
304230557Sjimharris}
305230557Sjimharris
306230557Sjimharris//******************************************************************************
307230557Sjimharris// Manual port configuration agent routines
308230557Sjimharris//******************************************************************************
309230557Sjimharris
310230557Sjimharris/**
311230557Sjimharris * This routine will verify that all of the phys in the same port are using
312230557Sjimharris * the same SAS address.
313230557Sjimharris *
314230557Sjimharris * @param[in] controller This is the controller that contains the PHYs to
315230557Sjimharris *            be verified.
316230557Sjimharris */
317230557Sjimharrisstatic
318230557SjimharrisSCI_STATUS scic_sds_mpc_agent_validate_phy_configuration(
319230557Sjimharris   SCIC_SDS_CONTROLLER_T               * controller,
320230557Sjimharris   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent
321230557Sjimharris)
322230557Sjimharris{
323230557Sjimharris   U32 phy_mask;
324230557Sjimharris   U32 assigned_phy_mask;
325230557Sjimharris   SCI_SAS_ADDRESS_T sas_address;
326230557Sjimharris   SCI_SAS_ADDRESS_T phy_assigned_address;
327230557Sjimharris   U8 port_index;
328230557Sjimharris   U8 phy_index;
329230557Sjimharris
330230557Sjimharris   assigned_phy_mask = 0;
331230557Sjimharris   sas_address.high = 0;
332230557Sjimharris   sas_address.low = 0;
333230557Sjimharris
334230557Sjimharris   SCIC_LOG_TRACE((
335230557Sjimharris      sci_base_object_get_logger(controller),
336230557Sjimharris      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT,
337230557Sjimharris      "scic_sds_mpc_agent_validate_phy_configuration(0x%08x, 0x%08x) enter\n",
338230557Sjimharris      controller, port_agent
339230557Sjimharris   ));
340230557Sjimharris
341230557Sjimharris   for (port_index = 0; port_index < SCI_MAX_PORTS; port_index++)
342230557Sjimharris   {
343230557Sjimharris      phy_mask = controller->oem_parameters.sds1.ports[port_index].phy_mask;
344230557Sjimharris
345230557Sjimharris      if (phy_mask != 0)
346230557Sjimharris      {
347230557Sjimharris         // Make sure that one or more of the phys were not already assinged to
348230557Sjimharris         // a different port.
349230557Sjimharris         if ((phy_mask & ~assigned_phy_mask) == 0)
350230557Sjimharris         {
351230557Sjimharris            return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
352230557Sjimharris         }
353230557Sjimharris
354230557Sjimharris         // Find the starting phy index for this round through the loop
355230557Sjimharris         for (phy_index = 0; phy_index < SCI_MAX_PHYS; phy_index++)
356230557Sjimharris         {
357230557Sjimharris            if ((1 << phy_index) & phy_mask)
358230557Sjimharris            {
359230557Sjimharris               scic_sds_phy_get_sas_address(
360230557Sjimharris                  &controller->phy_table[phy_index], &sas_address
361230557Sjimharris               );
362230557Sjimharris
363230557Sjimharris               // The phy_index can be used as the starting point for the
364230557Sjimharris               // port range since the hardware starts all logical ports
365230557Sjimharris               // the same as the PE index.
366230557Sjimharris               port_agent->phy_valid_port_range[phy_index].min_index = port_index;
367230557Sjimharris               port_agent->phy_valid_port_range[phy_index].max_index = phy_index;
368230557Sjimharris
369230557Sjimharris               if (phy_index != port_index)
370230557Sjimharris               {
371230557Sjimharris                  return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
372230557Sjimharris               }
373230557Sjimharris
374230557Sjimharris               break;
375230557Sjimharris            }
376230557Sjimharris         }
377230557Sjimharris
378230557Sjimharris         // See how many additional phys are being added to this logical port.
379230557Sjimharris         // Note: We have not moved the current phy_index so we will actually
380230557Sjimharris         //       compare the startting phy with itself.
381230557Sjimharris         //       This is expected and required to add the phy to the port.
382230557Sjimharris         while (phy_index < SCI_MAX_PHYS)
383230557Sjimharris         {
384230557Sjimharris            if ((1 << phy_index) & phy_mask)
385230557Sjimharris            {
386230557Sjimharris               scic_sds_phy_get_sas_address(
387230557Sjimharris                  &controller->phy_table[phy_index], &phy_assigned_address
388230557Sjimharris               );
389230557Sjimharris
390230557Sjimharris               if (sci_sas_address_compare(sas_address, phy_assigned_address) != 0)
391230557Sjimharris               {
392230557Sjimharris                  // The phy mask specified that this phy is part of the same port
393230557Sjimharris                  // as the starting phy and it is not so fail this configuration
394230557Sjimharris                  return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
395230557Sjimharris               }
396230557Sjimharris
397230557Sjimharris               port_agent->phy_valid_port_range[phy_index].min_index = port_index;
398230557Sjimharris               port_agent->phy_valid_port_range[phy_index].max_index = phy_index;
399230557Sjimharris
400230557Sjimharris               scic_sds_port_add_phy(
401230557Sjimharris                  &controller->port_table[port_index],
402230557Sjimharris                  &controller->phy_table[phy_index]
403230557Sjimharris               );
404230557Sjimharris
405230557Sjimharris               assigned_phy_mask |= (1 << phy_index);
406230557Sjimharris            }
407230557Sjimharris
408230557Sjimharris            phy_index++;
409230557Sjimharris         }
410230557Sjimharris      }
411230557Sjimharris   }
412230557Sjimharris
413230557Sjimharris   return scic_sds_port_configuration_agent_validate_ports(controller, port_agent);
414230557Sjimharris}
415230557Sjimharris
416230557Sjimharris/**
417230557Sjimharris * This timer routine is used to allow the SCI User to rediscover or change
418230557Sjimharris * device objects before a new series of link up notifications because a
419230557Sjimharris * link down has allowed a better port configuration.
420230557Sjimharris *
421230557Sjimharris * @param[in] controller This is the core controller object which is used
422230557Sjimharris *            to obtain the port configuration agent.
423230557Sjimharris */
424230557Sjimharrisstatic
425230557Sjimharrisvoid scic_sds_mpc_agent_timeout_handler(
426230557Sjimharris   void * object
427230557Sjimharris)
428230557Sjimharris{
429230557Sjimharris   U8 index;
430230557Sjimharris   SCIC_SDS_CONTROLLER_T * controller = (SCIC_SDS_CONTROLLER_T *)object;
431230557Sjimharris   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent = &controller->port_agent;
432230557Sjimharris   U16 configure_phy_mask;
433230557Sjimharris
434230557Sjimharris   SCIC_LOG_TRACE((
435230557Sjimharris      sci_base_object_get_logger(controller),
436230557Sjimharris      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT,
437230557Sjimharris      "scic_sds_mpc_agent_timeout_handler(0x%08x) enter\n",
438230557Sjimharris      controller
439230557Sjimharris   ));
440230557Sjimharris
441230557Sjimharris   port_agent->timer_pending = FALSE;
442230557Sjimharris
443230557Sjimharris   // Find the mask of phys that are reported read but as yet unconfigured into a port
444230557Sjimharris   configure_phy_mask = ~port_agent->phy_configured_mask & port_agent->phy_ready_mask;
445230557Sjimharris
446230557Sjimharris   for (index = 0; index < SCI_MAX_PHYS; index++)
447230557Sjimharris   {
448230557Sjimharris      if (configure_phy_mask & (1 << index))
449230557Sjimharris      {
450230557Sjimharris         port_agent->link_up_handler(
451230557Sjimharris                        controller,
452230557Sjimharris                        port_agent,
453230557Sjimharris                        scic_sds_phy_get_port(&controller->phy_table[index]),
454230557Sjimharris                        &controller->phy_table[index]
455230557Sjimharris                     );
456230557Sjimharris      }
457230557Sjimharris   }
458230557Sjimharris}
459230557Sjimharris
460230557Sjimharris/**
461230557Sjimharris * This method handles the manual port configuration link up notifications.
462230557Sjimharris * Since all ports and phys are associate at initialization time we just turn
463230557Sjimharris * around and notifiy the port object that there is a link up.  If this PHY is
464230557Sjimharris * not associated with a port there is no action taken.
465230557Sjimharris *
466230557Sjimharris * @param[in] controller This is the controller object that receives the
467230557Sjimharris *            link up notification.
468230557Sjimharris * @param[in] port This is the port object associated with the phy.  If the
469230557Sjimharris *            is no associated port this is an SCI_INVALID_HANDLE.
470230557Sjimharris * @param[in] phy This is the phy object which has gone ready.
471230557Sjimharris *
472230557Sjimharris * @note Is it possible to get a link up notification from a phy that has
473230557Sjimharris *       no assocoated port?
474230557Sjimharris */
475230557Sjimharrisstatic
476230557Sjimharrisvoid scic_sds_mpc_agent_link_up(
477230557Sjimharris   SCIC_SDS_CONTROLLER_T               * controller,
478230557Sjimharris   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent,
479230557Sjimharris   SCIC_SDS_PORT_T                     * port,
480230557Sjimharris   SCIC_SDS_PHY_T                      * phy
481230557Sjimharris)
482230557Sjimharris{
483230557Sjimharris   SCIC_LOG_TRACE((
484230557Sjimharris      sci_base_object_get_logger(controller),
485230557Sjimharris      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY,
486230557Sjimharris      "scic_sds_mpc_agent_link_up(0x%08x, 0x%08x, 0x%08x, 0x%08x) enter\n",
487230557Sjimharris      controller, port_agent, port, phy
488230557Sjimharris   ));
489230557Sjimharris
490230557Sjimharris   // If the port has an invalid handle then the phy was not assigned to
491230557Sjimharris   // a port.  This is because the phy was not given the same SAS Address
492230557Sjimharris   // as the other PHYs in the port.
493230557Sjimharris   if (port != SCI_INVALID_HANDLE)
494230557Sjimharris   {
495230557Sjimharris      port_agent->phy_ready_mask |= (1 << scic_sds_phy_get_index(phy));
496230557Sjimharris
497230557Sjimharris      scic_sds_port_link_up(port, phy);
498230557Sjimharris
499230557Sjimharris      if ((port->active_phy_mask & (1 << scic_sds_phy_get_index(phy))) != 0)
500230557Sjimharris      {
501230557Sjimharris         port_agent->phy_configured_mask |= (1 << scic_sds_phy_get_index(phy));
502230557Sjimharris      }
503230557Sjimharris   }
504230557Sjimharris}
505230557Sjimharris
506230557Sjimharris/**
507230557Sjimharris * This method handles the manual port configuration link down notifications.
508230557Sjimharris * Since all ports and phys are associated at initialization time we just turn
509230557Sjimharris * around and notifiy the port object of the link down event.  If this PHY is
510230557Sjimharris * not associated with a port there is no action taken.
511230557Sjimharris *
512230557Sjimharris * @param[in] controller This is the controller object that receives the
513230557Sjimharris *            link down notification.
514230557Sjimharris * @param[in] port This is the port object associated with the phy.  If the
515230557Sjimharris *            is no associated port this is an SCI_INVALID_HANDLE.  The port
516230557Sjimharris *            is an invalid handle only if the phy was never port of this
517230557Sjimharris *            port.  This happens when the phy is not broadcasting the same
518230557Sjimharris *            SAS address as the other phys in the assigned port.
519230557Sjimharris * @param[in] phy This is the phy object which has gone link down.
520230557Sjimharris *
521230557Sjimharris * @note Is it possible to get a link down notification from a phy that has
522230557Sjimharris *       no assocoated port?
523230557Sjimharris */
524230557Sjimharrisstatic
525230557Sjimharrisvoid scic_sds_mpc_agent_link_down(
526230557Sjimharris   SCIC_SDS_CONTROLLER_T               * controller,
527230557Sjimharris   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent,
528230557Sjimharris   SCIC_SDS_PORT_T                     * port,
529230557Sjimharris   SCIC_SDS_PHY_T                      * phy
530230557Sjimharris)
531230557Sjimharris{
532230557Sjimharris   SCIC_LOG_TRACE((
533230557Sjimharris      sci_base_object_get_logger(controller),
534230557Sjimharris      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY,
535230557Sjimharris      "scic_sds_mpc_agent_link_down(0x%08x, 0x%08x, 0x%08x, 0x%08x) enter\n",
536230557Sjimharris      controller, port_agent, port, phy
537230557Sjimharris   ));
538230557Sjimharris
539230557Sjimharris   if (port != SCI_INVALID_HANDLE)
540230557Sjimharris   {
541230557Sjimharris      // If we can form a new port from the remainder of the phys then we want
542230557Sjimharris      // to start the timer to allow the SCI User to cleanup old devices and
543230557Sjimharris      // rediscover the port before rebuilding the port with the phys that
544230557Sjimharris      // remain in the ready state.
545230557Sjimharris      port_agent->phy_ready_mask &= ~(1 << scic_sds_phy_get_index(phy));
546230557Sjimharris      port_agent->phy_configured_mask &= ~(1 << scic_sds_phy_get_index(phy));
547230557Sjimharris
548230557Sjimharris      // Check to see if there are more phys waiting to be configured into a port.
549230557Sjimharris      // If there are allow the SCI User to tear down this port, if necessary, and
550230557Sjimharris      // then reconstruc the port after the timeout.
551230557Sjimharris      if (
552230557Sjimharris            (port_agent->phy_configured_mask == 0x0000)
553230557Sjimharris         && (port_agent->phy_ready_mask != 0x0000)
554230557Sjimharris         && !port_agent->timer_pending
555230557Sjimharris         )
556230557Sjimharris      {
557230557Sjimharris         port_agent->timer_pending = TRUE;
558230557Sjimharris
559230557Sjimharris         scic_cb_timer_start(
560230557Sjimharris            controller,
561230557Sjimharris            port_agent->timer,
562230557Sjimharris            SCIC_SDS_MPC_RECONFIGURATION_TIMEOUT
563230557Sjimharris         );
564230557Sjimharris      }
565230557Sjimharris
566230557Sjimharris      scic_sds_port_link_down(port, phy);
567230557Sjimharris   }
568230557Sjimharris}
569230557Sjimharris
570230557Sjimharris//******************************************************************************
571230557Sjimharris// Automatic port configuration agent routines
572230557Sjimharris//******************************************************************************
573230557Sjimharris
574230557Sjimharris/**
575230557Sjimharris * This routine will verify that the phys are assigned a valid SAS address for
576230557Sjimharris * automatic port configuration mode.
577230557Sjimharris */
578230557Sjimharrisstatic
579230557SjimharrisSCI_STATUS scic_sds_apc_agent_validate_phy_configuration(
580230557Sjimharris   SCIC_SDS_CONTROLLER_T               * controller,
581230557Sjimharris   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent
582230557Sjimharris)
583230557Sjimharris{
584230557Sjimharris   U8 phy_index;
585230557Sjimharris   U8 port_index;
586230557Sjimharris   SCI_SAS_ADDRESS_T sas_address;
587230557Sjimharris   SCI_SAS_ADDRESS_T phy_assigned_address;
588230557Sjimharris
589230557Sjimharris   SCIC_LOG_TRACE((
590230557Sjimharris      sci_base_object_get_logger(controller),
591230557Sjimharris      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT,
592230557Sjimharris      "scic_sds_apc_agent_validate_phy_configuration(0x%08x, 0x%08x) enter\n",
593230557Sjimharris      controller, port_agent
594230557Sjimharris   ));
595230557Sjimharris
596230557Sjimharris   phy_index = 0;
597230557Sjimharris
598230557Sjimharris   while (phy_index < SCI_MAX_PHYS)
599230557Sjimharris   {
600230557Sjimharris      port_index = phy_index;
601230557Sjimharris
602230557Sjimharris      // Get the assigned SAS Address for the first PHY on the controller.
603230557Sjimharris      scic_sds_phy_get_sas_address(
604230557Sjimharris         &controller->phy_table[phy_index], &sas_address
605230557Sjimharris      );
606230557Sjimharris
607230557Sjimharris      while (++phy_index < SCI_MAX_PHYS)
608230557Sjimharris      {
609230557Sjimharris         scic_sds_phy_get_sas_address(
610230557Sjimharris            &controller->phy_table[phy_index], &phy_assigned_address
611230557Sjimharris         );
612230557Sjimharris
613230557Sjimharris         // Verify each of the SAS address are all the same for every PHY
614230557Sjimharris         if (sci_sas_address_compare(sas_address, phy_assigned_address) == 0)
615230557Sjimharris         {
616230557Sjimharris            port_agent->phy_valid_port_range[phy_index].min_index = port_index;
617230557Sjimharris            port_agent->phy_valid_port_range[phy_index].max_index = phy_index;
618230557Sjimharris         }
619230557Sjimharris         else
620230557Sjimharris         {
621230557Sjimharris            port_agent->phy_valid_port_range[phy_index].min_index = phy_index;
622230557Sjimharris            port_agent->phy_valid_port_range[phy_index].max_index = phy_index;
623230557Sjimharris            break;
624230557Sjimharris         }
625230557Sjimharris      }
626230557Sjimharris   }
627230557Sjimharris
628230557Sjimharris   return scic_sds_port_configuration_agent_validate_ports(controller, port_agent);
629230557Sjimharris}
630230557Sjimharris
631230557Sjimharris/**
632230557Sjimharris * This routine will restart the automatic port configuration timeout
633230557Sjimharris * timer for the next time period.  This could be caused by either a
634230557Sjimharris * link down event or a link up event where we can not yet tell to which
635230557Sjimharris * port a phy belongs.
636230557Sjimharris *
637230557Sjimharris * @param[in] controller This is the controller that to which the port
638230557Sjimharris *            agent is assigned.
639230557Sjimharris * @param[in] port_agent This is the port agent that is requesting the
640230557Sjimharris *            timer start operation.
641230557Sjimharris * @param[in] phy This is the phy that has caused the timer operation to
642230557Sjimharris *            be scheduled.
643230557Sjimharris * @param[in] timeout This is the timeout in ms.
644230557Sjimharris */
645230557Sjimharrisstatic
646230557Sjimharrisvoid scic_sds_apc_agent_start_timer(
647230557Sjimharris   SCIC_SDS_CONTROLLER_T               * controller,
648230557Sjimharris   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent,
649230557Sjimharris   SCIC_SDS_PHY_T                      * phy,
650230557Sjimharris   U32                                   timeout
651230557Sjimharris)
652230557Sjimharris{
653230557Sjimharris   SCIC_LOG_TRACE((
654230557Sjimharris      sci_base_object_get_logger(controller),
655230557Sjimharris      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY,
656230557Sjimharris      "scic_sds_apc_agent_start_timer(0x%08x, 0x%08x, 0x%08x, 0x%08x) enter\n",
657230557Sjimharris      controller, port_agent, phy, timeout
658230557Sjimharris   ));
659230557Sjimharris
660230557Sjimharris   if (port_agent->timer_pending)
661230557Sjimharris   {
662230557Sjimharris      scic_cb_timer_stop(controller, port_agent->timer);
663230557Sjimharris   }
664230557Sjimharris
665230557Sjimharris   port_agent->timer_pending = TRUE;
666230557Sjimharris
667230557Sjimharris   scic_cb_timer_start(controller, port_agent->timer, timeout);
668230557Sjimharris}
669230557Sjimharris
670230557Sjimharris/**
671230557Sjimharris * This method handles the automatic port configuration for link up notifications.
672230557Sjimharris *
673230557Sjimharris * @param[in] controller This is the controller object that receives the
674230557Sjimharris *            link up notification.
675230557Sjimharris * @param[in] phy This is the phy object which has gone link up.
676230557Sjimharris * @param[in] start_timer This tells the routine if it should start the timer for
677230557Sjimharris *            any phys that might be added to a port in the future.
678230557Sjimharris */
679230557Sjimharrisstatic
680230557Sjimharrisvoid scic_sds_apc_agent_configure_ports(
681230557Sjimharris   SCIC_SDS_CONTROLLER_T               * controller,
682230557Sjimharris   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent,
683230557Sjimharris   SCIC_SDS_PHY_T                      * phy,
684230557Sjimharris   BOOL                                  start_timer
685230557Sjimharris)
686230557Sjimharris{
687230557Sjimharris   U8 port_index;
688230557Sjimharris   SCI_STATUS status;
689230557Sjimharris   SCIC_SDS_PORT_T * port;
690230557Sjimharris   SCI_PORT_HANDLE_T port_handle;
691230557Sjimharris   enum SCIC_SDS_APC_ACTIVITY apc_activity = SCIC_SDS_APC_SKIP_PHY;
692230557Sjimharris
693230557Sjimharris   SCIC_LOG_TRACE((
694230557Sjimharris      sci_base_object_get_logger(controller),
695230557Sjimharris      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY,
696230557Sjimharris      "scic_sds_apc_agent_configure_ports(0x%08x, 0x%08x, 0x%08x, %d) enter\n",
697230557Sjimharris      controller, port_agent, phy, start_timer
698230557Sjimharris   ));
699230557Sjimharris
700230557Sjimharris   port = scic_sds_port_configuration_agent_find_port(controller, phy);
701230557Sjimharris
702230557Sjimharris   if (port != SCI_INVALID_HANDLE)
703230557Sjimharris   {
704230557Sjimharris      if (scic_sds_port_is_valid_phy_assignment(port, phy->phy_index))
705230557Sjimharris         apc_activity = SCIC_SDS_APC_ADD_PHY;
706230557Sjimharris      else
707230557Sjimharris         apc_activity = SCIC_SDS_APC_SKIP_PHY;
708230557Sjimharris   }
709230557Sjimharris   else
710230557Sjimharris   {
711230557Sjimharris      // There is no matching Port for this PHY so lets search through the
712230557Sjimharris      // Ports and see if we can add the PHY to its own port or maybe start
713230557Sjimharris      // the timer and wait to see if a wider port can be made.
714230557Sjimharris      //
715230557Sjimharris      // Note the break when we reach the condition of the port id == phy id
716230557Sjimharris      for (
717230557Sjimharris             port_index = port_agent->phy_valid_port_range[phy->phy_index].min_index;
718230557Sjimharris             port_index <= port_agent->phy_valid_port_range[phy->phy_index].max_index;
719230557Sjimharris             port_index++
720230557Sjimharris          )
721230557Sjimharris      {
722230557Sjimharris         scic_controller_get_port_handle(controller, port_index, &port_handle);
723230557Sjimharris
724230557Sjimharris         port = (SCIC_SDS_PORT_T *)port_handle;
725230557Sjimharris
726230557Sjimharris         // First we must make sure that this PHY can be added to this Port.
727230557Sjimharris         if (scic_sds_port_is_valid_phy_assignment(port, phy->phy_index))
728230557Sjimharris         {
729230557Sjimharris            // Port contains a PHY with a greater PHY ID than the current
730230557Sjimharris            // PHY that has gone link up.  This phy can not be part of any
731230557Sjimharris            // port so skip it and move on.
732230557Sjimharris            if (port->active_phy_mask > (1 << phy->phy_index))
733230557Sjimharris            {
734230557Sjimharris               apc_activity = SCIC_SDS_APC_SKIP_PHY;
735230557Sjimharris               break;
736230557Sjimharris            }
737230557Sjimharris
738230557Sjimharris            // We have reached the end of our Port list and have not found
739230557Sjimharris            // any reason why we should not either add the PHY to the port
740230557Sjimharris            // or wait for more phys to become active.
741230557Sjimharris            if (port->physical_port_index == phy->phy_index)
742230557Sjimharris            {
743230557Sjimharris               // The Port either has no active PHYs.
744230557Sjimharris               // Consider that if the port had any active PHYs we would have
745230557Sjimharris               // or active PHYs with
746230557Sjimharris               // a lower PHY Id than this PHY.
747230557Sjimharris               if (apc_activity != SCIC_SDS_APC_START_TIMER)
748230557Sjimharris               {
749230557Sjimharris                  apc_activity = SCIC_SDS_APC_ADD_PHY;
750230557Sjimharris               }
751230557Sjimharris
752230557Sjimharris               break;
753230557Sjimharris            }
754230557Sjimharris
755230557Sjimharris            // The current Port has no active PHYs and this PHY could be part
756230557Sjimharris            // of this Port.  Since we dont know as yet setup to start the
757230557Sjimharris            // timer and see if there is a better configuration.
758230557Sjimharris            if (port->active_phy_mask == 0)
759230557Sjimharris            {
760230557Sjimharris               apc_activity = SCIC_SDS_APC_START_TIMER;
761230557Sjimharris            }
762230557Sjimharris         }
763230557Sjimharris         else if (port->active_phy_mask != 0)
764230557Sjimharris         {
765230557Sjimharris            // The Port has an active phy and the current Phy can not
766230557Sjimharris            // participate in this port so skip the PHY and see if
767230557Sjimharris            // there is a better configuration.
768230557Sjimharris            apc_activity = SCIC_SDS_APC_SKIP_PHY;
769230557Sjimharris         }
770230557Sjimharris      }
771230557Sjimharris   }
772230557Sjimharris
773230557Sjimharris   // Check to see if the start timer operations should instead map to an
774230557Sjimharris   // add phy operation.  This is caused because we have been waiting to
775230557Sjimharris   // add a phy to a port but could not becuase the automatic port
776230557Sjimharris   // configuration engine had a choice of possible ports for the phy.
777230557Sjimharris   // Since we have gone through a timeout we are going to restrict the
778230557Sjimharris   // choice to the smallest possible port.
779230557Sjimharris   if (
780230557Sjimharris         (start_timer == FALSE)
781230557Sjimharris      && (apc_activity == SCIC_SDS_APC_START_TIMER)
782230557Sjimharris      )
783230557Sjimharris   {
784230557Sjimharris      apc_activity = SCIC_SDS_APC_ADD_PHY;
785230557Sjimharris   }
786230557Sjimharris
787230557Sjimharris   switch (apc_activity)
788230557Sjimharris   {
789230557Sjimharris   case SCIC_SDS_APC_ADD_PHY:
790230557Sjimharris      status = scic_sds_port_add_phy(port, phy);
791230557Sjimharris
792230557Sjimharris      if (status == SCI_SUCCESS)
793230557Sjimharris      {
794230557Sjimharris         port_agent->phy_configured_mask |= (1 << phy->phy_index);
795230557Sjimharris      }
796230557Sjimharris      break;
797230557Sjimharris
798230557Sjimharris   case SCIC_SDS_APC_START_TIMER:
799230557Sjimharris      scic_sds_apc_agent_start_timer(
800230557Sjimharris         controller, port_agent, phy, SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION
801230557Sjimharris      );
802230557Sjimharris      break;
803230557Sjimharris
804230557Sjimharris   case SCIC_SDS_APC_SKIP_PHY:
805230557Sjimharris   default:
806230557Sjimharris      // do nothing the PHY can not be made part of a port at this time.
807230557Sjimharris      break;
808230557Sjimharris   }
809230557Sjimharris}
810230557Sjimharris
811230557Sjimharris/**
812230557Sjimharris * This method handles the automatic port configuration for link up notifications.
813230557Sjimharris *
814230557Sjimharris * @param[in] controller This is the controller object that receives the
815230557Sjimharris *            link up notification.
816230557Sjimharris * @param[in] port This is the port object associated with the phy.  If the
817230557Sjimharris *            is no associated port this is an SCI_INVALID_HANDLE.
818230557Sjimharris * @param[in] phy This is the phy object which has gone link up.
819230557Sjimharris *
820230557Sjimharris * @note Is it possible to get a link down notification from a phy that has
821230557Sjimharris *       no assocoated port?
822230557Sjimharris */
823230557Sjimharrisstatic
824230557Sjimharrisvoid scic_sds_apc_agent_link_up(
825230557Sjimharris   SCIC_SDS_CONTROLLER_T               * controller,
826230557Sjimharris   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent,
827230557Sjimharris   SCIC_SDS_PORT_T                     * port,
828230557Sjimharris   SCIC_SDS_PHY_T                      * phy
829230557Sjimharris)
830230557Sjimharris{
831230557Sjimharris   SCIC_LOG_TRACE((
832230557Sjimharris      sci_base_object_get_logger(controller),
833230557Sjimharris      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY,
834230557Sjimharris      "scic_sds_apc_agent_link_up(0x%08x, 0x%08x, 0x%08x, 0x%08x) enter\n",
835230557Sjimharris      controller, port_agent, port, phy
836230557Sjimharris   ));
837230557Sjimharris
838230557Sjimharris   //the phy is not the part of this port, configure the port with this phy
839230557Sjimharris   if (port == SCI_INVALID_HANDLE)
840230557Sjimharris   {
841230557Sjimharris      port_agent->phy_ready_mask |= (1 << scic_sds_phy_get_index(phy));
842230557Sjimharris
843230557Sjimharris      scic_sds_apc_agent_start_timer(
844230557Sjimharris         controller, port_agent, phy, SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION
845230557Sjimharris      );
846230557Sjimharris   }
847230557Sjimharris   else
848230557Sjimharris   {
849230557Sjimharris      //the phy is already the part of the port
850230557Sjimharris
851230557Sjimharris      //if the PORT'S state is resetting then the link up is from port hard reset
852230557Sjimharris      //in this case, we need to tell the port that link up is recieved
853230557Sjimharris      if (  SCI_BASE_PORT_STATE_RESETTING
854230557Sjimharris            == port->parent.state_machine.current_state_id
855230557Sjimharris         )
856230557Sjimharris      {
857230557Sjimharris         //notify the port that port needs to be ready
858230557Sjimharris         port_agent->phy_ready_mask |= (1 << scic_sds_phy_get_index(phy));
859230557Sjimharris         scic_sds_port_link_up(port, phy);
860230557Sjimharris      }
861230557Sjimharris      else
862230557Sjimharris      {
863230557Sjimharris         ASSERT (0);
864230557Sjimharris      }
865230557Sjimharris   }
866230557Sjimharris}
867230557Sjimharris
868230557Sjimharris/**
869230557Sjimharris * This method handles the automatic port configuration link down notifications.
870230557Sjimharris * If this PHY is * not associated with a port there is no action taken.
871230557Sjimharris *
872230557Sjimharris * @param[in] controller This is the controller object that receives the
873230557Sjimharris *            link down notification.
874230557Sjimharris * @param[in] port This is the port object associated with the phy.  If the
875230557Sjimharris *            is no associated port this is an SCI_INVALID_HANDLE.
876230557Sjimharris * @param[in] phy This is the phy object which has gone link down.
877230557Sjimharris *
878230557Sjimharris * @note Is it possible to get a link down notification from a phy that has
879230557Sjimharris *       no assocoated port?
880230557Sjimharris */
881230557Sjimharrisstatic
882230557Sjimharrisvoid scic_sds_apc_agent_link_down(
883230557Sjimharris   SCIC_SDS_CONTROLLER_T               * controller,
884230557Sjimharris   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent,
885230557Sjimharris   SCIC_SDS_PORT_T                     * port,
886230557Sjimharris   SCIC_SDS_PHY_T                      * phy
887230557Sjimharris)
888230557Sjimharris{
889230557Sjimharris   SCIC_LOG_TRACE((
890230557Sjimharris      sci_base_object_get_logger(controller),
891230557Sjimharris      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY,
892230557Sjimharris      "scic_sds_apc_agent_link_down(0x%08x, 0x%08x, 0x%08x, 0x%08x) enter\n",
893230557Sjimharris      controller, port_agent, port, phy
894230557Sjimharris   ));
895230557Sjimharris
896230557Sjimharris   port_agent->phy_ready_mask &= ~(1 << scic_sds_phy_get_index(phy));
897230557Sjimharris
898230557Sjimharris   if (port != SCI_INVALID_HANDLE)
899230557Sjimharris   {
900230557Sjimharris      if (port_agent->phy_configured_mask & (1 << phy->phy_index))
901230557Sjimharris      {
902230557Sjimharris         SCI_STATUS status;
903230557Sjimharris
904230557Sjimharris         status = scic_sds_port_remove_phy(port, phy);
905230557Sjimharris
906230557Sjimharris         if (status == SCI_SUCCESS)
907230557Sjimharris         {
908230557Sjimharris            port_agent->phy_configured_mask &= ~(1 << phy->phy_index);
909230557Sjimharris         }
910230557Sjimharris      }
911230557Sjimharris   }
912230557Sjimharris}
913230557Sjimharris
914230557Sjimharris/**
915230557Sjimharris * This routine will try to configure the phys into ports when the timer fires.
916230557Sjimharris *
917230557Sjimharris * @param[in] object This is actually the controller that needs to have the
918230557Sjimharris *            pending phys configured.
919230557Sjimharris */
920230557Sjimharrisstatic
921230557Sjimharrisvoid scic_sds_apc_agent_timeout_handler(
922230557Sjimharris   void * object
923230557Sjimharris)
924230557Sjimharris{
925230557Sjimharris   U32 index;
926230557Sjimharris   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent;
927230557Sjimharris   SCIC_SDS_CONTROLLER_T * controller = (SCIC_SDS_CONTROLLER_T *)object;
928230557Sjimharris   U16 configure_phy_mask;
929230557Sjimharris
930230557Sjimharris   port_agent = scic_sds_controller_get_port_configuration_agent(controller);
931230557Sjimharris
932230557Sjimharris   SCIC_LOG_TRACE((
933230557Sjimharris      sci_base_object_get_logger(controller),
934230557Sjimharris      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT,
935230557Sjimharris      "scic_sds_apc_agent_timeout_handler(0x%08x) enter\n",
936230557Sjimharris      controller
937230557Sjimharris   ));
938230557Sjimharris
939230557Sjimharris   port_agent->timer_pending = FALSE;
940230557Sjimharris
941230557Sjimharris   configure_phy_mask = ~port_agent->phy_configured_mask & port_agent->phy_ready_mask;
942230557Sjimharris
943230557Sjimharris   if (configure_phy_mask != 0x00)
944230557Sjimharris   {
945230557Sjimharris      for (index = 0; index < SCI_MAX_PHYS; index++)
946230557Sjimharris      {
947230557Sjimharris         if (configure_phy_mask & (1 << index))
948230557Sjimharris         {
949230557Sjimharris            scic_sds_apc_agent_configure_ports(
950230557Sjimharris               controller, port_agent, &controller->phy_table[index], FALSE
951230557Sjimharris            );
952230557Sjimharris         }
953230557Sjimharris      }
954230557Sjimharris
955230557Sjimharris      //Notify the controller ports are configured.
956230557Sjimharris      if (
957230557Sjimharris            (port_agent->phy_ready_mask == port_agent->phy_configured_mask) &&
958230557Sjimharris            (controller->next_phy_to_start == SCI_MAX_PHYS) &&
959230557Sjimharris            (controller->phy_startup_timer_pending == FALSE)
960230557Sjimharris         )
961230557Sjimharris      {
962230557Sjimharris         // The controller has successfully finished the start process.
963230557Sjimharris         // Inform the SCI Core user and transition to the READY state.
964230557Sjimharris         if (scic_sds_controller_is_start_complete(controller) == TRUE)
965230557Sjimharris         {
966230557Sjimharris            scic_sds_controller_port_agent_configured_ports(controller);
967230557Sjimharris         }
968230557Sjimharris      }
969230557Sjimharris   }
970230557Sjimharris}
971230557Sjimharris
972230557Sjimharris//******************************************************************************
973230557Sjimharris// Public port configuration agent routines
974230557Sjimharris//******************************************************************************
975230557Sjimharris
976230557Sjimharris/**
977230557Sjimharris * This method will construct the port configuration agent for operation.
978230557Sjimharris * This call is universal for both manual port configuration and automatic
979230557Sjimharris * port configuration modes.
980230557Sjimharris *
981230557Sjimharris * @param[in] port_agent This is the port configuration agent for this
982230557Sjimharris *            controller object.
983230557Sjimharris */
984230557Sjimharrisvoid scic_sds_port_configuration_agent_construct(
985230557Sjimharris   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent
986230557Sjimharris)
987230557Sjimharris{
988230557Sjimharris   U32 index;
989230557Sjimharris
990230557Sjimharris   port_agent->phy_configured_mask = 0x00;
991230557Sjimharris   port_agent->phy_ready_mask = 0x00;
992230557Sjimharris
993230557Sjimharris   port_agent->link_up_handler = NULL;
994230557Sjimharris   port_agent->link_down_handler = NULL;
995230557Sjimharris
996230557Sjimharris   port_agent->timer_pending = FALSE;
997230557Sjimharris   port_agent->timer = NULL;
998230557Sjimharris
999230557Sjimharris   for (index = 0; index < SCI_MAX_PORTS; index++)
1000230557Sjimharris   {
1001230557Sjimharris      port_agent->phy_valid_port_range[index].min_index = 0;
1002230557Sjimharris      port_agent->phy_valid_port_range[index].max_index = 0;
1003230557Sjimharris   }
1004230557Sjimharris}
1005230557Sjimharris
1006230557Sjimharris/**
1007230557Sjimharris * This method will construct the port configuration agent for this controller.
1008230557Sjimharris *
1009230557Sjimharris * @param[in] controller This is the controller object for which the port
1010230557Sjimharris *            agent is being initialized.
1011230557Sjimharris *
1012230557Sjimharris * @param[in] port_agent This is the port configuration agent that is being
1013230557Sjimharris *            initialized.  The initialization path is handled differntly
1014230557Sjimharris *            for the automatic port configuration agent and the manual port
1015230557Sjimharris *            configuration agent.
1016230557Sjimharris *
1017230557Sjimharris * @return
1018230557Sjimharris */
1019230557SjimharrisSCI_STATUS scic_sds_port_configuration_agent_initialize(
1020230557Sjimharris   SCIC_SDS_CONTROLLER_T               * controller,
1021230557Sjimharris   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent
1022230557Sjimharris)
1023230557Sjimharris{
1024230557Sjimharris   SCI_STATUS status = SCI_SUCCESS;
1025230557Sjimharris   enum SCIC_PORT_CONFIGURATION_MODE mode;
1026230557Sjimharris
1027230557Sjimharris   SCIC_LOG_TRACE((
1028230557Sjimharris      sci_base_object_get_logger(controller),
1029230557Sjimharris      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT,
1030230557Sjimharris      "scic_sds_port_configuration_agent_initialize(0x%08x, 0x%08x) enter\n",
1031230557Sjimharris      controller, port_agent
1032230557Sjimharris   ));
1033230557Sjimharris
1034230557Sjimharris   mode = controller->oem_parameters.sds1.controller.mode_type;
1035230557Sjimharris
1036230557Sjimharris   if (mode == SCIC_PORT_MANUAL_CONFIGURATION_MODE)
1037230557Sjimharris   {
1038230557Sjimharris      status = scic_sds_mpc_agent_validate_phy_configuration(controller, port_agent);
1039230557Sjimharris
1040230557Sjimharris      port_agent->link_up_handler = scic_sds_mpc_agent_link_up;
1041230557Sjimharris      port_agent->link_down_handler = scic_sds_mpc_agent_link_down;
1042230557Sjimharris
1043230557Sjimharris      port_agent->timer = scic_cb_timer_create(
1044230557Sjimharris                              controller,
1045230557Sjimharris                              scic_sds_mpc_agent_timeout_handler,
1046230557Sjimharris                              controller
1047230557Sjimharris                          );
1048230557Sjimharris   }
1049230557Sjimharris   else
1050230557Sjimharris   {
1051230557Sjimharris      status = scic_sds_apc_agent_validate_phy_configuration(controller, port_agent);
1052230557Sjimharris
1053230557Sjimharris      port_agent->link_up_handler = scic_sds_apc_agent_link_up;
1054230557Sjimharris      port_agent->link_down_handler = scic_sds_apc_agent_link_down;
1055230557Sjimharris
1056230557Sjimharris      port_agent->timer = scic_cb_timer_create(
1057230557Sjimharris                              controller,
1058230557Sjimharris                              scic_sds_apc_agent_timeout_handler,
1059230557Sjimharris                              controller
1060230557Sjimharris                          );
1061230557Sjimharris   }
1062230557Sjimharris
1063230557Sjimharris   // Make sure we have actually gotten a timer
1064230557Sjimharris   if (status == SCI_SUCCESS && port_agent->timer == NULL)
1065230557Sjimharris   {
1066230557Sjimharris      SCIC_LOG_ERROR((
1067230557Sjimharris         sci_base_object_get_logger(controller),
1068230557Sjimharris         SCIC_LOG_OBJECT_CONTROLLER,
1069230557Sjimharris         "Controller 0x%x automatic port configuration agent could not get timer.\n",
1070230557Sjimharris         controller
1071230557Sjimharris     ));
1072230557Sjimharris
1073230557Sjimharris     status = SCI_FAILURE;
1074230557Sjimharris   }
1075230557Sjimharris
1076230557Sjimharris   return status;
1077230557Sjimharris}
1078230557Sjimharris
1079230557Sjimharris/**
1080230557Sjimharris * This method will destroy the port configuration agent for this controller.
1081230557Sjimharris *
1082230557Sjimharris * @param[in] controller This is the controller object for which the port
1083230557Sjimharris *            agent is being destroyed.
1084230557Sjimharris *
1085230557Sjimharris * @param[in] port_agent This is the port configuration agent that is being
1086230557Sjimharris *            destroyed.
1087230557Sjimharris *
1088230557Sjimharris * @return
1089230557Sjimharris */
1090230557Sjimharrisvoid scic_sds_port_configuration_agent_destroy(
1091230557Sjimharris   SCIC_SDS_CONTROLLER_T               * controller,
1092230557Sjimharris   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent
1093230557Sjimharris)
1094230557Sjimharris{
1095230557Sjimharris   if (port_agent->timer_pending == TRUE)
1096230557Sjimharris   {
1097230557Sjimharris      scic_cb_timer_stop(controller, port_agent->timer);
1098230557Sjimharris   }
1099230557Sjimharris
1100230557Sjimharris   scic_cb_timer_destroy(controller, port_agent->timer);
1101230557Sjimharris
1102230557Sjimharris   port_agent->timer_pending = FALSE;
1103230557Sjimharris   port_agent->timer = NULL;
1104230557Sjimharris}
1105230557Sjimharris
1106230557Sjimharris
1107230557Sjimharris/**
1108230557Sjimharris * @brief This method release resources in for a scic port configuration agent.
1109230557Sjimharris *
1110230557Sjimharris * @param[in] controller This parameter specifies the core controller, one of
1111230557Sjimharris *            its phy's resources are to be released.
1112249586Sgabor * @param[in] this_phy This parameter specifies the phy whose resource is to
1113230557Sjimharris *            be released.
1114230557Sjimharris */
1115230557Sjimharrisvoid scic_sds_port_configuration_agent_release_resource(
1116230557Sjimharris   SCIC_SDS_CONTROLLER_T               * controller,
1117230557Sjimharris   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent
1118230557Sjimharris)
1119230557Sjimharris{
1120230557Sjimharris   SCIC_LOG_TRACE((
1121230557Sjimharris      sci_base_object_get_logger(controller),
1122230557Sjimharris      SCIC_LOG_OBJECT_PORT,
1123230557Sjimharris      "scic_sds_port_configuration_agent_release_resource(0x%x, 0x%x)\n",
1124230557Sjimharris      controller, port_agent
1125230557Sjimharris   ));
1126230557Sjimharris
1127230557Sjimharris   //Currently, the only resource to be released is a timer.
1128230557Sjimharris   if (port_agent->timer != NULL)
1129230557Sjimharris   {
1130230557Sjimharris      scic_cb_timer_destroy(controller, port_agent->timer);
1131230557Sjimharris      port_agent->timer = NULL;
1132230557Sjimharris   }
1133230557Sjimharris}
1134