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