xref: /OK3568_Linux_fs/kernel/drivers/scsi/isci/port_config.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * This file is provided under a dual BSD/GPLv2 license.  When using or
3*4882a593Smuzhiyun  * redistributing this file, you may do so under either license.
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * GPL LICENSE SUMMARY
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
8*4882a593Smuzhiyun  *
9*4882a593Smuzhiyun  * This program is free software; you can redistribute it and/or modify
10*4882a593Smuzhiyun  * it under the terms of version 2 of the GNU General Public License as
11*4882a593Smuzhiyun  * published by the Free Software Foundation.
12*4882a593Smuzhiyun  *
13*4882a593Smuzhiyun  * This program is distributed in the hope that it will be useful, but
14*4882a593Smuzhiyun  * WITHOUT ANY WARRANTY; without even the implied warranty of
15*4882a593Smuzhiyun  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16*4882a593Smuzhiyun  * General Public License for more details.
17*4882a593Smuzhiyun  *
18*4882a593Smuzhiyun  * You should have received a copy of the GNU General Public License
19*4882a593Smuzhiyun  * along with this program; if not, write to the Free Software
20*4882a593Smuzhiyun  * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
21*4882a593Smuzhiyun  * The full GNU General Public License is included in this distribution
22*4882a593Smuzhiyun  * in the file called LICENSE.GPL.
23*4882a593Smuzhiyun  *
24*4882a593Smuzhiyun  * BSD LICENSE
25*4882a593Smuzhiyun  *
26*4882a593Smuzhiyun  * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
27*4882a593Smuzhiyun  * All rights reserved.
28*4882a593Smuzhiyun  *
29*4882a593Smuzhiyun  * Redistribution and use in source and binary forms, with or without
30*4882a593Smuzhiyun  * modification, are permitted provided that the following conditions
31*4882a593Smuzhiyun  * are met:
32*4882a593Smuzhiyun  *
33*4882a593Smuzhiyun  *   * Redistributions of source code must retain the above copyright
34*4882a593Smuzhiyun  *     notice, this list of conditions and the following disclaimer.
35*4882a593Smuzhiyun  *   * Redistributions in binary form must reproduce the above copyright
36*4882a593Smuzhiyun  *     notice, this list of conditions and the following disclaimer in
37*4882a593Smuzhiyun  *     the documentation and/or other materials provided with the
38*4882a593Smuzhiyun  *     distribution.
39*4882a593Smuzhiyun  *   * Neither the name of Intel Corporation nor the names of its
40*4882a593Smuzhiyun  *     contributors may be used to endorse or promote products derived
41*4882a593Smuzhiyun  *     from this software without specific prior written permission.
42*4882a593Smuzhiyun  *
43*4882a593Smuzhiyun  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
44*4882a593Smuzhiyun  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
45*4882a593Smuzhiyun  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
46*4882a593Smuzhiyun  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
47*4882a593Smuzhiyun  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
48*4882a593Smuzhiyun  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
49*4882a593Smuzhiyun  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
50*4882a593Smuzhiyun  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
51*4882a593Smuzhiyun  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
52*4882a593Smuzhiyun  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
53*4882a593Smuzhiyun  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
54*4882a593Smuzhiyun  */
55*4882a593Smuzhiyun 
56*4882a593Smuzhiyun #include "host.h"
57*4882a593Smuzhiyun 
58*4882a593Smuzhiyun #define SCIC_SDS_MPC_RECONFIGURATION_TIMEOUT    (10)
59*4882a593Smuzhiyun #define SCIC_SDS_APC_RECONFIGURATION_TIMEOUT    (10)
60*4882a593Smuzhiyun #define SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION  (1000)
61*4882a593Smuzhiyun 
62*4882a593Smuzhiyun enum SCIC_SDS_APC_ACTIVITY {
63*4882a593Smuzhiyun 	SCIC_SDS_APC_SKIP_PHY,
64*4882a593Smuzhiyun 	SCIC_SDS_APC_ADD_PHY,
65*4882a593Smuzhiyun 	SCIC_SDS_APC_START_TIMER,
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun 	SCIC_SDS_APC_ACTIVITY_MAX
68*4882a593Smuzhiyun };
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun /*
71*4882a593Smuzhiyun  * ******************************************************************************
72*4882a593Smuzhiyun  * General port configuration agent routines
73*4882a593Smuzhiyun  * ****************************************************************************** */
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun /**
76*4882a593Smuzhiyun  *
77*4882a593Smuzhiyun  * @address_one: A SAS Address to be compared.
78*4882a593Smuzhiyun  * @address_two: A SAS Address to be compared.
79*4882a593Smuzhiyun  *
80*4882a593Smuzhiyun  * Compare the two SAS Address and if SAS Address One is greater than SAS
81*4882a593Smuzhiyun  * Address Two then return > 0 else if SAS Address One is less than SAS Address
82*4882a593Smuzhiyun  * Two return < 0 Otherwise they are the same return 0 A signed value of x > 0
83*4882a593Smuzhiyun  * > y where x is returned for Address One > Address Two y is returned for
84*4882a593Smuzhiyun  * Address One < Address Two 0 is returned ofr Address One = Address Two
85*4882a593Smuzhiyun  */
sci_sas_address_compare(struct sci_sas_address address_one,struct sci_sas_address address_two)86*4882a593Smuzhiyun static s32 sci_sas_address_compare(
87*4882a593Smuzhiyun 	struct sci_sas_address address_one,
88*4882a593Smuzhiyun 	struct sci_sas_address address_two)
89*4882a593Smuzhiyun {
90*4882a593Smuzhiyun 	if (address_one.high > address_two.high) {
91*4882a593Smuzhiyun 		return 1;
92*4882a593Smuzhiyun 	} else if (address_one.high < address_two.high) {
93*4882a593Smuzhiyun 		return -1;
94*4882a593Smuzhiyun 	} else if (address_one.low > address_two.low) {
95*4882a593Smuzhiyun 		return 1;
96*4882a593Smuzhiyun 	} else if (address_one.low < address_two.low) {
97*4882a593Smuzhiyun 		return -1;
98*4882a593Smuzhiyun 	}
99*4882a593Smuzhiyun 
100*4882a593Smuzhiyun 	/* The two SAS Address must be identical */
101*4882a593Smuzhiyun 	return 0;
102*4882a593Smuzhiyun }
103*4882a593Smuzhiyun 
104*4882a593Smuzhiyun /**
105*4882a593Smuzhiyun  *
106*4882a593Smuzhiyun  * @controller: The controller object used for the port search.
107*4882a593Smuzhiyun  * @phy: The phy object to match.
108*4882a593Smuzhiyun  *
109*4882a593Smuzhiyun  * This routine will find a matching port for the phy.  This means that the
110*4882a593Smuzhiyun  * port and phy both have the same broadcast sas address and same received sas
111*4882a593Smuzhiyun  * address. The port address or the NULL if there is no matching
112*4882a593Smuzhiyun  * port. port address if the port can be found to match the phy.
113*4882a593Smuzhiyun  * NULL if there is no matching port for the phy.
114*4882a593Smuzhiyun  */
sci_port_configuration_agent_find_port(struct isci_host * ihost,struct isci_phy * iphy)115*4882a593Smuzhiyun static struct isci_port *sci_port_configuration_agent_find_port(
116*4882a593Smuzhiyun 	struct isci_host *ihost,
117*4882a593Smuzhiyun 	struct isci_phy *iphy)
118*4882a593Smuzhiyun {
119*4882a593Smuzhiyun 	u8 i;
120*4882a593Smuzhiyun 	struct sci_sas_address port_sas_address;
121*4882a593Smuzhiyun 	struct sci_sas_address port_attached_device_address;
122*4882a593Smuzhiyun 	struct sci_sas_address phy_sas_address;
123*4882a593Smuzhiyun 	struct sci_sas_address phy_attached_device_address;
124*4882a593Smuzhiyun 
125*4882a593Smuzhiyun 	/*
126*4882a593Smuzhiyun 	 * Since this phy can be a member of a wide port check to see if one or
127*4882a593Smuzhiyun 	 * more phys match the sent and received SAS address as this phy in which
128*4882a593Smuzhiyun 	 * case it should participate in the same port.
129*4882a593Smuzhiyun 	 */
130*4882a593Smuzhiyun 	sci_phy_get_sas_address(iphy, &phy_sas_address);
131*4882a593Smuzhiyun 	sci_phy_get_attached_sas_address(iphy, &phy_attached_device_address);
132*4882a593Smuzhiyun 
133*4882a593Smuzhiyun 	for (i = 0; i < ihost->logical_port_entries; i++) {
134*4882a593Smuzhiyun 		struct isci_port *iport = &ihost->ports[i];
135*4882a593Smuzhiyun 
136*4882a593Smuzhiyun 		sci_port_get_sas_address(iport, &port_sas_address);
137*4882a593Smuzhiyun 		sci_port_get_attached_sas_address(iport, &port_attached_device_address);
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun 		if (sci_sas_address_compare(port_sas_address, phy_sas_address) == 0 &&
140*4882a593Smuzhiyun 		    sci_sas_address_compare(port_attached_device_address, phy_attached_device_address) == 0)
141*4882a593Smuzhiyun 			return iport;
142*4882a593Smuzhiyun 	}
143*4882a593Smuzhiyun 
144*4882a593Smuzhiyun 	return NULL;
145*4882a593Smuzhiyun }
146*4882a593Smuzhiyun 
147*4882a593Smuzhiyun /**
148*4882a593Smuzhiyun  *
149*4882a593Smuzhiyun  * @controller: This is the controller object that contains the port agent
150*4882a593Smuzhiyun  * @port_agent: This is the port configuration agent for the controller.
151*4882a593Smuzhiyun  *
152*4882a593Smuzhiyun  * This routine will validate the port configuration is correct for the SCU
153*4882a593Smuzhiyun  * hardware.  The SCU hardware allows for port configurations as follows. LP0
154*4882a593Smuzhiyun  * -> (PE0), (PE0, PE1), (PE0, PE1, PE2, PE3) LP1 -> (PE1) LP2 -> (PE2), (PE2,
155*4882a593Smuzhiyun  * PE3) LP3 -> (PE3) enum sci_status SCI_SUCCESS the port configuration is valid for
156*4882a593Smuzhiyun  * this port configuration agent. SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION
157*4882a593Smuzhiyun  * the port configuration is not valid for this port configuration agent.
158*4882a593Smuzhiyun  */
sci_port_configuration_agent_validate_ports(struct isci_host * ihost,struct sci_port_configuration_agent * port_agent)159*4882a593Smuzhiyun static enum sci_status sci_port_configuration_agent_validate_ports(
160*4882a593Smuzhiyun 	struct isci_host *ihost,
161*4882a593Smuzhiyun 	struct sci_port_configuration_agent *port_agent)
162*4882a593Smuzhiyun {
163*4882a593Smuzhiyun 	struct sci_sas_address first_address;
164*4882a593Smuzhiyun 	struct sci_sas_address second_address;
165*4882a593Smuzhiyun 
166*4882a593Smuzhiyun 	/*
167*4882a593Smuzhiyun 	 * Sanity check the max ranges for all the phys the max index
168*4882a593Smuzhiyun 	 * is always equal to the port range index */
169*4882a593Smuzhiyun 	if (port_agent->phy_valid_port_range[0].max_index != 0 ||
170*4882a593Smuzhiyun 	    port_agent->phy_valid_port_range[1].max_index != 1 ||
171*4882a593Smuzhiyun 	    port_agent->phy_valid_port_range[2].max_index != 2 ||
172*4882a593Smuzhiyun 	    port_agent->phy_valid_port_range[3].max_index != 3)
173*4882a593Smuzhiyun 		return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
174*4882a593Smuzhiyun 
175*4882a593Smuzhiyun 	/*
176*4882a593Smuzhiyun 	 * This is a request to configure a single x4 port or at least attempt
177*4882a593Smuzhiyun 	 * to make all the phys into a single port */
178*4882a593Smuzhiyun 	if (port_agent->phy_valid_port_range[0].min_index == 0 &&
179*4882a593Smuzhiyun 	    port_agent->phy_valid_port_range[1].min_index == 0 &&
180*4882a593Smuzhiyun 	    port_agent->phy_valid_port_range[2].min_index == 0 &&
181*4882a593Smuzhiyun 	    port_agent->phy_valid_port_range[3].min_index == 0)
182*4882a593Smuzhiyun 		return SCI_SUCCESS;
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun 	/*
185*4882a593Smuzhiyun 	 * This is a degenerate case where phy 1 and phy 2 are assigned
186*4882a593Smuzhiyun 	 * to the same port this is explicitly disallowed by the hardware
187*4882a593Smuzhiyun 	 * unless they are part of the same x4 port and this condition was
188*4882a593Smuzhiyun 	 * already checked above. */
189*4882a593Smuzhiyun 	if (port_agent->phy_valid_port_range[2].min_index == 1) {
190*4882a593Smuzhiyun 		return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
191*4882a593Smuzhiyun 	}
192*4882a593Smuzhiyun 
193*4882a593Smuzhiyun 	/*
194*4882a593Smuzhiyun 	 * PE0 and PE3 can never have the same SAS Address unless they
195*4882a593Smuzhiyun 	 * are part of the same x4 wide port and we have already checked
196*4882a593Smuzhiyun 	 * for this condition. */
197*4882a593Smuzhiyun 	sci_phy_get_sas_address(&ihost->phys[0], &first_address);
198*4882a593Smuzhiyun 	sci_phy_get_sas_address(&ihost->phys[3], &second_address);
199*4882a593Smuzhiyun 
200*4882a593Smuzhiyun 	if (sci_sas_address_compare(first_address, second_address) == 0) {
201*4882a593Smuzhiyun 		return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
202*4882a593Smuzhiyun 	}
203*4882a593Smuzhiyun 
204*4882a593Smuzhiyun 	/*
205*4882a593Smuzhiyun 	 * PE0 and PE1 are configured into a 2x1 ports make sure that the
206*4882a593Smuzhiyun 	 * SAS Address for PE0 and PE2 are different since they can not be
207*4882a593Smuzhiyun 	 * part of the same port. */
208*4882a593Smuzhiyun 	if (port_agent->phy_valid_port_range[0].min_index == 0 &&
209*4882a593Smuzhiyun 	    port_agent->phy_valid_port_range[1].min_index == 1) {
210*4882a593Smuzhiyun 		sci_phy_get_sas_address(&ihost->phys[0], &first_address);
211*4882a593Smuzhiyun 		sci_phy_get_sas_address(&ihost->phys[2], &second_address);
212*4882a593Smuzhiyun 
213*4882a593Smuzhiyun 		if (sci_sas_address_compare(first_address, second_address) == 0) {
214*4882a593Smuzhiyun 			return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
215*4882a593Smuzhiyun 		}
216*4882a593Smuzhiyun 	}
217*4882a593Smuzhiyun 
218*4882a593Smuzhiyun 	/*
219*4882a593Smuzhiyun 	 * PE2 and PE3 are configured into a 2x1 ports make sure that the
220*4882a593Smuzhiyun 	 * SAS Address for PE1 and PE3 are different since they can not be
221*4882a593Smuzhiyun 	 * part of the same port. */
222*4882a593Smuzhiyun 	if (port_agent->phy_valid_port_range[2].min_index == 2 &&
223*4882a593Smuzhiyun 	    port_agent->phy_valid_port_range[3].min_index == 3) {
224*4882a593Smuzhiyun 		sci_phy_get_sas_address(&ihost->phys[1], &first_address);
225*4882a593Smuzhiyun 		sci_phy_get_sas_address(&ihost->phys[3], &second_address);
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun 		if (sci_sas_address_compare(first_address, second_address) == 0) {
228*4882a593Smuzhiyun 			return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
229*4882a593Smuzhiyun 		}
230*4882a593Smuzhiyun 	}
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun 	return SCI_SUCCESS;
233*4882a593Smuzhiyun }
234*4882a593Smuzhiyun 
235*4882a593Smuzhiyun /*
236*4882a593Smuzhiyun  * ******************************************************************************
237*4882a593Smuzhiyun  * Manual port configuration agent routines
238*4882a593Smuzhiyun  * ****************************************************************************** */
239*4882a593Smuzhiyun 
240*4882a593Smuzhiyun /* verify all of the phys in the same port are using the same SAS address */
241*4882a593Smuzhiyun static enum sci_status
sci_mpc_agent_validate_phy_configuration(struct isci_host * ihost,struct sci_port_configuration_agent * port_agent)242*4882a593Smuzhiyun sci_mpc_agent_validate_phy_configuration(struct isci_host *ihost,
243*4882a593Smuzhiyun 					      struct sci_port_configuration_agent *port_agent)
244*4882a593Smuzhiyun {
245*4882a593Smuzhiyun 	u32 phy_mask;
246*4882a593Smuzhiyun 	u32 assigned_phy_mask;
247*4882a593Smuzhiyun 	struct sci_sas_address sas_address;
248*4882a593Smuzhiyun 	struct sci_sas_address phy_assigned_address;
249*4882a593Smuzhiyun 	u8 port_index;
250*4882a593Smuzhiyun 	u8 phy_index;
251*4882a593Smuzhiyun 
252*4882a593Smuzhiyun 	assigned_phy_mask = 0;
253*4882a593Smuzhiyun 	sas_address.high = 0;
254*4882a593Smuzhiyun 	sas_address.low = 0;
255*4882a593Smuzhiyun 
256*4882a593Smuzhiyun 	for (port_index = 0; port_index < SCI_MAX_PORTS; port_index++) {
257*4882a593Smuzhiyun 		phy_mask = ihost->oem_parameters.ports[port_index].phy_mask;
258*4882a593Smuzhiyun 
259*4882a593Smuzhiyun 		if (!phy_mask)
260*4882a593Smuzhiyun 			continue;
261*4882a593Smuzhiyun 		/*
262*4882a593Smuzhiyun 		 * Make sure that one or more of the phys were not already assinged to
263*4882a593Smuzhiyun 		 * a different port. */
264*4882a593Smuzhiyun 		if ((phy_mask & ~assigned_phy_mask) == 0) {
265*4882a593Smuzhiyun 			return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
266*4882a593Smuzhiyun 		}
267*4882a593Smuzhiyun 
268*4882a593Smuzhiyun 		/* Find the starting phy index for this round through the loop */
269*4882a593Smuzhiyun 		for (phy_index = 0; phy_index < SCI_MAX_PHYS; phy_index++) {
270*4882a593Smuzhiyun 			if ((phy_mask & (1 << phy_index)) == 0)
271*4882a593Smuzhiyun 				continue;
272*4882a593Smuzhiyun 			sci_phy_get_sas_address(&ihost->phys[phy_index],
273*4882a593Smuzhiyun 						     &sas_address);
274*4882a593Smuzhiyun 
275*4882a593Smuzhiyun 			/*
276*4882a593Smuzhiyun 			 * The phy_index can be used as the starting point for the
277*4882a593Smuzhiyun 			 * port range since the hardware starts all logical ports
278*4882a593Smuzhiyun 			 * the same as the PE index. */
279*4882a593Smuzhiyun 			port_agent->phy_valid_port_range[phy_index].min_index = port_index;
280*4882a593Smuzhiyun 			port_agent->phy_valid_port_range[phy_index].max_index = phy_index;
281*4882a593Smuzhiyun 
282*4882a593Smuzhiyun 			if (phy_index != port_index) {
283*4882a593Smuzhiyun 				return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
284*4882a593Smuzhiyun 			}
285*4882a593Smuzhiyun 
286*4882a593Smuzhiyun 			break;
287*4882a593Smuzhiyun 		}
288*4882a593Smuzhiyun 
289*4882a593Smuzhiyun 		/*
290*4882a593Smuzhiyun 		 * See how many additional phys are being added to this logical port.
291*4882a593Smuzhiyun 		 * Note: We have not moved the current phy_index so we will actually
292*4882a593Smuzhiyun 		 *       compare the startting phy with itself.
293*4882a593Smuzhiyun 		 *       This is expected and required to add the phy to the port. */
294*4882a593Smuzhiyun 		for (; phy_index < SCI_MAX_PHYS; phy_index++) {
295*4882a593Smuzhiyun 			if ((phy_mask & (1 << phy_index)) == 0)
296*4882a593Smuzhiyun 				continue;
297*4882a593Smuzhiyun 			sci_phy_get_sas_address(&ihost->phys[phy_index],
298*4882a593Smuzhiyun 						     &phy_assigned_address);
299*4882a593Smuzhiyun 
300*4882a593Smuzhiyun 			if (sci_sas_address_compare(sas_address, phy_assigned_address) != 0) {
301*4882a593Smuzhiyun 				/*
302*4882a593Smuzhiyun 				 * The phy mask specified that this phy is part of the same port
303*4882a593Smuzhiyun 				 * as the starting phy and it is not so fail this configuration */
304*4882a593Smuzhiyun 				return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
305*4882a593Smuzhiyun 			}
306*4882a593Smuzhiyun 
307*4882a593Smuzhiyun 			port_agent->phy_valid_port_range[phy_index].min_index = port_index;
308*4882a593Smuzhiyun 			port_agent->phy_valid_port_range[phy_index].max_index = phy_index;
309*4882a593Smuzhiyun 
310*4882a593Smuzhiyun 			sci_port_add_phy(&ihost->ports[port_index],
311*4882a593Smuzhiyun 					      &ihost->phys[phy_index]);
312*4882a593Smuzhiyun 
313*4882a593Smuzhiyun 			assigned_phy_mask |= (1 << phy_index);
314*4882a593Smuzhiyun 		}
315*4882a593Smuzhiyun 
316*4882a593Smuzhiyun 	}
317*4882a593Smuzhiyun 
318*4882a593Smuzhiyun 	return sci_port_configuration_agent_validate_ports(ihost, port_agent);
319*4882a593Smuzhiyun }
320*4882a593Smuzhiyun 
mpc_agent_timeout(struct timer_list * t)321*4882a593Smuzhiyun static void mpc_agent_timeout(struct timer_list *t)
322*4882a593Smuzhiyun {
323*4882a593Smuzhiyun 	u8 index;
324*4882a593Smuzhiyun 	struct sci_timer *tmr = from_timer(tmr, t, timer);
325*4882a593Smuzhiyun 	struct sci_port_configuration_agent *port_agent;
326*4882a593Smuzhiyun 	struct isci_host *ihost;
327*4882a593Smuzhiyun 	unsigned long flags;
328*4882a593Smuzhiyun 	u16 configure_phy_mask;
329*4882a593Smuzhiyun 
330*4882a593Smuzhiyun 	port_agent = container_of(tmr, typeof(*port_agent), timer);
331*4882a593Smuzhiyun 	ihost = container_of(port_agent, typeof(*ihost), port_agent);
332*4882a593Smuzhiyun 
333*4882a593Smuzhiyun 	spin_lock_irqsave(&ihost->scic_lock, flags);
334*4882a593Smuzhiyun 
335*4882a593Smuzhiyun 	if (tmr->cancel)
336*4882a593Smuzhiyun 		goto done;
337*4882a593Smuzhiyun 
338*4882a593Smuzhiyun 	port_agent->timer_pending = false;
339*4882a593Smuzhiyun 
340*4882a593Smuzhiyun 	/* Find the mask of phys that are reported read but as yet unconfigured into a port */
341*4882a593Smuzhiyun 	configure_phy_mask = ~port_agent->phy_configured_mask & port_agent->phy_ready_mask;
342*4882a593Smuzhiyun 
343*4882a593Smuzhiyun 	for (index = 0; index < SCI_MAX_PHYS; index++) {
344*4882a593Smuzhiyun 		struct isci_phy *iphy = &ihost->phys[index];
345*4882a593Smuzhiyun 
346*4882a593Smuzhiyun 		if (configure_phy_mask & (1 << index)) {
347*4882a593Smuzhiyun 			port_agent->link_up_handler(ihost, port_agent,
348*4882a593Smuzhiyun 						    phy_get_non_dummy_port(iphy),
349*4882a593Smuzhiyun 						    iphy);
350*4882a593Smuzhiyun 		}
351*4882a593Smuzhiyun 	}
352*4882a593Smuzhiyun 
353*4882a593Smuzhiyun done:
354*4882a593Smuzhiyun 	spin_unlock_irqrestore(&ihost->scic_lock, flags);
355*4882a593Smuzhiyun }
356*4882a593Smuzhiyun 
sci_mpc_agent_link_up(struct isci_host * ihost,struct sci_port_configuration_agent * port_agent,struct isci_port * iport,struct isci_phy * iphy)357*4882a593Smuzhiyun static void sci_mpc_agent_link_up(struct isci_host *ihost,
358*4882a593Smuzhiyun 				       struct sci_port_configuration_agent *port_agent,
359*4882a593Smuzhiyun 				       struct isci_port *iport,
360*4882a593Smuzhiyun 				       struct isci_phy *iphy)
361*4882a593Smuzhiyun {
362*4882a593Smuzhiyun 	/* If the port is NULL then the phy was not assigned to a port.
363*4882a593Smuzhiyun 	 * This is because the phy was not given the same SAS Address as
364*4882a593Smuzhiyun 	 * the other PHYs in the port.
365*4882a593Smuzhiyun 	 */
366*4882a593Smuzhiyun 	if (!iport)
367*4882a593Smuzhiyun 		return;
368*4882a593Smuzhiyun 
369*4882a593Smuzhiyun 	port_agent->phy_ready_mask |= (1 << iphy->phy_index);
370*4882a593Smuzhiyun 	sci_port_link_up(iport, iphy);
371*4882a593Smuzhiyun 	if ((iport->active_phy_mask & (1 << iphy->phy_index)))
372*4882a593Smuzhiyun 		port_agent->phy_configured_mask |= (1 << iphy->phy_index);
373*4882a593Smuzhiyun }
374*4882a593Smuzhiyun 
375*4882a593Smuzhiyun /**
376*4882a593Smuzhiyun  *
377*4882a593Smuzhiyun  * @controller: This is the controller object that receives the link down
378*4882a593Smuzhiyun  *    notification.
379*4882a593Smuzhiyun  * @port: This is the port object associated with the phy.  If the is no
380*4882a593Smuzhiyun  *    associated port this is an NULL.  The port is an invalid
381*4882a593Smuzhiyun  *    handle only if the phy was never port of this port.  This happens when
382*4882a593Smuzhiyun  *    the phy is not broadcasting the same SAS address as the other phys in the
383*4882a593Smuzhiyun  *    assigned port.
384*4882a593Smuzhiyun  * @phy: This is the phy object which has gone link down.
385*4882a593Smuzhiyun  *
386*4882a593Smuzhiyun  * This function handles the manual port configuration link down notifications.
387*4882a593Smuzhiyun  * Since all ports and phys are associated at initialization time we just turn
388*4882a593Smuzhiyun  * around and notifiy the port object of the link down event.  If this PHY is
389*4882a593Smuzhiyun  * not associated with a port there is no action taken. Is it possible to get a
390*4882a593Smuzhiyun  * link down notification from a phy that has no assocoated port?
391*4882a593Smuzhiyun  */
sci_mpc_agent_link_down(struct isci_host * ihost,struct sci_port_configuration_agent * port_agent,struct isci_port * iport,struct isci_phy * iphy)392*4882a593Smuzhiyun static void sci_mpc_agent_link_down(
393*4882a593Smuzhiyun 	struct isci_host *ihost,
394*4882a593Smuzhiyun 	struct sci_port_configuration_agent *port_agent,
395*4882a593Smuzhiyun 	struct isci_port *iport,
396*4882a593Smuzhiyun 	struct isci_phy *iphy)
397*4882a593Smuzhiyun {
398*4882a593Smuzhiyun 	if (iport != NULL) {
399*4882a593Smuzhiyun 		/*
400*4882a593Smuzhiyun 		 * If we can form a new port from the remainder of the phys
401*4882a593Smuzhiyun 		 * then we want to start the timer to allow the SCI User to
402*4882a593Smuzhiyun 		 * cleanup old devices and rediscover the port before
403*4882a593Smuzhiyun 		 * rebuilding the port with the phys that remain in the ready
404*4882a593Smuzhiyun 		 * state.
405*4882a593Smuzhiyun 		 */
406*4882a593Smuzhiyun 		port_agent->phy_ready_mask &= ~(1 << iphy->phy_index);
407*4882a593Smuzhiyun 		port_agent->phy_configured_mask &= ~(1 << iphy->phy_index);
408*4882a593Smuzhiyun 
409*4882a593Smuzhiyun 		/*
410*4882a593Smuzhiyun 		 * Check to see if there are more phys waiting to be
411*4882a593Smuzhiyun 		 * configured into a port. If there are allow the SCI User
412*4882a593Smuzhiyun 		 * to tear down this port, if necessary, and then reconstruct
413*4882a593Smuzhiyun 		 * the port after the timeout.
414*4882a593Smuzhiyun 		 */
415*4882a593Smuzhiyun 		if ((port_agent->phy_configured_mask == 0x0000) &&
416*4882a593Smuzhiyun 		    (port_agent->phy_ready_mask != 0x0000) &&
417*4882a593Smuzhiyun 		    !port_agent->timer_pending) {
418*4882a593Smuzhiyun 			port_agent->timer_pending = true;
419*4882a593Smuzhiyun 
420*4882a593Smuzhiyun 			sci_mod_timer(&port_agent->timer,
421*4882a593Smuzhiyun 				      SCIC_SDS_MPC_RECONFIGURATION_TIMEOUT);
422*4882a593Smuzhiyun 		}
423*4882a593Smuzhiyun 
424*4882a593Smuzhiyun 		sci_port_link_down(iport, iphy);
425*4882a593Smuzhiyun 	}
426*4882a593Smuzhiyun }
427*4882a593Smuzhiyun 
428*4882a593Smuzhiyun /* verify phys are assigned a valid SAS address for automatic port
429*4882a593Smuzhiyun  * configuration mode.
430*4882a593Smuzhiyun  */
431*4882a593Smuzhiyun static enum sci_status
sci_apc_agent_validate_phy_configuration(struct isci_host * ihost,struct sci_port_configuration_agent * port_agent)432*4882a593Smuzhiyun sci_apc_agent_validate_phy_configuration(struct isci_host *ihost,
433*4882a593Smuzhiyun 					      struct sci_port_configuration_agent *port_agent)
434*4882a593Smuzhiyun {
435*4882a593Smuzhiyun 	u8 phy_index;
436*4882a593Smuzhiyun 	u8 port_index;
437*4882a593Smuzhiyun 	struct sci_sas_address sas_address;
438*4882a593Smuzhiyun 	struct sci_sas_address phy_assigned_address;
439*4882a593Smuzhiyun 
440*4882a593Smuzhiyun 	phy_index = 0;
441*4882a593Smuzhiyun 
442*4882a593Smuzhiyun 	while (phy_index < SCI_MAX_PHYS) {
443*4882a593Smuzhiyun 		port_index = phy_index;
444*4882a593Smuzhiyun 
445*4882a593Smuzhiyun 		/* Get the assigned SAS Address for the first PHY on the controller. */
446*4882a593Smuzhiyun 		sci_phy_get_sas_address(&ihost->phys[phy_index],
447*4882a593Smuzhiyun 					    &sas_address);
448*4882a593Smuzhiyun 
449*4882a593Smuzhiyun 		while (++phy_index < SCI_MAX_PHYS) {
450*4882a593Smuzhiyun 			sci_phy_get_sas_address(&ihost->phys[phy_index],
451*4882a593Smuzhiyun 						     &phy_assigned_address);
452*4882a593Smuzhiyun 
453*4882a593Smuzhiyun 			/* Verify each of the SAS address are all the same for every PHY */
454*4882a593Smuzhiyun 			if (sci_sas_address_compare(sas_address, phy_assigned_address) == 0) {
455*4882a593Smuzhiyun 				port_agent->phy_valid_port_range[phy_index].min_index = port_index;
456*4882a593Smuzhiyun 				port_agent->phy_valid_port_range[phy_index].max_index = phy_index;
457*4882a593Smuzhiyun 			} else {
458*4882a593Smuzhiyun 				port_agent->phy_valid_port_range[phy_index].min_index = phy_index;
459*4882a593Smuzhiyun 				port_agent->phy_valid_port_range[phy_index].max_index = phy_index;
460*4882a593Smuzhiyun 				break;
461*4882a593Smuzhiyun 			}
462*4882a593Smuzhiyun 		}
463*4882a593Smuzhiyun 	}
464*4882a593Smuzhiyun 
465*4882a593Smuzhiyun 	return sci_port_configuration_agent_validate_ports(ihost, port_agent);
466*4882a593Smuzhiyun }
467*4882a593Smuzhiyun 
468*4882a593Smuzhiyun /*
469*4882a593Smuzhiyun  * This routine will restart the automatic port configuration timeout
470*4882a593Smuzhiyun  * timer for the next time period. This could be caused by either a link
471*4882a593Smuzhiyun  * down event or a link up event where we can not yet tell to which a phy
472*4882a593Smuzhiyun  * belongs.
473*4882a593Smuzhiyun  */
sci_apc_agent_start_timer(struct sci_port_configuration_agent * port_agent,u32 timeout)474*4882a593Smuzhiyun static void sci_apc_agent_start_timer(struct sci_port_configuration_agent *port_agent,
475*4882a593Smuzhiyun 				      u32 timeout)
476*4882a593Smuzhiyun {
477*4882a593Smuzhiyun 	port_agent->timer_pending = true;
478*4882a593Smuzhiyun 	sci_mod_timer(&port_agent->timer, timeout);
479*4882a593Smuzhiyun }
480*4882a593Smuzhiyun 
sci_apc_agent_configure_ports(struct isci_host * ihost,struct sci_port_configuration_agent * port_agent,struct isci_phy * iphy,bool start_timer)481*4882a593Smuzhiyun static void sci_apc_agent_configure_ports(struct isci_host *ihost,
482*4882a593Smuzhiyun 					       struct sci_port_configuration_agent *port_agent,
483*4882a593Smuzhiyun 					       struct isci_phy *iphy,
484*4882a593Smuzhiyun 					       bool start_timer)
485*4882a593Smuzhiyun {
486*4882a593Smuzhiyun 	u8 port_index;
487*4882a593Smuzhiyun 	enum sci_status status;
488*4882a593Smuzhiyun 	struct isci_port *iport;
489*4882a593Smuzhiyun 	enum SCIC_SDS_APC_ACTIVITY apc_activity = SCIC_SDS_APC_SKIP_PHY;
490*4882a593Smuzhiyun 
491*4882a593Smuzhiyun 	iport = sci_port_configuration_agent_find_port(ihost, iphy);
492*4882a593Smuzhiyun 
493*4882a593Smuzhiyun 	if (iport) {
494*4882a593Smuzhiyun 		if (sci_port_is_valid_phy_assignment(iport, iphy->phy_index))
495*4882a593Smuzhiyun 			apc_activity = SCIC_SDS_APC_ADD_PHY;
496*4882a593Smuzhiyun 		else
497*4882a593Smuzhiyun 			apc_activity = SCIC_SDS_APC_SKIP_PHY;
498*4882a593Smuzhiyun 	} else {
499*4882a593Smuzhiyun 		/*
500*4882a593Smuzhiyun 		 * There is no matching Port for this PHY so lets search through the
501*4882a593Smuzhiyun 		 * Ports and see if we can add the PHY to its own port or maybe start
502*4882a593Smuzhiyun 		 * the timer and wait to see if a wider port can be made.
503*4882a593Smuzhiyun 		 *
504*4882a593Smuzhiyun 		 * Note the break when we reach the condition of the port id == phy id */
505*4882a593Smuzhiyun 		for (port_index = port_agent->phy_valid_port_range[iphy->phy_index].min_index;
506*4882a593Smuzhiyun 		     port_index <= port_agent->phy_valid_port_range[iphy->phy_index].max_index;
507*4882a593Smuzhiyun 		     port_index++) {
508*4882a593Smuzhiyun 
509*4882a593Smuzhiyun 			iport = &ihost->ports[port_index];
510*4882a593Smuzhiyun 
511*4882a593Smuzhiyun 			/* First we must make sure that this PHY can be added to this Port. */
512*4882a593Smuzhiyun 			if (sci_port_is_valid_phy_assignment(iport, iphy->phy_index)) {
513*4882a593Smuzhiyun 				/*
514*4882a593Smuzhiyun 				 * Port contains a PHY with a greater PHY ID than the current
515*4882a593Smuzhiyun 				 * PHY that has gone link up.  This phy can not be part of any
516*4882a593Smuzhiyun 				 * port so skip it and move on. */
517*4882a593Smuzhiyun 				if (iport->active_phy_mask > (1 << iphy->phy_index)) {
518*4882a593Smuzhiyun 					apc_activity = SCIC_SDS_APC_SKIP_PHY;
519*4882a593Smuzhiyun 					break;
520*4882a593Smuzhiyun 				}
521*4882a593Smuzhiyun 
522*4882a593Smuzhiyun 				/*
523*4882a593Smuzhiyun 				 * We have reached the end of our Port list and have not found
524*4882a593Smuzhiyun 				 * any reason why we should not either add the PHY to the port
525*4882a593Smuzhiyun 				 * or wait for more phys to become active. */
526*4882a593Smuzhiyun 				if (iport->physical_port_index == iphy->phy_index) {
527*4882a593Smuzhiyun 					/*
528*4882a593Smuzhiyun 					 * The Port either has no active PHYs.
529*4882a593Smuzhiyun 					 * Consider that if the port had any active PHYs we would have
530*4882a593Smuzhiyun 					 * or active PHYs with
531*4882a593Smuzhiyun 					 * a lower PHY Id than this PHY. */
532*4882a593Smuzhiyun 					if (apc_activity != SCIC_SDS_APC_START_TIMER) {
533*4882a593Smuzhiyun 						apc_activity = SCIC_SDS_APC_ADD_PHY;
534*4882a593Smuzhiyun 					}
535*4882a593Smuzhiyun 
536*4882a593Smuzhiyun 					break;
537*4882a593Smuzhiyun 				}
538*4882a593Smuzhiyun 
539*4882a593Smuzhiyun 				/*
540*4882a593Smuzhiyun 				 * The current Port has no active PHYs and this PHY could be part
541*4882a593Smuzhiyun 				 * of this Port.  Since we dont know as yet setup to start the
542*4882a593Smuzhiyun 				 * timer and see if there is a better configuration. */
543*4882a593Smuzhiyun 				if (iport->active_phy_mask == 0) {
544*4882a593Smuzhiyun 					apc_activity = SCIC_SDS_APC_START_TIMER;
545*4882a593Smuzhiyun 				}
546*4882a593Smuzhiyun 			} else if (iport->active_phy_mask != 0) {
547*4882a593Smuzhiyun 				/*
548*4882a593Smuzhiyun 				 * The Port has an active phy and the current Phy can not
549*4882a593Smuzhiyun 				 * participate in this port so skip the PHY and see if
550*4882a593Smuzhiyun 				 * there is a better configuration. */
551*4882a593Smuzhiyun 				apc_activity = SCIC_SDS_APC_SKIP_PHY;
552*4882a593Smuzhiyun 			}
553*4882a593Smuzhiyun 		}
554*4882a593Smuzhiyun 	}
555*4882a593Smuzhiyun 
556*4882a593Smuzhiyun 	/*
557*4882a593Smuzhiyun 	 * Check to see if the start timer operations should instead map to an
558*4882a593Smuzhiyun 	 * add phy operation.  This is caused because we have been waiting to
559*4882a593Smuzhiyun 	 * add a phy to a port but could not becuase the automatic port
560*4882a593Smuzhiyun 	 * configuration engine had a choice of possible ports for the phy.
561*4882a593Smuzhiyun 	 * Since we have gone through a timeout we are going to restrict the
562*4882a593Smuzhiyun 	 * choice to the smallest possible port. */
563*4882a593Smuzhiyun 	if (
564*4882a593Smuzhiyun 		(start_timer == false)
565*4882a593Smuzhiyun 		&& (apc_activity == SCIC_SDS_APC_START_TIMER)
566*4882a593Smuzhiyun 		) {
567*4882a593Smuzhiyun 		apc_activity = SCIC_SDS_APC_ADD_PHY;
568*4882a593Smuzhiyun 	}
569*4882a593Smuzhiyun 
570*4882a593Smuzhiyun 	switch (apc_activity) {
571*4882a593Smuzhiyun 	case SCIC_SDS_APC_ADD_PHY:
572*4882a593Smuzhiyun 		status = sci_port_add_phy(iport, iphy);
573*4882a593Smuzhiyun 
574*4882a593Smuzhiyun 		if (status == SCI_SUCCESS) {
575*4882a593Smuzhiyun 			port_agent->phy_configured_mask |= (1 << iphy->phy_index);
576*4882a593Smuzhiyun 		}
577*4882a593Smuzhiyun 		break;
578*4882a593Smuzhiyun 
579*4882a593Smuzhiyun 	case SCIC_SDS_APC_START_TIMER:
580*4882a593Smuzhiyun 		sci_apc_agent_start_timer(port_agent,
581*4882a593Smuzhiyun 					  SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION);
582*4882a593Smuzhiyun 		break;
583*4882a593Smuzhiyun 
584*4882a593Smuzhiyun 	case SCIC_SDS_APC_SKIP_PHY:
585*4882a593Smuzhiyun 	default:
586*4882a593Smuzhiyun 		/* do nothing the PHY can not be made part of a port at this time. */
587*4882a593Smuzhiyun 		break;
588*4882a593Smuzhiyun 	}
589*4882a593Smuzhiyun }
590*4882a593Smuzhiyun 
591*4882a593Smuzhiyun /**
592*4882a593Smuzhiyun  * sci_apc_agent_link_up - handle apc link up events
593*4882a593Smuzhiyun  * @scic: This is the controller object that receives the link up
594*4882a593Smuzhiyun  *    notification.
595*4882a593Smuzhiyun  * @sci_port: This is the port object associated with the phy.  If the is no
596*4882a593Smuzhiyun  *    associated port this is an NULL.
597*4882a593Smuzhiyun  * @sci_phy: This is the phy object which has gone link up.
598*4882a593Smuzhiyun  *
599*4882a593Smuzhiyun  * This method handles the automatic port configuration for link up
600*4882a593Smuzhiyun  * notifications. Is it possible to get a link down notification from a phy
601*4882a593Smuzhiyun  * that has no assocoated port?
602*4882a593Smuzhiyun  */
sci_apc_agent_link_up(struct isci_host * ihost,struct sci_port_configuration_agent * port_agent,struct isci_port * iport,struct isci_phy * iphy)603*4882a593Smuzhiyun static void sci_apc_agent_link_up(struct isci_host *ihost,
604*4882a593Smuzhiyun 				       struct sci_port_configuration_agent *port_agent,
605*4882a593Smuzhiyun 				       struct isci_port *iport,
606*4882a593Smuzhiyun 				       struct isci_phy *iphy)
607*4882a593Smuzhiyun {
608*4882a593Smuzhiyun 	u8 phy_index  = iphy->phy_index;
609*4882a593Smuzhiyun 
610*4882a593Smuzhiyun 	if (!iport) {
611*4882a593Smuzhiyun 		/* the phy is not the part of this port */
612*4882a593Smuzhiyun 		port_agent->phy_ready_mask |= 1 << phy_index;
613*4882a593Smuzhiyun 		sci_apc_agent_start_timer(port_agent,
614*4882a593Smuzhiyun 					  SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION);
615*4882a593Smuzhiyun 	} else {
616*4882a593Smuzhiyun 		/* the phy is already the part of the port */
617*4882a593Smuzhiyun 		port_agent->phy_ready_mask |= 1 << phy_index;
618*4882a593Smuzhiyun 		sci_port_link_up(iport, iphy);
619*4882a593Smuzhiyun 	}
620*4882a593Smuzhiyun }
621*4882a593Smuzhiyun 
622*4882a593Smuzhiyun /**
623*4882a593Smuzhiyun  *
624*4882a593Smuzhiyun  * @controller: This is the controller object that receives the link down
625*4882a593Smuzhiyun  *    notification.
626*4882a593Smuzhiyun  * @iport: This is the port object associated with the phy.  If the is no
627*4882a593Smuzhiyun  *    associated port this is an NULL.
628*4882a593Smuzhiyun  * @iphy: This is the phy object which has gone link down.
629*4882a593Smuzhiyun  *
630*4882a593Smuzhiyun  * This method handles the automatic port configuration link down
631*4882a593Smuzhiyun  * notifications. not associated with a port there is no action taken. Is it
632*4882a593Smuzhiyun  * possible to get a link down notification from a phy that has no assocoated
633*4882a593Smuzhiyun  * port?
634*4882a593Smuzhiyun  */
sci_apc_agent_link_down(struct isci_host * ihost,struct sci_port_configuration_agent * port_agent,struct isci_port * iport,struct isci_phy * iphy)635*4882a593Smuzhiyun static void sci_apc_agent_link_down(
636*4882a593Smuzhiyun 	struct isci_host *ihost,
637*4882a593Smuzhiyun 	struct sci_port_configuration_agent *port_agent,
638*4882a593Smuzhiyun 	struct isci_port *iport,
639*4882a593Smuzhiyun 	struct isci_phy *iphy)
640*4882a593Smuzhiyun {
641*4882a593Smuzhiyun 	port_agent->phy_ready_mask &= ~(1 << iphy->phy_index);
642*4882a593Smuzhiyun 
643*4882a593Smuzhiyun 	if (!iport)
644*4882a593Smuzhiyun 		return;
645*4882a593Smuzhiyun 	if (port_agent->phy_configured_mask & (1 << iphy->phy_index)) {
646*4882a593Smuzhiyun 		enum sci_status status;
647*4882a593Smuzhiyun 
648*4882a593Smuzhiyun 		status = sci_port_remove_phy(iport, iphy);
649*4882a593Smuzhiyun 
650*4882a593Smuzhiyun 		if (status == SCI_SUCCESS)
651*4882a593Smuzhiyun 			port_agent->phy_configured_mask &= ~(1 << iphy->phy_index);
652*4882a593Smuzhiyun 	}
653*4882a593Smuzhiyun }
654*4882a593Smuzhiyun 
655*4882a593Smuzhiyun /* configure the phys into ports when the timer fires */
apc_agent_timeout(struct timer_list * t)656*4882a593Smuzhiyun static void apc_agent_timeout(struct timer_list *t)
657*4882a593Smuzhiyun {
658*4882a593Smuzhiyun 	u32 index;
659*4882a593Smuzhiyun 	struct sci_timer *tmr = from_timer(tmr, t, timer);
660*4882a593Smuzhiyun 	struct sci_port_configuration_agent *port_agent;
661*4882a593Smuzhiyun 	struct isci_host *ihost;
662*4882a593Smuzhiyun 	unsigned long flags;
663*4882a593Smuzhiyun 	u16 configure_phy_mask;
664*4882a593Smuzhiyun 
665*4882a593Smuzhiyun 	port_agent = container_of(tmr, typeof(*port_agent), timer);
666*4882a593Smuzhiyun 	ihost = container_of(port_agent, typeof(*ihost), port_agent);
667*4882a593Smuzhiyun 
668*4882a593Smuzhiyun 	spin_lock_irqsave(&ihost->scic_lock, flags);
669*4882a593Smuzhiyun 
670*4882a593Smuzhiyun 	if (tmr->cancel)
671*4882a593Smuzhiyun 		goto done;
672*4882a593Smuzhiyun 
673*4882a593Smuzhiyun 	port_agent->timer_pending = false;
674*4882a593Smuzhiyun 
675*4882a593Smuzhiyun 	configure_phy_mask = ~port_agent->phy_configured_mask & port_agent->phy_ready_mask;
676*4882a593Smuzhiyun 
677*4882a593Smuzhiyun 	if (!configure_phy_mask)
678*4882a593Smuzhiyun 		goto done;
679*4882a593Smuzhiyun 
680*4882a593Smuzhiyun 	for (index = 0; index < SCI_MAX_PHYS; index++) {
681*4882a593Smuzhiyun 		if ((configure_phy_mask & (1 << index)) == 0)
682*4882a593Smuzhiyun 			continue;
683*4882a593Smuzhiyun 
684*4882a593Smuzhiyun 		sci_apc_agent_configure_ports(ihost, port_agent,
685*4882a593Smuzhiyun 						   &ihost->phys[index], false);
686*4882a593Smuzhiyun 	}
687*4882a593Smuzhiyun 
688*4882a593Smuzhiyun 	if (is_controller_start_complete(ihost))
689*4882a593Smuzhiyun 		sci_controller_transition_to_ready(ihost, SCI_SUCCESS);
690*4882a593Smuzhiyun 
691*4882a593Smuzhiyun done:
692*4882a593Smuzhiyun 	spin_unlock_irqrestore(&ihost->scic_lock, flags);
693*4882a593Smuzhiyun }
694*4882a593Smuzhiyun 
695*4882a593Smuzhiyun /*
696*4882a593Smuzhiyun  * ******************************************************************************
697*4882a593Smuzhiyun  * Public port configuration agent routines
698*4882a593Smuzhiyun  * ****************************************************************************** */
699*4882a593Smuzhiyun 
700*4882a593Smuzhiyun /**
701*4882a593Smuzhiyun  *
702*4882a593Smuzhiyun  *
703*4882a593Smuzhiyun  * This method will construct the port configuration agent for operation. This
704*4882a593Smuzhiyun  * call is universal for both manual port configuration and automatic port
705*4882a593Smuzhiyun  * configuration modes.
706*4882a593Smuzhiyun  */
sci_port_configuration_agent_construct(struct sci_port_configuration_agent * port_agent)707*4882a593Smuzhiyun void sci_port_configuration_agent_construct(
708*4882a593Smuzhiyun 	struct sci_port_configuration_agent *port_agent)
709*4882a593Smuzhiyun {
710*4882a593Smuzhiyun 	u32 index;
711*4882a593Smuzhiyun 
712*4882a593Smuzhiyun 	port_agent->phy_configured_mask = 0x00;
713*4882a593Smuzhiyun 	port_agent->phy_ready_mask = 0x00;
714*4882a593Smuzhiyun 
715*4882a593Smuzhiyun 	port_agent->link_up_handler = NULL;
716*4882a593Smuzhiyun 	port_agent->link_down_handler = NULL;
717*4882a593Smuzhiyun 
718*4882a593Smuzhiyun 	port_agent->timer_pending = false;
719*4882a593Smuzhiyun 
720*4882a593Smuzhiyun 	for (index = 0; index < SCI_MAX_PORTS; index++) {
721*4882a593Smuzhiyun 		port_agent->phy_valid_port_range[index].min_index = 0;
722*4882a593Smuzhiyun 		port_agent->phy_valid_port_range[index].max_index = 0;
723*4882a593Smuzhiyun 	}
724*4882a593Smuzhiyun }
725*4882a593Smuzhiyun 
is_port_config_apc(struct isci_host * ihost)726*4882a593Smuzhiyun bool is_port_config_apc(struct isci_host *ihost)
727*4882a593Smuzhiyun {
728*4882a593Smuzhiyun 	return ihost->port_agent.link_up_handler == sci_apc_agent_link_up;
729*4882a593Smuzhiyun }
730*4882a593Smuzhiyun 
sci_port_configuration_agent_initialize(struct isci_host * ihost,struct sci_port_configuration_agent * port_agent)731*4882a593Smuzhiyun enum sci_status sci_port_configuration_agent_initialize(
732*4882a593Smuzhiyun 	struct isci_host *ihost,
733*4882a593Smuzhiyun 	struct sci_port_configuration_agent *port_agent)
734*4882a593Smuzhiyun {
735*4882a593Smuzhiyun 	enum sci_status status;
736*4882a593Smuzhiyun 	enum sci_port_configuration_mode mode;
737*4882a593Smuzhiyun 
738*4882a593Smuzhiyun 	mode = ihost->oem_parameters.controller.mode_type;
739*4882a593Smuzhiyun 
740*4882a593Smuzhiyun 	if (mode == SCIC_PORT_MANUAL_CONFIGURATION_MODE) {
741*4882a593Smuzhiyun 		status = sci_mpc_agent_validate_phy_configuration(
742*4882a593Smuzhiyun 				ihost, port_agent);
743*4882a593Smuzhiyun 
744*4882a593Smuzhiyun 		port_agent->link_up_handler = sci_mpc_agent_link_up;
745*4882a593Smuzhiyun 		port_agent->link_down_handler = sci_mpc_agent_link_down;
746*4882a593Smuzhiyun 
747*4882a593Smuzhiyun 		sci_init_timer(&port_agent->timer, mpc_agent_timeout);
748*4882a593Smuzhiyun 	} else {
749*4882a593Smuzhiyun 		status = sci_apc_agent_validate_phy_configuration(
750*4882a593Smuzhiyun 				ihost, port_agent);
751*4882a593Smuzhiyun 
752*4882a593Smuzhiyun 		port_agent->link_up_handler = sci_apc_agent_link_up;
753*4882a593Smuzhiyun 		port_agent->link_down_handler = sci_apc_agent_link_down;
754*4882a593Smuzhiyun 
755*4882a593Smuzhiyun 		sci_init_timer(&port_agent->timer, apc_agent_timeout);
756*4882a593Smuzhiyun 	}
757*4882a593Smuzhiyun 
758*4882a593Smuzhiyun 	return status;
759*4882a593Smuzhiyun }
760