summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/scsi/isci/core/scic_port.h17
-rw-r--r--drivers/scsi/isci/core/scic_sds_controller.c22
-rw-r--r--drivers/scsi/isci/core/scic_sds_port.c334
-rw-r--r--drivers/scsi/isci/core/scic_sds_port.h9
-rw-r--r--drivers/scsi/isci/host.c7
5 files changed, 298 insertions, 91 deletions
diff --git a/drivers/scsi/isci/core/scic_port.h b/drivers/scsi/isci/core/scic_port.h
index e55abb68ff9a..8222443c9fd7 100644
--- a/drivers/scsi/isci/core/scic_port.h
+++ b/drivers/scsi/isci/core/scic_port.h
@@ -147,23 +147,6 @@ enum sci_status scic_port_get_properties(
struct scic_sds_port *port,
struct scic_port_properties *properties);
-
-
-/**
- * scic_port_start() - This method will make the port ready for operation.
- * Prior to calling the start method IO operation is not possible.
- * @port: This parameter specifies the port to be started.
- *
- * Indicate if the port was successfully started. SCI_SUCCESS This value is
- * returned if the port was successfully started. SCI_WARNING_ALREADY_IN_STATE
- * This value is returned if the port is in the process of starting.
- * SCI_FAILURE_INVALID_PORT This value is returned if the supplied port is not
- * valid. SCI_FAILURE_INVALID_STATE This value is returned if a start operation
- * can't be completed due to the state of port.
- */
-enum sci_status scic_port_start(
- struct scic_sds_port *port);
-
/**
* scic_port_stop() - This method will make the port no longer ready for
* operation. After invoking this method IO operation is not possible.
diff --git a/drivers/scsi/isci/core/scic_sds_controller.c b/drivers/scsi/isci/core/scic_sds_controller.c
index d642ff7be6db..5e26df7fcb3a 100644
--- a/drivers/scsi/isci/core/scic_sds_controller.c
+++ b/drivers/scsi/isci/core/scic_sds_controller.c
@@ -796,7 +796,11 @@ enum sci_status scic_sds_controller_stop_ports(struct scic_sds_controller *scic)
enum sci_status status = SCI_SUCCESS;
for (index = 0; index < scic->logical_port_entries; index++) {
- port_status = scic_port_stop(&scic->port_table[index]);
+ struct scic_sds_port *sci_port = &scic->port_table[index];
+ SCI_BASE_PORT_HANDLER_T stop;
+
+ stop = sci_port->state_handlers->parent.stop_handler;
+ port_status = stop(&sci_port->parent);
if ((port_status != SCI_SUCCESS) &&
(port_status != SCI_FAILURE_INVALID_STATE)) {
@@ -806,7 +810,7 @@ enum sci_status scic_sds_controller_stop_ports(struct scic_sds_controller *scic)
"%s: Controller stop operation failed to "
"stop port %d because of status %d.\n",
__func__,
- scic->port_table[index].logical_port_index,
+ sci_port->logical_port_index,
port_status);
}
}
@@ -3003,7 +3007,7 @@ static enum sci_status scic_sds_controller_initialized_state_start_handler(
scic_sds_controller_ram_initialization(this_controller);
}
- if (SCI_SUCCESS == result) {
+ if (result == SCI_SUCCESS) {
/* Build the TCi free pool */
sci_pool_initialize(this_controller->tci_pool);
for (index = 0; index < this_controller->task_context_entries; index++) {
@@ -3017,7 +3021,7 @@ static enum sci_status scic_sds_controller_initialized_state_start_handler(
);
}
- if (SCI_SUCCESS == result) {
+ if (result == SCI_SUCCESS) {
/*
* Before anything else lets make sure we will not be interrupted
* by the hardware. */
@@ -3036,7 +3040,15 @@ static enum sci_status scic_sds_controller_initialized_state_start_handler(
scic_sds_controller_initialize_unsolicited_frame_queue(this_controller);
}
- if (SCI_SUCCESS == result) {
+ /* Start all of the ports on this controller */
+ for (index = 0; index < this_controller->logical_port_entries &&
+ result == SCI_SUCCESS; index++) {
+ struct scic_sds_port *sci_port = &this_controller->port_table[index];
+
+ result = sci_port->state_handlers->parent.start_handler(&sci_port->parent);
+ }
+
+ if (result == SCI_SUCCESS) {
scic_sds_controller_start_next_phy(this_controller);
isci_event_timer_start(this_controller,
diff --git a/drivers/scsi/isci/core/scic_sds_port.c b/drivers/scsi/isci/core/scic_sds_port.c
index d374c7ac0b71..2a193d30c55d 100644
--- a/drivers/scsi/isci/core/scic_sds_port.c
+++ b/drivers/scsi/isci/core/scic_sds_port.c
@@ -67,6 +67,7 @@
#include "scic_sds_remote_node_context.h"
#include "scic_sds_request.h"
#include "sci_environment.h"
+#include "scic_sds_controller_registers.h"
static void scic_sds_port_invalid_link_up(
@@ -78,6 +79,7 @@ static void scic_sds_port_timeout_handler(
#define SCIC_SDS_PORT_MAX_TIMER_COUNT (SCI_MAX_PORTS)
#define SCIC_SDS_PORT_HARD_RESET_TIMEOUT (1000)
+#define SCU_DUMMY_INDEX (0xFFFF)
void sci_base_port_construct(
struct sci_base_port *base_port,
@@ -474,67 +476,131 @@ void scic_sds_port_get_attached_protocols(
}
/**
- * This method returns the amount of memory requred for a port object.
+ * scic_sds_port_construct_dummy_rnc() - create dummy rnc for si workaround
*
- * u32
- */
-
-/**
- * This method returns the minimum number of timers required for all port
- * objects.
+ * @sci_port: logical port on which we need to create the remote node context
+ * @rni: remote node index for this remote node context.
*
- * u32
+ * This routine will construct a dummy remote node context data structure
+ * This structure will be posted to the hardware to work around a scheduler
+ * error in the hardware.
*/
+void scic_sds_port_construct_dummy_rnc(struct scic_sds_port *sci_port, u16 rni)
+{
+ union scu_remote_node_context *rnc;
-/**
- * This method returns the maximum number of timers required for all port
- * objects.
- *
- * u32
- */
+ rnc = &sci_port->owning_controller->remote_node_context_table[rni];
+
+ memset(rnc, 0, sizeof(union scu_remote_node_context));
+
+ rnc->ssp.remote_sas_address_hi = 0;
+ rnc->ssp.remote_sas_address_lo = 0;
+
+ rnc->ssp.remote_node_index = rni;
+ rnc->ssp.remote_node_port_width = 1;
+ rnc->ssp.logical_port_index = sci_port->physical_port_index;
+
+ rnc->ssp.nexus_loss_timer_enable = false;
+ rnc->ssp.check_bit = false;
+ rnc->ssp.is_valid = true;
+ rnc->ssp.is_remote_node_context = true;
+ rnc->ssp.function_number = 0;
+ rnc->ssp.arbitration_wait_time = 0;
+}
/**
+ * scic_sds_port_construct_dummy_task() - create dummy task for si workaround
+ * @sci_port The logical port on which we need to create the
+ * remote node context.
+ * context.
+ * @tci The remote node index for this remote node context.
*
- * @this_port:
- * @port_index:
- *
+ * This routine will construct a dummy task context data structure. This
+ * structure will be posted to the hardwre to work around a scheduler error
+ * in the hardware.
*
*/
-void scic_sds_port_construct(
- struct scic_sds_port *this_port,
- u8 port_index,
- struct scic_sds_controller *owning_controller)
+void scic_sds_port_construct_dummy_task(struct scic_sds_port *sci_port, u16 tci)
+{
+ struct scu_task_context *task_context;
+
+ task_context = scic_sds_controller_get_task_context_buffer(sci_port->owning_controller, tci);
+
+ memset(task_context, 0, sizeof(struct scu_task_context));
+
+ task_context->abort = 0;
+ task_context->priority = 0;
+ task_context->initiator_request = 1;
+ task_context->connection_rate = 1;
+ task_context->protocol_engine_index = 0;
+ task_context->logical_port_index = sci_port->physical_port_index;
+ task_context->protocol_type = SCU_TASK_CONTEXT_PROTOCOL_SSP;
+ task_context->task_index = scic_sds_io_tag_get_index(tci);
+ task_context->valid = SCU_TASK_CONTEXT_VALID;
+ task_context->context_type = SCU_TASK_CONTEXT_TYPE;
+
+ task_context->remote_node_index = sci_port->reserved_rni;
+ task_context->command_code = 0;
+
+ task_context->link_layer_control = 0;
+ task_context->do_not_dma_ssp_good_response = 1;
+ task_context->strict_ordering = 0;
+ task_context->control_frame = 0;
+ task_context->timeout_enable = 0;
+ task_context->block_guard_enable = 0;
+
+ task_context->address_modifier = 0;
+
+ task_context->task_phase = 0x01;
+}
+
+void scic_sds_port_destroy_dummy_resources(struct scic_sds_port *sci_port)
+{
+ struct scic_sds_controller *scic = sci_port->owning_controller;
+
+ if (sci_port->reserved_tci != SCU_DUMMY_INDEX)
+ scic_controller_free_io_tag(scic, sci_port->reserved_tci);
+
+ if (sci_port->reserved_rni != SCU_DUMMY_INDEX)
+ scic_sds_remote_node_table_release_remote_node_index(&scic->available_remote_nodes,
+ 1, sci_port->reserved_rni);
+
+ sci_port->reserved_rni = SCU_DUMMY_INDEX;
+ sci_port->reserved_tci = SCU_DUMMY_INDEX;
+}
+
+void scic_sds_port_construct(struct scic_sds_port *sci_port, u8 port_index,
+ struct scic_sds_controller *scic)
{
u32 index;
- sci_base_port_construct(
- &this_port->parent,
- scic_sds_port_state_table
- );
+ sci_base_port_construct(&sci_port->parent, scic_sds_port_state_table);
sci_base_state_machine_construct(
- scic_sds_port_get_ready_substate_machine(this_port),
- &this_port->parent.parent,
+ scic_sds_port_get_ready_substate_machine(sci_port),
+ &sci_port->parent.parent,
scic_sds_port_ready_substate_table,
SCIC_SDS_PORT_READY_SUBSTATE_WAITING
);
- this_port->logical_port_index = SCIC_SDS_DUMMY_PORT;
- this_port->physical_port_index = port_index;
- this_port->active_phy_mask = 0;
+ sci_port->logical_port_index = SCIC_SDS_DUMMY_PORT;
+ sci_port->physical_port_index = port_index;
+ sci_port->active_phy_mask = 0;
- this_port->owning_controller = owning_controller;
+ sci_port->owning_controller = scic;
- this_port->started_request_count = 0;
- this_port->assigned_device_count = 0;
+ sci_port->started_request_count = 0;
+ sci_port->assigned_device_count = 0;
- this_port->timer_handle = NULL;
+ sci_port->reserved_rni = SCU_DUMMY_INDEX;
+ sci_port->reserved_tci = SCU_DUMMY_INDEX;
- this_port->port_task_scheduler_registers = NULL;
+ sci_port->timer_handle = NULL;
- for (index = 0; index < SCI_MAX_PHYS; index++) {
- this_port->phy_table[index] = NULL;
- }
+ sci_port->port_task_scheduler_registers = NULL;
+
+ for (index = 0; index < SCI_MAX_PHYS; index++)
+ sci_port->phy_table[index] = NULL;
}
/**
@@ -634,19 +700,11 @@ void scic_sds_port_general_link_up_handler(
}
}
-
-enum sci_status scic_port_start(struct scic_sds_port *port)
-{
- return port->state_handlers->parent.start_handler(&port->parent);
-}
-
-
enum sci_status scic_port_stop(struct scic_sds_port *port)
{
return port->state_handlers->parent.stop_handler(&port->parent);
}
-
enum sci_status scic_port_get_properties(
struct scic_sds_port *port,
struct scic_port_properties *prop)
@@ -1542,6 +1600,58 @@ static void scic_sds_port_suspend_port_task_scheduler(
}
/**
+ * scic_sds_port_post_dummy_request() - post dummy/workaround request
+ * @sci_port: port to post task
+ *
+ * Prevent the hardware scheduler from posting new requests to the front
+ * of the scheduler queue causing a starvation problem for currently
+ * ongoing requests.
+ *
+ */
+void scic_sds_port_post_dummy_request(struct scic_sds_port *sci_port)
+{
+ u32 command;
+ struct scu_task_context *task_context;
+ struct scic_sds_controller *scic = sci_port->owning_controller;
+ u16 tci = sci_port->reserved_tci;
+
+ task_context = scic_sds_controller_get_task_context_buffer(scic, tci);
+
+ task_context->abort = 0;
+
+ command = SCU_CONTEXT_COMMAND_REQUEST_TYPE_POST_TC |
+ sci_port->physical_port_index << SCU_CONTEXT_COMMAND_LOGICAL_PORT_SHIFT |
+ tci;
+
+ scic_sds_controller_post_request(scic, command);
+}
+
+/**
+ * This routine will abort the dummy request. This will alow the hardware to
+ * power down parts of the silicon to save power.
+ *
+ * @sci_port: The port on which the task must be aborted.
+ *
+ */
+void scic_sds_port_abort_dummy_request(struct scic_sds_port *sci_port)
+{
+ struct scic_sds_controller *scic = sci_port->owning_controller;
+ u16 tci = sci_port->reserved_tci;
+ struct scu_task_context *tc;
+ u32 command;
+
+ tc = scic_sds_controller_get_task_context_buffer(scic, tci);
+
+ tc->abort = 1;
+
+ command = SCU_CONTEXT_COMMAND_REQUEST_POST_TC_ABORT |
+ sci_port->physical_port_index << SCU_CONTEXT_COMMAND_LOGICAL_PORT_SHIFT |
+ tci;
+
+ scic_sds_controller_post_request(scic, command);
+}
+
+/**
*
* @this_port: This is the struct scic_sds_port object to resume.
*
@@ -1584,6 +1694,12 @@ static void scic_sds_port_ready_substate_waiting_enter(
scic_sds_port_suspend_port_task_scheduler(this_port);
+ /* Kill the dummy task for this port if it has not yet posted
+ * the hardware will treat this as a NOP and just return abort
+ * complete.
+ */
+ scic_sds_port_abort_dummy_request(this_port);
+
this_port->not_ready_reason = SCIC_PORT_NOT_READY_NO_ACTIVE_PHYS;
if (this_port->active_phy_mask != 0) {
@@ -1629,6 +1745,11 @@ static void scic_sds_port_ready_substate_operational_enter(
scic_sds_port_update_viit_entry(this_port);
scic_sds_port_resume_port_task_scheduler(this_port);
+
+ /* Post the dummy task for the port so the hardware can schedule
+ * io correctly
+ */
+ scic_sds_port_post_dummy_request(this_port);
}
/**
@@ -2062,6 +2183,7 @@ static enum sci_status scic_sds_port_general_complete_io_handler(
* **************************************************************************** */
/**
+ * scic_sds_port_stopped_state_start_handler() - stop a port from "started"
*
* @port: This is the struct sci_base_port object which is cast into a struct scic_sds_port
* object.
@@ -2075,13 +2197,14 @@ static enum sci_status scic_sds_port_general_complete_io_handler(
* start request is successful and the struct scic_sds_port object has transitioned to
* the SCI_BASE_PORT_STATE_READY.
*/
-static enum sci_status scic_sds_port_stopped_state_start_handler(
- struct sci_base_port *port)
+static enum sci_status scic_sds_port_stopped_state_start_handler(struct sci_base_port *base_port)
{
+ struct scic_sds_port *sci_port = container_of(base_port, typeof(*sci_port), parent);
+ struct scic_sds_controller *scic = sci_port->owning_controller;
+ enum sci_status status = SCI_SUCCESS;
u32 phy_mask;
- struct scic_sds_port *this_port = (struct scic_sds_port *)port;
- if (this_port->assigned_device_count > 0) {
+ if (sci_port->assigned_device_count > 0) {
/*
* / @todo This is a start failure operation because there are still
* / devices assigned to this port. There must be no devices
@@ -2089,22 +2212,56 @@ static enum sci_status scic_sds_port_stopped_state_start_handler(
return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
}
- phy_mask = scic_sds_port_get_phys(this_port);
+ sci_port->timer_handle = isci_event_timer_create(scic,
+ scic_sds_port_timeout_handler,
+ sci_port);
- /*
- * There are one or more phys assigned to this port. Make sure
- * the port's phy mask is in fact legal and supported by the
- * silicon. */
- if (scic_sds_port_is_phy_mask_valid(this_port, phy_mask) == true) {
- sci_base_state_machine_change_state(
- scic_sds_port_get_base_state_machine(this_port),
- SCI_BASE_PORT_STATE_READY
- );
+ if (!sci_port->timer_handle)
+ return SCI_FAILURE_INSUFFICIENT_RESOURCES;
- return SCI_SUCCESS;
+ if (sci_port->reserved_rni == SCU_DUMMY_INDEX) {
+ u16 rni = scic_sds_remote_node_table_allocate_remote_node(&scic->available_remote_nodes, 1);
+
+ if (rni != SCU_DUMMY_INDEX)
+ scic_sds_port_construct_dummy_rnc(sci_port, rni);
+ else
+ status = SCI_FAILURE_INSUFFICIENT_RESOURCES;
+ sci_port->reserved_rni = rni;
+ }
+
+ if (sci_port->reserved_tci == SCU_DUMMY_INDEX) {
+ /* Allocate a TCI and remove the sequence nibble */
+ u16 tci = scic_controller_allocate_io_tag(scic);
+
+ if (tci != SCU_DUMMY_INDEX)
+ scic_sds_port_construct_dummy_task(sci_port, tci);
+ else
+ status = SCI_FAILURE_INSUFFICIENT_RESOURCES;
+ sci_port->reserved_tci = tci;
+ }
+
+ if (status == SCI_SUCCESS) {
+ phy_mask = scic_sds_port_get_phys(sci_port);
+
+ /*
+ * There are one or more phys assigned to this port. Make sure
+ * the port's phy mask is in fact legal and supported by the
+ * silicon.
+ */
+ if (scic_sds_port_is_phy_mask_valid(sci_port, phy_mask) == true) {
+ sci_base_state_machine_change_state(
+ scic_sds_port_get_base_state_machine(sci_port),
+ SCI_BASE_PORT_STATE_READY);
+
+ return SCI_SUCCESS;
+ } else
+ status = SCI_FAILURE;
}
- return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
+ if (status != SCI_SUCCESS)
+ scic_sds_port_destroy_dummy_resources(sci_port);
+
+ return status;
}
/**
@@ -2467,6 +2624,52 @@ static void scic_sds_port_disable_port_task_scheduler(
scu_port_task_scheduler_write(this_port, control, pts_control_value);
}
+void scic_sds_port_post_dummy_remote_node(struct scic_sds_port *sci_port)
+{
+ struct scic_sds_controller *scic = sci_port->owning_controller;
+ u8 phys_index = sci_port->physical_port_index;
+ union scu_remote_node_context *rnc;
+ u16 rni = sci_port->reserved_rni;
+ u32 command;
+
+ rnc = &scic->remote_node_context_table[rni];
+ rnc->ssp.is_valid = true;
+
+ command = SCU_CONTEXT_COMMAND_POST_RNC_32 |
+ phys_index << SCU_CONTEXT_COMMAND_LOGICAL_PORT_SHIFT | rni;
+
+ scic_sds_controller_post_request(scic, command);
+
+ /* ensure hardware has seen the post rnc command and give it
+ * ample time to act before sending the suspend
+ */
+ SMU_ISR_READ(scic); /* flush */
+ udelay(10);
+
+ command = SCU_CONTEXT_COMMAND_POST_RNC_SUSPEND_TX_RX |
+ phys_index << SCU_CONTEXT_COMMAND_LOGICAL_PORT_SHIFT | rni;
+
+ scic_sds_controller_post_request(scic, command);
+}
+
+void scic_sds_port_invalidate_dummy_remote_node(struct scic_sds_port *sci_port)
+{
+ struct scic_sds_controller *scic = sci_port->owning_controller;
+ u8 phys_index = sci_port->physical_port_index;
+ union scu_remote_node_context *rnc;
+ u16 rni = sci_port->reserved_rni;
+ u32 command;
+
+ rnc = &scic->remote_node_context_table[rni];
+
+ rnc->ssp.is_valid = false;
+
+ command = SCU_CONTEXT_COMMAND_POST_RNC_INVALIDATE |
+ phys_index << SCU_CONTEXT_COMMAND_LOGICAL_PORT_SHIFT | rni;
+
+ scic_sds_controller_post_request(scic, command);
+}
+
/*
* ******************************************************************************
* * PORT STATE METHODS
@@ -2562,6 +2765,9 @@ static void scic_sds_port_ready_state_enter(
);
}
+ /* Post and suspend the dummy remote node context for this port. */
+ scic_sds_port_post_dummy_remote_node(this_port);
+
/* Start the ready substate machine */
sci_base_state_machine_start(
scic_sds_port_get_ready_substate_machine(this_port)
@@ -2583,6 +2789,8 @@ static void scic_sds_port_ready_state_exit(
this_port = (struct scic_sds_port *)object;
sci_base_state_machine_stop(&this_port->ready_substate_machine);
+
+ scic_sds_port_invalidate_dummy_remote_node(this_port);
}
/**
@@ -2663,6 +2871,8 @@ static void scic_sds_port_stopping_state_exit(
scic_sds_port_get_controller(this_port),
this_port->timer_handle
);
+
+ scic_sds_port_destroy_dummy_resources(this_port);
}
/**
diff --git a/drivers/scsi/isci/core/scic_sds_port.h b/drivers/scsi/isci/core/scic_sds_port.h
index 3eb80cb2ea47..c98caefa7cf2 100644
--- a/drivers/scsi/isci/core/scic_sds_port.h
+++ b/drivers/scsi/isci/core/scic_sds_port.h
@@ -73,6 +73,12 @@
#define SCIC_SDS_DUMMY_PORT 0xFF
/**
+ * This constant defines the value utilized by SCI Components to indicate
+ * an invalid handle.
+ */
+#define SCI_INVALID_HANDLE 0x0
+
+/**
* enum SCIC_SDS_PORT_READY_SUBSTATES -
*
* This enumeration depicts all of the states for the core port ready substate
@@ -134,6 +140,9 @@ struct scic_sds_port {
*/
u8 active_phy_mask;
+ u16 reserved_rni;
+ u16 reserved_tci;
+
/**
* This field contains the count of the io requests started on this port
* object. It is used to control controller shutdown.
diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c
index 1bc91f2b4f93..40614e9ab41b 100644
--- a/drivers/scsi/isci/host.c
+++ b/drivers/scsi/isci/host.c
@@ -381,7 +381,6 @@ int isci_host_init(struct isci_host *isci_host)
int index = 0;
enum sci_status status;
struct scic_sds_controller *controller;
- struct scic_sds_port *scic_port;
union scic_oem_parameters scic_oem_params;
union scic_user_parameters scic_user_params;
@@ -517,11 +516,5 @@ int isci_host_init(struct isci_host *isci_host)
for (index = 0; index < SCI_MAX_PHYS; index++)
isci_phy_init(&isci_host->phys[index], isci_host, index);
- /* Start the ports */
- for (index = 0; index < SCI_MAX_PORTS; index++) {
- scic_controller_get_port_handle(controller, index, &scic_port);
- scic_port_start(scic_port);
- }
-
return 0;
}