remote_node_context.c 23.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
/*
 * This file is provided under a dual BSD/GPLv2 license.  When using or
 * redistributing this file, you may do so under either license.
 *
 * GPL LICENSE SUMMARY
 *
 * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of version 2 of the GNU General Public License as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
 * The full GNU General Public License is included in this distribution
 * in the file called LICENSE.GPL.
 *
 * BSD LICENSE
 *
 * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in
 *     the documentation and/or other materials provided with the
 *     distribution.
 *   * Neither the name of Intel Corporation nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

56
#include "host.h"
D
Dan Williams 已提交
57
#include "state_machine.h"
58 59
#include "remote_device.h"
#include "remote_node_context.h"
60 61 62 63 64 65
#include "scu_event_codes.h"
#include "scu_task_context.h"


/**
 *
66
 * @sci_rnc: The RNC for which the is posted request is being made.
67 68 69 70 71 72 73 74 75 76 77
 *
 * This method will return true if the RNC is not in the initial state.  In all
 * other states the RNC is considered active and this will return true. The
 * destroy request of the state machine drives the RNC back to the initial
 * state.  If the state machine changes then this routine will also have to be
 * changed. bool true if the state machine is not in the initial state false if
 * the state machine is in the initial state
 */

/**
 *
78
 * @sci_rnc: The state of the remote node context object to check.
79 80 81 82 83 84
 *
 * This method will return true if the remote node context is in a READY state
 * otherwise it will return false bool true if the remote node context is in
 * the ready state. false if the remote node context is not in the ready state.
 */
bool scic_sds_remote_node_context_is_ready(
85
	struct scic_sds_remote_node_context *sci_rnc)
86
{
87
	u32 current_state = sci_base_state_machine_get_state(&sci_rnc->state_machine);
88 89 90 91 92 93 94 95 96 97

	if (current_state == SCIC_SDS_REMOTE_NODE_CONTEXT_READY_STATE) {
		return true;
	}

	return false;
}

/**
 *
98
 * @sci_dev: The remote device to use to construct the RNC buffer.
99 100 101 102
 * @rnc: The buffer into which the remote device data will be copied.
 *
 * This method will construct the RNC buffer for this remote device object. none
 */
D
Dan Williams 已提交
103
static void scic_sds_remote_node_context_construct_buffer(
104
	struct scic_sds_remote_node_context *sci_rnc)
105
{
106
	struct scic_sds_remote_device *sci_dev = rnc_to_dev(sci_rnc);
107
	struct domain_device *dev = sci_dev_to_domain(sci_dev);
108
	int rni = sci_rnc->remote_node_index;
109
	union scu_remote_node_context *rnc;
110
	struct scic_sds_controller *scic;
111
	__le64 sas_addr;
112

113
	scic = scic_sds_remote_device_get_controller(sci_dev);
114
	rnc = scic_sds_controller_get_remote_node_context_buffer(scic, rni);
115

116 117
	memset(rnc, 0, sizeof(union scu_remote_node_context)
		* scic_sds_remote_device_node_count(sci_dev));
118

119
	rnc->ssp.remote_node_index = rni;
120
	rnc->ssp.remote_node_port_width = sci_dev->device_port_width;
121
	rnc->ssp.logical_port_index = sci_dev->owning_port->physical_port_index;
122

123 124 125 126
	/* sas address is __be64, context ram format is __le64 */
	sas_addr = cpu_to_le64(SAS_ADDR(dev->sas_addr));
	rnc->ssp.remote_sas_address_hi = upper_32_bits(sas_addr);
	rnc->ssp.remote_sas_address_lo = lower_32_bits(sas_addr);
127 128 129 130 131 132 133 134 135

	rnc->ssp.nexus_loss_timer_enable = true;
	rnc->ssp.check_bit               = false;
	rnc->ssp.is_valid                = false;
	rnc->ssp.is_remote_node_context  = true;
	rnc->ssp.function_number         = 0;

	rnc->ssp.arbitration_wait_time = 0;

136
	if (dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP)) {
137
		rnc->ssp.connection_occupancy_timeout =
138
			scic->user_parameters.sds1.stp_max_occupancy_timeout;
139
		rnc->ssp.connection_inactivity_timeout =
140
			scic->user_parameters.sds1.stp_inactivity_timeout;
141 142
	} else {
		rnc->ssp.connection_occupancy_timeout  =
143
			scic->user_parameters.sds1.ssp_max_occupancy_timeout;
144
		rnc->ssp.connection_inactivity_timeout =
145
			scic->user_parameters.sds1.ssp_inactivity_timeout;
146 147 148 149 150
	}

	rnc->ssp.initial_arbitration_wait_time = 0;

	/* Open Address Frame Parameters */
