162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2021 Broadcom. All Rights Reserved. The term 462306a36Sopenharmony_ci * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include "efct_driver.h" 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include "efct_hw.h" 1062306a36Sopenharmony_ci#include "efct_unsol.h" 1162306a36Sopenharmony_ci#include "efct_scsi.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ciLIST_HEAD(efct_devices); 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_cistatic int logmask; 1662306a36Sopenharmony_cimodule_param(logmask, int, 0444); 1762306a36Sopenharmony_ciMODULE_PARM_DESC(logmask, "logging bitmask (default 0)"); 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic struct libefc_function_template efct_libefc_templ = { 2062306a36Sopenharmony_ci .issue_mbox_rqst = efct_issue_mbox_rqst, 2162306a36Sopenharmony_ci .send_els = efct_els_hw_srrs_send, 2262306a36Sopenharmony_ci .send_bls = efct_efc_bls_send, 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci .new_nport = efct_scsi_tgt_new_nport, 2562306a36Sopenharmony_ci .del_nport = efct_scsi_tgt_del_nport, 2662306a36Sopenharmony_ci .scsi_new_node = efct_scsi_new_initiator, 2762306a36Sopenharmony_ci .scsi_del_node = efct_scsi_del_initiator, 2862306a36Sopenharmony_ci .hw_seq_free = efct_efc_hw_sequence_free, 2962306a36Sopenharmony_ci}; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic int 3262306a36Sopenharmony_ciefct_device_init(void) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci int rc; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci /* driver-wide init for target-server */ 3762306a36Sopenharmony_ci rc = efct_scsi_tgt_driver_init(); 3862306a36Sopenharmony_ci if (rc) { 3962306a36Sopenharmony_ci pr_err("efct_scsi_tgt_init failed rc=%d\n", rc); 4062306a36Sopenharmony_ci return rc; 4162306a36Sopenharmony_ci } 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci rc = efct_scsi_reg_fc_transport(); 4462306a36Sopenharmony_ci if (rc) { 4562306a36Sopenharmony_ci efct_scsi_tgt_driver_exit(); 4662306a36Sopenharmony_ci pr_err("failed to register to FC host\n"); 4762306a36Sopenharmony_ci return rc; 4862306a36Sopenharmony_ci } 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci return 0; 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic void 5462306a36Sopenharmony_ciefct_device_shutdown(void) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci efct_scsi_release_fc_transport(); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci efct_scsi_tgt_driver_exit(); 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic void * 6262306a36Sopenharmony_ciefct_device_alloc(u32 nid) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci struct efct *efct = NULL; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci efct = kzalloc_node(sizeof(*efct), GFP_KERNEL, nid); 6762306a36Sopenharmony_ci if (!efct) 6862306a36Sopenharmony_ci return efct; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci INIT_LIST_HEAD(&efct->list_entry); 7162306a36Sopenharmony_ci list_add_tail(&efct->list_entry, &efct_devices); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci return efct; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic void 7762306a36Sopenharmony_ciefct_teardown_msix(struct efct *efct) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci u32 i; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci for (i = 0; i < efct->n_msix_vec; i++) { 8262306a36Sopenharmony_ci free_irq(pci_irq_vector(efct->pci, i), 8362306a36Sopenharmony_ci &efct->intr_context[i]); 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci pci_free_irq_vectors(efct->pci); 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic int 9062306a36Sopenharmony_ciefct_efclib_config(struct efct *efct, struct libefc_function_template *tt) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci struct efc *efc; 9362306a36Sopenharmony_ci struct sli4 *sli; 9462306a36Sopenharmony_ci int rc = 0; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci efc = kzalloc(sizeof(*efc), GFP_KERNEL); 9762306a36Sopenharmony_ci if (!efc) 9862306a36Sopenharmony_ci return -ENOMEM; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci efct->efcport = efc; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci memcpy(&efc->tt, tt, sizeof(*tt)); 10362306a36Sopenharmony_ci efc->base = efct; 10462306a36Sopenharmony_ci efc->pci = efct->pci; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci efc->def_wwnn = efct_get_wwnn(&efct->hw); 10762306a36Sopenharmony_ci efc->def_wwpn = efct_get_wwpn(&efct->hw); 10862306a36Sopenharmony_ci efc->enable_tgt = 1; 10962306a36Sopenharmony_ci efc->log_level = EFC_LOG_LIB; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci sli = &efct->hw.sli; 11262306a36Sopenharmony_ci efc->max_xfer_size = sli->sge_supported_length * 11362306a36Sopenharmony_ci sli_get_max_sgl(&efct->hw.sli); 11462306a36Sopenharmony_ci efc->sli = sli; 11562306a36Sopenharmony_ci efc->fcfi = efct->hw.fcf_indicator; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci rc = efcport_init(efc); 11862306a36Sopenharmony_ci if (rc) 11962306a36Sopenharmony_ci efc_log_err(efc, "efcport_init failed\n"); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci return rc; 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic int efct_request_firmware_update(struct efct *efct); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic const char* 12762306a36Sopenharmony_ciefct_pci_model(u16 device) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci switch (device) { 13062306a36Sopenharmony_ci case EFCT_DEVICE_LANCER_G6: return "LPE31004"; 13162306a36Sopenharmony_ci case EFCT_DEVICE_LANCER_G7: return "LPE36000"; 13262306a36Sopenharmony_ci default: return "unknown"; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic int 13762306a36Sopenharmony_ciefct_device_attach(struct efct *efct) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci u32 rc = 0, i = 0; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (efct->attached) { 14262306a36Sopenharmony_ci efc_log_err(efct, "Device is already attached\n"); 14362306a36Sopenharmony_ci return -EIO; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci snprintf(efct->name, sizeof(efct->name), "[%s%d] ", "fc", 14762306a36Sopenharmony_ci efct->instance_index); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci efct->logmask = logmask; 15062306a36Sopenharmony_ci efct->filter_def = EFCT_DEFAULT_FILTER; 15162306a36Sopenharmony_ci efct->max_isr_time_msec = EFCT_OS_MAX_ISR_TIME_MSEC; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci efct->model = efct_pci_model(efct->pci->device); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci efct->efct_req_fw_upgrade = true; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci /* Allocate transport object and bring online */ 15862306a36Sopenharmony_ci efct->xport = efct_xport_alloc(efct); 15962306a36Sopenharmony_ci if (!efct->xport) { 16062306a36Sopenharmony_ci efc_log_err(efct, "failed to allocate transport object\n"); 16162306a36Sopenharmony_ci rc = -ENOMEM; 16262306a36Sopenharmony_ci goto out; 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci rc = efct_xport_attach(efct->xport); 16662306a36Sopenharmony_ci if (rc) { 16762306a36Sopenharmony_ci efc_log_err(efct, "failed to attach transport object\n"); 16862306a36Sopenharmony_ci goto xport_out; 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci rc = efct_xport_initialize(efct->xport); 17262306a36Sopenharmony_ci if (rc) { 17362306a36Sopenharmony_ci efc_log_err(efct, "failed to initialize transport object\n"); 17462306a36Sopenharmony_ci goto xport_out; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci rc = efct_efclib_config(efct, &efct_libefc_templ); 17862306a36Sopenharmony_ci if (rc) { 17962306a36Sopenharmony_ci efc_log_err(efct, "failed to init efclib\n"); 18062306a36Sopenharmony_ci goto efclib_out; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci for (i = 0; i < efct->n_msix_vec; i++) { 18462306a36Sopenharmony_ci efc_log_debug(efct, "irq %d enabled\n", i); 18562306a36Sopenharmony_ci enable_irq(pci_irq_vector(efct->pci, i)); 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci efct->attached = true; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if (efct->efct_req_fw_upgrade) 19162306a36Sopenharmony_ci efct_request_firmware_update(efct); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci return rc; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ciefclib_out: 19662306a36Sopenharmony_ci efct_xport_detach(efct->xport); 19762306a36Sopenharmony_cixport_out: 19862306a36Sopenharmony_ci efct_xport_free(efct->xport); 19962306a36Sopenharmony_ci efct->xport = NULL; 20062306a36Sopenharmony_ciout: 20162306a36Sopenharmony_ci return rc; 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic int 20562306a36Sopenharmony_ciefct_device_detach(struct efct *efct) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci int i; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (!efct || !efct->attached) { 21062306a36Sopenharmony_ci pr_err("Device is not attached\n"); 21162306a36Sopenharmony_ci return -EIO; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (efct_xport_control(efct->xport, EFCT_XPORT_SHUTDOWN)) 21562306a36Sopenharmony_ci efc_log_err(efct, "Transport Shutdown timed out\n"); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci for (i = 0; i < efct->n_msix_vec; i++) 21862306a36Sopenharmony_ci disable_irq(pci_irq_vector(efct->pci, i)); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci efct_xport_detach(efct->xport); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci efct_xport_free(efct->xport); 22362306a36Sopenharmony_ci efct->xport = NULL; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci efcport_destroy(efct->efcport); 22662306a36Sopenharmony_ci kfree(efct->efcport); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci efct->attached = false; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci return 0; 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistatic void 23462306a36Sopenharmony_ciefct_fw_write_cb(int status, u32 actual_write_length, 23562306a36Sopenharmony_ci u32 change_status, void *arg) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci struct efct_fw_write_result *result = arg; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci result->status = status; 24062306a36Sopenharmony_ci result->actual_xfer = actual_write_length; 24162306a36Sopenharmony_ci result->change_status = change_status; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci complete(&result->done); 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic int 24762306a36Sopenharmony_ciefct_firmware_write(struct efct *efct, const u8 *buf, size_t buf_len, 24862306a36Sopenharmony_ci u8 *change_status) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci int rc = 0; 25162306a36Sopenharmony_ci u32 bytes_left; 25262306a36Sopenharmony_ci u32 xfer_size; 25362306a36Sopenharmony_ci u32 offset; 25462306a36Sopenharmony_ci struct efc_dma dma; 25562306a36Sopenharmony_ci int last = 0; 25662306a36Sopenharmony_ci struct efct_fw_write_result result; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci init_completion(&result.done); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci bytes_left = buf_len; 26162306a36Sopenharmony_ci offset = 0; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci dma.size = FW_WRITE_BUFSIZE; 26462306a36Sopenharmony_ci dma.virt = dma_alloc_coherent(&efct->pci->dev, 26562306a36Sopenharmony_ci dma.size, &dma.phys, GFP_KERNEL); 26662306a36Sopenharmony_ci if (!dma.virt) 26762306a36Sopenharmony_ci return -ENOMEM; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci while (bytes_left > 0) { 27062306a36Sopenharmony_ci if (bytes_left > FW_WRITE_BUFSIZE) 27162306a36Sopenharmony_ci xfer_size = FW_WRITE_BUFSIZE; 27262306a36Sopenharmony_ci else 27362306a36Sopenharmony_ci xfer_size = bytes_left; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci memcpy(dma.virt, buf + offset, xfer_size); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci if (bytes_left == xfer_size) 27862306a36Sopenharmony_ci last = 1; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci efct_hw_firmware_write(&efct->hw, &dma, xfer_size, offset, 28162306a36Sopenharmony_ci last, efct_fw_write_cb, &result); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci if (wait_for_completion_interruptible(&result.done) != 0) { 28462306a36Sopenharmony_ci rc = -ENXIO; 28562306a36Sopenharmony_ci break; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci if (result.actual_xfer == 0 || result.status != 0) { 28962306a36Sopenharmony_ci rc = -EFAULT; 29062306a36Sopenharmony_ci break; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (last) 29462306a36Sopenharmony_ci *change_status = result.change_status; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci bytes_left -= result.actual_xfer; 29762306a36Sopenharmony_ci offset += result.actual_xfer; 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci dma_free_coherent(&efct->pci->dev, dma.size, dma.virt, dma.phys); 30162306a36Sopenharmony_ci return rc; 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic int 30562306a36Sopenharmony_ciefct_fw_reset(struct efct *efct) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci /* 30862306a36Sopenharmony_ci * Firmware reset to activate the new firmware. 30962306a36Sopenharmony_ci * Function 0 will update and load the new firmware 31062306a36Sopenharmony_ci * during attach. 31162306a36Sopenharmony_ci */ 31262306a36Sopenharmony_ci if (timer_pending(&efct->xport->stats_timer)) 31362306a36Sopenharmony_ci del_timer(&efct->xport->stats_timer); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci if (efct_hw_reset(&efct->hw, EFCT_HW_RESET_FIRMWARE)) { 31662306a36Sopenharmony_ci efc_log_info(efct, "failed to reset firmware\n"); 31762306a36Sopenharmony_ci return -EIO; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci efc_log_info(efct, "successfully reset firmware.Now resetting port\n"); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci efct_device_detach(efct); 32362306a36Sopenharmony_ci return efct_device_attach(efct); 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic int 32762306a36Sopenharmony_ciefct_request_firmware_update(struct efct *efct) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci int rc = 0; 33062306a36Sopenharmony_ci u8 file_name[256], fw_change_status = 0; 33162306a36Sopenharmony_ci const struct firmware *fw; 33262306a36Sopenharmony_ci struct efct_hw_grp_hdr *fw_image; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci snprintf(file_name, 256, "%s.grp", efct->model); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci rc = request_firmware(&fw, file_name, &efct->pci->dev); 33762306a36Sopenharmony_ci if (rc) { 33862306a36Sopenharmony_ci efc_log_debug(efct, "Firmware file(%s) not found.\n", file_name); 33962306a36Sopenharmony_ci return rc; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci fw_image = (struct efct_hw_grp_hdr *)fw->data; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci if (!strncmp(efct->hw.sli.fw_name[0], fw_image->revision, 34562306a36Sopenharmony_ci strnlen(fw_image->revision, 16))) { 34662306a36Sopenharmony_ci efc_log_debug(efct, 34762306a36Sopenharmony_ci "Skip update. Firmware is already up to date.\n"); 34862306a36Sopenharmony_ci goto exit; 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci efc_log_info(efct, "Firmware update is initiated. %s -> %s\n", 35262306a36Sopenharmony_ci efct->hw.sli.fw_name[0], fw_image->revision); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci rc = efct_firmware_write(efct, fw->data, fw->size, &fw_change_status); 35562306a36Sopenharmony_ci if (rc) { 35662306a36Sopenharmony_ci efc_log_err(efct, "Firmware update failed. rc = %d\n", rc); 35762306a36Sopenharmony_ci goto exit; 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci efc_log_info(efct, "Firmware updated successfully\n"); 36162306a36Sopenharmony_ci switch (fw_change_status) { 36262306a36Sopenharmony_ci case 0x00: 36362306a36Sopenharmony_ci efc_log_info(efct, "New firmware is active.\n"); 36462306a36Sopenharmony_ci break; 36562306a36Sopenharmony_ci case 0x01: 36662306a36Sopenharmony_ci efc_log_info(efct, 36762306a36Sopenharmony_ci "System reboot needed to activate the new firmware\n"); 36862306a36Sopenharmony_ci break; 36962306a36Sopenharmony_ci case 0x02: 37062306a36Sopenharmony_ci case 0x03: 37162306a36Sopenharmony_ci efc_log_info(efct, 37262306a36Sopenharmony_ci "firmware reset to activate the new firmware\n"); 37362306a36Sopenharmony_ci efct_fw_reset(efct); 37462306a36Sopenharmony_ci break; 37562306a36Sopenharmony_ci default: 37662306a36Sopenharmony_ci efc_log_info(efct, "Unexpected value change_status:%d\n", 37762306a36Sopenharmony_ci fw_change_status); 37862306a36Sopenharmony_ci break; 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ciexit: 38262306a36Sopenharmony_ci release_firmware(fw); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci return rc; 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cistatic void 38862306a36Sopenharmony_ciefct_device_free(struct efct *efct) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci if (efct) { 39162306a36Sopenharmony_ci list_del(&efct->list_entry); 39262306a36Sopenharmony_ci kfree(efct); 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_cistatic int 39762306a36Sopenharmony_ciefct_device_interrupts_required(struct efct *efct) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci int rc; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci rc = efct_hw_setup(&efct->hw, efct, efct->pci); 40262306a36Sopenharmony_ci if (rc < 0) 40362306a36Sopenharmony_ci return rc; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci return efct->hw.config.n_eq; 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cistatic irqreturn_t 40962306a36Sopenharmony_ciefct_intr_thread(int irq, void *handle) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci struct efct_intr_context *intr_ctx = handle; 41262306a36Sopenharmony_ci struct efct *efct = intr_ctx->efct; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci efct_hw_process(&efct->hw, intr_ctx->index, efct->max_isr_time_msec); 41562306a36Sopenharmony_ci return IRQ_HANDLED; 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cistatic irqreturn_t 41962306a36Sopenharmony_ciefct_intr_msix(int irq, void *handle) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci return IRQ_WAKE_THREAD; 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic int 42562306a36Sopenharmony_ciefct_setup_msix(struct efct *efct, u32 num_intrs) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci int rc = 0, i; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci if (!pci_find_capability(efct->pci, PCI_CAP_ID_MSIX)) { 43062306a36Sopenharmony_ci dev_err(&efct->pci->dev, 43162306a36Sopenharmony_ci "%s : MSI-X not available\n", __func__); 43262306a36Sopenharmony_ci return -EIO; 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci efct->n_msix_vec = num_intrs; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci rc = pci_alloc_irq_vectors(efct->pci, num_intrs, num_intrs, 43862306a36Sopenharmony_ci PCI_IRQ_MSIX | PCI_IRQ_AFFINITY); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci if (rc < 0) { 44162306a36Sopenharmony_ci dev_err(&efct->pci->dev, "Failed to alloc irq : %d\n", rc); 44262306a36Sopenharmony_ci return rc; 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci for (i = 0; i < num_intrs; i++) { 44662306a36Sopenharmony_ci struct efct_intr_context *intr_ctx = NULL; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci intr_ctx = &efct->intr_context[i]; 44962306a36Sopenharmony_ci intr_ctx->efct = efct; 45062306a36Sopenharmony_ci intr_ctx->index = i; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci rc = request_threaded_irq(pci_irq_vector(efct->pci, i), 45362306a36Sopenharmony_ci efct_intr_msix, efct_intr_thread, 0, 45462306a36Sopenharmony_ci EFCT_DRIVER_NAME, intr_ctx); 45562306a36Sopenharmony_ci if (rc) { 45662306a36Sopenharmony_ci dev_err(&efct->pci->dev, 45762306a36Sopenharmony_ci "Failed to register %d vector: %d\n", i, rc); 45862306a36Sopenharmony_ci goto out; 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci return rc; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ciout: 46562306a36Sopenharmony_ci while (--i >= 0) 46662306a36Sopenharmony_ci free_irq(pci_irq_vector(efct->pci, i), 46762306a36Sopenharmony_ci &efct->intr_context[i]); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci pci_free_irq_vectors(efct->pci); 47062306a36Sopenharmony_ci return rc; 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_cistatic struct pci_device_id efct_pci_table[] = { 47462306a36Sopenharmony_ci {PCI_DEVICE(EFCT_VENDOR_ID, EFCT_DEVICE_LANCER_G6), 0}, 47562306a36Sopenharmony_ci {PCI_DEVICE(EFCT_VENDOR_ID, EFCT_DEVICE_LANCER_G7), 0}, 47662306a36Sopenharmony_ci {} /* terminate list */ 47762306a36Sopenharmony_ci}; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistatic int 48062306a36Sopenharmony_ciefct_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci struct efct *efct = NULL; 48362306a36Sopenharmony_ci int rc; 48462306a36Sopenharmony_ci u32 i, r; 48562306a36Sopenharmony_ci int num_interrupts = 0; 48662306a36Sopenharmony_ci int nid; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci dev_info(&pdev->dev, "%s\n", EFCT_DRIVER_NAME); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci rc = pci_enable_device_mem(pdev); 49162306a36Sopenharmony_ci if (rc) 49262306a36Sopenharmony_ci return rc; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci pci_set_master(pdev); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci rc = pci_set_mwi(pdev); 49762306a36Sopenharmony_ci if (rc) { 49862306a36Sopenharmony_ci dev_info(&pdev->dev, "pci_set_mwi returned %d\n", rc); 49962306a36Sopenharmony_ci goto mwi_out; 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci rc = pci_request_regions(pdev, EFCT_DRIVER_NAME); 50362306a36Sopenharmony_ci if (rc) { 50462306a36Sopenharmony_ci dev_err(&pdev->dev, "pci_request_regions failed %d\n", rc); 50562306a36Sopenharmony_ci goto req_regions_out; 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci /* Fetch the Numa node id for this device */ 50962306a36Sopenharmony_ci nid = dev_to_node(&pdev->dev); 51062306a36Sopenharmony_ci if (nid < 0) { 51162306a36Sopenharmony_ci dev_err(&pdev->dev, "Warning Numa node ID is %d\n", nid); 51262306a36Sopenharmony_ci nid = 0; 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci /* Allocate efct */ 51662306a36Sopenharmony_ci efct = efct_device_alloc(nid); 51762306a36Sopenharmony_ci if (!efct) { 51862306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to allocate efct\n"); 51962306a36Sopenharmony_ci rc = -ENOMEM; 52062306a36Sopenharmony_ci goto alloc_out; 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci efct->pci = pdev; 52462306a36Sopenharmony_ci efct->numa_node = nid; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci /* Map all memory BARs */ 52762306a36Sopenharmony_ci for (i = 0, r = 0; i < EFCT_PCI_MAX_REGS; i++) { 52862306a36Sopenharmony_ci if (pci_resource_flags(pdev, i) & IORESOURCE_MEM) { 52962306a36Sopenharmony_ci efct->reg[r] = ioremap(pci_resource_start(pdev, i), 53062306a36Sopenharmony_ci pci_resource_len(pdev, i)); 53162306a36Sopenharmony_ci r++; 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci /* 53562306a36Sopenharmony_ci * If the 64-bit attribute is set, both this BAR and the 53662306a36Sopenharmony_ci * next form the complete address. Skip processing the 53762306a36Sopenharmony_ci * next BAR. 53862306a36Sopenharmony_ci */ 53962306a36Sopenharmony_ci if (pci_resource_flags(pdev, i) & IORESOURCE_MEM_64) 54062306a36Sopenharmony_ci i++; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci pci_set_drvdata(pdev, efct); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); 54662306a36Sopenharmony_ci if (rc) { 54762306a36Sopenharmony_ci dev_err(&pdev->dev, "setting DMA_BIT_MASK failed\n"); 54862306a36Sopenharmony_ci goto dma_mask_out; 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci num_interrupts = efct_device_interrupts_required(efct); 55262306a36Sopenharmony_ci if (num_interrupts < 0) { 55362306a36Sopenharmony_ci efc_log_err(efct, "efct_device_interrupts_required failed\n"); 55462306a36Sopenharmony_ci rc = -1; 55562306a36Sopenharmony_ci goto dma_mask_out; 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci /* 55962306a36Sopenharmony_ci * Initialize MSIX interrupts, note, 56062306a36Sopenharmony_ci * efct_setup_msix() enables the interrupt 56162306a36Sopenharmony_ci */ 56262306a36Sopenharmony_ci rc = efct_setup_msix(efct, num_interrupts); 56362306a36Sopenharmony_ci if (rc) { 56462306a36Sopenharmony_ci dev_err(&pdev->dev, "Can't setup msix\n"); 56562306a36Sopenharmony_ci goto dma_mask_out; 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci /* Disable interrupt for now */ 56862306a36Sopenharmony_ci for (i = 0; i < efct->n_msix_vec; i++) { 56962306a36Sopenharmony_ci efc_log_debug(efct, "irq %d disabled\n", i); 57062306a36Sopenharmony_ci disable_irq(pci_irq_vector(efct->pci, i)); 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci rc = efct_device_attach(efct); 57462306a36Sopenharmony_ci if (rc) 57562306a36Sopenharmony_ci goto attach_out; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci return 0; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ciattach_out: 58062306a36Sopenharmony_ci efct_teardown_msix(efct); 58162306a36Sopenharmony_cidma_mask_out: 58262306a36Sopenharmony_ci pci_set_drvdata(pdev, NULL); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci for (i = 0; i < EFCT_PCI_MAX_REGS; i++) { 58562306a36Sopenharmony_ci if (efct->reg[i]) 58662306a36Sopenharmony_ci iounmap(efct->reg[i]); 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci efct_device_free(efct); 58962306a36Sopenharmony_cialloc_out: 59062306a36Sopenharmony_ci pci_release_regions(pdev); 59162306a36Sopenharmony_cireq_regions_out: 59262306a36Sopenharmony_ci pci_clear_mwi(pdev); 59362306a36Sopenharmony_cimwi_out: 59462306a36Sopenharmony_ci pci_disable_device(pdev); 59562306a36Sopenharmony_ci return rc; 59662306a36Sopenharmony_ci} 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_cistatic void 59962306a36Sopenharmony_ciefct_pci_remove(struct pci_dev *pdev) 60062306a36Sopenharmony_ci{ 60162306a36Sopenharmony_ci struct efct *efct = pci_get_drvdata(pdev); 60262306a36Sopenharmony_ci u32 i; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci if (!efct) 60562306a36Sopenharmony_ci return; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci efct_device_detach(efct); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci efct_teardown_msix(efct); 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci for (i = 0; i < EFCT_PCI_MAX_REGS; i++) { 61262306a36Sopenharmony_ci if (efct->reg[i]) 61362306a36Sopenharmony_ci iounmap(efct->reg[i]); 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci pci_set_drvdata(pdev, NULL); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci efct_device_free(efct); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci pci_release_regions(pdev); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci pci_disable_device(pdev); 62362306a36Sopenharmony_ci} 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_cistatic void 62662306a36Sopenharmony_ciefct_device_prep_for_reset(struct efct *efct, struct pci_dev *pdev) 62762306a36Sopenharmony_ci{ 62862306a36Sopenharmony_ci if (efct) { 62962306a36Sopenharmony_ci efc_log_debug(efct, 63062306a36Sopenharmony_ci "PCI channel disable preparing for reset\n"); 63162306a36Sopenharmony_ci efct_device_detach(efct); 63262306a36Sopenharmony_ci /* Disable interrupt and pci device */ 63362306a36Sopenharmony_ci efct_teardown_msix(efct); 63462306a36Sopenharmony_ci } 63562306a36Sopenharmony_ci pci_disable_device(pdev); 63662306a36Sopenharmony_ci} 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_cistatic void 63962306a36Sopenharmony_ciefct_device_prep_for_recover(struct efct *efct) 64062306a36Sopenharmony_ci{ 64162306a36Sopenharmony_ci if (efct) { 64262306a36Sopenharmony_ci efc_log_debug(efct, "PCI channel preparing for recovery\n"); 64362306a36Sopenharmony_ci efct_hw_io_abort_all(&efct->hw); 64462306a36Sopenharmony_ci } 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci/** 64862306a36Sopenharmony_ci * efct_pci_io_error_detected - method for handling PCI I/O error 64962306a36Sopenharmony_ci * @pdev: pointer to PCI device. 65062306a36Sopenharmony_ci * @state: the current PCI connection state. 65162306a36Sopenharmony_ci * 65262306a36Sopenharmony_ci * This routine is registered to the PCI subsystem for error handling. This 65362306a36Sopenharmony_ci * function is called by the PCI subsystem after a PCI bus error affecting 65462306a36Sopenharmony_ci * this device has been detected. When this routine is invoked, it dispatches 65562306a36Sopenharmony_ci * device error detected handling routine, which will perform the proper 65662306a36Sopenharmony_ci * error detected operation. 65762306a36Sopenharmony_ci * 65862306a36Sopenharmony_ci * Return codes 65962306a36Sopenharmony_ci * PCI_ERS_RESULT_NEED_RESET - need to reset before recovery 66062306a36Sopenharmony_ci * PCI_ERS_RESULT_DISCONNECT - device could not be recovered 66162306a36Sopenharmony_ci */ 66262306a36Sopenharmony_cistatic pci_ers_result_t 66362306a36Sopenharmony_ciefct_pci_io_error_detected(struct pci_dev *pdev, pci_channel_state_t state) 66462306a36Sopenharmony_ci{ 66562306a36Sopenharmony_ci struct efct *efct = pci_get_drvdata(pdev); 66662306a36Sopenharmony_ci pci_ers_result_t rc; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci switch (state) { 66962306a36Sopenharmony_ci case pci_channel_io_normal: 67062306a36Sopenharmony_ci efct_device_prep_for_recover(efct); 67162306a36Sopenharmony_ci rc = PCI_ERS_RESULT_CAN_RECOVER; 67262306a36Sopenharmony_ci break; 67362306a36Sopenharmony_ci case pci_channel_io_frozen: 67462306a36Sopenharmony_ci efct_device_prep_for_reset(efct, pdev); 67562306a36Sopenharmony_ci rc = PCI_ERS_RESULT_NEED_RESET; 67662306a36Sopenharmony_ci break; 67762306a36Sopenharmony_ci case pci_channel_io_perm_failure: 67862306a36Sopenharmony_ci efct_device_detach(efct); 67962306a36Sopenharmony_ci rc = PCI_ERS_RESULT_DISCONNECT; 68062306a36Sopenharmony_ci break; 68162306a36Sopenharmony_ci default: 68262306a36Sopenharmony_ci efc_log_debug(efct, "Unknown PCI error state:0x%x\n", state); 68362306a36Sopenharmony_ci efct_device_prep_for_reset(efct, pdev); 68462306a36Sopenharmony_ci rc = PCI_ERS_RESULT_NEED_RESET; 68562306a36Sopenharmony_ci break; 68662306a36Sopenharmony_ci } 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci return rc; 68962306a36Sopenharmony_ci} 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_cistatic pci_ers_result_t 69262306a36Sopenharmony_ciefct_pci_io_slot_reset(struct pci_dev *pdev) 69362306a36Sopenharmony_ci{ 69462306a36Sopenharmony_ci int rc; 69562306a36Sopenharmony_ci struct efct *efct = pci_get_drvdata(pdev); 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci rc = pci_enable_device_mem(pdev); 69862306a36Sopenharmony_ci if (rc) { 69962306a36Sopenharmony_ci efc_log_err(efct, "failed to enable PCI device after reset\n"); 70062306a36Sopenharmony_ci return PCI_ERS_RESULT_DISCONNECT; 70162306a36Sopenharmony_ci } 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci /* 70462306a36Sopenharmony_ci * As the new kernel behavior of pci_restore_state() API call clears 70562306a36Sopenharmony_ci * device saved_state flag, need to save the restored state again. 70662306a36Sopenharmony_ci */ 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci pci_save_state(pdev); 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci pci_set_master(pdev); 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci rc = efct_setup_msix(efct, efct->n_msix_vec); 71362306a36Sopenharmony_ci if (rc) 71462306a36Sopenharmony_ci efc_log_err(efct, "rc %d returned, IRQ allocation failed\n", 71562306a36Sopenharmony_ci rc); 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci /* Perform device reset */ 71862306a36Sopenharmony_ci efct_device_detach(efct); 71962306a36Sopenharmony_ci /* Bring device to online*/ 72062306a36Sopenharmony_ci efct_device_attach(efct); 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci return PCI_ERS_RESULT_RECOVERED; 72362306a36Sopenharmony_ci} 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_cistatic void 72662306a36Sopenharmony_ciefct_pci_io_resume(struct pci_dev *pdev) 72762306a36Sopenharmony_ci{ 72862306a36Sopenharmony_ci struct efct *efct = pci_get_drvdata(pdev); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci /* Perform device reset */ 73162306a36Sopenharmony_ci efct_device_detach(efct); 73262306a36Sopenharmony_ci /* Bring device to online*/ 73362306a36Sopenharmony_ci efct_device_attach(efct); 73462306a36Sopenharmony_ci} 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, efct_pci_table); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_cistatic struct pci_error_handlers efct_pci_err_handler = { 73962306a36Sopenharmony_ci .error_detected = efct_pci_io_error_detected, 74062306a36Sopenharmony_ci .slot_reset = efct_pci_io_slot_reset, 74162306a36Sopenharmony_ci .resume = efct_pci_io_resume, 74262306a36Sopenharmony_ci}; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_cistatic struct pci_driver efct_pci_driver = { 74562306a36Sopenharmony_ci .name = EFCT_DRIVER_NAME, 74662306a36Sopenharmony_ci .id_table = efct_pci_table, 74762306a36Sopenharmony_ci .probe = efct_pci_probe, 74862306a36Sopenharmony_ci .remove = efct_pci_remove, 74962306a36Sopenharmony_ci .err_handler = &efct_pci_err_handler, 75062306a36Sopenharmony_ci}; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_cistatic 75362306a36Sopenharmony_ciint __init efct_init(void) 75462306a36Sopenharmony_ci{ 75562306a36Sopenharmony_ci int rc; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci rc = efct_device_init(); 75862306a36Sopenharmony_ci if (rc) { 75962306a36Sopenharmony_ci pr_err("efct_device_init failed rc=%d\n", rc); 76062306a36Sopenharmony_ci return rc; 76162306a36Sopenharmony_ci } 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci rc = pci_register_driver(&efct_pci_driver); 76462306a36Sopenharmony_ci if (rc) { 76562306a36Sopenharmony_ci pr_err("pci_register_driver failed rc=%d\n", rc); 76662306a36Sopenharmony_ci efct_device_shutdown(); 76762306a36Sopenharmony_ci } 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci return rc; 77062306a36Sopenharmony_ci} 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_cistatic void __exit efct_exit(void) 77362306a36Sopenharmony_ci{ 77462306a36Sopenharmony_ci pci_unregister_driver(&efct_pci_driver); 77562306a36Sopenharmony_ci efct_device_shutdown(); 77662306a36Sopenharmony_ci} 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_cimodule_init(efct_init); 77962306a36Sopenharmony_cimodule_exit(efct_exit); 78062306a36Sopenharmony_ciMODULE_VERSION(EFCT_DRIVER_VERSION); 78162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 78262306a36Sopenharmony_ciMODULE_AUTHOR("Broadcom"); 783