Blame view

drivers/scsi/isci/host.c 83 KB
6f231dda6   Dan Williams   isci: Intel(R) C6...
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
  /*
   * 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.
   */
ac668c697   Dan Williams   isci: cleanup/opt...
55
  #include <linux/circ_buf.h>
cc9203bf3   Dan Williams   isci: move core/c...
56
57
58
  #include <linux/device.h>
  #include <scsi/sas.h>
  #include "host.h"
6f231dda6   Dan Williams   isci: Intel(R) C6...
59
  #include "isci.h"
6f231dda6   Dan Williams   isci: Intel(R) C6...
60
  #include "port.h"
6f231dda6   Dan Williams   isci: Intel(R) C6...
61
  #include "host.h"
d044af17a   Dan Williams   isci: Add support...
62
  #include "probe_roms.h"
cc9203bf3   Dan Williams   isci: move core/c...
63
64
  #include "remote_device.h"
  #include "request.h"
cc9203bf3   Dan Williams   isci: move core/c...
65
66
  #include "scu_completion_codes.h"
  #include "scu_event_codes.h"
63a3a15fb   Dan Williams   isci: uplevel reg...
67
  #include "registers.h"
cc9203bf3   Dan Williams   isci: move core/c...
68
69
  #include "scu_remote_node_context.h"
  #include "scu_task_context.h"
6f231dda6   Dan Williams   isci: Intel(R) C6...
70

cc9203bf3   Dan Williams   isci: move core/c...
71
  #define SCU_CONTEXT_RAM_INIT_STALL_TIME      200
7c78da317   Dan Williams   isci: remove 'min...
72
  #define smu_max_ports(dcc_value) \
cc9203bf3   Dan Williams   isci: move core/c...
73
74
75
76
  	(\
  		(((dcc_value) & SMU_DEVICE_CONTEXT_CAPACITY_MAX_LP_MASK) \
  		 >> SMU_DEVICE_CONTEXT_CAPACITY_MAX_LP_SHIFT) + 1 \
  	)
7c78da317   Dan Williams   isci: remove 'min...
77
  #define smu_max_task_contexts(dcc_value)	\
cc9203bf3   Dan Williams   isci: move core/c...
78
79
80
81
  	(\
  		(((dcc_value) & SMU_DEVICE_CONTEXT_CAPACITY_MAX_TC_MASK) \
  		 >> SMU_DEVICE_CONTEXT_CAPACITY_MAX_TC_SHIFT) + 1 \
  	)
7c78da317   Dan Williams   isci: remove 'min...
82
  #define smu_max_rncs(dcc_value) \
cc9203bf3   Dan Williams   isci: move core/c...
83
84
85
86
  	(\
  		(((dcc_value) & SMU_DEVICE_CONTEXT_CAPACITY_MAX_RNC_MASK) \
  		 >> SMU_DEVICE_CONTEXT_CAPACITY_MAX_RNC_SHIFT) + 1 \
  	)
cc9203bf3   Dan Williams   isci: move core/c...
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
  #define SCIC_SDS_CONTROLLER_PHY_START_TIMEOUT      100
  
  /**
   *
   *
   * The number of milliseconds to wait while a given phy is consuming power
   * before allowing another set of phys to consume power. Ultimately, this will
   * be specified by OEM parameter.
   */
  #define SCIC_SDS_CONTROLLER_POWER_CONTROL_INTERVAL 500
  
  /**
   * NORMALIZE_PUT_POINTER() -
   *
   * This macro will normalize the completion queue put pointer so its value can
   * be used as an array inde
   */
  #define NORMALIZE_PUT_POINTER(x) \
  	((x) & SMU_COMPLETION_QUEUE_PUT_POINTER_MASK)
  
  
  /**
   * NORMALIZE_EVENT_POINTER() -
   *
   * This macro will normalize the completion queue event entry so its value can
   * be used as an index.
   */
  #define NORMALIZE_EVENT_POINTER(x) \
  	(\
  		((x) & SMU_COMPLETION_QUEUE_GET_EVENT_POINTER_MASK) \
  		>> SMU_COMPLETION_QUEUE_GET_EVENT_POINTER_SHIFT	\
  	)
  
  /**
cc9203bf3   Dan Williams   isci: move core/c...
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
   * NORMALIZE_GET_POINTER() -
   *
   * This macro will normalize the completion queue get pointer so its value can
   * be used as an index into an array
   */
  #define NORMALIZE_GET_POINTER(x) \
  	((x) & SMU_COMPLETION_QUEUE_GET_POINTER_MASK)
  
  /**
   * NORMALIZE_GET_POINTER_CYCLE_BIT() -
   *
   * This macro will normalize the completion queue cycle pointer so it matches
   * the completion queue cycle bit
   */
  #define NORMALIZE_GET_POINTER_CYCLE_BIT(x) \
  	((SMU_CQGR_CYCLE_BIT & (x)) << (31 - SMU_COMPLETION_QUEUE_GET_CYCLE_BIT_SHIFT))
  
  /**
   * COMPLETION_QUEUE_CYCLE_BIT() -
   *
   * This macro will return the cycle bit of the completion queue entry
   */
  #define COMPLETION_QUEUE_CYCLE_BIT(x) ((x) & 0x80000000)
12ef65444   Edmund Nadolski   isci: additional ...
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
  /* Init the state machine and call the state entry function (if any) */
  void sci_init_sm(struct sci_base_state_machine *sm,
  		 const struct sci_base_state *state_table, u32 initial_state)
  {
  	sci_state_transition_t handler;
  
  	sm->initial_state_id    = initial_state;
  	sm->previous_state_id   = initial_state;
  	sm->current_state_id    = initial_state;
  	sm->state_table         = state_table;
  
  	handler = sm->state_table[initial_state].enter_state;
  	if (handler)
  		handler(sm);
  }
  
  /* Call the state exit fn, update the current state, call the state entry fn */
  void sci_change_state(struct sci_base_state_machine *sm, u32 next_state)
  {
  	sci_state_transition_t handler;
  
  	handler = sm->state_table[sm->current_state_id].exit_state;
  	if (handler)
  		handler(sm);
  
  	sm->previous_state_id = sm->current_state_id;
  	sm->current_state_id = next_state;
  
  	handler = sm->state_table[sm->current_state_id].enter_state;
  	if (handler)
  		handler(sm);
  }
89a7301f2   Dan Williams   isci: retire scic...
176
  static bool sci_controller_completion_queue_has_entries(struct isci_host *ihost)
cc9203bf3   Dan Williams   isci: move core/c...
177
  {
d9dcb4ba7   Dan Williams   isci: unify isci_...
178
  	u32 get_value = ihost->completion_queue_get;
cc9203bf3   Dan Williams   isci: move core/c...
179
180
181
  	u32 get_index = get_value & SMU_COMPLETION_QUEUE_GET_POINTER_MASK;
  
  	if (NORMALIZE_GET_POINTER_CYCLE_BIT(get_value) ==
d9dcb4ba7   Dan Williams   isci: unify isci_...
182
  	    COMPLETION_QUEUE_CYCLE_BIT(ihost->completion_queue[get_index]))
cc9203bf3   Dan Williams   isci: move core/c...
183
184
185
186
  		return true;
  
  	return false;
  }
89a7301f2   Dan Williams   isci: retire scic...
187
  static bool sci_controller_isr(struct isci_host *ihost)
cc9203bf3   Dan Williams   isci: move core/c...
188
  {
89a7301f2   Dan Williams   isci: retire scic...
189
  	if (sci_controller_completion_queue_has_entries(ihost)) {
cc9203bf3   Dan Williams   isci: move core/c...
190
191
192
193
194
  		return true;
  	} else {
  		/*
  		 * we have a spurious interrupt it could be that we have already
  		 * emptied the completion queue from a previous interrupt */
d9dcb4ba7   Dan Williams   isci: unify isci_...
195
  		writel(SMU_ISR_COMPLETION, &ihost->smu_registers->interrupt_status);
cc9203bf3   Dan Williams   isci: move core/c...
196
197
198
199
200
201
  
  		/*
  		 * There is a race in the hardware that could cause us not to be notified
  		 * of an interrupt completion if we do not take this step.  We will mask
  		 * then unmask the interrupts so if there is another interrupt pending
  		 * the clearing of the interrupt source we get the next interrupt message. */
d9dcb4ba7   Dan Williams   isci: unify isci_...
202
203
  		writel(0xFF000000, &ihost->smu_registers->interrupt_mask);
  		writel(0, &ihost->smu_registers->interrupt_mask);
cc9203bf3   Dan Williams   isci: move core/c...
204
205
206
207
  	}
  
  	return false;
  }
c7ef4031f   Dan Williams   isci: bypass scic...
208
  irqreturn_t isci_msix_isr(int vec, void *data)
6f231dda6   Dan Williams   isci: Intel(R) C6...
209
  {
c7ef4031f   Dan Williams   isci: bypass scic...
210
  	struct isci_host *ihost = data;
c7ef4031f   Dan Williams   isci: bypass scic...
211

89a7301f2   Dan Williams   isci: retire scic...
212
  	if (sci_controller_isr(ihost))
0cf89d1d2   Dan Williams   isci: cleanup "st...
213
  		tasklet_schedule(&ihost->completion_tasklet);
6f231dda6   Dan Williams   isci: Intel(R) C6...
214

c7ef4031f   Dan Williams   isci: bypass scic...
215
  	return IRQ_HANDLED;
6f231dda6   Dan Williams   isci: Intel(R) C6...
216
  }
89a7301f2   Dan Williams   isci: retire scic...
217
  static bool sci_controller_error_isr(struct isci_host *ihost)
cc9203bf3   Dan Williams   isci: move core/c...
218
219
220
221
  {
  	u32 interrupt_status;
  
  	interrupt_status =
d9dcb4ba7   Dan Williams   isci: unify isci_...
222
  		readl(&ihost->smu_registers->interrupt_status);
cc9203bf3   Dan Williams   isci: move core/c...
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
  	interrupt_status &= (SMU_ISR_QUEUE_ERROR | SMU_ISR_QUEUE_SUSPEND);
  
  	if (interrupt_status != 0) {
  		/*
  		 * There is an error interrupt pending so let it through and handle
  		 * in the callback */
  		return true;
  	}
  
  	/*
  	 * There is a race in the hardware that could cause us not to be notified
  	 * of an interrupt completion if we do not take this step.  We will mask
  	 * then unmask the error interrupts so if there was another interrupt
  	 * pending we will be notified.
  	 * Could we write the value of (SMU_ISR_QUEUE_ERROR | SMU_ISR_QUEUE_SUSPEND)? */
d9dcb4ba7   Dan Williams   isci: unify isci_...
238
239
  	writel(0xff, &ihost->smu_registers->interrupt_mask);
  	writel(0, &ihost->smu_registers->interrupt_mask);
cc9203bf3   Dan Williams   isci: move core/c...
240
241
242
  
  	return false;
  }
89a7301f2   Dan Williams   isci: retire scic...
243
  static void sci_controller_task_completion(struct isci_host *ihost, u32 ent)
cc9203bf3   Dan Williams   isci: move core/c...
244
  {
89a7301f2   Dan Williams   isci: retire scic...
245
  	u32 index = SCU_GET_COMPLETION_INDEX(ent);
db0562509   Dan Williams   isci: preallocate...
246
  	struct isci_request *ireq = ihost->reqs[index];
cc9203bf3   Dan Williams   isci: move core/c...
247
248
  
  	/* Make sure that we really want to process this IO request */
db0562509   Dan Williams   isci: preallocate...
249
  	if (test_bit(IREQ_ACTIVE, &ireq->flags) &&
5076a1a97   Dan Williams   isci: unify isci_...
250
  	    ireq->io_tag != SCI_CONTROLLER_INVALID_IO_TAG &&
d9dcb4ba7   Dan Williams   isci: unify isci_...
251
  	    ISCI_TAG_SEQ(ireq->io_tag) == ihost->io_request_sequence[index])
89a7301f2   Dan Williams   isci: retire scic...
252
253
254
255
  		/* Yep this is a valid io request pass it along to the
  		 * io request handler
  		 */
  		sci_io_request_tc_completion(ireq, ent);
cc9203bf3   Dan Williams   isci: move core/c...
256
  }
89a7301f2   Dan Williams   isci: retire scic...
257
  static void sci_controller_sdma_completion(struct isci_host *ihost, u32 ent)
cc9203bf3   Dan Williams   isci: move core/c...
258
259
  {
  	u32 index;
5076a1a97   Dan Williams   isci: unify isci_...
260
  	struct isci_request *ireq;
78a6f06e0   Dan Williams   isci: unify isci_...
261
  	struct isci_remote_device *idev;
cc9203bf3   Dan Williams   isci: move core/c...
262

89a7301f2   Dan Williams   isci: retire scic...
263
  	index = SCU_GET_COMPLETION_INDEX(ent);
cc9203bf3   Dan Williams   isci: move core/c...
264

89a7301f2   Dan Williams   isci: retire scic...
265
  	switch (scu_get_command_request_type(ent)) {
cc9203bf3   Dan Williams   isci: move core/c...
266
267
  	case SCU_CONTEXT_COMMAND_REQUEST_TYPE_POST_TC:
  	case SCU_CONTEXT_COMMAND_REQUEST_TYPE_DUMP_TC:
d9dcb4ba7   Dan Williams   isci: unify isci_...
268
269
270
  		ireq = ihost->reqs[index];
  		dev_warn(&ihost->pdev->dev, "%s: %x for io request %p
  ",
89a7301f2   Dan Williams   isci: retire scic...
271
  			 __func__, ent, ireq);
cc9203bf3   Dan Williams   isci: move core/c...
272
273
274
275
  		/* @todo For a post TC operation we need to fail the IO
  		 * request
  		 */
  		break;
cc9203bf3   Dan Williams   isci: move core/c...
276
277
278
  	case SCU_CONTEXT_COMMAND_REQUEST_TYPE_DUMP_RNC:
  	case SCU_CONTEXT_COMMAND_REQUEST_TYPE_OTHER_RNC:
  	case SCU_CONTEXT_COMMAND_REQUEST_TYPE_POST_RNC:
d9dcb4ba7   Dan Williams   isci: unify isci_...
279
280
281
  		idev = ihost->device_table[index];
  		dev_warn(&ihost->pdev->dev, "%s: %x for device %p
  ",
89a7301f2   Dan Williams   isci: retire scic...
282
  			 __func__, ent, idev);
cc9203bf3   Dan Williams   isci: move core/c...
283
284
285
286
  		/* @todo For a port RNC operation we need to fail the
  		 * device
  		 */
  		break;
cc9203bf3   Dan Williams   isci: move core/c...
287
  	default:
d9dcb4ba7   Dan Williams   isci: unify isci_...
288
289
  		dev_warn(&ihost->pdev->dev, "%s: unknown completion type %x
  ",
89a7301f2   Dan Williams   isci: retire scic...
290
  			 __func__, ent);
cc9203bf3   Dan Williams   isci: move core/c...
291
  		break;
cc9203bf3   Dan Williams   isci: move core/c...
292
293
  	}
  }
89a7301f2   Dan Williams   isci: retire scic...
294
  static void sci_controller_unsolicited_frame(struct isci_host *ihost, u32 ent)
cc9203bf3   Dan Williams   isci: move core/c...
295
296
297
  {
  	u32 index;
  	u32 frame_index;
cc9203bf3   Dan Williams   isci: move core/c...
298
  	struct scu_unsolicited_frame_header *frame_header;
852809559   Dan Williams   isci: unify isci_...
299
  	struct isci_phy *iphy;
78a6f06e0   Dan Williams   isci: unify isci_...
300
  	struct isci_remote_device *idev;
cc9203bf3   Dan Williams   isci: move core/c...
301
302
  
  	enum sci_status result = SCI_FAILURE;
89a7301f2   Dan Williams   isci: retire scic...
303
  	frame_index = SCU_GET_FRAME_INDEX(ent);
cc9203bf3   Dan Williams   isci: move core/c...
304

d9dcb4ba7   Dan Williams   isci: unify isci_...
305
306
  	frame_header = ihost->uf_control.buffers.array[frame_index].header;
  	ihost->uf_control.buffers.array[frame_index].state = UNSOLICITED_FRAME_IN_USE;
cc9203bf3   Dan Williams   isci: move core/c...
307

89a7301f2   Dan Williams   isci: retire scic...
308
  	if (SCU_GET_FRAME_ERROR(ent)) {
cc9203bf3   Dan Williams   isci: move core/c...
309
310
311
312
  		/*
  		 * / @todo If the IAF frame or SIGNATURE FIS frame has an error will
  		 * /       this cause a problem? We expect the phy initialization will
  		 * /       fail if there is an error in the frame. */
89a7301f2   Dan Williams   isci: retire scic...
313
  		sci_controller_release_frame(ihost, frame_index);
cc9203bf3   Dan Williams   isci: move core/c...
314
315
316
317
  		return;
  	}
  
  	if (frame_header->is_address_frame) {
89a7301f2   Dan Williams   isci: retire scic...
318
  		index = SCU_GET_PROTOCOL_ENGINE_INDEX(ent);
852809559   Dan Williams   isci: unify isci_...
319
  		iphy = &ihost->phys[index];
89a7301f2   Dan Williams   isci: retire scic...
320
  		result = sci_phy_frame_handler(iphy, frame_index);
cc9203bf3   Dan Williams   isci: move core/c...
321
  	} else {
89a7301f2   Dan Williams   isci: retire scic...
322
  		index = SCU_GET_COMPLETION_INDEX(ent);
cc9203bf3   Dan Williams   isci: move core/c...
323
324
325
326
327
328
  
  		if (index == SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX) {
  			/*
  			 * This is a signature fis or a frame from a direct attached SATA
  			 * device that has not yet been created.  In either case forwared
  			 * the frame to the PE and let it take care of the frame data. */
89a7301f2   Dan Williams   isci: retire scic...
329
  			index = SCU_GET_PROTOCOL_ENGINE_INDEX(ent);
852809559   Dan Williams   isci: unify isci_...
330
  			iphy = &ihost->phys[index];
89a7301f2   Dan Williams   isci: retire scic...
331
  			result = sci_phy_frame_handler(iphy, frame_index);
cc9203bf3   Dan Williams   isci: move core/c...
332
  		} else {
d9dcb4ba7   Dan Williams   isci: unify isci_...
333
334
  			if (index < ihost->remote_node_entries)
  				idev = ihost->device_table[index];
cc9203bf3   Dan Williams   isci: move core/c...
335
  			else
78a6f06e0   Dan Williams   isci: unify isci_...
336
  				idev = NULL;
cc9203bf3   Dan Williams   isci: move core/c...
337

78a6f06e0   Dan Williams   isci: unify isci_...
338
  			if (idev != NULL)
89a7301f2   Dan Williams   isci: retire scic...
339
  				result = sci_remote_device_frame_handler(idev, frame_index);
cc9203bf3   Dan Williams   isci: move core/c...
340
  			else
89a7301f2   Dan Williams   isci: retire scic...
341
  				sci_controller_release_frame(ihost, frame_index);
cc9203bf3   Dan Williams   isci: move core/c...
342
343
344
345
346
347
348
349
350
  		}
  	}
  
  	if (result != SCI_SUCCESS) {
  		/*
  		 * / @todo Is there any reason to report some additional error message
  		 * /       when we get this failure notifiction? */
  	}
  }
89a7301f2   Dan Williams   isci: retire scic...
351
  static void sci_controller_event_completion(struct isci_host *ihost, u32 ent)
cc9203bf3   Dan Williams   isci: move core/c...
352
  {
78a6f06e0   Dan Williams   isci: unify isci_...
353
  	struct isci_remote_device *idev;
5076a1a97   Dan Williams   isci: unify isci_...
354
  	struct isci_request *ireq;
852809559   Dan Williams   isci: unify isci_...
355
  	struct isci_phy *iphy;
cc9203bf3   Dan Williams   isci: move core/c...
356
  	u32 index;
89a7301f2   Dan Williams   isci: retire scic...
357
  	index = SCU_GET_COMPLETION_INDEX(ent);
cc9203bf3   Dan Williams   isci: move core/c...
358

89a7301f2   Dan Williams   isci: retire scic...
359
  	switch (scu_get_event_type(ent)) {
cc9203bf3   Dan Williams   isci: move core/c...
360
361
  	case SCU_EVENT_TYPE_SMU_COMMAND_ERROR:
  		/* / @todo The driver did something wrong and we need to fix the condtion. */
d9dcb4ba7   Dan Williams   isci: unify isci_...
362
  		dev_err(&ihost->pdev->dev,
cc9203bf3   Dan Williams   isci: move core/c...
363
364
365
366
  			"%s: SCIC Controller 0x%p received SMU command error "
  			"0x%x
  ",
  			__func__,
d9dcb4ba7   Dan Williams   isci: unify isci_...
367
  			ihost,
89a7301f2   Dan Williams   isci: retire scic...
368
  			ent);
cc9203bf3   Dan Williams   isci: move core/c...
369
370
371
372
373
374
375
376
  		break;
  
  	case SCU_EVENT_TYPE_SMU_PCQ_ERROR:
  	case SCU_EVENT_TYPE_SMU_ERROR:
  	case SCU_EVENT_TYPE_FATAL_MEMORY_ERROR:
  		/*
  		 * / @todo This is a hardware failure and its likely that we want to
  		 * /       reset the controller. */
d9dcb4ba7   Dan Williams   isci: unify isci_...
377
  		dev_err(&ihost->pdev->dev,
cc9203bf3   Dan Williams   isci: move core/c...
378
379
380
381
  			"%s: SCIC Controller 0x%p received fatal controller "
  			"event  0x%x
  ",
  			__func__,
d9dcb4ba7   Dan Williams   isci: unify isci_...
382
  			ihost,
89a7301f2   Dan Williams   isci: retire scic...
383
  			ent);
cc9203bf3   Dan Williams   isci: move core/c...
384
385
386
  		break;
  
  	case SCU_EVENT_TYPE_TRANSPORT_ERROR:
5076a1a97   Dan Williams   isci: unify isci_...
387
  		ireq = ihost->reqs[index];
89a7301f2   Dan Williams   isci: retire scic...
388
  		sci_io_request_event_handler(ireq, ent);
cc9203bf3   Dan Williams   isci: move core/c...
389
390
391
  		break;
  
  	case SCU_EVENT_TYPE_PTX_SCHEDULE_EVENT:
89a7301f2   Dan Williams   isci: retire scic...
392
  		switch (scu_get_event_specifier(ent)) {
cc9203bf3   Dan Williams   isci: move core/c...
393
394
  		case SCU_EVENT_SPECIFIC_SMP_RESPONSE_NO_PE:
  		case SCU_EVENT_SPECIFIC_TASK_TIMEOUT:
5076a1a97   Dan Williams   isci: unify isci_...
395
396
  			ireq = ihost->reqs[index];
  			if (ireq != NULL)
89a7301f2   Dan Williams   isci: retire scic...
397
  				sci_io_request_event_handler(ireq, ent);
cc9203bf3   Dan Williams   isci: move core/c...
398
  			else
d9dcb4ba7   Dan Williams   isci: unify isci_...
399
  				dev_warn(&ihost->pdev->dev,
cc9203bf3   Dan Williams   isci: move core/c...
400
401
402
403
404
  					 "%s: SCIC Controller 0x%p received "
  					 "event 0x%x for io request object "
  					 "that doesnt exist.
  ",
  					 __func__,
d9dcb4ba7   Dan Williams   isci: unify isci_...
405
  					 ihost,
89a7301f2   Dan Williams   isci: retire scic...
406
  					 ent);
cc9203bf3   Dan Williams   isci: move core/c...
407
408
409
410
  
  			break;
  
  		case SCU_EVENT_SPECIFIC_IT_NEXUS_TIMEOUT:
d9dcb4ba7   Dan Williams   isci: unify isci_...
411
  			idev = ihost->device_table[index];
78a6f06e0   Dan Williams   isci: unify isci_...
412
  			if (idev != NULL)
89a7301f2   Dan Williams   isci: retire scic...
413
  				sci_remote_device_event_handler(idev, ent);
cc9203bf3   Dan Williams   isci: move core/c...
414
  			else
d9dcb4ba7   Dan Williams   isci: unify isci_...
415
  				dev_warn(&ihost->pdev->dev,
cc9203bf3   Dan Williams   isci: move core/c...
416
417
418
419
420
  					 "%s: SCIC Controller 0x%p received "
  					 "event 0x%x for remote device object "
  					 "that doesnt exist.
  ",
  					 __func__,
d9dcb4ba7   Dan Williams   isci: unify isci_...
421
  					 ihost,
89a7301f2   Dan Williams   isci: retire scic...
422
  					 ent);
cc9203bf3   Dan Williams   isci: move core/c...
423
424
425
426
427
428
429
430
431
432
433
434
435
436
  
  			break;
  		}
  		break;
  
  	case SCU_EVENT_TYPE_BROADCAST_CHANGE:
  	/*
  	 * direct the broadcast change event to the phy first and then let
  	 * the phy redirect the broadcast change to the port object */
  	case SCU_EVENT_TYPE_ERR_CNT_EVENT:
  	/*
  	 * direct error counter event to the phy object since that is where
  	 * we get the event notification.  This is a type 4 event. */
  	case SCU_EVENT_TYPE_OSSP_EVENT:
89a7301f2   Dan Williams   isci: retire scic...
437
  		index = SCU_GET_PROTOCOL_ENGINE_INDEX(ent);
852809559   Dan Williams   isci: unify isci_...
438
  		iphy = &ihost->phys[index];
89a7301f2   Dan Williams   isci: retire scic...
439
  		sci_phy_event_handler(iphy, ent);
cc9203bf3   Dan Williams   isci: move core/c...
440
441
442
443
444
  		break;
  
  	case SCU_EVENT_TYPE_RNC_SUSPEND_TX:
  	case SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX:
  	case SCU_EVENT_TYPE_RNC_OPS_MISC:
d9dcb4ba7   Dan Williams   isci: unify isci_...
445
446
  		if (index < ihost->remote_node_entries) {
  			idev = ihost->device_table[index];
cc9203bf3   Dan Williams   isci: move core/c...
447

78a6f06e0   Dan Williams   isci: unify isci_...
448
  			if (idev != NULL)
89a7301f2   Dan Williams   isci: retire scic...
449
  				sci_remote_device_event_handler(idev, ent);
cc9203bf3   Dan Williams   isci: move core/c...
450
  		} else
d9dcb4ba7   Dan Williams   isci: unify isci_...
451
  			dev_err(&ihost->pdev->dev,
cc9203bf3   Dan Williams   isci: move core/c...
452
453
454
455
456
  				"%s: SCIC Controller 0x%p received event 0x%x "
  				"for remote device object 0x%0x that doesnt "
  				"exist.
  ",
  				__func__,
d9dcb4ba7   Dan Williams   isci: unify isci_...
457
  				ihost,
89a7301f2   Dan Williams   isci: retire scic...
458
  				ent,
cc9203bf3   Dan Williams   isci: move core/c...
459
460
461
462
463
  				index);
  
  		break;
  
  	default:
d9dcb4ba7   Dan Williams   isci: unify isci_...
464
  		dev_warn(&ihost->pdev->dev,
cc9203bf3   Dan Williams   isci: move core/c...
465
466
467
  			 "%s: SCIC Controller received unknown event code %x
  ",
  			 __func__,
89a7301f2   Dan Williams   isci: retire scic...
468
  			 ent);
cc9203bf3   Dan Williams   isci: move core/c...
469
470
471
  		break;
  	}
  }
