remote_node_context.c 33.0 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 180
	}
}

/**
 *
181 182
 * @sci_rnc:
 * @callback:
183 184 185 186 187 188
 * @callback_parameter:
 *
 * This method will setup the remote node context object so it will transistion
 * to its final state. none
 */
static void scic_sds_remote_node_context_setup_to_destory(
189 190
	struct scic_sds_remote_node_context *sci_rnc,
	scics_sds_remote_node_context_callback callback,
191 192
	void *callback_parameter)
{
193 194 195
	sci_rnc->destination_state = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_FINAL;
	sci_rnc->user_callback     = callback;
	sci_rnc->user_cookie       = callback_parameter;
196 197 198 199
}

/**
 *
200 201
 * @sci_rnc:
 * @callback:
202 203 204 205 206
 *
 * This method will continue to resume a remote node context.  This is used in
 * the states where a resume is requested while a resume is in progress.
 */
static enum sci_status scic_sds_remote_node_context_continue_to_resume_handler(
207 208
	struct scic_sds_remote_node_context *sci_rnc,
	scics_sds_remote_node_context_callback callback,
209 210
	void *callback_parameter)
{
211 212 213
	if (sci_rnc->destination_state == SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_READY) {
		sci_rnc->user_callback = callback;
		sci_rnc->user_cookie   = callback_parameter;
214 215 216 217 218 219 220 221 222 223

		return SCI_SUCCESS;
	}

	return SCI_FAILURE_INVALID_STATE;
}

/* --------------------------------------------------------------------------- */

static enum sci_status scic_sds_remote_node_context_default_destruct_handler(
224 225
	struct scic_sds_remote_node_context *sci_rnc,
	scics_sds_remote_node_context_callback callback,
226 227
	void *callback_parameter)
{
228
	dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
229 230 231
		 "%s: SCIC Remote Node Context 0x%p requested to stop while "
		 "in unexpected state %d\n",
		 __func__,
232 233
		 sci_rnc,
		 sci_base_state_machine_get_state(&sci_rnc->state_machine));
234 235 236 237 238 239 240 241

	/*
	 * 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;
}

static enum sci_status scic_sds_remote_node_context_default_suspend_handler(
242
	struct scic_sds_remote_node_context *sci_rnc,
243
	u32 suspend_type,
244
	scics_sds_remote_node_context_callback callback,
245 246
	void *callback_parameter)
{
247
	dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
248 249 250
		 "%s: SCIC Remote Node Context 0x%p requested to suspend "
		 "while in wrong state %d\n",
		 __func__,
251 252
		 sci_rnc,
		 sci_base_state_machine_get_state(&sci_rnc->state_machine));
253 254 255 256 257

	return SCI_FAILURE_INVALID_STATE;
}

static enum sci_status scic_sds_remote_node_context_default_resume_handler(
258 259
	struct scic_sds_remote_node_context *sci_rnc,
	scics_sds_remote_node_context_callback callback,
260 261
	void *callback_parameter)
{
262
	dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
263 264 265
		 "%s: SCIC Remote Node Context 0x%p requested to resume "
		 "while in wrong state %d\n",
		 __func__,
266 267
		 sci_rnc,
		 sci_base_state_machine_get_state(&sci_rnc->state_machine));
268 269 270 271 272

	return SCI_FAILURE_INVALID_STATE;
}

static enum sci_status scic_sds_remote_node_context_default_start_io_handler(
273 274
	struct scic_sds_remote_node_context *sci_rnc,
	struct scic_sds_request *sci_req)
275
{
276
	dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
277 278 279
		 "%s: SCIC Remote Node Context 0x%p requested to start io "
		 "0x%p while in wrong state %d\n",
		 __func__,
280 281 282
		 sci_rnc,
		 sci_req,
		 sci_base_state_machine_get_state(&sci_rnc->state_machine));
283 284 285 286 287

	return SCI_FAILURE_REMOTE_DEVICE_RESET_REQUIRED;
}

static enum sci_status scic_sds_remote_node_context_default_start_task_handler(
288 289
	struct scic_sds_remote_node_context *sci_rnc,
	struct scic_sds_request *sci_req)
290
{
291
	dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
292 293 294
		 "%s: SCIC Remote Node Context 0x%p requested to start "
		 "task 0x%p while in wrong state %d\n",
		 __func__,
295 296 297
		 sci_rnc,
		 sci_req,
		 sci_base_state_machine_get_state(&sci_rnc->state_machine));
298 299 300 301 302 303

	return SCI_FAILURE;
}

/**
 *
304 305
 * @sci_rnc: The rnc for which the task request is targeted.
 * @sci_req: The request which is going to be started.
306 307 308 309 310 311
 *
 * This method determines if the task request can be started by the SCU
 * hardware. When the RNC is in the ready state any task can be started.
 * enum sci_status SCI_SUCCESS
 */