151
	rnc->ssp.oaf_connection_rate = sci_dev->connection_rate;
152 153 154 155 156 157 158
	rnc->ssp.oaf_features = 0;
	rnc->ssp.oaf_source_zone_group = 0;
	rnc->ssp.oaf_more_compatibility_features = 0;
}

/**
 *
159 160
 * @sci_rnc:
 * @callback:
161 162 163 164 165 166 167
 * @callback_parameter:
 *
 * This method will setup the remote node context object so it will transition
 * to its ready state.  If the remote node context is already setup to
 * transition to its final state then this function does nothing. none
 */
static void scic_sds_remote_node_context_setup_to_resume(
168 169
	struct scic_sds_remote_node_context *sci_rnc,
	scics_sds_remote_node_context_callback callback,
170 171
	void *callback_parameter)
{
172 173 174 175
	if (sci_rnc->destination_state != SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_FINAL) {
		sci_rnc->destination_state = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_READY;
		sci_rnc->user_callback     = callback;
		sci_rnc->user_cookie       = callback_parameter;
176 177 178 179
	}
}

static void scic_sds_remote_node_context_setup_to_destory(
180 181
	struct scic_sds_remote_node_context *sci_rnc,
	scics_sds_remote_node_context_callback callback,
182 183
	void *callback_parameter)
{
184 185 186
	sci_rnc->destination_state = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_FINAL;
	sci_rnc->user_callback     = callback;
	sci_rnc->user_cookie       = callback_parameter;
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
}

/**
 *
 *
 * This method just calls the user callback function and then resets the
 * callback.
 */
static void scic_sds_remote_node_context_notify_user(
	struct scic_sds_remote_node_context *rnc)
{
	if (rnc->user_callback != NULL) {
		(*rnc->user_callback)(rnc->user_cookie);

		rnc->user_callback = NULL;
		rnc->user_cookie = NULL;
	}
}

206
static void scic_sds_remote_node_context_continue_state_transitions(struct scic_sds_remote_node_context *rnc)
207
{
208 209 210
	if (rnc->destination_state == SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_READY)
		scic_sds_remote_node_context_resume(rnc, rnc->user_callback,
						    rnc->user_cookie);
211 212 213 214
}

/**
 *
215
 * @sci_rnc: The remote node context object that is to be validated.
216 217 218 219 220
 *
 * This method will mark the rnc buffer as being valid and post the request to
 * the hardware. none
 */
static void scic_sds_remote_node_context_validate_context_buffer(
221
	struct scic_sds_remote_node_context *sci_rnc)
222
{
223
	struct scic_sds_remote_device *sci_dev = rnc_to_dev(sci_rnc);
224
	struct domain_device *dev = sci_dev_to_domain(sci_dev);
225 226 227
	union scu_remote_node_context *rnc_buffer;

	rnc_buffer = scic_sds_controller_get_remote_node_context_buffer(
228
		scic_sds_remote_device_get_controller(sci_dev),
229
		sci_rnc->remote_node_index
230 231 232 233
		);

	rnc_buffer->ssp.is_valid = true;

234
	if (!sci_dev->is_direct_attached &&
235
	    (dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP))) {
236 237
		scic_sds_remote_device_post_request(sci_dev,
						    SCU_CONTEXT_COMMAND_POST_RNC_96);
238
	} else {
239
		scic_sds_remote_device_post_request(sci_dev, SCU_CONTEXT_COMMAND_POST_RNC_32);
240

241 242 243
		if (sci_dev->is_direct_attached) {
			scic_sds_port_setup_transports(sci_dev->owning_port,
						       sci_rnc->remote_node_index);
244 245 246 247 248 249
		}
	}
}

