162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * linux/drivers/message/fusion/mptfc.c 362306a36Sopenharmony_ci * For use with LSI PCI chip/adapter(s) 462306a36Sopenharmony_ci * running LSI Fusion MPT (Message Passing Technology) firmware. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (c) 1999-2008 LSI Corporation 762306a36Sopenharmony_ci * (mailto:DL-MPTFusionLinux@lsi.com) 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ 1162306a36Sopenharmony_ci/* 1262306a36Sopenharmony_ci This program is free software; you can redistribute it and/or modify 1362306a36Sopenharmony_ci it under the terms of the GNU General Public License as published by 1462306a36Sopenharmony_ci the Free Software Foundation; version 2 of the License. 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci This program is distributed in the hope that it will be useful, 1762306a36Sopenharmony_ci but WITHOUT ANY WARRANTY; without even the implied warranty of 1862306a36Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1962306a36Sopenharmony_ci GNU General Public License for more details. 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci NO WARRANTY 2262306a36Sopenharmony_ci THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR 2362306a36Sopenharmony_ci CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT 2462306a36Sopenharmony_ci LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, 2562306a36Sopenharmony_ci MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is 2662306a36Sopenharmony_ci solely responsible for determining the appropriateness of using and 2762306a36Sopenharmony_ci distributing the Program and assumes all risks associated with its 2862306a36Sopenharmony_ci exercise of rights under this Agreement, including but not limited to 2962306a36Sopenharmony_ci the risks and costs of program errors, damage to or loss of data, 3062306a36Sopenharmony_ci programs or equipment, and unavailability or interruption of operations. 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci DISCLAIMER OF LIABILITY 3362306a36Sopenharmony_ci NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY 3462306a36Sopenharmony_ci DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 3562306a36Sopenharmony_ci DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND 3662306a36Sopenharmony_ci ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 3762306a36Sopenharmony_ci TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 3862306a36Sopenharmony_ci USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED 3962306a36Sopenharmony_ci HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci You should have received a copy of the GNU General Public License 4262306a36Sopenharmony_ci along with this program; if not, write to the Free Software 4362306a36Sopenharmony_ci Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 4462306a36Sopenharmony_ci*/ 4562306a36Sopenharmony_ci/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ 4662306a36Sopenharmony_ci#include <linux/module.h> 4762306a36Sopenharmony_ci#include <linux/kernel.h> 4862306a36Sopenharmony_ci#include <linux/init.h> 4962306a36Sopenharmony_ci#include <linux/errno.h> 5062306a36Sopenharmony_ci#include <linux/kdev_t.h> 5162306a36Sopenharmony_ci#include <linux/blkdev.h> 5262306a36Sopenharmony_ci#include <linux/delay.h> /* for mdelay */ 5362306a36Sopenharmony_ci#include <linux/interrupt.h> 5462306a36Sopenharmony_ci#include <linux/reboot.h> /* notifier code */ 5562306a36Sopenharmony_ci#include <linux/workqueue.h> 5662306a36Sopenharmony_ci#include <linux/sort.h> 5762306a36Sopenharmony_ci#include <linux/slab.h> 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#include <scsi/scsi.h> 6062306a36Sopenharmony_ci#include <scsi/scsi_cmnd.h> 6162306a36Sopenharmony_ci#include <scsi/scsi_device.h> 6262306a36Sopenharmony_ci#include <scsi/scsi_host.h> 6362306a36Sopenharmony_ci#include <scsi/scsi_tcq.h> 6462306a36Sopenharmony_ci#include <scsi/scsi_transport_fc.h> 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#include "mptbase.h" 6762306a36Sopenharmony_ci#include "mptscsih.h" 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ 7062306a36Sopenharmony_ci#define my_NAME "Fusion MPT FC Host driver" 7162306a36Sopenharmony_ci#define my_VERSION MPT_LINUX_VERSION_COMMON 7262306a36Sopenharmony_ci#define MYNAM "mptfc" 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ciMODULE_AUTHOR(MODULEAUTHOR); 7562306a36Sopenharmony_ciMODULE_DESCRIPTION(my_NAME); 7662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 7762306a36Sopenharmony_ciMODULE_VERSION(my_VERSION); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* Command line args */ 8062306a36Sopenharmony_ci#define MPTFC_DEV_LOSS_TMO (60) 8162306a36Sopenharmony_cistatic int mptfc_dev_loss_tmo = MPTFC_DEV_LOSS_TMO; /* reasonable default */ 8262306a36Sopenharmony_cimodule_param(mptfc_dev_loss_tmo, int, 0); 8362306a36Sopenharmony_ciMODULE_PARM_DESC(mptfc_dev_loss_tmo, " Initial time the driver programs the " 8462306a36Sopenharmony_ci " transport to wait for an rport to " 8562306a36Sopenharmony_ci " return following a device loss event." 8662306a36Sopenharmony_ci " Default=60."); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci/* scsi-mid layer global parmeter is max_report_luns, which is 511 */ 8962306a36Sopenharmony_ci#define MPTFC_MAX_LUN (16895) 9062306a36Sopenharmony_cistatic int max_lun = MPTFC_MAX_LUN; 9162306a36Sopenharmony_cimodule_param(max_lun, int, 0); 9262306a36Sopenharmony_ciMODULE_PARM_DESC(max_lun, " max lun, default=16895 "); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic u8 mptfcDoneCtx = MPT_MAX_PROTOCOL_DRIVERS; 9562306a36Sopenharmony_cistatic u8 mptfcTaskCtx = MPT_MAX_PROTOCOL_DRIVERS; 9662306a36Sopenharmony_cistatic u8 mptfcInternalCtx = MPT_MAX_PROTOCOL_DRIVERS; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic int mptfc_target_alloc(struct scsi_target *starget); 9962306a36Sopenharmony_cistatic int mptfc_slave_alloc(struct scsi_device *sdev); 10062306a36Sopenharmony_cistatic int mptfc_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *SCpnt); 10162306a36Sopenharmony_cistatic void mptfc_target_destroy(struct scsi_target *starget); 10262306a36Sopenharmony_cistatic void mptfc_set_rport_loss_tmo(struct fc_rport *rport, uint32_t timeout); 10362306a36Sopenharmony_cistatic void mptfc_remove(struct pci_dev *pdev); 10462306a36Sopenharmony_cistatic int mptfc_abort(struct scsi_cmnd *SCpnt); 10562306a36Sopenharmony_cistatic int mptfc_dev_reset(struct scsi_cmnd *SCpnt); 10662306a36Sopenharmony_cistatic int mptfc_bus_reset(struct scsi_cmnd *SCpnt); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic const struct scsi_host_template mptfc_driver_template = { 10962306a36Sopenharmony_ci .module = THIS_MODULE, 11062306a36Sopenharmony_ci .proc_name = "mptfc", 11162306a36Sopenharmony_ci .show_info = mptscsih_show_info, 11262306a36Sopenharmony_ci .name = "MPT FC Host", 11362306a36Sopenharmony_ci .info = mptscsih_info, 11462306a36Sopenharmony_ci .queuecommand = mptfc_qcmd, 11562306a36Sopenharmony_ci .target_alloc = mptfc_target_alloc, 11662306a36Sopenharmony_ci .slave_alloc = mptfc_slave_alloc, 11762306a36Sopenharmony_ci .slave_configure = mptscsih_slave_configure, 11862306a36Sopenharmony_ci .target_destroy = mptfc_target_destroy, 11962306a36Sopenharmony_ci .slave_destroy = mptscsih_slave_destroy, 12062306a36Sopenharmony_ci .change_queue_depth = mptscsih_change_queue_depth, 12162306a36Sopenharmony_ci .eh_timed_out = fc_eh_timed_out, 12262306a36Sopenharmony_ci .eh_abort_handler = mptfc_abort, 12362306a36Sopenharmony_ci .eh_device_reset_handler = mptfc_dev_reset, 12462306a36Sopenharmony_ci .eh_bus_reset_handler = mptfc_bus_reset, 12562306a36Sopenharmony_ci .eh_host_reset_handler = mptscsih_host_reset, 12662306a36Sopenharmony_ci .bios_param = mptscsih_bios_param, 12762306a36Sopenharmony_ci .can_queue = MPT_FC_CAN_QUEUE, 12862306a36Sopenharmony_ci .this_id = -1, 12962306a36Sopenharmony_ci .sg_tablesize = MPT_SCSI_SG_DEPTH, 13062306a36Sopenharmony_ci .max_sectors = 8192, 13162306a36Sopenharmony_ci .cmd_per_lun = 7, 13262306a36Sopenharmony_ci .shost_groups = mptscsih_host_attr_groups, 13362306a36Sopenharmony_ci}; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/**************************************************************************** 13662306a36Sopenharmony_ci * Supported hardware 13762306a36Sopenharmony_ci */ 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic struct pci_device_id mptfc_pci_table[] = { 14062306a36Sopenharmony_ci { PCI_VENDOR_ID_LSI_LOGIC, MPI_MANUFACTPAGE_DEVICEID_FC909, 14162306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID }, 14262306a36Sopenharmony_ci { PCI_VENDOR_ID_LSI_LOGIC, MPI_MANUFACTPAGE_DEVICEID_FC919, 14362306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID }, 14462306a36Sopenharmony_ci { PCI_VENDOR_ID_LSI_LOGIC, MPI_MANUFACTPAGE_DEVICEID_FC929, 14562306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID }, 14662306a36Sopenharmony_ci { PCI_VENDOR_ID_LSI_LOGIC, MPI_MANUFACTPAGE_DEVICEID_FC919X, 14762306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID }, 14862306a36Sopenharmony_ci { PCI_VENDOR_ID_LSI_LOGIC, MPI_MANUFACTPAGE_DEVICEID_FC929X, 14962306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID }, 15062306a36Sopenharmony_ci { PCI_VENDOR_ID_LSI_LOGIC, MPI_MANUFACTPAGE_DEVICEID_FC939X, 15162306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID }, 15262306a36Sopenharmony_ci { PCI_VENDOR_ID_LSI_LOGIC, MPI_MANUFACTPAGE_DEVICEID_FC949X, 15362306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID }, 15462306a36Sopenharmony_ci { PCI_VENDOR_ID_LSI_LOGIC, MPI_MANUFACTPAGE_DEVICEID_FC949E, 15562306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID }, 15662306a36Sopenharmony_ci { PCI_VENDOR_ID_BROCADE, MPI_MANUFACTPAGE_DEVICEID_FC949E, 15762306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID }, 15862306a36Sopenharmony_ci {0} /* Terminating entry */ 15962306a36Sopenharmony_ci}; 16062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, mptfc_pci_table); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic struct scsi_transport_template *mptfc_transport_template = NULL; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic struct fc_function_template mptfc_transport_functions = { 16562306a36Sopenharmony_ci .dd_fcrport_size = 8, 16662306a36Sopenharmony_ci .show_host_node_name = 1, 16762306a36Sopenharmony_ci .show_host_port_name = 1, 16862306a36Sopenharmony_ci .show_host_supported_classes = 1, 16962306a36Sopenharmony_ci .show_host_port_id = 1, 17062306a36Sopenharmony_ci .show_rport_supported_classes = 1, 17162306a36Sopenharmony_ci .show_starget_node_name = 1, 17262306a36Sopenharmony_ci .show_starget_port_name = 1, 17362306a36Sopenharmony_ci .show_starget_port_id = 1, 17462306a36Sopenharmony_ci .set_rport_dev_loss_tmo = mptfc_set_rport_loss_tmo, 17562306a36Sopenharmony_ci .show_rport_dev_loss_tmo = 1, 17662306a36Sopenharmony_ci .show_host_supported_speeds = 1, 17762306a36Sopenharmony_ci .show_host_maxframe_size = 1, 17862306a36Sopenharmony_ci .show_host_speed = 1, 17962306a36Sopenharmony_ci .show_host_fabric_name = 1, 18062306a36Sopenharmony_ci .show_host_port_type = 1, 18162306a36Sopenharmony_ci .show_host_port_state = 1, 18262306a36Sopenharmony_ci .show_host_symbolic_name = 1, 18362306a36Sopenharmony_ci}; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic int 18662306a36Sopenharmony_cimptfc_block_error_handler(struct scsi_cmnd *SCpnt, 18762306a36Sopenharmony_ci int (*func)(struct scsi_cmnd *SCpnt), 18862306a36Sopenharmony_ci const char *caller) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci MPT_SCSI_HOST *hd; 19162306a36Sopenharmony_ci struct scsi_device *sdev = SCpnt->device; 19262306a36Sopenharmony_ci struct Scsi_Host *shost = sdev->host; 19362306a36Sopenharmony_ci struct fc_rport *rport = starget_to_rport(scsi_target(sdev)); 19462306a36Sopenharmony_ci unsigned long flags; 19562306a36Sopenharmony_ci int ready; 19662306a36Sopenharmony_ci MPT_ADAPTER *ioc; 19762306a36Sopenharmony_ci int loops = 40; /* seconds */ 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci hd = shost_priv(SCpnt->device->host); 20062306a36Sopenharmony_ci ioc = hd->ioc; 20162306a36Sopenharmony_ci spin_lock_irqsave(shost->host_lock, flags); 20262306a36Sopenharmony_ci while ((ready = fc_remote_port_chkready(rport) >> 16) == DID_IMM_RETRY 20362306a36Sopenharmony_ci || (loops > 0 && ioc->active == 0)) { 20462306a36Sopenharmony_ci spin_unlock_irqrestore(shost->host_lock, flags); 20562306a36Sopenharmony_ci dfcprintk (ioc, printk(MYIOC_s_DEBUG_FMT 20662306a36Sopenharmony_ci "mptfc_block_error_handler.%d: %d:%llu, port status is " 20762306a36Sopenharmony_ci "%x, active flag %d, deferring %s recovery.\n", 20862306a36Sopenharmony_ci ioc->name, ioc->sh->host_no, 20962306a36Sopenharmony_ci SCpnt->device->id, SCpnt->device->lun, 21062306a36Sopenharmony_ci ready, ioc->active, caller)); 21162306a36Sopenharmony_ci msleep(1000); 21262306a36Sopenharmony_ci spin_lock_irqsave(shost->host_lock, flags); 21362306a36Sopenharmony_ci loops --; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci spin_unlock_irqrestore(shost->host_lock, flags); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if (ready == DID_NO_CONNECT || !SCpnt->device->hostdata 21862306a36Sopenharmony_ci || ioc->active == 0) { 21962306a36Sopenharmony_ci dfcprintk (ioc, printk(MYIOC_s_DEBUG_FMT 22062306a36Sopenharmony_ci "%s.%d: %d:%llu, failing recovery, " 22162306a36Sopenharmony_ci "port state %x, active %d, vdevice %p.\n", caller, 22262306a36Sopenharmony_ci ioc->name, ioc->sh->host_no, 22362306a36Sopenharmony_ci SCpnt->device->id, SCpnt->device->lun, ready, 22462306a36Sopenharmony_ci ioc->active, SCpnt->device->hostdata)); 22562306a36Sopenharmony_ci return FAILED; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci dfcprintk (ioc, printk(MYIOC_s_DEBUG_FMT 22862306a36Sopenharmony_ci "%s.%d: %d:%llu, executing recovery.\n", caller, 22962306a36Sopenharmony_ci ioc->name, ioc->sh->host_no, 23062306a36Sopenharmony_ci SCpnt->device->id, SCpnt->device->lun)); 23162306a36Sopenharmony_ci return (*func)(SCpnt); 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistatic int 23562306a36Sopenharmony_cimptfc_abort(struct scsi_cmnd *SCpnt) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci return 23862306a36Sopenharmony_ci mptfc_block_error_handler(SCpnt, mptscsih_abort, __func__); 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic int 24262306a36Sopenharmony_cimptfc_dev_reset(struct scsi_cmnd *SCpnt) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci return 24562306a36Sopenharmony_ci mptfc_block_error_handler(SCpnt, mptscsih_dev_reset, __func__); 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic int 24962306a36Sopenharmony_cimptfc_bus_reset(struct scsi_cmnd *SCpnt) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci return 25262306a36Sopenharmony_ci mptfc_block_error_handler(SCpnt, mptscsih_bus_reset, __func__); 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_cistatic void 25662306a36Sopenharmony_cimptfc_set_rport_loss_tmo(struct fc_rport *rport, uint32_t timeout) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci if (timeout > 0) 25962306a36Sopenharmony_ci rport->dev_loss_tmo = timeout; 26062306a36Sopenharmony_ci else 26162306a36Sopenharmony_ci rport->dev_loss_tmo = mptfc_dev_loss_tmo; 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic int 26562306a36Sopenharmony_cimptfc_FcDevPage0_cmp_func(const void *a, const void *b) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci FCDevicePage0_t **aa = (FCDevicePage0_t **)a; 26862306a36Sopenharmony_ci FCDevicePage0_t **bb = (FCDevicePage0_t **)b; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci if ((*aa)->CurrentBus == (*bb)->CurrentBus) { 27162306a36Sopenharmony_ci if ((*aa)->CurrentTargetID == (*bb)->CurrentTargetID) 27262306a36Sopenharmony_ci return 0; 27362306a36Sopenharmony_ci if ((*aa)->CurrentTargetID < (*bb)->CurrentTargetID) 27462306a36Sopenharmony_ci return -1; 27562306a36Sopenharmony_ci return 1; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci if ((*aa)->CurrentBus < (*bb)->CurrentBus) 27862306a36Sopenharmony_ci return -1; 27962306a36Sopenharmony_ci return 1; 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistatic int 28362306a36Sopenharmony_cimptfc_GetFcDevPage0(MPT_ADAPTER *ioc, int ioc_port, 28462306a36Sopenharmony_ci void(*func)(MPT_ADAPTER *ioc,int channel, FCDevicePage0_t *arg)) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci ConfigPageHeader_t hdr; 28762306a36Sopenharmony_ci CONFIGPARMS cfg; 28862306a36Sopenharmony_ci FCDevicePage0_t *ppage0_alloc, *fc; 28962306a36Sopenharmony_ci dma_addr_t page0_dma; 29062306a36Sopenharmony_ci int data_sz; 29162306a36Sopenharmony_ci int ii; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci FCDevicePage0_t *p0_array=NULL, *p_p0; 29462306a36Sopenharmony_ci FCDevicePage0_t **pp0_array=NULL, **p_pp0; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci int rc = -ENOMEM; 29762306a36Sopenharmony_ci U32 port_id = 0xffffff; 29862306a36Sopenharmony_ci int num_targ = 0; 29962306a36Sopenharmony_ci int max_bus = ioc->facts.MaxBuses; 30062306a36Sopenharmony_ci int max_targ; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci max_targ = (ioc->facts.MaxDevices == 0) ? 256 : ioc->facts.MaxDevices; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci data_sz = sizeof(FCDevicePage0_t) * max_bus * max_targ; 30562306a36Sopenharmony_ci p_p0 = p0_array = kzalloc(data_sz, GFP_KERNEL); 30662306a36Sopenharmony_ci if (!p0_array) 30762306a36Sopenharmony_ci goto out; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci data_sz = sizeof(FCDevicePage0_t *) * max_bus * max_targ; 31062306a36Sopenharmony_ci p_pp0 = pp0_array = kzalloc(data_sz, GFP_KERNEL); 31162306a36Sopenharmony_ci if (!pp0_array) 31262306a36Sopenharmony_ci goto out; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci do { 31562306a36Sopenharmony_ci /* Get FC Device Page 0 header */ 31662306a36Sopenharmony_ci hdr.PageVersion = 0; 31762306a36Sopenharmony_ci hdr.PageLength = 0; 31862306a36Sopenharmony_ci hdr.PageNumber = 0; 31962306a36Sopenharmony_ci hdr.PageType = MPI_CONFIG_PAGETYPE_FC_DEVICE; 32062306a36Sopenharmony_ci cfg.cfghdr.hdr = &hdr; 32162306a36Sopenharmony_ci cfg.physAddr = -1; 32262306a36Sopenharmony_ci cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; 32362306a36Sopenharmony_ci cfg.dir = 0; 32462306a36Sopenharmony_ci cfg.pageAddr = port_id; 32562306a36Sopenharmony_ci cfg.timeout = 0; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci if ((rc = mpt_config(ioc, &cfg)) != 0) 32862306a36Sopenharmony_ci break; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (hdr.PageLength <= 0) 33162306a36Sopenharmony_ci break; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci data_sz = hdr.PageLength * 4; 33462306a36Sopenharmony_ci ppage0_alloc = dma_alloc_coherent(&ioc->pcidev->dev, data_sz, 33562306a36Sopenharmony_ci &page0_dma, GFP_KERNEL); 33662306a36Sopenharmony_ci rc = -ENOMEM; 33762306a36Sopenharmony_ci if (!ppage0_alloc) 33862306a36Sopenharmony_ci break; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci cfg.physAddr = page0_dma; 34162306a36Sopenharmony_ci cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if ((rc = mpt_config(ioc, &cfg)) == 0) { 34462306a36Sopenharmony_ci ppage0_alloc->PortIdentifier = 34562306a36Sopenharmony_ci le32_to_cpu(ppage0_alloc->PortIdentifier); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci ppage0_alloc->WWNN.Low = 34862306a36Sopenharmony_ci le32_to_cpu(ppage0_alloc->WWNN.Low); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci ppage0_alloc->WWNN.High = 35162306a36Sopenharmony_ci le32_to_cpu(ppage0_alloc->WWNN.High); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci ppage0_alloc->WWPN.Low = 35462306a36Sopenharmony_ci le32_to_cpu(ppage0_alloc->WWPN.Low); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci ppage0_alloc->WWPN.High = 35762306a36Sopenharmony_ci le32_to_cpu(ppage0_alloc->WWPN.High); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci ppage0_alloc->BBCredit = 36062306a36Sopenharmony_ci le16_to_cpu(ppage0_alloc->BBCredit); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci ppage0_alloc->MaxRxFrameSize = 36362306a36Sopenharmony_ci le16_to_cpu(ppage0_alloc->MaxRxFrameSize); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci port_id = ppage0_alloc->PortIdentifier; 36662306a36Sopenharmony_ci num_targ++; 36762306a36Sopenharmony_ci *p_p0 = *ppage0_alloc; /* save data */ 36862306a36Sopenharmony_ci *p_pp0++ = p_p0++; /* save addr */ 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci dma_free_coherent(&ioc->pcidev->dev, data_sz, 37162306a36Sopenharmony_ci ppage0_alloc, page0_dma); 37262306a36Sopenharmony_ci if (rc != 0) 37362306a36Sopenharmony_ci break; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci } while (port_id <= 0xff0000); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci if (num_targ) { 37862306a36Sopenharmony_ci /* sort array */ 37962306a36Sopenharmony_ci if (num_targ > 1) 38062306a36Sopenharmony_ci sort (pp0_array, num_targ, sizeof(FCDevicePage0_t *), 38162306a36Sopenharmony_ci mptfc_FcDevPage0_cmp_func, NULL); 38262306a36Sopenharmony_ci /* call caller's func for each targ */ 38362306a36Sopenharmony_ci for (ii = 0; ii < num_targ; ii++) { 38462306a36Sopenharmony_ci fc = *(pp0_array+ii); 38562306a36Sopenharmony_ci func(ioc, ioc_port, fc); 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci out: 39062306a36Sopenharmony_ci kfree(pp0_array); 39162306a36Sopenharmony_ci kfree(p0_array); 39262306a36Sopenharmony_ci return rc; 39362306a36Sopenharmony_ci} 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_cistatic int 39662306a36Sopenharmony_cimptfc_generate_rport_ids(FCDevicePage0_t *pg0, struct fc_rport_identifiers *rid) 39762306a36Sopenharmony_ci{ 39862306a36Sopenharmony_ci /* not currently usable */ 39962306a36Sopenharmony_ci if (pg0->Flags & (MPI_FC_DEVICE_PAGE0_FLAGS_PLOGI_INVALID | 40062306a36Sopenharmony_ci MPI_FC_DEVICE_PAGE0_FLAGS_PRLI_INVALID)) 40162306a36Sopenharmony_ci return -1; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci if (!(pg0->Flags & MPI_FC_DEVICE_PAGE0_FLAGS_TARGETID_BUS_VALID)) 40462306a36Sopenharmony_ci return -1; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci if (!(pg0->Protocol & MPI_FC_DEVICE_PAGE0_PROT_FCP_TARGET)) 40762306a36Sopenharmony_ci return -1; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci /* 41062306a36Sopenharmony_ci * board data structure already normalized to platform endianness 41162306a36Sopenharmony_ci * shifted to avoid unaligned access on 64 bit architecture 41262306a36Sopenharmony_ci */ 41362306a36Sopenharmony_ci rid->node_name = ((u64)pg0->WWNN.High) << 32 | (u64)pg0->WWNN.Low; 41462306a36Sopenharmony_ci rid->port_name = ((u64)pg0->WWPN.High) << 32 | (u64)pg0->WWPN.Low; 41562306a36Sopenharmony_ci rid->port_id = pg0->PortIdentifier; 41662306a36Sopenharmony_ci rid->roles = FC_RPORT_ROLE_UNKNOWN; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci return 0; 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic void 42262306a36Sopenharmony_cimptfc_register_dev(MPT_ADAPTER *ioc, int channel, FCDevicePage0_t *pg0) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci struct fc_rport_identifiers rport_ids; 42562306a36Sopenharmony_ci struct fc_rport *rport; 42662306a36Sopenharmony_ci struct mptfc_rport_info *ri; 42762306a36Sopenharmony_ci int new_ri = 1; 42862306a36Sopenharmony_ci u64 pn, nn; 42962306a36Sopenharmony_ci VirtTarget *vtarget; 43062306a36Sopenharmony_ci u32 roles = FC_RPORT_ROLE_UNKNOWN; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci if (mptfc_generate_rport_ids(pg0, &rport_ids) < 0) 43362306a36Sopenharmony_ci return; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci roles |= FC_RPORT_ROLE_FCP_TARGET; 43662306a36Sopenharmony_ci if (pg0->Protocol & MPI_FC_DEVICE_PAGE0_PROT_FCP_INITIATOR) 43762306a36Sopenharmony_ci roles |= FC_RPORT_ROLE_FCP_INITIATOR; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci /* scan list looking for a match */ 44062306a36Sopenharmony_ci list_for_each_entry(ri, &ioc->fc_rports, list) { 44162306a36Sopenharmony_ci pn = (u64)ri->pg0.WWPN.High << 32 | (u64)ri->pg0.WWPN.Low; 44262306a36Sopenharmony_ci if (pn == rport_ids.port_name) { /* match */ 44362306a36Sopenharmony_ci list_move_tail(&ri->list, &ioc->fc_rports); 44462306a36Sopenharmony_ci new_ri = 0; 44562306a36Sopenharmony_ci break; 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci if (new_ri) { /* allocate one */ 44962306a36Sopenharmony_ci ri = kzalloc(sizeof(struct mptfc_rport_info), GFP_KERNEL); 45062306a36Sopenharmony_ci if (!ri) 45162306a36Sopenharmony_ci return; 45262306a36Sopenharmony_ci list_add_tail(&ri->list, &ioc->fc_rports); 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci ri->pg0 = *pg0; /* add/update pg0 data */ 45662306a36Sopenharmony_ci ri->flags &= ~MPT_RPORT_INFO_FLAGS_MISSING; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci /* MPT_RPORT_INFO_FLAGS_REGISTERED - rport not previously deleted */ 45962306a36Sopenharmony_ci if (!(ri->flags & MPT_RPORT_INFO_FLAGS_REGISTERED)) { 46062306a36Sopenharmony_ci ri->flags |= MPT_RPORT_INFO_FLAGS_REGISTERED; 46162306a36Sopenharmony_ci rport = fc_remote_port_add(ioc->sh, channel, &rport_ids); 46262306a36Sopenharmony_ci if (rport) { 46362306a36Sopenharmony_ci ri->rport = rport; 46462306a36Sopenharmony_ci if (new_ri) /* may have been reset by user */ 46562306a36Sopenharmony_ci rport->dev_loss_tmo = mptfc_dev_loss_tmo; 46662306a36Sopenharmony_ci /* 46762306a36Sopenharmony_ci * if already mapped, remap here. If not mapped, 46862306a36Sopenharmony_ci * target_alloc will allocate vtarget and map, 46962306a36Sopenharmony_ci * slave_alloc will fill in vdevice from vtarget. 47062306a36Sopenharmony_ci */ 47162306a36Sopenharmony_ci if (ri->starget) { 47262306a36Sopenharmony_ci vtarget = ri->starget->hostdata; 47362306a36Sopenharmony_ci if (vtarget) { 47462306a36Sopenharmony_ci vtarget->id = pg0->CurrentTargetID; 47562306a36Sopenharmony_ci vtarget->channel = pg0->CurrentBus; 47662306a36Sopenharmony_ci vtarget->deleted = 0; 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci *((struct mptfc_rport_info **)rport->dd_data) = ri; 48062306a36Sopenharmony_ci /* scan will be scheduled once rport becomes a target */ 48162306a36Sopenharmony_ci fc_remote_port_rolechg(rport,roles); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci pn = (u64)ri->pg0.WWPN.High << 32 | (u64)ri->pg0.WWPN.Low; 48462306a36Sopenharmony_ci nn = (u64)ri->pg0.WWNN.High << 32 | (u64)ri->pg0.WWNN.Low; 48562306a36Sopenharmony_ci dfcprintk (ioc, printk(MYIOC_s_DEBUG_FMT 48662306a36Sopenharmony_ci "mptfc_reg_dev.%d: %x, %llx / %llx, tid %d, " 48762306a36Sopenharmony_ci "rport tid %d, tmo %d\n", 48862306a36Sopenharmony_ci ioc->name, 48962306a36Sopenharmony_ci ioc->sh->host_no, 49062306a36Sopenharmony_ci pg0->PortIdentifier, 49162306a36Sopenharmony_ci (unsigned long long)nn, 49262306a36Sopenharmony_ci (unsigned long long)pn, 49362306a36Sopenharmony_ci pg0->CurrentTargetID, 49462306a36Sopenharmony_ci ri->rport->scsi_target_id, 49562306a36Sopenharmony_ci ri->rport->dev_loss_tmo)); 49662306a36Sopenharmony_ci } else { 49762306a36Sopenharmony_ci list_del(&ri->list); 49862306a36Sopenharmony_ci kfree(ri); 49962306a36Sopenharmony_ci ri = NULL; 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci} 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci/* 50562306a36Sopenharmony_ci * OS entry point to allow for host driver to free allocated memory 50662306a36Sopenharmony_ci * Called if no device present or device being unloaded 50762306a36Sopenharmony_ci */ 50862306a36Sopenharmony_cistatic void 50962306a36Sopenharmony_cimptfc_target_destroy(struct scsi_target *starget) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci struct fc_rport *rport; 51262306a36Sopenharmony_ci struct mptfc_rport_info *ri; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci rport = starget_to_rport(starget); 51562306a36Sopenharmony_ci if (rport) { 51662306a36Sopenharmony_ci ri = *((struct mptfc_rport_info **)rport->dd_data); 51762306a36Sopenharmony_ci if (ri) /* better be! */ 51862306a36Sopenharmony_ci ri->starget = NULL; 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci kfree(starget->hostdata); 52162306a36Sopenharmony_ci starget->hostdata = NULL; 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci/* 52562306a36Sopenharmony_ci * OS entry point to allow host driver to alloc memory 52662306a36Sopenharmony_ci * for each scsi target. Called once per device the bus scan. 52762306a36Sopenharmony_ci * Return non-zero if allocation fails. 52862306a36Sopenharmony_ci */ 52962306a36Sopenharmony_cistatic int 53062306a36Sopenharmony_cimptfc_target_alloc(struct scsi_target *starget) 53162306a36Sopenharmony_ci{ 53262306a36Sopenharmony_ci VirtTarget *vtarget; 53362306a36Sopenharmony_ci struct fc_rport *rport; 53462306a36Sopenharmony_ci struct mptfc_rport_info *ri; 53562306a36Sopenharmony_ci int rc; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci vtarget = kzalloc(sizeof(VirtTarget), GFP_KERNEL); 53862306a36Sopenharmony_ci if (!vtarget) 53962306a36Sopenharmony_ci return -ENOMEM; 54062306a36Sopenharmony_ci starget->hostdata = vtarget; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci rc = -ENODEV; 54362306a36Sopenharmony_ci rport = starget_to_rport(starget); 54462306a36Sopenharmony_ci if (rport) { 54562306a36Sopenharmony_ci ri = *((struct mptfc_rport_info **)rport->dd_data); 54662306a36Sopenharmony_ci if (ri) { /* better be! */ 54762306a36Sopenharmony_ci vtarget->id = ri->pg0.CurrentTargetID; 54862306a36Sopenharmony_ci vtarget->channel = ri->pg0.CurrentBus; 54962306a36Sopenharmony_ci ri->starget = starget; 55062306a36Sopenharmony_ci rc = 0; 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci if (rc != 0) { 55462306a36Sopenharmony_ci kfree(vtarget); 55562306a36Sopenharmony_ci starget->hostdata = NULL; 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci return rc; 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci/* 56162306a36Sopenharmony_ci * mptfc_dump_lun_info 56262306a36Sopenharmony_ci * @ioc 56362306a36Sopenharmony_ci * @rport 56462306a36Sopenharmony_ci * @sdev 56562306a36Sopenharmony_ci * 56662306a36Sopenharmony_ci */ 56762306a36Sopenharmony_cistatic void 56862306a36Sopenharmony_cimptfc_dump_lun_info(MPT_ADAPTER *ioc, struct fc_rport *rport, struct scsi_device *sdev, 56962306a36Sopenharmony_ci VirtTarget *vtarget) 57062306a36Sopenharmony_ci{ 57162306a36Sopenharmony_ci u64 nn, pn; 57262306a36Sopenharmony_ci struct mptfc_rport_info *ri; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci ri = *((struct mptfc_rport_info **)rport->dd_data); 57562306a36Sopenharmony_ci pn = (u64)ri->pg0.WWPN.High << 32 | (u64)ri->pg0.WWPN.Low; 57662306a36Sopenharmony_ci nn = (u64)ri->pg0.WWNN.High << 32 | (u64)ri->pg0.WWNN.Low; 57762306a36Sopenharmony_ci dfcprintk (ioc, printk(MYIOC_s_DEBUG_FMT 57862306a36Sopenharmony_ci "mptfc_slv_alloc.%d: num_luns %d, sdev.id %d, " 57962306a36Sopenharmony_ci "CurrentTargetID %d, %x %llx %llx\n", 58062306a36Sopenharmony_ci ioc->name, 58162306a36Sopenharmony_ci sdev->host->host_no, 58262306a36Sopenharmony_ci vtarget->num_luns, 58362306a36Sopenharmony_ci sdev->id, ri->pg0.CurrentTargetID, 58462306a36Sopenharmony_ci ri->pg0.PortIdentifier, 58562306a36Sopenharmony_ci (unsigned long long)pn, 58662306a36Sopenharmony_ci (unsigned long long)nn)); 58762306a36Sopenharmony_ci} 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci/* 59162306a36Sopenharmony_ci * OS entry point to allow host driver to alloc memory 59262306a36Sopenharmony_ci * for each scsi device. Called once per device the bus scan. 59362306a36Sopenharmony_ci * Return non-zero if allocation fails. 59462306a36Sopenharmony_ci * Init memory once per LUN. 59562306a36Sopenharmony_ci */ 59662306a36Sopenharmony_cistatic int 59762306a36Sopenharmony_cimptfc_slave_alloc(struct scsi_device *sdev) 59862306a36Sopenharmony_ci{ 59962306a36Sopenharmony_ci MPT_SCSI_HOST *hd; 60062306a36Sopenharmony_ci VirtTarget *vtarget; 60162306a36Sopenharmony_ci VirtDevice *vdevice; 60262306a36Sopenharmony_ci struct scsi_target *starget; 60362306a36Sopenharmony_ci struct fc_rport *rport; 60462306a36Sopenharmony_ci MPT_ADAPTER *ioc; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci starget = scsi_target(sdev); 60762306a36Sopenharmony_ci rport = starget_to_rport(starget); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci if (!rport || fc_remote_port_chkready(rport)) 61062306a36Sopenharmony_ci return -ENXIO; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci hd = shost_priv(sdev->host); 61362306a36Sopenharmony_ci ioc = hd->ioc; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci vdevice = kzalloc(sizeof(VirtDevice), GFP_KERNEL); 61662306a36Sopenharmony_ci if (!vdevice) { 61762306a36Sopenharmony_ci printk(MYIOC_s_ERR_FMT "slave_alloc kmalloc(%zd) FAILED!\n", 61862306a36Sopenharmony_ci ioc->name, sizeof(VirtDevice)); 61962306a36Sopenharmony_ci return -ENOMEM; 62062306a36Sopenharmony_ci } 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci sdev->hostdata = vdevice; 62462306a36Sopenharmony_ci vtarget = starget->hostdata; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci if (vtarget->num_luns == 0) { 62762306a36Sopenharmony_ci vtarget->ioc_id = ioc->id; 62862306a36Sopenharmony_ci vtarget->tflags = MPT_TARGET_FLAGS_Q_YES; 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci vdevice->vtarget = vtarget; 63262306a36Sopenharmony_ci vdevice->lun = sdev->lun; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci vtarget->num_luns++; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci mptfc_dump_lun_info(ioc, rport, sdev, vtarget); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci return 0; 64062306a36Sopenharmony_ci} 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_cistatic int 64362306a36Sopenharmony_cimptfc_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *SCpnt) 64462306a36Sopenharmony_ci{ 64562306a36Sopenharmony_ci struct mptfc_rport_info *ri; 64662306a36Sopenharmony_ci struct fc_rport *rport = starget_to_rport(scsi_target(SCpnt->device)); 64762306a36Sopenharmony_ci int err; 64862306a36Sopenharmony_ci VirtDevice *vdevice = SCpnt->device->hostdata; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci if (!vdevice || !vdevice->vtarget) { 65162306a36Sopenharmony_ci SCpnt->result = DID_NO_CONNECT << 16; 65262306a36Sopenharmony_ci scsi_done(SCpnt); 65362306a36Sopenharmony_ci return 0; 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci err = fc_remote_port_chkready(rport); 65762306a36Sopenharmony_ci if (unlikely(err)) { 65862306a36Sopenharmony_ci SCpnt->result = err; 65962306a36Sopenharmony_ci scsi_done(SCpnt); 66062306a36Sopenharmony_ci return 0; 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci /* dd_data is null until finished adding target */ 66462306a36Sopenharmony_ci ri = *((struct mptfc_rport_info **)rport->dd_data); 66562306a36Sopenharmony_ci if (unlikely(!ri)) { 66662306a36Sopenharmony_ci SCpnt->result = DID_IMM_RETRY << 16; 66762306a36Sopenharmony_ci scsi_done(SCpnt); 66862306a36Sopenharmony_ci return 0; 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci return mptscsih_qcmd(SCpnt); 67262306a36Sopenharmony_ci} 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci/* 67562306a36Sopenharmony_ci * mptfc_display_port_link_speed - displaying link speed 67662306a36Sopenharmony_ci * @ioc: Pointer to MPT_ADAPTER structure 67762306a36Sopenharmony_ci * @portnum: IOC Port number 67862306a36Sopenharmony_ci * @pp0dest: port page0 data payload 67962306a36Sopenharmony_ci * 68062306a36Sopenharmony_ci */ 68162306a36Sopenharmony_cistatic void 68262306a36Sopenharmony_cimptfc_display_port_link_speed(MPT_ADAPTER *ioc, int portnum, FCPortPage0_t *pp0dest) 68362306a36Sopenharmony_ci{ 68462306a36Sopenharmony_ci u8 old_speed, new_speed, state; 68562306a36Sopenharmony_ci char *old, *new; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci if (portnum >= 2) 68862306a36Sopenharmony_ci return; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci old_speed = ioc->fc_link_speed[portnum]; 69162306a36Sopenharmony_ci new_speed = pp0dest->CurrentSpeed; 69262306a36Sopenharmony_ci state = pp0dest->PortState; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci if (state != MPI_FCPORTPAGE0_PORTSTATE_OFFLINE && 69562306a36Sopenharmony_ci new_speed != MPI_FCPORTPAGE0_CURRENT_SPEED_UNKNOWN) { 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci old = old_speed == MPI_FCPORTPAGE0_CURRENT_SPEED_1GBIT ? "1 Gbps" : 69862306a36Sopenharmony_ci old_speed == MPI_FCPORTPAGE0_CURRENT_SPEED_2GBIT ? "2 Gbps" : 69962306a36Sopenharmony_ci old_speed == MPI_FCPORTPAGE0_CURRENT_SPEED_4GBIT ? "4 Gbps" : 70062306a36Sopenharmony_ci "Unknown"; 70162306a36Sopenharmony_ci new = new_speed == MPI_FCPORTPAGE0_CURRENT_SPEED_1GBIT ? "1 Gbps" : 70262306a36Sopenharmony_ci new_speed == MPI_FCPORTPAGE0_CURRENT_SPEED_2GBIT ? "2 Gbps" : 70362306a36Sopenharmony_ci new_speed == MPI_FCPORTPAGE0_CURRENT_SPEED_4GBIT ? "4 Gbps" : 70462306a36Sopenharmony_ci "Unknown"; 70562306a36Sopenharmony_ci if (old_speed == 0) 70662306a36Sopenharmony_ci printk(MYIOC_s_NOTE_FMT 70762306a36Sopenharmony_ci "FC Link Established, Speed = %s\n", 70862306a36Sopenharmony_ci ioc->name, new); 70962306a36Sopenharmony_ci else if (old_speed != new_speed) 71062306a36Sopenharmony_ci printk(MYIOC_s_WARN_FMT 71162306a36Sopenharmony_ci "FC Link Speed Change, Old Speed = %s, New Speed = %s\n", 71262306a36Sopenharmony_ci ioc->name, old, new); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci ioc->fc_link_speed[portnum] = new_speed; 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci} 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci/* 71962306a36Sopenharmony_ci * mptfc_GetFcPortPage0 - Fetch FCPort config Page0. 72062306a36Sopenharmony_ci * @ioc: Pointer to MPT_ADAPTER structure 72162306a36Sopenharmony_ci * @portnum: IOC Port number 72262306a36Sopenharmony_ci * 72362306a36Sopenharmony_ci * Return: 0 for success 72462306a36Sopenharmony_ci * -ENOMEM if no memory available 72562306a36Sopenharmony_ci * -EPERM if not allowed due to ISR context 72662306a36Sopenharmony_ci * -EAGAIN if no msg frames currently available 72762306a36Sopenharmony_ci * -EFAULT for non-successful reply or no reply (timeout) 72862306a36Sopenharmony_ci * -EINVAL portnum arg out of range (hardwired to two elements) 72962306a36Sopenharmony_ci */ 73062306a36Sopenharmony_cistatic int 73162306a36Sopenharmony_cimptfc_GetFcPortPage0(MPT_ADAPTER *ioc, int portnum) 73262306a36Sopenharmony_ci{ 73362306a36Sopenharmony_ci ConfigPageHeader_t hdr; 73462306a36Sopenharmony_ci CONFIGPARMS cfg; 73562306a36Sopenharmony_ci FCPortPage0_t *ppage0_alloc; 73662306a36Sopenharmony_ci FCPortPage0_t *pp0dest; 73762306a36Sopenharmony_ci dma_addr_t page0_dma; 73862306a36Sopenharmony_ci int data_sz; 73962306a36Sopenharmony_ci int copy_sz; 74062306a36Sopenharmony_ci int rc; 74162306a36Sopenharmony_ci int count = 400; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci if (portnum > 1) 74462306a36Sopenharmony_ci return -EINVAL; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci /* Get FCPort Page 0 header */ 74762306a36Sopenharmony_ci hdr.PageVersion = 0; 74862306a36Sopenharmony_ci hdr.PageLength = 0; 74962306a36Sopenharmony_ci hdr.PageNumber = 0; 75062306a36Sopenharmony_ci hdr.PageType = MPI_CONFIG_PAGETYPE_FC_PORT; 75162306a36Sopenharmony_ci cfg.cfghdr.hdr = &hdr; 75262306a36Sopenharmony_ci cfg.physAddr = -1; 75362306a36Sopenharmony_ci cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; 75462306a36Sopenharmony_ci cfg.dir = 0; 75562306a36Sopenharmony_ci cfg.pageAddr = portnum; 75662306a36Sopenharmony_ci cfg.timeout = 0; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci if ((rc = mpt_config(ioc, &cfg)) != 0) 75962306a36Sopenharmony_ci return rc; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci if (hdr.PageLength == 0) 76262306a36Sopenharmony_ci return 0; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci data_sz = hdr.PageLength * 4; 76562306a36Sopenharmony_ci rc = -ENOMEM; 76662306a36Sopenharmony_ci ppage0_alloc = dma_alloc_coherent(&ioc->pcidev->dev, data_sz, 76762306a36Sopenharmony_ci &page0_dma, GFP_KERNEL); 76862306a36Sopenharmony_ci if (ppage0_alloc) { 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci try_again: 77162306a36Sopenharmony_ci memset((u8 *)ppage0_alloc, 0, data_sz); 77262306a36Sopenharmony_ci cfg.physAddr = page0_dma; 77362306a36Sopenharmony_ci cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci if ((rc = mpt_config(ioc, &cfg)) == 0) { 77662306a36Sopenharmony_ci /* save the data */ 77762306a36Sopenharmony_ci pp0dest = &ioc->fc_port_page0[portnum]; 77862306a36Sopenharmony_ci copy_sz = min_t(int, sizeof(FCPortPage0_t), data_sz); 77962306a36Sopenharmony_ci memcpy(pp0dest, ppage0_alloc, copy_sz); 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci /* 78262306a36Sopenharmony_ci * Normalize endianness of structure data, 78362306a36Sopenharmony_ci * by byte-swapping all > 1 byte fields! 78462306a36Sopenharmony_ci */ 78562306a36Sopenharmony_ci pp0dest->Flags = le32_to_cpu(pp0dest->Flags); 78662306a36Sopenharmony_ci pp0dest->PortIdentifier = le32_to_cpu(pp0dest->PortIdentifier); 78762306a36Sopenharmony_ci pp0dest->WWNN.Low = le32_to_cpu(pp0dest->WWNN.Low); 78862306a36Sopenharmony_ci pp0dest->WWNN.High = le32_to_cpu(pp0dest->WWNN.High); 78962306a36Sopenharmony_ci pp0dest->WWPN.Low = le32_to_cpu(pp0dest->WWPN.Low); 79062306a36Sopenharmony_ci pp0dest->WWPN.High = le32_to_cpu(pp0dest->WWPN.High); 79162306a36Sopenharmony_ci pp0dest->SupportedServiceClass = le32_to_cpu(pp0dest->SupportedServiceClass); 79262306a36Sopenharmony_ci pp0dest->SupportedSpeeds = le32_to_cpu(pp0dest->SupportedSpeeds); 79362306a36Sopenharmony_ci pp0dest->CurrentSpeed = le32_to_cpu(pp0dest->CurrentSpeed); 79462306a36Sopenharmony_ci pp0dest->MaxFrameSize = le32_to_cpu(pp0dest->MaxFrameSize); 79562306a36Sopenharmony_ci pp0dest->FabricWWNN.Low = le32_to_cpu(pp0dest->FabricWWNN.Low); 79662306a36Sopenharmony_ci pp0dest->FabricWWNN.High = le32_to_cpu(pp0dest->FabricWWNN.High); 79762306a36Sopenharmony_ci pp0dest->FabricWWPN.Low = le32_to_cpu(pp0dest->FabricWWPN.Low); 79862306a36Sopenharmony_ci pp0dest->FabricWWPN.High = le32_to_cpu(pp0dest->FabricWWPN.High); 79962306a36Sopenharmony_ci pp0dest->DiscoveredPortsCount = le32_to_cpu(pp0dest->DiscoveredPortsCount); 80062306a36Sopenharmony_ci pp0dest->MaxInitiators = le32_to_cpu(pp0dest->MaxInitiators); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci /* 80362306a36Sopenharmony_ci * if still doing discovery, 80462306a36Sopenharmony_ci * hang loose a while until finished 80562306a36Sopenharmony_ci */ 80662306a36Sopenharmony_ci if ((pp0dest->PortState == MPI_FCPORTPAGE0_PORTSTATE_UNKNOWN) || 80762306a36Sopenharmony_ci (pp0dest->PortState == MPI_FCPORTPAGE0_PORTSTATE_ONLINE && 80862306a36Sopenharmony_ci (pp0dest->Flags & MPI_FCPORTPAGE0_FLAGS_ATTACH_TYPE_MASK) 80962306a36Sopenharmony_ci == MPI_FCPORTPAGE0_FLAGS_ATTACH_NO_INIT)) { 81062306a36Sopenharmony_ci if (count-- > 0) { 81162306a36Sopenharmony_ci msleep(100); 81262306a36Sopenharmony_ci goto try_again; 81362306a36Sopenharmony_ci } 81462306a36Sopenharmony_ci printk(MYIOC_s_INFO_FMT "Firmware discovery not" 81562306a36Sopenharmony_ci " complete.\n", 81662306a36Sopenharmony_ci ioc->name); 81762306a36Sopenharmony_ci } 81862306a36Sopenharmony_ci mptfc_display_port_link_speed(ioc, portnum, pp0dest); 81962306a36Sopenharmony_ci } 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci dma_free_coherent(&ioc->pcidev->dev, data_sz, ppage0_alloc, 82262306a36Sopenharmony_ci page0_dma); 82362306a36Sopenharmony_ci } 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci return rc; 82662306a36Sopenharmony_ci} 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_cistatic int 82962306a36Sopenharmony_cimptfc_WriteFcPortPage1(MPT_ADAPTER *ioc, int portnum) 83062306a36Sopenharmony_ci{ 83162306a36Sopenharmony_ci ConfigPageHeader_t hdr; 83262306a36Sopenharmony_ci CONFIGPARMS cfg; 83362306a36Sopenharmony_ci int rc; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci if (portnum > 1) 83662306a36Sopenharmony_ci return -EINVAL; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci if (!(ioc->fc_data.fc_port_page1[portnum].data)) 83962306a36Sopenharmony_ci return -EINVAL; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci /* get fcport page 1 header */ 84262306a36Sopenharmony_ci hdr.PageVersion = 0; 84362306a36Sopenharmony_ci hdr.PageLength = 0; 84462306a36Sopenharmony_ci hdr.PageNumber = 1; 84562306a36Sopenharmony_ci hdr.PageType = MPI_CONFIG_PAGETYPE_FC_PORT; 84662306a36Sopenharmony_ci cfg.cfghdr.hdr = &hdr; 84762306a36Sopenharmony_ci cfg.physAddr = -1; 84862306a36Sopenharmony_ci cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; 84962306a36Sopenharmony_ci cfg.dir = 0; 85062306a36Sopenharmony_ci cfg.pageAddr = portnum; 85162306a36Sopenharmony_ci cfg.timeout = 0; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci if ((rc = mpt_config(ioc, &cfg)) != 0) 85462306a36Sopenharmony_ci return rc; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci if (hdr.PageLength == 0) 85762306a36Sopenharmony_ci return -ENODEV; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci if (hdr.PageLength*4 != ioc->fc_data.fc_port_page1[portnum].pg_sz) 86062306a36Sopenharmony_ci return -EINVAL; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci cfg.physAddr = ioc->fc_data.fc_port_page1[portnum].dma; 86362306a36Sopenharmony_ci cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT; 86462306a36Sopenharmony_ci cfg.dir = 1; 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci rc = mpt_config(ioc, &cfg); 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci return rc; 86962306a36Sopenharmony_ci} 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_cistatic int 87262306a36Sopenharmony_cimptfc_GetFcPortPage1(MPT_ADAPTER *ioc, int portnum) 87362306a36Sopenharmony_ci{ 87462306a36Sopenharmony_ci ConfigPageHeader_t hdr; 87562306a36Sopenharmony_ci CONFIGPARMS cfg; 87662306a36Sopenharmony_ci FCPortPage1_t *page1_alloc; 87762306a36Sopenharmony_ci dma_addr_t page1_dma; 87862306a36Sopenharmony_ci int data_sz; 87962306a36Sopenharmony_ci int rc; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci if (portnum > 1) 88262306a36Sopenharmony_ci return -EINVAL; 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci /* get fcport page 1 header */ 88562306a36Sopenharmony_ci hdr.PageVersion = 0; 88662306a36Sopenharmony_ci hdr.PageLength = 0; 88762306a36Sopenharmony_ci hdr.PageNumber = 1; 88862306a36Sopenharmony_ci hdr.PageType = MPI_CONFIG_PAGETYPE_FC_PORT; 88962306a36Sopenharmony_ci cfg.cfghdr.hdr = &hdr; 89062306a36Sopenharmony_ci cfg.physAddr = -1; 89162306a36Sopenharmony_ci cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; 89262306a36Sopenharmony_ci cfg.dir = 0; 89362306a36Sopenharmony_ci cfg.pageAddr = portnum; 89462306a36Sopenharmony_ci cfg.timeout = 0; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci if ((rc = mpt_config(ioc, &cfg)) != 0) 89762306a36Sopenharmony_ci return rc; 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci if (hdr.PageLength == 0) 90062306a36Sopenharmony_ci return -ENODEV; 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_cistart_over: 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci if (ioc->fc_data.fc_port_page1[portnum].data == NULL) { 90562306a36Sopenharmony_ci data_sz = hdr.PageLength * 4; 90662306a36Sopenharmony_ci if (data_sz < sizeof(FCPortPage1_t)) 90762306a36Sopenharmony_ci data_sz = sizeof(FCPortPage1_t); 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci page1_alloc = dma_alloc_coherent(&ioc->pcidev->dev, data_sz, 91062306a36Sopenharmony_ci &page1_dma, GFP_KERNEL); 91162306a36Sopenharmony_ci if (!page1_alloc) 91262306a36Sopenharmony_ci return -ENOMEM; 91362306a36Sopenharmony_ci } 91462306a36Sopenharmony_ci else { 91562306a36Sopenharmony_ci page1_alloc = ioc->fc_data.fc_port_page1[portnum].data; 91662306a36Sopenharmony_ci page1_dma = ioc->fc_data.fc_port_page1[portnum].dma; 91762306a36Sopenharmony_ci data_sz = ioc->fc_data.fc_port_page1[portnum].pg_sz; 91862306a36Sopenharmony_ci if (hdr.PageLength * 4 > data_sz) { 91962306a36Sopenharmony_ci ioc->fc_data.fc_port_page1[portnum].data = NULL; 92062306a36Sopenharmony_ci dma_free_coherent(&ioc->pcidev->dev, data_sz, 92162306a36Sopenharmony_ci page1_alloc, page1_dma); 92262306a36Sopenharmony_ci goto start_over; 92362306a36Sopenharmony_ci } 92462306a36Sopenharmony_ci } 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci cfg.physAddr = page1_dma; 92762306a36Sopenharmony_ci cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci if ((rc = mpt_config(ioc, &cfg)) == 0) { 93062306a36Sopenharmony_ci ioc->fc_data.fc_port_page1[portnum].data = page1_alloc; 93162306a36Sopenharmony_ci ioc->fc_data.fc_port_page1[portnum].pg_sz = data_sz; 93262306a36Sopenharmony_ci ioc->fc_data.fc_port_page1[portnum].dma = page1_dma; 93362306a36Sopenharmony_ci } 93462306a36Sopenharmony_ci else { 93562306a36Sopenharmony_ci ioc->fc_data.fc_port_page1[portnum].data = NULL; 93662306a36Sopenharmony_ci dma_free_coherent(&ioc->pcidev->dev, data_sz, page1_alloc, 93762306a36Sopenharmony_ci page1_dma); 93862306a36Sopenharmony_ci } 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci return rc; 94162306a36Sopenharmony_ci} 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_cistatic void 94462306a36Sopenharmony_cimptfc_SetFcPortPage1_defaults(MPT_ADAPTER *ioc) 94562306a36Sopenharmony_ci{ 94662306a36Sopenharmony_ci int ii; 94762306a36Sopenharmony_ci FCPortPage1_t *pp1; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci #define MPTFC_FW_DEVICE_TIMEOUT (1) 95062306a36Sopenharmony_ci #define MPTFC_FW_IO_PEND_TIMEOUT (1) 95162306a36Sopenharmony_ci #define ON_FLAGS (MPI_FCPORTPAGE1_FLAGS_IMMEDIATE_ERROR_REPLY) 95262306a36Sopenharmony_ci #define OFF_FLAGS (MPI_FCPORTPAGE1_FLAGS_VERBOSE_RESCAN_EVENTS) 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci for (ii=0; ii<ioc->facts.NumberOfPorts; ii++) { 95562306a36Sopenharmony_ci if (mptfc_GetFcPortPage1(ioc, ii) != 0) 95662306a36Sopenharmony_ci continue; 95762306a36Sopenharmony_ci pp1 = ioc->fc_data.fc_port_page1[ii].data; 95862306a36Sopenharmony_ci if ((pp1->InitiatorDeviceTimeout == MPTFC_FW_DEVICE_TIMEOUT) 95962306a36Sopenharmony_ci && (pp1->InitiatorIoPendTimeout == MPTFC_FW_IO_PEND_TIMEOUT) 96062306a36Sopenharmony_ci && ((pp1->Flags & ON_FLAGS) == ON_FLAGS) 96162306a36Sopenharmony_ci && ((pp1->Flags & OFF_FLAGS) == 0)) 96262306a36Sopenharmony_ci continue; 96362306a36Sopenharmony_ci pp1->InitiatorDeviceTimeout = MPTFC_FW_DEVICE_TIMEOUT; 96462306a36Sopenharmony_ci pp1->InitiatorIoPendTimeout = MPTFC_FW_IO_PEND_TIMEOUT; 96562306a36Sopenharmony_ci pp1->Flags &= ~OFF_FLAGS; 96662306a36Sopenharmony_ci pp1->Flags |= ON_FLAGS; 96762306a36Sopenharmony_ci mptfc_WriteFcPortPage1(ioc, ii); 96862306a36Sopenharmony_ci } 96962306a36Sopenharmony_ci} 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_cistatic void 97362306a36Sopenharmony_cimptfc_init_host_attr(MPT_ADAPTER *ioc,int portnum) 97462306a36Sopenharmony_ci{ 97562306a36Sopenharmony_ci unsigned class = 0; 97662306a36Sopenharmony_ci unsigned cos = 0; 97762306a36Sopenharmony_ci unsigned speed; 97862306a36Sopenharmony_ci unsigned port_type; 97962306a36Sopenharmony_ci unsigned port_state; 98062306a36Sopenharmony_ci FCPortPage0_t *pp0; 98162306a36Sopenharmony_ci struct Scsi_Host *sh; 98262306a36Sopenharmony_ci char *sn; 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci /* don't know what to do as only one scsi (fc) host was allocated */ 98562306a36Sopenharmony_ci if (portnum != 0) 98662306a36Sopenharmony_ci return; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci pp0 = &ioc->fc_port_page0[portnum]; 98962306a36Sopenharmony_ci sh = ioc->sh; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci sn = fc_host_symbolic_name(sh); 99262306a36Sopenharmony_ci snprintf(sn, FC_SYMBOLIC_NAME_SIZE, "%s %s%08xh", 99362306a36Sopenharmony_ci ioc->prod_name, 99462306a36Sopenharmony_ci MPT_FW_REV_MAGIC_ID_STRING, 99562306a36Sopenharmony_ci ioc->facts.FWVersion.Word); 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci fc_host_tgtid_bind_type(sh) = FC_TGTID_BIND_BY_WWPN; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci fc_host_maxframe_size(sh) = pp0->MaxFrameSize; 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci fc_host_node_name(sh) = 100262306a36Sopenharmony_ci (u64)pp0->WWNN.High << 32 | (u64)pp0->WWNN.Low; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci fc_host_port_name(sh) = 100562306a36Sopenharmony_ci (u64)pp0->WWPN.High << 32 | (u64)pp0->WWPN.Low; 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci fc_host_port_id(sh) = pp0->PortIdentifier; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci class = pp0->SupportedServiceClass; 101062306a36Sopenharmony_ci if (class & MPI_FCPORTPAGE0_SUPPORT_CLASS_1) 101162306a36Sopenharmony_ci cos |= FC_COS_CLASS1; 101262306a36Sopenharmony_ci if (class & MPI_FCPORTPAGE0_SUPPORT_CLASS_2) 101362306a36Sopenharmony_ci cos |= FC_COS_CLASS2; 101462306a36Sopenharmony_ci if (class & MPI_FCPORTPAGE0_SUPPORT_CLASS_3) 101562306a36Sopenharmony_ci cos |= FC_COS_CLASS3; 101662306a36Sopenharmony_ci fc_host_supported_classes(sh) = cos; 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci if (pp0->CurrentSpeed == MPI_FCPORTPAGE0_CURRENT_SPEED_1GBIT) 101962306a36Sopenharmony_ci speed = FC_PORTSPEED_1GBIT; 102062306a36Sopenharmony_ci else if (pp0->CurrentSpeed == MPI_FCPORTPAGE0_CURRENT_SPEED_2GBIT) 102162306a36Sopenharmony_ci speed = FC_PORTSPEED_2GBIT; 102262306a36Sopenharmony_ci else if (pp0->CurrentSpeed == MPI_FCPORTPAGE0_CURRENT_SPEED_4GBIT) 102362306a36Sopenharmony_ci speed = FC_PORTSPEED_4GBIT; 102462306a36Sopenharmony_ci else if (pp0->CurrentSpeed == MPI_FCPORTPAGE0_CURRENT_SPEED_10GBIT) 102562306a36Sopenharmony_ci speed = FC_PORTSPEED_10GBIT; 102662306a36Sopenharmony_ci else 102762306a36Sopenharmony_ci speed = FC_PORTSPEED_UNKNOWN; 102862306a36Sopenharmony_ci fc_host_speed(sh) = speed; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci speed = 0; 103162306a36Sopenharmony_ci if (pp0->SupportedSpeeds & MPI_FCPORTPAGE0_SUPPORT_1GBIT_SPEED) 103262306a36Sopenharmony_ci speed |= FC_PORTSPEED_1GBIT; 103362306a36Sopenharmony_ci if (pp0->SupportedSpeeds & MPI_FCPORTPAGE0_SUPPORT_2GBIT_SPEED) 103462306a36Sopenharmony_ci speed |= FC_PORTSPEED_2GBIT; 103562306a36Sopenharmony_ci if (pp0->SupportedSpeeds & MPI_FCPORTPAGE0_SUPPORT_4GBIT_SPEED) 103662306a36Sopenharmony_ci speed |= FC_PORTSPEED_4GBIT; 103762306a36Sopenharmony_ci if (pp0->SupportedSpeeds & MPI_FCPORTPAGE0_SUPPORT_10GBIT_SPEED) 103862306a36Sopenharmony_ci speed |= FC_PORTSPEED_10GBIT; 103962306a36Sopenharmony_ci fc_host_supported_speeds(sh) = speed; 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci port_state = FC_PORTSTATE_UNKNOWN; 104262306a36Sopenharmony_ci if (pp0->PortState == MPI_FCPORTPAGE0_PORTSTATE_ONLINE) 104362306a36Sopenharmony_ci port_state = FC_PORTSTATE_ONLINE; 104462306a36Sopenharmony_ci else if (pp0->PortState == MPI_FCPORTPAGE0_PORTSTATE_OFFLINE) 104562306a36Sopenharmony_ci port_state = FC_PORTSTATE_LINKDOWN; 104662306a36Sopenharmony_ci fc_host_port_state(sh) = port_state; 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci port_type = FC_PORTTYPE_UNKNOWN; 104962306a36Sopenharmony_ci if (pp0->Flags & MPI_FCPORTPAGE0_FLAGS_ATTACH_POINT_TO_POINT) 105062306a36Sopenharmony_ci port_type = FC_PORTTYPE_PTP; 105162306a36Sopenharmony_ci else if (pp0->Flags & MPI_FCPORTPAGE0_FLAGS_ATTACH_PRIVATE_LOOP) 105262306a36Sopenharmony_ci port_type = FC_PORTTYPE_LPORT; 105362306a36Sopenharmony_ci else if (pp0->Flags & MPI_FCPORTPAGE0_FLAGS_ATTACH_PUBLIC_LOOP) 105462306a36Sopenharmony_ci port_type = FC_PORTTYPE_NLPORT; 105562306a36Sopenharmony_ci else if (pp0->Flags & MPI_FCPORTPAGE0_FLAGS_ATTACH_FABRIC_DIRECT) 105662306a36Sopenharmony_ci port_type = FC_PORTTYPE_NPORT; 105762306a36Sopenharmony_ci fc_host_port_type(sh) = port_type; 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci fc_host_fabric_name(sh) = 106062306a36Sopenharmony_ci (pp0->Flags & MPI_FCPORTPAGE0_FLAGS_FABRIC_WWN_VALID) ? 106162306a36Sopenharmony_ci (u64) pp0->FabricWWNN.High << 32 | (u64) pp0->FabricWWPN.Low : 106262306a36Sopenharmony_ci (u64)pp0->WWNN.High << 32 | (u64)pp0->WWNN.Low; 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci} 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_cistatic void 106762306a36Sopenharmony_cimptfc_link_status_change(struct work_struct *work) 106862306a36Sopenharmony_ci{ 106962306a36Sopenharmony_ci MPT_ADAPTER *ioc = 107062306a36Sopenharmony_ci container_of(work, MPT_ADAPTER, fc_rescan_work); 107162306a36Sopenharmony_ci int ii; 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci for (ii=0; ii < ioc->facts.NumberOfPorts; ii++) 107462306a36Sopenharmony_ci (void) mptfc_GetFcPortPage0(ioc, ii); 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci} 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_cistatic void 107962306a36Sopenharmony_cimptfc_setup_reset(struct work_struct *work) 108062306a36Sopenharmony_ci{ 108162306a36Sopenharmony_ci MPT_ADAPTER *ioc = 108262306a36Sopenharmony_ci container_of(work, MPT_ADAPTER, fc_setup_reset_work); 108362306a36Sopenharmony_ci u64 pn; 108462306a36Sopenharmony_ci struct mptfc_rport_info *ri; 108562306a36Sopenharmony_ci struct scsi_target *starget; 108662306a36Sopenharmony_ci VirtTarget *vtarget; 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci /* reset about to happen, delete (block) all rports */ 108962306a36Sopenharmony_ci list_for_each_entry(ri, &ioc->fc_rports, list) { 109062306a36Sopenharmony_ci if (ri->flags & MPT_RPORT_INFO_FLAGS_REGISTERED) { 109162306a36Sopenharmony_ci ri->flags &= ~MPT_RPORT_INFO_FLAGS_REGISTERED; 109262306a36Sopenharmony_ci fc_remote_port_delete(ri->rport); /* won't sleep */ 109362306a36Sopenharmony_ci ri->rport = NULL; 109462306a36Sopenharmony_ci starget = ri->starget; 109562306a36Sopenharmony_ci if (starget) { 109662306a36Sopenharmony_ci vtarget = starget->hostdata; 109762306a36Sopenharmony_ci if (vtarget) 109862306a36Sopenharmony_ci vtarget->deleted = 1; 109962306a36Sopenharmony_ci } 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci pn = (u64)ri->pg0.WWPN.High << 32 | 110262306a36Sopenharmony_ci (u64)ri->pg0.WWPN.Low; 110362306a36Sopenharmony_ci dfcprintk (ioc, printk(MYIOC_s_DEBUG_FMT 110462306a36Sopenharmony_ci "mptfc_setup_reset.%d: %llx deleted\n", 110562306a36Sopenharmony_ci ioc->name, 110662306a36Sopenharmony_ci ioc->sh->host_no, 110762306a36Sopenharmony_ci (unsigned long long)pn)); 110862306a36Sopenharmony_ci } 110962306a36Sopenharmony_ci } 111062306a36Sopenharmony_ci} 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_cistatic void 111362306a36Sopenharmony_cimptfc_rescan_devices(struct work_struct *work) 111462306a36Sopenharmony_ci{ 111562306a36Sopenharmony_ci MPT_ADAPTER *ioc = 111662306a36Sopenharmony_ci container_of(work, MPT_ADAPTER, fc_rescan_work); 111762306a36Sopenharmony_ci int ii; 111862306a36Sopenharmony_ci u64 pn; 111962306a36Sopenharmony_ci struct mptfc_rport_info *ri; 112062306a36Sopenharmony_ci struct scsi_target *starget; 112162306a36Sopenharmony_ci VirtTarget *vtarget; 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci /* start by tagging all ports as missing */ 112462306a36Sopenharmony_ci list_for_each_entry(ri, &ioc->fc_rports, list) { 112562306a36Sopenharmony_ci if (ri->flags & MPT_RPORT_INFO_FLAGS_REGISTERED) { 112662306a36Sopenharmony_ci ri->flags |= MPT_RPORT_INFO_FLAGS_MISSING; 112762306a36Sopenharmony_ci } 112862306a36Sopenharmony_ci } 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci /* 113162306a36Sopenharmony_ci * now rescan devices known to adapter, 113262306a36Sopenharmony_ci * will reregister existing rports 113362306a36Sopenharmony_ci */ 113462306a36Sopenharmony_ci for (ii=0; ii < ioc->facts.NumberOfPorts; ii++) { 113562306a36Sopenharmony_ci (void) mptfc_GetFcPortPage0(ioc, ii); 113662306a36Sopenharmony_ci mptfc_init_host_attr(ioc, ii); /* refresh */ 113762306a36Sopenharmony_ci mptfc_GetFcDevPage0(ioc, ii, mptfc_register_dev); 113862306a36Sopenharmony_ci } 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci /* delete devices still missing */ 114162306a36Sopenharmony_ci list_for_each_entry(ri, &ioc->fc_rports, list) { 114262306a36Sopenharmony_ci /* if newly missing, delete it */ 114362306a36Sopenharmony_ci if (ri->flags & MPT_RPORT_INFO_FLAGS_MISSING) { 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci ri->flags &= ~(MPT_RPORT_INFO_FLAGS_REGISTERED| 114662306a36Sopenharmony_ci MPT_RPORT_INFO_FLAGS_MISSING); 114762306a36Sopenharmony_ci fc_remote_port_delete(ri->rport); /* won't sleep */ 114862306a36Sopenharmony_ci ri->rport = NULL; 114962306a36Sopenharmony_ci starget = ri->starget; 115062306a36Sopenharmony_ci if (starget) { 115162306a36Sopenharmony_ci vtarget = starget->hostdata; 115262306a36Sopenharmony_ci if (vtarget) 115362306a36Sopenharmony_ci vtarget->deleted = 1; 115462306a36Sopenharmony_ci } 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci pn = (u64)ri->pg0.WWPN.High << 32 | 115762306a36Sopenharmony_ci (u64)ri->pg0.WWPN.Low; 115862306a36Sopenharmony_ci dfcprintk (ioc, printk(MYIOC_s_DEBUG_FMT 115962306a36Sopenharmony_ci "mptfc_rescan.%d: %llx deleted\n", 116062306a36Sopenharmony_ci ioc->name, 116162306a36Sopenharmony_ci ioc->sh->host_no, 116262306a36Sopenharmony_ci (unsigned long long)pn)); 116362306a36Sopenharmony_ci } 116462306a36Sopenharmony_ci } 116562306a36Sopenharmony_ci} 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_cistatic int 116862306a36Sopenharmony_cimptfc_probe(struct pci_dev *pdev, const struct pci_device_id *id) 116962306a36Sopenharmony_ci{ 117062306a36Sopenharmony_ci struct Scsi_Host *sh; 117162306a36Sopenharmony_ci MPT_SCSI_HOST *hd; 117262306a36Sopenharmony_ci MPT_ADAPTER *ioc; 117362306a36Sopenharmony_ci unsigned long flags; 117462306a36Sopenharmony_ci int ii; 117562306a36Sopenharmony_ci int numSGE = 0; 117662306a36Sopenharmony_ci int scale; 117762306a36Sopenharmony_ci int ioc_cap; 117862306a36Sopenharmony_ci int error=0; 117962306a36Sopenharmony_ci int r; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci if ((r = mpt_attach(pdev,id)) != 0) 118262306a36Sopenharmony_ci return r; 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci ioc = pci_get_drvdata(pdev); 118562306a36Sopenharmony_ci ioc->DoneCtx = mptfcDoneCtx; 118662306a36Sopenharmony_ci ioc->TaskCtx = mptfcTaskCtx; 118762306a36Sopenharmony_ci ioc->InternalCtx = mptfcInternalCtx; 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci /* Added sanity check on readiness of the MPT adapter. 119062306a36Sopenharmony_ci */ 119162306a36Sopenharmony_ci if (ioc->last_state != MPI_IOC_STATE_OPERATIONAL) { 119262306a36Sopenharmony_ci printk(MYIOC_s_WARN_FMT 119362306a36Sopenharmony_ci "Skipping because it's not operational!\n", 119462306a36Sopenharmony_ci ioc->name); 119562306a36Sopenharmony_ci error = -ENODEV; 119662306a36Sopenharmony_ci goto out_mptfc_probe; 119762306a36Sopenharmony_ci } 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci if (!ioc->active) { 120062306a36Sopenharmony_ci printk(MYIOC_s_WARN_FMT "Skipping because it's disabled!\n", 120162306a36Sopenharmony_ci ioc->name); 120262306a36Sopenharmony_ci error = -ENODEV; 120362306a36Sopenharmony_ci goto out_mptfc_probe; 120462306a36Sopenharmony_ci } 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci /* Sanity check - ensure at least 1 port is INITIATOR capable 120762306a36Sopenharmony_ci */ 120862306a36Sopenharmony_ci ioc_cap = 0; 120962306a36Sopenharmony_ci for (ii=0; ii < ioc->facts.NumberOfPorts; ii++) { 121062306a36Sopenharmony_ci if (ioc->pfacts[ii].ProtocolFlags & 121162306a36Sopenharmony_ci MPI_PORTFACTS_PROTOCOL_INITIATOR) 121262306a36Sopenharmony_ci ioc_cap ++; 121362306a36Sopenharmony_ci } 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci if (!ioc_cap) { 121662306a36Sopenharmony_ci printk(MYIOC_s_WARN_FMT 121762306a36Sopenharmony_ci "Skipping ioc=%p because SCSI Initiator mode is NOT enabled!\n", 121862306a36Sopenharmony_ci ioc->name, ioc); 121962306a36Sopenharmony_ci return 0; 122062306a36Sopenharmony_ci } 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci sh = scsi_host_alloc(&mptfc_driver_template, sizeof(MPT_SCSI_HOST)); 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci if (!sh) { 122562306a36Sopenharmony_ci printk(MYIOC_s_WARN_FMT 122662306a36Sopenharmony_ci "Unable to register controller with SCSI subsystem\n", 122762306a36Sopenharmony_ci ioc->name); 122862306a36Sopenharmony_ci error = -1; 122962306a36Sopenharmony_ci goto out_mptfc_probe; 123062306a36Sopenharmony_ci } 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci spin_lock_init(&ioc->fc_rescan_work_lock); 123362306a36Sopenharmony_ci INIT_WORK(&ioc->fc_rescan_work, mptfc_rescan_devices); 123462306a36Sopenharmony_ci INIT_WORK(&ioc->fc_setup_reset_work, mptfc_setup_reset); 123562306a36Sopenharmony_ci INIT_WORK(&ioc->fc_lsc_work, mptfc_link_status_change); 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci spin_lock_irqsave(&ioc->FreeQlock, flags); 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci /* Attach the SCSI Host to the IOC structure 124062306a36Sopenharmony_ci */ 124162306a36Sopenharmony_ci ioc->sh = sh; 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci sh->io_port = 0; 124462306a36Sopenharmony_ci sh->n_io_port = 0; 124562306a36Sopenharmony_ci sh->irq = 0; 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci /* set 16 byte cdb's */ 124862306a36Sopenharmony_ci sh->max_cmd_len = 16; 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci sh->max_id = ioc->pfacts->MaxDevices; 125162306a36Sopenharmony_ci sh->max_lun = max_lun; 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci /* Required entry. 125462306a36Sopenharmony_ci */ 125562306a36Sopenharmony_ci sh->unique_id = ioc->id; 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci /* Verify that we won't exceed the maximum 125862306a36Sopenharmony_ci * number of chain buffers 125962306a36Sopenharmony_ci * We can optimize: ZZ = req_sz/sizeof(SGE) 126062306a36Sopenharmony_ci * For 32bit SGE's: 126162306a36Sopenharmony_ci * numSGE = 1 + (ZZ-1)*(maxChain -1) + ZZ 126262306a36Sopenharmony_ci * + (req_sz - 64)/sizeof(SGE) 126362306a36Sopenharmony_ci * A slightly different algorithm is required for 126462306a36Sopenharmony_ci * 64bit SGEs. 126562306a36Sopenharmony_ci */ 126662306a36Sopenharmony_ci scale = ioc->req_sz/ioc->SGE_size; 126762306a36Sopenharmony_ci if (ioc->sg_addr_size == sizeof(u64)) { 126862306a36Sopenharmony_ci numSGE = (scale - 1) * 126962306a36Sopenharmony_ci (ioc->facts.MaxChainDepth-1) + scale + 127062306a36Sopenharmony_ci (ioc->req_sz - 60) / ioc->SGE_size; 127162306a36Sopenharmony_ci } else { 127262306a36Sopenharmony_ci numSGE = 1 + (scale - 1) * 127362306a36Sopenharmony_ci (ioc->facts.MaxChainDepth-1) + scale + 127462306a36Sopenharmony_ci (ioc->req_sz - 64) / ioc->SGE_size; 127562306a36Sopenharmony_ci } 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci if (numSGE < sh->sg_tablesize) { 127862306a36Sopenharmony_ci /* Reset this value */ 127962306a36Sopenharmony_ci dprintk(ioc, printk(MYIOC_s_DEBUG_FMT 128062306a36Sopenharmony_ci "Resetting sg_tablesize to %d from %d\n", 128162306a36Sopenharmony_ci ioc->name, numSGE, sh->sg_tablesize)); 128262306a36Sopenharmony_ci sh->sg_tablesize = numSGE; 128362306a36Sopenharmony_ci } 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci spin_unlock_irqrestore(&ioc->FreeQlock, flags); 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci hd = shost_priv(sh); 128862306a36Sopenharmony_ci hd->ioc = ioc; 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci /* SCSI needs scsi_cmnd lookup table! 129162306a36Sopenharmony_ci * (with size equal to req_depth*PtrSz!) 129262306a36Sopenharmony_ci */ 129362306a36Sopenharmony_ci ioc->ScsiLookup = kcalloc(ioc->req_depth, sizeof(void *), GFP_KERNEL); 129462306a36Sopenharmony_ci if (!ioc->ScsiLookup) { 129562306a36Sopenharmony_ci error = -ENOMEM; 129662306a36Sopenharmony_ci goto out_mptfc_probe; 129762306a36Sopenharmony_ci } 129862306a36Sopenharmony_ci spin_lock_init(&ioc->scsi_lookup_lock); 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci dprintk(ioc, printk(MYIOC_s_DEBUG_FMT "ScsiLookup @ %p\n", 130162306a36Sopenharmony_ci ioc->name, ioc->ScsiLookup)); 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci hd->last_queue_full = 0; 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci sh->transportt = mptfc_transport_template; 130662306a36Sopenharmony_ci error = scsi_add_host (sh, &ioc->pcidev->dev); 130762306a36Sopenharmony_ci if(error) { 130862306a36Sopenharmony_ci dprintk(ioc, printk(MYIOC_s_ERR_FMT 130962306a36Sopenharmony_ci "scsi_add_host failed\n", ioc->name)); 131062306a36Sopenharmony_ci goto out_mptfc_probe; 131162306a36Sopenharmony_ci } 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci /* initialize workqueue */ 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci snprintf(ioc->fc_rescan_work_q_name, sizeof(ioc->fc_rescan_work_q_name), 131662306a36Sopenharmony_ci "mptfc_wq_%d", sh->host_no); 131762306a36Sopenharmony_ci ioc->fc_rescan_work_q = 131862306a36Sopenharmony_ci alloc_ordered_workqueue(ioc->fc_rescan_work_q_name, 131962306a36Sopenharmony_ci WQ_MEM_RECLAIM); 132062306a36Sopenharmony_ci if (!ioc->fc_rescan_work_q) { 132162306a36Sopenharmony_ci error = -ENOMEM; 132262306a36Sopenharmony_ci goto out_mptfc_host; 132362306a36Sopenharmony_ci } 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci /* 132662306a36Sopenharmony_ci * Pre-fetch FC port WWN and stuff... 132762306a36Sopenharmony_ci * (FCPortPage0_t stuff) 132862306a36Sopenharmony_ci */ 132962306a36Sopenharmony_ci for (ii=0; ii < ioc->facts.NumberOfPorts; ii++) { 133062306a36Sopenharmony_ci (void) mptfc_GetFcPortPage0(ioc, ii); 133162306a36Sopenharmony_ci } 133262306a36Sopenharmony_ci mptfc_SetFcPortPage1_defaults(ioc); 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci /* 133562306a36Sopenharmony_ci * scan for rports - 133662306a36Sopenharmony_ci * by doing it via the workqueue, some locking is eliminated 133762306a36Sopenharmony_ci */ 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci queue_work(ioc->fc_rescan_work_q, &ioc->fc_rescan_work); 134062306a36Sopenharmony_ci flush_workqueue(ioc->fc_rescan_work_q); 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci return 0; 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ciout_mptfc_host: 134562306a36Sopenharmony_ci scsi_remove_host(sh); 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_ciout_mptfc_probe: 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci mptscsih_remove(pdev); 135062306a36Sopenharmony_ci return error; 135162306a36Sopenharmony_ci} 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_cistatic struct pci_driver mptfc_driver = { 135462306a36Sopenharmony_ci .name = "mptfc", 135562306a36Sopenharmony_ci .id_table = mptfc_pci_table, 135662306a36Sopenharmony_ci .probe = mptfc_probe, 135762306a36Sopenharmony_ci .remove = mptfc_remove, 135862306a36Sopenharmony_ci .shutdown = mptscsih_shutdown, 135962306a36Sopenharmony_ci#ifdef CONFIG_PM 136062306a36Sopenharmony_ci .suspend = mptscsih_suspend, 136162306a36Sopenharmony_ci .resume = mptscsih_resume, 136262306a36Sopenharmony_ci#endif 136362306a36Sopenharmony_ci}; 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_cistatic int 136662306a36Sopenharmony_cimptfc_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply) 136762306a36Sopenharmony_ci{ 136862306a36Sopenharmony_ci MPT_SCSI_HOST *hd; 136962306a36Sopenharmony_ci u8 event = le32_to_cpu(pEvReply->Event) & 0xFF; 137062306a36Sopenharmony_ci unsigned long flags; 137162306a36Sopenharmony_ci int rc=1; 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci if (ioc->bus_type != FC) 137462306a36Sopenharmony_ci return 0; 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci devtverboseprintk(ioc, printk(MYIOC_s_DEBUG_FMT "MPT event (=%02Xh) routed to SCSI host driver!\n", 137762306a36Sopenharmony_ci ioc->name, event)); 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_ci if (ioc->sh == NULL || 138062306a36Sopenharmony_ci ((hd = shost_priv(ioc->sh)) == NULL)) 138162306a36Sopenharmony_ci return 1; 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci switch (event) { 138462306a36Sopenharmony_ci case MPI_EVENT_RESCAN: 138562306a36Sopenharmony_ci spin_lock_irqsave(&ioc->fc_rescan_work_lock, flags); 138662306a36Sopenharmony_ci if (ioc->fc_rescan_work_q) { 138762306a36Sopenharmony_ci queue_work(ioc->fc_rescan_work_q, 138862306a36Sopenharmony_ci &ioc->fc_rescan_work); 138962306a36Sopenharmony_ci } 139062306a36Sopenharmony_ci spin_unlock_irqrestore(&ioc->fc_rescan_work_lock, flags); 139162306a36Sopenharmony_ci break; 139262306a36Sopenharmony_ci case MPI_EVENT_LINK_STATUS_CHANGE: 139362306a36Sopenharmony_ci spin_lock_irqsave(&ioc->fc_rescan_work_lock, flags); 139462306a36Sopenharmony_ci if (ioc->fc_rescan_work_q) { 139562306a36Sopenharmony_ci queue_work(ioc->fc_rescan_work_q, 139662306a36Sopenharmony_ci &ioc->fc_lsc_work); 139762306a36Sopenharmony_ci } 139862306a36Sopenharmony_ci spin_unlock_irqrestore(&ioc->fc_rescan_work_lock, flags); 139962306a36Sopenharmony_ci break; 140062306a36Sopenharmony_ci default: 140162306a36Sopenharmony_ci rc = mptscsih_event_process(ioc,pEvReply); 140262306a36Sopenharmony_ci break; 140362306a36Sopenharmony_ci } 140462306a36Sopenharmony_ci return rc; 140562306a36Sopenharmony_ci} 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_cistatic int 140862306a36Sopenharmony_cimptfc_ioc_reset(MPT_ADAPTER *ioc, int reset_phase) 140962306a36Sopenharmony_ci{ 141062306a36Sopenharmony_ci int rc; 141162306a36Sopenharmony_ci unsigned long flags; 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci rc = mptscsih_ioc_reset(ioc,reset_phase); 141462306a36Sopenharmony_ci if ((ioc->bus_type != FC) || (!rc)) 141562306a36Sopenharmony_ci return rc; 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_ci dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT 141962306a36Sopenharmony_ci ": IOC %s_reset routed to FC host driver!\n",ioc->name, 142062306a36Sopenharmony_ci reset_phase==MPT_IOC_SETUP_RESET ? "setup" : ( 142162306a36Sopenharmony_ci reset_phase==MPT_IOC_PRE_RESET ? "pre" : "post"))); 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci if (reset_phase == MPT_IOC_SETUP_RESET) { 142462306a36Sopenharmony_ci spin_lock_irqsave(&ioc->fc_rescan_work_lock, flags); 142562306a36Sopenharmony_ci if (ioc->fc_rescan_work_q) { 142662306a36Sopenharmony_ci queue_work(ioc->fc_rescan_work_q, 142762306a36Sopenharmony_ci &ioc->fc_setup_reset_work); 142862306a36Sopenharmony_ci } 142962306a36Sopenharmony_ci spin_unlock_irqrestore(&ioc->fc_rescan_work_lock, flags); 143062306a36Sopenharmony_ci } 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_ci else if (reset_phase == MPT_IOC_PRE_RESET) { 143362306a36Sopenharmony_ci } 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci else { /* MPT_IOC_POST_RESET */ 143662306a36Sopenharmony_ci mptfc_SetFcPortPage1_defaults(ioc); 143762306a36Sopenharmony_ci spin_lock_irqsave(&ioc->fc_rescan_work_lock, flags); 143862306a36Sopenharmony_ci if (ioc->fc_rescan_work_q) { 143962306a36Sopenharmony_ci queue_work(ioc->fc_rescan_work_q, 144062306a36Sopenharmony_ci &ioc->fc_rescan_work); 144162306a36Sopenharmony_ci } 144262306a36Sopenharmony_ci spin_unlock_irqrestore(&ioc->fc_rescan_work_lock, flags); 144362306a36Sopenharmony_ci } 144462306a36Sopenharmony_ci return 1; 144562306a36Sopenharmony_ci} 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_ci/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ 144862306a36Sopenharmony_ci/** 144962306a36Sopenharmony_ci * mptfc_init - Register MPT adapter(s) as SCSI host(s) with SCSI mid-layer. 145062306a36Sopenharmony_ci * 145162306a36Sopenharmony_ci * Returns 0 for success, non-zero for failure. 145262306a36Sopenharmony_ci */ 145362306a36Sopenharmony_cistatic int __init 145462306a36Sopenharmony_cimptfc_init(void) 145562306a36Sopenharmony_ci{ 145662306a36Sopenharmony_ci int error; 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_ci show_mptmod_ver(my_NAME, my_VERSION); 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci /* sanity check module parameters */ 146162306a36Sopenharmony_ci if (mptfc_dev_loss_tmo <= 0) 146262306a36Sopenharmony_ci mptfc_dev_loss_tmo = MPTFC_DEV_LOSS_TMO; 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_ci mptfc_transport_template = 146562306a36Sopenharmony_ci fc_attach_transport(&mptfc_transport_functions); 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_ci if (!mptfc_transport_template) 146862306a36Sopenharmony_ci return -ENODEV; 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_ci mptfcDoneCtx = mpt_register(mptscsih_io_done, MPTFC_DRIVER, 147162306a36Sopenharmony_ci "mptscsih_scandv_complete"); 147262306a36Sopenharmony_ci mptfcTaskCtx = mpt_register(mptscsih_taskmgmt_complete, MPTFC_DRIVER, 147362306a36Sopenharmony_ci "mptscsih_scandv_complete"); 147462306a36Sopenharmony_ci mptfcInternalCtx = mpt_register(mptscsih_scandv_complete, MPTFC_DRIVER, 147562306a36Sopenharmony_ci "mptscsih_scandv_complete"); 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_ci mpt_event_register(mptfcDoneCtx, mptfc_event_process); 147862306a36Sopenharmony_ci mpt_reset_register(mptfcDoneCtx, mptfc_ioc_reset); 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci error = pci_register_driver(&mptfc_driver); 148162306a36Sopenharmony_ci if (error) 148262306a36Sopenharmony_ci fc_release_transport(mptfc_transport_template); 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci return error; 148562306a36Sopenharmony_ci} 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ 148862306a36Sopenharmony_ci/** 148962306a36Sopenharmony_ci * mptfc_remove - Remove fc infrastructure for devices 149062306a36Sopenharmony_ci * @pdev: Pointer to pci_dev structure 149162306a36Sopenharmony_ci * 149262306a36Sopenharmony_ci */ 149362306a36Sopenharmony_cistatic void mptfc_remove(struct pci_dev *pdev) 149462306a36Sopenharmony_ci{ 149562306a36Sopenharmony_ci MPT_ADAPTER *ioc = pci_get_drvdata(pdev); 149662306a36Sopenharmony_ci struct mptfc_rport_info *p, *n; 149762306a36Sopenharmony_ci struct workqueue_struct *work_q; 149862306a36Sopenharmony_ci unsigned long flags; 149962306a36Sopenharmony_ci int ii; 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci /* destroy workqueue */ 150262306a36Sopenharmony_ci if ((work_q=ioc->fc_rescan_work_q)) { 150362306a36Sopenharmony_ci spin_lock_irqsave(&ioc->fc_rescan_work_lock, flags); 150462306a36Sopenharmony_ci ioc->fc_rescan_work_q = NULL; 150562306a36Sopenharmony_ci spin_unlock_irqrestore(&ioc->fc_rescan_work_lock, flags); 150662306a36Sopenharmony_ci destroy_workqueue(work_q); 150762306a36Sopenharmony_ci } 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci fc_remove_host(ioc->sh); 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_ci list_for_each_entry_safe(p, n, &ioc->fc_rports, list) { 151262306a36Sopenharmony_ci list_del(&p->list); 151362306a36Sopenharmony_ci kfree(p); 151462306a36Sopenharmony_ci } 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci for (ii=0; ii<ioc->facts.NumberOfPorts; ii++) { 151762306a36Sopenharmony_ci if (ioc->fc_data.fc_port_page1[ii].data) { 151862306a36Sopenharmony_ci dma_free_coherent(&ioc->pcidev->dev, 151962306a36Sopenharmony_ci ioc->fc_data.fc_port_page1[ii].pg_sz, 152062306a36Sopenharmony_ci ioc->fc_data.fc_port_page1[ii].data, 152162306a36Sopenharmony_ci ioc->fc_data.fc_port_page1[ii].dma); 152262306a36Sopenharmony_ci ioc->fc_data.fc_port_page1[ii].data = NULL; 152362306a36Sopenharmony_ci } 152462306a36Sopenharmony_ci } 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_ci scsi_remove_host(ioc->sh); 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_ci mptscsih_remove(pdev); 152962306a36Sopenharmony_ci} 153062306a36Sopenharmony_ci 153162306a36Sopenharmony_ci/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ 153262306a36Sopenharmony_ci/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ 153362306a36Sopenharmony_ci/** 153462306a36Sopenharmony_ci * mptfc_exit - Unregisters MPT adapter(s) 153562306a36Sopenharmony_ci * 153662306a36Sopenharmony_ci */ 153762306a36Sopenharmony_cistatic void __exit 153862306a36Sopenharmony_cimptfc_exit(void) 153962306a36Sopenharmony_ci{ 154062306a36Sopenharmony_ci pci_unregister_driver(&mptfc_driver); 154162306a36Sopenharmony_ci fc_release_transport(mptfc_transport_template); 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ci mpt_reset_deregister(mptfcDoneCtx); 154462306a36Sopenharmony_ci mpt_event_deregister(mptfcDoneCtx); 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci mpt_deregister(mptfcInternalCtx); 154762306a36Sopenharmony_ci mpt_deregister(mptfcTaskCtx); 154862306a36Sopenharmony_ci mpt_deregister(mptfcDoneCtx); 154962306a36Sopenharmony_ci} 155062306a36Sopenharmony_ci 155162306a36Sopenharmony_cimodule_init(mptfc_init); 155262306a36Sopenharmony_cimodule_exit(mptfc_exit); 1553