static enum sci_status scic_sds_remote_node_context_success_start_task_handler(
312 313
	struct scic_sds_remote_node_context *sci_rnc,
	struct scic_sds_request *sci_req)
314 315 316 317 318 319
{
	return SCI_SUCCESS;
}

/**
 *
320 321
 * @sci_rnc:
 * @callback:
322 323 324 325 326 327 328 329
 * @callback_parameter:
 *
 * This method handles destruct calls from the various state handlers.  The
 * remote node context can be requested to destroy from any state. If there was
 * a user callback it is always replaced with the request to destroy user
 * callback. enum sci_status
 */
static enum sci_status scic_sds_remote_node_context_general_destruct_handler(
330 331
	struct scic_sds_remote_node_context *sci_rnc,
	scics_sds_remote_node_context_callback callback,
332 333 334
	void *callback_parameter)
{
	scic_sds_remote_node_context_setup_to_destory(
335
		sci_rnc, callback, callback_parameter
336 337 338
		);

	sci_base_state_machine_change_state(
339
		&sci_rnc->state_machine,
340 341 342 343 344 345 346 347 348
		SCIC_SDS_REMOTE_NODE_CONTEXT_INVALIDATING_STATE
		);

	return SCI_SUCCESS;
}

/* --------------------------------------------------------------------------- */

static enum sci_status scic_sds_remote_node_context_initial_state_resume_handler(
349 350
	struct scic_sds_remote_node_context *sci_rnc,
	scics_sds_remote_node_context_callback callback,
351 352
	void *callback_parameter)
{
353
	if (sci_rnc->remote_node_index != SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX) {
354
		scic_sds_remote_node_context_setup_to_resume(
355
			sci_rnc, callback, callback_parameter
356 357
			);

358
		scic_sds_remote_node_context_construct_buffer(sci_rnc);
359 360

		sci_base_state_machine_change_state(
361
			&sci_rnc->state_machine,
362 363 364 365 366 367 368 369 370 371
			SCIC_SDS_REMOTE_NODE_CONTEXT_POSTING_STATE
			);

		return SCI_SUCCESS;
	}

	return SCI_FAILURE_INVALID_STATE;
}

static enum sci_status scic_sds_remote_node_context_invalidating_state_destruct_handler(
372 373
	struct scic_sds_remote_node_context *sci_rnc,
	scics_sds_remote_node_context_callback callback,
374 375 376
	void *callback_parameter)
{
	scic_sds_remote_node_context_setup_to_destory(
377
		sci_rnc, callback, callback_parameter
378 379 380 381 382 383 384
		);

	return SCI_SUCCESS;
}

/**
 *
385 386
 * @sci_rnc: The remote node context object being suspended.
 * @callback: The callback when the suspension is complete.
387 388 389 390 391 392
 * @callback_parameter: The parameter that is to be passed into the callback.
 *
 * This method will handle the suspend requests from the ready state.
 * SCI_SUCCESS
 */