/**
 *
250
 * @sci_rnc: The remote node context object that is to be invalidated.
251 252 253 254
 *
 * This method will update the RNC buffer and post the invalidate request. none
 */
static void scic_sds_remote_node_context_invalidate_context_buffer(
255
	struct scic_sds_remote_node_context *sci_rnc)
256 257 258 259
{
	union scu_remote_node_context *rnc_buffer;

	rnc_buffer = scic_sds_controller_get_remote_node_context_buffer(
260 261
		scic_sds_remote_device_get_controller(rnc_to_dev(sci_rnc)),
		sci_rnc->remote_node_index);
262 263 264

	rnc_buffer->ssp.is_valid = false;

265 266
	scic_sds_remote_device_post_request(rnc_to_dev(sci_rnc),
					    SCU_CONTEXT_COMMAND_POST_RNC_INVALIDATE);
267 268
}

269
static void scic_sds_remote_node_context_initial_state_enter(void *object)
270
{
271
	struct scic_sds_remote_node_context *rnc = object;
272
	struct sci_base_state_machine *sm = &rnc->state_machine;
273

274 275 276 277
	/* Check to see if we have gotten back to the initial state because
	 * someone requested to destroy the remote node context object.
	 */
	if (sm->previous_state_id == SCIC_SDS_REMOTE_NODE_CONTEXT_INVALIDATING_STATE) {
278 279 280 281 282
		rnc->destination_state = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_UNSPECIFIED;
		scic_sds_remote_node_context_notify_user(rnc);
	}
}

283
static void scic_sds_remote_node_context_posting_state_enter(void *object)
284
{
285
	struct scic_sds_remote_node_context *sci_rnc = object;
286

287
	scic_sds_remote_node_context_validate_context_buffer(sci_rnc);
288 289
}

290
static void scic_sds_remote_node_context_invalidating_state_enter(void *object)
291
{
292
	struct scic_sds_remote_node_context *rnc = object;
293 294 295 296

	scic_sds_remote_node_context_invalidate_context_buffer(rnc);
}

297
static void scic_sds_remote_node_context_resuming_state_enter(void *object)
298
{
299
	struct scic_sds_remote_node_context *rnc = object;
300
	struct scic_sds_remote_device *sci_dev;
301
	struct domain_device *dev;
302

303
	sci_dev = rnc_to_dev(rnc);
304
	dev = sci_dev_to_domain(sci_dev);
305

306 307 308 309 310 311
	/*
	 * For direct attached SATA devices we need to clear the TLCR
	 * NCQ to TCi tag mapping on the phy and in cases where we
	 * resume because of a target reset we also need to update
	 * the STPTLDARNI register with the RNi of the device
	 */
312 313
	if ((dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP)) &&
	    sci_dev->is_direct_attached)
314 315
		scic_sds_port_setup_transports(sci_dev->owning_port,
					       rnc->remote_node_index);
316

317
	scic_sds_remote_device_post_request(sci_dev, SCU_CONTEXT_COMMAND_POST_RNC_RESUME);
318 319
}

320
static void scic_sds_remote_node_context_ready_state_enter(void *object)
321
{
322
	struct scic_sds_remote_node_context *rnc = object;
323 324 325

	rnc->destination_state = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_UNSPECIFIED;

326
	if (rnc->user_callback)
327 328 329
		scic_sds_remote_node_context_notify_user(rnc);
}

330
static void scic_sds_remote_node_context_tx_suspended_state_enter(void *object)
331
{
332
	struct scic_sds_remote_node_context *rnc = object;
333 334 335 336 337

	scic_sds_remote_node_context_continue_state_transitions(rnc);
}

static void scic_sds_remote_node_context_tx_rx_suspended_state_enter(
338
		void *object)