89a7301f2   Dan Williams   isci: retire scic...
472
  static void sci_controller_process_completions(struct isci_host *ihost)
cc9203bf3   Dan Williams   isci: move core/c...
473
474
  {
  	u32 completion_count = 0;
89a7301f2   Dan Williams   isci: retire scic...
475
  	u32 ent;
cc9203bf3   Dan Williams   isci: move core/c...
476
477
  	u32 get_index;
  	u32 get_cycle;
994a9303d   Dan Williams   isci: cleanup/opt...
478
  	u32 event_get;
cc9203bf3   Dan Williams   isci: move core/c...
479
  	u32 event_cycle;
d9dcb4ba7   Dan Williams   isci: unify isci_...
480
  	dev_dbg(&ihost->pdev->dev,
cc9203bf3   Dan Williams   isci: move core/c...
481
482
483
  		"%s: completion queue begining get:0x%08x
  ",
  		__func__,
d9dcb4ba7   Dan Williams   isci: unify isci_...
484
  		ihost->completion_queue_get);
cc9203bf3   Dan Williams   isci: move core/c...
485
486
  
  	/* Get the component parts of the completion queue */
d9dcb4ba7   Dan Williams   isci: unify isci_...
487
488
  	get_index = NORMALIZE_GET_POINTER(ihost->completion_queue_get);
  	get_cycle = SMU_CQGR_CYCLE_BIT & ihost->completion_queue_get;
cc9203bf3   Dan Williams   isci: move core/c...
489

d9dcb4ba7   Dan Williams   isci: unify isci_...
490
491
  	event_get = NORMALIZE_EVENT_POINTER(ihost->completion_queue_get);
  	event_cycle = SMU_CQGR_EVENT_CYCLE_BIT & ihost->completion_queue_get;
cc9203bf3   Dan Williams   isci: move core/c...
492
493
494
  
  	while (
  		NORMALIZE_GET_POINTER_CYCLE_BIT(get_cycle)
d9dcb4ba7   Dan Williams   isci: unify isci_...
495
  		== COMPLETION_QUEUE_CYCLE_BIT(ihost->completion_queue[get_index])
cc9203bf3   Dan Williams   isci: move core/c...
496
497
  		) {
  		completion_count++;
89a7301f2   Dan Williams   isci: retire scic...
498
  		ent = ihost->completion_queue[get_index];
994a9303d   Dan Williams   isci: cleanup/opt...
499
500
501
502
503
  
  		/* increment the get pointer and check for rollover to toggle the cycle bit */
  		get_cycle ^= ((get_index+1) & SCU_MAX_COMPLETION_QUEUE_ENTRIES) <<
  			     (SMU_COMPLETION_QUEUE_GET_CYCLE_BIT_SHIFT - SCU_MAX_COMPLETION_QUEUE_SHIFT);
  		get_index = (get_index+1) & (SCU_MAX_COMPLETION_QUEUE_ENTRIES-1);
cc9203bf3   Dan Williams   isci: move core/c...
504

d9dcb4ba7   Dan Williams   isci: unify isci_...
505
  		dev_dbg(&ihost->pdev->dev,
cc9203bf3   Dan Williams   isci: move core/c...
506
507
508
  			"%s: completion queue entry:0x%08x
  ",
  			__func__,
89a7301f2   Dan Williams   isci: retire scic...
509
  			ent);
cc9203bf3   Dan Williams   isci: move core/c...
510

89a7301f2   Dan Williams   isci: retire scic...
511
  		switch (SCU_GET_COMPLETION_TYPE(ent)) {
cc9203bf3   Dan Williams   isci: move core/c...
512
  		case SCU_COMPLETION_TYPE_TASK:
89a7301f2   Dan Williams   isci: retire scic...
513
  			sci_controller_task_completion(ihost, ent);
cc9203bf3   Dan Williams   isci: move core/c...
514
515
516
  			break;
  
  		case SCU_COMPLETION_TYPE_SDMA:
89a7301f2   Dan Williams   isci: retire scic...
517
  			sci_controller_sdma_completion(ihost, ent);
cc9203bf3   Dan Williams   isci: move core/c...
518
519
520
  			break;
  
  		case SCU_COMPLETION_TYPE_UFI:
89a7301f2   Dan Williams   isci: retire scic...
521
  			sci_controller_unsolicited_frame(ihost, ent);
cc9203bf3   Dan Williams   isci: move core/c...
522
523
524
  			break;
  
  		case SCU_COMPLETION_TYPE_EVENT:
77cd72a53   Dan Williams   [SCSI] isci: fix ...
525
526
  			sci_controller_event_completion(ihost, ent);
  			break;
994a9303d   Dan Williams   isci: cleanup/opt...
527
528
529
530
  		case SCU_COMPLETION_TYPE_NOTIFY: {
  			event_cycle ^= ((event_get+1) & SCU_MAX_EVENTS) <<
  				       (SMU_COMPLETION_QUEUE_GET_EVENT_CYCLE_BIT_SHIFT - SCU_MAX_EVENTS_SHIFT);
  			event_get = (event_get+1) & (SCU_MAX_EVENTS-1);
cc9203bf3   Dan Williams   isci: move core/c...
531

89a7301f2   Dan Williams   isci: retire scic...
532
  			sci_controller_event_completion(ihost, ent);
cc9203bf3   Dan Williams   isci: move core/c...
533
  			break;
994a9303d   Dan Williams   isci: cleanup/opt...
534
  		}
cc9203bf3   Dan Williams   isci: move core/c...
535
  		default:
d9dcb4ba7   Dan Williams   isci: unify isci_...
536
  			dev_warn(&ihost->pdev->dev,
cc9203bf3   Dan Williams   isci: move core/c...
537
538
539
540
  				 "%s: SCIC Controller received unknown "
  				 "completion type %x
  ",
  				 __func__,
89a7301f2   Dan Williams   isci: retire scic...
541
  				 ent);
cc9203bf3   Dan Williams   isci: move core/c...
542
543
544
545
546
547
  			break;
  		}
  	}
  
  	/* Update the get register if we completed one or more entries */
  	if (completion_count > 0) {
d9dcb4ba7   Dan Williams   isci: unify isci_...
548
  		ihost->completion_queue_get =
cc9203bf3   Dan Williams   isci: move core/c...
549
550
551
  			SMU_CQGR_GEN_BIT(ENABLE) |
  			SMU_CQGR_GEN_BIT(EVENT_ENABLE) |
  			event_cycle |
994a9303d   Dan Williams   isci: cleanup/opt...
552
  			SMU_CQGR_GEN_VAL(EVENT_POINTER, event_get) |
cc9203bf3   Dan Williams   isci: move core/c...
553
554
  			get_cycle |
  			SMU_CQGR_GEN_VAL(POINTER, get_index);
d9dcb4ba7   Dan Williams   isci: unify isci_...
555
556
  		writel(ihost->completion_queue_get,
  		       &ihost->smu_registers->completion_queue_get);
cc9203bf3   Dan Williams   isci: move core/c...
557
558
  
  	}
d9dcb4ba7   Dan Williams   isci: unify isci_...
559
  	dev_dbg(&ihost->pdev->dev,
cc9203bf3   Dan Williams   isci: move core/c...
560
561
562
  		"%s: completion queue ending get:0x%08x
  ",
  		__func__,
d9dcb4ba7   Dan Williams   isci: unify isci_...
563
  		ihost->completion_queue_get);
cc9203bf3   Dan Williams   isci: move core/c...
564
565
  
  }
89a7301f2   Dan Williams   isci: retire scic...
566
  static void sci_controller_error_handler(struct isci_host *ihost)
cc9203bf3   Dan Williams   isci: move core/c...
567
568
569
570
  {
  	u32 interrupt_status;
  
  	interrupt_status =
d9dcb4ba7   Dan Williams   isci: unify isci_...
571
  		readl(&ihost->smu_registers->interrupt_status);
cc9203bf3   Dan Williams   isci: move core/c...
572
573
  
  	if ((interrupt_status & SMU_ISR_QUEUE_SUSPEND) &&
89a7301f2   Dan Williams   isci: retire scic...
574
  	    sci_controller_completion_queue_has_entries(ihost)) {
cc9203bf3   Dan Williams   isci: move core/c...
575

89a7301f2   Dan Williams   isci: retire scic...
576
  		sci_controller_process_completions(ihost);
d9dcb4ba7   Dan Williams   isci: unify isci_...
577
  		writel(SMU_ISR_QUEUE_SUSPEND, &ihost->smu_registers->interrupt_status);
cc9203bf3   Dan Williams   isci: move core/c...
578
  	} else {
d9dcb4ba7   Dan Williams   isci: unify isci_...
579
580
  		dev_err(&ihost->pdev->dev, "%s: status: %#x
  ", __func__,
cc9203bf3   Dan Williams   isci: move core/c...
581
  			interrupt_status);
d9dcb4ba7   Dan Williams   isci: unify isci_...
582
  		sci_change_state(&ihost->sm, SCIC_FAILED);
cc9203bf3   Dan Williams   isci: move core/c...
583
584
585
586
587
588
589
  
  		return;
  	}
  
  	/* If we dont process any completions I am not sure that we want to do this.
  	 * We are in the middle of a hardware fault and should probably be reset.
  	 */
d9dcb4ba7   Dan Williams   isci: unify isci_...
590
  	writel(0, &ihost->smu_registers->interrupt_mask);
cc9203bf3   Dan Williams   isci: move core/c...
591
  }
c7ef4031f   Dan Williams   isci: bypass scic...
592
  irqreturn_t isci_intx_isr(int vec, void *data)
6f231dda6   Dan Williams   isci: Intel(R) C6...
593
  {
6f231dda6   Dan Williams   isci: Intel(R) C6...
594
  	irqreturn_t ret = IRQ_NONE;
31e824ed0   Dan Williams   isci: rely on irq...
595
  	struct isci_host *ihost = data;
c7ef4031f   Dan Williams   isci: bypass scic...
596

89a7301f2   Dan Williams   isci: retire scic...
597
  	if (sci_controller_isr(ihost)) {
d9dcb4ba7   Dan Williams   isci: unify isci_...
598
  		writel(SMU_ISR_COMPLETION, &ihost->smu_registers->interrupt_status);
31e824ed0   Dan Williams   isci: rely on irq...
599
600
  		tasklet_schedule(&ihost->completion_tasklet);
  		ret = IRQ_HANDLED;
89a7301f2   Dan Williams   isci: retire scic...
601
  	} else if (sci_controller_error_isr(ihost)) {
31e824ed0   Dan Williams   isci: rely on irq...
602
  		spin_lock(&ihost->scic_lock);
89a7301f2   Dan Williams   isci: retire scic...
603
  		sci_controller_error_handler(ihost);
31e824ed0   Dan Williams   isci: rely on irq...
604
605
  		spin_unlock(&ihost->scic_lock);
  		ret = IRQ_HANDLED;
6f231dda6   Dan Williams   isci: Intel(R) C6...
606
  	}
92f4f0f54   Dan Williams   isci: implement e...
607

6f231dda6   Dan Williams   isci: Intel(R) C6...
608
609
  	return ret;
  }
92f4f0f54   Dan Williams   isci: implement e...
610
611
612
  irqreturn_t isci_error_isr(int vec, void *data)
  {
  	struct isci_host *ihost = data;
92f4f0f54   Dan Williams   isci: implement e...
613

89a7301f2   Dan Williams   isci: retire scic...
614
615
  	if (sci_controller_error_isr(ihost))
  		sci_controller_error_handler(ihost);
92f4f0f54   Dan Williams   isci: implement e...
616
617
618
  
  	return IRQ_HANDLED;
  }
6f231dda6   Dan Williams   isci: Intel(R) C6...
619
620
621
622
623
624
625
626
627
  
  /**
   * isci_host_start_complete() - This function is called by the core library,
   *    through the ISCI Module, to indicate controller start status.
   * @isci_host: This parameter specifies the ISCI host object
   * @completion_status: This parameter specifies the completion status from the
   *    core library.
   *
   */
cc9203bf3   Dan Williams   isci: move core/c...
628
  static void isci_host_start_complete(struct isci_host *ihost, enum sci_status completion_status)
