diff options
author | Dan Williams <dan.j.williams@intel.com> | 2011-07-03 01:56:22 -0400 |
---|---|---|
committer | Dan Williams <dan.j.williams@intel.com> | 2011-07-03 01:56:22 -0400 |
commit | 6f231dda68080759f1aed3769896e94c73099f0f (patch) | |
tree | 45b6ce02fa40e0e9c35526ac6c45950138387516 /drivers/scsi/isci/host.c | |
parent | 59c5f46fbe01a00eedf54a23789634438bb80603 (diff) |
isci: Intel(R) C600 Series Chipset Storage Control Unit Driver
Support for the up to 2x4-port 6Gb/s SAS controllers embedded in the
chipset.
This is a snapshot of the first publicly available version of the driver,
commit 4c1db2d0 in the 'historical' branch.
git://git.kernel.org/pub/scm/linux/kernel/git/djbw/isci.git historical
Signed-off-by: Maciej Trela <maciej.trela@intel.com>
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
Signed-off-by: Edmund Nadolski <edmund.nadolski@intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Diffstat (limited to 'drivers/scsi/isci/host.c')
-rw-r--r-- | drivers/scsi/isci/host.c | 781 |
1 files changed, 781 insertions, 0 deletions
diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c new file mode 100644 index 000000000000..6f16f4d6c82b --- /dev/null +++ b/drivers/scsi/isci/host.c | |||
@@ -0,0 +1,781 @@ | |||
1 | /* | ||
2 | * This file is provided under a dual BSD/GPLv2 license. When using or | ||
3 | * redistributing this file, you may do so under either license. | ||
4 | * | ||
5 | * GPL LICENSE SUMMARY | ||
6 | * | ||
7 | * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of version 2 of the GNU General Public License as | ||
11 | * published by the Free Software Foundation. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, but | ||
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
16 | * General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | ||
21 | * The full GNU General Public License is included in this distribution | ||
22 | * in the file called LICENSE.GPL. | ||
23 | * | ||
24 | * BSD LICENSE | ||
25 | * | ||
26 | * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. | ||
27 | * All rights reserved. | ||
28 | * | ||
29 | * Redistribution and use in source and binary forms, with or without | ||
30 | * modification, are permitted provided that the following conditions | ||
31 | * are met: | ||
32 | * | ||
33 | * * Redistributions of source code must retain the above copyright | ||
34 | * notice, this list of conditions and the following disclaimer. | ||
35 | * * Redistributions in binary form must reproduce the above copyright | ||
36 | * notice, this list of conditions and the following disclaimer in | ||
37 | * the documentation and/or other materials provided with the | ||
38 | * distribution. | ||
39 | * * Neither the name of Intel Corporation nor the names of its | ||
40 | * contributors may be used to endorse or promote products derived | ||
41 | * from this software without specific prior written permission. | ||
42 | * | ||
43 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
44 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
45 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
46 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
47 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
48 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
49 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
50 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
51 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
52 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
53 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
54 | */ | ||
55 | |||
56 | #include "isci.h" | ||
57 | #include "scic_io_request.h" | ||
58 | #include "scic_remote_device.h" | ||
59 | #include "scic_port.h" | ||
60 | |||
61 | #include "port.h" | ||
62 | #include "request.h" | ||
63 | #include "host.h" | ||
64 | |||
65 | /** | ||
66 | * isci_isr() - This function is the interrupt service routine for the | ||
67 | * controller. It schedules the tasklet and returns. | ||
68 | * @vec: This parameter specifies the interrupt vector. | ||
69 | * @data: This parameter specifies the ISCI host object. | ||
70 | * | ||
71 | * IRQ_HANDLED if out interrupt otherwise, IRQ_NONE | ||
72 | */ | ||
73 | irqreturn_t isci_isr(int vec, void *data) | ||
74 | { | ||
75 | struct isci_host *isci_host | ||
76 | = (struct isci_host *)data; | ||
77 | struct scic_controller_handler_methods *handlers | ||
78 | = &isci_host->scic_irq_handlers[SCI_MSIX_NORMAL_VECTOR]; | ||
79 | irqreturn_t ret = IRQ_NONE; | ||
80 | |||
81 | if (isci_host_get_state(isci_host) != isci_starting | ||
82 | && handlers->interrupt_handler) { | ||
83 | |||
84 | if (handlers->interrupt_handler(isci_host->core_controller)) { | ||
85 | if (isci_host_get_state(isci_host) != isci_stopped) { | ||
86 | tasklet_schedule( | ||
87 | &isci_host->completion_tasklet); | ||
88 | } else | ||
89 | dev_dbg(&isci_host->pdev->dev, | ||
90 | "%s: controller stopped\n", | ||
91 | __func__); | ||
92 | ret = IRQ_HANDLED; | ||
93 | } | ||
94 | } else | ||
95 | dev_warn(&isci_host->pdev->dev, | ||
96 | "%s: get_handler_methods failed, " | ||
97 | "isci_host->status = 0x%x\n", | ||
98 | __func__, | ||
99 | isci_host_get_state(isci_host)); | ||
100 | |||
101 | return ret; | ||
102 | } | ||
103 | |||
104 | irqreturn_t isci_legacy_isr(int vec, void *data) | ||
105 | { | ||
106 | struct pci_dev *pdev = data; | ||
107 | struct isci_host *isci_host; | ||
108 | struct scic_controller_handler_methods *handlers; | ||
109 | irqreturn_t ret = IRQ_NONE; | ||
110 | |||
111 | /* | ||
112 | * Since this is a legacy interrupt, either or both | ||
113 | * controllers could have triggered it. Thus, we have to call | ||
114 | * the legacy interrupt handler for all controllers on the | ||
115 | * PCI function. | ||
116 | */ | ||
117 | for_each_isci_host(isci_host, pdev) { | ||
118 | handlers = &isci_host->scic_irq_handlers[SCI_MSIX_NORMAL_VECTOR]; | ||
119 | |||
120 | if (isci_host_get_state(isci_host) != isci_starting | ||
121 | && handlers->interrupt_handler) { | ||
122 | |||
123 | if (handlers->interrupt_handler(isci_host->core_controller)) { | ||
124 | if (isci_host_get_state(isci_host) != isci_stopped) { | ||
125 | tasklet_schedule( | ||
126 | &isci_host->completion_tasklet); | ||
127 | } else | ||
128 | dev_dbg(&isci_host->pdev->dev, | ||
129 | "%s: controller stopped\n", | ||
130 | __func__); | ||
131 | ret = IRQ_HANDLED; | ||
132 | } | ||
133 | } else | ||
134 | dev_warn(&isci_host->pdev->dev, | ||
135 | "%s: get_handler_methods failed, " | ||
136 | "isci_host->status = 0x%x\n", | ||
137 | __func__, | ||
138 | isci_host_get_state(isci_host)); | ||
139 | } | ||
140 | return ret; | ||
141 | } | ||
142 | |||
143 | |||
144 | /** | ||
145 | * isci_host_start_complete() - This function is called by the core library, | ||
146 | * through the ISCI Module, to indicate controller start status. | ||
147 | * @isci_host: This parameter specifies the ISCI host object | ||
148 | * @completion_status: This parameter specifies the completion status from the | ||
149 | * core library. | ||
150 | * | ||
151 | */ | ||
152 | void isci_host_start_complete( | ||
153 | struct isci_host *isci_host, | ||
154 | enum sci_status completion_status) | ||
155 | { | ||
156 | if (completion_status == SCI_SUCCESS) { | ||
157 | dev_dbg(&isci_host->pdev->dev, | ||
158 | "%s: completion_status: SCI_SUCCESS\n", __func__); | ||
159 | isci_host_change_state(isci_host, isci_ready); | ||
160 | complete_all(&isci_host->start_complete); | ||
161 | } else | ||
162 | dev_err(&isci_host->pdev->dev, | ||
163 | "controller start failed with " | ||
164 | "completion_status = 0x%x;", | ||
165 | completion_status); | ||
166 | |||
167 | } | ||
168 | |||
169 | |||
170 | |||
171 | /** | ||
172 | * isci_host_scan_finished() - This function is one of the SCSI Host Template | ||
173 | * functions. The SCSI midlayer calls this function during a target scan, | ||
174 | * approx. once every 10 millisecs. | ||
175 | * @shost: This parameter specifies the SCSI host being scanned | ||
176 | * @time: This parameter specifies the number of ticks since the scan started. | ||
177 | * | ||
178 | * scan status, zero indicates the SCSI midlayer should continue to poll, | ||
179 | * otherwise assume controller is ready. | ||
180 | */ | ||
181 | int isci_host_scan_finished( | ||
182 | struct Scsi_Host *shost, | ||
183 | unsigned long time) | ||
184 | { | ||
185 | struct isci_host *isci_host | ||
186 | = isci_host_from_sas_ha(SHOST_TO_SAS_HA(shost)); | ||
187 | |||
188 | struct scic_controller_handler_methods *handlers | ||
189 | = &isci_host->scic_irq_handlers[SCI_MSIX_NORMAL_VECTOR]; | ||
190 | |||
191 | if (handlers->interrupt_handler == NULL) { | ||
192 | dev_err(&isci_host->pdev->dev, | ||
193 | "%s: scic_controller_get_handler_methods failed\n", | ||
194 | __func__); | ||
195 | return 1; | ||
196 | } | ||
197 | |||
198 | /** | ||
199 | * check interrupt_handler's status and call completion_handler if true, | ||
200 | * link_up events should be coming from the scu core lib, as phy's come | ||
201 | * online. for each link_up from the core, call | ||
202 | * get_received_identify_address_frame, copy the frame into the | ||
203 | * sas_phy object and call libsas notify_port_event(PORTE_BYTES_DMAED). | ||
204 | * continue to return zero from thee scan_finished routine until | ||
205 | * the scic_cb_controller_start_complete() call comes from the core. | ||
206 | **/ | ||
207 | if (handlers->interrupt_handler(isci_host->core_controller)) | ||
208 | handlers->completion_handler(isci_host->core_controller); | ||
209 | |||
210 | if (isci_starting == isci_host_get_state(isci_host) | ||
211 | && time < (HZ * 10)) { | ||
212 | dev_dbg(&isci_host->pdev->dev, | ||
213 | "%s: isci_host->status = %d, time = %ld\n", | ||
214 | __func__, isci_host_get_state(isci_host), time); | ||
215 | return 0; | ||
216 | } | ||
217 | |||
218 | |||
219 | dev_dbg(&isci_host->pdev->dev, | ||
220 | "%s: isci_host->status = %d, time = %ld\n", | ||
221 | __func__, isci_host_get_state(isci_host), time); | ||
222 | |||
223 | scic_controller_enable_interrupts(isci_host->core_controller); | ||
224 | |||
225 | return 1; | ||
226 | |||
227 | } | ||
228 | |||
229 | |||
230 | /** | ||
231 | * isci_host_scan_start() - This function is one of the SCSI Host Template | ||
232 | * function, called by the SCSI mid layer berfore a target scan begins. The | ||
233 | * core library controller start routine is called from here. | ||
234 | * @shost: This parameter specifies the SCSI host to be scanned | ||
235 | * | ||
236 | */ | ||
237 | void isci_host_scan_start(struct Scsi_Host *shost) | ||
238 | { | ||
239 | struct isci_host *isci_host; | ||
240 | |||
241 | isci_host = isci_host_from_sas_ha(SHOST_TO_SAS_HA(shost)); | ||
242 | isci_host_change_state(isci_host, isci_starting); | ||
243 | |||
244 | scic_controller_disable_interrupts(isci_host->core_controller); | ||
245 | init_completion(&isci_host->start_complete); | ||
246 | scic_controller_start( | ||
247 | isci_host->core_controller, | ||
248 | scic_controller_get_suggested_start_timeout( | ||
249 | isci_host->core_controller) | ||
250 | ); | ||
251 | } | ||
252 | |||
253 | void isci_host_stop_complete( | ||
254 | struct isci_host *isci_host, | ||
255 | enum sci_status completion_status) | ||
256 | { | ||
257 | isci_host_change_state(isci_host, isci_stopped); | ||
258 | scic_controller_disable_interrupts( | ||
259 | isci_host->core_controller | ||
260 | ); | ||
261 | complete(&isci_host->stop_complete); | ||
262 | } | ||
263 | |||
264 | static struct coherent_memory_info *isci_host_alloc_mdl_struct( | ||
265 | struct isci_host *isci_host, | ||
266 | u32 size) | ||
267 | { | ||
268 | struct coherent_memory_info *mdl_struct; | ||
269 | void *uncached_address = NULL; | ||
270 | |||
271 | |||
272 | mdl_struct = devm_kzalloc(&isci_host->pdev->dev, | ||
273 | sizeof(*mdl_struct), | ||
274 | GFP_KERNEL); | ||
275 | if (!mdl_struct) | ||
276 | return NULL; | ||
277 | |||
278 | INIT_LIST_HEAD(&mdl_struct->node); | ||
279 | |||
280 | uncached_address = dmam_alloc_coherent(&isci_host->pdev->dev, | ||
281 | size, | ||
282 | &mdl_struct->dma_handle, | ||
283 | GFP_KERNEL); | ||
284 | if (!uncached_address) | ||
285 | return NULL; | ||
286 | |||
287 | /* memset the whole memory area. */ | ||
288 | memset((char *)uncached_address, 0, size); | ||
289 | mdl_struct->vaddr = uncached_address; | ||
290 | mdl_struct->size = (size_t)size; | ||
291 | |||
292 | return mdl_struct; | ||
293 | } | ||
294 | |||
295 | static void isci_host_build_mde( | ||
296 | struct sci_physical_memory_descriptor *mde_struct, | ||
297 | struct coherent_memory_info *mdl_struct) | ||
298 | { | ||
299 | unsigned long address = 0; | ||
300 | dma_addr_t dma_addr = 0; | ||
301 | |||
302 | address = (unsigned long)mdl_struct->vaddr; | ||
303 | dma_addr = mdl_struct->dma_handle; | ||
304 | |||
305 | /* to satisfy the alignment. */ | ||
306 | if ((address % mde_struct->constant_memory_alignment) != 0) { | ||
307 | int align_offset | ||
308 | = (mde_struct->constant_memory_alignment | ||
309 | - (address % mde_struct->constant_memory_alignment)); | ||
310 | address += align_offset; | ||
311 | dma_addr += align_offset; | ||
312 | } | ||
313 | |||
314 | mde_struct->virtual_address = (void *)address; | ||
315 | mde_struct->physical_address = dma_addr; | ||
316 | mdl_struct->mde = mde_struct; | ||
317 | } | ||
318 | |||
319 | static int isci_host_mdl_allocate_coherent( | ||
320 | struct isci_host *isci_host) | ||
321 | { | ||
322 | struct sci_physical_memory_descriptor *current_mde; | ||
323 | struct coherent_memory_info *mdl_struct; | ||
324 | u32 size = 0; | ||
325 | |||
326 | struct sci_base_memory_descriptor_list *mdl_handle | ||
327 | = sci_controller_get_memory_descriptor_list_handle( | ||
328 | isci_host->core_controller); | ||
329 | |||
330 | sci_mdl_first_entry(mdl_handle); | ||
331 | |||
332 | current_mde = sci_mdl_get_current_entry(mdl_handle); | ||
333 | |||
334 | while (current_mde != NULL) { | ||
335 | |||
336 | size = (current_mde->constant_memory_size | ||
337 | + current_mde->constant_memory_alignment); | ||
338 | |||
339 | mdl_struct = isci_host_alloc_mdl_struct(isci_host, size); | ||
340 | if (!mdl_struct) | ||
341 | return -ENOMEM; | ||
342 | |||
343 | list_add_tail(&mdl_struct->node, &isci_host->mdl_struct_list); | ||
344 | |||
345 | isci_host_build_mde(current_mde, mdl_struct); | ||
346 | |||
347 | sci_mdl_next_entry(mdl_handle); | ||
348 | current_mde = sci_mdl_get_current_entry(mdl_handle); | ||
349 | } | ||
350 | |||
351 | return 0; | ||
352 | } | ||
353 | |||
354 | |||
355 | /** | ||
356 | * isci_host_completion_routine() - This function is the delayed service | ||
357 | * routine that calls the sci core library's completion handler. It's | ||
358 | * scheduled as a tasklet from the interrupt service routine when interrupts | ||
359 | * in use, or set as the timeout function in polled mode. | ||
360 | * @data: This parameter specifies the ISCI host object | ||
361 | * | ||
362 | */ | ||
363 | static void isci_host_completion_routine(unsigned long data) | ||
364 | { | ||
365 | struct isci_host *isci_host = (struct isci_host *)data; | ||
366 | struct scic_controller_handler_methods *handlers | ||
367 | = &isci_host->scic_irq_handlers[SCI_MSIX_NORMAL_VECTOR]; | ||
368 | struct list_head completed_request_list; | ||
369 | struct list_head aborted_request_list; | ||
370 | struct list_head *current_position; | ||
371 | struct list_head *next_position; | ||
372 | struct isci_request *request; | ||
373 | struct isci_request *next_request; | ||
374 | struct sas_task *task; | ||
375 | |||
376 | INIT_LIST_HEAD(&completed_request_list); | ||
377 | INIT_LIST_HEAD(&aborted_request_list); | ||
378 | |||
379 | spin_lock_irq(&isci_host->scic_lock); | ||
380 | |||
381 | if (handlers->completion_handler) { | ||
382 | handlers->completion_handler( | ||
383 | isci_host->core_controller | ||
384 | ); | ||
385 | } | ||
386 | /* Take the lists of completed I/Os from the host. */ | ||
387 | list_splice_init(&isci_host->requests_to_complete, | ||
388 | &completed_request_list); | ||
389 | |||
390 | list_splice_init(&isci_host->requests_to_abort, | ||
391 | &aborted_request_list); | ||
392 | |||
393 | spin_unlock_irq(&isci_host->scic_lock); | ||
394 | |||
395 | /* Process any completions in the lists. */ | ||
396 | list_for_each_safe(current_position, next_position, | ||
397 | &completed_request_list) { | ||
398 | |||
399 | request = list_entry(current_position, struct isci_request, | ||
400 | completed_node); | ||
401 | task = isci_request_access_task(request); | ||
402 | |||
403 | /* Normal notification (task_done) */ | ||
404 | dev_dbg(&isci_host->pdev->dev, | ||
405 | "%s: Normal - request/task = %p/%p\n", | ||
406 | __func__, | ||
407 | request, | ||
408 | task); | ||
409 | |||
410 | task->task_done(task); | ||
411 | task->lldd_task = NULL; | ||
412 | |||
413 | /* Free the request object. */ | ||
414 | isci_request_free(isci_host, request); | ||
415 | } | ||
416 | list_for_each_entry_safe(request, next_request, &aborted_request_list, | ||
417 | completed_node) { | ||
418 | |||
419 | task = isci_request_access_task(request); | ||
420 | |||
421 | /* Use sas_task_abort */ | ||
422 | dev_warn(&isci_host->pdev->dev, | ||
423 | "%s: Error - request/task = %p/%p\n", | ||
424 | __func__, | ||
425 | request, | ||
426 | task); | ||
427 | |||
428 | /* Put the task into the abort path. */ | ||
429 | sas_task_abort(task); | ||
430 | } | ||
431 | |||
432 | } | ||
433 | |||
434 | void isci_host_deinit( | ||
435 | struct isci_host *isci_host) | ||
436 | { | ||
437 | int i; | ||
438 | |||
439 | isci_host_change_state(isci_host, isci_stopping); | ||
440 | for (i = 0; i < SCI_MAX_PORTS; i++) { | ||
441 | struct isci_port *port = &isci_host->isci_ports[i]; | ||
442 | struct isci_remote_device *device, *tmpdev; | ||
443 | list_for_each_entry_safe(device, tmpdev, | ||
444 | &port->remote_dev_list, node) { | ||
445 | isci_remote_device_change_state(device, isci_stopping); | ||
446 | isci_remote_device_stop(device); | ||
447 | } | ||
448 | } | ||
449 | |||
450 | /* stop the comtroller and wait for completion. */ | ||
451 | init_completion(&isci_host->stop_complete); | ||
452 | scic_controller_stop( | ||
453 | isci_host->core_controller, | ||
454 | SCIC_CONTROLLER_STOP_TIMEOUT | ||
455 | ); | ||
456 | wait_for_completion(&isci_host->stop_complete); | ||
457 | /* next, reset the controller. */ | ||
458 | scic_controller_reset(isci_host->core_controller); | ||
459 | } | ||
460 | |||
461 | static int isci_verify_firmware(const struct firmware *fw, | ||
462 | struct isci_firmware *isci_fw) | ||
463 | { | ||
464 | const u8 *tmp; | ||
465 | |||
466 | if (fw->size < ISCI_FIRMWARE_MIN_SIZE) | ||
467 | return -EINVAL; | ||
468 | |||
469 | tmp = fw->data; | ||
470 | |||
471 | /* 12th char should be the NULL terminate for the ID string */ | ||
472 | if (tmp[11] != '\0') | ||
473 | return -EINVAL; | ||
474 | |||
475 | if (strncmp("#SCU MAGIC#", tmp, 11) != 0) | ||
476 | return -EINVAL; | ||
477 | |||
478 | isci_fw->id = tmp; | ||
479 | isci_fw->version = fw->data[ISCI_FW_VER_OFS]; | ||
480 | isci_fw->subversion = fw->data[ISCI_FW_SUBVER_OFS]; | ||
481 | |||
482 | tmp = fw->data + ISCI_FW_DATA_OFS; | ||
483 | |||
484 | while (*tmp != ISCI_FW_HDR_EOF) { | ||
485 | switch (*tmp) { | ||
486 | case ISCI_FW_HDR_PHYMASK: | ||
487 | tmp++; | ||
488 | isci_fw->phy_masks_size = *tmp; | ||
489 | tmp++; | ||
490 | isci_fw->phy_masks = (const u32 *)tmp; | ||
491 | tmp += sizeof(u32) * isci_fw->phy_masks_size; | ||
492 | break; | ||
493 | |||
494 | case ISCI_FW_HDR_PHYGEN: | ||
495 | tmp++; | ||
496 | isci_fw->phy_gens_size = *tmp; | ||
497 | tmp++; | ||
498 | isci_fw->phy_gens = (const u32 *)tmp; | ||
499 | tmp += sizeof(u32) * isci_fw->phy_gens_size; | ||
500 | break; | ||
501 | |||
502 | case ISCI_FW_HDR_SASADDR: | ||
503 | tmp++; | ||
504 | isci_fw->sas_addrs_size = *tmp; | ||
505 | tmp++; | ||
506 | isci_fw->sas_addrs = (const u64 *)tmp; | ||
507 | tmp += sizeof(u64) * isci_fw->sas_addrs_size; | ||
508 | break; | ||
509 | |||
510 | default: | ||
511 | pr_err("bad field in firmware binary blob\n"); | ||
512 | return -EINVAL; | ||
513 | } | ||
514 | } | ||
515 | |||
516 | pr_info("isci firmware v%u.%u loaded.\n", | ||
517 | isci_fw->version, isci_fw->subversion); | ||
518 | |||
519 | return SCI_SUCCESS; | ||
520 | } | ||
521 | |||
522 | static void __iomem *scu_base(struct isci_host *isci_host) | ||
523 | { | ||
524 | struct pci_dev *pdev = isci_host->pdev; | ||
525 | int id = isci_host->id; | ||
526 | |||
527 | return pcim_iomap_table(pdev)[SCI_SCU_BAR * 2] + SCI_SCU_BAR_SIZE * id; | ||
528 | } | ||
529 | |||
530 | static void __iomem *smu_base(struct isci_host *isci_host) | ||
531 | { | ||
532 | struct pci_dev *pdev = isci_host->pdev; | ||
533 | int id = isci_host->id; | ||
534 | |||
535 | return pcim_iomap_table(pdev)[SCI_SMU_BAR * 2] + SCI_SMU_BAR_SIZE * id; | ||
536 | } | ||
537 | |||
538 | #define SCI_MAX_TIMER_COUNT 25 | ||
539 | |||
540 | int isci_host_init(struct isci_host *isci_host) | ||
541 | { | ||
542 | int err = 0; | ||
543 | int index = 0; | ||
544 | enum sci_status status; | ||
545 | struct scic_sds_controller *controller; | ||
546 | struct scic_sds_port *scic_port; | ||
547 | struct scic_controller_handler_methods *handlers | ||
548 | = &isci_host->scic_irq_handlers[0]; | ||
549 | union scic_oem_parameters scic_oem_params; | ||
550 | union scic_user_parameters scic_user_params; | ||
551 | const struct firmware *fw = NULL; | ||
552 | struct isci_firmware *isci_fw = NULL; | ||
553 | |||
554 | INIT_LIST_HEAD(&isci_host->timer_list_struct.timers); | ||
555 | isci_timer_list_construct( | ||
556 | &isci_host->timer_list_struct, | ||
557 | SCI_MAX_TIMER_COUNT | ||
558 | ); | ||
559 | |||
560 | controller = scic_controller_alloc(&isci_host->pdev->dev); | ||
561 | |||
562 | if (!controller) { | ||
563 | err = -ENOMEM; | ||
564 | dev_err(&isci_host->pdev->dev, "%s: failed (%d)\n", __func__, err); | ||
565 | goto out; | ||
566 | } | ||
567 | |||
568 | isci_host->core_controller = controller; | ||
569 | spin_lock_init(&isci_host->state_lock); | ||
570 | spin_lock_init(&isci_host->scic_lock); | ||
571 | spin_lock_init(&isci_host->queue_lock); | ||
572 | |||
573 | isci_host_change_state(isci_host, isci_starting); | ||
574 | isci_host->can_queue = ISCI_CAN_QUEUE_VAL; | ||
575 | |||
576 | status = scic_controller_construct(controller, scu_base(isci_host), | ||
577 | smu_base(isci_host)); | ||
578 | |||
579 | if (status != SCI_SUCCESS) { | ||
580 | dev_err(&isci_host->pdev->dev, | ||
581 | "%s: scic_controller_construct failed - status = %x\n", | ||
582 | __func__, | ||
583 | status); | ||
584 | err = -ENODEV; | ||
585 | goto out; | ||
586 | } | ||
587 | |||
588 | isci_host->sas_ha.dev = &isci_host->pdev->dev; | ||
589 | isci_host->sas_ha.lldd_ha = isci_host; | ||
590 | |||
591 | /*----------- SCIC controller Initialization Stuff ------------------ | ||
592 | * set association host adapter struct in core controller. | ||
593 | */ | ||
594 | sci_object_set_association(isci_host->core_controller, | ||
595 | (void *)isci_host | ||
596 | ); | ||
597 | |||
598 | /* grab initial values stored in the controller object for OEM and USER | ||
599 | * parameters */ | ||
600 | scic_oem_parameters_get(controller, &scic_oem_params); | ||
601 | scic_user_parameters_get(controller, &scic_user_params); | ||
602 | |||
603 | isci_fw = devm_kzalloc(&isci_host->pdev->dev, | ||
604 | sizeof(struct isci_firmware), | ||
605 | GFP_KERNEL); | ||
606 | if (!isci_fw) { | ||
607 | dev_warn(&isci_host->pdev->dev, | ||
608 | "allocating firmware struct failed\n"); | ||
609 | dev_warn(&isci_host->pdev->dev, | ||
610 | "Default OEM configuration being used:" | ||
611 | " 4 narrow ports, and default SAS Addresses\n"); | ||
612 | goto set_default_params; | ||
613 | } | ||
614 | |||
615 | status = request_firmware(&fw, ISCI_FW_NAME, &isci_host->pdev->dev); | ||
616 | if (status) { | ||
617 | dev_warn(&isci_host->pdev->dev, | ||
618 | "Loading firmware failed, using default values\n"); | ||
619 | dev_warn(&isci_host->pdev->dev, | ||
620 | "Default OEM configuration being used:" | ||
621 | " 4 narrow ports, and default SAS Addresses\n"); | ||
622 | goto set_default_params; | ||
623 | } | ||
624 | else { | ||
625 | status = isci_verify_firmware(fw, isci_fw); | ||
626 | if (status != SCI_SUCCESS) { | ||
627 | dev_warn(&isci_host->pdev->dev, | ||
628 | "firmware verification failed\n"); | ||
629 | dev_warn(&isci_host->pdev->dev, | ||
630 | "Default OEM configuration being used:" | ||
631 | " 4 narrow ports, and default SAS " | ||
632 | "Addresses\n"); | ||
633 | goto set_default_params; | ||
634 | } | ||
635 | |||
636 | /* grab any OEM and USER parameters specified at module load */ | ||
637 | status = isci_parse_oem_parameters(&scic_oem_params, | ||
638 | isci_host->id, isci_fw); | ||
639 | if (status != SCI_SUCCESS) { | ||
640 | dev_warn(&isci_host->pdev->dev, | ||
641 | "parsing firmware oem parameters failed\n"); | ||
642 | err = -EINVAL; | ||
643 | goto out; | ||
644 | } | ||
645 | |||
646 | status = isci_parse_user_parameters(&scic_user_params, | ||
647 | isci_host->id, isci_fw); | ||
648 | if (status != SCI_SUCCESS) { | ||
649 | dev_warn(&isci_host->pdev->dev, | ||
650 | "%s: isci_parse_user_parameters" | ||
651 | " failed\n", __func__); | ||
652 | err = -EINVAL; | ||
653 | goto out; | ||
654 | } | ||
655 | } | ||
656 | |||
657 | set_default_params: | ||
658 | |||
659 | status = scic_oem_parameters_set(isci_host->core_controller, | ||
660 | &scic_oem_params | ||
661 | ); | ||
662 | |||
663 | if (status != SCI_SUCCESS) { | ||
664 | dev_warn(&isci_host->pdev->dev, | ||
665 | "%s: scic_oem_parameters_set failed\n", | ||
666 | __func__); | ||
667 | err = -ENODEV; | ||
668 | goto out; | ||
669 | } | ||
670 | |||
671 | |||
672 | status = scic_user_parameters_set(isci_host->core_controller, | ||
673 | &scic_user_params | ||
674 | ); | ||
675 | |||
676 | if (status != SCI_SUCCESS) { | ||
677 | dev_warn(&isci_host->pdev->dev, | ||
678 | "%s: scic_user_parameters_set failed\n", | ||
679 | __func__); | ||
680 | err = -ENODEV; | ||
681 | goto out; | ||
682 | } | ||
683 | |||
684 | status = scic_controller_initialize(isci_host->core_controller); | ||
685 | if (status != SCI_SUCCESS) { | ||
686 | dev_warn(&isci_host->pdev->dev, | ||
687 | "%s: scic_controller_initialize failed -" | ||
688 | " status = 0x%x\n", | ||
689 | __func__, status); | ||
690 | err = -ENODEV; | ||
691 | goto out; | ||
692 | } | ||
693 | |||
694 | /* @todo: use both MSI-X interrupts, and don't do indirect | ||
695 | * calls to the handlers just register direct calls | ||
696 | */ | ||
697 | if (isci_host->pdev->msix_enabled) { | ||
698 | status = scic_controller_get_handler_methods( | ||
699 | SCIC_MSIX_INTERRUPT_TYPE, | ||
700 | SCI_MSIX_DOUBLE_VECTOR, | ||
701 | handlers | ||
702 | ); | ||
703 | } else { | ||
704 | status = scic_controller_get_handler_methods( | ||
705 | SCIC_LEGACY_LINE_INTERRUPT_TYPE, | ||
706 | 0, | ||
707 | handlers | ||
708 | ); | ||
709 | } | ||
710 | |||
711 | if (status != SCI_SUCCESS) { | ||
712 | handlers->interrupt_handler = NULL; | ||
713 | handlers->completion_handler = NULL; | ||
714 | dev_err(&isci_host->pdev->dev, | ||
715 | "%s: scic_controller_get_handler_methods failed\n", | ||
716 | __func__); | ||
717 | } | ||
718 | |||
719 | tasklet_init(&isci_host->completion_tasklet, | ||
720 | isci_host_completion_routine, | ||
721 | (unsigned long)isci_host | ||
722 | ); | ||
723 | |||
724 | INIT_LIST_HEAD(&(isci_host->mdl_struct_list)); | ||
725 | |||
726 | INIT_LIST_HEAD(&isci_host->requests_to_complete); | ||
727 | INIT_LIST_HEAD(&isci_host->requests_to_abort); | ||
728 | |||
729 | /* populate mdl with dma memory. scu_mdl_allocate_coherent() */ | ||
730 | err = isci_host_mdl_allocate_coherent(isci_host); | ||
731 | |||
732 | if (err) | ||
733 | goto err_out; | ||
734 | |||
735 | /* | ||
736 | * keep the pool alloc size around, will use it for a bounds checking | ||
737 | * when trying to convert virtual addresses to physical addresses | ||
738 | */ | ||
739 | isci_host->dma_pool_alloc_size = sizeof(struct isci_request) + | ||
740 | scic_io_request_get_object_size(); | ||
741 | isci_host->dma_pool = dmam_pool_create(DRV_NAME, &isci_host->pdev->dev, | ||
742 | isci_host->dma_pool_alloc_size, | ||
743 | SLAB_HWCACHE_ALIGN, 0); | ||
744 | |||
745 | if (!isci_host->dma_pool) { | ||
746 | err = -ENOMEM; | ||
747 | goto req_obj_err_out; | ||
748 | } | ||
749 | |||
750 | for (index = 0; index < SCI_MAX_PORTS; index++) { | ||
751 | isci_port_init(&isci_host->isci_ports[index], | ||
752 | isci_host, index); | ||
753 | } | ||
754 | |||
755 | for (index = 0; index < SCI_MAX_PHYS; index++) | ||
756 | isci_phy_init(&isci_host->phys[index], isci_host, index); | ||
757 | |||
758 | /* Why are we doing this? Is this even necessary? */ | ||
759 | memcpy(&isci_host->sas_addr[0], &isci_host->phys[0].sas_addr[0], | ||
760 | SAS_ADDR_SIZE); | ||
761 | |||
762 | /* Start the ports */ | ||
763 | for (index = 0; index < SCI_MAX_PORTS; index++) { | ||
764 | |||
765 | scic_controller_get_port_handle(controller, index, &scic_port); | ||
766 | scic_port_start(scic_port); | ||
767 | } | ||
768 | |||
769 | goto out; | ||
770 | |||
771 | /* SPB_Debug: destroy request object cache */ | ||
772 | req_obj_err_out: | ||
773 | /* SPB_Debug: destroy remote object cache */ | ||
774 | err_out: | ||
775 | /* SPB_Debug: undo controller init, construct and alloc, remove from parent | ||
776 | * controller list. */ | ||
777 | out: | ||
778 | if (fw) | ||
779 | release_firmware(fw); | ||
780 | return err; | ||
781 | } | ||