339
{
340
	struct scic_sds_remote_node_context *rnc = object;
341 342 343 344

	scic_sds_remote_node_context_continue_state_transitions(rnc);
}

D
Dan Williams 已提交
345
static const struct sci_base_state scic_sds_remote_node_context_state_table[] = {
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
	[SCIC_SDS_REMOTE_NODE_CONTEXT_INITIAL_STATE] = {
		.enter_state = scic_sds_remote_node_context_initial_state_enter,
	},
	[SCIC_SDS_REMOTE_NODE_CONTEXT_POSTING_STATE] = {
		.enter_state = scic_sds_remote_node_context_posting_state_enter,
	},
	[SCIC_SDS_REMOTE_NODE_CONTEXT_INVALIDATING_STATE] = {
		.enter_state = scic_sds_remote_node_context_invalidating_state_enter,
	},
	[SCIC_SDS_REMOTE_NODE_CONTEXT_RESUMING_STATE] = {
		.enter_state = scic_sds_remote_node_context_resuming_state_enter,
	},
	[SCIC_SDS_REMOTE_NODE_CONTEXT_READY_STATE] = {
		.enter_state = scic_sds_remote_node_context_ready_state_enter,
	},
	[SCIC_SDS_REMOTE_NODE_CONTEXT_TX_SUSPENDED_STATE] = {
		.enter_state = scic_sds_remote_node_context_tx_suspended_state_enter,
	},
	[SCIC_SDS_REMOTE_NODE_CONTEXT_TX_RX_SUSPENDED_STATE] = {
		.enter_state = scic_sds_remote_node_context_tx_rx_suspended_state_enter,
	},
367
	[SCIC_SDS_REMOTE_NODE_CONTEXT_AWAIT_SUSPENSION_STATE] = { },
368 369
};

370 371
void scic_sds_remote_node_context_construct(struct scic_sds_remote_node_context *rnc,
					    u16 remote_node_index)
D
Dan Williams 已提交
372 373 374 375 376 377 378 379
{
	memset(rnc, 0, sizeof(struct scic_sds_remote_node_context));

	rnc->remote_node_index = remote_node_index;
	rnc->destination_state = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_UNSPECIFIED;

	sci_base_state_machine_construct(
		&rnc->state_machine,
380
		rnc,
D
Dan Williams 已提交
381 382 383 384 385 386
		scic_sds_remote_node_context_state_table,
		SCIC_SDS_REMOTE_NODE_CONTEXT_INITIAL_STATE
		);

	sci_base_state_machine_start(&rnc->state_machine);
}
D
Dan Williams 已提交
387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493