static enum sci_status scic_sds_remote_node_context_ready_state_suspend_handler(
393
	struct scic_sds_remote_node_context *sci_rnc,
394
	u32 suspend_type,
395
	scics_sds_remote_node_context_callback callback,
396 397
	void *callback_parameter)
{
398 399 400
	sci_rnc->user_callback   = callback;
	sci_rnc->user_cookie     = callback_parameter;
	sci_rnc->suspension_code = suspend_type;
401 402

	if (suspend_type == SCI_SOFTWARE_SUSPENSION) {
403 404
		scic_sds_remote_device_post_request(rnc_to_dev(sci_rnc),
						    SCU_CONTEXT_COMMAND_POST_RNC_SUSPEND_TX);
405 406 407
	}

	sci_base_state_machine_change_state(
408
		&sci_rnc->state_machine,
409 410 411 412 413 414 415 416
		SCIC_SDS_REMOTE_NODE_CONTEXT_AWAIT_SUSPENSION_STATE
		);

	return SCI_SUCCESS;
}

/**
 *
417 418
 * @sci_rnc: The rnc for which the io request is targeted.
 * @sci_req: The request which is going to be started.
419 420 421 422 423 424
 *
 * This method determines if the io request can be started by the SCU hardware.
 * When the RNC is in the ready state any io request can be started. enum sci_status
 * SCI_SUCCESS
 */
static enum sci_status scic_sds_remote_node_context_ready_state_start_io_handler(
425 426
	struct scic_sds_remote_node_context *sci_rnc,
	struct scic_sds_request *sci_req)
427 428 429 430 431
{
	return SCI_SUCCESS;
}

static enum sci_status scic_sds_remote_node_context_tx_suspended_state_resume_handler(
432 433
	struct scic_sds_remote_node_context *sci_rnc,
	scics_sds_remote_node_context_callback callback,
434 435
	void *callback_parameter)
{
436 437 438
	struct scic_sds_remote_device *sci_dev = rnc_to_dev(sci_rnc);
	struct domain_device *dev = sci_dev_to_domain(sci_dev);
	enum sci_status status = SCI_SUCCESS;
439

440 441
	scic_sds_remote_node_context_setup_to_resume(sci_rnc, callback,
						     callback_parameter);
442 443

	/* TODO: consider adding a resume action of NONE, INVALIDATE, WRITE_TLCR */
444 445 446 447 448
	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) {
449 450
			/* @todo Fix this since I am being silly in writing to the STPTLDARNI register. */
			sci_base_state_machine_change_state(
451
				&sci_rnc->state_machine,
452
				SCIC_SDS_REMOTE_NODE_CONTEXT_RESUMING_STATE);
453 454
		} else {
			sci_base_state_machine_change_state(
455
				&sci_rnc->state_machine,
456
				SCIC_SDS_REMOTE_NODE_CONTEXT_INVALIDATING_STATE);
457
		}
458
	} else
459 460 461 462 463 464 465
		status = SCI_FAILURE;

	return status;
}

/**
 *
466 467
 * @sci_rnc: The remote node context which is to receive the task request.
 * @sci_req: The task request to be transmitted to to the remote target
468 469 470 471 472 473 474 475
 *    device.
 *
 * This method will report a success or failure attempt to start a new task
 * request to the hardware.  Since all task requests are sent on the high
 * priority queue they can be sent when the RCN is in a TX suspend state.
 * enum sci_status SCI_SUCCESS
 */
static enum sci_status scic_sds_remote_node_context_suspended_start_task_handler(
476 477
	struct scic_sds_remote_node_context *sci_rnc,
	struct scic_sds_request *sci_req)
478
{
479
	scic_sds_remote_node_context_resume(sci_rnc, NULL, NULL);
480 481 482 483 484 485 486

	return SCI_SUCCESS;
}

/* --------------------------------------------------------------------------- */

