2 * SPDX-License-Identifier: BSD-2-Clause OR GPL-2.0
4 * This file is provided under a dual BSD/GPLv2 license. When using or
5 * redistributing this file, you may do so under either license.
9 * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of version 2 of the GNU General Public License as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
23 * The full GNU General Public License is included in this distribution
24 * in the file called LICENSE.GPL.
28 * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
29 * All rights reserved.
31 * Redistribution and use in source and binary forms, with or without
32 * modification, are permitted provided that the following conditions
35 * * Redistributions of source code must retain the above copyright
36 * notice, this list of conditions and the following disclaimer.
37 * * Redistributions in binary form must reproduce the above copyright
38 * notice, this list of conditions and the following disclaimer in
39 * the documentation and/or other materials provided with the
42 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
43 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
44 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
45 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
46 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
47 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
48 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
49 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
50 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
51 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
52 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
55 #include <sys/cdefs.h>
56 __FBSDID("$FreeBSD$");
61 * @brief This file contains all of the entrance and exit methods for each
62 * of the domain states defined by the SCI_BASE_DOMAIN state
66 #include <dev/isci/scil/intel_sas.h>
67 #include <dev/isci/scil/scic_port.h>
69 #include <dev/isci/scil/scif_sas_logger.h>
70 #include <dev/isci/scil/scif_sas_domain.h>
71 #include <dev/isci/scil/scif_sas_controller.h>
72 #include <dev/isci/scil/scic_controller.h>
74 //******************************************************************************
75 //* P R O T E C T E D M E T H O D S
76 //******************************************************************************
79 * @brief This method will attempt to transition to the stopped state.
80 * The transition will only occur if the criteria for transition is
81 * met (i.e. all IOs are complete and all devices are stopped).
83 * @param[in] fw_domain This parameter specifies the domain in which to
84 * to attempt to perform the transition.
88 void scif_sas_domain_transition_to_stopped_state(
89 SCIF_SAS_DOMAIN_T * fw_domain
93 sci_base_object_get_logger(fw_domain),
94 SCIF_LOG_OBJECT_DOMAIN | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
95 "scif_sas_domain_transition_to_stopped_state(0x%x) enter\n",
99 // If IOs are quiesced, and all remote devices are stopped,
100 // then transition directly to the STOPPED state.
101 if ( (fw_domain->request_list.element_count == 0)
102 && (fw_domain->device_start_count == 0) )
105 sci_base_object_get_logger(fw_domain),
106 SCIF_LOG_OBJECT_DOMAIN | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
107 "Domain:0x%x immediate transition to STOPPED\n",
111 sci_base_state_machine_change_state(
112 &fw_domain->parent.state_machine, SCI_BASE_DOMAIN_STATE_STOPPED
119 * @brief This method is called upon entrance to all states where the
120 * previous state may have been the DISCOVERING state.
121 * We issue the scif_cb_domain_discovery_complete() notification
122 * from this method, assuming pre-requisites are met, as opposed
123 * to in the exit handler of the DISCOVERING state, so that the
124 * appropriate state handlers are in place should the user decide
125 * to call scif_domain_discover() again.
127 * @param[in] fw_domain This parameter specifies the domain for which
128 * the state transition has occurred.
133 void scif_sas_domain_transition_from_discovering_state(
134 SCIF_SAS_DOMAIN_T * fw_domain
138 sci_base_object_get_logger(fw_domain),
139 SCIF_LOG_OBJECT_DOMAIN | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
140 "scif_sas_domain_transition_from_discovering_state(0x%x) enter\n",
144 if (fw_domain->parent.state_machine.previous_state_id
145 == SCI_BASE_DOMAIN_STATE_DISCOVERING)
147 scif_sas_controller_restore_interrupt_coalescence(fw_domain->controller);
149 scif_cb_timer_stop(fw_domain->controller, fw_domain->operation.timer);
151 scif_cb_domain_discovery_complete(
152 fw_domain->controller, fw_domain, fw_domain->operation.status
159 * @brief This method is called upon entrance to DISCOVERING state. Right before
160 * transitioning to DISCOVERING state, we temporarily change interrupt
161 * coalescence scheme.
163 * @param[in] fw_domain This parameter specifies the domain for which
164 * the state transition has occurred.
168 void scif_sas_domain_transition_to_discovering_state(
169 SCIF_SAS_DOMAIN_T * fw_domain
172 scif_sas_controller_save_interrupt_coalescence(fw_domain->controller);
174 sci_base_state_machine_change_state(
175 &fw_domain->parent.state_machine, SCI_BASE_DOMAIN_STATE_DISCOVERING
181 * @brief This method implements the actions taken when entering the
184 * @param[in] object This parameter specifies the base object for which
185 * the state transition is occurring. This is cast into a
186 * SCIF_SAS_DOMAIN object in the method implementation.
191 void scif_sas_domain_initial_state_enter(
192 SCI_BASE_OBJECT_T * object
195 SCIF_SAS_DOMAIN_T * fw_domain = (SCIF_SAS_DOMAIN_T *)object;
199 scif_sas_domain_state_handler_table,
200 SCI_BASE_DOMAIN_STATE_INITIAL
204 sci_base_object_get_logger(fw_domain),
205 SCIF_LOG_OBJECT_DOMAIN,
206 "scif_sas_domain_initial_state_enter(0x%x) enter\n",
212 * @brief This method implements the actions taken when entering the
213 * STARTING state. This includes setting the state handlers and
214 * checking to see if the core port has already become READY.
216 * @param[in] object This parameter specifies the base object for which
217 * the state transition is occurring. This is cast into a
218 * SCIF_SAS_DOMAIN object in the method implementation.
223 void scif_sas_domain_starting_state_enter(
224 SCI_BASE_OBJECT_T * object
227 SCIF_SAS_DOMAIN_T * fw_domain = (SCIF_SAS_DOMAIN_T *)object;
231 scif_sas_domain_state_handler_table,
232 SCI_BASE_DOMAIN_STATE_STARTING
236 sci_base_object_get_logger(fw_domain),
237 SCIF_LOG_OBJECT_DOMAIN | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
238 "scif_sas_domain_starting_state_enter(0x%x) enter\n",
242 scif_sas_domain_transition_from_discovering_state(fw_domain);
244 // If we entered the STARTING state and the core port is actually ready,
245 // then directly transition into the READY state. This can occur
246 // if we were in the middle of discovery when the port failed
247 // (causing a transition to STOPPING), then before reaching STOPPED
248 // the port becomes ready again.
249 if (fw_domain->is_port_ready == TRUE)
251 sci_base_state_machine_change_state(
252 &fw_domain->parent.state_machine, SCI_BASE_DOMAIN_STATE_READY
258 * @brief This method implements the actions taken when entering the
259 * READY state. If the transition into this state came from:
260 * - the STARTING state, then alert the user via a
261 * scif_cb_domain_change_notification() that the domain
262 * has at least 1 device ready for discovery.
263 * - the DISCOVERING state, then alert the user that
264 * discovery is complete via the
265 * scif_cb_domain_discovery_complete() notification that
266 * discovery is finished.
268 * @param[in] object This parameter specifies the base object for which
269 * the state transition is occurring. This is cast into a
270 * SCIF_SAS_DOMAIN object in the method implementation.
275 void scif_sas_domain_ready_state_enter(
276 SCI_BASE_OBJECT_T * object
279 SCIF_SAS_DOMAIN_T * fw_domain = (SCIF_SAS_DOMAIN_T *)object;
283 scif_sas_domain_state_handler_table,
284 SCI_BASE_DOMAIN_STATE_READY
288 sci_base_object_get_logger(fw_domain),
289 SCIF_LOG_OBJECT_DOMAIN | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
290 "scif_sas_domain_ready_state_enter(0x%x) enter\n",
294 if (fw_domain->parent.state_machine.previous_state_id
295 == SCI_BASE_DOMAIN_STATE_STARTING)
297 scif_cb_domain_ready(fw_domain->controller, fw_domain);
299 // Only indicate the domain change notification if the previous
300 // state was the STARTING state. We issue the notification here
301 // as opposed to exit of the STARTING state so that the appropriate
302 // state handlers are in place should the user call
303 // scif_domain_discover() from scif_cb_domain_change_notification()
304 scif_cb_domain_change_notification(fw_domain->controller, fw_domain);
306 else if (fw_domain->parent.state_machine.previous_state_id
307 == SCI_BASE_DOMAIN_STATE_DISCOVERING)
309 //if domain discovery timed out, we will NOT go back to discover even
310 //the broadcast change count is not zero. Instead we finish the discovery
311 //back to user. User can check the operation status and decide to
312 //retry discover all over again.
313 if (fw_domain->operation.status == SCI_FAILURE_TIMEOUT)
314 fw_domain->broadcast_change_count = 0;
316 // Check the broadcast change count to determine if discovery
317 // is indeed complete.
318 if (fw_domain->broadcast_change_count == 0)
320 scif_sas_domain_transition_from_discovering_state(fw_domain);
321 scif_cb_domain_ready(fw_domain->controller, fw_domain);
325 // The broadcast change count indicates something my have
326 // changed in the domain, while a discovery was ongoing.
327 // Thus, we should start discovery over again.
328 sci_base_state_machine_change_state(
329 &fw_domain->parent.state_machine, SCI_BASE_DOMAIN_STATE_DISCOVERING
333 // Enable the BCN because underneath hardware may disabled any further
335 scic_port_enable_broadcast_change_notification(fw_domain->core_object);
340 * @brief This method implements the actions taken when exiting the
343 * @param[in] object This parameter specifies the base object for which
344 * the state transition is occurring. This is cast into a
345 * SCIF_SAS_DOMAIN object in the method implementation.
350 void scif_sas_domain_ready_state_exit(
351 SCI_BASE_OBJECT_T * object
354 SCIF_SAS_DOMAIN_T * fw_domain = (SCIF_SAS_DOMAIN_T *)object;
357 sci_base_object_get_logger(fw_domain),
358 SCIF_LOG_OBJECT_DOMAIN | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
359 "scif_sas_domain_ready_state_exit(0x%x) enter\n",
363 scif_cb_domain_not_ready(fw_domain->controller, fw_domain);
367 * @brief This method implements the actions taken when entering the
370 * @param[in] object This parameter specifies the base object for which
371 * the state transition is occurring. This is cast into a
372 * SCIF_SAS_DOMAIN object in the method implementation.
377 void scif_sas_domain_stopping_state_enter(
378 SCI_BASE_OBJECT_T * object
381 SCIF_SAS_REMOTE_DEVICE_T * fw_device;
382 SCIF_SAS_DOMAIN_T * fw_domain = (SCIF_SAS_DOMAIN_T *)object;
383 SCI_ABSTRACT_ELEMENT_T * element = sci_abstract_list_get_front(
384 &fw_domain->remote_device_list
389 scif_sas_domain_state_handler_table,
390 SCI_BASE_DOMAIN_STATE_STOPPING
393 // This must be invoked after the state handlers are set to ensure
394 // appropriate processing will occur if the user attempts to perform
395 // additional actions.
396 scif_sas_domain_transition_from_discovering_state(fw_domain);
399 sci_base_object_get_logger(fw_domain),
400 SCIF_LOG_OBJECT_DOMAIN | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
401 "scif_sas_domain_stopping_state_enter(0x%x) enter\n",
405 scif_sas_high_priority_request_queue_purge_domain(
406 &fw_domain->controller->hprq, fw_domain
409 // Search the domain's list of devices and put them all in the STOPPING
411 while (element != NULL)
413 fw_device = (SCIF_SAS_REMOTE_DEVICE_T*)
414 sci_abstract_list_get_object(element);
416 // This method will stop the core device. The core will terminate
417 // all IO requests currently outstanding.
418 fw_device->state_handlers->parent.stop_handler(&fw_device->parent);
420 element = sci_abstract_list_get_next(element);
423 // Attempt to transition to the stopped state.
424 scif_sas_domain_transition_to_stopped_state(fw_domain);
428 * @brief This method implements the actions taken when entering the
431 * @param[in] object This parameter specifies the base object for which
432 * the state transition is occurring. This is cast into a
433 * SCIF_SAS_DOMAIN object in the method implementation.
438 void scif_sas_domain_stopped_state_enter(
439 SCI_BASE_OBJECT_T * object
442 SCIF_SAS_DOMAIN_T * fw_domain = (SCIF_SAS_DOMAIN_T *)object;
446 scif_sas_domain_state_handler_table,
447 SCI_BASE_DOMAIN_STATE_STOPPED
451 sci_base_object_get_logger(fw_domain),
452 SCIF_LOG_OBJECT_DOMAIN | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
453 "scif_sas_domain_stopped_state_enter(0x%x) enter\n",
457 // A hot unplug of the direct attached device has occurred. Thus,
458 // notify the user. Note, if the controller is not in READY state,
459 // mostly likely the controller is in STOPPING or STOPPED state,
460 // meaning the controller is in the process of stopping, we should
461 // not call back to user in the middle of controller stopping.
462 if(fw_domain->controller->parent.state_machine.current_state_id
463 == SCI_BASE_CONTROLLER_STATE_READY)
464 scif_cb_domain_change_notification(fw_domain->controller, fw_domain);
468 * @brief This method implements the actions taken when entering the
469 * DISCOVERING state. This includes determining from which
470 * state we entered. If we entered from stopping that some sort
471 * of hot-remove of the port occurred. In the hot-remove case
472 * all devices should be in the STOPPED state already and, as
473 * a result, are removed from the domain with a notification sent
474 * to the framework user.
476 * @note This method currently only handles hot-insert/hot-remove of
477 * direct attached SSP devices.
479 * @param[in] object This parameter specifies the base object for which
480 * the state transition is occurring. This is cast into a
481 * SCIF_SAS_DOMAIN object in the method implementation.
486 void scif_sas_domain_discovering_state_enter(
487 SCI_BASE_OBJECT_T * object
490 SCIF_SAS_DOMAIN_T * fw_domain = (SCIF_SAS_DOMAIN_T *)object;
494 scif_sas_domain_state_handler_table,
495 SCI_BASE_DOMAIN_STATE_DISCOVERING
499 sci_base_object_get_logger(fw_domain),
500 SCIF_LOG_OBJECT_DOMAIN | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
501 "scif_sas_domain_discovering_state_enter(0x%x) enter\n",
505 fw_domain->broadcast_change_count = 0;
507 // Did the domain just go through a port not ready action? If it did,
508 // then we will be entering from the STOPPED state.
509 if (fw_domain->parent.state_machine.previous_state_id
510 != SCI_BASE_DOMAIN_STATE_STOPPED)
512 SCIF_SAS_REMOTE_DEVICE_T * remote_device;
513 SCIC_PORT_PROPERTIES_T properties;
515 scic_port_get_properties(fw_domain->core_object, &properties);
517 // If the device has not yet been added to the domain, then
518 // inform the user that the device is new.
519 remote_device = (SCIF_SAS_REMOTE_DEVICE_T *)
520 scif_domain_get_device_by_sas_address(
521 fw_domain, &properties.remote.sas_address
523 if (remote_device == SCI_INVALID_HANDLE)
525 // simply notify the user of the new DA device and be done
527 scif_cb_domain_da_device_added(
528 fw_domain->controller,
530 &properties.remote.sas_address,
531 &properties.remote.protocols
536 if(properties.remote.protocols.u.bits.smp_target)
537 //kick off the smp discover process.
538 scif_sas_domain_start_smp_discover(fw_domain, remote_device);
541 else //entered from STOPPED state.
543 SCI_ABSTRACT_ELEMENT_T * current_element =
544 sci_abstract_list_get_front(&(fw_domain->remote_device_list) );
546 SCIF_SAS_REMOTE_DEVICE_T * fw_device;
548 while (current_element != NULL)
550 fw_device = (SCIF_SAS_REMOTE_DEVICE_T *)
551 sci_abstract_list_get_object(current_element);
553 ASSERT(fw_device->parent.state_machine.current_state_id
554 == SCI_BASE_REMOTE_DEVICE_STATE_STOPPED);
557 sci_abstract_list_get_next(current_element);
560 sci_base_object_get_logger(fw_domain),
561 SCIF_LOG_OBJECT_DOMAIN | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
562 "Controller:0x%x Domain:0x%x Device:0x%x removed\n",
563 fw_domain->controller, fw_domain, fw_device
566 // Notify the framework user of the device removal.
567 scif_cb_domain_device_removed(
568 fw_domain->controller, fw_domain, fw_device
572 ASSERT(fw_domain->request_list.element_count == 0);
573 ASSERT(sci_abstract_list_size(&fw_domain->remote_device_list) == 0);
575 sci_base_state_machine_change_state(
576 &fw_domain->parent.state_machine, SCI_BASE_DOMAIN_STATE_STARTING
581 SCI_BASE_STATE_T scif_sas_domain_state_table[SCI_BASE_DOMAIN_MAX_STATES] =
584 SCI_BASE_DOMAIN_STATE_INITIAL,
585 scif_sas_domain_initial_state_enter,
589 SCI_BASE_DOMAIN_STATE_STARTING,
590 scif_sas_domain_starting_state_enter,
594 SCI_BASE_DOMAIN_STATE_READY,
595 scif_sas_domain_ready_state_enter,
596 scif_sas_domain_ready_state_exit,
599 SCI_BASE_DOMAIN_STATE_STOPPING,
600 scif_sas_domain_stopping_state_enter,
604 SCI_BASE_DOMAIN_STATE_STOPPED,
605 scif_sas_domain_stopped_state_enter,
609 SCI_BASE_DOMAIN_STATE_DISCOVERING,
610 scif_sas_domain_discovering_state_enter,