wake_up(&ihost->eventq);
}
+static enum sci_status sci_remote_device_suspend(
+ struct isci_remote_device *idev)
+{
+ return sci_remote_node_context_suspend(
+ &idev->rnc,
+ SCI_SOFTWARE_SUSPENSION,
+ SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT,
+ NULL, NULL);
+}
+
+static int isci_remote_device_suspendcheck(struct isci_remote_device *idev)
+{
+ return test_bit(IDEV_TXRX_SUSPENDED, &idev->flags)
+ || !test_bit(IDEV_ALLOCATED, &idev->flags);
+}
+
+enum sci_status isci_remote_device_suspend(
+ struct isci_host *ihost,
+ struct isci_remote_device *idev)
+{
+ enum sci_status status;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ihost->scic_lock, flags);
+ if (isci_get_device(idev->domain_dev) == NULL) {
+ spin_unlock_irqrestore(&ihost->scic_lock, flags);
+ status = SCI_FAILURE;
+ } else {
+ status = sci_remote_device_suspend(idev);
+ spin_unlock_irqrestore(&ihost->scic_lock, flags);
+ if (status == SCI_SUCCESS) {
+ dev_dbg(&ihost->pdev->dev,
+ "%s: idev=%p, about to wait\n",
+ __func__, idev);
+ wait_event(ihost->eventq,
+ isci_remote_device_suspendcheck(idev));
+ status = test_bit(IDEV_TXRX_SUSPENDED, &idev->flags)
+ ? SCI_SUCCESS : SCI_FAILURE;
+ dev_dbg(&ihost->pdev->dev,
+ "%s: idev=%p, wait done, device is %s\n",
+ __func__, idev,
+ test_bit(IDEV_TXRX_SUSPENDED, &idev->flags)
+ ? "<suspended>" : "<deallocated!>");
+
+ } else
+ dev_dbg(scirdev_to_dev(idev),
+ "%s: sci_remote_device_suspend failed, "
+ "status = %d\n", __func__, status);
+ isci_put_device(idev);
+ }
+ return status;
+}
+
/* called once the remote node context is ready to be freed.
* The remote device can now report that its stop operation is complete. none
*/
sci_change_state(&idev->sm, SCI_DEV_STOPPED);
}
-static enum sci_status sci_remote_device_terminate_requests(struct isci_remote_device *idev)
+static enum sci_status sci_remote_device_terminate_requests_checkabort(
+ struct isci_remote_device *idev,
+ int check_abort_pending)
{
struct isci_host *ihost = idev->owning_port->owning_controller;
enum sci_status status = SCI_SUCCESS;
enum sci_status s;
if (!test_bit(IREQ_ACTIVE, &ireq->flags) ||
- ireq->target_device != idev)
+ (ireq->target_device != idev) ||
+ (check_abort_pending && !test_bit(IREQ_PENDING_ABORT,
+ &ireq->flags)))
continue;
s = sci_controller_terminate_request(ihost, idev, ireq);
return status;
}
+enum sci_status sci_remote_device_terminate_requests(
+ struct isci_remote_device *idev)
+{
+ return sci_remote_device_terminate_requests_checkabort(idev, 0);
+}
+
enum sci_status sci_remote_device_stop(struct isci_remote_device *idev,
u32 timeout)
{
return SCI_SUCCESS;
}
-enum sci_status sci_remote_device_suspend(struct isci_remote_device *idev,
- u32 suspend_type)
-{
- struct sci_base_state_machine *sm = &idev->sm;
- enum sci_remote_device_states state = sm->current_state_id;
-
- if (state != SCI_STP_DEV_CMD) {
- dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %s\n",
- __func__, dev_state_name(state));
- return SCI_FAILURE_INVALID_STATE;
- }
-
- return sci_remote_node_context_suspend(&idev->rnc,
- suspend_type, NULL, NULL);
-}
-
enum sci_status sci_remote_device_frame_handler(struct isci_remote_device *idev,
u32 frame_index)
{
enum sci_status sci_remote_device_event_handler(struct isci_remote_device *idev,
u32 event_code)
{
- struct sci_base_state_machine *sm = &idev->sm;
- enum sci_remote_device_states state = sm->current_state_id;
enum sci_status status;
switch (scu_get_event_type(event_code)) {
status = SCI_SUCCESS;
/* Suspend the associated RNC */
- sci_remote_node_context_suspend(&idev->rnc,
- SCI_SOFTWARE_SUSPENSION,
- NULL, NULL);
+ sci_remote_node_context_suspend(
+ &idev->rnc,
+ SCI_SOFTWARE_SUSPENSION,
+ SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT,
+ NULL, NULL);
dev_dbg(scirdev_to_dev(idev),
"%s: device: %p event code: %x: %s\n",
if (status != SCI_SUCCESS)
return status;
- if (state == SCI_STP_DEV_ATAPI_ERROR) {
- /* For ATAPI error state resume the RNC right away. */
- if (scu_get_event_type(event_code) == SCU_EVENT_TYPE_RNC_SUSPEND_TX ||
- scu_get_event_type(event_code) == SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX) {
- return sci_remote_node_context_resume(&idev->rnc,
- atapi_remote_device_resume_done,
- idev);
- }
- }
-
- if (state == SCI_STP_DEV_IDLE) {
-
- /* We pick up suspension events to handle specifically to this
- * state. We resume the RNC right away.
- */
- if (scu_get_event_type(event_code) == SCU_EVENT_TYPE_RNC_SUSPEND_TX ||
- scu_get_event_type(event_code) == SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX)
- status = sci_remote_node_context_resume(&idev->rnc, NULL, NULL);
- }
-
return status;
}
* the correct action when the remote node context is suspended
* and later resumed.
*/
- sci_remote_node_context_suspend(&idev->rnc,
- SCI_SOFTWARE_SUSPENSION, NULL, NULL);
- sci_remote_node_context_resume(&idev->rnc,
- sci_remote_device_continue_request,
- idev);
+ sci_remote_node_context_suspend(
+ &idev->rnc, SCI_SOFTWARE_SUSPENSION,
+ SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT, NULL, NULL);
+ sci_remote_node_context_resume(
+ &idev->rnc, sci_remote_device_continue_request, idev);
out:
sci_remote_device_start_request(idev, ireq, status);
static void sci_remote_device_resetting_state_enter(struct sci_base_state_machine *sm)
{
struct isci_remote_device *idev = container_of(sm, typeof(*idev), sm);
+ struct isci_host *ihost = idev->owning_port->owning_controller;
+
+ dev_dbg(&ihost->pdev->dev,
+ "%s: isci_device = %p\n", __func__, idev);
sci_remote_node_context_suspend(
- &idev->rnc, SCI_SOFTWARE_SUSPENSION, NULL, NULL);
+ &idev->rnc, SCI_SOFTWARE_SUSPENSION,
+ SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT, NULL, NULL);
}
static void sci_remote_device_resetting_state_exit(struct sci_base_state_machine *sm)
{
struct isci_remote_device *idev = container_of(sm, typeof(*idev), sm);
+ struct isci_host *ihost = idev->owning_port->owning_controller;
+
+ dev_dbg(&ihost->pdev->dev,
+ "%s: isci_device = %p\n", __func__, idev);
sci_remote_node_context_resume(&idev->rnc, NULL, NULL);
}
idev->not_ready_reason);
}
+static void sci_stp_remote_device_atapi_error_substate_enter(
+ struct sci_base_state_machine *sm)
+{
+ struct isci_remote_device *idev = container_of(sm, typeof(*idev), sm);
+
+ /* This state is entered when an I/O is decoded with an error
+ * condition. By this point the RNC expected suspension state is set.
+ * The error conditions suspend the device, so unsuspend here if
+ * possible.
+ */
+ sci_remote_node_context_resume(&idev->rnc,
+ atapi_remote_device_resume_done,
+ idev);
+}
+
static void sci_smp_remote_device_ready_idle_substate_enter(struct sci_base_state_machine *sm)
{
struct isci_remote_device *idev = container_of(sm, typeof(*idev), sm);
[SCI_STP_DEV_NCQ_ERROR] = {
.enter_state = sci_stp_remote_device_ready_ncq_error_substate_enter,
},
- [SCI_STP_DEV_ATAPI_ERROR] = { },
+ [SCI_STP_DEV_ATAPI_ERROR] = {
+ .enter_state = sci_stp_remote_device_atapi_error_substate_enter,
+ },
[SCI_STP_DEV_AWAIT_RESET] = { },
[SCI_SMP_DEV_IDLE] = {
.enter_state = sci_smp_remote_device_ready_idle_substate_enter,
{
enum sci_status status;
struct sci_port_properties properties;
- struct domain_device *dev = idev->domain_dev;
sci_remote_device_construct(iport, idev);
- /*
- * This information is request to determine how many remote node context
- * entries will be needed to store the remote node.
- */
- idev->is_direct_attached = true;
-
sci_port_get_properties(iport, &properties);
/* Get accurate port width from port's phy mask for a DA device. */
idev->device_port_width = hweight32(properties.phy_mask);
status = sci_controller_allocate_remote_node_context(iport->owning_controller,
- idev,
- &idev->rnc.remote_node_index);
+ idev,
+ &idev->rnc.remote_node_index);
if (status != SCI_SUCCESS)
return status;
- if (dev->dev_type == SAS_END_DEV || dev->dev_type == SATA_DEV ||
- (dev->tproto & SAS_PROTOCOL_STP) || dev_is_expander(dev))
- /* pass */;
- else
- return SCI_FAILURE_UNSUPPORTED_PROTOCOL;
-
idev->connection_rate = sci_port_get_max_allowed_speed(iport);
return SCI_SUCCESS;
if (status != SCI_SUCCESS)
return status;
- if (dev->dev_type == SAS_END_DEV || dev->dev_type == SATA_DEV ||
- (dev->tproto & SAS_PROTOCOL_STP) || dev_is_expander(dev))
- /* pass */;
- else
- return SCI_FAILURE_UNSUPPORTED_PROTOCOL;
-
- /*
- * For SAS-2 the physical link rate is actually a logical link
+ /* For SAS-2 the physical link rate is actually a logical link
* rate that incorporates multiplexing. The SCU doesn't
* incorporate multiplexing and for the purposes of the
* connection the logical link rate is that same as the
* physical. Furthermore, the SAS-2 and SAS-1.1 fields overlay
- * one another, so this code works for both situations. */
+ * one another, so this code works for both situations.
+ */
idev->connection_rate = min_t(u16, sci_port_get_max_allowed_speed(iport),
dev->linkrate);
return SCI_SUCCESS;
}
+enum sci_status sci_remote_device_resume(
+ struct isci_remote_device *idev,
+ scics_sds_remote_node_context_callback cb_fn,
+ void *cb_p)
+{
+ enum sci_status status;
+
+ status = sci_remote_node_context_resume(&idev->rnc, cb_fn, cb_p);
+ if (status != SCI_SUCCESS)
+ dev_dbg(scirdev_to_dev(idev), "%s: failed to resume: %d\n",
+ __func__, status);
+ return status;
+}
+
+enum sci_status isci_remote_device_resume(
+ struct isci_host *ihost,
+ struct isci_remote_device *idev,
+ scics_sds_remote_node_context_callback cb_fn,
+ void *cb_p)
+{
+ unsigned long flags;
+ enum sci_status status;
+
+ spin_lock_irqsave(&ihost->scic_lock, flags);
+ status = sci_remote_device_resume(idev, cb_fn, cb_p);
+ spin_unlock_irqrestore(&ihost->scic_lock, flags);
+
+ return status;
+}
/**
* sci_remote_device_start() - This method will start the supplied remote
* device. This method enables normal IO requests to flow through to the
* the device when there have been no phys added to it.
*/
static enum sci_status sci_remote_device_start(struct isci_remote_device *idev,
- u32 timeout)
+ u32 timeout)
{
struct sci_base_state_machine *sm = &idev->sm;
enum sci_remote_device_states state = sm->current_state_id;
return SCI_FAILURE_INVALID_STATE;
}
- status = sci_remote_node_context_resume(&idev->rnc,
- remote_device_resume_done,
- idev);
+ status = sci_remote_device_resume(idev, remote_device_resume_done,
+ idev);
if (status != SCI_SUCCESS)
return status;
return status == SCI_SUCCESS ? 0 : -ENODEV;
}
+
+enum sci_status isci_remote_device_reset(
+ struct isci_host *ihost,
+ struct isci_remote_device *idev)
+{
+ unsigned long flags;
+ enum sci_status status;
+
+ /* Put the device into a reset state so the suspension will not
+ * automatically resume.
+ */
+ spin_lock_irqsave(&ihost->scic_lock, flags);
+ status = sci_remote_device_reset(idev);
+ spin_unlock_irqrestore(&ihost->scic_lock, flags);
+ if (status != SCI_SUCCESS) {
+ dev_dbg(&ihost->pdev->dev,
+ "%s: sci_remote_device_reset(%p) returned %d!\n",
+ __func__, idev, status);
+ return status;
+ }
+ /* Wait for the device suspend. */
+ status = isci_remote_device_suspend(ihost, idev);
+ if (status != SCI_SUCCESS) {
+ dev_dbg(&ihost->pdev->dev,
+ "%s: isci_remote_device_suspend(%p) returned %d!\n",
+ __func__, idev, status);
+ }
+ return status;
+}
+
+int isci_remote_device_is_safe_to_abort(
+ struct isci_remote_device *idev)
+{
+ return sci_remote_node_context_is_safe_to_abort(&idev->rnc);
+}
+
+enum sci_status sci_remote_device_abort_requests_pending_abort(
+ struct isci_remote_device *idev)
+{
+ return sci_remote_device_terminate_requests_checkabort(idev, 1);
+}
+
+enum sci_status isci_remote_device_reset_complete(
+ struct isci_host *ihost,
+ struct isci_remote_device *idev)
+{
+ unsigned long flags;
+ enum sci_status status;
+
+ spin_lock_irqsave(&ihost->scic_lock, flags);
+ status = sci_remote_device_reset_complete(idev);
+ sci_remote_device_resume(idev, NULL, NULL);
+ spin_unlock_irqrestore(&ihost->scic_lock, flags);
+
+ return status;
+}
+