static enum sci_status scic_sds_remote_node_context_tx_rx_suspended_state_resume_handler(
487 488
	struct scic_sds_remote_node_context *sci_rnc,
	scics_sds_remote_node_context_callback callback,
489 490 491
	void *callback_parameter)
{
	scic_sds_remote_node_context_setup_to_resume(
492
		sci_rnc, callback, callback_parameter
493 494 495
		);

	sci_base_state_machine_change_state(
496
		&sci_rnc->state_machine,
497 498 499 500 501 502 503 504 505 506 507 508 509 510
		SCIC_SDS_REMOTE_NODE_CONTEXT_RESUMING_STATE
		);

	return SCI_FAILURE_INVALID_STATE;
}

/* --------------------------------------------------------------------------- */

/**
 *
 *
 *
 */
static enum sci_status scic_sds_remote_node_context_await_suspension_state_resume_handler(
511 512
	struct scic_sds_remote_node_context *sci_rnc,
	scics_sds_remote_node_context_callback callback,
513 514 515
	void *callback_parameter)
{
	scic_sds_remote_node_context_setup_to_resume(
516
		sci_rnc, callback, callback_parameter
517 518 519 520 521 522 523
		);

	return SCI_SUCCESS;
}

/**
 *
524 525
 * @sci_rnc: The remote node context which is to receive the task request.
 * @sci_req: The task request to be transmitted to to the remote target
526 527 528 529 530 531 532 533
 *    device.
 *
 * This method will report a success or failure attempt to start a new task
 * request to the hardware.  Since all task requests are sent on the high
 * priority queue they can be sent when the RCN is in a TX suspend state.
 * enum sci_status SCI_SUCCESS
 */
static enum sci_status scic_sds_remote_node_context_await_suspension_state_start_task_handler(
534 535
	struct scic_sds_remote_node_context *sci_rnc,
	struct scic_sds_request *sci_req)
536 537 538 539
{
	return SCI_SUCCESS;
}