6f231dda6   Dan Williams   isci: Intel(R) C6...
629
  {
0cf89d1d2   Dan Williams   isci: cleanup "st...
630
631
632
633
634
635
636
  	if (completion_status != SCI_SUCCESS)
  		dev_info(&ihost->pdev->dev,
  			"controller start timed out, continuing...
  ");
  	isci_host_change_state(ihost, isci_ready);
  	clear_bit(IHOST_START_PENDING, &ihost->flags);
  	wake_up(&ihost->eventq);
6f231dda6   Dan Williams   isci: Intel(R) C6...
637
  }
c7ef4031f   Dan Williams   isci: bypass scic...
638
  int isci_host_scan_finished(struct Scsi_Host *shost, unsigned long time)
6f231dda6   Dan Williams   isci: Intel(R) C6...
639
  {
4393aa4e6   Dan Williams   isci: fix fragile...
640
  	struct isci_host *ihost = SHOST_TO_SAS_HA(shost)->lldd_ha;
6f231dda6   Dan Williams   isci: Intel(R) C6...
641

77950f51f   Edmund Nadolski   isci: enable inte...
642
  	if (test_bit(IHOST_START_PENDING, &ihost->flags))
6f231dda6   Dan Williams   isci: Intel(R) C6...
643
  		return 0;
6f231dda6   Dan Williams   isci: Intel(R) C6...
644

77950f51f   Edmund Nadolski   isci: enable inte...
645
646
647
648
  	/* todo: use sas_flush_discovery once it is upstream */
  	scsi_flush_work(shost);
  
  	scsi_flush_work(shost);
6f231dda6   Dan Williams   isci: Intel(R) C6...
649

0cf89d1d2   Dan Williams   isci: cleanup "st...
650
651
652
653
  	dev_dbg(&ihost->pdev->dev,
  		"%s: ihost->status = %d, time = %ld
  ",
  		 __func__, isci_host_get_state(ihost), time);
6f231dda6   Dan Williams   isci: Intel(R) C6...
654

6f231dda6   Dan Williams   isci: Intel(R) C6...
655
656
657
  	return 1;
  
  }
cc9203bf3   Dan Williams   isci: move core/c...
658
  /**
89a7301f2   Dan Williams   isci: retire scic...
659
660
   * sci_controller_get_suggested_start_timeout() - This method returns the
   *    suggested sci_controller_start() timeout amount.  The user is free to
cc9203bf3   Dan Williams   isci: move core/c...
661
662
663
664
665
666
667
668
669
   *    use any timeout value, but this method provides the suggested minimum
   *    start timeout value.  The returned value is based upon empirical
   *    information determined as a result of interoperability testing.
   * @controller: the handle to the controller object for which to return the
   *    suggested start timeout.
   *
   * This method returns the number of milliseconds for the suggested start
   * operation timeout.
   */
89a7301f2   Dan Williams   isci: retire scic...
670
  static u32 sci_controller_get_suggested_start_timeout(struct isci_host *ihost)
cc9203bf3   Dan Williams   isci: move core/c...
671
672
  {
  	/* Validate the user supplied parameters. */
d9dcb4ba7   Dan Williams   isci: unify isci_...
673
  	if (!ihost)
cc9203bf3   Dan Williams   isci: move core/c...
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
  		return 0;
  
  	/*
  	 * The suggested minimum timeout value for a controller start operation:
  	 *
  	 *     Signature FIS Timeout
  	 *   + Phy Start Timeout
  	 *   + Number of Phy Spin Up Intervals
  	 *   ---------------------------------
  	 *   Number of milliseconds for the controller start operation.
  	 *
  	 * NOTE: The number of phy spin up intervals will be equivalent
  	 *       to the number of phys divided by the number phys allowed
  	 *       per interval - 1 (once OEM parameters are supported).
  	 *       Currently we assume only 1 phy per interval. */
  
  	return SCIC_SDS_SIGNATURE_FIS_TIMEOUT
  		+ SCIC_SDS_CONTROLLER_PHY_START_TIMEOUT
  		+ ((SCI_MAX_PHYS - 1) * SCIC_SDS_CONTROLLER_POWER_CONTROL_INTERVAL);
  }
89a7301f2   Dan Williams   isci: retire scic...
694
  static void sci_controller_enable_interrupts(struct isci_host *ihost)
cc9203bf3   Dan Williams   isci: move core/c...
695
  {
d9dcb4ba7   Dan Williams   isci: unify isci_...
696
697
  	BUG_ON(ihost->smu_registers == NULL);
  	writel(0, &ihost->smu_registers->interrupt_mask);
cc9203bf3   Dan Williams   isci: move core/c...
698
  }
89a7301f2   Dan Williams   isci: retire scic...
699
  void sci_controller_disable_interrupts(struct isci_host *ihost)
cc9203bf3   Dan Williams   isci: move core/c...
700
  {
d9dcb4ba7   Dan Williams   isci: unify isci_...
701
702
  	BUG_ON(ihost->smu_registers == NULL);
  	writel(0xffffffff, &ihost->smu_registers->interrupt_mask);
cc9203bf3   Dan Williams   isci: move core/c...
703
  }
89a7301f2   Dan Williams   isci: retire scic...
704
  static void sci_controller_enable_port_task_scheduler(struct isci_host *ihost)
cc9203bf3   Dan Williams   isci: move core/c...
705
706
707
708
  {
  	u32 port_task_scheduler_value;
  
  	port_task_scheduler_value =
d9dcb4ba7   Dan Williams   isci: unify isci_...
709
  		readl(&ihost->scu_registers->peg0.ptsg.control);
cc9203bf3   Dan Williams   isci: move core/c...
710
711
712
713
  	port_task_scheduler_value |=
  		(SCU_PTSGCR_GEN_BIT(ETM_ENABLE) |
  		 SCU_PTSGCR_GEN_BIT(PTSG_ENABLE));
  	writel(port_task_scheduler_value,
d9dcb4ba7   Dan Williams   isci: unify isci_...
714
  	       &ihost->scu_registers->peg0.ptsg.control);
cc9203bf3   Dan Williams   isci: move core/c...
715
  }
89a7301f2   Dan Williams   isci: retire scic...
716
  static void sci_controller_assign_task_entries(struct isci_host *ihost)
cc9203bf3   Dan Williams   isci: move core/c...
717
718
719
720
721
722
723
724
725
  {
  	u32 task_assignment;
  
  	/*
  	 * Assign all the TCs to function 0
  	 * TODO: Do we actually need to read this register to write it back?
  	 */
  
  	task_assignment =
d9dcb4ba7   Dan Williams   isci: unify isci_...
726
  		readl(&ihost->smu_registers->task_context_assignment[0]);
cc9203bf3   Dan Williams   isci: move core/c...
727
728
  
  	task_assignment |= (SMU_TCA_GEN_VAL(STARTING, 0)) |
d9dcb4ba7   Dan Williams   isci: unify isci_...
729
  		(SMU_TCA_GEN_VAL(ENDING,  ihost->task_context_entries - 1)) |
cc9203bf3   Dan Williams   isci: move core/c...
730
731
732
  		(SMU_TCA_GEN_BIT(RANGE_CHECK_ENABLE));
  
  	writel(task_assignment,
d9dcb4ba7   Dan Williams   isci: unify isci_...
733
  		&ihost->smu_registers->task_context_assignment[0]);
cc9203bf3   Dan Williams   isci: move core/c...
734
735
  
  }
89a7301f2   Dan Williams   isci: retire scic...
736
  static void sci_controller_initialize_completion_queue(struct isci_host *ihost)
cc9203bf3   Dan Williams   isci: move core/c...
737
738
739
740
741
  {
  	u32 index;
  	u32 completion_queue_control_value;
  	u32 completion_queue_get_value;
  	u32 completion_queue_put_value;
d9dcb4ba7   Dan Williams   isci: unify isci_...
742
  	ihost->completion_queue_get = 0;
cc9203bf3   Dan Williams   isci: move core/c...
743

7c78da317   Dan Williams   isci: remove 'min...
744
745
746
  	completion_queue_control_value =
  		(SMU_CQC_QUEUE_LIMIT_SET(SCU_MAX_COMPLETION_QUEUE_ENTRIES - 1) |
  		 SMU_CQC_EVENT_LIMIT_SET(SCU_MAX_EVENTS - 1));
cc9203bf3   Dan Williams   isci: move core/c...
747
748
  
  	writel(completion_queue_control_value,
d9dcb4ba7   Dan Williams   isci: unify isci_...
749
  	       &ihost->smu_registers->completion_queue_control);
cc9203bf3   Dan Williams   isci: move core/c...
750
751
752
753
754
755
756
757
758
759
760
  
  
  	/* Set the completion queue get pointer and enable the queue */
  	completion_queue_get_value = (
  		(SMU_CQGR_GEN_VAL(POINTER, 0))
  		| (SMU_CQGR_GEN_VAL(EVENT_POINTER, 0))
  		| (SMU_CQGR_GEN_BIT(ENABLE))
  		| (SMU_CQGR_GEN_BIT(EVENT_ENABLE))
  		);
  
  	writel(completion_queue_get_value,
d9dcb4ba7   Dan Williams   isci: unify isci_...
761
  	       &ihost->smu_registers->completion_queue_get);
cc9203bf3   Dan Williams   isci: move core/c...
762
763
764
765
766
767
768
769
  
  	/* Set the completion queue put pointer */
  	completion_queue_put_value = (
  		(SMU_CQPR_GEN_VAL(POINTER, 0))
  		| (SMU_CQPR_GEN_VAL(EVENT_POINTER, 0))
  		);
  
  	writel(completion_queue_put_value,
d9dcb4ba7   Dan Williams   isci: unify isci_...
770
  	       &ihost->smu_registers->completion_queue_put);
cc9203bf3   Dan Williams   isci: move core/c...
771
772
  
  	/* Initialize the cycle bit of the completion queue entries */
7c78da317   Dan Williams   isci: remove 'min...
773
  	for (index = 0; index < SCU_MAX_COMPLETION_QUEUE_ENTRIES; index++) {
cc9203bf3   Dan Williams   isci: move core/c...
774
775
776
777
  		/*
  		 * If get.cycle_bit != completion_queue.cycle_bit
  		 * its not a valid completion queue entry
  		 * so at system start all entries are invalid */
d9dcb4ba7   Dan Williams   isci: unify isci_...
778
  		ihost->completion_queue[index] = 0x80000000;
cc9203bf3   Dan Williams   isci: move core/c...
779
780
  	}
  }
89a7301f2   Dan Williams   isci: retire scic...
781
  static void sci_controller_initialize_unsolicited_frame_queue(struct isci_host *ihost)
cc9203bf3   Dan Williams   isci: move core/c...
782
783
784
785
786
787
788
  {
  	u32 frame_queue_control_value;
  	u32 frame_queue_get_value;
  	u32 frame_queue_put_value;
  
  	/* Write the queue size */
  	frame_queue_control_value =
7c78da317   Dan Williams   isci: remove 'min...
789
  		SCU_UFQC_GEN_VAL(QUEUE_SIZE, SCU_MAX_UNSOLICITED_FRAMES);
cc9203bf3   Dan Williams   isci: move core/c...
790
791
  
  	writel(frame_queue_control_value,
d9dcb4ba7   Dan Williams   isci: unify isci_...
792
  	       &ihost->scu_registers->sdma.unsolicited_frame_queue_control);
cc9203bf3   Dan Williams   isci: move core/c...
793
794
795
796
797
798
799
800
  
  	/* Setup the get pointer for the unsolicited frame queue */
  	frame_queue_get_value = (
  		SCU_UFQGP_GEN_VAL(POINTER, 0)
  		|  SCU_UFQGP_GEN_BIT(ENABLE_BIT)
  		);
  
  	writel(frame_queue_get_value,
d9dcb4ba7   Dan Williams   isci: unify isci_...
801
  	       &ihost->scu_registers->sdma.unsolicited_frame_get_pointer);
cc9203bf3   Dan Williams   isci: move core/c...
802
803
804
  	/* Setup the put pointer for the unsolicited frame queue */
  	frame_queue_put_value = SCU_UFQPP_GEN_VAL(POINTER, 0);
  	writel(frame_queue_put_value,
d9dcb4ba7   Dan Williams   isci: unify isci_...
805
  	       &ihost->scu_registers->sdma.unsolicited_frame_put_pointer);
cc9203bf3   Dan Williams   isci: move core/c...
806
  }
89a7301f2   Dan Williams   isci: retire scic...
807
  static void sci_controller_transition_to_ready(struct isci_host *ihost, enum sci_status status)
cc9203bf3   Dan Williams   isci: move core/c...
808
  {
d9dcb4ba7   Dan Williams   isci: unify isci_...
809
  	if (ihost->sm.current_state_id == SCIC_STARTING) {
cc9203bf3   Dan Williams   isci: move core/c...
810
811
812
813
  		/*
  		 * We move into the ready state, because some of the phys/ports
  		 * may be up and operational.
  		 */
d9dcb4ba7   Dan Williams   isci: unify isci_...
814
  		sci_change_state(&ihost->sm, SCIC_READY);
cc9203bf3   Dan Williams   isci: move core/c...
815
816
817
818
  
  		isci_host_start_complete(ihost, status);
  	}
  }
852809559   Dan Williams   isci: unify isci_...
819
  static bool is_phy_starting(struct isci_phy *iphy)
4a33c525f   Adam Gruchala   isci: merge phy s...
820
  {
89a7301f2   Dan Williams   isci: retire scic...
821
  	enum sci_phy_states state;
4a33c525f   Adam Gruchala   isci: merge phy s...
822

852809559   Dan Williams   isci: unify isci_...
823
  	state = iphy->sm.current_state_id;
4a33c525f   Adam Gruchala   isci: merge phy s...
824
  	switch (state) {
e301370ac   Edmund Nadolski   isci: state machi...
825
826
827
828
829
830
831
832
833
834
  	case SCI_PHY_STARTING:
  	case SCI_PHY_SUB_INITIAL:
  	case SCI_PHY_SUB_AWAIT_SAS_SPEED_EN:
  	case SCI_PHY_SUB_AWAIT_IAF_UF:
  	case SCI_PHY_SUB_AWAIT_SAS_POWER:
  	case SCI_PHY_SUB_AWAIT_SATA_POWER:
  	case SCI_PHY_SUB_AWAIT_SATA_PHY_EN:
  	case SCI_PHY_SUB_AWAIT_SATA_SPEED_EN:
  	case SCI_PHY_SUB_AWAIT_SIG_FIS_UF:
  	case SCI_PHY_SUB_FINAL:
4a33c525f   Adam Gruchala   isci: merge phy s...
835
836
837
838
839
  		return true;
  	default:
  		return false;
  	}
  }
cc9203bf3   Dan Williams   isci: move core/c...
840
  /**
89a7301f2   Dan Williams   isci: retire scic...
841
   * sci_controller_start_next_phy - start phy
cc9203bf3   Dan Williams   isci: move core/c...
842
843
844
845
   * @scic: controller
   *
   * If all the phys have been started, then attempt to transition the
   * controller to the READY state and inform the user
89a7301f2   Dan Williams   isci: retire scic...
846
   * (sci_cb_controller_start_complete()).
cc9203bf3   Dan Williams   isci: move core/c...
847
   */
89a7301f2   Dan Williams   isci: retire scic...
848
  static enum sci_status sci_controller_start_next_phy(struct isci_host *ihost)
cc9203bf3   Dan Williams   isci: move core/c...
849
  {
89a7301f2   Dan Williams   isci: retire scic...
850
  	struct sci_oem_params *oem = &ihost->oem_parameters;
852809559   Dan Williams   isci: unify isci_...
851
  	struct isci_phy *iphy;
cc9203bf3   Dan Williams   isci: move core/c...
852
853
854
  	enum sci_status status;
  
  	status = SCI_SUCCESS;
d9dcb4ba7   Dan Williams   isci: unify isci_...
855
  	if (ihost->phy_startup_timer_pending)
cc9203bf3   Dan Williams   isci: move core/c...
856
  		return status;
d9dcb4ba7   Dan Williams   isci: unify isci_...
857
  	if (ihost->next_phy_to_start >= SCI_MAX_PHYS) {
cc9203bf3   Dan Williams   isci: move core/c...
858
859
860
861
862
  		bool is_controller_start_complete = true;
  		u32 state;
  		u8 index;
  
  		for (index = 0; index < SCI_MAX_PHYS; index++) {
852809559   Dan Williams   isci: unify isci_...
863
864
  			iphy = &ihost->phys[index];
  			state = iphy->sm.current_state_id;
cc9203bf3   Dan Williams   isci: move core/c...
865

852809559   Dan Williams   isci: unify isci_...
866
  			if (!phy_get_non_dummy_port(iphy))
cc9203bf3   Dan Williams   isci: move core/c...
867
868
869
870
871
872
873
874
  				continue;
  
  			/* The controller start operation is complete iff:
  			 * - all links have been given an opportunity to start
  			 * - have no indication of a connected device
  			 * - have an indication of a connected device and it has
  			 *   finished the link training process.
  			 */
852809559   Dan Williams   isci: unify isci_...
875
876
877
  			if ((iphy->is_in_link_training == false && state == SCI_PHY_INITIAL) ||
  			    (iphy->is_in_link_training == false && state == SCI_PHY_STOPPED) ||
  			    (iphy->is_in_link_training == true && is_phy_starting(iphy))) {
cc9203bf3   Dan Williams   isci: move core/c...
878
879
880
881
882
883
884
885
886
  				is_controller_start_complete = false;
  				break;
  			}
  		}
  
  		/*
  		 * The controller has successfully finished the start process.
  		 * Inform the SCI Core user and transition to the READY state. */
  		if (is_controller_start_complete == true) {
89a7301f2   Dan Williams   isci: retire scic...
887
  			sci_controller_transition_to_ready(ihost, SCI_SUCCESS);
d9dcb4ba7   Dan Williams   isci: unify isci_...
888
889
  			sci_del_timer(&ihost->phy_timer);
  			ihost->phy_startup_timer_pending = false;
cc9203bf3   Dan Williams   isci: move core/c...
890
891
  		}
  	} else {
d9dcb4ba7   Dan Williams   isci: unify isci_...
892
  		iphy = &ihost->phys[ihost->next_phy_to_start];
cc9203bf3   Dan Williams   isci: move core/c...
893
894
  
  		if (oem->controller.mode_type == SCIC_PORT_MANUAL_CONFIGURATION_MODE) {
852809559   Dan Williams   isci: unify isci_...
895
  			if (phy_get_non_dummy_port(iphy) == NULL) {
d9dcb4ba7   Dan Williams   isci: unify isci_...
896
  				ihost->next_phy_to_start++;
cc9203bf3   Dan Williams   isci: move core/c...
897
898
899
900
901
902
903
904
905
906
  
  				/* Caution recursion ahead be forwarned
  				 *
  				 * The PHY was never added to a PORT in MPC mode
  				 * so start the next phy in sequence This phy
  				 * will never go link up and will not draw power
  				 * the OEM parameters either configured the phy
  				 * incorrectly for the PORT or it was never
  				 * assigned to a PORT
  				 */
89a7301f2   Dan Williams   isci: retire scic...
907
  				return sci_controller_start_next_phy(ihost);
cc9203bf3   Dan Williams   isci: move core/c...
908
909
  			}
  		}
89a7301f2   Dan Williams   isci: retire scic...
910
  		status = sci_phy_start(iphy);
cc9203bf3   Dan Williams   isci: move core/c...
911
912
  
  		if (status == SCI_SUCCESS) {
d9dcb4ba7   Dan Williams   isci: unify isci_...
913
  			sci_mod_timer(&ihost->phy_timer,
bb3dbdf6c   Edmund Nadolski   isci: convert phy...
914
  				      SCIC_SDS_CONTROLLER_PHY_START_TIMEOUT);
d9dcb4ba7   Dan Williams   isci: unify isci_...
915
  			ihost->phy_startup_timer_pending = true;
cc9203bf3   Dan Williams   isci: move core/c...
916
  		} else {
d9dcb4ba7   Dan Williams   isci: unify isci_...
917
  			dev_warn(&ihost->pdev->dev,
cc9203bf3   Dan Williams   isci: move core/c...
918
919
920
921
922
  				 "%s: Controller stop operation failed "
  				 "to stop phy %d because of status "
  				 "%d.
  ",
  				 __func__,
d9dcb4ba7   Dan Williams   isci: unify isci_...
923
  				 ihost->phys[ihost->next_phy_to_start].phy_index,
cc9203bf3   Dan Williams   isci: move core/c...
924
925
  				 status);
  		}
d9dcb4ba7   Dan Williams   isci: unify isci_...
926
  		ihost->next_phy_to_start++;
cc9203bf3   Dan Williams   isci: move core/c...
927
928
929
930
  	}
  
  	return status;
  }
bb3dbdf6c   Edmund Nadolski   isci: convert phy...
931
  static void phy_startup_timeout(unsigned long data)
cc9203bf3   Dan Williams   isci: move core/c...
932
  {
bb3dbdf6c   Edmund Nadolski   isci: convert phy...
933
  	struct sci_timer *tmr = (struct sci_timer *)data;
d9dcb4ba7   Dan Williams   isci: unify isci_...
934
  	struct isci_host *ihost = container_of(tmr, typeof(*ihost), phy_timer);
bb3dbdf6c   Edmund Nadolski   isci: convert phy...
935
  	unsigned long flags;
cc9203bf3   Dan Williams   isci: move core/c...
936
  	enum sci_status status;
bb3dbdf6c   Edmund Nadolski   isci: convert phy...
937
938
939
940
  	spin_lock_irqsave(&ihost->scic_lock, flags);
  
  	if (tmr->cancel)
  		goto done;
d9dcb4ba7   Dan Williams   isci: unify isci_...
941
  	ihost->phy_startup_timer_pending = false;
bb3dbdf6c   Edmund Nadolski   isci: convert phy...
942
943
  
  	do {
89a7301f2   Dan Williams   isci: retire scic...
944
  		status = sci_controller_start_next_phy(ihost);
bb3dbdf6c   Edmund Nadolski   isci: convert phy...
945
946
947
948
  	} while (status != SCI_SUCCESS);
  
  done:
  	spin_unlock_irqrestore(&ihost->scic_lock, flags);
cc9203bf3   Dan Williams   isci: move core/c...
949
  }
ac668c697   Dan Williams   isci: cleanup/opt...
950
951
952
953
  static u16 isci_tci_active(struct isci_host *ihost)
  {
  	return CIRC_CNT(ihost->tci_head, ihost->tci_tail, SCI_MAX_IO_REQUESTS);
  }
89a7301f2   Dan Williams   isci: retire scic...
954
  static enum sci_status sci_controller_start(struct isci_host *ihost,
cc9203bf3   Dan Williams   isci: move core/c...
955
956
  					     u32 timeout)
  {
cc9203bf3   Dan Williams   isci: move core/c...
957
958
  	enum sci_status result;
  	u16 index;
d9dcb4ba7   Dan Williams   isci: unify isci_...
959
960
  	if (ihost->sm.current_state_id != SCIC_INITIALIZED) {
  		dev_warn(&ihost->pdev->dev,
cc9203bf3   Dan Williams   isci: move core/c...
961
962
963
964
965
966
967
  			 "SCIC Controller start operation requested in "
  			 "invalid state
  ");
  		return SCI_FAILURE_INVALID_STATE;
  	}
  
  	/* Build the TCi free pool */
ac668c697   Dan Williams   isci: cleanup/opt...
968
969
970
  	BUILD_BUG_ON(SCI_MAX_IO_REQUESTS > 1 << sizeof(ihost->tci_pool[0]) * 8);
  	ihost->tci_head = 0;
  	ihost->tci_tail = 0;
d9dcb4ba7   Dan Williams   isci: unify isci_...
971
  	for (index = 0; index < ihost->task_context_entries; index++)
ac668c697   Dan Williams   isci: cleanup/opt...
972
  		isci_tci_free(ihost, index);
cc9203bf3   Dan Williams   isci: move core/c...
973
974
  
  	/* Build the RNi free pool */
89a7301f2   Dan Williams   isci: retire scic...
975
976
  	sci_remote_node_table_initialize(&ihost->available_remote_nodes,
  					 ihost->remote_node_entries);
cc9203bf3   Dan Williams   isci: move core/c...
977
978
979
980
981
  
  	/*
  	 * Before anything else lets make sure we will not be
  	 * interrupted by the hardware.
  	 */
89a7301f2   Dan Williams   isci: retire scic...
982
  	sci_controller_disable_interrupts(ihost);
cc9203bf3   Dan Williams   isci: move core/c...
983
984
  
  	/* Enable the port task scheduler */
89a7301f2   Dan Williams   isci: retire scic...
985
  	sci_controller_enable_port_task_scheduler(ihost);
cc9203bf3   Dan Williams   isci: move core/c...
986

d9dcb4ba7   Dan Williams   isci: unify isci_...
987
  	/* Assign all the task entries to ihost physical function */
89a7301f2   Dan Williams   isci: retire scic...
988
  	sci_controller_assign_task_entries(ihost);
cc9203bf3   Dan Williams   isci: move core/c...
989
990
  
  	/* Now initialize the completion queue */
89a7301f2   Dan Williams   isci: retire scic...
991
  	sci_controller_initialize_completion_queue(ihost);
cc9203bf3   Dan Williams   isci: move core/c...
992
993
  
  	/* Initialize the unsolicited frame queue for use */
89a7301f2   Dan Williams   isci: retire scic...
994
  	sci_controller_initialize_unsolicited_frame_queue(ihost);
cc9203bf3   Dan Williams   isci: move core/c...
995
996
  
  	/* Start all of the ports on this controller */
d9dcb4ba7   Dan Williams   isci: unify isci_...
997
  	for (index = 0; index < ihost->logical_port_entries; index++) {
ffe191c92   Dan Williams   isci: unify isci_...
998
  		struct isci_port *iport = &ihost->ports[index];
cc9203bf3   Dan Williams   isci: move core/c...
999

89a7301f2   Dan Williams   isci: retire scic...
1000
  		result = sci_port_start(iport);
cc9203bf3   Dan Williams   isci: move core/c...
1001
1002
1003
  		if (result)
  			return result;
  	}
89a7301f2   Dan Williams   isci: retire scic...
1004
  	sci_controller_start_next_phy(ihost);
cc9203bf3   Dan Williams   isci: move core/c...
1005

d9dcb4ba7   Dan Williams   isci: unify isci_...
1006
  	sci_mod_timer(&ihost->timer, timeout);
cc9203bf3   Dan Williams   isci: move core/c...
1007

d9dcb4ba7   Dan Williams   isci: unify isci_...
1008
  	sci_change_state(&ihost->sm, SCIC_STARTING);
cc9203bf3   Dan Williams   isci: move core/c...
1009
1010
1011
  
  	return SCI_SUCCESS;
  }
6f231dda6   Dan Williams   isci: Intel(R) C6...
1012
1013
  void isci_host_scan_start(struct Scsi_Host *shost)
  {
4393aa4e6   Dan Williams   isci: fix fragile...
1014
  	struct isci_host *ihost = SHOST_TO_SAS_HA(shost)->lldd_ha;
89a7301f2   Dan Williams   isci: retire scic...
1015
  	unsigned long tmo = sci_controller_get_suggested_start_timeout(ihost);
6f231dda6   Dan Williams   isci: Intel(R) C6...
1016

0cf89d1d2   Dan Williams   isci: cleanup "st...
1017
  	set_bit(IHOST_START_PENDING, &ihost->flags);
77950f51f   Edmund Nadolski   isci: enable inte...
1018
1019
  
  	spin_lock_irq(&ihost->scic_lock);
89a7301f2   Dan Williams   isci: retire scic...
1020
1021
  	sci_controller_start(ihost, tmo);
  	sci_controller_enable_interrupts(ihost);
77950f51f   Edmund Nadolski   isci: enable inte...
1022
  	spin_unlock_irq(&ihost->scic_lock);
6f231dda6   Dan Williams   isci: Intel(R) C6...
1023
  }
cc9203bf3   Dan Williams   isci: move core/c...
1024
  static void isci_host_stop_complete(struct isci_host *ihost, enum sci_status completion_status)
6f231dda6   Dan Williams   isci: Intel(R) C6...
1025
  {
0cf89d1d2   Dan Williams   isci: cleanup "st...
1026
  	isci_host_change_state(ihost, isci_stopped);
89a7301f2   Dan Williams   isci: retire scic...
1027
  	sci_controller_disable_interrupts(ihost);
0cf89d1d2   Dan Williams   isci: cleanup "st...
1028
1029
  	clear_bit(IHOST_STOP_PENDING, &ihost->flags);
  	wake_up(&ihost->eventq);
6f231dda6   Dan Williams   isci: Intel(R) C6...
1030
  }
89a7301f2   Dan Williams   isci: retire scic...
1031
  static void sci_controller_completion_handler(struct isci_host *ihost)
cc9203bf3   Dan Williams   isci: move core/c...
1032
1033
  {
  	/* Empty out the completion queue */
89a7301f2   Dan Williams   isci: retire scic...
1034
1035
  	if (sci_controller_completion_queue_has_entries(ihost))
  		sci_controller_process_completions(ihost);
cc9203bf3   Dan Williams   isci: move core/c...
1036
1037
  
  	/* Clear the interrupt and enable all interrupts again */
d9dcb4ba7   Dan Williams   isci: unify isci_...
1038
  	writel(SMU_ISR_COMPLETION, &ihost->smu_registers->interrupt_status);
cc9203bf3   Dan Williams   isci: move core/c...
1039
  	/* Could we write the value of SMU_ISR_COMPLETION? */
d9dcb4ba7   Dan Williams   isci: unify isci_...
1040
1041
  	writel(0xFF000000, &ihost->smu_registers->interrupt_mask);
  	writel(0, &ihost->smu_registers->interrupt_mask);
cc9203bf3   Dan Williams   isci: move core/c...
1042
  }
6f231dda6   Dan Williams   isci: Intel(R) C6...
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
  /**
   * isci_host_completion_routine() - This function is the delayed service
   *    routine that calls the sci core library's completion handler. It's
   *    scheduled as a tasklet from the interrupt service routine when interrupts
   *    in use, or set as the timeout function in polled mode.
   * @data: This parameter specifies the ISCI host object
   *
   */
  static void isci_host_completion_routine(unsigned long data)
  {
d9dcb4ba7   Dan Williams   isci: unify isci_...
1053
  	struct isci_host *ihost = (struct isci_host *)data;
11b00c194   Jeff Skirvin   isci: Changes in ...
1054
1055
1056
1057
  	struct list_head    completed_request_list;
  	struct list_head    errored_request_list;
  	struct list_head    *current_position;
  	struct list_head    *next_position;
6f231dda6   Dan Williams   isci: Intel(R) C6...
1058
1059
  	struct isci_request *request;
  	struct isci_request *next_request;
11b00c194   Jeff Skirvin   isci: Changes in ...
1060
  	struct sas_task     *task;
9b4be5289   Dan Williams   [SCSI] isci: dyna...
1061
  	u16 active;
6f231dda6   Dan Williams   isci: Intel(R) C6...
1062
1063
  
  	INIT_LIST_HEAD(&completed_request_list);
11b00c194   Jeff Skirvin   isci: Changes in ...
1064
  	INIT_LIST_HEAD(&errored_request_list);
6f231dda6   Dan Williams   isci: Intel(R) C6...
1065

d9dcb4ba7   Dan Williams   isci: unify isci_...
1066
  	spin_lock_irq(&ihost->scic_lock);
6f231dda6   Dan Williams   isci: Intel(R) C6...
1067

89a7301f2   Dan Williams   isci: retire scic...
1068
  	sci_controller_completion_handler(ihost);
c7ef4031f   Dan Williams   isci: bypass scic...
1069

6f231dda6   Dan Williams   isci: Intel(R) C6...
1070
  	/* Take the lists of completed I/Os from the host. */
11b00c194   Jeff Skirvin   isci: Changes in ...
1071

d9dcb4ba7   Dan Williams   isci: unify isci_...
1072
  	list_splice_init(&ihost->requests_to_complete,
6f231dda6   Dan Williams   isci: Intel(R) C6...
1073
  			 &completed_request_list);
11b00c194   Jeff Skirvin   isci: Changes in ...
1074
  	/* Take the list of errored I/Os from the host. */
d9dcb4ba7   Dan Williams   isci: unify isci_...
1075
  	list_splice_init(&ihost->requests_to_errorback,
11b00c194   Jeff Skirvin   isci: Changes in ...
1076
  			 &errored_request_list);
6f231dda6   Dan Williams   isci: Intel(R) C6...
1077

d9dcb4ba7   Dan Williams   isci: unify isci_...
1078
  	spin_unlock_irq(&ihost->scic_lock);
6f231dda6   Dan Williams   isci: Intel(R) C6...
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
  
  	/* Process any completions in the lists. */
  	list_for_each_safe(current_position, next_position,
  			   &completed_request_list) {
  
  		request = list_entry(current_position, struct isci_request,
  				     completed_node);
  		task = isci_request_access_task(request);
  
  		/* Normal notification (task_done) */
d9dcb4ba7   Dan Williams   isci: unify isci_...
1089
  		dev_dbg(&ihost->pdev->dev,
6f231dda6   Dan Williams   isci: Intel(R) C6...
1090
1091
1092
1093
1094
  			"%s: Normal - request/task = %p/%p
  ",
  			__func__,
  			request,
  			task);
11b00c194   Jeff Skirvin   isci: Changes in ...
1095
1096
1097
1098
1099
  		/* Return the task to libsas */
  		if (task != NULL) {
  
  			task->lldd_task = NULL;
  			if (!(task->task_state_flags & SAS_TASK_STATE_ABORTED)) {
6f231dda6   Dan Williams   isci: Intel(R) C6...
1100

11b00c194   Jeff Skirvin   isci: Changes in ...
1101
1102
1103
1104
1105
1106
  				/* If the task is already in the abort path,
  				* the task_done callback cannot be called.
  				*/
  				task->task_done(task);
  			}
  		}
312e0c245   Dan Williams   isci: unify can_q...
1107

d9dcb4ba7   Dan Williams   isci: unify isci_...
1108
1109
1110
  		spin_lock_irq(&ihost->scic_lock);
  		isci_free_tag(ihost, request->io_tag);
  		spin_unlock_irq(&ihost->scic_lock);
6f231dda6   Dan Williams   isci: Intel(R) C6...
1111
  	}
11b00c194   Jeff Skirvin   isci: Changes in ...
1112
  	list_for_each_entry_safe(request, next_request, &errored_request_list,
6f231dda6   Dan Williams   isci: Intel(R) C6...
1113
1114
1115
1116
1117
  				 completed_node) {
  
  		task = isci_request_access_task(request);
  
  		/* Use sas_task_abort */
d9dcb4ba7   Dan Williams   isci: unify isci_...
1118
  		dev_warn(&ihost->pdev->dev,
6f231dda6   Dan Williams   isci: Intel(R) C6...
1119
1120
1121
1122
1123
  			 "%s: Error - request/task = %p/%p
  ",
  			 __func__,
  			 request,
  			 task);
11b00c194   Jeff Skirvin   isci: Changes in ...
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
  		if (task != NULL) {
  
  			/* Put the task into the abort path if it's not there
  			 * already.
  			 */
  			if (!(task->task_state_flags & SAS_TASK_STATE_ABORTED))
  				sas_task_abort(task);
  
  		} else {
  			/* This is a case where the request has completed with a
  			 * status such that it needed further target servicing,
  			 * but the sas_task reference has already been removed
  			 * from the request.  Since it was errored, it was not
  			 * being aborted, so there is nothing to do except free
  			 * it.
  			 */
d9dcb4ba7   Dan Williams   isci: unify isci_...
1140
  			spin_lock_irq(&ihost->scic_lock);
11b00c194   Jeff Skirvin   isci: Changes in ...
1141
1142
1143
1144
  			/* Remove the request from the remote device's list
  			* of pending requests.
  			*/
  			list_del_init(&request->dev_node);
d9dcb4ba7   Dan Williams   isci: unify isci_...
1145
1146
  			isci_free_tag(ihost, request->io_tag);
  			spin_unlock_irq(&ihost->scic_lock);
11b00c194   Jeff Skirvin   isci: Changes in ...
1147
  		}
6f231dda6   Dan Williams   isci: Intel(R) C6...
1148
  	}
9b4be5289   Dan Williams   [SCSI] isci: dyna...
1149
1150
1151
1152
1153
1154
1155
  	/* the coalesence timeout doubles at each encoding step, so
  	 * update it based on the ilog2 value of the outstanding requests
  	 */
  	active = isci_tci_active(ihost);
  	writel(SMU_ICC_GEN_VAL(NUMBER, active) |
  	       SMU_ICC_GEN_VAL(TIMER, ISCI_COALESCE_BASE + ilog2(active)),
  	       &ihost->smu_registers->interrupt_coalesce_control);
6f231dda6   Dan Williams   isci: Intel(R) C6...
1156
  }
cc9203bf3   Dan Williams   isci: move core/c...
1157
  /**
89a7301f2   Dan Williams   isci: retire scic...
1158
   * sci_controller_stop() - This method will stop an individual controller
cc9203bf3   Dan Williams   isci: move core/c...
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
   *    object.This method will invoke the associated user callback upon
   *    completion.  The completion callback is called when the following
   *    conditions are met: -# the method return status is SCI_SUCCESS. -# the
   *    controller has been quiesced. This method will ensure that all IO
   *    requests are quiesced, phys are stopped, and all additional operation by
   *    the hardware is halted.
   * @controller: the handle to the controller object to stop.
   * @timeout: This parameter specifies the number of milliseconds in which the
   *    stop operation should complete.
   *
   * The controller must be in the STARTED or STOPPED state. Indicate if the
   * controller stop method succeeded or failed in some way. SCI_SUCCESS if the
   * stop operation successfully began. SCI_WARNING_ALREADY_IN_STATE if the
   * controller is already in the STOPPED state. SCI_FAILURE_INVALID_STATE if the
   * controller is not either in the STARTED or STOPPED states.
   */
89a7301f2   Dan Williams   isci: retire scic...
1175
  static enum sci_status sci_controller_stop(struct isci_host *ihost, u32 timeout)
6f231dda6   Dan Williams   isci: Intel(R) C6...
1176
  {
d9dcb4ba7   Dan Williams   isci: unify isci_...
1177
1178
  	if (ihost->sm.current_state_id != SCIC_READY) {
  		dev_warn(&ihost->pdev->dev,
cc9203bf3   Dan Williams   isci: move core/c...
1179
1180
1181
1182
1183
  			 "SCIC Controller stop operation requested in "
  			 "invalid state
  ");
  		return SCI_FAILURE_INVALID_STATE;
  	}
6f231dda6   Dan Williams   isci: Intel(R) C6...
1184

d9dcb4ba7   Dan Williams   isci: unify isci_...
1185
1186
  	sci_mod_timer(&ihost->timer, timeout);
  	sci_change_state(&ihost->sm, SCIC_STOPPING);
cc9203bf3   Dan Williams   isci: move core/c...
1187
1188
1189
1190
  	return SCI_SUCCESS;
  }
  
  /**
89a7301f2   Dan Williams   isci: retire scic...
1191
   * sci_controller_reset() - This method will reset the supplied core
cc9203bf3   Dan Williams   isci: move core/c...
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
   *    controller regardless of the state of said controller.  This operation is
   *    considered destructive.  In other words, all current operations are wiped
   *    out.  No IO completions for outstanding devices occur.  Outstanding IO
   *    requests are not aborted or completed at the actual remote device.
   * @controller: the handle to the controller object to reset.
   *
   * Indicate if the controller reset method succeeded or failed in some way.
   * SCI_SUCCESS if the reset operation successfully started. SCI_FATAL_ERROR if
   * the controller reset operation is unable to complete.
   */
89a7301f2   Dan Williams   isci: retire scic...
1202
  static enum sci_status sci_controller_reset(struct isci_host *ihost)
cc9203bf3   Dan Williams   isci: move core/c...
1203
  {
d9dcb4ba7   Dan Williams   isci: unify isci_...
1204
  	switch (ihost->sm.current_state_id) {
e301370ac   Edmund Nadolski   isci: state machi...
1205
1206
1207
1208
  	case SCIC_RESET:
  	case SCIC_READY:
  	case SCIC_STOPPED:
  	case SCIC_FAILED:
cc9203bf3   Dan Williams   isci: move core/c...
1209
1210
1211
1212
  		/*
  		 * The reset operation is not a graceful cleanup, just
  		 * perform the state transition.
  		 */
d9dcb4ba7   Dan Williams   isci: unify isci_...
1213
  		sci_change_state(&ihost->sm, SCIC_RESETTING);
cc9203bf3   Dan Williams   isci: move core/c...
1214
1215
  		return SCI_SUCCESS;
  	default:
d9dcb4ba7   Dan Williams   isci: unify isci_...
1216
  		dev_warn(&ihost->pdev->dev,
cc9203bf3   Dan Williams   isci: move core/c...
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
  			 "SCIC Controller reset operation requested in "
  			 "invalid state
  ");
  		return SCI_FAILURE_INVALID_STATE;
  	}
  }
  
  void isci_host_deinit(struct isci_host *ihost)
  {
  	int i;
ad4f4c1de   Dan Williams   [SCSI] isci: init...
1227
1228
1229
  	/* disable output data selects */
  	for (i = 0; i < isci_gpio_count(ihost); i++)
  		writel(SGPIO_HW_CONTROL, &ihost->scu_registers->peg0.sgpio.output_data_select[i]);
cc9203bf3   Dan Williams   isci: move core/c...
1230
  	isci_host_change_state(ihost, isci_stopping);
6f231dda6   Dan Williams   isci: Intel(R) C6...
1231
  	for (i = 0; i < SCI_MAX_PORTS; i++) {
e531381e2   Dan Williams   isci: unify port ...
1232
  		struct isci_port *iport = &ihost->ports[i];
0cf89d1d2   Dan Williams   isci: cleanup "st...
1233
  		struct isci_remote_device *idev, *d;
e531381e2   Dan Williams   isci: unify port ...
1234
  		list_for_each_entry_safe(idev, d, &iport->remote_dev_list, node) {
209fae14f   Dan Williams   isci: atomic devi...
1235
1236
  			if (test_bit(IDEV_ALLOCATED, &idev->flags))
  				isci_remote_device_stop(ihost, idev);
6f231dda6   Dan Williams   isci: Intel(R) C6...
1237
1238
  		}
  	}
0cf89d1d2   Dan Williams   isci: cleanup "st...
1239
  	set_bit(IHOST_STOP_PENDING, &ihost->flags);
7c40a8035   Dan Williams   isci: rework time...
1240
1241
  
  	spin_lock_irq(&ihost->scic_lock);
89a7301f2   Dan Williams   isci: retire scic...
1242
  	sci_controller_stop(ihost, SCIC_CONTROLLER_STOP_TIMEOUT);
7c40a8035   Dan Williams   isci: rework time...
1243
  	spin_unlock_irq(&ihost->scic_lock);
0cf89d1d2   Dan Williams   isci: cleanup "st...
1244
  	wait_for_stop(ihost);
ad4f4c1de   Dan Williams   [SCSI] isci: init...
1245
1246
1247
1248
1249
  
  	/* disable sgpio: where the above wait should give time for the
  	 * enclosure to sample the gpios going inactive
  	 */
  	writel(0, &ihost->scu_registers->peg0.sgpio.interface_control);
89a7301f2   Dan Williams   isci: retire scic...
1250
  	sci_controller_reset(ihost);
5553ba2be   Edmund Nadolski   isci: replace isc...
1251
1252
  
  	/* Cancel any/all outstanding port timers */
d9dcb4ba7   Dan Williams   isci: unify isci_...
1253
  	for (i = 0; i < ihost->logical_port_entries; i++) {
ffe191c92   Dan Williams   isci: unify isci_...
1254
1255
  		struct isci_port *iport = &ihost->ports[i];
  		del_timer_sync(&iport->timer.timer);
5553ba2be   Edmund Nadolski   isci: replace isc...
1256
  	}
a628d4785   Edmund Nadolski   isci: convert phy...
1257
1258
  	/* Cancel any/all outstanding phy timers */
  	for (i = 0; i < SCI_MAX_PHYS; i++) {
852809559   Dan Williams   isci: unify isci_...
1259
1260
  		struct isci_phy *iphy = &ihost->phys[i];
  		del_timer_sync(&iphy->sata_timer.timer);
a628d4785   Edmund Nadolski   isci: convert phy...
1261
  	}
d9dcb4ba7   Dan Williams   isci: unify isci_...
1262
  	del_timer_sync(&ihost->port_agent.timer.timer);
ac0eeb4f7   Edmund Nadolski   isci: convert por...
1263

d9dcb4ba7   Dan Williams   isci: unify isci_...
1264
  	del_timer_sync(&ihost->power_control.timer.timer);
0473661a1   Edmund Nadolski   isci: convert pow...
1265

d9dcb4ba7   Dan Williams   isci: unify isci_...
1266
  	del_timer_sync(&ihost->timer.timer);
6cb5853d3   Edmund Nadolski   isci: convert sci...
1267

d9dcb4ba7   Dan Williams   isci: unify isci_...
1268
  	del_timer_sync(&ihost->phy_timer.timer);
6f231dda6   Dan Williams   isci: Intel(R) C6...
1269
  }
6f231dda6   Dan Williams   isci: Intel(R) C6...
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
  static void __iomem *scu_base(struct isci_host *isci_host)
  {
  	struct pci_dev *pdev = isci_host->pdev;
  	int id = isci_host->id;
  
  	return pcim_iomap_table(pdev)[SCI_SCU_BAR * 2] + SCI_SCU_BAR_SIZE * id;
  }
  
  static void __iomem *smu_base(struct isci_host *isci_host)
  {
  	struct pci_dev *pdev = isci_host->pdev;
  	int id = isci_host->id;
  
  	return pcim_iomap_table(pdev)[SCI_SMU_BAR * 2] + SCI_SMU_BAR_SIZE * id;
  }
89a7301f2   Dan Williams   isci: retire scic...
1285
  static void isci_user_parameters_get(struct sci_user_parameters *u)
b5f18a201   Dave Jiang   isci: exposing us...
1286
  {
b5f18a201   Dave Jiang   isci: exposing us...
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
  	int i;
  
  	for (i = 0; i < SCI_MAX_PHYS; i++) {
  		struct sci_phy_user_params *u_phy = &u->phys[i];
  
  		u_phy->max_speed_generation = phy_gen;
  
  		/* we are not exporting these for now */
  		u_phy->align_insertion_frequency = 0x7f;
  		u_phy->in_connection_align_insertion_frequency = 0xff;
  		u_phy->notify_enable_spin_up_insertion_frequency = 0x33;
  	}
  
  	u->stp_inactivity_timeout = stp_inactive_to;
  	u->ssp_inactivity_timeout = ssp_inactive_to;
  	u->stp_max_occupancy_timeout = stp_max_occ_to;
  	u->ssp_max_occupancy_timeout = ssp_max_occ_to;
  	u->no_outbound_task_timeout = no_outbound_task_to;
7000f7c71   Andrzej Jakowski   [SCSI] isci: over...
1305
  	u->max_concurr_spinup = max_concurr_spinup;
b5f18a201   Dave Jiang   isci: exposing us...
1306
  }
89a7301f2   Dan Williams   isci: retire scic...
1307
  static void sci_controller_initial_state_enter(struct sci_base_state_machine *sm)
cc9203bf3   Dan Williams   isci: move core/c...
1308
  {
d9dcb4ba7   Dan Williams   isci: unify isci_...
1309
  	struct isci_host *ihost = container_of(sm, typeof(*ihost), sm);
cc9203bf3   Dan Williams   isci: move core/c...
1310

d9dcb4ba7   Dan Williams   isci: unify isci_...
1311
  	sci_change_state(&ihost->sm, SCIC_RESET);
cc9203bf3   Dan Williams   isci: move core/c...
1312
  }
89a7301f2   Dan Williams   isci: retire scic...
1313
  static inline void sci_controller_starting_state_exit(struct sci_base_state_machine *sm)
cc9203bf3   Dan Williams   isci: move core/c...
1314
  {
d9dcb4ba7   Dan Williams   isci: unify isci_...
1315
  	struct isci_host *ihost = container_of(sm, typeof(*ihost), sm);
cc9203bf3   Dan Williams   isci: move core/c...
1316

d9dcb4ba7   Dan Williams   isci: unify isci_...
1317
  	sci_del_timer(&ihost->timer);
cc9203bf3   Dan Williams   isci: move core/c...
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
  }
  
  #define INTERRUPT_COALESCE_TIMEOUT_BASE_RANGE_LOWER_BOUND_NS 853
  #define INTERRUPT_COALESCE_TIMEOUT_BASE_RANGE_UPPER_BOUND_NS 1280
  #define INTERRUPT_COALESCE_TIMEOUT_MAX_US                    2700000
  #define INTERRUPT_COALESCE_NUMBER_MAX                        256
  #define INTERRUPT_COALESCE_TIMEOUT_ENCODE_MIN                7
  #define INTERRUPT_COALESCE_TIMEOUT_ENCODE_MAX                28
  
  /**
89a7301f2   Dan Williams   isci: retire scic...
1328
   * sci_controller_set_interrupt_coalescence() - This method allows the user to
cc9203bf3   Dan Williams   isci: move core/c...
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
   *    configure the interrupt coalescence.
   * @controller: This parameter represents the handle to the controller object
   *    for which its interrupt coalesce register is overridden.
   * @coalesce_number: Used to control the number of entries in the Completion
   *    Queue before an interrupt is generated. If the number of entries exceed
   *    this number, an interrupt will be generated. The valid range of the input
   *    is [0, 256]. A setting of 0 results in coalescing being disabled.
   * @coalesce_timeout: Timeout value in microseconds. The valid range of the
   *    input is [0, 2700000] . A setting of 0 is allowed and results in no
   *    interrupt coalescing timeout.
   *
   * Indicate if the user successfully set the interrupt coalesce parameters.
   * SCI_SUCCESS The user successfully updated the interrutp coalescence.
   * SCI_FAILURE_INVALID_PARAMETER_VALUE The user input value is out of range.
   */
d9dcb4ba7   Dan Williams   isci: unify isci_...
1344
  static enum sci_status
89a7301f2   Dan Williams   isci: retire scic...
1345
1346
1347
  sci_controller_set_interrupt_coalescence(struct isci_host *ihost,
  					 u32 coalesce_number,
  					 u32 coalesce_timeout)
cc9203bf3   Dan Williams   isci: move core/c...
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
  {
  	u8 timeout_encode = 0;
  	u32 min = 0;
  	u32 max = 0;
  
  	/* Check if the input parameters fall in the range. */
  	if (coalesce_number > INTERRUPT_COALESCE_NUMBER_MAX)
  		return SCI_FAILURE_INVALID_PARAMETER_VALUE;
  
  	/*
  	 *  Defined encoding for interrupt coalescing timeout:
  	 *              Value   Min      Max     Units
  	 *              -----   ---      ---     -----
  	 *              0       -        -       Disabled
  	 *              1       13.3     20.0    ns
  	 *              2       26.7     40.0
  	 *              3       53.3     80.0
  	 *              4       106.7    160.0
  	 *              5       213.3    320.0
  	 *              6       426.7    640.0
  	 *              7       853.3    1280.0
  	 *              8       1.7      2.6     us
  	 *              9       3.4      5.1
  	 *              10      6.8      10.2
  	 *              11      13.7     20.5
  	 *              12      27.3     41.0
  	 *              13      54.6     81.9
  	 *              14      109.2    163.8
  	 *              15      218.5    327.7
  	 *              16      436.9    655.4
  	 *              17      873.8    1310.7
  	 *              18      1.7      2.6     ms
  	 *              19      3.5      5.2
  	 *              20      7.0      10.5
  	 *              21      14.0     21.0
  	 *              22      28.0     41.9
  	 *              23      55.9     83.9
  	 *              24      111.8    167.8
  	 *              25      223.7    335.5
  	 *              26      447.4    671.1
  	 *              27      894.8    1342.2
  	 *              28      1.8      2.7     s
  	 *              Others Undefined */
  
  	/*
  	 * Use the table above to decide the encode of interrupt coalescing timeout
  	 * value for register writing. */
  	if (coalesce_timeout == 0)
  		timeout_encode = 0;
  	else{
  		/* make the timeout value in unit of (10 ns). */
  		coalesce_timeout = coalesce_timeout * 100;
  		min = INTERRUPT_COALESCE_TIMEOUT_BASE_RANGE_LOWER_BOUND_NS / 10;
  		max = INTERRUPT_COALESCE_TIMEOUT_BASE_RANGE_UPPER_BOUND_NS / 10;
  
  		/* get the encode of timeout for register writing. */
  		for (timeout_encode = INTERRUPT_COALESCE_TIMEOUT_ENCODE_MIN;
  		      timeout_encode <= INTERRUPT_COALESCE_TIMEOUT_ENCODE_MAX;
  		      timeout_encode++) {
  			if (min <= coalesce_timeout &&  max > coalesce_timeout)
  				break;
  			else if (coalesce_timeout >= max && coalesce_timeout < min * 2
  				 && coalesce_timeout <= INTERRUPT_COALESCE_TIMEOUT_MAX_US * 100) {
  				if ((coalesce_timeout - max) < (2 * min - coalesce_timeout))
  					break;
  				else{
  					timeout_encode++;
  					break;
  				}
  			} else {
  				max = max * 2;
  				min = min * 2;
  			}
  		}
  
  		if (timeout_encode == INTERRUPT_COALESCE_TIMEOUT_ENCODE_MAX + 1)
  			/* the value is out of range. */
  			return SCI_FAILURE_INVALID_PARAMETER_VALUE;
  	}
  
  	writel(SMU_ICC_GEN_VAL(NUMBER, coalesce_number) |
  	       SMU_ICC_GEN_VAL(TIMER, timeout_encode),
d9dcb4ba7   Dan Williams   isci: unify isci_...
1430
  	       &ihost->smu_registers->interrupt_coalesce_control);
cc9203bf3   Dan Williams   isci: move core/c...
1431

d9dcb4ba7   Dan Williams   isci: unify isci_...
1432
1433
  	ihost->interrupt_coalesce_number = (u16)coalesce_number;
  	ihost->interrupt_coalesce_timeout = coalesce_timeout / 100;
cc9203bf3   Dan Williams   isci: move core/c...
1434
1435
1436
  
  	return SCI_SUCCESS;
  }
89a7301f2   Dan Williams   isci: retire scic...
1437
  static void sci_controller_ready_state_enter(struct sci_base_state_machine *sm)
cc9203bf3   Dan Williams   isci: move core/c...
1438
  {
d9dcb4ba7   Dan Williams   isci: unify isci_...
1439
  	struct isci_host *ihost = container_of(sm, typeof(*ihost), sm);
cc9203bf3   Dan Williams   isci: move core/c...
1440
1441
  
  	/* set the default interrupt coalescence number and timeout value. */
9b4be5289   Dan Williams   [SCSI] isci: dyna...
1442
  	sci_controller_set_interrupt_coalescence(ihost, 0, 0);
cc9203bf3   Dan Williams   isci: move core/c...
1443
  }
89a7301f2   Dan Williams   isci: retire scic...
1444
  static void sci_controller_ready_state_exit(struct sci_base_state_machine *sm)
cc9203bf3   Dan Williams   isci: move core/c...
1445
  {
d9dcb4ba7   Dan Williams   isci: unify isci_...
1446
  	struct isci_host *ihost = container_of(sm, typeof(*ihost), sm);
cc9203bf3   Dan Williams   isci: move core/c...
1447
1448
  
  	/* disable interrupt coalescence. */
89a7301f2   Dan Williams   isci: retire scic...
1449
  	sci_controller_set_interrupt_coalescence(ihost, 0, 0);
cc9203bf3   Dan Williams   isci: move core/c...
1450
  }
89a7301f2   Dan Williams   isci: retire scic...
1451
  static enum sci_status sci_controller_stop_phys(struct isci_host *ihost)
cc9203bf3   Dan Williams   isci: move core/c...
1452
1453
1454
1455
  {
  	u32 index;
  	enum sci_status status;
  	enum sci_status phy_status;
cc9203bf3   Dan Williams   isci: move core/c...
1456
1457
1458
1459
  
  	status = SCI_SUCCESS;
  
  	for (index = 0; index < SCI_MAX_PHYS; index++) {
89a7301f2   Dan Williams   isci: retire scic...
1460
  		phy_status = sci_phy_stop(&ihost->phys[index]);
cc9203bf3   Dan Williams   isci: move core/c...
1461
1462
1463
1464
  
  		if (phy_status != SCI_SUCCESS &&
  		    phy_status != SCI_FAILURE_INVALID_STATE) {
  			status = SCI_FAILURE;
d9dcb4ba7   Dan Williams   isci: unify isci_...
1465
  			dev_warn(&ihost->pdev->dev,
cc9203bf3   Dan Williams   isci: move core/c...
1466
1467
1468
1469
  				 "%s: Controller stop operation failed to stop "
  				 "phy %d because of status %d.
  ",
  				 __func__,
852809559   Dan Williams   isci: unify isci_...
1470
  				 ihost->phys[index].phy_index, phy_status);
cc9203bf3   Dan Williams   isci: move core/c...
1471
1472
1473
1474
1475
  		}
  	}
  
  	return status;
  }
89a7301f2   Dan Williams   isci: retire scic...
1476
  static enum sci_status sci_controller_stop_ports(struct isci_host *ihost)
cc9203bf3   Dan Williams   isci: move core/c...
1477
1478
1479
1480
  {
  	u32 index;
  	enum sci_status port_status;
  	enum sci_status status = SCI_SUCCESS;
cc9203bf3   Dan Williams   isci: move core/c...
1481

d9dcb4ba7   Dan Williams   isci: unify isci_...
1482
  	for (index = 0; index < ihost->logical_port_entries; index++) {
ffe191c92   Dan Williams   isci: unify isci_...
1483
  		struct isci_port *iport = &ihost->ports[index];
cc9203bf3   Dan Williams   isci: move core/c...
1484

89a7301f2   Dan Williams   isci: retire scic...
1485
  		port_status = sci_port_stop(iport);
cc9203bf3   Dan Williams   isci: move core/c...
1486
1487
1488
1489
  
  		if ((port_status != SCI_SUCCESS) &&
  		    (port_status != SCI_FAILURE_INVALID_STATE)) {
  			status = SCI_FAILURE;
d9dcb4ba7   Dan Williams   isci: unify isci_...
1490
  			dev_warn(&ihost->pdev->dev,
cc9203bf3   Dan Williams   isci: move core/c...
1491
1492
1493
1494
  				 "%s: Controller stop operation failed to "
  				 "stop port %d because of status %d.
  ",
  				 __func__,
ffe191c92   Dan Williams   isci: unify isci_...
1495
  				 iport->logical_port_index,
cc9203bf3   Dan Williams   isci: move core/c...
1496
1497
1498
1499
1500
1501
  				 port_status);
  		}
  	}
  
  	return status;
  }
89a7301f2   Dan Williams   isci: retire scic...
1502
  static enum sci_status sci_controller_stop_devices(struct isci_host *ihost)
cc9203bf3   Dan Williams   isci: move core/c...
1503
1504
1505
1506
1507
1508
  {
  	u32 index;
  	enum sci_status status;
  	enum sci_status device_status;
  
  	status = SCI_SUCCESS;
d9dcb4ba7   Dan Williams   isci: unify isci_...
1509
1510
  	for (index = 0; index < ihost->remote_node_entries; index++) {
  		if (ihost->device_table[index] != NULL) {
cc9203bf3   Dan Williams   isci: move core/c...
1511
  			/* / @todo What timeout value do we want to provide to this request? */
89a7301f2   Dan Williams   isci: retire scic...
1512
  			device_status = sci_remote_device_stop(ihost->device_table[index], 0);
cc9203bf3   Dan Williams   isci: move core/c...
1513
1514
1515
  
  			if ((device_status != SCI_SUCCESS) &&
  			    (device_status != SCI_FAILURE_INVALID_STATE)) {
d9dcb4ba7   Dan Williams   isci: unify isci_...
1516
  				dev_warn(&ihost->pdev->dev,
cc9203bf3   Dan Williams   isci: move core/c...
1517
1518
1519
1520
1521
  					 "%s: Controller stop operation failed "
  					 "to stop device 0x%p because of "
  					 "status %d.
  ",
  					 __func__,
d9dcb4ba7   Dan Williams   isci: unify isci_...
1522
  					 ihost->device_table[index], device_status);
cc9203bf3   Dan Williams   isci: move core/c...
1523
1524
1525
1526
1527
1528
  			}
  		}
  	}
  
  	return status;
  }
89a7301f2   Dan Williams   isci: retire scic...
1529
  static void sci_controller_stopping_state_enter(struct sci_base_state_machine *sm)
cc9203bf3   Dan Williams   isci: move core/c...
1530
  {
d9dcb4ba7   Dan Williams   isci: unify isci_...
1531
  	struct isci_host *ihost = container_of(sm, typeof(*ihost), sm);
cc9203bf3   Dan Williams   isci: move core/c...
1532
1533
  
  	/* Stop all of the components for this controller */
89a7301f2   Dan Williams   isci: retire scic...
1534
1535
1536
  	sci_controller_stop_phys(ihost);
  	sci_controller_stop_ports(ihost);
  	sci_controller_stop_devices(ihost);
cc9203bf3   Dan Williams   isci: move core/c...
1537
  }
89a7301f2   Dan Williams   isci: retire scic...
1538
  static void sci_controller_stopping_state_exit(struct sci_base_state_machine *sm)
cc9203bf3   Dan Williams   isci: move core/c...
1539
  {
d9dcb4ba7   Dan Williams   isci: unify isci_...
1540
  	struct isci_host *ihost = container_of(sm, typeof(*ihost), sm);
cc9203bf3   Dan Williams   isci: move core/c...
1541

d9dcb4ba7   Dan Williams   isci: unify isci_...
1542
  	sci_del_timer(&ihost->timer);
cc9203bf3   Dan Williams   isci: move core/c...
1543
  }
89a7301f2   Dan Williams   isci: retire scic...
1544
  static void sci_controller_reset_hardware(struct isci_host *ihost)
cc9203bf3   Dan Williams   isci: move core/c...
1545
1546
  {
  	/* Disable interrupts so we dont take any spurious interrupts */
89a7301f2   Dan Williams   isci: retire scic...
1547
  	sci_controller_disable_interrupts(ihost);
cc9203bf3   Dan Williams   isci: move core/c...
1548
1549
  
  	/* Reset the SCU */
d9dcb4ba7   Dan Williams   isci: unify isci_...
1550
  	writel(0xFFFFFFFF, &ihost->smu_registers->soft_reset_control);
cc9203bf3   Dan Williams   isci: move core/c...
1551
1552
1553
1554
1555
  
  	/* Delay for 1ms to before clearing the CQP and UFQPR. */
  	udelay(1000);
  
  	/* The write to the CQGR clears the CQP */
d9dcb4ba7   Dan Williams   isci: unify isci_...
1556
  	writel(0x00000000, &ihost->smu_registers->completion_queue_get);
cc9203bf3   Dan Williams   isci: move core/c...
1557
1558
  
  	/* The write to the UFQGP clears the UFQPR */
d9dcb4ba7   Dan Williams   isci: unify isci_...
1559
  	writel(0, &ihost->scu_registers->sdma.unsolicited_frame_get_pointer);
cc9203bf3   Dan Williams   isci: move core/c...
1560
  }
89a7301f2   Dan Williams   isci: retire scic...
1561
  static void sci_controller_resetting_state_enter(struct sci_base_state_machine *sm)
cc9203bf3   Dan Williams   isci: move core/c...
1562
  {
d9dcb4ba7   Dan Williams   isci: unify isci_...
1563
  	struct isci_host *ihost = container_of(sm, typeof(*ihost), sm);
cc9203bf3   Dan Williams   isci: move core/c...
1564

89a7301f2   Dan Williams   isci: retire scic...
1565
  	sci_controller_reset_hardware(ihost);
d9dcb4ba7   Dan Williams   isci: unify isci_...
1566
  	sci_change_state(&ihost->sm, SCIC_RESET);
cc9203bf3   Dan Williams   isci: move core/c...
1567
  }
89a7301f2   Dan Williams   isci: retire scic...
1568
  static const struct sci_base_state sci_controller_state_table[] = {
e301370ac   Edmund Nadolski   isci: state machi...
1569
  	[SCIC_INITIAL] = {
89a7301f2   Dan Williams   isci: retire scic...
1570
  		.enter_state = sci_controller_initial_state_enter,
cc9203bf3   Dan Williams   isci: move core/c...
1571
  	},
e301370ac   Edmund Nadolski   isci: state machi...
1572
1573
1574
1575
  	[SCIC_RESET] = {},
  	[SCIC_INITIALIZING] = {},
  	[SCIC_INITIALIZED] = {},
  	[SCIC_STARTING] = {
89a7301f2   Dan Williams   isci: retire scic...
1576
  		.exit_state  = sci_controller_starting_state_exit,
cc9203bf3   Dan Williams   isci: move core/c...
1577
  	},
e301370ac   Edmund Nadolski   isci: state machi...
1578
  	[SCIC_READY] = {
89a7301f2   Dan Williams   isci: retire scic...
1579
1580
  		.enter_state = sci_controller_ready_state_enter,
  		.exit_state  = sci_controller_ready_state_exit,
cc9203bf3   Dan Williams   isci: move core/c...
1581
  	},
e301370ac   Edmund Nadolski   isci: state machi...
1582
  	[SCIC_RESETTING] = {
89a7301f2   Dan Williams   isci: retire scic...
1583
  		.enter_state = sci_controller_resetting_state_enter,
cc9203bf3   Dan Williams   isci: move core/c...
1584
  	},
e301370ac   Edmund Nadolski   isci: state machi...
1585
  	[SCIC_STOPPING] = {
89a7301f2   Dan Williams   isci: retire scic...
1586
1587
  		.enter_state = sci_controller_stopping_state_enter,
  		.exit_state = sci_controller_stopping_state_exit,
cc9203bf3   Dan Williams   isci: move core/c...
1588
  	},
e301370ac   Edmund Nadolski   isci: state machi...
1589
1590
  	[SCIC_STOPPED] = {},
  	[SCIC_FAILED] = {}
cc9203bf3   Dan Williams   isci: move core/c...
1591
  };
89a7301f2   Dan Williams   isci: retire scic...
1592
  static void sci_controller_set_default_config_parameters(struct isci_host *ihost)
cc9203bf3   Dan Williams   isci: move core/c...
1593
1594
  {
  	/* these defaults are overridden by the platform / firmware */
cc9203bf3   Dan Williams   isci: move core/c...
1595
1596
1597
  	u16 index;
  
  	/* Default to APC mode. */
89a7301f2   Dan Williams   isci: retire scic...
1598
  	ihost->oem_parameters.controller.mode_type = SCIC_PORT_AUTOMATIC_CONFIGURATION_MODE;
cc9203bf3   Dan Williams   isci: move core/c...
1599
1600
  
  	/* Default to APC mode. */
7000f7c71   Andrzej Jakowski   [SCSI] isci: over...
1601
  	ihost->oem_parameters.controller.max_concurr_spin_up = 1;
cc9203bf3   Dan Williams   isci: move core/c...
1602
1603
  
  	/* Default to no SSC operation. */
89a7301f2   Dan Williams   isci: retire scic...
1604
  	ihost->oem_parameters.controller.do_enable_ssc = false;
cc9203bf3   Dan Williams   isci: move core/c...
1605
1606
1607
  
  	/* Initialize all of the port parameter information to narrow ports. */
  	for (index = 0; index < SCI_MAX_PORTS; index++) {
89a7301f2   Dan Williams   isci: retire scic...
1608
  		ihost->oem_parameters.ports[index].phy_mask = 0;
cc9203bf3   Dan Williams   isci: move core/c...
1609
1610
1611
1612
1613
  	}
  
  	/* Initialize all of the phy parameter information. */
  	for (index = 0; index < SCI_MAX_PHYS; index++) {
  		/* Default to 6G (i.e. Gen 3) for now. */
89a7301f2   Dan Williams   isci: retire scic...
1614
  		ihost->user_parameters.phys[index].max_speed_generation = 3;
cc9203bf3   Dan Williams   isci: move core/c...
1615
1616
  
  		/* the frequencies cannot be 0 */
89a7301f2   Dan Williams   isci: retire scic...
1617
1618
1619
  		ihost->user_parameters.phys[index].align_insertion_frequency = 0x7f;
  		ihost->user_parameters.phys[index].in_connection_align_insertion_frequency = 0xff;
  		ihost->user_parameters.phys[index].notify_enable_spin_up_insertion_frequency = 0x33;
cc9203bf3   Dan Williams   isci: move core/c...
1620
1621
1622
1623
1624
1625
  
  		/*
  		 * Previous Vitesse based expanders had a arbitration issue that
  		 * is worked around by having the upper 32-bits of SAS address
  		 * with a value greater then the Vitesse company identifier.
  		 * Hence, usage of 0x5FCFFFFF. */
89a7301f2   Dan Williams   isci: retire scic...
1626
1627
  		ihost->oem_parameters.phys[index].sas_address.low = 0x1 + ihost->id;
  		ihost->oem_parameters.phys[index].sas_address.high = 0x5FCFFFFF;
cc9203bf3   Dan Williams   isci: move core/c...
1628
  	}
89a7301f2   Dan Williams   isci: retire scic...
1629
1630
1631
1632
1633
  	ihost->user_parameters.stp_inactivity_timeout = 5;
  	ihost->user_parameters.ssp_inactivity_timeout = 5;
  	ihost->user_parameters.stp_max_occupancy_timeout = 5;
  	ihost->user_parameters.ssp_max_occupancy_timeout = 20;
  	ihost->user_parameters.no_outbound_task_timeout = 20;
cc9203bf3   Dan Williams   isci: move core/c...
1634
  }
6cb5853d3   Edmund Nadolski   isci: convert sci...
1635
1636
1637
  static void controller_timeout(unsigned long data)
  {
  	struct sci_timer *tmr = (struct sci_timer *)data;
d9dcb4ba7   Dan Williams   isci: unify isci_...
1638
1639
  	struct isci_host *ihost = container_of(tmr, typeof(*ihost), timer);
  	struct sci_base_state_machine *sm = &ihost->sm;
6cb5853d3   Edmund Nadolski   isci: convert sci...
1640
1641
1642
1643
1644
1645
  	unsigned long flags;
  
  	spin_lock_irqsave(&ihost->scic_lock, flags);
  
  	if (tmr->cancel)
  		goto done;
e301370ac   Edmund Nadolski   isci: state machi...
1646
  	if (sm->current_state_id == SCIC_STARTING)
89a7301f2   Dan Williams   isci: retire scic...
1647
  		sci_controller_transition_to_ready(ihost, SCI_FAILURE_TIMEOUT);
e301370ac   Edmund Nadolski   isci: state machi...
1648
1649
  	else if (sm->current_state_id == SCIC_STOPPING) {
  		sci_change_state(sm, SCIC_FAILED);
6cb5853d3   Edmund Nadolski   isci: convert sci...
1650
1651
  		isci_host_stop_complete(ihost, SCI_FAILURE_TIMEOUT);
  	} else	/* / @todo Now what do we want to do in this case? */
d9dcb4ba7   Dan Williams   isci: unify isci_...
1652
  		dev_err(&ihost->pdev->dev,
6cb5853d3   Edmund Nadolski   isci: convert sci...
1653
1654
1655
1656
  			"%s: Controller timer fired when controller was not "
  			"in a state being timed.
  ",
  			__func__);
cc9203bf3   Dan Williams   isci: move core/c...
1657

6cb5853d3   Edmund Nadolski   isci: convert sci...
1658
1659
1660
  done:
  	spin_unlock_irqrestore(&ihost->scic_lock, flags);
  }
cc9203bf3   Dan Williams   isci: move core/c...
1661

89a7301f2   Dan Williams   isci: retire scic...
1662
1663
1664
  static enum sci_status sci_controller_construct(struct isci_host *ihost,
  						void __iomem *scu_base,
  						void __iomem *smu_base)
cc9203bf3   Dan Williams   isci: move core/c...
1665
  {
cc9203bf3   Dan Williams   isci: move core/c...
1666
  	u8 i;
89a7301f2   Dan Williams   isci: retire scic...
1667
  	sci_init_sm(&ihost->sm, sci_controller_state_table, SCIC_INITIAL);
cc9203bf3   Dan Williams   isci: move core/c...
1668

d9dcb4ba7   Dan Williams   isci: unify isci_...
1669
1670
  	ihost->scu_registers = scu_base;
  	ihost->smu_registers = smu_base;
cc9203bf3   Dan Williams   isci: move core/c...
1671

89a7301f2   Dan Williams   isci: retire scic...
1672
  	sci_port_configuration_agent_construct(&ihost->port_agent);
cc9203bf3   Dan Williams   isci: move core/c...
1673
1674
1675
  
  	/* Construct the ports for this controller */
  	for (i = 0; i < SCI_MAX_PORTS; i++)
89a7301f2   Dan Williams   isci: retire scic...
1676
1677
  		sci_port_construct(&ihost->ports[i], i, ihost);
  	sci_port_construct(&ihost->ports[i], SCIC_SDS_DUMMY_PORT, ihost);
cc9203bf3   Dan Williams   isci: move core/c...
1678
1679
1680
1681
  
  	/* Construct the phys for this controller */
  	for (i = 0; i < SCI_MAX_PHYS; i++) {
  		/* Add all the PHYs to the dummy port */
89a7301f2   Dan Williams   isci: retire scic...
1682
1683
  		sci_phy_construct(&ihost->phys[i],
  				  &ihost->ports[SCI_MAX_PORTS], i);
cc9203bf3   Dan Williams   isci: move core/c...
1684
  	}
d9dcb4ba7   Dan Williams   isci: unify isci_...
1685
  	ihost->invalid_phy_mask = 0;
cc9203bf3   Dan Williams   isci: move core/c...
1686

d9dcb4ba7   Dan Williams   isci: unify isci_...
1687
  	sci_init_timer(&ihost->timer, controller_timeout);
6cb5853d3   Edmund Nadolski   isci: convert sci...
1688

cc9203bf3   Dan Williams   isci: move core/c...
1689
  	/* Initialize the User and OEM parameters to default values. */
89a7301f2   Dan Williams   isci: retire scic...
1690
  	sci_controller_set_default_config_parameters(ihost);
cc9203bf3   Dan Williams   isci: move core/c...
1691

89a7301f2   Dan Williams   isci: retire scic...
1692
  	return sci_controller_reset(ihost);
cc9203bf3   Dan Williams   isci: move core/c...
1693
  }
89a7301f2   Dan Williams   isci: retire scic...
1694
  int sci_oem_parameters_validate(struct sci_oem_params *oem)
cc9203bf3   Dan Williams   isci: move core/c...
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
  {
  	int i;
  
  	for (i = 0; i < SCI_MAX_PORTS; i++)
  		if (oem->ports[i].phy_mask > SCIC_SDS_PARM_PHY_MASK_MAX)
  			return -EINVAL;
  
  	for (i = 0; i < SCI_MAX_PHYS; i++)
  		if (oem->phys[i].sas_address.high == 0 &&
  		    oem->phys[i].sas_address.low == 0)
  			return -EINVAL;
  
  	if (oem->controller.mode_type == SCIC_PORT_AUTOMATIC_CONFIGURATION_MODE) {
  		for (i = 0; i < SCI_MAX_PHYS; i++)
  			if (oem->ports[i].phy_mask != 0)
  				return -EINVAL;
  	} else if (oem->controller.mode_type == SCIC_PORT_MANUAL_CONFIGURATION_MODE) {
  		u8 phy_mask = 0;
  
  		for (i = 0; i < SCI_MAX_PHYS; i++)
  			phy_mask |= oem->ports[i].phy_mask;
  
  		if (phy_mask == 0)
  			return -EINVAL;
  	} else
  		return -EINVAL;
7000f7c71   Andrzej Jakowski   [SCSI] isci: over...
1721
1722
  	if (oem->controller.max_concurr_spin_up > MAX_CONCURRENT_DEVICE_SPIN_UP_COUNT ||
  	    oem->controller.max_concurr_spin_up < 1)
cc9203bf3   Dan Williams   isci: move core/c...
1723
1724
1725
1726
  		return -EINVAL;
  
  	return 0;
  }
89a7301f2   Dan Williams   isci: retire scic...
1727
  static enum sci_status sci_oem_parameters_set(struct isci_host *ihost)
cc9203bf3   Dan Williams   isci: move core/c...
1728
  {
d9dcb4ba7   Dan Williams   isci: unify isci_...
1729
  	u32 state = ihost->sm.current_state_id;
cc9203bf3   Dan Williams   isci: move core/c...
1730

e301370ac   Edmund Nadolski   isci: state machi...
1731
1732
1733
  	if (state == SCIC_RESET ||
  	    state == SCIC_INITIALIZING ||
  	    state == SCIC_INITIALIZED) {
cc9203bf3   Dan Williams   isci: move core/c...
1734

89a7301f2   Dan Williams   isci: retire scic...
1735
  		if (sci_oem_parameters_validate(&ihost->oem_parameters))
cc9203bf3   Dan Williams   isci: move core/c...
1736
  			return SCI_FAILURE_INVALID_PARAMETER_VALUE;
cc9203bf3   Dan Williams   isci: move core/c...
1737
1738
1739
1740
1741
1742
  
  		return SCI_SUCCESS;
  	}
  
  	return SCI_FAILURE_INVALID_STATE;
  }
7000f7c71   Andrzej Jakowski   [SCSI] isci: over...
1743
1744
1745
1746
1747
1748
1749
1750
1751
  static u8 max_spin_up(struct isci_host *ihost)
  {
  	if (ihost->user_parameters.max_concurr_spinup)
  		return min_t(u8, ihost->user_parameters.max_concurr_spinup,
  			     MAX_CONCURRENT_DEVICE_SPIN_UP_COUNT);
  	else
  		return min_t(u8, ihost->oem_parameters.controller.max_concurr_spin_up,
  			     MAX_CONCURRENT_DEVICE_SPIN_UP_COUNT);
  }
0473661a1   Edmund Nadolski   isci: convert pow...
1752
  static void power_control_timeout(unsigned long data)
cc9203bf3   Dan Williams   isci: move core/c...
1753
  {
0473661a1   Edmund Nadolski   isci: convert pow...
1754
  	struct sci_timer *tmr = (struct sci_timer *)data;
d9dcb4ba7   Dan Williams   isci: unify isci_...
1755
  	struct isci_host *ihost = container_of(tmr, typeof(*ihost), power_control.timer);
852809559   Dan Williams   isci: unify isci_...
1756
  	struct isci_phy *iphy;
0473661a1   Edmund Nadolski   isci: convert pow...
1757
1758
  	unsigned long flags;
  	u8 i;
cc9203bf3   Dan Williams   isci: move core/c...
1759

0473661a1   Edmund Nadolski   isci: convert pow...
1760
  	spin_lock_irqsave(&ihost->scic_lock, flags);
cc9203bf3   Dan Williams   isci: move core/c...
1761

0473661a1   Edmund Nadolski   isci: convert pow...
1762
1763
  	if (tmr->cancel)
  		goto done;
d9dcb4ba7   Dan Williams   isci: unify isci_...
1764
  	ihost->power_control.phys_granted_power = 0;
0473661a1   Edmund Nadolski   isci: convert pow...
1765

d9dcb4ba7   Dan Williams   isci: unify isci_...
1766
1767
  	if (ihost->power_control.phys_waiting == 0) {
  		ihost->power_control.timer_started = false;
0473661a1   Edmund Nadolski   isci: convert pow...
1768
  		goto done;
cc9203bf3   Dan Williams   isci: move core/c...
1769
  	}
cc9203bf3   Dan Williams   isci: move core/c...
1770

0473661a1   Edmund Nadolski   isci: convert pow...
1771
  	for (i = 0; i < SCI_MAX_PHYS; i++) {
cc9203bf3   Dan Williams   isci: move core/c...
1772

d9dcb4ba7   Dan Williams   isci: unify isci_...
1773
  		if (ihost->power_control.phys_waiting == 0)
0473661a1   Edmund Nadolski   isci: convert pow...
1774
  			break;
cc9203bf3   Dan Williams   isci: move core/c...
1775

d9dcb4ba7   Dan Williams   isci: unify isci_...
1776
  		iphy = ihost->power_control.requesters[i];
852809559   Dan Williams   isci: unify isci_...
1777
  		if (iphy == NULL)
0473661a1   Edmund Nadolski   isci: convert pow...
1778
  			continue;
cc9203bf3   Dan Williams   isci: move core/c...
1779

7000f7c71   Andrzej Jakowski   [SCSI] isci: over...
1780
  		if (ihost->power_control.phys_granted_power >= max_spin_up(ihost))
0473661a1   Edmund Nadolski   isci: convert pow...
1781
  			break;
cc9203bf3   Dan Williams   isci: move core/c...
1782

d9dcb4ba7   Dan Williams   isci: unify isci_...
1783
1784
1785
  		ihost->power_control.requesters[i] = NULL;
  		ihost->power_control.phys_waiting--;
  		ihost->power_control.phys_granted_power++;
89a7301f2   Dan Williams   isci: retire scic...
1786
  		sci_phy_consume_power_handler(iphy);
cc9203bf3   Dan Williams   isci: move core/c...
1787
  	}
0473661a1   Edmund Nadolski   isci: convert pow...
1788
1789
1790
1791
1792
1793
  
  	/*
  	 * It doesn't matter if the power list is empty, we need to start the
  	 * timer in case another phy becomes ready.
  	 */
  	sci_mod_timer(tmr, SCIC_SDS_CONTROLLER_POWER_CONTROL_INTERVAL);
d9dcb4ba7   Dan Williams   isci: unify isci_...
1794
  	ihost->power_control.timer_started = true;
0473661a1   Edmund Nadolski   isci: convert pow...
1795
1796
1797
  
  done:
  	spin_unlock_irqrestore(&ihost->scic_lock, flags);
cc9203bf3   Dan Williams   isci: move core/c...
1798
  }
89a7301f2   Dan Williams   isci: retire scic...
1799
1800
  void sci_controller_power_control_queue_insert(struct isci_host *ihost,
  					       struct isci_phy *iphy)
cc9203bf3   Dan Williams   isci: move core/c...
1801
  {
852809559   Dan Williams   isci: unify isci_...
1802
  	BUG_ON(iphy == NULL);
cc9203bf3   Dan Williams   isci: move core/c...
1803

7000f7c71   Andrzej Jakowski   [SCSI] isci: over...
1804
  	if (ihost->power_control.phys_granted_power < max_spin_up(ihost)) {
d9dcb4ba7   Dan Williams   isci: unify isci_...
1805
  		ihost->power_control.phys_granted_power++;
89a7301f2   Dan Williams   isci: retire scic...
1806
  		sci_phy_consume_power_handler(iphy);
cc9203bf3   Dan Williams   isci: move core/c...
1807
1808
1809
1810
1811
  
  		/*
  		 * stop and start the power_control timer. When the timer fires, the
  		 * no_of_phys_granted_power will be set to 0
  		 */
d9dcb4ba7   Dan Williams   isci: unify isci_...
1812
1813
  		if (ihost->power_control.timer_started)
  			sci_del_timer(&ihost->power_control.timer);
0473661a1   Edmund Nadolski   isci: convert pow...
1814

d9dcb4ba7   Dan Williams   isci: unify isci_...
1815
  		sci_mod_timer(&ihost->power_control.timer,
0473661a1   Edmund Nadolski   isci: convert pow...
1816
  				 SCIC_SDS_CONTROLLER_POWER_CONTROL_INTERVAL);
d9dcb4ba7   Dan Williams   isci: unify isci_...
1817
  		ihost->power_control.timer_started = true;
0473661a1   Edmund Nadolski   isci: convert pow...
1818

cc9203bf3   Dan Williams   isci: move core/c...
1819
1820
  	} else {
  		/* Add the phy in the waiting list */
d9dcb4ba7   Dan Williams   isci: unify isci_...
1821
1822
  		ihost->power_control.requesters[iphy->phy_index] = iphy;
  		ihost->power_control.phys_waiting++;
cc9203bf3   Dan Williams   isci: move core/c...
1823
1824
  	}
  }
89a7301f2   Dan Williams   isci: retire scic...
1825
1826
  void sci_controller_power_control_queue_remove(struct isci_host *ihost,
  					       struct isci_phy *iphy)
cc9203bf3   Dan Williams   isci: move core/c...
1827
  {
852809559   Dan Williams   isci: unify isci_...
1828
  	BUG_ON(iphy == NULL);
cc9203bf3   Dan Williams   isci: move core/c...
1829

89a7301f2   Dan Williams   isci: retire scic...
1830
  	if (ihost->power_control.requesters[iphy->phy_index])
d9dcb4ba7   Dan Williams   isci: unify isci_...
1831
  		ihost->power_control.phys_waiting--;
cc9203bf3   Dan Williams   isci: move core/c...
1832

d9dcb4ba7   Dan Williams   isci: unify isci_...
1833
  	ihost->power_control.requesters[iphy->phy_index] = NULL;
cc9203bf3   Dan Williams   isci: move core/c...
1834
1835
1836
1837
1838
1839
1840
  }
  
  #define AFE_REGISTER_WRITE_DELAY 10
  
  /* Initialize the AFE for this phy index. We need to read the AFE setup from
   * the OEM parameters
   */
89a7301f2   Dan Williams   isci: retire scic...
1841
  static void sci_controller_afe_initialization(struct isci_host *ihost)
cc9203bf3   Dan Williams   isci: move core/c...
1842
  {
89a7301f2   Dan Williams   isci: retire scic...
1843
  	const struct sci_oem_params *oem = &ihost->oem_parameters;
dc00c8b69   Dan Williams   isci: cleanup sil...
1844
  	struct pci_dev *pdev = ihost->pdev;
cc9203bf3   Dan Williams   isci: move core/c...
1845
1846
1847
1848
  	u32 afe_status;
  	u32 phy_id;
  
  	/* Clear DFX Status registers */
d9dcb4ba7   Dan Williams   isci: unify isci_...
1849
  	writel(0x0081000f, &ihost->scu_registers->afe.afe_dfx_master_control0);
cc9203bf3   Dan Williams   isci: move core/c...
1850
  	udelay(AFE_REGISTER_WRITE_DELAY);
dc00c8b69   Dan Williams   isci: cleanup sil...
1851
  	if (is_b0(pdev)) {
cc9203bf3   Dan Williams   isci: move core/c...
1852
1853
  		/* PM Rx Equalization Save, PM SPhy Rx Acknowledgement
  		 * Timer, PM Stagger Timer */
d9dcb4ba7   Dan Williams   isci: unify isci_...
1854
  		writel(0x0007BFFF, &ihost->scu_registers->afe.afe_pmsn_master_control2);
cc9203bf3   Dan Williams   isci: move core/c...
1855
1856
1857
1858
  		udelay(AFE_REGISTER_WRITE_DELAY);
  	}
  
  	/* Configure bias currents to normal */
dc00c8b69   Dan Williams   isci: cleanup sil...
1859
  	if (is_a2(pdev))
d9dcb4ba7   Dan Williams   isci: unify isci_...
1860
  		writel(0x00005A00, &ihost->scu_registers->afe.afe_bias_control);
dc00c8b69   Dan Williams   isci: cleanup sil...
1861
  	else if (is_b0(pdev) || is_c0(pdev))
d9dcb4ba7   Dan Williams   isci: unify isci_...
1862
  		writel(0x00005F00, &ihost->scu_registers->afe.afe_bias_control);
cc9203bf3   Dan Williams   isci: move core/c...
1863
1864
1865
1866
  
  	udelay(AFE_REGISTER_WRITE_DELAY);
  
  	/* Enable PLL */
dc00c8b69   Dan Williams   isci: cleanup sil...
1867
  	if (is_b0(pdev) || is_c0(pdev))
d9dcb4ba7   Dan Williams   isci: unify isci_...
1868
  		writel(0x80040A08, &ihost->scu_registers->afe.afe_pll_control0);
cc9203bf3   Dan Williams   isci: move core/c...
1869
  	else
d9dcb4ba7   Dan Williams   isci: unify isci_...
1870
  		writel(0x80040908, &ihost->scu_registers->afe.afe_pll_control0);
cc9203bf3   Dan Williams   isci: move core/c...
1871
1872
1873
1874
1875
  
  	udelay(AFE_REGISTER_WRITE_DELAY);
  
  	/* Wait for the PLL to lock */
  	do {
d9dcb4ba7   Dan Williams   isci: unify isci_...
1876
  		afe_status = readl(&ihost->scu_registers->afe.afe_common_block_status);
cc9203bf3   Dan Williams   isci: move core/c...
1877
1878
  		udelay(AFE_REGISTER_WRITE_DELAY);
  	} while ((afe_status & 0x00001000) == 0);
dc00c8b69   Dan Williams   isci: cleanup sil...
1879
  	if (is_a2(pdev)) {
cc9203bf3   Dan Williams   isci: move core/c...
1880
  		/* Shorten SAS SNW lock time (RxLock timer value from 76 us to 50 us) */
d9dcb4ba7   Dan Williams   isci: unify isci_...
1881
  		writel(0x7bcc96ad, &ihost->scu_registers->afe.afe_pmsn_master_control0);
cc9203bf3   Dan Williams   isci: move core/c...
1882
1883
1884
1885
1886
  		udelay(AFE_REGISTER_WRITE_DELAY);
  	}
  
  	for (phy_id = 0; phy_id < SCI_MAX_PHYS; phy_id++) {
  		const struct sci_phy_oem_params *oem_phy = &oem->phys[phy_id];
dc00c8b69   Dan Williams   isci: cleanup sil...
1887
  		if (is_b0(pdev)) {
cc9203bf3   Dan Williams   isci: move core/c...
1888
  			 /* Configure transmitter SSC parameters */
d9dcb4ba7   Dan Williams   isci: unify isci_...
1889
  			writel(0x00030000, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_tx_ssc_control);
cc9203bf3   Dan Williams   isci: move core/c...
1890
  			udelay(AFE_REGISTER_WRITE_DELAY);
dc00c8b69   Dan Williams   isci: cleanup sil...
1891
  		} else if (is_c0(pdev)) {
dbb0743a5   Adam Gruchala   isci: Added suppo...
1892
  			 /* Configure transmitter SSC parameters */
d9dcb4ba7   Dan Williams   isci: unify isci_...
1893
  			writel(0x0003000, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_tx_ssc_control);
dbb0743a5   Adam Gruchala   isci: Added suppo...
1894
1895
1896
1897
1898
  			udelay(AFE_REGISTER_WRITE_DELAY);
  
  			/*
  			 * All defaults, except the Receive Word Alignament/Comma Detect
  			 * Enable....(0xe800) */
d9dcb4ba7   Dan Williams   isci: unify isci_...
1899
  			writel(0x00004500, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_xcvr_control0);
dbb0743a5   Adam Gruchala   isci: Added suppo...
1900
  			udelay(AFE_REGISTER_WRITE_DELAY);
cc9203bf3   Dan Williams   isci: move core/c...
1901
1902
1903
1904
  		} else {
  			/*
  			 * All defaults, except the Receive Word Alignament/Comma Detect
  			 * Enable....(0xe800) */
d9dcb4ba7   Dan Williams   isci: unify isci_...
1905
  			writel(0x00004512, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_xcvr_control0);
cc9203bf3   Dan Williams   isci: move core/c...
1906
  			udelay(AFE_REGISTER_WRITE_DELAY);
d9dcb4ba7   Dan Williams   isci: unify isci_...
1907
  			writel(0x0050100F, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_xcvr_control1);
cc9203bf3   Dan Williams   isci: move core/c...
1908
1909
1910
1911
1912
1913
  			udelay(AFE_REGISTER_WRITE_DELAY);
  		}
  
  		/*
  		 * Power up TX and RX out from power down (PWRDNTX and PWRDNRX)
  		 * & increase TX int & ext bias 20%....(0xe85c) */
dc00c8b69   Dan Williams   isci: cleanup sil...
1914
  		if (is_a2(pdev))
d9dcb4ba7   Dan Williams   isci: unify isci_...
1915
  			writel(0x000003F0, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_channel_control);
dc00c8b69   Dan Williams   isci: cleanup sil...
1916
  		else if (is_b0(pdev)) {
cc9203bf3   Dan Williams   isci: move core/c...
1917
  			 /* Power down TX and RX (PWRDNTX and PWRDNRX) */
d9dcb4ba7   Dan Williams   isci: unify isci_...
1918
  			writel(0x000003D7, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_channel_control);
dbb0743a5   Adam Gruchala   isci: Added suppo...
1919
1920
1921
1922
1923
  			udelay(AFE_REGISTER_WRITE_DELAY);
  
  			/*
  			 * Power up TX and RX out from power down (PWRDNTX and PWRDNRX)
  			 * & increase TX int & ext bias 20%....(0xe85c) */
d9dcb4ba7   Dan Williams   isci: unify isci_...
1924
  			writel(0x000003D4, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_channel_control);
dbb0743a5   Adam Gruchala   isci: Added suppo...
1925
  		} else {
d9dcb4ba7   Dan Williams   isci: unify isci_...
1926
  			writel(0x000001E7, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_channel_control);
cc9203bf3   Dan Williams   isci: move core/c...
1927
1928
1929
1930
1931
  			udelay(AFE_REGISTER_WRITE_DELAY);
  
  			/*
  			 * Power up TX and RX out from power down (PWRDNTX and PWRDNRX)
  			 * & increase TX int & ext bias 20%....(0xe85c) */
d9dcb4ba7   Dan Williams   isci: unify isci_...
1932
  			writel(0x000001E4, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_channel_control);
cc9203bf3   Dan Williams   isci: move core/c...
1933
1934
  		}
  		udelay(AFE_REGISTER_WRITE_DELAY);
dc00c8b69   Dan Williams   isci: cleanup sil...
1935
  		if (is_a2(pdev)) {
cc9203bf3   Dan Williams   isci: move core/c...
1936
  			/* Enable TX equalization (0xe824) */
d9dcb4ba7   Dan Williams   isci: unify isci_...
1937
  			writel(0x00040000, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_tx_control);
cc9203bf3   Dan Williams   isci: move core/c...
1938
1939
1940
1941
1942
1943
  			udelay(AFE_REGISTER_WRITE_DELAY);
  		}
  
  		/*
  		 * RDPI=0x0(RX Power On), RXOOBDETPDNC=0x0, TPD=0x0(TX Power On),
  		 * RDD=0x0(RX Detect Enabled) ....(0xe800) */
d9dcb4ba7   Dan Williams   isci: unify isci_...
1944
  		writel(0x00004100, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_xcvr_control0);
cc9203bf3   Dan Williams   isci: move core/c...
1945
1946
1947
  		udelay(AFE_REGISTER_WRITE_DELAY);
  
  		/* Leave DFE/FFE on */
dc00c8b69   Dan Williams   isci: cleanup sil...
1948
  		if (is_a2(pdev))
d9dcb4ba7   Dan Williams   isci: unify isci_...
1949
  			writel(0x3F11103F, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_rx_ssc_control0);
dc00c8b69   Dan Williams   isci: cleanup sil...
1950
  		else if (is_b0(pdev)) {
d9dcb4ba7   Dan Williams   isci: unify isci_...
1951
  			writel(0x3F11103F, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_rx_ssc_control0);
cc9203bf3   Dan Williams   isci: move core/c...
1952
1953
  			udelay(AFE_REGISTER_WRITE_DELAY);
  			/* Enable TX equalization (0xe824) */
d9dcb4ba7   Dan Williams   isci: unify isci_...
1954
  			writel(0x00040000, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_tx_control);
dbb0743a5   Adam Gruchala   isci: Added suppo...
1955
  		} else {
d9dcb4ba7   Dan Williams   isci: unify isci_...
1956
  			writel(0x0140DF0F, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_rx_ssc_control1);
dbb0743a5   Adam Gruchala   isci: Added suppo...
1957
  			udelay(AFE_REGISTER_WRITE_DELAY);
d9dcb4ba7   Dan Williams   isci: unify isci_...
1958
  			writel(0x3F6F103F, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_rx_ssc_control0);
dbb0743a5   Adam Gruchala   isci: Added suppo...
1959
1960
1961
  			udelay(AFE_REGISTER_WRITE_DELAY);
  
  			/* Enable TX equalization (0xe824) */
d9dcb4ba7   Dan Williams   isci: unify isci_...
1962
  			writel(0x00040000, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_tx_control);
cc9203bf3   Dan Williams   isci: move core/c...
1963
  		}
dbb0743a5   Adam Gruchala   isci: Added suppo...
1964

cc9203bf3   Dan Williams   isci: move core/c...
1965
1966
1967
  		udelay(AFE_REGISTER_WRITE_DELAY);
  
  		writel(oem_phy->afe_tx_amp_control0,
d9dcb4ba7   Dan Williams   isci: unify isci_...
1968
  			&ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_tx_amp_control0);
cc9203bf3   Dan Williams   isci: move core/c...
1969
1970
1971
  		udelay(AFE_REGISTER_WRITE_DELAY);
  
  		writel(oem_phy->afe_tx_amp_control1,
d9dcb4ba7   Dan Williams   isci: unify isci_...
1972
  			&ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_tx_amp_control1);
cc9203bf3   Dan Williams   isci: move core/c...
1973
1974
1975
  		udelay(AFE_REGISTER_WRITE_DELAY);
  
  		writel(oem_phy->afe_tx_amp_control2,
d9dcb4ba7   Dan Williams   isci: unify isci_...
1976
  			&ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_tx_amp_control2);
cc9203bf3   Dan Williams   isci: move core/c...
1977
1978
1979
  		udelay(AFE_REGISTER_WRITE_DELAY);
  
  		writel(oem_phy->afe_tx_amp_control3,
d9dcb4ba7   Dan Williams   isci: unify isci_...
1980
  			&ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_tx_amp_control3);
cc9203bf3   Dan Williams   isci: move core/c...
1981
1982
1983
1984
  		udelay(AFE_REGISTER_WRITE_DELAY);
  	}
  
  	/* Transfer control to the PEs */
d9dcb4ba7   Dan Williams   isci: unify isci_...
1985
  	writel(0x00010f00, &ihost->scu_registers->afe.afe_dfx_master_control0);
cc9203bf3   Dan Williams   isci: move core/c...
1986
1987
  	udelay(AFE_REGISTER_WRITE_DELAY);
  }
89a7301f2   Dan Williams   isci: retire scic...
1988
  static void sci_controller_initialize_power_control(struct isci_host *ihost)
cc9203bf3   Dan Williams   isci: move core/c...
1989
  {
d9dcb4ba7   Dan Williams   isci: unify isci_...
1990
  	sci_init_timer(&ihost->power_control.timer, power_control_timeout);
cc9203bf3   Dan Williams   isci: move core/c...
1991

d9dcb4ba7   Dan Williams   isci: unify isci_...
1992
1993
  	memset(ihost->power_control.requesters, 0,
  	       sizeof(ihost->power_control.requesters));
cc9203bf3   Dan Williams   isci: move core/c...
1994

d9dcb4ba7   Dan Williams   isci: unify isci_...
1995
1996
  	ihost->power_control.phys_waiting = 0;
  	ihost->power_control.phys_granted_power = 0;
cc9203bf3   Dan Williams   isci: move core/c...
1997
  }
89a7301f2   Dan Williams   isci: retire scic...
1998
  static enum sci_status sci_controller_initialize(struct isci_host *ihost)
cc9203bf3   Dan Williams   isci: move core/c...
1999
  {
d9dcb4ba7   Dan Williams   isci: unify isci_...
2000
  	struct sci_base_state_machine *sm = &ihost->sm;
7c78da317   Dan Williams   isci: remove 'min...
2001
2002
  	enum sci_status result = SCI_FAILURE;
  	unsigned long i, state, val;
cc9203bf3   Dan Williams   isci: move core/c...
2003

d9dcb4ba7   Dan Williams   isci: unify isci_...
2004
2005
  	if (ihost->sm.current_state_id != SCIC_RESET) {
  		dev_warn(&ihost->pdev->dev,
cc9203bf3   Dan Williams   isci: move core/c...
2006
2007
2008
2009
2010
  			 "SCIC Controller initialize operation requested "
  			 "in invalid state
  ");
  		return SCI_FAILURE_INVALID_STATE;
  	}
e301370ac   Edmund Nadolski   isci: state machi...
2011
  	sci_change_state(sm, SCIC_INITIALIZING);
cc9203bf3   Dan Williams   isci: move core/c...
2012

d9dcb4ba7   Dan Williams   isci: unify isci_...
2013
  	sci_init_timer(&ihost->phy_timer, phy_startup_timeout);
bb3dbdf6c   Edmund Nadolski   isci: convert phy...
2014

d9dcb4ba7   Dan Williams   isci: unify isci_...
2015
2016
  	ihost->next_phy_to_start = 0;
  	ihost->phy_startup_timer_pending = false;
cc9203bf3   Dan Williams   isci: move core/c...
2017

89a7301f2   Dan Williams   isci: retire scic...
2018
  	sci_controller_initialize_power_control(ihost);
cc9203bf3   Dan Williams   isci: move core/c...
2019
2020
2021
2022
2023
2024
  
  	/*
  	 * There is nothing to do here for B0 since we do not have to
  	 * program the AFE registers.
  	 * / @todo The AFE settings are supposed to be correct for the B0 but
  	 * /       presently they seem to be wrong. */
89a7301f2   Dan Williams   isci: retire scic...
2025
  	sci_controller_afe_initialization(ihost);
cc9203bf3   Dan Williams   isci: move core/c...
2026

cc9203bf3   Dan Williams   isci: move core/c...
2027

7c78da317   Dan Williams   isci: remove 'min...
2028
  	/* Take the hardware out of reset */
d9dcb4ba7   Dan Williams   isci: unify isci_...
2029
  	writel(0, &ihost->smu_registers->soft_reset_control);
cc9203bf3   Dan Williams   isci: move core/c...
2030

7c78da317   Dan Williams   isci: remove 'min...
2031
2032
2033
2034
2035
  	/*
  	 * / @todo Provide meaningfull error code for hardware failure
  	 * result = SCI_FAILURE_CONTROLLER_HARDWARE; */
  	for (i = 100; i >= 1; i--) {
  		u32 status;
cc9203bf3   Dan Williams   isci: move core/c...
2036

7c78da317   Dan Williams   isci: remove 'min...
2037
2038
  		/* Loop until the hardware reports success */
  		udelay(SCU_CONTEXT_RAM_INIT_STALL_TIME);
d9dcb4ba7   Dan Williams   isci: unify isci_...
2039
  		status = readl(&ihost->smu_registers->control_status);
cc9203bf3   Dan Williams   isci: move core/c...
2040

7c78da317   Dan Williams   isci: remove 'min...
2041
2042
2043
2044
2045
  		if ((status & SCU_RAM_INIT_COMPLETED) == SCU_RAM_INIT_COMPLETED)
  			break;
  	}
  	if (i == 0)
  		goto out;
cc9203bf3   Dan Williams   isci: move core/c...
2046

7c78da317   Dan Williams   isci: remove 'min...
2047
2048
2049
  	/*
  	 * Determine what are the actaul device capacities that the
  	 * hardware will support */
d9dcb4ba7   Dan Williams   isci: unify isci_...
2050
  	val = readl(&ihost->smu_registers->device_context_capacity);
cc9203bf3   Dan Williams   isci: move core/c...
2051

7c78da317   Dan Williams   isci: remove 'min...
2052
  	/* Record the smaller of the two capacity values */
d9dcb4ba7   Dan Williams   isci: unify isci_...
2053
2054
2055
  	ihost->logical_port_entries = min(smu_max_ports(val), SCI_MAX_PORTS);
  	ihost->task_context_entries = min(smu_max_task_contexts(val), SCI_MAX_IO_REQUESTS);
  	ihost->remote_node_entries = min(smu_max_rncs(val), SCI_MAX_REMOTE_DEVICES);
cc9203bf3   Dan Williams   isci: move core/c...
2056

7c78da317   Dan Williams   isci: remove 'min...
2057
2058
2059
2060
  	/*
  	 * Make all PEs that are unassigned match up with the
  	 * logical ports
  	 */
d9dcb4ba7   Dan Williams   isci: unify isci_...
2061
  	for (i = 0; i < ihost->logical_port_entries; i++) {
7c78da317   Dan Williams   isci: remove 'min...
2062
  		struct scu_port_task_scheduler_group_registers __iomem
d9dcb4ba7   Dan Williams   isci: unify isci_...
2063
  			*ptsg = &ihost->scu_registers->peg0.ptsg;
cc9203bf3   Dan Williams   isci: move core/c...
2064

7c78da317   Dan Williams   isci: remove 'min...
2065
  		writel(i, &ptsg->protocol_engine[i]);
cc9203bf3   Dan Williams   isci: move core/c...
2066
2067
2068
  	}
  
  	/* Initialize hardware PCI Relaxed ordering in DMA engines */
d9dcb4ba7   Dan Williams   isci: unify isci_...
2069
  	val = readl(&ihost->scu_registers->sdma.pdma_configuration);
7c78da317   Dan Williams   isci: remove 'min...
2070
  	val |= SCU_PDMACR_GEN_BIT(PCI_RELAXED_ORDERING_ENABLE);
d9dcb4ba7   Dan Williams   isci: unify isci_...
2071
  	writel(val, &ihost->scu_registers->sdma.pdma_configuration);
7c78da317   Dan Williams   isci: remove 'min...
2072

d9dcb4ba7   Dan Williams   isci: unify isci_...
2073
  	val = readl(&ihost->scu_registers->sdma.cdma_configuration);
7c78da317   Dan Williams   isci: remove 'min...
2074
  	val |= SCU_CDMACR_GEN_BIT(PCI_RELAXED_ORDERING_ENABLE);
d9dcb4ba7   Dan Williams   isci: unify isci_...
2075
  	writel(val, &ihost->scu_registers->sdma.cdma_configuration);
cc9203bf3   Dan Williams   isci: move core/c...
2076
2077
2078
2079
2080
  
  	/*
  	 * Initialize the PHYs before the PORTs because the PHY registers
  	 * are accessed during the port initialization.
  	 */
7c78da317   Dan Williams   isci: remove 'min...
2081
  	for (i = 0; i < SCI_MAX_PHYS; i++) {
89a7301f2   Dan Williams   isci: retire scic...
2082
2083
2084
  		result = sci_phy_initialize(&ihost->phys[i],
  					    &ihost->scu_registers->peg0.pe[i].tl,
  					    &ihost->scu_registers->peg0.pe[i].ll);
7c78da317   Dan Williams   isci: remove 'min...
2085
2086
  		if (result != SCI_SUCCESS)
  			goto out;
cc9203bf3   Dan Williams   isci: move core/c...
2087
  	}
d9dcb4ba7   Dan Williams   isci: unify isci_...
2088
  	for (i = 0; i < ihost->logical_port_entries; i++) {
89a7301f2   Dan Williams   isci: retire scic...
2089
  		struct isci_port *iport = &ihost->ports[i];
7c78da317   Dan Williams   isci: remove 'min...
2090

89a7301f2   Dan Williams   isci: retire scic...
2091
2092
2093
  		iport->port_task_scheduler_registers = &ihost->scu_registers->peg0.ptsg.port[i];
  		iport->port_pe_configuration_register = &ihost->scu_registers->peg0.ptsg.protocol_engine[0];
  		iport->viit_registers = &ihost->scu_registers->peg0.viit[i];
cc9203bf3   Dan Williams   isci: move core/c...
2094
  	}
89a7301f2   Dan Williams   isci: retire scic...
2095
  	result = sci_port_configuration_agent_initialize(ihost, &ihost->port_agent);
cc9203bf3   Dan Williams   isci: move core/c...
2096

7c78da317   Dan Williams   isci: remove 'min...
2097
   out:
cc9203bf3   Dan Williams   isci: move core/c...
2098
2099
  	/* Advance the controller state machine */
  	if (result == SCI_SUCCESS)
e301370ac   Edmund Nadolski   isci: state machi...
2100
  		state = SCIC_INITIALIZED;
cc9203bf3   Dan Williams   isci: move core/c...
2101
  	else
e301370ac   Edmund Nadolski   isci: state machi...
2102
2103
  		state = SCIC_FAILED;
  	sci_change_state(sm, state);
cc9203bf3   Dan Williams   isci: move core/c...
2104
2105
2106
  
  	return result;
  }
89a7301f2   Dan Williams   isci: retire scic...
2107
2108
  static enum sci_status sci_user_parameters_set(struct isci_host *ihost,
  					       struct sci_user_parameters *sci_parms)
cc9203bf3   Dan Williams   isci: move core/c...
2109
  {
d9dcb4ba7   Dan Williams   isci: unify isci_...
2110
  	u32 state = ihost->sm.current_state_id;
cc9203bf3   Dan Williams   isci: move core/c...
2111

e301370ac   Edmund Nadolski   isci: state machi...
2112
2113
2114
  	if (state == SCIC_RESET ||
  	    state == SCIC_INITIALIZING ||
  	    state == SCIC_INITIALIZED) {
cc9203bf3   Dan Williams   isci: move core/c...
2115
2116
2117
2118
2119
2120
2121
2122
  		u16 index;
  
  		/*
  		 * Validate the user parameters.  If they are not legal, then
  		 * return a failure.
  		 */
  		for (index = 0; index < SCI_MAX_PHYS; index++) {
  			struct sci_phy_user_params *user_phy;
89a7301f2   Dan Williams   isci: retire scic...
2123
  			user_phy = &sci_parms->phys[index];
cc9203bf3   Dan Williams   isci: move core/c...
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
  
  			if (!((user_phy->max_speed_generation <=
  						SCIC_SDS_PARM_MAX_SPEED) &&
  			      (user_phy->max_speed_generation >
  						SCIC_SDS_PARM_NO_SPEED)))
  				return SCI_FAILURE_INVALID_PARAMETER_VALUE;
  
  			if (user_phy->in_connection_align_insertion_frequency <
  					3)
  				return SCI_FAILURE_INVALID_PARAMETER_VALUE;
  
  			if ((user_phy->in_connection_align_insertion_frequency <
  						3) ||
  			    (user_phy->align_insertion_frequency == 0) ||
  			    (user_phy->
  				notify_enable_spin_up_insertion_frequency ==
  						0))
  				return SCI_FAILURE_INVALID_PARAMETER_VALUE;
  		}
89a7301f2   Dan Williams   isci: retire scic...
2143
2144
2145
2146
2147
  		if ((sci_parms->stp_inactivity_timeout == 0) ||
  		    (sci_parms->ssp_inactivity_timeout == 0) ||
  		    (sci_parms->stp_max_occupancy_timeout == 0) ||
  		    (sci_parms->ssp_max_occupancy_timeout == 0) ||
  		    (sci_parms->no_outbound_task_timeout == 0))
cc9203bf3   Dan Williams   isci: move core/c...
2148
  			return SCI_FAILURE_INVALID_PARAMETER_VALUE;
89a7301f2   Dan Williams   isci: retire scic...
2149
  		memcpy(&ihost->user_parameters, sci_parms, sizeof(*sci_parms));
cc9203bf3   Dan Williams   isci: move core/c...
2150
2151
2152
2153
2154
2155
  
  		return SCI_SUCCESS;
  	}
  
  	return SCI_FAILURE_INVALID_STATE;
  }
89a7301f2   Dan Williams   isci: retire scic...
2156
  static int sci_controller_mem_init(struct isci_host *ihost)
cc9203bf3   Dan Williams   isci: move core/c...
2157
  {
d9dcb4ba7   Dan Williams   isci: unify isci_...
2158
  	struct device *dev = &ihost->pdev->dev;
7c78da317   Dan Williams   isci: remove 'min...
2159
2160
2161
  	dma_addr_t dma;
  	size_t size;
  	int err;
cc9203bf3   Dan Williams   isci: move core/c...
2162

7c78da317   Dan Williams   isci: remove 'min...
2163
  	size = SCU_MAX_COMPLETION_QUEUE_ENTRIES * sizeof(u32);
d9dcb4ba7   Dan Williams   isci: unify isci_...
2164
2165
  	ihost->completion_queue = dmam_alloc_coherent(dev, size, &dma, GFP_KERNEL);
  	if (!ihost->completion_queue)
cc9203bf3   Dan Williams   isci: move core/c...
2166
  		return -ENOMEM;
d9dcb4ba7   Dan Williams   isci: unify isci_...
2167
2168
  	writel(lower_32_bits(dma), &ihost->smu_registers->completion_queue_lower);
  	writel(upper_32_bits(dma), &ihost->smu_registers->completion_queue_upper);
cc9203bf3   Dan Williams   isci: move core/c...
2169

d9dcb4ba7   Dan Williams   isci: unify isci_...
2170
2171
  	size = ihost->remote_node_entries * sizeof(union scu_remote_node_context);
  	ihost->remote_node_context_table = dmam_alloc_coherent(dev, size, &dma,
89a7301f2   Dan Williams   isci: retire scic...
2172
  							       GFP_KERNEL);
d9dcb4ba7   Dan Williams   isci: unify isci_...
2173
  	if (!ihost->remote_node_context_table)
cc9203bf3   Dan Williams   isci: move core/c...
2174
  		return -ENOMEM;
d9dcb4ba7   Dan Williams   isci: unify isci_...
2175
2176
  	writel(lower_32_bits(dma), &ihost->smu_registers->remote_node_context_lower);
  	writel(upper_32_bits(dma), &ihost->smu_registers->remote_node_context_upper);
cc9203bf3   Dan Williams   isci: move core/c...
2177

d9dcb4ba7   Dan Williams   isci: unify isci_...
2178
2179
2180
  	size = ihost->task_context_entries * sizeof(struct scu_task_context),
  	ihost->task_context_table = dmam_alloc_coherent(dev, size, &dma, GFP_KERNEL);
  	if (!ihost->task_context_table)
cc9203bf3   Dan Williams   isci: move core/c...
2181
  		return -ENOMEM;
d9dcb4ba7   Dan Williams   isci: unify isci_...
2182
2183
2184
  	ihost->task_context_dma = dma;
  	writel(lower_32_bits(dma), &ihost->smu_registers->host_task_table_lower);
  	writel(upper_32_bits(dma), &ihost->smu_registers->host_task_table_upper);
cc9203bf3   Dan Williams   isci: move core/c...
2185

89a7301f2   Dan Williams   isci: retire scic...
2186
  	err = sci_unsolicited_frame_control_construct(ihost);
7c78da317   Dan Williams   isci: remove 'min...
2187
2188
  	if (err)
  		return err;
cc9203bf3   Dan Williams   isci: move core/c...
2189
2190
2191
2192
2193
  
  	/*
  	 * Inform the silicon as to the location of the UF headers and
  	 * address table.
  	 */
d9dcb4ba7   Dan Williams   isci: unify isci_...
2194
2195
2196
2197
  	writel(lower_32_bits(ihost->uf_control.headers.physical_address),
  		&ihost->scu_registers->sdma.uf_header_base_address_lower);
  	writel(upper_32_bits(ihost->uf_control.headers.physical_address),
  		&ihost->scu_registers->sdma.uf_header_base_address_upper);
cc9203bf3   Dan Williams   isci: move core/c...
2198

d9dcb4ba7   Dan Williams   isci: unify isci_...
2199
2200
2201
2202
  	writel(lower_32_bits(ihost->uf_control.address_table.physical_address),
  		&ihost->scu_registers->sdma.uf_address_table_lower);
  	writel(upper_32_bits(ihost->uf_control.address_table.physical_address),
  		&ihost->scu_registers->sdma.uf_address_table_upper);
cc9203bf3   Dan Williams   isci: move core/c...
2203
2204
2205
  
  	return 0;
  }
d9dcb4ba7   Dan Williams   isci: unify isci_...
2206
  int isci_host_init(struct isci_host *ihost)
6f231dda6   Dan Williams   isci: Intel(R) C6...
2207
  {
d9c37390c   Dan Williams   isci: preallocate...
2208
  	int err = 0, i;
6f231dda6   Dan Williams   isci: Intel(R) C6...
2209
  	enum sci_status status;
89a7301f2   Dan Williams   isci: retire scic...
2210
  	struct sci_user_parameters sci_user_params;
d9dcb4ba7   Dan Williams   isci: unify isci_...
2211
  	struct isci_pci_info *pci_info = to_pci_info(ihost->pdev);
6f231dda6   Dan Williams   isci: Intel(R) C6...
2212

d9dcb4ba7   Dan Williams   isci: unify isci_...
2213
2214
2215
  	spin_lock_init(&ihost->state_lock);
  	spin_lock_init(&ihost->scic_lock);
  	init_waitqueue_head(&ihost->eventq);
6f231dda6   Dan Williams   isci: Intel(R) C6...
2216

d9dcb4ba7   Dan Williams   isci: unify isci_...
2217
  	isci_host_change_state(ihost, isci_starting);
6f231dda6   Dan Williams   isci: Intel(R) C6...
2218

89a7301f2   Dan Williams   isci: retire scic...
2219
2220
  	status = sci_controller_construct(ihost, scu_base(ihost),
  					  smu_base(ihost));
6f231dda6   Dan Williams   isci: Intel(R) C6...
2221
2222
  
  	if (status != SCI_SUCCESS) {
d9dcb4ba7   Dan Williams   isci: unify isci_...
2223
  		dev_err(&ihost->pdev->dev,
89a7301f2   Dan Williams   isci: retire scic...
2224
2225
  			"%s: sci_controller_construct failed - status = %x
  ",
6f231dda6   Dan Williams   isci: Intel(R) C6...
2226
2227
  			__func__,
  			status);
858d4aa74   Dave Jiang   isci: Move firmwa...
2228
  		return -ENODEV;
6f231dda6   Dan Williams   isci: Intel(R) C6...
2229
  	}
d9dcb4ba7   Dan Williams   isci: unify isci_...
2230
2231
  	ihost->sas_ha.dev = &ihost->pdev->dev;
  	ihost->sas_ha.lldd_ha = ihost;
6f231dda6   Dan Williams   isci: Intel(R) C6...
2232

d044af17a   Dan Williams   isci: Add support...
2233
2234
2235
2236
  	/*
  	 * grab initial values stored in the controller object for OEM and USER
  	 * parameters
  	 */
89a7301f2   Dan Williams   isci: retire scic...
2237
2238
  	isci_user_parameters_get(&sci_user_params);
  	status = sci_user_parameters_set(ihost, &sci_user_params);
d044af17a   Dan Williams   isci: Add support...
2239
  	if (status != SCI_SUCCESS) {
d9dcb4ba7   Dan Williams   isci: unify isci_...
2240
  		dev_warn(&ihost->pdev->dev,
89a7301f2   Dan Williams   isci: retire scic...
2241
2242
  			 "%s: sci_user_parameters_set failed
  ",
d044af17a   Dan Williams   isci: Add support...
2243
2244
2245
  			 __func__);
  		return -ENODEV;
  	}
d044af17a   Dan Williams   isci: Add support...
2246
2247
  	/* grab any OEM parameters specified in orom */
  	if (pci_info->orom) {
89a7301f2   Dan Williams   isci: retire scic...
2248
  		status = isci_parse_oem_parameters(&ihost->oem_parameters,
d044af17a   Dan Williams   isci: Add support...
2249
  						   pci_info->orom,
d9dcb4ba7   Dan Williams   isci: unify isci_...
2250
  						   ihost->id);
6f231dda6   Dan Williams   isci: Intel(R) C6...
2251
  		if (status != SCI_SUCCESS) {
d9dcb4ba7   Dan Williams   isci: unify isci_...
2252
  			dev_warn(&ihost->pdev->dev,
6f231dda6   Dan Williams   isci: Intel(R) C6...
2253
2254
  				 "parsing firmware oem parameters failed
  ");
858d4aa74   Dave Jiang   isci: Move firmwa...
2255
  			return -EINVAL;
6f231dda6   Dan Williams   isci: Intel(R) C6...
2256
  		}
4711ba10b   Dan Williams   isci: fix oem par...
2257
  	}
89a7301f2   Dan Williams   isci: retire scic...
2258
  	status = sci_oem_parameters_set(ihost);
4711ba10b   Dan Williams   isci: fix oem par...
2259
  	if (status != SCI_SUCCESS) {
d9dcb4ba7   Dan Williams   isci: unify isci_...
2260
  		dev_warn(&ihost->pdev->dev,
89a7301f2   Dan Williams   isci: retire scic...
2261
2262
  				"%s: sci_oem_parameters_set failed
  ",
4711ba10b   Dan Williams   isci: fix oem par...
2263
2264
  				__func__);
  		return -ENODEV;
6f231dda6   Dan Williams   isci: Intel(R) C6...
2265
  	}
d9dcb4ba7   Dan Williams   isci: unify isci_...
2266
2267
  	tasklet_init(&ihost->completion_tasklet,
  		     isci_host_completion_routine, (unsigned long)ihost);
7c40a8035   Dan Williams   isci: rework time...
2268

d9dcb4ba7   Dan Williams   isci: unify isci_...
2269
2270
  	INIT_LIST_HEAD(&ihost->requests_to_complete);
  	INIT_LIST_HEAD(&ihost->requests_to_errorback);
7c40a8035   Dan Williams   isci: rework time...
2271

d9dcb4ba7   Dan Williams   isci: unify isci_...
2272
  	spin_lock_irq(&ihost->scic_lock);
89a7301f2   Dan Williams   isci: retire scic...
2273
  	status = sci_controller_initialize(ihost);
d9dcb4ba7   Dan Williams   isci: unify isci_...
2274
  	spin_unlock_irq(&ihost->scic_lock);
6f231dda6   Dan Williams   isci: Intel(R) C6...
2275
  	if (status != SCI_SUCCESS) {
d9dcb4ba7   Dan Williams   isci: unify isci_...
2276
  		dev_warn(&ihost->pdev->dev,
89a7301f2   Dan Williams   isci: retire scic...
2277
  			 "%s: sci_controller_initialize failed -"
6f231dda6   Dan Williams   isci: Intel(R) C6...
2278
2279
2280
  			 " status = 0x%x
  ",
  			 __func__, status);
858d4aa74   Dave Jiang   isci: Move firmwa...
2281
  		return -ENODEV;
6f231dda6   Dan Williams   isci: Intel(R) C6...
2282
  	}
89a7301f2   Dan Williams   isci: retire scic...
2283
  	err = sci_controller_mem_init(ihost);
6f231dda6   Dan Williams   isci: Intel(R) C6...
2284
  	if (err)
858d4aa74   Dave Jiang   isci: Move firmwa...
2285
  		return err;
6f231dda6   Dan Williams   isci: Intel(R) C6...
2286

d9c37390c   Dan Williams   isci: preallocate...
2287
  	for (i = 0; i < SCI_MAX_PORTS; i++)
d9dcb4ba7   Dan Williams   isci: unify isci_...
2288
  		isci_port_init(&ihost->ports[i], ihost, i);
6f231dda6   Dan Williams   isci: Intel(R) C6...
2289

d9c37390c   Dan Williams   isci: preallocate...
2290
  	for (i = 0; i < SCI_MAX_PHYS; i++)
d9dcb4ba7   Dan Williams   isci: unify isci_...
2291
  		isci_phy_init(&ihost->phys[i], ihost, i);
d9c37390c   Dan Williams   isci: preallocate...
2292

ad4f4c1de   Dan Williams   [SCSI] isci: init...
2293
2294
2295
2296
2297
  	/* enable sgpio */
  	writel(1, &ihost->scu_registers->peg0.sgpio.interface_control);
  	for (i = 0; i < isci_gpio_count(ihost); i++)
  		writel(SGPIO_HW_CONTROL, &ihost->scu_registers->peg0.sgpio.output_data_select[i]);
  	writel(0, &ihost->scu_registers->peg0.sgpio.vendor_specific_code);
d9c37390c   Dan Williams   isci: preallocate...
2298
  	for (i = 0; i < SCI_MAX_REMOTE_DEVICES; i++) {
d9dcb4ba7   Dan Williams   isci: unify isci_...
2299
  		struct isci_remote_device *idev = &ihost->devices[i];
d9c37390c   Dan Williams   isci: preallocate...
2300
2301
2302
  
  		INIT_LIST_HEAD(&idev->reqs_in_process);
  		INIT_LIST_HEAD(&idev->node);
d9c37390c   Dan Williams   isci: preallocate...
2303
  	}
6f231dda6   Dan Williams   isci: Intel(R) C6...
2304

db0562509   Dan Williams   isci: preallocate...
2305
2306
2307
  	for (i = 0; i < SCI_MAX_IO_REQUESTS; i++) {
  		struct isci_request *ireq;
  		dma_addr_t dma;
d9dcb4ba7   Dan Williams   isci: unify isci_...
2308
  		ireq = dmam_alloc_coherent(&ihost->pdev->dev,
db0562509   Dan Williams   isci: preallocate...
2309
2310
2311
2312
  					   sizeof(struct isci_request), &dma,
  					   GFP_KERNEL);
  		if (!ireq)
  			return -ENOMEM;
d9dcb4ba7   Dan Williams   isci: unify isci_...
2313
2314
  		ireq->tc = &ihost->task_context_table[i];
  		ireq->owning_controller = ihost;
db0562509   Dan Williams   isci: preallocate...
2315
2316
  		spin_lock_init(&ireq->state_lock);
  		ireq->request_daddr = dma;
d9dcb4ba7   Dan Williams   isci: unify isci_...
2317
2318
  		ireq->isci_host = ihost;
  		ihost->reqs[i] = ireq;
db0562509   Dan Williams   isci: preallocate...
2319
  	}
858d4aa74   Dave Jiang   isci: Move firmwa...
2320
  	return 0;
6f231dda6   Dan Williams   isci: Intel(R) C6...
2321
  }
cc9203bf3   Dan Williams   isci: move core/c...
2322

89a7301f2   Dan Williams   isci: retire scic...
2323
2324
  void sci_controller_link_up(struct isci_host *ihost, struct isci_port *iport,
  			    struct isci_phy *iphy)
cc9203bf3   Dan Williams   isci: move core/c...
2325
  {
d9dcb4ba7   Dan Williams   isci: unify isci_...
2326
  	switch (ihost->sm.current_state_id) {
e301370ac   Edmund Nadolski   isci: state machi...
2327
  	case SCIC_STARTING:
d9dcb4ba7   Dan Williams   isci: unify isci_...
2328
2329
2330
  		sci_del_timer(&ihost->phy_timer);
  		ihost->phy_startup_timer_pending = false;
  		ihost->port_agent.link_up_handler(ihost, &ihost->port_agent,
89a7301f2   Dan Williams   isci: retire scic...
2331
2332
  						  iport, iphy);
  		sci_controller_start_next_phy(ihost);
cc9203bf3   Dan Williams   isci: move core/c...
2333
  		break;
e301370ac   Edmund Nadolski   isci: state machi...
2334
  	case SCIC_READY:
d9dcb4ba7   Dan Williams   isci: unify isci_...
2335
  		ihost->port_agent.link_up_handler(ihost, &ihost->port_agent,
89a7301f2   Dan Williams   isci: retire scic...
2336
  						  iport, iphy);
cc9203bf3   Dan Williams   isci: move core/c...
2337
2338
  		break;
  	default:
d9dcb4ba7   Dan Williams   isci: unify isci_...
2339
  		dev_dbg(&ihost->pdev->dev,
cc9203bf3   Dan Williams   isci: move core/c...
2340
  			"%s: SCIC Controller linkup event from phy %d in "
852809559   Dan Williams   isci: unify isci_...
2341
2342
  			"unexpected state %d
  ", __func__, iphy->phy_index,
d9dcb4ba7   Dan Williams   isci: unify isci_...
2343
  			ihost->sm.current_state_id);
cc9203bf3   Dan Williams   isci: move core/c...
2344
2345
  	}
  }
89a7301f2   Dan Williams   isci: retire scic...
2346
2347
  void sci_controller_link_down(struct isci_host *ihost, struct isci_port *iport,
  			      struct isci_phy *iphy)
cc9203bf3   Dan Williams   isci: move core/c...
2348
  {
d9dcb4ba7   Dan Williams   isci: unify isci_...
2349
  	switch (ihost->sm.current_state_id) {
e301370ac   Edmund Nadolski   isci: state machi...
2350
2351
  	case SCIC_STARTING:
  	case SCIC_READY:
d9dcb4ba7   Dan Williams   isci: unify isci_...
2352
  		ihost->port_agent.link_down_handler(ihost, &ihost->port_agent,
ffe191c92   Dan Williams   isci: unify isci_...
2353
  						   iport, iphy);
cc9203bf3   Dan Williams   isci: move core/c...
2354
2355
  		break;
  	default:
d9dcb4ba7   Dan Williams   isci: unify isci_...
2356
  		dev_dbg(&ihost->pdev->dev,
cc9203bf3   Dan Williams   isci: move core/c...
2357
2358
2359
2360
  			"%s: SCIC Controller linkdown event from phy %d in "
  			"unexpected state %d
  ",
  			__func__,
852809559   Dan Williams   isci: unify isci_...
2361
  			iphy->phy_index,
d9dcb4ba7   Dan Williams   isci: unify isci_...
2362
  			ihost->sm.current_state_id);
cc9203bf3   Dan Williams   isci: move core/c...
2363
2364
  	}
  }
89a7301f2   Dan Williams   isci: retire scic...
2365
  static bool sci_controller_has_remote_devices_stopping(struct isci_host *ihost)
cc9203bf3   Dan Williams   isci: move core/c...
2366
2367
  {
  	u32 index;
d9dcb4ba7   Dan Williams   isci: unify isci_...
2368
2369
2370
  	for (index = 0; index < ihost->remote_node_entries; index++) {
  		if ((ihost->device_table[index] != NULL) &&
  		   (ihost->device_table[index]->sm.current_state_id == SCI_DEV_STOPPING))
cc9203bf3   Dan Williams   isci: move core/c...
2371
2372
2373
2374
2375
  			return true;
  	}
  
  	return false;
  }
89a7301f2   Dan Williams   isci: retire scic...
2376
2377
  void sci_controller_remote_device_stopped(struct isci_host *ihost,
  					  struct isci_remote_device *idev)
cc9203bf3   Dan Williams   isci: move core/c...
2378
  {
d9dcb4ba7   Dan Williams   isci: unify isci_...
2379
2380
  	if (ihost->sm.current_state_id != SCIC_STOPPING) {
  		dev_dbg(&ihost->pdev->dev,
cc9203bf3   Dan Williams   isci: move core/c...
2381
2382
2383
  			"SCIC Controller 0x%p remote device stopped event "
  			"from device 0x%p in unexpected state %d
  ",
d9dcb4ba7   Dan Williams   isci: unify isci_...
2384
2385
  			ihost, idev,
  			ihost->sm.current_state_id);
cc9203bf3   Dan Williams   isci: move core/c...
2386
2387
  		return;
  	}
89a7301f2   Dan Williams   isci: retire scic...
2388
  	if (!sci_controller_has_remote_devices_stopping(ihost))
d9dcb4ba7   Dan Williams   isci: unify isci_...
2389
  		sci_change_state(&ihost->sm, SCIC_STOPPED);
cc9203bf3   Dan Williams   isci: move core/c...
2390
  }
89a7301f2   Dan Williams   isci: retire scic...
2391
  void sci_controller_post_request(struct isci_host *ihost, u32 request)
cc9203bf3   Dan Williams   isci: move core/c...
2392
  {
89a7301f2   Dan Williams   isci: retire scic...
2393
2394
2395
  	dev_dbg(&ihost->pdev->dev, "%s[%d]: %#x
  ",
  		__func__, ihost->id, request);
cc9203bf3   Dan Williams   isci: move core/c...
2396

d9dcb4ba7   Dan Williams   isci: unify isci_...
2397
  	writel(request, &ihost->smu_registers->post_context_port);
cc9203bf3   Dan Williams   isci: move core/c...
2398
  }
89a7301f2   Dan Williams   isci: retire scic...
2399
  struct isci_request *sci_request_by_tag(struct isci_host *ihost, u16 io_tag)
cc9203bf3   Dan Williams   isci: move core/c...
2400
2401
2402
  {
  	u16 task_index;
  	u16 task_sequence;
dd047c8e2   Dan Williams   isci: cleanup tag...
2403
  	task_index = ISCI_TAG_TCI(io_tag);
cc9203bf3   Dan Williams   isci: move core/c...
2404

d9dcb4ba7   Dan Williams   isci: unify isci_...
2405
2406
  	if (task_index < ihost->task_context_entries) {
  		struct isci_request *ireq = ihost->reqs[task_index];
db0562509   Dan Williams   isci: preallocate...
2407
2408
  
  		if (test_bit(IREQ_ACTIVE, &ireq->flags)) {
dd047c8e2   Dan Williams   isci: cleanup tag...
2409
  			task_sequence = ISCI_TAG_SEQ(io_tag);
cc9203bf3   Dan Williams   isci: move core/c...
2410

d9dcb4ba7   Dan Williams   isci: unify isci_...
2411
  			if (task_sequence == ihost->io_request_sequence[task_index])
5076a1a97   Dan Williams   isci: unify isci_...
2412
  				return ireq;
cc9203bf3   Dan Williams   isci: move core/c...
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
  		}
  	}
  
  	return NULL;
  }
  
  /**
   * This method allocates remote node index and the reserves the remote node
   *    context space for use. This method can fail if there are no more remote
   *    node index available.
   * @scic: This is the controller object which contains the set of
   *    free remote node ids
   * @sci_dev: This is the device object which is requesting the a remote node
   *    id
   * @node_id: This is the remote node id that is assinged to the device if one
   *    is available
   *
   * enum sci_status SCI_FAILURE_OUT_OF_RESOURCES if there are no available remote
   * node index available.
   */
89a7301f2   Dan Williams   isci: retire scic...
2433
2434
2435
  enum sci_status sci_controller_allocate_remote_node_context(struct isci_host *ihost,
  							    struct isci_remote_device *idev,
  							    u16 *node_id)
cc9203bf3   Dan Williams   isci: move core/c...
2436
2437
  {
  	u16 node_index;
89a7301f2   Dan Williams   isci: retire scic...
2438
  	u32 remote_node_count = sci_remote_device_node_count(idev);
cc9203bf3   Dan Williams   isci: move core/c...
2439

89a7301f2   Dan Williams   isci: retire scic...
2440
  	node_index = sci_remote_node_table_allocate_remote_node(
d9dcb4ba7   Dan Williams   isci: unify isci_...
2441
  		&ihost->available_remote_nodes, remote_node_count
cc9203bf3   Dan Williams   isci: move core/c...
2442
2443
2444
  		);
  
  	if (node_index != SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX) {
d9dcb4ba7   Dan Williams   isci: unify isci_...
2445
  		ihost->device_table[node_index] = idev;
cc9203bf3   Dan Williams   isci: move core/c...
2446
2447
2448
2449
2450
2451
2452
2453
  
  		*node_id = node_index;
  
  		return SCI_SUCCESS;
  	}
  
  	return SCI_FAILURE_INSUFFICIENT_RESOURCES;
  }
89a7301f2   Dan Williams   isci: retire scic...
2454
2455
2456
  void sci_controller_free_remote_node_context(struct isci_host *ihost,
  					     struct isci_remote_device *idev,
  					     u16 node_id)
cc9203bf3   Dan Williams   isci: move core/c...
2457
  {
89a7301f2   Dan Williams   isci: retire scic...
2458
  	u32 remote_node_count = sci_remote_device_node_count(idev);
cc9203bf3   Dan Williams   isci: move core/c...
2459

d9dcb4ba7   Dan Williams   isci: unify isci_...
2460
2461
  	if (ihost->device_table[node_id] == idev) {
  		ihost->device_table[node_id] = NULL;
cc9203bf3   Dan Williams   isci: move core/c...
2462

89a7301f2   Dan Williams   isci: retire scic...
2463
  		sci_remote_node_table_release_remote_node_index(
d9dcb4ba7   Dan Williams   isci: unify isci_...
2464
  			&ihost->available_remote_nodes, remote_node_count, node_id
cc9203bf3   Dan Williams   isci: move core/c...
2465
2466
2467
  			);
  	}
  }
89a7301f2   Dan Williams   isci: retire scic...
2468
2469
2470
  void sci_controller_copy_sata_response(void *response_buffer,
  				       void *frame_header,
  				       void *frame_buffer)
cc9203bf3   Dan Williams   isci: move core/c...
2471
  {
89a7301f2   Dan Williams   isci: retire scic...
2472
  	/* XXX type safety? */
cc9203bf3   Dan Williams   isci: move core/c...
2473
2474
2475
2476
2477
2478
  	memcpy(response_buffer, frame_header, sizeof(u32));
  
  	memcpy(response_buffer + sizeof(u32),
  	       frame_buffer,
  	       sizeof(struct dev_to_host_fis) - sizeof(u32));
  }
89a7301f2   Dan Williams   isci: retire scic...
2479
  void sci_controller_release_frame(struct isci_host *ihost, u32 frame_index)
cc9203bf3   Dan Williams   isci: move core/c...
2480
  {
89a7301f2   Dan Williams   isci: retire scic...
2481
  	if (sci_unsolicited_frame_control_release_frame(&ihost->uf_control, frame_index))
d9dcb4ba7   Dan Williams   isci: unify isci_...
2482
2483
  		writel(ihost->uf_control.get,
  			&ihost->scu_registers->sdma.unsolicited_frame_get_pointer);
cc9203bf3   Dan Williams   isci: move core/c...
2484
  }
312e0c245   Dan Williams   isci: unify can_q...
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
  void isci_tci_free(struct isci_host *ihost, u16 tci)
  {
  	u16 tail = ihost->tci_tail & (SCI_MAX_IO_REQUESTS-1);
  
  	ihost->tci_pool[tail] = tci;
  	ihost->tci_tail = tail + 1;
  }
  
  static u16 isci_tci_alloc(struct isci_host *ihost)
  {
  	u16 head = ihost->tci_head & (SCI_MAX_IO_REQUESTS-1);
  	u16 tci = ihost->tci_pool[head];
  
  	ihost->tci_head = head + 1;
  	return tci;
  }
  
  static u16 isci_tci_space(struct isci_host *ihost)
  {
  	return CIRC_SPACE(ihost->tci_head, ihost->tci_tail, SCI_MAX_IO_REQUESTS);
  }
  
  u16 isci_alloc_tag(struct isci_host *ihost)
  {
  	if (isci_tci_space(ihost)) {
  		u16 tci = isci_tci_alloc(ihost);
d9dcb4ba7   Dan Williams   isci: unify isci_...
2511
  		u8 seq = ihost->io_request_sequence[tci];
312e0c245   Dan Williams   isci: unify can_q...
2512
2513
2514
2515
2516
2517
2518
2519
2520
  
  		return ISCI_TAG(seq, tci);
  	}
  
  	return SCI_CONTROLLER_INVALID_IO_TAG;
  }
  
  enum sci_status isci_free_tag(struct isci_host *ihost, u16 io_tag)
  {
312e0c245   Dan Williams   isci: unify can_q...
2521
2522
2523
2524
2525
2526
  	u16 tci = ISCI_TAG_TCI(io_tag);
  	u16 seq = ISCI_TAG_SEQ(io_tag);
  
  	/* prevent tail from passing head */
  	if (isci_tci_active(ihost) == 0)
  		return SCI_FAILURE_INVALID_IO_TAG;
d9dcb4ba7   Dan Williams   isci: unify isci_...
2527
2528
  	if (seq == ihost->io_request_sequence[tci]) {
  		ihost->io_request_sequence[tci] = (seq+1) & (SCI_MAX_SEQ-1);
312e0c245   Dan Williams   isci: unify can_q...
2529
2530
2531
2532
2533
2534
2535
  
  		isci_tci_free(ihost, tci);
  
  		return SCI_SUCCESS;
  	}
  	return SCI_FAILURE_INVALID_IO_TAG;
  }
89a7301f2   Dan Williams   isci: retire scic...
2536
2537
2538
  enum sci_status sci_controller_start_io(struct isci_host *ihost,
  					struct isci_remote_device *idev,
  					struct isci_request *ireq)
cc9203bf3   Dan Williams   isci: move core/c...
2539
2540
  {
  	enum sci_status status;
d9dcb4ba7   Dan Williams   isci: unify isci_...
2541
2542
  	if (ihost->sm.current_state_id != SCIC_READY) {
  		dev_warn(&ihost->pdev->dev, "invalid state to start I/O");
cc9203bf3   Dan Williams   isci: move core/c...
2543
2544
  		return SCI_FAILURE_INVALID_STATE;
  	}
89a7301f2   Dan Williams   isci: retire scic...
2545
  	status = sci_remote_device_start_io(ihost, idev, ireq);
cc9203bf3   Dan Williams   isci: move core/c...
2546
2547
  	if (status != SCI_SUCCESS)
  		return status;
5076a1a97   Dan Williams   isci: unify isci_...
2548
  	set_bit(IREQ_ACTIVE, &ireq->flags);
34a991587   Dan Williams   isci: kill 'get/s...
2549
  	sci_controller_post_request(ihost, ireq->post_context);
cc9203bf3   Dan Williams   isci: move core/c...
2550
2551
  	return SCI_SUCCESS;
  }
89a7301f2   Dan Williams   isci: retire scic...
2552
2553
2554
  enum sci_status sci_controller_terminate_request(struct isci_host *ihost,
  						 struct isci_remote_device *idev,
  						 struct isci_request *ireq)
cc9203bf3   Dan Williams   isci: move core/c...
2555
  {
89a7301f2   Dan Williams   isci: retire scic...
2556
2557
2558
2559
  	/* terminate an ongoing (i.e. started) core IO request.  This does not
  	 * abort the IO request at the target, but rather removes the IO
  	 * request from the host controller.
  	 */
cc9203bf3   Dan Williams   isci: move core/c...
2560
  	enum sci_status status;
d9dcb4ba7   Dan Williams   isci: unify isci_...
2561
2562
  	if (ihost->sm.current_state_id != SCIC_READY) {
  		dev_warn(&ihost->pdev->dev,
cc9203bf3   Dan Williams   isci: move core/c...
2563
2564
2565
2566
  			 "invalid state to terminate request
  ");
  		return SCI_FAILURE_INVALID_STATE;
  	}
89a7301f2   Dan Williams   isci: retire scic...
2567
  	status = sci_io_request_terminate(ireq);
cc9203bf3   Dan Williams   isci: move core/c...
2568
2569
2570
2571
2572
2573
2574
  	if (status != SCI_SUCCESS)
  		return status;
  
  	/*
  	 * Utilize the original post context command and or in the POST_TC_ABORT
  	 * request sub-type.
  	 */
89a7301f2   Dan Williams   isci: retire scic...
2575
2576
  	sci_controller_post_request(ihost,
  				    ireq->post_context | SCU_CONTEXT_COMMAND_REQUEST_POST_TC_ABORT);
cc9203bf3   Dan Williams   isci: move core/c...
2577
2578
2579
2580
  	return SCI_SUCCESS;
  }
  
  /**
89a7301f2   Dan Williams   isci: retire scic...
2581
   * sci_controller_complete_io() - This method will perform core specific
cc9203bf3   Dan Williams   isci: move core/c...
2582
2583
2584
   *    completion operations for an IO request.  After this method is invoked,
   *    the user should consider the IO request as invalid until it is properly
   *    reused (i.e. re-constructed).
89a7301f2   Dan Williams   isci: retire scic...
2585
   * @ihost: The handle to the controller object for which to complete the
cc9203bf3   Dan Williams   isci: move core/c...
2586
   *    IO request.
89a7301f2   Dan Williams   isci: retire scic...
2587
   * @idev: The handle to the remote device object for which to complete
cc9203bf3   Dan Williams   isci: move core/c...
2588
   *    the IO request.
89a7301f2   Dan Williams   isci: retire scic...
2589
   * @ireq: the handle to the io request object to complete.
cc9203bf3   Dan Williams   isci: move core/c...
2590
   */
89a7301f2   Dan Williams   isci: retire scic...
2591
2592
2593
  enum sci_status sci_controller_complete_io(struct isci_host *ihost,
  					   struct isci_remote_device *idev,
  					   struct isci_request *ireq)
cc9203bf3   Dan Williams   isci: move core/c...
2594
2595
2596
  {
  	enum sci_status status;
  	u16 index;
d9dcb4ba7   Dan Williams   isci: unify isci_...
2597
  	switch (ihost->sm.current_state_id) {
e301370ac   Edmund Nadolski   isci: state machi...
2598
  	case SCIC_STOPPING:
cc9203bf3   Dan Williams   isci: move core/c...
2599
2600
  		/* XXX: Implement this function */
  		return SCI_FAILURE;
e301370ac   Edmund Nadolski   isci: state machi...
2601
  	case SCIC_READY:
89a7301f2   Dan Williams   isci: retire scic...
2602
  		status = sci_remote_device_complete_io(ihost, idev, ireq);
cc9203bf3   Dan Williams   isci: move core/c...
2603
2604
  		if (status != SCI_SUCCESS)
  			return status;
5076a1a97   Dan Williams   isci: unify isci_...
2605
2606
  		index = ISCI_TAG_TCI(ireq->io_tag);
  		clear_bit(IREQ_ACTIVE, &ireq->flags);
cc9203bf3   Dan Williams   isci: move core/c...
2607
2608
  		return SCI_SUCCESS;
  	default:
d9dcb4ba7   Dan Williams   isci: unify isci_...
2609
  		dev_warn(&ihost->pdev->dev, "invalid state to complete I/O");
cc9203bf3   Dan Williams   isci: move core/c...
2610
2611
2612
2613
  		return SCI_FAILURE_INVALID_STATE;
  	}
  
  }
89a7301f2   Dan Williams   isci: retire scic...
2614
  enum sci_status sci_controller_continue_io(struct isci_request *ireq)
cc9203bf3   Dan Williams   isci: move core/c...
2615
  {
d9dcb4ba7   Dan Williams   isci: unify isci_...
2616
  	struct isci_host *ihost = ireq->owning_controller;
cc9203bf3   Dan Williams   isci: move core/c...
2617

d9dcb4ba7   Dan Williams   isci: unify isci_...
2618
2619
  	if (ihost->sm.current_state_id != SCIC_READY) {
  		dev_warn(&ihost->pdev->dev, "invalid state to continue I/O");
cc9203bf3   Dan Williams   isci: move core/c...
2620
2621
  		return SCI_FAILURE_INVALID_STATE;
  	}
5076a1a97   Dan Williams   isci: unify isci_...
2622
  	set_bit(IREQ_ACTIVE, &ireq->flags);
34a991587   Dan Williams   isci: kill 'get/s...
2623
  	sci_controller_post_request(ihost, ireq->post_context);
cc9203bf3   Dan Williams   isci: move core/c...
2624
2625
2626
2627
  	return SCI_SUCCESS;
  }
  
  /**
89a7301f2   Dan Williams   isci: retire scic...
2628
   * sci_controller_start_task() - This method is called by the SCIC user to
cc9203bf3   Dan Williams   isci: move core/c...
2629
2630
2631
2632
2633
2634
   *    send/start a framework task management request.
   * @controller: the handle to the controller object for which to start the task
   *    management request.
   * @remote_device: the handle to the remote device object for which to start
   *    the task management request.
   * @task_request: the handle to the task request object to start.
cc9203bf3   Dan Williams   isci: move core/c...
2635
   */
89a7301f2   Dan Williams   isci: retire scic...
2636
2637
2638
  enum sci_task_status sci_controller_start_task(struct isci_host *ihost,
  					       struct isci_remote_device *idev,
  					       struct isci_request *ireq)
cc9203bf3   Dan Williams   isci: move core/c...
2639
2640
  {
  	enum sci_status status;
d9dcb4ba7   Dan Williams   isci: unify isci_...
2641
2642
  	if (ihost->sm.current_state_id != SCIC_READY) {
  		dev_warn(&ihost->pdev->dev,
cc9203bf3   Dan Williams   isci: move core/c...
2643
2644
2645
2646
2647
2648
  			 "%s: SCIC Controller starting task from invalid "
  			 "state
  ",
  			 __func__);
  		return SCI_TASK_FAILURE_INVALID_STATE;
  	}
89a7301f2   Dan Williams   isci: retire scic...
2649
  	status = sci_remote_device_start_task(ihost, idev, ireq);
cc9203bf3   Dan Williams   isci: move core/c...
2650
2651
  	switch (status) {
  	case SCI_FAILURE_RESET_DEVICE_PARTIAL_SUCCESS:
db0562509   Dan Williams   isci: preallocate...
2652
  		set_bit(IREQ_ACTIVE, &ireq->flags);
cc9203bf3   Dan Williams   isci: move core/c...
2653
2654
2655
2656
2657
2658
2659
2660
  
  		/*
  		 * We will let framework know this task request started successfully,
  		 * although core is still woring on starting the request (to post tc when
  		 * RNC is resumed.)
  		 */
  		return SCI_SUCCESS;
  	case SCI_SUCCESS:
db0562509   Dan Williams   isci: preallocate...
2661
  		set_bit(IREQ_ACTIVE, &ireq->flags);
34a991587   Dan Williams   isci: kill 'get/s...
2662
  		sci_controller_post_request(ihost, ireq->post_context);
cc9203bf3   Dan Williams   isci: move core/c...
2663
2664
2665
2666
2667
2668
2669
  		break;
  	default:
  		break;
  	}
  
  	return status;
  }
ad4f4c1de   Dan Williams   [SCSI] isci: init...
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
  
  static int sci_write_gpio_tx_gp(struct isci_host *ihost, u8 reg_index, u8 reg_count, u8 *write_data)
  {
  	int d;
  
  	/* no support for TX_GP_CFG */
  	if (reg_index == 0)
  		return -EINVAL;
  
  	for (d = 0; d < isci_gpio_count(ihost); d++) {
  		u32 val = 0x444; /* all ODx.n clear */
  		int i;
  
  		for (i = 0; i < 3; i++) {
  			int bit = (i << 2) + 2;
  
  			bit = try_test_sas_gpio_gp_bit(to_sas_gpio_od(d, i),
  						       write_data, reg_index,
  						       reg_count);
  			if (bit < 0)
  				break;
  
  			/* if od is set, clear the 'invert' bit */
  			val &= ~(bit << ((i << 2) + 2));
  		}
  
  		if (i < 3)
  			break;
  		writel(val, &ihost->scu_registers->peg0.sgpio.output_data_select[d]);
  	}
  
  	/* unless reg_index is > 1, we should always be able to write at
  	 * least one register
  	 */
  	return d > 0;
  }
  
  int isci_gpio_write(struct sas_ha_struct *sas_ha, u8 reg_type, u8 reg_index,
  		    u8 reg_count, u8 *write_data)
  {
  	struct isci_host *ihost = sas_ha->lldd_ha;
  	int written;
  
  	switch (reg_type) {
  	case SAS_GPIO_REG_TX_GP:
  		written = sci_write_gpio_tx_gp(ihost, reg_index, reg_count, write_data);
  		break;
  	default:
  		written = -EINVAL;
  	}
  
  	return written;
  }