enum sci_status scic_sds_remote_node_context_event_handler(struct scic_sds_remote_node_context *sci_rnc,
							   u32 event_code)
{
	enum scis_sds_remote_node_context_states state;

	state = sci_rnc->state_machine.current_state_id;
	switch (state) {
	case SCIC_SDS_REMOTE_NODE_CONTEXT_POSTING_STATE:
		switch (scu_get_event_code(event_code)) {
		case SCU_EVENT_POST_RNC_COMPLETE:
			sci_base_state_machine_change_state(&sci_rnc->state_machine,
							    SCIC_SDS_REMOTE_NODE_CONTEXT_READY_STATE);
			break;
		default:
			goto out;
		}
		break;
	case SCIC_SDS_REMOTE_NODE_CONTEXT_INVALIDATING_STATE:
		if (scu_get_event_code(event_code) == SCU_EVENT_POST_RNC_INVALIDATE_COMPLETE) {
			if (sci_rnc->destination_state == SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_FINAL)
				state = SCIC_SDS_REMOTE_NODE_CONTEXT_INITIAL_STATE;
			else
				state = SCIC_SDS_REMOTE_NODE_CONTEXT_POSTING_STATE;
			sci_base_state_machine_change_state(&sci_rnc->state_machine,
							    state);
		} else {
			switch (scu_get_event_type(event_code)) {
			case SCU_EVENT_TYPE_RNC_SUSPEND_TX:
			case SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX:
				/* We really dont care if the hardware is going to suspend
				 * the device since it's being invalidated anyway */
				dev_dbg(scirdev_to_dev(rnc_to_dev(sci_rnc)),
					"%s: SCIC Remote Node Context 0x%p was "
					"suspeneded by hardware while being "
					"invalidated.\n", __func__, sci_rnc);
				break;
			default:
				goto out;
			}
		}
		break;
	case SCIC_SDS_REMOTE_NODE_CONTEXT_RESUMING_STATE:
		if (scu_get_event_code(event_code) == SCU_EVENT_POST_RCN_RELEASE) {
			sci_base_state_machine_change_state(&sci_rnc->state_machine,
							    SCIC_SDS_REMOTE_NODE_CONTEXT_READY_STATE);
		} else {
			switch (scu_get_event_type(event_code)) {
			case SCU_EVENT_TYPE_RNC_SUSPEND_TX:
			case SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX:
				/* We really dont care if the hardware is going to suspend
				 * the device since it's being resumed anyway */
				dev_dbg(scirdev_to_dev(rnc_to_dev(sci_rnc)),
					"%s: SCIC Remote Node Context 0x%p was "
					"suspeneded by hardware while being resumed.\n",
					__func__, sci_rnc);
				break;
			default:
				goto out;
			}
		}
		break;
	case SCIC_SDS_REMOTE_NODE_CONTEXT_READY_STATE:
		switch (scu_get_event_type(event_code)) {
		case SCU_EVENT_TL_RNC_SUSPEND_TX:
			sci_base_state_machine_change_state(&sci_rnc->state_machine,
							    SCIC_SDS_REMOTE_NODE_CONTEXT_TX_SUSPENDED_STATE);
			sci_rnc->suspension_code = scu_get_event_specifier(event_code);
			break;
		case SCU_EVENT_TL_RNC_SUSPEND_TX_RX:
			sci_base_state_machine_change_state(&sci_rnc->state_machine,
							    SCIC_SDS_REMOTE_NODE_CONTEXT_TX_RX_SUSPENDED_STATE);
			sci_rnc->suspension_code = scu_get_event_specifier(event_code);
			break;
		default:
			goto out;
		}
		break;
	case SCIC_SDS_REMOTE_NODE_CONTEXT_AWAIT_SUSPENSION_STATE:
		switch (scu_get_event_type(event_code)) {
		case SCU_EVENT_TL_RNC_SUSPEND_TX:
			sci_base_state_machine_change_state(&sci_rnc->state_machine,
							    SCIC_SDS_REMOTE_NODE_CONTEXT_TX_SUSPENDED_STATE);
			sci_rnc->suspension_code = scu_get_event_specifier(event_code);
			break;
		case SCU_EVENT_TL_RNC_SUSPEND_TX_RX:
			sci_base_state_machine_change_state(&sci_rnc->state_machine,
							    SCIC_SDS_REMOTE_NODE_CONTEXT_TX_RX_SUSPENDED_STATE);
			sci_rnc->suspension_code = scu_get_event_specifier(event_code);
			break;
		default:
			goto out;
		}
		break;
	default:
		dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
			 "%s: invalid state %d\n", __func__, state);
		return SCI_FAILURE_INVALID_STATE;
	}
	return SCI_SUCCESS;

 out:
	dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
		 "%s: code: %#x state: %d\n", __func__, event_code, state);
	return SCI_FAILURE;

}
D
Dan Williams 已提交
494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529