D
Dan Williams 已提交
540
static struct scic_sds_remote_node_context_handlers scic_sds_remote_node_context_state_handler_table[] = {
541 542 543 544 545 546
	[SCIC_SDS_REMOTE_NODE_CONTEXT_INITIAL_STATE] = {
		.destruct_handler	= scic_sds_remote_node_context_default_destruct_handler,
		.suspend_handler	= scic_sds_remote_node_context_default_suspend_handler,
		.resume_handler		= scic_sds_remote_node_context_initial_state_resume_handler,
		.start_io_handler	= scic_sds_remote_node_context_default_start_io_handler,
		.start_task_handler	= scic_sds_remote_node_context_default_start_task_handler,
547
	},
548 549 550 551 552 553
	[SCIC_SDS_REMOTE_NODE_CONTEXT_POSTING_STATE] = {
		.destruct_handler	= scic_sds_remote_node_context_general_destruct_handler,
		.suspend_handler	= scic_sds_remote_node_context_default_suspend_handler,
		.resume_handler		= scic_sds_remote_node_context_continue_to_resume_handler,
		.start_io_handler	= scic_sds_remote_node_context_default_start_io_handler,
		.start_task_handler	= scic_sds_remote_node_context_default_start_task_handler,
554
	},
555 556 557 558 559 560
	[SCIC_SDS_REMOTE_NODE_CONTEXT_INVALIDATING_STATE] = {
		.destruct_handler	= scic_sds_remote_node_context_invalidating_state_destruct_handler,
		.suspend_handler	= scic_sds_remote_node_context_default_suspend_handler,
		.resume_handler		= scic_sds_remote_node_context_continue_to_resume_handler,
		.start_io_handler	= scic_sds_remote_node_context_default_start_io_handler,
		.start_task_handler	= scic_sds_remote_node_context_default_start_task_handler,
561
	},
562 563 564 565 566 567
	[SCIC_SDS_REMOTE_NODE_CONTEXT_RESUMING_STATE] = {
		.destruct_handler	= scic_sds_remote_node_context_general_destruct_handler,
		.suspend_handler	= scic_sds_remote_node_context_default_suspend_handler,
		.resume_handler		= scic_sds_remote_node_context_continue_to_resume_handler,
		.start_io_handler	= scic_sds_remote_node_context_default_start_io_handler,
		.start_task_handler	= scic_sds_remote_node_context_success_start_task_handler,
568
	},
569 570 571 572 573 574
	[SCIC_SDS_REMOTE_NODE_CONTEXT_READY_STATE] = {
		.destruct_handler	= scic_sds_remote_node_context_general_destruct_handler,
		.suspend_handler	= scic_sds_remote_node_context_ready_state_suspend_handler,
		.resume_handler		= scic_sds_remote_node_context_default_resume_handler,
		.start_io_handler	= scic_sds_remote_node_context_ready_state_start_io_handler,
		.start_task_handler	= scic_sds_remote_node_context_success_start_task_handler,
575
	},
576 577 578 579 580 581
	[SCIC_SDS_REMOTE_NODE_CONTEXT_TX_SUSPENDED_STATE] = {
		.destruct_handler	= scic_sds_remote_node_context_general_destruct_handler,
		.suspend_handler	= scic_sds_remote_node_context_default_suspend_handler,
		.resume_handler		= scic_sds_remote_node_context_tx_suspended_state_resume_handler,
		.start_io_handler	= scic_sds_remote_node_context_default_start_io_handler,
		.start_task_handler	= scic_sds_remote_node_context_suspended_start_task_handler,
582
	},
583 584 585 586 587 588
	[SCIC_SDS_REMOTE_NODE_CONTEXT_TX_RX_SUSPENDED_STATE] = {
		.destruct_handler	= scic_sds_remote_node_context_general_destruct_handler,
		.suspend_handler	= scic_sds_remote_node_context_default_suspend_handler,
		.resume_handler		= scic_sds_remote_node_context_tx_rx_suspended_state_resume_handler,
		.start_io_handler	= scic_sds_remote_node_context_default_start_io_handler,
		.start_task_handler	= scic_sds_remote_node_context_suspended_start_task_handler,
589
	},
590 591 592 593 594 595
	[SCIC_SDS_REMOTE_NODE_CONTEXT_AWAIT_SUSPENSION_STATE] = {
		.destruct_handler	= scic_sds_remote_node_context_general_destruct_handler,
		.suspend_handler	= scic_sds_remote_node_context_default_suspend_handler,
		.resume_handler		= scic_sds_remote_node_context_await_suspension_state_resume_handler,
		.start_io_handler	= scic_sds_remote_node_context_default_start_io_handler,
		.start_task_handler	= scic_sds_remote_node_context_await_suspension_state_start_task_handler,
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 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639
	}
};

/*
 * *****************************************************************************
 * * REMOTE NODE CONTEXT PRIVATE METHODS
 * ***************************************************************************** */

/**
 *
 *
 * 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;
	}
}

/**
 *
 *
 * This method will continue the remote node context state machine by
 * requesting to resume the remote node context state machine from its current
 * state.
 */
static void scic_sds_remote_node_context_continue_state_transitions(
	struct scic_sds_remote_node_context *rnc)
{
	if (rnc->destination_state == SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_READY) {
		rnc->state_handlers->resume_handler(
			rnc, rnc->user_callback, rnc->user_cookie
			);
	}
}

