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