enum sci_status scic_sds_remote_node_context_destruct(struct scic_sds_remote_node_context *sci_rnc,
						      scics_sds_remote_node_context_callback cb_fn,
						      void *cb_p)
{
	enum scis_sds_remote_node_context_states state;

	state = sci_rnc->state_machine.current_state_id;
	switch (state) {
	case SCIC_SDS_REMOTE_NODE_CONTEXT_INVALIDATING_STATE:
		scic_sds_remote_node_context_setup_to_destory(sci_rnc, cb_fn, cb_p);
		return SCI_SUCCESS;
	case SCIC_SDS_REMOTE_NODE_CONTEXT_POSTING_STATE:
	case SCIC_SDS_REMOTE_NODE_CONTEXT_RESUMING_STATE:
	case SCIC_SDS_REMOTE_NODE_CONTEXT_READY_STATE:
	case SCIC_SDS_REMOTE_NODE_CONTEXT_TX_SUSPENDED_STATE:
	case SCIC_SDS_REMOTE_NODE_CONTEXT_TX_RX_SUSPENDED_STATE:
	case SCIC_SDS_REMOTE_NODE_CONTEXT_AWAIT_SUSPENSION_STATE:
		scic_sds_remote_node_context_setup_to_destory(sci_rnc, cb_fn, cb_p);
		sci_base_state_machine_change_state(&sci_rnc->state_machine,
						    SCIC_SDS_REMOTE_NODE_CONTEXT_INVALIDATING_STATE);
		return SCI_SUCCESS;
	case SCIC_SDS_REMOTE_NODE_CONTEXT_INITIAL_STATE:
		dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
			 "%s: invalid state %d\n", __func__, state);
		/* We have decided that the destruct request on the remote node context
		 * can not fail since it is either in the initial/destroyed state or is
		 * can be destroyed.
		 */
		return SCI_SUCCESS;
	default:
		dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
			 "%s: invalid state %d\n", __func__, state);
		return SCI_FAILURE_INVALID_STATE;
	}
}
530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621

enum sci_status scic_sds_remote_node_context_suspend(struct scic_sds_remote_node_context *sci_rnc,
						     u32 suspend_type,
						     scics_sds_remote_node_context_callback cb_fn,
						     void *cb_p)
{
	enum scis_sds_remote_node_context_states state;

	state = sci_rnc->state_machine.current_state_id;
	if (state != SCIC_SDS_REMOTE_NODE_CONTEXT_READY_STATE) {
		dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
			 "%s: invalid state %d\n", __func__, state);
		return SCI_FAILURE_INVALID_STATE;
	}

	sci_rnc->user_callback   = cb_fn;
	sci_rnc->user_cookie     = cb_p;
	sci_rnc->suspension_code = suspend_type;

	if (suspend_type == SCI_SOFTWARE_SUSPENSION) {
		scic_sds_remote_device_post_request(rnc_to_dev(sci_rnc),
						    SCU_CONTEXT_COMMAND_POST_RNC_SUSPEND_TX);
	}

	sci_base_state_machine_change_state(&sci_rnc->state_machine,
					    SCIC_SDS_REMOTE_NODE_CONTEXT_AWAIT_SUSPENSION_STATE);
	return SCI_SUCCESS;
}