/**
 *
640
 * @sci_rnc: The remote node context object that is to be validated.
641 642 643 644 645
 *
 * 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(
646
	struct scic_sds_remote_node_context *sci_rnc)
647
{
648
	struct scic_sds_remote_device *sci_dev = rnc_to_dev(sci_rnc);
649
	struct domain_device *dev = sci_dev_to_domain(sci_dev);
650 651 652
	union scu_remote_node_context *rnc_buffer;

	rnc_buffer = scic_sds_controller_get_remote_node_context_buffer(
653
		scic_sds_remote_device_get_controller(sci_dev),
654
		sci_rnc->remote_node_index
655 656 657 658
		);

	rnc_buffer->ssp.is_valid = true;

659
	if (!sci_dev->is_direct_attached &&
660
	    (dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP))) {
661 662
		scic_sds_remote_device_post_request(sci_dev,
						    SCU_CONTEXT_COMMAND_POST_RNC_96);
663
	} else {
664
		scic_sds_remote_device_post_request(sci_dev, SCU_CONTEXT_COMMAND_POST_RNC_32);
665

666 667 668
		if (sci_dev->is_direct_attached) {
			scic_sds_port_setup_transports(sci_dev->owning_port,
						       sci_rnc->remote_node_index);
669 670 671 672 673 674
		}
	}
}

/**
 *
675
 * @sci_rnc: The remote node context object that is to be invalidated.
676 677 678 679
 *
 * This method will update the RNC buffer and post the invalidate request. none
 */
static void scic_sds_remote_node_context_invalidate_context_buffer(
680
	struct scic_sds_remote_node_context *sci_rnc)
681 682 683 684
{
	union scu_remote_node_context *rnc_buffer;

	rnc_buffer = scic_sds_controller_get_remote_node_context_buffer(
685 686
		scic_sds_remote_device_get_controller(rnc_to_dev(sci_rnc)),
		sci_rnc->remote_node_index);
687 688 689

	rnc_buffer->ssp.is_valid = false;

690 691
	scic_sds_remote_device_post_request(rnc_to_dev(sci_rnc),
					    SCU_CONTEXT_COMMAND_POST_RNC_INVALIDATE);
692 693 694 695 696 697 698 699 700 701 702 703
}

/*
 * *****************************************************************************
 * * REMOTE NODE CONTEXT STATE ENTER AND EXIT METHODS
 * ***************************************************************************** */

/**
 *
 *
 *
 */
704
static void scic_sds_remote_node_context_initial_state_enter(void *object)
705
{
706
	struct scic_sds_remote_node_context *rnc = object;
707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731

	SET_STATE_HANDLER(
		rnc,
		scic_sds_remote_node_context_state_handler_table,
		SCIC_SDS_REMOTE_NODE_CONTEXT_INITIAL_STATE
		);

	/*
	 * Check to see if we have gotten back to the initial state because someone
	 * requested to destroy the remote node context object. */
	if (
		rnc->state_machine.previous_state_id
		== SCIC_SDS_REMOTE_NODE_CONTEXT_INVALIDATING_STATE
		) {
		rnc->destination_state = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_UNSPECIFIED;

		scic_sds_remote_node_context_notify_user(rnc);
	}
}

/**
 *
 *
 *
 */
732
static void scic_sds_remote_node_context_posting_state_enter(void *object)
733
{
734
	struct scic_sds_remote_node_context *sci_rnc = object;
735 736

	SET_STATE_HANDLER(
737
		sci_rnc,
738 739 740 741
		scic_sds_remote_node_context_state_handler_table,
		SCIC_SDS_REMOTE_NODE_CONTEXT_POSTING_STATE
		);

742
	scic_sds_remote_node_context_validate_context_buffer(sci_rnc);
743 744 745 746 747 748 749
}

/**
 *
 *
 *
 */
750
static void scic_sds_remote_node_context_invalidating_state_enter(void *object)
751
{
752
	struct scic_sds_remote_node_context *rnc = object;
753 754 755 756 757 758 759 760 761 762 763 764 765 766 767

	SET_STATE_HANDLER(
		rnc,
		scic_sds_remote_node_context_state_handler_table,
		SCIC_SDS_REMOTE_NODE_CONTEXT_INVALIDATING_STATE
		);

	scic_sds_remote_node_context_invalidate_context_buffer(rnc);
}

/**
 *
 *
 *
 */