enum sci_status scic_sds_remote_node_context_resume(struct scic_sds_remote_node_context *sci_rnc,
						    scics_sds_remote_node_context_callback cb_fn,
						    void *cb_p)
{
	enum scis_sds_remote_node_context_states state;

	state = sci_rnc->state_machine.current_state_id;
	switch (state) {
	case SCIC_SDS_REMOTE_NODE_CONTEXT_INITIAL_STATE:
		if (sci_rnc->remote_node_index == SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX)
			return SCI_FAILURE_INVALID_STATE;

		scic_sds_remote_node_context_setup_to_resume(sci_rnc, cb_fn, cb_p);
		scic_sds_remote_node_context_construct_buffer(sci_rnc);
		sci_base_state_machine_change_state(&sci_rnc->state_machine,
						    SCIC_SDS_REMOTE_NODE_CONTEXT_POSTING_STATE);
		return SCI_SUCCESS;
	case SCIC_SDS_REMOTE_NODE_CONTEXT_POSTING_STATE:
	case SCIC_SDS_REMOTE_NODE_CONTEXT_INVALIDATING_STATE:
	case SCIC_SDS_REMOTE_NODE_CONTEXT_RESUMING_STATE:
		if (sci_rnc->destination_state != SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_READY)
			return SCI_FAILURE_INVALID_STATE;

		sci_rnc->user_callback = cb_fn;
		sci_rnc->user_cookie   = cb_p;
		return SCI_SUCCESS;
	case SCIC_SDS_REMOTE_NODE_CONTEXT_TX_SUSPENDED_STATE: {
		struct scic_sds_remote_device *sci_dev = rnc_to_dev(sci_rnc);
		struct domain_device *dev = sci_dev_to_domain(sci_dev);

		scic_sds_remote_node_context_setup_to_resume(sci_rnc, cb_fn, cb_p);

		/* TODO: consider adding a resume action of NONE, INVALIDATE, WRITE_TLCR */
		if (dev->dev_type == SAS_END_DEV || dev_is_expander(dev))
			sci_base_state_machine_change_state(&sci_rnc->state_machine,
							    SCIC_SDS_REMOTE_NODE_CONTEXT_RESUMING_STATE);
		else if (dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP)) {
			if (sci_dev->is_direct_attached) {
				/* @todo Fix this since I am being silly in writing to the STPTLDARNI register. */
				sci_base_state_machine_change_state(&sci_rnc->state_machine,
					SCIC_SDS_REMOTE_NODE_CONTEXT_RESUMING_STATE);
			} else {
				sci_base_state_machine_change_state(&sci_rnc->state_machine,
					SCIC_SDS_REMOTE_NODE_CONTEXT_INVALIDATING_STATE);
			}
		} else
			return SCI_FAILURE;
		return SCI_SUCCESS;
	}
	case SCIC_SDS_REMOTE_NODE_CONTEXT_TX_RX_SUSPENDED_STATE:
		scic_sds_remote_node_context_setup_to_resume(sci_rnc, cb_fn, cb_p);
		sci_base_state_machine_change_state(&sci_rnc->state_machine,
						    SCIC_SDS_REMOTE_NODE_CONTEXT_RESUMING_STATE);
		return SCI_FAILURE_INVALID_STATE;
	case SCIC_SDS_REMOTE_NODE_CONTEXT_AWAIT_SUSPENSION_STATE:
		scic_sds_remote_node_context_setup_to_resume(sci_rnc, cb_fn, cb_p);
		return SCI_SUCCESS;
	default:
		dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
			 "%s: invalid state %d\n", __func__, state);
		return SCI_FAILURE_INVALID_STATE;
	}
}
622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657

enum sci_status scic_sds_remote_node_context_start_io(struct scic_sds_remote_node_context *sci_rnc,
							     struct scic_sds_request *sci_req)
{
	enum scis_sds_remote_node_context_states state;

	state = sci_rnc->state_machine.current_state_id;
	if (state != SCIC_SDS_REMOTE_NODE_CONTEXT_READY_STATE) {
		dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
			 "%s: invalid state %d\n", __func__, state);
		return SCI_FAILURE_REMOTE_DEVICE_RESET_REQUIRED;
	}
	return SCI_SUCCESS;
}

enum sci_status scic_sds_remote_node_context_start_task(struct scic_sds_remote_node_context *sci_rnc,
							struct scic_sds_request *sci_req)
{
	enum scis_sds_remote_node_context_states state;

	state = sci_rnc->state_machine.current_state_id;
	switch (state) {
	case SCIC_SDS_REMOTE_NODE_CONTEXT_RESUMING_STATE:
	case SCIC_SDS_REMOTE_NODE_CONTEXT_READY_STATE:
	case SCIC_SDS_REMOTE_NODE_CONTEXT_AWAIT_SUSPENSION_STATE:
		return SCI_SUCCESS;
	case SCIC_SDS_REMOTE_NODE_CONTEXT_TX_SUSPENDED_STATE:
	case SCIC_SDS_REMOTE_NODE_CONTEXT_TX_RX_SUSPENDED_STATE:
		scic_sds_remote_node_context_resume(sci_rnc, NULL, NULL);
		return SCI_SUCCESS;
	default:
		dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
			 "%s: invalid state %d\n", __func__, state);
		return SCI_FAILURE_INVALID_STATE;
	}
}