768
static void scic_sds_remote_node_context_resuming_state_enter(void *object)
769
{
770
	struct scic_sds_remote_node_context *rnc = object;
771
	struct scic_sds_remote_device *sci_dev;
772
	struct domain_device *dev;
773

774
	sci_dev = rnc_to_dev(rnc);
775
	dev = sci_dev_to_domain(sci_dev);
776 777 778 779 780 781 782

	SET_STATE_HANDLER(
		rnc,
		scic_sds_remote_node_context_state_handler_table,
		SCIC_SDS_REMOTE_NODE_CONTEXT_RESUMING_STATE
		);

783 784 785 786 787 788
	/*
	 * 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
	 */
789 790
	if ((dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP)) &&
	    sci_dev->is_direct_attached)
791 792
		scic_sds_port_setup_transports(sci_dev->owning_port,
					       rnc->remote_node_index);
793

794
	scic_sds_remote_device_post_request(sci_dev, SCU_CONTEXT_COMMAND_POST_RNC_RESUME);
795 796 797 798 799 800 801
}

/**
 *
 *
 *
 */
802
static void scic_sds_remote_node_context_ready_state_enter(void *object)
803
{
804
	struct scic_sds_remote_node_context *rnc = object;
805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823

	SET_STATE_HANDLER(
		rnc,
		scic_sds_remote_node_context_state_handler_table,
		SCIC_SDS_REMOTE_NODE_CONTEXT_READY_STATE
		);

	rnc->destination_state = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_UNSPECIFIED;

	if (rnc->user_callback != NULL) {
		scic_sds_remote_node_context_notify_user(rnc);
	}
}

/**
 *
 *
 *
 */
824
static void scic_sds_remote_node_context_tx_suspended_state_enter(void *object)
825
{
826
	struct scic_sds_remote_node_context *rnc = object;
827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842

	SET_STATE_HANDLER(
		rnc,
		scic_sds_remote_node_context_state_handler_table,
		SCIC_SDS_REMOTE_NODE_CONTEXT_TX_SUSPENDED_STATE
		);

	scic_sds_remote_node_context_continue_state_transitions(rnc);
}

/**
 *
 *
 *
 */
static void scic_sds_remote_node_context_tx_rx_suspended_state_enter(
843
		void *object)
844
{
845
	struct scic_sds_remote_node_context *rnc = object;
846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861

	SET_STATE_HANDLER(
		rnc,
		scic_sds_remote_node_context_state_handler_table,
		SCIC_SDS_REMOTE_NODE_CONTEXT_TX_RX_SUSPENDED_STATE
		);

	scic_sds_remote_node_context_continue_state_transitions(rnc);
}

/**
 *
 *
 *
 */
static void scic_sds_remote_node_context_await_suspension_state_enter(
862
	void *object)
863
{
864
	struct scic_sds_remote_node_context *rnc = object;
865 866 867 868 869 870 871 872 873 874

	SET_STATE_HANDLER(
		rnc,
		scic_sds_remote_node_context_state_handler_table,
		SCIC_SDS_REMOTE_NODE_CONTEXT_AWAIT_SUSPENSION_STATE
		);
}

/* --------------------------------------------------------------------------- */

D
Dan Williams 已提交
875
static const struct sci_base_state scic_sds_remote_node_context_state_table[] = {
876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901
	[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,
	},
	[SCIC_SDS_REMOTE_NODE_CONTEXT_AWAIT_SUSPENSION_STATE] = {
		.enter_state = scic_sds_remote_node_context_await_suspension_state_enter,
	},
};

902 903
void scic_sds_remote_node_context_construct(struct scic_sds_remote_node_context *rnc,
					    u16 remote_node_index)
D
Dan Williams 已提交
904 905 906 907 908 909 910 911
{
	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,
912
		rnc,
D
Dan Williams 已提交
913 914 915 916 917 918
		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 已提交
919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025

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;

}